diff options
Diffstat (limited to 'pkgs')
431 files changed, 59929 insertions, 1 deletions
diff --git a/pkgs/default.nix b/pkgs/default.nix index be0cc4f..082cca1 100644 --- a/pkgs/default.nix +++ b/pkgs/default.nix @@ -1,8 +1,11 @@ { nixpkgs ? <nixpkgs>, nixlib ? nixpkgs.lib }: +with builtins; +with nixlib; + let pkgs = nixpkgs // turrispkgs; - callPackage = nixlib.callPackageWith pkgs; + callPackage = callPackageWith pkgs; turrispkgs = with pkgs; { @@ -11,6 +14,14 @@ let mox-otp = python3Packages.callPackage ./mox-otp { }; crypto-wrapper = callPackage ./crypto-wrapper { }; + # Turris kernels with patches + linux_turris_5_15 = callPackage + "${nixpkgs.path}/pkgs/os-specific/linux/kernel/linux-5.15.nix" { + kernelPatches = map (p: { name = toString p; patch = ./patches-linux-5.15 + "/${p}"; }) ( + attrNames (filterAttrs (n: v: v == "regular") ( + readDir ./patches-linux-5.15))); + }; + # NOR Firmwares armTrustedFirmwareTurrisMox = buildArmTrustedFirmware rec { platform = "a3700"; diff --git a/pkgs/patches-linux-5.15/0001-PCI-mvebu-Replace-pci_ioremap_io-usage-by-devm_pci_r.patch b/pkgs/patches-linux-5.15/0001-PCI-mvebu-Replace-pci_ioremap_io-usage-by-devm_pci_r.patch new file mode 100644 index 0000000..abe0360 --- /dev/null +++ b/pkgs/patches-linux-5.15/0001-PCI-mvebu-Replace-pci_ioremap_io-usage-by-devm_pci_r.patch @@ -0,0 +1,62 @@ +From 42b54ca7612af1051a744abf00fdd5a04bccb627 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org> +Date: Tue, 23 Nov 2021 12:22:59 +0100 +Subject: [PATCH 01/90] PCI: mvebu: Replace pci_ioremap_io() usage by + devm_pci_remap_iospace() +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Now when ARM architecture code also provides standard PCI core function +pci_remap_iospace(), use its devm_pci_remap_iospace() variant in +pci-mvebu.c driver instead of old ARM-specific pci_ioremap_io() function. + +Call devm_pci_remap_iospace() before adding IO resource to host bridge +structure, at the place where it should be. + +Signed-off-by: Pali Rohár <pali@kernel.org> +--- + drivers/pci/controller/pci-mvebu.c | 13 ++++--------- + 1 file changed, 4 insertions(+), 9 deletions(-) + +diff --git a/drivers/pci/controller/pci-mvebu.c b/drivers/pci/controller/pci-mvebu.c +index 2a3bf82aa4e2..12661311853b 100644 +--- a/drivers/pci/controller/pci-mvebu.c ++++ b/drivers/pci/controller/pci-mvebu.c +@@ -1082,6 +1082,10 @@ static int mvebu_pcie_parse_request_resources(struct mvebu_pcie *pcie) + resource_size(&pcie->io) - 1); + pcie->realio.name = "PCI I/O"; + ++ ret = devm_pci_remap_iospace(dev, &pcie->realio, pcie->io.start); ++ if (ret) ++ return ret; ++ + pci_add_resource(&bridge->windows, &pcie->realio); + ret = devm_request_resource(dev, &ioport_resource, &pcie->realio); + if (ret) +@@ -1100,7 +1104,6 @@ static int mvebu_pcie_parse_request_resources(struct mvebu_pcie *pcie) + */ + static int mvebu_pci_host_probe(struct pci_host_bridge *bridge) + { +- struct mvebu_pcie *pcie; + struct pci_bus *bus, *child; + int ret; + +@@ -1110,14 +1113,6 @@ static int mvebu_pci_host_probe(struct pci_host_bridge *bridge) + return ret; + } + +- pcie = pci_host_bridge_priv(bridge); +- if (resource_size(&pcie->io) != 0) { +- unsigned int i; +- +- for (i = 0; i < resource_size(&pcie->realio); i += SZ_64K) +- pci_ioremap_io(i, pcie->io.start + i); +- } +- + bus = bridge->bus; + + /* +-- +2.34.1 + diff --git a/pkgs/patches-linux-5.15/0002-PCI-mvebu-Remove-custom-mvebu_pci_host_probe-functio.patch b/pkgs/patches-linux-5.15/0002-PCI-mvebu-Remove-custom-mvebu_pci_host_probe-functio.patch new file mode 100644 index 0000000..ea40f97 --- /dev/null +++ b/pkgs/patches-linux-5.15/0002-PCI-mvebu-Remove-custom-mvebu_pci_host_probe-functio.patch @@ -0,0 +1,82 @@ +From 74c5cf43b5e5de7832075264762c9ea85ef06b0a Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org> +Date: Tue, 16 Nov 2021 20:19:52 +0100 +Subject: [PATCH 02/90] PCI: mvebu: Remove custom mvebu_pci_host_probe() + function +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Now after pci_ioremap_io() usage was replaced by devm_pci_remap_iospace() +function, there is no need to use custom mvebu_pci_host_probe() function. +Current implementation of mvebu_pci_host_probe() is same as standard PCI +core functionn pci_host_probe(). So replace mvebu_pci_host_probe() call by +pci_host_probe() and remove custom mvebu_pci_host_probe() function. + +Signed-off-by: Pali Rohár <pali@kernel.org> +--- + drivers/pci/controller/pci-mvebu.c | 41 +----------------------------- + 1 file changed, 1 insertion(+), 40 deletions(-) + +diff --git a/drivers/pci/controller/pci-mvebu.c b/drivers/pci/controller/pci-mvebu.c +index 12661311853b..9cbf10d6fc30 100644 +--- a/drivers/pci/controller/pci-mvebu.c ++++ b/drivers/pci/controller/pci-mvebu.c +@@ -1095,45 +1095,6 @@ static int mvebu_pcie_parse_request_resources(struct mvebu_pcie *pcie) + return 0; + } + +-/* +- * This is a copy of pci_host_probe(), except that it does the I/O +- * remap as the last step, once we are sure we won't fail. +- * +- * It should be removed once the I/O remap error handling issue has +- * been sorted out. +- */ +-static int mvebu_pci_host_probe(struct pci_host_bridge *bridge) +-{ +- struct pci_bus *bus, *child; +- int ret; +- +- ret = pci_scan_root_bus_bridge(bridge); +- if (ret < 0) { +- dev_err(bridge->dev.parent, "Scanning root bridge failed"); +- return ret; +- } +- +- bus = bridge->bus; +- +- /* +- * We insert PCI resources into the iomem_resource and +- * ioport_resource trees in either pci_bus_claim_resources() +- * or pci_bus_assign_resources(). +- */ +- if (pci_has_flag(PCI_PROBE_ONLY)) { +- pci_bus_claim_resources(bus); +- } else { +- pci_bus_size_bridges(bus); +- pci_bus_assign_resources(bus); +- +- list_for_each_entry(child, &bus->children, node) +- pcie_bus_configure_settings(child); +- } +- +- pci_bus_add_devices(bus); +- return 0; +-} +- + static int mvebu_pcie_probe(struct platform_device *pdev) + { + struct device *dev = &pdev->dev; +@@ -1293,7 +1254,7 @@ static int mvebu_pcie_probe(struct platform_device *pdev) + bridge->ops = &mvebu_pcie_ops; + bridge->align_resource = mvebu_pcie_align_resource; + +- return mvebu_pci_host_probe(bridge); ++ return pci_host_probe(bridge); + } + + static const struct of_device_id mvebu_pcie_of_match_table[] = { +-- +2.34.1 + diff --git a/pkgs/patches-linux-5.15/0003-arm-ioremap-Replace-pci_ioremap_io-usage-by-pci_rema.patch b/pkgs/patches-linux-5.15/0003-arm-ioremap-Replace-pci_ioremap_io-usage-by-pci_rema.patch new file mode 100644 index 0000000..eb1d636 --- /dev/null +++ b/pkgs/patches-linux-5.15/0003-arm-ioremap-Replace-pci_ioremap_io-usage-by-pci_rema.patch @@ -0,0 +1,170 @@ +From f7b0b5aa413f2a38083e4155baf211117121674c Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org> +Date: Tue, 23 Nov 2021 12:05:54 +0100 +Subject: [PATCH 03/90] arm: ioremap: Replace pci_ioremap_io() usage by + pci_remap_iospace() +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Replace all usage of ARM specific pci_ioremap_io() function by standard PCI +core API function pci_remap_iospace() in all drivers and arm march code. + +Signed-off-by: Pali Rohár <pali@kernel.org> +--- + arch/arm/mach-dove/pcie.c | 9 +++++---- + arch/arm/mach-iop32x/pci.c | 5 ++++- + arch/arm/mach-mv78xx0/pcie.c | 5 ++++- + arch/arm/mach-orion5x/pci.c | 10 ++++++++-- + drivers/pcmcia/at91_cf.c | 6 +++++- + 5 files changed, 26 insertions(+), 9 deletions(-) + +diff --git a/arch/arm/mach-dove/pcie.c b/arch/arm/mach-dove/pcie.c +index ee91ac6b5ebf..2a493bdfffc6 100644 +--- a/arch/arm/mach-dove/pcie.c ++++ b/arch/arm/mach-dove/pcie.c +@@ -38,6 +38,7 @@ static int num_pcie_ports; + static int __init dove_pcie_setup(int nr, struct pci_sys_data *sys) + { + struct pcie_port *pp; ++ struct resource realio; + + if (nr >= num_pcie_ports) + return 0; +@@ -53,10 +54,10 @@ static int __init dove_pcie_setup(int nr, struct pci_sys_data *sys) + + orion_pcie_setup(pp->base); + +- if (pp->index == 0) +- pci_ioremap_io(sys->busnr * SZ_64K, DOVE_PCIE0_IO_PHYS_BASE); +- else +- pci_ioremap_io(sys->busnr * SZ_64K, DOVE_PCIE1_IO_PHYS_BASE); ++ realio.start = sys->busnr * SZ_64K; ++ realio.end = realio.start + SZ_64K - 1; ++ pci_remap_iospace(&realio, pp->index == 0 ? DOVE_PCIE0_IO_PHYS_BASE : ++ DOVE_PCIE1_IO_PHYS_BASE); + + /* + * IORESOURCE_MEM +diff --git a/arch/arm/mach-iop32x/pci.c b/arch/arm/mach-iop32x/pci.c +index ab0010dc3145..7a215d2ee7e2 100644 +--- a/arch/arm/mach-iop32x/pci.c ++++ b/arch/arm/mach-iop32x/pci.c +@@ -185,6 +185,7 @@ iop3xx_pci_abort(unsigned long addr, unsigned int fsr, struct pt_regs *regs) + int iop3xx_pci_setup(int nr, struct pci_sys_data *sys) + { + struct resource *res; ++ struct resource realio; + + if (nr != 0) + return 0; +@@ -206,7 +207,9 @@ int iop3xx_pci_setup(int nr, struct pci_sys_data *sys) + + pci_add_resource_offset(&sys->resources, res, sys->mem_offset); + +- pci_ioremap_io(0, IOP3XX_PCI_LOWER_IO_PA); ++ realio.start = 0; ++ realio.end = realio.start + SZ_64K - 1; ++ pci_remap_iospace(&realio, IOP3XX_PCI_LOWER_IO_PA); + + return 1; + } +diff --git a/arch/arm/mach-mv78xx0/pcie.c b/arch/arm/mach-mv78xx0/pcie.c +index 636d84b40466..e15646af7f26 100644 +--- a/arch/arm/mach-mv78xx0/pcie.c ++++ b/arch/arm/mach-mv78xx0/pcie.c +@@ -101,6 +101,7 @@ static void __init mv78xx0_pcie_preinit(void) + static int __init mv78xx0_pcie_setup(int nr, struct pci_sys_data *sys) + { + struct pcie_port *pp; ++ struct resource realio; + + if (nr >= num_pcie_ports) + return 0; +@@ -115,7 +116,9 @@ static int __init mv78xx0_pcie_setup(int nr, struct pci_sys_data *sys) + orion_pcie_set_local_bus_nr(pp->base, sys->busnr); + orion_pcie_setup(pp->base); + +- pci_ioremap_io(nr * SZ_64K, MV78XX0_PCIE_IO_PHYS_BASE(nr)); ++ realio.start = nr * SZ_64K; ++ realio.end = realio.start + SZ_64K - 1; ++ pci_remap_iospace(&realio, MV78XX0_PCIE_IO_PHYS_BASE(nr)); + + pci_add_resource_offset(&sys->resources, &pp->res, sys->mem_offset); + +diff --git a/arch/arm/mach-orion5x/pci.c b/arch/arm/mach-orion5x/pci.c +index 76951bfbacf5..92e938bba20d 100644 +--- a/arch/arm/mach-orion5x/pci.c ++++ b/arch/arm/mach-orion5x/pci.c +@@ -142,6 +142,7 @@ static struct pci_ops pcie_ops = { + static int __init pcie_setup(struct pci_sys_data *sys) + { + struct resource *res; ++ struct resource realio; + int dev; + + /* +@@ -164,7 +165,9 @@ static int __init pcie_setup(struct pci_sys_data *sys) + pcie_ops.read = pcie_rd_conf_wa; + } + +- pci_ioremap_io(sys->busnr * SZ_64K, ORION5X_PCIE_IO_PHYS_BASE); ++ realio.start = sys->busnr * SZ_64K; ++ realio.end = realio.start + SZ_64K - 1; ++ pci_remap_iospace(&realio, ORION5X_PCIE_IO_PHYS_BASE); + + /* + * Request resources. +@@ -466,6 +469,7 @@ static void __init orion5x_setup_pci_wins(void) + static int __init pci_setup(struct pci_sys_data *sys) + { + struct resource *res; ++ struct resource realio; + + /* + * Point PCI unit MBUS decode windows to DRAM space. +@@ -482,7 +486,9 @@ static int __init pci_setup(struct pci_sys_data *sys) + */ + orion5x_setbits(PCI_CMD, PCI_CMD_HOST_REORDER); + +- pci_ioremap_io(sys->busnr * SZ_64K, ORION5X_PCI_IO_PHYS_BASE); ++ realio.start = sys->busnr * SZ_64K; ++ realio.end = realio.start + SZ_64K - 1; ++ pci_remap_iospace(&realio, ORION5X_PCI_IO_PHYS_BASE); + + /* + * Request resources +diff --git a/drivers/pcmcia/at91_cf.c b/drivers/pcmcia/at91_cf.c +index 6b1edfc890a3..92df2c2c5d07 100644 +--- a/drivers/pcmcia/at91_cf.c ++++ b/drivers/pcmcia/at91_cf.c +@@ -20,6 +20,7 @@ + #include <linux/of.h> + #include <linux/of_device.h> + #include <linux/of_gpio.h> ++#include <linux/pci.h> + #include <linux/regmap.h> + + #include <pcmcia/ss.h> +@@ -230,6 +231,7 @@ static int at91_cf_probe(struct platform_device *pdev) + struct at91_cf_socket *cf; + struct at91_cf_data *board; + struct resource *io; ++ struct resource realio; + int status; + + board = devm_kzalloc(&pdev->dev, sizeof(*board), GFP_KERNEL); +@@ -307,7 +309,9 @@ static int at91_cf_probe(struct platform_device *pdev) + * io_offset is set to 0x10000 to avoid the check in static_find_io(). + * */ + cf->socket.io_offset = 0x10000; +- status = pci_ioremap_io(0x10000, cf->phys_baseaddr + CF_IO_PHYS); ++ realio.start = cf->socket.io_offset; ++ realio.end = realio.start + SZ_64K - 1; ++ status = pci_remap_iospace(&realio, cf->phys_baseaddr + CF_IO_PHYS); + if (status) + goto fail0a; + +-- +2.34.1 + diff --git a/pkgs/patches-linux-5.15/0004-arm-ioremap-Remove-unused-ARM-specific-function-pci_.patch b/pkgs/patches-linux-5.15/0004-arm-ioremap-Remove-unused-ARM-specific-function-pci_.patch new file mode 100644 index 0000000..78a5b02 --- /dev/null +++ b/pkgs/patches-linux-5.15/0004-arm-ioremap-Remove-unused-ARM-specific-function-pci_.patch @@ -0,0 +1,55 @@ +From 084358229bf6939d2f5e54e4d3e9aa84bc9240fa Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org> +Date: Tue, 23 Nov 2021 12:24:04 +0100 +Subject: [PATCH 04/90] arm: ioremap: Remove unused ARM-specific function + pci_ioremap_io() +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +This function is not used by any driver anymore. So completely remove it. + +Signed-off-by: Pali Rohár <pali@kernel.org> +--- + arch/arm/include/asm/io.h | 2 -- + arch/arm/mm/ioremap.c | 11 ----------- + 2 files changed, 13 deletions(-) + +diff --git a/arch/arm/include/asm/io.h b/arch/arm/include/asm/io.h +index 79d246ac93ab..2d551fa12a34 100644 +--- a/arch/arm/include/asm/io.h ++++ b/arch/arm/include/asm/io.h +@@ -179,8 +179,6 @@ void pci_ioremap_set_mem_type(int mem_type); + static inline void pci_ioremap_set_mem_type(int mem_type) {} + #endif + +-extern int pci_ioremap_io(unsigned int offset, phys_addr_t phys_addr); +- + /* + * PCI configuration space mapping function. + * +diff --git a/arch/arm/mm/ioremap.c b/arch/arm/mm/ioremap.c +index 2660bdfcad4d..0e67162c978d 100644 +--- a/arch/arm/mm/ioremap.c ++++ b/arch/arm/mm/ioremap.c +@@ -453,17 +453,6 @@ void pci_ioremap_set_mem_type(int mem_type) + pci_ioremap_mem_type = mem_type; + } + +-int pci_ioremap_io(unsigned int offset, phys_addr_t phys_addr) +-{ +- BUG_ON(offset + SZ_64K - 1 > IO_SPACE_LIMIT); +- +- return ioremap_page_range(PCI_IO_VIRT_BASE + offset, +- PCI_IO_VIRT_BASE + offset + SZ_64K, +- phys_addr, +- __pgprot(get_mem_type(pci_ioremap_mem_type)->prot_pte)); +-} +-EXPORT_SYMBOL_GPL(pci_ioremap_io); +- + void __iomem *pci_remap_cfgspace(resource_size_t res_cookie, size_t size) + { + return arch_ioremap_caller(res_cookie, size, MT_UNCACHED, +-- +2.34.1 + diff --git a/pkgs/patches-linux-5.15/0005-bus-mvebu-mbus-Export-symbols-for-public-API-window-.patch b/pkgs/patches-linux-5.15/0005-bus-mvebu-mbus-Export-symbols-for-public-API-window-.patch new file mode 100644 index 0000000..87a7e9b --- /dev/null +++ b/pkgs/patches-linux-5.15/0005-bus-mvebu-mbus-Export-symbols-for-public-API-window-.patch @@ -0,0 +1,63 @@ +From c472757ed4c9b9f3383301842b06f83b1444d743 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org> +Date: Sun, 31 Oct 2021 22:50:14 +0100 +Subject: [PATCH 05/90] bus: mvebu-mbus: Export symbols for public API window + functions +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +This would allow to compile pci-mvebu.c driver as module. + +Signed-off-by: Pali Rohár <pali@kernel.org> +--- + drivers/bus/mvebu-mbus.c | 5 +++++ + 1 file changed, 5 insertions(+) + +diff --git a/drivers/bus/mvebu-mbus.c b/drivers/bus/mvebu-mbus.c +index ea0424922de7..db612045616f 100644 +--- a/drivers/bus/mvebu-mbus.c ++++ b/drivers/bus/mvebu-mbus.c +@@ -914,6 +914,7 @@ int mvebu_mbus_add_window_remap_by_id(unsigned int target, + + return mvebu_mbus_alloc_window(s, base, size, remap, target, attribute); + } ++EXPORT_SYMBOL_GPL(mvebu_mbus_add_window_remap_by_id); + + int mvebu_mbus_add_window_by_id(unsigned int target, unsigned int attribute, + phys_addr_t base, size_t size) +@@ -921,6 +922,7 @@ int mvebu_mbus_add_window_by_id(unsigned int target, unsigned int attribute, + return mvebu_mbus_add_window_remap_by_id(target, attribute, base, + size, MVEBU_MBUS_NO_REMAP); + } ++EXPORT_SYMBOL_GPL(mvebu_mbus_add_window_by_id); + + int mvebu_mbus_del_window(phys_addr_t base, size_t size) + { +@@ -933,6 +935,7 @@ int mvebu_mbus_del_window(phys_addr_t base, size_t size) + mvebu_mbus_disable_window(&mbus_state, win); + return 0; + } ++EXPORT_SYMBOL_GPL(mvebu_mbus_del_window); + + void mvebu_mbus_get_pcie_mem_aperture(struct resource *res) + { +@@ -940,6 +943,7 @@ void mvebu_mbus_get_pcie_mem_aperture(struct resource *res) + return; + *res = mbus_state.pcie_mem_aperture; + } ++EXPORT_SYMBOL_GPL(mvebu_mbus_get_pcie_mem_aperture); + + void mvebu_mbus_get_pcie_io_aperture(struct resource *res) + { +@@ -947,6 +951,7 @@ void mvebu_mbus_get_pcie_io_aperture(struct resource *res) + return; + *res = mbus_state.pcie_io_aperture; + } ++EXPORT_SYMBOL_GPL(mvebu_mbus_get_pcie_io_aperture); + + int mvebu_mbus_get_dram_win_info(phys_addr_t phyaddr, u8 *target, u8 *attr) + { +-- +2.34.1 + diff --git a/pkgs/patches-linux-5.15/0006-PCI-mvebu-Add-support-for-compiling-driver-as-module.patch b/pkgs/patches-linux-5.15/0006-PCI-mvebu-Add-support-for-compiling-driver-as-module.patch new file mode 100644 index 0000000..2ff5eef --- /dev/null +++ b/pkgs/patches-linux-5.15/0006-PCI-mvebu-Add-support-for-compiling-driver-as-module.patch @@ -0,0 +1,175 @@ +From 2d12fdead984145ea791cac44c0b0ce25504f4c5 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org> +Date: Sun, 31 Oct 2021 22:55:20 +0100 +Subject: [PATCH 06/90] PCI: mvebu: Add support for compiling driver as module +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Now when driver uses devm_pci_remap_iospace() function, it is possible +implement ->remove() callback for unbinding device from driver. + +Implement mvebu_pcie_remove() callback with proper cleanup phase, drop +driver's suppress_bind_attrs flag and switch type of CONFIG_PCI_MVEBU +option from bool to tristate. + +This allows to compile pci-mvebu.c driver as loadable module pci-mvebu.ko +with ability to unload it. + +Signed-off-by: Pali Rohár <pali@kernel.org> +--- + drivers/pci/controller/Kconfig | 2 +- + drivers/pci/controller/pci-mvebu.c | 91 +++++++++++++++++++++++++----- + 2 files changed, 77 insertions(+), 16 deletions(-) + +diff --git a/drivers/pci/controller/Kconfig b/drivers/pci/controller/Kconfig +index 326f7d13024f..a4c99ebff85e 100644 +--- a/drivers/pci/controller/Kconfig ++++ b/drivers/pci/controller/Kconfig +@@ -4,7 +4,7 @@ menu "PCI controller drivers" + depends on PCI + + config PCI_MVEBU +- bool "Marvell EBU PCIe controller" ++ tristate "Marvell EBU PCIe controller" + depends on ARCH_MVEBU || ARCH_DOVE || COMPILE_TEST + depends on MVEBU_MBUS + depends on ARM +diff --git a/drivers/pci/controller/pci-mvebu.c b/drivers/pci/controller/pci-mvebu.c +index 9cbf10d6fc30..504eb9b94c0f 100644 +--- a/drivers/pci/controller/pci-mvebu.c ++++ b/drivers/pci/controller/pci-mvebu.c +@@ -6,6 +6,7 @@ + */ + + #include <linux/kernel.h> ++#include <linux/module.h> + #include <linux/pci.h> + #include <linux/clk.h> + #include <linux/delay.h> +@@ -154,22 +155,13 @@ static void mvebu_pcie_set_local_dev_nr(struct mvebu_pcie_port *port, int nr) + mvebu_writel(port, stat, PCIE_STAT_OFF); + } + +-/* +- * Setup PCIE BARs and Address Decode Wins: +- * BAR[0] -> internal registers (needed for MSI) +- * BAR[1] -> covers all DRAM banks +- * BAR[2] -> Disabled +- * WIN[0-3] -> DRAM bank[0-3] +- */ +-static void mvebu_pcie_setup_wins(struct mvebu_pcie_port *port) ++static void mvebu_pcie_disable_wins(struct mvebu_pcie_port *port) + { +- const struct mbus_dram_target_info *dram; +- u32 size; + int i; + +- dram = mv_mbus_dram_info(); ++ mvebu_writel(port, 0, PCIE_BAR_LO_OFF(0)); ++ mvebu_writel(port, 0, PCIE_BAR_HI_OFF(0)); + +- /* First, disable and clear BARs and windows. */ + for (i = 1; i < 3; i++) { + mvebu_writel(port, 0, PCIE_BAR_CTRL_OFF(i)); + mvebu_writel(port, 0, PCIE_BAR_LO_OFF(i)); +@@ -185,6 +177,25 @@ static void mvebu_pcie_setup_wins(struct mvebu_pcie_port *port) + mvebu_writel(port, 0, PCIE_WIN5_CTRL_OFF); + mvebu_writel(port, 0, PCIE_WIN5_BASE_OFF); + mvebu_writel(port, 0, PCIE_WIN5_REMAP_OFF); ++} ++ ++/* ++ * Setup PCIE BARs and Address Decode Wins: ++ * BAR[0] -> internal registers (needed for MSI) ++ * BAR[1] -> covers all DRAM banks ++ * BAR[2] -> Disabled ++ * WIN[0-3] -> DRAM bank[0-3] ++ */ ++static void mvebu_pcie_setup_wins(struct mvebu_pcie_port *port) ++{ ++ const struct mbus_dram_target_info *dram; ++ u32 size; ++ int i; ++ ++ dram = mv_mbus_dram_info(); ++ ++ /* First, disable and clear BARs and windows. */ ++ mvebu_pcie_disable_wins(port); + + /* Setup windows for DDR banks. Count total DDR size on the fly. */ + size = 0; +@@ -1257,6 +1268,52 @@ static int mvebu_pcie_probe(struct platform_device *pdev) + return pci_host_probe(bridge); + } + ++static int mvebu_pcie_remove(struct platform_device *pdev) ++{ ++ struct mvebu_pcie *pcie = platform_get_drvdata(pdev); ++ struct pci_host_bridge *bridge = pci_host_bridge_from_priv(pcie); ++ u32 cmd; ++ int i; ++ ++ /* Remove PCI bus with all devices. */ ++ pci_lock_rescan_remove(); ++ pci_stop_root_bus(bridge->bus); ++ pci_remove_root_bus(bridge->bus); ++ pci_unlock_rescan_remove(); ++ ++ for (i = 0; i < pcie->nports; i++) { ++ struct mvebu_pcie_port *port = &pcie->ports[i]; ++ ++ if (!port->base) ++ continue; ++ ++ /* Disable Root Bridge I/O space, memory space and bus mastering. */ ++ cmd = mvebu_readl(port, PCIE_CMD_OFF); ++ cmd &= ~(PCI_COMMAND_IO | PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER); ++ mvebu_writel(port, cmd, PCIE_CMD_OFF); ++ ++ /* Mask all interrupt sources. */ ++ mvebu_writel(port, 0, PCIE_MASK_OFF); ++ ++ /* Free config space for emulated root bridge. */ ++ pci_bridge_emul_cleanup(&port->bridge); ++ ++ /* Disable and clear BARs and windows. */ ++ mvebu_pcie_disable_wins(port); ++ ++ /* Delete PCIe IO and MEM windows. */ ++ if (port->iowin.size) ++ mvebu_pcie_del_windows(port, port->iowin.base, port->iowin.size); ++ if (port->memwin.size) ++ mvebu_pcie_del_windows(port, port->memwin.base, port->memwin.size); ++ ++ /* Power down card and disable clocks. Must be the last step. */ ++ mvebu_pcie_powerdown(port); ++ } ++ ++ return 0; ++} ++ + static const struct of_device_id mvebu_pcie_of_match_table[] = { + { .compatible = "marvell,armada-xp-pcie", }, + { .compatible = "marvell,armada-370-pcie", }, +@@ -1273,10 +1330,14 @@ static struct platform_driver mvebu_pcie_driver = { + .driver = { + .name = "mvebu-pcie", + .of_match_table = mvebu_pcie_of_match_table, +- /* driver unloading/unbinding currently not supported */ +- .suppress_bind_attrs = true, + .pm = &mvebu_pcie_pm_ops, + }, + .probe = mvebu_pcie_probe, ++ .remove = mvebu_pcie_remove, + }; +-builtin_platform_driver(mvebu_pcie_driver); ++module_platform_driver(mvebu_pcie_driver); ++ ++MODULE_AUTHOR("Thomas Petazzoni <thomas.petazzoni@bootlin.com>"); ++MODULE_AUTHOR("Pali Rohár <pali@kernel.org>"); ++MODULE_DESCRIPTION("Marvell EBU PCIe controller"); ++MODULE_LICENSE("GPL v2"); +-- +2.34.1 + diff --git a/pkgs/patches-linux-5.15/0007-PCI-mvebu-Check-for-valid-ports.patch b/pkgs/patches-linux-5.15/0007-PCI-mvebu-Check-for-valid-ports.patch new file mode 100644 index 0000000..9ffd1bb --- /dev/null +++ b/pkgs/patches-linux-5.15/0007-PCI-mvebu-Check-for-valid-ports.patch @@ -0,0 +1,54 @@ +From 848b0bb310d53bfdf4492605ea23f85f5534d7d3 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org> +Date: Tue, 16 Nov 2021 10:51:43 +0100 +Subject: [PATCH 07/90] PCI: mvebu: Check for valid ports +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Some mvebu ports do not have to be initialized. So skip these uninitialized +mvebu ports in every port iteration function to prevent access to unmapped +memory or dereferencing NULL pointers. Uninitialized mvebu port has base +address set to NULL. + +Signed-off-by: Pali Rohár <pali@kernel.org> +Cc: stable@vger.kernel.org +--- + drivers/pci/controller/pci-mvebu.c | 7 +++++++ + 1 file changed, 7 insertions(+) + +diff --git a/drivers/pci/controller/pci-mvebu.c b/drivers/pci/controller/pci-mvebu.c +index 504eb9b94c0f..e4c6dc73159f 100644 +--- a/drivers/pci/controller/pci-mvebu.c ++++ b/drivers/pci/controller/pci-mvebu.c +@@ -707,6 +707,9 @@ static struct mvebu_pcie_port *mvebu_pcie_find_port(struct mvebu_pcie *pcie, + for (i = 0; i < pcie->nports; i++) { + struct mvebu_pcie_port *port = &pcie->ports[i]; + ++ if (!port->base) ++ continue; ++ + if (bus->number == 0 && port->devfn == devfn) + return port; + if (bus->number != 0 && +@@ -882,6 +885,8 @@ static int mvebu_pcie_suspend(struct device *dev) + pcie = dev_get_drvdata(dev); + for (i = 0; i < pcie->nports; i++) { + struct mvebu_pcie_port *port = pcie->ports + i; ++ if (!port->base) ++ continue; + port->saved_pcie_stat = mvebu_readl(port, PCIE_STAT_OFF); + } + +@@ -896,6 +901,8 @@ static int mvebu_pcie_resume(struct device *dev) + pcie = dev_get_drvdata(dev); + for (i = 0; i < pcie->nports; i++) { + struct mvebu_pcie_port *port = pcie->ports + i; ++ if (!port->base) ++ continue; + mvebu_writel(port, port->saved_pcie_stat, PCIE_STAT_OFF); + mvebu_pcie_setup_hw(port); + } +-- +2.34.1 + diff --git a/pkgs/patches-linux-5.15/0008-PCI-mvebu-Check-that-PCI-bridge-specified-in-DT-has-.patch b/pkgs/patches-linux-5.15/0008-PCI-mvebu-Check-that-PCI-bridge-specified-in-DT-has-.patch new file mode 100644 index 0000000..3b943f7 --- /dev/null +++ b/pkgs/patches-linux-5.15/0008-PCI-mvebu-Check-that-PCI-bridge-specified-in-DT-has-.patch @@ -0,0 +1,38 @@ +From 978d8acb29959079c59f700fbd9df105147a3c44 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org> +Date: Fri, 17 Sep 2021 11:39:48 +0200 +Subject: [PATCH 08/90] PCI: mvebu: Check that PCI bridge specified in DT has + function number zero +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Driver cannot handle PCI bridges at non-zero function address. So add +appropriate check. Currently all in-tree kernel DTS files set PCI bridge +function to zero. + +Signed-off-by: Pali Rohár <pali@kernel.org> +Cc: stable@vger.kernel.org +--- + drivers/pci/controller/pci-mvebu.c | 5 +++++ + 1 file changed, 5 insertions(+) + +diff --git a/drivers/pci/controller/pci-mvebu.c b/drivers/pci/controller/pci-mvebu.c +index e4c6dc73159f..b0965067e4ce 100644 +--- a/drivers/pci/controller/pci-mvebu.c ++++ b/drivers/pci/controller/pci-mvebu.c +@@ -946,6 +946,11 @@ static int mvebu_pcie_parse_port(struct mvebu_pcie *pcie, + port->devfn = of_pci_get_devfn(child); + if (port->devfn < 0) + goto skip; ++ if (PCI_FUNC(port->devfn) != 0) { ++ dev_err(dev, "%s: invalid function number, must be zero\n", ++ port->name); ++ goto skip; ++ } + + ret = mvebu_get_tgt_attr(dev->of_node, port->devfn, IORESOURCE_MEM, + &port->mem_target, &port->mem_attr); +-- +2.34.1 + diff --git a/pkgs/patches-linux-5.15/0009-PCI-mvebu-Handle-invalid-size-of-read-config-request.patch b/pkgs/patches-linux-5.15/0009-PCI-mvebu-Handle-invalid-size-of-read-config-request.patch new file mode 100644 index 0000000..93aa727 --- /dev/null +++ b/pkgs/patches-linux-5.15/0009-PCI-mvebu-Handle-invalid-size-of-read-config-request.patch @@ -0,0 +1,35 @@ +From f117e4e1d4d4ba03cdf376392dc996e84c04ddb5 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org> +Date: Fri, 17 Sep 2021 13:13:52 +0200 +Subject: [PATCH 09/90] PCI: mvebu: Handle invalid size of read config request +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Function mvebu_pcie_hw_rd_conf() does not handle invalid size. So correctly +set read value to all-ones and return appropriate error return value +PCIBIOS_BAD_REGISTER_NUMBER like in mvebu_pcie_hw_wr_conf() function. + +Signed-off-by: Pali Rohár <pali@kernel.org> +Cc: stable@vger.kernel.org +--- + drivers/pci/controller/pci-mvebu.c | 3 +++ + 1 file changed, 3 insertions(+) + +diff --git a/drivers/pci/controller/pci-mvebu.c b/drivers/pci/controller/pci-mvebu.c +index b0965067e4ce..5bf1889cfb38 100644 +--- a/drivers/pci/controller/pci-mvebu.c ++++ b/drivers/pci/controller/pci-mvebu.c +@@ -273,6 +273,9 @@ static int mvebu_pcie_hw_rd_conf(struct mvebu_pcie_port *port, + case 4: + *val = readl_relaxed(conf_data); + break; ++ default: ++ *val = 0xffffffff; ++ return PCIBIOS_BAD_REGISTER_NUMBER; + } + + return PCIBIOS_SUCCESSFUL; +-- +2.34.1 + diff --git a/pkgs/patches-linux-5.15/0010-PCI-mvebu-Disallow-mapping-interrupts-on-emulated-br.patch b/pkgs/patches-linux-5.15/0010-PCI-mvebu-Disallow-mapping-interrupts-on-emulated-br.patch new file mode 100644 index 0000000..cfcb1e5 --- /dev/null +++ b/pkgs/patches-linux-5.15/0010-PCI-mvebu-Disallow-mapping-interrupts-on-emulated-br.patch @@ -0,0 +1,51 @@ +From 7931a2a131998d990e5f9732fc3dc448ec672046 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org> +Date: Fri, 17 Sep 2021 13:42:10 +0200 +Subject: [PATCH 10/90] PCI: mvebu: Disallow mapping interrupts on emulated + bridges +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Interrupt support on mvebu emulated bridges is not implemented yet. + +So properly indicate return value to callers that they cannot request +interrupts from emulated bridge. + +Signed-off-by: Pali Rohár <pali@kernel.org> +Cc: stable@vger.kernel.org +--- + drivers/pci/controller/pci-mvebu.c | 10 ++++++++++ + 1 file changed, 10 insertions(+) + +diff --git a/drivers/pci/controller/pci-mvebu.c b/drivers/pci/controller/pci-mvebu.c +index 5bf1889cfb38..0a8b552364aa 100644 +--- a/drivers/pci/controller/pci-mvebu.c ++++ b/drivers/pci/controller/pci-mvebu.c +@@ -787,6 +787,15 @@ static struct pci_ops mvebu_pcie_ops = { + .write = mvebu_pcie_wr_conf, + }; + ++static int mvebu_pcie_map_irq(const struct pci_dev *dev, u8 slot, u8 pin) ++{ ++ /* Interrupt support on mvebu emulated bridges is not implemented yet */ ++ if (dev->bus->number == 0) ++ return 0; /* Proper return code 0 == NO_IRQ */ ++ ++ return of_irq_parse_and_map_pci(dev, slot, pin); ++} ++ + static resource_size_t mvebu_pcie_align_resource(struct pci_dev *dev, + const struct resource *res, + resource_size_t start, +@@ -1279,6 +1288,7 @@ static int mvebu_pcie_probe(struct platform_device *pdev) + bridge->sysdata = pcie; + bridge->ops = &mvebu_pcie_ops; + bridge->align_resource = mvebu_pcie_align_resource; ++ bridge->map_irq = mvebu_pcie_map_irq; + + return pci_host_probe(bridge); + } +-- +2.34.1 + diff --git a/pkgs/patches-linux-5.15/0011-PCI-mvebu-Propagate-errors-when-updating-PCI_IO_BASE.patch b/pkgs/patches-linux-5.15/0011-PCI-mvebu-Propagate-errors-when-updating-PCI_IO_BASE.patch new file mode 100644 index 0000000..225965c --- /dev/null +++ b/pkgs/patches-linux-5.15/0011-PCI-mvebu-Propagate-errors-when-updating-PCI_IO_BASE.patch @@ -0,0 +1,195 @@ +From 14d8c749e493bd09b00e5766ba96631794e1a109 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org> +Date: Fri, 17 Sep 2021 11:34:50 +0200 +Subject: [PATCH 11/90] PCI: mvebu: Propagate errors when updating PCI_IO_BASE + and PCI_MEM_BASE registers +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Properly propagate failure from mvebu_pcie_add_windows() function back to +the caller mvebu_pci_bridge_emul_base_conf_write() and correctly updates +PCI_IO_BASE, PCI_MEM_BASE and PCI_IO_BASE_UPPER16 registers on error. +On error set base value higher than limit value which indicates that +address range is disabled. When IO is unsupported then let IO registers +zeroed as required by PCIe base specification. + +Signed-off-by: Pali Rohár <pali@kernel.org> +Cc: stable@vger.kernel.org +--- + drivers/pci/controller/pci-mvebu.c | 82 ++++++++++++++++++++---------- + 1 file changed, 55 insertions(+), 27 deletions(-) + +diff --git a/drivers/pci/controller/pci-mvebu.c b/drivers/pci/controller/pci-mvebu.c +index 0a8b552364aa..32aa78059e96 100644 +--- a/drivers/pci/controller/pci-mvebu.c ++++ b/drivers/pci/controller/pci-mvebu.c +@@ -329,7 +329,7 @@ static void mvebu_pcie_del_windows(struct mvebu_pcie_port *port, + * areas each having a power of two size. We start from the largest + * one (i.e highest order bit set in the size). + */ +-static void mvebu_pcie_add_windows(struct mvebu_pcie_port *port, ++static int mvebu_pcie_add_windows(struct mvebu_pcie_port *port, + unsigned int target, unsigned int attribute, + phys_addr_t base, size_t size, + phys_addr_t remap) +@@ -350,7 +350,7 @@ static void mvebu_pcie_add_windows(struct mvebu_pcie_port *port, + &base, &end, ret); + mvebu_pcie_del_windows(port, base - size_mapped, + size_mapped); +- return; ++ return ret; + } + + size -= sz; +@@ -359,16 +359,20 @@ static void mvebu_pcie_add_windows(struct mvebu_pcie_port *port, + if (remap != MVEBU_MBUS_NO_REMAP) + remap += sz; + } ++ ++ return 0; + } + +-static void mvebu_pcie_set_window(struct mvebu_pcie_port *port, ++static int mvebu_pcie_set_window(struct mvebu_pcie_port *port, + unsigned int target, unsigned int attribute, + const struct mvebu_pcie_window *desired, + struct mvebu_pcie_window *cur) + { ++ int ret; ++ + if (desired->base == cur->base && desired->remap == cur->remap && + desired->size == cur->size) +- return; ++ return 0; + + if (cur->size != 0) { + mvebu_pcie_del_windows(port, cur->base, cur->size); +@@ -383,30 +387,35 @@ static void mvebu_pcie_set_window(struct mvebu_pcie_port *port, + } + + if (desired->size == 0) +- return; ++ return 0; ++ ++ ret = mvebu_pcie_add_windows(port, target, attribute, desired->base, ++ desired->size, desired->remap); ++ if (ret) { ++ cur->size = 0; ++ cur->base = 0; ++ return ret; ++ } + +- mvebu_pcie_add_windows(port, target, attribute, desired->base, +- desired->size, desired->remap); + *cur = *desired; ++ return 0; + } + +-static void mvebu_pcie_handle_iobase_change(struct mvebu_pcie_port *port) ++static int mvebu_pcie_handle_iobase_change(struct mvebu_pcie_port *port) + { + struct mvebu_pcie_window desired = {}; + struct pci_bridge_emul_conf *conf = &port->bridge.conf; + + /* Are the new iobase/iolimit values invalid? */ + if (conf->iolimit < conf->iobase || +- conf->iolimitupper < conf->iobaseupper) { +- mvebu_pcie_set_window(port, port->io_target, port->io_attr, +- &desired, &port->iowin); +- return; +- } ++ conf->iolimitupper < conf->iobaseupper) ++ return mvebu_pcie_set_window(port, port->io_target, port->io_attr, ++ &desired, &port->iowin); + + if (!mvebu_has_ioport(port)) { + dev_WARN(&port->pcie->pdev->dev, + "Attempt to set IO when IO is disabled\n"); +- return; ++ return -EOPNOTSUPP; + } + + /* +@@ -424,21 +433,19 @@ static void mvebu_pcie_handle_iobase_change(struct mvebu_pcie_port *port) + desired.remap) + + 1; + +- mvebu_pcie_set_window(port, port->io_target, port->io_attr, &desired, +- &port->iowin); ++ return mvebu_pcie_set_window(port, port->io_target, port->io_attr, &desired, ++ &port->iowin); + } + +-static void mvebu_pcie_handle_membase_change(struct mvebu_pcie_port *port) ++static int mvebu_pcie_handle_membase_change(struct mvebu_pcie_port *port) + { + struct mvebu_pcie_window desired = {.remap = MVEBU_MBUS_NO_REMAP}; + struct pci_bridge_emul_conf *conf = &port->bridge.conf; + + /* Are the new membase/memlimit values invalid? */ +- if (conf->memlimit < conf->membase) { +- mvebu_pcie_set_window(port, port->mem_target, port->mem_attr, +- &desired, &port->memwin); +- return; +- } ++ if (conf->memlimit < conf->membase) ++ return mvebu_pcie_set_window(port, port->mem_target, port->mem_attr, ++ &desired, &port->memwin); + + /* + * We read the PCI-to-PCI bridge emulated registers, and +@@ -450,8 +457,8 @@ static void mvebu_pcie_handle_membase_change(struct mvebu_pcie_port *port) + desired.size = (((conf->memlimit & 0xFFF0) << 16) | 0xFFFFF) - + desired.base + 1; + +- mvebu_pcie_set_window(port, port->mem_target, port->mem_attr, &desired, +- &port->memwin); ++ return mvebu_pcie_set_window(port, port->mem_target, port->mem_attr, &desired, ++ &port->memwin); + } + + static pci_bridge_emul_read_status_t +@@ -576,15 +583,36 @@ mvebu_pci_bridge_emul_base_conf_write(struct pci_bridge_emul *bridge, + break; + + case PCI_IO_BASE: +- mvebu_pcie_handle_iobase_change(port); ++ if ((mask & 0xffff) && mvebu_pcie_handle_iobase_change(port)) { ++ /* On error disable IO range */ ++ conf->iobase &= ~0xf0; ++ conf->iolimit &= ~0xf0; ++ conf->iobaseupper = cpu_to_le16(0x0000); ++ conf->iolimitupper = cpu_to_le16(0x0000); ++ if (mvebu_has_ioport(port)) ++ conf->iobase |= 0xf0; ++ } + break; + + case PCI_MEMORY_BASE: +- mvebu_pcie_handle_membase_change(port); ++ if (mvebu_pcie_handle_membase_change(port)) { ++ /* On error disable mem range */ ++ conf->membase = cpu_to_le16(le16_to_cpu(conf->membase) & ~0xfff0); ++ conf->memlimit = cpu_to_le16(le16_to_cpu(conf->memlimit) & ~0xfff0); ++ conf->membase = cpu_to_le16(le16_to_cpu(conf->membase) | 0xfff0); ++ } + break; + + case PCI_IO_BASE_UPPER16: +- mvebu_pcie_handle_iobase_change(port); ++ if (mvebu_pcie_handle_iobase_change(port)) { ++ /* On error disable IO range */ ++ conf->iobase &= ~0xf0; ++ conf->iolimit &= ~0xf0; ++ conf->iobaseupper = cpu_to_le16(0x0000); ++ conf->iolimitupper = cpu_to_le16(0x0000); ++ if (mvebu_has_ioport(port)) ++ conf->iobase |= 0xf0; ++ } + break; + + case PCI_PRIMARY_BUS: +-- +2.34.1 + diff --git a/pkgs/patches-linux-5.15/0012-PCI-mvebu-Set-PCI-Bridge-Class-Code-to-PCI-Bridge.patch b/pkgs/patches-linux-5.15/0012-PCI-mvebu-Set-PCI-Bridge-Class-Code-to-PCI-Bridge.patch new file mode 100644 index 0000000..10d6b49 --- /dev/null +++ b/pkgs/patches-linux-5.15/0012-PCI-mvebu-Set-PCI-Bridge-Class-Code-to-PCI-Bridge.patch @@ -0,0 +1,73 @@ +From 0d7c624cae34b5a31715fc3bd2614315d2982bd9 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org> +Date: Fri, 17 Sep 2021 11:51:43 +0200 +Subject: [PATCH 12/90] PCI: mvebu: Set PCI Bridge Class Code to PCI Bridge +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +The default value of Class Code of this bridge corresponds to a Memory +controller, though. This is probably relict from the past when old +Marvell/Galileo PCI-based controllers were used as standalone PCI device +for connecting SDRAM or workaround for PCs with broken BIOS. Details are +in commit 36de23a4c5f0 ("MIPS: Cobalt: Explain GT64111 early PCI fixup"). + +Change the Class Code to correspond to a PCI Bridge. + +Add comment explaining this change. + +Signed-off-by: Pali Rohár <pali@kernel.org> +Cc: stable@vger.kernel.org +--- + drivers/pci/controller/pci-mvebu.c | 28 +++++++++++++++++++++++++++- + 1 file changed, 27 insertions(+), 1 deletion(-) + +diff --git a/drivers/pci/controller/pci-mvebu.c b/drivers/pci/controller/pci-mvebu.c +index 32aa78059e96..68aa94a258ff 100644 +--- a/drivers/pci/controller/pci-mvebu.c ++++ b/drivers/pci/controller/pci-mvebu.c +@@ -233,7 +233,7 @@ static void mvebu_pcie_setup_wins(struct mvebu_pcie_port *port) + + static void mvebu_pcie_setup_hw(struct mvebu_pcie_port *port) + { +- u32 ctrl, cmd, mask; ++ u32 ctrl, cmd, dev_rev, mask; + + /* Setup PCIe controller to Root Complex mode. */ + ctrl = mvebu_readl(port, PCIE_CTRL_OFF); +@@ -245,6 +245,32 @@ static void mvebu_pcie_setup_hw(struct mvebu_pcie_port *port) + cmd &= ~(PCI_COMMAND_IO | PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER); + mvebu_writel(port, cmd, PCIE_CMD_OFF); + ++ /* ++ * Change Class Code of PCI Bridge device to PCI Bridge (0x6004) ++ * because default value is Memory controller (0x5080). ++ * ++ * Note that this mvebu PCI Bridge does not have compliant Type 1 ++ * Configuration Space. Header Type is reported as Type 0 and it ++ * has format of Type 0 config space. ++ * ++ * Moreover Type 0 BAR registers (ranges 0x10 - 0x28 and 0x30 - 0x34) ++ * have the same format in Marvell's specification as in PCIe ++ * specification, but their meaning is totally different and they do ++ * different things: they are aliased into internal mvebu registers ++ * (e.g. PCIE_BAR_LO_OFF) and these should not be changed or ++ * reconfigured by pci device drivers. ++ * ++ * Therefore driver uses emulation of PCI Bridge which emulates ++ * access to configuration space via internal mvebu registers or ++ * emulated configuration buffer. Driver access these PCI Bridge ++ * directly for simplification, but these registers can be accessed ++ * also via standard mvebu way for accessing PCI config space. ++ */ ++ dev_rev = mvebu_readl(port, PCIE_DEV_REV_OFF); ++ dev_rev &= ~0xffffff00; ++ dev_rev |= (PCI_CLASS_BRIDGE_PCI << 8) << 8; ++ mvebu_writel(port, dev_rev, PCIE_DEV_REV_OFF); ++ + /* Point PCIe unit MBUS decode windows to DRAM space. */ + mvebu_pcie_setup_wins(port); + +-- +2.34.1 + diff --git a/pkgs/patches-linux-5.15/0013-MAINTAINERS-Add-Pali-Roh-r-as-pci-mvebu.c-maintainer.patch b/pkgs/patches-linux-5.15/0013-MAINTAINERS-Add-Pali-Roh-r-as-pci-mvebu.c-maintainer.patch new file mode 100644 index 0000000..96c4dd3 --- /dev/null +++ b/pkgs/patches-linux-5.15/0013-MAINTAINERS-Add-Pali-Roh-r-as-pci-mvebu.c-maintainer.patch @@ -0,0 +1,30 @@ +From b8db9ac56b79c68e4501491b259b19487eccd7db Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org> +Date: Thu, 25 Nov 2021 13:51:15 +0100 +Subject: [PATCH 13/90] =?UTF-8?q?MAINTAINERS:=20Add=20Pali=20Roh=C3=A1r=20?= + =?UTF-8?q?as=20pci-mvebu.c=20maintainer?= +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Signed-off-by: Pali Rohár <pali@kernel.org> +Acked-by: Thomas Petazzoni <thomas.petazzoni@bootlin.com> +--- + MAINTAINERS | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/MAINTAINERS b/MAINTAINERS +index edc32575828b..5bb588d01805 100644 +--- a/MAINTAINERS ++++ b/MAINTAINERS +@@ -14396,6 +14396,7 @@ F: drivers/pci/controller/mobiveil/pcie-mobiveil* + + PCI DRIVER FOR MVEBU (Marvell Armada 370 and Armada XP SOC support) + M: Thomas Petazzoni <thomas.petazzoni@bootlin.com> ++M: Pali Rohár <pali@kernel.org> + L: linux-pci@vger.kernel.org + L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers) + S: Maintained +-- +2.34.1 + diff --git a/pkgs/patches-linux-5.15/0014-PCI-pci-bridge-emul-Make-struct-pci_bridge_emul_ops-.patch b/pkgs/patches-linux-5.15/0014-PCI-pci-bridge-emul-Make-struct-pci_bridge_emul_ops-.patch new file mode 100644 index 0000000..014b632 --- /dev/null +++ b/pkgs/patches-linux-5.15/0014-PCI-pci-bridge-emul-Make-struct-pci_bridge_emul_ops-.patch @@ -0,0 +1,61 @@ +From 002ef7f63fb628d49f079a02ab0d8f3e08efb94a Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org> +Date: Tue, 16 Nov 2021 10:54:50 +0100 +Subject: [PATCH 14/90] PCI: pci-bridge-emul: Make struct pci_bridge_emul_ops + as const +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +It is read-only constant structure, so properly mark it with const keyword. + +Signed-off-by: Pali Rohár <pali@kernel.org> +Acked-by: Thomas Petazzoni <thomas.petazzoni@bootlin.com> +--- + drivers/pci/controller/pci-aardvark.c | 2 +- + drivers/pci/controller/pci-mvebu.c | 2 +- + drivers/pci/pci-bridge-emul.h | 2 +- + 3 files changed, 3 insertions(+), 3 deletions(-) + +diff --git a/drivers/pci/controller/pci-aardvark.c b/drivers/pci/controller/pci-aardvark.c +index 7cc2c54daad0..3fe4d3329267 100644 +--- a/drivers/pci/controller/pci-aardvark.c ++++ b/drivers/pci/controller/pci-aardvark.c +@@ -944,7 +944,7 @@ advk_pci_bridge_emul_pcie_conf_write(struct pci_bridge_emul *bridge, + } + } + +-static struct pci_bridge_emul_ops advk_pci_bridge_emul_ops = { ++static const struct pci_bridge_emul_ops advk_pci_bridge_emul_ops = { + .read_base = advk_pci_bridge_emul_base_conf_read, + .write_base = advk_pci_bridge_emul_base_conf_write, + .read_pcie = advk_pci_bridge_emul_pcie_conf_read, +diff --git a/drivers/pci/controller/pci-mvebu.c b/drivers/pci/controller/pci-mvebu.c +index 68aa94a258ff..2ecc1ab12249 100644 +--- a/drivers/pci/controller/pci-mvebu.c ++++ b/drivers/pci/controller/pci-mvebu.c +@@ -709,7 +709,7 @@ mvebu_pci_bridge_emul_pcie_conf_write(struct pci_bridge_emul *bridge, + } + } + +-static struct pci_bridge_emul_ops mvebu_pci_bridge_emul_ops = { ++static const struct pci_bridge_emul_ops mvebu_pci_bridge_emul_ops = { + .read_base = mvebu_pci_bridge_emul_base_conf_read, + .write_base = mvebu_pci_bridge_emul_base_conf_write, + .read_pcie = mvebu_pci_bridge_emul_pcie_conf_read, +diff --git a/drivers/pci/pci-bridge-emul.h b/drivers/pci/pci-bridge-emul.h +index 49bbd37ee318..0690b6369755 100644 +--- a/drivers/pci/pci-bridge-emul.h ++++ b/drivers/pci/pci-bridge-emul.h +@@ -112,7 +112,7 @@ struct pci_bridge_reg_behavior; + struct pci_bridge_emul { + struct pci_bridge_emul_conf conf; + struct pci_bridge_emul_pcie_conf pcie_conf; +- struct pci_bridge_emul_ops *ops; ++ const struct pci_bridge_emul_ops *ops; + struct pci_bridge_reg_behavior *pci_regs_behavior; + struct pci_bridge_reg_behavior *pcie_cap_regs_behavior; + void *data; +-- +2.34.1 + diff --git a/pkgs/patches-linux-5.15/0015-PCI-pci-bridge-emul-Rename-PCI_BRIDGE_EMUL_NO_PREFET.patch b/pkgs/patches-linux-5.15/0015-PCI-pci-bridge-emul-Rename-PCI_BRIDGE_EMUL_NO_PREFET.patch new file mode 100644 index 0000000..2226c30 --- /dev/null +++ b/pkgs/patches-linux-5.15/0015-PCI-pci-bridge-emul-Rename-PCI_BRIDGE_EMUL_NO_PREFET.patch @@ -0,0 +1,69 @@ +From bb5a2178ce576f3100ae3679599777d823f459a4 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org> +Date: Tue, 16 Nov 2021 10:59:52 +0100 +Subject: [PATCH 15/90] PCI: pci-bridge-emul: Rename + PCI_BRIDGE_EMUL_NO_PREFETCHABLE_BAR to PCI_BRIDGE_EMUL_NO_PREFMEM_FORWARD +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +This flag describe whether PCI bridge supports forwarding of prefetchable +memory requests in given range between primary and secondary buses. It does +not specify if bridge has support for prefetchable memory BAR (moreover +this pci-bridge-emul.c driver does not provide support for BARs). + +So change name of this flag to be less misleading and add comment. + +Signed-off-by: Pali Rohár <pali@kernel.org> +--- + drivers/pci/controller/pci-mvebu.c | 2 +- + drivers/pci/pci-bridge-emul.c | 2 +- + drivers/pci/pci-bridge-emul.h | 6 +++++- + 3 files changed, 7 insertions(+), 3 deletions(-) + +diff --git a/drivers/pci/controller/pci-mvebu.c b/drivers/pci/controller/pci-mvebu.c +index 2ecc1ab12249..2e10ade660a1 100644 +--- a/drivers/pci/controller/pci-mvebu.c ++++ b/drivers/pci/controller/pci-mvebu.c +@@ -747,7 +747,7 @@ static int mvebu_pci_bridge_emul_init(struct mvebu_pcie_port *port) + bridge->data = port; + bridge->ops = &mvebu_pci_bridge_emul_ops; + +- return pci_bridge_emul_init(bridge, PCI_BRIDGE_EMUL_NO_PREFETCHABLE_BAR); ++ return pci_bridge_emul_init(bridge, PCI_BRIDGE_EMUL_NO_PREFMEM_FORWARD); + } + + static inline struct mvebu_pcie *sys_to_pcie(struct pci_sys_data *sys) +diff --git a/drivers/pci/pci-bridge-emul.c b/drivers/pci/pci-bridge-emul.c +index c994ebec2360..ceacba03e714 100644 +--- a/drivers/pci/pci-bridge-emul.c ++++ b/drivers/pci/pci-bridge-emul.c +@@ -377,7 +377,7 @@ int pci_bridge_emul_init(struct pci_bridge_emul *bridge, + ~(BIT(10) << 16); + } + +- if (flags & PCI_BRIDGE_EMUL_NO_PREFETCHABLE_BAR) { ++ if (flags & PCI_BRIDGE_EMUL_NO_PREFMEM_FORWARD) { + bridge->pci_regs_behavior[PCI_PREF_MEMORY_BASE / 4].ro = ~0; + bridge->pci_regs_behavior[PCI_PREF_MEMORY_BASE / 4].rw = 0; + } +diff --git a/drivers/pci/pci-bridge-emul.h b/drivers/pci/pci-bridge-emul.h +index 0690b6369755..087b5fa01bcf 100644 +--- a/drivers/pci/pci-bridge-emul.h ++++ b/drivers/pci/pci-bridge-emul.h +@@ -120,7 +120,11 @@ struct pci_bridge_emul { + }; + + enum { +- PCI_BRIDGE_EMUL_NO_PREFETCHABLE_BAR = BIT(0), ++ /* ++ * PCI bridge does not support forwarding of prefetchable memory ++ * requests between primary and secondary buses. ++ */ ++ PCI_BRIDGE_EMUL_NO_PREFMEM_FORWARD = BIT(0), + }; + + int pci_bridge_emul_init(struct pci_bridge_emul *bridge, +-- +2.34.1 + diff --git a/pkgs/patches-linux-5.15/0016-PCI-pci-bridge-emul-Add-support-for-new-flag-PCI_BRI.patch b/pkgs/patches-linux-5.15/0016-PCI-pci-bridge-emul-Add-support-for-new-flag-PCI_BRI.patch new file mode 100644 index 0000000..5d0dad6 --- /dev/null +++ b/pkgs/patches-linux-5.15/0016-PCI-pci-bridge-emul-Add-support-for-new-flag-PCI_BRI.patch @@ -0,0 +1,62 @@ +From 369e0346a0e54c96ef8776bd32d810c4e1cbbb1d Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org> +Date: Tue, 16 Nov 2021 16:53:33 +0100 +Subject: [PATCH 16/90] PCI: pci-bridge-emul: Add support for new flag + PCI_BRIDGE_EMUL_NO_IO_FORWARD +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Like PCI_BRIDGE_EMUL_NO_PREFMEM_FORWARD, this new flag specifies that +emulated PCI bridge does not support forwarding of IO requests in given +range between primary and secondary buses. This flag should be used as +argument for pci_bridge_emul_init() for hardware setup without IO support. + +Setting this flag cause that IO base and limit registers are read-only. + +Signed-off-by: Pali Rohár <pali@kernel.org> +--- + drivers/pci/pci-bridge-emul.c | 9 +++++++++ + drivers/pci/pci-bridge-emul.h | 6 ++++++ + 2 files changed, 15 insertions(+) + +diff --git a/drivers/pci/pci-bridge-emul.c b/drivers/pci/pci-bridge-emul.c +index ceacba03e714..a16f9e30099e 100644 +--- a/drivers/pci/pci-bridge-emul.c ++++ b/drivers/pci/pci-bridge-emul.c +@@ -382,6 +382,15 @@ int pci_bridge_emul_init(struct pci_bridge_emul *bridge, + bridge->pci_regs_behavior[PCI_PREF_MEMORY_BASE / 4].rw = 0; + } + ++ if (flags & PCI_BRIDGE_EMUL_NO_IO_FORWARD) { ++ bridge->pci_regs_behavior[PCI_COMMAND / 4].ro |= PCI_COMMAND_IO; ++ bridge->pci_regs_behavior[PCI_COMMAND / 4].rw &= ~PCI_COMMAND_IO; ++ bridge->pci_regs_behavior[PCI_IO_BASE / 4].ro |= GENMASK(15, 0); ++ bridge->pci_regs_behavior[PCI_IO_BASE / 4].rw &= ~GENMASK(15, 0); ++ bridge->pci_regs_behavior[PCI_IO_BASE_UPPER16 / 4].ro = ~0; ++ bridge->pci_regs_behavior[PCI_IO_BASE_UPPER16 / 4].rw = 0; ++ } ++ + return 0; + } + EXPORT_SYMBOL_GPL(pci_bridge_emul_init); +diff --git a/drivers/pci/pci-bridge-emul.h b/drivers/pci/pci-bridge-emul.h +index 087b5fa01bcf..4953274cac18 100644 +--- a/drivers/pci/pci-bridge-emul.h ++++ b/drivers/pci/pci-bridge-emul.h +@@ -125,6 +125,12 @@ enum { + * requests between primary and secondary buses. + */ + PCI_BRIDGE_EMUL_NO_PREFMEM_FORWARD = BIT(0), ++ ++ /* ++ * PCI bridge does not support forwarding of IO requests between ++ * primary and secondary buses. ++ */ ++ PCI_BRIDGE_EMUL_NO_IO_FORWARD = BIT(1), + }; + + int pci_bridge_emul_init(struct pci_bridge_emul *bridge, +-- +2.34.1 + diff --git a/pkgs/patches-linux-5.15/0017-PCI-mvebu-Add-help-string-for-CONFIG_PCI_MVEBU-optio.patch b/pkgs/patches-linux-5.15/0017-PCI-mvebu-Add-help-string-for-CONFIG_PCI_MVEBU-optio.patch new file mode 100644 index 0000000..36e7147 --- /dev/null +++ b/pkgs/patches-linux-5.15/0017-PCI-mvebu-Add-help-string-for-CONFIG_PCI_MVEBU-optio.patch @@ -0,0 +1,34 @@ +From 3eaa66f27317eff5741cc91279e19e00c22f5643 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org> +Date: Sun, 31 Oct 2021 22:57:40 +0100 +Subject: [PATCH 17/90] PCI: mvebu: Add help string for CONFIG_PCI_MVEBU option +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +There is no description for CONFIG_PCI_MVEBU option. Add it. + +Signed-off-by: Pali Rohár <pali@kernel.org> +Acked-by: Thomas Petazzoni <thomas.petazzoni@bootlin.com> +--- + drivers/pci/controller/Kconfig | 4 ++++ + 1 file changed, 4 insertions(+) + +diff --git a/drivers/pci/controller/Kconfig b/drivers/pci/controller/Kconfig +index a4c99ebff85e..46fa9bbff177 100644 +--- a/drivers/pci/controller/Kconfig ++++ b/drivers/pci/controller/Kconfig +@@ -10,6 +10,10 @@ config PCI_MVEBU + depends on ARM + depends on OF + select PCI_BRIDGE_EMUL ++ help ++ Add support for Marvell EBU PCIe controller. This PCIe controller ++ is used on 32-bit Marvell ARM SoCs: Dove, Kirkwood, Armada 370, ++ Armada XP, Armada 375, Armada 38x and Armada 39x. + + config PCI_AARDVARK + tristate "Aardvark PCIe controller" +-- +2.34.1 + diff --git a/pkgs/patches-linux-5.15/0018-PCI-mvebu-Remove-duplicate-nports-assignment.patch b/pkgs/patches-linux-5.15/0018-PCI-mvebu-Remove-duplicate-nports-assignment.patch new file mode 100644 index 0000000..616ca2b --- /dev/null +++ b/pkgs/patches-linux-5.15/0018-PCI-mvebu-Remove-duplicate-nports-assignment.patch @@ -0,0 +1,33 @@ +From 4a4519b81a342e4fe0616d839f07c215325aed86 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org> +Date: Tue, 16 Nov 2021 20:18:18 +0100 +Subject: [PATCH 18/90] PCI: mvebu: Remove duplicate nports assignment +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Member pcie->nports is initialized to correct value before the previous +for-loop. There is not need to initialize it more times. + +Signed-off-by: Pali Rohár <pali@kernel.org> +Acked-by: Thomas Petazzoni <thomas.petazzoni@bootlin.com> +--- + drivers/pci/controller/pci-mvebu.c | 2 -- + 1 file changed, 2 deletions(-) + +diff --git a/drivers/pci/controller/pci-mvebu.c b/drivers/pci/controller/pci-mvebu.c +index 2e10ade660a1..016f709b3067 100644 +--- a/drivers/pci/controller/pci-mvebu.c ++++ b/drivers/pci/controller/pci-mvebu.c +@@ -1337,8 +1337,6 @@ static int mvebu_pcie_probe(struct platform_device *pdev) + mvebu_pcie_set_local_bus_nr(port, 0); + } + +- pcie->nports = i; +- + bridge->sysdata = pcie; + bridge->ops = &mvebu_pcie_ops; + bridge->align_resource = mvebu_pcie_align_resource; +-- +2.34.1 + diff --git a/pkgs/patches-linux-5.15/0019-PCI-mvebu-Set-PCI_BRIDGE_EMUL_NO_IO_FORWARD-when-IO-.patch b/pkgs/patches-linux-5.15/0019-PCI-mvebu-Set-PCI_BRIDGE_EMUL_NO_IO_FORWARD-when-IO-.patch new file mode 100644 index 0000000..02e364a --- /dev/null +++ b/pkgs/patches-linux-5.15/0019-PCI-mvebu-Set-PCI_BRIDGE_EMUL_NO_IO_FORWARD-when-IO-.patch @@ -0,0 +1,114 @@ +From 1269339f0ff159e788117fb1329159b527da490f Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org> +Date: Tue, 16 Nov 2021 17:22:03 +0100 +Subject: [PATCH 19/90] PCI: mvebu: Set PCI_BRIDGE_EMUL_NO_IO_FORWARD when IO + is unsupported +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +This will make PCI bridge to return zeros when accessing IO base and limit +registers, as required by PCIe base specification. + +This allows to remove adhoc checks around mvebu_pcie_handle_iobase_change() +function for unsupported IO ranges. PCI_BRIDGE_EMUL_NO_IO_FORWARD ensures +that there will be no non-zeros write to IO registers when IO is not +supported. + +Signed-off-by: Pali Rohár <pali@kernel.org> +--- + drivers/pci/controller/pci-mvebu.c | 29 ++++++++++------------------- + 1 file changed, 10 insertions(+), 19 deletions(-) + +diff --git a/drivers/pci/controller/pci-mvebu.c b/drivers/pci/controller/pci-mvebu.c +index 016f709b3067..551f55af5226 100644 +--- a/drivers/pci/controller/pci-mvebu.c ++++ b/drivers/pci/controller/pci-mvebu.c +@@ -438,12 +438,6 @@ static int mvebu_pcie_handle_iobase_change(struct mvebu_pcie_port *port) + return mvebu_pcie_set_window(port, port->io_target, port->io_attr, + &desired, &port->iowin); + +- if (!mvebu_has_ioport(port)) { +- dev_WARN(&port->pcie->pdev->dev, +- "Attempt to set IO when IO is disabled\n"); +- return -EOPNOTSUPP; +- } +- + /* + * We read the PCI-to-PCI bridge emulated registers, and + * calculate the base address and size of the address decoding +@@ -599,24 +593,18 @@ mvebu_pci_bridge_emul_base_conf_write(struct pci_bridge_emul *bridge, + + switch (reg) { + case PCI_COMMAND: +- if (!mvebu_has_ioport(port)) { +- conf->command = cpu_to_le16( +- le16_to_cpu(conf->command) & ~PCI_COMMAND_IO); +- new &= ~PCI_COMMAND_IO; +- } +- + mvebu_writel(port, new, PCIE_CMD_OFF); + break; + + case PCI_IO_BASE: +- if ((mask & 0xffff) && mvebu_pcie_handle_iobase_change(port)) { ++ if ((mask & 0xffff) && mvebu_has_ioport(port) && ++ mvebu_pcie_handle_iobase_change(port)) { + /* On error disable IO range */ + conf->iobase &= ~0xf0; + conf->iolimit &= ~0xf0; ++ conf->iobase |= 0xf0; + conf->iobaseupper = cpu_to_le16(0x0000); + conf->iolimitupper = cpu_to_le16(0x0000); +- if (mvebu_has_ioport(port)) +- conf->iobase |= 0xf0; + } + break; + +@@ -630,14 +618,14 @@ mvebu_pci_bridge_emul_base_conf_write(struct pci_bridge_emul *bridge, + break; + + case PCI_IO_BASE_UPPER16: +- if (mvebu_pcie_handle_iobase_change(port)) { ++ if (mvebu_has_ioport(port) && ++ mvebu_pcie_handle_iobase_change(port)) { + /* On error disable IO range */ + conf->iobase &= ~0xf0; + conf->iolimit &= ~0xf0; ++ conf->iobase |= 0xf0; + conf->iobaseupper = cpu_to_le16(0x0000); + conf->iolimitupper = cpu_to_le16(0x0000); +- if (mvebu_has_ioport(port)) +- conf->iobase |= 0xf0; + } + break; + +@@ -722,6 +710,7 @@ static const struct pci_bridge_emul_ops mvebu_pci_bridge_emul_ops = { + */ + static int mvebu_pci_bridge_emul_init(struct mvebu_pcie_port *port) + { ++ unsigned int bridge_flags = PCI_BRIDGE_EMUL_NO_PREFMEM_FORWARD; + struct pci_bridge_emul *bridge = &port->bridge; + u32 pcie_cap = mvebu_readl(port, PCIE_CAP_PCIEXP); + u8 pcie_cap_ver = ((pcie_cap >> 16) & PCI_EXP_FLAGS_VERS); +@@ -735,6 +724,8 @@ static int mvebu_pci_bridge_emul_init(struct mvebu_pcie_port *port) + /* We support 32 bits I/O addressing */ + bridge->conf.iobase = PCI_IO_RANGE_TYPE_32; + bridge->conf.iolimit = PCI_IO_RANGE_TYPE_32; ++ } else { ++ bridge_flags |= PCI_BRIDGE_EMUL_NO_IO_FORWARD; + } + + /* +@@ -747,7 +738,7 @@ static int mvebu_pci_bridge_emul_init(struct mvebu_pcie_port *port) + bridge->data = port; + bridge->ops = &mvebu_pci_bridge_emul_ops; + +- return pci_bridge_emul_init(bridge, PCI_BRIDGE_EMUL_NO_PREFMEM_FORWARD); ++ return pci_bridge_emul_init(bridge, bridge_flags); + } + + static inline struct mvebu_pcie *sys_to_pcie(struct pci_sys_data *sys) +-- +2.34.1 + diff --git a/pkgs/patches-linux-5.15/0020-PCI-mvebu-Properly-initialize-vendor-device-and-revi.patch b/pkgs/patches-linux-5.15/0020-PCI-mvebu-Properly-initialize-vendor-device-and-revi.patch new file mode 100644 index 0000000..3025003 --- /dev/null +++ b/pkgs/patches-linux-5.15/0020-PCI-mvebu-Properly-initialize-vendor-device-and-revi.patch @@ -0,0 +1,42 @@ +From 4ae6a8f60faf1d918e7421410d04386ee8b06894 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org> +Date: Fri, 17 Sep 2021 14:48:21 +0200 +Subject: [PATCH 20/90] PCI: mvebu: Properly initialize vendor, device and + revision of emulated bridge +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +With this change also PCI vendor id is read from mvebu registers. + +Signed-off-by: Pali Rohár <pali@kernel.org> +--- + drivers/pci/controller/pci-mvebu.c | 9 +++++---- + 1 file changed, 5 insertions(+), 4 deletions(-) + +diff --git a/drivers/pci/controller/pci-mvebu.c b/drivers/pci/controller/pci-mvebu.c +index 551f55af5226..94ef00b6d697 100644 +--- a/drivers/pci/controller/pci-mvebu.c ++++ b/drivers/pci/controller/pci-mvebu.c +@@ -712,13 +712,14 @@ static int mvebu_pci_bridge_emul_init(struct mvebu_pcie_port *port) + { + unsigned int bridge_flags = PCI_BRIDGE_EMUL_NO_PREFMEM_FORWARD; + struct pci_bridge_emul *bridge = &port->bridge; ++ u32 dev_id = mvebu_readl(port, PCIE_DEV_ID_OFF); ++ u32 dev_rev = mvebu_readl(port, PCIE_DEV_REV_OFF); + u32 pcie_cap = mvebu_readl(port, PCIE_CAP_PCIEXP); + u8 pcie_cap_ver = ((pcie_cap >> 16) & PCI_EXP_FLAGS_VERS); + +- bridge->conf.vendor = PCI_VENDOR_ID_MARVELL; +- bridge->conf.device = mvebu_readl(port, PCIE_DEV_ID_OFF) >> 16; +- bridge->conf.class_revision = +- mvebu_readl(port, PCIE_DEV_REV_OFF) & 0xff; ++ bridge->conf.vendor = cpu_to_le16(dev_id & 0xffff); ++ bridge->conf.device = cpu_to_le16(dev_id >> 16); ++ bridge->conf.class_revision = cpu_to_le32(dev_rev & 0xff); + + if (mvebu_has_ioport(port)) { + /* We support 32 bits I/O addressing */ +-- +2.34.1 + diff --git a/pkgs/patches-linux-5.15/0021-PCI-mvebu-Update-comment-for-PCI_EXP_LNKCAP-register.patch b/pkgs/patches-linux-5.15/0021-PCI-mvebu-Update-comment-for-PCI_EXP_LNKCAP-register.patch new file mode 100644 index 0000000..1239337 --- /dev/null +++ b/pkgs/patches-linux-5.15/0021-PCI-mvebu-Update-comment-for-PCI_EXP_LNKCAP-register.patch @@ -0,0 +1,34 @@ +From 91948e605b7b0f3f65919d6e19ef4e39535faf6a Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org> +Date: Fri, 17 Sep 2021 11:54:37 +0200 +Subject: [PATCH 21/90] PCI: mvebu: Update comment for PCI_EXP_LNKCAP register + on emulated bridge +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Reason for clearing this bit is because mvebu hw returns incorrectly this bit set to 1. + +Signed-off-by: Pali Rohár <pali@kernel.org> +--- + drivers/pci/controller/pci-mvebu.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/drivers/pci/controller/pci-mvebu.c b/drivers/pci/controller/pci-mvebu.c +index 94ef00b6d697..1aac65977b97 100644 +--- a/drivers/pci/controller/pci-mvebu.c ++++ b/drivers/pci/controller/pci-mvebu.c +@@ -546,8 +546,8 @@ mvebu_pci_bridge_emul_pcie_conf_read(struct pci_bridge_emul *bridge, + + case PCI_EXP_LNKCAP: + /* +- * PCIe requires the clock power management capability to be +- * hard-wired to zero for downstream ports ++ * PCIe requires that the Clock Power Management capability bit ++ * is hard-wired to zero for downstream ports but HW returns 1. + */ + *value = mvebu_readl(port, PCIE_CAP_PCIEXP + PCI_EXP_LNKCAP) & + ~PCI_EXP_LNKCAP_CLKPM; +-- +2.34.1 + diff --git a/pkgs/patches-linux-5.15/0022-PCI-mvebu-Update-comment-for-PCI_EXP_LNKCTL-register.patch b/pkgs/patches-linux-5.15/0022-PCI-mvebu-Update-comment-for-PCI_EXP_LNKCTL-register.patch new file mode 100644 index 0000000..49bd9e2 --- /dev/null +++ b/pkgs/patches-linux-5.15/0022-PCI-mvebu-Update-comment-for-PCI_EXP_LNKCTL-register.patch @@ -0,0 +1,39 @@ +From b47763b59b70859fdcb98e228ac5d762cca39ab7 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org> +Date: Fri, 17 Sep 2021 11:57:20 +0200 +Subject: [PATCH 22/90] PCI: mvebu: Update comment for PCI_EXP_LNKCTL register + on emulated bridge +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Logic and code for clearing PCI_EXP_LNKCTL_CLKREQ_EN bit is correct, but +comment describing it is misleading. PCI_EXP_LNKCTL_CLKREQ_EN bit should be +hardwired to zero but mvebu hw allows to change it. + +Signed-off-by: Pali Rohár <pali@kernel.org> +--- + drivers/pci/controller/pci-mvebu.c | 7 +++---- + 1 file changed, 3 insertions(+), 4 deletions(-) + +diff --git a/drivers/pci/controller/pci-mvebu.c b/drivers/pci/controller/pci-mvebu.c +index 1aac65977b97..dffa330de174 100644 +--- a/drivers/pci/controller/pci-mvebu.c ++++ b/drivers/pci/controller/pci-mvebu.c +@@ -663,10 +663,9 @@ mvebu_pci_bridge_emul_pcie_conf_write(struct pci_bridge_emul *bridge, + + case PCI_EXP_LNKCTL: + /* +- * If we don't support CLKREQ, we must ensure that the +- * CLKREQ enable bit always reads zero. Since we haven't +- * had this capability, and it's dependent on board wiring, +- * disable it for the time being. ++ * PCIe requires that the Enable Clock Power Management bit ++ * is hard-wired to zero for downstream ports but HW allows ++ * to change it. + */ + new &= ~PCI_EXP_LNKCTL_CLKREQ_EN; + +-- +2.34.1 + diff --git a/pkgs/patches-linux-5.15/0023-PCI-mvebu-Fix-reporting-Data-Link-Layer-Link-Active-.patch b/pkgs/patches-linux-5.15/0023-PCI-mvebu-Fix-reporting-Data-Link-Layer-Link-Active-.patch new file mode 100644 index 0000000..9c4b93c --- /dev/null +++ b/pkgs/patches-linux-5.15/0023-PCI-mvebu-Fix-reporting-Data-Link-Layer-Link-Active-.patch @@ -0,0 +1,50 @@ +From 65913d4ca9e1f51ddd086b530373da620c934502 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org> +Date: Fri, 17 Sep 2021 13:16:37 +0200 +Subject: [PATCH 23/90] PCI: mvebu: Fix reporting Data Link Layer Link Active + on emulated bridge +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Add support for reporting PCI_EXP_LNKSTA_DLLLA bit in Link Control register +on emulated bridge via PCIE_STAT_OFF reg. Function mvebu_pcie_link_up() +already parses this register and returns if Data Link is Active or not. + +Also correctly indicate DLLLA capability via PCI_EXP_LNKCAP_DLLLARC bit in +Link Control Capability register which is required for reporting DLLLA bit. + +Signed-off-by: Pali Rohár <pali@kernel.org> +--- + drivers/pci/controller/pci-mvebu.c | 11 ++++++++--- + 1 file changed, 8 insertions(+), 3 deletions(-) + +diff --git a/drivers/pci/controller/pci-mvebu.c b/drivers/pci/controller/pci-mvebu.c +index dffa330de174..a075ba26cff1 100644 +--- a/drivers/pci/controller/pci-mvebu.c ++++ b/drivers/pci/controller/pci-mvebu.c +@@ -548,13 +548,18 @@ mvebu_pci_bridge_emul_pcie_conf_read(struct pci_bridge_emul *bridge, + /* + * PCIe requires that the Clock Power Management capability bit + * is hard-wired to zero for downstream ports but HW returns 1. ++ * Additionally enable Data Link Layer Link Active Reporting ++ * Capable bit as DL_Active indication is provided too. + */ +- *value = mvebu_readl(port, PCIE_CAP_PCIEXP + PCI_EXP_LNKCAP) & +- ~PCI_EXP_LNKCAP_CLKPM; ++ *value = (mvebu_readl(port, PCIE_CAP_PCIEXP + PCI_EXP_LNKCAP) & ++ ~PCI_EXP_LNKCAP_CLKPM) | PCI_EXP_LNKCAP_DLLLARC; + break; + + case PCI_EXP_LNKCTL: +- *value = mvebu_readl(port, PCIE_CAP_PCIEXP + PCI_EXP_LNKCTL); ++ /* DL_Active indication is provided via PCIE_STAT_OFF */ ++ *value = mvebu_readl(port, PCIE_CAP_PCIEXP + PCI_EXP_LNKCTL) | ++ (mvebu_pcie_link_up(port) ? ++ (PCI_EXP_LNKSTA_DLLLA << 16) : 0); + break; + + case PCI_EXP_SLTCTL: +-- +2.34.1 + diff --git a/pkgs/patches-linux-5.15/0024-PCI-pci-bridge-emul-Re-arrange-register-tests.patch b/pkgs/patches-linux-5.15/0024-PCI-pci-bridge-emul-Re-arrange-register-tests.patch new file mode 100644 index 0000000..6fee553 --- /dev/null +++ b/pkgs/patches-linux-5.15/0024-PCI-pci-bridge-emul-Re-arrange-register-tests.patch @@ -0,0 +1,117 @@ +From fd539c7270dca28ee9943f7b93d445481ab71721 Mon Sep 17 00:00:00 2001 +From: Russell King <rmk+kernel@armlinux.org.uk> +Date: Tue, 2 Feb 2021 13:45:28 +0000 +Subject: [PATCH 24/90] PCI: pci-bridge-emul: Re-arrange register tests +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Re-arrange the tests for which sets of registers are being accessed so that +it is easier to add further regions later. No functional change. + +Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk> +[pali: Fix reading old value in pci_bridge_emul_conf_write] +Signed-off-by: Pali Rohár <pali@kernel.org> +Signed-off-by: Marek Behún <kabel@kernel.org> +--- + drivers/pci/pci-bridge-emul.c | 61 ++++++++++++++++++----------------- + 1 file changed, 31 insertions(+), 30 deletions(-) + +diff --git a/drivers/pci/pci-bridge-emul.c b/drivers/pci/pci-bridge-emul.c +index a16f9e30099e..a956408834d6 100644 +--- a/drivers/pci/pci-bridge-emul.c ++++ b/drivers/pci/pci-bridge-emul.c +@@ -422,25 +422,25 @@ int pci_bridge_emul_conf_read(struct pci_bridge_emul *bridge, int where, + __le32 *cfgspace; + const struct pci_bridge_reg_behavior *behavior; + +- if (bridge->has_pcie && reg >= PCI_CAP_PCIE_END) { +- *value = 0; +- return PCIBIOS_SUCCESSFUL; +- } +- +- if (!bridge->has_pcie && reg >= PCI_BRIDGE_CONF_END) { ++ if (reg < PCI_BRIDGE_CONF_END) { ++ /* Emulated PCI space */ ++ read_op = bridge->ops->read_base; ++ cfgspace = (__le32 *) &bridge->conf; ++ behavior = bridge->pci_regs_behavior; ++ } else if (!bridge->has_pcie) { ++ /* PCIe space is not implemented, and no PCI capabilities */ + *value = 0; + return PCIBIOS_SUCCESSFUL; +- } +- +- if (bridge->has_pcie && reg >= PCI_CAP_PCIE_START) { ++ } else if (reg < PCI_CAP_PCIE_END) { ++ /* Our emulated PCIe capability */ + reg -= PCI_CAP_PCIE_START; + read_op = bridge->ops->read_pcie; + cfgspace = (__le32 *) &bridge->pcie_conf; + behavior = bridge->pcie_cap_regs_behavior; + } else { +- read_op = bridge->ops->read_base; +- cfgspace = (__le32 *) &bridge->conf; +- behavior = bridge->pci_regs_behavior; ++ /* Beyond our PCIe space */ ++ *value = 0; ++ return PCIBIOS_SUCCESSFUL; + } + + if (read_op) +@@ -484,11 +484,27 @@ int pci_bridge_emul_conf_write(struct pci_bridge_emul *bridge, int where, + __le32 *cfgspace; + const struct pci_bridge_reg_behavior *behavior; + +- if (bridge->has_pcie && reg >= PCI_CAP_PCIE_END) +- return PCIBIOS_SUCCESSFUL; ++ ret = pci_bridge_emul_conf_read(bridge, reg, 4, &old); ++ if (ret != PCIBIOS_SUCCESSFUL) ++ return ret; + +- if (!bridge->has_pcie && reg >= PCI_BRIDGE_CONF_END) ++ if (reg < PCI_BRIDGE_CONF_END) { ++ /* Emulated PCI space */ ++ write_op = bridge->ops->write_base; ++ cfgspace = (__le32 *) &bridge->conf; ++ behavior = bridge->pci_regs_behavior; ++ } else if (!bridge->has_pcie) { ++ /* PCIe space is not implemented, and no PCI capabilities */ + return PCIBIOS_SUCCESSFUL; ++ } else if (reg < PCI_CAP_PCIE_END) { ++ /* Our emulated PCIe capability */ ++ reg -= PCI_CAP_PCIE_START; ++ write_op = bridge->ops->write_pcie; ++ cfgspace = (__le32 *) &bridge->pcie_conf; ++ behavior = bridge->pcie_cap_regs_behavior; ++ } else { ++ return PCIBIOS_SUCCESSFUL; ++ } + + shift = (where & 0x3) * 8; + +@@ -501,21 +517,6 @@ int pci_bridge_emul_conf_write(struct pci_bridge_emul *bridge, int where, + else + return PCIBIOS_BAD_REGISTER_NUMBER; + +- ret = pci_bridge_emul_conf_read(bridge, reg, 4, &old); +- if (ret != PCIBIOS_SUCCESSFUL) +- return ret; +- +- if (bridge->has_pcie && reg >= PCI_CAP_PCIE_START) { +- reg -= PCI_CAP_PCIE_START; +- write_op = bridge->ops->write_pcie; +- cfgspace = (__le32 *) &bridge->pcie_conf; +- behavior = bridge->pcie_cap_regs_behavior; +- } else { +- write_op = bridge->ops->write_base; +- cfgspace = (__le32 *) &bridge->conf; +- behavior = bridge->pci_regs_behavior; +- } +- + /* Keep all bits, except the RW bits */ + new = old & (~mask | ~behavior[reg / 4].rw); + +-- +2.34.1 + diff --git a/pkgs/patches-linux-5.15/0025-PCI-pci-bridge-emul-Add-support-for-PCIe-extended-ca.patch b/pkgs/patches-linux-5.15/0025-PCI-pci-bridge-emul-Add-support-for-PCIe-extended-ca.patch new file mode 100644 index 0000000..27e7b57 --- /dev/null +++ b/pkgs/patches-linux-5.15/0025-PCI-pci-bridge-emul-Add-support-for-PCIe-extended-ca.patch @@ -0,0 +1,179 @@ +From 09ccac06bf207e939bd00487bb8e2b7bdc1cddf2 Mon Sep 17 00:00:00 2001 +From: Russell King <rmk+kernel@armlinux.org.uk> +Date: Tue, 2 Feb 2021 13:57:04 +0000 +Subject: [PATCH 25/90] PCI: pci-bridge-emul: Add support for PCIe extended + capabilities +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Add support for PCIe extended capabilities, which we just redirect to the +emulating driver. + +Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk> +[pali: Fix writing new value with W1C bits] +Signed-off-by: Pali Rohár <pali@kernel.org> +Signed-off-by: Marek Behún <kabel@kernel.org> +--- + drivers/pci/pci-bridge-emul.c | 77 +++++++++++++++++++++++------------ + drivers/pci/pci-bridge-emul.h | 15 +++++++ + 2 files changed, 67 insertions(+), 25 deletions(-) + +diff --git a/drivers/pci/pci-bridge-emul.c b/drivers/pci/pci-bridge-emul.c +index a956408834d6..c4b9837006ff 100644 +--- a/drivers/pci/pci-bridge-emul.c ++++ b/drivers/pci/pci-bridge-emul.c +@@ -437,10 +437,16 @@ int pci_bridge_emul_conf_read(struct pci_bridge_emul *bridge, int where, + read_op = bridge->ops->read_pcie; + cfgspace = (__le32 *) &bridge->pcie_conf; + behavior = bridge->pcie_cap_regs_behavior; +- } else { +- /* Beyond our PCIe space */ ++ } else if (reg < PCI_CFG_SPACE_SIZE) { ++ /* Rest of PCI space not implemented */ + *value = 0; + return PCIBIOS_SUCCESSFUL; ++ } else { ++ /* PCIe extended capability space */ ++ reg -= PCI_CFG_SPACE_SIZE; ++ read_op = bridge->ops->read_ext; ++ cfgspace = NULL; ++ behavior = NULL; + } + + if (read_op) +@@ -448,15 +454,20 @@ int pci_bridge_emul_conf_read(struct pci_bridge_emul *bridge, int where, + else + ret = PCI_BRIDGE_EMUL_NOT_HANDLED; + +- if (ret == PCI_BRIDGE_EMUL_NOT_HANDLED) +- *value = le32_to_cpu(cfgspace[reg / 4]); ++ if (ret == PCI_BRIDGE_EMUL_NOT_HANDLED) { ++ if (cfgspace) ++ *value = le32_to_cpu(cfgspace[reg / 4]); ++ else ++ *value = 0; ++ } + + /* + * Make sure we never return any reserved bit with a value + * different from 0. + */ +- *value &= behavior[reg / 4].ro | behavior[reg / 4].rw | +- behavior[reg / 4].w1c; ++ if (behavior) ++ *value &= behavior[reg / 4].ro | behavior[reg / 4].rw | ++ behavior[reg / 4].w1c; + + if (size == 1) + *value = (*value >> (8 * (where & 3))) & 0xff; +@@ -502,8 +513,15 @@ int pci_bridge_emul_conf_write(struct pci_bridge_emul *bridge, int where, + write_op = bridge->ops->write_pcie; + cfgspace = (__le32 *) &bridge->pcie_conf; + behavior = bridge->pcie_cap_regs_behavior; +- } else { ++ } else if (reg < PCI_CFG_SPACE_SIZE) { ++ /* Rest of PCI space not implemented */ + return PCIBIOS_SUCCESSFUL; ++ } else { ++ /* PCIe extended capability space */ ++ reg -= PCI_CFG_SPACE_SIZE; ++ write_op = bridge->ops->write_ext; ++ cfgspace = NULL; ++ behavior = NULL; + } + + shift = (where & 0x3) * 8; +@@ -517,29 +535,38 @@ int pci_bridge_emul_conf_write(struct pci_bridge_emul *bridge, int where, + else + return PCIBIOS_BAD_REGISTER_NUMBER; + +- /* Keep all bits, except the RW bits */ +- new = old & (~mask | ~behavior[reg / 4].rw); ++ if (behavior) { ++ /* Keep all bits, except the RW bits */ ++ new = old & (~mask | ~behavior[reg / 4].rw); + +- /* Update the value of the RW bits */ +- new |= (value << shift) & (behavior[reg / 4].rw & mask); ++ /* Update the value of the RW bits */ ++ new |= (value << shift) & (behavior[reg / 4].rw & mask); + +- /* Clear the W1C bits */ +- new &= ~((value << shift) & (behavior[reg / 4].w1c & mask)); ++ /* Clear the W1C bits */ ++ new &= ~((value << shift) & (behavior[reg / 4].w1c & mask)); ++ } else { ++ new = old & ~mask; ++ new |= (value << shift) & mask; ++ } + +- /* Save the new value with the cleared W1C bits into the cfgspace */ +- cfgspace[reg / 4] = cpu_to_le32(new); ++ if (cfgspace) { ++ /* Save the new value with the cleared W1C bits into the cfgspace */ ++ cfgspace[reg / 4] = cpu_to_le32(new); ++ } + +- /* +- * Clear the W1C bits not specified by the write mask, so that the +- * write_op() does not clear them. +- */ +- new &= ~(behavior[reg / 4].w1c & ~mask); ++ if (behavior) { ++ /* ++ * Clear the W1C bits not specified by the write mask, so that the ++ * write_op() does not clear them. ++ */ ++ new &= ~(behavior[reg / 4].w1c & ~mask); + +- /* +- * Set the W1C bits specified by the write mask, so that write_op() +- * knows about that they are to be cleared. +- */ +- new |= (value << shift) & (behavior[reg / 4].w1c & mask); ++ /* ++ * Set the W1C bits specified by the write mask, so that write_op() ++ * knows about that they are to be cleared. ++ */ ++ new |= (value << shift) & (behavior[reg / 4].w1c & mask); ++ } + + if (write_op) + write_op(bridge, reg, old, new, mask); +diff --git a/drivers/pci/pci-bridge-emul.h b/drivers/pci/pci-bridge-emul.h +index 4953274cac18..6b5f75b2ad02 100644 +--- a/drivers/pci/pci-bridge-emul.h ++++ b/drivers/pci/pci-bridge-emul.h +@@ -90,6 +90,14 @@ struct pci_bridge_emul_ops { + */ + pci_bridge_emul_read_status_t (*read_pcie)(struct pci_bridge_emul *bridge, + int reg, u32 *value); ++ ++ /* ++ * Same as ->read_base(), except it is for reading from the ++ * PCIe extended capability configuration space. ++ */ ++ pci_bridge_emul_read_status_t (*read_ext)(struct pci_bridge_emul *bridge, ++ int reg, u32 *value); ++ + /* + * Called when writing to the regular PCI bridge configuration + * space. old is the current value, new is the new value being +@@ -105,6 +113,13 @@ struct pci_bridge_emul_ops { + */ + void (*write_pcie)(struct pci_bridge_emul *bridge, int reg, + u32 old, u32 new, u32 mask); ++ ++ /* ++ * Same as ->write_base(), except it is for writing from the ++ * PCIe extended capability configuration space. ++ */ ++ void (*write_ext)(struct pci_bridge_emul *bridge, int reg, ++ u32 old, u32 new, u32 mask); + }; + + struct pci_bridge_reg_behavior; +-- +2.34.1 + diff --git a/pkgs/patches-linux-5.15/0026-PCI-pci-bridge-emul-Add-support-for-PCI-Bridge-Subsy.patch b/pkgs/patches-linux-5.15/0026-PCI-pci-bridge-emul-Add-support-for-PCI-Bridge-Subsy.patch new file mode 100644 index 0000000..bcaa3d1 --- /dev/null +++ b/pkgs/patches-linux-5.15/0026-PCI-pci-bridge-emul-Add-support-for-PCI-Bridge-Subsy.patch @@ -0,0 +1,169 @@ +From 097c7cbac9190481535ff83ac2271ede383e8f0a Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org> +Date: Fri, 8 Oct 2021 11:39:06 +0200 +Subject: [PATCH 26/90] PCI: pci-bridge-emul: Add support for PCI Bridge + Subsystem Vendor ID capability +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +This is read-only capability in PCI config space. Put it between base PCI +capability and base PCI Express capability. + +Driver just have to specify subsystem_vendor_id and subsystem_id fields in +emulated bridge structure and pci-bridge-emul takes care of correctly +compose PCI Bridge Subsystem Vendor ID capability. + +Signed-off-by: Pali Rohár <pali@kernel.org> +--- + drivers/pci/pci-bridge-emul.c | 69 +++++++++++++++++++++++++---------- + drivers/pci/pci-bridge-emul.h | 2 + + 2 files changed, 51 insertions(+), 20 deletions(-) + +diff --git a/drivers/pci/pci-bridge-emul.c b/drivers/pci/pci-bridge-emul.c +index c4b9837006ff..a5b662cc89d0 100644 +--- a/drivers/pci/pci-bridge-emul.c ++++ b/drivers/pci/pci-bridge-emul.c +@@ -21,8 +21,11 @@ + #include "pci-bridge-emul.h" + + #define PCI_BRIDGE_CONF_END PCI_STD_HEADER_SIZEOF ++#define PCI_CAP_SSID_SIZEOF (PCI_SSVID_DEVICE_ID + 2) ++#define PCI_CAP_SSID_START PCI_BRIDGE_CONF_END ++#define PCI_CAP_SSID_END (PCI_CAP_SSID_START + PCI_CAP_SSID_SIZEOF) + #define PCI_CAP_PCIE_SIZEOF (PCI_EXP_SLTSTA2 + 2) +-#define PCI_CAP_PCIE_START PCI_BRIDGE_CONF_END ++#define PCI_CAP_PCIE_START PCI_CAP_SSID_END + #define PCI_CAP_PCIE_END (PCI_CAP_PCIE_START + PCI_CAP_PCIE_SIZEOF) + + /** +@@ -315,6 +318,25 @@ struct pci_bridge_reg_behavior pcie_cap_regs_behavior[PCI_CAP_PCIE_SIZEOF / 4] = + }, + }; + ++static pci_bridge_emul_read_status_t ++pci_bridge_emul_read_ssid(struct pci_bridge_emul *bridge, int reg, u32 *value) ++{ ++ switch (reg) { ++ case PCI_CAP_LIST_ID: ++ *value = PCI_CAP_ID_SSVID | ++ (bridge->has_pcie ? (PCI_CAP_PCIE_START << 8) : 0); ++ return PCI_BRIDGE_EMUL_HANDLED; ++ ++ case PCI_SSVID_VENDOR_ID: ++ *value = bridge->subsystem_vendor_id | ++ (bridge->subsystem_id << 16); ++ return PCI_BRIDGE_EMUL_HANDLED; ++ ++ default: ++ return PCI_BRIDGE_EMUL_NOT_HANDLED; ++ } ++} ++ + /* + * Initialize a pci_bridge_emul structure to represent a fake PCI + * bridge configuration space. The caller needs to have initialized +@@ -341,9 +363,17 @@ int pci_bridge_emul_init(struct pci_bridge_emul *bridge, + if (!bridge->pci_regs_behavior) + return -ENOMEM; + +- if (bridge->has_pcie) { ++ if (bridge->subsystem_vendor_id) ++ bridge->conf.capabilities_pointer = PCI_CAP_SSID_START; ++ else if (bridge->has_pcie) + bridge->conf.capabilities_pointer = PCI_CAP_PCIE_START; ++ else ++ bridge->conf.capabilities_pointer = 0; ++ ++ if (bridge->conf.capabilities_pointer) + bridge->conf.status |= cpu_to_le16(PCI_STATUS_CAP_LIST); ++ ++ if (bridge->has_pcie) { + bridge->pcie_conf.cap_id = PCI_CAP_ID_EXP; + bridge->pcie_conf.cap |= cpu_to_le16(PCI_EXP_TYPE_ROOT_PORT << 4); + bridge->pcie_cap_regs_behavior = +@@ -427,26 +457,28 @@ int pci_bridge_emul_conf_read(struct pci_bridge_emul *bridge, int where, + read_op = bridge->ops->read_base; + cfgspace = (__le32 *) &bridge->conf; + behavior = bridge->pci_regs_behavior; +- } else if (!bridge->has_pcie) { +- /* PCIe space is not implemented, and no PCI capabilities */ +- *value = 0; +- return PCIBIOS_SUCCESSFUL; +- } else if (reg < PCI_CAP_PCIE_END) { ++ } else if (reg >= PCI_CAP_SSID_START && reg < PCI_CAP_SSID_END && bridge->subsystem_vendor_id) { ++ /* Emulated PCI Bridge Subsystem Vendor ID capability */ ++ reg -= PCI_CAP_SSID_START; ++ read_op = pci_bridge_emul_read_ssid; ++ cfgspace = NULL; ++ behavior = NULL; ++ } else if (reg >= PCI_CAP_PCIE_START && reg < PCI_CAP_PCIE_END && bridge->has_pcie) { + /* Our emulated PCIe capability */ + reg -= PCI_CAP_PCIE_START; + read_op = bridge->ops->read_pcie; + cfgspace = (__le32 *) &bridge->pcie_conf; + behavior = bridge->pcie_cap_regs_behavior; +- } else if (reg < PCI_CFG_SPACE_SIZE) { +- /* Rest of PCI space not implemented */ +- *value = 0; +- return PCIBIOS_SUCCESSFUL; +- } else { ++ } else if (reg >= PCI_CFG_SPACE_SIZE && bridge->has_pcie) { + /* PCIe extended capability space */ + reg -= PCI_CFG_SPACE_SIZE; + read_op = bridge->ops->read_ext; + cfgspace = NULL; + behavior = NULL; ++ } else { ++ /* Not implemented */ ++ *value = 0; ++ return PCIBIOS_SUCCESSFUL; + } + + if (read_op) +@@ -504,24 +536,21 @@ int pci_bridge_emul_conf_write(struct pci_bridge_emul *bridge, int where, + write_op = bridge->ops->write_base; + cfgspace = (__le32 *) &bridge->conf; + behavior = bridge->pci_regs_behavior; +- } else if (!bridge->has_pcie) { +- /* PCIe space is not implemented, and no PCI capabilities */ +- return PCIBIOS_SUCCESSFUL; +- } else if (reg < PCI_CAP_PCIE_END) { ++ } else if (reg >= PCI_CAP_PCIE_START && reg < PCI_CAP_PCIE_END && bridge->has_pcie) { + /* Our emulated PCIe capability */ + reg -= PCI_CAP_PCIE_START; + write_op = bridge->ops->write_pcie; + cfgspace = (__le32 *) &bridge->pcie_conf; + behavior = bridge->pcie_cap_regs_behavior; +- } else if (reg < PCI_CFG_SPACE_SIZE) { +- /* Rest of PCI space not implemented */ +- return PCIBIOS_SUCCESSFUL; +- } else { ++ } else if (reg >= PCI_CFG_SPACE_SIZE && bridge->has_pcie) { + /* PCIe extended capability space */ + reg -= PCI_CFG_SPACE_SIZE; + write_op = bridge->ops->write_ext; + cfgspace = NULL; + behavior = NULL; ++ } else { ++ /* Not implemented */ ++ return PCIBIOS_SUCCESSFUL; + } + + shift = (where & 0x3) * 8; +diff --git a/drivers/pci/pci-bridge-emul.h b/drivers/pci/pci-bridge-emul.h +index 6b5f75b2ad02..71392b67471d 100644 +--- a/drivers/pci/pci-bridge-emul.h ++++ b/drivers/pci/pci-bridge-emul.h +@@ -132,6 +132,8 @@ struct pci_bridge_emul { + struct pci_bridge_reg_behavior *pcie_cap_regs_behavior; + void *data; + bool has_pcie; ++ u16 subsystem_vendor_id; ++ u16 subsystem_id; + }; + + enum { +-- +2.34.1 + diff --git a/pkgs/patches-linux-5.15/0027-dt-bindings-PCI-mvebu-Add-num-lanes-property.patch b/pkgs/patches-linux-5.15/0027-dt-bindings-PCI-mvebu-Add-num-lanes-property.patch new file mode 100644 index 0000000..339456b --- /dev/null +++ b/pkgs/patches-linux-5.15/0027-dt-bindings-PCI-mvebu-Add-num-lanes-property.patch @@ -0,0 +1,114 @@ +From cca87cdc18be3c7ab387aad99cbc3d2e2a5e16dc Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org> +Date: Wed, 3 Nov 2021 14:32:52 +0100 +Subject: [PATCH 27/90] dt-bindings: PCI: mvebu: Add num-lanes property +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Controller driver needs to correctly configure PCIe link if it contains 1 +or 4 SerDes PCIe lanes. Therefore add a new 'num-lanes' DT property for +mvebu PCIe controller. Property 'num-lanes' seems to be de-facto standard +way how number of lanes is specified in other PCIe controllers. + +Signed-off-by: Pali Rohár <pali@kernel.org> +Acked-by: Rob Herring <robh@kernel.org> +--- + Documentation/devicetree/bindings/pci/mvebu-pci.txt | 11 +++++++++++ + 1 file changed, 11 insertions(+) + +diff --git a/Documentation/devicetree/bindings/pci/mvebu-pci.txt b/Documentation/devicetree/bindings/pci/mvebu-pci.txt +index 6173af6885f8..24225852bce0 100644 +--- a/Documentation/devicetree/bindings/pci/mvebu-pci.txt ++++ b/Documentation/devicetree/bindings/pci/mvebu-pci.txt +@@ -77,6 +77,7 @@ and the following optional properties: + - marvell,pcie-lane: the physical PCIe lane number, for ports having + multiple lanes. If this property is not found, we assume that the + value is 0. ++- num-lanes: number of SerDes PCIe lanes for this link (1 or 4) + - reset-gpios: optional GPIO to PERST# + - reset-delay-us: delay in us to wait after reset de-assertion, if not + specified will default to 100ms, as required by the PCIe specification. +@@ -141,6 +142,7 @@ pcie-controller { + interrupt-map = <0 0 0 0 &mpic 58>; + marvell,pcie-port = <0>; + marvell,pcie-lane = <0>; ++ num-lanes = <1>; + /* low-active PERST# reset on GPIO 25 */ + reset-gpios = <&gpio0 25 1>; + /* wait 20ms for device settle after reset deassertion */ +@@ -161,6 +163,7 @@ pcie-controller { + interrupt-map = <0 0 0 0 &mpic 59>; + marvell,pcie-port = <0>; + marvell,pcie-lane = <1>; ++ num-lanes = <1>; + clocks = <&gateclk 6>; + }; + +@@ -177,6 +180,7 @@ pcie-controller { + interrupt-map = <0 0 0 0 &mpic 60>; + marvell,pcie-port = <0>; + marvell,pcie-lane = <2>; ++ num-lanes = <1>; + clocks = <&gateclk 7>; + }; + +@@ -193,6 +197,7 @@ pcie-controller { + interrupt-map = <0 0 0 0 &mpic 61>; + marvell,pcie-port = <0>; + marvell,pcie-lane = <3>; ++ num-lanes = <1>; + clocks = <&gateclk 8>; + }; + +@@ -209,6 +214,7 @@ pcie-controller { + interrupt-map = <0 0 0 0 &mpic 62>; + marvell,pcie-port = <1>; + marvell,pcie-lane = <0>; ++ num-lanes = <1>; + clocks = <&gateclk 9>; + }; + +@@ -225,6 +231,7 @@ pcie-controller { + interrupt-map = <0 0 0 0 &mpic 63>; + marvell,pcie-port = <1>; + marvell,pcie-lane = <1>; ++ num-lanes = <1>; + clocks = <&gateclk 10>; + }; + +@@ -241,6 +248,7 @@ pcie-controller { + interrupt-map = <0 0 0 0 &mpic 64>; + marvell,pcie-port = <1>; + marvell,pcie-lane = <2>; ++ num-lanes = <1>; + clocks = <&gateclk 11>; + }; + +@@ -257,6 +265,7 @@ pcie-controller { + interrupt-map = <0 0 0 0 &mpic 65>; + marvell,pcie-port = <1>; + marvell,pcie-lane = <3>; ++ num-lanes = <1>; + clocks = <&gateclk 12>; + }; + +@@ -273,6 +282,7 @@ pcie-controller { + interrupt-map = <0 0 0 0 &mpic 99>; + marvell,pcie-port = <2>; + marvell,pcie-lane = <0>; ++ num-lanes = <1>; + clocks = <&gateclk 26>; + }; + +@@ -289,6 +299,7 @@ pcie-controller { + interrupt-map = <0 0 0 0 &mpic 103>; + marvell,pcie-port = <3>; + marvell,pcie-lane = <0>; ++ num-lanes = <1>; + clocks = <&gateclk 27>; + }; + }; +-- +2.34.1 + diff --git a/pkgs/patches-linux-5.15/0028-PCI-mvebu-Correctly-configure-x1-x4-mode.patch b/pkgs/patches-linux-5.15/0028-PCI-mvebu-Correctly-configure-x1-x4-mode.patch new file mode 100644 index 0000000..bb87163 --- /dev/null +++ b/pkgs/patches-linux-5.15/0028-PCI-mvebu-Correctly-configure-x1-x4-mode.patch @@ -0,0 +1,78 @@ +From 2137824f2a4e824b5d54b17c12d1005a6455ca12 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org> +Date: Mon, 11 Oct 2021 11:30:05 +0200 +Subject: [PATCH 28/90] PCI: mvebu: Correctly configure x1/x4 mode +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +If x1/x4 mode is not set correctly then link with endpoint card is not +established. + +Use DTS property 'num-lanes' to deteriminate x1/x4 mode. + +Signed-off-by: Pali Rohár <pali@kernel.org> +--- + drivers/pci/controller/pci-mvebu.c | 19 ++++++++++++++++++- + 1 file changed, 18 insertions(+), 1 deletion(-) + +diff --git a/drivers/pci/controller/pci-mvebu.c b/drivers/pci/controller/pci-mvebu.c +index a075ba26cff1..0f2ec0a17874 100644 +--- a/drivers/pci/controller/pci-mvebu.c ++++ b/drivers/pci/controller/pci-mvebu.c +@@ -93,6 +93,7 @@ struct mvebu_pcie_port { + void __iomem *base; + u32 port; + u32 lane; ++ bool is_x4; + int devfn; + unsigned int mem_target; + unsigned int mem_attr; +@@ -233,13 +234,25 @@ static void mvebu_pcie_setup_wins(struct mvebu_pcie_port *port) + + static void mvebu_pcie_setup_hw(struct mvebu_pcie_port *port) + { +- u32 ctrl, cmd, dev_rev, mask; ++ u32 ctrl, lnkcap, cmd, dev_rev, mask; + + /* Setup PCIe controller to Root Complex mode. */ + ctrl = mvebu_readl(port, PCIE_CTRL_OFF); + ctrl |= PCIE_CTRL_RC_MODE; + mvebu_writel(port, ctrl, PCIE_CTRL_OFF); + ++ /* ++ * Set Maximum Link Width to X1 or X4 in Root Port's PCIe Link ++ * Capability register. This register is defined by PCIe specification ++ * as read-only but this mvebu controller has it as read-write and must ++ * be set to number of SerDes PCIe lanes (1 or 4). If this register is ++ * not set correctly then link with endpoint card is not established. ++ */ ++ lnkcap = mvebu_readl(port, PCIE_CAP_PCIEXP + PCI_EXP_LNKCAP); ++ lnkcap &= ~PCI_EXP_LNKCAP_MLW; ++ lnkcap |= (port->is_x4 ? 4 : 1) << 4; ++ mvebu_writel(port, lnkcap, PCIE_CAP_PCIEXP + PCI_EXP_LNKCAP); ++ + /* Disable Root Bridge I/O space, memory space and bus mastering. */ + cmd = mvebu_readl(port, PCIE_CMD_OFF); + cmd &= ~(PCI_COMMAND_IO | PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER); +@@ -986,6 +999,7 @@ static int mvebu_pcie_parse_port(struct mvebu_pcie *pcie, + struct device *dev = &pcie->pdev->dev; + enum of_gpio_flags flags; + int reset_gpio, ret; ++ u32 num_lanes; + + port->pcie = pcie; + +@@ -998,6 +1012,9 @@ static int mvebu_pcie_parse_port(struct mvebu_pcie *pcie, + if (of_property_read_u32(child, "marvell,pcie-lane", &port->lane)) + port->lane = 0; + ++ if (!of_property_read_u32(child, "num-lanes", &num_lanes) && num_lanes == 4) ++ port->is_x4 = true; ++ + port->name = devm_kasprintf(dev, GFP_KERNEL, "pcie%d.%d", port->port, + port->lane); + if (!port->name) { +-- +2.34.1 + diff --git a/pkgs/patches-linux-5.15/0029-PCI-mvebu-Add-support-for-PCI-Bridge-Subsystem-Vendo.patch b/pkgs/patches-linux-5.15/0029-PCI-mvebu-Add-support-for-PCI-Bridge-Subsystem-Vendo.patch new file mode 100644 index 0000000..7f26476 --- /dev/null +++ b/pkgs/patches-linux-5.15/0029-PCI-mvebu-Add-support-for-PCI-Bridge-Subsystem-Vendo.patch @@ -0,0 +1,53 @@ +From 1192a8856544109e30555ca15aed7353297bae79 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org> +Date: Fri, 8 Oct 2021 11:47:35 +0200 +Subject: [PATCH 29/90] PCI: mvebu: Add support for PCI Bridge Subsystem Vendor + ID on emulated bridge +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Register with Subsystem Device/Vendor ID is at offset 0x2c. Export is via +emulated bridge. + +After this change Subsystem ID is visible in lspci output at line: + + Capabilities: [40] Subsystem + +Signed-off-by: Pali Rohár <pali@kernel.org> +--- + drivers/pci/controller/pci-mvebu.c | 4 ++++ + 1 file changed, 4 insertions(+) + +diff --git a/drivers/pci/controller/pci-mvebu.c b/drivers/pci/controller/pci-mvebu.c +index 0f2ec0a17874..811af9e6ede5 100644 +--- a/drivers/pci/controller/pci-mvebu.c ++++ b/drivers/pci/controller/pci-mvebu.c +@@ -32,6 +32,7 @@ + #define PCIE_DEV_REV_OFF 0x0008 + #define PCIE_BAR_LO_OFF(n) (0x0010 + ((n) << 3)) + #define PCIE_BAR_HI_OFF(n) (0x0014 + ((n) << 3)) ++#define PCIE_SSDEV_ID_OFF 0x002c + #define PCIE_CAP_PCIEXP 0x0060 + #define PCIE_HEADER_LOG_4_OFF 0x0128 + #define PCIE_BAR_CTRL_OFF(n) (0x1804 + (((n) - 1) * 4)) +@@ -731,6 +732,7 @@ static int mvebu_pci_bridge_emul_init(struct mvebu_pcie_port *port) + struct pci_bridge_emul *bridge = &port->bridge; + u32 dev_id = mvebu_readl(port, PCIE_DEV_ID_OFF); + u32 dev_rev = mvebu_readl(port, PCIE_DEV_REV_OFF); ++ u32 ssdev_id = mvebu_readl(port, PCIE_SSDEV_ID_OFF); + u32 pcie_cap = mvebu_readl(port, PCIE_CAP_PCIEXP); + u8 pcie_cap_ver = ((pcie_cap >> 16) & PCI_EXP_FLAGS_VERS); + +@@ -752,6 +754,8 @@ static int mvebu_pci_bridge_emul_init(struct mvebu_pcie_port *port) + */ + bridge->pcie_conf.cap = cpu_to_le16(pcie_cap_ver); + ++ bridge->subsystem_vendor_id = ssdev_id & 0xffff; ++ bridge->subsystem_id = ssdev_id >> 16; + bridge->has_pcie = true; + bridge->data = port; + bridge->ops = &mvebu_pci_bridge_emul_ops; +-- +2.34.1 + diff --git a/pkgs/patches-linux-5.15/0030-PCI-mvebu-Add-support-for-Advanced-Error-Reporting-r.patch b/pkgs/patches-linux-5.15/0030-PCI-mvebu-Add-support-for-Advanced-Error-Reporting-r.patch new file mode 100644 index 0000000..fb806e5 --- /dev/null +++ b/pkgs/patches-linux-5.15/0030-PCI-mvebu-Add-support-for-Advanced-Error-Reporting-r.patch @@ -0,0 +1,125 @@ +From 6ccfcdf4eef16aaac2bddc3b625a2dabe357bcb1 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org> +Date: Fri, 17 Sep 2021 13:46:44 +0200 +Subject: [PATCH 30/90] PCI: mvebu: Add support for Advanced Error Reporting + registers on emulated bridge +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +AER registers start at mvebu offset 0x0100. Registers PCI_ERR_ROOT_COMMAND, +PCI_ERR_ROOT_STATUS and PCI_ERR_ROOT_ERR_SRC are not supported on pre-XP +hardware and returns zeros. + +Note that AER interrupt is not supported yet as mvebu emulated bridge does +not implement interrupts support at all yet. + +Also remove custom macro PCIE_HEADER_LOG_4_OFF as it is unused and +correctly this register should be referenced via standard macros with +offset, e.g. as: PCIE_CAP_PCIERR_OFF + PCI_ERR_HEADER_LOG + 4. + +Signed-off-by: Pali Rohár <pali@kernel.org> +--- + drivers/pci/controller/pci-mvebu.c | 67 +++++++++++++++++++++++++++++- + 1 file changed, 66 insertions(+), 1 deletion(-) + +diff --git a/drivers/pci/controller/pci-mvebu.c b/drivers/pci/controller/pci-mvebu.c +index 811af9e6ede5..9ea2f6a7c2b0 100644 +--- a/drivers/pci/controller/pci-mvebu.c ++++ b/drivers/pci/controller/pci-mvebu.c +@@ -34,7 +34,7 @@ + #define PCIE_BAR_HI_OFF(n) (0x0014 + ((n) << 3)) + #define PCIE_SSDEV_ID_OFF 0x002c + #define PCIE_CAP_PCIEXP 0x0060 +-#define PCIE_HEADER_LOG_4_OFF 0x0128 ++#define PCIE_CAP_PCIERR_OFF 0x0100 + #define PCIE_BAR_CTRL_OFF(n) (0x1804 + (((n) - 1) * 4)) + #define PCIE_WIN04_CTRL_OFF(n) (0x1820 + ((n) << 4)) + #define PCIE_WIN04_BASE_OFF(n) (0x1824 + ((n) << 4)) +@@ -603,6 +603,37 @@ mvebu_pci_bridge_emul_pcie_conf_read(struct pci_bridge_emul *bridge, + return PCI_BRIDGE_EMUL_HANDLED; + } + ++static pci_bridge_emul_read_status_t ++mvebu_pci_bridge_emul_ext_conf_read(struct pci_bridge_emul *bridge, ++ int reg, u32 *value) ++{ ++ struct mvebu_pcie_port *port = bridge->data; ++ ++ switch (reg) { ++ case 0: ++ case PCI_ERR_UNCOR_STATUS: ++ case PCI_ERR_UNCOR_MASK: ++ case PCI_ERR_UNCOR_SEVER: ++ case PCI_ERR_COR_STATUS: ++ case PCI_ERR_COR_MASK: ++ case PCI_ERR_CAP: ++ case PCI_ERR_HEADER_LOG+0: ++ case PCI_ERR_HEADER_LOG+4: ++ case PCI_ERR_HEADER_LOG+8: ++ case PCI_ERR_HEADER_LOG+12: ++ case PCI_ERR_ROOT_COMMAND: ++ case PCI_ERR_ROOT_STATUS: ++ case PCI_ERR_ROOT_ERR_SRC: ++ *value = mvebu_readl(port, PCIE_CAP_PCIERR_OFF + reg); ++ break; ++ ++ default: ++ return PCI_BRIDGE_EMUL_NOT_HANDLED; ++ } ++ ++ return PCI_BRIDGE_EMUL_HANDLED; ++} ++ + static void + mvebu_pci_bridge_emul_base_conf_write(struct pci_bridge_emul *bridge, + int reg, u32 old, u32 new, u32 mask) +@@ -715,11 +746,45 @@ mvebu_pci_bridge_emul_pcie_conf_write(struct pci_bridge_emul *bridge, + } + } + ++static void ++mvebu_pci_bridge_emul_ext_conf_write(struct pci_bridge_emul *bridge, ++ int reg, u32 old, u32 new, u32 mask) ++{ ++ struct mvebu_pcie_port *port = bridge->data; ++ ++ switch (reg) { ++ /* These are W1C registers, so clear other bits */ ++ case PCI_ERR_UNCOR_STATUS: ++ case PCI_ERR_COR_STATUS: ++ case PCI_ERR_ROOT_STATUS: ++ new &= mask; ++ fallthrough; ++ ++ case PCI_ERR_UNCOR_MASK: ++ case PCI_ERR_UNCOR_SEVER: ++ case PCI_ERR_COR_MASK: ++ case PCI_ERR_CAP: ++ case PCI_ERR_HEADER_LOG+0: ++ case PCI_ERR_HEADER_LOG+4: ++ case PCI_ERR_HEADER_LOG+8: ++ case PCI_ERR_HEADER_LOG+12: ++ case PCI_ERR_ROOT_COMMAND: ++ case PCI_ERR_ROOT_ERR_SRC: ++ mvebu_writel(port, new, PCIE_CAP_PCIERR_OFF + reg); ++ break; ++ ++ default: ++ break; ++ } ++} ++ + static const struct pci_bridge_emul_ops mvebu_pci_bridge_emul_ops = { + .read_base = mvebu_pci_bridge_emul_base_conf_read, + .write_base = mvebu_pci_bridge_emul_base_conf_write, + .read_pcie = mvebu_pci_bridge_emul_pcie_conf_read, + .write_pcie = mvebu_pci_bridge_emul_pcie_conf_write, ++ .read_ext = mvebu_pci_bridge_emul_ext_conf_read, ++ .write_ext = mvebu_pci_bridge_emul_ext_conf_write, + }; + + /* +-- +2.34.1 + diff --git a/pkgs/patches-linux-5.15/0031-PCI-mvebu-Use-child_ops-API.patch b/pkgs/patches-linux-5.15/0031-PCI-mvebu-Use-child_ops-API.patch new file mode 100644 index 0000000..41d7430 --- /dev/null +++ b/pkgs/patches-linux-5.15/0031-PCI-mvebu-Use-child_ops-API.patch @@ -0,0 +1,165 @@ +From a32d68b41dac1a1aeab94a2c8b2239534ac59c15 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org> +Date: Tue, 16 Nov 2021 10:43:17 +0100 +Subject: [PATCH 31/90] PCI: mvebu: Use child_ops API +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Split struct pci_ops between ops and child_ops. Member ops is used for +accessing PCIe Root Ports via pci-bridge-emul.c driver and child_ops for +accessing real PCIe cards. + +There is no need to mix these two struct pci_ops into one as PCI core code +already provides separate callbacks via bridge->ops and bridge->child_ops. + +Signed-off-by: Pali Rohár <pali@kernel.org> +--- + drivers/pci/controller/pci-mvebu.c | 82 ++++++++++++++++-------------- + 1 file changed, 44 insertions(+), 38 deletions(-) + +diff --git a/drivers/pci/controller/pci-mvebu.c b/drivers/pci/controller/pci-mvebu.c +index 9ea2f6a7c2b0..1e90ab888075 100644 +--- a/drivers/pci/controller/pci-mvebu.c ++++ b/drivers/pci/controller/pci-mvebu.c +@@ -294,11 +294,29 @@ static void mvebu_pcie_setup_hw(struct mvebu_pcie_port *port) + mvebu_writel(port, mask, PCIE_MASK_OFF); + } + +-static int mvebu_pcie_hw_rd_conf(struct mvebu_pcie_port *port, +- struct pci_bus *bus, +- u32 devfn, int where, int size, u32 *val) ++static struct mvebu_pcie_port *mvebu_pcie_find_port(struct mvebu_pcie *pcie, ++ struct pci_bus *bus, ++ int devfn); ++ ++static int mvebu_pcie_child_rd_conf(struct pci_bus *bus, u32 devfn, int where, ++ int size, u32 *val) + { +- void __iomem *conf_data = port->base + PCIE_CONF_DATA_OFF; ++ struct mvebu_pcie *pcie = bus->sysdata; ++ struct mvebu_pcie_port *port; ++ void __iomem *conf_data; ++ ++ port = mvebu_pcie_find_port(pcie, bus, devfn); ++ if (!port) { ++ *val = 0xffffffff; ++ return PCIBIOS_DEVICE_NOT_FOUND; ++ } ++ ++ if (!mvebu_pcie_link_up(port)) { ++ *val = 0xffffffff; ++ return PCIBIOS_DEVICE_NOT_FOUND; ++ } ++ ++ conf_data = port->base + PCIE_CONF_DATA_OFF; + + mvebu_writel(port, PCIE_CONF_ADDR(bus->number, devfn, where), + PCIE_CONF_ADDR_OFF); +@@ -321,11 +339,21 @@ static int mvebu_pcie_hw_rd_conf(struct mvebu_pcie_port *port, + return PCIBIOS_SUCCESSFUL; + } + +-static int mvebu_pcie_hw_wr_conf(struct mvebu_pcie_port *port, +- struct pci_bus *bus, +- u32 devfn, int where, int size, u32 val) ++static int mvebu_pcie_child_wr_conf(struct pci_bus *bus, u32 devfn, ++ int where, int size, u32 val) + { +- void __iomem *conf_data = port->base + PCIE_CONF_DATA_OFF; ++ struct mvebu_pcie *pcie = bus->sysdata; ++ struct mvebu_pcie_port *port; ++ void __iomem *conf_data; ++ ++ port = mvebu_pcie_find_port(pcie, bus, devfn); ++ if (!port) ++ return PCIBIOS_DEVICE_NOT_FOUND; ++ ++ if (!mvebu_pcie_link_up(port)) ++ return PCIBIOS_DEVICE_NOT_FOUND; ++ ++ conf_data = port->base + PCIE_CONF_DATA_OFF; + + mvebu_writel(port, PCIE_CONF_ADDR(bus->number, devfn, where), + PCIE_CONF_ADDR_OFF); +@@ -347,6 +375,11 @@ static int mvebu_pcie_hw_wr_conf(struct mvebu_pcie_port *port, + return PCIBIOS_SUCCESSFUL; + } + ++static struct pci_ops mvebu_pcie_child_ops = { ++ .read = mvebu_pcie_child_rd_conf, ++ .write = mvebu_pcie_child_wr_conf, ++}; ++ + /* + * Remove windows, starting from the largest ones to the smallest + * ones. +@@ -862,25 +895,12 @@ static int mvebu_pcie_wr_conf(struct pci_bus *bus, u32 devfn, + { + struct mvebu_pcie *pcie = bus->sysdata; + struct mvebu_pcie_port *port; +- int ret; + + port = mvebu_pcie_find_port(pcie, bus, devfn); + if (!port) + return PCIBIOS_DEVICE_NOT_FOUND; + +- /* Access the emulated PCI-to-PCI bridge */ +- if (bus->number == 0) +- return pci_bridge_emul_conf_write(&port->bridge, where, +- size, val); +- +- if (!mvebu_pcie_link_up(port)) +- return PCIBIOS_DEVICE_NOT_FOUND; +- +- /* Access the real PCIe interface */ +- ret = mvebu_pcie_hw_wr_conf(port, bus, devfn, +- where, size, val); +- +- return ret; ++ return pci_bridge_emul_conf_write(&port->bridge, where, size, val); + } + + /* PCI configuration space read function */ +@@ -889,7 +909,6 @@ static int mvebu_pcie_rd_conf(struct pci_bus *bus, u32 devfn, int where, + { + struct mvebu_pcie *pcie = bus->sysdata; + struct mvebu_pcie_port *port; +- int ret; + + port = mvebu_pcie_find_port(pcie, bus, devfn); + if (!port) { +@@ -897,21 +916,7 @@ static int mvebu_pcie_rd_conf(struct pci_bus *bus, u32 devfn, int where, + return PCIBIOS_DEVICE_NOT_FOUND; + } + +- /* Access the emulated PCI-to-PCI bridge */ +- if (bus->number == 0) +- return pci_bridge_emul_conf_read(&port->bridge, where, +- size, val); +- +- if (!mvebu_pcie_link_up(port)) { +- *val = 0xffffffff; +- return PCIBIOS_DEVICE_NOT_FOUND; +- } +- +- /* Access the real PCIe interface */ +- ret = mvebu_pcie_hw_rd_conf(port, bus, devfn, +- where, size, val); +- +- return ret; ++ return pci_bridge_emul_conf_read(&port->bridge, where, size, val); + } + + static struct pci_ops mvebu_pcie_ops = { +@@ -1421,6 +1426,7 @@ static int mvebu_pcie_probe(struct platform_device *pdev) + + bridge->sysdata = pcie; + bridge->ops = &mvebu_pcie_ops; ++ bridge->child_ops = &mvebu_pcie_child_ops; + bridge->align_resource = mvebu_pcie_align_resource; + bridge->map_irq = mvebu_pcie_map_irq; + +-- +2.34.1 + diff --git a/pkgs/patches-linux-5.15/0032-dt-bindings-PCI-mvebu-Update-information-about-intx-.patch b/pkgs/patches-linux-5.15/0032-dt-bindings-PCI-mvebu-Update-information-about-intx-.patch new file mode 100644 index 0000000..9e71020 --- /dev/null +++ b/pkgs/patches-linux-5.15/0032-dt-bindings-PCI-mvebu-Update-information-about-intx-.patch @@ -0,0 +1,34 @@ +From 3ef4b961298bb64ba871616f51a0d869cb7c4555 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org> +Date: Fri, 17 Sep 2021 14:43:39 +0200 +Subject: [PATCH 32/90] dt-bindings: PCI: mvebu: Update information about intx + interrupts +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Signed-off-by: Pali Rohár <pali@kernel.org> +Acked-by: Rob Herring <robh@kernel.org> +--- + Documentation/devicetree/bindings/pci/mvebu-pci.txt | 5 +++++ + 1 file changed, 5 insertions(+) + +diff --git a/Documentation/devicetree/bindings/pci/mvebu-pci.txt b/Documentation/devicetree/bindings/pci/mvebu-pci.txt +index 24225852bce0..6d022a9d36ee 100644 +--- a/Documentation/devicetree/bindings/pci/mvebu-pci.txt ++++ b/Documentation/devicetree/bindings/pci/mvebu-pci.txt +@@ -81,6 +81,11 @@ and the following optional properties: + - reset-gpios: optional GPIO to PERST# + - reset-delay-us: delay in us to wait after reset de-assertion, if not + specified will default to 100ms, as required by the PCIe specification. ++- interrupt-names: list of interrupt names, supported are: ++ - "intx" - interrupt line triggered by one of the legacy interrupt ++- interrupts or interrupts-extended: List of the interrupt sources which ++ corresponding to the "interrupt-names". If non-empty then also additional ++ 'interrupt-controller' subnode must be defined. + + Example: + +-- +2.34.1 + diff --git a/pkgs/patches-linux-5.15/0033-PCI-mvebu-Fix-macro-names-and-comments-about-legacy-.patch b/pkgs/patches-linux-5.15/0033-PCI-mvebu-Fix-macro-names-and-comments-about-legacy-.patch new file mode 100644 index 0000000..185b1c2 --- /dev/null +++ b/pkgs/patches-linux-5.15/0033-PCI-mvebu-Fix-macro-names-and-comments-about-legacy-.patch @@ -0,0 +1,79 @@ +From 8fa82d166233f0c6b4ec82b4b92dbb356cd69675 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org> +Date: Mon, 14 Feb 2022 13:12:48 +0100 +Subject: [PATCH 33/90] PCI: mvebu: Fix macro names and comments about legacy + interrupts +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Register 0x1910 unmasks interrupts and legacy INTx interrupts are unmasked +because driver does not support individual masking yet. + +Signed-off-by: Pali Rohár <pali@kernel.org> +--- + drivers/pci/controller/pci-mvebu.c | 26 ++++++++++++++++++-------- + 1 file changed, 18 insertions(+), 8 deletions(-) + +diff --git a/drivers/pci/controller/pci-mvebu.c b/drivers/pci/controller/pci-mvebu.c +index 1e90ab888075..5f8b8b4ddbea 100644 +--- a/drivers/pci/controller/pci-mvebu.c ++++ b/drivers/pci/controller/pci-mvebu.c +@@ -54,9 +54,10 @@ + PCIE_CONF_ADDR_EN) + #define PCIE_CONF_DATA_OFF 0x18fc + #define PCIE_INT_CAUSE_OFF 0x1900 ++#define PCIE_INT_UNMASK_OFF 0x1910 ++#define PCIE_INT_INTX(i) BIT(24+i) + #define PCIE_INT_PM_PME BIT(28) +-#define PCIE_MASK_OFF 0x1910 +-#define PCIE_MASK_ENABLE_INTS 0x0f000000 ++#define PCIE_INT_ALL_MASK GENMASK(31, 0) + #define PCIE_CTRL_OFF 0x1a00 + #define PCIE_CTRL_X1_MODE 0x0001 + #define PCIE_CTRL_RC_MODE BIT(1) +@@ -235,7 +236,7 @@ static void mvebu_pcie_setup_wins(struct mvebu_pcie_port *port) + + static void mvebu_pcie_setup_hw(struct mvebu_pcie_port *port) + { +- u32 ctrl, lnkcap, cmd, dev_rev, mask; ++ u32 ctrl, lnkcap, cmd, dev_rev, unmask; + + /* Setup PCIe controller to Root Complex mode. */ + ctrl = mvebu_readl(port, PCIE_CTRL_OFF); +@@ -288,10 +289,19 @@ static void mvebu_pcie_setup_hw(struct mvebu_pcie_port *port) + /* Point PCIe unit MBUS decode windows to DRAM space. */ + mvebu_pcie_setup_wins(port); + +- /* Enable interrupt lines A-D. */ +- mask = mvebu_readl(port, PCIE_MASK_OFF); +- mask |= PCIE_MASK_ENABLE_INTS; +- mvebu_writel(port, mask, PCIE_MASK_OFF); ++ /* ++ * Unmask all legacy INTx interrupts as driver does not provide a way ++ * for masking and unmasking of individual legacy INTx interrupts. ++ * Legacy INTx are reported via one shared GIC source and therefore ++ * kernel cannot distinguish which individual legacy INTx was triggered. ++ * These interrupts are shared, so it should not cause any issue. Just ++ * performance penalty as every PCIe interrupt handler needs to be ++ * called when some interrupt is triggered. ++ */ ++ unmask = mvebu_readl(port, PCIE_INT_UNMASK_OFF); ++ unmask |= PCIE_INT_INTX(0) | PCIE_INT_INTX(1) | ++ PCIE_INT_INTX(2) | PCIE_INT_INTX(3); ++ mvebu_writel(port, unmask, PCIE_INT_UNMASK_OFF); + } + + static struct mvebu_pcie_port *mvebu_pcie_find_port(struct mvebu_pcie *pcie, +@@ -1458,7 +1468,7 @@ static int mvebu_pcie_remove(struct platform_device *pdev) + mvebu_writel(port, cmd, PCIE_CMD_OFF); + + /* Mask all interrupt sources. */ +- mvebu_writel(port, 0, PCIE_MASK_OFF); ++ mvebu_writel(port, ~PCIE_INT_ALL_MASK, PCIE_INT_UNMASK_OFF); + + /* Free config space for emulated root bridge. */ + pci_bridge_emul_cleanup(&port->bridge); +-- +2.34.1 + diff --git a/pkgs/patches-linux-5.15/0034-PCI-mvebu-Implement-support-for-legacy-INTx-interrup.patch b/pkgs/patches-linux-5.15/0034-PCI-mvebu-Implement-support-for-legacy-INTx-interrup.patch new file mode 100644 index 0000000..d02a2c4 --- /dev/null +++ b/pkgs/patches-linux-5.15/0034-PCI-mvebu-Implement-support-for-legacy-INTx-interrup.patch @@ -0,0 +1,251 @@ +From 09e38b818e55358009d7c20aeddb7c59f0b3a3e1 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org> +Date: Tue, 2 Nov 2021 10:30:20 +0100 +Subject: [PATCH 34/90] PCI: mvebu: Implement support for legacy INTx + interrupts +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +This adds support for legacy INTx interrupts received from other PCIe +devices and which are reported by a new INTx irq chip. + +With this change, kernel can distinguish between INTA, INTB, INTC and INTD +interrupts. + +Note that for this support, device tree files has to be properly adjusted +to provide "interrupts" or "interrupts-extended" property with intx +interrupt source, "interrupt-names" property with "intx" string and also +'interrupt-controller' subnode must be defined. + +If device tree files do not provide these nodes then driver would work as +before. + +Signed-off-by: Pali Rohár <pali@kernel.org> +--- + drivers/pci/controller/pci-mvebu.c | 159 +++++++++++++++++++++++++++++ + 1 file changed, 159 insertions(+) + +diff --git a/drivers/pci/controller/pci-mvebu.c b/drivers/pci/controller/pci-mvebu.c +index 5f8b8b4ddbea..c9311829dfe3 100644 +--- a/drivers/pci/controller/pci-mvebu.c ++++ b/drivers/pci/controller/pci-mvebu.c +@@ -111,6 +111,9 @@ struct mvebu_pcie_port { + struct mvebu_pcie_window iowin; + u32 saved_pcie_stat; + struct resource regs; ++ struct irq_domain *intx_irq_domain; ++ raw_spinlock_t irq_lock; ++ int intx_irq; + }; + + static inline void mvebu_writel(struct mvebu_pcie_port *port, u32 val, u32 reg) +@@ -289,7 +292,18 @@ static void mvebu_pcie_setup_hw(struct mvebu_pcie_port *port) + /* Point PCIe unit MBUS decode windows to DRAM space. */ + mvebu_pcie_setup_wins(port); + ++ /* Mask all interrupt sources. */ ++ mvebu_writel(port, ~PCIE_INT_ALL_MASK, PCIE_INT_UNMASK_OFF); ++ ++ /* Clear all interrupt causes. */ ++ mvebu_writel(port, ~PCIE_INT_ALL_MASK, PCIE_INT_CAUSE_OFF); ++ ++ /* Check if "intx" interrupt was specified in DT. */ ++ if (port->intx_irq > 0) ++ return; ++ + /* ++ * Fallback code when "intx" interrupt was not specified in DT: + * Unmask all legacy INTx interrupts as driver does not provide a way + * for masking and unmasking of individual legacy INTx interrupts. + * Legacy INTx are reported via one shared GIC source and therefore +@@ -934,6 +948,108 @@ static struct pci_ops mvebu_pcie_ops = { + .write = mvebu_pcie_wr_conf, + }; + ++static void mvebu_pcie_intx_irq_mask(struct irq_data *d) ++{ ++ struct mvebu_pcie_port *port = d->domain->host_data; ++ irq_hw_number_t hwirq = irqd_to_hwirq(d); ++ unsigned long flags; ++ u32 unmask; ++ ++ raw_spin_lock_irqsave(&port->irq_lock, flags); ++ unmask = mvebu_readl(port, PCIE_INT_UNMASK_OFF); ++ unmask &= ~PCIE_INT_INTX(hwirq); ++ mvebu_writel(port, unmask, PCIE_INT_UNMASK_OFF); ++ raw_spin_unlock_irqrestore(&port->irq_lock, flags); ++} ++ ++static void mvebu_pcie_intx_irq_unmask(struct irq_data *d) ++{ ++ struct mvebu_pcie_port *port = d->domain->host_data; ++ irq_hw_number_t hwirq = irqd_to_hwirq(d); ++ unsigned long flags; ++ u32 unmask; ++ ++ raw_spin_lock_irqsave(&port->irq_lock, flags); ++ unmask = mvebu_readl(port, PCIE_INT_UNMASK_OFF); ++ unmask |= PCIE_INT_INTX(hwirq); ++ mvebu_writel(port, unmask, PCIE_INT_UNMASK_OFF); ++ raw_spin_unlock_irqrestore(&port->irq_lock, flags); ++} ++ ++static struct irq_chip intx_irq_chip = { ++ .name = "mvebu-INTx", ++ .irq_mask = mvebu_pcie_intx_irq_mask, ++ .irq_unmask = mvebu_pcie_intx_irq_unmask, ++}; ++ ++static int mvebu_pcie_intx_irq_map(struct irq_domain *h, ++ unsigned int virq, irq_hw_number_t hwirq) ++{ ++ struct mvebu_pcie_port *port = h->host_data; ++ ++ irq_set_status_flags(virq, IRQ_LEVEL); ++ irq_set_chip_and_handler(virq, &intx_irq_chip, handle_level_irq); ++ irq_set_chip_data(virq, port); ++ ++ return 0; ++} ++ ++static const struct irq_domain_ops mvebu_pcie_intx_irq_domain_ops = { ++ .map = mvebu_pcie_intx_irq_map, ++ .xlate = irq_domain_xlate_onecell, ++}; ++ ++static int mvebu_pcie_init_irq_domain(struct mvebu_pcie_port *port) ++{ ++ struct device *dev = &port->pcie->pdev->dev; ++ struct device_node *pcie_intc_node; ++ ++ raw_spin_lock_init(&port->irq_lock); ++ ++ pcie_intc_node = of_get_next_child(port->dn, NULL); ++ if (!pcie_intc_node) { ++ dev_err(dev, "No PCIe Intc node found for %s\n", port->name); ++ return -ENODEV; ++ } ++ ++ port->intx_irq_domain = irq_domain_add_linear(pcie_intc_node, PCI_NUM_INTX, ++ &mvebu_pcie_intx_irq_domain_ops, ++ port); ++ of_node_put(pcie_intc_node); ++ if (!port->intx_irq_domain) { ++ dev_err(dev, "Failed to get INTx IRQ domain for %s\n", port->name); ++ return -ENOMEM; ++ } ++ ++ return 0; ++} ++ ++static void mvebu_pcie_irq_handler(struct irq_desc *desc) ++{ ++ struct mvebu_pcie_port *port = irq_desc_get_handler_data(desc); ++ struct irq_chip *chip = irq_desc_get_chip(desc); ++ struct device *dev = &port->pcie->pdev->dev; ++ u32 cause, unmask, status; ++ int i; ++ ++ chained_irq_enter(chip, desc); ++ ++ cause = mvebu_readl(port, PCIE_INT_CAUSE_OFF); ++ unmask = mvebu_readl(port, PCIE_INT_UNMASK_OFF); ++ status = cause & unmask; ++ ++ /* Process legacy INTx interrupts */ ++ for (i = 0; i < PCI_NUM_INTX; i++) { ++ if (!(status & PCIE_INT_INTX(i))) ++ continue; ++ ++ if (generic_handle_domain_irq(port->intx_irq_domain, i) == -EINVAL) ++ dev_err_ratelimited(dev, "unexpected INT%c IRQ\n", (char)i+'A'); ++ } ++ ++ chained_irq_exit(chip, desc); ++} ++ + static int mvebu_pcie_map_irq(const struct pci_dev *dev, u8 slot, u8 pin) + { + /* Interrupt support on mvebu emulated bridges is not implemented yet */ +@@ -1131,6 +1247,21 @@ static int mvebu_pcie_parse_port(struct mvebu_pcie *pcie, + port->io_attr = -1; + } + ++ /* ++ * Old DT bindings do not contain "intx" interrupt ++ * so do not fail probing driver when interrupt does not exist. ++ */ ++ port->intx_irq = of_irq_get_byname(child, "intx"); ++ if (port->intx_irq == -EPROBE_DEFER) { ++ ret = port->intx_irq; ++ goto err; ++ } ++ if (port->intx_irq <= 0) { ++ dev_warn(dev, "%s: legacy INTx interrupts cannot be masked individually, " ++ "%pOF does not contain intx interrupt\n", ++ port->name, child); ++ } ++ + reset_gpio = of_get_named_gpio_flags(child, "reset-gpios", 0, &flags); + if (reset_gpio == -EPROBE_DEFER) { + ret = reset_gpio; +@@ -1327,6 +1458,7 @@ static int mvebu_pcie_probe(struct platform_device *pdev) + + for (i = 0; i < pcie->nports; i++) { + struct mvebu_pcie_port *port = &pcie->ports[i]; ++ int irq = port->intx_irq; + + child = port->dn; + if (!child) +@@ -1354,6 +1486,22 @@ static int mvebu_pcie_probe(struct platform_device *pdev) + continue; + } + ++ if (irq > 0) { ++ ret = mvebu_pcie_init_irq_domain(port); ++ if (ret) { ++ dev_err(dev, "%s: cannot init irq domain\n", ++ port->name); ++ pci_bridge_emul_cleanup(&port->bridge); ++ devm_iounmap(dev, port->base); ++ port->base = NULL; ++ mvebu_pcie_powerdown(port); ++ continue; ++ } ++ irq_set_chained_handler_and_data(irq, ++ mvebu_pcie_irq_handler, ++ port); ++ } ++ + /* + * PCIe topology exported by mvebu hw is quite complicated. In + * reality has something like N fully independent host bridges +@@ -1458,6 +1606,7 @@ static int mvebu_pcie_remove(struct platform_device *pdev) + + for (i = 0; i < pcie->nports; i++) { + struct mvebu_pcie_port *port = &pcie->ports[i]; ++ int irq = port->intx_irq; + + if (!port->base) + continue; +@@ -1470,6 +1619,16 @@ static int mvebu_pcie_remove(struct platform_device *pdev) + /* Mask all interrupt sources. */ + mvebu_writel(port, ~PCIE_INT_ALL_MASK, PCIE_INT_UNMASK_OFF); + ++ /* Clear all interrupt causes. */ ++ mvebu_writel(port, ~PCIE_INT_ALL_MASK, PCIE_INT_CAUSE_OFF); ++ ++ if (irq > 0) ++ irq_set_chained_handler_and_data(irq, NULL, NULL); ++ ++ /* Remove IRQ domains. */ ++ if (port->intx_irq_domain) ++ irq_domain_remove(port->intx_irq_domain); ++ + /* Free config space for emulated root bridge. */ + pci_bridge_emul_cleanup(&port->bridge); + +-- +2.34.1 + diff --git a/pkgs/patches-linux-5.15/0035-ARM-dts-kirkwood-Add-definitions-for-PCIe-legacy-INT.patch b/pkgs/patches-linux-5.15/0035-ARM-dts-kirkwood-Add-definitions-for-PCIe-legacy-INT.patch new file mode 100644 index 0000000..5f72bc4 --- /dev/null +++ b/pkgs/patches-linux-5.15/0035-ARM-dts-kirkwood-Add-definitions-for-PCIe-legacy-INT.patch @@ -0,0 +1,161 @@ +From 17c0e92f81db8b06c2fccc47da25bc14af58e03c Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org> +Date: Mon, 27 Jun 2022 20:14:33 +0200 +Subject: [PATCH 35/90] ARM: dts: kirkwood: Add definitions for PCIe legacy + INTx interrupts +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Signed-off-by: Pali Rohár <pali@kernel.org> +--- + arch/arm/boot/dts/kirkwood-6192.dtsi | 14 ++++++++++-- + arch/arm/boot/dts/kirkwood-6281.dtsi | 14 ++++++++++-- + arch/arm/boot/dts/kirkwood-6282.dtsi | 28 ++++++++++++++++++++---- + arch/arm/boot/dts/kirkwood-98dx4122.dtsi | 14 ++++++++++-- + 4 files changed, 60 insertions(+), 10 deletions(-) + +diff --git a/arch/arm/boot/dts/kirkwood-6192.dtsi b/arch/arm/boot/dts/kirkwood-6192.dtsi +index 396bcba08adb..07f4f7f98c0c 100644 +--- a/arch/arm/boot/dts/kirkwood-6192.dtsi ++++ b/arch/arm/boot/dts/kirkwood-6192.dtsi +@@ -26,12 +26,22 @@ pcie0: pcie@1,0 { + ranges = <0x82000000 0 0 0x82000000 0x1 0 1 0 + 0x81000000 0 0 0x81000000 0x1 0 1 0>; + bus-range = <0x00 0xff>; +- interrupt-map-mask = <0 0 0 0>; +- interrupt-map = <0 0 0 0 &intc 9>; ++ interrupt-names = "intx"; ++ interrupts = <9>; ++ interrupt-map-mask = <0 0 0 7>; ++ interrupt-map = <0 0 0 1 &pcie_intc 0>, ++ <0 0 0 2 &pcie_intc 1>, ++ <0 0 0 3 &pcie_intc 2>, ++ <0 0 0 4 &pcie_intc 3>; + marvell,pcie-port = <0>; + marvell,pcie-lane = <0>; + clocks = <&gate_clk 2>; + status = "disabled"; ++ ++ pcie_intc: interrupt-controller { ++ interrupt-controller; ++ #interrupt-cells = <1>; ++ }; + }; + }; + }; +diff --git a/arch/arm/boot/dts/kirkwood-6281.dtsi b/arch/arm/boot/dts/kirkwood-6281.dtsi +index faa05849a40d..d08a9a5ecc26 100644 +--- a/arch/arm/boot/dts/kirkwood-6281.dtsi ++++ b/arch/arm/boot/dts/kirkwood-6281.dtsi +@@ -26,12 +26,22 @@ pcie0: pcie@1,0 { + ranges = <0x82000000 0 0 0x82000000 0x1 0 1 0 + 0x81000000 0 0 0x81000000 0x1 0 1 0>; + bus-range = <0x00 0xff>; +- interrupt-map-mask = <0 0 0 0>; +- interrupt-map = <0 0 0 0 &intc 9>; ++ interrupt-names = "intx"; ++ interrupts = <9>; ++ interrupt-map-mask = <0 0 0 7>; ++ interrupt-map = <0 0 0 1 &pcie_intc 0>, ++ <0 0 0 2 &pcie_intc 1>, ++ <0 0 0 3 &pcie_intc 2>, ++ <0 0 0 4 &pcie_intc 3>; + marvell,pcie-port = <0>; + marvell,pcie-lane = <0>; + clocks = <&gate_clk 2>; + status = "disabled"; ++ ++ pcie_intc: interrupt-controller { ++ interrupt-controller; ++ #interrupt-cells = <1>; ++ }; + }; + }; + }; +diff --git a/arch/arm/boot/dts/kirkwood-6282.dtsi b/arch/arm/boot/dts/kirkwood-6282.dtsi +index e84c54b77dea..2eea5b304f47 100644 +--- a/arch/arm/boot/dts/kirkwood-6282.dtsi ++++ b/arch/arm/boot/dts/kirkwood-6282.dtsi +@@ -30,12 +30,22 @@ pcie0: pcie@1,0 { + ranges = <0x82000000 0 0 0x82000000 0x1 0 1 0 + 0x81000000 0 0 0x81000000 0x1 0 1 0>; + bus-range = <0x00 0xff>; +- interrupt-map-mask = <0 0 0 0>; +- interrupt-map = <0 0 0 0 &intc 9>; ++ interrupt-names = "intx"; ++ interrupts = <9>; ++ interrupt-map-mask = <0 0 0 7>; ++ interrupt-map = <0 0 0 1 &pcie0_intc 0>, ++ <0 0 0 2 &pcie0_intc 1>, ++ <0 0 0 3 &pcie0_intc 2>, ++ <0 0 0 4 &pcie0_intc 3>; + marvell,pcie-port = <0>; + marvell,pcie-lane = <0>; + clocks = <&gate_clk 2>; + status = "disabled"; ++ ++ pcie0_intc: interrupt-controller { ++ interrupt-controller; ++ #interrupt-cells = <1>; ++ }; + }; + + pcie1: pcie@2,0 { +@@ -48,12 +58,22 @@ pcie1: pcie@2,0 { + ranges = <0x82000000 0 0 0x82000000 0x2 0 1 0 + 0x81000000 0 0 0x81000000 0x2 0 1 0>; + bus-range = <0x00 0xff>; +- interrupt-map-mask = <0 0 0 0>; +- interrupt-map = <0 0 0 0 &intc 10>; ++ interrupt-names = "intx"; ++ interrupts = <10>; ++ interrupt-map-mask = <0 0 0 7>; ++ interrupt-map = <0 0 0 1 &pcie1_intc 0>, ++ <0 0 0 2 &pcie1_intc 1>, ++ <0 0 0 3 &pcie1_intc 2>, ++ <0 0 0 4 &pcie1_intc 3>; + marvell,pcie-port = <1>; + marvell,pcie-lane = <0>; + clocks = <&gate_clk 18>; + status = "disabled"; ++ ++ pcie1_intc: interrupt-controller { ++ interrupt-controller; ++ #interrupt-cells = <1>; ++ }; + }; + }; + }; +diff --git a/arch/arm/boot/dts/kirkwood-98dx4122.dtsi b/arch/arm/boot/dts/kirkwood-98dx4122.dtsi +index 299c147298c3..070bc13242b8 100644 +--- a/arch/arm/boot/dts/kirkwood-98dx4122.dtsi ++++ b/arch/arm/boot/dts/kirkwood-98dx4122.dtsi +@@ -26,12 +26,22 @@ pcie0: pcie@1,0 { + ranges = <0x82000000 0 0 0x82000000 0x1 0 1 0 + 0x81000000 0 0 0x81000000 0x1 0 1 0>; + bus-range = <0x00 0xff>; +- interrupt-map-mask = <0 0 0 0>; +- interrupt-map = <0 0 0 0 &intc 9>; ++ interrupt-names = "intx"; ++ interrupts = <9>; ++ interrupt-map-mask = <0 0 0 7>; ++ interrupt-map = <0 0 0 1 &pcie_intc 0>, ++ <0 0 0 2 &pcie_intc 1>, ++ <0 0 0 3 &pcie_intc 2>, ++ <0 0 0 4 &pcie_intc 3>; + marvell,pcie-port = <0>; + marvell,pcie-lane = <0>; + clocks = <&gate_clk 2>; + status = "disabled"; ++ ++ pcie_intc: interrupt-controller { ++ interrupt-controller; ++ #interrupt-cells = <1>; ++ }; + }; + }; + }; +-- +2.34.1 + diff --git a/pkgs/patches-linux-5.15/0036-ARM-dts-dove-Add-definitions-for-PCIe-legacy-INTx-in.patch b/pkgs/patches-linux-5.15/0036-ARM-dts-dove-Add-definitions-for-PCIe-legacy-INTx-in.patch new file mode 100644 index 0000000..fd511e5 --- /dev/null +++ b/pkgs/patches-linux-5.15/0036-ARM-dts-dove-Add-definitions-for-PCIe-legacy-INTx-in.patch @@ -0,0 +1,63 @@ +From bec69dde1c0cbabc5a35d32af0ff3d6dbb4e4fec Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org> +Date: Mon, 27 Jun 2022 20:17:27 +0200 +Subject: [PATCH 36/90] ARM: dts: dove: Add definitions for PCIe legacy INTx + interrupts +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Signed-off-by: Pali Rohár <pali@kernel.org> +--- + arch/arm/boot/dts/dove.dtsi | 28 ++++++++++++++++++++++++---- + 1 file changed, 24 insertions(+), 4 deletions(-) + +diff --git a/arch/arm/boot/dts/dove.dtsi b/arch/arm/boot/dts/dove.dtsi +index 89e0bdaf3a85..96ba47c061a7 100644 +--- a/arch/arm/boot/dts/dove.dtsi ++++ b/arch/arm/boot/dts/dove.dtsi +@@ -122,8 +122,18 @@ pcie0: pcie@1 { + bus-range = <0x00 0xff>; + + #interrupt-cells = <1>; +- interrupt-map-mask = <0 0 0 0>; +- interrupt-map = <0 0 0 0 &intc 16>; ++ interrupt-names = "intx"; ++ interrupts = <16>; ++ interrupt-map-mask = <0 0 0 7>; ++ interrupt-map = <0 0 0 1 &pcie0_intc 0>, ++ <0 0 0 2 &pcie0_intc 1>, ++ <0 0 0 3 &pcie0_intc 2>, ++ <0 0 0 4 &pcie0_intc 3>; ++ ++ pcie0_intc: interrupt-controller { ++ interrupt-controller; ++ #interrupt-cells = <1>; ++ }; + }; + + pcie1: pcie@2 { +@@ -141,8 +151,18 @@ pcie1: pcie@2 { + bus-range = <0x00 0xff>; + + #interrupt-cells = <1>; +- interrupt-map-mask = <0 0 0 0>; +- interrupt-map = <0 0 0 0 &intc 18>; ++ interrupt-names = "intx"; ++ interrupts = <18>; ++ interrupt-map-mask = <0 0 0 7>; ++ interrupt-map = <0 0 0 1 &pcie1_intc 0>, ++ <0 0 0 2 &pcie1_intc 1>, ++ <0 0 0 3 &pcie1_intc 2>, ++ <0 0 0 4 &pcie1_intc 3>; ++ ++ pcie1_intc: interrupt-controller { ++ interrupt-controller; ++ #interrupt-cells = <1>; ++ }; + }; + }; + +-- +2.34.1 + diff --git a/pkgs/patches-linux-5.15/0037-ARM-dts-armada-370.dtsi-Add-definitions-for-PCIe-leg.patch b/pkgs/patches-linux-5.15/0037-ARM-dts-armada-370.dtsi-Add-definitions-for-PCIe-leg.patch new file mode 100644 index 0000000..02d4717 --- /dev/null +++ b/pkgs/patches-linux-5.15/0037-ARM-dts-armada-370.dtsi-Add-definitions-for-PCIe-leg.patch @@ -0,0 +1,79 @@ +From 19977423f7549278ed5f4fae7f171d96c31f5817 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org> +Date: Mon, 27 Jun 2022 16:24:58 +0200 +Subject: [PATCH 37/90] ARM: dts: armada-370.dtsi: Add definitions for PCIe + legacy INTx interrupts +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Signed-off-by: Pali Rohár <pali@kernel.org> +--- + arch/arm/boot/dts/armada-370.dtsi | 28 ++++++++++++++++++++++++---- + 1 file changed, 24 insertions(+), 4 deletions(-) + +diff --git a/arch/arm/boot/dts/armada-370.dtsi b/arch/arm/boot/dts/armada-370.dtsi +index 46e6d3ed8f35..9dc928859ad3 100644 +--- a/arch/arm/boot/dts/armada-370.dtsi ++++ b/arch/arm/boot/dts/armada-370.dtsi +@@ -60,16 +60,26 @@ pcie0: pcie@1,0 { + reg = <0x0800 0 0 0 0>; + #address-cells = <3>; + #size-cells = <2>; ++ interrupt-names = "intx"; ++ interrupts-extended = <&mpic 58>; + #interrupt-cells = <1>; + ranges = <0x82000000 0 0 0x82000000 0x1 0 1 0 + 0x81000000 0 0 0x81000000 0x1 0 1 0>; + bus-range = <0x00 0xff>; +- interrupt-map-mask = <0 0 0 0>; +- interrupt-map = <0 0 0 0 &mpic 58>; ++ interrupt-map-mask = <0 0 0 7>; ++ interrupt-map = <0 0 0 1 &pcie0_intc 0>, ++ <0 0 0 2 &pcie0_intc 1>, ++ <0 0 0 3 &pcie0_intc 2>, ++ <0 0 0 4 &pcie0_intc 3>; + marvell,pcie-port = <0>; + marvell,pcie-lane = <0>; + clocks = <&gateclk 5>; + status = "disabled"; ++ ++ pcie0_intc: interrupt-controller { ++ interrupt-controller; ++ #interrupt-cells = <1>; ++ }; + }; + + pcie2: pcie@2,0 { +@@ -78,16 +88,26 @@ pcie2: pcie@2,0 { + reg = <0x1000 0 0 0 0>; + #address-cells = <3>; + #size-cells = <2>; ++ interrupt-names = "intx"; ++ interrupts-extended = <&mpic 62>; + #interrupt-cells = <1>; + ranges = <0x82000000 0 0 0x82000000 0x2 0 1 0 + 0x81000000 0 0 0x81000000 0x2 0 1 0>; + bus-range = <0x00 0xff>; +- interrupt-map-mask = <0 0 0 0>; +- interrupt-map = <0 0 0 0 &mpic 62>; ++ interrupt-map-mask = <0 0 0 7>; ++ interrupt-map = <0 0 0 1 &pcie2_intc 0>, ++ <0 0 0 2 &pcie2_intc 1>, ++ <0 0 0 3 &pcie2_intc 2>, ++ <0 0 0 4 &pcie2_intc 3>; + marvell,pcie-port = <1>; + marvell,pcie-lane = <0>; + clocks = <&gateclk 9>; + status = "disabled"; ++ ++ pcie2_intc: interrupt-controller { ++ interrupt-controller; ++ #interrupt-cells = <1>; ++ }; + }; + }; + +-- +2.34.1 + diff --git a/pkgs/patches-linux-5.15/0038-ARM-dts-armada-xp-98dx3236.dtsi-Add-definitions-for-.patch b/pkgs/patches-linux-5.15/0038-ARM-dts-armada-xp-98dx3236.dtsi-Add-definitions-for-.patch new file mode 100644 index 0000000..f2dfbae --- /dev/null +++ b/pkgs/patches-linux-5.15/0038-ARM-dts-armada-xp-98dx3236.dtsi-Add-definitions-for-.patch @@ -0,0 +1,50 @@ +From c1c851bad2eb2c04a24f80dca20bfeb69d8263f3 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org> +Date: Mon, 27 Jun 2022 17:02:23 +0200 +Subject: [PATCH 38/90] ARM: dts: armada-xp-98dx3236.dtsi: Add definitions for + PCIe legacy INTx interrupts +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Signed-off-by: Pali Rohár <pali@kernel.org> +--- + arch/arm/boot/dts/armada-xp-98dx3236.dtsi | 14 ++++++++++++-- + 1 file changed, 12 insertions(+), 2 deletions(-) + +diff --git a/arch/arm/boot/dts/armada-xp-98dx3236.dtsi b/arch/arm/boot/dts/armada-xp-98dx3236.dtsi +index 38a052a0312d..b21ffb819b1d 100644 +--- a/arch/arm/boot/dts/armada-xp-98dx3236.dtsi ++++ b/arch/arm/boot/dts/armada-xp-98dx3236.dtsi +@@ -76,16 +76,26 @@ pcie1: pcie@1,0 { + reg = <0x0800 0 0 0 0>; + #address-cells = <3>; + #size-cells = <2>; ++ interrupt-names = "intx"; ++ interrupts-extended = <&mpic 58>; + #interrupt-cells = <1>; + ranges = <0x82000000 0 0 0x82000000 0x1 0 1 0 + 0x81000000 0 0 0x81000000 0x1 0 1 0>; + bus-range = <0x00 0xff>; +- interrupt-map-mask = <0 0 0 0>; +- interrupt-map = <0 0 0 0 &mpic 58>; ++ interrupt-map-mask = <0 0 0 7>; ++ interrupt-map = <0 0 0 1 &pcie1_intc 0>, ++ <0 0 0 2 &pcie1_intc 1>, ++ <0 0 0 3 &pcie1_intc 2>, ++ <0 0 0 4 &pcie1_intc 3>; + marvell,pcie-port = <0>; + marvell,pcie-lane = <0>; + clocks = <&gateclk 5>; + status = "disabled"; ++ ++ pcie1_intc: interrupt-controller { ++ interrupt-controller; ++ #interrupt-cells = <1>; ++ }; + }; + }; + +-- +2.34.1 + diff --git a/pkgs/patches-linux-5.15/0039-ARM-dts-armada-xp-mv78230.dtsi-Add-definitions-for-P.patch b/pkgs/patches-linux-5.15/0039-ARM-dts-armada-xp-mv78230.dtsi-Add-definitions-for-P.patch new file mode 100644 index 0000000..a6a60a5 --- /dev/null +++ b/pkgs/patches-linux-5.15/0039-ARM-dts-armada-xp-mv78230.dtsi-Add-definitions-for-P.patch @@ -0,0 +1,166 @@ +From 7cf39d981b03d76923f797115dabbf8d8db9bd74 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org> +Date: Mon, 27 Jun 2022 19:26:59 +0200 +Subject: [PATCH 39/90] ARM: dts: armada-xp-mv78230.dtsi: Add definitions for + PCIe legacy INTx interrupts +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Signed-off-by: Pali Rohár <pali@kernel.org> +--- + arch/arm/boot/dts/armada-xp-mv78230.dtsi | 70 ++++++++++++++++++++---- + 1 file changed, 60 insertions(+), 10 deletions(-) + +diff --git a/arch/arm/boot/dts/armada-xp-mv78230.dtsi b/arch/arm/boot/dts/armada-xp-mv78230.dtsi +index 8558bf6bb54c..bf9360f41e0a 100644 +--- a/arch/arm/boot/dts/armada-xp-mv78230.dtsi ++++ b/arch/arm/boot/dts/armada-xp-mv78230.dtsi +@@ -83,16 +83,26 @@ pcie1: pcie@1,0 { + reg = <0x0800 0 0 0 0>; + #address-cells = <3>; + #size-cells = <2>; ++ interrupt-names = "intx"; ++ interrupts-extended = <&mpic 58>; + #interrupt-cells = <1>; + ranges = <0x82000000 0 0 0x82000000 0x1 0 1 0 + 0x81000000 0 0 0x81000000 0x1 0 1 0>; + bus-range = <0x00 0xff>; +- interrupt-map-mask = <0 0 0 0>; +- interrupt-map = <0 0 0 0 &mpic 58>; ++ interrupt-map-mask = <0 0 0 7>; ++ interrupt-map = <0 0 0 1 &pcie1_intc 0>, ++ <0 0 0 2 &pcie1_intc 1>, ++ <0 0 0 3 &pcie1_intc 2>, ++ <0 0 0 4 &pcie1_intc 3>; + marvell,pcie-port = <0>; + marvell,pcie-lane = <0>; + clocks = <&gateclk 5>; + status = "disabled"; ++ ++ pcie1_intc: interrupt-controller { ++ interrupt-controller; ++ #interrupt-cells = <1>; ++ }; + }; + + pcie2: pcie@2,0 { +@@ -101,16 +111,26 @@ pcie2: pcie@2,0 { + reg = <0x1000 0 0 0 0>; + #address-cells = <3>; + #size-cells = <2>; ++ interrupt-names = "intx"; ++ interrupts-extended = <&mpic 59>; + #interrupt-cells = <1>; + ranges = <0x82000000 0 0 0x82000000 0x2 0 1 0 + 0x81000000 0 0 0x81000000 0x2 0 1 0>; + bus-range = <0x00 0xff>; +- interrupt-map-mask = <0 0 0 0>; +- interrupt-map = <0 0 0 0 &mpic 59>; ++ interrupt-map-mask = <0 0 0 7>; ++ interrupt-map = <0 0 0 1 &pcie2_intc 0>, ++ <0 0 0 2 &pcie2_intc 1>, ++ <0 0 0 3 &pcie2_intc 2>, ++ <0 0 0 4 &pcie2_intc 3>; + marvell,pcie-port = <0>; + marvell,pcie-lane = <1>; + clocks = <&gateclk 6>; + status = "disabled"; ++ ++ pcie2_intc: interrupt-controller { ++ interrupt-controller; ++ #interrupt-cells = <1>; ++ }; + }; + + pcie3: pcie@3,0 { +@@ -119,16 +139,26 @@ pcie3: pcie@3,0 { + reg = <0x1800 0 0 0 0>; + #address-cells = <3>; + #size-cells = <2>; ++ interrupt-names = "intx"; ++ interrupts-extended = <&mpic 60>; + #interrupt-cells = <1>; + ranges = <0x82000000 0 0 0x82000000 0x3 0 1 0 + 0x81000000 0 0 0x81000000 0x3 0 1 0>; + bus-range = <0x00 0xff>; +- interrupt-map-mask = <0 0 0 0>; +- interrupt-map = <0 0 0 0 &mpic 60>; ++ interrupt-map-mask = <0 0 0 7>; ++ interrupt-map = <0 0 0 1 &pcie3_intc 0>, ++ <0 0 0 2 &pcie3_intc 1>, ++ <0 0 0 3 &pcie3_intc 2>, ++ <0 0 0 4 &pcie3_intc 3>; + marvell,pcie-port = <0>; + marvell,pcie-lane = <2>; + clocks = <&gateclk 7>; + status = "disabled"; ++ ++ pcie3_intc: interrupt-controller { ++ interrupt-controller; ++ #interrupt-cells = <1>; ++ }; + }; + + pcie4: pcie@4,0 { +@@ -137,16 +167,26 @@ pcie4: pcie@4,0 { + reg = <0x2000 0 0 0 0>; + #address-cells = <3>; + #size-cells = <2>; ++ interrupt-names = "intx"; ++ interrupts-extended = <&mpic 61>; + #interrupt-cells = <1>; + ranges = <0x82000000 0 0 0x82000000 0x4 0 1 0 + 0x81000000 0 0 0x81000000 0x4 0 1 0>; + bus-range = <0x00 0xff>; +- interrupt-map-mask = <0 0 0 0>; +- interrupt-map = <0 0 0 0 &mpic 61>; ++ interrupt-map-mask = <0 0 0 7>; ++ interrupt-map = <0 0 0 1 &pcie4_intc 0>, ++ <0 0 0 2 &pcie4_intc 1>, ++ <0 0 0 3 &pcie4_intc 2>, ++ <0 0 0 4 &pcie4_intc 3>; + marvell,pcie-port = <0>; + marvell,pcie-lane = <3>; + clocks = <&gateclk 8>; + status = "disabled"; ++ ++ pcie4_intc: interrupt-controller { ++ interrupt-controller; ++ #interrupt-cells = <1>; ++ }; + }; + + pcie5: pcie@5,0 { +@@ -155,16 +195,26 @@ pcie5: pcie@5,0 { + reg = <0x2800 0 0 0 0>; + #address-cells = <3>; + #size-cells = <2>; ++ interrupt-names = "intx"; ++ interrupts-extended = <&mpic 62>; + #interrupt-cells = <1>; + ranges = <0x82000000 0 0 0x82000000 0x5 0 1 0 + 0x81000000 0 0 0x81000000 0x5 0 1 0>; + bus-range = <0x00 0xff>; +- interrupt-map-mask = <0 0 0 0>; +- interrupt-map = <0 0 0 0 &mpic 62>; ++ interrupt-map-mask = <0 0 0 7>; ++ interrupt-map = <0 0 0 1 &pcie5_intc 0>, ++ <0 0 0 2 &pcie5_intc 1>, ++ <0 0 0 3 &pcie5_intc 2>, ++ <0 0 0 4 &pcie5_intc 3>; + marvell,pcie-port = <1>; + marvell,pcie-lane = <0>; + clocks = <&gateclk 9>; + status = "disabled"; ++ ++ pcie5_intc: interrupt-controller { ++ interrupt-controller; ++ #interrupt-cells = <1>; ++ }; + }; + }; + +-- +2.34.1 + diff --git a/pkgs/patches-linux-5.15/0040-ARM-dts-armada-xp-mv78260.dtsi-Add-definitions-for-P.patch b/pkgs/patches-linux-5.15/0040-ARM-dts-armada-xp-mv78260.dtsi-Add-definitions-for-P.patch new file mode 100644 index 0000000..9ac3d91 --- /dev/null +++ b/pkgs/patches-linux-5.15/0040-ARM-dts-armada-xp-mv78260.dtsi-Add-definitions-for-P.patch @@ -0,0 +1,282 @@ +From 4c45fc119653486c76c2931cd4cfb05c4770f1e8 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org> +Date: Mon, 27 Jun 2022 19:54:06 +0200 +Subject: [PATCH 40/90] ARM: dts: armada-xp-mv78260.dtsi: Add definitions for + PCIe legacy INTx interrupts +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Signed-off-by: Pali Rohár <pali@kernel.org> +--- + arch/arm/boot/dts/armada-xp-mv78260.dtsi | 126 +++++++++++++++++++---- + 1 file changed, 108 insertions(+), 18 deletions(-) + +diff --git a/arch/arm/boot/dts/armada-xp-mv78260.dtsi b/arch/arm/boot/dts/armada-xp-mv78260.dtsi +index 2d85fe8ac327..0714af52e607 100644 +--- a/arch/arm/boot/dts/armada-xp-mv78260.dtsi ++++ b/arch/arm/boot/dts/armada-xp-mv78260.dtsi +@@ -98,16 +98,26 @@ pcie1: pcie@1,0 { + reg = <0x0800 0 0 0 0>; + #address-cells = <3>; + #size-cells = <2>; ++ interrupt-names = "intx"; ++ interrupts-extended = <&mpic 58>; + #interrupt-cells = <1>; + ranges = <0x82000000 0 0 0x82000000 0x1 0 1 0 + 0x81000000 0 0 0x81000000 0x1 0 1 0>; + bus-range = <0x00 0xff>; +- interrupt-map-mask = <0 0 0 0>; +- interrupt-map = <0 0 0 0 &mpic 58>; ++ interrupt-map-mask = <0 0 0 7>; ++ interrupt-map = <0 0 0 1 &pcie1_intc 0>, ++ <0 0 0 2 &pcie1_intc 1>, ++ <0 0 0 3 &pcie1_intc 2>, ++ <0 0 0 4 &pcie1_intc 3>; + marvell,pcie-port = <0>; + marvell,pcie-lane = <0>; + clocks = <&gateclk 5>; + status = "disabled"; ++ ++ pcie1_intc: interrupt-controller { ++ interrupt-controller; ++ #interrupt-cells = <1>; ++ }; + }; + + pcie2: pcie@2,0 { +@@ -116,16 +126,26 @@ pcie2: pcie@2,0 { + reg = <0x1000 0 0 0 0>; + #address-cells = <3>; + #size-cells = <2>; ++ interrupt-names = "intx"; ++ interrupts-extended = <&mpic 59>; + #interrupt-cells = <1>; + ranges = <0x82000000 0 0 0x82000000 0x2 0 1 0 + 0x81000000 0 0 0x81000000 0x2 0 1 0>; + bus-range = <0x00 0xff>; +- interrupt-map-mask = <0 0 0 0>; +- interrupt-map = <0 0 0 0 &mpic 59>; ++ interrupt-map-mask = <0 0 0 7>; ++ interrupt-map = <0 0 0 1 &pcie2_intc 0>, ++ <0 0 0 2 &pcie2_intc 1>, ++ <0 0 0 3 &pcie2_intc 2>, ++ <0 0 0 4 &pcie2_intc 3>; + marvell,pcie-port = <0>; + marvell,pcie-lane = <1>; + clocks = <&gateclk 6>; + status = "disabled"; ++ ++ pcie2_intc: interrupt-controller { ++ interrupt-controller; ++ #interrupt-cells = <1>; ++ }; + }; + + pcie3: pcie@3,0 { +@@ -134,16 +154,26 @@ pcie3: pcie@3,0 { + reg = <0x1800 0 0 0 0>; + #address-cells = <3>; + #size-cells = <2>; ++ interrupt-names = "intx"; ++ interrupts-extended = <&mpic 60>; + #interrupt-cells = <1>; + ranges = <0x82000000 0 0 0x82000000 0x3 0 1 0 + 0x81000000 0 0 0x81000000 0x3 0 1 0>; + bus-range = <0x00 0xff>; +- interrupt-map-mask = <0 0 0 0>; +- interrupt-map = <0 0 0 0 &mpic 60>; ++ interrupt-map-mask = <0 0 0 7>; ++ interrupt-map = <0 0 0 1 &pcie3_intc 0>, ++ <0 0 0 2 &pcie3_intc 1>, ++ <0 0 0 3 &pcie3_intc 2>, ++ <0 0 0 4 &pcie3_intc 3>; + marvell,pcie-port = <0>; + marvell,pcie-lane = <2>; + clocks = <&gateclk 7>; + status = "disabled"; ++ ++ pcie3_intc: interrupt-controller { ++ interrupt-controller; ++ #interrupt-cells = <1>; ++ }; + }; + + pcie4: pcie@4,0 { +@@ -152,16 +182,26 @@ pcie4: pcie@4,0 { + reg = <0x2000 0 0 0 0>; + #address-cells = <3>; + #size-cells = <2>; ++ interrupt-names = "intx"; ++ interrupts-extended = <&mpic 61>; + #interrupt-cells = <1>; + ranges = <0x82000000 0 0 0x82000000 0x4 0 1 0 + 0x81000000 0 0 0x81000000 0x4 0 1 0>; + bus-range = <0x00 0xff>; +- interrupt-map-mask = <0 0 0 0>; +- interrupt-map = <0 0 0 0 &mpic 61>; ++ interrupt-map-mask = <0 0 0 7>; ++ interrupt-map = <0 0 0 1 &pcie4_intc 0>, ++ <0 0 0 2 &pcie4_intc 1>, ++ <0 0 0 3 &pcie4_intc 2>, ++ <0 0 0 4 &pcie4_intc 3>; + marvell,pcie-port = <0>; + marvell,pcie-lane = <3>; + clocks = <&gateclk 8>; + status = "disabled"; ++ ++ pcie4_intc: interrupt-controller { ++ interrupt-controller; ++ #interrupt-cells = <1>; ++ }; + }; + + pcie5: pcie@5,0 { +@@ -170,16 +210,26 @@ pcie5: pcie@5,0 { + reg = <0x2800 0 0 0 0>; + #address-cells = <3>; + #size-cells = <2>; ++ interrupt-names = "intx"; ++ interrupts-extended = <&mpic 62>; + #interrupt-cells = <1>; + ranges = <0x82000000 0 0 0x82000000 0x5 0 1 0 + 0x81000000 0 0 0x81000000 0x5 0 1 0>; + bus-range = <0x00 0xff>; +- interrupt-map-mask = <0 0 0 0>; +- interrupt-map = <0 0 0 0 &mpic 62>; ++ interrupt-map-mask = <0 0 0 7>; ++ interrupt-map = <0 0 0 1 &pcie5_intc 0>, ++ <0 0 0 2 &pcie5_intc 1>, ++ <0 0 0 3 &pcie5_intc 2>, ++ <0 0 0 4 &pcie5_intc 3>; + marvell,pcie-port = <1>; + marvell,pcie-lane = <0>; + clocks = <&gateclk 9>; + status = "disabled"; ++ ++ pcie5_intc: interrupt-controller { ++ interrupt-controller; ++ #interrupt-cells = <1>; ++ }; + }; + + pcie6: pcie@6,0 { +@@ -188,16 +238,26 @@ pcie6: pcie@6,0 { + reg = <0x3000 0 0 0 0>; + #address-cells = <3>; + #size-cells = <2>; ++ interrupt-names = "intx"; ++ interrupts-extended = <&mpic 63>; + #interrupt-cells = <1>; + ranges = <0x82000000 0 0 0x82000000 0x6 0 1 0 + 0x81000000 0 0 0x81000000 0x6 0 1 0>; + bus-range = <0x00 0xff>; +- interrupt-map-mask = <0 0 0 0>; +- interrupt-map = <0 0 0 0 &mpic 63>; ++ interrupt-map-mask = <0 0 0 7>; ++ interrupt-map = <0 0 0 1 &pcie6_intc 0>, ++ <0 0 0 2 &pcie6_intc 1>, ++ <0 0 0 3 &pcie6_intc 2>, ++ <0 0 0 4 &pcie6_intc 3>; + marvell,pcie-port = <1>; + marvell,pcie-lane = <1>; + clocks = <&gateclk 10>; + status = "disabled"; ++ ++ pcie6_intc: interrupt-controller { ++ interrupt-controller; ++ #interrupt-cells = <1>; ++ }; + }; + + pcie7: pcie@7,0 { +@@ -206,16 +266,26 @@ pcie7: pcie@7,0 { + reg = <0x3800 0 0 0 0>; + #address-cells = <3>; + #size-cells = <2>; ++ interrupt-names = "intx"; ++ interrupts-extended = <&mpic 64>; + #interrupt-cells = <1>; + ranges = <0x82000000 0 0 0x82000000 0x7 0 1 0 + 0x81000000 0 0 0x81000000 0x7 0 1 0>; + bus-range = <0x00 0xff>; +- interrupt-map-mask = <0 0 0 0>; +- interrupt-map = <0 0 0 0 &mpic 64>; ++ interrupt-map-mask = <0 0 0 7>; ++ interrupt-map = <0 0 0 1 &pcie7_intc 0>, ++ <0 0 0 2 &pcie7_intc 1>, ++ <0 0 0 3 &pcie7_intc 2>, ++ <0 0 0 4 &pcie7_intc 3>; + marvell,pcie-port = <1>; + marvell,pcie-lane = <2>; + clocks = <&gateclk 11>; + status = "disabled"; ++ ++ pcie7_intc: interrupt-controller { ++ interrupt-controller; ++ #interrupt-cells = <1>; ++ }; + }; + + pcie8: pcie@8,0 { +@@ -224,16 +294,26 @@ pcie8: pcie@8,0 { + reg = <0x4000 0 0 0 0>; + #address-cells = <3>; + #size-cells = <2>; ++ interrupt-names = "intx"; ++ interrupts-extended = <&mpic 65>; + #interrupt-cells = <1>; + ranges = <0x82000000 0 0 0x82000000 0x8 0 1 0 + 0x81000000 0 0 0x81000000 0x8 0 1 0>; + bus-range = <0x00 0xff>; +- interrupt-map-mask = <0 0 0 0>; +- interrupt-map = <0 0 0 0 &mpic 65>; ++ interrupt-map-mask = <0 0 0 7>; ++ interrupt-map = <0 0 0 1 &pcie8_intc 0>, ++ <0 0 0 2 &pcie8_intc 1>, ++ <0 0 0 3 &pcie8_intc 2>, ++ <0 0 0 4 &pcie8_intc 3>; + marvell,pcie-port = <1>; + marvell,pcie-lane = <3>; + clocks = <&gateclk 12>; + status = "disabled"; ++ ++ pcie8_intc: interrupt-controller { ++ interrupt-controller; ++ #interrupt-cells = <1>; ++ }; + }; + + pcie9: pcie@9,0 { +@@ -242,16 +322,26 @@ pcie9: pcie@9,0 { + reg = <0x4800 0 0 0 0>; + #address-cells = <3>; + #size-cells = <2>; ++ interrupt-names = "intx"; ++ interrupts-extended = <&mpic 99>; + #interrupt-cells = <1>; + ranges = <0x82000000 0 0 0x82000000 0x9 0 1 0 + 0x81000000 0 0 0x81000000 0x9 0 1 0>; + bus-range = <0x00 0xff>; +- interrupt-map-mask = <0 0 0 0>; +- interrupt-map = <0 0 0 0 &mpic 99>; ++ interrupt-map-mask = <0 0 0 7>; ++ interrupt-map = <0 0 0 1 &pcie9_intc 0>, ++ <0 0 0 2 &pcie9_intc 1>, ++ <0 0 0 3 &pcie9_intc 2>, ++ <0 0 0 4 &pcie9_intc 3>; + marvell,pcie-port = <2>; + marvell,pcie-lane = <0>; + clocks = <&gateclk 26>; + status = "disabled"; ++ ++ pcie9_intc: interrupt-controller { ++ interrupt-controller; ++ #interrupt-cells = <1>; ++ }; + }; + }; + +-- +2.34.1 + diff --git a/pkgs/patches-linux-5.15/0041-ARM-dts-armada-xp-mv78460.dtsi-Add-definitions-for-P.patch b/pkgs/patches-linux-5.15/0041-ARM-dts-armada-xp-mv78460.dtsi-Add-definitions-for-P.patch new file mode 100644 index 0000000..e987e42 --- /dev/null +++ b/pkgs/patches-linux-5.15/0041-ARM-dts-armada-xp-mv78460.dtsi-Add-definitions-for-P.patch @@ -0,0 +1,311 @@ +From 7d19ddf1d053706976824ccc9153c2bd7caf4f94 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org> +Date: Mon, 27 Jun 2022 19:58:06 +0200 +Subject: [PATCH 41/90] ARM: dts: armada-xp-mv78460.dtsi: Add definitions for + PCIe legacy INTx interrupts +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Signed-off-by: Pali Rohár <pali@kernel.org> +--- + arch/arm/boot/dts/armada-xp-mv78460.dtsi | 140 +++++++++++++++++++---- + 1 file changed, 120 insertions(+), 20 deletions(-) + +diff --git a/arch/arm/boot/dts/armada-xp-mv78460.dtsi b/arch/arm/boot/dts/armada-xp-mv78460.dtsi +index 230a3fd36b30..16185edf9aa5 100644 +--- a/arch/arm/boot/dts/armada-xp-mv78460.dtsi ++++ b/arch/arm/boot/dts/armada-xp-mv78460.dtsi +@@ -119,16 +119,26 @@ pcie1: pcie@1,0 { + reg = <0x0800 0 0 0 0>; + #address-cells = <3>; + #size-cells = <2>; ++ interrupt-names = "intx"; ++ interrupts-extended = <&mpic 58>; + #interrupt-cells = <1>; + ranges = <0x82000000 0 0 0x82000000 0x1 0 1 0 + 0x81000000 0 0 0x81000000 0x1 0 1 0>; + bus-range = <0x00 0xff>; +- interrupt-map-mask = <0 0 0 0>; +- interrupt-map = <0 0 0 0 &mpic 58>; ++ interrupt-map-mask = <0 0 0 7>; ++ interrupt-map = <0 0 0 1 &pcie1_intc 0>, ++ <0 0 0 2 &pcie1_intc 1>, ++ <0 0 0 3 &pcie1_intc 2>, ++ <0 0 0 4 &pcie1_intc 3>; + marvell,pcie-port = <0>; + marvell,pcie-lane = <0>; + clocks = <&gateclk 5>; + status = "disabled"; ++ ++ pcie1_intc: interrupt-controller { ++ interrupt-controller; ++ #interrupt-cells = <1>; ++ }; + }; + + pcie2: pcie@2,0 { +@@ -137,16 +147,26 @@ pcie2: pcie@2,0 { + reg = <0x1000 0 0 0 0>; + #address-cells = <3>; + #size-cells = <2>; ++ interrupt-names = "intx"; ++ interrupts-extended = <&mpic 59>; + #interrupt-cells = <1>; + ranges = <0x82000000 0 0 0x82000000 0x2 0 1 0 + 0x81000000 0 0 0x81000000 0x2 0 1 0>; + bus-range = <0x00 0xff>; +- interrupt-map-mask = <0 0 0 0>; +- interrupt-map = <0 0 0 0 &mpic 59>; ++ interrupt-map-mask = <0 0 0 7>; ++ interrupt-map = <0 0 0 1 &pcie2_intc 0>, ++ <0 0 0 2 &pcie2_intc 1>, ++ <0 0 0 3 &pcie2_intc 2>, ++ <0 0 0 4 &pcie2_intc 3>; + marvell,pcie-port = <0>; + marvell,pcie-lane = <1>; + clocks = <&gateclk 6>; + status = "disabled"; ++ ++ pcie2_intc: interrupt-controller { ++ interrupt-controller; ++ #interrupt-cells = <1>; ++ }; + }; + + pcie3: pcie@3,0 { +@@ -155,16 +175,26 @@ pcie3: pcie@3,0 { + reg = <0x1800 0 0 0 0>; + #address-cells = <3>; + #size-cells = <2>; ++ interrupt-names = "intx"; ++ interrupts-extended = <&mpic 60>; + #interrupt-cells = <1>; + ranges = <0x82000000 0 0 0x82000000 0x3 0 1 0 + 0x81000000 0 0 0x81000000 0x3 0 1 0>; + bus-range = <0x00 0xff>; +- interrupt-map-mask = <0 0 0 0>; +- interrupt-map = <0 0 0 0 &mpic 60>; ++ interrupt-map-mask = <0 0 0 7>; ++ interrupt-map = <0 0 0 1 &pcie3_intc 0>, ++ <0 0 0 2 &pcie3_intc 1>, ++ <0 0 0 3 &pcie3_intc 2>, ++ <0 0 0 4 &pcie3_intc 3>; + marvell,pcie-port = <0>; + marvell,pcie-lane = <2>; + clocks = <&gateclk 7>; + status = "disabled"; ++ ++ pcie3_intc: interrupt-controller { ++ interrupt-controller; ++ #interrupt-cells = <1>; ++ }; + }; + + pcie4: pcie@4,0 { +@@ -173,16 +203,26 @@ pcie4: pcie@4,0 { + reg = <0x2000 0 0 0 0>; + #address-cells = <3>; + #size-cells = <2>; ++ interrupt-names = "intx"; ++ interrupts-extended = <&mpic 61>; + #interrupt-cells = <1>; + ranges = <0x82000000 0 0 0x82000000 0x4 0 1 0 + 0x81000000 0 0 0x81000000 0x4 0 1 0>; + bus-range = <0x00 0xff>; +- interrupt-map-mask = <0 0 0 0>; +- interrupt-map = <0 0 0 0 &mpic 61>; ++ interrupt-map-mask = <0 0 0 7>; ++ interrupt-map = <0 0 0 1 &pcie4_intc 0>, ++ <0 0 0 2 &pcie4_intc 1>, ++ <0 0 0 3 &pcie4_intc 2>, ++ <0 0 0 4 &pcie4_intc 3>; + marvell,pcie-port = <0>; + marvell,pcie-lane = <3>; + clocks = <&gateclk 8>; + status = "disabled"; ++ ++ pcie4_intc: interrupt-controller { ++ interrupt-controller; ++ #interrupt-cells = <1>; ++ }; + }; + + pcie5: pcie@5,0 { +@@ -191,16 +231,26 @@ pcie5: pcie@5,0 { + reg = <0x2800 0 0 0 0>; + #address-cells = <3>; + #size-cells = <2>; ++ interrupt-names = "intx"; ++ interrupts-extended = <&mpic 62>; + #interrupt-cells = <1>; + ranges = <0x82000000 0 0 0x82000000 0x5 0 1 0 + 0x81000000 0 0 0x81000000 0x5 0 1 0>; + bus-range = <0x00 0xff>; +- interrupt-map-mask = <0 0 0 0>; +- interrupt-map = <0 0 0 0 &mpic 62>; ++ interrupt-map-mask = <0 0 0 7>; ++ interrupt-map = <0 0 0 1 &pcie5_intc 0>, ++ <0 0 0 2 &pcie5_intc 1>, ++ <0 0 0 3 &pcie5_intc 2>, ++ <0 0 0 4 &pcie5_intc 3>; + marvell,pcie-port = <1>; + marvell,pcie-lane = <0>; + clocks = <&gateclk 9>; + status = "disabled"; ++ ++ pcie5_intc: interrupt-controller { ++ interrupt-controller; ++ #interrupt-cells = <1>; ++ }; + }; + + pcie6: pcie@6,0 { +@@ -209,16 +259,26 @@ pcie6: pcie@6,0 { + reg = <0x3000 0 0 0 0>; + #address-cells = <3>; + #size-cells = <2>; ++ interrupt-names = "intx"; ++ interrupts-extended = <&mpic 63>; + #interrupt-cells = <1>; + ranges = <0x82000000 0 0 0x82000000 0x6 0 1 0 + 0x81000000 0 0 0x81000000 0x6 0 1 0>; + bus-range = <0x00 0xff>; +- interrupt-map-mask = <0 0 0 0>; +- interrupt-map = <0 0 0 0 &mpic 63>; ++ interrupt-map-mask = <0 0 0 7>; ++ interrupt-map = <0 0 0 1 &pcie6_intc 0>, ++ <0 0 0 2 &pcie6_intc 1>, ++ <0 0 0 3 &pcie6_intc 2>, ++ <0 0 0 4 &pcie6_intc 3>; + marvell,pcie-port = <1>; + marvell,pcie-lane = <1>; + clocks = <&gateclk 10>; + status = "disabled"; ++ ++ pcie6_intc: interrupt-controller { ++ interrupt-controller; ++ #interrupt-cells = <1>; ++ }; + }; + + pcie7: pcie@7,0 { +@@ -227,16 +287,26 @@ pcie7: pcie@7,0 { + reg = <0x3800 0 0 0 0>; + #address-cells = <3>; + #size-cells = <2>; ++ interrupt-names = "intx"; ++ interrupts-extended = <&mpic 64>; + #interrupt-cells = <1>; + ranges = <0x82000000 0 0 0x82000000 0x7 0 1 0 + 0x81000000 0 0 0x81000000 0x7 0 1 0>; + bus-range = <0x00 0xff>; +- interrupt-map-mask = <0 0 0 0>; +- interrupt-map = <0 0 0 0 &mpic 64>; ++ interrupt-map-mask = <0 0 0 7>; ++ interrupt-map = <0 0 0 1 &pcie7_intc 0>, ++ <0 0 0 2 &pcie7_intc 1>, ++ <0 0 0 3 &pcie7_intc 2>, ++ <0 0 0 4 &pcie7_intc 3>; + marvell,pcie-port = <1>; + marvell,pcie-lane = <2>; + clocks = <&gateclk 11>; + status = "disabled"; ++ ++ pcie7_intc: interrupt-controller { ++ interrupt-controller; ++ #interrupt-cells = <1>; ++ }; + }; + + pcie8: pcie@8,0 { +@@ -245,16 +315,26 @@ pcie8: pcie@8,0 { + reg = <0x4000 0 0 0 0>; + #address-cells = <3>; + #size-cells = <2>; ++ interrupt-names = "intx"; ++ interrupts-extended = <&mpic 65>; + #interrupt-cells = <1>; + ranges = <0x82000000 0 0 0x82000000 0x8 0 1 0 + 0x81000000 0 0 0x81000000 0x8 0 1 0>; + bus-range = <0x00 0xff>; +- interrupt-map-mask = <0 0 0 0>; +- interrupt-map = <0 0 0 0 &mpic 65>; ++ interrupt-map-mask = <0 0 0 7>; ++ interrupt-map = <0 0 0 1 &pcie8_intc 0>, ++ <0 0 0 2 &pcie8_intc 1>, ++ <0 0 0 3 &pcie8_intc 2>, ++ <0 0 0 4 &pcie8_intc 3>; + marvell,pcie-port = <1>; + marvell,pcie-lane = <3>; + clocks = <&gateclk 12>; + status = "disabled"; ++ ++ pcie8_intc: interrupt-controller { ++ interrupt-controller; ++ #interrupt-cells = <1>; ++ }; + }; + + pcie9: pcie@9,0 { +@@ -263,16 +343,26 @@ pcie9: pcie@9,0 { + reg = <0x4800 0 0 0 0>; + #address-cells = <3>; + #size-cells = <2>; ++ interrupt-names = "intx"; ++ interrupts-extended = <&mpic 99>; + #interrupt-cells = <1>; + ranges = <0x82000000 0 0 0x82000000 0x9 0 1 0 + 0x81000000 0 0 0x81000000 0x9 0 1 0>; + bus-range = <0x00 0xff>; +- interrupt-map-mask = <0 0 0 0>; +- interrupt-map = <0 0 0 0 &mpic 99>; ++ interrupt-map-mask = <0 0 0 7>; ++ interrupt-map = <0 0 0 1 &pcie9_intc 0>, ++ <0 0 0 2 &pcie9_intc 1>, ++ <0 0 0 3 &pcie9_intc 2>, ++ <0 0 0 4 &pcie9_intc 3>; + marvell,pcie-port = <2>; + marvell,pcie-lane = <0>; + clocks = <&gateclk 26>; + status = "disabled"; ++ ++ pcie9_intc: interrupt-controller { ++ interrupt-controller; ++ #interrupt-cells = <1>; ++ }; + }; + + pcie10: pcie@a,0 { +@@ -281,16 +371,26 @@ pcie10: pcie@a,0 { + reg = <0x5000 0 0 0 0>; + #address-cells = <3>; + #size-cells = <2>; ++ interrupt-names = "intx"; ++ interrupts-extended = <&mpic 103>; + #interrupt-cells = <1>; + ranges = <0x82000000 0 0 0x82000000 0xa 0 1 0 + 0x81000000 0 0 0x81000000 0xa 0 1 0>; + bus-range = <0x00 0xff>; +- interrupt-map-mask = <0 0 0 0>; +- interrupt-map = <0 0 0 0 &mpic 103>; ++ interrupt-map-mask = <0 0 0 7>; ++ interrupt-map = <0 0 0 1 &pcie10_intc 0>, ++ <0 0 0 2 &pcie10_intc 1>, ++ <0 0 0 3 &pcie10_intc 2>, ++ <0 0 0 4 &pcie10_intc 3>; + marvell,pcie-port = <3>; + marvell,pcie-lane = <0>; + clocks = <&gateclk 27>; + status = "disabled"; ++ ++ pcie10_intc: interrupt-controller { ++ interrupt-controller; ++ #interrupt-cells = <1>; ++ }; + }; + }; + +-- +2.34.1 + diff --git a/pkgs/patches-linux-5.15/0042-ARM-dts-armada-375.dtsi-Add-definitions-for-PCIe-leg.patch b/pkgs/patches-linux-5.15/0042-ARM-dts-armada-375.dtsi-Add-definitions-for-PCIe-leg.patch new file mode 100644 index 0000000..0f5ce95 --- /dev/null +++ b/pkgs/patches-linux-5.15/0042-ARM-dts-armada-375.dtsi-Add-definitions-for-PCIe-leg.patch @@ -0,0 +1,79 @@ +From d1655b563fd705b6a9bf25a37e85076716bb1c93 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org> +Date: Mon, 27 Jun 2022 16:38:11 +0200 +Subject: [PATCH 42/90] ARM: dts: armada-375.dtsi: Add definitions for PCIe + legacy INTx interrupts +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Signed-off-by: Pali Rohár <pali@kernel.org> +--- + arch/arm/boot/dts/armada-375.dtsi | 28 ++++++++++++++++++++++++---- + 1 file changed, 24 insertions(+), 4 deletions(-) + +diff --git a/arch/arm/boot/dts/armada-375.dtsi b/arch/arm/boot/dts/armada-375.dtsi +index 7f2f24a29e6c..929deaf312a5 100644 +--- a/arch/arm/boot/dts/armada-375.dtsi ++++ b/arch/arm/boot/dts/armada-375.dtsi +@@ -568,16 +568,26 @@ pcie0: pcie@1,0 { + reg = <0x0800 0 0 0 0>; + #address-cells = <3>; + #size-cells = <2>; ++ interrupt-names = "intx"; ++ interrupts-extended = <&gic GIC_SPI 29 IRQ_TYPE_LEVEL_HIGH>; + #interrupt-cells = <1>; + ranges = <0x82000000 0 0 0x82000000 0x1 0 1 0 + 0x81000000 0 0 0x81000000 0x1 0 1 0>; + bus-range = <0x00 0xff>; +- interrupt-map-mask = <0 0 0 0>; +- interrupt-map = <0 0 0 0 &gic GIC_SPI 29 IRQ_TYPE_LEVEL_HIGH>; ++ interrupt-map-mask = <0 0 0 7>; ++ interrupt-map = <0 0 0 1 &pcie0_intc 0>, ++ <0 0 0 2 &pcie0_intc 1>, ++ <0 0 0 3 &pcie0_intc 2>, ++ <0 0 0 4 &pcie0_intc 3>; + marvell,pcie-port = <0>; + marvell,pcie-lane = <0>; + clocks = <&gateclk 5>; + status = "disabled"; ++ ++ pcie0_intc: interrupt-controller { ++ interrupt-controller; ++ #interrupt-cells = <1>; ++ }; + }; + + pcie1: pcie@2,0 { +@@ -586,16 +596,26 @@ pcie1: pcie@2,0 { + reg = <0x1000 0 0 0 0>; + #address-cells = <3>; + #size-cells = <2>; ++ interrupt-names = "intx"; ++ interrupts-extended = <&gic GIC_SPI 33 IRQ_TYPE_LEVEL_HIGH>; + #interrupt-cells = <1>; + ranges = <0x82000000 0 0 0x82000000 0x2 0 1 0 + 0x81000000 0 0 0x81000000 0x2 0 1 0>; + bus-range = <0x00 0xff>; +- interrupt-map-mask = <0 0 0 0>; +- interrupt-map = <0 0 0 0 &gic GIC_SPI 33 IRQ_TYPE_LEVEL_HIGH>; ++ interrupt-map-mask = <0 0 0 7>; ++ interrupt-map = <0 0 0 1 &pcie1_intc 0>, ++ <0 0 0 2 &pcie1_intc 1>, ++ <0 0 0 3 &pcie1_intc 2>, ++ <0 0 0 4 &pcie1_intc 3>; + marvell,pcie-port = <0>; + marvell,pcie-lane = <1>; + clocks = <&gateclk 6>; + status = "disabled"; ++ ++ pcie1_intc: interrupt-controller { ++ interrupt-controller; ++ #interrupt-cells = <1>; ++ }; + }; + + }; +-- +2.34.1 + diff --git a/pkgs/patches-linux-5.15/0043-ARM-dts-armada-380.dtsi-Add-definitions-for-PCIe-leg.patch b/pkgs/patches-linux-5.15/0043-ARM-dts-armada-380.dtsi-Add-definitions-for-PCIe-leg.patch new file mode 100644 index 0000000..319396c --- /dev/null +++ b/pkgs/patches-linux-5.15/0043-ARM-dts-armada-380.dtsi-Add-definitions-for-PCIe-leg.patch @@ -0,0 +1,108 @@ +From c13a941742f4496f0a73ccc9f1fcb535548d7067 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org> +Date: Mon, 27 Jun 2022 17:39:06 +0200 +Subject: [PATCH 43/90] ARM: dts: armada-380.dtsi: Add definitions for PCIe + legacy INTx interrupts +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Signed-off-by: Pali Rohár <pali@kernel.org> +--- + arch/arm/boot/dts/armada-380.dtsi | 42 ++++++++++++++++++++++++++----- + 1 file changed, 36 insertions(+), 6 deletions(-) + +diff --git a/arch/arm/boot/dts/armada-380.dtsi b/arch/arm/boot/dts/armada-380.dtsi +index cff1269f3fbf..ce1dddb2269b 100644 +--- a/arch/arm/boot/dts/armada-380.dtsi ++++ b/arch/arm/boot/dts/armada-380.dtsi +@@ -64,16 +64,26 @@ pcie@1,0 { + reg = <0x0800 0 0 0 0>; + #address-cells = <3>; + #size-cells = <2>; ++ interrupt-names = "intx"; ++ interrupts-extended = <&gic GIC_SPI 29 IRQ_TYPE_LEVEL_HIGH>; + #interrupt-cells = <1>; + ranges = <0x82000000 0 0 0x82000000 0x1 0 1 0 + 0x81000000 0 0 0x81000000 0x1 0 1 0>; + bus-range = <0x00 0xff>; +- interrupt-map-mask = <0 0 0 0>; +- interrupt-map = <0 0 0 0 &gic GIC_SPI 29 IRQ_TYPE_LEVEL_HIGH>; ++ interrupt-map-mask = <0 0 0 7>; ++ interrupt-map = <0 0 0 1 &pcie1_intc 0>, ++ <0 0 0 2 &pcie1_intc 1>, ++ <0 0 0 3 &pcie1_intc 2>, ++ <0 0 0 4 &pcie1_intc 3>; + marvell,pcie-port = <0>; + marvell,pcie-lane = <0>; + clocks = <&gateclk 8>; + status = "disabled"; ++ ++ pcie1_intc: interrupt-controller { ++ interrupt-controller; ++ #interrupt-cells = <1>; ++ }; + }; + + /* x1 port */ +@@ -83,16 +93,26 @@ pcie@2,0 { + reg = <0x1000 0 0 0 0>; + #address-cells = <3>; + #size-cells = <2>; ++ interrupt-names = "intx"; ++ interrupts-extended = <&gic GIC_SPI 33 IRQ_TYPE_LEVEL_HIGH>; + #interrupt-cells = <1>; + ranges = <0x82000000 0 0 0x82000000 0x2 0 1 0 + 0x81000000 0 0 0x81000000 0x2 0 1 0>; + bus-range = <0x00 0xff>; +- interrupt-map-mask = <0 0 0 0>; +- interrupt-map = <0 0 0 0 &gic GIC_SPI 33 IRQ_TYPE_LEVEL_HIGH>; ++ interrupt-map-mask = <0 0 0 7>; ++ interrupt-map = <0 0 0 1 &pcie2_intc 0>, ++ <0 0 0 2 &pcie2_intc 1>, ++ <0 0 0 3 &pcie2_intc 2>, ++ <0 0 0 4 &pcie2_intc 3>; + marvell,pcie-port = <1>; + marvell,pcie-lane = <0>; + clocks = <&gateclk 5>; + status = "disabled"; ++ ++ pcie2_intc: interrupt-controller { ++ interrupt-controller; ++ #interrupt-cells = <1>; ++ }; + }; + + /* x1 port */ +@@ -102,16 +122,26 @@ pcie@3,0 { + reg = <0x1800 0 0 0 0>; + #address-cells = <3>; + #size-cells = <2>; ++ interrupt-names = "intx"; ++ interrupts-extended = <&gic GIC_SPI 70 IRQ_TYPE_LEVEL_HIGH>; + #interrupt-cells = <1>; + ranges = <0x82000000 0 0 0x82000000 0x3 0 1 0 + 0x81000000 0 0 0x81000000 0x3 0 1 0>; + bus-range = <0x00 0xff>; +- interrupt-map-mask = <0 0 0 0>; +- interrupt-map = <0 0 0 0 &gic GIC_SPI 70 IRQ_TYPE_LEVEL_HIGH>; ++ interrupt-map-mask = <0 0 0 7>; ++ interrupt-map = <0 0 0 1 &pcie3_intc 0>, ++ <0 0 0 2 &pcie3_intc 1>, ++ <0 0 0 3 &pcie3_intc 2>, ++ <0 0 0 4 &pcie3_intc 3>; + marvell,pcie-port = <2>; + marvell,pcie-lane = <0>; + clocks = <&gateclk 6>; + status = "disabled"; ++ ++ pcie3_intc: interrupt-controller { ++ interrupt-controller; ++ #interrupt-cells = <1>; ++ }; + }; + }; + }; +-- +2.34.1 + diff --git a/pkgs/patches-linux-5.15/0044-ARM-dts-armada-385.dtsi-Add-definitions-for-PCIe-leg.patch b/pkgs/patches-linux-5.15/0044-ARM-dts-armada-385.dtsi-Add-definitions-for-PCIe-leg.patch new file mode 100644 index 0000000..be96c72 --- /dev/null +++ b/pkgs/patches-linux-5.15/0044-ARM-dts-armada-385.dtsi-Add-definitions-for-PCIe-leg.patch @@ -0,0 +1,138 @@ +From 080d8811f806c957992baa43edffa2ec017be274 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org> +Date: Fri, 17 Sep 2021 14:56:58 +0200 +Subject: [PATCH 44/90] ARM: dts: armada-385.dtsi: Add definitions for PCIe + legacy INTx interrupts +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +With this change legacy INTA, INTB, INTC and INTD interrupts are reported +separately and not mixed into one Linux virq source anymore. + +Signed-off-by: Pali Rohár <pali@kernel.org> +Acked-by: Gregory CLEMENT <gregory.clement@bootlin.com> +Tested-by: Luis Mendes <luis.p.mendes@gmail.com> +--- + arch/arm/boot/dts/armada-385.dtsi | 52 ++++++++++++++++++++++++++----- + 1 file changed, 44 insertions(+), 8 deletions(-) + +diff --git a/arch/arm/boot/dts/armada-385.dtsi b/arch/arm/boot/dts/armada-385.dtsi +index f0022d10c715..83392b92dae2 100644 +--- a/arch/arm/boot/dts/armada-385.dtsi ++++ b/arch/arm/boot/dts/armada-385.dtsi +@@ -69,16 +69,25 @@ pcie1: pcie@1,0 { + reg = <0x0800 0 0 0 0>; + #address-cells = <3>; + #size-cells = <2>; ++ interrupt-names = "intx"; ++ interrupts-extended = <&gic GIC_SPI 29 IRQ_TYPE_LEVEL_HIGH>; + #interrupt-cells = <1>; + ranges = <0x82000000 0 0 0x82000000 0x1 0 1 0 + 0x81000000 0 0 0x81000000 0x1 0 1 0>; + bus-range = <0x00 0xff>; +- interrupt-map-mask = <0 0 0 0>; +- interrupt-map = <0 0 0 0 &gic GIC_SPI 29 IRQ_TYPE_LEVEL_HIGH>; ++ interrupt-map-mask = <0 0 0 7>; ++ interrupt-map = <0 0 0 1 &pcie1_intc 0>, ++ <0 0 0 2 &pcie1_intc 1>, ++ <0 0 0 3 &pcie1_intc 2>, ++ <0 0 0 4 &pcie1_intc 3>; + marvell,pcie-port = <0>; + marvell,pcie-lane = <0>; + clocks = <&gateclk 8>; + status = "disabled"; ++ pcie1_intc: interrupt-controller { ++ interrupt-controller; ++ #interrupt-cells = <1>; ++ }; + }; + + /* x1 port */ +@@ -88,16 +97,25 @@ pcie2: pcie@2,0 { + reg = <0x1000 0 0 0 0>; + #address-cells = <3>; + #size-cells = <2>; ++ interrupt-names = "intx"; ++ interrupts-extended = <&gic GIC_SPI 33 IRQ_TYPE_LEVEL_HIGH>; + #interrupt-cells = <1>; + ranges = <0x82000000 0 0 0x82000000 0x2 0 1 0 + 0x81000000 0 0 0x81000000 0x2 0 1 0>; + bus-range = <0x00 0xff>; +- interrupt-map-mask = <0 0 0 0>; +- interrupt-map = <0 0 0 0 &gic GIC_SPI 33 IRQ_TYPE_LEVEL_HIGH>; ++ interrupt-map-mask = <0 0 0 7>; ++ interrupt-map = <0 0 0 1 &pcie2_intc 0>, ++ <0 0 0 2 &pcie2_intc 1>, ++ <0 0 0 3 &pcie2_intc 2>, ++ <0 0 0 4 &pcie2_intc 3>; + marvell,pcie-port = <1>; + marvell,pcie-lane = <0>; + clocks = <&gateclk 5>; + status = "disabled"; ++ pcie2_intc: interrupt-controller { ++ interrupt-controller; ++ #interrupt-cells = <1>; ++ }; + }; + + /* x1 port */ +@@ -107,16 +125,25 @@ pcie3: pcie@3,0 { + reg = <0x1800 0 0 0 0>; + #address-cells = <3>; + #size-cells = <2>; ++ interrupt-names = "intx"; ++ interrupts-extended = <&gic GIC_SPI 70 IRQ_TYPE_LEVEL_HIGH>; + #interrupt-cells = <1>; + ranges = <0x82000000 0 0 0x82000000 0x3 0 1 0 + 0x81000000 0 0 0x81000000 0x3 0 1 0>; + bus-range = <0x00 0xff>; +- interrupt-map-mask = <0 0 0 0>; +- interrupt-map = <0 0 0 0 &gic GIC_SPI 70 IRQ_TYPE_LEVEL_HIGH>; ++ interrupt-map-mask = <0 0 0 7>; ++ interrupt-map = <0 0 0 1 &pcie3_intc 0>, ++ <0 0 0 2 &pcie3_intc 1>, ++ <0 0 0 3 &pcie3_intc 2>, ++ <0 0 0 4 &pcie3_intc 3>; + marvell,pcie-port = <2>; + marvell,pcie-lane = <0>; + clocks = <&gateclk 6>; + status = "disabled"; ++ pcie3_intc: interrupt-controller { ++ interrupt-controller; ++ #interrupt-cells = <1>; ++ }; + }; + + /* +@@ -129,16 +156,25 @@ pcie4: pcie@4,0 { + reg = <0x2000 0 0 0 0>; + #address-cells = <3>; + #size-cells = <2>; ++ interrupt-names = "intx"; ++ interrupts-extended = <&gic GIC_SPI 71 IRQ_TYPE_LEVEL_HIGH>; + #interrupt-cells = <1>; + ranges = <0x82000000 0 0 0x82000000 0x4 0 1 0 + 0x81000000 0 0 0x81000000 0x4 0 1 0>; + bus-range = <0x00 0xff>; +- interrupt-map-mask = <0 0 0 0>; +- interrupt-map = <0 0 0 0 &gic GIC_SPI 71 IRQ_TYPE_LEVEL_HIGH>; ++ interrupt-map-mask = <0 0 0 7>; ++ interrupt-map = <0 0 0 1 &pcie4_intc 0>, ++ <0 0 0 2 &pcie4_intc 1>, ++ <0 0 0 3 &pcie4_intc 2>, ++ <0 0 0 4 &pcie4_intc 3>; + marvell,pcie-port = <3>; + marvell,pcie-lane = <0>; + clocks = <&gateclk 7>; + status = "disabled"; ++ pcie4_intc: interrupt-controller { ++ interrupt-controller; ++ #interrupt-cells = <1>; ++ }; + }; + }; + }; +-- +2.34.1 + diff --git a/pkgs/patches-linux-5.15/0045-ARM-dts-armada-39x.dtsi-Add-definitions-for-PCIe-leg.patch b/pkgs/patches-linux-5.15/0045-ARM-dts-armada-39x.dtsi-Add-definitions-for-PCIe-leg.patch new file mode 100644 index 0000000..6264dcb --- /dev/null +++ b/pkgs/patches-linux-5.15/0045-ARM-dts-armada-39x.dtsi-Add-definitions-for-PCIe-leg.patch @@ -0,0 +1,137 @@ +From 6538b798220816625295fb0d3a15fe44ac2445d8 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org> +Date: Mon, 27 Jun 2022 17:47:53 +0200 +Subject: [PATCH 45/90] ARM: dts: armada-39x.dtsi: Add definitions for PCIe + legacy INTx interrupts +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Signed-off-by: Pali Rohár <pali@kernel.org> +--- + arch/arm/boot/dts/armada-39x.dtsi | 56 ++++++++++++++++++++++++++----- + 1 file changed, 48 insertions(+), 8 deletions(-) + +diff --git a/arch/arm/boot/dts/armada-39x.dtsi b/arch/arm/boot/dts/armada-39x.dtsi +index e0b7c2099831..923b035a3ab3 100644 +--- a/arch/arm/boot/dts/armada-39x.dtsi ++++ b/arch/arm/boot/dts/armada-39x.dtsi +@@ -438,16 +438,26 @@ pcie@1,0 { + reg = <0x0800 0 0 0 0>; + #address-cells = <3>; + #size-cells = <2>; ++ interrupt-names = "intx"; ++ interrupts-extended = <&gic GIC_SPI 29 IRQ_TYPE_LEVEL_HIGH>; + #interrupt-cells = <1>; + ranges = <0x82000000 0 0 0x82000000 0x1 0 1 0 + 0x81000000 0 0 0x81000000 0x1 0 1 0>; + bus-range = <0x00 0xff>; +- interrupt-map-mask = <0 0 0 0>; +- interrupt-map = <0 0 0 0 &gic GIC_SPI 29 IRQ_TYPE_LEVEL_HIGH>; ++ interrupt-map-mask = <0 0 0 7>; ++ interrupt-map = <0 0 0 1 &pcie1_intc 0>, ++ <0 0 0 2 &pcie1_intc 1>, ++ <0 0 0 3 &pcie1_intc 2>, ++ <0 0 0 4 &pcie1_intc 3>; + marvell,pcie-port = <0>; + marvell,pcie-lane = <0>; + clocks = <&gateclk 8>; + status = "disabled"; ++ ++ pcie1_intc: interrupt-controller { ++ interrupt-controller; ++ #interrupt-cells = <1>; ++ }; + }; + + /* x1 port */ +@@ -457,16 +467,26 @@ pcie@2,0 { + reg = <0x1000 0 0 0 0>; + #address-cells = <3>; + #size-cells = <2>; ++ interrupt-names = "intx"; ++ interrupts-extended = <&gic GIC_SPI 33 IRQ_TYPE_LEVEL_HIGH>; + #interrupt-cells = <1>; + ranges = <0x82000000 0 0 0x82000000 0x2 0 1 0 + 0x81000000 0 0 0x81000000 0x2 0 1 0>; + bus-range = <0x00 0xff>; +- interrupt-map-mask = <0 0 0 0>; +- interrupt-map = <0 0 0 0 &gic GIC_SPI 33 IRQ_TYPE_LEVEL_HIGH>; ++ interrupt-map-mask = <0 0 0 7>; ++ interrupt-map = <0 0 0 1 &pcie2_intc 0>, ++ <0 0 0 2 &pcie2_intc 1>, ++ <0 0 0 3 &pcie2_intc 2>, ++ <0 0 0 4 &pcie2_intc 3>; + marvell,pcie-port = <1>; + marvell,pcie-lane = <0>; + clocks = <&gateclk 5>; + status = "disabled"; ++ ++ pcie2_intc: interrupt-controller { ++ interrupt-controller; ++ #interrupt-cells = <1>; ++ }; + }; + + /* x1 port */ +@@ -476,16 +496,26 @@ pcie@3,0 { + reg = <0x1800 0 0 0 0>; + #address-cells = <3>; + #size-cells = <2>; ++ interrupt-names = "intx"; ++ interrupts-extended = <&gic GIC_SPI 70 IRQ_TYPE_LEVEL_HIGH>; + #interrupt-cells = <1>; + ranges = <0x82000000 0 0 0x82000000 0x3 0 1 0 + 0x81000000 0 0 0x81000000 0x3 0 1 0>; + bus-range = <0x00 0xff>; +- interrupt-map-mask = <0 0 0 0>; +- interrupt-map = <0 0 0 0 &gic GIC_SPI 70 IRQ_TYPE_LEVEL_HIGH>; ++ interrupt-map-mask = <0 0 0 7>; ++ interrupt-map = <0 0 0 1 &pcie3_intc 0>, ++ <0 0 0 2 &pcie3_intc 1>, ++ <0 0 0 3 &pcie3_intc 2>, ++ <0 0 0 4 &pcie3_intc 3>; + marvell,pcie-port = <2>; + marvell,pcie-lane = <0>; + clocks = <&gateclk 6>; + status = "disabled"; ++ ++ pcie3_intc: interrupt-controller { ++ interrupt-controller; ++ #interrupt-cells = <1>; ++ }; + }; + + /* +@@ -498,16 +528,26 @@ pcie@4,0 { + reg = <0x2000 0 0 0 0>; + #address-cells = <3>; + #size-cells = <2>; ++ interrupt-names = "intx"; ++ interrupts-extended = <&gic GIC_SPI 71 IRQ_TYPE_LEVEL_HIGH>; + #interrupt-cells = <1>; + ranges = <0x82000000 0 0 0x82000000 0x4 0 1 0 + 0x81000000 0 0 0x81000000 0x4 0 1 0>; + bus-range = <0x00 0xff>; +- interrupt-map-mask = <0 0 0 0>; +- interrupt-map = <0 0 0 0 &gic GIC_SPI 71 IRQ_TYPE_LEVEL_HIGH>; ++ interrupt-map-mask = <0 0 0 7>; ++ interrupt-map = <0 0 0 1 &pcie4_intc 0>, ++ <0 0 0 2 &pcie4_intc 1>, ++ <0 0 0 3 &pcie4_intc 2>, ++ <0 0 0 4 &pcie4_intc 3>; + marvell,pcie-port = <3>; + marvell,pcie-lane = <0>; + clocks = <&gateclk 7>; + status = "disabled"; ++ ++ pcie4_intc: interrupt-controller { ++ interrupt-controller; ++ #interrupt-cells = <1>; ++ }; + }; + }; + +-- +2.34.1 + diff --git a/pkgs/patches-linux-5.15/0046-PCI-Add-PCI_EXP_SLTCTL_ASPL_DISABLE-macro.patch b/pkgs/patches-linux-5.15/0046-PCI-Add-PCI_EXP_SLTCTL_ASPL_DISABLE-macro.patch new file mode 100644 index 0000000..35af364 --- /dev/null +++ b/pkgs/patches-linux-5.15/0046-PCI-Add-PCI_EXP_SLTCTL_ASPL_DISABLE-macro.patch @@ -0,0 +1,33 @@ +From 864663e14d534b51c066d7a686f43c2502bb311a Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org> +Date: Fri, 2 Apr 2021 22:53:01 +0200 +Subject: [PATCH 46/90] PCI: Add PCI_EXP_SLTCTL_ASPL_DISABLE macro +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Add macro defining Auto Slot Power Limit Disable bit in Slot Control +Register. + +Signed-off-by: Pali Rohár <pali@kernel.org> +Signed-off-by: Marek Behún <kabel@kernel.org> +Acked-by: Bjorn Helgaas <bhelgaas@google.com> +--- + include/uapi/linux/pci_regs.h | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/include/uapi/linux/pci_regs.h b/include/uapi/linux/pci_regs.h +index ff6ccbc6efe9..e3456b8050f5 100644 +--- a/include/uapi/linux/pci_regs.h ++++ b/include/uapi/linux/pci_regs.h +@@ -616,6 +616,7 @@ + #define PCI_EXP_SLTCTL_PWR_OFF 0x0400 /* Power Off */ + #define PCI_EXP_SLTCTL_EIC 0x0800 /* Electromechanical Interlock Control */ + #define PCI_EXP_SLTCTL_DLLSCE 0x1000 /* Data Link Layer State Changed Enable */ ++#define PCI_EXP_SLTCTL_ASPL_DISABLE 0x2000 /* Auto Slot Power Limit Disable */ + #define PCI_EXP_SLTCTL_IBPD_DISABLE 0x4000 /* In-band PD disable */ + #define PCI_EXP_SLTSTA 26 /* Slot Status */ + #define PCI_EXP_SLTSTA_ABP 0x0001 /* Attention Button Pressed */ +-- +2.34.1 + diff --git a/pkgs/patches-linux-5.15/0047-dt-bindings-Add-slot-power-limit-milliwatt-PCIe-port.patch b/pkgs/patches-linux-5.15/0047-dt-bindings-Add-slot-power-limit-milliwatt-PCIe-port.patch new file mode 100644 index 0000000..ebe671d --- /dev/null +++ b/pkgs/patches-linux-5.15/0047-dt-bindings-Add-slot-power-limit-milliwatt-PCIe-port.patch @@ -0,0 +1,46 @@ +From 0b9d2e40cc7379a4ad00dc198f755748eb654b49 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org> +Date: Sun, 31 Oct 2021 16:07:05 +0100 +Subject: [PATCH 47/90] dt-bindings: Add 'slot-power-limit-milliwatt' PCIe port + property +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +This property specifies slot power limit in mW unit. It is a form-factor +and board specific value and must be initialized by hardware. + +Some PCIe controllers delegate this work to software to allow hardware +flexibility and therefore this property basically specifies what should +host bridge program into PCIe Slot Capabilities registers. + +The property needs to be specified in mW unit instead of the special format +defined by Slot Capabilities (which encodes scaling factor or different +unit). Host drivers should convert the value from mW to needed format. + +Signed-off-by: Pali Rohár <pali@kernel.org> +Signed-off-by: Marek Behún <kabel@kernel.org> +--- + Documentation/devicetree/bindings/pci/pci.txt | 6 ++++++ + 1 file changed, 6 insertions(+) + +diff --git a/Documentation/devicetree/bindings/pci/pci.txt b/Documentation/devicetree/bindings/pci/pci.txt +index 6a8f2874a24d..b0cc133ed00d 100644 +--- a/Documentation/devicetree/bindings/pci/pci.txt ++++ b/Documentation/devicetree/bindings/pci/pci.txt +@@ -32,6 +32,12 @@ driver implementation may support the following properties: + root port to downstream device and host bridge drivers can do programming + which depends on CLKREQ signal existence. For example, programming root port + not to advertise ASPM L1 Sub-States support if there is no CLKREQ signal. ++- slot-power-limit-milliwatt: ++ If present, this property specifies slot power limit in milliwatts. Host ++ drivers can parse this property and use it for programming Root Port or host ++ bridge, or for composing and sending PCIe Set_Slot_Power_Limit messages ++ through the Root Port or host bridge when transitioning PCIe link from a ++ non-DL_Up Status to a DL_Up Status. + + PCI-PCI Bridge properties + ------------------------- +-- +2.34.1 + diff --git a/pkgs/patches-linux-5.15/0048-PCI-Add-function-for-parsing-slot-power-limit-milliw.patch b/pkgs/patches-linux-5.15/0048-PCI-Add-function-for-parsing-slot-power-limit-milliw.patch new file mode 100644 index 0000000..4518849 --- /dev/null +++ b/pkgs/patches-linux-5.15/0048-PCI-Add-function-for-parsing-slot-power-limit-milliw.patch @@ -0,0 +1,135 @@ +From 3514828cb2cf52e545fe71bb7dc2d105d8b0084e Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org> +Date: Sun, 31 Oct 2021 16:07:06 +0100 +Subject: [PATCH 48/90] PCI: Add function for parsing + 'slot-power-limit-milliwatt' DT property +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Add function of_pci_get_slot_power_limit(), which parses the +'slot-power-limit-milliwatt' DT property, returning the value in +milliwatts and in format ready for the PCIe Slot Capabilities Register. + +Signed-off-by: Pali Rohár <pali@kernel.org> +Signed-off-by: Marek Behún <kabel@kernel.org> +Reviewed-by: Rob Herring <robh@kernel.org> +--- + drivers/pci/of.c | 70 +++++++++++++++++++++++++++++++++++++++++++++++ + drivers/pci/pci.h | 15 ++++++++++ + 2 files changed, 85 insertions(+) + +diff --git a/drivers/pci/of.c b/drivers/pci/of.c +index d84381ce82b5..1372da653929 100644 +--- a/drivers/pci/of.c ++++ b/drivers/pci/of.c +@@ -627,3 +627,73 @@ int of_pci_get_max_link_speed(struct device_node *node) + return max_link_speed; + } + EXPORT_SYMBOL_GPL(of_pci_get_max_link_speed); ++ ++/** ++ * of_pci_get_slot_power_limit - Parses the "slot-power-limit-milliwatt" ++ * property. ++ * ++ * @node: device tree node with the slot power limit information ++ * @slot_power_limit_value: pointer where the value should be stored in PCIe ++ * Slot Capabilities Register format ++ * @slot_power_limit_scale: pointer where the scale should be stored in PCIe ++ * Slot Capabilities Register format ++ * ++ * Returns the slot power limit in milliwatts and if @slot_power_limit_value ++ * and @slot_power_limit_scale pointers are non-NULL, fills in the value and ++ * scale in format used by PCIe Slot Capabilities Register. ++ * ++ * If the property is not found or is invalid, returns 0. ++ */ ++u32 of_pci_get_slot_power_limit(struct device_node *node, ++ u8 *slot_power_limit_value, ++ u8 *slot_power_limit_scale) ++{ ++ u32 slot_power_limit_mw; ++ u8 value, scale; ++ ++ if (of_property_read_u32(node, "slot-power-limit-milliwatt", ++ &slot_power_limit_mw)) ++ slot_power_limit_mw = 0; ++ ++ /* Calculate Slot Power Limit Value and Slot Power Limit Scale */ ++ if (slot_power_limit_mw == 0) { ++ value = 0x00; ++ scale = 0; ++ } else if (slot_power_limit_mw <= 255) { ++ value = slot_power_limit_mw; ++ scale = 3; ++ } else if (slot_power_limit_mw <= 255*10) { ++ value = slot_power_limit_mw / 10; ++ scale = 2; ++ slot_power_limit_mw = slot_power_limit_mw / 10 * 10; ++ } else if (slot_power_limit_mw <= 255*100) { ++ value = slot_power_limit_mw / 100; ++ scale = 1; ++ slot_power_limit_mw = slot_power_limit_mw / 100 * 100; ++ } else if (slot_power_limit_mw <= 239*1000) { ++ value = slot_power_limit_mw / 1000; ++ scale = 0; ++ slot_power_limit_mw = slot_power_limit_mw / 1000 * 1000; ++ } else if (slot_power_limit_mw < 250*1000) { ++ value = 0xEF; ++ scale = 0; ++ slot_power_limit_mw = 239*1000; ++ } else if (slot_power_limit_mw <= 600*1000) { ++ value = 0xF0 + (slot_power_limit_mw / 1000 - 250) / 25; ++ scale = 0; ++ slot_power_limit_mw = slot_power_limit_mw / (1000*25) * (1000*25); ++ } else { ++ value = 0xFE; ++ scale = 0; ++ slot_power_limit_mw = 600*1000; ++ } ++ ++ if (slot_power_limit_value) ++ *slot_power_limit_value = value; ++ ++ if (slot_power_limit_scale) ++ *slot_power_limit_scale = scale; ++ ++ return slot_power_limit_mw; ++} ++EXPORT_SYMBOL_GPL(of_pci_get_slot_power_limit); +diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h +index 1cce56c2aea0..9352278141be 100644 +--- a/drivers/pci/pci.h ++++ b/drivers/pci/pci.h +@@ -665,6 +665,9 @@ struct device_node; + int of_pci_parse_bus_range(struct device_node *node, struct resource *res); + int of_get_pci_domain_nr(struct device_node *node); + int of_pci_get_max_link_speed(struct device_node *node); ++u32 of_pci_get_slot_power_limit(struct device_node *node, ++ u8 *slot_power_limit_value, ++ u8 *slot_power_limit_scale); + void pci_set_of_node(struct pci_dev *dev); + void pci_release_of_node(struct pci_dev *dev); + void pci_set_bus_of_node(struct pci_bus *bus); +@@ -691,6 +694,18 @@ of_pci_get_max_link_speed(struct device_node *node) + return -EINVAL; + } + ++static inline u32 ++of_pci_get_slot_power_limit(struct device_node *node, ++ u8 *slot_power_limit_value, ++ u8 *slot_power_limit_scale) ++{ ++ if (slot_power_limit_value) ++ *slot_power_limit_value = 0; ++ if (slot_power_limit_scale) ++ *slot_power_limit_scale = 0; ++ return 0; ++} ++ + static inline void pci_set_of_node(struct pci_dev *dev) { } + static inline void pci_release_of_node(struct pci_dev *dev) { } + static inline void pci_set_bus_of_node(struct pci_bus *bus) { } +-- +2.34.1 + diff --git a/pkgs/patches-linux-5.15/0049-PCI-mvebu-Add-support-for-sending-Set_Slot_Power_Lim.patch b/pkgs/patches-linux-5.15/0049-PCI-mvebu-Add-support-for-sending-Set_Slot_Power_Lim.patch new file mode 100644 index 0000000..4c526f6 --- /dev/null +++ b/pkgs/patches-linux-5.15/0049-PCI-mvebu-Add-support-for-sending-Set_Slot_Power_Lim.patch @@ -0,0 +1,210 @@ +From 5c87d5ff707236098214303289cd5724aebbd0ab Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org> +Date: Fri, 17 Sep 2021 14:25:33 +0200 +Subject: [PATCH 49/90] PCI: mvebu: Add support for sending + Set_Slot_Power_Limit message +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +If DT supplies the 'slot-power-limit-milliwatt' property, program +the value in the Slot Power Limit in the Slot Capabilities register +and program the Root Port to send a Set_Slot_Power_Limit Message +when the Link transitions to DL_Up. + +Signed-off-by: Pali Rohár <pali@kernel.org> +Reviewed-by: Rob Herring <robh@kernel.org> +--- + drivers/pci/controller/pci-mvebu.c | 97 ++++++++++++++++++++++++++++-- + 1 file changed, 92 insertions(+), 5 deletions(-) + +diff --git a/drivers/pci/controller/pci-mvebu.c b/drivers/pci/controller/pci-mvebu.c +index c9311829dfe3..3c48b15e3948 100644 +--- a/drivers/pci/controller/pci-mvebu.c ++++ b/drivers/pci/controller/pci-mvebu.c +@@ -8,6 +8,7 @@ + #include <linux/kernel.h> + #include <linux/module.h> + #include <linux/pci.h> ++#include <linux/bitfield.h> + #include <linux/clk.h> + #include <linux/delay.h> + #include <linux/gpio.h> +@@ -66,6 +67,12 @@ + #define PCIE_STAT_BUS 0xff00 + #define PCIE_STAT_DEV 0x1f0000 + #define PCIE_STAT_LINK_DOWN BIT(0) ++#define PCIE_SSPL_OFF 0x1a0c ++#define PCIE_SSPL_VALUE_SHIFT 0 ++#define PCIE_SSPL_VALUE_MASK GENMASK(7, 0) ++#define PCIE_SSPL_SCALE_SHIFT 8 ++#define PCIE_SSPL_SCALE_MASK GENMASK(9, 8) ++#define PCIE_SSPL_ENABLE BIT(16) + #define PCIE_RC_RTSTA 0x1a14 + #define PCIE_DEBUG_CTRL 0x1a60 + #define PCIE_DEBUG_SOFT_RESET BIT(20) +@@ -111,6 +118,8 @@ struct mvebu_pcie_port { + struct mvebu_pcie_window iowin; + u32 saved_pcie_stat; + struct resource regs; ++ u8 slot_power_limit_value; ++ u8 slot_power_limit_scale; + struct irq_domain *intx_irq_domain; + raw_spinlock_t irq_lock; + int intx_irq; +@@ -239,7 +248,7 @@ static void mvebu_pcie_setup_wins(struct mvebu_pcie_port *port) + + static void mvebu_pcie_setup_hw(struct mvebu_pcie_port *port) + { +- u32 ctrl, lnkcap, cmd, dev_rev, unmask; ++ u32 ctrl, lnkcap, cmd, dev_rev, unmask, sspl; + + /* Setup PCIe controller to Root Complex mode. */ + ctrl = mvebu_readl(port, PCIE_CTRL_OFF); +@@ -292,6 +301,20 @@ static void mvebu_pcie_setup_hw(struct mvebu_pcie_port *port) + /* Point PCIe unit MBUS decode windows to DRAM space. */ + mvebu_pcie_setup_wins(port); + ++ /* ++ * Program Root Port to automatically send Set_Slot_Power_Limit ++ * PCIe Message when changing status from Dl_Down to Dl_Up and valid ++ * slot power limit was specified. ++ */ ++ sspl = mvebu_readl(port, PCIE_SSPL_OFF); ++ sspl &= ~(PCIE_SSPL_VALUE_MASK | PCIE_SSPL_SCALE_MASK | PCIE_SSPL_ENABLE); ++ if (port->slot_power_limit_value) { ++ sspl |= port->slot_power_limit_value << PCIE_SSPL_VALUE_SHIFT; ++ sspl |= port->slot_power_limit_scale << PCIE_SSPL_SCALE_SHIFT; ++ sspl |= PCIE_SSPL_ENABLE; ++ } ++ mvebu_writel(port, sspl, PCIE_SSPL_OFF); ++ + /* Mask all interrupt sources. */ + mvebu_writel(port, ~PCIE_INT_ALL_MASK, PCIE_INT_UNMASK_OFF); + +@@ -633,9 +656,24 @@ mvebu_pci_bridge_emul_pcie_conf_read(struct pci_bridge_emul *bridge, + (PCI_EXP_LNKSTA_DLLLA << 16) : 0); + break; + +- case PCI_EXP_SLTCTL: +- *value = PCI_EXP_SLTSTA_PDS << 16; ++ case PCI_EXP_SLTCTL: { ++ u16 slotctl = le16_to_cpu(bridge->pcie_conf.slotctl); ++ u16 slotsta = le16_to_cpu(bridge->pcie_conf.slotsta); ++ u32 val = 0; ++ /* ++ * When slot power limit was not specified in DT then ++ * ASPL_DISABLE bit is stored only in emulated config space. ++ * Otherwise reflect status of PCIE_SSPL_ENABLE bit in HW. ++ */ ++ if (!port->slot_power_limit_value) ++ val |= slotctl & PCI_EXP_SLTCTL_ASPL_DISABLE; ++ else if (!(mvebu_readl(port, PCIE_SSPL_OFF) & PCIE_SSPL_ENABLE)) ++ val |= PCI_EXP_SLTCTL_ASPL_DISABLE; ++ /* This callback is 32-bit and in high bits is slot status. */ ++ val |= slotsta << 16; ++ *value = val; + break; ++ } + + case PCI_EXP_RTSTA: + *value = mvebu_readl(port, PCIE_RC_RTSTA); +@@ -779,6 +817,22 @@ mvebu_pci_bridge_emul_pcie_conf_write(struct pci_bridge_emul *bridge, + mvebu_writel(port, new, PCIE_CAP_PCIEXP + PCI_EXP_LNKCTL); + break; + ++ case PCI_EXP_SLTCTL: ++ /* ++ * Allow to change PCIE_SSPL_ENABLE bit only when slot power ++ * limit was specified in DT and configured into HW. ++ */ ++ if ((mask & PCI_EXP_SLTCTL_ASPL_DISABLE) && ++ port->slot_power_limit_value) { ++ u32 sspl = mvebu_readl(port, PCIE_SSPL_OFF); ++ if (new & PCI_EXP_SLTCTL_ASPL_DISABLE) ++ sspl &= ~PCIE_SSPL_ENABLE; ++ else ++ sspl |= PCIE_SSPL_ENABLE; ++ mvebu_writel(port, sspl, PCIE_SSPL_OFF); ++ } ++ break; ++ + case PCI_EXP_RTSTA: + /* + * PME Status bit in Root Status Register (PCIE_RC_RTSTA) +@@ -873,8 +927,26 @@ static int mvebu_pci_bridge_emul_init(struct mvebu_pcie_port *port) + /* + * Older mvebu hardware provides PCIe Capability structure only in + * version 1. New hardware provides it in version 2. ++ * Enable slot support which is emulated. + */ +- bridge->pcie_conf.cap = cpu_to_le16(pcie_cap_ver); ++ bridge->pcie_conf.cap = cpu_to_le16(pcie_cap_ver | PCI_EXP_FLAGS_SLOT); ++ ++ /* ++ * Set Presence Detect State bit permanently as there is no support for ++ * unplugging PCIe card from the slot. Assume that PCIe card is always ++ * connected in slot. ++ * ++ * Set physical slot number to port+1 as mvebu ports are indexed from ++ * zero and zero value is reserved for ports within the same silicon ++ * as Root Port which is not mvebu case. ++ * ++ * Also set correct slot power limit. ++ */ ++ bridge->pcie_conf.slotcap = cpu_to_le32( ++ FIELD_PREP(PCI_EXP_SLTCAP_SPLV, port->slot_power_limit_value) | ++ FIELD_PREP(PCI_EXP_SLTCAP_SPLS, port->slot_power_limit_scale) | ++ FIELD_PREP(PCI_EXP_SLTCAP_PSN, port->port+1)); ++ bridge->pcie_conf.slotsta = cpu_to_le16(PCI_EXP_SLTSTA_PDS); + + bridge->subsystem_vendor_id = ssdev_id & 0xffff; + bridge->subsystem_id = ssdev_id >> 16; +@@ -1198,6 +1270,7 @@ static int mvebu_pcie_parse_port(struct mvebu_pcie *pcie, + { + struct device *dev = &pcie->pdev->dev; + enum of_gpio_flags flags; ++ u32 slot_power_limit; + int reset_gpio, ret; + u32 num_lanes; + +@@ -1298,6 +1371,15 @@ static int mvebu_pcie_parse_port(struct mvebu_pcie *pcie, + port->reset_gpio = gpio_to_desc(reset_gpio); + } + ++ slot_power_limit = of_pci_get_slot_power_limit(child, ++ &port->slot_power_limit_value, ++ &port->slot_power_limit_scale); ++ if (slot_power_limit) ++ dev_info(dev, "%s: Slot power limit %u.%uW\n", ++ port->name, ++ slot_power_limit / 1000, ++ (slot_power_limit / 100) % 10); ++ + port->clk = of_clk_get_by_name(child, NULL); + if (IS_ERR(port->clk)) { + dev_err(dev, "%s: cannot get clock\n", port->name); +@@ -1595,7 +1677,7 @@ static int mvebu_pcie_remove(struct platform_device *pdev) + { + struct mvebu_pcie *pcie = platform_get_drvdata(pdev); + struct pci_host_bridge *bridge = pci_host_bridge_from_priv(pcie); +- u32 cmd; ++ u32 cmd, sspl; + int i; + + /* Remove PCI bus with all devices. */ +@@ -1632,6 +1714,11 @@ static int mvebu_pcie_remove(struct platform_device *pdev) + /* Free config space for emulated root bridge. */ + pci_bridge_emul_cleanup(&port->bridge); + ++ /* Disable sending Set_Slot_Power_Limit PCIe Message. */ ++ sspl = mvebu_readl(port, PCIE_SSPL_OFF); ++ sspl &= ~(PCIE_SSPL_VALUE_MASK | PCIE_SSPL_SCALE_MASK | PCIE_SSPL_ENABLE); ++ mvebu_writel(port, sspl, PCIE_SSPL_OFF); ++ + /* Disable and clear BARs and windows. */ + mvebu_pcie_disable_wins(port); + +-- +2.34.1 + diff --git a/pkgs/patches-linux-5.15/0050-ARM-dts-turris-omnia-Set-PCIe-slot-power-limit-milli.patch b/pkgs/patches-linux-5.15/0050-ARM-dts-turris-omnia-Set-PCIe-slot-power-limit-milli.patch new file mode 100644 index 0000000..f14ff4d --- /dev/null +++ b/pkgs/patches-linux-5.15/0050-ARM-dts-turris-omnia-Set-PCIe-slot-power-limit-milli.patch @@ -0,0 +1,43 @@ +From f06d18dc4bdc230d247370c54a9ad9d4676ceb6d Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org> +Date: Mon, 27 Sep 2021 11:40:05 +0200 +Subject: [PATCH 50/90] ARM: dts: turris-omnia: Set PCIe + slot-power-limit-milliwatt properties +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +All 3 miniPCIe slots in Turris Omnia are designed for 10 W. + +Signed-off-by: Pali Rohár <pali@kernel.org> +--- + arch/arm/boot/dts/armada-385-turris-omnia.dts | 3 +++ + 1 file changed, 3 insertions(+) + +diff --git a/arch/arm/boot/dts/armada-385-turris-omnia.dts b/arch/arm/boot/dts/armada-385-turris-omnia.dts +index 5bd6a66d2c2b..f240018148f6 100644 +--- a/arch/arm/boot/dts/armada-385-turris-omnia.dts ++++ b/arch/arm/boot/dts/armada-385-turris-omnia.dts +@@ -71,16 +71,19 @@ pcie { + pcie@1,0 { + /* Port 0, Lane 0 */ + status = "okay"; ++ slot-power-limit-milliwatt = <10000>; + }; + + pcie@2,0 { + /* Port 1, Lane 0 */ + status = "okay"; ++ slot-power-limit-milliwatt = <10000>; + }; + + pcie@3,0 { + /* Port 2, Lane 0 */ + status = "okay"; ++ slot-power-limit-milliwatt = <10000>; + }; + }; + }; +-- +2.34.1 + diff --git a/pkgs/patches-linux-5.15/0051-PCI-pci-bridge-emul-Set-position-of-PCI-capabilities.patch b/pkgs/patches-linux-5.15/0051-PCI-pci-bridge-emul-Set-position-of-PCI-capabilities.patch new file mode 100644 index 0000000..0d3b160 --- /dev/null +++ b/pkgs/patches-linux-5.15/0051-PCI-pci-bridge-emul-Set-position-of-PCI-capabilities.patch @@ -0,0 +1,164 @@ +From f0a61d9c1f4396e7f3a241b9be1378ed4e5bb947 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org> +Date: Sun, 3 Jul 2022 12:40:13 +0200 +Subject: [PATCH 51/90] PCI: pci-bridge-emul: Set position of PCI capabilities + to real HW value +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +mvebu and aardvark HW have PCIe capabilities on different offset in PCI +config space. Extend pci-bridge-emul.c code to allow setting custom driver +custom value where PCIe capabilities starts. + +With this change PCIe capabilities of both drivers are reported at the same +location as where they are reported by U-Boot - in their real HW offset. + +Signed-off-by: Pali Rohár <pali@kernel.org> +--- + drivers/pci/controller/pci-aardvark.c | 1 + + drivers/pci/controller/pci-mvebu.c | 1 + + drivers/pci/pci-bridge-emul.c | 48 +++++++++++++++++---------- + drivers/pci/pci-bridge-emul.h | 2 ++ + 4 files changed, 35 insertions(+), 17 deletions(-) + +diff --git a/drivers/pci/controller/pci-aardvark.c b/drivers/pci/controller/pci-aardvark.c +index 3fe4d3329267..4ed1b3ed3d35 100644 +--- a/drivers/pci/controller/pci-aardvark.c ++++ b/drivers/pci/controller/pci-aardvark.c +@@ -984,6 +984,7 @@ static int advk_sw_pci_bridge_init(struct advk_pcie *pcie) + bridge->pcie_conf.rootcap = cpu_to_le16(PCI_EXP_RTCAP_CRSVIS); + + bridge->has_pcie = true; ++ bridge->pcie_start = PCIE_CORE_PCIEXP_CAP; + bridge->data = pcie; + bridge->ops = &advk_pci_bridge_emul_ops; + +diff --git a/drivers/pci/controller/pci-mvebu.c b/drivers/pci/controller/pci-mvebu.c +index 3c48b15e3948..8205b4d1f04c 100644 +--- a/drivers/pci/controller/pci-mvebu.c ++++ b/drivers/pci/controller/pci-mvebu.c +@@ -951,6 +951,7 @@ static int mvebu_pci_bridge_emul_init(struct mvebu_pcie_port *port) + bridge->subsystem_vendor_id = ssdev_id & 0xffff; + bridge->subsystem_id = ssdev_id >> 16; + bridge->has_pcie = true; ++ bridge->pcie_start = PCIE_CAP_PCIEXP; + bridge->data = port; + bridge->ops = &mvebu_pci_bridge_emul_ops; + +diff --git a/drivers/pci/pci-bridge-emul.c b/drivers/pci/pci-bridge-emul.c +index a5b662cc89d0..7a8a7c804050 100644 +--- a/drivers/pci/pci-bridge-emul.c ++++ b/drivers/pci/pci-bridge-emul.c +@@ -22,11 +22,7 @@ + + #define PCI_BRIDGE_CONF_END PCI_STD_HEADER_SIZEOF + #define PCI_CAP_SSID_SIZEOF (PCI_SSVID_DEVICE_ID + 2) +-#define PCI_CAP_SSID_START PCI_BRIDGE_CONF_END +-#define PCI_CAP_SSID_END (PCI_CAP_SSID_START + PCI_CAP_SSID_SIZEOF) + #define PCI_CAP_PCIE_SIZEOF (PCI_EXP_SLTSTA2 + 2) +-#define PCI_CAP_PCIE_START PCI_CAP_SSID_END +-#define PCI_CAP_PCIE_END (PCI_CAP_PCIE_START + PCI_CAP_PCIE_SIZEOF) + + /** + * struct pci_bridge_reg_behavior - register bits behaviors +@@ -324,7 +320,7 @@ pci_bridge_emul_read_ssid(struct pci_bridge_emul *bridge, int reg, u32 *value) + switch (reg) { + case PCI_CAP_LIST_ID: + *value = PCI_CAP_ID_SSVID | +- (bridge->has_pcie ? (PCI_CAP_PCIE_START << 8) : 0); ++ ((bridge->pcie_start > bridge->ssid_start) ? (bridge->pcie_start << 8) : 0); + return PCI_BRIDGE_EMUL_HANDLED; + + case PCI_SSVID_VENDOR_ID: +@@ -363,18 +359,33 @@ int pci_bridge_emul_init(struct pci_bridge_emul *bridge, + if (!bridge->pci_regs_behavior) + return -ENOMEM; + +- if (bridge->subsystem_vendor_id) +- bridge->conf.capabilities_pointer = PCI_CAP_SSID_START; +- else if (bridge->has_pcie) +- bridge->conf.capabilities_pointer = PCI_CAP_PCIE_START; +- else +- bridge->conf.capabilities_pointer = 0; ++ /* If ssid_start and pcie_start were not specified then choose the lowest possible value. */ ++ if (!bridge->ssid_start && !bridge->pcie_start) { ++ if (bridge->subsystem_vendor_id) ++ bridge->ssid_start = PCI_BRIDGE_CONF_END; ++ if (bridge->has_pcie) ++ bridge->pcie_start = bridge->ssid_start + PCI_CAP_SSID_SIZEOF; ++ } else if (!bridge->ssid_start && bridge->subsystem_vendor_id) { ++ if (bridge->pcie_start - PCI_BRIDGE_CONF_END >= PCI_CAP_SSID_SIZEOF) ++ bridge->ssid_start = PCI_BRIDGE_CONF_END; ++ else ++ bridge->ssid_start = bridge->pcie_start + PCI_CAP_PCIE_SIZEOF; ++ } else if (!bridge->pcie_start && bridge->has_pcie) { ++ if (bridge->ssid_start - PCI_BRIDGE_CONF_END >= PCI_CAP_PCIE_SIZEOF) ++ bridge->pcie_start = PCI_BRIDGE_CONF_END; ++ else ++ bridge->pcie_start = bridge->ssid_start + PCI_CAP_SSID_SIZEOF; ++ } ++ ++ bridge->conf.capabilities_pointer = min(bridge->ssid_start, bridge->pcie_start); + + if (bridge->conf.capabilities_pointer) + bridge->conf.status |= cpu_to_le16(PCI_STATUS_CAP_LIST); + + if (bridge->has_pcie) { + bridge->pcie_conf.cap_id = PCI_CAP_ID_EXP; ++ bridge->pcie_conf.next = (bridge->ssid_start > bridge->pcie_start) ? ++ bridge->ssid_start : 0; + bridge->pcie_conf.cap |= cpu_to_le16(PCI_EXP_TYPE_ROOT_PORT << 4); + bridge->pcie_cap_regs_behavior = + kmemdup(pcie_cap_regs_behavior, +@@ -457,15 +468,17 @@ int pci_bridge_emul_conf_read(struct pci_bridge_emul *bridge, int where, + read_op = bridge->ops->read_base; + cfgspace = (__le32 *) &bridge->conf; + behavior = bridge->pci_regs_behavior; +- } else if (reg >= PCI_CAP_SSID_START && reg < PCI_CAP_SSID_END && bridge->subsystem_vendor_id) { ++ } else if (reg >= bridge->ssid_start && reg < bridge->ssid_start + PCI_CAP_SSID_SIZEOF && ++ bridge->subsystem_vendor_id) { + /* Emulated PCI Bridge Subsystem Vendor ID capability */ +- reg -= PCI_CAP_SSID_START; ++ reg -= bridge->ssid_start; + read_op = pci_bridge_emul_read_ssid; + cfgspace = NULL; + behavior = NULL; +- } else if (reg >= PCI_CAP_PCIE_START && reg < PCI_CAP_PCIE_END && bridge->has_pcie) { ++ } else if (reg >= bridge->pcie_start && reg < bridge->pcie_start + PCI_CAP_PCIE_SIZEOF && ++ bridge->has_pcie) { + /* Our emulated PCIe capability */ +- reg -= PCI_CAP_PCIE_START; ++ reg -= bridge->pcie_start; + read_op = bridge->ops->read_pcie; + cfgspace = (__le32 *) &bridge->pcie_conf; + behavior = bridge->pcie_cap_regs_behavior; +@@ -536,9 +549,10 @@ int pci_bridge_emul_conf_write(struct pci_bridge_emul *bridge, int where, + write_op = bridge->ops->write_base; + cfgspace = (__le32 *) &bridge->conf; + behavior = bridge->pci_regs_behavior; +- } else if (reg >= PCI_CAP_PCIE_START && reg < PCI_CAP_PCIE_END && bridge->has_pcie) { ++ } else if (reg >= bridge->pcie_start && reg < bridge->pcie_start + PCI_CAP_PCIE_SIZEOF && ++ bridge->has_pcie) { + /* Our emulated PCIe capability */ +- reg -= PCI_CAP_PCIE_START; ++ reg -= bridge->pcie_start; + write_op = bridge->ops->write_pcie; + cfgspace = (__le32 *) &bridge->pcie_conf; + behavior = bridge->pcie_cap_regs_behavior; +diff --git a/drivers/pci/pci-bridge-emul.h b/drivers/pci/pci-bridge-emul.h +index 71392b67471d..2a0e59c7f0d9 100644 +--- a/drivers/pci/pci-bridge-emul.h ++++ b/drivers/pci/pci-bridge-emul.h +@@ -131,6 +131,8 @@ struct pci_bridge_emul { + struct pci_bridge_reg_behavior *pci_regs_behavior; + struct pci_bridge_reg_behavior *pcie_cap_regs_behavior; + void *data; ++ u8 pcie_start; ++ u8 ssid_start; + bool has_pcie; + u16 subsystem_vendor_id; + u16 subsystem_id; +-- +2.34.1 + diff --git a/pkgs/patches-linux-5.15/0052-irqchip-armada-370-xp-Do-not-allow-mapping-IRQ-0-and.patch b/pkgs/patches-linux-5.15/0052-irqchip-armada-370-xp-Do-not-allow-mapping-IRQ-0-and.patch new file mode 100644 index 0000000..f93f96f --- /dev/null +++ b/pkgs/patches-linux-5.15/0052-irqchip-armada-370-xp-Do-not-allow-mapping-IRQ-0-and.patch @@ -0,0 +1,37 @@ +From 9e71688dc62f92af7691bc3c0c30c25f82022f6d Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org> +Date: Sun, 17 Apr 2022 22:56:55 +0200 +Subject: [PATCH 52/90] irqchip/armada-370-xp: Do not allow mapping IRQ 0 and 1 +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +IRQs 0 and 1 cannot be mapped, they are handled internally by this driver +and this driver does not call generic_handle_domain_irq() for these IRQs. +So do not allow mapping these IRQs and correctly propagate error from the +.irq_map callback. + +Signed-off-by: Pali Rohár <pali@kernel.org> +Cc: stable@vger.kernel.org +--- + drivers/irqchip/irq-armada-370-xp.c | 4 ++++ + 1 file changed, 4 insertions(+) + +diff --git a/drivers/irqchip/irq-armada-370-xp.c b/drivers/irqchip/irq-armada-370-xp.c +index 01709c61e364..3fa6bd70684b 100644 +--- a/drivers/irqchip/irq-armada-370-xp.c ++++ b/drivers/irqchip/irq-armada-370-xp.c +@@ -546,6 +546,10 @@ static struct irq_chip armada_370_xp_irq_chip = { + static int armada_370_xp_mpic_irq_map(struct irq_domain *h, + unsigned int virq, irq_hw_number_t hw) + { ++ /* IRQs 0 and 1 cannot be mapped, they are handled internally */ ++ if (hw <= 1) ++ return -EINVAL; ++ + armada_370_xp_irq_mask(irq_get_irq_data(virq)); + if (!is_percpu_irq(hw)) + writel(hw, per_cpu_int_base + +-- +2.34.1 + diff --git a/pkgs/patches-linux-5.15/0053-PCI-mvebu-Use-devm_request_irq-for-registering-inter.patch b/pkgs/patches-linux-5.15/0053-PCI-mvebu-Use-devm_request_irq-for-registering-inter.patch new file mode 100644 index 0000000..d09446f --- /dev/null +++ b/pkgs/patches-linux-5.15/0053-PCI-mvebu-Use-devm_request_irq-for-registering-inter.patch @@ -0,0 +1,99 @@ +From 149b6e20ecbc96ede412736d5c26496382d382e7 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org> +Date: Tue, 24 May 2022 13:57:37 +0200 +Subject: [PATCH 53/90] PCI: mvebu: Use devm_request_irq() for registering + interrupt handler +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Same as in commit a3b69dd0ad62 ("Revert "PCI: aardvark: Rewrite IRQ code to +chained IRQ handler"") for pci-aardvark driver, use devm_request_irq() +instead of chained IRQ handler in pci-mvebu.c driver. + +This change fixes affinity support and allows to pin interrupts from +different PCIe controllers to different CPU cores. + +Fixes: ec075262648f ("PCI: mvebu: Implement support for legacy INTx interrupts") +Signed-off-by: Pali Rohár <pali@kernel.org> +--- + drivers/pci/controller/pci-mvebu.c | 30 +++++++++++++++++------------- + 1 file changed, 17 insertions(+), 13 deletions(-) + +diff --git a/drivers/pci/controller/pci-mvebu.c b/drivers/pci/controller/pci-mvebu.c +index 8205b4d1f04c..d8cba09649ba 100644 +--- a/drivers/pci/controller/pci-mvebu.c ++++ b/drivers/pci/controller/pci-mvebu.c +@@ -1097,16 +1097,13 @@ static int mvebu_pcie_init_irq_domain(struct mvebu_pcie_port *port) + return 0; + } + +-static void mvebu_pcie_irq_handler(struct irq_desc *desc) ++static irqreturn_t mvebu_pcie_irq_handler(int irq, void *arg) + { +- struct mvebu_pcie_port *port = irq_desc_get_handler_data(desc); +- struct irq_chip *chip = irq_desc_get_chip(desc); ++ struct mvebu_pcie_port *port = arg; + struct device *dev = &port->pcie->pdev->dev; + u32 cause, unmask, status; + int i; + +- chained_irq_enter(chip, desc); +- + cause = mvebu_readl(port, PCIE_INT_CAUSE_OFF); + unmask = mvebu_readl(port, PCIE_INT_UNMASK_OFF); + status = cause & unmask; +@@ -1120,7 +1117,7 @@ static void mvebu_pcie_irq_handler(struct irq_desc *desc) + dev_err_ratelimited(dev, "unexpected INT%c IRQ\n", (char)i+'A'); + } + +- chained_irq_exit(chip, desc); ++ return status ? IRQ_HANDLED : IRQ_NONE; + } + + static int mvebu_pcie_map_irq(const struct pci_dev *dev, u8 slot, u8 pin) +@@ -1580,9 +1577,20 @@ static int mvebu_pcie_probe(struct platform_device *pdev) + mvebu_pcie_powerdown(port); + continue; + } +- irq_set_chained_handler_and_data(irq, +- mvebu_pcie_irq_handler, +- port); ++ ++ ret = devm_request_irq(dev, irq, mvebu_pcie_irq_handler, ++ IRQF_SHARED | IRQF_NO_THREAD, ++ port->name, port); ++ if (ret) { ++ dev_err(dev, "%s: cannot register interrupt handler: %d\n", ++ port->name, ret); ++ irq_domain_remove(port->intx_irq_domain); ++ pci_bridge_emul_cleanup(&port->bridge); ++ devm_iounmap(dev, port->base); ++ port->base = NULL; ++ mvebu_pcie_powerdown(port); ++ continue; ++ } + } + + /* +@@ -1689,7 +1697,6 @@ static int mvebu_pcie_remove(struct platform_device *pdev) + + for (i = 0; i < pcie->nports; i++) { + struct mvebu_pcie_port *port = &pcie->ports[i]; +- int irq = port->intx_irq; + + if (!port->base) + continue; +@@ -1705,9 +1712,6 @@ static int mvebu_pcie_remove(struct platform_device *pdev) + /* Clear all interrupt causes. */ + mvebu_writel(port, ~PCIE_INT_ALL_MASK, PCIE_INT_CAUSE_OFF); + +- if (irq > 0) +- irq_set_chained_handler_and_data(irq, NULL, NULL); +- + /* Remove IRQ domains. */ + if (port->intx_irq_domain) + irq_domain_remove(port->intx_irq_domain); +-- +2.34.1 + diff --git a/pkgs/patches-linux-5.15/0054-PCI-mvebu-Dispose-INTx-irqs-prior-to-removing-INTx-d.patch b/pkgs/patches-linux-5.15/0054-PCI-mvebu-Dispose-INTx-irqs-prior-to-removing-INTx-d.patch new file mode 100644 index 0000000..cd1f82f --- /dev/null +++ b/pkgs/patches-linux-5.15/0054-PCI-mvebu-Dispose-INTx-irqs-prior-to-removing-INTx-d.patch @@ -0,0 +1,47 @@ +From a3502251f0a195b584efc6b29ecac0ac53ac582a Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org> +Date: Sat, 9 Jul 2022 16:12:40 +0200 +Subject: [PATCH 54/90] PCI: mvebu: Dispose INTx irqs prior to removing INTx + domain +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Documentation for irq_domain_remove() says that all mapping within the +domain must be disposed prior to domain remove. + +Currently INTx irqs are not disposed in pci-mvebu.c device unbind callback +which cause that kernel crashes after unloading driver and trying to read +/sys/kernel/debug/irq/irqs/<num> or /proc/interrupts. + +Fixes: ec075262648f ("PCI: mvebu: Implement support for legacy INTx interrupts") +Reported-by: Hajo Noerenberg <hajo-linux-bugzilla@noerenberg.de> +Signed-off-by: Pali Rohár <pali@kernel.org> +--- + drivers/pci/controller/pci-mvebu.c | 9 ++++++++- + 1 file changed, 8 insertions(+), 1 deletion(-) + +diff --git a/drivers/pci/controller/pci-mvebu.c b/drivers/pci/controller/pci-mvebu.c +index d8cba09649ba..0122c65b0269 100644 +--- a/drivers/pci/controller/pci-mvebu.c ++++ b/drivers/pci/controller/pci-mvebu.c +@@ -1713,8 +1713,15 @@ static int mvebu_pcie_remove(struct platform_device *pdev) + mvebu_writel(port, ~PCIE_INT_ALL_MASK, PCIE_INT_CAUSE_OFF); + + /* Remove IRQ domains. */ +- if (port->intx_irq_domain) ++ if (port->intx_irq_domain) { ++ int virq, j; ++ for (j = 0; j < PCI_NUM_INTX; j++) { ++ virq = irq_find_mapping(port->intx_irq_domain, j); ++ if (virq > 0) ++ irq_dispose_mapping(virq); ++ } + irq_domain_remove(port->intx_irq_domain); ++ } + + /* Free config space for emulated root bridge. */ + pci_bridge_emul_cleanup(&port->bridge); +-- +2.34.1 + diff --git a/pkgs/patches-linux-5.15/0055-PCI-Assign-PCI-domain-by-ida_alloc.patch b/pkgs/patches-linux-5.15/0055-PCI-Assign-PCI-domain-by-ida_alloc.patch new file mode 100644 index 0000000..b639b72 --- /dev/null +++ b/pkgs/patches-linux-5.15/0055-PCI-Assign-PCI-domain-by-ida_alloc.patch @@ -0,0 +1,215 @@ +From 4194dcecf85a825287ce0d4931cf27c36ba899ce Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org> +Date: Sat, 2 Jul 2022 21:37:51 +0200 +Subject: [PATCH 55/90] PCI: Assign PCI domain by ida_alloc() +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Replace assignment of PCI domain from atomic_inc_return() to ida_alloc(). + +Use two IDAs, one for static domain allocations (those which are defined in +device tree) and second for dynamic allocations (all other). + +During removal of root bus / host bridge release also allocated domain id. +So released id can be reused again, for example in situation when +dynamically loading and unloading native PCI host bridge drivers. + +This change also allows to mix static device tree assignment and dynamic by +kernel as all static allocations are reserved in dynamic pool. + +Signed-off-by: Pali Rohár <pali@kernel.org> +--- + drivers/pci/pci.c | 103 +++++++++++++++++++++++++------------------ + drivers/pci/probe.c | 5 +++ + drivers/pci/remove.c | 6 +++ + include/linux/pci.h | 1 + + 4 files changed, 72 insertions(+), 43 deletions(-) + +diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c +index 2bfff2328cf8..99aaf6d8d92c 100644 +--- a/drivers/pci/pci.c ++++ b/drivers/pci/pci.c +@@ -6679,60 +6679,70 @@ static void pci_no_domains(void) + } + + #ifdef CONFIG_PCI_DOMAINS_GENERIC +-static atomic_t __domain_nr = ATOMIC_INIT(-1); ++static DEFINE_IDA(pci_domain_nr_static_ida); ++static DEFINE_IDA(pci_domain_nr_dynamic_ida); + +-static int pci_get_new_domain_nr(void) ++static void of_pci_reserve_static_domain_nr(void) + { +- return atomic_inc_return(&__domain_nr); ++ struct device_node *np; ++ int domain_nr; ++ ++ for_each_node_by_type(np, "pci") { ++ domain_nr = of_get_pci_domain_nr(np); ++ if (domain_nr < 0) ++ continue; ++ /* ++ * Permanently allocate domain_nr in dynamic_ida ++ * to prevent it from dynamic allocation. ++ */ ++ ida_alloc_range(&pci_domain_nr_dynamic_ida, ++ domain_nr, domain_nr, GFP_KERNEL); ++ } + } + + static int of_pci_bus_find_domain_nr(struct device *parent) + { +- static int use_dt_domains = -1; +- int domain = -1; ++ static bool static_domains_reserved = false; ++ int domain_nr; + +- if (parent) +- domain = of_get_pci_domain_nr(parent->of_node); ++ /* On the first call scan device tree for static allocations. */ ++ if (!static_domains_reserved) { ++ of_pci_reserve_static_domain_nr(); ++ static_domains_reserved = true; ++ } ++ ++ if (parent) { ++ /* ++ * If domain is in DT then allocate it in static IDA. ++ * This prevent duplicate static allocations in case ++ * of errors in DT. ++ */ ++ domain_nr = of_get_pci_domain_nr(parent->of_node); ++ if (domain_nr >= 0) ++ return ida_alloc_range(&pci_domain_nr_static_ida, ++ domain_nr, domain_nr, ++ GFP_KERNEL); ++ } + + /* +- * Check DT domain and use_dt_domains values. +- * +- * If DT domain property is valid (domain >= 0) and +- * use_dt_domains != 0, the DT assignment is valid since this means +- * we have not previously allocated a domain number by using +- * pci_get_new_domain_nr(); we should also update use_dt_domains to +- * 1, to indicate that we have just assigned a domain number from +- * DT. +- * +- * If DT domain property value is not valid (ie domain < 0), and we +- * have not previously assigned a domain number from DT +- * (use_dt_domains != 1) we should assign a domain number by +- * using the: +- * +- * pci_get_new_domain_nr() +- * +- * API and update the use_dt_domains value to keep track of method we +- * are using to assign domain numbers (use_dt_domains = 0). +- * +- * All other combinations imply we have a platform that is trying +- * to mix domain numbers obtained from DT and pci_get_new_domain_nr(), +- * which is a recipe for domain mishandling and it is prevented by +- * invalidating the domain value (domain = -1) and printing a +- * corresponding error. ++ * If domain was not specified in DT then choose free id from dynamic ++ * allocations. All domain numbers from DT are permanently in dynamic ++ * allocations to prevent assigning them to other DT nodes without ++ * static domain. + */ +- if (domain >= 0 && use_dt_domains) { +- use_dt_domains = 1; +- } else if (domain < 0 && use_dt_domains != 1) { +- use_dt_domains = 0; +- domain = pci_get_new_domain_nr(); +- } else { +- if (parent) +- pr_err("Node %pOF has ", parent->of_node); +- pr_err("Inconsistent \"linux,pci-domain\" property in DT\n"); +- domain = -1; +- } ++ return ida_alloc(&pci_domain_nr_dynamic_ida, GFP_KERNEL); ++} + +- return domain; ++static void of_pci_bus_release_domain_nr(struct pci_bus *bus, struct device *parent) ++{ ++ if (bus->domain_nr < 0) ++ return; ++ ++ /* Release domain from ida in which was it allocated. */ ++ if (of_get_pci_domain_nr(parent->of_node) == bus->domain_nr) ++ ida_free(&pci_domain_nr_static_ida, bus->domain_nr); ++ else ++ ida_free(&pci_domain_nr_dynamic_ida, bus->domain_nr); + } + + int pci_bus_find_domain_nr(struct pci_bus *bus, struct device *parent) +@@ -6740,6 +6750,13 @@ int pci_bus_find_domain_nr(struct pci_bus *bus, struct device *parent) + return acpi_disabled ? of_pci_bus_find_domain_nr(parent) : + acpi_pci_bus_find_domain_nr(bus); + } ++ ++void pci_bus_release_domain_nr(struct pci_bus *bus, struct device *parent) ++{ ++ if (!acpi_disabled) ++ return; ++ of_pci_bus_release_domain_nr(bus, parent); ++} + #endif + + /** +diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c +index d9fc02a71baa..1b6f817454e2 100644 +--- a/drivers/pci/probe.c ++++ b/drivers/pci/probe.c +@@ -908,6 +908,8 @@ static int pci_register_host_bridge(struct pci_host_bridge *bridge) + bus->domain_nr = pci_bus_find_domain_nr(bus, parent); + else + bus->domain_nr = bridge->domain_nr; ++ if (bus->domain_nr < 0) ++ goto free; + #endif + + b = pci_find_bus(pci_domain_nr(bus), bridge->busnr); +@@ -1007,6 +1009,9 @@ static int pci_register_host_bridge(struct pci_host_bridge *bridge) + device_del(&bridge->dev); + + free: ++#ifdef CONFIG_PCI_DOMAINS_GENERIC ++ pci_bus_release_domain_nr(bus, parent); ++#endif + kfree(bus); + return err; + } +diff --git a/drivers/pci/remove.c b/drivers/pci/remove.c +index 4c54c75050dc..0145aef1b930 100644 +--- a/drivers/pci/remove.c ++++ b/drivers/pci/remove.c +@@ -160,6 +160,12 @@ void pci_remove_root_bus(struct pci_bus *bus) + pci_remove_bus(bus); + host_bridge->bus = NULL; + ++#ifdef CONFIG_PCI_DOMAINS_GENERIC ++ /* Release domain_nr if it was dynamically allocated */ ++ if (host_bridge->domain_nr == PCI_DOMAIN_NR_NOT_SET) ++ pci_bus_release_domain_nr(bus, host_bridge->dev.parent); ++#endif ++ + /* remove the host bridge */ + device_del(&host_bridge->dev); + } +diff --git a/include/linux/pci.h b/include/linux/pci.h +index 9d6e75222868..e127ef7c3930 100644 +--- a/include/linux/pci.h ++++ b/include/linux/pci.h +@@ -1707,6 +1707,7 @@ static inline int acpi_pci_bus_find_domain_nr(struct pci_bus *bus) + { return 0; } + #endif + int pci_bus_find_domain_nr(struct pci_bus *bus, struct device *parent); ++void pci_bus_release_domain_nr(struct pci_bus *bus, struct device *parent); + #endif + + /* Some architectures require additional setup to direct VGA traffic */ +-- +2.34.1 + diff --git a/pkgs/patches-linux-5.15/0056-PCI-mvebu-Fix-endianity-when-accessing-pci-emul-brid.patch b/pkgs/patches-linux-5.15/0056-PCI-mvebu-Fix-endianity-when-accessing-pci-emul-brid.patch new file mode 100644 index 0000000..323e8bc --- /dev/null +++ b/pkgs/patches-linux-5.15/0056-PCI-mvebu-Fix-endianity-when-accessing-pci-emul-brid.patch @@ -0,0 +1,68 @@ +From 600952067c786972461d1ec16bfd71221066341f Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org> +Date: Fri, 12 Aug 2022 11:09:11 +0200 +Subject: [PATCH 56/90] PCI: mvebu: Fix endianity when accessing pci emul + bridge members +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +PCI emul bridge members iolimitupper, iobaseupper, memlimit and membase are +of type __le16, so correctly access these members via le16_to_cpu() macros. + +Fixes: 4ded69473adb ("PCI: mvebu: Propagate errors when updating PCI_IO_BASE and PCI_MEM_BASE registers") +Reported-by: kernel test robot <lkp@intel.com> +Signed-off-by: Pali Rohár <pali@kernel.org> +--- + drivers/pci/controller/pci-mvebu.c | 12 ++++++------ + 1 file changed, 6 insertions(+), 6 deletions(-) + +diff --git a/drivers/pci/controller/pci-mvebu.c b/drivers/pci/controller/pci-mvebu.c +index 0122c65b0269..c5e5bc085a9d 100644 +--- a/drivers/pci/controller/pci-mvebu.c ++++ b/drivers/pci/controller/pci-mvebu.c +@@ -528,7 +528,7 @@ static int mvebu_pcie_handle_iobase_change(struct mvebu_pcie_port *port) + + /* Are the new iobase/iolimit values invalid? */ + if (conf->iolimit < conf->iobase || +- conf->iolimitupper < conf->iobaseupper) ++ le16_to_cpu(conf->iolimitupper) < le16_to_cpu(conf->iobaseupper)) + return mvebu_pcie_set_window(port, port->io_target, port->io_attr, + &desired, &port->iowin); + +@@ -540,10 +540,10 @@ static int mvebu_pcie_handle_iobase_change(struct mvebu_pcie_port *port) + * is the CPU address. + */ + desired.remap = ((conf->iobase & 0xF0) << 8) | +- (conf->iobaseupper << 16); ++ (le16_to_cpu(conf->iobaseupper) << 16); + desired.base = port->pcie->io.start + desired.remap; + desired.size = ((0xFFF | ((conf->iolimit & 0xF0) << 8) | +- (conf->iolimitupper << 16)) - ++ (le16_to_cpu(conf->iolimitupper) << 16)) - + desired.remap) + + 1; + +@@ -557,7 +557,7 @@ static int mvebu_pcie_handle_membase_change(struct mvebu_pcie_port *port) + struct pci_bridge_emul_conf *conf = &port->bridge.conf; + + /* Are the new membase/memlimit values invalid? */ +- if (conf->memlimit < conf->membase) ++ if (le16_to_cpu(conf->memlimit) < le16_to_cpu(conf->membase)) + return mvebu_pcie_set_window(port, port->mem_target, port->mem_attr, + &desired, &port->memwin); + +@@ -567,8 +567,8 @@ static int mvebu_pcie_handle_membase_change(struct mvebu_pcie_port *port) + * window to setup, according to the PCI-to-PCI bridge + * specifications. + */ +- desired.base = ((conf->membase & 0xFFF0) << 16); +- desired.size = (((conf->memlimit & 0xFFF0) << 16) | 0xFFFFF) - ++ desired.base = ((le16_to_cpu(conf->membase) & 0xFFF0) << 16); ++ desired.size = (((le16_to_cpu(conf->memlimit) & 0xFFF0) << 16) | 0xFFFFF) - + desired.base + 1; + + return mvebu_pcie_set_window(port, port->mem_target, port->mem_attr, &desired, +-- +2.34.1 + diff --git a/pkgs/patches-linux-5.15/0057-ARM-dts-dove-Fix-assigned-addresses-for-every-PCIe-R.patch b/pkgs/patches-linux-5.15/0057-ARM-dts-dove-Fix-assigned-addresses-for-every-PCIe-R.patch new file mode 100644 index 0000000..99f1872 --- /dev/null +++ b/pkgs/patches-linux-5.15/0057-ARM-dts-dove-Fix-assigned-addresses-for-every-PCIe-R.patch @@ -0,0 +1,35 @@ +From ebcb5a3c9803cacf33ff2d029325519a4e27ec66 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org> +Date: Wed, 17 Aug 2022 23:46:32 +0200 +Subject: [PATCH 57/90] ARM: dts: dove: Fix assigned-addresses for every PCIe + Root Port +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +BDF of resource in DT assigned-addresses property of Marvell PCIe Root Port +(PCI-to-PCI bridge) should match BDF in address part in that DT node name +as specified resource belongs to Marvell PCIe Root Port itself. + +Fixes: 74ecaa403a74 ("ARM: dove: add PCIe controllers to SoC DT") +Signed-off-by: Pali Rohár <pali@kernel.org> +--- + arch/arm/boot/dts/dove.dtsi | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/arch/arm/boot/dts/dove.dtsi b/arch/arm/boot/dts/dove.dtsi +index 96ba47c061a7..70d45d2b1258 100644 +--- a/arch/arm/boot/dts/dove.dtsi ++++ b/arch/arm/boot/dts/dove.dtsi +@@ -139,7 +139,7 @@ pcie0_intc: interrupt-controller { + pcie1: pcie@2 { + device_type = "pci"; + status = "disabled"; +- assigned-addresses = <0x82002800 0 0x80000 0 0x2000>; ++ assigned-addresses = <0x82001000 0 0x80000 0 0x2000>; + reg = <0x1000 0 0 0 0>; + clocks = <&gate_clk 5>; + marvell,pcie-port = <1>; +-- +2.34.1 + diff --git a/pkgs/patches-linux-5.15/0058-ARM-dts-armada-370-Fix-assigned-addresses-for-every-.patch b/pkgs/patches-linux-5.15/0058-ARM-dts-armada-370-Fix-assigned-addresses-for-every-.patch new file mode 100644 index 0000000..985fc20 --- /dev/null +++ b/pkgs/patches-linux-5.15/0058-ARM-dts-armada-370-Fix-assigned-addresses-for-every-.patch @@ -0,0 +1,35 @@ +From 20f5fa51c6e4dc3ee944b3a207d641ba2e6964df Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org> +Date: Thu, 18 Aug 2022 00:01:14 +0200 +Subject: [PATCH 58/90] ARM: dts: armada-370: Fix assigned-addresses for every + PCIe Root Port +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +BDF of resource in DT assigned-addresses property of Marvell PCIe Root Port +(PCI-to-PCI bridge) should match BDF in address part in that DT node name +as specified resource belongs to Marvell PCIe Root Port itself. + +Fixes: a09a0b7c6ff1 ("arm: mvebu: add PCIe Device Tree informations for Armada 370") +Signed-off-by: Pali Rohár <pali@kernel.org> +--- + arch/arm/boot/dts/armada-370.dtsi | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/arch/arm/boot/dts/armada-370.dtsi b/arch/arm/boot/dts/armada-370.dtsi +index 9dc928859ad3..2013a5ccecd3 100644 +--- a/arch/arm/boot/dts/armada-370.dtsi ++++ b/arch/arm/boot/dts/armada-370.dtsi +@@ -84,7 +84,7 @@ pcie0_intc: interrupt-controller { + + pcie2: pcie@2,0 { + device_type = "pci"; +- assigned-addresses = <0x82002800 0 0x80000 0 0x2000>; ++ assigned-addresses = <0x82001000 0 0x80000 0 0x2000>; + reg = <0x1000 0 0 0 0>; + #address-cells = <3>; + #size-cells = <2>; +-- +2.34.1 + diff --git a/pkgs/patches-linux-5.15/0059-ARM-dts-armada-xp-Fix-assigned-addresses-for-every-P.patch b/pkgs/patches-linux-5.15/0059-ARM-dts-armada-xp-Fix-assigned-addresses-for-every-P.patch new file mode 100644 index 0000000..018ed65 --- /dev/null +++ b/pkgs/patches-linux-5.15/0059-ARM-dts-armada-xp-Fix-assigned-addresses-for-every-P.patch @@ -0,0 +1,141 @@ +From f0c729c073b7436ae8bcdd1dfb68d45787e8ae6d Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org> +Date: Thu, 18 Aug 2022 00:01:47 +0200 +Subject: [PATCH 59/90] ARM: dts: armada-xp: Fix assigned-addresses for every + PCIe Root Port +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +BDF of resource in DT assigned-addresses property of Marvell PCIe Root Port +(PCI-to-PCI bridge) should match BDF in address part in that DT node name +as specified resource belongs to Marvell PCIe Root Port itself. + +Fixes: 9d8f44f02d4a ("arm: mvebu: add PCIe Device Tree informations for Armada XP") +Fixes: 12b69a599745 ("ARM: mvebu: second PCIe unit of Armada XP mv78230 is only x1 capable") +Fixes: 2163e61c92d9 ("ARM: mvebu: fix second and third PCIe unit of Armada XP mv78260") +Signed-off-by: Pali Rohár <pali@kernel.org> +--- + arch/arm/boot/dts/armada-xp-mv78230.dtsi | 8 ++++---- + arch/arm/boot/dts/armada-xp-mv78260.dtsi | 16 ++++++++-------- + 2 files changed, 12 insertions(+), 12 deletions(-) + +diff --git a/arch/arm/boot/dts/armada-xp-mv78230.dtsi b/arch/arm/boot/dts/armada-xp-mv78230.dtsi +index bf9360f41e0a..5ea9d509cd30 100644 +--- a/arch/arm/boot/dts/armada-xp-mv78230.dtsi ++++ b/arch/arm/boot/dts/armada-xp-mv78230.dtsi +@@ -107,7 +107,7 @@ pcie1_intc: interrupt-controller { + + pcie2: pcie@2,0 { + device_type = "pci"; +- assigned-addresses = <0x82000800 0 0x44000 0 0x2000>; ++ assigned-addresses = <0x82001000 0 0x44000 0 0x2000>; + reg = <0x1000 0 0 0 0>; + #address-cells = <3>; + #size-cells = <2>; +@@ -135,7 +135,7 @@ pcie2_intc: interrupt-controller { + + pcie3: pcie@3,0 { + device_type = "pci"; +- assigned-addresses = <0x82000800 0 0x48000 0 0x2000>; ++ assigned-addresses = <0x82001800 0 0x48000 0 0x2000>; + reg = <0x1800 0 0 0 0>; + #address-cells = <3>; + #size-cells = <2>; +@@ -163,7 +163,7 @@ pcie3_intc: interrupt-controller { + + pcie4: pcie@4,0 { + device_type = "pci"; +- assigned-addresses = <0x82000800 0 0x4c000 0 0x2000>; ++ assigned-addresses = <0x82002000 0 0x4c000 0 0x2000>; + reg = <0x2000 0 0 0 0>; + #address-cells = <3>; + #size-cells = <2>; +@@ -191,7 +191,7 @@ pcie4_intc: interrupt-controller { + + pcie5: pcie@5,0 { + device_type = "pci"; +- assigned-addresses = <0x82000800 0 0x80000 0 0x2000>; ++ assigned-addresses = <0x82002800 0 0x80000 0 0x2000>; + reg = <0x2800 0 0 0 0>; + #address-cells = <3>; + #size-cells = <2>; +diff --git a/arch/arm/boot/dts/armada-xp-mv78260.dtsi b/arch/arm/boot/dts/armada-xp-mv78260.dtsi +index 0714af52e607..6c6fbb9faf5a 100644 +--- a/arch/arm/boot/dts/armada-xp-mv78260.dtsi ++++ b/arch/arm/boot/dts/armada-xp-mv78260.dtsi +@@ -122,7 +122,7 @@ pcie1_intc: interrupt-controller { + + pcie2: pcie@2,0 { + device_type = "pci"; +- assigned-addresses = <0x82000800 0 0x44000 0 0x2000>; ++ assigned-addresses = <0x82001000 0 0x44000 0 0x2000>; + reg = <0x1000 0 0 0 0>; + #address-cells = <3>; + #size-cells = <2>; +@@ -150,7 +150,7 @@ pcie2_intc: interrupt-controller { + + pcie3: pcie@3,0 { + device_type = "pci"; +- assigned-addresses = <0x82000800 0 0x48000 0 0x2000>; ++ assigned-addresses = <0x82001800 0 0x48000 0 0x2000>; + reg = <0x1800 0 0 0 0>; + #address-cells = <3>; + #size-cells = <2>; +@@ -178,7 +178,7 @@ pcie3_intc: interrupt-controller { + + pcie4: pcie@4,0 { + device_type = "pci"; +- assigned-addresses = <0x82000800 0 0x4c000 0 0x2000>; ++ assigned-addresses = <0x82002000 0 0x4c000 0 0x2000>; + reg = <0x2000 0 0 0 0>; + #address-cells = <3>; + #size-cells = <2>; +@@ -206,7 +206,7 @@ pcie4_intc: interrupt-controller { + + pcie5: pcie@5,0 { + device_type = "pci"; +- assigned-addresses = <0x82000800 0 0x80000 0 0x2000>; ++ assigned-addresses = <0x82002800 0 0x80000 0 0x2000>; + reg = <0x2800 0 0 0 0>; + #address-cells = <3>; + #size-cells = <2>; +@@ -234,7 +234,7 @@ pcie5_intc: interrupt-controller { + + pcie6: pcie@6,0 { + device_type = "pci"; +- assigned-addresses = <0x82000800 0 0x84000 0 0x2000>; ++ assigned-addresses = <0x82003000 0 0x84000 0 0x2000>; + reg = <0x3000 0 0 0 0>; + #address-cells = <3>; + #size-cells = <2>; +@@ -262,7 +262,7 @@ pcie6_intc: interrupt-controller { + + pcie7: pcie@7,0 { + device_type = "pci"; +- assigned-addresses = <0x82000800 0 0x88000 0 0x2000>; ++ assigned-addresses = <0x82003800 0 0x88000 0 0x2000>; + reg = <0x3800 0 0 0 0>; + #address-cells = <3>; + #size-cells = <2>; +@@ -290,7 +290,7 @@ pcie7_intc: interrupt-controller { + + pcie8: pcie@8,0 { + device_type = "pci"; +- assigned-addresses = <0x82000800 0 0x8c000 0 0x2000>; ++ assigned-addresses = <0x82004000 0 0x8c000 0 0x2000>; + reg = <0x4000 0 0 0 0>; + #address-cells = <3>; + #size-cells = <2>; +@@ -318,7 +318,7 @@ pcie8_intc: interrupt-controller { + + pcie9: pcie@9,0 { + device_type = "pci"; +- assigned-addresses = <0x82000800 0 0x42000 0 0x2000>; ++ assigned-addresses = <0x82004800 0 0x42000 0 0x2000>; + reg = <0x4800 0 0 0 0>; + #address-cells = <3>; + #size-cells = <2>; +-- +2.34.1 + diff --git a/pkgs/patches-linux-5.15/0060-ARM-dts-armada-375-Fix-assigned-addresses-for-every-.patch b/pkgs/patches-linux-5.15/0060-ARM-dts-armada-375-Fix-assigned-addresses-for-every-.patch new file mode 100644 index 0000000..d87ae0f --- /dev/null +++ b/pkgs/patches-linux-5.15/0060-ARM-dts-armada-375-Fix-assigned-addresses-for-every-.patch @@ -0,0 +1,35 @@ +From 5664d7274ba0c37e52b0aa6c2639866dc2785baf Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org> +Date: Thu, 18 Aug 2022 00:02:33 +0200 +Subject: [PATCH 60/90] ARM: dts: armada-375: Fix assigned-addresses for every + PCIe Root Port +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +BDF of resource in DT assigned-addresses property of Marvell PCIe Root Port +(PCI-to-PCI bridge) should match BDF in address part in that DT node name +as specified resource belongs to Marvell PCIe Root Port itself. + +Fixes: 4de59085091f ("ARM: mvebu: add Device Tree description of the Armada 375 SoC") +Signed-off-by: Pali Rohár <pali@kernel.org> +--- + arch/arm/boot/dts/armada-375.dtsi | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/arch/arm/boot/dts/armada-375.dtsi b/arch/arm/boot/dts/armada-375.dtsi +index 929deaf312a5..c310ef26d1cc 100644 +--- a/arch/arm/boot/dts/armada-375.dtsi ++++ b/arch/arm/boot/dts/armada-375.dtsi +@@ -592,7 +592,7 @@ pcie0_intc: interrupt-controller { + + pcie1: pcie@2,0 { + device_type = "pci"; +- assigned-addresses = <0x82000800 0 0x44000 0 0x2000>; ++ assigned-addresses = <0x82001000 0 0x44000 0 0x2000>; + reg = <0x1000 0 0 0 0>; + #address-cells = <3>; + #size-cells = <2>; +-- +2.34.1 + diff --git a/pkgs/patches-linux-5.15/0061-ARM-dts-armada-38x-Fix-assigned-addresses-for-every-.patch b/pkgs/patches-linux-5.15/0061-ARM-dts-armada-38x-Fix-assigned-addresses-for-every-.patch new file mode 100644 index 0000000..5bdd431 --- /dev/null +++ b/pkgs/patches-linux-5.15/0061-ARM-dts-armada-38x-Fix-assigned-addresses-for-every-.patch @@ -0,0 +1,76 @@ +From 31b9db5d6e0fc33b81ea8409c32294731238e5b1 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org> +Date: Thu, 18 Aug 2022 00:03:45 +0200 +Subject: [PATCH 61/90] ARM: dts: armada-38x: Fix assigned-addresses for every + PCIe Root Port +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +BDF of resource in DT assigned-addresses property of Marvell PCIe Root Port +(PCI-to-PCI bridge) should match BDF in address part in that DT node name +as specified resource belongs to Marvell PCIe Root Port itself. + +Fixes: 0d3d96ab0059 ("ARM: mvebu: add Device Tree description of the Armada 380/385 SoCs") +Signed-off-by: Pali Rohár <pali@kernel.org> +--- + arch/arm/boot/dts/armada-380.dtsi | 4 ++-- + arch/arm/boot/dts/armada-385.dtsi | 6 +++--- + 2 files changed, 5 insertions(+), 5 deletions(-) + +diff --git a/arch/arm/boot/dts/armada-380.dtsi b/arch/arm/boot/dts/armada-380.dtsi +index ce1dddb2269b..e94f22b0e9b5 100644 +--- a/arch/arm/boot/dts/armada-380.dtsi ++++ b/arch/arm/boot/dts/armada-380.dtsi +@@ -89,7 +89,7 @@ pcie1_intc: interrupt-controller { + /* x1 port */ + pcie@2,0 { + device_type = "pci"; +- assigned-addresses = <0x82000800 0 0x40000 0 0x2000>; ++ assigned-addresses = <0x82001000 0 0x40000 0 0x2000>; + reg = <0x1000 0 0 0 0>; + #address-cells = <3>; + #size-cells = <2>; +@@ -118,7 +118,7 @@ pcie2_intc: interrupt-controller { + /* x1 port */ + pcie@3,0 { + device_type = "pci"; +- assigned-addresses = <0x82000800 0 0x44000 0 0x2000>; ++ assigned-addresses = <0x82001800 0 0x44000 0 0x2000>; + reg = <0x1800 0 0 0 0>; + #address-cells = <3>; + #size-cells = <2>; +diff --git a/arch/arm/boot/dts/armada-385.dtsi b/arch/arm/boot/dts/armada-385.dtsi +index 83392b92dae2..be8d607c59b2 100644 +--- a/arch/arm/boot/dts/armada-385.dtsi ++++ b/arch/arm/boot/dts/armada-385.dtsi +@@ -93,7 +93,7 @@ pcie1_intc: interrupt-controller { + /* x1 port */ + pcie2: pcie@2,0 { + device_type = "pci"; +- assigned-addresses = <0x82000800 0 0x40000 0 0x2000>; ++ assigned-addresses = <0x82001000 0 0x40000 0 0x2000>; + reg = <0x1000 0 0 0 0>; + #address-cells = <3>; + #size-cells = <2>; +@@ -121,7 +121,7 @@ pcie2_intc: interrupt-controller { + /* x1 port */ + pcie3: pcie@3,0 { + device_type = "pci"; +- assigned-addresses = <0x82000800 0 0x44000 0 0x2000>; ++ assigned-addresses = <0x82001800 0 0x44000 0 0x2000>; + reg = <0x1800 0 0 0 0>; + #address-cells = <3>; + #size-cells = <2>; +@@ -152,7 +152,7 @@ pcie3_intc: interrupt-controller { + */ + pcie4: pcie@4,0 { + device_type = "pci"; +- assigned-addresses = <0x82000800 0 0x48000 0 0x2000>; ++ assigned-addresses = <0x82002000 0 0x48000 0 0x2000>; + reg = <0x2000 0 0 0 0>; + #address-cells = <3>; + #size-cells = <2>; +-- +2.34.1 + diff --git a/pkgs/patches-linux-5.15/0062-ARM-dts-armada-39x-Fix-assigned-addresses-for-every-.patch b/pkgs/patches-linux-5.15/0062-ARM-dts-armada-39x-Fix-assigned-addresses-for-every-.patch new file mode 100644 index 0000000..849b2cf --- /dev/null +++ b/pkgs/patches-linux-5.15/0062-ARM-dts-armada-39x-Fix-assigned-addresses-for-every-.patch @@ -0,0 +1,53 @@ +From da049d058681865c4f31e393168e76a911e2d6df Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org> +Date: Thu, 18 Aug 2022 00:04:33 +0200 +Subject: [PATCH 62/90] ARM: dts: armada-39x: Fix assigned-addresses for every + PCIe Root Port +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +BDF of resource in DT assigned-addresses property of Marvell PCIe Root Port +(PCI-to-PCI bridge) should match BDF in address part in that DT node name +as specified resource belongs to Marvell PCIe Root Port itself. + +Fixes: 538da83ddbea ("ARM: mvebu: add Device Tree files for Armada 39x SoC and board") +Signed-off-by: Pali Rohár <pali@kernel.org> +--- + arch/arm/boot/dts/armada-39x.dtsi | 6 +++--- + 1 file changed, 3 insertions(+), 3 deletions(-) + +diff --git a/arch/arm/boot/dts/armada-39x.dtsi b/arch/arm/boot/dts/armada-39x.dtsi +index 923b035a3ab3..9d1cac49c022 100644 +--- a/arch/arm/boot/dts/armada-39x.dtsi ++++ b/arch/arm/boot/dts/armada-39x.dtsi +@@ -463,7 +463,7 @@ pcie1_intc: interrupt-controller { + /* x1 port */ + pcie@2,0 { + device_type = "pci"; +- assigned-addresses = <0x82000800 0 0x40000 0 0x2000>; ++ assigned-addresses = <0x82001000 0 0x40000 0 0x2000>; + reg = <0x1000 0 0 0 0>; + #address-cells = <3>; + #size-cells = <2>; +@@ -492,7 +492,7 @@ pcie2_intc: interrupt-controller { + /* x1 port */ + pcie@3,0 { + device_type = "pci"; +- assigned-addresses = <0x82000800 0 0x44000 0 0x2000>; ++ assigned-addresses = <0x82001800 0 0x44000 0 0x2000>; + reg = <0x1800 0 0 0 0>; + #address-cells = <3>; + #size-cells = <2>; +@@ -524,7 +524,7 @@ pcie3_intc: interrupt-controller { + */ + pcie@4,0 { + device_type = "pci"; +- assigned-addresses = <0x82000800 0 0x48000 0 0x2000>; ++ assigned-addresses = <0x82002000 0 0x48000 0 0x2000>; + reg = <0x2000 0 0 0 0>; + #address-cells = <3>; + #size-cells = <2>; +-- +2.34.1 + diff --git a/pkgs/patches-linux-5.15/0063-irqchip-armada-370-xp-Do-not-call-ipi_resume-when-IP.patch b/pkgs/patches-linux-5.15/0063-irqchip-armada-370-xp-Do-not-call-ipi_resume-when-IP.patch new file mode 100644 index 0000000..867e6dd --- /dev/null +++ b/pkgs/patches-linux-5.15/0063-irqchip-armada-370-xp-Do-not-call-ipi_resume-when-IP.patch @@ -0,0 +1,49 @@ +From 748cb95b78593cba8dd0abbbb7d466322c46bde2 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org> +Date: Thu, 11 Aug 2022 10:57:05 +0200 +Subject: [PATCH 63/90] irqchip/armada-370-xp: Do not call ipi_resume() when + IPI is not used +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +When IPI is not used then ipi_resume() does nothing. IPI is used only on +systems where mpic controller does not parent GIC IRQ (e.g. on Armada XP). +For documentation purpose add condition when ipi_resume() should be called. +This simplify understanding of irq-armada-370-xp.c driver when individual +driver functions are called when they do something. No functional change. + +Signed-off-by: Pali Rohár <pali@kernel.org> +--- + drivers/irqchip/irq-armada-370-xp.c | 8 ++++++-- + 1 file changed, 6 insertions(+), 2 deletions(-) + +diff --git a/drivers/irqchip/irq-armada-370-xp.c b/drivers/irqchip/irq-armada-370-xp.c +index 3fa6bd70684b..11b6ce81d17e 100644 +--- a/drivers/irqchip/irq-armada-370-xp.c ++++ b/drivers/irqchip/irq-armada-370-xp.c +@@ -509,7 +509,9 @@ static void armada_xp_mpic_reenable_percpu(void) + armada_370_xp_irq_unmask(data); + } + +- ipi_resume(); ++ /* IPI is used only when we do not have parent irq */ ++ if (parent_irq <= 0) ++ ipi_resume(); + } + + static int armada_xp_mpic_starting_cpu(unsigned int cpu) +@@ -735,7 +737,9 @@ static void armada_370_xp_mpic_resume(void) + if (doorbell_mask_reg & PCI_MSI_DOORBELL_MASK) + writel(1, per_cpu_int_base + ARMADA_370_XP_INT_CLEAR_MASK_OFFS); + +- ipi_resume(); ++ /* IPI is used only when we do not have parent irq */ ++ if (parent_irq <= 0) ++ ipi_resume(); + } + + static struct syscore_ops armada_370_xp_mpic_syscore_ops = { +-- +2.34.1 + diff --git a/pkgs/patches-linux-5.15/0064-irqchip-armada-370-xp-Fix-comment-about-unmasking-mp.patch b/pkgs/patches-linux-5.15/0064-irqchip-armada-370-xp-Fix-comment-about-unmasking-mp.patch new file mode 100644 index 0000000..e50699b --- /dev/null +++ b/pkgs/patches-linux-5.15/0064-irqchip-armada-370-xp-Fix-comment-about-unmasking-mp.patch @@ -0,0 +1,35 @@ +From e5a5d4fd63c10ff98847e9c1cfa3c6161f4b69e8 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org> +Date: Thu, 11 Aug 2022 11:15:56 +0200 +Subject: [PATCH 64/90] irqchip/armada-370-xp: Fix comment about unmasking mpic + source 1 +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +mpic source 1 unmasks MSI interrupts, not IPI interrupt (IPI has source 0). +Function armada_370_xp_msi_init() is used for initializing MSI interrupts, +so code is correct (it should enable MSI interrupts; not IPI), just comment +was wrong. + +Signed-off-by: Pali Rohár <pali@kernel.org> +--- + drivers/irqchip/irq-armada-370-xp.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/drivers/irqchip/irq-armada-370-xp.c b/drivers/irqchip/irq-armada-370-xp.c +index 11b6ce81d17e..9b28e6269176 100644 +--- a/drivers/irqchip/irq-armada-370-xp.c ++++ b/drivers/irqchip/irq-armada-370-xp.c +@@ -293,7 +293,7 @@ static int armada_370_xp_msi_init(struct device_node *node, + writel(reg, per_cpu_int_base + + ARMADA_370_XP_IN_DRBEL_MSK_OFFS); + +- /* Unmask IPI interrupt */ ++ /* Unmask MSI interrupt */ + writel(1, per_cpu_int_base + ARMADA_370_XP_INT_CLEAR_MASK_OFFS); + + return 0; +-- +2.34.1 + diff --git a/pkgs/patches-linux-5.15/0065-irqchip-armada-370-xp-Do-not-touch-IPI-registers-on-.patch b/pkgs/patches-linux-5.15/0065-irqchip-armada-370-xp-Do-not-touch-IPI-registers-on-.patch new file mode 100644 index 0000000..038ce8e --- /dev/null +++ b/pkgs/patches-linux-5.15/0065-irqchip-armada-370-xp-Do-not-touch-IPI-registers-on-.patch @@ -0,0 +1,67 @@ +From bfe14db697196f06c0a7abe7f0d44edac04f0c11 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org> +Date: Thu, 11 Aug 2022 11:35:53 +0200 +Subject: [PATCH 65/90] irqchip/armada-370-xp: Do not touch IPI registers on + platforms without IPI +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +IPI is used only when we do not have parent irq. On platforms with parent +irq are those IPI registers used for additional set of MSI interrupts +(which are currently unused). So do not touch these registers when IPI is +not used. + +Signed-off-by: Pali Rohár <pali@kernel.org> +--- + drivers/irqchip/irq-armada-370-xp.c | 14 ++++++++++++-- + 1 file changed, 12 insertions(+), 2 deletions(-) + +diff --git a/drivers/irqchip/irq-armada-370-xp.c b/drivers/irqchip/irq-armada-370-xp.c +index 9b28e6269176..397bacf638a2 100644 +--- a/drivers/irqchip/irq-armada-370-xp.c ++++ b/drivers/irqchip/irq-armada-370-xp.c +@@ -478,6 +478,10 @@ static void armada_xp_mpic_smp_cpu_init(void) + for (i = 0; i < nr_irqs; i++) + writel(i, per_cpu_int_base + ARMADA_370_XP_INT_SET_MASK_OFFS); + ++ /* IPI is not used when we do have parent irq */ ++ if (parent_irq > 0) ++ return; ++ + /* Disable all IPIs */ + writel(0, per_cpu_int_base + ARMADA_370_XP_IN_DRBEL_MSK_OFFS); + +@@ -732,7 +736,8 @@ static void armada_370_xp_mpic_resume(void) + /* Reconfigure doorbells for IPIs and MSIs */ + writel(doorbell_mask_reg, + per_cpu_int_base + ARMADA_370_XP_IN_DRBEL_MSK_OFFS); +- if (doorbell_mask_reg & IPI_DOORBELL_MASK) ++ /* IPI is used only when we do not have parent irq */ ++ if (parent_irq <= 0 && (doorbell_mask_reg & IPI_DOORBELL_MASK)) + writel(0, per_cpu_int_base + ARMADA_370_XP_INT_CLEAR_MASK_OFFS); + if (doorbell_mask_reg & PCI_MSI_DOORBELL_MASK) + writel(1, per_cpu_int_base + ARMADA_370_XP_INT_CLEAR_MASK_OFFS); +@@ -784,13 +789,18 @@ static int __init armada_370_xp_mpic_of_init(struct device_node *node, + BUG_ON(!armada_370_xp_mpic_domain); + irq_domain_update_bus_token(armada_370_xp_mpic_domain, DOMAIN_BUS_WIRED); + ++ /* ++ * parent_irq is used for distinguish between IPI and non-IPI platforms. ++ * So initialize it before calling any other driver functions. ++ */ ++ parent_irq = irq_of_parse_and_map(node, 0); ++ + /* Setup for the boot CPU */ + armada_xp_mpic_perf_init(); + armada_xp_mpic_smp_cpu_init(); + + armada_370_xp_msi_init(node, main_int_res.start); + +- parent_irq = irq_of_parse_and_map(node, 0); + if (parent_irq <= 0) { + irq_set_default_host(armada_370_xp_mpic_domain); + set_handle_irq(armada_370_xp_handle_irq); +-- +2.34.1 + diff --git a/pkgs/patches-linux-5.15/0066-irqchip-armada-370-xp-Add-support-for-32-MSI-interru.patch b/pkgs/patches-linux-5.15/0066-irqchip-armada-370-xp-Add-support-for-32-MSI-interru.patch new file mode 100644 index 0000000..0eefb9a --- /dev/null +++ b/pkgs/patches-linux-5.15/0066-irqchip-armada-370-xp-Add-support-for-32-MSI-interru.patch @@ -0,0 +1,181 @@ +From adf4ef16f1869c4f8a35bfb95ce95e0d418f72b9 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org> +Date: Thu, 11 Aug 2022 13:16:26 +0200 +Subject: [PATCH 66/90] irqchip/armada-370-xp: Add support for 32 MSI + interrupts on non-IPI platforms +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Currently only upper 16 MSI interrupts on non-IPI platforms are used. +Low 16 MSI interrupts on non-IPI platforms are mapped into IPI registers. +Implement support also for low 16 MSI interrupts which allow increase +number of MSI interrupts on non-IPI platforms from 16 to 32. + +Signed-off-by: Pali Rohár <pali@kernel.org> +--- + drivers/irqchip/irq-armada-370-xp.c | 54 +++++++++++++++++++++-------- + 1 file changed, 40 insertions(+), 14 deletions(-) + +diff --git a/drivers/irqchip/irq-armada-370-xp.c b/drivers/irqchip/irq-armada-370-xp.c +index 397bacf638a2..8abc70ed30c1 100644 +--- a/drivers/irqchip/irq-armada-370-xp.c ++++ b/drivers/irqchip/irq-armada-370-xp.c +@@ -135,6 +135,7 @@ + + #define ARMADA_370_XP_MAX_PER_CPU_IRQS (28) + ++/* IPI and MSI interrupt definitions for IPI platforms */ + #define IPI_DOORBELL_START (0) + #define IPI_DOORBELL_END (8) + #define IPI_DOORBELL_MASK 0xFF +@@ -143,6 +144,12 @@ + #define PCI_MSI_DOORBELL_END (32) + #define PCI_MSI_DOORBELL_MASK 0xFFFF0000 + ++/* MSI interrupt definitions for non-IPI platforms */ ++#define PCI_MSI_FULL_DOORBELL_START (0) ++#define PCI_MSI_FULL_DOORBELL_NR (32) ++#define PCI_MSI_FULL_DOORBELL_END (32) ++#define PCI_MSI_FULL_DOORBELL_MASK (0xFFFFFFFF) ++ + static void __iomem *per_cpu_int_base; + static void __iomem *main_int_base; + static struct irq_domain *armada_370_xp_mpic_domain; +@@ -151,7 +158,7 @@ static int parent_irq; + #ifdef CONFIG_PCI_MSI + static struct irq_domain *armada_370_xp_msi_domain; + static struct irq_domain *armada_370_xp_msi_inner_domain; +-static DECLARE_BITMAP(msi_used, PCI_MSI_DOORBELL_NR); ++static DECLARE_BITMAP(msi_used, PCI_MSI_FULL_DOORBELL_NR); + static DEFINE_MUTEX(msi_used_lock); + static phys_addr_t msi_doorbell_addr; + #endif +@@ -209,9 +216,10 @@ static struct msi_domain_info armada_370_xp_msi_domain_info = { + + static void armada_370_xp_compose_msi_msg(struct irq_data *data, struct msi_msg *msg) + { ++ u32 msi_start = (parent_irq <= 0) ? PCI_MSI_DOORBELL_START : PCI_MSI_FULL_DOORBELL_START; + msg->address_lo = lower_32_bits(msi_doorbell_addr); + msg->address_hi = upper_32_bits(msi_doorbell_addr); +- msg->data = 0xf00 | (data->hwirq + PCI_MSI_DOORBELL_START); ++ msg->data = 0xf00 | (data->hwirq + msi_start); + } + + static int armada_370_xp_msi_set_affinity(struct irq_data *irq_data, +@@ -229,10 +237,11 @@ static struct irq_chip armada_370_xp_msi_bottom_irq_chip = { + static int armada_370_xp_msi_alloc(struct irq_domain *domain, unsigned int virq, + unsigned int nr_irqs, void *args) + { ++ unsigned int msi_nr = (parent_irq <= 0) ? PCI_MSI_DOORBELL_NR : PCI_MSI_FULL_DOORBELL_NR; + int hwirq, i; + + mutex_lock(&msi_used_lock); +- hwirq = bitmap_find_free_region(msi_used, PCI_MSI_DOORBELL_NR, ++ hwirq = bitmap_find_free_region(msi_used, msi_nr, + order_base_2(nr_irqs)); + mutex_unlock(&msi_used_lock); + +@@ -267,13 +276,15 @@ static const struct irq_domain_ops armada_370_xp_msi_domain_ops = { + static int armada_370_xp_msi_init(struct device_node *node, + phys_addr_t main_int_phys_base) + { ++ unsigned int msi_nr = (parent_irq <= 0) ? PCI_MSI_DOORBELL_NR : PCI_MSI_FULL_DOORBELL_NR; ++ u32 msi_mask = (parent_irq <= 0) ? PCI_MSI_DOORBELL_MASK: PCI_MSI_FULL_DOORBELL_MASK; + u32 reg; + + msi_doorbell_addr = main_int_phys_base + + ARMADA_370_XP_SW_TRIG_INT_OFFS; + + armada_370_xp_msi_inner_domain = +- irq_domain_add_linear(NULL, PCI_MSI_DOORBELL_NR, ++ irq_domain_add_linear(NULL, msi_nr, + &armada_370_xp_msi_domain_ops, NULL); + if (!armada_370_xp_msi_inner_domain) + return -ENOMEM; +@@ -288,7 +299,7 @@ static int armada_370_xp_msi_init(struct device_node *node, + } + + reg = readl(per_cpu_int_base + ARMADA_370_XP_IN_DRBEL_MSK_OFFS) +- | PCI_MSI_DOORBELL_MASK; ++ | msi_mask; + + writel(reg, per_cpu_int_base + + ARMADA_370_XP_IN_DRBEL_MSK_OFFS); +@@ -296,6 +307,10 @@ static int armada_370_xp_msi_init(struct device_node *node, + /* Unmask MSI interrupt */ + writel(1, per_cpu_int_base + ARMADA_370_XP_INT_CLEAR_MASK_OFFS); + ++ /* Unmask low 16 MSI irqs on non-IPI platforms */ ++ if (parent_irq > 0) ++ writel(0, per_cpu_int_base + ARMADA_370_XP_INT_CLEAR_MASK_OFFS); ++ + return 0; + } + #else +@@ -586,23 +601,25 @@ static const struct irq_domain_ops armada_370_xp_mpic_irq_ops = { + #ifdef CONFIG_PCI_MSI + static void armada_370_xp_handle_msi_irq(struct pt_regs *regs, bool is_chained) + { ++ u32 msi_start = (parent_irq <= 0) ? PCI_MSI_DOORBELL_START : PCI_MSI_FULL_DOORBELL_START; ++ u32 msi_end = (parent_irq <= 0) ? PCI_MSI_DOORBELL_END : PCI_MSI_FULL_DOORBELL_END; ++ u32 msi_mask = (parent_irq <= 0) ? PCI_MSI_DOORBELL_MASK: PCI_MSI_FULL_DOORBELL_MASK; + u32 msimask, msinr; + + msimask = readl_relaxed(per_cpu_int_base + + ARMADA_370_XP_IN_DRBEL_CAUSE_OFFS) +- & PCI_MSI_DOORBELL_MASK; ++ & msi_mask; + + writel(~msimask, per_cpu_int_base + + ARMADA_370_XP_IN_DRBEL_CAUSE_OFFS); + +- for (msinr = PCI_MSI_DOORBELL_START; +- msinr < PCI_MSI_DOORBELL_END; msinr++) { ++ for (msinr = msi_start; msinr < msi_end; msinr++) { + unsigned int irq; + + if (!(msimask & BIT(msinr))) + continue; + +- irq = msinr - PCI_MSI_DOORBELL_START; ++ irq = msinr - msi_start; + + if (is_chained) + generic_handle_domain_irq(armada_370_xp_msi_inner_domain, +@@ -636,7 +653,7 @@ static void armada_370_xp_mpic_handle_cascade_irq(struct irq_desc *desc) + if (!(irqsrc & ARMADA_370_XP_INT_IRQ_FIQ_MASK(cpuid))) + continue; + +- if (irqn == 1) { ++ if (irqn == 0 || irqn == 1) { + armada_370_xp_handle_msi_irq(NULL, true); + continue; + } +@@ -737,10 +754,19 @@ static void armada_370_xp_mpic_resume(void) + writel(doorbell_mask_reg, + per_cpu_int_base + ARMADA_370_XP_IN_DRBEL_MSK_OFFS); + /* IPI is used only when we do not have parent irq */ +- if (parent_irq <= 0 && (doorbell_mask_reg & IPI_DOORBELL_MASK)) +- writel(0, per_cpu_int_base + ARMADA_370_XP_INT_CLEAR_MASK_OFFS); +- if (doorbell_mask_reg & PCI_MSI_DOORBELL_MASK) +- writel(1, per_cpu_int_base + ARMADA_370_XP_INT_CLEAR_MASK_OFFS); ++ if (parent_irq <= 0) { ++ /* On IPI platforms is source 0 used for IPI and source 1 for MSI */ ++ if (doorbell_mask_reg & IPI_DOORBELL_MASK) ++ writel(0, per_cpu_int_base + ARMADA_370_XP_INT_CLEAR_MASK_OFFS); ++ if (doorbell_mask_reg & PCI_MSI_DOORBELL_MASK) ++ writel(1, per_cpu_int_base + ARMADA_370_XP_INT_CLEAR_MASK_OFFS); ++ } else { ++ /* On non-IPI platforms is source 0 used for MSI 0-15 and 1 for MSI 16-31 */ ++ if (doorbell_mask_reg & GENMASK(15, 0)) ++ writel(0, per_cpu_int_base + ARMADA_370_XP_INT_CLEAR_MASK_OFFS); ++ if (doorbell_mask_reg & GENMASK(31, 16)) ++ writel(1, per_cpu_int_base + ARMADA_370_XP_INT_CLEAR_MASK_OFFS); ++ } + + /* IPI is used only when we do not have parent irq */ + if (parent_irq <= 0) +-- +2.34.1 + diff --git a/pkgs/patches-linux-5.15/0067-dt-bindings-PCI-mvebu-Update-information-about-error.patch b/pkgs/patches-linux-5.15/0067-dt-bindings-PCI-mvebu-Update-information-about-error.patch new file mode 100644 index 0000000..c0e08dd --- /dev/null +++ b/pkgs/patches-linux-5.15/0067-dt-bindings-PCI-mvebu-Update-information-about-error.patch @@ -0,0 +1,32 @@ +From 71d68bf335708fe14724bd520d72afcd71ee56dd Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org> +Date: Tue, 2 Nov 2021 11:06:18 +0100 +Subject: [PATCH 67/90] dt-bindings: PCI: mvebu: Update information about error + interrupt +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +mvebu error interrupt is triggered by any non-intx event, which is mainly +some pcie error. + +Signed-off-by: Pali Rohár <pali@kernel.org> +--- + Documentation/devicetree/bindings/pci/mvebu-pci.txt | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/Documentation/devicetree/bindings/pci/mvebu-pci.txt b/Documentation/devicetree/bindings/pci/mvebu-pci.txt +index 6d022a9d36ee..8f0bca42113f 100644 +--- a/Documentation/devicetree/bindings/pci/mvebu-pci.txt ++++ b/Documentation/devicetree/bindings/pci/mvebu-pci.txt +@@ -83,6 +83,7 @@ and the following optional properties: + specified will default to 100ms, as required by the PCIe specification. + - interrupt-names: list of interrupt names, supported are: + - "intx" - interrupt line triggered by one of the legacy interrupt ++ - "error" - interrupt line triggered by any other event (non-intx) + - interrupts or interrupts-extended: List of the interrupt sources which + corresponding to the "interrupt-names". If non-empty then also additional + 'interrupt-controller' subnode must be defined. +-- +2.34.1 + diff --git a/pkgs/patches-linux-5.15/0068-PCI-mvebu-Implement-support-for-interrupts-on-emulat.patch b/pkgs/patches-linux-5.15/0068-PCI-mvebu-Implement-support-for-interrupts-on-emulat.patch new file mode 100644 index 0000000..a26cd8a --- /dev/null +++ b/pkgs/patches-linux-5.15/0068-PCI-mvebu-Implement-support-for-interrupts-on-emulat.patch @@ -0,0 +1,437 @@ +From 580e9959e0110a244ffec06c4007921737955748 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org> +Date: Fri, 17 Sep 2021 14:40:17 +0200 +Subject: [PATCH 68/90] PCI: mvebu: Implement support for interrupts on + emulated bridge +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +This adds support for PME and ERR interrupts reported by emulated bridge +(for PME and AER kernel drivers) via new Root Port irq chip as these +interrupts from PCIe Root Ports are handled by mvebu hardware completely +separately from INTx and MSI interrupts send by real PCIe devices. + +With this change, kernel PME and AER drivers start working as they can +acquire required interrupt lines (provided by mvebu rp virtual irq chip). + +Note that for this support, device tree files has to be properly adjusted +to provide "interrupts" or "interrupts-extended" property with error +interrupt source and "interrupt-names" property with "error" string. + +If device tree files do not provide these properties then driver would work +as before and would not provide interrupts on emulated bridge, like before. + +Signed-off-by: Pali Rohár <pali@kernel.org> +--- + drivers/pci/controller/pci-mvebu.c | 256 ++++++++++++++++++++++++++--- + 1 file changed, 237 insertions(+), 19 deletions(-) + +diff --git a/drivers/pci/controller/pci-mvebu.c b/drivers/pci/controller/pci-mvebu.c +index c5e5bc085a9d..319cd2b98545 100644 +--- a/drivers/pci/controller/pci-mvebu.c ++++ b/drivers/pci/controller/pci-mvebu.c +@@ -56,8 +56,16 @@ + #define PCIE_CONF_DATA_OFF 0x18fc + #define PCIE_INT_CAUSE_OFF 0x1900 + #define PCIE_INT_UNMASK_OFF 0x1910 ++#define PCIE_INT_DET_COR BIT(8) ++#define PCIE_INT_DET_NONFATAL BIT(9) ++#define PCIE_INT_DET_FATAL BIT(10) ++#define PCIE_INT_ERR_FATAL BIT(16) ++#define PCIE_INT_ERR_NONFATAL BIT(17) ++#define PCIE_INT_ERR_COR BIT(18) + #define PCIE_INT_INTX(i) BIT(24+i) + #define PCIE_INT_PM_PME BIT(28) ++#define PCIE_INT_DET_MASK (PCIE_INT_DET_COR | PCIE_INT_DET_NONFATAL | PCIE_INT_DET_FATAL) ++#define PCIE_INT_ERR_MASK (PCIE_INT_ERR_FATAL | PCIE_INT_ERR_NONFATAL | PCIE_INT_ERR_COR) + #define PCIE_INT_ALL_MASK GENMASK(31, 0) + #define PCIE_CTRL_OFF 0x1a00 + #define PCIE_CTRL_X1_MODE 0x0001 +@@ -120,9 +128,12 @@ struct mvebu_pcie_port { + struct resource regs; + u8 slot_power_limit_value; + u8 slot_power_limit_scale; ++ struct irq_domain *rp_irq_domain; + struct irq_domain *intx_irq_domain; + raw_spinlock_t irq_lock; ++ int error_irq; + int intx_irq; ++ bool pme_pending; + }; + + static inline void mvebu_writel(struct mvebu_pcie_port *port, u32 val, u32 reg) +@@ -321,9 +332,19 @@ static void mvebu_pcie_setup_hw(struct mvebu_pcie_port *port) + /* Clear all interrupt causes. */ + mvebu_writel(port, ~PCIE_INT_ALL_MASK, PCIE_INT_CAUSE_OFF); + +- /* Check if "intx" interrupt was specified in DT. */ +- if (port->intx_irq > 0) +- return; ++ /* ++ * Unmask all error interrupts which are internally generated. ++ * They cannot be disabled by SERR# Enable bit in PCI Command register, ++ * see Figure 6-3: Pseudo Logic Diagram for Error Message Controls in ++ * PCIe base specification. ++ * Internally generated mvebu interrupts are reported via mvebu summary ++ * interrupt which requires "error" interrupt to be specified in DT. ++ */ ++ if (port->error_irq > 0) { ++ unmask = mvebu_readl(port, PCIE_INT_UNMASK_OFF); ++ unmask |= PCIE_INT_DET_MASK; ++ mvebu_writel(port, unmask, PCIE_INT_UNMASK_OFF); ++ } + + /* + * Fallback code when "intx" interrupt was not specified in DT: +@@ -335,10 +356,12 @@ static void mvebu_pcie_setup_hw(struct mvebu_pcie_port *port) + * performance penalty as every PCIe interrupt handler needs to be + * called when some interrupt is triggered. + */ +- unmask = mvebu_readl(port, PCIE_INT_UNMASK_OFF); +- unmask |= PCIE_INT_INTX(0) | PCIE_INT_INTX(1) | +- PCIE_INT_INTX(2) | PCIE_INT_INTX(3); +- mvebu_writel(port, unmask, PCIE_INT_UNMASK_OFF); ++ if (port->intx_irq <= 0) { ++ unmask = mvebu_readl(port, PCIE_INT_UNMASK_OFF); ++ unmask |= PCIE_INT_INTX(0) | PCIE_INT_INTX(1) | ++ PCIE_INT_INTX(2) | PCIE_INT_INTX(3); ++ mvebu_writel(port, unmask, PCIE_INT_UNMASK_OFF); ++ } + } + + static struct mvebu_pcie_port *mvebu_pcie_find_port(struct mvebu_pcie *pcie, +@@ -603,11 +626,16 @@ mvebu_pci_bridge_emul_base_conf_read(struct pci_bridge_emul *bridge, + case PCI_INTERRUPT_LINE: { + /* + * From the whole 32bit register we support reading from HW only +- * one bit: PCI_BRIDGE_CTL_BUS_RESET. ++ * two bits: PCI_BRIDGE_CTL_BUS_RESET and PCI_BRIDGE_CTL_SERR. + * Other bits are retrieved only from emulated config buffer. + */ + __le32 *cfgspace = (__le32 *)&bridge->conf; + u32 val = le32_to_cpu(cfgspace[PCI_INTERRUPT_LINE / 4]); ++ if ((mvebu_readl(port, PCIE_INT_UNMASK_OFF) & ++ PCIE_INT_ERR_MASK) == PCIE_INT_ERR_MASK) ++ val |= PCI_BRIDGE_CTL_SERR << 16; ++ else ++ val &= ~(PCI_BRIDGE_CTL_SERR << 16); + if (mvebu_readl(port, PCIE_CTRL_OFF) & PCIE_CTRL_MASTER_HOT_RESET) + val |= PCI_BRIDGE_CTL_BUS_RESET << 16; + else +@@ -675,6 +703,11 @@ mvebu_pci_bridge_emul_pcie_conf_read(struct pci_bridge_emul *bridge, + break; + } + ++ case PCI_EXP_RTCTL: ++ *value = (mvebu_readl(port, PCIE_INT_UNMASK_OFF) & ++ PCIE_INT_PM_PME) ? PCI_EXP_RTCTL_PMEIE : 0; ++ break; ++ + case PCI_EXP_RTSTA: + *value = mvebu_readl(port, PCIE_RC_RTSTA); + break; +@@ -780,6 +813,14 @@ mvebu_pci_bridge_emul_base_conf_write(struct pci_bridge_emul *bridge, + break; + + case PCI_INTERRUPT_LINE: ++ if ((mask & (PCI_BRIDGE_CTL_SERR << 16)) && port->error_irq > 0) { ++ u32 unmask = mvebu_readl(port, PCIE_INT_UNMASK_OFF); ++ if (new & (PCI_BRIDGE_CTL_SERR << 16)) ++ unmask |= PCIE_INT_ERR_MASK; ++ else ++ unmask &= ~PCIE_INT_ERR_MASK; ++ mvebu_writel(port, unmask, PCIE_INT_UNMASK_OFF); ++ } + if (mask & (PCI_BRIDGE_CTL_BUS_RESET << 16)) { + u32 ctrl = mvebu_readl(port, PCIE_CTRL_OFF); + if (new & (PCI_BRIDGE_CTL_BUS_RESET << 16)) +@@ -838,10 +879,25 @@ mvebu_pci_bridge_emul_pcie_conf_write(struct pci_bridge_emul *bridge, + * PME Status bit in Root Status Register (PCIE_RC_RTSTA) + * is read-only and can be cleared only by writing 0b to the + * Interrupt Cause RW0C register (PCIE_INT_CAUSE_OFF). So +- * clear PME via Interrupt Cause. ++ * clear PME via Interrupt Cause and also set port->pme_pending ++ * variable to false value to start processing PME interrupts ++ * in interrupt handler again. + */ +- if (new & PCI_EXP_RTSTA_PME) ++ if (new & PCI_EXP_RTSTA_PME) { + mvebu_writel(port, ~PCIE_INT_PM_PME, PCIE_INT_CAUSE_OFF); ++ port->pme_pending = false; ++ } ++ break; ++ ++ case PCI_EXP_RTCTL: ++ if ((mask & PCI_EXP_RTCTL_PMEIE) && port->error_irq > 0) { ++ u32 unmask = mvebu_readl(port, PCIE_INT_UNMASK_OFF); ++ if (new & PCI_EXP_RTCTL_PMEIE) ++ unmask |= PCIE_INT_PM_PME; ++ else ++ unmask &= ~PCIE_INT_PM_PME; ++ mvebu_writel(port, unmask, PCIE_INT_UNMASK_OFF); ++ } + break; + + case PCI_EXP_DEVCTL2: +@@ -924,6 +980,14 @@ static int mvebu_pci_bridge_emul_init(struct mvebu_pcie_port *port) + bridge_flags |= PCI_BRIDGE_EMUL_NO_IO_FORWARD; + } + ++ /* ++ * Interrupts on emulated bridge are supported only when "error" ++ * interrupt was specified in DT. Without it emulated bridge cannot ++ * emulate interrupts. ++ */ ++ if (port->error_irq > 0) ++ bridge->conf.intpin = PCI_INTERRUPT_INTA; ++ + /* + * Older mvebu hardware provides PCIe Capability structure only in + * version 1. New hardware provides it in version 2. +@@ -1072,6 +1136,26 @@ static const struct irq_domain_ops mvebu_pcie_intx_irq_domain_ops = { + .xlate = irq_domain_xlate_onecell, + }; + ++static struct irq_chip rp_irq_chip = { ++ .name = "mvebu-rp", ++}; ++ ++static int mvebu_pcie_rp_irq_map(struct irq_domain *h, ++ unsigned int virq, irq_hw_number_t hwirq) ++{ ++ struct mvebu_pcie_port *port = h->host_data; ++ ++ irq_set_chip_and_handler(virq, &rp_irq_chip, handle_simple_irq); ++ irq_set_chip_data(virq, port); ++ ++ return 0; ++} ++ ++static const struct irq_domain_ops mvebu_pcie_rp_irq_domain_ops = { ++ .map = mvebu_pcie_rp_irq_map, ++ .xlate = irq_domain_xlate_onecell, ++}; ++ + static int mvebu_pcie_init_irq_domain(struct mvebu_pcie_port *port) + { + struct device *dev = &port->pcie->pdev->dev; +@@ -1094,10 +1178,72 @@ static int mvebu_pcie_init_irq_domain(struct mvebu_pcie_port *port) + return -ENOMEM; + } + ++ /* ++ * When "error" interrupt was not specified in DT then there is no ++ * support for interrupts on emulated root bridge. So skip following ++ * initialization. ++ */ ++ if (port->error_irq <= 0) ++ return 0; ++ ++ port->rp_irq_domain = irq_domain_add_linear(NULL, 1, ++ &mvebu_pcie_rp_irq_domain_ops, ++ port); ++ if (!port->rp_irq_domain) { ++ irq_domain_remove(port->intx_irq_domain); ++ dev_err(dev, "Failed to add Root Port IRQ domain for %s\n", port->name); ++ return -ENOMEM; ++ } ++ + return 0; + } + +-static irqreturn_t mvebu_pcie_irq_handler(int irq, void *arg) ++static irqreturn_t mvebu_pcie_error_irq_handler(int irq, void *arg) ++{ ++ struct mvebu_pcie_port *port = arg; ++ struct device *dev = &port->pcie->pdev->dev; ++ u32 cause, unmask, status; ++ ++ cause = mvebu_readl(port, PCIE_INT_CAUSE_OFF); ++ unmask = mvebu_readl(port, PCIE_INT_UNMASK_OFF); ++ status = cause & unmask; ++ ++ /* "error" interrupt handler does not process INTX interrupts */ ++ status &= ~(PCIE_INT_INTX(0) | PCIE_INT_INTX(1) | ++ PCIE_INT_INTX(2) | PCIE_INT_INTX(3)); ++ ++ /* Process PME interrupt */ ++ if ((status & PCIE_INT_PM_PME) && !port->pme_pending) { ++ /* ++ * Do not clear PME interrupt bit in Cause Register as it ++ * invalidates also content of Root Status Register. Instead ++ * set port->pme_pending variable to true to indicate that ++ * next time PME interrupt should be ignored until variable ++ * is back to the false value. ++ */ ++ port->pme_pending = true; ++ if (generic_handle_domain_irq(port->rp_irq_domain, 0) == -EINVAL) ++ dev_err_ratelimited(dev, "unhandled PME IRQ\n"); ++ } ++ ++ /* Process ERR interrupt */ ++ if (status & PCIE_INT_ERR_MASK) { ++ mvebu_writel(port, ~PCIE_INT_ERR_MASK, PCIE_INT_CAUSE_OFF); ++ if (generic_handle_domain_irq(port->rp_irq_domain, 0) == -EINVAL) ++ dev_err_ratelimited(dev, "unhandled ERR IRQ\n"); ++ } ++ ++ /* Process local ERR interrupt */ ++ if (status & PCIE_INT_DET_MASK) { ++ mvebu_writel(port, ~PCIE_INT_DET_MASK, PCIE_INT_CAUSE_OFF); ++ if (generic_handle_domain_irq(port->rp_irq_domain, 0) == -EINVAL) ++ dev_err_ratelimited(dev, "unhandled ERR IRQ\n"); ++ } ++ ++ return status ? IRQ_HANDLED : IRQ_NONE; ++} ++ ++static irqreturn_t mvebu_pcie_intx_irq_handler(int irq, void *arg) + { + struct mvebu_pcie_port *port = arg; + struct device *dev = &port->pcie->pdev->dev; +@@ -1108,6 +1254,10 @@ static irqreturn_t mvebu_pcie_irq_handler(int irq, void *arg) + unmask = mvebu_readl(port, PCIE_INT_UNMASK_OFF); + status = cause & unmask; + ++ /* "intx" interrupt handler process only INTX interrupts */ ++ status &= PCIE_INT_INTX(0) | PCIE_INT_INTX(1) | ++ PCIE_INT_INTX(2) | PCIE_INT_INTX(3); ++ + /* Process legacy INTx interrupts */ + for (i = 0; i < PCI_NUM_INTX; i++) { + if (!(status & PCIE_INT_INTX(i))) +@@ -1122,9 +1272,29 @@ static irqreturn_t mvebu_pcie_irq_handler(int irq, void *arg) + + static int mvebu_pcie_map_irq(const struct pci_dev *dev, u8 slot, u8 pin) + { +- /* Interrupt support on mvebu emulated bridges is not implemented yet */ +- if (dev->bus->number == 0) +- return 0; /* Proper return code 0 == NO_IRQ */ ++ struct mvebu_pcie_port *port; ++ struct mvebu_pcie *pcie; ++ ++ if (dev->bus->number == 0) { ++ /* ++ * Each emulated root bridge for every mvebu port has its own ++ * Root Port irq chip and irq domain. Argument pin is the INTx ++ * pin (1=INTA, 2=INTB, 3=INTC, 4=INTD) and hwirq for function ++ * irq_create_mapping() is indexed from zero. ++ */ ++ pcie = dev->bus->sysdata; ++ port = mvebu_pcie_find_port(pcie, dev->bus, PCI_DEVFN(slot, 0)); ++ if (!port) ++ return 0; /* Proper return code 0 == NO_IRQ */ ++ /* ++ * port->rp_irq_domain is available only when "error" interrupt ++ * was specified in DT. When is not available then interrupts ++ * for emulated root bridge are not provided. ++ */ ++ if (port->error_irq <= 0) ++ return 0; /* Proper return code 0 == NO_IRQ */ ++ return irq_create_mapping(port->rp_irq_domain, pin - 1); ++ } + + return of_irq_parse_and_map_pci(dev, slot, pin); + } +@@ -1333,6 +1503,21 @@ static int mvebu_pcie_parse_port(struct mvebu_pcie *pcie, + port->name, child); + } + ++ /* ++ * Old DT bindings do not contain "error" interrupt ++ * so do not fail probing driver when interrupt does not exist. ++ */ ++ port->error_irq = of_irq_get_byname(child, "error"); ++ if (port->error_irq == -EPROBE_DEFER) { ++ ret = port->error_irq; ++ goto err; ++ } ++ if (port->error_irq <= 0) { ++ dev_warn(dev, "%s: interrupts on Root Port are unsupported, " ++ "%pOF does not contain error interrupt\n", ++ port->name, child); ++ } ++ + reset_gpio = of_get_named_gpio_flags(child, "reset-gpios", 0, &flags); + if (reset_gpio == -EPROBE_DEFER) { + ret = reset_gpio; +@@ -1538,7 +1723,6 @@ static int mvebu_pcie_probe(struct platform_device *pdev) + + for (i = 0; i < pcie->nports; i++) { + struct mvebu_pcie_port *port = &pcie->ports[i]; +- int irq = port->intx_irq; + + child = port->dn; + if (!child) +@@ -1566,7 +1750,7 @@ static int mvebu_pcie_probe(struct platform_device *pdev) + continue; + } + +- if (irq > 0) { ++ if (port->error_irq > 0 || port->intx_irq > 0) { + ret = mvebu_pcie_init_irq_domain(port); + if (ret) { + dev_err(dev, "%s: cannot init irq domain\n", +@@ -1577,14 +1761,42 @@ static int mvebu_pcie_probe(struct platform_device *pdev) + mvebu_pcie_powerdown(port); + continue; + } ++ } ++ ++ if (port->error_irq > 0) { ++ ret = devm_request_irq(dev, port->error_irq, ++ mvebu_pcie_error_irq_handler, ++ IRQF_SHARED | IRQF_NO_THREAD, ++ port->name, port); ++ if (ret) { ++ dev_err(dev, "%s: cannot register error interrupt handler: %d\n", ++ port->name, ret); ++ if (port->intx_irq_domain) ++ irq_domain_remove(port->intx_irq_domain); ++ if (port->rp_irq_domain) ++ irq_domain_remove(port->rp_irq_domain); ++ pci_bridge_emul_cleanup(&port->bridge); ++ devm_iounmap(dev, port->base); ++ port->base = NULL; ++ mvebu_pcie_powerdown(port); ++ continue; ++ } ++ } + +- ret = devm_request_irq(dev, irq, mvebu_pcie_irq_handler, ++ if (port->intx_irq > 0) { ++ ret = devm_request_irq(dev, port->intx_irq, ++ mvebu_pcie_intx_irq_handler, + IRQF_SHARED | IRQF_NO_THREAD, + port->name, port); + if (ret) { +- dev_err(dev, "%s: cannot register interrupt handler: %d\n", ++ dev_err(dev, "%s: cannot register intx interrupt handler: %d\n", + port->name, ret); +- irq_domain_remove(port->intx_irq_domain); ++ if (port->error_irq > 0) ++ devm_free_irq(dev, port->error_irq, port); ++ if (port->intx_irq_domain) ++ irq_domain_remove(port->intx_irq_domain); ++ if (port->rp_irq_domain) ++ irq_domain_remove(port->rp_irq_domain); + pci_bridge_emul_cleanup(&port->bridge); + devm_iounmap(dev, port->base); + port->base = NULL; +@@ -1722,6 +1934,12 @@ static int mvebu_pcie_remove(struct platform_device *pdev) + } + irq_domain_remove(port->intx_irq_domain); + } ++ if (port->rp_irq_domain) { ++ int virq = irq_find_mapping(port->rp_irq_domain, 0); ++ if (virq > 0) ++ irq_dispose_mapping(virq); ++ irq_domain_remove(port->rp_irq_domain); ++ } + + /* Free config space for emulated root bridge. */ + pci_bridge_emul_cleanup(&port->bridge); +-- +2.34.1 + diff --git a/pkgs/patches-linux-5.15/0069-ARM-dts-kirkwood-Add-definitions-for-PCIe-error-inte.patch b/pkgs/patches-linux-5.15/0069-ARM-dts-kirkwood-Add-definitions-for-PCIe-error-inte.patch new file mode 100644 index 0000000..e1b97f9 --- /dev/null +++ b/pkgs/patches-linux-5.15/0069-ARM-dts-kirkwood-Add-definitions-for-PCIe-error-inte.patch @@ -0,0 +1,94 @@ +From 1e7448b4d20311d861dd8e7e9cfa60fea643b001 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org> +Date: Mon, 27 Jun 2022 20:15:56 +0200 +Subject: [PATCH 69/90] ARM: dts: kirkwood: Add definitions for PCIe error + interrupts +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +First PCIe controller on Kirkwood SoC reports error interrupt via IRQ 44 +and second PCIe controller via IRQ 45. + +Signed-off-by: Pali Rohár <pali@kernel.org> +--- + arch/arm/boot/dts/kirkwood-6192.dtsi | 4 ++-- + arch/arm/boot/dts/kirkwood-6281.dtsi | 4 ++-- + arch/arm/boot/dts/kirkwood-6282.dtsi | 8 ++++---- + arch/arm/boot/dts/kirkwood-98dx4122.dtsi | 4 ++-- + 4 files changed, 10 insertions(+), 10 deletions(-) + +diff --git a/arch/arm/boot/dts/kirkwood-6192.dtsi b/arch/arm/boot/dts/kirkwood-6192.dtsi +index 07f4f7f98c0c..705c0d7effed 100644 +--- a/arch/arm/boot/dts/kirkwood-6192.dtsi ++++ b/arch/arm/boot/dts/kirkwood-6192.dtsi +@@ -26,8 +26,8 @@ pcie0: pcie@1,0 { + ranges = <0x82000000 0 0 0x82000000 0x1 0 1 0 + 0x81000000 0 0 0x81000000 0x1 0 1 0>; + bus-range = <0x00 0xff>; +- interrupt-names = "intx"; +- interrupts = <9>; ++ interrupt-names = "intx", "error"; ++ interrupts = <9>, <44>; + interrupt-map-mask = <0 0 0 7>; + interrupt-map = <0 0 0 1 &pcie_intc 0>, + <0 0 0 2 &pcie_intc 1>, +diff --git a/arch/arm/boot/dts/kirkwood-6281.dtsi b/arch/arm/boot/dts/kirkwood-6281.dtsi +index d08a9a5ecc26..8e311165fd13 100644 +--- a/arch/arm/boot/dts/kirkwood-6281.dtsi ++++ b/arch/arm/boot/dts/kirkwood-6281.dtsi +@@ -26,8 +26,8 @@ pcie0: pcie@1,0 { + ranges = <0x82000000 0 0 0x82000000 0x1 0 1 0 + 0x81000000 0 0 0x81000000 0x1 0 1 0>; + bus-range = <0x00 0xff>; +- interrupt-names = "intx"; +- interrupts = <9>; ++ interrupt-names = "intx", "error"; ++ interrupts = <9>, <44>; + interrupt-map-mask = <0 0 0 7>; + interrupt-map = <0 0 0 1 &pcie_intc 0>, + <0 0 0 2 &pcie_intc 1>, +diff --git a/arch/arm/boot/dts/kirkwood-6282.dtsi b/arch/arm/boot/dts/kirkwood-6282.dtsi +index 2eea5b304f47..e33723160ce7 100644 +--- a/arch/arm/boot/dts/kirkwood-6282.dtsi ++++ b/arch/arm/boot/dts/kirkwood-6282.dtsi +@@ -30,8 +30,8 @@ pcie0: pcie@1,0 { + ranges = <0x82000000 0 0 0x82000000 0x1 0 1 0 + 0x81000000 0 0 0x81000000 0x1 0 1 0>; + bus-range = <0x00 0xff>; +- interrupt-names = "intx"; +- interrupts = <9>; ++ interrupt-names = "intx", "error"; ++ interrupts = <9>, <44>; + interrupt-map-mask = <0 0 0 7>; + interrupt-map = <0 0 0 1 &pcie0_intc 0>, + <0 0 0 2 &pcie0_intc 1>, +@@ -58,8 +58,8 @@ pcie1: pcie@2,0 { + ranges = <0x82000000 0 0 0x82000000 0x2 0 1 0 + 0x81000000 0 0 0x81000000 0x2 0 1 0>; + bus-range = <0x00 0xff>; +- interrupt-names = "intx"; +- interrupts = <10>; ++ interrupt-names = "intx", "error"; ++ interrupts = <10>, <45>; + interrupt-map-mask = <0 0 0 7>; + interrupt-map = <0 0 0 1 &pcie1_intc 0>, + <0 0 0 2 &pcie1_intc 1>, +diff --git a/arch/arm/boot/dts/kirkwood-98dx4122.dtsi b/arch/arm/boot/dts/kirkwood-98dx4122.dtsi +index 070bc13242b8..c3469a2fc58a 100644 +--- a/arch/arm/boot/dts/kirkwood-98dx4122.dtsi ++++ b/arch/arm/boot/dts/kirkwood-98dx4122.dtsi +@@ -26,8 +26,8 @@ pcie0: pcie@1,0 { + ranges = <0x82000000 0 0 0x82000000 0x1 0 1 0 + 0x81000000 0 0 0x81000000 0x1 0 1 0>; + bus-range = <0x00 0xff>; +- interrupt-names = "intx"; +- interrupts = <9>; ++ interrupt-names = "intx", "error"; ++ interrupts = <9>, <44>; + interrupt-map-mask = <0 0 0 7>; + interrupt-map = <0 0 0 1 &pcie_intc 0>, + <0 0 0 2 &pcie_intc 1>, +-- +2.34.1 + diff --git a/pkgs/patches-linux-5.15/0070-ARM-dts-dove-Add-definitions-for-PCIe-error-interrup.patch b/pkgs/patches-linux-5.15/0070-ARM-dts-dove-Add-definitions-for-PCIe-error-interrup.patch new file mode 100644 index 0000000..194d64e --- /dev/null +++ b/pkgs/patches-linux-5.15/0070-ARM-dts-dove-Add-definitions-for-PCIe-error-interrup.patch @@ -0,0 +1,46 @@ +From db1cf8c55bbea9735a91e550bcea4b2d9abf1d39 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org> +Date: Mon, 27 Jun 2022 20:18:11 +0200 +Subject: [PATCH 70/90] ARM: dts: dove: Add definitions for PCIe error + interrupts +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +First PCIe controller on Dove SoC reports error interrupt via IRQ 15 +and second PCIe controller via IRQ 17. + +Signed-off-by: Pali Rohár <pali@kernel.org> +--- + arch/arm/boot/dts/dove.dtsi | 8 ++++---- + 1 file changed, 4 insertions(+), 4 deletions(-) + +diff --git a/arch/arm/boot/dts/dove.dtsi b/arch/arm/boot/dts/dove.dtsi +index 70d45d2b1258..9aee3cfd3e98 100644 +--- a/arch/arm/boot/dts/dove.dtsi ++++ b/arch/arm/boot/dts/dove.dtsi +@@ -122,8 +122,8 @@ pcie0: pcie@1 { + bus-range = <0x00 0xff>; + + #interrupt-cells = <1>; +- interrupt-names = "intx"; +- interrupts = <16>; ++ interrupt-names = "intx", "error"; ++ interrupts = <16>, <15>; + interrupt-map-mask = <0 0 0 7>; + interrupt-map = <0 0 0 1 &pcie0_intc 0>, + <0 0 0 2 &pcie0_intc 1>, +@@ -151,8 +151,8 @@ pcie1: pcie@2 { + bus-range = <0x00 0xff>; + + #interrupt-cells = <1>; +- interrupt-names = "intx"; +- interrupts = <18>; ++ interrupt-names = "intx", "error"; ++ interrupts = <18>, <17>; + interrupt-map-mask = <0 0 0 7>; + interrupt-map = <0 0 0 1 &pcie1_intc 0>, + <0 0 0 2 &pcie1_intc 1>, +-- +2.34.1 + diff --git a/pkgs/patches-linux-5.15/0071-dt-bindings-irqchip-armada-370-xp-Update-information.patch b/pkgs/patches-linux-5.15/0071-dt-bindings-irqchip-armada-370-xp-Update-information.patch new file mode 100644 index 0000000..2819708 --- /dev/null +++ b/pkgs/patches-linux-5.15/0071-dt-bindings-irqchip-armada-370-xp-Update-information.patch @@ -0,0 +1,42 @@ +From 7324692109764dbd416a308e796ef1d463d07100 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org> +Date: Fri, 6 May 2022 14:22:28 +0200 +Subject: [PATCH 71/90] dt-bindings: irqchip: armada-370-xp: Update information + about MPIC SoC Error +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Signed-off-by: Pali Rohár <pali@kernel.org> +--- + .../interrupt-controller/marvell,armada-370-xp-mpic.txt | 9 +++++++++ + 1 file changed, 9 insertions(+) + +diff --git a/Documentation/devicetree/bindings/interrupt-controller/marvell,armada-370-xp-mpic.txt b/Documentation/devicetree/bindings/interrupt-controller/marvell,armada-370-xp-mpic.txt +index 5fc03134a999..8cddbc16ddbd 100644 +--- a/Documentation/devicetree/bindings/interrupt-controller/marvell,armada-370-xp-mpic.txt ++++ b/Documentation/devicetree/bindings/interrupt-controller/marvell,armada-370-xp-mpic.txt +@@ -24,6 +24,11 @@ Optional properties: + connected as a slave to the Cortex-A9 GIC. The provided interrupt + indicate to which GIC interrupt the MPIC output is connected. + ++Optional subnodes: ++ ++- interrupt-controller@20 with interrupt-controller property for ++ MPIC SoC Error IRQ controller ++ + Example: + + mpic: interrupt-controller@d0020000 { +@@ -35,4 +40,8 @@ Example: + msi-controller; + reg = <0xd0020a00 0x1d0>, + <0xd0021070 0x58>; ++ soc_err: interrupt-controller@20 { ++ interrupt-controller; ++ #interrupt-cells = <1>; ++ }; + }; +-- +2.34.1 + diff --git a/pkgs/patches-linux-5.15/0072-irqchip-armada-370-xp-Implement-SoC-Error-interrupts.patch b/pkgs/patches-linux-5.15/0072-irqchip-armada-370-xp-Implement-SoC-Error-interrupts.patch new file mode 100644 index 0000000..5bd6786 --- /dev/null +++ b/pkgs/patches-linux-5.15/0072-irqchip-armada-370-xp-Implement-SoC-Error-interrupts.patch @@ -0,0 +1,348 @@ +From e4f14222579ceded47baafcbe10fc78f080e538b Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org> +Date: Mon, 18 Apr 2022 00:04:32 +0200 +Subject: [PATCH 72/90] irqchip/armada-370-xp: Implement SoC Error interrupts +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +MPIC IRQ 4 is used as SoC Error Summary interrupt and provides access to +another hierarchy of SoC Error interrupts. Implement a new IRQ chip and +domain for accessing this IRQ hierarchy. + +Signed-off-by: Pali Rohár <pali@kernel.org> +--- + drivers/irqchip/irq-armada-370-xp.c | 212 +++++++++++++++++++++++++++- + 1 file changed, 209 insertions(+), 3 deletions(-) + +diff --git a/drivers/irqchip/irq-armada-370-xp.c b/drivers/irqchip/irq-armada-370-xp.c +index 8abc70ed30c1..2df9e21e1559 100644 +--- a/drivers/irqchip/irq-armada-370-xp.c ++++ b/drivers/irqchip/irq-armada-370-xp.c +@@ -117,6 +117,8 @@ + /* Registers relative to main_int_base */ + #define ARMADA_370_XP_INT_CONTROL (0x00) + #define ARMADA_370_XP_SW_TRIG_INT_OFFS (0x04) ++#define ARMADA_370_XP_INT_SOC_ERR_0_CAUSE_OFFS (0x20) ++#define ARMADA_370_XP_INT_SOC_ERR_1_CAUSE_OFFS (0x24) + #define ARMADA_370_XP_INT_SET_ENABLE_OFFS (0x30) + #define ARMADA_370_XP_INT_CLEAR_ENABLE_OFFS (0x34) + #define ARMADA_370_XP_INT_SOURCE_CTL(irq) (0x100 + irq*4) +@@ -130,6 +132,8 @@ + #define ARMADA_370_XP_CPU_INTACK_OFFS (0x44) + #define ARMADA_370_XP_INT_SET_MASK_OFFS (0x48) + #define ARMADA_370_XP_INT_CLEAR_MASK_OFFS (0x4C) ++#define ARMADA_370_XP_INT_SOC_ERR_0_MASK_OFF (0x50) ++#define ARMADA_370_XP_INT_SOC_ERR_1_MASK_OFF (0x54) + #define ARMADA_370_XP_INT_FABRIC_MASK_OFFS (0x54) + #define ARMADA_370_XP_INT_CAUSE_PERF(cpu) (1 << cpu) + +@@ -153,6 +157,8 @@ + static void __iomem *per_cpu_int_base; + static void __iomem *main_int_base; + static struct irq_domain *armada_370_xp_mpic_domain; ++static struct irq_domain *armada_370_xp_soc_err_domain; ++static unsigned int soc_err_irq_num_regs; + static u32 doorbell_mask_reg; + static int parent_irq; + #ifdef CONFIG_PCI_MSI +@@ -163,6 +169,8 @@ static DEFINE_MUTEX(msi_used_lock); + static phys_addr_t msi_doorbell_addr; + #endif + ++static void armada_370_xp_soc_err_irq_unmask(struct irq_data *d); ++ + static inline bool is_percpu_irq(irq_hw_number_t irq) + { + if (irq <= ARMADA_370_XP_MAX_PER_CPU_IRQS) +@@ -528,6 +536,27 @@ static void armada_xp_mpic_reenable_percpu(void) + armada_370_xp_irq_unmask(data); + } + ++ /* Re-enable per-CPU SoC Error interrupts that were enabled before suspend */ ++ for (irq = 0; irq < soc_err_irq_num_regs * 32; irq++) { ++ struct irq_data *data; ++ int virq; ++ ++ virq = irq_linear_revmap(armada_370_xp_soc_err_domain, irq); ++ if (virq == 0) ++ continue; ++ ++ data = irq_get_irq_data(virq); ++ ++ if (!irq_percpu_is_enabled(virq)) ++ continue; ++ ++ armada_370_xp_soc_err_irq_unmask(data); ++ } ++ ++ /* Unmask summary SoC Error Interrupt */ ++ if (soc_err_irq_num_regs > 0) ++ writel(4, per_cpu_int_base + ARMADA_370_XP_INT_CLEAR_MASK_OFFS); ++ + /* IPI is used only when we do not have parent irq */ + if (parent_irq <= 0) + ipi_resume(); +@@ -567,8 +596,8 @@ static struct irq_chip armada_370_xp_irq_chip = { + static int armada_370_xp_mpic_irq_map(struct irq_domain *h, + unsigned int virq, irq_hw_number_t hw) + { +- /* IRQs 0 and 1 cannot be mapped, they are handled internally */ +- if (hw <= 1) ++ /* IRQs 0, 1 and 4 cannot be mapped, they are handled internally */ ++ if (hw <= 1 || hw == 4) + return -EINVAL; + + armada_370_xp_irq_mask(irq_get_irq_data(virq)); +@@ -598,6 +627,98 @@ static const struct irq_domain_ops armada_370_xp_mpic_irq_ops = { + .xlate = irq_domain_xlate_onecell, + }; + ++static DEFINE_RAW_SPINLOCK(armada_370_xp_soc_err_lock); ++ ++static void armada_370_xp_soc_err_irq_mask(struct irq_data *d) ++{ ++ irq_hw_number_t hwirq = irqd_to_hwirq(d); ++ u32 reg, mask; ++ ++ reg = hwirq >= 32 ? ARMADA_370_XP_INT_SOC_ERR_1_MASK_OFF ++ : ARMADA_370_XP_INT_SOC_ERR_0_MASK_OFF; ++ ++ raw_spin_lock(&armada_370_xp_soc_err_lock); ++ mask = readl(per_cpu_int_base + reg); ++ mask &= ~BIT(hwirq % 32); ++ writel(mask, per_cpu_int_base + reg); ++ raw_spin_unlock(&armada_370_xp_soc_err_lock); ++} ++ ++static void armada_370_xp_soc_err_irq_unmask(struct irq_data *d) ++{ ++ irq_hw_number_t hwirq = irqd_to_hwirq(d); ++ u32 reg, mask; ++ ++ reg = hwirq >= 32 ? ARMADA_370_XP_INT_SOC_ERR_1_MASK_OFF ++ : ARMADA_370_XP_INT_SOC_ERR_0_MASK_OFF; ++ ++ raw_spin_lock(&armada_370_xp_soc_err_lock); ++ mask = readl(per_cpu_int_base + reg); ++ mask |= BIT(hwirq % 32); ++ writel(mask, per_cpu_int_base + reg); ++ raw_spin_unlock(&armada_370_xp_soc_err_lock); ++} ++ ++static int armada_370_xp_soc_err_irq_mask_on_cpu(void *par) ++{ ++ struct irq_data *d = par; ++ armada_370_xp_soc_err_irq_mask(d); ++ return 0; ++} ++ ++static int armada_370_xp_soc_err_irq_unmask_on_cpu(void *par) ++{ ++ struct irq_data *d = par; ++ armada_370_xp_soc_err_irq_unmask(d); ++ return 0; ++} ++ ++static int armada_xp_soc_err_irq_set_affinity(struct irq_data *d, ++ const struct cpumask *mask, ++ bool force) ++{ ++ unsigned int cpu; ++ ++ cpus_read_lock(); ++ ++ /* First disable IRQ on all cores */ ++ for_each_online_cpu(cpu) ++ smp_call_on_cpu(cpu, armada_370_xp_soc_err_irq_mask_on_cpu, d, true); ++ ++ /* Select a single core from the affinity mask which is online */ ++ cpu = cpumask_any_and(mask, cpu_online_mask); ++ smp_call_on_cpu(cpu, armada_370_xp_soc_err_irq_unmask_on_cpu, d, true); ++ ++ cpus_read_unlock(); ++ ++ irq_data_update_effective_affinity(d, cpumask_of(cpu)); ++ ++ return IRQ_SET_MASK_OK; ++} ++ ++static struct irq_chip armada_370_xp_soc_err_irq_chip = { ++ .name = "MPIC SOC", ++ .irq_mask = armada_370_xp_soc_err_irq_mask, ++ .irq_unmask = armada_370_xp_soc_err_irq_unmask, ++ .irq_set_affinity = armada_xp_soc_err_irq_set_affinity, ++}; ++ ++static int armada_370_xp_soc_err_irq_map(struct irq_domain *h, ++ unsigned int virq, irq_hw_number_t hw) ++{ ++ armada_370_xp_soc_err_irq_mask(irq_get_irq_data(virq)); ++ irq_set_status_flags(virq, IRQ_LEVEL); ++ irq_set_chip_and_handler(virq, &armada_370_xp_soc_err_irq_chip, ++ handle_level_irq); ++ irq_set_probe(virq); ++ return 0; ++} ++ ++static const struct irq_domain_ops armada_370_xp_soc_err_irq_ops = { ++ .map = armada_370_xp_soc_err_irq_map, ++ .xlate = irq_domain_xlate_onecell, ++}; ++ + #ifdef CONFIG_PCI_MSI + static void armada_370_xp_handle_msi_irq(struct pt_regs *regs, bool is_chained) + { +@@ -633,6 +754,32 @@ static void armada_370_xp_handle_msi_irq(struct pt_regs *regs, bool is_chained) + static void armada_370_xp_handle_msi_irq(struct pt_regs *r, bool b) {} + #endif + ++static void armada_370_xp_handle_soc_err_irq(void) ++{ ++ unsigned long status, bit; ++ u32 mask, cause; ++ ++ if (soc_err_irq_num_regs < 1) ++ return; ++ ++ mask = readl(per_cpu_int_base + ARMADA_370_XP_INT_SOC_ERR_0_MASK_OFF); ++ cause = readl(main_int_base + ARMADA_370_XP_INT_SOC_ERR_0_CAUSE_OFFS); ++ status = cause & mask; ++ ++ for_each_set_bit(bit, &status, 32) ++ generic_handle_domain_irq(armada_370_xp_soc_err_domain, bit); ++ ++ if (soc_err_irq_num_regs < 2) ++ return; ++ ++ mask = readl(per_cpu_int_base + ARMADA_370_XP_INT_SOC_ERR_1_MASK_OFF); ++ cause = readl(main_int_base + ARMADA_370_XP_INT_SOC_ERR_1_CAUSE_OFFS); ++ status = cause & mask; ++ ++ for_each_set_bit(bit, &status, 32) ++ generic_handle_domain_irq(armada_370_xp_soc_err_domain, bit + 32); ++} ++ + static void armada_370_xp_mpic_handle_cascade_irq(struct irq_desc *desc) + { + struct irq_chip *chip = irq_desc_get_chip(desc); +@@ -658,6 +805,11 @@ static void armada_370_xp_mpic_handle_cascade_irq(struct irq_desc *desc) + continue; + } + ++ if (irqn == 4) { ++ armada_370_xp_handle_soc_err_irq(); ++ continue; ++ } ++ + generic_handle_domain_irq(armada_370_xp_mpic_domain, irqn); + } + +@@ -677,7 +829,7 @@ armada_370_xp_handle_irq(struct pt_regs *regs) + if (irqnr > 1022) + break; + +- if (irqnr > 1) { ++ if (irqnr > 1 && irqnr != 4) { + handle_domain_irq(armada_370_xp_mpic_domain, + irqnr, regs); + continue; +@@ -687,6 +839,10 @@ armada_370_xp_handle_irq(struct pt_regs *regs) + if (irqnr == 1) + armada_370_xp_handle_msi_irq(regs, false); + ++ /* SoC Error handling */ ++ if (irqnr == 4) ++ armada_370_xp_handle_soc_err_irq(); ++ + #ifdef CONFIG_SMP + /* IPI Handling */ + if (irqnr == 0) { +@@ -750,6 +906,26 @@ static void armada_370_xp_mpic_resume(void) + } + } + ++ /* Re-enable per-CPU SoC Error interrupts */ ++ for (irq = 0; irq < soc_err_irq_num_regs * 32; irq++) { ++ struct irq_data *data; ++ int virq; ++ ++ virq = irq_linear_revmap(armada_370_xp_soc_err_domain, irq); ++ if (virq == 0) ++ continue; ++ ++ data = irq_get_irq_data(virq); ++ ++ /* ++ * Re-enable on the current CPU, ++ * armada_xp_mpic_reenable_percpu() will take ++ * care of secondary CPUs when they come up. ++ */ ++ if (irq_percpu_is_enabled(virq)) ++ armada_370_xp_soc_err_irq_unmask(data); ++ } ++ + /* Reconfigure doorbells for IPIs and MSIs */ + writel(doorbell_mask_reg, + per_cpu_int_base + ARMADA_370_XP_IN_DRBEL_MSK_OFFS); +@@ -768,6 +944,10 @@ static void armada_370_xp_mpic_resume(void) + writel(1, per_cpu_int_base + ARMADA_370_XP_INT_CLEAR_MASK_OFFS); + } + ++ /* Unmask summary SoC Error Interrupt */ ++ if (soc_err_irq_num_regs > 0) ++ writel(4, per_cpu_int_base + ARMADA_370_XP_INT_CLEAR_MASK_OFFS); ++ + /* IPI is used only when we do not have parent irq */ + if (parent_irq <= 0) + ipi_resume(); +@@ -782,6 +962,7 @@ static int __init armada_370_xp_mpic_of_init(struct device_node *node, + struct device_node *parent) + { + struct resource main_int_res, per_cpu_int_res; ++ struct device_node *soc_err_node; + int nr_irqs, i; + u32 control; + +@@ -815,6 +996,27 @@ static int __init armada_370_xp_mpic_of_init(struct device_node *node, + BUG_ON(!armada_370_xp_mpic_domain); + irq_domain_update_bus_token(armada_370_xp_mpic_domain, DOMAIN_BUS_WIRED); + ++ soc_err_node = of_get_next_child(node, NULL); ++ if (!soc_err_node) { ++ pr_warn("Missing SoC Error Interrupt Controller node\n"); ++ pr_warn("Extended interrupts are not supported\n"); ++ } else { ++ pr_info("Registering MPIC SoC Error Interrupt Controller\n"); ++ /* ++ * Armada 370 and XP have only 32 SoC Error IRQs in one register ++ * and other Armada platforms have 64 IRQs in two registers. ++ */ ++ soc_err_irq_num_regs = ++ of_machine_is_compatible("marvell,armada-370-xp") ? 1 : 2; ++ armada_370_xp_soc_err_domain = ++ irq_domain_add_hierarchy(armada_370_xp_mpic_domain, 0, ++ soc_err_irq_num_regs * 32, ++ soc_err_node, ++ &armada_370_xp_soc_err_irq_ops, ++ NULL); ++ BUG_ON(!armada_370_xp_soc_err_domain); ++ } ++ + /* + * parent_irq is used for distinguish between IPI and non-IPI platforms. + * So initialize it before calling any other driver functions. +@@ -827,6 +1029,10 @@ static int __init armada_370_xp_mpic_of_init(struct device_node *node, + + armada_370_xp_msi_init(node, main_int_res.start); + ++ /* Unmask summary SoC Error Interrupt */ ++ if (soc_err_irq_num_regs > 0) ++ writel(4, per_cpu_int_base + ARMADA_370_XP_INT_CLEAR_MASK_OFFS); ++ + if (parent_irq <= 0) { + irq_set_default_host(armada_370_xp_mpic_domain); + set_handle_irq(armada_370_xp_handle_irq); +-- +2.34.1 + diff --git a/pkgs/patches-linux-5.15/0073-ARM-dts-armada-370-xp.dtsi-Add-node-for-MPIC-SoC-Err.patch b/pkgs/patches-linux-5.15/0073-ARM-dts-armada-370-xp.dtsi-Add-node-for-MPIC-SoC-Err.patch new file mode 100644 index 0000000..6dec138 --- /dev/null +++ b/pkgs/patches-linux-5.15/0073-ARM-dts-armada-370-xp.dtsi-Add-node-for-MPIC-SoC-Err.patch @@ -0,0 +1,33 @@ +From e43fb41649eb974e62b64debc234cba8aed4f978 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org> +Date: Mon, 27 Jun 2022 16:25:39 +0200 +Subject: [PATCH 73/90] ARM: dts: armada-370-xp.dtsi: Add node for MPIC SoC + Error IRQ controller +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Signed-off-by: Pali Rohár <pali@kernel.org> +--- + arch/arm/boot/dts/armada-370-xp.dtsi | 5 +++++ + 1 file changed, 5 insertions(+) + +diff --git a/arch/arm/boot/dts/armada-370-xp.dtsi b/arch/arm/boot/dts/armada-370-xp.dtsi +index 0b8c2a64b36f..7aedacff2c00 100644 +--- a/arch/arm/boot/dts/armada-370-xp.dtsi ++++ b/arch/arm/boot/dts/armada-370-xp.dtsi +@@ -171,6 +171,11 @@ mpic: interrupt-controller@20a00 { + #size-cells = <1>; + interrupt-controller; + msi-controller; ++ ++ soc_err: interrupt-controller@20 { ++ interrupt-controller; ++ #interrupt-cells = <1>; ++ }; + }; + + coherencyfab: coherency-fabric@20200 { +-- +2.34.1 + diff --git a/pkgs/patches-linux-5.15/0074-ARM-dts-armada-375.dtsi-Add-node-for-MPIC-SoC-Error-.patch b/pkgs/patches-linux-5.15/0074-ARM-dts-armada-375.dtsi-Add-node-for-MPIC-SoC-Error-.patch new file mode 100644 index 0000000..410109c --- /dev/null +++ b/pkgs/patches-linux-5.15/0074-ARM-dts-armada-375.dtsi-Add-node-for-MPIC-SoC-Error-.patch @@ -0,0 +1,33 @@ +From 2a2f2b633f0ecce7a56322c17a8ed91db71977eb Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org> +Date: Mon, 27 Jun 2022 16:43:14 +0200 +Subject: [PATCH 74/90] ARM: dts: armada-375.dtsi: Add node for MPIC SoC Error + IRQ controller +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Signed-off-by: Pali Rohár <pali@kernel.org> +--- + arch/arm/boot/dts/armada-375.dtsi | 5 +++++ + 1 file changed, 5 insertions(+) + +diff --git a/arch/arm/boot/dts/armada-375.dtsi b/arch/arm/boot/dts/armada-375.dtsi +index c310ef26d1cc..82f0a59d112f 100644 +--- a/arch/arm/boot/dts/armada-375.dtsi ++++ b/arch/arm/boot/dts/armada-375.dtsi +@@ -376,6 +376,11 @@ mpic: interrupt-controller@20a00 { + interrupt-controller; + msi-controller; + interrupts = <GIC_PPI 15 IRQ_TYPE_LEVEL_HIGH>; ++ ++ soc_err: interrupt-controller@20 { ++ interrupt-controller; ++ #interrupt-cells = <1>; ++ }; + }; + + timer1: timer@20300 { +-- +2.34.1 + diff --git a/pkgs/patches-linux-5.15/0075-ARM-dts-armada-38x.dtsi-Add-node-for-MPIC-SoC-Error-.patch b/pkgs/patches-linux-5.15/0075-ARM-dts-armada-38x.dtsi-Add-node-for-MPIC-SoC-Error-.patch new file mode 100644 index 0000000..67d1064 --- /dev/null +++ b/pkgs/patches-linux-5.15/0075-ARM-dts-armada-38x.dtsi-Add-node-for-MPIC-SoC-Error-.patch @@ -0,0 +1,35 @@ +From 29284b9f12db226af974191bdf2164bc3155c2c3 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org> +Date: Mon, 18 Apr 2022 00:39:52 +0200 +Subject: [PATCH 75/90] ARM: dts: armada-38x.dtsi: Add node for MPIC SoC Error + IRQ controller +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +It is child of the MPIC IRQ controller. + +Signed-off-by: Pali Rohár <pali@kernel.org> +--- + arch/arm/boot/dts/armada-38x.dtsi | 5 +++++ + 1 file changed, 5 insertions(+) + +diff --git a/arch/arm/boot/dts/armada-38x.dtsi b/arch/arm/boot/dts/armada-38x.dtsi +index df3c8d1d8f64..099f167b65aa 100644 +--- a/arch/arm/boot/dts/armada-38x.dtsi ++++ b/arch/arm/boot/dts/armada-38x.dtsi +@@ -398,6 +398,11 @@ mpic: interrupt-controller@20a00 { + interrupt-controller; + msi-controller; + interrupts = <GIC_PPI 15 IRQ_TYPE_LEVEL_HIGH>; ++ ++ soc_err: interrupt-controller@20 { ++ interrupt-controller; ++ #interrupt-cells = <1>; ++ }; + }; + + timer: timer@20300 { +-- +2.34.1 + diff --git a/pkgs/patches-linux-5.15/0076-ARM-dts-armada-39x.dtsi-Add-node-for-MPIC-SoC-Error-.patch b/pkgs/patches-linux-5.15/0076-ARM-dts-armada-39x.dtsi-Add-node-for-MPIC-SoC-Error-.patch new file mode 100644 index 0000000..bf59a95 --- /dev/null +++ b/pkgs/patches-linux-5.15/0076-ARM-dts-armada-39x.dtsi-Add-node-for-MPIC-SoC-Error-.patch @@ -0,0 +1,33 @@ +From add0bae184216783b6f6bc67c6d4b48fec781ad5 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org> +Date: Mon, 27 Jun 2022 17:49:36 +0200 +Subject: [PATCH 76/90] ARM: dts: armada-39x.dtsi: Add node for MPIC SoC Error + IRQ controller +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Signed-off-by: Pali Rohár <pali@kernel.org> +--- + arch/arm/boot/dts/armada-39x.dtsi | 5 +++++ + 1 file changed, 5 insertions(+) + +diff --git a/arch/arm/boot/dts/armada-39x.dtsi b/arch/arm/boot/dts/armada-39x.dtsi +index 9d1cac49c022..f21231a1f244 100644 +--- a/arch/arm/boot/dts/armada-39x.dtsi ++++ b/arch/arm/boot/dts/armada-39x.dtsi +@@ -272,6 +272,11 @@ mpic: interrupt-controller@20a00 { + interrupt-controller; + msi-controller; + interrupts = <GIC_PPI 15 IRQ_TYPE_LEVEL_HIGH>; ++ ++ soc_err: interrupt-controller@20 { ++ interrupt-controller; ++ #interrupt-cells = <1>; ++ }; + }; + + timer@20300 { +-- +2.34.1 + diff --git a/pkgs/patches-linux-5.15/0077-ARM-dts-armada-370.dtsi-Add-definitions-for-PCIe-err.patch b/pkgs/patches-linux-5.15/0077-ARM-dts-armada-370.dtsi-Add-definitions-for-PCIe-err.patch new file mode 100644 index 0000000..2093cfd --- /dev/null +++ b/pkgs/patches-linux-5.15/0077-ARM-dts-armada-370.dtsi-Add-definitions-for-PCIe-err.patch @@ -0,0 +1,43 @@ +From 9740547f8b4a338e7d67760db774ef49033ed033 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org> +Date: Mon, 27 Jun 2022 16:31:57 +0200 +Subject: [PATCH 77/90] ARM: dts: armada-370.dtsi: Add definitions for PCIe + error interrupts +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Signed-off-by: Pali Rohár <pali@kernel.org> +--- + arch/arm/boot/dts/armada-370.dtsi | 8 ++++---- + 1 file changed, 4 insertions(+), 4 deletions(-) + +diff --git a/arch/arm/boot/dts/armada-370.dtsi b/arch/arm/boot/dts/armada-370.dtsi +index 2013a5ccecd3..9daece786a53 100644 +--- a/arch/arm/boot/dts/armada-370.dtsi ++++ b/arch/arm/boot/dts/armada-370.dtsi +@@ -60,8 +60,8 @@ pcie0: pcie@1,0 { + reg = <0x0800 0 0 0 0>; + #address-cells = <3>; + #size-cells = <2>; +- interrupt-names = "intx"; +- interrupts-extended = <&mpic 58>; ++ interrupt-names = "intx", "error"; ++ interrupts-extended = <&mpic 58>, <&soc_err 4>; + #interrupt-cells = <1>; + ranges = <0x82000000 0 0 0x82000000 0x1 0 1 0 + 0x81000000 0 0 0x81000000 0x1 0 1 0>; +@@ -88,8 +88,8 @@ pcie2: pcie@2,0 { + reg = <0x1000 0 0 0 0>; + #address-cells = <3>; + #size-cells = <2>; +- interrupt-names = "intx"; +- interrupts-extended = <&mpic 62>; ++ interrupt-names = "intx", "error"; ++ interrupts-extended = <&mpic 62>, <&soc_err 5>; + #interrupt-cells = <1>; + ranges = <0x82000000 0 0 0x82000000 0x2 0 1 0 + 0x81000000 0 0 0x81000000 0x2 0 1 0>; +-- +2.34.1 + diff --git a/pkgs/patches-linux-5.15/0078-ARM-dts-armada-xp-mv78230.dtsi-Add-definitions-for-P.patch b/pkgs/patches-linux-5.15/0078-ARM-dts-armada-xp-mv78230.dtsi-Add-definitions-for-P.patch new file mode 100644 index 0000000..e8ced75 --- /dev/null +++ b/pkgs/patches-linux-5.15/0078-ARM-dts-armada-xp-mv78230.dtsi-Add-definitions-for-P.patch @@ -0,0 +1,79 @@ +From d7af6212b6962f75c52dd61516ab92eb448a83bb Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org> +Date: Mon, 27 Jun 2022 19:33:45 +0200 +Subject: [PATCH 78/90] ARM: dts: armada-xp-mv78230.dtsi: Add definitions for + PCIe error interrupts +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +PCIe controllers on Marvell Port 0 share MPIC SoC Error IRQ 4 and PCIe +controller on Marvell Port 1 uses MPIC SoC Error IRQ 5. + +Signed-off-by: Pali Rohár <pali@kernel.org> +--- + arch/arm/boot/dts/armada-xp-mv78230.dtsi | 20 ++++++++++---------- + 1 file changed, 10 insertions(+), 10 deletions(-) + +diff --git a/arch/arm/boot/dts/armada-xp-mv78230.dtsi b/arch/arm/boot/dts/armada-xp-mv78230.dtsi +index 5ea9d509cd30..b8d169c4feec 100644 +--- a/arch/arm/boot/dts/armada-xp-mv78230.dtsi ++++ b/arch/arm/boot/dts/armada-xp-mv78230.dtsi +@@ -83,8 +83,8 @@ pcie1: pcie@1,0 { + reg = <0x0800 0 0 0 0>; + #address-cells = <3>; + #size-cells = <2>; +- interrupt-names = "intx"; +- interrupts-extended = <&mpic 58>; ++ interrupt-names = "intx", "error"; ++ interrupts-extended = <&mpic 58>, <&soc_err 4>; + #interrupt-cells = <1>; + ranges = <0x82000000 0 0 0x82000000 0x1 0 1 0 + 0x81000000 0 0 0x81000000 0x1 0 1 0>; +@@ -111,8 +111,8 @@ pcie2: pcie@2,0 { + reg = <0x1000 0 0 0 0>; + #address-cells = <3>; + #size-cells = <2>; +- interrupt-names = "intx"; +- interrupts-extended = <&mpic 59>; ++ interrupt-names = "intx", "error"; ++ interrupts-extended = <&mpic 59>, <&soc_err 4>; + #interrupt-cells = <1>; + ranges = <0x82000000 0 0 0x82000000 0x2 0 1 0 + 0x81000000 0 0 0x81000000 0x2 0 1 0>; +@@ -139,8 +139,8 @@ pcie3: pcie@3,0 { + reg = <0x1800 0 0 0 0>; + #address-cells = <3>; + #size-cells = <2>; +- interrupt-names = "intx"; +- interrupts-extended = <&mpic 60>; ++ interrupt-names = "intx", "error"; ++ interrupts-extended = <&mpic 60>, <&soc_err 4>; + #interrupt-cells = <1>; + ranges = <0x82000000 0 0 0x82000000 0x3 0 1 0 + 0x81000000 0 0 0x81000000 0x3 0 1 0>; +@@ -167,8 +167,8 @@ pcie4: pcie@4,0 { + reg = <0x2000 0 0 0 0>; + #address-cells = <3>; + #size-cells = <2>; +- interrupt-names = "intx"; +- interrupts-extended = <&mpic 61>; ++ interrupt-names = "intx", "error"; ++ interrupts-extended = <&mpic 61>, <&soc_err 4>; + #interrupt-cells = <1>; + ranges = <0x82000000 0 0 0x82000000 0x4 0 1 0 + 0x81000000 0 0 0x81000000 0x4 0 1 0>; +@@ -195,8 +195,8 @@ pcie5: pcie@5,0 { + reg = <0x2800 0 0 0 0>; + #address-cells = <3>; + #size-cells = <2>; +- interrupt-names = "intx"; +- interrupts-extended = <&mpic 62>; ++ interrupt-names = "intx", "error"; ++ interrupts-extended = <&mpic 62>, <&soc_err 5>; + #interrupt-cells = <1>; + ranges = <0x82000000 0 0 0x82000000 0x5 0 1 0 + 0x81000000 0 0 0x81000000 0x5 0 1 0>; +-- +2.34.1 + diff --git a/pkgs/patches-linux-5.15/0079-ARM-dts-armada-xp-mv78260.dtsi-Add-definitions-for-P.patch b/pkgs/patches-linux-5.15/0079-ARM-dts-armada-xp-mv78260.dtsi-Add-definitions-for-P.patch new file mode 100644 index 0000000..de93813 --- /dev/null +++ b/pkgs/patches-linux-5.15/0079-ARM-dts-armada-xp-mv78260.dtsi-Add-definitions-for-P.patch @@ -0,0 +1,124 @@ +From 9197b016f20750c4df3e33b48c9252fbb5972425 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org> +Date: Mon, 27 Jun 2022 20:02:26 +0200 +Subject: [PATCH 79/90] ARM: dts: armada-xp-mv78260.dtsi: Add definitions for + PCIe error interrupts +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +PCIe controllers on Marvell Port 0 share MPIC SoC Error IRQ 4, PCIe +controllers on Marvell Port 1 share MPIC SoC Error IRQ 5 and PCIe +controller on Marvell Port 2 uses MPIC SoC Error IRQ 15. + +Signed-off-by: Pali Rohár <pali@kernel.org> +--- + arch/arm/boot/dts/armada-xp-mv78260.dtsi | 36 ++++++++++++------------ + 1 file changed, 18 insertions(+), 18 deletions(-) + +diff --git a/arch/arm/boot/dts/armada-xp-mv78260.dtsi b/arch/arm/boot/dts/armada-xp-mv78260.dtsi +index 6c6fbb9faf5a..febd9d98a44e 100644 +--- a/arch/arm/boot/dts/armada-xp-mv78260.dtsi ++++ b/arch/arm/boot/dts/armada-xp-mv78260.dtsi +@@ -98,8 +98,8 @@ pcie1: pcie@1,0 { + reg = <0x0800 0 0 0 0>; + #address-cells = <3>; + #size-cells = <2>; +- interrupt-names = "intx"; +- interrupts-extended = <&mpic 58>; ++ interrupt-names = "intx", "error"; ++ interrupts-extended = <&mpic 58>, <&soc_err 4>; + #interrupt-cells = <1>; + ranges = <0x82000000 0 0 0x82000000 0x1 0 1 0 + 0x81000000 0 0 0x81000000 0x1 0 1 0>; +@@ -126,8 +126,8 @@ pcie2: pcie@2,0 { + reg = <0x1000 0 0 0 0>; + #address-cells = <3>; + #size-cells = <2>; +- interrupt-names = "intx"; +- interrupts-extended = <&mpic 59>; ++ interrupt-names = "intx", "error"; ++ interrupts-extended = <&mpic 59>, <&soc_err 4>; + #interrupt-cells = <1>; + ranges = <0x82000000 0 0 0x82000000 0x2 0 1 0 + 0x81000000 0 0 0x81000000 0x2 0 1 0>; +@@ -154,8 +154,8 @@ pcie3: pcie@3,0 { + reg = <0x1800 0 0 0 0>; + #address-cells = <3>; + #size-cells = <2>; +- interrupt-names = "intx"; +- interrupts-extended = <&mpic 60>; ++ interrupt-names = "intx", "error"; ++ interrupts-extended = <&mpic 60>, <&soc_err 4>; + #interrupt-cells = <1>; + ranges = <0x82000000 0 0 0x82000000 0x3 0 1 0 + 0x81000000 0 0 0x81000000 0x3 0 1 0>; +@@ -182,8 +182,8 @@ pcie4: pcie@4,0 { + reg = <0x2000 0 0 0 0>; + #address-cells = <3>; + #size-cells = <2>; +- interrupt-names = "intx"; +- interrupts-extended = <&mpic 61>; ++ interrupt-names = "intx", "error"; ++ interrupts-extended = <&mpic 61>, <&soc_err 4>; + #interrupt-cells = <1>; + ranges = <0x82000000 0 0 0x82000000 0x4 0 1 0 + 0x81000000 0 0 0x81000000 0x4 0 1 0>; +@@ -210,8 +210,8 @@ pcie5: pcie@5,0 { + reg = <0x2800 0 0 0 0>; + #address-cells = <3>; + #size-cells = <2>; +- interrupt-names = "intx"; +- interrupts-extended = <&mpic 62>; ++ interrupt-names = "intx", "error"; ++ interrupts-extended = <&mpic 62>, <&soc_err 5>; + #interrupt-cells = <1>; + ranges = <0x82000000 0 0 0x82000000 0x5 0 1 0 + 0x81000000 0 0 0x81000000 0x5 0 1 0>; +@@ -238,8 +238,8 @@ pcie6: pcie@6,0 { + reg = <0x3000 0 0 0 0>; + #address-cells = <3>; + #size-cells = <2>; +- interrupt-names = "intx"; +- interrupts-extended = <&mpic 63>; ++ interrupt-names = "intx", "error"; ++ interrupts-extended = <&mpic 63>, <&soc_err 5>; + #interrupt-cells = <1>; + ranges = <0x82000000 0 0 0x82000000 0x6 0 1 0 + 0x81000000 0 0 0x81000000 0x6 0 1 0>; +@@ -266,8 +266,8 @@ pcie7: pcie@7,0 { + reg = <0x3800 0 0 0 0>; + #address-cells = <3>; + #size-cells = <2>; +- interrupt-names = "intx"; +- interrupts-extended = <&mpic 64>; ++ interrupt-names = "intx", "error"; ++ interrupts-extended = <&mpic 64>, <&soc_err 5>; + #interrupt-cells = <1>; + ranges = <0x82000000 0 0 0x82000000 0x7 0 1 0 + 0x81000000 0 0 0x81000000 0x7 0 1 0>; +@@ -294,8 +294,8 @@ pcie8: pcie@8,0 { + reg = <0x4000 0 0 0 0>; + #address-cells = <3>; + #size-cells = <2>; +- interrupt-names = "intx"; +- interrupts-extended = <&mpic 65>; ++ interrupt-names = "intx", "error"; ++ interrupts-extended = <&mpic 65>, <&soc_err 5>; + #interrupt-cells = <1>; + ranges = <0x82000000 0 0 0x82000000 0x8 0 1 0 + 0x81000000 0 0 0x81000000 0x8 0 1 0>; +@@ -322,8 +322,8 @@ pcie9: pcie@9,0 { + reg = <0x4800 0 0 0 0>; + #address-cells = <3>; + #size-cells = <2>; +- interrupt-names = "intx"; +- interrupts-extended = <&mpic 99>; ++ interrupt-names = "intx", "error"; ++ interrupts-extended = <&mpic 99>, <&soc_err 15>; + #interrupt-cells = <1>; + ranges = <0x82000000 0 0 0x82000000 0x9 0 1 0 + 0x81000000 0 0 0x81000000 0x9 0 1 0>; +-- +2.34.1 + diff --git a/pkgs/patches-linux-5.15/0080-ARM-dts-armada-xp-mv78460.dtsi-Add-definitions-for-P.patch b/pkgs/patches-linux-5.15/0080-ARM-dts-armada-xp-mv78460.dtsi-Add-definitions-for-P.patch new file mode 100644 index 0000000..cdea44f --- /dev/null +++ b/pkgs/patches-linux-5.15/0080-ARM-dts-armada-xp-mv78460.dtsi-Add-definitions-for-P.patch @@ -0,0 +1,136 @@ +From 20d6c3c5af016a0a2c176e4bed82a7fede362462 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org> +Date: Mon, 27 Jun 2022 20:05:35 +0200 +Subject: [PATCH 80/90] ARM: dts: armada-xp-mv78460.dtsi: Add definitions for + PCIe error interrupts +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +PCIe controllers on Marvell Port 0 share MPIC SoC Error IRQ 4, PCIe +controllers on Marvell Port 1 share MPIC SoC Error IRQ 5, PCIe +controller on Marvell Port 2 uses MPIC SoC Error IRQ 15 and PCIe +controller on Marvell Port 3 uses MPIC SoC Error IRQ 16. + +Signed-off-by: Pali Rohár <pali@kernel.org> +--- + arch/arm/boot/dts/armada-xp-mv78460.dtsi | 40 ++++++++++++------------ + 1 file changed, 20 insertions(+), 20 deletions(-) + +diff --git a/arch/arm/boot/dts/armada-xp-mv78460.dtsi b/arch/arm/boot/dts/armada-xp-mv78460.dtsi +index 16185edf9aa5..3b8adbc89a06 100644 +--- a/arch/arm/boot/dts/armada-xp-mv78460.dtsi ++++ b/arch/arm/boot/dts/armada-xp-mv78460.dtsi +@@ -119,8 +119,8 @@ pcie1: pcie@1,0 { + reg = <0x0800 0 0 0 0>; + #address-cells = <3>; + #size-cells = <2>; +- interrupt-names = "intx"; +- interrupts-extended = <&mpic 58>; ++ interrupt-names = "intx", "error"; ++ interrupts-extended = <&mpic 58>, <&soc_err 4>; + #interrupt-cells = <1>; + ranges = <0x82000000 0 0 0x82000000 0x1 0 1 0 + 0x81000000 0 0 0x81000000 0x1 0 1 0>; +@@ -147,8 +147,8 @@ pcie2: pcie@2,0 { + reg = <0x1000 0 0 0 0>; + #address-cells = <3>; + #size-cells = <2>; +- interrupt-names = "intx"; +- interrupts-extended = <&mpic 59>; ++ interrupt-names = "intx", "error"; ++ interrupts-extended = <&mpic 59>, <&soc_err 4>; + #interrupt-cells = <1>; + ranges = <0x82000000 0 0 0x82000000 0x2 0 1 0 + 0x81000000 0 0 0x81000000 0x2 0 1 0>; +@@ -175,8 +175,8 @@ pcie3: pcie@3,0 { + reg = <0x1800 0 0 0 0>; + #address-cells = <3>; + #size-cells = <2>; +- interrupt-names = "intx"; +- interrupts-extended = <&mpic 60>; ++ interrupt-names = "intx", "error"; ++ interrupts-extended = <&mpic 60>, <&soc_err 4>; + #interrupt-cells = <1>; + ranges = <0x82000000 0 0 0x82000000 0x3 0 1 0 + 0x81000000 0 0 0x81000000 0x3 0 1 0>; +@@ -203,8 +203,8 @@ pcie4: pcie@4,0 { + reg = <0x2000 0 0 0 0>; + #address-cells = <3>; + #size-cells = <2>; +- interrupt-names = "intx"; +- interrupts-extended = <&mpic 61>; ++ interrupt-names = "intx", "error"; ++ interrupts-extended = <&mpic 61>, <&soc_err 4>; + #interrupt-cells = <1>; + ranges = <0x82000000 0 0 0x82000000 0x4 0 1 0 + 0x81000000 0 0 0x81000000 0x4 0 1 0>; +@@ -231,8 +231,8 @@ pcie5: pcie@5,0 { + reg = <0x2800 0 0 0 0>; + #address-cells = <3>; + #size-cells = <2>; +- interrupt-names = "intx"; +- interrupts-extended = <&mpic 62>; ++ interrupt-names = "intx", "error"; ++ interrupts-extended = <&mpic 62>, <&soc_err 5>; + #interrupt-cells = <1>; + ranges = <0x82000000 0 0 0x82000000 0x5 0 1 0 + 0x81000000 0 0 0x81000000 0x5 0 1 0>; +@@ -259,8 +259,8 @@ pcie6: pcie@6,0 { + reg = <0x3000 0 0 0 0>; + #address-cells = <3>; + #size-cells = <2>; +- interrupt-names = "intx"; +- interrupts-extended = <&mpic 63>; ++ interrupt-names = "intx", "error"; ++ interrupts-extended = <&mpic 63>, <&soc_err 5>; + #interrupt-cells = <1>; + ranges = <0x82000000 0 0 0x82000000 0x6 0 1 0 + 0x81000000 0 0 0x81000000 0x6 0 1 0>; +@@ -287,8 +287,8 @@ pcie7: pcie@7,0 { + reg = <0x3800 0 0 0 0>; + #address-cells = <3>; + #size-cells = <2>; +- interrupt-names = "intx"; +- interrupts-extended = <&mpic 64>; ++ interrupt-names = "intx", "error"; ++ interrupts-extended = <&mpic 64>, <&soc_err 5>; + #interrupt-cells = <1>; + ranges = <0x82000000 0 0 0x82000000 0x7 0 1 0 + 0x81000000 0 0 0x81000000 0x7 0 1 0>; +@@ -315,8 +315,8 @@ pcie8: pcie@8,0 { + reg = <0x4000 0 0 0 0>; + #address-cells = <3>; + #size-cells = <2>; +- interrupt-names = "intx"; +- interrupts-extended = <&mpic 65>; ++ interrupt-names = "intx", "error"; ++ interrupts-extended = <&mpic 65>, <&soc_err 5>; + #interrupt-cells = <1>; + ranges = <0x82000000 0 0 0x82000000 0x8 0 1 0 + 0x81000000 0 0 0x81000000 0x8 0 1 0>; +@@ -343,8 +343,8 @@ pcie9: pcie@9,0 { + reg = <0x4800 0 0 0 0>; + #address-cells = <3>; + #size-cells = <2>; +- interrupt-names = "intx"; +- interrupts-extended = <&mpic 99>; ++ interrupt-names = "intx", "error"; ++ interrupts-extended = <&mpic 99>, <&soc_err 15>; + #interrupt-cells = <1>; + ranges = <0x82000000 0 0 0x82000000 0x9 0 1 0 + 0x81000000 0 0 0x81000000 0x9 0 1 0>; +@@ -371,8 +371,8 @@ pcie10: pcie@a,0 { + reg = <0x5000 0 0 0 0>; + #address-cells = <3>; + #size-cells = <2>; +- interrupt-names = "intx"; +- interrupts-extended = <&mpic 103>; ++ interrupt-names = "intx", "error"; ++ interrupts-extended = <&mpic 103>, <&soc_err 16>; + #interrupt-cells = <1>; + ranges = <0x82000000 0 0 0x82000000 0xa 0 1 0 + 0x81000000 0 0 0x81000000 0xa 0 1 0>; +-- +2.34.1 + diff --git a/pkgs/patches-linux-5.15/0081-ARM-dts-armada-xp-98dx3236.dtsi-Add-definitions-for-.patch b/pkgs/patches-linux-5.15/0081-ARM-dts-armada-xp-98dx3236.dtsi-Add-definitions-for-.patch new file mode 100644 index 0000000..814d003 --- /dev/null +++ b/pkgs/patches-linux-5.15/0081-ARM-dts-armada-xp-98dx3236.dtsi-Add-definitions-for-.patch @@ -0,0 +1,32 @@ +From 776df9728a4b2d3ae935487d815fbf8570b86701 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org> +Date: Mon, 27 Jun 2022 17:30:55 +0200 +Subject: [PATCH 81/90] ARM: dts: armada-xp-98dx3236.dtsi: Add definitions for + PCIe error interrupts +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Signed-off-by: Pali Rohár <pali@kernel.org> +--- + arch/arm/boot/dts/armada-xp-98dx3236.dtsi | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/arch/arm/boot/dts/armada-xp-98dx3236.dtsi b/arch/arm/boot/dts/armada-xp-98dx3236.dtsi +index b21ffb819b1d..0d021f3b86be 100644 +--- a/arch/arm/boot/dts/armada-xp-98dx3236.dtsi ++++ b/arch/arm/boot/dts/armada-xp-98dx3236.dtsi +@@ -76,8 +76,8 @@ pcie1: pcie@1,0 { + reg = <0x0800 0 0 0 0>; + #address-cells = <3>; + #size-cells = <2>; +- interrupt-names = "intx"; +- interrupts-extended = <&mpic 58>; ++ interrupt-names = "intx", "error"; ++ interrupts-extended = <&mpic 58>, <&soc_err 4>; + #interrupt-cells = <1>; + ranges = <0x82000000 0 0 0x82000000 0x1 0 1 0 + 0x81000000 0 0 0x81000000 0x1 0 1 0>; +-- +2.34.1 + diff --git a/pkgs/patches-linux-5.15/0082-ARM-dts-armada-375.dtsi-Add-definitions-for-PCIe-err.patch b/pkgs/patches-linux-5.15/0082-ARM-dts-armada-375.dtsi-Add-definitions-for-PCIe-err.patch new file mode 100644 index 0000000..93d506c --- /dev/null +++ b/pkgs/patches-linux-5.15/0082-ARM-dts-armada-375.dtsi-Add-definitions-for-PCIe-err.patch @@ -0,0 +1,43 @@ +From 3fca9286a8603517109f21d743bac3bf657b39b0 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org> +Date: Mon, 27 Jun 2022 16:45:29 +0200 +Subject: [PATCH 82/90] ARM: dts: armada-375.dtsi: Add definitions for PCIe + error interrupts +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Signed-off-by: Pali Rohár <pali@kernel.org> +--- + arch/arm/boot/dts/armada-375.dtsi | 8 ++++---- + 1 file changed, 4 insertions(+), 4 deletions(-) + +diff --git a/arch/arm/boot/dts/armada-375.dtsi b/arch/arm/boot/dts/armada-375.dtsi +index 82f0a59d112f..71b01a089c81 100644 +--- a/arch/arm/boot/dts/armada-375.dtsi ++++ b/arch/arm/boot/dts/armada-375.dtsi +@@ -573,8 +573,8 @@ pcie0: pcie@1,0 { + reg = <0x0800 0 0 0 0>; + #address-cells = <3>; + #size-cells = <2>; +- interrupt-names = "intx"; +- interrupts-extended = <&gic GIC_SPI 29 IRQ_TYPE_LEVEL_HIGH>; ++ interrupt-names = "intx", "error"; ++ interrupts-extended = <&gic GIC_SPI 29 IRQ_TYPE_LEVEL_HIGH>, <&soc_err 4>; + #interrupt-cells = <1>; + ranges = <0x82000000 0 0 0x82000000 0x1 0 1 0 + 0x81000000 0 0 0x81000000 0x1 0 1 0>; +@@ -601,8 +601,8 @@ pcie1: pcie@2,0 { + reg = <0x1000 0 0 0 0>; + #address-cells = <3>; + #size-cells = <2>; +- interrupt-names = "intx"; +- interrupts-extended = <&gic GIC_SPI 33 IRQ_TYPE_LEVEL_HIGH>; ++ interrupt-names = "intx", "error"; ++ interrupts-extended = <&gic GIC_SPI 33 IRQ_TYPE_LEVEL_HIGH>, <&soc_err 5>; + #interrupt-cells = <1>; + ranges = <0x82000000 0 0 0x82000000 0x2 0 1 0 + 0x81000000 0 0 0x81000000 0x2 0 1 0>; +-- +2.34.1 + diff --git a/pkgs/patches-linux-5.15/0083-ARM-dts-armada-380.dtsi-Add-definitions-for-PCIe-err.patch b/pkgs/patches-linux-5.15/0083-ARM-dts-armada-380.dtsi-Add-definitions-for-PCIe-err.patch new file mode 100644 index 0000000..1c5ddb2 --- /dev/null +++ b/pkgs/patches-linux-5.15/0083-ARM-dts-armada-380.dtsi-Add-definitions-for-PCIe-err.patch @@ -0,0 +1,57 @@ +From 8bbc60f9c76f529c929def04a9feb39bf05dc355 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org> +Date: Mon, 27 Jun 2022 17:41:39 +0200 +Subject: [PATCH 83/90] ARM: dts: armada-380.dtsi: Add definitions for PCIe + error interrupts +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Signed-off-by: Pali Rohár <pali@kernel.org> +--- + arch/arm/boot/dts/armada-380.dtsi | 15 +++++++++------ + 1 file changed, 9 insertions(+), 6 deletions(-) + +diff --git a/arch/arm/boot/dts/armada-380.dtsi b/arch/arm/boot/dts/armada-380.dtsi +index e94f22b0e9b5..970ac6820db9 100644 +--- a/arch/arm/boot/dts/armada-380.dtsi ++++ b/arch/arm/boot/dts/armada-380.dtsi +@@ -64,8 +64,9 @@ pcie@1,0 { + reg = <0x0800 0 0 0 0>; + #address-cells = <3>; + #size-cells = <2>; +- interrupt-names = "intx"; +- interrupts-extended = <&gic GIC_SPI 29 IRQ_TYPE_LEVEL_HIGH>; ++ interrupt-names = "intx", "error"; ++ interrupts-extended = <&gic GIC_SPI 29 IRQ_TYPE_LEVEL_HIGH>, ++ <&soc_err 4>; + #interrupt-cells = <1>; + ranges = <0x82000000 0 0 0x82000000 0x1 0 1 0 + 0x81000000 0 0 0x81000000 0x1 0 1 0>; +@@ -93,8 +94,9 @@ pcie@2,0 { + reg = <0x1000 0 0 0 0>; + #address-cells = <3>; + #size-cells = <2>; +- interrupt-names = "intx"; +- interrupts-extended = <&gic GIC_SPI 33 IRQ_TYPE_LEVEL_HIGH>; ++ interrupt-names = "intx", "error"; ++ interrupts-extended = <&gic GIC_SPI 33 IRQ_TYPE_LEVEL_HIGH>, ++ <&soc_err 5>; + #interrupt-cells = <1>; + ranges = <0x82000000 0 0 0x82000000 0x2 0 1 0 + 0x81000000 0 0 0x81000000 0x2 0 1 0>; +@@ -122,8 +124,9 @@ pcie@3,0 { + reg = <0x1800 0 0 0 0>; + #address-cells = <3>; + #size-cells = <2>; +- interrupt-names = "intx"; +- interrupts-extended = <&gic GIC_SPI 70 IRQ_TYPE_LEVEL_HIGH>; ++ interrupt-names = "intx", "error"; ++ interrupts-extended = <&gic GIC_SPI 70 IRQ_TYPE_LEVEL_HIGH>, ++ <&soc_err 15>; + #interrupt-cells = <1>; + ranges = <0x82000000 0 0 0x82000000 0x3 0 1 0 + 0x81000000 0 0 0x81000000 0x3 0 1 0>; +-- +2.34.1 + diff --git a/pkgs/patches-linux-5.15/0084-ARM-dts-armada-385.dtsi-Add-definitions-for-PCIe-err.patch b/pkgs/patches-linux-5.15/0084-ARM-dts-armada-385.dtsi-Add-definitions-for-PCIe-err.patch new file mode 100644 index 0000000..b4aaf8b --- /dev/null +++ b/pkgs/patches-linux-5.15/0084-ARM-dts-armada-385.dtsi-Add-definitions-for-PCIe-err.patch @@ -0,0 +1,71 @@ +From bb12d452fcc9386758e8d144c3090eddb0ef3fcd Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org> +Date: Mon, 18 Apr 2022 00:40:05 +0200 +Subject: [PATCH 84/90] ARM: dts: armada-385.dtsi: Add definitions for PCIe + error interrupts +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +PCIe error interrupt is reported by MPIC SoC Error IRQ controller. + +Signed-off-by: Pali Rohár <pali@kernel.org> +--- + arch/arm/boot/dts/armada-385.dtsi | 20 ++++++++++++-------- + 1 file changed, 12 insertions(+), 8 deletions(-) + +diff --git a/arch/arm/boot/dts/armada-385.dtsi b/arch/arm/boot/dts/armada-385.dtsi +index be8d607c59b2..d603de5aa574 100644 +--- a/arch/arm/boot/dts/armada-385.dtsi ++++ b/arch/arm/boot/dts/armada-385.dtsi +@@ -69,8 +69,9 @@ pcie1: pcie@1,0 { + reg = <0x0800 0 0 0 0>; + #address-cells = <3>; + #size-cells = <2>; +- interrupt-names = "intx"; +- interrupts-extended = <&gic GIC_SPI 29 IRQ_TYPE_LEVEL_HIGH>; ++ interrupt-names = "intx", "error"; ++ interrupts-extended = <&gic GIC_SPI 29 IRQ_TYPE_LEVEL_HIGH>, ++ <&soc_err 4>; + #interrupt-cells = <1>; + ranges = <0x82000000 0 0 0x82000000 0x1 0 1 0 + 0x81000000 0 0 0x81000000 0x1 0 1 0>; +@@ -97,8 +98,9 @@ pcie2: pcie@2,0 { + reg = <0x1000 0 0 0 0>; + #address-cells = <3>; + #size-cells = <2>; +- interrupt-names = "intx"; +- interrupts-extended = <&gic GIC_SPI 33 IRQ_TYPE_LEVEL_HIGH>; ++ interrupt-names = "intx", "error"; ++ interrupts-extended = <&gic GIC_SPI 33 IRQ_TYPE_LEVEL_HIGH>, ++ <&soc_err 5>; + #interrupt-cells = <1>; + ranges = <0x82000000 0 0 0x82000000 0x2 0 1 0 + 0x81000000 0 0 0x81000000 0x2 0 1 0>; +@@ -125,8 +127,9 @@ pcie3: pcie@3,0 { + reg = <0x1800 0 0 0 0>; + #address-cells = <3>; + #size-cells = <2>; +- interrupt-names = "intx"; +- interrupts-extended = <&gic GIC_SPI 70 IRQ_TYPE_LEVEL_HIGH>; ++ interrupt-names = "intx", "error"; ++ interrupts-extended = <&gic GIC_SPI 70 IRQ_TYPE_LEVEL_HIGH>, ++ <&soc_err 15>; + #interrupt-cells = <1>; + ranges = <0x82000000 0 0 0x82000000 0x3 0 1 0 + 0x81000000 0 0 0x81000000 0x3 0 1 0>; +@@ -156,8 +159,9 @@ pcie4: pcie@4,0 { + reg = <0x2000 0 0 0 0>; + #address-cells = <3>; + #size-cells = <2>; +- interrupt-names = "intx"; +- interrupts-extended = <&gic GIC_SPI 71 IRQ_TYPE_LEVEL_HIGH>; ++ interrupt-names = "intx", "error"; ++ interrupts-extended = <&gic GIC_SPI 71 IRQ_TYPE_LEVEL_HIGH>, ++ <&soc_err 16>; + #interrupt-cells = <1>; + ranges = <0x82000000 0 0 0x82000000 0x4 0 1 0 + 0x81000000 0 0 0x81000000 0x4 0 1 0>; +-- +2.34.1 + diff --git a/pkgs/patches-linux-5.15/0085-ARM-dts-armada-39x.dtsi-Add-definitions-for-PCIe-err.patch b/pkgs/patches-linux-5.15/0085-ARM-dts-armada-39x.dtsi-Add-definitions-for-PCIe-err.patch new file mode 100644 index 0000000..1b0be45 --- /dev/null +++ b/pkgs/patches-linux-5.15/0085-ARM-dts-armada-39x.dtsi-Add-definitions-for-PCIe-err.patch @@ -0,0 +1,69 @@ +From 3eb30a9715bc5754c09341c20d296d968d7885a0 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org> +Date: Mon, 27 Jun 2022 17:50:54 +0200 +Subject: [PATCH 85/90] ARM: dts: armada-39x.dtsi: Add definitions for PCIe + error interrupts +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Signed-off-by: Pali Rohár <pali@kernel.org> +--- + arch/arm/boot/dts/armada-39x.dtsi | 20 ++++++++++++-------- + 1 file changed, 12 insertions(+), 8 deletions(-) + +diff --git a/arch/arm/boot/dts/armada-39x.dtsi b/arch/arm/boot/dts/armada-39x.dtsi +index f21231a1f244..f58bd456e5ad 100644 +--- a/arch/arm/boot/dts/armada-39x.dtsi ++++ b/arch/arm/boot/dts/armada-39x.dtsi +@@ -443,8 +443,9 @@ pcie@1,0 { + reg = <0x0800 0 0 0 0>; + #address-cells = <3>; + #size-cells = <2>; +- interrupt-names = "intx"; +- interrupts-extended = <&gic GIC_SPI 29 IRQ_TYPE_LEVEL_HIGH>; ++ interrupt-names = "intx", "error"; ++ interrupts-extended = <&gic GIC_SPI 29 IRQ_TYPE_LEVEL_HIGH>, ++ <&soc_err 4>; + #interrupt-cells = <1>; + ranges = <0x82000000 0 0 0x82000000 0x1 0 1 0 + 0x81000000 0 0 0x81000000 0x1 0 1 0>; +@@ -472,8 +473,9 @@ pcie@2,0 { + reg = <0x1000 0 0 0 0>; + #address-cells = <3>; + #size-cells = <2>; +- interrupt-names = "intx"; +- interrupts-extended = <&gic GIC_SPI 33 IRQ_TYPE_LEVEL_HIGH>; ++ interrupt-names = "intx", "error"; ++ interrupts-extended = <&gic GIC_SPI 33 IRQ_TYPE_LEVEL_HIGH>, ++ <&soc_err 5>; + #interrupt-cells = <1>; + ranges = <0x82000000 0 0 0x82000000 0x2 0 1 0 + 0x81000000 0 0 0x81000000 0x2 0 1 0>; +@@ -501,8 +503,9 @@ pcie@3,0 { + reg = <0x1800 0 0 0 0>; + #address-cells = <3>; + #size-cells = <2>; +- interrupt-names = "intx"; +- interrupts-extended = <&gic GIC_SPI 70 IRQ_TYPE_LEVEL_HIGH>; ++ interrupt-names = "intx", "error"; ++ interrupts-extended = <&gic GIC_SPI 70 IRQ_TYPE_LEVEL_HIGH>, ++ <&soc_err 15>; + #interrupt-cells = <1>; + ranges = <0x82000000 0 0 0x82000000 0x3 0 1 0 + 0x81000000 0 0 0x81000000 0x3 0 1 0>; +@@ -533,8 +536,9 @@ pcie@4,0 { + reg = <0x2000 0 0 0 0>; + #address-cells = <3>; + #size-cells = <2>; +- interrupt-names = "intx"; +- interrupts-extended = <&gic GIC_SPI 71 IRQ_TYPE_LEVEL_HIGH>; ++ interrupt-names = "intx", "error"; ++ interrupts-extended = <&gic GIC_SPI 71 IRQ_TYPE_LEVEL_HIGH>, ++ <&soc_err 16>; + #interrupt-cells = <1>; + ranges = <0x82000000 0 0 0x82000000 0x4 0 1 0 + 0x81000000 0 0 0x81000000 0x4 0 1 0>; +-- +2.34.1 + diff --git a/pkgs/patches-linux-5.15/0086-PCI-pciehp-Enable-DLLSC-interrupt-only-if-supported.patch b/pkgs/patches-linux-5.15/0086-PCI-pciehp-Enable-DLLSC-interrupt-only-if-supported.patch new file mode 100644 index 0000000..51b8d55 --- /dev/null +++ b/pkgs/patches-linux-5.15/0086-PCI-pciehp-Enable-DLLSC-interrupt-only-if-supported.patch @@ -0,0 +1,139 @@ +From ff967b37402c672f475854be60f6b47a69732a59 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org> +Date: Wed, 31 Mar 2021 15:12:50 +0200 +Subject: [PATCH 86/90] PCI: pciehp: Enable DLLSC interrupt only if supported +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Don't enable Data Link Layer State Changed interrupt if it isn't +supported. + +Data Link Layer Link Active Reporting Capable bit in Link Capabilities +register indicates if Data Link Layer State Changed Enable is supported. + +Signed-off-by: Pali Rohár <pali@kernel.org> +Signed-off-by: Marek Behún <kabel@kernel.org> +--- + drivers/pci/hotplug/pciehp_hpc.c | 32 ++++++++++++++++++++++++-------- + drivers/pci/hotplug/pnv_php.c | 13 +++++++++---- + 2 files changed, 33 insertions(+), 12 deletions(-) + +diff --git a/drivers/pci/hotplug/pciehp_hpc.c b/drivers/pci/hotplug/pciehp_hpc.c +index 60098a701e83..5b0d48435b36 100644 +--- a/drivers/pci/hotplug/pciehp_hpc.c ++++ b/drivers/pci/hotplug/pciehp_hpc.c +@@ -788,6 +788,7 @@ static int pciehp_poll(void *data) + static void pcie_enable_notification(struct controller *ctrl) + { + u16 cmd, mask; ++ u32 link_cap; + + /* + * TBD: Power fault detected software notification support. +@@ -800,12 +801,17 @@ static void pcie_enable_notification(struct controller *ctrl) + * next power fault detected interrupt was notified again. + */ + ++ pcie_capability_read_dword(ctrl_dev(ctrl), PCI_EXP_LNKCAP, &link_cap); ++ + /* +- * Always enable link events: thus link-up and link-down shall +- * always be treated as hotplug and unplug respectively. Enable +- * presence detect only if Attention Button is not present. +- */ +- cmd = PCI_EXP_SLTCTL_DLLSCE; ++ * Enable link events if their support is indicated in Link Capability ++ * register: thus link-up and link-down shall always be treated as ++ * hotplug and unplug respectively. Enable presence detect only if ++ * Attention Button is not present. ++ */ ++ cmd = 0; ++ if (link_cap & PCI_EXP_LNKCAP_DLLLARC) ++ cmd |= PCI_EXP_SLTCTL_DLLSCE; + if (ATTN_BUTTN(ctrl)) + cmd |= PCI_EXP_SLTCTL_ABPE; + else +@@ -845,8 +851,13 @@ void pcie_clear_hotplug_events(struct controller *ctrl) + void pcie_enable_interrupt(struct controller *ctrl) + { + u16 mask; ++ u32 link_cap; + +- mask = PCI_EXP_SLTCTL_HPIE | PCI_EXP_SLTCTL_DLLSCE; ++ pcie_capability_read_dword(ctrl_dev(ctrl), PCI_EXP_LNKCAP, &link_cap); ++ ++ mask = PCI_EXP_SLTCTL_HPIE; ++ if (link_cap & PCI_EXP_LNKCAP_DLLLARC) ++ mask |= PCI_EXP_SLTCTL_DLLSCE; + pcie_write_cmd(ctrl, mask, mask); + } + +@@ -904,19 +915,24 @@ int pciehp_reset_slot(struct hotplug_slot *hotplug_slot, bool probe) + struct controller *ctrl = to_ctrl(hotplug_slot); + struct pci_dev *pdev = ctrl_dev(ctrl); + u16 stat_mask = 0, ctrl_mask = 0; ++ u32 link_cap; + int rc; + + if (probe) + return 0; + ++ pcie_capability_read_dword(pdev, PCI_EXP_LNKCAP, &link_cap); ++ + down_write_nested(&ctrl->reset_lock, ctrl->depth); + + if (!ATTN_BUTTN(ctrl)) { + ctrl_mask |= PCI_EXP_SLTCTL_PDCE; + stat_mask |= PCI_EXP_SLTSTA_PDC; + } +- ctrl_mask |= PCI_EXP_SLTCTL_DLLSCE; +- stat_mask |= PCI_EXP_SLTSTA_DLLSC; ++ if (link_cap & PCI_EXP_LNKCAP_DLLLARC) { ++ ctrl_mask |= PCI_EXP_SLTCTL_DLLSCE; ++ stat_mask |= PCI_EXP_SLTSTA_DLLSC; ++ } + + pcie_write_cmd(ctrl, 0, ctrl_mask); + ctrl_dbg(ctrl, "%s: SLOTCTRL %x write cmd %x\n", __func__, +diff --git a/drivers/pci/hotplug/pnv_php.c b/drivers/pci/hotplug/pnv_php.c +index f4c2e6e01be0..41b86fae41c8 100644 +--- a/drivers/pci/hotplug/pnv_php.c ++++ b/drivers/pci/hotplug/pnv_php.c +@@ -839,6 +839,7 @@ static void pnv_php_init_irq(struct pnv_php_slot *php_slot, int irq) + { + struct pci_dev *pdev = php_slot->pdev; + u32 broken_pdc = 0; ++ u32 link_cap; + u16 sts, ctrl; + int ret; + +@@ -873,17 +874,21 @@ static void pnv_php_init_irq(struct pnv_php_slot *php_slot, int irq) + return; + } + ++ pcie_capability_read_dword(pdev, PCI_EXP_LNKCAP, &link_cap); ++ + /* Enable the interrupts */ + pcie_capability_read_word(pdev, PCI_EXP_SLTCTL, &ctrl); + if (php_slot->flags & PNV_PHP_FLAG_BROKEN_PDC) { + ctrl &= ~PCI_EXP_SLTCTL_PDCE; +- ctrl |= (PCI_EXP_SLTCTL_HPIE | +- PCI_EXP_SLTCTL_DLLSCE); ++ ctrl |= PCI_EXP_SLTCTL_HPIE; + } else { + ctrl |= (PCI_EXP_SLTCTL_HPIE | +- PCI_EXP_SLTCTL_PDCE | +- PCI_EXP_SLTCTL_DLLSCE); ++ PCI_EXP_SLTCTL_PDCE); + } ++ if (link_cap & PCI_EXP_LNKCAP_DLLLARC) ++ ctrl |= PCI_EXP_SLTCTL_DLLSCE; ++ else ++ ctrl &= ~PCI_EXP_SLTCTL_DLLSCE; + pcie_capability_write_word(pdev, PCI_EXP_SLTCTL, ctrl); + + /* The interrupt is initialized successfully when @irq is valid */ +-- +2.34.1 + diff --git a/pkgs/patches-linux-5.15/0087-PCI-pciehp-Enable-Command-Completed-Interrupt-only-i.patch b/pkgs/patches-linux-5.15/0087-PCI-pciehp-Enable-Command-Completed-Interrupt-only-i.patch new file mode 100644 index 0000000..af62890 --- /dev/null +++ b/pkgs/patches-linux-5.15/0087-PCI-pciehp-Enable-Command-Completed-Interrupt-only-i.patch @@ -0,0 +1,38 @@ +From 589599f1ebbfa109bbcad2758af525b48c01b495 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org> +Date: Wed, 31 Mar 2021 15:14:29 +0200 +Subject: [PATCH 87/90] PCI: pciehp: Enable Command Completed Interrupt only if + supported +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +The No Command Completed Support bit in the Slot Capabilities register +indicates whether Command Completed Interrupt Enable is unsupported. + +Enable this interrupt only in the case it is supported. + +Signed-off-by: Pali Rohár <pali@kernel.org> +Signed-off-by: Marek Behún <kabel@kernel.org> +--- + drivers/pci/hotplug/pciehp_hpc.c | 4 +++- + 1 file changed, 3 insertions(+), 1 deletion(-) + +diff --git a/drivers/pci/hotplug/pciehp_hpc.c b/drivers/pci/hotplug/pciehp_hpc.c +index 5b0d48435b36..701031d809f5 100644 +--- a/drivers/pci/hotplug/pciehp_hpc.c ++++ b/drivers/pci/hotplug/pciehp_hpc.c +@@ -817,7 +817,9 @@ static void pcie_enable_notification(struct controller *ctrl) + else + cmd |= PCI_EXP_SLTCTL_PDCE; + if (!pciehp_poll_mode) +- cmd |= PCI_EXP_SLTCTL_HPIE | PCI_EXP_SLTCTL_CCIE; ++ cmd |= PCI_EXP_SLTCTL_HPIE; ++ if (!pciehp_poll_mode && !NO_CMD_CMPL(ctrl)) ++ cmd |= PCI_EXP_SLTCTL_CCIE; + + mask = (PCI_EXP_SLTCTL_PDCE | PCI_EXP_SLTCTL_ABPE | + PCI_EXP_SLTCTL_PFDE | +-- +2.34.1 + diff --git a/pkgs/patches-linux-5.15/0088-PCI-mvebu-Add-support-for-PCI_EXP_SLTSTA_DLLSC-via-h.patch b/pkgs/patches-linux-5.15/0088-PCI-mvebu-Add-support-for-PCI_EXP_SLTSTA_DLLSC-via-h.patch new file mode 100644 index 0000000..ad01551 --- /dev/null +++ b/pkgs/patches-linux-5.15/0088-PCI-mvebu-Add-support-for-PCI_EXP_SLTSTA_DLLSC-via-h.patch @@ -0,0 +1,297 @@ +From 38251bffc7552d62689e5fd6831f0aae1d842438 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org> +Date: Fri, 17 Sep 2021 14:53:11 +0200 +Subject: [PATCH 88/90] PCI: mvebu: Add support for PCI_EXP_SLTSTA_DLLSC via + hot plug interrupt +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +If link up/down state is changed in mvebu_pcie_link_up() then trigger +hot plug interrupt with DLLSC state change. + +Also triggers hot plug interrupt when mvebu triggers Link Failure interrupt +which indicates that link was changed from active state or when mvebu +triggers TxReq No Link interrupt which indicates that link is down while +trying to transmit PCIe transaction. + +And this hot plug interrupt also when explicit Link Disable or PCIe Host +Reset is issued as mvebu does not trigger Link Failure when dropping to +Detect via Hot Reset or Link Disable. + +Signed-off-by: Pali Rohár <pali@kernel.org> +--- + drivers/pci/controller/Kconfig | 3 + + drivers/pci/controller/pci-mvebu.c | 147 ++++++++++++++++++++++++++++- + 2 files changed, 149 insertions(+), 1 deletion(-) + +diff --git a/drivers/pci/controller/Kconfig b/drivers/pci/controller/Kconfig +index 46fa9bbff177..c217ea3bc703 100644 +--- a/drivers/pci/controller/Kconfig ++++ b/drivers/pci/controller/Kconfig +@@ -10,6 +10,9 @@ config PCI_MVEBU + depends on ARM + depends on OF + select PCI_BRIDGE_EMUL ++ select PCIEPORTBUS ++ select HOTPLUG_PCI ++ select HOTPLUG_PCI_PCIE + help + Add support for Marvell EBU PCIe controller. This PCIe controller + is used on 32-bit Marvell ARM SoCs: Dove, Kirkwood, Armada 370, +diff --git a/drivers/pci/controller/pci-mvebu.c b/drivers/pci/controller/pci-mvebu.c +index 319cd2b98545..2792b02278b9 100644 +--- a/drivers/pci/controller/pci-mvebu.c ++++ b/drivers/pci/controller/pci-mvebu.c +@@ -56,12 +56,14 @@ + #define PCIE_CONF_DATA_OFF 0x18fc + #define PCIE_INT_CAUSE_OFF 0x1900 + #define PCIE_INT_UNMASK_OFF 0x1910 ++#define PCIE_INT_TXREQ_NOLINK BIT(0) + #define PCIE_INT_DET_COR BIT(8) + #define PCIE_INT_DET_NONFATAL BIT(9) + #define PCIE_INT_DET_FATAL BIT(10) + #define PCIE_INT_ERR_FATAL BIT(16) + #define PCIE_INT_ERR_NONFATAL BIT(17) + #define PCIE_INT_ERR_COR BIT(18) ++#define PCIE_INT_LINK_FAIL BIT(23) + #define PCIE_INT_INTX(i) BIT(24+i) + #define PCIE_INT_PM_PME BIT(28) + #define PCIE_INT_DET_MASK (PCIE_INT_DET_COR | PCIE_INT_DET_NONFATAL | PCIE_INT_DET_FATAL) +@@ -134,6 +136,8 @@ struct mvebu_pcie_port { + int error_irq; + int intx_irq; + bool pme_pending; ++ struct timer_list link_irq_timer; ++ bool link_was_up; + }; + + static inline void mvebu_writel(struct mvebu_pcie_port *port, u32 val, u32 reg) +@@ -153,7 +157,26 @@ static inline bool mvebu_has_ioport(struct mvebu_pcie_port *port) + + static bool mvebu_pcie_link_up(struct mvebu_pcie_port *port) + { +- return !(mvebu_readl(port, PCIE_STAT_OFF) & PCIE_STAT_LINK_DOWN); ++ bool link_is_up; ++ u16 slotsta; ++ ++ link_is_up = !(mvebu_readl(port, PCIE_STAT_OFF) & PCIE_STAT_LINK_DOWN); ++ ++ if (link_is_up != port->link_was_up) { ++ port->link_was_up = link_is_up; ++ /* ++ * Link IRQ timer/handler is available only when "error" ++ * interrupt was specified in DT. ++ */ ++ if (port->error_irq > 0) { ++ slotsta = le16_to_cpu(port->bridge.pcie_conf.slotsta); ++ port->bridge.pcie_conf.slotsta = ++ cpu_to_le16(slotsta | PCI_EXP_SLTSTA_DLLSC); ++ mod_timer(&port->link_irq_timer, jiffies + 1); ++ } ++ } ++ ++ return link_is_up; + } + + static u8 mvebu_pcie_get_local_bus_nr(struct mvebu_pcie_port *port) +@@ -346,6 +369,19 @@ static void mvebu_pcie_setup_hw(struct mvebu_pcie_port *port) + mvebu_writel(port, unmask, PCIE_INT_UNMASK_OFF); + } + ++ /* ++ * Unmask No Link and Link Failure interrupts to process Link Down ++ * events. These events are reported as Data Link Layer State Changed ++ * notification via Hot Plug Interrupt. Other parts of Link change ++ * events are available only when "error" interrupt was specified in DT. ++ * So enable these interrupts under same conditions. ++ */ ++ if (port->error_irq > 0) { ++ unmask = mvebu_readl(port, PCIE_INT_UNMASK_OFF); ++ unmask |= PCIE_INT_TXREQ_NOLINK | PCIE_INT_LINK_FAIL; ++ mvebu_writel(port, unmask, PCIE_INT_UNMASK_OFF); ++ } ++ + /* + * Fallback code when "intx" interrupt was not specified in DT: + * Unmask all legacy INTx interrupts as driver does not provide a way +@@ -697,6 +733,14 @@ mvebu_pci_bridge_emul_pcie_conf_read(struct pci_bridge_emul *bridge, + val |= slotctl & PCI_EXP_SLTCTL_ASPL_DISABLE; + else if (!(mvebu_readl(port, PCIE_SSPL_OFF) & PCIE_SSPL_ENABLE)) + val |= PCI_EXP_SLTCTL_ASPL_DISABLE; ++ /* ++ * HPIE and DLLSCE bits are stored only in emulated config ++ * space buffer and are supported only when or "error" interrupt ++ * was specified in DT. ++ */ ++ if (port->error_irq > 0) ++ val |= slotctl & (PCI_EXP_SLTCTL_HPIE | ++ PCI_EXP_SLTCTL_DLLSCE); + /* This callback is 32-bit and in high bits is slot status. */ + val |= slotsta << 16; + *value = val; +@@ -828,6 +872,25 @@ mvebu_pci_bridge_emul_base_conf_write(struct pci_bridge_emul *bridge, + else + ctrl &= ~PCIE_CTRL_MASTER_HOT_RESET; + mvebu_writel(port, ctrl, PCIE_CTRL_OFF); ++ /* ++ * When dropping to Detect via Hot Reset, Disable Link ++ * or Loopback states, the Link Failure interrupt is not ++ * asserted. So when setting Secondary Bus Reset / Hot ++ * Reset bit, call link IRQ timer/handler manually. ++ */ ++ if ((ctrl & PCIE_CTRL_MASTER_HOT_RESET) && port->link_was_up) { ++ port->link_was_up = false; ++ /* ++ * Link IRQ timer/handler is available only when ++ * "error" interrupt was specified in DT. ++ */ ++ if (port->error_irq > 0) { ++ u16 slotsta = le16_to_cpu(port->bridge.pcie_conf.slotsta); ++ port->bridge.pcie_conf.slotsta = ++ cpu_to_le16(slotsta | PCI_EXP_SLTSTA_DLLSC); ++ mod_timer(&port->link_irq_timer, jiffies + 1); ++ } ++ } + } + break; + +@@ -856,6 +919,25 @@ mvebu_pci_bridge_emul_pcie_conf_write(struct pci_bridge_emul *bridge, + new &= ~PCI_EXP_LNKCTL_CLKREQ_EN; + + mvebu_writel(port, new, PCIE_CAP_PCIEXP + PCI_EXP_LNKCTL); ++ /* ++ * When dropping to Detect via Hot Reset, Disable Link ++ * or Loopback states, the Link Failure interrupt is not ++ * asserted. So when setting Link Disable bit, call link ++ * IRQ timer/handler manually. ++ */ ++ if ((new & PCI_EXP_LNKCTL_LD) && port->link_was_up) { ++ port->link_was_up = false; ++ /* ++ * Link IRQ timer/handler is available only when ++ * "error" interrupt was specified in DT. ++ */ ++ if (port->error_irq > 0) { ++ u16 slotsta = le16_to_cpu(port->bridge.pcie_conf.slotsta); ++ port->bridge.pcie_conf.slotsta = ++ cpu_to_le16(slotsta | PCI_EXP_SLTSTA_DLLSC); ++ mod_timer(&port->link_irq_timer, jiffies + 1); ++ } ++ } + break; + + case PCI_EXP_SLTCTL: +@@ -996,6 +1078,15 @@ static int mvebu_pci_bridge_emul_init(struct mvebu_pcie_port *port) + bridge->pcie_conf.cap = cpu_to_le16(pcie_cap_ver | PCI_EXP_FLAGS_SLOT); + + /* ++ * When "error" interrupt was specified in DT then driver is able to ++ * deliver Data Link Layer State Change interrupt. So in this case mark ++ * bridge as Hot Plug Capable as this is the way how to enable ++ * delivering of Data Link Layer State Change interrupts. ++ * ++ * No Command Completed Support is set because bridge does not support ++ * Command Completed Interrupt. Every command is executed immediately ++ * without any delay. ++ * + * Set Presence Detect State bit permanently as there is no support for + * unplugging PCIe card from the slot. Assume that PCIe card is always + * connected in slot. +@@ -1007,6 +1098,8 @@ static int mvebu_pci_bridge_emul_init(struct mvebu_pcie_port *port) + * Also set correct slot power limit. + */ + bridge->pcie_conf.slotcap = cpu_to_le32( ++ PCI_EXP_SLTCAP_NCCS | ++ (port->error_irq > 0 ? PCI_EXP_SLTCAP_HPC : 0) | + FIELD_PREP(PCI_EXP_SLTCAP_SPLV, port->slot_power_limit_value) | + FIELD_PREP(PCI_EXP_SLTCAP_SPLS, port->slot_power_limit_scale) | + FIELD_PREP(PCI_EXP_SLTCAP_PSN, port->port+1)); +@@ -1198,11 +1291,29 @@ static int mvebu_pcie_init_irq_domain(struct mvebu_pcie_port *port) + return 0; + } + ++static void mvebu_pcie_link_irq_handler(struct timer_list *timer) ++{ ++ struct mvebu_pcie_port *port = from_timer(port, timer, link_irq_timer); ++ struct device *dev = &port->pcie->pdev->dev; ++ u16 slotctl; ++ ++ dev_info(dev, "%s: link %s\n", port->name, port->link_was_up ? "up" : "down"); ++ ++ slotctl = le16_to_cpu(port->bridge.pcie_conf.slotctl); ++ if (!(slotctl & PCI_EXP_SLTCTL_DLLSCE) || ++ !(slotctl & PCI_EXP_SLTCTL_HPIE)) ++ return; ++ ++ if (generic_handle_domain_irq(port->rp_irq_domain, 0) == -EINVAL) ++ dev_err_ratelimited(dev, "unhandled HP IRQ\n"); ++} ++ + static irqreturn_t mvebu_pcie_error_irq_handler(int irq, void *arg) + { + struct mvebu_pcie_port *port = arg; + struct device *dev = &port->pcie->pdev->dev; + u32 cause, unmask, status; ++ u16 slotsta; + + cause = mvebu_readl(port, PCIE_INT_CAUSE_OFF); + unmask = mvebu_readl(port, PCIE_INT_UNMASK_OFF); +@@ -1240,6 +1351,25 @@ static irqreturn_t mvebu_pcie_error_irq_handler(int irq, void *arg) + dev_err_ratelimited(dev, "unhandled ERR IRQ\n"); + } + ++ /* Process No Link and Link Failure interrupts as HP IRQ */ ++ if (status & (PCIE_INT_TXREQ_NOLINK | PCIE_INT_LINK_FAIL)) { ++ mvebu_writel(port, ++ ~(PCIE_INT_TXREQ_NOLINK | PCIE_INT_LINK_FAIL), ++ PCIE_INT_CAUSE_OFF); ++ if (port->link_was_up) { ++ port->link_was_up = false; ++ slotsta = le16_to_cpu(port->bridge.pcie_conf.slotsta); ++ port->bridge.pcie_conf.slotsta = ++ cpu_to_le16(slotsta | PCI_EXP_SLTSTA_DLLSC); ++ /* ++ * Deactivate timer and call mvebu_pcie_link_irq_handler() ++ * function directly as we are in the interrupt context. ++ */ ++ del_timer_sync(&port->link_irq_timer); ++ mvebu_pcie_link_irq_handler(&port->link_irq_timer); ++ } ++ } ++ + return status ? IRQ_HANDLED : IRQ_NONE; + } + +@@ -1805,6 +1935,18 @@ static int mvebu_pcie_probe(struct platform_device *pdev) + } + } + ++ /* ++ * Function mvebu_pcie_link_irq_handler() calls function ++ * generic_handle_irq() and it expects local IRQs to be disabled ++ * as normally generic_handle_irq() is called from the interrupt ++ * context. So use TIMER_IRQSAFE flag for this link_irq_timer. ++ * Available only if "or "error" interrupt was specified. ++ */ ++ if (port->error_irq > 0) ++ timer_setup(&port->link_irq_timer, ++ mvebu_pcie_link_irq_handler, ++ TIMER_IRQSAFE); ++ + /* + * PCIe topology exported by mvebu hw is quite complicated. In + * reality has something like N fully independent host bridges +@@ -1941,6 +2083,9 @@ static int mvebu_pcie_remove(struct platform_device *pdev) + irq_domain_remove(port->rp_irq_domain); + } + ++ if (port->error_irq > 0) ++ del_timer_sync(&port->link_irq_timer); ++ + /* Free config space for emulated root bridge. */ + pci_bridge_emul_cleanup(&port->bridge); + +-- +2.34.1 + diff --git a/pkgs/patches-linux-5.15/0089-PCI-mvebu-use-BIT-and-GENMASK-macros-instead-of-hard.patch b/pkgs/patches-linux-5.15/0089-PCI-mvebu-use-BIT-and-GENMASK-macros-instead-of-hard.patch new file mode 100644 index 0000000..a7dbad5 --- /dev/null +++ b/pkgs/patches-linux-5.15/0089-PCI-mvebu-use-BIT-and-GENMASK-macros-instead-of-hard.patch @@ -0,0 +1,47 @@ +From fbe986f71d0b5c280cbf08a3ce11d121739f9c8f Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org> +Date: Fri, 17 Sep 2021 14:54:29 +0200 +Subject: [PATCH 89/90] PCI: mvebu: use BIT() and GENMASK() macros instead of + hardcoded hex values +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Signed-off-by: Pali Rohár <pali@kernel.org> +--- + drivers/pci/controller/pci-mvebu.c | 8 ++++---- + 1 file changed, 4 insertions(+), 4 deletions(-) + +diff --git a/drivers/pci/controller/pci-mvebu.c b/drivers/pci/controller/pci-mvebu.c +index 2792b02278b9..87f1c2aa5010 100644 +--- a/drivers/pci/controller/pci-mvebu.c ++++ b/drivers/pci/controller/pci-mvebu.c +@@ -44,7 +44,7 @@ + #define PCIE_WIN5_BASE_OFF 0x1884 + #define PCIE_WIN5_REMAP_OFF 0x188c + #define PCIE_CONF_ADDR_OFF 0x18f8 +-#define PCIE_CONF_ADDR_EN 0x80000000 ++#define PCIE_CONF_ADDR_EN BIT(31) + #define PCIE_CONF_REG(r) ((((r) & 0xf00) << 16) | ((r) & 0xfc)) + #define PCIE_CONF_BUS(b) (((b) & 0xff) << 16) + #define PCIE_CONF_DEV(d) (((d) & 0x1f) << 11) +@@ -70,13 +70,13 @@ + #define PCIE_INT_ERR_MASK (PCIE_INT_ERR_FATAL | PCIE_INT_ERR_NONFATAL | PCIE_INT_ERR_COR) + #define PCIE_INT_ALL_MASK GENMASK(31, 0) + #define PCIE_CTRL_OFF 0x1a00 +-#define PCIE_CTRL_X1_MODE 0x0001 ++#define PCIE_CTRL_X1_MODE BIT(0) + #define PCIE_CTRL_RC_MODE BIT(1) + #define PCIE_CTRL_MASTER_HOT_RESET BIT(24) + #define PCIE_STAT_OFF 0x1a04 +-#define PCIE_STAT_BUS 0xff00 +-#define PCIE_STAT_DEV 0x1f0000 + #define PCIE_STAT_LINK_DOWN BIT(0) ++#define PCIE_STAT_BUS GENMASK(15, 8) ++#define PCIE_STAT_DEV GENMASK(20, 16) + #define PCIE_SSPL_OFF 0x1a0c + #define PCIE_SSPL_VALUE_SHIFT 0 + #define PCIE_SSPL_VALUE_MASK GENMASK(7, 0) +-- +2.34.1 + diff --git a/pkgs/patches-linux-5.15/0090-PCI-mvebu-For-consistency-add-_OFF-suffix-to-all-reg.patch b/pkgs/patches-linux-5.15/0090-PCI-mvebu-For-consistency-add-_OFF-suffix-to-all-reg.patch new file mode 100644 index 0000000..8679fdf --- /dev/null +++ b/pkgs/patches-linux-5.15/0090-PCI-mvebu-For-consistency-add-_OFF-suffix-to-all-reg.patch @@ -0,0 +1,167 @@ +From 905b0eda8ec724de75d825b1d3625c27beb0bc6d Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org> +Date: Fri, 17 Sep 2021 14:55:03 +0200 +Subject: [PATCH 90/90] PCI: mvebu: For consistency add _OFF suffix to all + registers +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Signed-off-by: Pali Rohár <pali@kernel.org> +--- + drivers/pci/controller/pci-mvebu.c | 40 +++++++++++++++--------------- + 1 file changed, 20 insertions(+), 20 deletions(-) + +diff --git a/drivers/pci/controller/pci-mvebu.c b/drivers/pci/controller/pci-mvebu.c +index 87f1c2aa5010..b04b9bbe9217 100644 +--- a/drivers/pci/controller/pci-mvebu.c ++++ b/drivers/pci/controller/pci-mvebu.c +@@ -34,7 +34,7 @@ + #define PCIE_BAR_LO_OFF(n) (0x0010 + ((n) << 3)) + #define PCIE_BAR_HI_OFF(n) (0x0014 + ((n) << 3)) + #define PCIE_SSDEV_ID_OFF 0x002c +-#define PCIE_CAP_PCIEXP 0x0060 ++#define PCIE_CAP_PCIEXP_OFF 0x0060 + #define PCIE_CAP_PCIERR_OFF 0x0100 + #define PCIE_BAR_CTRL_OFF(n) (0x1804 + (((n) - 1) * 4)) + #define PCIE_WIN04_CTRL_OFF(n) (0x1820 + ((n) << 4)) +@@ -83,8 +83,8 @@ + #define PCIE_SSPL_SCALE_SHIFT 8 + #define PCIE_SSPL_SCALE_MASK GENMASK(9, 8) + #define PCIE_SSPL_ENABLE BIT(16) +-#define PCIE_RC_RTSTA 0x1a14 +-#define PCIE_DEBUG_CTRL 0x1a60 ++#define PCIE_RC_RTSTA_OFF 0x1a14 ++#define PCIE_DEBUG_CTRL_OFF 0x1a60 + #define PCIE_DEBUG_SOFT_RESET BIT(20) + + struct mvebu_pcie_port; +@@ -296,10 +296,10 @@ static void mvebu_pcie_setup_hw(struct mvebu_pcie_port *port) + * be set to number of SerDes PCIe lanes (1 or 4). If this register is + * not set correctly then link with endpoint card is not established. + */ +- lnkcap = mvebu_readl(port, PCIE_CAP_PCIEXP + PCI_EXP_LNKCAP); ++ lnkcap = mvebu_readl(port, PCIE_CAP_PCIEXP_OFF + PCI_EXP_LNKCAP); + lnkcap &= ~PCI_EXP_LNKCAP_MLW; + lnkcap |= (port->is_x4 ? 4 : 1) << 4; +- mvebu_writel(port, lnkcap, PCIE_CAP_PCIEXP + PCI_EXP_LNKCAP); ++ mvebu_writel(port, lnkcap, PCIE_CAP_PCIEXP_OFF + PCI_EXP_LNKCAP); + + /* Disable Root Bridge I/O space, memory space and bus mastering. */ + cmd = mvebu_readl(port, PCIE_CMD_OFF); +@@ -695,11 +695,11 @@ mvebu_pci_bridge_emul_pcie_conf_read(struct pci_bridge_emul *bridge, + + switch (reg) { + case PCI_EXP_DEVCAP: +- *value = mvebu_readl(port, PCIE_CAP_PCIEXP + PCI_EXP_DEVCAP); ++ *value = mvebu_readl(port, PCIE_CAP_PCIEXP_OFF + PCI_EXP_DEVCAP); + break; + + case PCI_EXP_DEVCTL: +- *value = mvebu_readl(port, PCIE_CAP_PCIEXP + PCI_EXP_DEVCTL); ++ *value = mvebu_readl(port, PCIE_CAP_PCIEXP_OFF + PCI_EXP_DEVCTL); + break; + + case PCI_EXP_LNKCAP: +@@ -709,13 +709,13 @@ mvebu_pci_bridge_emul_pcie_conf_read(struct pci_bridge_emul *bridge, + * Additionally enable Data Link Layer Link Active Reporting + * Capable bit as DL_Active indication is provided too. + */ +- *value = (mvebu_readl(port, PCIE_CAP_PCIEXP + PCI_EXP_LNKCAP) & ++ *value = (mvebu_readl(port, PCIE_CAP_PCIEXP_OFF + PCI_EXP_LNKCAP) & + ~PCI_EXP_LNKCAP_CLKPM) | PCI_EXP_LNKCAP_DLLLARC; + break; + + case PCI_EXP_LNKCTL: + /* DL_Active indication is provided via PCIE_STAT_OFF */ +- *value = mvebu_readl(port, PCIE_CAP_PCIEXP + PCI_EXP_LNKCTL) | ++ *value = mvebu_readl(port, PCIE_CAP_PCIEXP_OFF + PCI_EXP_LNKCTL) | + (mvebu_pcie_link_up(port) ? + (PCI_EXP_LNKSTA_DLLLA << 16) : 0); + break; +@@ -753,19 +753,19 @@ mvebu_pci_bridge_emul_pcie_conf_read(struct pci_bridge_emul *bridge, + break; + + case PCI_EXP_RTSTA: +- *value = mvebu_readl(port, PCIE_RC_RTSTA); ++ *value = mvebu_readl(port, PCIE_RC_RTSTA_OFF); + break; + + case PCI_EXP_DEVCAP2: +- *value = mvebu_readl(port, PCIE_CAP_PCIEXP + PCI_EXP_DEVCAP2); ++ *value = mvebu_readl(port, PCIE_CAP_PCIEXP_OFF + PCI_EXP_DEVCAP2); + break; + + case PCI_EXP_DEVCTL2: +- *value = mvebu_readl(port, PCIE_CAP_PCIEXP + PCI_EXP_DEVCTL2); ++ *value = mvebu_readl(port, PCIE_CAP_PCIEXP_OFF + PCI_EXP_DEVCTL2); + break; + + case PCI_EXP_LNKCTL2: +- *value = mvebu_readl(port, PCIE_CAP_PCIEXP + PCI_EXP_LNKCTL2); ++ *value = mvebu_readl(port, PCIE_CAP_PCIEXP_OFF + PCI_EXP_LNKCTL2); + break; + + default: +@@ -907,7 +907,7 @@ mvebu_pci_bridge_emul_pcie_conf_write(struct pci_bridge_emul *bridge, + + switch (reg) { + case PCI_EXP_DEVCTL: +- mvebu_writel(port, new, PCIE_CAP_PCIEXP + PCI_EXP_DEVCTL); ++ mvebu_writel(port, new, PCIE_CAP_PCIEXP_OFF + PCI_EXP_DEVCTL); + break; + + case PCI_EXP_LNKCTL: +@@ -918,7 +918,7 @@ mvebu_pci_bridge_emul_pcie_conf_write(struct pci_bridge_emul *bridge, + */ + new &= ~PCI_EXP_LNKCTL_CLKREQ_EN; + +- mvebu_writel(port, new, PCIE_CAP_PCIEXP + PCI_EXP_LNKCTL); ++ mvebu_writel(port, new, PCIE_CAP_PCIEXP_OFF + PCI_EXP_LNKCTL); + /* + * When dropping to Detect via Hot Reset, Disable Link + * or Loopback states, the Link Failure interrupt is not +@@ -958,7 +958,7 @@ mvebu_pci_bridge_emul_pcie_conf_write(struct pci_bridge_emul *bridge, + + case PCI_EXP_RTSTA: + /* +- * PME Status bit in Root Status Register (PCIE_RC_RTSTA) ++ * PME Status bit in Root Status Register (PCIE_RC_RTSTA_OFF) + * is read-only and can be cleared only by writing 0b to the + * Interrupt Cause RW0C register (PCIE_INT_CAUSE_OFF). So + * clear PME via Interrupt Cause and also set port->pme_pending +@@ -983,11 +983,11 @@ mvebu_pci_bridge_emul_pcie_conf_write(struct pci_bridge_emul *bridge, + break; + + case PCI_EXP_DEVCTL2: +- mvebu_writel(port, new, PCIE_CAP_PCIEXP + PCI_EXP_DEVCTL2); ++ mvebu_writel(port, new, PCIE_CAP_PCIEXP_OFF + PCI_EXP_DEVCTL2); + break; + + case PCI_EXP_LNKCTL2: +- mvebu_writel(port, new, PCIE_CAP_PCIEXP + PCI_EXP_LNKCTL2); ++ mvebu_writel(port, new, PCIE_CAP_PCIEXP_OFF + PCI_EXP_LNKCTL2); + break; + + default: +@@ -1047,7 +1047,7 @@ static int mvebu_pci_bridge_emul_init(struct mvebu_pcie_port *port) + u32 dev_id = mvebu_readl(port, PCIE_DEV_ID_OFF); + u32 dev_rev = mvebu_readl(port, PCIE_DEV_REV_OFF); + u32 ssdev_id = mvebu_readl(port, PCIE_SSDEV_ID_OFF); +- u32 pcie_cap = mvebu_readl(port, PCIE_CAP_PCIEXP); ++ u32 pcie_cap = mvebu_readl(port, PCIE_CAP_PCIEXP_OFF); + u8 pcie_cap_ver = ((pcie_cap >> 16) & PCI_EXP_FLAGS_VERS); + + bridge->conf.vendor = cpu_to_le16(dev_id & 0xffff); +@@ -1108,7 +1108,7 @@ static int mvebu_pci_bridge_emul_init(struct mvebu_pcie_port *port) + bridge->subsystem_vendor_id = ssdev_id & 0xffff; + bridge->subsystem_id = ssdev_id >> 16; + bridge->has_pcie = true; +- bridge->pcie_start = PCIE_CAP_PCIEXP; ++ bridge->pcie_start = PCIE_CAP_PCIEXP_OFF; + bridge->data = port; + bridge->ops = &mvebu_pci_bridge_emul_ops; + +-- +2.34.1 + diff --git a/pkgs/patches-linux-5.15/0091-PCI-aardvark-Add-support-for-PCI-Bridge-Subsystem-Ve.patch b/pkgs/patches-linux-5.15/0091-PCI-aardvark-Add-support-for-PCI-Bridge-Subsystem-Ve.patch new file mode 100644 index 0000000..b058048 --- /dev/null +++ b/pkgs/patches-linux-5.15/0091-PCI-aardvark-Add-support-for-PCI-Bridge-Subsystem-Ve.patch @@ -0,0 +1,45 @@ +From a6e8a5fd098c65dd8608d21d64df48f5cc7c1957 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org> +Date: Tue, 19 Oct 2021 13:54:19 +0200 +Subject: [PATCH 1/9] PCI: aardvark: Add support for PCI Bridge Subsystem + Vendor ID on emulated bridge +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Register with Subsystem Device/Vendor ID is at offset 0x2c. Export is via +emulated bridge. + +After this change Subsystem ID is visible in lspci output at line: + + Capabilities: [40] Subsystem + +Signed-off-by: Pali Rohár <pali@kernel.org> +--- + drivers/pci/controller/pci-aardvark.c | 3 +++ + 1 file changed, 3 insertions(+) + +diff --git a/drivers/pci/controller/pci-aardvark.c b/drivers/pci/controller/pci-aardvark.c +index 4ed1b3ed3d35..8f01bf82a754 100644 +--- a/drivers/pci/controller/pci-aardvark.c ++++ b/drivers/pci/controller/pci-aardvark.c +@@ -32,6 +32,7 @@ + #define PCIE_CORE_DEV_ID_REG 0x0 + #define PCIE_CORE_CMD_STATUS_REG 0x4 + #define PCIE_CORE_DEV_REV_REG 0x8 ++#define PCIE_CORE_SSDEV_ID_REG 0x2c + #define PCIE_CORE_PCIEXP_CAP 0xc0 + #define PCIE_CORE_ERR_CAPCTL_REG 0x118 + #define PCIE_CORE_ERR_CAPCTL_ECRC_CHK_TX BIT(5) +@@ -983,6 +984,8 @@ static int advk_sw_pci_bridge_init(struct advk_pcie *pcie) + /* Indicates supports for Completion Retry Status */ + bridge->pcie_conf.rootcap = cpu_to_le16(PCI_EXP_RTCAP_CRSVIS); + ++ bridge->subsystem_vendor_id = advk_readl(pcie, PCIE_CORE_SSDEV_ID_REG) & 0xffff; ++ bridge->subsystem_id = advk_readl(pcie, PCIE_CORE_SSDEV_ID_REG) >> 16; + bridge->has_pcie = true; + bridge->pcie_start = PCIE_CORE_PCIEXP_CAP; + bridge->data = pcie; +-- +2.34.1 + diff --git a/pkgs/patches-linux-5.15/0092-PCI-aardvark-Dispose-INTx-irqs-prior-to-removing-INT.patch b/pkgs/patches-linux-5.15/0092-PCI-aardvark-Dispose-INTx-irqs-prior-to-removing-INT.patch new file mode 100644 index 0000000..bc21fc1 --- /dev/null +++ b/pkgs/patches-linux-5.15/0092-PCI-aardvark-Dispose-INTx-irqs-prior-to-removing-INT.patch @@ -0,0 +1,44 @@ +From dbd2881ab44968823962a14f6de0ed73f916e7b5 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org> +Date: Sat, 9 Jul 2022 16:55:54 +0200 +Subject: [PATCH 2/9] PCI: aardvark: Dispose INTx irqs prior to removing INTx + domain +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Documentation for irq_domain_remove() says that all mapping within the +domain must be disposed prior to domain remove. + +Currently INTx irqs are not disposed in pci-aardvark.c device unbind callback +which cause that kernel crashes after unloading driver and trying to read +/sys/kernel/debug/irq/irqs/<num> or /proc/interrupts. + +Fixes: 526a76991b7b ("PCI: aardvark: Implement driver 'remove' function and allow to build it as module") +Signed-off-by: Pali Rohár <pali@kernel.org> +--- + drivers/pci/controller/pci-aardvark.c | 8 ++++++++ + 1 file changed, 8 insertions(+) + +diff --git a/drivers/pci/controller/pci-aardvark.c b/drivers/pci/controller/pci-aardvark.c +index 8f01bf82a754..5dda72ce4d7e 100644 +--- a/drivers/pci/controller/pci-aardvark.c ++++ b/drivers/pci/controller/pci-aardvark.c +@@ -1439,6 +1439,14 @@ static int advk_pcie_init_irq_domain(struct advk_pcie *pcie) + + static void advk_pcie_remove_irq_domain(struct advk_pcie *pcie) + { ++ int virq, i; ++ ++ for (i = 0; i < PCI_NUM_INTX; i++) { ++ virq = irq_find_mapping(pcie->irq_domain, i); ++ if (virq > 0) ++ irq_dispose_mapping(virq); ++ } ++ + irq_domain_remove(pcie->irq_domain); + } + +-- +2.34.1 + diff --git a/pkgs/patches-linux-5.15/0093-PCI-aardvark-Add-support-for-AER-registers-on-emulat.patch b/pkgs/patches-linux-5.15/0093-PCI-aardvark-Add-support-for-AER-registers-on-emulat.patch new file mode 100644 index 0000000..008ef8d --- /dev/null +++ b/pkgs/patches-linux-5.15/0093-PCI-aardvark-Add-support-for-AER-registers-on-emulat.patch @@ -0,0 +1,139 @@ +From b7f4aea2c3b7f25333b8cf086dd7af7e836a1b6a Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org> +Date: Tue, 24 May 2022 15:28:26 +0200 +Subject: [PATCH 3/9] PCI: aardvark: Add support for AER registers on emulated + bridge +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Aardvark controller supports Advanced Error Reporting configuration +registers. + +Export these registers on the emulated root bridge via new .read_ext() and +.write_ext() methods. + +Note that in the Advanced Error Reporting Capability header the offset to +the next Extended Capability header is set, but it is not documented in +Armada 3700 Functional Specification. Since this change adds support only +for Advanced Error Reporting, explicitly clear PCI_EXT_CAP_NEXT bits in AER +capability header. + +Now the pcieport driver correctly detects AER support and allows PCIe AER +driver to start receiving ERR interrupts. Kernel log now says: + + pcieport 0000:00:00.0: AER: enabled with IRQ 52 + +Link: https://lore.kernel.org/r/20220524132827.8837-2-kabel@kernel.org +Signed-off-by: Pali Rohár <pali@kernel.org> +Signed-off-by: Marek Behún <kabel@kernel.org> +Signed-off-by: Bjorn Helgaas <bhelgaas@google.com> +--- + drivers/pci/controller/pci-aardvark.c | 79 +++++++++++++++++++++++++++ + 1 file changed, 79 insertions(+) + +diff --git a/drivers/pci/controller/pci-aardvark.c b/drivers/pci/controller/pci-aardvark.c +index 5dda72ce4d7e..73c37164fd44 100644 +--- a/drivers/pci/controller/pci-aardvark.c ++++ b/drivers/pci/controller/pci-aardvark.c +@@ -34,6 +34,7 @@ + #define PCIE_CORE_DEV_REV_REG 0x8 + #define PCIE_CORE_SSDEV_ID_REG 0x2c + #define PCIE_CORE_PCIEXP_CAP 0xc0 ++#define PCIE_CORE_PCIERR_CAP 0x100 + #define PCIE_CORE_ERR_CAPCTL_REG 0x118 + #define PCIE_CORE_ERR_CAPCTL_ECRC_CHK_TX BIT(5) + #define PCIE_CORE_ERR_CAPCTL_ECRC_CHK_TX_EN BIT(6) +@@ -945,11 +946,89 @@ advk_pci_bridge_emul_pcie_conf_write(struct pci_bridge_emul *bridge, + } + } + ++static pci_bridge_emul_read_status_t ++advk_pci_bridge_emul_ext_conf_read(struct pci_bridge_emul *bridge, ++ int reg, u32 *value) ++{ ++ struct advk_pcie *pcie = bridge->data; ++ ++ switch (reg) { ++ case 0: ++ *value = advk_readl(pcie, PCIE_CORE_PCIERR_CAP + reg); ++ ++ /* ++ * PCI_EXT_CAP_NEXT bits are set to offset 0x150, but Armada ++ * 3700 Functional Specification does not document registers ++ * at those addresses. ++ * ++ * Thus we clear PCI_EXT_CAP_NEXT bits to make Advanced Error ++ * Reporting Capability header the last Extended Capability. ++ * If we obtain documentation for those registers in the ++ * future, this can be changed. ++ */ ++ *value &= 0x000fffff; ++ return PCI_BRIDGE_EMUL_HANDLED; ++ ++ case PCI_ERR_UNCOR_STATUS: ++ case PCI_ERR_UNCOR_MASK: ++ case PCI_ERR_UNCOR_SEVER: ++ case PCI_ERR_COR_STATUS: ++ case PCI_ERR_COR_MASK: ++ case PCI_ERR_CAP: ++ case PCI_ERR_HEADER_LOG + 0: ++ case PCI_ERR_HEADER_LOG + 4: ++ case PCI_ERR_HEADER_LOG + 8: ++ case PCI_ERR_HEADER_LOG + 12: ++ case PCI_ERR_ROOT_COMMAND: ++ case PCI_ERR_ROOT_STATUS: ++ case PCI_ERR_ROOT_ERR_SRC: ++ *value = advk_readl(pcie, PCIE_CORE_PCIERR_CAP + reg); ++ return PCI_BRIDGE_EMUL_HANDLED; ++ ++ default: ++ return PCI_BRIDGE_EMUL_NOT_HANDLED; ++ } ++} ++ ++static void ++advk_pci_bridge_emul_ext_conf_write(struct pci_bridge_emul *bridge, ++ int reg, u32 old, u32 new, u32 mask) ++{ ++ struct advk_pcie *pcie = bridge->data; ++ ++ switch (reg) { ++ /* These are W1C registers, so clear other bits */ ++ case PCI_ERR_UNCOR_STATUS: ++ case PCI_ERR_COR_STATUS: ++ case PCI_ERR_ROOT_STATUS: ++ new &= mask; ++ fallthrough; ++ ++ case PCI_ERR_UNCOR_MASK: ++ case PCI_ERR_UNCOR_SEVER: ++ case PCI_ERR_COR_MASK: ++ case PCI_ERR_CAP: ++ case PCI_ERR_HEADER_LOG + 0: ++ case PCI_ERR_HEADER_LOG + 4: ++ case PCI_ERR_HEADER_LOG + 8: ++ case PCI_ERR_HEADER_LOG + 12: ++ case PCI_ERR_ROOT_COMMAND: ++ case PCI_ERR_ROOT_ERR_SRC: ++ advk_writel(pcie, new, PCIE_CORE_PCIERR_CAP + reg); ++ break; ++ ++ default: ++ break; ++ } ++} ++ + static const struct pci_bridge_emul_ops advk_pci_bridge_emul_ops = { + .read_base = advk_pci_bridge_emul_base_conf_read, + .write_base = advk_pci_bridge_emul_base_conf_write, + .read_pcie = advk_pci_bridge_emul_pcie_conf_read, + .write_pcie = advk_pci_bridge_emul_pcie_conf_write, ++ .read_ext = advk_pci_bridge_emul_ext_conf_read, ++ .write_ext = advk_pci_bridge_emul_ext_conf_write, + }; + + /* +-- +2.34.1 + diff --git a/pkgs/patches-linux-5.15/0095-PCI-aardvark-Dispose-bridge-irq-prior-to-removing-br.patch b/pkgs/patches-linux-5.15/0095-PCI-aardvark-Dispose-bridge-irq-prior-to-removing-br.patch new file mode 100644 index 0000000..6cd3904 --- /dev/null +++ b/pkgs/patches-linux-5.15/0095-PCI-aardvark-Dispose-bridge-irq-prior-to-removing-br.patch @@ -0,0 +1,41 @@ +From 2749ad1d3c854bf6926d2582cc2a7f727d0d53b3 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org> +Date: Wed, 24 Aug 2022 15:59:49 +0200 +Subject: [PATCH 5/9] PCI: aardvark: Dispose bridge irq prior to removing + bridge domain +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Documentation for irq_domain_remove() says that all mapping within the +domain must be disposed prior to domain remove. + +Currently bridge irq is not disposed in pci-aardvark.c device unbind callback +which cause that kernel crashes after unloading driver and trying to read +/sys/kernel/debug/irq/irqs/<num> or /proc/interrupts. + +Fixes: 815bc3136867 ("PCI: aardvark: Use separate INTA interrupt for emulated root bridge") +Signed-off-by: Pali Rohár <pali@kernel.org> +--- + drivers/pci/controller/pci-aardvark.c | 5 +++++ + 1 file changed, 5 insertions(+) + +diff --git a/drivers/pci/controller/pci-aardvark.c b/drivers/pci/controller/pci-aardvark.c +index f2be2ba51217..db97dc0a8ad7 100644 +--- a/drivers/pci/controller/pci-aardvark.c ++++ b/drivers/pci/controller/pci-aardvark.c +@@ -1579,6 +1579,11 @@ static int advk_pcie_init_rp_irq_domain(struct advk_pcie *pcie) + + static void advk_pcie_remove_rp_irq_domain(struct advk_pcie *pcie) + { ++ int virq; ++ ++ virq = irq_find_mapping(pcie->rp_irq_domain, 0); ++ if (virq > 0) ++ irq_dispose_mapping(virq); + irq_domain_remove(pcie->rp_irq_domain); + } + +-- +2.34.1 + diff --git a/pkgs/patches-linux-5.15/0096-PCI-aardvark-Add-support-for-DLLSC-and-hotplug-inter.patch b/pkgs/patches-linux-5.15/0096-PCI-aardvark-Add-support-for-DLLSC-and-hotplug-inter.patch new file mode 100644 index 0000000..7d1b9b0 --- /dev/null +++ b/pkgs/patches-linux-5.15/0096-PCI-aardvark-Add-support-for-DLLSC-and-hotplug-inter.patch @@ -0,0 +1,268 @@ +From f36491f0f18ae6b9018ade51cbfea4fbd14ae2a6 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org> +Date: Wed, 31 Aug 2022 15:55:46 +0200 +Subject: [PATCH 6/9] PCI: aardvark: Add support for DLLSC and hotplug + interrupt +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Add support for Data Link Layer State Change in the emulated slot +registers and hotplug interrupt via the emulated root bridge. + +This is mainly useful for when an error causes link down event. With +this change, drivers can try recovery. + +Link down state change can be implemented because Aardvark supports Link +Down event interrupt. Use it for signaling that Data Link Layer Link is +not active anymore via Hot-Plug Interrupt on emulated root bridge. + +Link up interrupt is not available on Aardvark, but we check for whether +link is up in the advk_pcie_link_up() function. By triggering Hot-Plug +Interrupt from this function we achieve Link up event, so long as the +function is called (which it is after probe and when rescanning). +Although it is not ideal, it is better than nothing. + +Since advk_pcie_link_up() is not called from interrupt handler, we +cannot call generic_handle_domain_irq() from it directly. Instead create +a TIMER_IRQSAFE timer and trigger it from advk_pcie_link_up(). + +(We haven't been able to find any documentation for a Link Up interrupt + on Aardvark, but it is possible there is one, in some undocumented + register. If we manage to find this information, this can be + rewritten.) + +Signed-off-by: Pali Rohár <pali@kernel.org> +Signed-off-by: Marek Behún <kabel@kernel.org> +--- + drivers/pci/controller/Kconfig | 3 + + drivers/pci/controller/pci-aardvark.c | 101 ++++++++++++++++++++++++-- + 2 files changed, 99 insertions(+), 5 deletions(-) + +diff --git a/drivers/pci/controller/Kconfig b/drivers/pci/controller/Kconfig +index c217ea3bc703..1a3e123570ed 100644 +--- a/drivers/pci/controller/Kconfig ++++ b/drivers/pci/controller/Kconfig +@@ -24,6 +24,9 @@ config PCI_AARDVARK + depends on OF + depends on PCI_MSI_IRQ_DOMAIN + select PCI_BRIDGE_EMUL ++ select PCIEPORTBUS ++ select HOTPLUG_PCI ++ select HOTPLUG_PCI_PCIE + help + Add support for Aardvark 64bit PCIe Host Controller. This + controller is part of the South Bridge of the Marvel Armada +diff --git a/drivers/pci/controller/pci-aardvark.c b/drivers/pci/controller/pci-aardvark.c +index db97dc0a8ad7..8aa05777da88 100644 +--- a/drivers/pci/controller/pci-aardvark.c ++++ b/drivers/pci/controller/pci-aardvark.c +@@ -25,6 +25,7 @@ + #include <linux/of_address.h> + #include <linux/of_gpio.h> + #include <linux/of_pci.h> ++#include <linux/timer.h> + + #include "../pci.h" + #include "../pci-bridge-emul.h" +@@ -101,6 +102,7 @@ + #define PCIE_MSG_PM_PME_MASK BIT(7) + #define PCIE_ISR0_MASK_REG (CONTROL_BASE_ADDR + 0x44) + #define PCIE_ISR0_MSI_INT_PENDING BIT(24) ++#define PCIE_ISR0_LINK_DOWN BIT(1) + #define PCIE_ISR0_CORR_ERR BIT(11) + #define PCIE_ISR0_NFAT_ERR BIT(12) + #define PCIE_ISR0_FAT_ERR BIT(13) +@@ -285,6 +287,8 @@ struct advk_pcie { + DECLARE_BITMAP(msi_used, MSI_IRQ_NUM); + struct mutex msi_used_lock; + int link_gen; ++ bool link_was_up; ++ struct timer_list link_irq_timer; + struct pci_bridge_emul bridge; + struct gpio_desc *reset_gpio; + struct phy *phy; +@@ -314,7 +318,24 @@ static inline bool advk_pcie_link_up(struct advk_pcie *pcie) + { + /* check if LTSSM is in normal operation - some L* state */ + u8 ltssm_state = advk_pcie_ltssm_state(pcie); +- return ltssm_state >= LTSSM_L0 && ltssm_state < LTSSM_DISABLED; ++ bool link_is_up; ++ u16 slotsta; ++ ++ link_is_up = ltssm_state >= LTSSM_L0 && ltssm_state < LTSSM_DISABLED; ++ ++ if (link_is_up && !pcie->link_was_up) { ++ dev_info(&pcie->pdev->dev, "link up\n"); ++ ++ pcie->link_was_up = true; ++ ++ slotsta = le16_to_cpu(pcie->bridge.pcie_conf.slotsta); ++ slotsta |= PCI_EXP_SLTSTA_DLLSC; ++ pcie->bridge.pcie_conf.slotsta = cpu_to_le16(slotsta); ++ ++ mod_timer(&pcie->link_irq_timer, jiffies + 1); ++ } ++ ++ return link_is_up; + } + + static inline bool advk_pcie_link_active(struct advk_pcie *pcie) +@@ -443,8 +464,6 @@ static void advk_pcie_train_link(struct advk_pcie *pcie) + ret = advk_pcie_wait_for_link(pcie); + if (ret < 0) + dev_err(dev, "link never came up\n"); +- else +- dev_info(dev, "link up\n"); + } + + /* +@@ -593,6 +612,11 @@ static void advk_pcie_setup_hw(struct advk_pcie *pcie) + reg &= ~PCIE_ISR0_MSI_INT_PENDING; + advk_writel(pcie, reg, PCIE_ISR0_MASK_REG); + ++ /* Unmask Link Down interrupt */ ++ reg = advk_readl(pcie, PCIE_ISR0_MASK_REG); ++ reg &= ~PCIE_ISR0_LINK_DOWN; ++ advk_writel(pcie, reg, PCIE_ISR0_MASK_REG); ++ + /* Unmask PME interrupt for processing of PME requester */ + reg = advk_readl(pcie, PCIE_ISR0_MASK_REG); + reg &= ~PCIE_MSG_PM_PME_MASK; +@@ -919,6 +943,14 @@ advk_pci_bridge_emul_pcie_conf_write(struct pci_bridge_emul *bridge, + advk_pcie_wait_for_retrain(pcie); + break; + ++ case PCI_EXP_SLTCTL: { ++ u16 slotctl = le16_to_cpu(bridge->pcie_conf.slotctl); ++ /* Only emulation of HPIE and DLLSCE bits is provided */ ++ slotctl &= PCI_EXP_SLTCTL_HPIE | PCI_EXP_SLTCTL_DLLSCE; ++ bridge->pcie_conf.slotctl = cpu_to_le16(slotctl); ++ break; ++ } ++ + case PCI_EXP_RTCTL: { + u16 rootctl = le16_to_cpu(bridge->pcie_conf.rootctl); + /* Only emulation of PMEIE and CRSSVE bits is provided */ +@@ -1036,6 +1068,7 @@ static const struct pci_bridge_emul_ops advk_pci_bridge_emul_ops = { + static int advk_sw_pci_bridge_init(struct advk_pcie *pcie) + { + struct pci_bridge_emul *bridge = &pcie->bridge; ++ u32 slotcap; + + bridge->conf.vendor = + cpu_to_le16(advk_readl(pcie, PCIE_CORE_DEV_ID_REG) & 0xffff); +@@ -1062,6 +1095,13 @@ static int advk_sw_pci_bridge_init(struct advk_pcie *pcie) + bridge->pcie_conf.cap = cpu_to_le16(2 | PCI_EXP_FLAGS_SLOT); + + /* ++ * Mark bridge as Hot Plug Capable since this is the way how to enable ++ * delivering of Data Link Layer State Change interrupts. ++ * ++ * Set No Command Completed Support because bridge does not support ++ * Command Completed Interrupt. Every command is executed immediately ++ * without any delay. ++ * + * Set Presence Detect State bit permanently since there is no support + * for unplugging the card nor detecting whether it is plugged. (If a + * platform exists in the future that supports it, via a GPIO for +@@ -1071,8 +1111,9 @@ static int advk_sw_pci_bridge_init(struct advk_pcie *pcie) + * value is reserved for ports within the same silicon as Root Port + * which is not our case. + */ +- bridge->pcie_conf.slotcap = cpu_to_le32(FIELD_PREP(PCI_EXP_SLTCAP_PSN, +- 1)); ++ slotcap = PCI_EXP_SLTCAP_NCCS | PCI_EXP_SLTCAP_HPC | ++ FIELD_PREP(PCI_EXP_SLTCAP_PSN, 1); ++ bridge->pcie_conf.slotcap = cpu_to_le32(slotcap); + bridge->pcie_conf.slotsta = cpu_to_le16(PCI_EXP_SLTSTA_PDS); + + /* Indicates supports for Completion Retry Status */ +@@ -1587,6 +1628,24 @@ static void advk_pcie_remove_rp_irq_domain(struct advk_pcie *pcie) + irq_domain_remove(pcie->rp_irq_domain); + } + ++static void advk_pcie_link_irq_handler(struct timer_list *timer) ++{ ++ struct advk_pcie *pcie = from_timer(pcie, timer, link_irq_timer); ++ u16 slotctl; ++ ++ slotctl = le16_to_cpu(pcie->bridge.pcie_conf.slotctl); ++ if (!(slotctl & PCI_EXP_SLTCTL_DLLSCE) || ++ !(slotctl & PCI_EXP_SLTCTL_HPIE)) ++ return; ++ ++ /* ++ * Aardvark HW returns zero for PCI_EXP_FLAGS_IRQ, so use PCIe ++ * interrupt 0 ++ */ ++ if (generic_handle_domain_irq(pcie->rp_irq_domain, 0) == -EINVAL) ++ dev_err_ratelimited(&pcie->pdev->dev, "unhandled HP IRQ\n"); ++} ++ + static void advk_pcie_handle_pme(struct advk_pcie *pcie) + { + u32 requester = advk_readl(pcie, PCIE_MSG_LOG_REG) >> 16; +@@ -1638,6 +1697,7 @@ static void advk_pcie_handle_int(struct advk_pcie *pcie) + { + u32 isr0_val, isr0_mask, isr0_status; + u32 isr1_val, isr1_mask, isr1_status; ++ u16 slotsta; + int i; + + isr0_val = advk_readl(pcie, PCIE_ISR0_REG); +@@ -1664,6 +1724,26 @@ static void advk_pcie_handle_int(struct advk_pcie *pcie) + dev_err_ratelimited(&pcie->pdev->dev, "unhandled ERR IRQ\n"); + } + ++ /* Process Link Down interrupt as HP IRQ */ ++ if (isr0_status & PCIE_ISR0_LINK_DOWN) { ++ advk_writel(pcie, PCIE_ISR0_LINK_DOWN, PCIE_ISR0_REG); ++ ++ dev_info(&pcie->pdev->dev, "link down\n"); ++ ++ pcie->link_was_up = false; ++ ++ slotsta = le16_to_cpu(pcie->bridge.pcie_conf.slotsta); ++ slotsta |= PCI_EXP_SLTSTA_DLLSC; ++ pcie->bridge.pcie_conf.slotsta = cpu_to_le16(slotsta); ++ ++ /* ++ * Deactivate timer and call advk_pcie_link_irq_handler() ++ * function directly as we are in the interrupt context. ++ */ ++ del_timer_sync(&pcie->link_irq_timer); ++ advk_pcie_link_irq_handler(&pcie->link_irq_timer); ++ } ++ + /* Process MSI interrupts */ + if (isr0_status & PCIE_ISR0_MSI_INT_PENDING) + advk_pcie_handle_msi(pcie); +@@ -1902,6 +1982,14 @@ static int advk_pcie_probe(struct platform_device *pdev) + if (ret) + return ret; + ++ /* ++ * generic_handle_domain_irq() expects local IRQs to be disabled since ++ * normally it is called from interrupt context, so use TIMER_IRQSAFE ++ * flag for this link_irq_timer. ++ */ ++ timer_setup(&pcie->link_irq_timer, advk_pcie_link_irq_handler, ++ TIMER_IRQSAFE); ++ + advk_pcie_setup_hw(pcie); + + ret = advk_sw_pci_bridge_init(pcie); +@@ -1990,6 +2078,9 @@ static int advk_pcie_remove(struct platform_device *pdev) + advk_pcie_remove_msi_irq_domain(pcie); + advk_pcie_remove_irq_domain(pcie); + ++ /* Deactivate link event timer */ ++ del_timer_sync(&pcie->link_irq_timer); ++ + /* Free config space for emulated root bridge */ + pci_bridge_emul_cleanup(&pcie->bridge); + +-- +2.34.1 + diff --git a/pkgs/patches-linux-5.15/0097-PCI-aardvark-Send-Set_Slot_Power_Limit-message.patch b/pkgs/patches-linux-5.15/0097-PCI-aardvark-Send-Set_Slot_Power_Limit-message.patch new file mode 100644 index 0000000..fc39f95 --- /dev/null +++ b/pkgs/patches-linux-5.15/0097-PCI-aardvark-Send-Set_Slot_Power_Limit-message.patch @@ -0,0 +1,148 @@ +From c60b0ae4a0a225d24993a7d8e8396209a745a7a0 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org> +Date: Wed, 31 Aug 2022 15:57:01 +0200 +Subject: [PATCH 7/9] PCI: aardvark: Send Set_Slot_Power_Limit message +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Emulate Slot PowerLimit Scale and Value bits in the Slot Capabilities +register of the emulated bridge and if slot power limit value is +defined, send that Set_Slot_Power_Limit message via Message Generation +Control Register in Link Up handler on link up event. + +Slot power limit value is read from device-tree property +'slot-power-limit-milliwatt'. If this property is not specified, we +treat it as "Slot Capabilities register has not yet been initialized". + +According to PCIe Base specification 3.0, when transitioning from a +non-DL_Up Status to a DL_Up Status, the Port must initiate the +transmission of a Set_Slot_Power_Limit Message to the other component +on the Link to convey the value programmed in the Slot Power Limit +Scale and Value fields of the Slot Capabilities register. This +transmission is optional if the Slot Capabilities register has not +yet been initialized. + +Signed-off-by: Pali Rohár <pali@kernel.org> +Signed-off-by: Marek Behún <kabel@kernel.org> +--- + drivers/pci/controller/pci-aardvark.c | 51 ++++++++++++++++++++++++--- + 1 file changed, 47 insertions(+), 4 deletions(-) + +diff --git a/drivers/pci/controller/pci-aardvark.c b/drivers/pci/controller/pci-aardvark.c +index 8aa05777da88..cf493d62b889 100644 +--- a/drivers/pci/controller/pci-aardvark.c ++++ b/drivers/pci/controller/pci-aardvark.c +@@ -214,6 +214,11 @@ enum { + }; + + #define VENDOR_ID_REG (LMI_BASE_ADDR + 0x44) ++#define PME_MSG_GEN_CTRL (LMI_BASE_ADDR + 0x220) ++#define SEND_SET_SLOT_POWER_LIMIT BIT(13) ++#define SEND_PME_TURN_OFF BIT(14) ++#define SLOT_POWER_LIMIT_DATA_SHIFT 16 ++#define SLOT_POWER_LIMIT_DATA_MASK GENMASK(25, 16) + + /* PCIe core controller registers */ + #define CTRL_CORE_BASE_ADDR 0x18000 +@@ -286,6 +291,8 @@ struct advk_pcie { + raw_spinlock_t msi_irq_lock; + DECLARE_BITMAP(msi_used, MSI_IRQ_NUM); + struct mutex msi_used_lock; ++ u8 slot_power_limit_value; ++ u8 slot_power_limit_scale; + int link_gen; + bool link_was_up; + struct timer_list link_irq_timer; +@@ -318,8 +325,9 @@ static inline bool advk_pcie_link_up(struct advk_pcie *pcie) + { + /* check if LTSSM is in normal operation - some L* state */ + u8 ltssm_state = advk_pcie_ltssm_state(pcie); ++ u16 slotsta, slotctl; ++ u32 slotpwr, val; + bool link_is_up; +- u16 slotsta; + + link_is_up = ltssm_state >= LTSSM_L0 && ltssm_state < LTSSM_DISABLED; + +@@ -333,6 +341,27 @@ static inline bool advk_pcie_link_up(struct advk_pcie *pcie) + pcie->bridge.pcie_conf.slotsta = cpu_to_le16(slotsta); + + mod_timer(&pcie->link_irq_timer, jiffies + 1); ++ ++ /* ++ * According to PCIe Base specification 3.0, when transitioning ++ * from a non-DL_Up Status to a DL_Up Status, the Port must ++ * initiate the transmission of a Set_Slot_Power_Limit Message ++ * to the other component on the Link to convey the value ++ * programmed in the Slot Power Limit Scale and Value fields of ++ * the Slot Capabilities register. This transmission is optional ++ * if the Slot Capabilities register has not yet been ++ * initialized. ++ */ ++ slotctl = le16_to_cpu(pcie->bridge.pcie_conf.slotctl); ++ slotpwr = FIELD_GET(PCI_EXP_SLTCAP_SPLV | PCI_EXP_SLTCAP_SPLS, ++ le32_to_cpu(pcie->bridge.pcie_conf.slotcap)); ++ if (!(slotctl & PCI_EXP_SLTCTL_ASPL_DISABLE) && slotpwr) { ++ val = advk_readl(pcie, PME_MSG_GEN_CTRL); ++ val &= ~SLOT_POWER_LIMIT_DATA_MASK; ++ val |= slotpwr << SLOT_POWER_LIMIT_DATA_SHIFT; ++ val |= SEND_SET_SLOT_POWER_LIMIT; ++ advk_writel(pcie, val, PME_MSG_GEN_CTRL); ++ } + } + + return link_is_up; +@@ -945,8 +974,9 @@ advk_pci_bridge_emul_pcie_conf_write(struct pci_bridge_emul *bridge, + + case PCI_EXP_SLTCTL: { + u16 slotctl = le16_to_cpu(bridge->pcie_conf.slotctl); +- /* Only emulation of HPIE and DLLSCE bits is provided */ +- slotctl &= PCI_EXP_SLTCTL_HPIE | PCI_EXP_SLTCTL_DLLSCE; ++ /* Only emulation of HPIE, DLLSCE and ASPLD bits is provided */ ++ slotctl &= PCI_EXP_SLTCTL_HPIE | PCI_EXP_SLTCTL_DLLSCE | ++ PCI_EXP_SLTCTL_ASPL_DISABLE; + bridge->pcie_conf.slotctl = cpu_to_le16(slotctl); + break; + } +@@ -1110,9 +1140,13 @@ static int advk_sw_pci_bridge_init(struct advk_pcie *pcie) + * Set physical slot number to 1 since there is only one port and zero + * value is reserved for ports within the same silicon as Root Port + * which is not our case. ++ * ++ * Set slot power limit. + */ + slotcap = PCI_EXP_SLTCAP_NCCS | PCI_EXP_SLTCAP_HPC | +- FIELD_PREP(PCI_EXP_SLTCAP_PSN, 1); ++ FIELD_PREP(PCI_EXP_SLTCAP_PSN, 1) | ++ FIELD_PREP(PCI_EXP_SLTCAP_SPLV, pcie->slot_power_limit_value) | ++ FIELD_PREP(PCI_EXP_SLTCAP_SPLS, pcie->slot_power_limit_scale); + bridge->pcie_conf.slotcap = cpu_to_le32(slotcap); + bridge->pcie_conf.slotsta = cpu_to_le16(PCI_EXP_SLTSTA_PDS); + +@@ -1858,6 +1892,7 @@ static int advk_pcie_probe(struct platform_device *pdev) + struct advk_pcie *pcie; + struct pci_host_bridge *bridge; + struct resource_entry *entry; ++ u32 slot_power_limit; + int ret, irq; + + bridge = devm_pci_alloc_host_bridge(dev, sizeof(struct advk_pcie)); +@@ -1978,6 +2013,14 @@ static int advk_pcie_probe(struct platform_device *pdev) + else + pcie->link_gen = ret; + ++ slot_power_limit = of_pci_get_slot_power_limit(dev->of_node, ++ &pcie->slot_power_limit_value, ++ &pcie->slot_power_limit_scale); ++ if (slot_power_limit) ++ dev_info(dev, "Slot Power Limit: %u.%uW\n", ++ slot_power_limit / 1000, ++ (slot_power_limit / 100) % 10); ++ + ret = advk_pcie_setup_phy(pcie); + if (ret) + return ret; +-- +2.34.1 + diff --git a/pkgs/patches-linux-5.15/0098-PCI-aardvark-Add-clock-support.patch b/pkgs/patches-linux-5.15/0098-PCI-aardvark-Add-clock-support.patch new file mode 100644 index 0000000..48d129d --- /dev/null +++ b/pkgs/patches-linux-5.15/0098-PCI-aardvark-Add-clock-support.patch @@ -0,0 +1,93 @@ +From 336a4404b02b564930725715659ca583b8a58a6b Mon Sep 17 00:00:00 2001 +From: Miquel Raynal <miquel.raynal@bootlin.com> +Date: Wed, 31 Aug 2022 15:59:39 +0200 +Subject: [PATCH 8/9] PCI: aardvark: Add clock support +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +The IP relies on a gated clock. When we will add S2RAM support, this +clock will need to be resumed before any PCIe registers are +accessed. Add support for this clock. + +Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com> +Signed-off-by: Pali Rohár <pali@kernel.org> +Signed-off-by: Marek Behún <kabel@kernel.org> +--- + drivers/pci/controller/pci-aardvark.c | 32 +++++++++++++++++++++++++++ + 1 file changed, 32 insertions(+) + +diff --git a/drivers/pci/controller/pci-aardvark.c b/drivers/pci/controller/pci-aardvark.c +index cf493d62b889..17aed65aab91 100644 +--- a/drivers/pci/controller/pci-aardvark.c ++++ b/drivers/pci/controller/pci-aardvark.c +@@ -9,6 +9,7 @@ + */ + + #include <linux/bitfield.h> ++#include <linux/clk.h> + #include <linux/delay.h> + #include <linux/gpio/consumer.h> + #include <linux/interrupt.h> +@@ -298,6 +299,7 @@ struct advk_pcie { + struct timer_list link_irq_timer; + struct pci_bridge_emul bridge; + struct gpio_desc *reset_gpio; ++ struct clk *clk; + struct phy *phy; + }; + +@@ -1828,6 +1830,29 @@ static int advk_pcie_map_irq(const struct pci_dev *dev, u8 slot, u8 pin) + return of_irq_parse_and_map_pci(dev, slot, pin); + } + ++static int advk_pcie_setup_clk(struct advk_pcie *pcie) ++{ ++ struct device *dev = &pcie->pdev->dev; ++ int ret; ++ ++ pcie->clk = devm_clk_get(dev, NULL); ++ if (IS_ERR(pcie->clk) && (PTR_ERR(pcie->clk) == -EPROBE_DEFER)) ++ return PTR_ERR(pcie->clk); ++ ++ /* Old bindings miss the clock handle */ ++ if (IS_ERR(pcie->clk)) { ++ dev_warn(dev, "Clock unavailable (%ld)\n", PTR_ERR(pcie->clk)); ++ pcie->clk = NULL; ++ return 0; ++ } ++ ++ ret = clk_prepare_enable(pcie->clk); ++ if (ret) ++ dev_err(dev, "Clock initialization failed (%d)\n", ret); ++ ++ return ret; ++} ++ + static void advk_pcie_disable_phy(struct advk_pcie *pcie) + { + phy_power_off(pcie->phy); +@@ -2021,6 +2046,10 @@ static int advk_pcie_probe(struct platform_device *pdev) + slot_power_limit / 1000, + (slot_power_limit / 100) % 10); + ++ ret = advk_pcie_setup_clk(pcie); ++ if (ret) ++ return ret; ++ + ret = advk_pcie_setup_phy(pcie); + if (ret) + return ret; +@@ -2143,6 +2172,9 @@ static int advk_pcie_remove(struct platform_device *pdev) + /* Disable phy */ + advk_pcie_disable_phy(pcie); + ++ /* Disable clock */ ++ clk_disable_unprepare(pcie->clk); ++ + return 0; + } + +-- +2.34.1 + diff --git a/pkgs/patches-linux-5.15/0099-PCI-aardvark-Add-suspend-to-RAM-support.patch b/pkgs/patches-linux-5.15/0099-PCI-aardvark-Add-suspend-to-RAM-support.patch new file mode 100644 index 0000000..c51dd8b --- /dev/null +++ b/pkgs/patches-linux-5.15/0099-PCI-aardvark-Add-suspend-to-RAM-support.patch @@ -0,0 +1,74 @@ +From 4cfecad11f7a6cebb77a37b7bbd42d7f910cce80 Mon Sep 17 00:00:00 2001 +From: Miquel Raynal <miquel.raynal@bootlin.com> +Date: Wed, 31 Aug 2022 16:07:27 +0200 +Subject: [PATCH 9/9] PCI: aardvark: Add suspend to RAM support +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Add suspend and resume callbacks. We need to use the NOIRQ variants to +ensure the controller's IRQ handlers are not run during suspend() / +resume(), which could cause races. + +Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com> +Signed-off-by: Pali Rohár <pali@kernel.org> +Signed-off-by: Marek Behún <kabel@kernel.org> +--- + drivers/pci/controller/pci-aardvark.c | 34 +++++++++++++++++++++++++++ + 1 file changed, 34 insertions(+) + +diff --git a/drivers/pci/controller/pci-aardvark.c b/drivers/pci/controller/pci-aardvark.c +index 17aed65aab91..f3984bdf1d96 100644 +--- a/drivers/pci/controller/pci-aardvark.c ++++ b/drivers/pci/controller/pci-aardvark.c +@@ -1911,6 +1911,39 @@ static int advk_pcie_setup_phy(struct advk_pcie *pcie) + return ret; + } + ++static int advk_pcie_suspend(struct device *dev) ++{ ++ struct advk_pcie *pcie = dev_get_drvdata(dev); ++ ++ advk_pcie_disable_phy(pcie); ++ ++ clk_disable_unprepare(pcie->clk); ++ ++ return 0; ++} ++ ++static int advk_pcie_resume(struct device *dev) ++{ ++ struct advk_pcie *pcie = dev_get_drvdata(dev); ++ int ret; ++ ++ ret = clk_prepare_enable(pcie->clk); ++ if (ret) ++ return ret; ++ ++ ret = advk_pcie_enable_phy(pcie); ++ if (ret) ++ return ret; ++ ++ advk_pcie_setup_hw(pcie); ++ ++ return 0; ++} ++ ++static const struct dev_pm_ops advk_pcie_dev_pm_ops = { ++ SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(advk_pcie_suspend, advk_pcie_resume) ++}; ++ + static int advk_pcie_probe(struct platform_device *pdev) + { + struct device *dev = &pdev->dev; +@@ -2188,6 +2221,7 @@ static struct platform_driver advk_pcie_driver = { + .driver = { + .name = "advk-pcie", + .of_match_table = advk_pcie_of_match_table, ++ .pm = &advk_pcie_dev_pm_ops, + }, + .probe = advk_pcie_probe, + .remove = advk_pcie_remove, +-- +2.34.1 + diff --git a/pkgs/patches-linux-5.15/0100-PCI-aardvark-Replace-custom-PCIE_CORE_ERR_CAPCTL_-ma.patch b/pkgs/patches-linux-5.15/0100-PCI-aardvark-Replace-custom-PCIE_CORE_ERR_CAPCTL_-ma.patch new file mode 100644 index 0000000..f161bff --- /dev/null +++ b/pkgs/patches-linux-5.15/0100-PCI-aardvark-Replace-custom-PCIE_CORE_ERR_CAPCTL_-ma.patch @@ -0,0 +1,51 @@ +From f589f5a4a08608cf0fc5184b82e1404250632530 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org> +Date: Thu, 1 Sep 2022 11:32:28 +0200 +Subject: [PATCH 1/2] PCI: aardvark: Replace custom PCIE_CORE_ERR_CAPCTL_* + macros by linux/pci_regs.h macros +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Kernel already has these macros defined under different names. + +Signed-off-by: Pali Rohár <pali@kernel.org> +Signed-off-by: Marek Behún <kabel@kernel.org> +--- + drivers/pci/controller/pci-aardvark.c | 13 +++---------- + 1 file changed, 3 insertions(+), 10 deletions(-) + +diff --git a/drivers/pci/controller/pci-aardvark.c b/drivers/pci/controller/pci-aardvark.c +index defaf74935a3..a5b1ebfb9520 100644 +--- a/drivers/pci/controller/pci-aardvark.c ++++ b/drivers/pci/controller/pci-aardvark.c +@@ -38,11 +38,6 @@ + #define PCIE_CORE_SSDEV_ID_REG 0x2c + #define PCIE_CORE_PCIEXP_CAP 0xc0 + #define PCIE_CORE_PCIERR_CAP 0x100 +-#define PCIE_CORE_ERR_CAPCTL_REG 0x118 +-#define PCIE_CORE_ERR_CAPCTL_ECRC_CHK_TX BIT(5) +-#define PCIE_CORE_ERR_CAPCTL_ECRC_CHK_TX_EN BIT(6) +-#define PCIE_CORE_ERR_CAPCTL_ECRC_CHCK BIT(7) +-#define PCIE_CORE_ERR_CAPCTL_ECRC_CHCK_RCV BIT(8) + /* PIO registers base address and register offsets */ + #define PIO_BASE_ADDR 0x4000 + #define PIO_CTRL (PIO_BASE_ADDR + 0x0) +@@ -592,11 +587,9 @@ static void advk_pcie_setup_hw(struct advk_pcie *pcie) + advk_writel(pcie, reg, PCIE_CORE_CMD_STATUS_REG); + + /* Set Advanced Error Capabilities and Control PF0 register */ +- reg = PCIE_CORE_ERR_CAPCTL_ECRC_CHK_TX | +- PCIE_CORE_ERR_CAPCTL_ECRC_CHK_TX_EN | +- PCIE_CORE_ERR_CAPCTL_ECRC_CHCK | +- PCIE_CORE_ERR_CAPCTL_ECRC_CHCK_RCV; +- advk_writel(pcie, reg, PCIE_CORE_ERR_CAPCTL_REG); ++ reg = PCI_ERR_CAP_ECRC_GENC | PCI_ERR_CAP_ECRC_GENE | ++ PCI_ERR_CAP_ECRC_CHKC | PCI_ERR_CAP_ECRC_CHKE; ++ advk_writel(pcie, reg, PCIE_CORE_PCIERR_CAP + PCI_ERR_CAP); + + /* Set PCIe Device Control register */ + reg = advk_readl(pcie, PCIE_CORE_PCIEXP_CAP + PCI_EXP_DEVCTL); +-- +2.34.1 + diff --git a/pkgs/patches-linux-5.15/0101-PCI-aardvark-Don-t-write-read-only-bits-explicitly-i.patch b/pkgs/patches-linux-5.15/0101-PCI-aardvark-Don-t-write-read-only-bits-explicitly-i.patch new file mode 100644 index 0000000..edce284 --- /dev/null +++ b/pkgs/patches-linux-5.15/0101-PCI-aardvark-Don-t-write-read-only-bits-explicitly-i.patch @@ -0,0 +1,39 @@ +From 055c623c268ee0031d05c1346f9f4c24fe3f0846 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Marek=20Beh=C3=BAn?= <kabel@kernel.org> +Date: Thu, 18 Aug 2022 15:51:38 +0200 +Subject: [PATCH 2/2] PCI: aardvark: Don't write read-only bits explicitly in + PCI_ERR_CAP register +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +The bits PCI_ERR_CAP_ECRC_GENC and PCI_ERR_CAP_ECRC_CHKC are read only, +reporting the capability of ECRC. Don't write them explicitly, instead +read the register (where they are set), and add the bits that enable +these features. + +Signed-off-by: Marek Behún <kabel@kernel.org> +--- + drivers/pci/controller/pci-aardvark.c | 6 +++--- + 1 file changed, 3 insertions(+), 3 deletions(-) + +diff --git a/drivers/pci/controller/pci-aardvark.c b/drivers/pci/controller/pci-aardvark.c +index a5b1ebfb9520..325c22092e16 100644 +--- a/drivers/pci/controller/pci-aardvark.c ++++ b/drivers/pci/controller/pci-aardvark.c +@@ -586,9 +586,9 @@ static void advk_pcie_setup_hw(struct advk_pcie *pcie) + reg &= ~(PCI_COMMAND_IO | PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER); + advk_writel(pcie, reg, PCIE_CORE_CMD_STATUS_REG); + +- /* Set Advanced Error Capabilities and Control PF0 register */ +- reg = PCI_ERR_CAP_ECRC_GENC | PCI_ERR_CAP_ECRC_GENE | +- PCI_ERR_CAP_ECRC_CHKC | PCI_ERR_CAP_ECRC_CHKE; ++ /* Enable generation and checking of ECRC on Root Bridge */ ++ reg = advk_readl(pcie, PCIE_CORE_PCIERR_CAP + PCI_ERR_CAP); ++ reg |= PCI_ERR_CAP_ECRC_GENE | PCI_ERR_CAP_ECRC_CHKE; + advk_writel(pcie, reg, PCIE_CORE_PCIERR_CAP + PCI_ERR_CAP); + + /* Set PCIe Device Control register */ +-- +2.34.1 + diff --git a/pkgs/patches-linux-5.15/011-kbuild-export-SUBARCH.patch b/pkgs/patches-linux-5.15/011-kbuild-export-SUBARCH.patch new file mode 100644 index 0000000..0aedad4 --- /dev/null +++ b/pkgs/patches-linux-5.15/011-kbuild-export-SUBARCH.patch @@ -0,0 +1,21 @@ +From 173019b66dcc9d68ad9333aa744dad1e369b5aa8 Mon Sep 17 00:00:00 2001 +From: Felix Fietkau <nbd@nbd.name> +Date: Sun, 9 Jul 2017 00:26:53 +0200 +Subject: [PATCH 34/34] kernel: add compile fix for linux 4.9 on x86 + +Signed-off-by: Felix Fietkau <nbd@nbd.name> +--- + Makefile | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +--- a/Makefile ++++ b/Makefile +@@ -523,7 +523,7 @@ KBUILD_LDFLAGS_MODULE := + KBUILD_LDFLAGS := + CLANG_FLAGS := + +-export ARCH SRCARCH CONFIG_SHELL BASH HOSTCC KBUILD_HOSTCFLAGS CROSS_COMPILE LD CC ++export ARCH SRCARCH SUBARCH CONFIG_SHELL BASH HOSTCC KBUILD_HOSTCFLAGS CROSS_COMPILE LD CC + export CPP AR NM STRIP OBJCOPY OBJDUMP READELF PAHOLE RESOLVE_BTFIDS LEX YACC AWK INSTALLKERNEL + export PERL PYTHON3 CHECK CHECKFLAGS MAKE UTS_MACHINE HOSTCXX + export KGZIP KBZIP2 KLZOP LZMA LZ4 XZ ZSTD diff --git a/pkgs/patches-linux-5.15/050-dtc-checks-Drop-interrupt-provider-address-cells-check.patch b/pkgs/patches-linux-5.15/050-dtc-checks-Drop-interrupt-provider-address-cells-check.patch new file mode 100644 index 0000000..75f6372 --- /dev/null +++ b/pkgs/patches-linux-5.15/050-dtc-checks-Drop-interrupt-provider-address-cells-check.patch @@ -0,0 +1,28 @@ +From d8d1a9a77863a8c7031ae82a1d461aa78eb72a7b Mon Sep 17 00:00:00 2001 +From: Rob Herring <robh@kernel.org> +Date: Mon, 11 Oct 2021 14:12:43 -0500 +Subject: [PATCH] checks: Drop interrupt provider '#address-cells' check + +'#address-cells' is only needed when parsing 'interrupt-map' properties, so +remove it from the common interrupt-provider test. + +Cc: Andre Przywara <andre.przywara@arm.com> +Reviewed-by: David Gibson <david@gibson.dropbear.id.au> +Signed-off-by: Rob Herring <robh@kernel.org> +Message-Id: <20211011191245.1009682-3-robh@kernel.org> +Signed-off-by: David Gibson <david@gibson.dropbear.id.au> +--- +--- a/scripts/dtc/checks.c ++++ b/scripts/dtc/checks.c +@@ -1569,11 +1569,6 @@ static void check_interrupt_provider(str + if (!prop) + FAIL(c, dti, node, + "Missing #interrupt-cells in interrupt provider"); +- +- prop = get_property(node, "#address-cells"); +- if (!prop) +- FAIL(c, dti, node, +- "Missing #address-cells in interrupt provider"); + } + WARNING(interrupt_provider, check_interrupt_provider, NULL); + diff --git a/pkgs/patches-linux-5.15/050-v5.16-00-MIPS-uasm-Enable-muhu-opcode-for-MIPS-R6.patch b/pkgs/patches-linux-5.15/050-v5.16-00-MIPS-uasm-Enable-muhu-opcode-for-MIPS-R6.patch new file mode 100644 index 0000000..82feb74 --- /dev/null +++ b/pkgs/patches-linux-5.15/050-v5.16-00-MIPS-uasm-Enable-muhu-opcode-for-MIPS-R6.patch @@ -0,0 +1,65 @@ +From: Johan Almbladh <johan.almbladh@anyfinetworks.com> +Date: Tue, 5 Oct 2021 18:54:02 +0200 +Subject: [PATCH] MIPS: uasm: Enable muhu opcode for MIPS R6 + +Enable the 'muhu' instruction, complementing the existing 'mulu', needed +to implement a MIPS32 BPF JIT. + +Also fix a typo in the existing definition of 'dmulu'. + +Signed-off-by: Tony Ambardar <Tony.Ambardar@gmail.com> + +This patch is a dependency for my 32-bit MIPS eBPF JIT. + +Signed-off-by: Johan Almbladh <johan.almbladh@anyfinetworks.com> +--- + +--- a/arch/mips/include/asm/uasm.h ++++ b/arch/mips/include/asm/uasm.h +@@ -145,6 +145,7 @@ Ip_u1(_mtlo); + Ip_u3u1u2(_mul); + Ip_u1u2(_multu); + Ip_u3u1u2(_mulu); ++Ip_u3u1u2(_muhu); + Ip_u3u1u2(_nor); + Ip_u3u1u2(_or); + Ip_u2u1u3(_ori); +--- a/arch/mips/mm/uasm-mips.c ++++ b/arch/mips/mm/uasm-mips.c +@@ -90,7 +90,7 @@ static const struct insn insn_table[insn + RS | RT | RD}, + [insn_dmtc0] = {M(cop0_op, dmtc_op, 0, 0, 0, 0), RT | RD | SET}, + [insn_dmultu] = {M(spec_op, 0, 0, 0, 0, dmultu_op), RS | RT}, +- [insn_dmulu] = {M(spec_op, 0, 0, 0, dmult_dmul_op, dmultu_op), ++ [insn_dmulu] = {M(spec_op, 0, 0, 0, dmultu_dmulu_op, dmultu_op), + RS | RT | RD}, + [insn_drotr] = {M(spec_op, 1, 0, 0, 0, dsrl_op), RT | RD | RE}, + [insn_drotr32] = {M(spec_op, 1, 0, 0, 0, dsrl32_op), RT | RD | RE}, +@@ -150,6 +150,8 @@ static const struct insn insn_table[insn + [insn_mtlo] = {M(spec_op, 0, 0, 0, 0, mtlo_op), RS}, + [insn_mulu] = {M(spec_op, 0, 0, 0, multu_mulu_op, multu_op), + RS | RT | RD}, ++ [insn_muhu] = {M(spec_op, 0, 0, 0, multu_muhu_op, multu_op), ++ RS | RT | RD}, + #ifndef CONFIG_CPU_MIPSR6 + [insn_mul] = {M(spec2_op, 0, 0, 0, 0, mul_op), RS | RT | RD}, + #else +--- a/arch/mips/mm/uasm.c ++++ b/arch/mips/mm/uasm.c +@@ -59,7 +59,7 @@ enum opcode { + insn_lddir, insn_ldpte, insn_ldx, insn_lh, insn_lhu, insn_ll, insn_lld, + insn_lui, insn_lw, insn_lwu, insn_lwx, insn_mfc0, insn_mfhc0, insn_mfhi, + insn_mflo, insn_modu, insn_movn, insn_movz, insn_mtc0, insn_mthc0, +- insn_mthi, insn_mtlo, insn_mul, insn_multu, insn_mulu, insn_nor, ++ insn_mthi, insn_mtlo, insn_mul, insn_multu, insn_mulu, insn_muhu, insn_nor, + insn_or, insn_ori, insn_pref, insn_rfe, insn_rotr, insn_sb, insn_sc, + insn_scd, insn_seleqz, insn_selnez, insn_sd, insn_sh, insn_sll, + insn_sllv, insn_slt, insn_slti, insn_sltiu, insn_sltu, insn_sra, +@@ -344,6 +344,7 @@ I_u1(_mtlo) + I_u3u1u2(_mul) + I_u1u2(_multu) + I_u3u1u2(_mulu) ++I_u3u1u2(_muhu) + I_u3u1u2(_nor) + I_u3u1u2(_or) + I_u2u1u3(_ori) diff --git a/pkgs/patches-linux-5.15/050-v5.16-01-mips-uasm-Add-workaround-for-Loongson-2F-nop-CPU-err.patch b/pkgs/patches-linux-5.15/050-v5.16-01-mips-uasm-Add-workaround-for-Loongson-2F-nop-CPU-err.patch new file mode 100644 index 0000000..3a4d573 --- /dev/null +++ b/pkgs/patches-linux-5.15/050-v5.16-01-mips-uasm-Add-workaround-for-Loongson-2F-nop-CPU-err.patch @@ -0,0 +1,31 @@ +From: Johan Almbladh <johan.almbladh@anyfinetworks.com> +Date: Tue, 5 Oct 2021 18:54:03 +0200 +Subject: [PATCH] mips: uasm: Add workaround for Loongson-2F nop CPU errata + +This patch implements a workaround for the Loongson-2F nop in generated, +code, if the existing option CONFIG_CPU_NOP_WORKAROUND is set. Before, +the binutils option -mfix-loongson2f-nop was enabled, but no workaround +was done when emitting MIPS code. Now, the nop pseudo instruction is +emitted as "or ax,ax,zero" instead of the default "sll zero,zero,0". This +is consistent with the workaround implemented by binutils. + +Link: https://sourceware.org/legacy-ml/binutils/2009-11/msg00387.html + +Signed-off-by: Johan Almbladh <johan.almbladh@anyfinetworks.com> +Reviewed-by: Jiaxun Yang <jiaxun.yang@flygoat.com> +--- + +--- a/arch/mips/include/asm/uasm.h ++++ b/arch/mips/include/asm/uasm.h +@@ -249,7 +249,11 @@ static inline void uasm_l##lb(struct uas + #define uasm_i_bnezl(buf, rs, off) uasm_i_bnel(buf, rs, 0, off) + #define uasm_i_ehb(buf) uasm_i_sll(buf, 0, 0, 3) + #define uasm_i_move(buf, a, b) UASM_i_ADDU(buf, a, 0, b) ++#ifdef CONFIG_CPU_NOP_WORKAROUNDS ++#define uasm_i_nop(buf) uasm_i_or(buf, 1, 1, 0) ++#else + #define uasm_i_nop(buf) uasm_i_sll(buf, 0, 0, 0) ++#endif + #define uasm_i_ssnop(buf) uasm_i_sll(buf, 0, 0, 1) + + static inline void uasm_i_drotr_safe(u32 **p, unsigned int a1, diff --git a/pkgs/patches-linux-5.15/050-v5.16-02-mips-bpf-Add-eBPF-JIT-for-32-bit-MIPS.patch b/pkgs/patches-linux-5.15/050-v5.16-02-mips-bpf-Add-eBPF-JIT-for-32-bit-MIPS.patch new file mode 100644 index 0000000..7980659 --- /dev/null +++ b/pkgs/patches-linux-5.15/050-v5.16-02-mips-bpf-Add-eBPF-JIT-for-32-bit-MIPS.patch @@ -0,0 +1,3078 @@ +From: Johan Almbladh <johan.almbladh@anyfinetworks.com> +Date: Tue, 5 Oct 2021 18:54:04 +0200 +Subject: [PATCH] mips: bpf: Add eBPF JIT for 32-bit MIPS + +This is an implementation of an eBPF JIT for 32-bit MIPS I-V and MIPS32. +The implementation supports all 32-bit and 64-bit ALU and JMP operations, +including the recently-added atomics. 64-bit div/mod and 64-bit atomics +are implemented using function calls to math64 and atomic64 functions, +respectively. All 32-bit operations are implemented natively by the JIT, +except if the CPU lacks ll/sc instructions. + +Register mapping +================ +All 64-bit eBPF registers are mapped to native 32-bit MIPS register pairs, +and does not use any stack scratch space for register swapping. This means +that all eBPF register data is kept in CPU registers all the time, and +this simplifies the register management a lot. It also reduces the JIT's +pressure on temporary registers since we do not have to move data around. + +Native register pairs are ordered according to CPU endiannes, following +the O32 calling convention for passing 64-bit arguments and return values. +The eBPF return value, arguments and callee-saved registers are mapped to +their native MIPS equivalents. + +Since the 32 highest bits in the eBPF FP (frame pointer) register are +always zero, only one general-purpose register is actually needed for the +mapping. The MIPS fp register is used for this purpose. The high bits are +mapped to MIPS register r0. This saves us one CPU register, which is much +needed for temporaries, while still allowing us to treat the R10 (FP) +register just like any other eBPF register in the JIT. + +The MIPS gp (global pointer) and at (assembler temporary) registers are +used as internal temporary registers for constant blinding. CPU registers +t6-t9 are used internally by the JIT when constructing more complex 64-bit +operations. This is precisely what is needed - two registers to store an +operand value, and two more as scratch registers when performing the +operation. + +The register mapping is shown below. + + R0 - $v1, $v0 return value + R1 - $a1, $a0 argument 1, passed in registers + R2 - $a3, $a2 argument 2, passed in registers + R3 - $t1, $t0 argument 3, passed on stack + R4 - $t3, $t2 argument 4, passed on stack + R5 - $t4, $t3 argument 5, passed on stack + R6 - $s1, $s0 callee-saved + R7 - $s3, $s2 callee-saved + R8 - $s5, $s4 callee-saved + R9 - $s7, $s6 callee-saved + FP - $r0, $fp 32-bit frame pointer + AX - $gp, $at constant-blinding + $t6 - $t9 unallocated, JIT temporaries + +Jump offsets +============ +The JIT tries to map all conditional JMP operations to MIPS conditional +PC-relative branches. The MIPS branch offset field is 18 bits, in bytes, +which is equivalent to the eBPF 16-bit instruction offset. However, since +the JIT may emit more than one CPU instruction per eBPF instruction, the +field width may overflow. If that happens, the JIT converts the long +conditional jump to a short PC-relative branch with the condition +inverted, jumping over a long unconditional absolute jmp (j). + +This conversion will change the instruction offset mapping used for jumps, +and may in turn result in more branch offset overflows. The JIT therefore +dry-runs the translation until no more branches are converted and the +offsets do not change anymore. There is an upper bound on this of course, +and if the JIT hits that limit, the last two iterations are run with all +branches being converted. + +Tail call count +=============== +The current tail call count is stored in the 16-byte area of the caller's +stack frame that is reserved for the callee in the o32 ABI. The value is +initialized in the prologue, and propagated to the tail-callee by skipping +the initialization instructions when emitting the tail call. + +Signed-off-by: Johan Almbladh <johan.almbladh@anyfinetworks.com> +--- + create mode 100644 arch/mips/net/bpf_jit_comp.c + create mode 100644 arch/mips/net/bpf_jit_comp.h + create mode 100644 arch/mips/net/bpf_jit_comp32.c + +--- a/arch/mips/net/Makefile ++++ b/arch/mips/net/Makefile +@@ -2,4 +2,9 @@ + # MIPS networking code + + obj-$(CONFIG_MIPS_CBPF_JIT) += bpf_jit.o bpf_jit_asm.o +-obj-$(CONFIG_MIPS_EBPF_JIT) += ebpf_jit.o ++ ++ifeq ($(CONFIG_32BIT),y) ++ obj-$(CONFIG_MIPS_EBPF_JIT) += bpf_jit_comp.o bpf_jit_comp32.o ++else ++ obj-$(CONFIG_MIPS_EBPF_JIT) += ebpf_jit.o ++endif +--- /dev/null ++++ b/arch/mips/net/bpf_jit_comp.c +@@ -0,0 +1,1020 @@ ++// SPDX-License-Identifier: GPL-2.0-only ++/* ++ * Just-In-Time compiler for eBPF bytecode on MIPS. ++ * Implementation of JIT functions common to 32-bit and 64-bit CPUs. ++ * ++ * Copyright (c) 2021 Anyfi Networks AB. ++ * Author: Johan Almbladh <johan.almbladh@gmail.com> ++ * ++ * Based on code and ideas from ++ * Copyright (c) 2017 Cavium, Inc. ++ * Copyright (c) 2017 Shubham Bansal <illusionist.neo@gmail.com> ++ * Copyright (c) 2011 Mircea Gherzan <mgherzan@gmail.com> ++ */ ++ ++/* ++ * Code overview ++ * ============= ++ * ++ * - bpf_jit_comp.h ++ * Common definitions and utilities. ++ * ++ * - bpf_jit_comp.c ++ * Implementation of JIT top-level logic and exported JIT API functions. ++ * Implementation of internal operations shared by 32-bit and 64-bit code. ++ * JMP and ALU JIT control code, register control code, shared ALU and ++ * JMP/JMP32 JIT operations. ++ * ++ * - bpf_jit_comp32.c ++ * Implementation of functions to JIT prologue, epilogue and a single eBPF ++ * instruction for 32-bit MIPS CPUs. The functions use shared operations ++ * where possible, and implement the rest for 32-bit MIPS such as ALU64 ++ * operations. ++ * ++ * - bpf_jit_comp64.c ++ * Ditto, for 64-bit MIPS CPUs. ++ * ++ * Zero and sign extension ++ * ======================== ++ * 32-bit MIPS instructions on 64-bit MIPS registers use sign extension, ++ * but the eBPF instruction set mandates zero extension. We let the verifier ++ * insert explicit zero-extensions after 32-bit ALU operations, both for ++ * 32-bit and 64-bit MIPS JITs. Conditional JMP32 operations on 64-bit MIPs ++ * are JITed with sign extensions inserted when so expected. ++ * ++ * ALU operations ++ * ============== ++ * ALU operations on 32/64-bit MIPS and ALU64 operations on 64-bit MIPS are ++ * JITed in the following steps. ALU64 operations on 32-bit MIPS are more ++ * complicated and therefore only processed by special implementations in ++ * step (3). ++ * ++ * 1) valid_alu_i: ++ * Determine if an immediate operation can be emitted as such, or if ++ * we must fall back to the register version. ++ * ++ * 2) rewrite_alu_i: ++ * Convert BPF operation and immediate value to a canonical form for ++ * JITing. In some degenerate cases this form may be a no-op. ++ * ++ * 3) emit_alu_{i,i64,r,64}: ++ * Emit instructions for an ALU or ALU64 immediate or register operation. ++ * ++ * JMP operations ++ * ============== ++ * JMP and JMP32 operations require an JIT instruction offset table for ++ * translating the jump offset. This table is computed by dry-running the ++ * JIT without actually emitting anything. However, the computed PC-relative ++ * offset may overflow the 18-bit offset field width of the native MIPS ++ * branch instruction. In such cases, the long jump is converted into the ++ * following sequence. ++ * ++ * <branch> !<cond> +2 Inverted PC-relative branch ++ * nop Delay slot ++ * j <offset> Unconditional absolute long jump ++ * nop Delay slot ++ * ++ * Since this converted sequence alters the offset table, all offsets must ++ * be re-calculated. This may in turn trigger new branch conversions, so ++ * the process is repeated until no further changes are made. Normally it ++ * completes in 1-2 iterations. If JIT_MAX_ITERATIONS should reached, we ++ * fall back to converting every remaining jump operation. The branch ++ * conversion is independent of how the JMP or JMP32 condition is JITed. ++ * ++ * JMP32 and JMP operations are JITed as follows. ++ * ++ * 1) setup_jmp_{i,r}: ++ * Convert jump conditional and offset into a form that can be JITed. ++ * This form may be a no-op, a canonical form, or an inverted PC-relative ++ * jump if branch conversion is necessary. ++ * ++ * 2) valid_jmp_i: ++ * Determine if an immediate operations can be emitted as such, or if ++ * we must fall back to the register version. Applies to JMP32 for 32-bit ++ * MIPS, and both JMP and JMP32 for 64-bit MIPS. ++ * ++ * 3) emit_jmp_{i,i64,r,r64}: ++ * Emit instructions for an JMP or JMP32 immediate or register operation. ++ * ++ * 4) finish_jmp_{i,r}: ++ * Emit any instructions needed to finish the jump. This includes a nop ++ * for the delay slot if a branch was emitted, and a long absolute jump ++ * if the branch was converted. ++ */ ++ ++#include <linux/limits.h> ++#include <linux/bitops.h> ++#include <linux/errno.h> ++#include <linux/filter.h> ++#include <linux/bpf.h> ++#include <linux/slab.h> ++#include <asm/bitops.h> ++#include <asm/cacheflush.h> ++#include <asm/cpu-features.h> ++#include <asm/isa-rev.h> ++#include <asm/uasm.h> ++ ++#include "bpf_jit_comp.h" ++ ++/* Convenience macros for descriptor access */ ++#define CONVERTED(desc) ((desc) & JIT_DESC_CONVERT) ++#define INDEX(desc) ((desc) & ~JIT_DESC_CONVERT) ++ ++/* ++ * Push registers on the stack, starting at a given depth from the stack ++ * pointer and increasing. The next depth to be written is returned. ++ */ ++int push_regs(struct jit_context *ctx, u32 mask, u32 excl, int depth) ++{ ++ int reg; ++ ++ for (reg = 0; reg < BITS_PER_BYTE * sizeof(mask); reg++) ++ if (mask & BIT(reg)) { ++ if ((excl & BIT(reg)) == 0) { ++ if (sizeof(long) == 4) ++ emit(ctx, sw, reg, depth, MIPS_R_SP); ++ else /* sizeof(long) == 8 */ ++ emit(ctx, sd, reg, depth, MIPS_R_SP); ++ } ++ depth += sizeof(long); ++ } ++ ++ ctx->stack_used = max((int)ctx->stack_used, depth); ++ return depth; ++} ++ ++/* ++ * Pop registers from the stack, starting at a given depth from the stack ++ * pointer and increasing. The next depth to be read is returned. ++ */ ++int pop_regs(struct jit_context *ctx, u32 mask, u32 excl, int depth) ++{ ++ int reg; ++ ++ for (reg = 0; reg < BITS_PER_BYTE * sizeof(mask); reg++) ++ if (mask & BIT(reg)) { ++ if ((excl & BIT(reg)) == 0) { ++ if (sizeof(long) == 4) ++ emit(ctx, lw, reg, depth, MIPS_R_SP); ++ else /* sizeof(long) == 8 */ ++ emit(ctx, ld, reg, depth, MIPS_R_SP); ++ } ++ depth += sizeof(long); ++ } ++ ++ return depth; ++} ++ ++/* Compute the 28-bit jump target address from a BPF program location */ ++int get_target(struct jit_context *ctx, u32 loc) ++{ ++ u32 index = INDEX(ctx->descriptors[loc]); ++ unsigned long pc = (unsigned long)&ctx->target[ctx->jit_index]; ++ unsigned long addr = (unsigned long)&ctx->target[index]; ++ ++ if (!ctx->target) ++ return 0; ++ ++ if ((addr ^ pc) & ~MIPS_JMP_MASK) ++ return -1; ++ ++ return addr & MIPS_JMP_MASK; ++} ++ ++/* Compute the PC-relative offset to relative BPF program offset */ ++int get_offset(const struct jit_context *ctx, int off) ++{ ++ return (INDEX(ctx->descriptors[ctx->bpf_index + off]) - ++ ctx->jit_index - 1) * sizeof(u32); ++} ++ ++/* dst = imm (register width) */ ++void emit_mov_i(struct jit_context *ctx, u8 dst, s32 imm) ++{ ++ if (imm >= -0x8000 && imm <= 0x7fff) { ++ emit(ctx, addiu, dst, MIPS_R_ZERO, imm); ++ } else { ++ emit(ctx, lui, dst, (s16)((u32)imm >> 16)); ++ emit(ctx, ori, dst, dst, (u16)(imm & 0xffff)); ++ } ++ clobber_reg(ctx, dst); ++} ++ ++/* dst = src (register width) */ ++void emit_mov_r(struct jit_context *ctx, u8 dst, u8 src) ++{ ++ emit(ctx, ori, dst, src, 0); ++ clobber_reg(ctx, dst); ++} ++ ++/* Validate ALU immediate range */ ++bool valid_alu_i(u8 op, s32 imm) ++{ ++ switch (BPF_OP(op)) { ++ case BPF_NEG: ++ case BPF_LSH: ++ case BPF_RSH: ++ case BPF_ARSH: ++ /* All legal eBPF values are valid */ ++ return true; ++ case BPF_ADD: ++ /* imm must be 16 bits */ ++ return imm >= -0x8000 && imm <= 0x7fff; ++ case BPF_SUB: ++ /* -imm must be 16 bits */ ++ return imm >= -0x7fff && imm <= 0x8000; ++ case BPF_AND: ++ case BPF_OR: ++ case BPF_XOR: ++ /* imm must be 16 bits unsigned */ ++ return imm >= 0 && imm <= 0xffff; ++ case BPF_MUL: ++ /* imm must be zero or a positive power of two */ ++ return imm == 0 || (imm > 0 && is_power_of_2(imm)); ++ case BPF_DIV: ++ case BPF_MOD: ++ /* imm must be an 17-bit power of two */ ++ return (u32)imm <= 0x10000 && is_power_of_2((u32)imm); ++ } ++ return false; ++} ++ ++/* Rewrite ALU immediate operation */ ++bool rewrite_alu_i(u8 op, s32 imm, u8 *alu, s32 *val) ++{ ++ bool act = true; ++ ++ switch (BPF_OP(op)) { ++ case BPF_LSH: ++ case BPF_RSH: ++ case BPF_ARSH: ++ case BPF_ADD: ++ case BPF_SUB: ++ case BPF_OR: ++ case BPF_XOR: ++ /* imm == 0 is a no-op */ ++ act = imm != 0; ++ break; ++ case BPF_MUL: ++ if (imm == 1) { ++ /* dst * 1 is a no-op */ ++ act = false; ++ } else if (imm == 0) { ++ /* dst * 0 is dst & 0 */ ++ op = BPF_AND; ++ } else { ++ /* dst * (1 << n) is dst << n */ ++ op = BPF_LSH; ++ imm = ilog2(abs(imm)); ++ } ++ break; ++ case BPF_DIV: ++ if (imm == 1) { ++ /* dst / 1 is a no-op */ ++ act = false; ++ } else { ++ /* dst / (1 << n) is dst >> n */ ++ op = BPF_RSH; ++ imm = ilog2(imm); ++ } ++ break; ++ case BPF_MOD: ++ /* dst % (1 << n) is dst & ((1 << n) - 1) */ ++ op = BPF_AND; ++ imm--; ++ break; ++ } ++ ++ *alu = op; ++ *val = imm; ++ return act; ++} ++ ++/* ALU immediate operation (32-bit) */ ++void emit_alu_i(struct jit_context *ctx, u8 dst, s32 imm, u8 op) ++{ ++ switch (BPF_OP(op)) { ++ /* dst = -dst */ ++ case BPF_NEG: ++ emit(ctx, subu, dst, MIPS_R_ZERO, dst); ++ break; ++ /* dst = dst & imm */ ++ case BPF_AND: ++ emit(ctx, andi, dst, dst, (u16)imm); ++ break; ++ /* dst = dst | imm */ ++ case BPF_OR: ++ emit(ctx, ori, dst, dst, (u16)imm); ++ break; ++ /* dst = dst ^ imm */ ++ case BPF_XOR: ++ emit(ctx, xori, dst, dst, (u16)imm); ++ break; ++ /* dst = dst << imm */ ++ case BPF_LSH: ++ emit(ctx, sll, dst, dst, imm); ++ break; ++ /* dst = dst >> imm */ ++ case BPF_RSH: ++ emit(ctx, srl, dst, dst, imm); ++ break; ++ /* dst = dst >> imm (arithmetic) */ ++ case BPF_ARSH: ++ emit(ctx, sra, dst, dst, imm); ++ break; ++ /* dst = dst + imm */ ++ case BPF_ADD: ++ emit(ctx, addiu, dst, dst, imm); ++ break; ++ /* dst = dst - imm */ ++ case BPF_SUB: ++ emit(ctx, addiu, dst, dst, -imm); ++ break; ++ } ++ clobber_reg(ctx, dst); ++} ++ ++/* ALU register operation (32-bit) */ ++void emit_alu_r(struct jit_context *ctx, u8 dst, u8 src, u8 op) ++{ ++ switch (BPF_OP(op)) { ++ /* dst = dst & src */ ++ case BPF_AND: ++ emit(ctx, and, dst, dst, src); ++ break; ++ /* dst = dst | src */ ++ case BPF_OR: ++ emit(ctx, or, dst, dst, src); ++ break; ++ /* dst = dst ^ src */ ++ case BPF_XOR: ++ emit(ctx, xor, dst, dst, src); ++ break; ++ /* dst = dst << src */ ++ case BPF_LSH: ++ emit(ctx, sllv, dst, dst, src); ++ break; ++ /* dst = dst >> src */ ++ case BPF_RSH: ++ emit(ctx, srlv, dst, dst, src); ++ break; ++ /* dst = dst >> src (arithmetic) */ ++ case BPF_ARSH: ++ emit(ctx, srav, dst, dst, src); ++ break; ++ /* dst = dst + src */ ++ case BPF_ADD: ++ emit(ctx, addu, dst, dst, src); ++ break; ++ /* dst = dst - src */ ++ case BPF_SUB: ++ emit(ctx, subu, dst, dst, src); ++ break; ++ /* dst = dst * src */ ++ case BPF_MUL: ++ if (cpu_has_mips32r1 || cpu_has_mips32r6) { ++ emit(ctx, mul, dst, dst, src); ++ } else { ++ emit(ctx, multu, dst, src); ++ emit(ctx, mflo, dst); ++ } ++ break; ++ /* dst = dst / src */ ++ case BPF_DIV: ++ if (cpu_has_mips32r6) { ++ emit(ctx, divu_r6, dst, dst, src); ++ } else { ++ emit(ctx, divu, dst, src); ++ emit(ctx, mflo, dst); ++ } ++ break; ++ /* dst = dst % src */ ++ case BPF_MOD: ++ if (cpu_has_mips32r6) { ++ emit(ctx, modu, dst, dst, src); ++ } else { ++ emit(ctx, divu, dst, src); ++ emit(ctx, mfhi, dst); ++ } ++ break; ++ } ++ clobber_reg(ctx, dst); ++} ++ ++/* Atomic read-modify-write (32-bit) */ ++void emit_atomic_r(struct jit_context *ctx, u8 dst, u8 src, s16 off, u8 code) ++{ ++ emit(ctx, ll, MIPS_R_T9, off, dst); ++ switch (code) { ++ case BPF_ADD: ++ emit(ctx, addu, MIPS_R_T8, MIPS_R_T9, src); ++ break; ++ case BPF_AND: ++ emit(ctx, and, MIPS_R_T8, MIPS_R_T9, src); ++ break; ++ case BPF_OR: ++ emit(ctx, or, MIPS_R_T8, MIPS_R_T9, src); ++ break; ++ case BPF_XOR: ++ emit(ctx, xor, MIPS_R_T8, MIPS_R_T9, src); ++ break; ++ } ++ emit(ctx, sc, MIPS_R_T8, off, dst); ++ emit(ctx, beqz, MIPS_R_T8, -16); ++ emit(ctx, nop); /* Delay slot */ ++} ++ ++/* Atomic compare-and-exchange (32-bit) */ ++void emit_cmpxchg_r(struct jit_context *ctx, u8 dst, u8 src, u8 res, s16 off) ++{ ++ emit(ctx, ll, MIPS_R_T9, off, dst); ++ emit(ctx, bne, MIPS_R_T9, res, 12); ++ emit(ctx, move, MIPS_R_T8, src); /* Delay slot */ ++ emit(ctx, sc, MIPS_R_T8, off, dst); ++ emit(ctx, beqz, MIPS_R_T8, -20); ++ emit(ctx, move, res, MIPS_R_T9); /* Delay slot */ ++ clobber_reg(ctx, res); ++} ++ ++/* Swap bytes and truncate a register word or half word */ ++void emit_bswap_r(struct jit_context *ctx, u8 dst, u32 width) ++{ ++ u8 tmp = MIPS_R_T8; ++ u8 msk = MIPS_R_T9; ++ ++ switch (width) { ++ /* Swap bytes in a word */ ++ case 32: ++ if (cpu_has_mips32r2 || cpu_has_mips32r6) { ++ emit(ctx, wsbh, dst, dst); ++ emit(ctx, rotr, dst, dst, 16); ++ } else { ++ emit(ctx, sll, tmp, dst, 16); /* tmp = dst << 16 */ ++ emit(ctx, srl, dst, dst, 16); /* dst = dst >> 16 */ ++ emit(ctx, or, dst, dst, tmp); /* dst = dst | tmp */ ++ ++ emit(ctx, lui, msk, 0xff); /* msk = 0x00ff0000 */ ++ emit(ctx, ori, msk, msk, 0xff); /* msk = msk | 0xff */ ++ ++ emit(ctx, and, tmp, dst, msk); /* tmp = dst & msk */ ++ emit(ctx, sll, tmp, tmp, 8); /* tmp = tmp << 8 */ ++ emit(ctx, srl, dst, dst, 8); /* dst = dst >> 8 */ ++ emit(ctx, and, dst, dst, msk); /* dst = dst & msk */ ++ emit(ctx, or, dst, dst, tmp); /* reg = dst | tmp */ ++ } ++ break; ++ /* Swap bytes in a half word */ ++ case 16: ++ if (cpu_has_mips32r2 || cpu_has_mips32r6) { ++ emit(ctx, wsbh, dst, dst); ++ emit(ctx, andi, dst, dst, 0xffff); ++ } else { ++ emit(ctx, andi, tmp, dst, 0xff00); /* t = d & 0xff00 */ ++ emit(ctx, srl, tmp, tmp, 8); /* t = t >> 8 */ ++ emit(ctx, andi, dst, dst, 0x00ff); /* d = d & 0x00ff */ ++ emit(ctx, sll, dst, dst, 8); /* d = d << 8 */ ++ emit(ctx, or, dst, dst, tmp); /* d = d | t */ ++ } ++ break; ++ } ++ clobber_reg(ctx, dst); ++} ++ ++/* Validate jump immediate range */ ++bool valid_jmp_i(u8 op, s32 imm) ++{ ++ switch (op) { ++ case JIT_JNOP: ++ /* Immediate value not used */ ++ return true; ++ case BPF_JEQ: ++ case BPF_JNE: ++ /* No immediate operation */ ++ return false; ++ case BPF_JSET: ++ case JIT_JNSET: ++ /* imm must be 16 bits unsigned */ ++ return imm >= 0 && imm <= 0xffff; ++ case BPF_JGE: ++ case BPF_JLT: ++ case BPF_JSGE: ++ case BPF_JSLT: ++ /* imm must be 16 bits */ ++ return imm >= -0x8000 && imm <= 0x7fff; ++ case BPF_JGT: ++ case BPF_JLE: ++ case BPF_JSGT: ++ case BPF_JSLE: ++ /* imm + 1 must be 16 bits */ ++ return imm >= -0x8001 && imm <= 0x7ffe; ++ } ++ return false; ++} ++ ++/* Invert a conditional jump operation */ ++static u8 invert_jmp(u8 op) ++{ ++ switch (op) { ++ case BPF_JA: return JIT_JNOP; ++ case BPF_JEQ: return BPF_JNE; ++ case BPF_JNE: return BPF_JEQ; ++ case BPF_JSET: return JIT_JNSET; ++ case BPF_JGT: return BPF_JLE; ++ case BPF_JGE: return BPF_JLT; ++ case BPF_JLT: return BPF_JGE; ++ case BPF_JLE: return BPF_JGT; ++ case BPF_JSGT: return BPF_JSLE; ++ case BPF_JSGE: return BPF_JSLT; ++ case BPF_JSLT: return BPF_JSGE; ++ case BPF_JSLE: return BPF_JSGT; ++ } ++ return 0; ++} ++ ++/* Prepare a PC-relative jump operation */ ++static void setup_jmp(struct jit_context *ctx, u8 bpf_op, ++ s16 bpf_off, u8 *jit_op, s32 *jit_off) ++{ ++ u32 *descp = &ctx->descriptors[ctx->bpf_index]; ++ int op = bpf_op; ++ int offset = 0; ++ ++ /* Do not compute offsets on the first pass */ ++ if (INDEX(*descp) == 0) ++ goto done; ++ ++ /* Skip jumps never taken */ ++ if (bpf_op == JIT_JNOP) ++ goto done; ++ ++ /* Convert jumps always taken */ ++ if (bpf_op == BPF_JA) ++ *descp |= JIT_DESC_CONVERT; ++ ++ /* ++ * Current ctx->jit_index points to the start of the branch preamble. ++ * Since the preamble differs among different branch conditionals, ++ * the current index cannot be used to compute the branch offset. ++ * Instead, we use the offset table value for the next instruction, ++ * which gives the index immediately after the branch delay slot. ++ */ ++ if (!CONVERTED(*descp)) { ++ int target = ctx->bpf_index + bpf_off + 1; ++ int origin = ctx->bpf_index + 1; ++ ++ offset = (INDEX(ctx->descriptors[target]) - ++ INDEX(ctx->descriptors[origin]) + 1) * sizeof(u32); ++ } ++ ++ /* ++ * The PC-relative branch offset field on MIPS is 18 bits signed, ++ * so if the computed offset is larger than this we generate a an ++ * absolute jump that we skip with an inverted conditional branch. ++ */ ++ if (CONVERTED(*descp) || offset < -0x20000 || offset > 0x1ffff) { ++ offset = 3 * sizeof(u32); ++ op = invert_jmp(bpf_op); ++ ctx->changes += !CONVERTED(*descp); ++ *descp |= JIT_DESC_CONVERT; ++ } ++ ++done: ++ *jit_off = offset; ++ *jit_op = op; ++} ++ ++/* Prepare a PC-relative jump operation with immediate conditional */ ++void setup_jmp_i(struct jit_context *ctx, s32 imm, u8 width, ++ u8 bpf_op, s16 bpf_off, u8 *jit_op, s32 *jit_off) ++{ ++ bool always = false; ++ bool never = false; ++ ++ switch (bpf_op) { ++ case BPF_JEQ: ++ case BPF_JNE: ++ break; ++ case BPF_JSET: ++ case BPF_JLT: ++ never = imm == 0; ++ break; ++ case BPF_JGE: ++ always = imm == 0; ++ break; ++ case BPF_JGT: ++ never = (u32)imm == U32_MAX; ++ break; ++ case BPF_JLE: ++ always = (u32)imm == U32_MAX; ++ break; ++ case BPF_JSGT: ++ never = imm == S32_MAX && width == 32; ++ break; ++ case BPF_JSGE: ++ always = imm == S32_MIN && width == 32; ++ break; ++ case BPF_JSLT: ++ never = imm == S32_MIN && width == 32; ++ break; ++ case BPF_JSLE: ++ always = imm == S32_MAX && width == 32; ++ break; ++ } ++ ++ if (never) ++ bpf_op = JIT_JNOP; ++ if (always) ++ bpf_op = BPF_JA; ++ setup_jmp(ctx, bpf_op, bpf_off, jit_op, jit_off); ++} ++ ++/* Prepare a PC-relative jump operation with register conditional */ ++void setup_jmp_r(struct jit_context *ctx, bool same_reg, ++ u8 bpf_op, s16 bpf_off, u8 *jit_op, s32 *jit_off) ++{ ++ switch (bpf_op) { ++ case BPF_JSET: ++ break; ++ case BPF_JEQ: ++ case BPF_JGE: ++ case BPF_JLE: ++ case BPF_JSGE: ++ case BPF_JSLE: ++ if (same_reg) ++ bpf_op = BPF_JA; ++ break; ++ case BPF_JNE: ++ case BPF_JLT: ++ case BPF_JGT: ++ case BPF_JSGT: ++ case BPF_JSLT: ++ if (same_reg) ++ bpf_op = JIT_JNOP; ++ break; ++ } ++ setup_jmp(ctx, bpf_op, bpf_off, jit_op, jit_off); ++} ++ ++/* Finish a PC-relative jump operation */ ++int finish_jmp(struct jit_context *ctx, u8 jit_op, s16 bpf_off) ++{ ++ /* Emit conditional branch delay slot */ ++ if (jit_op != JIT_JNOP) ++ emit(ctx, nop); ++ /* ++ * Emit an absolute long jump with delay slot, ++ * if the PC-relative branch was converted. ++ */ ++ if (CONVERTED(ctx->descriptors[ctx->bpf_index])) { ++ int target = get_target(ctx, ctx->bpf_index + bpf_off + 1); ++ ++ if (target < 0) ++ return -1; ++ emit(ctx, j, target); ++ emit(ctx, nop); ++ } ++ return 0; ++} ++ ++/* Jump immediate (32-bit) */ ++void emit_jmp_i(struct jit_context *ctx, u8 dst, s32 imm, s32 off, u8 op) ++{ ++ switch (op) { ++ /* No-op, used internally for branch optimization */ ++ case JIT_JNOP: ++ break; ++ /* PC += off if dst & imm */ ++ case BPF_JSET: ++ emit(ctx, andi, MIPS_R_T9, dst, (u16)imm); ++ emit(ctx, bnez, MIPS_R_T9, off); ++ break; ++ /* PC += off if (dst & imm) == 0 (not in BPF, used for long jumps) */ ++ case JIT_JNSET: ++ emit(ctx, andi, MIPS_R_T9, dst, (u16)imm); ++ emit(ctx, beqz, MIPS_R_T9, off); ++ break; ++ /* PC += off if dst > imm */ ++ case BPF_JGT: ++ emit(ctx, sltiu, MIPS_R_T9, dst, imm + 1); ++ emit(ctx, beqz, MIPS_R_T9, off); ++ break; ++ /* PC += off if dst >= imm */ ++ case BPF_JGE: ++ emit(ctx, sltiu, MIPS_R_T9, dst, imm); ++ emit(ctx, beqz, MIPS_R_T9, off); ++ break; ++ /* PC += off if dst < imm */ ++ case BPF_JLT: ++ emit(ctx, sltiu, MIPS_R_T9, dst, imm); ++ emit(ctx, bnez, MIPS_R_T9, off); ++ break; ++ /* PC += off if dst <= imm */ ++ case BPF_JLE: ++ emit(ctx, sltiu, MIPS_R_T9, dst, imm + 1); ++ emit(ctx, bnez, MIPS_R_T9, off); ++ break; ++ /* PC += off if dst > imm (signed) */ ++ case BPF_JSGT: ++ emit(ctx, slti, MIPS_R_T9, dst, imm + 1); ++ emit(ctx, beqz, MIPS_R_T9, off); ++ break; ++ /* PC += off if dst >= imm (signed) */ ++ case BPF_JSGE: ++ emit(ctx, slti, MIPS_R_T9, dst, imm); ++ emit(ctx, beqz, MIPS_R_T9, off); ++ break; ++ /* PC += off if dst < imm (signed) */ ++ case BPF_JSLT: ++ emit(ctx, slti, MIPS_R_T9, dst, imm); ++ emit(ctx, bnez, MIPS_R_T9, off); ++ break; ++ /* PC += off if dst <= imm (signed) */ ++ case BPF_JSLE: ++ emit(ctx, slti, MIPS_R_T9, dst, imm + 1); ++ emit(ctx, bnez, MIPS_R_T9, off); ++ break; ++ } ++} ++ ++/* Jump register (32-bit) */ ++void emit_jmp_r(struct jit_context *ctx, u8 dst, u8 src, s32 off, u8 op) ++{ ++ switch (op) { ++ /* No-op, used internally for branch optimization */ ++ case JIT_JNOP: ++ break; ++ /* PC += off if dst == src */ ++ case BPF_JEQ: ++ emit(ctx, beq, dst, src, off); ++ break; ++ /* PC += off if dst != src */ ++ case BPF_JNE: ++ emit(ctx, bne, dst, src, off); ++ break; ++ /* PC += off if dst & src */ ++ case BPF_JSET: ++ emit(ctx, and, MIPS_R_T9, dst, src); ++ emit(ctx, bnez, MIPS_R_T9, off); ++ break; ++ /* PC += off if (dst & imm) == 0 (not in BPF, used for long jumps) */ ++ case JIT_JNSET: ++ emit(ctx, and, MIPS_R_T9, dst, src); ++ emit(ctx, beqz, MIPS_R_T9, off); ++ break; ++ /* PC += off if dst > src */ ++ case BPF_JGT: ++ emit(ctx, sltu, MIPS_R_T9, src, dst); ++ emit(ctx, bnez, MIPS_R_T9, off); ++ break; ++ /* PC += off if dst >= src */ ++ case BPF_JGE: ++ emit(ctx, sltu, MIPS_R_T9, dst, src); ++ emit(ctx, beqz, MIPS_R_T9, off); ++ break; ++ /* PC += off if dst < src */ ++ case BPF_JLT: ++ emit(ctx, sltu, MIPS_R_T9, dst, src); ++ emit(ctx, bnez, MIPS_R_T9, off); ++ break; ++ /* PC += off if dst <= src */ ++ case BPF_JLE: ++ emit(ctx, sltu, MIPS_R_T9, src, dst); ++ emit(ctx, beqz, MIPS_R_T9, off); ++ break; ++ /* PC += off if dst > src (signed) */ ++ case BPF_JSGT: ++ emit(ctx, slt, MIPS_R_T9, src, dst); ++ emit(ctx, bnez, MIPS_R_T9, off); ++ break; ++ /* PC += off if dst >= src (signed) */ ++ case BPF_JSGE: ++ emit(ctx, slt, MIPS_R_T9, dst, src); ++ emit(ctx, beqz, MIPS_R_T9, off); ++ break; ++ /* PC += off if dst < src (signed) */ ++ case BPF_JSLT: ++ emit(ctx, slt, MIPS_R_T9, dst, src); ++ emit(ctx, bnez, MIPS_R_T9, off); ++ break; ++ /* PC += off if dst <= src (signed) */ ++ case BPF_JSLE: ++ emit(ctx, slt, MIPS_R_T9, src, dst); ++ emit(ctx, beqz, MIPS_R_T9, off); ++ break; ++ } ++} ++ ++/* Jump always */ ++int emit_ja(struct jit_context *ctx, s16 off) ++{ ++ int target = get_target(ctx, ctx->bpf_index + off + 1); ++ ++ if (target < 0) ++ return -1; ++ emit(ctx, j, target); ++ emit(ctx, nop); ++ return 0; ++} ++ ++/* Jump to epilogue */ ++int emit_exit(struct jit_context *ctx) ++{ ++ int target = get_target(ctx, ctx->program->len); ++ ++ if (target < 0) ++ return -1; ++ emit(ctx, j, target); ++ emit(ctx, nop); ++ return 0; ++} ++ ++/* Build the program body from eBPF bytecode */ ++static int build_body(struct jit_context *ctx) ++{ ++ const struct bpf_prog *prog = ctx->program; ++ unsigned int i; ++ ++ ctx->stack_used = 0; ++ for (i = 0; i < prog->len; i++) { ++ const struct bpf_insn *insn = &prog->insnsi[i]; ++ u32 *descp = &ctx->descriptors[i]; ++ int ret; ++ ++ access_reg(ctx, insn->src_reg); ++ access_reg(ctx, insn->dst_reg); ++ ++ ctx->bpf_index = i; ++ if (ctx->target == NULL) { ++ ctx->changes += INDEX(*descp) != ctx->jit_index; ++ *descp &= JIT_DESC_CONVERT; ++ *descp |= ctx->jit_index; ++ } ++ ++ ret = build_insn(insn, ctx); ++ if (ret < 0) ++ return ret; ++ ++ if (ret > 0) { ++ i++; ++ if (ctx->target == NULL) ++ descp[1] = ctx->jit_index; ++ } ++ } ++ ++ /* Store the end offset, where the epilogue begins */ ++ ctx->descriptors[prog->len] = ctx->jit_index; ++ return 0; ++} ++ ++/* Set the branch conversion flag on all instructions */ ++static void set_convert_flag(struct jit_context *ctx, bool enable) ++{ ++ const struct bpf_prog *prog = ctx->program; ++ u32 flag = enable ? JIT_DESC_CONVERT : 0; ++ unsigned int i; ++ ++ for (i = 0; i <= prog->len; i++) ++ ctx->descriptors[i] = INDEX(ctx->descriptors[i]) | flag; ++} ++ ++static void jit_fill_hole(void *area, unsigned int size) ++{ ++ u32 *p; ++ ++ /* We are guaranteed to have aligned memory. */ ++ for (p = area; size >= sizeof(u32); size -= sizeof(u32)) ++ uasm_i_break(&p, BRK_BUG); /* Increments p */ ++} ++ ++bool bpf_jit_needs_zext(void) ++{ ++ return true; ++} ++ ++struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *prog) ++{ ++ struct bpf_prog *tmp, *orig_prog = prog; ++ struct bpf_binary_header *header = NULL; ++ struct jit_context ctx; ++ bool tmp_blinded = false; ++ unsigned int tmp_idx; ++ unsigned int image_size; ++ u8 *image_ptr; ++ int tries; ++ ++ /* ++ * If BPF JIT was not enabled then we must fall back to ++ * the interpreter. ++ */ ++ if (!prog->jit_requested) ++ return orig_prog; ++ /* ++ * If constant blinding was enabled and we failed during blinding ++ * then we must fall back to the interpreter. Otherwise, we save ++ * the new JITed code. ++ */ ++ tmp = bpf_jit_blind_constants(prog); ++ if (IS_ERR(tmp)) ++ return orig_prog; ++ if (tmp != prog) { ++ tmp_blinded = true; ++ prog = tmp; ++ } ++ ++ memset(&ctx, 0, sizeof(ctx)); ++ ctx.program = prog; ++ ++ /* ++ * Not able to allocate memory for descriptors[], then ++ * we must fall back to the interpreter ++ */ ++ ctx.descriptors = kcalloc(prog->len + 1, sizeof(*ctx.descriptors), ++ GFP_KERNEL); ++ if (ctx.descriptors == NULL) ++ goto out_err; ++ ++ /* First pass discovers used resources */ ++ if (build_body(&ctx) < 0) ++ goto out_err; ++ /* ++ * Second pass computes instruction offsets. ++ * If any PC-relative branches are out of range, a sequence of ++ * a PC-relative branch + a jump is generated, and we have to ++ * try again from the beginning to generate the new offsets. ++ * This is done until no additional conversions are necessary. ++ * The last two iterations are done with all branches being ++ * converted, to guarantee offset table convergence within a ++ * fixed number of iterations. ++ */ ++ ctx.jit_index = 0; ++ build_prologue(&ctx); ++ tmp_idx = ctx.jit_index; ++ ++ tries = JIT_MAX_ITERATIONS; ++ do { ++ ctx.jit_index = tmp_idx; ++ ctx.changes = 0; ++ if (tries == 2) ++ set_convert_flag(&ctx, true); ++ if (build_body(&ctx) < 0) ++ goto out_err; ++ } while (ctx.changes > 0 && --tries > 0); ++ ++ if (WARN_ONCE(ctx.changes > 0, "JIT offsets failed to converge")) ++ goto out_err; ++ ++ build_epilogue(&ctx, MIPS_R_RA); ++ ++ /* Now we know the size of the structure to make */ ++ image_size = sizeof(u32) * ctx.jit_index; ++ header = bpf_jit_binary_alloc(image_size, &image_ptr, ++ sizeof(u32), jit_fill_hole); ++ /* ++ * Not able to allocate memory for the structure then ++ * we must fall back to the interpretation ++ */ ++ if (header == NULL) ++ goto out_err; ++ ++ /* Actual pass to generate final JIT code */ ++ ctx.target = (u32 *)image_ptr; ++ ctx.jit_index = 0; ++ ++ /* ++ * If building the JITed code fails somehow, ++ * we fall back to the interpretation. ++ */ ++ build_prologue(&ctx); ++ if (build_body(&ctx) < 0) ++ goto out_err; ++ build_epilogue(&ctx, MIPS_R_RA); ++ ++ /* Populate line info meta data */ ++ set_convert_flag(&ctx, false); ++ bpf_prog_fill_jited_linfo(prog, &ctx.descriptors[1]); ++ ++ /* Set as read-only exec and flush instruction cache */ ++ bpf_jit_binary_lock_ro(header); ++ flush_icache_range((unsigned long)header, ++ (unsigned long)&ctx.target[ctx.jit_index]); ++ ++ if (bpf_jit_enable > 1) ++ bpf_jit_dump(prog->len, image_size, 2, ctx.target); ++ ++ prog->bpf_func = (void *)ctx.target; ++ prog->jited = 1; ++ prog->jited_len = image_size; ++ ++out: ++ if (tmp_blinded) ++ bpf_jit_prog_release_other(prog, prog == orig_prog ? ++ tmp : orig_prog); ++ kfree(ctx.descriptors); ++ return prog; ++ ++out_err: ++ prog = orig_prog; ++ if (header) ++ bpf_jit_binary_free(header); ++ goto out; ++} +--- /dev/null ++++ b/arch/mips/net/bpf_jit_comp.h +@@ -0,0 +1,211 @@ ++/* SPDX-License-Identifier: GPL-2.0-only */ ++/* ++ * Just-In-Time compiler for eBPF bytecode on 32-bit and 64-bit MIPS. ++ * ++ * Copyright (c) 2021 Anyfi Networks AB. ++ * Author: Johan Almbladh <johan.almbladh@gmail.com> ++ * ++ * Based on code and ideas from ++ * Copyright (c) 2017 Cavium, Inc. ++ * Copyright (c) 2017 Shubham Bansal <illusionist.neo@gmail.com> ++ * Copyright (c) 2011 Mircea Gherzan <mgherzan@gmail.com> ++ */ ++ ++#ifndef _BPF_JIT_COMP_H ++#define _BPF_JIT_COMP_H ++ ++/* MIPS registers */ ++#define MIPS_R_ZERO 0 /* Const zero */ ++#define MIPS_R_AT 1 /* Asm temp */ ++#define MIPS_R_V0 2 /* Result */ ++#define MIPS_R_V1 3 /* Result */ ++#define MIPS_R_A0 4 /* Argument */ ++#define MIPS_R_A1 5 /* Argument */ ++#define MIPS_R_A2 6 /* Argument */ ++#define MIPS_R_A3 7 /* Argument */ ++#define MIPS_R_A4 8 /* Arg (n64) */ ++#define MIPS_R_A5 9 /* Arg (n64) */ ++#define MIPS_R_A6 10 /* Arg (n64) */ ++#define MIPS_R_A7 11 /* Arg (n64) */ ++#define MIPS_R_T0 8 /* Temp (o32) */ ++#define MIPS_R_T1 9 /* Temp (o32) */ ++#define MIPS_R_T2 10 /* Temp (o32) */ ++#define MIPS_R_T3 11 /* Temp (o32) */ ++#define MIPS_R_T4 12 /* Temporary */ ++#define MIPS_R_T5 13 /* Temporary */ ++#define MIPS_R_T6 14 /* Temporary */ ++#define MIPS_R_T7 15 /* Temporary */ ++#define MIPS_R_S0 16 /* Saved */ ++#define MIPS_R_S1 17 /* Saved */ ++#define MIPS_R_S2 18 /* Saved */ ++#define MIPS_R_S3 19 /* Saved */ ++#define MIPS_R_S4 20 /* Saved */ ++#define MIPS_R_S5 21 /* Saved */ ++#define MIPS_R_S6 22 /* Saved */ ++#define MIPS_R_S7 23 /* Saved */ ++#define MIPS_R_T8 24 /* Temporary */ ++#define MIPS_R_T9 25 /* Temporary */ ++/* MIPS_R_K0 26 Reserved */ ++/* MIPS_R_K1 27 Reserved */ ++#define MIPS_R_GP 28 /* Global ptr */ ++#define MIPS_R_SP 29 /* Stack ptr */ ++#define MIPS_R_FP 30 /* Frame ptr */ ++#define MIPS_R_RA 31 /* Return */ ++ ++/* ++ * Jump address mask for immediate jumps. The four most significant bits ++ * must be equal to PC. ++ */ ++#define MIPS_JMP_MASK 0x0fffffffUL ++ ++/* Maximum number of iterations in offset table computation */ ++#define JIT_MAX_ITERATIONS 8 ++ ++/* ++ * Jump pseudo-instructions used internally ++ * for branch conversion and branch optimization. ++ */ ++#define JIT_JNSET 0xe0 ++#define JIT_JNOP 0xf0 ++ ++/* Descriptor flag for PC-relative branch conversion */ ++#define JIT_DESC_CONVERT BIT(31) ++ ++/* JIT context for an eBPF program */ ++struct jit_context { ++ struct bpf_prog *program; /* The eBPF program being JITed */ ++ u32 *descriptors; /* eBPF to JITed CPU insn descriptors */ ++ u32 *target; /* JITed code buffer */ ++ u32 bpf_index; /* Index of current BPF program insn */ ++ u32 jit_index; /* Index of current JIT target insn */ ++ u32 changes; /* Number of PC-relative branch conv */ ++ u32 accessed; /* Bit mask of read eBPF registers */ ++ u32 clobbered; /* Bit mask of modified CPU registers */ ++ u32 stack_size; /* Total allocated stack size in bytes */ ++ u32 saved_size; /* Size of callee-saved registers */ ++ u32 stack_used; /* Stack size used for function calls */ ++}; ++ ++/* Emit the instruction if the JIT memory space has been allocated */ ++#define emit(ctx, func, ...) \ ++do { \ ++ if ((ctx)->target != NULL) { \ ++ u32 *p = &(ctx)->target[ctx->jit_index]; \ ++ uasm_i_##func(&p, ##__VA_ARGS__); \ ++ } \ ++ (ctx)->jit_index++; \ ++} while (0) ++ ++/* ++ * Mark a BPF register as accessed, it needs to be ++ * initialized by the program if expected, e.g. FP. ++ */ ++static inline void access_reg(struct jit_context *ctx, u8 reg) ++{ ++ ctx->accessed |= BIT(reg); ++} ++ ++/* ++ * Mark a CPU register as clobbered, it needs to be ++ * saved/restored by the program if callee-saved. ++ */ ++static inline void clobber_reg(struct jit_context *ctx, u8 reg) ++{ ++ ctx->clobbered |= BIT(reg); ++} ++ ++/* ++ * Push registers on the stack, starting at a given depth from the stack ++ * pointer and increasing. The next depth to be written is returned. ++ */ ++int push_regs(struct jit_context *ctx, u32 mask, u32 excl, int depth); ++ ++/* ++ * Pop registers from the stack, starting at a given depth from the stack ++ * pointer and increasing. The next depth to be read is returned. ++ */ ++int pop_regs(struct jit_context *ctx, u32 mask, u32 excl, int depth); ++ ++/* Compute the 28-bit jump target address from a BPF program location */ ++int get_target(struct jit_context *ctx, u32 loc); ++ ++/* Compute the PC-relative offset to relative BPF program offset */ ++int get_offset(const struct jit_context *ctx, int off); ++ ++/* dst = imm (32-bit) */ ++void emit_mov_i(struct jit_context *ctx, u8 dst, s32 imm); ++ ++/* dst = src (32-bit) */ ++void emit_mov_r(struct jit_context *ctx, u8 dst, u8 src); ++ ++/* Validate ALU/ALU64 immediate range */ ++bool valid_alu_i(u8 op, s32 imm); ++ ++/* Rewrite ALU/ALU64 immediate operation */ ++bool rewrite_alu_i(u8 op, s32 imm, u8 *alu, s32 *val); ++ ++/* ALU immediate operation (32-bit) */ ++void emit_alu_i(struct jit_context *ctx, u8 dst, s32 imm, u8 op); ++ ++/* ALU register operation (32-bit) */ ++void emit_alu_r(struct jit_context *ctx, u8 dst, u8 src, u8 op); ++ ++/* Atomic read-modify-write (32-bit) */ ++void emit_atomic_r(struct jit_context *ctx, u8 dst, u8 src, s16 off, u8 code); ++ ++/* Atomic compare-and-exchange (32-bit) */ ++void emit_cmpxchg_r(struct jit_context *ctx, u8 dst, u8 src, u8 res, s16 off); ++ ++/* Swap bytes and truncate a register word or half word */ ++void emit_bswap_r(struct jit_context *ctx, u8 dst, u32 width); ++ ++/* Validate JMP/JMP32 immediate range */ ++bool valid_jmp_i(u8 op, s32 imm); ++ ++/* Prepare a PC-relative jump operation with immediate conditional */ ++void setup_jmp_i(struct jit_context *ctx, s32 imm, u8 width, ++ u8 bpf_op, s16 bpf_off, u8 *jit_op, s32 *jit_off); ++ ++/* Prepare a PC-relative jump operation with register conditional */ ++void setup_jmp_r(struct jit_context *ctx, bool same_reg, ++ u8 bpf_op, s16 bpf_off, u8 *jit_op, s32 *jit_off); ++ ++/* Finish a PC-relative jump operation */ ++int finish_jmp(struct jit_context *ctx, u8 jit_op, s16 bpf_off); ++ ++/* Conditional JMP/JMP32 immediate */ ++void emit_jmp_i(struct jit_context *ctx, u8 dst, s32 imm, s32 off, u8 op); ++ ++/* Conditional JMP/JMP32 register */ ++void emit_jmp_r(struct jit_context *ctx, u8 dst, u8 src, s32 off, u8 op); ++ ++/* Jump always */ ++int emit_ja(struct jit_context *ctx, s16 off); ++ ++/* Jump to epilogue */ ++int emit_exit(struct jit_context *ctx); ++ ++/* ++ * Build program prologue to set up the stack and registers. ++ * This function is implemented separately for 32-bit and 64-bit JITs. ++ */ ++void build_prologue(struct jit_context *ctx); ++ ++/* ++ * Build the program epilogue to restore the stack and registers. ++ * This function is implemented separately for 32-bit and 64-bit JITs. ++ */ ++void build_epilogue(struct jit_context *ctx, int dest_reg); ++ ++/* ++ * Convert an eBPF instruction to native instruction, i.e ++ * JITs an eBPF instruction. ++ * Returns : ++ * 0 - Successfully JITed an 8-byte eBPF instruction ++ * >0 - Successfully JITed a 16-byte eBPF instruction ++ * <0 - Failed to JIT. ++ * This function is implemented separately for 32-bit and 64-bit JITs. ++ */ ++int build_insn(const struct bpf_insn *insn, struct jit_context *ctx); ++ ++#endif /* _BPF_JIT_COMP_H */ +--- /dev/null ++++ b/arch/mips/net/bpf_jit_comp32.c +@@ -0,0 +1,1741 @@ ++// SPDX-License-Identifier: GPL-2.0-only ++/* ++ * Just-In-Time compiler for eBPF bytecode on MIPS. ++ * Implementation of JIT functions for 32-bit CPUs. ++ * ++ * Copyright (c) 2021 Anyfi Networks AB. ++ * Author: Johan Almbladh <johan.almbladh@gmail.com> ++ * ++ * Based on code and ideas from ++ * Copyright (c) 2017 Cavium, Inc. ++ * Copyright (c) 2017 Shubham Bansal <illusionist.neo@gmail.com> ++ * Copyright (c) 2011 Mircea Gherzan <mgherzan@gmail.com> ++ */ ++ ++#include <linux/math64.h> ++#include <linux/errno.h> ++#include <linux/filter.h> ++#include <linux/bpf.h> ++#include <asm/cpu-features.h> ++#include <asm/isa-rev.h> ++#include <asm/uasm.h> ++ ++#include "bpf_jit_comp.h" ++ ++/* MIPS a4-a7 are not available in the o32 ABI */ ++#undef MIPS_R_A4 ++#undef MIPS_R_A5 ++#undef MIPS_R_A6 ++#undef MIPS_R_A7 ++ ++/* Stack is 8-byte aligned in o32 ABI */ ++#define MIPS_STACK_ALIGNMENT 8 ++ ++/* ++ * The top 16 bytes of a stack frame is reserved for the callee in O32 ABI. ++ * This corresponds to stack space for register arguments a0-a3. ++ */ ++#define JIT_RESERVED_STACK 16 ++ ++/* Temporary 64-bit register used by JIT */ ++#define JIT_REG_TMP MAX_BPF_JIT_REG ++ ++/* ++ * Number of prologue bytes to skip when doing a tail call. ++ * Tail call count (TCC) initialization (8 bytes) always, plus ++ * R0-to-v0 assignment (4 bytes) if big endian. ++ */ ++#ifdef __BIG_ENDIAN ++#define JIT_TCALL_SKIP 12 ++#else ++#define JIT_TCALL_SKIP 8 ++#endif ++ ++/* CPU registers holding the callee return value */ ++#define JIT_RETURN_REGS \ ++ (BIT(MIPS_R_V0) | \ ++ BIT(MIPS_R_V1)) ++ ++/* CPU registers arguments passed to callee directly */ ++#define JIT_ARG_REGS \ ++ (BIT(MIPS_R_A0) | \ ++ BIT(MIPS_R_A1) | \ ++ BIT(MIPS_R_A2) | \ ++ BIT(MIPS_R_A3)) ++ ++/* CPU register arguments passed to callee on stack */ ++#define JIT_STACK_REGS \ ++ (BIT(MIPS_R_T0) | \ ++ BIT(MIPS_R_T1) | \ ++ BIT(MIPS_R_T2) | \ ++ BIT(MIPS_R_T3) | \ ++ BIT(MIPS_R_T4) | \ ++ BIT(MIPS_R_T5)) ++ ++/* Caller-saved CPU registers */ ++#define JIT_CALLER_REGS \ ++ (JIT_RETURN_REGS | \ ++ JIT_ARG_REGS | \ ++ JIT_STACK_REGS) ++ ++/* Callee-saved CPU registers */ ++#define JIT_CALLEE_REGS \ ++ (BIT(MIPS_R_S0) | \ ++ BIT(MIPS_R_S1) | \ ++ BIT(MIPS_R_S2) | \ ++ BIT(MIPS_R_S3) | \ ++ BIT(MIPS_R_S4) | \ ++ BIT(MIPS_R_S5) | \ ++ BIT(MIPS_R_S6) | \ ++ BIT(MIPS_R_S7) | \ ++ BIT(MIPS_R_GP) | \ ++ BIT(MIPS_R_FP) | \ ++ BIT(MIPS_R_RA)) ++ ++/* ++ * Mapping of 64-bit eBPF registers to 32-bit native MIPS registers. ++ * ++ * 1) Native register pairs are ordered according to CPU endiannes, following ++ * the MIPS convention for passing 64-bit arguments and return values. ++ * 2) The eBPF return value, arguments and callee-saved registers are mapped ++ * to their native MIPS equivalents. ++ * 3) Since the 32 highest bits in the eBPF FP register are always zero, ++ * only one general-purpose register is actually needed for the mapping. ++ * We use the fp register for this purpose, and map the highest bits to ++ * the MIPS register r0 (zero). ++ * 4) We use the MIPS gp and at registers as internal temporary registers ++ * for constant blinding. The gp register is callee-saved. ++ * 5) One 64-bit temporary register is mapped for use when sign-extending ++ * immediate operands. MIPS registers t6-t9 are available to the JIT ++ * for as temporaries when implementing complex 64-bit operations. ++ * ++ * With this scheme all eBPF registers are being mapped to native MIPS ++ * registers without having to use any stack scratch space. The direct ++ * register mapping (2) simplifies the handling of function calls. ++ */ ++static const u8 bpf2mips32[][2] = { ++ /* Return value from in-kernel function, and exit value from eBPF */ ++ [BPF_REG_0] = {MIPS_R_V1, MIPS_R_V0}, ++ /* Arguments from eBPF program to in-kernel function */ ++ [BPF_REG_1] = {MIPS_R_A1, MIPS_R_A0}, ++ [BPF_REG_2] = {MIPS_R_A3, MIPS_R_A2}, ++ /* Remaining arguments, to be passed on the stack per O32 ABI */ ++ [BPF_REG_3] = {MIPS_R_T1, MIPS_R_T0}, ++ [BPF_REG_4] = {MIPS_R_T3, MIPS_R_T2}, ++ [BPF_REG_5] = {MIPS_R_T5, MIPS_R_T4}, ++ /* Callee-saved registers that in-kernel function will preserve */ ++ [BPF_REG_6] = {MIPS_R_S1, MIPS_R_S0}, ++ [BPF_REG_7] = {MIPS_R_S3, MIPS_R_S2}, ++ [BPF_REG_8] = {MIPS_R_S5, MIPS_R_S4}, ++ [BPF_REG_9] = {MIPS_R_S7, MIPS_R_S6}, ++ /* Read-only frame pointer to access the eBPF stack */ ++#ifdef __BIG_ENDIAN ++ [BPF_REG_FP] = {MIPS_R_FP, MIPS_R_ZERO}, ++#else ++ [BPF_REG_FP] = {MIPS_R_ZERO, MIPS_R_FP}, ++#endif ++ /* Temporary register for blinding constants */ ++ [BPF_REG_AX] = {MIPS_R_GP, MIPS_R_AT}, ++ /* Temporary register for internal JIT use */ ++ [JIT_REG_TMP] = {MIPS_R_T7, MIPS_R_T6}, ++}; ++ ++/* Get low CPU register for a 64-bit eBPF register mapping */ ++static inline u8 lo(const u8 reg[]) ++{ ++#ifdef __BIG_ENDIAN ++ return reg[0]; ++#else ++ return reg[1]; ++#endif ++} ++ ++/* Get high CPU register for a 64-bit eBPF register mapping */ ++static inline u8 hi(const u8 reg[]) ++{ ++#ifdef __BIG_ENDIAN ++ return reg[1]; ++#else ++ return reg[0]; ++#endif ++} ++ ++/* ++ * Mark a 64-bit CPU register pair as clobbered, it needs to be ++ * saved/restored by the program if callee-saved. ++ */ ++static void clobber_reg64(struct jit_context *ctx, const u8 reg[]) ++{ ++ clobber_reg(ctx, reg[0]); ++ clobber_reg(ctx, reg[1]); ++} ++ ++/* dst = imm (sign-extended) */ ++static void emit_mov_se_i64(struct jit_context *ctx, const u8 dst[], s32 imm) ++{ ++ emit_mov_i(ctx, lo(dst), imm); ++ if (imm < 0) ++ emit(ctx, addiu, hi(dst), MIPS_R_ZERO, -1); ++ else ++ emit(ctx, move, hi(dst), MIPS_R_ZERO); ++ clobber_reg64(ctx, dst); ++} ++ ++/* Zero extension, if verifier does not do it for us */ ++static void emit_zext_ver(struct jit_context *ctx, const u8 dst[]) ++{ ++ if (!ctx->program->aux->verifier_zext) { ++ emit(ctx, move, hi(dst), MIPS_R_ZERO); ++ clobber_reg(ctx, hi(dst)); ++ } ++} ++ ++/* Load delay slot, if ISA mandates it */ ++static void emit_load_delay(struct jit_context *ctx) ++{ ++ if (!cpu_has_mips_2_3_4_5_r) ++ emit(ctx, nop); ++} ++ ++/* ALU immediate operation (64-bit) */ ++static void emit_alu_i64(struct jit_context *ctx, ++ const u8 dst[], s32 imm, u8 op) ++{ ++ u8 src = MIPS_R_T6; ++ ++ /* ++ * ADD/SUB with all but the max negative imm can be handled by ++ * inverting the operation and the imm value, saving one insn. ++ */ ++ if (imm > S32_MIN && imm < 0) ++ switch (op) { ++ case BPF_ADD: ++ op = BPF_SUB; ++ imm = -imm; ++ break; ++ case BPF_SUB: ++ op = BPF_ADD; ++ imm = -imm; ++ break; ++ } ++ ++ /* Move immediate to temporary register */ ++ emit_mov_i(ctx, src, imm); ++ ++ switch (op) { ++ /* dst = dst + imm */ ++ case BPF_ADD: ++ emit(ctx, addu, lo(dst), lo(dst), src); ++ emit(ctx, sltu, MIPS_R_T9, lo(dst), src); ++ emit(ctx, addu, hi(dst), hi(dst), MIPS_R_T9); ++ if (imm < 0) ++ emit(ctx, addiu, hi(dst), hi(dst), -1); ++ break; ++ /* dst = dst - imm */ ++ case BPF_SUB: ++ emit(ctx, sltu, MIPS_R_T9, lo(dst), src); ++ emit(ctx, subu, lo(dst), lo(dst), src); ++ emit(ctx, subu, hi(dst), hi(dst), MIPS_R_T9); ++ if (imm < 0) ++ emit(ctx, addiu, hi(dst), hi(dst), 1); ++ break; ++ /* dst = dst | imm */ ++ case BPF_OR: ++ emit(ctx, or, lo(dst), lo(dst), src); ++ if (imm < 0) ++ emit(ctx, addiu, hi(dst), MIPS_R_ZERO, -1); ++ break; ++ /* dst = dst & imm */ ++ case BPF_AND: ++ emit(ctx, and, lo(dst), lo(dst), src); ++ if (imm >= 0) ++ emit(ctx, move, hi(dst), MIPS_R_ZERO); ++ break; ++ /* dst = dst ^ imm */ ++ case BPF_XOR: ++ emit(ctx, xor, lo(dst), lo(dst), src); ++ if (imm < 0) { ++ emit(ctx, subu, hi(dst), MIPS_R_ZERO, hi(dst)); ++ emit(ctx, addiu, hi(dst), hi(dst), -1); ++ } ++ break; ++ } ++ clobber_reg64(ctx, dst); ++} ++ ++/* ALU register operation (64-bit) */ ++static void emit_alu_r64(struct jit_context *ctx, ++ const u8 dst[], const u8 src[], u8 op) ++{ ++ switch (BPF_OP(op)) { ++ /* dst = dst + src */ ++ case BPF_ADD: ++ if (src == dst) { ++ emit(ctx, srl, MIPS_R_T9, lo(dst), 31); ++ emit(ctx, addu, lo(dst), lo(dst), lo(dst)); ++ } else { ++ emit(ctx, addu, lo(dst), lo(dst), lo(src)); ++ emit(ctx, sltu, MIPS_R_T9, lo(dst), lo(src)); ++ } ++ emit(ctx, addu, hi(dst), hi(dst), hi(src)); ++ emit(ctx, addu, hi(dst), hi(dst), MIPS_R_T9); ++ break; ++ /* dst = dst - src */ ++ case BPF_SUB: ++ emit(ctx, sltu, MIPS_R_T9, lo(dst), lo(src)); ++ emit(ctx, subu, lo(dst), lo(dst), lo(src)); ++ emit(ctx, subu, hi(dst), hi(dst), hi(src)); ++ emit(ctx, subu, hi(dst), hi(dst), MIPS_R_T9); ++ break; ++ /* dst = dst | src */ ++ case BPF_OR: ++ emit(ctx, or, lo(dst), lo(dst), lo(src)); ++ emit(ctx, or, hi(dst), hi(dst), hi(src)); ++ break; ++ /* dst = dst & src */ ++ case BPF_AND: ++ emit(ctx, and, lo(dst), lo(dst), lo(src)); ++ emit(ctx, and, hi(dst), hi(dst), hi(src)); ++ break; ++ /* dst = dst ^ src */ ++ case BPF_XOR: ++ emit(ctx, xor, lo(dst), lo(dst), lo(src)); ++ emit(ctx, xor, hi(dst), hi(dst), hi(src)); ++ break; ++ } ++ clobber_reg64(ctx, dst); ++} ++ ++/* ALU invert (64-bit) */ ++static void emit_neg_i64(struct jit_context *ctx, const u8 dst[]) ++{ ++ emit(ctx, sltu, MIPS_R_T9, MIPS_R_ZERO, lo(dst)); ++ emit(ctx, subu, lo(dst), MIPS_R_ZERO, lo(dst)); ++ emit(ctx, subu, hi(dst), MIPS_R_ZERO, hi(dst)); ++ emit(ctx, subu, hi(dst), hi(dst), MIPS_R_T9); ++ ++ clobber_reg64(ctx, dst); ++} ++ ++/* ALU shift immediate (64-bit) */ ++static void emit_shift_i64(struct jit_context *ctx, ++ const u8 dst[], u32 imm, u8 op) ++{ ++ switch (BPF_OP(op)) { ++ /* dst = dst << imm */ ++ case BPF_LSH: ++ if (imm < 32) { ++ emit(ctx, srl, MIPS_R_T9, lo(dst), 32 - imm); ++ emit(ctx, sll, lo(dst), lo(dst), imm); ++ emit(ctx, sll, hi(dst), hi(dst), imm); ++ emit(ctx, or, hi(dst), hi(dst), MIPS_R_T9); ++ } else { ++ emit(ctx, sll, hi(dst), lo(dst), imm - 32); ++ emit(ctx, move, lo(dst), MIPS_R_ZERO); ++ } ++ break; ++ /* dst = dst >> imm */ ++ case BPF_RSH: ++ if (imm < 32) { ++ emit(ctx, sll, MIPS_R_T9, hi(dst), 32 - imm); ++ emit(ctx, srl, lo(dst), lo(dst), imm); ++ emit(ctx, srl, hi(dst), hi(dst), imm); ++ emit(ctx, or, lo(dst), lo(dst), MIPS_R_T9); ++ } else { ++ emit(ctx, srl, lo(dst), hi(dst), imm - 32); ++ emit(ctx, move, hi(dst), MIPS_R_ZERO); ++ } ++ break; ++ /* dst = dst >> imm (arithmetic) */ ++ case BPF_ARSH: ++ if (imm < 32) { ++ emit(ctx, sll, MIPS_R_T9, hi(dst), 32 - imm); ++ emit(ctx, srl, lo(dst), lo(dst), imm); ++ emit(ctx, sra, hi(dst), hi(dst), imm); ++ emit(ctx, or, lo(dst), lo(dst), MIPS_R_T9); ++ } else { ++ emit(ctx, sra, lo(dst), hi(dst), imm - 32); ++ emit(ctx, sra, hi(dst), hi(dst), 31); ++ } ++ break; ++ } ++ clobber_reg64(ctx, dst); ++} ++ ++/* ALU shift register (64-bit) */ ++static void emit_shift_r64(struct jit_context *ctx, ++ const u8 dst[], u8 src, u8 op) ++{ ++ u8 t1 = MIPS_R_T8; ++ u8 t2 = MIPS_R_T9; ++ ++ emit(ctx, andi, t1, src, 32); /* t1 = src & 32 */ ++ emit(ctx, beqz, t1, 16); /* PC += 16 if t1 == 0 */ ++ emit(ctx, nor, t2, src, MIPS_R_ZERO); /* t2 = ~src (delay slot) */ ++ ++ switch (BPF_OP(op)) { ++ /* dst = dst << src */ ++ case BPF_LSH: ++ /* Next: shift >= 32 */ ++ emit(ctx, sllv, hi(dst), lo(dst), src); /* dh = dl << src */ ++ emit(ctx, move, lo(dst), MIPS_R_ZERO); /* dl = 0 */ ++ emit(ctx, b, 20); /* PC += 20 */ ++ /* +16: shift < 32 */ ++ emit(ctx, srl, t1, lo(dst), 1); /* t1 = dl >> 1 */ ++ emit(ctx, srlv, t1, t1, t2); /* t1 = t1 >> t2 */ ++ emit(ctx, sllv, lo(dst), lo(dst), src); /* dl = dl << src */ ++ emit(ctx, sllv, hi(dst), hi(dst), src); /* dh = dh << src */ ++ emit(ctx, or, hi(dst), hi(dst), t1); /* dh = dh | t1 */ ++ break; ++ /* dst = dst >> src */ ++ case BPF_RSH: ++ /* Next: shift >= 32 */ ++ emit(ctx, srlv, lo(dst), hi(dst), src); /* dl = dh >> src */ ++ emit(ctx, move, hi(dst), MIPS_R_ZERO); /* dh = 0 */ ++ emit(ctx, b, 20); /* PC += 20 */ ++ /* +16: shift < 32 */ ++ emit(ctx, sll, t1, hi(dst), 1); /* t1 = dl << 1 */ ++ emit(ctx, sllv, t1, t1, t2); /* t1 = t1 << t2 */ ++ emit(ctx, srlv, lo(dst), lo(dst), src); /* dl = dl >> src */ ++ emit(ctx, srlv, hi(dst), hi(dst), src); /* dh = dh >> src */ ++ emit(ctx, or, lo(dst), lo(dst), t1); /* dl = dl | t1 */ ++ break; ++ /* dst = dst >> src (arithmetic) */ ++ case BPF_ARSH: ++ /* Next: shift >= 32 */ ++ emit(ctx, srav, lo(dst), hi(dst), src); /* dl = dh >>a src */ ++ emit(ctx, sra, hi(dst), hi(dst), 31); /* dh = dh >>a 31 */ ++ emit(ctx, b, 20); /* PC += 20 */ ++ /* +16: shift < 32 */ ++ emit(ctx, sll, t1, hi(dst), 1); /* t1 = dl << 1 */ ++ emit(ctx, sllv, t1, t1, t2); /* t1 = t1 << t2 */ ++ emit(ctx, srlv, lo(dst), lo(dst), src); /* dl = dl >>a src */ ++ emit(ctx, srav, hi(dst), hi(dst), src); /* dh = dh >> src */ ++ emit(ctx, or, lo(dst), lo(dst), t1); /* dl = dl | t1 */ ++ break; ++ } ++ ++ /* +20: Done */ ++ clobber_reg64(ctx, dst); ++} ++ ++/* ALU mul immediate (64x32-bit) */ ++static void emit_mul_i64(struct jit_context *ctx, const u8 dst[], s32 imm) ++{ ++ u8 src = MIPS_R_T6; ++ u8 tmp = MIPS_R_T9; ++ ++ switch (imm) { ++ /* dst = dst * 1 is a no-op */ ++ case 1: ++ break; ++ /* dst = dst * -1 */ ++ case -1: ++ emit_neg_i64(ctx, dst); ++ break; ++ case 0: ++ emit_mov_r(ctx, lo(dst), MIPS_R_ZERO); ++ emit_mov_r(ctx, hi(dst), MIPS_R_ZERO); ++ break; ++ /* Full 64x32 multiply */ ++ default: ++ /* hi(dst) = hi(dst) * src(imm) */ ++ emit_mov_i(ctx, src, imm); ++ if (cpu_has_mips32r1 || cpu_has_mips32r6) { ++ emit(ctx, mul, hi(dst), hi(dst), src); ++ } else { ++ emit(ctx, multu, hi(dst), src); ++ emit(ctx, mflo, hi(dst)); ++ } ++ ++ /* hi(dst) = hi(dst) - lo(dst) */ ++ if (imm < 0) ++ emit(ctx, subu, hi(dst), hi(dst), lo(dst)); ++ ++ /* tmp = lo(dst) * src(imm) >> 32 */ ++ /* lo(dst) = lo(dst) * src(imm) */ ++ if (cpu_has_mips32r6) { ++ emit(ctx, muhu, tmp, lo(dst), src); ++ emit(ctx, mulu, lo(dst), lo(dst), src); ++ } else { ++ emit(ctx, multu, lo(dst), src); ++ emit(ctx, mflo, lo(dst)); ++ emit(ctx, mfhi, tmp); ++ } ++ ++ /* hi(dst) += tmp */ ++ emit(ctx, addu, hi(dst), hi(dst), tmp); ++ clobber_reg64(ctx, dst); ++ break; ++ } ++} ++ ++/* ALU mul register (64x64-bit) */ ++static void emit_mul_r64(struct jit_context *ctx, ++ const u8 dst[], const u8 src[]) ++{ ++ u8 acc = MIPS_R_T8; ++ u8 tmp = MIPS_R_T9; ++ ++ /* acc = hi(dst) * lo(src) */ ++ if (cpu_has_mips32r1 || cpu_has_mips32r6) { ++ emit(ctx, mul, acc, hi(dst), lo(src)); ++ } else { ++ emit(ctx, multu, hi(dst), lo(src)); ++ emit(ctx, mflo, acc); ++ } ++ ++ /* tmp = lo(dst) * hi(src) */ ++ if (cpu_has_mips32r1 || cpu_has_mips32r6) { ++ emit(ctx, mul, tmp, lo(dst), hi(src)); ++ } else { ++ emit(ctx, multu, lo(dst), hi(src)); ++ emit(ctx, mflo, tmp); ++ } ++ ++ /* acc += tmp */ ++ emit(ctx, addu, acc, acc, tmp); ++ ++ /* tmp = lo(dst) * lo(src) >> 32 */ ++ /* lo(dst) = lo(dst) * lo(src) */ ++ if (cpu_has_mips32r6) { ++ emit(ctx, muhu, tmp, lo(dst), lo(src)); ++ emit(ctx, mulu, lo(dst), lo(dst), lo(src)); ++ } else { ++ emit(ctx, multu, lo(dst), lo(src)); ++ emit(ctx, mflo, lo(dst)); ++ emit(ctx, mfhi, tmp); ++ } ++ ++ /* hi(dst) = acc + tmp */ ++ emit(ctx, addu, hi(dst), acc, tmp); ++ clobber_reg64(ctx, dst); ++} ++ ++/* Helper function for 64-bit modulo */ ++static u64 jit_mod64(u64 a, u64 b) ++{ ++ u64 rem; ++ ++ div64_u64_rem(a, b, &rem); ++ return rem; ++} ++ ++/* ALU div/mod register (64-bit) */ ++static void emit_divmod_r64(struct jit_context *ctx, ++ const u8 dst[], const u8 src[], u8 op) ++{ ++ const u8 *r0 = bpf2mips32[BPF_REG_0]; /* Mapped to v0-v1 */ ++ const u8 *r1 = bpf2mips32[BPF_REG_1]; /* Mapped to a0-a1 */ ++ const u8 *r2 = bpf2mips32[BPF_REG_2]; /* Mapped to a2-a3 */ ++ int exclude, k; ++ u32 addr = 0; ++ ++ /* Push caller-saved registers on stack */ ++ push_regs(ctx, ctx->clobbered & JIT_CALLER_REGS, ++ 0, JIT_RESERVED_STACK); ++ ++ /* Put 64-bit arguments 1 and 2 in registers a0-a3 */ ++ for (k = 0; k < 2; k++) { ++ emit(ctx, move, MIPS_R_T9, src[k]); ++ emit(ctx, move, r1[k], dst[k]); ++ emit(ctx, move, r2[k], MIPS_R_T9); ++ } ++ ++ /* Emit function call */ ++ switch (BPF_OP(op)) { ++ /* dst = dst / src */ ++ case BPF_DIV: ++ addr = (u32)&div64_u64; ++ break; ++ /* dst = dst % src */ ++ case BPF_MOD: ++ addr = (u32)&jit_mod64; ++ break; ++ } ++ emit_mov_i(ctx, MIPS_R_T9, addr); ++ emit(ctx, jalr, MIPS_R_RA, MIPS_R_T9); ++ emit(ctx, nop); /* Delay slot */ ++ ++ /* Store the 64-bit result in dst */ ++ emit(ctx, move, dst[0], r0[0]); ++ emit(ctx, move, dst[1], r0[1]); ++ ++ /* Restore caller-saved registers, excluding the computed result */ ++ exclude = BIT(lo(dst)) | BIT(hi(dst)); ++ pop_regs(ctx, ctx->clobbered & JIT_CALLER_REGS, ++ exclude, JIT_RESERVED_STACK); ++ emit_load_delay(ctx); ++ ++ clobber_reg64(ctx, dst); ++ clobber_reg(ctx, MIPS_R_V0); ++ clobber_reg(ctx, MIPS_R_V1); ++ clobber_reg(ctx, MIPS_R_RA); ++} ++ ++/* Swap bytes in a register word */ ++static void emit_swap8_r(struct jit_context *ctx, u8 dst, u8 src, u8 mask) ++{ ++ u8 tmp = MIPS_R_T9; ++ ++ emit(ctx, and, tmp, src, mask); /* tmp = src & 0x00ff00ff */ ++ emit(ctx, sll, tmp, tmp, 8); /* tmp = tmp << 8 */ ++ emit(ctx, srl, dst, src, 8); /* dst = src >> 8 */ ++ emit(ctx, and, dst, dst, mask); /* dst = dst & 0x00ff00ff */ ++ emit(ctx, or, dst, dst, tmp); /* dst = dst | tmp */ ++} ++ ++/* Swap half words in a register word */ ++static void emit_swap16_r(struct jit_context *ctx, u8 dst, u8 src) ++{ ++ u8 tmp = MIPS_R_T9; ++ ++ emit(ctx, sll, tmp, src, 16); /* tmp = src << 16 */ ++ emit(ctx, srl, dst, src, 16); /* dst = src >> 16 */ ++ emit(ctx, or, dst, dst, tmp); /* dst = dst | tmp */ ++} ++ ++/* Swap bytes and truncate a register double word, word or half word */ ++static void emit_bswap_r64(struct jit_context *ctx, const u8 dst[], u32 width) ++{ ++ u8 tmp = MIPS_R_T8; ++ ++ switch (width) { ++ /* Swap bytes in a double word */ ++ case 64: ++ if (cpu_has_mips32r2 || cpu_has_mips32r6) { ++ emit(ctx, rotr, tmp, hi(dst), 16); ++ emit(ctx, rotr, hi(dst), lo(dst), 16); ++ emit(ctx, wsbh, lo(dst), tmp); ++ emit(ctx, wsbh, hi(dst), hi(dst)); ++ } else { ++ emit_swap16_r(ctx, tmp, lo(dst)); ++ emit_swap16_r(ctx, lo(dst), hi(dst)); ++ emit(ctx, move, hi(dst), tmp); ++ ++ emit(ctx, lui, tmp, 0xff); /* tmp = 0x00ff0000 */ ++ emit(ctx, ori, tmp, tmp, 0xff); /* tmp = 0x00ff00ff */ ++ emit_swap8_r(ctx, lo(dst), lo(dst), tmp); ++ emit_swap8_r(ctx, hi(dst), hi(dst), tmp); ++ } ++ break; ++ /* Swap bytes in a word */ ++ /* Swap bytes in a half word */ ++ case 32: ++ case 16: ++ emit_bswap_r(ctx, lo(dst), width); ++ emit(ctx, move, hi(dst), MIPS_R_ZERO); ++ break; ++ } ++ clobber_reg64(ctx, dst); ++} ++ ++/* Truncate a register double word, word or half word */ ++static void emit_trunc_r64(struct jit_context *ctx, const u8 dst[], u32 width) ++{ ++ switch (width) { ++ case 64: ++ break; ++ /* Zero-extend a word */ ++ case 32: ++ emit(ctx, move, hi(dst), MIPS_R_ZERO); ++ clobber_reg(ctx, hi(dst)); ++ break; ++ /* Zero-extend a half word */ ++ case 16: ++ emit(ctx, move, hi(dst), MIPS_R_ZERO); ++ emit(ctx, andi, lo(dst), lo(dst), 0xffff); ++ clobber_reg64(ctx, dst); ++ break; ++ } ++} ++ ++/* Load operation: dst = *(size*)(src + off) */ ++static void emit_ldx(struct jit_context *ctx, ++ const u8 dst[], u8 src, s16 off, u8 size) ++{ ++ switch (size) { ++ /* Load a byte */ ++ case BPF_B: ++ emit(ctx, lbu, lo(dst), off, src); ++ emit(ctx, move, hi(dst), MIPS_R_ZERO); ++ break; ++ /* Load a half word */ ++ case BPF_H: ++ emit(ctx, lhu, lo(dst), off, src); ++ emit(ctx, move, hi(dst), MIPS_R_ZERO); ++ break; ++ /* Load a word */ ++ case BPF_W: ++ emit(ctx, lw, lo(dst), off, src); ++ emit(ctx, move, hi(dst), MIPS_R_ZERO); ++ break; ++ /* Load a double word */ ++ case BPF_DW: ++ if (dst[1] == src) { ++ emit(ctx, lw, dst[0], off + 4, src); ++ emit(ctx, lw, dst[1], off, src); ++ } else { ++ emit(ctx, lw, dst[1], off, src); ++ emit(ctx, lw, dst[0], off + 4, src); ++ } ++ emit_load_delay(ctx); ++ break; ++ } ++ clobber_reg64(ctx, dst); ++} ++ ++/* Store operation: *(size *)(dst + off) = src */ ++static void emit_stx(struct jit_context *ctx, ++ const u8 dst, const u8 src[], s16 off, u8 size) ++{ ++ switch (size) { ++ /* Store a byte */ ++ case BPF_B: ++ emit(ctx, sb, lo(src), off, dst); ++ break; ++ /* Store a half word */ ++ case BPF_H: ++ emit(ctx, sh, lo(src), off, dst); ++ break; ++ /* Store a word */ ++ case BPF_W: ++ emit(ctx, sw, lo(src), off, dst); ++ break; ++ /* Store a double word */ ++ case BPF_DW: ++ emit(ctx, sw, src[1], off, dst); ++ emit(ctx, sw, src[0], off + 4, dst); ++ break; ++ } ++} ++ ++/* Atomic read-modify-write (32-bit, non-ll/sc fallback) */ ++static void emit_atomic_r32(struct jit_context *ctx, ++ u8 dst, u8 src, s16 off, u8 code) ++{ ++ u32 exclude = 0; ++ u32 addr = 0; ++ ++ /* Push caller-saved registers on stack */ ++ push_regs(ctx, ctx->clobbered & JIT_CALLER_REGS, ++ 0, JIT_RESERVED_STACK); ++ /* ++ * Argument 1: dst+off if xchg, otherwise src, passed in register a0 ++ * Argument 2: src if xchg, othersize dst+off, passed in register a1 ++ */ ++ emit(ctx, move, MIPS_R_T9, dst); ++ emit(ctx, move, MIPS_R_A0, src); ++ emit(ctx, addiu, MIPS_R_A1, MIPS_R_T9, off); ++ ++ /* Emit function call */ ++ switch (code) { ++ case BPF_ADD: ++ addr = (u32)&atomic_add; ++ break; ++ case BPF_SUB: ++ addr = (u32)&atomic_sub; ++ break; ++ case BPF_OR: ++ addr = (u32)&atomic_or; ++ break; ++ case BPF_AND: ++ addr = (u32)&atomic_and; ++ break; ++ case BPF_XOR: ++ addr = (u32)&atomic_xor; ++ break; ++ } ++ emit_mov_i(ctx, MIPS_R_T9, addr); ++ emit(ctx, jalr, MIPS_R_RA, MIPS_R_T9); ++ emit(ctx, nop); /* Delay slot */ ++ ++ /* Restore caller-saved registers, except any fetched value */ ++ pop_regs(ctx, ctx->clobbered & JIT_CALLER_REGS, ++ exclude, JIT_RESERVED_STACK); ++ emit_load_delay(ctx); ++ clobber_reg(ctx, MIPS_R_RA); ++} ++ ++/* Atomic read-modify-write (64-bit) */ ++static void emit_atomic_r64(struct jit_context *ctx, ++ u8 dst, const u8 src[], s16 off, u8 code) ++{ ++ const u8 *r1 = bpf2mips32[BPF_REG_1]; /* Mapped to a0-a1 */ ++ u32 exclude = 0; ++ u32 addr = 0; ++ ++ /* Push caller-saved registers on stack */ ++ push_regs(ctx, ctx->clobbered & JIT_CALLER_REGS, ++ 0, JIT_RESERVED_STACK); ++ /* ++ * Argument 1: 64-bit src, passed in registers a0-a1 ++ * Argument 2: 32-bit dst+off, passed in register a2 ++ */ ++ emit(ctx, move, MIPS_R_T9, dst); ++ emit(ctx, move, r1[0], src[0]); ++ emit(ctx, move, r1[1], src[1]); ++ emit(ctx, addiu, MIPS_R_A2, MIPS_R_T9, off); ++ ++ /* Emit function call */ ++ switch (code) { ++ case BPF_ADD: ++ addr = (u32)&atomic64_add; ++ break; ++ case BPF_SUB: ++ addr = (u32)&atomic64_sub; ++ break; ++ case BPF_OR: ++ addr = (u32)&atomic64_or; ++ break; ++ case BPF_AND: ++ addr = (u32)&atomic64_and; ++ break; ++ case BPF_XOR: ++ addr = (u32)&atomic64_xor; ++ break; ++ } ++ emit_mov_i(ctx, MIPS_R_T9, addr); ++ emit(ctx, jalr, MIPS_R_RA, MIPS_R_T9); ++ emit(ctx, nop); /* Delay slot */ ++ ++ /* Restore caller-saved registers, except any fetched value */ ++ pop_regs(ctx, ctx->clobbered & JIT_CALLER_REGS, ++ exclude, JIT_RESERVED_STACK); ++ emit_load_delay(ctx); ++ clobber_reg(ctx, MIPS_R_RA); ++} ++ ++/* ++ * Conditional movz or an emulated equivalent. ++ * Note that the rs register may be modified. ++ */ ++static void emit_movz_r(struct jit_context *ctx, u8 rd, u8 rs, u8 rt) ++{ ++ if (cpu_has_mips_2) { ++ emit(ctx, movz, rd, rs, rt); /* rd = rt ? rd : rs */ ++ } else if (cpu_has_mips32r6) { ++ if (rs != MIPS_R_ZERO) ++ emit(ctx, seleqz, rs, rs, rt); /* rs = 0 if rt == 0 */ ++ emit(ctx, selnez, rd, rd, rt); /* rd = 0 if rt != 0 */ ++ if (rs != MIPS_R_ZERO) ++ emit(ctx, or, rd, rd, rs); /* rd = rd | rs */ ++ } else { ++ emit(ctx, bnez, rt, 8); /* PC += 8 if rd != 0 */ ++ emit(ctx, nop); /* +0: delay slot */ ++ emit(ctx, or, rd, rs, MIPS_R_ZERO); /* +4: rd = rs */ ++ } ++ clobber_reg(ctx, rd); ++ clobber_reg(ctx, rs); ++} ++ ++/* ++ * Conditional movn or an emulated equivalent. ++ * Note that the rs register may be modified. ++ */ ++static void emit_movn_r(struct jit_context *ctx, u8 rd, u8 rs, u8 rt) ++{ ++ if (cpu_has_mips_2) { ++ emit(ctx, movn, rd, rs, rt); /* rd = rt ? rs : rd */ ++ } else if (cpu_has_mips32r6) { ++ if (rs != MIPS_R_ZERO) ++ emit(ctx, selnez, rs, rs, rt); /* rs = 0 if rt == 0 */ ++ emit(ctx, seleqz, rd, rd, rt); /* rd = 0 if rt != 0 */ ++ if (rs != MIPS_R_ZERO) ++ emit(ctx, or, rd, rd, rs); /* rd = rd | rs */ ++ } else { ++ emit(ctx, beqz, rt, 8); /* PC += 8 if rd == 0 */ ++ emit(ctx, nop); /* +0: delay slot */ ++ emit(ctx, or, rd, rs, MIPS_R_ZERO); /* +4: rd = rs */ ++ } ++ clobber_reg(ctx, rd); ++ clobber_reg(ctx, rs); ++} ++ ++/* Emulation of 64-bit sltiu rd, rs, imm, where imm may be S32_MAX + 1 */ ++static void emit_sltiu_r64(struct jit_context *ctx, u8 rd, ++ const u8 rs[], s64 imm) ++{ ++ u8 tmp = MIPS_R_T9; ++ ++ if (imm < 0) { ++ emit_mov_i(ctx, rd, imm); /* rd = imm */ ++ emit(ctx, sltu, rd, lo(rs), rd); /* rd = rsl < rd */ ++ emit(ctx, sltiu, tmp, hi(rs), -1); /* tmp = rsh < ~0U */ ++ emit(ctx, or, rd, rd, tmp); /* rd = rd | tmp */ ++ } else { /* imm >= 0 */ ++ if (imm > 0x7fff) { ++ emit_mov_i(ctx, rd, (s32)imm); /* rd = imm */ ++ emit(ctx, sltu, rd, lo(rs), rd); /* rd = rsl < rd */ ++ } else { ++ emit(ctx, sltiu, rd, lo(rs), imm); /* rd = rsl < imm */ ++ } ++ emit_movn_r(ctx, rd, MIPS_R_ZERO, hi(rs)); /* rd = 0 if rsh */ ++ } ++} ++ ++/* Emulation of 64-bit sltu rd, rs, rt */ ++static void emit_sltu_r64(struct jit_context *ctx, u8 rd, ++ const u8 rs[], const u8 rt[]) ++{ ++ u8 tmp = MIPS_R_T9; ++ ++ emit(ctx, sltu, rd, lo(rs), lo(rt)); /* rd = rsl < rtl */ ++ emit(ctx, subu, tmp, hi(rs), hi(rt)); /* tmp = rsh - rth */ ++ emit_movn_r(ctx, rd, MIPS_R_ZERO, tmp); /* rd = 0 if tmp != 0 */ ++ emit(ctx, sltu, tmp, hi(rs), hi(rt)); /* tmp = rsh < rth */ ++ emit(ctx, or, rd, rd, tmp); /* rd = rd | tmp */ ++} ++ ++/* Emulation of 64-bit slti rd, rs, imm, where imm may be S32_MAX + 1 */ ++static void emit_slti_r64(struct jit_context *ctx, u8 rd, ++ const u8 rs[], s64 imm) ++{ ++ u8 t1 = MIPS_R_T8; ++ u8 t2 = MIPS_R_T9; ++ u8 cmp; ++ ++ /* ++ * if ((rs < 0) ^ (imm < 0)) t1 = imm >u rsl ++ * else t1 = rsl <u imm ++ */ ++ emit_mov_i(ctx, rd, (s32)imm); ++ emit(ctx, sltu, t1, lo(rs), rd); /* t1 = rsl <u imm */ ++ emit(ctx, sltu, t2, rd, lo(rs)); /* t2 = imm <u rsl */ ++ emit(ctx, srl, rd, hi(rs), 31); /* rd = rsh >> 31 */ ++ if (imm < 0) ++ emit_movz_r(ctx, t1, t2, rd); /* t1 = rd ? t1 : t2 */ ++ else ++ emit_movn_r(ctx, t1, t2, rd); /* t1 = rd ? t2 : t1 */ ++ /* ++ * if ((imm < 0 && rsh != 0xffffffff) || ++ * (imm >= 0 && rsh != 0)) ++ * t1 = 0 ++ */ ++ if (imm < 0) { ++ emit(ctx, addiu, rd, hi(rs), 1); /* rd = rsh + 1 */ ++ cmp = rd; ++ } else { /* imm >= 0 */ ++ cmp = hi(rs); ++ } ++ emit_movn_r(ctx, t1, MIPS_R_ZERO, cmp); /* t1 = 0 if cmp != 0 */ ++ ++ /* ++ * if (imm < 0) rd = rsh < -1 ++ * else rd = rsh != 0 ++ * rd = rd | t1 ++ */ ++ emit(ctx, slti, rd, hi(rs), imm < 0 ? -1 : 0); /* rd = rsh < hi(imm) */ ++ emit(ctx, or, rd, rd, t1); /* rd = rd | t1 */ ++} ++ ++/* Emulation of 64-bit(slt rd, rs, rt) */ ++static void emit_slt_r64(struct jit_context *ctx, u8 rd, ++ const u8 rs[], const u8 rt[]) ++{ ++ u8 t1 = MIPS_R_T7; ++ u8 t2 = MIPS_R_T8; ++ u8 t3 = MIPS_R_T9; ++ ++ /* ++ * if ((rs < 0) ^ (rt < 0)) t1 = rtl <u rsl ++ * else t1 = rsl <u rtl ++ * if (rsh == rth) t1 = 0 ++ */ ++ emit(ctx, sltu, t1, lo(rs), lo(rt)); /* t1 = rsl <u rtl */ ++ emit(ctx, sltu, t2, lo(rt), lo(rs)); /* t2 = rtl <u rsl */ ++ emit(ctx, xor, t3, hi(rs), hi(rt)); /* t3 = rlh ^ rth */ ++ emit(ctx, srl, rd, t3, 31); /* rd = t3 >> 31 */ ++ emit_movn_r(ctx, t1, t2, rd); /* t1 = rd ? t2 : t1 */ ++ emit_movn_r(ctx, t1, MIPS_R_ZERO, t3); /* t1 = 0 if t3 != 0 */ ++ ++ /* rd = (rsh < rth) | t1 */ ++ emit(ctx, slt, rd, hi(rs), hi(rt)); /* rd = rsh <s rth */ ++ emit(ctx, or, rd, rd, t1); /* rd = rd | t1 */ ++} ++ ++/* Jump immediate (64-bit) */ ++static void emit_jmp_i64(struct jit_context *ctx, ++ const u8 dst[], s32 imm, s32 off, u8 op) ++{ ++ u8 tmp = MIPS_R_T6; ++ ++ switch (op) { ++ /* No-op, used internally for branch optimization */ ++ case JIT_JNOP: ++ break; ++ /* PC += off if dst == imm */ ++ /* PC += off if dst != imm */ ++ case BPF_JEQ: ++ case BPF_JNE: ++ if (imm >= -0x7fff && imm <= 0x8000) { ++ emit(ctx, addiu, tmp, lo(dst), -imm); ++ } else if ((u32)imm <= 0xffff) { ++ emit(ctx, xori, tmp, lo(dst), imm); ++ } else { /* Register fallback */ ++ emit_mov_i(ctx, tmp, imm); ++ emit(ctx, xor, tmp, lo(dst), tmp); ++ } ++ if (imm < 0) { /* Compare sign extension */ ++ emit(ctx, addu, MIPS_R_T9, hi(dst), 1); ++ emit(ctx, or, tmp, tmp, MIPS_R_T9); ++ } else { /* Compare zero extension */ ++ emit(ctx, or, tmp, tmp, hi(dst)); ++ } ++ if (op == BPF_JEQ) ++ emit(ctx, beqz, tmp, off); ++ else /* BPF_JNE */ ++ emit(ctx, bnez, tmp, off); ++ break; ++ /* PC += off if dst & imm */ ++ /* PC += off if (dst & imm) == 0 (not in BPF, used for long jumps) */ ++ case BPF_JSET: ++ case JIT_JNSET: ++ if ((u32)imm <= 0xffff) { ++ emit(ctx, andi, tmp, lo(dst), imm); ++ } else { /* Register fallback */ ++ emit_mov_i(ctx, tmp, imm); ++ emit(ctx, and, tmp, lo(dst), tmp); ++ } ++ if (imm < 0) /* Sign-extension pulls in high word */ ++ emit(ctx, or, tmp, tmp, hi(dst)); ++ if (op == BPF_JSET) ++ emit(ctx, bnez, tmp, off); ++ else /* JIT_JNSET */ ++ emit(ctx, beqz, tmp, off); ++ break; ++ /* PC += off if dst > imm */ ++ case BPF_JGT: ++ emit_sltiu_r64(ctx, tmp, dst, (s64)imm + 1); ++ emit(ctx, beqz, tmp, off); ++ break; ++ /* PC += off if dst >= imm */ ++ case BPF_JGE: ++ emit_sltiu_r64(ctx, tmp, dst, imm); ++ emit(ctx, beqz, tmp, off); ++ break; ++ /* PC += off if dst < imm */ ++ case BPF_JLT: ++ emit_sltiu_r64(ctx, tmp, dst, imm); ++ emit(ctx, bnez, tmp, off); ++ break; ++ /* PC += off if dst <= imm */ ++ case BPF_JLE: ++ emit_sltiu_r64(ctx, tmp, dst, (s64)imm + 1); ++ emit(ctx, bnez, tmp, off); ++ break; ++ /* PC += off if dst > imm (signed) */ ++ case BPF_JSGT: ++ emit_slti_r64(ctx, tmp, dst, (s64)imm + 1); ++ emit(ctx, beqz, tmp, off); ++ break; ++ /* PC += off if dst >= imm (signed) */ ++ case BPF_JSGE: ++ emit_slti_r64(ctx, tmp, dst, imm); ++ emit(ctx, beqz, tmp, off); ++ break; ++ /* PC += off if dst < imm (signed) */ ++ case BPF_JSLT: ++ emit_slti_r64(ctx, tmp, dst, imm); ++ emit(ctx, bnez, tmp, off); ++ break; ++ /* PC += off if dst <= imm (signed) */ ++ case BPF_JSLE: ++ emit_slti_r64(ctx, tmp, dst, (s64)imm + 1); ++ emit(ctx, bnez, tmp, off); ++ break; ++ } ++} ++ ++/* Jump register (64-bit) */ ++static void emit_jmp_r64(struct jit_context *ctx, ++ const u8 dst[], const u8 src[], s32 off, u8 op) ++{ ++ u8 t1 = MIPS_R_T6; ++ u8 t2 = MIPS_R_T7; ++ ++ switch (op) { ++ /* No-op, used internally for branch optimization */ ++ case JIT_JNOP: ++ break; ++ /* PC += off if dst == src */ ++ /* PC += off if dst != src */ ++ case BPF_JEQ: ++ case BPF_JNE: ++ emit(ctx, subu, t1, lo(dst), lo(src)); ++ emit(ctx, subu, t2, hi(dst), hi(src)); ++ emit(ctx, or, t1, t1, t2); ++ if (op == BPF_JEQ) ++ emit(ctx, beqz, t1, off); ++ else /* BPF_JNE */ ++ emit(ctx, bnez, t1, off); ++ break; ++ /* PC += off if dst & src */ ++ /* PC += off if (dst & imm) == 0 (not in BPF, used for long jumps) */ ++ case BPF_JSET: ++ case JIT_JNSET: ++ emit(ctx, and, t1, lo(dst), lo(src)); ++ emit(ctx, and, t2, hi(dst), hi(src)); ++ emit(ctx, or, t1, t1, t2); ++ if (op == BPF_JSET) ++ emit(ctx, bnez, t1, off); ++ else /* JIT_JNSET */ ++ emit(ctx, beqz, t1, off); ++ break; ++ /* PC += off if dst > src */ ++ case BPF_JGT: ++ emit_sltu_r64(ctx, t1, src, dst); ++ emit(ctx, bnez, t1, off); ++ break; ++ /* PC += off if dst >= src */ ++ case BPF_JGE: ++ emit_sltu_r64(ctx, t1, dst, src); ++ emit(ctx, beqz, t1, off); ++ break; ++ /* PC += off if dst < src */ ++ case BPF_JLT: ++ emit_sltu_r64(ctx, t1, dst, src); ++ emit(ctx, bnez, t1, off); ++ break; ++ /* PC += off if dst <= src */ ++ case BPF_JLE: ++ emit_sltu_r64(ctx, t1, src, dst); ++ emit(ctx, beqz, t1, off); ++ break; ++ /* PC += off if dst > src (signed) */ ++ case BPF_JSGT: ++ emit_slt_r64(ctx, t1, src, dst); ++ emit(ctx, bnez, t1, off); ++ break; ++ /* PC += off if dst >= src (signed) */ ++ case BPF_JSGE: ++ emit_slt_r64(ctx, t1, dst, src); ++ emit(ctx, beqz, t1, off); ++ break; ++ /* PC += off if dst < src (signed) */ ++ case BPF_JSLT: ++ emit_slt_r64(ctx, t1, dst, src); ++ emit(ctx, bnez, t1, off); ++ break; ++ /* PC += off if dst <= src (signed) */ ++ case BPF_JSLE: ++ emit_slt_r64(ctx, t1, src, dst); ++ emit(ctx, beqz, t1, off); ++ break; ++ } ++} ++ ++/* Function call */ ++static int emit_call(struct jit_context *ctx, const struct bpf_insn *insn) ++{ ++ bool fixed; ++ u64 addr; ++ ++ /* Decode the call address */ ++ if (bpf_jit_get_func_addr(ctx->program, insn, false, ++ &addr, &fixed) < 0) ++ return -1; ++ if (!fixed) ++ return -1; ++ ++ /* Push stack arguments */ ++ push_regs(ctx, JIT_STACK_REGS, 0, JIT_RESERVED_STACK); ++ ++ /* Emit function call */ ++ emit_mov_i(ctx, MIPS_R_T9, addr); ++ emit(ctx, jalr, MIPS_R_RA, MIPS_R_T9); ++ emit(ctx, nop); /* Delay slot */ ++ ++ clobber_reg(ctx, MIPS_R_RA); ++ clobber_reg(ctx, MIPS_R_V0); ++ clobber_reg(ctx, MIPS_R_V1); ++ return 0; ++} ++ ++/* Function tail call */ ++static int emit_tail_call(struct jit_context *ctx) ++{ ++ u8 ary = lo(bpf2mips32[BPF_REG_2]); ++ u8 ind = lo(bpf2mips32[BPF_REG_3]); ++ u8 t1 = MIPS_R_T8; ++ u8 t2 = MIPS_R_T9; ++ int off; ++ ++ /* ++ * Tail call: ++ * eBPF R1 - function argument (context ptr), passed in a0-a1 ++ * eBPF R2 - ptr to object with array of function entry points ++ * eBPF R3 - array index of function to be called ++ * stack[sz] - remaining tail call count, initialized in prologue ++ */ ++ ++ /* if (ind >= ary->map.max_entries) goto out */ ++ off = offsetof(struct bpf_array, map.max_entries); ++ if (off > 0x7fff) ++ return -1; ++ emit(ctx, lw, t1, off, ary); /* t1 = ary->map.max_entries*/ ++ emit_load_delay(ctx); /* Load delay slot */ ++ emit(ctx, sltu, t1, ind, t1); /* t1 = ind < t1 */ ++ emit(ctx, beqz, t1, get_offset(ctx, 1)); /* PC += off(1) if t1 == 0 */ ++ /* (next insn delay slot) */ ++ /* if (TCC-- <= 0) goto out */ ++ emit(ctx, lw, t2, ctx->stack_size, MIPS_R_SP); /* t2 = *(SP + size) */ ++ emit_load_delay(ctx); /* Load delay slot */ ++ emit(ctx, blez, t2, get_offset(ctx, 1)); /* PC += off(1) if t2 < 0 */ ++ emit(ctx, addiu, t2, t2, -1); /* t2-- (delay slot) */ ++ emit(ctx, sw, t2, ctx->stack_size, MIPS_R_SP); /* *(SP + size) = t2 */ ++ ++ /* prog = ary->ptrs[ind] */ ++ off = offsetof(struct bpf_array, ptrs); ++ if (off > 0x7fff) ++ return -1; ++ emit(ctx, sll, t1, ind, 2); /* t1 = ind << 2 */ ++ emit(ctx, addu, t1, t1, ary); /* t1 += ary */ ++ emit(ctx, lw, t2, off, t1); /* t2 = *(t1 + off) */ ++ emit_load_delay(ctx); /* Load delay slot */ ++ ++ /* if (prog == 0) goto out */ ++ emit(ctx, beqz, t2, get_offset(ctx, 1)); /* PC += off(1) if t2 == 0 */ ++ emit(ctx, nop); /* Delay slot */ ++ ++ /* func = prog->bpf_func + 8 (prologue skip offset) */ ++ off = offsetof(struct bpf_prog, bpf_func); ++ if (off > 0x7fff) ++ return -1; ++ emit(ctx, lw, t1, off, t2); /* t1 = *(t2 + off) */ ++ emit_load_delay(ctx); /* Load delay slot */ ++ emit(ctx, addiu, t1, t1, JIT_TCALL_SKIP); /* t1 += skip (8 or 12) */ ++ ++ /* goto func */ ++ build_epilogue(ctx, t1); ++ return 0; ++} ++ ++/* ++ * Stack frame layout for a JITed program (stack grows down). ++ * ++ * Higher address : Caller's stack frame : ++ * :----------------------------: ++ * : 64-bit eBPF args r3-r5 : ++ * :----------------------------: ++ * : Reserved / tail call count : ++ * +============================+ <--- MIPS sp before call ++ * | Callee-saved registers, | ++ * | including RA and FP | ++ * +----------------------------+ <--- eBPF FP (MIPS zero,fp) ++ * | Local eBPF variables | ++ * | allocated by program | ++ * +----------------------------+ ++ * | Reserved for caller-saved | ++ * | registers | ++ * +----------------------------+ ++ * | Reserved for 64-bit eBPF | ++ * | args r3-r5 & args passed | ++ * | on stack in kernel calls | ++ * Lower address +============================+ <--- MIPS sp ++ */ ++ ++/* Build program prologue to set up the stack and registers */ ++void build_prologue(struct jit_context *ctx) ++{ ++ const u8 *r1 = bpf2mips32[BPF_REG_1]; ++ const u8 *fp = bpf2mips32[BPF_REG_FP]; ++ int stack, saved, locals, reserved; ++ ++ /* ++ * The first two instructions initialize TCC in the reserved (for us) ++ * 16-byte area in the parent's stack frame. On a tail call, the ++ * calling function jumps into the prologue after these instructions. ++ */ ++ emit(ctx, ori, MIPS_R_T9, MIPS_R_ZERO, ++ min(MAX_TAIL_CALL_CNT + 1, 0xffff)); ++ emit(ctx, sw, MIPS_R_T9, 0, MIPS_R_SP); ++ ++ /* ++ * Register eBPF R1 contains the 32-bit context pointer argument. ++ * A 32-bit argument is always passed in MIPS register a0, regardless ++ * of CPU endianness. Initialize R1 accordingly and zero-extend. ++ */ ++#ifdef __BIG_ENDIAN ++ emit(ctx, move, lo(r1), MIPS_R_A0); ++#endif ++ ++ /* === Entry-point for tail calls === */ ++ ++ /* Zero-extend the 32-bit argument */ ++ emit(ctx, move, hi(r1), MIPS_R_ZERO); ++ ++ /* If the eBPF frame pointer was accessed it must be saved */ ++ if (ctx->accessed & BIT(BPF_REG_FP)) ++ clobber_reg64(ctx, fp); ++ ++ /* Compute the stack space needed for callee-saved registers */ ++ saved = hweight32(ctx->clobbered & JIT_CALLEE_REGS) * sizeof(u32); ++ saved = ALIGN(saved, MIPS_STACK_ALIGNMENT); ++ ++ /* Stack space used by eBPF program local data */ ++ locals = ALIGN(ctx->program->aux->stack_depth, MIPS_STACK_ALIGNMENT); ++ ++ /* ++ * If we are emitting function calls, reserve extra stack space for ++ * caller-saved registers and function arguments passed on the stack. ++ * The required space is computed automatically during resource ++ * usage discovery (pass 1). ++ */ ++ reserved = ctx->stack_used; ++ ++ /* Allocate the stack frame */ ++ stack = ALIGN(saved + locals + reserved, MIPS_STACK_ALIGNMENT); ++ emit(ctx, addiu, MIPS_R_SP, MIPS_R_SP, -stack); ++ ++ /* Store callee-saved registers on stack */ ++ push_regs(ctx, ctx->clobbered & JIT_CALLEE_REGS, 0, stack - saved); ++ ++ /* Initialize the eBPF frame pointer if accessed */ ++ if (ctx->accessed & BIT(BPF_REG_FP)) ++ emit(ctx, addiu, lo(fp), MIPS_R_SP, stack - saved); ++ ++ ctx->saved_size = saved; ++ ctx->stack_size = stack; ++} ++ ++/* Build the program epilogue to restore the stack and registers */ ++void build_epilogue(struct jit_context *ctx, int dest_reg) ++{ ++ /* Restore callee-saved registers from stack */ ++ pop_regs(ctx, ctx->clobbered & JIT_CALLEE_REGS, 0, ++ ctx->stack_size - ctx->saved_size); ++ /* ++ * A 32-bit return value is always passed in MIPS register v0, ++ * but on big-endian targets the low part of R0 is mapped to v1. ++ */ ++#ifdef __BIG_ENDIAN ++ emit(ctx, move, MIPS_R_V0, MIPS_R_V1); ++#endif ++ ++ /* Jump to the return address and adjust the stack pointer */ ++ emit(ctx, jr, dest_reg); ++ emit(ctx, addiu, MIPS_R_SP, MIPS_R_SP, ctx->stack_size); ++} ++ ++/* Build one eBPF instruction */ ++int build_insn(const struct bpf_insn *insn, struct jit_context *ctx) ++{ ++ const u8 *dst = bpf2mips32[insn->dst_reg]; ++ const u8 *src = bpf2mips32[insn->src_reg]; ++ const u8 *tmp = bpf2mips32[JIT_REG_TMP]; ++ u8 code = insn->code; ++ s16 off = insn->off; ++ s32 imm = insn->imm; ++ s32 val, rel; ++ u8 alu, jmp; ++ ++ switch (code) { ++ /* ALU operations */ ++ /* dst = imm */ ++ case BPF_ALU | BPF_MOV | BPF_K: ++ emit_mov_i(ctx, lo(dst), imm); ++ emit_zext_ver(ctx, dst); ++ break; ++ /* dst = src */ ++ case BPF_ALU | BPF_MOV | BPF_X: ++ if (imm == 1) { ++ /* Special mov32 for zext */ ++ emit_mov_i(ctx, hi(dst), 0); ++ } else { ++ emit_mov_r(ctx, lo(dst), lo(src)); ++ emit_zext_ver(ctx, dst); ++ } ++ break; ++ /* dst = -dst */ ++ case BPF_ALU | BPF_NEG: ++ emit_alu_i(ctx, lo(dst), 0, BPF_NEG); ++ emit_zext_ver(ctx, dst); ++ break; ++ /* dst = dst & imm */ ++ /* dst = dst | imm */ ++ /* dst = dst ^ imm */ ++ /* dst = dst << imm */ ++ /* dst = dst >> imm */ ++ /* dst = dst >> imm (arithmetic) */ ++ /* dst = dst + imm */ ++ /* dst = dst - imm */ ++ /* dst = dst * imm */ ++ /* dst = dst / imm */ ++ /* dst = dst % imm */ ++ case BPF_ALU | BPF_OR | BPF_K: ++ case BPF_ALU | BPF_AND | BPF_K: ++ case BPF_ALU | BPF_XOR | BPF_K: ++ case BPF_ALU | BPF_LSH | BPF_K: ++ case BPF_ALU | BPF_RSH | BPF_K: ++ case BPF_ALU | BPF_ARSH | BPF_K: ++ case BPF_ALU | BPF_ADD | BPF_K: ++ case BPF_ALU | BPF_SUB | BPF_K: ++ case BPF_ALU | BPF_MUL | BPF_K: ++ case BPF_ALU | BPF_DIV | BPF_K: ++ case BPF_ALU | BPF_MOD | BPF_K: ++ if (!valid_alu_i(BPF_OP(code), imm)) { ++ emit_mov_i(ctx, MIPS_R_T6, imm); ++ emit_alu_r(ctx, lo(dst), MIPS_R_T6, BPF_OP(code)); ++ } else if (rewrite_alu_i(BPF_OP(code), imm, &alu, &val)) { ++ emit_alu_i(ctx, lo(dst), val, alu); ++ } ++ emit_zext_ver(ctx, dst); ++ break; ++ /* dst = dst & src */ ++ /* dst = dst | src */ ++ /* dst = dst ^ src */ ++ /* dst = dst << src */ ++ /* dst = dst >> src */ ++ /* dst = dst >> src (arithmetic) */ ++ /* dst = dst + src */ ++ /* dst = dst - src */ ++ /* dst = dst * src */ ++ /* dst = dst / src */ ++ /* dst = dst % src */ ++ case BPF_ALU | BPF_AND | BPF_X: ++ case BPF_ALU | BPF_OR | BPF_X: ++ case BPF_ALU | BPF_XOR | BPF_X: ++ case BPF_ALU | BPF_LSH | BPF_X: ++ case BPF_ALU | BPF_RSH | BPF_X: ++ case BPF_ALU | BPF_ARSH | BPF_X: ++ case BPF_ALU | BPF_ADD | BPF_X: ++ case BPF_ALU | BPF_SUB | BPF_X: ++ case BPF_ALU | BPF_MUL | BPF_X: ++ case BPF_ALU | BPF_DIV | BPF_X: ++ case BPF_ALU | BPF_MOD | BPF_X: ++ emit_alu_r(ctx, lo(dst), lo(src), BPF_OP(code)); ++ emit_zext_ver(ctx, dst); ++ break; ++ /* dst = imm (64-bit) */ ++ case BPF_ALU64 | BPF_MOV | BPF_K: ++ emit_mov_se_i64(ctx, dst, imm); ++ break; ++ /* dst = src (64-bit) */ ++ case BPF_ALU64 | BPF_MOV | BPF_X: ++ emit_mov_r(ctx, lo(dst), lo(src)); ++ emit_mov_r(ctx, hi(dst), hi(src)); ++ break; ++ /* dst = -dst (64-bit) */ ++ case BPF_ALU64 | BPF_NEG: ++ emit_neg_i64(ctx, dst); ++ break; ++ /* dst = dst & imm (64-bit) */ ++ case BPF_ALU64 | BPF_AND | BPF_K: ++ emit_alu_i64(ctx, dst, imm, BPF_OP(code)); ++ break; ++ /* dst = dst | imm (64-bit) */ ++ /* dst = dst ^ imm (64-bit) */ ++ /* dst = dst + imm (64-bit) */ ++ /* dst = dst - imm (64-bit) */ ++ case BPF_ALU64 | BPF_OR | BPF_K: ++ case BPF_ALU64 | BPF_XOR | BPF_K: ++ case BPF_ALU64 | BPF_ADD | BPF_K: ++ case BPF_ALU64 | BPF_SUB | BPF_K: ++ if (imm) ++ emit_alu_i64(ctx, dst, imm, BPF_OP(code)); ++ break; ++ /* dst = dst << imm (64-bit) */ ++ /* dst = dst >> imm (64-bit) */ ++ /* dst = dst >> imm (64-bit, arithmetic) */ ++ case BPF_ALU64 | BPF_LSH | BPF_K: ++ case BPF_ALU64 | BPF_RSH | BPF_K: ++ case BPF_ALU64 | BPF_ARSH | BPF_K: ++ if (imm) ++ emit_shift_i64(ctx, dst, imm, BPF_OP(code)); ++ break; ++ /* dst = dst * imm (64-bit) */ ++ case BPF_ALU64 | BPF_MUL | BPF_K: ++ emit_mul_i64(ctx, dst, imm); ++ break; ++ /* dst = dst / imm (64-bit) */ ++ /* dst = dst % imm (64-bit) */ ++ case BPF_ALU64 | BPF_DIV | BPF_K: ++ case BPF_ALU64 | BPF_MOD | BPF_K: ++ /* ++ * Sign-extend the immediate value into a temporary register, ++ * and then do the operation on this register. ++ */ ++ emit_mov_se_i64(ctx, tmp, imm); ++ emit_divmod_r64(ctx, dst, tmp, BPF_OP(code)); ++ break; ++ /* dst = dst & src (64-bit) */ ++ /* dst = dst | src (64-bit) */ ++ /* dst = dst ^ src (64-bit) */ ++ /* dst = dst + src (64-bit) */ ++ /* dst = dst - src (64-bit) */ ++ case BPF_ALU64 | BPF_AND | BPF_X: ++ case BPF_ALU64 | BPF_OR | BPF_X: ++ case BPF_ALU64 | BPF_XOR | BPF_X: ++ case BPF_ALU64 | BPF_ADD | BPF_X: ++ case BPF_ALU64 | BPF_SUB | BPF_X: ++ emit_alu_r64(ctx, dst, src, BPF_OP(code)); ++ break; ++ /* dst = dst << src (64-bit) */ ++ /* dst = dst >> src (64-bit) */ ++ /* dst = dst >> src (64-bit, arithmetic) */ ++ case BPF_ALU64 | BPF_LSH | BPF_X: ++ case BPF_ALU64 | BPF_RSH | BPF_X: ++ case BPF_ALU64 | BPF_ARSH | BPF_X: ++ emit_shift_r64(ctx, dst, lo(src), BPF_OP(code)); ++ break; ++ /* dst = dst * src (64-bit) */ ++ case BPF_ALU64 | BPF_MUL | BPF_X: ++ emit_mul_r64(ctx, dst, src); ++ break; ++ /* dst = dst / src (64-bit) */ ++ /* dst = dst % src (64-bit) */ ++ case BPF_ALU64 | BPF_DIV | BPF_X: ++ case BPF_ALU64 | BPF_MOD | BPF_X: ++ emit_divmod_r64(ctx, dst, src, BPF_OP(code)); ++ break; ++ /* dst = htole(dst) */ ++ /* dst = htobe(dst) */ ++ case BPF_ALU | BPF_END | BPF_FROM_LE: ++ case BPF_ALU | BPF_END | BPF_FROM_BE: ++ if (BPF_SRC(code) == ++#ifdef __BIG_ENDIAN ++ BPF_FROM_LE ++#else ++ BPF_FROM_BE ++#endif ++ ) ++ emit_bswap_r64(ctx, dst, imm); ++ else ++ emit_trunc_r64(ctx, dst, imm); ++ break; ++ /* dst = imm64 */ ++ case BPF_LD | BPF_IMM | BPF_DW: ++ emit_mov_i(ctx, lo(dst), imm); ++ emit_mov_i(ctx, hi(dst), insn[1].imm); ++ return 1; ++ /* LDX: dst = *(size *)(src + off) */ ++ case BPF_LDX | BPF_MEM | BPF_W: ++ case BPF_LDX | BPF_MEM | BPF_H: ++ case BPF_LDX | BPF_MEM | BPF_B: ++ case BPF_LDX | BPF_MEM | BPF_DW: ++ emit_ldx(ctx, dst, lo(src), off, BPF_SIZE(code)); ++ break; ++ /* ST: *(size *)(dst + off) = imm */ ++ case BPF_ST | BPF_MEM | BPF_W: ++ case BPF_ST | BPF_MEM | BPF_H: ++ case BPF_ST | BPF_MEM | BPF_B: ++ case BPF_ST | BPF_MEM | BPF_DW: ++ switch (BPF_SIZE(code)) { ++ case BPF_DW: ++ /* Sign-extend immediate value into temporary reg */ ++ emit_mov_se_i64(ctx, tmp, imm); ++ break; ++ case BPF_W: ++ case BPF_H: ++ case BPF_B: ++ emit_mov_i(ctx, lo(tmp), imm); ++ break; ++ } ++ emit_stx(ctx, lo(dst), tmp, off, BPF_SIZE(code)); ++ break; ++ /* STX: *(size *)(dst + off) = src */ ++ case BPF_STX | BPF_MEM | BPF_W: ++ case BPF_STX | BPF_MEM | BPF_H: ++ case BPF_STX | BPF_MEM | BPF_B: ++ case BPF_STX | BPF_MEM | BPF_DW: ++ emit_stx(ctx, lo(dst), src, off, BPF_SIZE(code)); ++ break; ++ /* Speculation barrier */ ++ case BPF_ST | BPF_NOSPEC: ++ break; ++ /* Atomics */ ++ case BPF_STX | BPF_XADD | BPF_W: ++ switch (imm) { ++ case BPF_ADD: ++ case BPF_AND: ++ case BPF_OR: ++ case BPF_XOR: ++ if (cpu_has_llsc) ++ emit_atomic_r(ctx, lo(dst), lo(src), off, imm); ++ else /* Non-ll/sc fallback */ ++ emit_atomic_r32(ctx, lo(dst), lo(src), ++ off, imm); ++ break; ++ default: ++ goto notyet; ++ } ++ break; ++ /* Atomics (64-bit) */ ++ case BPF_STX | BPF_XADD | BPF_DW: ++ switch (imm) { ++ case BPF_ADD: ++ case BPF_AND: ++ case BPF_OR: ++ case BPF_XOR: ++ emit_atomic_r64(ctx, lo(dst), src, off, imm); ++ break; ++ default: ++ goto notyet; ++ } ++ break; ++ /* PC += off if dst == src */ ++ /* PC += off if dst != src */ ++ /* PC += off if dst & src */ ++ /* PC += off if dst > src */ ++ /* PC += off if dst >= src */ ++ /* PC += off if dst < src */ ++ /* PC += off if dst <= src */ ++ /* PC += off if dst > src (signed) */ ++ /* PC += off if dst >= src (signed) */ ++ /* PC += off if dst < src (signed) */ ++ /* PC += off if dst <= src (signed) */ ++ case BPF_JMP32 | BPF_JEQ | BPF_X: ++ case BPF_JMP32 | BPF_JNE | BPF_X: ++ case BPF_JMP32 | BPF_JSET | BPF_X: ++ case BPF_JMP32 | BPF_JGT | BPF_X: ++ case BPF_JMP32 | BPF_JGE | BPF_X: ++ case BPF_JMP32 | BPF_JLT | BPF_X: ++ case BPF_JMP32 | BPF_JLE | BPF_X: ++ case BPF_JMP32 | BPF_JSGT | BPF_X: ++ case BPF_JMP32 | BPF_JSGE | BPF_X: ++ case BPF_JMP32 | BPF_JSLT | BPF_X: ++ case BPF_JMP32 | BPF_JSLE | BPF_X: ++ if (off == 0) ++ break; ++ setup_jmp_r(ctx, dst == src, BPF_OP(code), off, &jmp, &rel); ++ emit_jmp_r(ctx, lo(dst), lo(src), rel, jmp); ++ if (finish_jmp(ctx, jmp, off) < 0) ++ goto toofar; ++ break; ++ /* PC += off if dst == imm */ ++ /* PC += off if dst != imm */ ++ /* PC += off if dst & imm */ ++ /* PC += off if dst > imm */ ++ /* PC += off if dst >= imm */ ++ /* PC += off if dst < imm */ ++ /* PC += off if dst <= imm */ ++ /* PC += off if dst > imm (signed) */ ++ /* PC += off if dst >= imm (signed) */ ++ /* PC += off if dst < imm (signed) */ ++ /* PC += off if dst <= imm (signed) */ ++ case BPF_JMP32 | BPF_JEQ | BPF_K: ++ case BPF_JMP32 | BPF_JNE | BPF_K: ++ case BPF_JMP32 | BPF_JSET | BPF_K: ++ case BPF_JMP32 | BPF_JGT | BPF_K: ++ case BPF_JMP32 | BPF_JGE | BPF_K: ++ case BPF_JMP32 | BPF_JLT | BPF_K: ++ case BPF_JMP32 | BPF_JLE | BPF_K: ++ case BPF_JMP32 | BPF_JSGT | BPF_K: ++ case BPF_JMP32 | BPF_JSGE | BPF_K: ++ case BPF_JMP32 | BPF_JSLT | BPF_K: ++ case BPF_JMP32 | BPF_JSLE | BPF_K: ++ if (off == 0) ++ break; ++ setup_jmp_i(ctx, imm, 32, BPF_OP(code), off, &jmp, &rel); ++ if (valid_jmp_i(jmp, imm)) { ++ emit_jmp_i(ctx, lo(dst), imm, rel, jmp); ++ } else { ++ /* Move large immediate to register */ ++ emit_mov_i(ctx, MIPS_R_T6, imm); ++ emit_jmp_r(ctx, lo(dst), MIPS_R_T6, rel, jmp); ++ } ++ if (finish_jmp(ctx, jmp, off) < 0) ++ goto toofar; ++ break; ++ /* PC += off if dst == src */ ++ /* PC += off if dst != src */ ++ /* PC += off if dst & src */ ++ /* PC += off if dst > src */ ++ /* PC += off if dst >= src */ ++ /* PC += off if dst < src */ ++ /* PC += off if dst <= src */ ++ /* PC += off if dst > src (signed) */ ++ /* PC += off if dst >= src (signed) */ ++ /* PC += off if dst < src (signed) */ ++ /* PC += off if dst <= src (signed) */ ++ case BPF_JMP | BPF_JEQ | BPF_X: ++ case BPF_JMP | BPF_JNE | BPF_X: ++ case BPF_JMP | BPF_JSET | BPF_X: ++ case BPF_JMP | BPF_JGT | BPF_X: ++ case BPF_JMP | BPF_JGE | BPF_X: ++ case BPF_JMP | BPF_JLT | BPF_X: ++ case BPF_JMP | BPF_JLE | BPF_X: ++ case BPF_JMP | BPF_JSGT | BPF_X: ++ case BPF_JMP | BPF_JSGE | BPF_X: ++ case BPF_JMP | BPF_JSLT | BPF_X: ++ case BPF_JMP | BPF_JSLE | BPF_X: ++ if (off == 0) ++ break; ++ setup_jmp_r(ctx, dst == src, BPF_OP(code), off, &jmp, &rel); ++ emit_jmp_r64(ctx, dst, src, rel, jmp); ++ if (finish_jmp(ctx, jmp, off) < 0) ++ goto toofar; ++ break; ++ /* PC += off if dst == imm */ ++ /* PC += off if dst != imm */ ++ /* PC += off if dst & imm */ ++ /* PC += off if dst > imm */ ++ /* PC += off if dst >= imm */ ++ /* PC += off if dst < imm */ ++ /* PC += off if dst <= imm */ ++ /* PC += off if dst > imm (signed) */ ++ /* PC += off if dst >= imm (signed) */ ++ /* PC += off if dst < imm (signed) */ ++ /* PC += off if dst <= imm (signed) */ ++ case BPF_JMP | BPF_JEQ | BPF_K: ++ case BPF_JMP | BPF_JNE | BPF_K: ++ case BPF_JMP | BPF_JSET | BPF_K: ++ case BPF_JMP | BPF_JGT | BPF_K: ++ case BPF_JMP | BPF_JGE | BPF_K: ++ case BPF_JMP | BPF_JLT | BPF_K: ++ case BPF_JMP | BPF_JLE | BPF_K: ++ case BPF_JMP | BPF_JSGT | BPF_K: ++ case BPF_JMP | BPF_JSGE | BPF_K: ++ case BPF_JMP | BPF_JSLT | BPF_K: ++ case BPF_JMP | BPF_JSLE | BPF_K: ++ if (off == 0) ++ break; ++ setup_jmp_i(ctx, imm, 64, BPF_OP(code), off, &jmp, &rel); ++ emit_jmp_i64(ctx, dst, imm, rel, jmp); ++ if (finish_jmp(ctx, jmp, off) < 0) ++ goto toofar; ++ break; ++ /* PC += off */ ++ case BPF_JMP | BPF_JA: ++ if (off == 0) ++ break; ++ if (emit_ja(ctx, off) < 0) ++ goto toofar; ++ break; ++ /* Tail call */ ++ case BPF_JMP | BPF_TAIL_CALL: ++ if (emit_tail_call(ctx) < 0) ++ goto invalid; ++ break; ++ /* Function call */ ++ case BPF_JMP | BPF_CALL: ++ if (emit_call(ctx, insn) < 0) ++ goto invalid; ++ break; ++ /* Function return */ ++ case BPF_JMP | BPF_EXIT: ++ /* ++ * Optimization: when last instruction is EXIT ++ * simply continue to epilogue. ++ */ ++ if (ctx->bpf_index == ctx->program->len - 1) ++ break; ++ if (emit_exit(ctx) < 0) ++ goto toofar; ++ break; ++ ++ default: ++invalid: ++ pr_err_once("unknown opcode %02x\n", code); ++ return -EINVAL; ++notyet: ++ pr_info_once("*** NOT YET: opcode %02x ***\n", code); ++ return -EFAULT; ++toofar: ++ pr_info_once("*** TOO FAR: jump at %u opcode %02x ***\n", ++ ctx->bpf_index, code); ++ return -E2BIG; ++ } ++ return 0; ++} diff --git a/pkgs/patches-linux-5.15/050-v5.16-03-mips-bpf-Add-new-eBPF-JIT-for-64-bit-MIPS.patch b/pkgs/patches-linux-5.15/050-v5.16-03-mips-bpf-Add-new-eBPF-JIT-for-64-bit-MIPS.patch new file mode 100644 index 0000000..38b46c0 --- /dev/null +++ b/pkgs/patches-linux-5.15/050-v5.16-03-mips-bpf-Add-new-eBPF-JIT-for-64-bit-MIPS.patch @@ -0,0 +1,1005 @@ +From: Johan Almbladh <johan.almbladh@anyfinetworks.com> +Date: Tue, 5 Oct 2021 18:54:05 +0200 +Subject: [PATCH] mips: bpf: Add new eBPF JIT for 64-bit MIPS + +This is an implementation on of an eBPF JIT for 64-bit MIPS III-V and +MIPS64r1-r6. It uses the same framework introduced by the 32-bit JIT. + +Signed-off-by: Johan Almbladh <johan.almbladh@anyfinetworks.com> +--- + create mode 100644 arch/mips/net/bpf_jit_comp64.c + +--- /dev/null ++++ b/arch/mips/net/bpf_jit_comp64.c +@@ -0,0 +1,991 @@ ++// SPDX-License-Identifier: GPL-2.0-only ++/* ++ * Just-In-Time compiler for eBPF bytecode on MIPS. ++ * Implementation of JIT functions for 64-bit CPUs. ++ * ++ * Copyright (c) 2021 Anyfi Networks AB. ++ * Author: Johan Almbladh <johan.almbladh@gmail.com> ++ * ++ * Based on code and ideas from ++ * Copyright (c) 2017 Cavium, Inc. ++ * Copyright (c) 2017 Shubham Bansal <illusionist.neo@gmail.com> ++ * Copyright (c) 2011 Mircea Gherzan <mgherzan@gmail.com> ++ */ ++ ++#include <linux/errno.h> ++#include <linux/filter.h> ++#include <linux/bpf.h> ++#include <asm/cpu-features.h> ++#include <asm/isa-rev.h> ++#include <asm/uasm.h> ++ ++#include "bpf_jit_comp.h" ++ ++/* MIPS t0-t3 are not available in the n64 ABI */ ++#undef MIPS_R_T0 ++#undef MIPS_R_T1 ++#undef MIPS_R_T2 ++#undef MIPS_R_T3 ++ ++/* Stack is 16-byte aligned in n64 ABI */ ++#define MIPS_STACK_ALIGNMENT 16 ++ ++/* Extra 64-bit eBPF registers used by JIT */ ++#define JIT_REG_TC (MAX_BPF_JIT_REG + 0) ++#define JIT_REG_ZX (MAX_BPF_JIT_REG + 1) ++ ++/* Number of prologue bytes to skip when doing a tail call */ ++#define JIT_TCALL_SKIP 4 ++ ++/* Callee-saved CPU registers that the JIT must preserve */ ++#define JIT_CALLEE_REGS \ ++ (BIT(MIPS_R_S0) | \ ++ BIT(MIPS_R_S1) | \ ++ BIT(MIPS_R_S2) | \ ++ BIT(MIPS_R_S3) | \ ++ BIT(MIPS_R_S4) | \ ++ BIT(MIPS_R_S5) | \ ++ BIT(MIPS_R_S6) | \ ++ BIT(MIPS_R_S7) | \ ++ BIT(MIPS_R_GP) | \ ++ BIT(MIPS_R_FP) | \ ++ BIT(MIPS_R_RA)) ++ ++/* Caller-saved CPU registers available for JIT use */ ++#define JIT_CALLER_REGS \ ++ (BIT(MIPS_R_A5) | \ ++ BIT(MIPS_R_A6) | \ ++ BIT(MIPS_R_A7)) ++/* ++ * Mapping of 64-bit eBPF registers to 64-bit native MIPS registers. ++ * MIPS registers t4 - t7 may be used by the JIT as temporary registers. ++ * MIPS registers t8 - t9 are reserved for single-register common functions. ++ */ ++static const u8 bpf2mips64[] = { ++ /* Return value from in-kernel function, and exit value from eBPF */ ++ [BPF_REG_0] = MIPS_R_V0, ++ /* Arguments from eBPF program to in-kernel function */ ++ [BPF_REG_1] = MIPS_R_A0, ++ [BPF_REG_2] = MIPS_R_A1, ++ [BPF_REG_3] = MIPS_R_A2, ++ [BPF_REG_4] = MIPS_R_A3, ++ [BPF_REG_5] = MIPS_R_A4, ++ /* Callee-saved registers that in-kernel function will preserve */ ++ [BPF_REG_6] = MIPS_R_S0, ++ [BPF_REG_7] = MIPS_R_S1, ++ [BPF_REG_8] = MIPS_R_S2, ++ [BPF_REG_9] = MIPS_R_S3, ++ /* Read-only frame pointer to access the eBPF stack */ ++ [BPF_REG_FP] = MIPS_R_FP, ++ /* Temporary register for blinding constants */ ++ [BPF_REG_AX] = MIPS_R_AT, ++ /* Tail call count register, caller-saved */ ++ [JIT_REG_TC] = MIPS_R_A5, ++ /* Constant for register zero-extension */ ++ [JIT_REG_ZX] = MIPS_R_V1, ++}; ++ ++/* ++ * MIPS 32-bit operations on 64-bit registers generate a sign-extended ++ * result. However, the eBPF ISA mandates zero-extension, so we rely on the ++ * verifier to add that for us (emit_zext_ver). In addition, ALU arithmetic ++ * operations, right shift and byte swap require properly sign-extended ++ * operands or the result is unpredictable. We emit explicit sign-extensions ++ * in those cases. ++ */ ++ ++/* Sign extension */ ++static void emit_sext(struct jit_context *ctx, u8 dst, u8 src) ++{ ++ emit(ctx, sll, dst, src, 0); ++ clobber_reg(ctx, dst); ++} ++ ++/* Zero extension */ ++static void emit_zext(struct jit_context *ctx, u8 dst) ++{ ++ if (cpu_has_mips64r2 || cpu_has_mips64r6) { ++ emit(ctx, dinsu, dst, MIPS_R_ZERO, 32, 32); ++ } else { ++ emit(ctx, and, dst, dst, bpf2mips64[JIT_REG_ZX]); ++ access_reg(ctx, JIT_REG_ZX); /* We need the ZX register */ ++ } ++ clobber_reg(ctx, dst); ++} ++ ++/* Zero extension, if verifier does not do it for us */ ++static void emit_zext_ver(struct jit_context *ctx, u8 dst) ++{ ++ if (!ctx->program->aux->verifier_zext) ++ emit_zext(ctx, dst); ++} ++ ++/* dst = imm (64-bit) */ ++static void emit_mov_i64(struct jit_context *ctx, u8 dst, u64 imm64) ++{ ++ if (imm64 >= 0xffffffffffff8000ULL || imm64 < 0x8000ULL) { ++ emit(ctx, daddiu, dst, MIPS_R_ZERO, (s16)imm64); ++ } else if (imm64 >= 0xffffffff80000000ULL || ++ (imm64 < 0x80000000 && imm64 > 0xffff)) { ++ emit(ctx, lui, dst, (s16)(imm64 >> 16)); ++ emit(ctx, ori, dst, dst, (u16)imm64 & 0xffff); ++ } else { ++ u8 acc = MIPS_R_ZERO; ++ int k; ++ ++ for (k = 0; k < 4; k++) { ++ u16 half = imm64 >> (48 - 16 * k); ++ ++ if (acc == dst) ++ emit(ctx, dsll, dst, dst, 16); ++ ++ if (half) { ++ emit(ctx, ori, dst, acc, half); ++ acc = dst; ++ } ++ } ++ } ++ clobber_reg(ctx, dst); ++} ++ ++/* ALU immediate operation (64-bit) */ ++static void emit_alu_i64(struct jit_context *ctx, u8 dst, s32 imm, u8 op) ++{ ++ switch (BPF_OP(op)) { ++ /* dst = dst | imm */ ++ case BPF_OR: ++ emit(ctx, ori, dst, dst, (u16)imm); ++ break; ++ /* dst = dst ^ imm */ ++ case BPF_XOR: ++ emit(ctx, xori, dst, dst, (u16)imm); ++ break; ++ /* dst = -dst */ ++ case BPF_NEG: ++ emit(ctx, dsubu, dst, MIPS_R_ZERO, dst); ++ break; ++ /* dst = dst << imm */ ++ case BPF_LSH: ++ emit(ctx, dsll_safe, dst, dst, imm); ++ break; ++ /* dst = dst >> imm */ ++ case BPF_RSH: ++ emit(ctx, dsrl_safe, dst, dst, imm); ++ break; ++ /* dst = dst >> imm (arithmetic) */ ++ case BPF_ARSH: ++ emit(ctx, dsra_safe, dst, dst, imm); ++ break; ++ /* dst = dst + imm */ ++ case BPF_ADD: ++ emit(ctx, daddiu, dst, dst, imm); ++ break; ++ /* dst = dst - imm */ ++ case BPF_SUB: ++ emit(ctx, daddiu, dst, dst, -imm); ++ break; ++ default: ++ /* Width-generic operations */ ++ emit_alu_i(ctx, dst, imm, op); ++ } ++ clobber_reg(ctx, dst); ++} ++ ++/* ALU register operation (64-bit) */ ++static void emit_alu_r64(struct jit_context *ctx, u8 dst, u8 src, u8 op) ++{ ++ switch (BPF_OP(op)) { ++ /* dst = dst << src */ ++ case BPF_LSH: ++ emit(ctx, dsllv, dst, dst, src); ++ break; ++ /* dst = dst >> src */ ++ case BPF_RSH: ++ emit(ctx, dsrlv, dst, dst, src); ++ break; ++ /* dst = dst >> src (arithmetic) */ ++ case BPF_ARSH: ++ emit(ctx, dsrav, dst, dst, src); ++ break; ++ /* dst = dst + src */ ++ case BPF_ADD: ++ emit(ctx, daddu, dst, dst, src); ++ break; ++ /* dst = dst - src */ ++ case BPF_SUB: ++ emit(ctx, dsubu, dst, dst, src); ++ break; ++ /* dst = dst * src */ ++ case BPF_MUL: ++ if (cpu_has_mips64r6) { ++ emit(ctx, dmulu, dst, dst, src); ++ } else { ++ emit(ctx, dmultu, dst, src); ++ emit(ctx, mflo, dst); ++ } ++ break; ++ /* dst = dst / src */ ++ case BPF_DIV: ++ if (cpu_has_mips64r6) { ++ emit(ctx, ddivu_r6, dst, dst, src); ++ } else { ++ emit(ctx, ddivu, dst, src); ++ emit(ctx, mflo, dst); ++ } ++ break; ++ /* dst = dst % src */ ++ case BPF_MOD: ++ if (cpu_has_mips64r6) { ++ emit(ctx, dmodu, dst, dst, src); ++ } else { ++ emit(ctx, ddivu, dst, src); ++ emit(ctx, mfhi, dst); ++ } ++ break; ++ default: ++ /* Width-generic operations */ ++ emit_alu_r(ctx, dst, src, op); ++ } ++ clobber_reg(ctx, dst); ++} ++ ++/* Swap sub words in a register double word */ ++static void emit_swap_r64(struct jit_context *ctx, u8 dst, u8 mask, u32 bits) ++{ ++ u8 tmp = MIPS_R_T9; ++ ++ emit(ctx, and, tmp, dst, mask); /* tmp = dst & mask */ ++ emit(ctx, dsll, tmp, tmp, bits); /* tmp = tmp << bits */ ++ emit(ctx, dsrl, dst, dst, bits); /* dst = dst >> bits */ ++ emit(ctx, and, dst, dst, mask); /* dst = dst & mask */ ++ emit(ctx, or, dst, dst, tmp); /* dst = dst | tmp */ ++} ++ ++/* Swap bytes and truncate a register double word, word or half word */ ++static void emit_bswap_r64(struct jit_context *ctx, u8 dst, u32 width) ++{ ++ switch (width) { ++ /* Swap bytes in a double word */ ++ case 64: ++ if (cpu_has_mips64r2 || cpu_has_mips64r6) { ++ emit(ctx, dsbh, dst, dst); ++ emit(ctx, dshd, dst, dst); ++ } else { ++ u8 t1 = MIPS_R_T6; ++ u8 t2 = MIPS_R_T7; ++ ++ emit(ctx, dsll32, t2, dst, 0); /* t2 = dst << 32 */ ++ emit(ctx, dsrl32, dst, dst, 0); /* dst = dst >> 32 */ ++ emit(ctx, or, dst, dst, t2); /* dst = dst | t2 */ ++ ++ emit(ctx, ori, t2, MIPS_R_ZERO, 0xffff); ++ emit(ctx, dsll32, t1, t2, 0); /* t1 = t2 << 32 */ ++ emit(ctx, or, t1, t1, t2); /* t1 = t1 | t2 */ ++ emit_swap_r64(ctx, dst, t1, 16);/* dst = swap16(dst) */ ++ ++ emit(ctx, lui, t2, 0xff); /* t2 = 0x00ff0000 */ ++ emit(ctx, ori, t2, t2, 0xff); /* t2 = t2 | 0x00ff */ ++ emit(ctx, dsll32, t1, t2, 0); /* t1 = t2 << 32 */ ++ emit(ctx, or, t1, t1, t2); /* t1 = t1 | t2 */ ++ emit_swap_r64(ctx, dst, t1, 8); /* dst = swap8(dst) */ ++ } ++ break; ++ /* Swap bytes in a half word */ ++ /* Swap bytes in a word */ ++ case 32: ++ case 16: ++ emit_sext(ctx, dst, dst); ++ emit_bswap_r(ctx, dst, width); ++ if (cpu_has_mips64r2 || cpu_has_mips64r6) ++ emit_zext(ctx, dst); ++ break; ++ } ++ clobber_reg(ctx, dst); ++} ++ ++/* Truncate a register double word, word or half word */ ++static void emit_trunc_r64(struct jit_context *ctx, u8 dst, u32 width) ++{ ++ switch (width) { ++ case 64: ++ break; ++ /* Zero-extend a word */ ++ case 32: ++ emit_zext(ctx, dst); ++ break; ++ /* Zero-extend a half word */ ++ case 16: ++ emit(ctx, andi, dst, dst, 0xffff); ++ break; ++ } ++ clobber_reg(ctx, dst); ++} ++ ++/* Load operation: dst = *(size*)(src + off) */ ++static void emit_ldx(struct jit_context *ctx, u8 dst, u8 src, s16 off, u8 size) ++{ ++ switch (size) { ++ /* Load a byte */ ++ case BPF_B: ++ emit(ctx, lbu, dst, off, src); ++ break; ++ /* Load a half word */ ++ case BPF_H: ++ emit(ctx, lhu, dst, off, src); ++ break; ++ /* Load a word */ ++ case BPF_W: ++ emit(ctx, lwu, dst, off, src); ++ break; ++ /* Load a double word */ ++ case BPF_DW: ++ emit(ctx, ld, dst, off, src); ++ break; ++ } ++ clobber_reg(ctx, dst); ++} ++ ++/* Store operation: *(size *)(dst + off) = src */ ++static void emit_stx(struct jit_context *ctx, u8 dst, u8 src, s16 off, u8 size) ++{ ++ switch (size) { ++ /* Store a byte */ ++ case BPF_B: ++ emit(ctx, sb, src, off, dst); ++ break; ++ /* Store a half word */ ++ case BPF_H: ++ emit(ctx, sh, src, off, dst); ++ break; ++ /* Store a word */ ++ case BPF_W: ++ emit(ctx, sw, src, off, dst); ++ break; ++ /* Store a double word */ ++ case BPF_DW: ++ emit(ctx, sd, src, off, dst); ++ break; ++ } ++} ++ ++/* Atomic read-modify-write */ ++static void emit_atomic_r64(struct jit_context *ctx, ++ u8 dst, u8 src, s16 off, u8 code) ++{ ++ u8 t1 = MIPS_R_T6; ++ u8 t2 = MIPS_R_T7; ++ ++ emit(ctx, lld, t1, off, dst); ++ switch (code) { ++ case BPF_ADD: ++ emit(ctx, daddu, t2, t1, src); ++ break; ++ case BPF_AND: ++ emit(ctx, and, t2, t1, src); ++ break; ++ case BPF_OR: ++ emit(ctx, or, t2, t1, src); ++ break; ++ case BPF_XOR: ++ emit(ctx, xor, t2, t1, src); ++ break; ++ } ++ emit(ctx, scd, t2, off, dst); ++ emit(ctx, beqz, t2, -16); ++ emit(ctx, nop); /* Delay slot */ ++} ++ ++/* Function call */ ++static int emit_call(struct jit_context *ctx, const struct bpf_insn *insn) ++{ ++ u8 zx = bpf2mips64[JIT_REG_ZX]; ++ u8 tmp = MIPS_R_T6; ++ bool fixed; ++ u64 addr; ++ ++ /* Decode the call address */ ++ if (bpf_jit_get_func_addr(ctx->program, insn, false, ++ &addr, &fixed) < 0) ++ return -1; ++ if (!fixed) ++ return -1; ++ ++ /* Push caller-saved registers on stack */ ++ push_regs(ctx, ctx->clobbered & JIT_CALLER_REGS, 0, 0); ++ ++ /* Emit function call */ ++ emit_mov_i64(ctx, tmp, addr); ++ emit(ctx, jalr, MIPS_R_RA, tmp); ++ emit(ctx, nop); /* Delay slot */ ++ ++ /* Restore caller-saved registers */ ++ pop_regs(ctx, ctx->clobbered & JIT_CALLER_REGS, 0, 0); ++ ++ /* Re-initialize the JIT zero-extension register if accessed */ ++ if (ctx->accessed & BIT(JIT_REG_ZX)) { ++ emit(ctx, daddiu, zx, MIPS_R_ZERO, -1); ++ emit(ctx, dsrl32, zx, zx, 0); ++ } ++ ++ clobber_reg(ctx, MIPS_R_RA); ++ clobber_reg(ctx, MIPS_R_V0); ++ clobber_reg(ctx, MIPS_R_V1); ++ return 0; ++} ++ ++/* Function tail call */ ++static int emit_tail_call(struct jit_context *ctx) ++{ ++ u8 ary = bpf2mips64[BPF_REG_2]; ++ u8 ind = bpf2mips64[BPF_REG_3]; ++ u8 tcc = bpf2mips64[JIT_REG_TC]; ++ u8 tmp = MIPS_R_T6; ++ int off; ++ ++ /* ++ * Tail call: ++ * eBPF R1 - function argument (context ptr), passed in a0-a1 ++ * eBPF R2 - ptr to object with array of function entry points ++ * eBPF R3 - array index of function to be called ++ */ ++ ++ /* if (ind >= ary->map.max_entries) goto out */ ++ off = offsetof(struct bpf_array, map.max_entries); ++ if (off > 0x7fff) ++ return -1; ++ emit(ctx, lwu, tmp, off, ary); /* tmp = ary->map.max_entrs*/ ++ emit(ctx, sltu, tmp, ind, tmp); /* tmp = ind < t1 */ ++ emit(ctx, beqz, tmp, get_offset(ctx, 1)); /* PC += off(1) if tmp == 0*/ ++ ++ /* if (--TCC < 0) goto out */ ++ emit(ctx, daddiu, tcc, tcc, -1); /* tcc-- (delay slot) */ ++ emit(ctx, bltz, tcc, get_offset(ctx, 1)); /* PC += off(1) if tcc < 0 */ ++ /* (next insn delay slot) */ ++ /* prog = ary->ptrs[ind] */ ++ off = offsetof(struct bpf_array, ptrs); ++ if (off > 0x7fff) ++ return -1; ++ emit(ctx, dsll, tmp, ind, 3); /* tmp = ind << 3 */ ++ emit(ctx, daddu, tmp, tmp, ary); /* tmp += ary */ ++ emit(ctx, ld, tmp, off, tmp); /* tmp = *(tmp + off) */ ++ ++ /* if (prog == 0) goto out */ ++ emit(ctx, beqz, tmp, get_offset(ctx, 1)); /* PC += off(1) if tmp == 0*/ ++ emit(ctx, nop); /* Delay slot */ ++ ++ /* func = prog->bpf_func + 8 (prologue skip offset) */ ++ off = offsetof(struct bpf_prog, bpf_func); ++ if (off > 0x7fff) ++ return -1; ++ emit(ctx, ld, tmp, off, tmp); /* tmp = *(tmp + off) */ ++ emit(ctx, daddiu, tmp, tmp, JIT_TCALL_SKIP); /* tmp += skip (4) */ ++ ++ /* goto func */ ++ build_epilogue(ctx, tmp); ++ access_reg(ctx, JIT_REG_TC); ++ return 0; ++} ++ ++/* ++ * Stack frame layout for a JITed program (stack grows down). ++ * ++ * Higher address : Previous stack frame : ++ * +===========================+ <--- MIPS sp before call ++ * | Callee-saved registers, | ++ * | including RA and FP | ++ * +---------------------------+ <--- eBPF FP (MIPS fp) ++ * | Local eBPF variables | ++ * | allocated by program | ++ * +---------------------------+ ++ * | Reserved for caller-saved | ++ * | registers | ++ * Lower address +===========================+ <--- MIPS sp ++ */ ++ ++/* Build program prologue to set up the stack and registers */ ++void build_prologue(struct jit_context *ctx) ++{ ++ u8 fp = bpf2mips64[BPF_REG_FP]; ++ u8 tc = bpf2mips64[JIT_REG_TC]; ++ u8 zx = bpf2mips64[JIT_REG_ZX]; ++ int stack, saved, locals, reserved; ++ ++ /* ++ * The first instruction initializes the tail call count register. ++ * On a tail call, the calling function jumps into the prologue ++ * after this instruction. ++ */ ++ emit(ctx, addiu, tc, MIPS_R_ZERO, min(MAX_TAIL_CALL_CNT + 1, 0xffff)); ++ ++ /* === Entry-point for tail calls === */ ++ ++ /* ++ * If the eBPF frame pointer and tail call count registers were ++ * accessed they must be preserved. Mark them as clobbered here ++ * to save and restore them on the stack as needed. ++ */ ++ if (ctx->accessed & BIT(BPF_REG_FP)) ++ clobber_reg(ctx, fp); ++ if (ctx->accessed & BIT(JIT_REG_TC)) ++ clobber_reg(ctx, tc); ++ if (ctx->accessed & BIT(JIT_REG_ZX)) ++ clobber_reg(ctx, zx); ++ ++ /* Compute the stack space needed for callee-saved registers */ ++ saved = hweight32(ctx->clobbered & JIT_CALLEE_REGS) * sizeof(u64); ++ saved = ALIGN(saved, MIPS_STACK_ALIGNMENT); ++ ++ /* Stack space used by eBPF program local data */ ++ locals = ALIGN(ctx->program->aux->stack_depth, MIPS_STACK_ALIGNMENT); ++ ++ /* ++ * If we are emitting function calls, reserve extra stack space for ++ * caller-saved registers needed by the JIT. The required space is ++ * computed automatically during resource usage discovery (pass 1). ++ */ ++ reserved = ctx->stack_used; ++ ++ /* Allocate the stack frame */ ++ stack = ALIGN(saved + locals + reserved, MIPS_STACK_ALIGNMENT); ++ if (stack) ++ emit(ctx, daddiu, MIPS_R_SP, MIPS_R_SP, -stack); ++ ++ /* Store callee-saved registers on stack */ ++ push_regs(ctx, ctx->clobbered & JIT_CALLEE_REGS, 0, stack - saved); ++ ++ /* Initialize the eBPF frame pointer if accessed */ ++ if (ctx->accessed & BIT(BPF_REG_FP)) ++ emit(ctx, daddiu, fp, MIPS_R_SP, stack - saved); ++ ++ /* Initialize the ePF JIT zero-extension register if accessed */ ++ if (ctx->accessed & BIT(JIT_REG_ZX)) { ++ emit(ctx, daddiu, zx, MIPS_R_ZERO, -1); ++ emit(ctx, dsrl32, zx, zx, 0); ++ } ++ ++ ctx->saved_size = saved; ++ ctx->stack_size = stack; ++} ++ ++/* Build the program epilogue to restore the stack and registers */ ++void build_epilogue(struct jit_context *ctx, int dest_reg) ++{ ++ /* Restore callee-saved registers from stack */ ++ pop_regs(ctx, ctx->clobbered & JIT_CALLEE_REGS, 0, ++ ctx->stack_size - ctx->saved_size); ++ ++ /* Release the stack frame */ ++ if (ctx->stack_size) ++ emit(ctx, daddiu, MIPS_R_SP, MIPS_R_SP, ctx->stack_size); ++ ++ /* Jump to return address and sign-extend the 32-bit return value */ ++ emit(ctx, jr, dest_reg); ++ emit(ctx, sll, MIPS_R_V0, MIPS_R_V0, 0); /* Delay slot */ ++} ++ ++/* Build one eBPF instruction */ ++int build_insn(const struct bpf_insn *insn, struct jit_context *ctx) ++{ ++ u8 dst = bpf2mips64[insn->dst_reg]; ++ u8 src = bpf2mips64[insn->src_reg]; ++ u8 code = insn->code; ++ s16 off = insn->off; ++ s32 imm = insn->imm; ++ s32 val, rel; ++ u8 alu, jmp; ++ ++ switch (code) { ++ /* ALU operations */ ++ /* dst = imm */ ++ case BPF_ALU | BPF_MOV | BPF_K: ++ emit_mov_i(ctx, dst, imm); ++ emit_zext_ver(ctx, dst); ++ break; ++ /* dst = src */ ++ case BPF_ALU | BPF_MOV | BPF_X: ++ if (imm == 1) { ++ /* Special mov32 for zext */ ++ emit_zext(ctx, dst); ++ } else { ++ emit_mov_r(ctx, dst, src); ++ emit_zext_ver(ctx, dst); ++ } ++ break; ++ /* dst = -dst */ ++ case BPF_ALU | BPF_NEG: ++ emit_sext(ctx, dst, dst); ++ emit_alu_i(ctx, dst, 0, BPF_NEG); ++ emit_zext_ver(ctx, dst); ++ break; ++ /* dst = dst & imm */ ++ /* dst = dst | imm */ ++ /* dst = dst ^ imm */ ++ /* dst = dst << imm */ ++ case BPF_ALU | BPF_OR | BPF_K: ++ case BPF_ALU | BPF_AND | BPF_K: ++ case BPF_ALU | BPF_XOR | BPF_K: ++ case BPF_ALU | BPF_LSH | BPF_K: ++ if (!valid_alu_i(BPF_OP(code), imm)) { ++ emit_mov_i(ctx, MIPS_R_T4, imm); ++ emit_alu_r(ctx, dst, MIPS_R_T4, BPF_OP(code)); ++ } else if (rewrite_alu_i(BPF_OP(code), imm, &alu, &val)) { ++ emit_alu_i(ctx, dst, val, alu); ++ } ++ emit_zext_ver(ctx, dst); ++ break; ++ /* dst = dst >> imm */ ++ /* dst = dst >> imm (arithmetic) */ ++ /* dst = dst + imm */ ++ /* dst = dst - imm */ ++ /* dst = dst * imm */ ++ /* dst = dst / imm */ ++ /* dst = dst % imm */ ++ case BPF_ALU | BPF_RSH | BPF_K: ++ case BPF_ALU | BPF_ARSH | BPF_K: ++ case BPF_ALU | BPF_ADD | BPF_K: ++ case BPF_ALU | BPF_SUB | BPF_K: ++ case BPF_ALU | BPF_MUL | BPF_K: ++ case BPF_ALU | BPF_DIV | BPF_K: ++ case BPF_ALU | BPF_MOD | BPF_K: ++ if (!valid_alu_i(BPF_OP(code), imm)) { ++ emit_sext(ctx, dst, dst); ++ emit_mov_i(ctx, MIPS_R_T4, imm); ++ emit_alu_r(ctx, dst, MIPS_R_T4, BPF_OP(code)); ++ } else if (rewrite_alu_i(BPF_OP(code), imm, &alu, &val)) { ++ emit_sext(ctx, dst, dst); ++ emit_alu_i(ctx, dst, val, alu); ++ } ++ emit_zext_ver(ctx, dst); ++ break; ++ /* dst = dst & src */ ++ /* dst = dst | src */ ++ /* dst = dst ^ src */ ++ /* dst = dst << src */ ++ case BPF_ALU | BPF_AND | BPF_X: ++ case BPF_ALU | BPF_OR | BPF_X: ++ case BPF_ALU | BPF_XOR | BPF_X: ++ case BPF_ALU | BPF_LSH | BPF_X: ++ emit_alu_r(ctx, dst, src, BPF_OP(code)); ++ emit_zext_ver(ctx, dst); ++ break; ++ /* dst = dst >> src */ ++ /* dst = dst >> src (arithmetic) */ ++ /* dst = dst + src */ ++ /* dst = dst - src */ ++ /* dst = dst * src */ ++ /* dst = dst / src */ ++ /* dst = dst % src */ ++ case BPF_ALU | BPF_RSH | BPF_X: ++ case BPF_ALU | BPF_ARSH | BPF_X: ++ case BPF_ALU | BPF_ADD | BPF_X: ++ case BPF_ALU | BPF_SUB | BPF_X: ++ case BPF_ALU | BPF_MUL | BPF_X: ++ case BPF_ALU | BPF_DIV | BPF_X: ++ case BPF_ALU | BPF_MOD | BPF_X: ++ emit_sext(ctx, dst, dst); ++ emit_sext(ctx, MIPS_R_T4, src); ++ emit_alu_r(ctx, dst, MIPS_R_T4, BPF_OP(code)); ++ emit_zext_ver(ctx, dst); ++ break; ++ /* dst = imm (64-bit) */ ++ case BPF_ALU64 | BPF_MOV | BPF_K: ++ emit_mov_i(ctx, dst, imm); ++ break; ++ /* dst = src (64-bit) */ ++ case BPF_ALU64 | BPF_MOV | BPF_X: ++ emit_mov_r(ctx, dst, src); ++ break; ++ /* dst = -dst (64-bit) */ ++ case BPF_ALU64 | BPF_NEG: ++ emit_alu_i64(ctx, dst, 0, BPF_NEG); ++ break; ++ /* dst = dst & imm (64-bit) */ ++ /* dst = dst | imm (64-bit) */ ++ /* dst = dst ^ imm (64-bit) */ ++ /* dst = dst << imm (64-bit) */ ++ /* dst = dst >> imm (64-bit) */ ++ /* dst = dst >> imm ((64-bit, arithmetic) */ ++ /* dst = dst + imm (64-bit) */ ++ /* dst = dst - imm (64-bit) */ ++ /* dst = dst * imm (64-bit) */ ++ /* dst = dst / imm (64-bit) */ ++ /* dst = dst % imm (64-bit) */ ++ case BPF_ALU64 | BPF_AND | BPF_K: ++ case BPF_ALU64 | BPF_OR | BPF_K: ++ case BPF_ALU64 | BPF_XOR | BPF_K: ++ case BPF_ALU64 | BPF_LSH | BPF_K: ++ case BPF_ALU64 | BPF_RSH | BPF_K: ++ case BPF_ALU64 | BPF_ARSH | BPF_K: ++ case BPF_ALU64 | BPF_ADD | BPF_K: ++ case BPF_ALU64 | BPF_SUB | BPF_K: ++ case BPF_ALU64 | BPF_MUL | BPF_K: ++ case BPF_ALU64 | BPF_DIV | BPF_K: ++ case BPF_ALU64 | BPF_MOD | BPF_K: ++ if (!valid_alu_i(BPF_OP(code), imm)) { ++ emit_mov_i(ctx, MIPS_R_T4, imm); ++ emit_alu_r64(ctx, dst, MIPS_R_T4, BPF_OP(code)); ++ } else if (rewrite_alu_i(BPF_OP(code), imm, &alu, &val)) { ++ emit_alu_i64(ctx, dst, val, alu); ++ } ++ break; ++ /* dst = dst & src (64-bit) */ ++ /* dst = dst | src (64-bit) */ ++ /* dst = dst ^ src (64-bit) */ ++ /* dst = dst << src (64-bit) */ ++ /* dst = dst >> src (64-bit) */ ++ /* dst = dst >> src (64-bit, arithmetic) */ ++ /* dst = dst + src (64-bit) */ ++ /* dst = dst - src (64-bit) */ ++ /* dst = dst * src (64-bit) */ ++ /* dst = dst / src (64-bit) */ ++ /* dst = dst % src (64-bit) */ ++ case BPF_ALU64 | BPF_AND | BPF_X: ++ case BPF_ALU64 | BPF_OR | BPF_X: ++ case BPF_ALU64 | BPF_XOR | BPF_X: ++ case BPF_ALU64 | BPF_LSH | BPF_X: ++ case BPF_ALU64 | BPF_RSH | BPF_X: ++ case BPF_ALU64 | BPF_ARSH | BPF_X: ++ case BPF_ALU64 | BPF_ADD | BPF_X: ++ case BPF_ALU64 | BPF_SUB | BPF_X: ++ case BPF_ALU64 | BPF_MUL | BPF_X: ++ case BPF_ALU64 | BPF_DIV | BPF_X: ++ case BPF_ALU64 | BPF_MOD | BPF_X: ++ emit_alu_r64(ctx, dst, src, BPF_OP(code)); ++ break; ++ /* dst = htole(dst) */ ++ /* dst = htobe(dst) */ ++ case BPF_ALU | BPF_END | BPF_FROM_LE: ++ case BPF_ALU | BPF_END | BPF_FROM_BE: ++ if (BPF_SRC(code) == ++#ifdef __BIG_ENDIAN ++ BPF_FROM_LE ++#else ++ BPF_FROM_BE ++#endif ++ ) ++ emit_bswap_r64(ctx, dst, imm); ++ else ++ emit_trunc_r64(ctx, dst, imm); ++ break; ++ /* dst = imm64 */ ++ case BPF_LD | BPF_IMM | BPF_DW: ++ emit_mov_i64(ctx, dst, (u32)imm | ((u64)insn[1].imm << 32)); ++ return 1; ++ /* LDX: dst = *(size *)(src + off) */ ++ case BPF_LDX | BPF_MEM | BPF_W: ++ case BPF_LDX | BPF_MEM | BPF_H: ++ case BPF_LDX | BPF_MEM | BPF_B: ++ case BPF_LDX | BPF_MEM | BPF_DW: ++ emit_ldx(ctx, dst, src, off, BPF_SIZE(code)); ++ break; ++ /* ST: *(size *)(dst + off) = imm */ ++ case BPF_ST | BPF_MEM | BPF_W: ++ case BPF_ST | BPF_MEM | BPF_H: ++ case BPF_ST | BPF_MEM | BPF_B: ++ case BPF_ST | BPF_MEM | BPF_DW: ++ emit_mov_i(ctx, MIPS_R_T4, imm); ++ emit_stx(ctx, dst, MIPS_R_T4, off, BPF_SIZE(code)); ++ break; ++ /* STX: *(size *)(dst + off) = src */ ++ case BPF_STX | BPF_MEM | BPF_W: ++ case BPF_STX | BPF_MEM | BPF_H: ++ case BPF_STX | BPF_MEM | BPF_B: ++ case BPF_STX | BPF_MEM | BPF_DW: ++ emit_stx(ctx, dst, src, off, BPF_SIZE(code)); ++ break; ++ /* Speculation barrier */ ++ case BPF_ST | BPF_NOSPEC: ++ break; ++ /* Atomics */ ++ case BPF_STX | BPF_XADD | BPF_W: ++ case BPF_STX | BPF_XADD | BPF_DW: ++ switch (imm) { ++ case BPF_ADD: ++ case BPF_AND: ++ case BPF_OR: ++ case BPF_XOR: ++ if (BPF_SIZE(code) == BPF_DW) { ++ emit_atomic_r64(ctx, dst, src, off, imm); ++ } else { /* 32-bit, no fetch */ ++ emit_sext(ctx, MIPS_R_T4, src); ++ emit_atomic_r(ctx, dst, MIPS_R_T4, off, imm); ++ } ++ break; ++ default: ++ goto notyet; ++ } ++ break; ++ /* PC += off if dst == src */ ++ /* PC += off if dst != src */ ++ /* PC += off if dst & src */ ++ /* PC += off if dst > src */ ++ /* PC += off if dst >= src */ ++ /* PC += off if dst < src */ ++ /* PC += off if dst <= src */ ++ /* PC += off if dst > src (signed) */ ++ /* PC += off if dst >= src (signed) */ ++ /* PC += off if dst < src (signed) */ ++ /* PC += off if dst <= src (signed) */ ++ case BPF_JMP32 | BPF_JEQ | BPF_X: ++ case BPF_JMP32 | BPF_JNE | BPF_X: ++ case BPF_JMP32 | BPF_JSET | BPF_X: ++ case BPF_JMP32 | BPF_JGT | BPF_X: ++ case BPF_JMP32 | BPF_JGE | BPF_X: ++ case BPF_JMP32 | BPF_JLT | BPF_X: ++ case BPF_JMP32 | BPF_JLE | BPF_X: ++ case BPF_JMP32 | BPF_JSGT | BPF_X: ++ case BPF_JMP32 | BPF_JSGE | BPF_X: ++ case BPF_JMP32 | BPF_JSLT | BPF_X: ++ case BPF_JMP32 | BPF_JSLE | BPF_X: ++ if (off == 0) ++ break; ++ setup_jmp_r(ctx, dst == src, BPF_OP(code), off, &jmp, &rel); ++ emit_sext(ctx, MIPS_R_T4, dst); /* Sign-extended dst */ ++ emit_sext(ctx, MIPS_R_T5, src); /* Sign-extended src */ ++ emit_jmp_r(ctx, MIPS_R_T4, MIPS_R_T5, rel, jmp); ++ if (finish_jmp(ctx, jmp, off) < 0) ++ goto toofar; ++ break; ++ /* PC += off if dst == imm */ ++ /* PC += off if dst != imm */ ++ /* PC += off if dst & imm */ ++ /* PC += off if dst > imm */ ++ /* PC += off if dst >= imm */ ++ /* PC += off if dst < imm */ ++ /* PC += off if dst <= imm */ ++ /* PC += off if dst > imm (signed) */ ++ /* PC += off if dst >= imm (signed) */ ++ /* PC += off if dst < imm (signed) */ ++ /* PC += off if dst <= imm (signed) */ ++ case BPF_JMP32 | BPF_JEQ | BPF_K: ++ case BPF_JMP32 | BPF_JNE | BPF_K: ++ case BPF_JMP32 | BPF_JSET | BPF_K: ++ case BPF_JMP32 | BPF_JGT | BPF_K: ++ case BPF_JMP32 | BPF_JGE | BPF_K: ++ case BPF_JMP32 | BPF_JLT | BPF_K: ++ case BPF_JMP32 | BPF_JLE | BPF_K: ++ case BPF_JMP32 | BPF_JSGT | BPF_K: ++ case BPF_JMP32 | BPF_JSGE | BPF_K: ++ case BPF_JMP32 | BPF_JSLT | BPF_K: ++ case BPF_JMP32 | BPF_JSLE | BPF_K: ++ if (off == 0) ++ break; ++ setup_jmp_i(ctx, imm, 32, BPF_OP(code), off, &jmp, &rel); ++ emit_sext(ctx, MIPS_R_T4, dst); /* Sign-extended dst */ ++ if (valid_jmp_i(jmp, imm)) { ++ emit_jmp_i(ctx, MIPS_R_T4, imm, rel, jmp); ++ } else { ++ /* Move large immediate to register, sign-extended */ ++ emit_mov_i(ctx, MIPS_R_T5, imm); ++ emit_jmp_r(ctx, MIPS_R_T4, MIPS_R_T5, rel, jmp); ++ } ++ if (finish_jmp(ctx, jmp, off) < 0) ++ goto toofar; ++ break; ++ /* PC += off if dst == src */ ++ /* PC += off if dst != src */ ++ /* PC += off if dst & src */ ++ /* PC += off if dst > src */ ++ /* PC += off if dst >= src */ ++ /* PC += off if dst < src */ ++ /* PC += off if dst <= src */ ++ /* PC += off if dst > src (signed) */ ++ /* PC += off if dst >= src (signed) */ ++ /* PC += off if dst < src (signed) */ ++ /* PC += off if dst <= src (signed) */ ++ case BPF_JMP | BPF_JEQ | BPF_X: ++ case BPF_JMP | BPF_JNE | BPF_X: ++ case BPF_JMP | BPF_JSET | BPF_X: ++ case BPF_JMP | BPF_JGT | BPF_X: ++ case BPF_JMP | BPF_JGE | BPF_X: ++ case BPF_JMP | BPF_JLT | BPF_X: ++ case BPF_JMP | BPF_JLE | BPF_X: ++ case BPF_JMP | BPF_JSGT | BPF_X: ++ case BPF_JMP | BPF_JSGE | BPF_X: ++ case BPF_JMP | BPF_JSLT | BPF_X: ++ case BPF_JMP | BPF_JSLE | BPF_X: ++ if (off == 0) ++ break; ++ setup_jmp_r(ctx, dst == src, BPF_OP(code), off, &jmp, &rel); ++ emit_jmp_r(ctx, dst, src, rel, jmp); ++ if (finish_jmp(ctx, jmp, off) < 0) ++ goto toofar; ++ break; ++ /* PC += off if dst == imm */ ++ /* PC += off if dst != imm */ ++ /* PC += off if dst & imm */ ++ /* PC += off if dst > imm */ ++ /* PC += off if dst >= imm */ ++ /* PC += off if dst < imm */ ++ /* PC += off if dst <= imm */ ++ /* PC += off if dst > imm (signed) */ ++ /* PC += off if dst >= imm (signed) */ ++ /* PC += off if dst < imm (signed) */ ++ /* PC += off if dst <= imm (signed) */ ++ case BPF_JMP | BPF_JEQ | BPF_K: ++ case BPF_JMP | BPF_JNE | BPF_K: ++ case BPF_JMP | BPF_JSET | BPF_K: ++ case BPF_JMP | BPF_JGT | BPF_K: ++ case BPF_JMP | BPF_JGE | BPF_K: ++ case BPF_JMP | BPF_JLT | BPF_K: ++ case BPF_JMP | BPF_JLE | BPF_K: ++ case BPF_JMP | BPF_JSGT | BPF_K: ++ case BPF_JMP | BPF_JSGE | BPF_K: ++ case BPF_JMP | BPF_JSLT | BPF_K: ++ case BPF_JMP | BPF_JSLE | BPF_K: ++ if (off == 0) ++ break; ++ setup_jmp_i(ctx, imm, 64, BPF_OP(code), off, &jmp, &rel); ++ if (valid_jmp_i(jmp, imm)) { ++ emit_jmp_i(ctx, dst, imm, rel, jmp); ++ } else { ++ /* Move large immediate to register */ ++ emit_mov_i(ctx, MIPS_R_T4, imm); ++ emit_jmp_r(ctx, dst, MIPS_R_T4, rel, jmp); ++ } ++ if (finish_jmp(ctx, jmp, off) < 0) ++ goto toofar; ++ break; ++ /* PC += off */ ++ case BPF_JMP | BPF_JA: ++ if (off == 0) ++ break; ++ if (emit_ja(ctx, off) < 0) ++ goto toofar; ++ break; ++ /* Tail call */ ++ case BPF_JMP | BPF_TAIL_CALL: ++ if (emit_tail_call(ctx) < 0) ++ goto invalid; ++ break; ++ /* Function call */ ++ case BPF_JMP | BPF_CALL: ++ if (emit_call(ctx, insn) < 0) ++ goto invalid; ++ break; ++ /* Function return */ ++ case BPF_JMP | BPF_EXIT: ++ /* ++ * Optimization: when last instruction is EXIT ++ * simply continue to epilogue. ++ */ ++ if (ctx->bpf_index == ctx->program->len - 1) ++ break; ++ if (emit_exit(ctx) < 0) ++ goto toofar; ++ break; ++ ++ default: ++invalid: ++ pr_err_once("unknown opcode %02x\n", code); ++ return -EINVAL; ++notyet: ++ pr_info_once("*** NOT YET: opcode %02x ***\n", code); ++ return -EFAULT; ++toofar: ++ pr_info_once("*** TOO FAR: jump at %u opcode %02x ***\n", ++ ctx->bpf_index, code); ++ return -E2BIG; ++ } ++ return 0; ++} diff --git a/pkgs/patches-linux-5.15/050-v5.16-04-mips-bpf-Add-JIT-workarounds-for-CPU-errata.patch b/pkgs/patches-linux-5.15/050-v5.16-04-mips-bpf-Add-JIT-workarounds-for-CPU-errata.patch new file mode 100644 index 0000000..63553eb --- /dev/null +++ b/pkgs/patches-linux-5.15/050-v5.16-04-mips-bpf-Add-JIT-workarounds-for-CPU-errata.patch @@ -0,0 +1,120 @@ +From: Johan Almbladh <johan.almbladh@anyfinetworks.com> +Date: Tue, 5 Oct 2021 18:54:06 +0200 +Subject: [PATCH] mips: bpf: Add JIT workarounds for CPU errata + +This patch adds workarounds for the following CPU errata to the MIPS +eBPF JIT, if enabled in the kernel configuration. + + - R10000 ll/sc weak ordering + - Loongson-3 ll/sc weak ordering + - Loongson-2F jump hang + +The Loongson-2F nop errata is implemented in uasm, which the JIT uses, +so no additional mitigations are needed for that. + +Signed-off-by: Johan Almbladh <johan.almbladh@anyfinetworks.com> +Reviewed-by: Jiaxun Yang <jiaxun.yang@flygoat.com> +--- + +--- a/arch/mips/net/bpf_jit_comp.c ++++ b/arch/mips/net/bpf_jit_comp.c +@@ -404,6 +404,7 @@ void emit_alu_r(struct jit_context *ctx, + /* Atomic read-modify-write (32-bit) */ + void emit_atomic_r(struct jit_context *ctx, u8 dst, u8 src, s16 off, u8 code) + { ++ LLSC_sync(ctx); + emit(ctx, ll, MIPS_R_T9, off, dst); + switch (code) { + case BPF_ADD: +@@ -420,18 +421,19 @@ void emit_atomic_r(struct jit_context *c + break; + } + emit(ctx, sc, MIPS_R_T8, off, dst); +- emit(ctx, beqz, MIPS_R_T8, -16); ++ emit(ctx, LLSC_beqz, MIPS_R_T8, -16 - LLSC_offset); + emit(ctx, nop); /* Delay slot */ + } + + /* Atomic compare-and-exchange (32-bit) */ + void emit_cmpxchg_r(struct jit_context *ctx, u8 dst, u8 src, u8 res, s16 off) + { ++ LLSC_sync(ctx); + emit(ctx, ll, MIPS_R_T9, off, dst); + emit(ctx, bne, MIPS_R_T9, res, 12); + emit(ctx, move, MIPS_R_T8, src); /* Delay slot */ + emit(ctx, sc, MIPS_R_T8, off, dst); +- emit(ctx, beqz, MIPS_R_T8, -20); ++ emit(ctx, LLSC_beqz, MIPS_R_T8, -20 - LLSC_offset); + emit(ctx, move, res, MIPS_R_T9); /* Delay slot */ + clobber_reg(ctx, res); + } +--- a/arch/mips/net/bpf_jit_comp.h ++++ b/arch/mips/net/bpf_jit_comp.h +@@ -87,7 +87,7 @@ struct jit_context { + }; + + /* Emit the instruction if the JIT memory space has been allocated */ +-#define emit(ctx, func, ...) \ ++#define __emit(ctx, func, ...) \ + do { \ + if ((ctx)->target != NULL) { \ + u32 *p = &(ctx)->target[ctx->jit_index]; \ +@@ -95,6 +95,30 @@ do { \ + } \ + (ctx)->jit_index++; \ + } while (0) ++#define emit(...) __emit(__VA_ARGS__) ++ ++/* Workaround for R10000 ll/sc errata */ ++#ifdef CONFIG_WAR_R10000 ++#define LLSC_beqz beqzl ++#else ++#define LLSC_beqz beqz ++#endif ++ ++/* Workaround for Loongson-3 ll/sc errata */ ++#ifdef CONFIG_CPU_LOONGSON3_WORKAROUNDS ++#define LLSC_sync(ctx) emit(ctx, sync, 0) ++#define LLSC_offset 4 ++#else ++#define LLSC_sync(ctx) ++#define LLSC_offset 0 ++#endif ++ ++/* Workaround for Loongson-2F jump errata */ ++#ifdef CONFIG_CPU_JUMP_WORKAROUNDS ++#define JALR_MASK 0xffffffffcfffffffULL ++#else ++#define JALR_MASK (~0ULL) ++#endif + + /* + * Mark a BPF register as accessed, it needs to be +--- a/arch/mips/net/bpf_jit_comp64.c ++++ b/arch/mips/net/bpf_jit_comp64.c +@@ -375,6 +375,7 @@ static void emit_atomic_r64(struct jit_c + u8 t1 = MIPS_R_T6; + u8 t2 = MIPS_R_T7; + ++ LLSC_sync(ctx); + emit(ctx, lld, t1, off, dst); + switch (code) { + case BPF_ADD: +@@ -391,7 +392,7 @@ static void emit_atomic_r64(struct jit_c + break; + } + emit(ctx, scd, t2, off, dst); +- emit(ctx, beqz, t2, -16); ++ emit(ctx, LLSC_beqz, t2, -16 - LLSC_offset); + emit(ctx, nop); /* Delay slot */ + } + +@@ -414,7 +415,7 @@ static int emit_call(struct jit_context + push_regs(ctx, ctx->clobbered & JIT_CALLER_REGS, 0, 0); + + /* Emit function call */ +- emit_mov_i64(ctx, tmp, addr); ++ emit_mov_i64(ctx, tmp, addr & JALR_MASK); + emit(ctx, jalr, MIPS_R_RA, tmp); + emit(ctx, nop); /* Delay slot */ + diff --git a/pkgs/patches-linux-5.15/050-v5.16-05-mips-bpf-Enable-eBPF-JITs.patch b/pkgs/patches-linux-5.15/050-v5.16-05-mips-bpf-Enable-eBPF-JITs.patch new file mode 100644 index 0000000..41b3d81 --- /dev/null +++ b/pkgs/patches-linux-5.15/050-v5.16-05-mips-bpf-Enable-eBPF-JITs.patch @@ -0,0 +1,61 @@ +From: Johan Almbladh <johan.almbladh@anyfinetworks.com> +Date: Tue, 5 Oct 2021 18:54:07 +0200 +Subject: [PATCH] mips: bpf: Enable eBPF JITs + +This patch enables the new eBPF JITs for 32-bit and 64-bit MIPS. It also +disables the old cBPF JIT to so cBPF programs are converted to use the +new JIT. + +Workarounds for R4000 CPU errata are not implemented by the JIT, so the +JIT is disabled if any of those workarounds are configured. + +Signed-off-by: Johan Almbladh <johan.almbladh@anyfinetworks.com> +--- + +--- a/MAINTAINERS ++++ b/MAINTAINERS +@@ -3430,6 +3430,7 @@ S: Supported + F: arch/arm64/net/ + + BPF JIT for MIPS (32-BIT AND 64-BIT) ++M: Johan Almbladh <johan.almbladh@anyfinetworks.com> + M: Paul Burton <paulburton@kernel.org> + L: netdev@vger.kernel.org + L: bpf@vger.kernel.org +--- a/arch/mips/Kconfig ++++ b/arch/mips/Kconfig +@@ -57,7 +57,6 @@ config MIPS + select HAVE_ARCH_TRACEHOOK + select HAVE_ARCH_TRANSPARENT_HUGEPAGE if CPU_SUPPORTS_HUGEPAGES + select HAVE_ASM_MODVERSIONS +- select HAVE_CBPF_JIT if !64BIT && !CPU_MICROMIPS + select HAVE_CONTEXT_TRACKING + select HAVE_TIF_NOHZ + select HAVE_C_RECORDMCOUNT +@@ -65,7 +64,10 @@ config MIPS + select HAVE_DEBUG_STACKOVERFLOW + select HAVE_DMA_CONTIGUOUS + select HAVE_DYNAMIC_FTRACE +- select HAVE_EBPF_JIT if 64BIT && !CPU_MICROMIPS && TARGET_ISA_REV >= 2 ++ select HAVE_EBPF_JIT if !CPU_MICROMIPS && \ ++ !CPU_DADDI_WORKAROUNDS && \ ++ !CPU_R4000_WORKAROUNDS && \ ++ !CPU_R4400_WORKAROUNDS + select HAVE_EXIT_THREAD + select HAVE_FAST_GUP + select HAVE_FTRACE_MCOUNT_RECORD +--- a/arch/mips/net/Makefile ++++ b/arch/mips/net/Makefile +@@ -2,9 +2,10 @@ + # MIPS networking code + + obj-$(CONFIG_MIPS_CBPF_JIT) += bpf_jit.o bpf_jit_asm.o ++obj-$(CONFIG_MIPS_EBPF_JIT) += bpf_jit_comp.o + + ifeq ($(CONFIG_32BIT),y) +- obj-$(CONFIG_MIPS_EBPF_JIT) += bpf_jit_comp.o bpf_jit_comp32.o ++ obj-$(CONFIG_MIPS_EBPF_JIT) += bpf_jit_comp32.o + else +- obj-$(CONFIG_MIPS_EBPF_JIT) += ebpf_jit.o ++ obj-$(CONFIG_MIPS_EBPF_JIT) += bpf_jit_comp64.o + endif diff --git a/pkgs/patches-linux-5.15/050-v5.16-06-mips-bpf-Remove-old-BPF-JIT-implementations.patch b/pkgs/patches-linux-5.15/050-v5.16-06-mips-bpf-Remove-old-BPF-JIT-implementations.patch new file mode 100644 index 0000000..e25c336 --- /dev/null +++ b/pkgs/patches-linux-5.15/050-v5.16-06-mips-bpf-Remove-old-BPF-JIT-implementations.patch @@ -0,0 +1,387 @@ +From: Johan Almbladh <johan.almbladh@anyfinetworks.com> +Date: Tue, 5 Oct 2021 18:54:08 +0200 +Subject: [PATCH] mips: bpf: Remove old BPF JIT implementations + +This patch removes the old 32-bit cBPF and 64-bit eBPF JIT implementations. +They are replaced by a new eBPF implementation that supports both 32-bit +and 64-bit MIPS CPUs. + +Signed-off-by: Johan Almbladh <johan.almbladh@anyfinetworks.com> +--- + delete mode 100644 arch/mips/net/bpf_jit.c + delete mode 100644 arch/mips/net/bpf_jit.h + delete mode 100644 arch/mips/net/bpf_jit_asm.S + delete mode 100644 arch/mips/net/ebpf_jit.c + +--- a/arch/mips/net/bpf_jit.h ++++ /dev/null +@@ -1,81 +0,0 @@ +-/* SPDX-License-Identifier: GPL-2.0-only */ +-/* +- * Just-In-Time compiler for BPF filters on MIPS +- * +- * Copyright (c) 2014 Imagination Technologies Ltd. +- * Author: Markos Chandras <markos.chandras@imgtec.com> +- */ +- +-#ifndef BPF_JIT_MIPS_OP_H +-#define BPF_JIT_MIPS_OP_H +- +-/* Registers used by JIT */ +-#define MIPS_R_ZERO 0 +-#define MIPS_R_V0 2 +-#define MIPS_R_A0 4 +-#define MIPS_R_A1 5 +-#define MIPS_R_T4 12 +-#define MIPS_R_T5 13 +-#define MIPS_R_T6 14 +-#define MIPS_R_T7 15 +-#define MIPS_R_S0 16 +-#define MIPS_R_S1 17 +-#define MIPS_R_S2 18 +-#define MIPS_R_S3 19 +-#define MIPS_R_S4 20 +-#define MIPS_R_S5 21 +-#define MIPS_R_S6 22 +-#define MIPS_R_S7 23 +-#define MIPS_R_SP 29 +-#define MIPS_R_RA 31 +- +-/* Conditional codes */ +-#define MIPS_COND_EQ 0x1 +-#define MIPS_COND_GE (0x1 << 1) +-#define MIPS_COND_GT (0x1 << 2) +-#define MIPS_COND_NE (0x1 << 3) +-#define MIPS_COND_ALL (0x1 << 4) +-/* Conditionals on X register or K immediate */ +-#define MIPS_COND_X (0x1 << 5) +-#define MIPS_COND_K (0x1 << 6) +- +-#define r_ret MIPS_R_V0 +- +-/* +- * Use 2 scratch registers to avoid pipeline interlocks. +- * There is no overhead during epilogue and prologue since +- * any of the $s0-$s6 registers will only be preserved if +- * they are going to actually be used. +- */ +-#define r_skb_hl MIPS_R_S0 /* skb header length */ +-#define r_skb_data MIPS_R_S1 /* skb actual data */ +-#define r_off MIPS_R_S2 +-#define r_A MIPS_R_S3 +-#define r_X MIPS_R_S4 +-#define r_skb MIPS_R_S5 +-#define r_M MIPS_R_S6 +-#define r_skb_len MIPS_R_S7 +-#define r_s0 MIPS_R_T4 /* scratch reg 1 */ +-#define r_s1 MIPS_R_T5 /* scratch reg 2 */ +-#define r_tmp_imm MIPS_R_T6 /* No need to preserve this */ +-#define r_tmp MIPS_R_T7 /* No need to preserve this */ +-#define r_zero MIPS_R_ZERO +-#define r_sp MIPS_R_SP +-#define r_ra MIPS_R_RA +- +-#ifndef __ASSEMBLY__ +- +-/* Declare ASM helpers */ +- +-#define DECLARE_LOAD_FUNC(func) \ +- extern u8 func(unsigned long *skb, int offset); \ +- extern u8 func##_negative(unsigned long *skb, int offset); \ +- extern u8 func##_positive(unsigned long *skb, int offset) +- +-DECLARE_LOAD_FUNC(sk_load_word); +-DECLARE_LOAD_FUNC(sk_load_half); +-DECLARE_LOAD_FUNC(sk_load_byte); +- +-#endif +- +-#endif /* BPF_JIT_MIPS_OP_H */ +--- a/arch/mips/net/bpf_jit_asm.S ++++ /dev/null +@@ -1,285 +0,0 @@ +-/* +- * bpf_jib_asm.S: Packet/header access helper functions for MIPS/MIPS64 BPF +- * compiler. +- * +- * Copyright (C) 2015 Imagination Technologies Ltd. +- * Author: Markos Chandras <markos.chandras@imgtec.com> +- * +- * This program is free software; you can redistribute it and/or modify it +- * under the terms of the GNU General Public License as published by the +- * Free Software Foundation; version 2 of the License. +- */ +- +-#include <asm/asm.h> +-#include <asm/isa-rev.h> +-#include <asm/regdef.h> +-#include "bpf_jit.h" +- +-/* ABI +- * +- * r_skb_hl skb header length +- * r_skb_data skb data +- * r_off(a1) offset register +- * r_A BPF register A +- * r_X PF register X +- * r_skb(a0) *skb +- * r_M *scratch memory +- * r_skb_le skb length +- * r_s0 Scratch register 0 +- * r_s1 Scratch register 1 +- * +- * On entry: +- * a0: *skb +- * a1: offset (imm or imm + X) +- * +- * All non-BPF-ABI registers are free for use. On return, we only +- * care about r_ret. The BPF-ABI registers are assumed to remain +- * unmodified during the entire filter operation. +- */ +- +-#define skb a0 +-#define offset a1 +-#define SKF_LL_OFF (-0x200000) /* Can't include linux/filter.h in assembly */ +- +- /* We know better :) so prevent assembler reordering etc */ +- .set noreorder +- +-#define is_offset_negative(TYPE) \ +- /* If offset is negative we have more work to do */ \ +- slti t0, offset, 0; \ +- bgtz t0, bpf_slow_path_##TYPE##_neg; \ +- /* Be careful what follows in DS. */ +- +-#define is_offset_in_header(SIZE, TYPE) \ +- /* Reading from header? */ \ +- addiu $r_s0, $r_skb_hl, -SIZE; \ +- slt t0, $r_s0, offset; \ +- bgtz t0, bpf_slow_path_##TYPE; \ +- +-LEAF(sk_load_word) +- is_offset_negative(word) +-FEXPORT(sk_load_word_positive) +- is_offset_in_header(4, word) +- /* Offset within header boundaries */ +- PTR_ADDU t1, $r_skb_data, offset +- .set reorder +- lw $r_A, 0(t1) +- .set noreorder +-#ifdef CONFIG_CPU_LITTLE_ENDIAN +-# if MIPS_ISA_REV >= 2 +- wsbh t0, $r_A +- rotr $r_A, t0, 16 +-# else +- sll t0, $r_A, 24 +- srl t1, $r_A, 24 +- srl t2, $r_A, 8 +- or t0, t0, t1 +- andi t2, t2, 0xff00 +- andi t1, $r_A, 0xff00 +- or t0, t0, t2 +- sll t1, t1, 8 +- or $r_A, t0, t1 +-# endif +-#endif +- jr $r_ra +- move $r_ret, zero +- END(sk_load_word) +- +-LEAF(sk_load_half) +- is_offset_negative(half) +-FEXPORT(sk_load_half_positive) +- is_offset_in_header(2, half) +- /* Offset within header boundaries */ +- PTR_ADDU t1, $r_skb_data, offset +- lhu $r_A, 0(t1) +-#ifdef CONFIG_CPU_LITTLE_ENDIAN +-# if MIPS_ISA_REV >= 2 +- wsbh $r_A, $r_A +-# else +- sll t0, $r_A, 8 +- srl t1, $r_A, 8 +- andi t0, t0, 0xff00 +- or $r_A, t0, t1 +-# endif +-#endif +- jr $r_ra +- move $r_ret, zero +- END(sk_load_half) +- +-LEAF(sk_load_byte) +- is_offset_negative(byte) +-FEXPORT(sk_load_byte_positive) +- is_offset_in_header(1, byte) +- /* Offset within header boundaries */ +- PTR_ADDU t1, $r_skb_data, offset +- lbu $r_A, 0(t1) +- jr $r_ra +- move $r_ret, zero +- END(sk_load_byte) +- +-/* +- * call skb_copy_bits: +- * (prototype in linux/skbuff.h) +- * +- * int skb_copy_bits(sk_buff *skb, int offset, void *to, int len) +- * +- * o32 mandates we leave 4 spaces for argument registers in case +- * the callee needs to use them. Even though we don't care about +- * the argument registers ourselves, we need to allocate that space +- * to remain ABI compliant since the callee may want to use that space. +- * We also allocate 2 more spaces for $r_ra and our return register (*to). +- * +- * n64 is a bit different. The *caller* will allocate the space to preserve +- * the arguments. So in 64-bit kernels, we allocate the 4-arg space for no +- * good reason but it does not matter that much really. +- * +- * (void *to) is returned in r_s0 +- * +- */ +-#ifdef CONFIG_CPU_LITTLE_ENDIAN +-#define DS_OFFSET(SIZE) (4 * SZREG) +-#else +-#define DS_OFFSET(SIZE) ((4 * SZREG) + (4 - SIZE)) +-#endif +-#define bpf_slow_path_common(SIZE) \ +- /* Quick check. Are we within reasonable boundaries? */ \ +- LONG_ADDIU $r_s1, $r_skb_len, -SIZE; \ +- sltu $r_s0, offset, $r_s1; \ +- beqz $r_s0, fault; \ +- /* Load 4th argument in DS */ \ +- LONG_ADDIU a3, zero, SIZE; \ +- PTR_ADDIU $r_sp, $r_sp, -(6 * SZREG); \ +- PTR_LA t0, skb_copy_bits; \ +- PTR_S $r_ra, (5 * SZREG)($r_sp); \ +- /* Assign low slot to a2 */ \ +- PTR_ADDIU a2, $r_sp, DS_OFFSET(SIZE); \ +- jalr t0; \ +- /* Reset our destination slot (DS but it's ok) */ \ +- INT_S zero, (4 * SZREG)($r_sp); \ +- /* \ +- * skb_copy_bits returns 0 on success and -EFAULT \ +- * on error. Our data live in a2. Do not bother with \ +- * our data if an error has been returned. \ +- */ \ +- /* Restore our frame */ \ +- PTR_L $r_ra, (5 * SZREG)($r_sp); \ +- INT_L $r_s0, (4 * SZREG)($r_sp); \ +- bltz v0, fault; \ +- PTR_ADDIU $r_sp, $r_sp, 6 * SZREG; \ +- move $r_ret, zero; \ +- +-NESTED(bpf_slow_path_word, (6 * SZREG), $r_sp) +- bpf_slow_path_common(4) +-#ifdef CONFIG_CPU_LITTLE_ENDIAN +-# if MIPS_ISA_REV >= 2 +- wsbh t0, $r_s0 +- jr $r_ra +- rotr $r_A, t0, 16 +-# else +- sll t0, $r_s0, 24 +- srl t1, $r_s0, 24 +- srl t2, $r_s0, 8 +- or t0, t0, t1 +- andi t2, t2, 0xff00 +- andi t1, $r_s0, 0xff00 +- or t0, t0, t2 +- sll t1, t1, 8 +- jr $r_ra +- or $r_A, t0, t1 +-# endif +-#else +- jr $r_ra +- move $r_A, $r_s0 +-#endif +- +- END(bpf_slow_path_word) +- +-NESTED(bpf_slow_path_half, (6 * SZREG), $r_sp) +- bpf_slow_path_common(2) +-#ifdef CONFIG_CPU_LITTLE_ENDIAN +-# if MIPS_ISA_REV >= 2 +- jr $r_ra +- wsbh $r_A, $r_s0 +-# else +- sll t0, $r_s0, 8 +- andi t1, $r_s0, 0xff00 +- andi t0, t0, 0xff00 +- srl t1, t1, 8 +- jr $r_ra +- or $r_A, t0, t1 +-# endif +-#else +- jr $r_ra +- move $r_A, $r_s0 +-#endif +- +- END(bpf_slow_path_half) +- +-NESTED(bpf_slow_path_byte, (6 * SZREG), $r_sp) +- bpf_slow_path_common(1) +- jr $r_ra +- move $r_A, $r_s0 +- +- END(bpf_slow_path_byte) +- +-/* +- * Negative entry points +- */ +- .macro bpf_is_end_of_data +- li t0, SKF_LL_OFF +- /* Reading link layer data? */ +- slt t1, offset, t0 +- bgtz t1, fault +- /* Be careful what follows in DS. */ +- .endm +-/* +- * call skb_copy_bits: +- * (prototype in linux/filter.h) +- * +- * void *bpf_internal_load_pointer_neg_helper(const struct sk_buff *skb, +- * int k, unsigned int size) +- * +- * see above (bpf_slow_path_common) for ABI restrictions +- */ +-#define bpf_negative_common(SIZE) \ +- PTR_ADDIU $r_sp, $r_sp, -(6 * SZREG); \ +- PTR_LA t0, bpf_internal_load_pointer_neg_helper; \ +- PTR_S $r_ra, (5 * SZREG)($r_sp); \ +- jalr t0; \ +- li a2, SIZE; \ +- PTR_L $r_ra, (5 * SZREG)($r_sp); \ +- /* Check return pointer */ \ +- beqz v0, fault; \ +- PTR_ADDIU $r_sp, $r_sp, 6 * SZREG; \ +- /* Preserve our pointer */ \ +- move $r_s0, v0; \ +- /* Set return value */ \ +- move $r_ret, zero; \ +- +-bpf_slow_path_word_neg: +- bpf_is_end_of_data +-NESTED(sk_load_word_negative, (6 * SZREG), $r_sp) +- bpf_negative_common(4) +- jr $r_ra +- lw $r_A, 0($r_s0) +- END(sk_load_word_negative) +- +-bpf_slow_path_half_neg: +- bpf_is_end_of_data +-NESTED(sk_load_half_negative, (6 * SZREG), $r_sp) +- bpf_negative_common(2) +- jr $r_ra +- lhu $r_A, 0($r_s0) +- END(sk_load_half_negative) +- +-bpf_slow_path_byte_neg: +- bpf_is_end_of_data +-NESTED(sk_load_byte_negative, (6 * SZREG), $r_sp) +- bpf_negative_common(1) +- jr $r_ra +- lbu $r_A, 0($r_s0) +- END(sk_load_byte_negative) +- +-fault: +- jr $r_ra +- addiu $r_ret, zero, 1 diff --git a/pkgs/patches-linux-5.15/081-net-next-regmap-allow-to-define-reg_update_bits-for-no-bus.patch b/pkgs/patches-linux-5.15/081-net-next-regmap-allow-to-define-reg_update_bits-for-no-bus.patch new file mode 100644 index 0000000..e4c0833 --- /dev/null +++ b/pkgs/patches-linux-5.15/081-net-next-regmap-allow-to-define-reg_update_bits-for-no-bus.patch @@ -0,0 +1,52 @@ +From 02d6fdecb9c38de19065f6bed8d5214556fd061d Mon Sep 17 00:00:00 2001 +From: Ansuel Smith <ansuelsmth@gmail.com> +Date: Thu, 4 Nov 2021 16:00:40 +0100 +Subject: regmap: allow to define reg_update_bits for no bus configuration + +Some device requires a special handling for reg_update_bits and can't use +the normal regmap read write logic. An example is when locking is +handled by the device and rmw operations requires to do atomic operations. +Allow to declare a dedicated function in regmap_config for +reg_update_bits in no bus configuration. + +Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com> +Link: https://lore.kernel.org/r/20211104150040.1260-1-ansuelsmth@gmail.com +Signed-off-by: Mark Brown <broonie@kernel.org> +--- + drivers/base/regmap/regmap.c | 1 + + include/linux/regmap.h | 7 +++++++ + 2 files changed, 8 insertions(+) + +--- a/drivers/base/regmap/regmap.c ++++ b/drivers/base/regmap/regmap.c +@@ -877,6 +877,7 @@ struct regmap *__regmap_init(struct devi + if (!bus) { + map->reg_read = config->reg_read; + map->reg_write = config->reg_write; ++ map->reg_update_bits = config->reg_update_bits; + + map->defer_caching = false; + goto skip_format_initialization; +--- a/include/linux/regmap.h ++++ b/include/linux/regmap.h +@@ -290,6 +290,11 @@ typedef void (*regmap_unlock)(void *); + * read operation on a bus such as SPI, I2C, etc. Most of the + * devices do not need this. + * @reg_write: Same as above for writing. ++ * @reg_update_bits: Optional callback that if filled will be used to perform ++ * all the update_bits(rmw) operation. Should only be provided ++ * if the function require special handling with lock and reg ++ * handling and the operation cannot be represented as a simple ++ * update_bits operation on a bus such as SPI, I2C, etc. + * @fast_io: Register IO is fast. Use a spinlock instead of a mutex + * to perform locking. This field is ignored if custom lock/unlock + * functions are used (see fields lock/unlock of struct regmap_config). +@@ -372,6 +377,8 @@ struct regmap_config { + + int (*reg_read)(void *context, unsigned int reg, unsigned int *val); + int (*reg_write)(void *context, unsigned int reg, unsigned int val); ++ int (*reg_update_bits)(void *context, unsigned int reg, ++ unsigned int mask, unsigned int val); + + bool fast_io; + diff --git a/pkgs/patches-linux-5.15/100-ARM-dts-turris-omnia-configure-LED-0-pin-function-to.patch b/pkgs/patches-linux-5.15/100-ARM-dts-turris-omnia-configure-LED-0-pin-function-to.patch new file mode 100644 index 0000000..135b691 --- /dev/null +++ b/pkgs/patches-linux-5.15/100-ARM-dts-turris-omnia-configure-LED-0-pin-function-to.patch @@ -0,0 +1,43 @@ +From 81c0004a6433ff90fa6129418802c3c367e453c2 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Marek=20Beh=C3=BAn?= <kabel@kernel.org> +Date: Mon, 4 Jul 2022 13:36:21 +0200 +Subject: [PATCH 1/5] ARM: dts: turris-omnia: configure LED[0] pin function to + link/activity +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +The marvell PHY driver changes the LED[0] pin function to "On - 1000 +Mbps Link, Off - Else". + +Turris Omnia expects that the function is "On - Link, Blink - Activity, +Off - No link". + +Use the `marvell,reg-init` DT property to change the function. + +In the future, once netdev trigger will support HW offloading, we will +be able to have this configured via the combination of PHY driver and +leds-turris-omnia driver. + +Signed-off-by: Marek Behún <kabel@kernel.org> +--- + arch/arm/boot/dts/armada-385-turris-omnia.dts | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/arch/arm/boot/dts/armada-385-turris-omnia.dts b/arch/arm/boot/dts/armada-385-turris-omnia.dts +index 5bd6a66d2c2b..1c65de55a17b 100644 +--- a/arch/arm/boot/dts/armada-385-turris-omnia.dts ++++ b/arch/arm/boot/dts/armada-385-turris-omnia.dts +@@ -390,7 +390,8 @@ &mdio { + phy1: ethernet-phy@1 { + compatible = "ethernet-phy-ieee802.3-c22"; + reg = <1>; +- marvell,reg-init = <3 18 0 0x4985>; ++ marvell,reg-init = <3 18 0 0x4985>, ++ <3 16 0xfff0 0x0001>; + + /* irq is connected to &pcawan pin 7 */ + }; +-- +2.34.1 + diff --git a/pkgs/patches-linux-5.15/100-aardvark-workaround-PCIe.patch b/pkgs/patches-linux-5.15/100-aardvark-workaround-PCIe.patch new file mode 100644 index 0000000..39a7934 --- /dev/null +++ b/pkgs/patches-linux-5.15/100-aardvark-workaround-PCIe.patch @@ -0,0 +1,81 @@ +From b004eff0818f657f66a6e1dc63aca0ffd9b1fb2f Mon Sep 17 00:00:00 2001 +From: Pali Rohár <pali@kernel.org> +Date: Wed, 31 Aug 2022 19:44:08 +0200 +Subject: [PATCH] PCI: aardvark: Implement workaround for PCIe Completion + Timeout + +Marvell Armada 3700 Functional Errata, Guidelines, and Restrictions +document describes in erratum 3.12 PCIe Completion Timeout (Ref #: 251), +that PCIe IP does not support a strong-ordered model for inbound posted vs. +outbound completion. + +As a workaround for this erratum, DIS_ORD_CHK flag in Debug Mux Control +register must be set. It disables the ordering check in the core between +Completions and Posted requests received from the link. + +Marvell also suggests to do full memory barrier at the beginning of +aardvark summary interrupt handler before calling interrupt handlers of +endpoint drivers in order to minimize the risk for the race condition +documented in the Erratum between the DMA done status reading and the +completion of writing to the host memory. + +More details about this issue and suggested workarounds are in discussion: +https://lore.kernel.org/linux-pci/BN9PR18MB425154FE5019DCAF2028A1D5DB8D9@BN9PR18MB4251.namprd18.prod.outlook.com/t/#u + +It was reported that enabling this workaround fixes instability issues and +"Unhandled fault" errors when using 60 GHz WiFi 802.11ad card with Qualcomm +QCA6335 chip under significant load which were caused by interrupt status +stuck in the outbound CMPLT queue traced back to this erratum. + +This workaround fixes also kernel panic triggered after some minutes of +usage 5 GHz WiFi 802.11ax card with Mediatek MT7915 chip: + + Internal error: synchronous external abort: 96000210 [#1] SMP + Kernel panic - not syncing: Fatal exception in interrupt + +Signed-off-by: Thomas Petazzoni <thomas.petazzoni@bootlin.com> +Signed-off-by: Pali Rohár <pali@kernel.org> +Fixes: 8c39d710363c ("PCI: aardvark: Add Aardvark PCI host controller driver") +Cc: stable@vger.kernel.org +--- + drivers/pci/controller/pci-aardvark.c | 10 ++++++++++ + 1 file changed, 10 insertions(+) + +diff --git a/drivers/pci/controller/pci-aardvark.c b/drivers/pci/controller/pci-aardvark.c +index f3984bdf1d96..defaf74935a3 100644 +--- a/drivers/pci/controller/pci-aardvark.c ++++ b/drivers/pci/controller/pci-aardvark.c +@@ -215,6 +215,8 @@ enum { + }; + + #define VENDOR_ID_REG (LMI_BASE_ADDR + 0x44) ++#define DEBUG_MUX_CTRL_REG (LMI_BASE_ADDR + 0x208) ++#define DIS_ORD_CHK BIT(30) + #define PME_MSG_GEN_CTRL (LMI_BASE_ADDR + 0x220) + #define SEND_SET_SLOT_POWER_LIMIT BIT(13) + #define SEND_PME_TURN_OFF BIT(14) +@@ -611,6 +613,11 @@ static void advk_pcie_setup_hw(struct advk_pcie *pcie) + PCIE_CORE_CTRL2_TD_ENABLE; + advk_writel(pcie, reg, PCIE_CORE_CTRL2_REG); + ++ /* Disable ordering checks, workaround for erratum 3.12 "PCIe completion timeout" */ ++ reg = advk_readl(pcie, DEBUG_MUX_CTRL_REG); ++ reg |= DIS_ORD_CHK; ++ advk_writel(pcie, reg, DEBUG_MUX_CTRL_REG); ++ + /* Set lane X1 */ + reg = advk_readl(pcie, PCIE_CORE_CTRL0_REG); + reg &= ~LANE_CNT_MSK; +@@ -1803,6 +1810,9 @@ static irqreturn_t advk_pcie_irq_handler(int irq, void *arg) + struct advk_pcie *pcie = arg; + u32 status; + ++ /* Full memory barrier (ARM dsb sy), workaround for erratum 3.12 "PCIe completion timeout" */ ++ mb(); ++ + status = advk_readl(pcie, HOST_CTRL_INT_STATUS_REG); + if (!(status & PCIE_IRQ_CORE_INT)) + return IRQ_NONE; +-- +2.34.1 + diff --git a/pkgs/patches-linux-5.15/100-compiler.h-only-include-asm-rwonce.h-for-kernel-code.patch b/pkgs/patches-linux-5.15/100-compiler.h-only-include-asm-rwonce.h-for-kernel-code.patch new file mode 100644 index 0000000..22f52c1 --- /dev/null +++ b/pkgs/patches-linux-5.15/100-compiler.h-only-include-asm-rwonce.h-for-kernel-code.patch @@ -0,0 +1,29 @@ +From: Felix Fietkau <nbd@nbd.name> +Date: Thu, 22 Oct 2020 22:00:03 +0200 +Subject: [PATCH] compiler.h: only include asm/rwonce.h for kernel code + +This header file is not in uapi, which makes any user space code that includes +linux/compiler.h to fail with the error 'asm/rwonce.h: No such file or directory' + +Fixes: e506ea451254 ("compiler.h: Split {READ,WRITE}_ONCE definitions out into rwonce.h") +Signed-off-by: Felix Fietkau <nbd@nbd.name> +--- + +--- a/include/linux/compiler.h ++++ b/include/linux/compiler.h +@@ -220,6 +220,8 @@ void ftrace_likely_update(struct ftrace_ + #define function_nocfi(x) (x) + #endif + ++#include <asm/rwonce.h> ++ + #endif /* __KERNEL__ */ + + /* +@@ -252,6 +254,4 @@ static inline void *offset_to_ptr(const + */ + #define prevent_tail_call_optimization() mb() + +-#include <asm/rwonce.h> +- + #endif /* __LINUX_COMPILER_H */ diff --git a/pkgs/patches-linux-5.15/100-v5.18-tty-serial-bcm63xx-use-more-precise-Kconfig-symbol.patch b/pkgs/patches-linux-5.15/100-v5.18-tty-serial-bcm63xx-use-more-precise-Kconfig-symbol.patch new file mode 100644 index 0000000..7de3cbb --- /dev/null +++ b/pkgs/patches-linux-5.15/100-v5.18-tty-serial-bcm63xx-use-more-precise-Kconfig-symbol.patch @@ -0,0 +1,37 @@ +From 0dc0da881b4574d1e04a079ab2ea75da61f5ad2e Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <rafal@milecki.pl> +Date: Fri, 11 Mar 2022 10:32:33 +0100 +Subject: [PATCH] tty: serial: bcm63xx: use more precise Kconfig symbol +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Patches lowering SERIAL_BCM63XX dependencies led to a discussion and +documentation change regarding "depends" usage. Adjust Kconfig entry to +match current guidelines. Make this symbol available for relevant +architectures only. + +Cc: Geert Uytterhoeven <geert@linux-m68k.org> +Reviewed-by: Geert Uytterhoeven <geert+renesas@glider.be> +Acked-by: Florian Fainelli <f.fainelli@gmail.com> +Signed-off-by: Rafał Miłecki <rafal@milecki.pl> +Ref: f35a07f92616 ("tty: serial: bcm63xx: lower driver dependencies") +Ref: 18084e435ff6 ("Documentation/kbuild: Document platform dependency practises") +Link: https://lore.kernel.org/r/20220311093233.10012-1-zajec5@gmail.com +Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> +--- + drivers/tty/serial/Kconfig | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +--- a/drivers/tty/serial/Kconfig ++++ b/drivers/tty/serial/Kconfig +@@ -1098,7 +1098,8 @@ config SERIAL_TIMBERDALE + config SERIAL_BCM63XX + tristate "Broadcom BCM63xx/BCM33xx UART support" + select SERIAL_CORE +- depends on COMMON_CLK ++ depends on ARCH_BCM4908 || ARCH_BCM_63XX || BCM63XX || BMIPS_GENERIC || COMPILE_TEST ++ default ARCH_BCM4908 || ARCH_BCM_63XX || BCM63XX || BMIPS_GENERIC + help + This enables the driver for the onchip UART core found on + the following chipsets: diff --git a/pkgs/patches-linux-5.15/101-ARM-dts-turris-omnia-enable-LED-controller-node.patch b/pkgs/patches-linux-5.15/101-ARM-dts-turris-omnia-enable-LED-controller-node.patch new file mode 100644 index 0000000..626a766 --- /dev/null +++ b/pkgs/patches-linux-5.15/101-ARM-dts-turris-omnia-enable-LED-controller-node.patch @@ -0,0 +1,53 @@ +From fed7cef5e4f2df8c6a79bebf5da1fdd3783ff6f3 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Marek=20Beh=C3=BAn?= <kabel@kernel.org> +Date: Mon, 4 Jul 2022 13:36:22 +0200 +Subject: [PATCH] ARM: dts: turris-omnia: enable LED controller node +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +The LED controller node is disabled because the leds-turris-omnia driver +does not support setting the LED blinking to be controlled by the MCU. + +The patches for that have now been sent [1], so let's enable the node. + +[1] https://lore.kernel.org/linux-leds/20220704105955.15474-1-kabel@kernel.org/T/ + +Signed-off-by: Marek Behún <kabel@kernel.org> +--- + arch/arm/boot/dts/armada-385-turris-omnia.dts | 7 ++----- + 1 file changed, 2 insertions(+), 5 deletions(-) + +diff --git a/arch/arm/boot/dts/armada-385-turris-omnia.dts b/arch/arm/boot/dts/armada-385-turris-omnia.dts +index 1c65de55a17b..1e7d6e63c58d 100644 +--- a/arch/arm/boot/dts/armada-385-turris-omnia.dts ++++ b/arch/arm/boot/dts/armada-385-turris-omnia.dts +@@ -188,15 +188,13 @@ led-controller@2b { + reg = <0x2b>; + #address-cells = <1>; + #size-cells = <0>; ++ status = "okay"; + + /* + * LEDs are controlled by MCU (STM32F0) at + * address 0x2b. + * +- * The driver does not support HW control mode +- * for the LEDs yet. Disable the LEDs for now. +- * +- * Also LED functions are not stable yet: ++ * LED functions are not stable yet: + * - there are 3 LEDs connected via MCU to PCIe + * ports. One of these ports supports mSATA. + * There is no mSATA nor PCIe function. +@@ -207,7 +205,6 @@ led-controller@2b { + * B. Again there is no such function defined. + * For now we use LED_FUNCTION_INDICATOR + */ +- status = "disabled"; + + multi-led@0 { + reg = <0x0>; +-- +2.34.1 + diff --git a/pkgs/patches-linux-5.15/101-Use-stddefs.h-instead-of-compiler.h.patch b/pkgs/patches-linux-5.15/101-Use-stddefs.h-instead-of-compiler.h.patch new file mode 100644 index 0000000..eefbe4b --- /dev/null +++ b/pkgs/patches-linux-5.15/101-Use-stddefs.h-instead-of-compiler.h.patch @@ -0,0 +1,26 @@ +From d3c5b26768dbe990c4e1bd79e420c11ce7491d51 Mon Sep 17 00:00:00 2001 +From: OpenWrt community <openwrt-devel@lists.openwrt.org> +Date: Wed, 13 Jul 2022 11:36:00 +0200 +Subject: [PATCH] swab: use stddefs.h instead of compiler.h + +Fix an issue with kernel headers that broke perf. + +--- + include/uapi/linux/swab.h | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/include/uapi/linux/swab.h b/include/uapi/linux/swab.h +index 7272f85d6d6a..3736f2fe1541 100644 +--- a/include/uapi/linux/swab.h ++++ b/include/uapi/linux/swab.h +@@ -3,7 +3,7 @@ + #define _UAPI_LINUX_SWAB_H + + #include <linux/types.h> +-#include <linux/compiler.h> ++#include <linux/stddef.h> + #include <asm/bitsperlong.h> + #include <asm/swab.h> + +-- + diff --git a/pkgs/patches-linux-5.15/102-MIPS-only-process-negative-stack-offsets-on-stack-tr.patch b/pkgs/patches-linux-5.15/102-MIPS-only-process-negative-stack-offsets-on-stack-tr.patch new file mode 100644 index 0000000..95a9656 --- /dev/null +++ b/pkgs/patches-linux-5.15/102-MIPS-only-process-negative-stack-offsets-on-stack-tr.patch @@ -0,0 +1,57 @@ +From: Felix Fietkau <nbd@nbd.name> +Date: Wed, 18 Apr 2018 10:50:05 +0200 +Subject: [PATCH] MIPS: only process negative stack offsets on stack traces + +Fixes endless back traces in cases where the compiler emits a stack +pointer increase in a branch delay slot (probably for some form of +function return). + +[ 3.475442] BUG: MAX_STACK_TRACE_ENTRIES too low! +[ 3.480070] turning off the locking correctness validator. +[ 3.485521] CPU: 0 PID: 1 Comm: swapper/0 Not tainted 4.14.34 #0 +[ 3.491475] Stack : 00000000 00000000 00000000 00000000 80e0fce2 00000034 00000000 00000000 +[ 3.499764] 87c3838c 80696377 8061047c 00000000 00000001 00000001 87c2d850 6534689f +[ 3.508059] 00000000 00000000 80e10000 00000000 00000000 000000cf 0000000f 00000000 +[ 3.516353] 00000000 806a0000 00076891 00000000 00000000 00000000 ffffffff 00000000 +[ 3.524648] 806c0000 00000004 80e10000 806a0000 00000003 80690000 00000000 80700000 +[ 3.532942] ... +[ 3.535362] Call Trace: +[ 3.537818] [<80010a48>] show_stack+0x58/0x100 +[ 3.542207] [<804c2f78>] dump_stack+0xe8/0x170 +[ 3.546613] [<80079f90>] save_trace+0xf0/0x110 +[ 3.551010] [<8007b1ec>] mark_lock+0x33c/0x78c +[ 3.555413] [<8007bf48>] __lock_acquire+0x2ac/0x1a08 +[ 3.560337] [<8007de60>] lock_acquire+0x64/0x8c +[ 3.564846] [<804e1570>] _raw_spin_lock_irqsave+0x54/0x78 +[ 3.570186] [<801b618c>] kernfs_notify+0x94/0xac +[ 3.574770] [<801b7b10>] sysfs_notify+0x74/0xa0 +[ 3.579257] [<801b618c>] kernfs_notify+0x94/0xac +[ 3.583839] [<801b7b10>] sysfs_notify+0x74/0xa0 +[ 3.588329] [<801b618c>] kernfs_notify+0x94/0xac +[ 3.592911] [<801b7b10>] sysfs_notify+0x74/0xa0 +[ 3.597401] [<801b618c>] kernfs_notify+0x94/0xac +[ 3.601983] [<801b7b10>] sysfs_notify+0x74/0xa0 +[ 3.606473] [<801b618c>] kernfs_notify+0x94/0xac +[ 3.611055] [<801b7b10>] sysfs_notify+0x74/0xa0 +[ 3.615545] [<801b618c>] kernfs_notify+0x94/0xac +[ 3.620125] [<801b7b10>] sysfs_notify+0x74/0xa0 +[ 3.624619] [<801b618c>] kernfs_notify+0x94/0xac +[ 3.629197] [<801b7b10>] sysfs_notify+0x74/0xa0 +[ 3.633691] [<801b618c>] kernfs_notify+0x94/0xac +[ 3.638269] [<801b7b10>] sysfs_notify+0x74/0xa0 +[ 3.642763] [<801b618c>] kernfs_notify+0x94/0xac + +Signed-off-by: Felix Fietkau <nbd@nbd.name> +--- + +--- a/arch/mips/kernel/process.c ++++ b/arch/mips/kernel/process.c +@@ -393,6 +393,8 @@ static inline int is_sp_move_ins(union m + + if (ip->i_format.opcode == addiu_op || + ip->i_format.opcode == daddiu_op) { ++ if (ip->i_format.simmediate > 0) ++ return 0; + *frame_size = -ip->i_format.simmediate; + return 1; + } diff --git a/pkgs/patches-linux-5.15/102-leds-turris-omnia-support-HW-controlled-mode-via-pri.patch b/pkgs/patches-linux-5.15/102-leds-turris-omnia-support-HW-controlled-mode-via-pri.patch new file mode 100644 index 0000000..05520a8 --- /dev/null +++ b/pkgs/patches-linux-5.15/102-leds-turris-omnia-support-HW-controlled-mode-via-pri.patch @@ -0,0 +1,125 @@ +From 80e643510cb14f116f687e992210c0008a09d869 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Marek=20Beh=C3=BAn?= <kabel@kernel.org> +Date: Mon, 4 Jul 2022 12:59:53 +0200 +Subject: [PATCH] leds: turris-omnia: support HW controlled mode via + private trigger +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Add support for enabling MCU controlled mode of the Turris Omnia LEDs +via a LED private trigger called "omnia-mcu". + +When in MCU controlled mode, the user can still set LED color, but the +blinking is done by MCU, which does different things for various LEDs: +- WAN LED is blinked according to the LED[0] pin of the WAN PHY +- LAN LEDs are blinked according to the LED[0] output of corresponding + port of the LAN switch +- PCIe LEDs are blinked according to the logical OR of the MiniPCIe port + LED pins + +For a long time I wanted to actually do this differently: I wanted to +make the netdev trigger to transparently offload the blinking to the HW +if user set compatible settings for the netdev trigger. +There was some work on this, and hopefully we will be able to complete +it sometime, but since there are various complications, it will probably +not be soon. + +In the meantime let's support HW controlled mode via this private LED +trigger. If, in the future, we manage to complete the netdev trigger +offloading, we can still keep this private trigger for backwards +compatiblity, if needed. + +We also set "omnia-mcu" to cdev->default_trigger, so that the MCU keeps +control until the user first wants to take over it. If a different +default trigger is specified in device-tree via the +`linux,default-trigger` property, LED class will overwrite +cdev->default_trigger, and so the DT property will be respected. + +Signed-off-by: Marek Behún <kabel@kernel.org> +--- + drivers/leds/Kconfig | 1 + + drivers/leds/leds-turris-omnia.c | 41 ++++++++++++++++++++++++++++++++ + 2 files changed, 42 insertions(+) + +diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig +index ed800f5da7d8..52f010b8f58e 100644 +--- a/drivers/leds/Kconfig ++++ b/drivers/leds/Kconfig +@@ -163,6 +163,7 @@ config LEDS_TURRIS_OMNIA + depends on I2C + depends on MACH_ARMADA_38X || COMPILE_TEST + depends on OF ++ select LEDS_TRIGGERS + help + This option enables basic support for the LEDs found on the front + side of CZ.NIC's Turris Omnia router. There are 12 RGB LEDs on the +diff --git a/drivers/leds/leds-turris-omnia.c b/drivers/leds/leds-turris-omnia.c +index 1adfed1c0619..c2dfb22d3065 100644 +--- a/drivers/leds/leds-turris-omnia.c ++++ b/drivers/leds/leds-turris-omnia.c +@@ -41,6 +41,39 @@ struct omnia_leds { + struct omnia_led leds[]; + }; + ++static struct led_hw_trigger_type omnia_hw_trigger_type; ++ ++static int omnia_hwtrig_activate(struct led_classdev *cdev) ++{ ++ struct omnia_leds *leds = dev_get_drvdata(cdev->dev->parent); ++ struct omnia_led *led = to_omnia_led(lcdev_to_mccdev(cdev)); ++ ++ /* put the LED into MCU controlled mode */ ++ return i2c_smbus_write_byte_data(leds->client, CMD_LED_MODE, ++ CMD_LED_MODE_LED(led->reg)); ++} ++ ++static void omnia_hwtrig_deactivate(struct led_classdev *cdev) ++{ ++ struct omnia_leds *leds = dev_get_drvdata(cdev->dev->parent); ++ struct omnia_led *led = to_omnia_led(lcdev_to_mccdev(cdev)); ++ int ret; ++ ++ /* put the LED into software mode */ ++ ret = i2c_smbus_write_byte_data(leds->client, CMD_LED_MODE, ++ CMD_LED_MODE_LED(led->reg) | ++ CMD_LED_MODE_USER); ++ if (ret < 0) ++ dev_err(cdev->dev, "Cannot put to software mode: %i\n", ret); ++} ++ ++static struct led_trigger omnia_hw_trigger = { ++ .name = "omnia-mcu", ++ .activate = omnia_hwtrig_activate, ++ .deactivate = omnia_hwtrig_deactivate, ++ .trigger_type = &omnia_hw_trigger_type, ++}; ++ + static int omnia_led_brightness_set_blocking(struct led_classdev *cdev, + enum led_brightness brightness) + { +@@ -112,6 +145,8 @@ static int omnia_led_register(struct i2c_client *client, struct omnia_led *led, + cdev = &led->mc_cdev.led_cdev; + cdev->max_brightness = 255; + cdev->brightness_set_blocking = omnia_led_brightness_set_blocking; ++ cdev->trigger_type = &omnia_hw_trigger_type; ++ cdev->default_trigger = omnia_hw_trigger.name; + + /* put the LED into software mode */ + ret = i2c_smbus_write_byte_data(client, CMD_LED_MODE, +@@ -228,6 +263,12 @@ static int omnia_leds_probe(struct i2c_client *client, + + mutex_init(&leds->lock); + ++ ret = devm_led_trigger_register(dev, &omnia_hw_trigger); ++ if (ret < 0) { ++ dev_err(dev, "Cannot register private LED trigger: %d\n", ret); ++ return ret; ++ } ++ + led = &leds->leds[0]; + for_each_available_child_of_node(np, child) { + ret = omnia_led_register(client, led, child); +-- +2.34.1 + diff --git a/pkgs/patches-linux-5.15/103-leds-turris-omnia-initialize-multi-intensity-to-full.patch b/pkgs/patches-linux-5.15/103-leds-turris-omnia-initialize-multi-intensity-to-full.patch new file mode 100644 index 0000000..88b85ac --- /dev/null +++ b/pkgs/patches-linux-5.15/103-leds-turris-omnia-initialize-multi-intensity-to-full.patch @@ -0,0 +1,38 @@ +From bda176cceb735b9b46c1900658b6486c34e13ae6 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Marek=20Beh=C3=BAn?= <kabel@kernel.org> +Date: Mon, 4 Jul 2022 12:59:54 +0200 +Subject: [PATCH] leds: turris-omnia: initialize multi-intensity to full +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +The default color of each LED before driver probe (255, 255, 255). +Initialize multi_intensity to this value, so that it corresponds to the +reality. + +Signed-off-by: Marek Behún <kabel@kernel.org> +--- + drivers/leds/leds-turris-omnia.c | 3 +++ + 1 file changed, 3 insertions(+) + +diff --git a/drivers/leds/leds-turris-omnia.c b/drivers/leds/leds-turris-omnia.c +index c2dfb22d3065..fae155bd119c 100644 +--- a/drivers/leds/leds-turris-omnia.c ++++ b/drivers/leds/leds-turris-omnia.c +@@ -131,10 +131,13 @@ static int omnia_led_register(struct i2c_client *client, struct omnia_led *led, + } + + led->subled_info[0].color_index = LED_COLOR_ID_RED; ++ led->subled_info[0].intensity = 255; + led->subled_info[0].channel = 0; + led->subled_info[1].color_index = LED_COLOR_ID_GREEN; ++ led->subled_info[1].intensity = 255; + led->subled_info[1].channel = 1; + led->subled_info[2].color_index = LED_COLOR_ID_BLUE; ++ led->subled_info[2].intensity = 255; + led->subled_info[2].channel = 2; + + led->mc_cdev.subled_info = led->subled_info; +-- +2.34.1 + diff --git a/pkgs/patches-linux-5.15/104-leds-turris-omnia-change-max-brightness-from-255-to-.patch b/pkgs/patches-linux-5.15/104-leds-turris-omnia-change-max-brightness-from-255-to-.patch new file mode 100644 index 0000000..b99c0dc --- /dev/null +++ b/pkgs/patches-linux-5.15/104-leds-turris-omnia-change-max-brightness-from-255-to-.patch @@ -0,0 +1,36 @@ +From 349cbe949b9622cc05b148ecfa6268cbbae35b45 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Marek=20Beh=C3=BAn?= <kabel@kernel.org> +Date: Mon, 4 Jul 2022 12:59:55 +0200 +Subject: [PATCH] leds: turris-omnia: change max brightness from 255 to 1 +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Using binary brightness makes more sense for this controller, because +internally in the MCU it works that way: the LED has a color, and a +state whether it is ON or OFF. + +The resulting brightness computation with led_mc_calc_color_components() +will now always result in either (0, 0, 0) or the multi_intensity value. + +Signed-off-by: Marek Behún <kabel@kernel.org> +--- + drivers/leds/leds-turris-omnia.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/drivers/leds/leds-turris-omnia.c b/drivers/leds/leds-turris-omnia.c +index fae155bd119c..f53bdc3f4cea 100644 +--- a/drivers/leds/leds-turris-omnia.c ++++ b/drivers/leds/leds-turris-omnia.c +@@ -146,7 +146,7 @@ static int omnia_led_register(struct i2c_client *client, struct omnia_led *led, + init_data.fwnode = &np->fwnode; + + cdev = &led->mc_cdev.led_cdev; +- cdev->max_brightness = 255; ++ cdev->max_brightness = 1; + cdev->brightness_set_blocking = omnia_led_brightness_set_blocking; + cdev->trigger_type = &omnia_hw_trigger_type; + cdev->default_trigger = omnia_hw_trigger.name; +-- +2.34.1 + diff --git a/pkgs/patches-linux-5.15/110-net-sfp-move-quirk-handling-into-sfp.c.patch b/pkgs/patches-linux-5.15/110-net-sfp-move-quirk-handling-into-sfp.c.patch new file mode 100644 index 0000000..fa74804 --- /dev/null +++ b/pkgs/patches-linux-5.15/110-net-sfp-move-quirk-handling-into-sfp.c.patch @@ -0,0 +1,299 @@ +From cb3a4e33a0415f84736734e77fb2d6c1a5fab1d7 Mon Sep 17 00:00:00 2001 +From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk> +Date: Fri, 26 Aug 2022 08:43:30 +0100 +Subject: [PATCH 1/4] net: sfp: move quirk handling into sfp.c + +We need to handle more quirks than just those which affect the link +modes of the module. Move the quirk lookup into sfp.c, and pass the +quirk to sfp-bus.c + +Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk> +--- + drivers/net/phy/sfp-bus.c | 98 ++------------------------------------- + drivers/net/phy/sfp.c | 94 ++++++++++++++++++++++++++++++++++++- + drivers/net/phy/sfp.h | 9 +++- + 3 files changed, 104 insertions(+), 97 deletions(-) + +diff --git a/drivers/net/phy/sfp-bus.c b/drivers/net/phy/sfp-bus.c +index 4369d6249e7b..267182d32bd5 100644 +--- a/drivers/net/phy/sfp-bus.c ++++ b/drivers/net/phy/sfp-bus.c +@@ -10,12 +10,6 @@ + + #include "sfp.h" + +-struct sfp_quirk { +- const char *vendor; +- const char *part; +- void (*modes)(const struct sfp_eeprom_id *id, unsigned long *modes); +-}; +- + /** + * struct sfp_bus - internal representation of a sfp bus + */ +@@ -38,93 +32,6 @@ struct sfp_bus { + bool started; + }; + +-static void sfp_quirk_2500basex(const struct sfp_eeprom_id *id, +- unsigned long *modes) +-{ +- phylink_set(modes, 2500baseX_Full); +-} +- +-static void sfp_quirk_ubnt_uf_instant(const struct sfp_eeprom_id *id, +- unsigned long *modes) +-{ +- /* Ubiquiti U-Fiber Instant module claims that support all transceiver +- * types including 10G Ethernet which is not truth. So clear all claimed +- * modes and set only one mode which module supports: 1000baseX_Full. +- */ +- phylink_zero(modes); +- phylink_set(modes, 1000baseX_Full); +-} +- +-static const struct sfp_quirk sfp_quirks[] = { +- { +- // Alcatel Lucent G-010S-P can operate at 2500base-X, but +- // incorrectly report 2500MBd NRZ in their EEPROM +- .vendor = "ALCATELLUCENT", +- .part = "G010SP", +- .modes = sfp_quirk_2500basex, +- }, { +- // Alcatel Lucent G-010S-A can operate at 2500base-X, but +- // report 3.2GBd NRZ in their EEPROM +- .vendor = "ALCATELLUCENT", +- .part = "3FE46541AA", +- .modes = sfp_quirk_2500basex, +- }, { +- // Huawei MA5671A can operate at 2500base-X, but report 1.2GBd +- // NRZ in their EEPROM +- .vendor = "HUAWEI", +- .part = "MA5671A", +- .modes = sfp_quirk_2500basex, +- }, { +- // Lantech 8330-262D-E can operate at 2500base-X, but +- // incorrectly report 2500MBd NRZ in their EEPROM +- .vendor = "Lantech", +- .part = "8330-262D-E", +- .modes = sfp_quirk_2500basex, +- }, { +- .vendor = "UBNT", +- .part = "UF-INSTANT", +- .modes = sfp_quirk_ubnt_uf_instant, +- }, +-}; +- +-static size_t sfp_strlen(const char *str, size_t maxlen) +-{ +- size_t size, i; +- +- /* Trailing characters should be filled with space chars */ +- for (i = 0, size = 0; i < maxlen; i++) +- if (str[i] != ' ') +- size = i + 1; +- +- return size; +-} +- +-static bool sfp_match(const char *qs, const char *str, size_t len) +-{ +- if (!qs) +- return true; +- if (strlen(qs) != len) +- return false; +- return !strncmp(qs, str, len); +-} +- +-static const struct sfp_quirk *sfp_lookup_quirk(const struct sfp_eeprom_id *id) +-{ +- const struct sfp_quirk *q; +- unsigned int i; +- size_t vs, ps; +- +- vs = sfp_strlen(id->base.vendor_name, ARRAY_SIZE(id->base.vendor_name)); +- ps = sfp_strlen(id->base.vendor_pn, ARRAY_SIZE(id->base.vendor_pn)); +- +- for (i = 0, q = sfp_quirks; i < ARRAY_SIZE(sfp_quirks); i++, q++) +- if (sfp_match(q->vendor, id->base.vendor_name, vs) && +- sfp_match(q->part, id->base.vendor_pn, ps)) +- return q; +- +- return NULL; +-} +- + /** + * sfp_parse_port() - Parse the EEPROM base ID, setting the port type + * @bus: a pointer to the &struct sfp_bus structure for the sfp module +@@ -786,12 +693,13 @@ void sfp_link_down(struct sfp_bus *bus) + } + EXPORT_SYMBOL_GPL(sfp_link_down); + +-int sfp_module_insert(struct sfp_bus *bus, const struct sfp_eeprom_id *id) ++int sfp_module_insert(struct sfp_bus *bus, const struct sfp_eeprom_id *id, ++ const struct sfp_quirk *quirk) + { + const struct sfp_upstream_ops *ops = sfp_get_upstream_ops(bus); + int ret = 0; + +- bus->sfp_quirk = sfp_lookup_quirk(id); ++ bus->sfp_quirk = quirk; + + if (ops && ops->module_insert) + ret = ops->module_insert(bus->upstream, id); +diff --git a/drivers/net/phy/sfp.c b/drivers/net/phy/sfp.c +index 028a5df5c538..7be9eb47c40f 100644 +--- a/drivers/net/phy/sfp.c ++++ b/drivers/net/phy/sfp.c +@@ -252,6 +252,8 @@ struct sfp { + unsigned int module_t_start_up; + bool tx_fault_ignore; + ++ const struct sfp_quirk *quirk; ++ + #if IS_ENABLED(CONFIG_HWMON) + struct sfp_diag diag; + struct delayed_work hwmon_probe; +@@ -308,6 +310,93 @@ static const struct of_device_id sfp_of_match[] = { + }; + MODULE_DEVICE_TABLE(of, sfp_of_match); + ++static void sfp_quirk_2500basex(const struct sfp_eeprom_id *id, ++ unsigned long *modes) ++{ ++ linkmode_set_bit(ETHTOOL_LINK_MODE_2500baseX_Full_BIT, modes); ++} ++ ++static void sfp_quirk_ubnt_uf_instant(const struct sfp_eeprom_id *id, ++ unsigned long *modes) ++{ ++ /* Ubiquiti U-Fiber Instant module claims that support all transceiver ++ * types including 10G Ethernet which is not truth. So clear all claimed ++ * modes and set only one mode which module supports: 1000baseX_Full. ++ */ ++ linkmode_zero(modes); ++ linkmode_set_bit(ETHTOOL_LINK_MODE_1000baseX_Full_BIT, modes); ++} ++ ++static const struct sfp_quirk sfp_quirks[] = { ++ { ++ // Alcatel Lucent G-010S-P can operate at 2500base-X, but ++ // incorrectly report 2500MBd NRZ in their EEPROM ++ .vendor = "ALCATELLUCENT", ++ .part = "G010SP", ++ .modes = sfp_quirk_2500basex, ++ }, { ++ // Alcatel Lucent G-010S-A can operate at 2500base-X, but ++ // report 3.2GBd NRZ in their EEPROM ++ .vendor = "ALCATELLUCENT", ++ .part = "3FE46541AA", ++ .modes = sfp_quirk_2500basex, ++ }, { ++ // Huawei MA5671A can operate at 2500base-X, but report 1.2GBd ++ // NRZ in their EEPROM ++ .vendor = "HUAWEI", ++ .part = "MA5671A", ++ .modes = sfp_quirk_2500basex, ++ }, { ++ // Lantech 8330-262D-E can operate at 2500base-X, but ++ // incorrectly report 2500MBd NRZ in their EEPROM ++ .vendor = "Lantech", ++ .part = "8330-262D-E", ++ .modes = sfp_quirk_2500basex, ++ }, { ++ .vendor = "UBNT", ++ .part = "UF-INSTANT", ++ .modes = sfp_quirk_ubnt_uf_instant, ++ }, ++}; ++ ++static size_t sfp_strlen(const char *str, size_t maxlen) ++{ ++ size_t size, i; ++ ++ /* Trailing characters should be filled with space chars */ ++ for (i = 0, size = 0; i < maxlen; i++) ++ if (str[i] != ' ') ++ size = i + 1; ++ ++ return size; ++} ++ ++static bool sfp_match(const char *qs, const char *str, size_t len) ++{ ++ if (!qs) ++ return true; ++ if (strlen(qs) != len) ++ return false; ++ return !strncmp(qs, str, len); ++} ++ ++static const struct sfp_quirk *sfp_lookup_quirk(const struct sfp_eeprom_id *id) ++{ ++ const struct sfp_quirk *q; ++ unsigned int i; ++ size_t vs, ps; ++ ++ vs = sfp_strlen(id->base.vendor_name, ARRAY_SIZE(id->base.vendor_name)); ++ ps = sfp_strlen(id->base.vendor_pn, ARRAY_SIZE(id->base.vendor_pn)); ++ ++ for (i = 0, q = sfp_quirks; i < ARRAY_SIZE(sfp_quirks); i++, q++) ++ if (sfp_match(q->vendor, id->base.vendor_name, vs) && ++ sfp_match(q->part, id->base.vendor_pn, ps)) ++ return q; ++ ++ return NULL; ++} ++ + static unsigned long poll_jiffies; + + static unsigned int sfp_gpio_get_state(struct sfp *sfp) +@@ -1952,6 +2041,8 @@ static int sfp_sm_mod_probe(struct sfp *sfp, bool report) + else + sfp->tx_fault_ignore = false; + ++ sfp->quirk = sfp_lookup_quirk(&id); ++ + return 0; + } + +@@ -2063,7 +2154,8 @@ static void sfp_sm_module(struct sfp *sfp, unsigned int event) + break; + + /* Report the module insertion to the upstream device */ +- err = sfp_module_insert(sfp->sfp_bus, &sfp->id); ++ err = sfp_module_insert(sfp->sfp_bus, &sfp->id, ++ sfp->quirk); + if (err < 0) { + sfp_sm_mod_next(sfp, SFP_MOD_ERROR, 0); + break; +diff --git a/drivers/net/phy/sfp.h b/drivers/net/phy/sfp.h +index 27226535c72b..03f1d47fe6ca 100644 +--- a/drivers/net/phy/sfp.h ++++ b/drivers/net/phy/sfp.h +@@ -6,6 +6,12 @@ + + struct sfp; + ++struct sfp_quirk { ++ const char *vendor; ++ const char *part; ++ void (*modes)(const struct sfp_eeprom_id *id, unsigned long *modes); ++}; ++ + struct sfp_socket_ops { + void (*attach)(struct sfp *sfp); + void (*detach)(struct sfp *sfp); +@@ -23,7 +29,8 @@ int sfp_add_phy(struct sfp_bus *bus, struct phy_device *phydev); + void sfp_remove_phy(struct sfp_bus *bus); + void sfp_link_up(struct sfp_bus *bus); + void sfp_link_down(struct sfp_bus *bus); +-int sfp_module_insert(struct sfp_bus *bus, const struct sfp_eeprom_id *id); ++int sfp_module_insert(struct sfp_bus *bus, const struct sfp_eeprom_id *id, ++ const struct sfp_quirk *quirk); + void sfp_module_remove(struct sfp_bus *bus); + int sfp_module_start(struct sfp_bus *bus); + void sfp_module_stop(struct sfp_bus *bus); +-- +2.34.1 + diff --git a/pkgs/patches-linux-5.15/111-net-sfp-move-Alcatel-Lucent-3FE46541AA-fixup.patch b/pkgs/patches-linux-5.15/111-net-sfp-move-Alcatel-Lucent-3FE46541AA-fixup.patch new file mode 100644 index 0000000..56716ca --- /dev/null +++ b/pkgs/patches-linux-5.15/111-net-sfp-move-Alcatel-Lucent-3FE46541AA-fixup.patch @@ -0,0 +1,75 @@ +From d189313ed65a21a425889664abc5e7c4646a00ba Mon Sep 17 00:00:00 2001 +From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk> +Date: Fri, 26 Aug 2022 08:43:35 +0100 +Subject: [PATCH 2/4] net: sfp: move Alcatel Lucent 3FE46541AA fixup + +Add a new fixup mechanism to the SFP quirks, and use it for this +module. + +Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk> +--- + drivers/net/phy/sfp.c | 14 +++++++++----- + drivers/net/phy/sfp.h | 1 + + 2 files changed, 10 insertions(+), 5 deletions(-) + +diff --git a/drivers/net/phy/sfp.c b/drivers/net/phy/sfp.c +index 7be9eb47c40f..900b10887c3f 100644 +--- a/drivers/net/phy/sfp.c ++++ b/drivers/net/phy/sfp.c +@@ -310,6 +310,11 @@ static const struct of_device_id sfp_of_match[] = { + }; + MODULE_DEVICE_TABLE(of, sfp_of_match); + ++static void sfp_fixup_long_startup(struct sfp *sfp) ++{ ++ sfp->module_t_start_up = T_START_UP_BAD_GPON; ++} ++ + static void sfp_quirk_2500basex(const struct sfp_eeprom_id *id, + unsigned long *modes) + { +@@ -340,6 +345,7 @@ static const struct sfp_quirk sfp_quirks[] = { + .vendor = "ALCATELLUCENT", + .part = "3FE46541AA", + .modes = sfp_quirk_2500basex, ++ .fixup = sfp_fixup_long_startup, + }, { + // Huawei MA5671A can operate at 2500base-X, but report 1.2GBd + // NRZ in their EEPROM +@@ -2029,11 +2035,7 @@ static int sfp_sm_mod_probe(struct sfp *sfp, bool report) + if (ret < 0) + return ret; + +- if (!memcmp(id.base.vendor_name, "ALCATELLUCENT ", 16) && +- !memcmp(id.base.vendor_pn, "3FE46541AA ", 16)) +- sfp->module_t_start_up = T_START_UP_BAD_GPON; +- else +- sfp->module_t_start_up = T_START_UP; ++ sfp->module_t_start_up = T_START_UP; + + if (!memcmp(id.base.vendor_name, "HUAWEI ", 16) && + !memcmp(id.base.vendor_pn, "MA5671A ", 16)) +@@ -2042,6 +2044,8 @@ static int sfp_sm_mod_probe(struct sfp *sfp, bool report) + sfp->tx_fault_ignore = false; + + sfp->quirk = sfp_lookup_quirk(&id); ++ if (sfp->quirk && sfp->quirk->fixup) ++ sfp->quirk->fixup(sfp); + + return 0; + } +diff --git a/drivers/net/phy/sfp.h b/drivers/net/phy/sfp.h +index 03f1d47fe6ca..7ad06deae76c 100644 +--- a/drivers/net/phy/sfp.h ++++ b/drivers/net/phy/sfp.h +@@ -10,6 +10,7 @@ struct sfp_quirk { + const char *vendor; + const char *part; + void (*modes)(const struct sfp_eeprom_id *id, unsigned long *modes); ++ void (*fixup)(struct sfp *sfp); + }; + + struct sfp_socket_ops { +-- +2.34.1 + diff --git a/pkgs/patches-linux-5.15/112-net-sfp-move-Huawei-MA5671A-fixup.patch b/pkgs/patches-linux-5.15/112-net-sfp-move-Huawei-MA5671A-fixup.patch new file mode 100644 index 0000000..416f3a3 --- /dev/null +++ b/pkgs/patches-linux-5.15/112-net-sfp-move-Huawei-MA5671A-fixup.patch @@ -0,0 +1,52 @@ +From a9e957e4da9f28797663b162e0bdd0147ace7853 Mon Sep 17 00:00:00 2001 +From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk> +Date: Fri, 26 Aug 2022 08:43:40 +0100 +Subject: [PATCH 3/4] net: sfp: move Huawei MA5671A fixup + +Move this module over to the new fixup mechanism. + +Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk> +--- + drivers/net/phy/sfp.c | 12 +++++++----- + 1 file changed, 7 insertions(+), 5 deletions(-) + +diff --git a/drivers/net/phy/sfp.c b/drivers/net/phy/sfp.c +index 900b10887c3f..db922d27ee79 100644 +--- a/drivers/net/phy/sfp.c ++++ b/drivers/net/phy/sfp.c +@@ -315,6 +315,11 @@ static void sfp_fixup_long_startup(struct sfp *sfp) + sfp->module_t_start_up = T_START_UP_BAD_GPON; + } + ++static void sfp_fixup_ignore_tx_fault(struct sfp *sfp) ++{ ++ sfp->tx_fault_ignore = true; ++} ++ + static void sfp_quirk_2500basex(const struct sfp_eeprom_id *id, + unsigned long *modes) + { +@@ -352,6 +357,7 @@ static const struct sfp_quirk sfp_quirks[] = { + .vendor = "HUAWEI", + .part = "MA5671A", + .modes = sfp_quirk_2500basex, ++ .fixup = sfp_fixup_ignore_tx_fault, + }, { + // Lantech 8330-262D-E can operate at 2500base-X, but + // incorrectly report 2500MBd NRZ in their EEPROM +@@ -2037,11 +2043,7 @@ static int sfp_sm_mod_probe(struct sfp *sfp, bool report) + + sfp->module_t_start_up = T_START_UP; + +- if (!memcmp(id.base.vendor_name, "HUAWEI ", 16) && +- !memcmp(id.base.vendor_pn, "MA5671A ", 16)) +- sfp->tx_fault_ignore = true; +- else +- sfp->tx_fault_ignore = false; ++ sfp->tx_fault_ignore = false; + + sfp->quirk = sfp_lookup_quirk(&id); + if (sfp->quirk && sfp->quirk->fixup) +-- +2.34.1 + diff --git a/pkgs/patches-linux-5.15/113-net-sfp-add-support-for-HALNy-GPON-SFP.patch b/pkgs/patches-linux-5.15/113-net-sfp-add-support-for-HALNy-GPON-SFP.patch new file mode 100644 index 0000000..e66bcb7 --- /dev/null +++ b/pkgs/patches-linux-5.15/113-net-sfp-add-support-for-HALNy-GPON-SFP.patch @@ -0,0 +1,93 @@ +From 9490075c53cec3925039d2b1c5afc4507e772660 Mon Sep 17 00:00:00 2001 +From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk> +Date: Fri, 26 Aug 2022 08:43:45 +0100 +Subject: [PATCH 4/4] net: sfp: add support for HALNy GPON SFP + +Add a quirk for the HALNy HL-GSFP module, which appears to have an +inverted RX_LOS signal, and possibly uses TX_FAULT as an inverted +host-link status signal. As we can't be certain about the modules +use of TX_FAULT, we ignore it. + +Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk> +--- + drivers/net/phy/sfp-bus.c | 2 +- + drivers/net/phy/sfp.c | 29 ++++++++++++++++++++++++++--- + 2 files changed, 27 insertions(+), 4 deletions(-) + +diff --git a/drivers/net/phy/sfp-bus.c b/drivers/net/phy/sfp-bus.c +index 267182d32bd5..4566348a6d05 100644 +--- a/drivers/net/phy/sfp-bus.c ++++ b/drivers/net/phy/sfp-bus.c +@@ -283,7 +283,7 @@ void sfp_parse_support(struct sfp_bus *bus, const struct sfp_eeprom_id *id, + phylink_set(modes, 2500baseX_Full); + } + +- if (bus->sfp_quirk) ++ if (bus->sfp_quirk && bus->sfp_quirk->modes) + bus->sfp_quirk->modes(id, modes); + + bitmap_or(support, support, modes, __ETHTOOL_LINK_MODE_MASK_NBITS); +diff --git a/drivers/net/phy/sfp.c b/drivers/net/phy/sfp.c +index db922d27ee79..2ebc577bfc56 100644 +--- a/drivers/net/phy/sfp.c ++++ b/drivers/net/phy/sfp.c +@@ -320,6 +320,23 @@ static void sfp_fixup_ignore_tx_fault(struct sfp *sfp) + sfp->tx_fault_ignore = true; + } + ++static void sfp_fixup_inverted_los(struct sfp *sfp) ++{ ++ const __be16 los_inverted = cpu_to_be16(SFP_OPTIONS_LOS_INVERTED); ++ const __be16 los_normal = cpu_to_be16(SFP_OPTIONS_LOS_NORMAL); ++ ++ sfp->id.ext.options &= ~los_normal; ++ sfp->id.ext.options |= los_inverted; ++} ++ ++static void sfp_fixup_hanly_gsfp(struct sfp *sfp) ++{ ++ /* LOS is inverted */ ++ sfp_fixup_inverted_los(sfp); ++ /* TX fault might be inverted, but we don't know for certain. */ ++ sfp_fixup_ignore_tx_fault(sfp); ++} ++ + static void sfp_quirk_2500basex(const struct sfp_eeprom_id *id, + unsigned long *modes) + { +@@ -351,6 +368,10 @@ static const struct sfp_quirk sfp_quirks[] = { + .part = "3FE46541AA", + .modes = sfp_quirk_2500basex, + .fixup = sfp_fixup_long_startup, ++ }, { ++ .vendor = "HANLy", ++ .part = "HL-GSFP", ++ .fixup = sfp_fixup_hanly_gsfp, + }, { + // Huawei MA5671A can operate at 2500base-X, but report 1.2GBd + // NRZ in their EEPROM +@@ -368,16 +389,18 @@ static const struct sfp_quirk sfp_quirks[] = { + .vendor = "UBNT", + .part = "UF-INSTANT", + .modes = sfp_quirk_ubnt_uf_instant, +- }, ++ } + }; + + static size_t sfp_strlen(const char *str, size_t maxlen) + { + size_t size, i; + +- /* Trailing characters should be filled with space chars */ ++ /* Trailing characters should be filled with space chars, but ++ * some manufacturers can't read SFF-8472 and use NUL. ++ */ + for (i = 0, size = 0; i < maxlen; i++) +- if (str[i] != ' ') ++ if (str[i] != ' ' && str[i] != '\0') + size = i + 1; + + return size; +-- +2.34.1 + diff --git a/pkgs/patches-linux-5.15/114-fixup-net-sfp-add-support-for-HALNy-GPON-SFP.patch b/pkgs/patches-linux-5.15/114-fixup-net-sfp-add-support-for-HALNy-GPON-SFP.patch new file mode 100644 index 0000000..99ca247 --- /dev/null +++ b/pkgs/patches-linux-5.15/114-fixup-net-sfp-add-support-for-HALNy-GPON-SFP.patch @@ -0,0 +1,42 @@ +From 6dc810804bb2fa91d9972601c209471fab852509 Mon Sep 17 00:00:00 2001 +From: Josef Schlehofer <pepe.schlehofer@gmail.com> +Date: Fri, 26 Aug 2022 20:50:51 +0200 +Subject: [PATCH] fixup! net: sfp: add support for HALNy GPON SFP + +There are a few typos, which was reported by Orest Worhacz +(@AreYouLoco), which needs to be fixed. + +SFP is detected as +[ 9.607535] sfp sfp: module HALNy HL-GSFP rev V1.0 sn HALN1010493c dc 20150525 +--- + drivers/net/phy/sfp.c | 6 +++--- + 1 file changed, 3 insertions(+), 3 deletions(-) + +diff --git a/drivers/net/phy/sfp.c b/drivers/net/phy/sfp.c +index 2ebc577bfc56..277292e2ed35 100644 +--- a/drivers/net/phy/sfp.c ++++ b/drivers/net/phy/sfp.c +@@ -329,7 +329,7 @@ static void sfp_fixup_inverted_los(struct sfp *sfp) + sfp->id.ext.options |= los_inverted; + } + +-static void sfp_fixup_hanly_gsfp(struct sfp *sfp) ++static void sfp_fixup_halny_gsfp(struct sfp *sfp) + { + /* LOS is inverted */ + sfp_fixup_inverted_los(sfp); +@@ -369,9 +369,9 @@ static const struct sfp_quirk sfp_quirks[] = { + .modes = sfp_quirk_2500basex, + .fixup = sfp_fixup_long_startup, + }, { +- .vendor = "HANLy", ++ .vendor = "HALNy", + .part = "HL-GSFP", +- .fixup = sfp_fixup_hanly_gsfp, ++ .fixup = sfp_fixup_halny_gsfp, + }, { + // Huawei MA5671A can operate at 2500base-X, but report 1.2GBd + // NRZ in their EEPROM +-- +2.34.1 + diff --git a/pkgs/patches-linux-5.15/115-net-sfp-redo-soft-state-polling.patch b/pkgs/patches-linux-5.15/115-net-sfp-redo-soft-state-polling.patch new file mode 100644 index 0000000..8b7adea --- /dev/null +++ b/pkgs/patches-linux-5.15/115-net-sfp-redo-soft-state-polling.patch @@ -0,0 +1,84 @@ +From f2721c55176d31ff46d06bcfa512b97ada701ef7 Mon Sep 17 00:00:00 2001 +From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk> +Date: Fri, 26 Aug 2022 08:48:20 +0100 +Subject: [PATCH 1/2] net: sfp: redo soft state polling + +Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk> +--- + drivers/net/phy/sfp.c | 35 ++++++++++++++++++++++++----------- + 1 file changed, 24 insertions(+), 11 deletions(-) + +diff --git a/drivers/net/phy/sfp.c b/drivers/net/phy/sfp.c +index 277292e2ed35..8db84e0dbc04 100644 +--- a/drivers/net/phy/sfp.c ++++ b/drivers/net/phy/sfp.c +@@ -234,6 +234,7 @@ struct sfp { + bool need_poll; + + struct mutex st_mutex; /* Protects state */ ++ unsigned int state_ignore_hw_mask; + unsigned int state_soft_mask; + unsigned int state; + struct delayed_work poll; +@@ -623,17 +624,18 @@ static void sfp_soft_set_state(struct sfp *sfp, unsigned int state) + static void sfp_soft_start_poll(struct sfp *sfp) + { + const struct sfp_eeprom_id *id = &sfp->id; ++ unsigned int mask = 0; + + sfp->state_soft_mask = 0; +- if (id->ext.enhopts & SFP_ENHOPTS_SOFT_TX_DISABLE && +- !sfp->gpio[GPIO_TX_DISABLE]) +- sfp->state_soft_mask |= SFP_F_TX_DISABLE; +- if (id->ext.enhopts & SFP_ENHOPTS_SOFT_TX_FAULT && +- !sfp->gpio[GPIO_TX_FAULT]) +- sfp->state_soft_mask |= SFP_F_TX_FAULT; +- if (id->ext.enhopts & SFP_ENHOPTS_SOFT_RX_LOS && +- !sfp->gpio[GPIO_LOS]) +- sfp->state_soft_mask |= SFP_F_LOS; ++ if (id->ext.enhopts & SFP_ENHOPTS_SOFT_TX_DISABLE) ++ mask |= SFP_F_TX_DISABLE; ++ if (id->ext.enhopts & SFP_ENHOPTS_SOFT_TX_FAULT) ++ mask |= SFP_F_TX_FAULT; ++ if (id->ext.enhopts & SFP_ENHOPTS_SOFT_RX_LOS) ++ mask |= SFP_F_LOS; ++ ++ // Poll the soft state for hardware pins we want to ignore ++ sfp->state_soft_mask = sfp->state_ignore_hw_mask & mask; + + if (sfp->state_soft_mask & (SFP_F_LOS | SFP_F_TX_FAULT) && + !sfp->need_poll) +@@ -647,10 +649,12 @@ static void sfp_soft_stop_poll(struct sfp *sfp) + + static unsigned int sfp_get_state(struct sfp *sfp) + { ++ unsigned int soft = sfp->state_soft_mask & (SFP_F_LOS | SFP_F_TX_FAULT); + unsigned int state = sfp->get_state(sfp); + +- if (state & SFP_F_PRESENT && +- sfp->state_soft_mask & (SFP_F_LOS | SFP_F_TX_FAULT)) ++ state &= ~sfp->state_ignore_hw_mask; ++ ++ if (state & SFP_F_PRESENT && soft) + state |= sfp_soft_get_state(sfp); + + return state; +@@ -2064,6 +2068,15 @@ static int sfp_sm_mod_probe(struct sfp *sfp, bool report) + if (ret < 0) + return ret; + ++ /* Initialise state bits to ignore from hardware */ ++ sfp->state_ignore_hw_mask = 0; ++ if (!sfp->gpio[GPIO_TX_DISABLE]) ++ sfp->state_ignore_hw_mask |= SFP_F_TX_DISABLE; ++ if (!sfp->gpio[GPIO_TX_FAULT]) ++ sfp->state_ignore_hw_mask |= SFP_F_TX_FAULT; ++ if (!sfp->gpio[GPIO_LOS]) ++ sfp->state_ignore_hw_mask |= SFP_F_LOS; ++ + sfp->module_t_start_up = T_START_UP; + + sfp->tx_fault_ignore = false; +-- +2.34.1 + diff --git a/pkgs/patches-linux-5.15/116-net-sfp-change-HALNy-to-ignore-hardware-pins.patch b/pkgs/patches-linux-5.15/116-net-sfp-change-HALNy-to-ignore-hardware-pins.patch new file mode 100644 index 0000000..11b3e31 --- /dev/null +++ b/pkgs/patches-linux-5.15/116-net-sfp-change-HALNy-to-ignore-hardware-pins.patch @@ -0,0 +1,40 @@ +From 8852f9aa3c1ae0c58a64b8517f11ae62309b84ad Mon Sep 17 00:00:00 2001 +From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk> +Date: Fri, 26 Aug 2022 08:48:25 +0100 +Subject: [PATCH 2/2] net: sfp: change HALNy to ignore hardware pins + +Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk> +--- + drivers/net/phy/sfp.c | 14 +------------- + 1 file changed, 1 insertion(+), 13 deletions(-) + +diff --git a/drivers/net/phy/sfp.c b/drivers/net/phy/sfp.c +index 8db84e0dbc04..1df5d5008ab2 100644 +--- a/drivers/net/phy/sfp.c ++++ b/drivers/net/phy/sfp.c +@@ -321,21 +321,9 @@ static void sfp_fixup_ignore_tx_fault(struct sfp *sfp) + sfp->tx_fault_ignore = true; + } + +-static void sfp_fixup_inverted_los(struct sfp *sfp) +-{ +- const __be16 los_inverted = cpu_to_be16(SFP_OPTIONS_LOS_INVERTED); +- const __be16 los_normal = cpu_to_be16(SFP_OPTIONS_LOS_NORMAL); +- +- sfp->id.ext.options &= ~los_normal; +- sfp->id.ext.options |= los_inverted; +-} +- + static void sfp_fixup_halny_gsfp(struct sfp *sfp) + { +- /* LOS is inverted */ +- sfp_fixup_inverted_los(sfp); +- /* TX fault might be inverted, but we don't know for certain. */ +- sfp_fixup_ignore_tx_fault(sfp); ++ sfp->state_ignore_hw_mask |= SFP_F_TX_FAULT | SFP_F_LOS; + } + + static void sfp_quirk_2500basex(const struct sfp_eeprom_id *id, +-- +2.34.1 + diff --git a/pkgs/patches-linux-5.15/120-Fix-alloc_node_mem_map-with-ARCH_PFN_OFFSET-calcu.patch b/pkgs/patches-linux-5.15/120-Fix-alloc_node_mem_map-with-ARCH_PFN_OFFSET-calcu.patch new file mode 100644 index 0000000..30c70a6 --- /dev/null +++ b/pkgs/patches-linux-5.15/120-Fix-alloc_node_mem_map-with-ARCH_PFN_OFFSET-calcu.patch @@ -0,0 +1,82 @@ +From: Tobias Wolf <dev-NTEO@vplace.de> +Subject: mm: Fix alloc_node_mem_map with ARCH_PFN_OFFSET calculation + +An rt288x (ralink) based router (Belkin F5D8235 v1) does not boot with any +kernel beyond version 4.3 resulting in: + +BUG: Bad page state in process swapper pfn:086ac + +bisect resulted in: + +a1c34a3bf00af2cede839879502e12dc68491ad5 is the first bad commit +commit a1c34a3bf00af2cede839879502e12dc68491ad5 +Author: Laura Abbott <laura@labbott.name> +Date: Thu Nov 5 18:48:46 2015 -0800 + + mm: Don't offset memmap for flatmem + + Srinivas Kandagatla reported bad page messages when trying to remove the + bottom 2MB on an ARM based IFC6410 board + + BUG: Bad page state in process swapper pfn:fffa8 + page:ef7fb500 count:0 mapcount:0 mapping: (null) index:0x0 + flags: 0x96640253(locked|error|dirty|active|arch_1|reclaim|mlocked) + page dumped because: PAGE_FLAGS_CHECK_AT_FREE flag(s) set + bad because of flags: + flags: 0x200041(locked|active|mlocked) + Modules linked in: + CPU: 0 PID: 0 Comm: swapper Not tainted 3.19.0-rc3-00007-g412f9ba-dirty +#816 + Hardware name: Qualcomm (Flattened Device Tree) + unwind_backtrace + show_stack + dump_stack + bad_page + free_pages_prepare + free_hot_cold_page + __free_pages + free_highmem_page + mem_init + start_kernel + Disabling lock debugging due to kernel taint + [...] +:040000 040000 2de013c372345fd471cd58f0553c9b38b0ef1cc4 +0a8156f848733dfa21e16c196dfb6c0a76290709 M mm + +This fix for ARM does not account ARCH_PFN_OFFSET for mem_map as later used by +page_to_pfn anymore. + +The following output was generated with two hacked in printk statements: + +printk("before %p vs. %p or %p\n", mem_map, mem_map - offset, mem_map - +(pgdat->node_start_pfn - ARCH_PFN_OFFSET)); + if (page_to_pfn(mem_map) != pgdat->node_start_pfn) + mem_map -= offset + (pgdat->node_start_pfn - ARCH_PFN_OFFSET); +printk("after %p\n", mem_map); + +Output: + +[ 0.000000] before 8861b280 vs. 8861b280 or 8851b280 +[ 0.000000] after 8851b280 + +As seen in the first line mem_map with subtraction of offset does not equal the +mem_map after subtraction of ARCH_PFN_OFFSET. + +After adding the offset of ARCH_PFN_OFFSET as well to mem_map as the +previously calculated offset is zero for the named platform it is able to boot +4.4 and 4.9-rc7 again. + +Signed-off-by: Tobias Wolf <dev-NTEO@vplace.de> +--- + +--- a/mm/page_alloc.c ++++ b/mm/page_alloc.c +@@ -7552,7 +7552,7 @@ static void __init alloc_node_mem_map(st + if (pgdat == NODE_DATA(0)) { + mem_map = NODE_DATA(0)->node_mem_map; + if (page_to_pfn(mem_map) != pgdat->node_start_pfn) +- mem_map -= offset; ++ mem_map -= offset + (pgdat->node_start_pfn - ARCH_PFN_OFFSET); + } + #endif + } diff --git a/pkgs/patches-linux-5.15/130-add-linux-spidev-compatible-si3210.patch b/pkgs/patches-linux-5.15/130-add-linux-spidev-compatible-si3210.patch new file mode 100644 index 0000000..d260cf1 --- /dev/null +++ b/pkgs/patches-linux-5.15/130-add-linux-spidev-compatible-si3210.patch @@ -0,0 +1,18 @@ +From: Giuseppe Lippolis <giu.lippolis@gmail.com> +Subject: Add the linux,spidev compatible in spidev Several device in ramips have this binding in the dts + +Signed-off-by: Giuseppe Lippolis <giu.lippolis@gmail.com> +--- + drivers/spi/spidev.c | 1 + + 1 file changed, 1 insertion(+) + +--- a/drivers/spi/spidev.c ++++ b/drivers/spi/spidev.c +@@ -696,6 +696,7 @@ static const struct of_device_id spidev_ + { .compatible = "menlo,m53cpld" }, + { .compatible = "cisco,spi-petra" }, + { .compatible = "micron,spi-authenta" }, ++ { .compatible = "siliconlabs,si3210" }, + {}, + }; + MODULE_DEVICE_TABLE(of, spidev_dt_ids); diff --git a/pkgs/patches-linux-5.15/140-jffs2-use-.rename2-and-add-RENAME_WHITEOUT-support.patch b/pkgs/patches-linux-5.15/140-jffs2-use-.rename2-and-add-RENAME_WHITEOUT-support.patch new file mode 100644 index 0000000..8f40ae3 --- /dev/null +++ b/pkgs/patches-linux-5.15/140-jffs2-use-.rename2-and-add-RENAME_WHITEOUT-support.patch @@ -0,0 +1,81 @@ +From: Felix Fietkau <nbd@nbd.name> +Subject: jffs2: use .rename2 and add RENAME_WHITEOUT support + +It is required for renames on overlayfs + +Signed-off-by: Felix Fietkau <nbd@nbd.name> +--- + +--- a/fs/jffs2/dir.c ++++ b/fs/jffs2/dir.c +@@ -614,8 +614,8 @@ static int jffs2_rmdir (struct inode *di + return ret; + } + +-static int jffs2_mknod (struct user_namespace *mnt_userns, struct inode *dir_i, +- struct dentry *dentry, umode_t mode, dev_t rdev) ++static int __jffs2_mknod (struct user_namespace *mnt_userns, struct inode *dir_i, ++ struct dentry *dentry, umode_t mode, dev_t rdev, bool whiteout) + { + struct jffs2_inode_info *f, *dir_f; + struct jffs2_sb_info *c; +@@ -754,7 +754,11 @@ static int jffs2_mknod (struct user_name + mutex_unlock(&dir_f->sem); + jffs2_complete_reservation(c); + +- d_instantiate_new(dentry, inode); ++ if (!whiteout) ++ d_instantiate_new(dentry, inode); ++ else ++ unlock_new_inode(inode); ++ + return 0; + + fail: +@@ -762,6 +766,19 @@ static int jffs2_mknod (struct user_name + return ret; + } + ++static int jffs2_mknod (struct user_namespace *mnt_userns, struct inode *dir_i, ++ struct dentry *dentry, umode_t mode, dev_t rdev) ++{ ++ return __jffs2_mknod(mnt_userns, dir_i, dentry, mode, rdev, false); ++} ++ ++static int jffs2_whiteout (struct user_namespace *mnt_userns, struct inode *old_dir, ++ struct dentry *old_dentry) ++{ ++ return __jffs2_mknod(mnt_userns, old_dir, old_dentry, S_IFCHR | WHITEOUT_MODE, ++ WHITEOUT_DEV, true); ++} ++ + static int jffs2_rename (struct user_namespace *mnt_userns, + struct inode *old_dir_i, struct dentry *old_dentry, + struct inode *new_dir_i, struct dentry *new_dentry, +@@ -773,7 +790,7 @@ static int jffs2_rename (struct user_nam + uint8_t type; + uint32_t now; + +- if (flags & ~RENAME_NOREPLACE) ++ if (flags & ~(RENAME_NOREPLACE|RENAME_WHITEOUT)) + return -EINVAL; + + /* The VFS will check for us and prevent trying to rename a +@@ -839,9 +856,14 @@ static int jffs2_rename (struct user_nam + if (d_is_dir(old_dentry) && !victim_f) + inc_nlink(new_dir_i); + +- /* Unlink the original */ +- ret = jffs2_do_unlink(c, JFFS2_INODE_INFO(old_dir_i), +- old_dentry->d_name.name, old_dentry->d_name.len, NULL, now); ++ if (flags & RENAME_WHITEOUT) ++ /* Replace with whiteout */ ++ ret = jffs2_whiteout(mnt_userns, old_dir_i, old_dentry); ++ else ++ /* Unlink the original */ ++ ret = jffs2_do_unlink(c, JFFS2_INODE_INFO(old_dir_i), ++ old_dentry->d_name.name, ++ old_dentry->d_name.len, NULL, now); + + /* We don't touch inode->i_nlink */ + diff --git a/pkgs/patches-linux-5.15/141-jffs2-add-RENAME_EXCHANGE-support.patch b/pkgs/patches-linux-5.15/141-jffs2-add-RENAME_EXCHANGE-support.patch new file mode 100644 index 0000000..f58fc79 --- /dev/null +++ b/pkgs/patches-linux-5.15/141-jffs2-add-RENAME_EXCHANGE-support.patch @@ -0,0 +1,73 @@ +From: Felix Fietkau <nbd@nbd.name> +Subject: jffs2: add RENAME_EXCHANGE support + +Signed-off-by: Felix Fietkau <nbd@nbd.name> +--- + +--- a/fs/jffs2/dir.c ++++ b/fs/jffs2/dir.c +@@ -787,18 +787,31 @@ static int jffs2_rename (struct user_nam + int ret; + struct jffs2_sb_info *c = JFFS2_SB_INFO(old_dir_i->i_sb); + struct jffs2_inode_info *victim_f = NULL; ++ struct inode *fst_inode = d_inode(old_dentry); ++ struct inode *snd_inode = d_inode(new_dentry); + uint8_t type; + uint32_t now; + +- if (flags & ~(RENAME_NOREPLACE|RENAME_WHITEOUT)) ++ if (flags & ~(RENAME_NOREPLACE|RENAME_WHITEOUT|RENAME_EXCHANGE)) + return -EINVAL; + ++ if ((flags & RENAME_EXCHANGE) && (old_dir_i != new_dir_i)) { ++ if (S_ISDIR(fst_inode->i_mode) && !S_ISDIR(snd_inode->i_mode)) { ++ inc_nlink(new_dir_i); ++ drop_nlink(old_dir_i); ++ } ++ else if (!S_ISDIR(fst_inode->i_mode) && S_ISDIR(snd_inode->i_mode)) { ++ drop_nlink(new_dir_i); ++ inc_nlink(old_dir_i); ++ } ++ } ++ + /* The VFS will check for us and prevent trying to rename a + * file over a directory and vice versa, but if it's a directory, + * the VFS can't check whether the victim is empty. The filesystem + * needs to do that for itself. + */ +- if (d_really_is_positive(new_dentry)) { ++ if (d_really_is_positive(new_dentry) && !(flags & RENAME_EXCHANGE)) { + victim_f = JFFS2_INODE_INFO(d_inode(new_dentry)); + if (d_is_dir(new_dentry)) { + struct jffs2_full_dirent *fd; +@@ -833,7 +846,7 @@ static int jffs2_rename (struct user_nam + if (ret) + return ret; + +- if (victim_f) { ++ if (victim_f && !(flags & RENAME_EXCHANGE)) { + /* There was a victim. Kill it off nicely */ + if (d_is_dir(new_dentry)) + clear_nlink(d_inode(new_dentry)); +@@ -859,6 +872,12 @@ static int jffs2_rename (struct user_nam + if (flags & RENAME_WHITEOUT) + /* Replace with whiteout */ + ret = jffs2_whiteout(mnt_userns, old_dir_i, old_dentry); ++ else if (flags & RENAME_EXCHANGE) ++ /* Replace the original */ ++ ret = jffs2_do_link(c, JFFS2_INODE_INFO(old_dir_i), ++ d_inode(new_dentry)->i_ino, type, ++ old_dentry->d_name.name, old_dentry->d_name.len, ++ now); + else + /* Unlink the original */ + ret = jffs2_do_unlink(c, JFFS2_INODE_INFO(old_dir_i), +@@ -890,7 +909,7 @@ static int jffs2_rename (struct user_nam + return ret; + } + +- if (d_is_dir(old_dentry)) ++ if (d_is_dir(old_dentry) && !(flags & RENAME_EXCHANGE)) + drop_nlink(old_dir_i); + + new_dir_i->i_mtime = new_dir_i->i_ctime = old_dir_i->i_mtime = old_dir_i->i_ctime = ITIME(now); diff --git a/pkgs/patches-linux-5.15/142-jffs2-add-splice-ops.patch b/pkgs/patches-linux-5.15/142-jffs2-add-splice-ops.patch new file mode 100644 index 0000000..de847a1 --- /dev/null +++ b/pkgs/patches-linux-5.15/142-jffs2-add-splice-ops.patch @@ -0,0 +1,20 @@ +From: Felix Fietkau <nbd@nbd.name> +Subject: jffs2: add splice ops + +Add splice_read using generic_file_splice_read. +Add splice_write using iter_file_splice_write + +Signed-off-by: Felix Fietkau <nbd@nbd.name> +--- + +--- a/fs/jffs2/file.c ++++ b/fs/jffs2/file.c +@@ -53,6 +53,8 @@ const struct file_operations jffs2_file_ + .open = generic_file_open, + .read_iter = generic_file_read_iter, + .write_iter = generic_file_write_iter, ++ .splice_read = generic_file_splice_read, ++ .splice_write = iter_file_splice_write, + .unlocked_ioctl=jffs2_ioctl, + .mmap = generic_file_readonly_mmap, + .fsync = jffs2_fsync, diff --git a/pkgs/patches-linux-5.15/150-bridge_allow_receiption_on_disabled_port.patch b/pkgs/patches-linux-5.15/150-bridge_allow_receiption_on_disabled_port.patch new file mode 100644 index 0000000..9968a79 --- /dev/null +++ b/pkgs/patches-linux-5.15/150-bridge_allow_receiption_on_disabled_port.patch @@ -0,0 +1,45 @@ +From: Stephen Hemminger <stephen@networkplumber.org> +Subject: bridge: allow receiption on disabled port + +When an ethernet device is enslaved to a bridge, and the bridge STP +detects loss of carrier (or operational state down), then normally +packet receiption is blocked. + +This breaks control applications like WPA which maybe expecting to +receive packets to negotiate to bring link up. The bridge needs to +block forwarding packets from these disabled ports, but there is no +hard requirement to not allow local packet delivery. + +Signed-off-by: Stephen Hemminger <stephen@networkplumber.org> +Signed-off-by: Felix Fietkau <nbd@nbd.name> + +--- a/net/bridge/br_input.c ++++ b/net/bridge/br_input.c +@@ -204,6 +204,9 @@ static void __br_handle_local_finish(str + /* note: already called with rcu_read_lock */ + static int br_handle_local_finish(struct net *net, struct sock *sk, struct sk_buff *skb) + { ++ struct net_bridge_port *p = br_port_get_rcu(skb->dev); ++ ++ if (p->state != BR_STATE_DISABLED) + __br_handle_local_finish(skb); + + /* return 1 to signal the okfn() was called so it's ok to use the skb */ +@@ -369,6 +372,17 @@ static rx_handler_result_t br_handle_fra + + forward: + switch (p->state) { ++ case BR_STATE_DISABLED: ++ if (ether_addr_equal(p->br->dev->dev_addr, dest)) ++ skb->pkt_type = PACKET_HOST; ++ ++ if (NF_HOOK(NFPROTO_BRIDGE, NF_BR_PRE_ROUTING, ++ dev_net(skb->dev), NULL, skb, skb->dev, NULL, ++ br_handle_local_finish) == 1) { ++ return RX_HANDLER_PASS; ++ } ++ break; ++ + case BR_STATE_FORWARDING: + case BR_STATE_LEARNING: + if (ether_addr_equal(p->br->dev->dev_addr, dest)) diff --git a/pkgs/patches-linux-5.15/190-rtc-rs5c372-support_alarms_up_to_1_week.patch b/pkgs/patches-linux-5.15/190-rtc-rs5c372-support_alarms_up_to_1_week.patch new file mode 100644 index 0000000..13b79b5 --- /dev/null +++ b/pkgs/patches-linux-5.15/190-rtc-rs5c372-support_alarms_up_to_1_week.patch @@ -0,0 +1,94 @@ +From: Daniel González Cabanelas <dgcbueu@gmail.com> +Subject: [PATCH 1/2] rtc: rs5c372: support alarms up to 1 week + +The Ricoh R2221x, R2223x, RS5C372, RV5C387A chips can handle 1 week +alarms. + +Read the "wday" alarm register and convert it to a date to support up 1 +week in our driver. + +Signed-off-by: Daniel González Cabanelas <dgcbueu@gmail.com> +--- + drivers/rtc/rtc-rs5c372.c | 48 ++++++++++++++++++++++++++++++++++----- + 1 file changed, 42 insertions(+), 6 deletions(-) + +--- a/drivers/rtc/rtc-rs5c372.c ++++ b/drivers/rtc/rtc-rs5c372.c +@@ -393,7 +393,9 @@ static int rs5c_read_alarm(struct device + { + struct i2c_client *client = to_i2c_client(dev); + struct rs5c372 *rs5c = i2c_get_clientdata(client); +- int status; ++ int status, wday_offs; ++ struct rtc_time rtc; ++ unsigned long alarm_secs; + + status = rs5c_get_regs(rs5c); + if (status < 0) +@@ -403,6 +405,30 @@ static int rs5c_read_alarm(struct device + t->time.tm_sec = 0; + t->time.tm_min = bcd2bin(rs5c->regs[RS5C_REG_ALARM_A_MIN] & 0x7f); + t->time.tm_hour = rs5c_reg2hr(rs5c, rs5c->regs[RS5C_REG_ALARM_A_HOURS]); ++ t->time.tm_wday = ffs(rs5c->regs[RS5C_REG_ALARM_A_WDAY] & 0x7f) - 1; ++ ++ /* determine the day, month and year based on alarm wday, taking as a ++ * reference the current time from the rtc ++ */ ++ status = rs5c372_rtc_read_time(dev, &rtc); ++ if (status < 0) ++ return status; ++ ++ wday_offs = t->time.tm_wday - rtc.tm_wday; ++ alarm_secs = mktime64(rtc.tm_year + 1900, ++ rtc.tm_mon + 1, ++ rtc.tm_mday + wday_offs, ++ t->time.tm_hour, ++ t->time.tm_min, ++ t->time.tm_sec); ++ ++ if (wday_offs < 0 || (wday_offs == 0 && ++ (t->time.tm_hour < rtc.tm_hour || ++ (t->time.tm_hour == rtc.tm_hour && ++ t->time.tm_min <= rtc.tm_min)))) ++ alarm_secs += 7 * 86400; ++ ++ rtc_time64_to_tm(alarm_secs, &t->time); + + /* ... and status */ + t->enabled = !!(rs5c->regs[RS5C_REG_CTRL1] & RS5C_CTRL1_AALE); +@@ -417,12 +443,20 @@ static int rs5c_set_alarm(struct device + struct rs5c372 *rs5c = i2c_get_clientdata(client); + int status, addr, i; + unsigned char buf[3]; ++ struct rtc_time rtc_tm; ++ unsigned long rtc_secs, alarm_secs; + +- /* only handle up to 24 hours in the future, like RTC_ALM_SET */ +- if (t->time.tm_mday != -1 +- || t->time.tm_mon != -1 +- || t->time.tm_year != -1) ++ /* chip only can handle alarms up to one week in the future*/ ++ status = rs5c372_rtc_read_time(dev, &rtc_tm); ++ if (status) ++ return status; ++ rtc_secs = rtc_tm_to_time64(&rtc_tm); ++ alarm_secs = rtc_tm_to_time64(&t->time); ++ if (alarm_secs >= rtc_secs + 7 * 86400) { ++ dev_err(dev, "%s: alarm maximum is one week in the future (%d)\n", ++ __func__, status); + return -EINVAL; ++ } + + /* REVISIT: round up tm_sec */ + +@@ -443,7 +477,9 @@ static int rs5c_set_alarm(struct device + /* set alarm */ + buf[0] = bin2bcd(t->time.tm_min); + buf[1] = rs5c_hr2reg(rs5c, t->time.tm_hour); +- buf[2] = 0x7f; /* any/all days */ ++ /* each bit is the day of the week, 0x7f means all days */ ++ buf[2] = (t->time.tm_wday >= 0 && t->time.tm_wday < 7) ? ++ BIT(t->time.tm_wday) : 0x7f; + + for (i = 0; i < sizeof(buf); i++) { + addr = RS5C_ADDR(RS5C_REG_ALARM_A_MIN + i); diff --git a/pkgs/patches-linux-5.15/191-rtc-rs5c372-let_the_alarm_to_be_used_as_wakeup_source.patch b/pkgs/patches-linux-5.15/191-rtc-rs5c372-let_the_alarm_to_be_used_as_wakeup_source.patch new file mode 100644 index 0000000..7e9d0e6 --- /dev/null +++ b/pkgs/patches-linux-5.15/191-rtc-rs5c372-let_the_alarm_to_be_used_as_wakeup_source.patch @@ -0,0 +1,70 @@ +From: Daniel González Cabanelas <dgcbueu@gmail.com> +Subject: [PATCH 2/2] rtc: rs5c372: let the alarm to be used as wakeup source + +Currently there is no use for the interrupts on the rs5c372 RTC and the +wakealarm isn't enabled. There are some devices like NASes which use this +RTC to wake up from the power off state when the INTR pin is activated by +the alarm clock. + +Enable the alarm and let to be used as a wakeup source. + +Tested on a Buffalo LS421DE NAS. + +Signed-off-by: Daniel González Cabanelas <dgcbueu@gmail.com> +--- + drivers/rtc/rtc-rs5c372.c | 16 ++++++++++++++++ + 1 file changed, 16 insertions(+) + +--- a/drivers/rtc/rtc-rs5c372.c ++++ b/drivers/rtc/rtc-rs5c372.c +@@ -654,6 +654,7 @@ static int rs5c372_probe(struct i2c_clie + int err = 0; + int smbus_mode = 0; + struct rs5c372 *rs5c372; ++ bool rs5c372_can_wakeup_device = false; + + dev_dbg(&client->dev, "%s\n", __func__); + +@@ -689,6 +690,12 @@ static int rs5c372_probe(struct i2c_clie + else + rs5c372->type = id->driver_data; + ++#ifdef CONFIG_OF ++ if(of_property_read_bool(client->dev.of_node, ++ "wakeup-source")) ++ rs5c372_can_wakeup_device = true; ++#endif ++ + /* we read registers 0x0f then 0x00-0x0f; skip the first one */ + rs5c372->regs = &rs5c372->buf[1]; + rs5c372->smbus = smbus_mode; +@@ -722,6 +729,8 @@ static int rs5c372_probe(struct i2c_clie + goto exit; + } + ++ rs5c372->has_irq = 1; ++ + /* if the oscillator lost power and no other software (like + * the bootloader) set it up, do it here. + * +@@ -748,6 +757,10 @@ static int rs5c372_probe(struct i2c_clie + ); + + /* REVISIT use client->irq to register alarm irq ... */ ++ if (rs5c372_can_wakeup_device) { ++ device_init_wakeup(&client->dev, true); ++ } ++ + rs5c372->rtc = devm_rtc_device_register(&client->dev, + rs5c372_driver.driver.name, + &rs5c372_rtc_ops, THIS_MODULE); +@@ -761,6 +774,9 @@ static int rs5c372_probe(struct i2c_clie + if (err) + goto exit; + ++ /* the rs5c372 alarm only supports a minute accuracy */ ++ rs5c372->rtc->uie_unsupported = 1; ++ + return 0; + + exit: diff --git a/pkgs/patches-linux-5.15/200-v5.18-tools-resolve_btfids-Build-with-host-flags.patch b/pkgs/patches-linux-5.15/200-v5.18-tools-resolve_btfids-Build-with-host-flags.patch new file mode 100644 index 0000000..caec8db --- /dev/null +++ b/pkgs/patches-linux-5.15/200-v5.18-tools-resolve_btfids-Build-with-host-flags.patch @@ -0,0 +1,49 @@ +From cdbc4e3399ed8cdcf234a85f7a2482b622379e82 Mon Sep 17 00:00:00 2001 +From: Connor O'Brien <connoro@google.com> +Date: Wed, 12 Jan 2022 00:25:03 +0000 +Subject: [PATCH] tools/resolve_btfids: Build with host flags + +resolve_btfids is built using $(HOSTCC) and $(HOSTLD) but does not +pick up the corresponding flags. As a result, host-specific settings +(such as a sysroot specified via HOSTCFLAGS=--sysroot=..., or a linker +specified via HOSTLDFLAGS=-fuse-ld=...) will not be respected. + +Fix this by setting CFLAGS to KBUILD_HOSTCFLAGS and LDFLAGS to +KBUILD_HOSTLDFLAGS. + +Also pass the cflags through to libbpf via EXTRA_CFLAGS to ensure that +the host libbpf is built with flags consistent with resolve_btfids. + +Signed-off-by: Connor O'Brien <connoro@google.com> +Signed-off-by: Andrii Nakryiko <andrii@kernel.org> +Acked-by: Song Liu <songliubraving@fb.com> +Link: https://lore.kernel.org/bpf/20220112002503.115968-1-connoro@google.com +(cherry picked from commit 0e3a1c902ffb56e9fe4416f0cd382c97b09ecbf6) +Signed-off-by: Stijn Tintel <stijn@linux-ipv6.be> +--- + tools/bpf/resolve_btfids/Makefile | 6 ++++-- + 1 file changed, 4 insertions(+), 2 deletions(-) + +--- a/tools/bpf/resolve_btfids/Makefile ++++ b/tools/bpf/resolve_btfids/Makefile +@@ -23,6 +23,8 @@ CC = $(HOSTCC) + LD = $(HOSTLD) + ARCH = $(HOSTARCH) + RM ?= rm ++CFLAGS := $(KBUILD_HOSTCFLAGS) ++LDFLAGS := $(KBUILD_HOSTLDFLAGS) + + OUTPUT ?= $(srctree)/tools/bpf/resolve_btfids/ + +@@ -45,9 +47,9 @@ $(SUBCMDOBJ): fixdep FORCE | $(OUTPUT)/l + $(Q)$(MAKE) -C $(SUBCMD_SRC) OUTPUT=$(abspath $(dir $@))/ $(abspath $@) + + $(BPFOBJ): $(wildcard $(LIBBPF_SRC)/*.[ch] $(LIBBPF_SRC)/Makefile) | $(OUTPUT)/libbpf +- $(Q)$(MAKE) $(submake_extras) -C $(LIBBPF_SRC) OUTPUT=$(abspath $(dir $@))/ $(abspath $@) ++ $(Q)$(MAKE) $(submake_extras) -C $(LIBBPF_SRC) OUTPUT=$(abspath $(dir $@))/ EXTRA_CFLAGS="$(CFLAGS)" $(abspath $@) + +-CFLAGS := -g \ ++CFLAGS += -g \ + -I$(srctree)/tools/include \ + -I$(srctree)/tools/include/uapi \ + -I$(LIBBPF_SRC) \ diff --git a/pkgs/patches-linux-5.15/203-kallsyms_uncompressed.patch b/pkgs/patches-linux-5.15/203-kallsyms_uncompressed.patch new file mode 100644 index 0000000..ea407d6 --- /dev/null +++ b/pkgs/patches-linux-5.15/203-kallsyms_uncompressed.patch @@ -0,0 +1,119 @@ +From: Felix Fietkau <nbd@nbd.name> +Subject: kernel: add a config option for keeping the kallsyms table uncompressed, saving ~9kb kernel size after lzma on ar71xx + +[john@phrozen.org: added to my upstream queue 30.12.2016] +lede-commit: e0e3509b5ce2ccf93d4d67ea907613f5f7ec2eed +Signed-off-by: Felix Fietkau <nbd@nbd.name> +--- + init/Kconfig | 11 +++++++++++ + kernel/kallsyms.c | 8 ++++++++ + scripts/kallsyms.c | 12 ++++++++++++ + scripts/link-vmlinux.sh | 4 ++++ + 4 files changed, 35 insertions(+) + +--- a/init/Kconfig ++++ b/init/Kconfig +@@ -1443,6 +1443,17 @@ config SYSCTL_ARCH_UNALIGN_ALLOW + the unaligned access emulation. + see arch/parisc/kernel/unaligned.c for reference + ++config KALLSYMS_UNCOMPRESSED ++ bool "Keep kallsyms uncompressed" ++ depends on KALLSYMS ++ help ++ Normally kallsyms contains compressed symbols (using a token table), ++ reducing the uncompressed kernel image size. Keeping the symbol table ++ uncompressed significantly improves the size of this part in compressed ++ kernel images. ++ ++ Say N unless you need compressed kernel images to be small. ++ + config HAVE_PCSPKR_PLATFORM + bool + +--- a/kernel/kallsyms.c ++++ b/kernel/kallsyms.c +@@ -80,6 +80,11 @@ static unsigned int kallsyms_expand_symb + * For every byte on the compressed symbol data, copy the table + * entry for that byte. + */ ++#ifdef CONFIG_KALLSYMS_UNCOMPRESSED ++ memcpy(result, data + 1, len - 1); ++ result += len - 1; ++ len = 0; ++#endif + while (len) { + tptr = &kallsyms_token_table[kallsyms_token_index[*data]]; + data++; +@@ -112,6 +117,9 @@ tail: + */ + static char kallsyms_get_symbol_type(unsigned int off) + { ++#ifdef CONFIG_KALLSYMS_UNCOMPRESSED ++ return kallsyms_names[off + 1]; ++#endif + /* + * Get just the first code, look it up in the token table, + * and return the first char from this token. +--- a/scripts/kallsyms.c ++++ b/scripts/kallsyms.c +@@ -58,6 +58,7 @@ static struct addr_range percpu_range = + static struct sym_entry **table; + static unsigned int table_size, table_cnt; + static int all_symbols; ++static int uncompressed; + static int absolute_percpu; + static int base_relative; + +@@ -486,6 +487,9 @@ static void write_src(void) + + free(markers); + ++ if (uncompressed) ++ return; ++ + output_label("kallsyms_token_table"); + off = 0; + for (i = 0; i < 256; i++) { +@@ -537,6 +541,9 @@ static unsigned char *find_token(unsigne + { + int i; + ++ if (uncompressed) ++ return NULL; ++ + for (i = 0; i < len - 1; i++) { + if (str[i] == token[0] && str[i+1] == token[1]) + return &str[i]; +@@ -609,6 +616,9 @@ static void optimize_result(void) + { + int i, best; + ++ if (uncompressed) ++ return; ++ + /* using the '\0' symbol last allows compress_symbols to use standard + * fast string functions */ + for (i = 255; i >= 0; i--) { +@@ -773,6 +783,8 @@ int main(int argc, char **argv) + absolute_percpu = 1; + else if (strcmp(argv[i], "--base-relative") == 0) + base_relative = 1; ++ else if (strcmp(argv[i], "--uncompressed") == 0) ++ uncompressed = 1; + else + usage(); + } +--- a/scripts/link-vmlinux.sh ++++ b/scripts/link-vmlinux.sh +@@ -263,6 +263,10 @@ kallsyms() + kallsymopt="${kallsymopt} --base-relative" + fi + ++ if [ -n "${CONFIG_KALLSYMS_UNCOMPRESSED}" ]; then ++ kallsymopt="${kallsymopt} --uncompressed" ++ fi ++ + info KSYMS ${2} + ${NM} -n ${1} | scripts/kallsyms ${kallsymopt} > ${2} + } diff --git a/pkgs/patches-linux-5.15/204-module_strip.patch b/pkgs/patches-linux-5.15/204-module_strip.patch new file mode 100644 index 0000000..dee240d --- /dev/null +++ b/pkgs/patches-linux-5.15/204-module_strip.patch @@ -0,0 +1,212 @@ +From a779a482fb9b9f8fcdf8b2519c789b4b9bb5dd05 Mon Sep 17 00:00:00 2001 +From: Felix Fietkau <nbd@nbd.name> +Date: Fri, 7 Jul 2017 16:56:48 +0200 +Subject: build: add a hack for removing non-essential module info + +Signed-off-by: Felix Fietkau <nbd@nbd.name> +--- + include/linux/module.h | 13 ++++++++----- + include/linux/moduleparam.h | 15 ++++++++++++--- + init/Kconfig | 7 +++++++ + kernel/module.c | 5 ++++- + scripts/mod/modpost.c | 12 ++++++++++++ + 5 files changed, 43 insertions(+), 9 deletions(-) + +--- a/include/linux/module.h ++++ b/include/linux/module.h +@@ -164,6 +164,7 @@ extern void cleanup_module(void); + + /* Generic info of form tag = "info" */ + #define MODULE_INFO(tag, info) __MODULE_INFO(tag, tag, info) ++#define MODULE_INFO_STRIP(tag, info) __MODULE_INFO_STRIP(tag, tag, info) + + /* For userspace: you can also call me... */ + #define MODULE_ALIAS(_alias) MODULE_INFO(alias, _alias) +@@ -233,12 +234,12 @@ extern void cleanup_module(void); + * Author(s), use "Name <email>" or just "Name", for multiple + * authors use multiple MODULE_AUTHOR() statements/lines. + */ +-#define MODULE_AUTHOR(_author) MODULE_INFO(author, _author) ++#define MODULE_AUTHOR(_author) MODULE_INFO_STRIP(author, _author) + + /* What your module does. */ +-#define MODULE_DESCRIPTION(_description) MODULE_INFO(description, _description) ++#define MODULE_DESCRIPTION(_description) MODULE_INFO_STRIP(description, _description) + +-#ifdef MODULE ++#if defined(MODULE) && !defined(CONFIG_MODULE_STRIPPED) + /* Creates an alias so file2alias.c can find device table. */ + #define MODULE_DEVICE_TABLE(type, name) \ + extern typeof(name) __mod_##type##__##name##_device_table \ +@@ -265,7 +266,9 @@ extern typeof(name) __mod_##type##__##na + */ + + #if defined(MODULE) || !defined(CONFIG_SYSFS) +-#define MODULE_VERSION(_version) MODULE_INFO(version, _version) ++#define MODULE_VERSION(_version) MODULE_INFO_STRIP(version, _version) ++#elif defined(CONFIG_MODULE_STRIPPED) ++#define MODULE_VERSION(_version) __MODULE_INFO_DISABLED(version) + #else + #define MODULE_VERSION(_version) \ + MODULE_INFO(version, _version); \ +@@ -288,7 +291,7 @@ extern typeof(name) __mod_##type##__##na + /* Optional firmware file (or files) needed by the module + * format is simply firmware file name. Multiple firmware + * files require multiple MODULE_FIRMWARE() specifiers */ +-#define MODULE_FIRMWARE(_firmware) MODULE_INFO(firmware, _firmware) ++#define MODULE_FIRMWARE(_firmware) MODULE_INFO_STRIP(firmware, _firmware) + + #define MODULE_IMPORT_NS(ns) MODULE_INFO(import_ns, #ns) + +--- a/include/linux/moduleparam.h ++++ b/include/linux/moduleparam.h +@@ -20,6 +20,16 @@ + /* Chosen so that structs with an unsigned long line up. */ + #define MAX_PARAM_PREFIX_LEN (64 - sizeof(unsigned long)) + ++/* This struct is here for syntactic coherency, it is not used */ ++#define __MODULE_INFO_DISABLED(name) \ ++ struct __UNIQUE_ID(name) {} ++ ++#ifdef CONFIG_MODULE_STRIPPED ++#define __MODULE_INFO_STRIP(tag, name, info) __MODULE_INFO_DISABLED(name) ++#else ++#define __MODULE_INFO_STRIP(tag, name, info) __MODULE_INFO(tag, name, info) ++#endif ++ + #define __MODULE_INFO(tag, name, info) \ + static const char __UNIQUE_ID(name)[] \ + __used __section(".modinfo") __aligned(1) \ +@@ -31,7 +41,7 @@ + /* One for each parameter, describing how to use it. Some files do + multiple of these per line, so can't just use MODULE_INFO. */ + #define MODULE_PARM_DESC(_parm, desc) \ +- __MODULE_INFO(parm, _parm, #_parm ":" desc) ++ __MODULE_INFO_STRIP(parm, _parm, #_parm ":" desc) + + struct kernel_param; + +--- a/init/Kconfig ++++ b/init/Kconfig +@@ -2352,6 +2352,13 @@ config UNUSED_KSYMS_WHITELIST + one per line. The path can be absolute, or relative to the kernel + source tree. + ++config MODULE_STRIPPED ++ bool "Reduce module size" ++ depends on MODULES ++ help ++ Remove module parameter descriptions, author info, version, aliases, ++ device tables, etc. ++ + endif # MODULES + + config MODULES_TREE_LOOKUP +--- a/kernel/module.c ++++ b/kernel/module.c +@@ -1218,6 +1218,7 @@ static struct module_attribute *modinfo_ + + static const char vermagic[] = VERMAGIC_STRING; + ++#if defined(CONFIG_MODVERSIONS) || !defined(CONFIG_MODULE_STRIPPED) + static int try_to_force_load(struct module *mod, const char *reason) + { + #ifdef CONFIG_MODULE_FORCE_LOAD +@@ -1229,6 +1230,7 @@ static int try_to_force_load(struct modu + return -ENOEXEC; + #endif + } ++#endif + + #ifdef CONFIG_MODVERSIONS + +@@ -3266,9 +3268,11 @@ static int setup_load_info(struct load_i + + static int check_modinfo(struct module *mod, struct load_info *info, int flags) + { +- const char *modmagic = get_modinfo(info, "vermagic"); + int err; + ++#ifndef CONFIG_MODULE_STRIPPED ++ const char *modmagic = get_modinfo(info, "vermagic"); ++ + if (flags & MODULE_INIT_IGNORE_VERMAGIC) + modmagic = NULL; + +@@ -3289,6 +3293,7 @@ static int check_modinfo(struct module * + mod->name); + add_taint_module(mod, TAINT_OOT_MODULE, LOCKDEP_STILL_OK); + } ++#endif + + check_modinfo_retpoline(mod, info); + +--- a/scripts/mod/modpost.c ++++ b/scripts/mod/modpost.c +@@ -2034,7 +2034,9 @@ static void read_symbols(const char *mod + symname = remove_dot(info.strtab + sym->st_name); + + handle_symbol(mod, &info, sym, symname); ++#ifndef CONFIG_MODULE_STRIPPED + handle_moddevtable(mod, &info, sym, symname); ++#endif + } + + for (sym = info.symtab_start; sym < info.symtab_stop; sym++) { +@@ -2213,8 +2215,10 @@ static void add_header(struct buffer *b, + buf_printf(b, "BUILD_SALT;\n"); + buf_printf(b, "BUILD_LTO_INFO;\n"); + buf_printf(b, "\n"); ++#ifndef CONFIG_MODULE_STRIPPED + buf_printf(b, "MODULE_INFO(vermagic, VERMAGIC_STRING);\n"); + buf_printf(b, "MODULE_INFO(name, KBUILD_MODNAME);\n"); ++#endif + buf_printf(b, "\n"); + buf_printf(b, "__visible struct module __this_module\n"); + buf_printf(b, "__section(\".gnu.linkonce.this_module\") = {\n"); +@@ -2231,8 +2235,10 @@ static void add_header(struct buffer *b, + + static void add_intree_flag(struct buffer *b, int is_intree) + { ++#ifndef CONFIG_MODULE_STRIPPED + if (is_intree) + buf_printf(b, "\nMODULE_INFO(intree, \"Y\");\n"); ++#endif + } + + /* Cannot check for assembler */ +@@ -2245,8 +2251,10 @@ static void add_retpoline(struct buffer + + static void add_staging_flag(struct buffer *b, const char *name) + { ++#ifndef CONFIG_MODULE_STRIPPED + if (strstarts(name, "drivers/staging")) + buf_printf(b, "\nMODULE_INFO(staging, \"Y\");\n"); ++#endif + } + + /** +@@ -2326,11 +2334,13 @@ static void add_depends(struct buffer *b + + static void add_srcversion(struct buffer *b, struct module *mod) + { ++#ifndef CONFIG_MODULE_STRIPPED + if (mod->srcversion[0]) { + buf_printf(b, "\n"); + buf_printf(b, "MODULE_INFO(srcversion, \"%s\");\n", + mod->srcversion); + } ++#endif + } + + static void write_buf(struct buffer *b, const char *fname) +@@ -2579,7 +2589,9 @@ int main(int argc, char **argv) + add_staging_flag(&buf, mod->name); + add_versions(&buf, mod); + add_depends(&buf, mod); ++#ifndef CONFIG_MODULE_STRIPPED + add_moddevtable(&buf, mod); ++#endif + add_srcversion(&buf, mod); + + sprintf(fname, "%s.mod.c", mod->name); diff --git a/pkgs/patches-linux-5.15/205-backtrace_module_info.patch b/pkgs/patches-linux-5.15/205-backtrace_module_info.patch new file mode 100644 index 0000000..6379ce0 --- /dev/null +++ b/pkgs/patches-linux-5.15/205-backtrace_module_info.patch @@ -0,0 +1,41 @@ +From: Felix Fietkau <nbd@nbd.name> +Subject: kernel: when KALLSYMS is disabled, print module address + size for matching backtrace entries + +[john@phrozen.org: felix will add this to his upstream queue] + +lede-commit 53827cdc824556cda910b23ce5030c363b8f1461 +Signed-off-by: Felix Fietkau <nbd@nbd.name> +--- + lib/vsprintf.c | 15 +++++++++++---- + 1 file changed, 11 insertions(+), 4 deletions(-) + +--- a/lib/vsprintf.c ++++ b/lib/vsprintf.c +@@ -1003,8 +1003,10 @@ char *symbol_string(char *buf, char *end + struct printf_spec spec, const char *fmt) + { + unsigned long value; +-#ifdef CONFIG_KALLSYMS + char sym[KSYM_SYMBOL_LEN]; ++#ifndef CONFIG_KALLSYMS ++ struct module *mod; ++ int len; + #endif + + if (fmt[1] == 'R') +@@ -1025,8 +1027,14 @@ char *symbol_string(char *buf, char *end + + return string_nocheck(buf, end, sym, spec); + #else +- return special_hex_number(buf, end, value, sizeof(void *)); ++ len = snprintf(sym, sizeof(sym), "0x%lx", value); ++ mod = __module_address(value); ++ if (mod) ++ snprintf(sym + len, sizeof(sym) - len, " [%s@%p+0x%x]", ++ mod->name, mod->core_layout.base, ++ mod->core_layout.size); + #endif ++ return string(buf, end, sym, spec); + } + + static const struct printf_spec default_str_spec = { diff --git a/pkgs/patches-linux-5.15/205-kconfig-exit.patch b/pkgs/patches-linux-5.15/205-kconfig-exit.patch new file mode 100644 index 0000000..9f3bb8f --- /dev/null +++ b/pkgs/patches-linux-5.15/205-kconfig-exit.patch @@ -0,0 +1,20 @@ +From 300d26562ce4dc427154cb247beb75db4b1f0774 Mon Sep 17 00:00:00 2001 +From: OpenWrt community <openwrt-devel@lists.openwrt.org> +Date: Wed, 13 Jul 2022 13:29:57 +0200 +Subject: [PATCH] scripts/Kconfig: Kconfig exit + +--- + scripts/kconfig/conf.c | 2 ++ + 1 file changed, 2 insertions(+) + +--- a/scripts/kconfig/conf.c ++++ b/scripts/kconfig/conf.c +@@ -435,6 +435,8 @@ static int conf_sym(struct menu *menu) + break; + continue; + case 0: ++ if (!sym_has_value(sym) && !tty_stdio && getenv("FAIL_ON_UNCONFIGURED")) ++ exit(1); + newval = oldval; + break; + case '?': diff --git a/pkgs/patches-linux-5.15/210-darwin_scripts_include.patch b/pkgs/patches-linux-5.15/210-darwin_scripts_include.patch new file mode 100644 index 0000000..be6adc0 --- /dev/null +++ b/pkgs/patches-linux-5.15/210-darwin_scripts_include.patch @@ -0,0 +1,3053 @@ +From db7c30dcd9a0391bf13b62c9f91e144d762ef43a Mon Sep 17 00:00:00 2001 +From: Florian Fainelli <f.fainelli@gmail.com> +Date: Fri, 7 Jul 2017 17:00:49 +0200 +Subject: Add an OSX specific patch to make the kernel be compiled + +lede-commit: 3fc2a24f0422b2f55f9ed43f116db3111f700526 +Signed-off-by: Florian Fainelli <f.fainelli@gmail.com> +--- + scripts/kconfig/Makefile | 3 + + scripts/mod/elf.h | 3007 ++++++++++++++++++++++++++++++++++++++++++++ + scripts/mod/mk_elfconfig.c | 4 + + scripts/mod/modpost.h | 4 + + 4 files changed, 3018 insertions(+) + create mode 100644 scripts/mod/elf.h + +--- /dev/null ++++ b/scripts/mod/elf.h +@@ -0,0 +1,3007 @@ ++/* This file defines standard ELF types, structures, and macros. ++ Copyright (C) 1995-2012 Free Software Foundation, Inc. ++ This file is part of the GNU C Library. ++ ++ The GNU C Library is free software; you can redistribute it and/or ++ modify it under the terms of the GNU Lesser General Public ++ License as published by the Free Software Foundation; either ++ version 2.1 of the License, or (at your option) any later version. ++ ++ The GNU C Library is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ Lesser General Public License for more details. ++ ++ You should have received a copy of the GNU Lesser General Public ++ License along with the GNU C Library; if not, see ++ <http://www.gnu.org/licenses/>. */ ++ ++#ifndef _ELF_H ++#define _ELF_H 1 ++ ++/* Standard ELF types. */ ++ ++#include <stdint.h> ++ ++/* Type for a 16-bit quantity. */ ++typedef uint16_t Elf32_Half; ++typedef uint16_t Elf64_Half; ++ ++/* Types for signed and unsigned 32-bit quantities. */ ++typedef uint32_t Elf32_Word; ++typedef int32_t Elf32_Sword; ++typedef uint32_t Elf64_Word; ++typedef int32_t Elf64_Sword; ++ ++/* Types for signed and unsigned 64-bit quantities. */ ++typedef uint64_t Elf32_Xword; ++typedef int64_t Elf32_Sxword; ++typedef uint64_t Elf64_Xword; ++typedef int64_t Elf64_Sxword; ++ ++/* Type of addresses. */ ++typedef uint32_t Elf32_Addr; ++typedef uint64_t Elf64_Addr; ++ ++/* Type of file offsets. */ ++typedef uint32_t Elf32_Off; ++typedef uint64_t Elf64_Off; ++ ++/* Type for section indices, which are 16-bit quantities. */ ++typedef uint16_t Elf32_Section; ++typedef uint16_t Elf64_Section; ++ ++/* Type for version symbol information. */ ++typedef Elf32_Half Elf32_Versym; ++typedef Elf64_Half Elf64_Versym; ++ ++ ++/* The ELF file header. This appears at the start of every ELF file. */ ++ ++#define EI_NIDENT (16) ++ ++typedef struct ++{ ++ unsigned char e_ident[EI_NIDENT]; /* Magic number and other info */ ++ Elf32_Half e_type; /* Object file type */ ++ Elf32_Half e_machine; /* Architecture */ ++ Elf32_Word e_version; /* Object file version */ ++ Elf32_Addr e_entry; /* Entry point virtual address */ ++ Elf32_Off e_phoff; /* Program header table file offset */ ++ Elf32_Off e_shoff; /* Section header table file offset */ ++ Elf32_Word e_flags; /* Processor-specific flags */ ++ Elf32_Half e_ehsize; /* ELF header size in bytes */ ++ Elf32_Half e_phentsize; /* Program header table entry size */ ++ Elf32_Half e_phnum; /* Program header table entry count */ ++ Elf32_Half e_shentsize; /* Section header table entry size */ ++ Elf32_Half e_shnum; /* Section header table entry count */ ++ Elf32_Half e_shstrndx; /* Section header string table index */ ++} Elf32_Ehdr; ++ ++typedef struct ++{ ++ unsigned char e_ident[EI_NIDENT]; /* Magic number and other info */ ++ Elf64_Half e_type; /* Object file type */ ++ Elf64_Half e_machine; /* Architecture */ ++ Elf64_Word e_version; /* Object file version */ ++ Elf64_Addr e_entry; /* Entry point virtual address */ ++ Elf64_Off e_phoff; /* Program header table file offset */ ++ Elf64_Off e_shoff; /* Section header table file offset */ ++ Elf64_Word e_flags; /* Processor-specific flags */ ++ Elf64_Half e_ehsize; /* ELF header size in bytes */ ++ Elf64_Half e_phentsize; /* Program header table entry size */ ++ Elf64_Half e_phnum; /* Program header table entry count */ ++ Elf64_Half e_shentsize; /* Section header table entry size */ ++ Elf64_Half e_shnum; /* Section header table entry count */ ++ Elf64_Half e_shstrndx; /* Section header string table index */ ++} Elf64_Ehdr; ++ ++/* Fields in the e_ident array. The EI_* macros are indices into the ++ array. The macros under each EI_* macro are the values the byte ++ may have. */ ++ ++#define EI_MAG0 0 /* File identification byte 0 index */ ++#define ELFMAG0 0x7f /* Magic number byte 0 */ ++ ++#define EI_MAG1 1 /* File identification byte 1 index */ ++#define ELFMAG1 'E' /* Magic number byte 1 */ ++ ++#define EI_MAG2 2 /* File identification byte 2 index */ ++#define ELFMAG2 'L' /* Magic number byte 2 */ ++ ++#define EI_MAG3 3 /* File identification byte 3 index */ ++#define ELFMAG3 'F' /* Magic number byte 3 */ ++ ++/* Conglomeration of the identification bytes, for easy testing as a word. */ ++#define ELFMAG "\177ELF" ++#define SELFMAG 4 ++ ++#define EI_CLASS 4 /* File class byte index */ ++#define ELFCLASSNONE 0 /* Invalid class */ ++#define ELFCLASS32 1 /* 32-bit objects */ ++#define ELFCLASS64 2 /* 64-bit objects */ ++#define ELFCLASSNUM 3 ++ ++#define EI_DATA 5 /* Data encoding byte index */ ++#define ELFDATANONE 0 /* Invalid data encoding */ ++#define ELFDATA2LSB 1 /* 2's complement, little endian */ ++#define ELFDATA2MSB 2 /* 2's complement, big endian */ ++#define ELFDATANUM 3 ++ ++#define EI_VERSION 6 /* File version byte index */ ++ /* Value must be EV_CURRENT */ ++ ++#define EI_OSABI 7 /* OS ABI identification */ ++#define ELFOSABI_NONE 0 /* UNIX System V ABI */ ++#define ELFOSABI_SYSV 0 /* Alias. */ ++#define ELFOSABI_HPUX 1 /* HP-UX */ ++#define ELFOSABI_NETBSD 2 /* NetBSD. */ ++#define ELFOSABI_GNU 3 /* Object uses GNU ELF extensions. */ ++#define ELFOSABI_LINUX ELFOSABI_GNU /* Compatibility alias. */ ++#define ELFOSABI_SOLARIS 6 /* Sun Solaris. */ ++#define ELFOSABI_AIX 7 /* IBM AIX. */ ++#define ELFOSABI_IRIX 8 /* SGI Irix. */ ++#define ELFOSABI_FREEBSD 9 /* FreeBSD. */ ++#define ELFOSABI_TRU64 10 /* Compaq TRU64 UNIX. */ ++#define ELFOSABI_MODESTO 11 /* Novell Modesto. */ ++#define ELFOSABI_OPENBSD 12 /* OpenBSD. */ ++#define ELFOSABI_ARM_AEABI 64 /* ARM EABI */ ++#define ELFOSABI_ARM 97 /* ARM */ ++#define ELFOSABI_STANDALONE 255 /* Standalone (embedded) application */ ++ ++#define EI_ABIVERSION 8 /* ABI version */ ++ ++#define EI_PAD 9 /* Byte index of padding bytes */ ++ ++/* Legal values for e_type (object file type). */ ++ ++#define ET_NONE 0 /* No file type */ ++#define ET_REL 1 /* Relocatable file */ ++#define ET_EXEC 2 /* Executable file */ ++#define ET_DYN 3 /* Shared object file */ ++#define ET_CORE 4 /* Core file */ ++#define ET_NUM 5 /* Number of defined types */ ++#define ET_LOOS 0xfe00 /* OS-specific range start */ ++#define ET_HIOS 0xfeff /* OS-specific range end */ ++#define ET_LOPROC 0xff00 /* Processor-specific range start */ ++#define ET_HIPROC 0xffff /* Processor-specific range end */ ++ ++/* Legal values for e_machine (architecture). */ ++ ++#define EM_NONE 0 /* No machine */ ++#define EM_M32 1 /* AT&T WE 32100 */ ++#define EM_SPARC 2 /* SUN SPARC */ ++#define EM_386 3 /* Intel 80386 */ ++#define EM_68K 4 /* Motorola m68k family */ ++#define EM_88K 5 /* Motorola m88k family */ ++#define EM_860 7 /* Intel 80860 */ ++#define EM_MIPS 8 /* MIPS R3000 big-endian */ ++#define EM_S370 9 /* IBM System/370 */ ++#define EM_MIPS_RS3_LE 10 /* MIPS R3000 little-endian */ ++ ++#define EM_PARISC 15 /* HPPA */ ++#define EM_VPP500 17 /* Fujitsu VPP500 */ ++#define EM_SPARC32PLUS 18 /* Sun's "v8plus" */ ++#define EM_960 19 /* Intel 80960 */ ++#define EM_PPC 20 /* PowerPC */ ++#define EM_PPC64 21 /* PowerPC 64-bit */ ++#define EM_S390 22 /* IBM S390 */ ++ ++#define EM_V800 36 /* NEC V800 series */ ++#define EM_FR20 37 /* Fujitsu FR20 */ ++#define EM_RH32 38 /* TRW RH-32 */ ++#define EM_RCE 39 /* Motorola RCE */ ++#define EM_ARM 40 /* ARM */ ++#define EM_FAKE_ALPHA 41 /* Digital Alpha */ ++#define EM_SH 42 /* Hitachi SH */ ++#define EM_SPARCV9 43 /* SPARC v9 64-bit */ ++#define EM_TRICORE 44 /* Siemens Tricore */ ++#define EM_ARC 45 /* Argonaut RISC Core */ ++#define EM_H8_300 46 /* Hitachi H8/300 */ ++#define EM_H8_300H 47 /* Hitachi H8/300H */ ++#define EM_H8S 48 /* Hitachi H8S */ ++#define EM_H8_500 49 /* Hitachi H8/500 */ ++#define EM_IA_64 50 /* Intel Merced */ ++#define EM_MIPS_X 51 /* Stanford MIPS-X */ ++#define EM_COLDFIRE 52 /* Motorola Coldfire */ ++#define EM_68HC12 53 /* Motorola M68HC12 */ ++#define EM_MMA 54 /* Fujitsu MMA Multimedia Accelerator*/ ++#define EM_PCP 55 /* Siemens PCP */ ++#define EM_NCPU 56 /* Sony nCPU embeeded RISC */ ++#define EM_NDR1 57 /* Denso NDR1 microprocessor */ ++#define EM_STARCORE 58 /* Motorola Start*Core processor */ ++#define EM_ME16 59 /* Toyota ME16 processor */ ++#define EM_ST100 60 /* STMicroelectronic ST100 processor */ ++#define EM_TINYJ 61 /* Advanced Logic Corp. Tinyj emb.fam*/ ++#define EM_X86_64 62 /* AMD x86-64 architecture */ ++#define EM_PDSP 63 /* Sony DSP Processor */ ++ ++#define EM_FX66 66 /* Siemens FX66 microcontroller */ ++#define EM_ST9PLUS 67 /* STMicroelectronics ST9+ 8/16 mc */ ++#define EM_ST7 68 /* STmicroelectronics ST7 8 bit mc */ ++#define EM_68HC16 69 /* Motorola MC68HC16 microcontroller */ ++#define EM_68HC11 70 /* Motorola MC68HC11 microcontroller */ ++#define EM_68HC08 71 /* Motorola MC68HC08 microcontroller */ ++#define EM_68HC05 72 /* Motorola MC68HC05 microcontroller */ ++#define EM_SVX 73 /* Silicon Graphics SVx */ ++#define EM_ST19 74 /* STMicroelectronics ST19 8 bit mc */ ++#define EM_VAX 75 /* Digital VAX */ ++#define EM_CRIS 76 /* Axis Communications 32-bit embedded processor */ ++#define EM_JAVELIN 77 /* Infineon Technologies 32-bit embedded processor */ ++#define EM_FIREPATH 78 /* Element 14 64-bit DSP Processor */ ++#define EM_ZSP 79 /* LSI Logic 16-bit DSP Processor */ ++#define EM_MMIX 80 /* Donald Knuth's educational 64-bit processor */ ++#define EM_HUANY 81 /* Harvard University machine-independent object files */ ++#define EM_PRISM 82 /* SiTera Prism */ ++#define EM_AVR 83 /* Atmel AVR 8-bit microcontroller */ ++#define EM_FR30 84 /* Fujitsu FR30 */ ++#define EM_D10V 85 /* Mitsubishi D10V */ ++#define EM_D30V 86 /* Mitsubishi D30V */ ++#define EM_V850 87 /* NEC v850 */ ++#define EM_M32R 88 /* Mitsubishi M32R */ ++#define EM_MN10300 89 /* Matsushita MN10300 */ ++#define EM_MN10200 90 /* Matsushita MN10200 */ ++#define EM_PJ 91 /* picoJava */ ++#define EM_OPENRISC 92 /* OpenRISC 32-bit embedded processor */ ++#define EM_ARC_A5 93 /* ARC Cores Tangent-A5 */ ++#define EM_XTENSA 94 /* Tensilica Xtensa Architecture */ ++#define EM_TILEPRO 188 /* Tilera TILEPro */ ++#define EM_TILEGX 191 /* Tilera TILE-Gx */ ++#define EM_NUM 192 ++ ++/* If it is necessary to assign new unofficial EM_* values, please ++ pick large random numbers (0x8523, 0xa7f2, etc.) to minimize the ++ chances of collision with official or non-GNU unofficial values. */ ++ ++#define EM_ALPHA 0x9026 ++ ++/* Legal values for e_version (version). */ ++ ++#define EV_NONE 0 /* Invalid ELF version */ ++#define EV_CURRENT 1 /* Current version */ ++#define EV_NUM 2 ++ ++/* Section header. */ ++ ++typedef struct ++{ ++ Elf32_Word sh_name; /* Section name (string tbl index) */ ++ Elf32_Word sh_type; /* Section type */ ++ Elf32_Word sh_flags; /* Section flags */ ++ Elf32_Addr sh_addr; /* Section virtual addr at execution */ ++ Elf32_Off sh_offset; /* Section file offset */ ++ Elf32_Word sh_size; /* Section size in bytes */ ++ Elf32_Word sh_link; /* Link to another section */ ++ Elf32_Word sh_info; /* Additional section information */ ++ Elf32_Word sh_addralign; /* Section alignment */ ++ Elf32_Word sh_entsize; /* Entry size if section holds table */ ++} Elf32_Shdr; ++ ++typedef struct ++{ ++ Elf64_Word sh_name; /* Section name (string tbl index) */ ++ Elf64_Word sh_type; /* Section type */ ++ Elf64_Xword sh_flags; /* Section flags */ ++ Elf64_Addr sh_addr; /* Section virtual addr at execution */ ++ Elf64_Off sh_offset; /* Section file offset */ ++ Elf64_Xword sh_size; /* Section size in bytes */ ++ Elf64_Word sh_link; /* Link to another section */ ++ Elf64_Word sh_info; /* Additional section information */ ++ Elf64_Xword sh_addralign; /* Section alignment */ ++ Elf64_Xword sh_entsize; /* Entry size if section holds table */ ++} Elf64_Shdr; ++ ++/* Special section indices. */ ++ ++#define SHN_UNDEF 0 /* Undefined section */ ++#define SHN_LORESERVE 0xff00 /* Start of reserved indices */ ++#define SHN_LOPROC 0xff00 /* Start of processor-specific */ ++#define SHN_BEFORE 0xff00 /* Order section before all others ++ (Solaris). */ ++#define SHN_AFTER 0xff01 /* Order section after all others ++ (Solaris). */ ++#define SHN_HIPROC 0xff1f /* End of processor-specific */ ++#define SHN_LOOS 0xff20 /* Start of OS-specific */ ++#define SHN_HIOS 0xff3f /* End of OS-specific */ ++#define SHN_ABS 0xfff1 /* Associated symbol is absolute */ ++#define SHN_COMMON 0xfff2 /* Associated symbol is common */ ++#define SHN_XINDEX 0xffff /* Index is in extra table. */ ++#define SHN_HIRESERVE 0xffff /* End of reserved indices */ ++ ++/* Legal values for sh_type (section type). */ ++ ++#define SHT_NULL 0 /* Section header table entry unused */ ++#define SHT_PROGBITS 1 /* Program data */ ++#define SHT_SYMTAB 2 /* Symbol table */ ++#define SHT_STRTAB 3 /* String table */ ++#define SHT_RELA 4 /* Relocation entries with addends */ ++#define SHT_HASH 5 /* Symbol hash table */ ++#define SHT_DYNAMIC 6 /* Dynamic linking information */ ++#define SHT_NOTE 7 /* Notes */ ++#define SHT_NOBITS 8 /* Program space with no data (bss) */ ++#define SHT_REL 9 /* Relocation entries, no addends */ ++#define SHT_SHLIB 10 /* Reserved */ ++#define SHT_DYNSYM 11 /* Dynamic linker symbol table */ ++#define SHT_INIT_ARRAY 14 /* Array of constructors */ ++#define SHT_FINI_ARRAY 15 /* Array of destructors */ ++#define SHT_PREINIT_ARRAY 16 /* Array of pre-constructors */ ++#define SHT_GROUP 17 /* Section group */ ++#define SHT_SYMTAB_SHNDX 18 /* Extended section indeces */ ++#define SHT_NUM 19 /* Number of defined types. */ ++#define SHT_LOOS 0x60000000 /* Start OS-specific. */ ++#define SHT_GNU_ATTRIBUTES 0x6ffffff5 /* Object attributes. */ ++#define SHT_GNU_HASH 0x6ffffff6 /* GNU-style hash table. */ ++#define SHT_GNU_LIBLIST 0x6ffffff7 /* Prelink library list */ ++#define SHT_CHECKSUM 0x6ffffff8 /* Checksum for DSO content. */ ++#define SHT_LOSUNW 0x6ffffffa /* Sun-specific low bound. */ ++#define SHT_SUNW_move 0x6ffffffa ++#define SHT_SUNW_COMDAT 0x6ffffffb ++#define SHT_SUNW_syminfo 0x6ffffffc ++#define SHT_GNU_verdef 0x6ffffffd /* Version definition section. */ ++#define SHT_GNU_verneed 0x6ffffffe /* Version needs section. */ ++#define SHT_GNU_versym 0x6fffffff /* Version symbol table. */ ++#define SHT_HISUNW 0x6fffffff /* Sun-specific high bound. */ ++#define SHT_HIOS 0x6fffffff /* End OS-specific type */ ++#define SHT_LOPROC 0x70000000 /* Start of processor-specific */ ++#define SHT_HIPROC 0x7fffffff /* End of processor-specific */ ++#define SHT_LOUSER 0x80000000 /* Start of application-specific */ ++#define SHT_HIUSER 0x8fffffff /* End of application-specific */ ++ ++/* Legal values for sh_flags (section flags). */ ++ ++#define SHF_WRITE (1 << 0) /* Writable */ ++#define SHF_ALLOC (1 << 1) /* Occupies memory during execution */ ++#define SHF_EXECINSTR (1 << 2) /* Executable */ ++#define SHF_MERGE (1 << 4) /* Might be merged */ ++#define SHF_STRINGS (1 << 5) /* Contains nul-terminated strings */ ++#define SHF_INFO_LINK (1 << 6) /* `sh_info' contains SHT index */ ++#define SHF_LINK_ORDER (1 << 7) /* Preserve order after combining */ ++#define SHF_OS_NONCONFORMING (1 << 8) /* Non-standard OS specific handling ++ required */ ++#define SHF_GROUP (1 << 9) /* Section is member of a group. */ ++#define SHF_TLS (1 << 10) /* Section hold thread-local data. */ ++#define SHF_MASKOS 0x0ff00000 /* OS-specific. */ ++#define SHF_MASKPROC 0xf0000000 /* Processor-specific */ ++#define SHF_ORDERED (1 << 30) /* Special ordering requirement ++ (Solaris). */ ++#define SHF_EXCLUDE (1 << 31) /* Section is excluded unless ++ referenced or allocated (Solaris).*/ ++ ++/* Section group handling. */ ++#define GRP_COMDAT 0x1 /* Mark group as COMDAT. */ ++ ++/* Symbol table entry. */ ++ ++typedef struct ++{ ++ Elf32_Word st_name; /* Symbol name (string tbl index) */ ++ Elf32_Addr st_value; /* Symbol value */ ++ Elf32_Word st_size; /* Symbol size */ ++ unsigned char st_info; /* Symbol type and binding */ ++ unsigned char st_other; /* Symbol visibility */ ++ Elf32_Section st_shndx; /* Section index */ ++} Elf32_Sym; ++ ++typedef struct ++{ ++ Elf64_Word st_name; /* Symbol name (string tbl index) */ ++ unsigned char st_info; /* Symbol type and binding */ ++ unsigned char st_other; /* Symbol visibility */ ++ Elf64_Section st_shndx; /* Section index */ ++ Elf64_Addr st_value; /* Symbol value */ ++ Elf64_Xword st_size; /* Symbol size */ ++} Elf64_Sym; ++ ++/* The syminfo section if available contains additional information about ++ every dynamic symbol. */ ++ ++typedef struct ++{ ++ Elf32_Half si_boundto; /* Direct bindings, symbol bound to */ ++ Elf32_Half si_flags; /* Per symbol flags */ ++} Elf32_Syminfo; ++ ++typedef struct ++{ ++ Elf64_Half si_boundto; /* Direct bindings, symbol bound to */ ++ Elf64_Half si_flags; /* Per symbol flags */ ++} Elf64_Syminfo; ++ ++/* Possible values for si_boundto. */ ++#define SYMINFO_BT_SELF 0xffff /* Symbol bound to self */ ++#define SYMINFO_BT_PARENT 0xfffe /* Symbol bound to parent */ ++#define SYMINFO_BT_LOWRESERVE 0xff00 /* Beginning of reserved entries */ ++ ++/* Possible bitmasks for si_flags. */ ++#define SYMINFO_FLG_DIRECT 0x0001 /* Direct bound symbol */ ++#define SYMINFO_FLG_PASSTHRU 0x0002 /* Pass-thru symbol for translator */ ++#define SYMINFO_FLG_COPY 0x0004 /* Symbol is a copy-reloc */ ++#define SYMINFO_FLG_LAZYLOAD 0x0008 /* Symbol bound to object to be lazy ++ loaded */ ++/* Syminfo version values. */ ++#define SYMINFO_NONE 0 ++#define SYMINFO_CURRENT 1 ++#define SYMINFO_NUM 2 ++ ++ ++/* How to extract and insert information held in the st_info field. */ ++ ++#define ELF32_ST_BIND(val) (((unsigned char) (val)) >> 4) ++#define ELF32_ST_TYPE(val) ((val) & 0xf) ++#define ELF32_ST_INFO(bind, type) (((bind) << 4) + ((type) & 0xf)) ++ ++/* Both Elf32_Sym and Elf64_Sym use the same one-byte st_info field. */ ++#define ELF64_ST_BIND(val) ELF32_ST_BIND (val) ++#define ELF64_ST_TYPE(val) ELF32_ST_TYPE (val) ++#define ELF64_ST_INFO(bind, type) ELF32_ST_INFO ((bind), (type)) ++ ++/* Legal values for ST_BIND subfield of st_info (symbol binding). */ ++ ++#define STB_LOCAL 0 /* Local symbol */ ++#define STB_GLOBAL 1 /* Global symbol */ ++#define STB_WEAK 2 /* Weak symbol */ ++#define STB_NUM 3 /* Number of defined types. */ ++#define STB_LOOS 10 /* Start of OS-specific */ ++#define STB_GNU_UNIQUE 10 /* Unique symbol. */ ++#define STB_HIOS 12 /* End of OS-specific */ ++#define STB_LOPROC 13 /* Start of processor-specific */ ++#define STB_HIPROC 15 /* End of processor-specific */ ++ ++/* Legal values for ST_TYPE subfield of st_info (symbol type). */ ++ ++#define STT_NOTYPE 0 /* Symbol type is unspecified */ ++#define STT_OBJECT 1 /* Symbol is a data object */ ++#define STT_FUNC 2 /* Symbol is a code object */ ++#define STT_SECTION 3 /* Symbol associated with a section */ ++#define STT_FILE 4 /* Symbol's name is file name */ ++#define STT_COMMON 5 /* Symbol is a common data object */ ++#define STT_TLS 6 /* Symbol is thread-local data object*/ ++#define STT_NUM 7 /* Number of defined types. */ ++#define STT_LOOS 10 /* Start of OS-specific */ ++#define STT_GNU_IFUNC 10 /* Symbol is indirect code object */ ++#define STT_HIOS 12 /* End of OS-specific */ ++#define STT_LOPROC 13 /* Start of processor-specific */ ++#define STT_HIPROC 15 /* End of processor-specific */ ++ ++ ++/* Symbol table indices are found in the hash buckets and chain table ++ of a symbol hash table section. This special index value indicates ++ the end of a chain, meaning no further symbols are found in that bucket. */ ++ ++#define STN_UNDEF 0 /* End of a chain. */ ++ ++ ++/* How to extract and insert information held in the st_other field. */ ++ ++#define ELF32_ST_VISIBILITY(o) ((o) & 0x03) ++ ++/* For ELF64 the definitions are the same. */ ++#define ELF64_ST_VISIBILITY(o) ELF32_ST_VISIBILITY (o) ++ ++/* Symbol visibility specification encoded in the st_other field. */ ++#define STV_DEFAULT 0 /* Default symbol visibility rules */ ++#define STV_INTERNAL 1 /* Processor specific hidden class */ ++#define STV_HIDDEN 2 /* Sym unavailable in other modules */ ++#define STV_PROTECTED 3 /* Not preemptible, not exported */ ++ ++ ++/* Relocation table entry without addend (in section of type SHT_REL). */ ++ ++typedef struct ++{ ++ Elf32_Addr r_offset; /* Address */ ++ Elf32_Word r_info; /* Relocation type and symbol index */ ++} Elf32_Rel; ++ ++/* I have seen two different definitions of the Elf64_Rel and ++ Elf64_Rela structures, so we'll leave them out until Novell (or ++ whoever) gets their act together. */ ++/* The following, at least, is used on Sparc v9, MIPS, and Alpha. */ ++ ++typedef struct ++{ ++ Elf64_Addr r_offset; /* Address */ ++ Elf64_Xword r_info; /* Relocation type and symbol index */ ++} Elf64_Rel; ++ ++/* Relocation table entry with addend (in section of type SHT_RELA). */ ++ ++typedef struct ++{ ++ Elf32_Addr r_offset; /* Address */ ++ Elf32_Word r_info; /* Relocation type and symbol index */ ++ Elf32_Sword r_addend; /* Addend */ ++} Elf32_Rela; ++ ++typedef struct ++{ ++ Elf64_Addr r_offset; /* Address */ ++ Elf64_Xword r_info; /* Relocation type and symbol index */ ++ Elf64_Sxword r_addend; /* Addend */ ++} Elf64_Rela; ++ ++/* How to extract and insert information held in the r_info field. */ ++ ++#define ELF32_R_SYM(val) ((val) >> 8) ++#define ELF32_R_TYPE(val) ((val) & 0xff) ++#define ELF32_R_INFO(sym, type) (((sym) << 8) + ((type) & 0xff)) ++ ++#define ELF64_R_SYM(i) ((i) >> 32) ++#define ELF64_R_TYPE(i) ((i) & 0xffffffff) ++#define ELF64_R_INFO(sym,type) ((((Elf64_Xword) (sym)) << 32) + (type)) ++ ++/* Program segment header. */ ++ ++typedef struct ++{ ++ Elf32_Word p_type; /* Segment type */ ++ Elf32_Off p_offset; /* Segment file offset */ ++ Elf32_Addr p_vaddr; /* Segment virtual address */ ++ Elf32_Addr p_paddr; /* Segment physical address */ ++ Elf32_Word p_filesz; /* Segment size in file */ ++ Elf32_Word p_memsz; /* Segment size in memory */ ++ Elf32_Word p_flags; /* Segment flags */ ++ Elf32_Word p_align; /* Segment alignment */ ++} Elf32_Phdr; ++ ++typedef struct ++{ ++ Elf64_Word p_type; /* Segment type */ ++ Elf64_Word p_flags; /* Segment flags */ ++ Elf64_Off p_offset; /* Segment file offset */ ++ Elf64_Addr p_vaddr; /* Segment virtual address */ ++ Elf64_Addr p_paddr; /* Segment physical address */ ++ Elf64_Xword p_filesz; /* Segment size in file */ ++ Elf64_Xword p_memsz; /* Segment size in memory */ ++ Elf64_Xword p_align; /* Segment alignment */ ++} Elf64_Phdr; ++ ++/* Special value for e_phnum. This indicates that the real number of ++ program headers is too large to fit into e_phnum. Instead the real ++ value is in the field sh_info of section 0. */ ++ ++#define PN_XNUM 0xffff ++ ++/* Legal values for p_type (segment type). */ ++ ++#define PT_NULL 0 /* Program header table entry unused */ ++#define PT_LOAD 1 /* Loadable program segment */ ++#define PT_DYNAMIC 2 /* Dynamic linking information */ ++#define PT_INTERP 3 /* Program interpreter */ ++#define PT_NOTE 4 /* Auxiliary information */ ++#define PT_SHLIB 5 /* Reserved */ ++#define PT_PHDR 6 /* Entry for header table itself */ ++#define PT_TLS 7 /* Thread-local storage segment */ ++#define PT_NUM 8 /* Number of defined types */ ++#define PT_LOOS 0x60000000 /* Start of OS-specific */ ++#define PT_GNU_EH_FRAME 0x6474e550 /* GCC .eh_frame_hdr segment */ ++#define PT_GNU_STACK 0x6474e551 /* Indicates stack executability */ ++#define PT_GNU_RELRO 0x6474e552 /* Read-only after relocation */ ++#define PT_LOSUNW 0x6ffffffa ++#define PT_SUNWBSS 0x6ffffffa /* Sun Specific segment */ ++#define PT_SUNWSTACK 0x6ffffffb /* Stack segment */ ++#define PT_HISUNW 0x6fffffff ++#define PT_HIOS 0x6fffffff /* End of OS-specific */ ++#define PT_LOPROC 0x70000000 /* Start of processor-specific */ ++#define PT_HIPROC 0x7fffffff /* End of processor-specific */ ++ ++/* Legal values for p_flags (segment flags). */ ++ ++#define PF_X (1 << 0) /* Segment is executable */ ++#define PF_W (1 << 1) /* Segment is writable */ ++#define PF_R (1 << 2) /* Segment is readable */ ++#define PF_MASKOS 0x0ff00000 /* OS-specific */ ++#define PF_MASKPROC 0xf0000000 /* Processor-specific */ ++ ++/* Legal values for note segment descriptor types for core files. */ ++ ++#define NT_PRSTATUS 1 /* Contains copy of prstatus struct */ ++#define NT_FPREGSET 2 /* Contains copy of fpregset struct */ ++#define NT_PRPSINFO 3 /* Contains copy of prpsinfo struct */ ++#define NT_PRXREG 4 /* Contains copy of prxregset struct */ ++#define NT_TASKSTRUCT 4 /* Contains copy of task structure */ ++#define NT_PLATFORM 5 /* String from sysinfo(SI_PLATFORM) */ ++#define NT_AUXV 6 /* Contains copy of auxv array */ ++#define NT_GWINDOWS 7 /* Contains copy of gwindows struct */ ++#define NT_ASRS 8 /* Contains copy of asrset struct */ ++#define NT_PSTATUS 10 /* Contains copy of pstatus struct */ ++#define NT_PSINFO 13 /* Contains copy of psinfo struct */ ++#define NT_PRCRED 14 /* Contains copy of prcred struct */ ++#define NT_UTSNAME 15 /* Contains copy of utsname struct */ ++#define NT_LWPSTATUS 16 /* Contains copy of lwpstatus struct */ ++#define NT_LWPSINFO 17 /* Contains copy of lwpinfo struct */ ++#define NT_PRFPXREG 20 /* Contains copy of fprxregset struct */ ++#define NT_PRXFPREG 0x46e62b7f /* Contains copy of user_fxsr_struct */ ++#define NT_PPC_VMX 0x100 /* PowerPC Altivec/VMX registers */ ++#define NT_PPC_SPE 0x101 /* PowerPC SPE/EVR registers */ ++#define NT_PPC_VSX 0x102 /* PowerPC VSX registers */ ++#define NT_386_TLS 0x200 /* i386 TLS slots (struct user_desc) */ ++#define NT_386_IOPERM 0x201 /* x86 io permission bitmap (1=deny) */ ++#define NT_X86_XSTATE 0x202 /* x86 extended state using xsave */ ++ ++/* Legal values for the note segment descriptor types for object files. */ ++ ++#define NT_VERSION 1 /* Contains a version string. */ ++ ++ ++/* Dynamic section entry. */ ++ ++typedef struct ++{ ++ Elf32_Sword d_tag; /* Dynamic entry type */ ++ union ++ { ++ Elf32_Word d_val; /* Integer value */ ++ Elf32_Addr d_ptr; /* Address value */ ++ } d_un; ++} Elf32_Dyn; ++ ++typedef struct ++{ ++ Elf64_Sxword d_tag; /* Dynamic entry type */ ++ union ++ { ++ Elf64_Xword d_val; /* Integer value */ ++ Elf64_Addr d_ptr; /* Address value */ ++ } d_un; ++} Elf64_Dyn; ++ ++/* Legal values for d_tag (dynamic entry type). */ ++ ++#define DT_NULL 0 /* Marks end of dynamic section */ ++#define DT_NEEDED 1 /* Name of needed library */ ++#define DT_PLTRELSZ 2 /* Size in bytes of PLT relocs */ ++#define DT_PLTGOT 3 /* Processor defined value */ ++#define DT_HASH 4 /* Address of symbol hash table */ ++#define DT_STRTAB 5 /* Address of string table */ ++#define DT_SYMTAB 6 /* Address of symbol table */ ++#define DT_RELA 7 /* Address of Rela relocs */ ++#define DT_RELASZ 8 /* Total size of Rela relocs */ ++#define DT_RELAENT 9 /* Size of one Rela reloc */ ++#define DT_STRSZ 10 /* Size of string table */ ++#define DT_SYMENT 11 /* Size of one symbol table entry */ ++#define DT_INIT 12 /* Address of init function */ ++#define DT_FINI 13 /* Address of termination function */ ++#define DT_SONAME 14 /* Name of shared object */ ++#define DT_RPATH 15 /* Library search path (deprecated) */ ++#define DT_SYMBOLIC 16 /* Start symbol search here */ ++#define DT_REL 17 /* Address of Rel relocs */ ++#define DT_RELSZ 18 /* Total size of Rel relocs */ ++#define DT_RELENT 19 /* Size of one Rel reloc */ ++#define DT_PLTREL 20 /* Type of reloc in PLT */ ++#define DT_DEBUG 21 /* For debugging; unspecified */ ++#define DT_TEXTREL 22 /* Reloc might modify .text */ ++#define DT_JMPREL 23 /* Address of PLT relocs */ ++#define DT_BIND_NOW 24 /* Process relocations of object */ ++#define DT_INIT_ARRAY 25 /* Array with addresses of init fct */ ++#define DT_FINI_ARRAY 26 /* Array with addresses of fini fct */ ++#define DT_INIT_ARRAYSZ 27 /* Size in bytes of DT_INIT_ARRAY */ ++#define DT_FINI_ARRAYSZ 28 /* Size in bytes of DT_FINI_ARRAY */ ++#define DT_RUNPATH 29 /* Library search path */ ++#define DT_FLAGS 30 /* Flags for the object being loaded */ ++#define DT_ENCODING 32 /* Start of encoded range */ ++#define DT_PREINIT_ARRAY 32 /* Array with addresses of preinit fct*/ ++#define DT_PREINIT_ARRAYSZ 33 /* size in bytes of DT_PREINIT_ARRAY */ ++#define DT_NUM 34 /* Number used */ ++#define DT_LOOS 0x6000000d /* Start of OS-specific */ ++#define DT_HIOS 0x6ffff000 /* End of OS-specific */ ++#define DT_LOPROC 0x70000000 /* Start of processor-specific */ ++#define DT_HIPROC 0x7fffffff /* End of processor-specific */ ++#define DT_PROCNUM DT_MIPS_NUM /* Most used by any processor */ ++ ++/* DT_* entries which fall between DT_VALRNGHI & DT_VALRNGLO use the ++ Dyn.d_un.d_val field of the Elf*_Dyn structure. This follows Sun's ++ approach. */ ++#define DT_VALRNGLO 0x6ffffd00 ++#define DT_GNU_PRELINKED 0x6ffffdf5 /* Prelinking timestamp */ ++#define DT_GNU_CONFLICTSZ 0x6ffffdf6 /* Size of conflict section */ ++#define DT_GNU_LIBLISTSZ 0x6ffffdf7 /* Size of library list */ ++#define DT_CHECKSUM 0x6ffffdf8 ++#define DT_PLTPADSZ 0x6ffffdf9 ++#define DT_MOVEENT 0x6ffffdfa ++#define DT_MOVESZ 0x6ffffdfb ++#define DT_FEATURE_1 0x6ffffdfc /* Feature selection (DTF_*). */ ++#define DT_POSFLAG_1 0x6ffffdfd /* Flags for DT_* entries, effecting ++ the following DT_* entry. */ ++#define DT_SYMINSZ 0x6ffffdfe /* Size of syminfo table (in bytes) */ ++#define DT_SYMINENT 0x6ffffdff /* Entry size of syminfo */ ++#define DT_VALRNGHI 0x6ffffdff ++#define DT_VALTAGIDX(tag) (DT_VALRNGHI - (tag)) /* Reverse order! */ ++#define DT_VALNUM 12 ++ ++/* DT_* entries which fall between DT_ADDRRNGHI & DT_ADDRRNGLO use the ++ Dyn.d_un.d_ptr field of the Elf*_Dyn structure. ++ ++ If any adjustment is made to the ELF object after it has been ++ built these entries will need to be adjusted. */ ++#define DT_ADDRRNGLO 0x6ffffe00 ++#define DT_GNU_HASH 0x6ffffef5 /* GNU-style hash table. */ ++#define DT_TLSDESC_PLT 0x6ffffef6 ++#define DT_TLSDESC_GOT 0x6ffffef7 ++#define DT_GNU_CONFLICT 0x6ffffef8 /* Start of conflict section */ ++#define DT_GNU_LIBLIST 0x6ffffef9 /* Library list */ ++#define DT_CONFIG 0x6ffffefa /* Configuration information. */ ++#define DT_DEPAUDIT 0x6ffffefb /* Dependency auditing. */ ++#define DT_AUDIT 0x6ffffefc /* Object auditing. */ ++#define DT_PLTPAD 0x6ffffefd /* PLT padding. */ ++#define DT_MOVETAB 0x6ffffefe /* Move table. */ ++#define DT_SYMINFO 0x6ffffeff /* Syminfo table. */ ++#define DT_ADDRRNGHI 0x6ffffeff ++#define DT_ADDRTAGIDX(tag) (DT_ADDRRNGHI - (tag)) /* Reverse order! */ ++#define DT_ADDRNUM 11 ++ ++/* The versioning entry types. The next are defined as part of the ++ GNU extension. */ ++#define DT_VERSYM 0x6ffffff0 ++ ++#define DT_RELACOUNT 0x6ffffff9 ++#define DT_RELCOUNT 0x6ffffffa ++ ++/* These were chosen by Sun. */ ++#define DT_FLAGS_1 0x6ffffffb /* State flags, see DF_1_* below. */ ++#define DT_VERDEF 0x6ffffffc /* Address of version definition ++ table */ ++#define DT_VERDEFNUM 0x6ffffffd /* Number of version definitions */ ++#define DT_VERNEED 0x6ffffffe /* Address of table with needed ++ versions */ ++#define DT_VERNEEDNUM 0x6fffffff /* Number of needed versions */ ++#define DT_VERSIONTAGIDX(tag) (DT_VERNEEDNUM - (tag)) /* Reverse order! */ ++#define DT_VERSIONTAGNUM 16 ++ ++/* Sun added these machine-independent extensions in the "processor-specific" ++ range. Be compatible. */ ++#define DT_AUXILIARY 0x7ffffffd /* Shared object to load before self */ ++#define DT_FILTER 0x7fffffff /* Shared object to get values from */ ++#define DT_EXTRATAGIDX(tag) ((Elf32_Word)-((Elf32_Sword) (tag) <<1>>1)-1) ++#define DT_EXTRANUM 3 ++ ++/* Values of `d_un.d_val' in the DT_FLAGS entry. */ ++#define DF_ORIGIN 0x00000001 /* Object may use DF_ORIGIN */ ++#define DF_SYMBOLIC 0x00000002 /* Symbol resolutions starts here */ ++#define DF_TEXTREL 0x00000004 /* Object contains text relocations */ ++#define DF_BIND_NOW 0x00000008 /* No lazy binding for this object */ ++#define DF_STATIC_TLS 0x00000010 /* Module uses the static TLS model */ ++ ++/* State flags selectable in the `d_un.d_val' element of the DT_FLAGS_1 ++ entry in the dynamic section. */ ++#define DF_1_NOW 0x00000001 /* Set RTLD_NOW for this object. */ ++#define DF_1_GLOBAL 0x00000002 /* Set RTLD_GLOBAL for this object. */ ++#define DF_1_GROUP 0x00000004 /* Set RTLD_GROUP for this object. */ ++#define DF_1_NODELETE 0x00000008 /* Set RTLD_NODELETE for this object.*/ ++#define DF_1_LOADFLTR 0x00000010 /* Trigger filtee loading at runtime.*/ ++#define DF_1_INITFIRST 0x00000020 /* Set RTLD_INITFIRST for this object*/ ++#define DF_1_NOOPEN 0x00000040 /* Set RTLD_NOOPEN for this object. */ ++#define DF_1_ORIGIN 0x00000080 /* $ORIGIN must be handled. */ ++#define DF_1_DIRECT 0x00000100 /* Direct binding enabled. */ ++#define DF_1_TRANS 0x00000200 ++#define DF_1_INTERPOSE 0x00000400 /* Object is used to interpose. */ ++#define DF_1_NODEFLIB 0x00000800 /* Ignore default lib search path. */ ++#define DF_1_NODUMP 0x00001000 /* Object can't be dldump'ed. */ ++#define DF_1_CONFALT 0x00002000 /* Configuration alternative created.*/ ++#define DF_1_ENDFILTEE 0x00004000 /* Filtee terminates filters search. */ ++#define DF_1_DISPRELDNE 0x00008000 /* Disp reloc applied at build time. */ ++#define DF_1_DISPRELPND 0x00010000 /* Disp reloc applied at run-time. */ ++ ++/* Flags for the feature selection in DT_FEATURE_1. */ ++#define DTF_1_PARINIT 0x00000001 ++#define DTF_1_CONFEXP 0x00000002 ++ ++/* Flags in the DT_POSFLAG_1 entry effecting only the next DT_* entry. */ ++#define DF_P1_LAZYLOAD 0x00000001 /* Lazyload following object. */ ++#define DF_P1_GROUPPERM 0x00000002 /* Symbols from next object are not ++ generally available. */ ++ ++/* Version definition sections. */ ++ ++typedef struct ++{ ++ Elf32_Half vd_version; /* Version revision */ ++ Elf32_Half vd_flags; /* Version information */ ++ Elf32_Half vd_ndx; /* Version Index */ ++ Elf32_Half vd_cnt; /* Number of associated aux entries */ ++ Elf32_Word vd_hash; /* Version name hash value */ ++ Elf32_Word vd_aux; /* Offset in bytes to verdaux array */ ++ Elf32_Word vd_next; /* Offset in bytes to next verdef ++ entry */ ++} Elf32_Verdef; ++ ++typedef struct ++{ ++ Elf64_Half vd_version; /* Version revision */ ++ Elf64_Half vd_flags; /* Version information */ ++ Elf64_Half vd_ndx; /* Version Index */ ++ Elf64_Half vd_cnt; /* Number of associated aux entries */ ++ Elf64_Word vd_hash; /* Version name hash value */ ++ Elf64_Word vd_aux; /* Offset in bytes to verdaux array */ ++ Elf64_Word vd_next; /* Offset in bytes to next verdef ++ entry */ ++} Elf64_Verdef; ++ ++ ++/* Legal values for vd_version (version revision). */ ++#define VER_DEF_NONE 0 /* No version */ ++#define VER_DEF_CURRENT 1 /* Current version */ ++#define VER_DEF_NUM 2 /* Given version number */ ++ ++/* Legal values for vd_flags (version information flags). */ ++#define VER_FLG_BASE 0x1 /* Version definition of file itself */ ++#define VER_FLG_WEAK 0x2 /* Weak version identifier */ ++ ++/* Versym symbol index values. */ ++#define VER_NDX_LOCAL 0 /* Symbol is local. */ ++#define VER_NDX_GLOBAL 1 /* Symbol is global. */ ++#define VER_NDX_LORESERVE 0xff00 /* Beginning of reserved entries. */ ++#define VER_NDX_ELIMINATE 0xff01 /* Symbol is to be eliminated. */ ++ ++/* Auxialiary version information. */ ++ ++typedef struct ++{ ++ Elf32_Word vda_name; /* Version or dependency names */ ++ Elf32_Word vda_next; /* Offset in bytes to next verdaux ++ entry */ ++} Elf32_Verdaux; ++ ++typedef struct ++{ ++ Elf64_Word vda_name; /* Version or dependency names */ ++ Elf64_Word vda_next; /* Offset in bytes to next verdaux ++ entry */ ++} Elf64_Verdaux; ++ ++ ++/* Version dependency section. */ ++ ++typedef struct ++{ ++ Elf32_Half vn_version; /* Version of structure */ ++ Elf32_Half vn_cnt; /* Number of associated aux entries */ ++ Elf32_Word vn_file; /* Offset of filename for this ++ dependency */ ++ Elf32_Word vn_aux; /* Offset in bytes to vernaux array */ ++ Elf32_Word vn_next; /* Offset in bytes to next verneed ++ entry */ ++} Elf32_Verneed; ++ ++typedef struct ++{ ++ Elf64_Half vn_version; /* Version of structure */ ++ Elf64_Half vn_cnt; /* Number of associated aux entries */ ++ Elf64_Word vn_file; /* Offset of filename for this ++ dependency */ ++ Elf64_Word vn_aux; /* Offset in bytes to vernaux array */ ++ Elf64_Word vn_next; /* Offset in bytes to next verneed ++ entry */ ++} Elf64_Verneed; ++ ++ ++/* Legal values for vn_version (version revision). */ ++#define VER_NEED_NONE 0 /* No version */ ++#define VER_NEED_CURRENT 1 /* Current version */ ++#define VER_NEED_NUM 2 /* Given version number */ ++ ++/* Auxiliary needed version information. */ ++ ++typedef struct ++{ ++ Elf32_Word vna_hash; /* Hash value of dependency name */ ++ Elf32_Half vna_flags; /* Dependency specific information */ ++ Elf32_Half vna_other; /* Unused */ ++ Elf32_Word vna_name; /* Dependency name string offset */ ++ Elf32_Word vna_next; /* Offset in bytes to next vernaux ++ entry */ ++} Elf32_Vernaux; ++ ++typedef struct ++{ ++ Elf64_Word vna_hash; /* Hash value of dependency name */ ++ Elf64_Half vna_flags; /* Dependency specific information */ ++ Elf64_Half vna_other; /* Unused */ ++ Elf64_Word vna_name; /* Dependency name string offset */ ++ Elf64_Word vna_next; /* Offset in bytes to next vernaux ++ entry */ ++} Elf64_Vernaux; ++ ++ ++/* Legal values for vna_flags. */ ++#define VER_FLG_WEAK 0x2 /* Weak version identifier */ ++ ++ ++/* Auxiliary vector. */ ++ ++/* This vector is normally only used by the program interpreter. The ++ usual definition in an ABI supplement uses the name auxv_t. The ++ vector is not usually defined in a standard <elf.h> file, but it ++ can't hurt. We rename it to avoid conflicts. The sizes of these ++ types are an arrangement between the exec server and the program ++ interpreter, so we don't fully specify them here. */ ++ ++typedef struct ++{ ++ uint32_t a_type; /* Entry type */ ++ union ++ { ++ uint32_t a_val; /* Integer value */ ++ /* We use to have pointer elements added here. We cannot do that, ++ though, since it does not work when using 32-bit definitions ++ on 64-bit platforms and vice versa. */ ++ } a_un; ++} Elf32_auxv_t; ++ ++typedef struct ++{ ++ uint64_t a_type; /* Entry type */ ++ union ++ { ++ uint64_t a_val; /* Integer value */ ++ /* We use to have pointer elements added here. We cannot do that, ++ though, since it does not work when using 32-bit definitions ++ on 64-bit platforms and vice versa. */ ++ } a_un; ++} Elf64_auxv_t; ++ ++/* Legal values for a_type (entry type). */ ++ ++#define AT_NULL 0 /* End of vector */ ++#define AT_IGNORE 1 /* Entry should be ignored */ ++#define AT_EXECFD 2 /* File descriptor of program */ ++#define AT_PHDR 3 /* Program headers for program */ ++#define AT_PHENT 4 /* Size of program header entry */ ++#define AT_PHNUM 5 /* Number of program headers */ ++#define AT_PAGESZ 6 /* System page size */ ++#define AT_BASE 7 /* Base address of interpreter */ ++#define AT_FLAGS 8 /* Flags */ ++#define AT_ENTRY 9 /* Entry point of program */ ++#define AT_NOTELF 10 /* Program is not ELF */ ++#define AT_UID 11 /* Real uid */ ++#define AT_EUID 12 /* Effective uid */ ++#define AT_GID 13 /* Real gid */ ++#define AT_EGID 14 /* Effective gid */ ++#define AT_CLKTCK 17 /* Frequency of times() */ ++ ++/* Some more special a_type values describing the hardware. */ ++#define AT_PLATFORM 15 /* String identifying platform. */ ++#define AT_HWCAP 16 /* Machine dependent hints about ++ processor capabilities. */ ++ ++/* This entry gives some information about the FPU initialization ++ performed by the kernel. */ ++#define AT_FPUCW 18 /* Used FPU control word. */ ++ ++/* Cache block sizes. */ ++#define AT_DCACHEBSIZE 19 /* Data cache block size. */ ++#define AT_ICACHEBSIZE 20 /* Instruction cache block size. */ ++#define AT_UCACHEBSIZE 21 /* Unified cache block size. */ ++ ++/* A special ignored value for PPC, used by the kernel to control the ++ interpretation of the AUXV. Must be > 16. */ ++#define AT_IGNOREPPC 22 /* Entry should be ignored. */ ++ ++#define AT_SECURE 23 /* Boolean, was exec setuid-like? */ ++ ++#define AT_BASE_PLATFORM 24 /* String identifying real platforms.*/ ++ ++#define AT_RANDOM 25 /* Address of 16 random bytes. */ ++ ++#define AT_EXECFN 31 /* Filename of executable. */ ++ ++/* Pointer to the global system page used for system calls and other ++ nice things. */ ++#define AT_SYSINFO 32 ++#define AT_SYSINFO_EHDR 33 ++ ++/* Shapes of the caches. Bits 0-3 contains associativity; bits 4-7 contains ++ log2 of line size; mask those to get cache size. */ ++#define AT_L1I_CACHESHAPE 34 ++#define AT_L1D_CACHESHAPE 35 ++#define AT_L2_CACHESHAPE 36 ++#define AT_L3_CACHESHAPE 37 ++ ++/* Note section contents. Each entry in the note section begins with ++ a header of a fixed form. */ ++ ++typedef struct ++{ ++ Elf32_Word n_namesz; /* Length of the note's name. */ ++ Elf32_Word n_descsz; /* Length of the note's descriptor. */ ++ Elf32_Word n_type; /* Type of the note. */ ++} Elf32_Nhdr; ++ ++typedef struct ++{ ++ Elf64_Word n_namesz; /* Length of the note's name. */ ++ Elf64_Word n_descsz; /* Length of the note's descriptor. */ ++ Elf64_Word n_type; /* Type of the note. */ ++} Elf64_Nhdr; ++ ++/* Known names of notes. */ ++ ++/* Solaris entries in the note section have this name. */ ++#define ELF_NOTE_SOLARIS "SUNW Solaris" ++ ++/* Note entries for GNU systems have this name. */ ++#define ELF_NOTE_GNU "GNU" ++ ++ ++/* Defined types of notes for Solaris. */ ++ ++/* Value of descriptor (one word) is desired pagesize for the binary. */ ++#define ELF_NOTE_PAGESIZE_HINT 1 ++ ++ ++/* Defined note types for GNU systems. */ ++ ++/* ABI information. The descriptor consists of words: ++ word 0: OS descriptor ++ word 1: major version of the ABI ++ word 2: minor version of the ABI ++ word 3: subminor version of the ABI ++*/ ++#define NT_GNU_ABI_TAG 1 ++#define ELF_NOTE_ABI NT_GNU_ABI_TAG /* Old name. */ ++ ++/* Known OSes. These values can appear in word 0 of an ++ NT_GNU_ABI_TAG note section entry. */ ++#define ELF_NOTE_OS_LINUX 0 ++#define ELF_NOTE_OS_GNU 1 ++#define ELF_NOTE_OS_SOLARIS2 2 ++#define ELF_NOTE_OS_FREEBSD 3 ++ ++/* Synthetic hwcap information. The descriptor begins with two words: ++ word 0: number of entries ++ word 1: bitmask of enabled entries ++ Then follow variable-length entries, one byte followed by a ++ '\0'-terminated hwcap name string. The byte gives the bit ++ number to test if enabled, (1U << bit) & bitmask. */ ++#define NT_GNU_HWCAP 2 ++ ++/* Build ID bits as generated by ld --build-id. ++ The descriptor consists of any nonzero number of bytes. */ ++#define NT_GNU_BUILD_ID 3 ++ ++/* Version note generated by GNU gold containing a version string. */ ++#define NT_GNU_GOLD_VERSION 4 ++ ++ ++/* Move records. */ ++typedef struct ++{ ++ Elf32_Xword m_value; /* Symbol value. */ ++ Elf32_Word m_info; /* Size and index. */ ++ Elf32_Word m_poffset; /* Symbol offset. */ ++ Elf32_Half m_repeat; /* Repeat count. */ ++ Elf32_Half m_stride; /* Stride info. */ ++} Elf32_Move; ++ ++typedef struct ++{ ++ Elf64_Xword m_value; /* Symbol value. */ ++ Elf64_Xword m_info; /* Size and index. */ ++ Elf64_Xword m_poffset; /* Symbol offset. */ ++ Elf64_Half m_repeat; /* Repeat count. */ ++ Elf64_Half m_stride; /* Stride info. */ ++} Elf64_Move; ++ ++/* Macro to construct move records. */ ++#define ELF32_M_SYM(info) ((info) >> 8) ++#define ELF32_M_SIZE(info) ((unsigned char) (info)) ++#define ELF32_M_INFO(sym, size) (((sym) << 8) + (unsigned char) (size)) ++ ++#define ELF64_M_SYM(info) ELF32_M_SYM (info) ++#define ELF64_M_SIZE(info) ELF32_M_SIZE (info) ++#define ELF64_M_INFO(sym, size) ELF32_M_INFO (sym, size) ++ ++ ++/* Motorola 68k specific definitions. */ ++ ++/* Values for Elf32_Ehdr.e_flags. */ ++#define EF_CPU32 0x00810000 ++ ++/* m68k relocs. */ ++ ++#define R_68K_NONE 0 /* No reloc */ ++#define R_68K_32 1 /* Direct 32 bit */ ++#define R_68K_16 2 /* Direct 16 bit */ ++#define R_68K_8 3 /* Direct 8 bit */ ++#define R_68K_PC32 4 /* PC relative 32 bit */ ++#define R_68K_PC16 5 /* PC relative 16 bit */ ++#define R_68K_PC8 6 /* PC relative 8 bit */ ++#define R_68K_GOT32 7 /* 32 bit PC relative GOT entry */ ++#define R_68K_GOT16 8 /* 16 bit PC relative GOT entry */ ++#define R_68K_GOT8 9 /* 8 bit PC relative GOT entry */ ++#define R_68K_GOT32O 10 /* 32 bit GOT offset */ ++#define R_68K_GOT16O 11 /* 16 bit GOT offset */ ++#define R_68K_GOT8O 12 /* 8 bit GOT offset */ ++#define R_68K_PLT32 13 /* 32 bit PC relative PLT address */ ++#define R_68K_PLT16 14 /* 16 bit PC relative PLT address */ ++#define R_68K_PLT8 15 /* 8 bit PC relative PLT address */ ++#define R_68K_PLT32O 16 /* 32 bit PLT offset */ ++#define R_68K_PLT16O 17 /* 16 bit PLT offset */ ++#define R_68K_PLT8O 18 /* 8 bit PLT offset */ ++#define R_68K_COPY 19 /* Copy symbol at runtime */ ++#define R_68K_GLOB_DAT 20 /* Create GOT entry */ ++#define R_68K_JMP_SLOT 21 /* Create PLT entry */ ++#define R_68K_RELATIVE 22 /* Adjust by program base */ ++#define R_68K_TLS_GD32 25 /* 32 bit GOT offset for GD */ ++#define R_68K_TLS_GD16 26 /* 16 bit GOT offset for GD */ ++#define R_68K_TLS_GD8 27 /* 8 bit GOT offset for GD */ ++#define R_68K_TLS_LDM32 28 /* 32 bit GOT offset for LDM */ ++#define R_68K_TLS_LDM16 29 /* 16 bit GOT offset for LDM */ ++#define R_68K_TLS_LDM8 30 /* 8 bit GOT offset for LDM */ ++#define R_68K_TLS_LDO32 31 /* 32 bit module-relative offset */ ++#define R_68K_TLS_LDO16 32 /* 16 bit module-relative offset */ ++#define R_68K_TLS_LDO8 33 /* 8 bit module-relative offset */ ++#define R_68K_TLS_IE32 34 /* 32 bit GOT offset for IE */ ++#define R_68K_TLS_IE16 35 /* 16 bit GOT offset for IE */ ++#define R_68K_TLS_IE8 36 /* 8 bit GOT offset for IE */ ++#define R_68K_TLS_LE32 37 /* 32 bit offset relative to ++ static TLS block */ ++#define R_68K_TLS_LE16 38 /* 16 bit offset relative to ++ static TLS block */ ++#define R_68K_TLS_LE8 39 /* 8 bit offset relative to ++ static TLS block */ ++#define R_68K_TLS_DTPMOD32 40 /* 32 bit module number */ ++#define R_68K_TLS_DTPREL32 41 /* 32 bit module-relative offset */ ++#define R_68K_TLS_TPREL32 42 /* 32 bit TP-relative offset */ ++/* Keep this the last entry. */ ++#define R_68K_NUM 43 ++ ++/* Intel 80386 specific definitions. */ ++ ++/* i386 relocs. */ ++ ++#define R_386_NONE 0 /* No reloc */ ++#define R_386_32 1 /* Direct 32 bit */ ++#define R_386_PC32 2 /* PC relative 32 bit */ ++#define R_386_GOT32 3 /* 32 bit GOT entry */ ++#define R_386_PLT32 4 /* 32 bit PLT address */ ++#define R_386_COPY 5 /* Copy symbol at runtime */ ++#define R_386_GLOB_DAT 6 /* Create GOT entry */ ++#define R_386_JMP_SLOT 7 /* Create PLT entry */ ++#define R_386_RELATIVE 8 /* Adjust by program base */ ++#define R_386_GOTOFF 9 /* 32 bit offset to GOT */ ++#define R_386_GOTPC 10 /* 32 bit PC relative offset to GOT */ ++#define R_386_32PLT 11 ++#define R_386_TLS_TPOFF 14 /* Offset in static TLS block */ ++#define R_386_TLS_IE 15 /* Address of GOT entry for static TLS ++ block offset */ ++#define R_386_TLS_GOTIE 16 /* GOT entry for static TLS block ++ offset */ ++#define R_386_TLS_LE 17 /* Offset relative to static TLS ++ block */ ++#define R_386_TLS_GD 18 /* Direct 32 bit for GNU version of ++ general dynamic thread local data */ ++#define R_386_TLS_LDM 19 /* Direct 32 bit for GNU version of ++ local dynamic thread local data ++ in LE code */ ++#define R_386_16 20 ++#define R_386_PC16 21 ++#define R_386_8 22 ++#define R_386_PC8 23 ++#define R_386_TLS_GD_32 24 /* Direct 32 bit for general dynamic ++ thread local data */ ++#define R_386_TLS_GD_PUSH 25 /* Tag for pushl in GD TLS code */ ++#define R_386_TLS_GD_CALL 26 /* Relocation for call to ++ __tls_get_addr() */ ++#define R_386_TLS_GD_POP 27 /* Tag for popl in GD TLS code */ ++#define R_386_TLS_LDM_32 28 /* Direct 32 bit for local dynamic ++ thread local data in LE code */ ++#define R_386_TLS_LDM_PUSH 29 /* Tag for pushl in LDM TLS code */ ++#define R_386_TLS_LDM_CALL 30 /* Relocation for call to ++ __tls_get_addr() in LDM code */ ++#define R_386_TLS_LDM_POP 31 /* Tag for popl in LDM TLS code */ ++#define R_386_TLS_LDO_32 32 /* Offset relative to TLS block */ ++#define R_386_TLS_IE_32 33 /* GOT entry for negated static TLS ++ block offset */ ++#define R_386_TLS_LE_32 34 /* Negated offset relative to static ++ TLS block */ ++#define R_386_TLS_DTPMOD32 35 /* ID of module containing symbol */ ++#define R_386_TLS_DTPOFF32 36 /* Offset in TLS block */ ++#define R_386_TLS_TPOFF32 37 /* Negated offset in static TLS block */ ++/* 38? */ ++#define R_386_TLS_GOTDESC 39 /* GOT offset for TLS descriptor. */ ++#define R_386_TLS_DESC_CALL 40 /* Marker of call through TLS ++ descriptor for ++ relaxation. */ ++#define R_386_TLS_DESC 41 /* TLS descriptor containing ++ pointer to code and to ++ argument, returning the TLS ++ offset for the symbol. */ ++#define R_386_IRELATIVE 42 /* Adjust indirectly by program base */ ++/* Keep this the last entry. */ ++#define R_386_NUM 43 ++ ++/* SUN SPARC specific definitions. */ ++ ++/* Legal values for ST_TYPE subfield of st_info (symbol type). */ ++ ++#define STT_SPARC_REGISTER 13 /* Global register reserved to app. */ ++ ++/* Values for Elf64_Ehdr.e_flags. */ ++ ++#define EF_SPARCV9_MM 3 ++#define EF_SPARCV9_TSO 0 ++#define EF_SPARCV9_PSO 1 ++#define EF_SPARCV9_RMO 2 ++#define EF_SPARC_LEDATA 0x800000 /* little endian data */ ++#define EF_SPARC_EXT_MASK 0xFFFF00 ++#define EF_SPARC_32PLUS 0x000100 /* generic V8+ features */ ++#define EF_SPARC_SUN_US1 0x000200 /* Sun UltraSPARC1 extensions */ ++#define EF_SPARC_HAL_R1 0x000400 /* HAL R1 extensions */ ++#define EF_SPARC_SUN_US3 0x000800 /* Sun UltraSPARCIII extensions */ ++ ++/* SPARC relocs. */ ++ ++#define R_SPARC_NONE 0 /* No reloc */ ++#define R_SPARC_8 1 /* Direct 8 bit */ ++#define R_SPARC_16 2 /* Direct 16 bit */ ++#define R_SPARC_32 3 /* Direct 32 bit */ ++#define R_SPARC_DISP8 4 /* PC relative 8 bit */ ++#define R_SPARC_DISP16 5 /* PC relative 16 bit */ ++#define R_SPARC_DISP32 6 /* PC relative 32 bit */ ++#define R_SPARC_WDISP30 7 /* PC relative 30 bit shifted */ ++#define R_SPARC_WDISP22 8 /* PC relative 22 bit shifted */ ++#define R_SPARC_HI22 9 /* High 22 bit */ ++#define R_SPARC_22 10 /* Direct 22 bit */ ++#define R_SPARC_13 11 /* Direct 13 bit */ ++#define R_SPARC_LO10 12 /* Truncated 10 bit */ ++#define R_SPARC_GOT10 13 /* Truncated 10 bit GOT entry */ ++#define R_SPARC_GOT13 14 /* 13 bit GOT entry */ ++#define R_SPARC_GOT22 15 /* 22 bit GOT entry shifted */ ++#define R_SPARC_PC10 16 /* PC relative 10 bit truncated */ ++#define R_SPARC_PC22 17 /* PC relative 22 bit shifted */ ++#define R_SPARC_WPLT30 18 /* 30 bit PC relative PLT address */ ++#define R_SPARC_COPY 19 /* Copy symbol at runtime */ ++#define R_SPARC_GLOB_DAT 20 /* Create GOT entry */ ++#define R_SPARC_JMP_SLOT 21 /* Create PLT entry */ ++#define R_SPARC_RELATIVE 22 /* Adjust by program base */ ++#define R_SPARC_UA32 23 /* Direct 32 bit unaligned */ ++ ++/* Additional Sparc64 relocs. */ ++ ++#define R_SPARC_PLT32 24 /* Direct 32 bit ref to PLT entry */ ++#define R_SPARC_HIPLT22 25 /* High 22 bit PLT entry */ ++#define R_SPARC_LOPLT10 26 /* Truncated 10 bit PLT entry */ ++#define R_SPARC_PCPLT32 27 /* PC rel 32 bit ref to PLT entry */ ++#define R_SPARC_PCPLT22 28 /* PC rel high 22 bit PLT entry */ ++#define R_SPARC_PCPLT10 29 /* PC rel trunc 10 bit PLT entry */ ++#define R_SPARC_10 30 /* Direct 10 bit */ ++#define R_SPARC_11 31 /* Direct 11 bit */ ++#define R_SPARC_64 32 /* Direct 64 bit */ ++#define R_SPARC_OLO10 33 /* 10bit with secondary 13bit addend */ ++#define R_SPARC_HH22 34 /* Top 22 bits of direct 64 bit */ ++#define R_SPARC_HM10 35 /* High middle 10 bits of ... */ ++#define R_SPARC_LM22 36 /* Low middle 22 bits of ... */ ++#define R_SPARC_PC_HH22 37 /* Top 22 bits of pc rel 64 bit */ ++#define R_SPARC_PC_HM10 38 /* High middle 10 bit of ... */ ++#define R_SPARC_PC_LM22 39 /* Low miggle 22 bits of ... */ ++#define R_SPARC_WDISP16 40 /* PC relative 16 bit shifted */ ++#define R_SPARC_WDISP19 41 /* PC relative 19 bit shifted */ ++#define R_SPARC_GLOB_JMP 42 /* was part of v9 ABI but was removed */ ++#define R_SPARC_7 43 /* Direct 7 bit */ ++#define R_SPARC_5 44 /* Direct 5 bit */ ++#define R_SPARC_6 45 /* Direct 6 bit */ ++#define R_SPARC_DISP64 46 /* PC relative 64 bit */ ++#define R_SPARC_PLT64 47 /* Direct 64 bit ref to PLT entry */ ++#define R_SPARC_HIX22 48 /* High 22 bit complemented */ ++#define R_SPARC_LOX10 49 /* Truncated 11 bit complemented */ ++#define R_SPARC_H44 50 /* Direct high 12 of 44 bit */ ++#define R_SPARC_M44 51 /* Direct mid 22 of 44 bit */ ++#define R_SPARC_L44 52 /* Direct low 10 of 44 bit */ ++#define R_SPARC_REGISTER 53 /* Global register usage */ ++#define R_SPARC_UA64 54 /* Direct 64 bit unaligned */ ++#define R_SPARC_UA16 55 /* Direct 16 bit unaligned */ ++#define R_SPARC_TLS_GD_HI22 56 ++#define R_SPARC_TLS_GD_LO10 57 ++#define R_SPARC_TLS_GD_ADD 58 ++#define R_SPARC_TLS_GD_CALL 59 ++#define R_SPARC_TLS_LDM_HI22 60 ++#define R_SPARC_TLS_LDM_LO10 61 ++#define R_SPARC_TLS_LDM_ADD 62 ++#define R_SPARC_TLS_LDM_CALL 63 ++#define R_SPARC_TLS_LDO_HIX22 64 ++#define R_SPARC_TLS_LDO_LOX10 65 ++#define R_SPARC_TLS_LDO_ADD 66 ++#define R_SPARC_TLS_IE_HI22 67 ++#define R_SPARC_TLS_IE_LO10 68 ++#define R_SPARC_TLS_IE_LD 69 ++#define R_SPARC_TLS_IE_LDX 70 ++#define R_SPARC_TLS_IE_ADD 71 ++#define R_SPARC_TLS_LE_HIX22 72 ++#define R_SPARC_TLS_LE_LOX10 73 ++#define R_SPARC_TLS_DTPMOD32 74 ++#define R_SPARC_TLS_DTPMOD64 75 ++#define R_SPARC_TLS_DTPOFF32 76 ++#define R_SPARC_TLS_DTPOFF64 77 ++#define R_SPARC_TLS_TPOFF32 78 ++#define R_SPARC_TLS_TPOFF64 79 ++#define R_SPARC_GOTDATA_HIX22 80 ++#define R_SPARC_GOTDATA_LOX10 81 ++#define R_SPARC_GOTDATA_OP_HIX22 82 ++#define R_SPARC_GOTDATA_OP_LOX10 83 ++#define R_SPARC_GOTDATA_OP 84 ++#define R_SPARC_H34 85 ++#define R_SPARC_SIZE32 86 ++#define R_SPARC_SIZE64 87 ++#define R_SPARC_WDISP10 88 ++#define R_SPARC_JMP_IREL 248 ++#define R_SPARC_IRELATIVE 249 ++#define R_SPARC_GNU_VTINHERIT 250 ++#define R_SPARC_GNU_VTENTRY 251 ++#define R_SPARC_REV32 252 ++/* Keep this the last entry. */ ++#define R_SPARC_NUM 253 ++ ++/* For Sparc64, legal values for d_tag of Elf64_Dyn. */ ++ ++#define DT_SPARC_REGISTER 0x70000001 ++#define DT_SPARC_NUM 2 ++ ++/* MIPS R3000 specific definitions. */ ++ ++/* Legal values for e_flags field of Elf32_Ehdr. */ ++ ++#define EF_MIPS_NOREORDER 1 /* A .noreorder directive was used */ ++#define EF_MIPS_PIC 2 /* Contains PIC code */ ++#define EF_MIPS_CPIC 4 /* Uses PIC calling sequence */ ++#define EF_MIPS_XGOT 8 ++#define EF_MIPS_64BIT_WHIRL 16 ++#define EF_MIPS_ABI2 32 ++#define EF_MIPS_ABI_ON32 64 ++#define EF_MIPS_ARCH 0xf0000000 /* MIPS architecture level */ ++ ++/* Legal values for MIPS architecture level. */ ++ ++#define EF_MIPS_ARCH_1 0x00000000 /* -mips1 code. */ ++#define EF_MIPS_ARCH_2 0x10000000 /* -mips2 code. */ ++#define EF_MIPS_ARCH_3 0x20000000 /* -mips3 code. */ ++#define EF_MIPS_ARCH_4 0x30000000 /* -mips4 code. */ ++#define EF_MIPS_ARCH_5 0x40000000 /* -mips5 code. */ ++#define EF_MIPS_ARCH_32 0x60000000 /* MIPS32 code. */ ++#define EF_MIPS_ARCH_64 0x70000000 /* MIPS64 code. */ ++ ++/* The following are non-official names and should not be used. */ ++ ++#define E_MIPS_ARCH_1 0x00000000 /* -mips1 code. */ ++#define E_MIPS_ARCH_2 0x10000000 /* -mips2 code. */ ++#define E_MIPS_ARCH_3 0x20000000 /* -mips3 code. */ ++#define E_MIPS_ARCH_4 0x30000000 /* -mips4 code. */ ++#define E_MIPS_ARCH_5 0x40000000 /* -mips5 code. */ ++#define E_MIPS_ARCH_32 0x60000000 /* MIPS32 code. */ ++#define E_MIPS_ARCH_64 0x70000000 /* MIPS64 code. */ ++ ++/* Special section indices. */ ++ ++#define SHN_MIPS_ACOMMON 0xff00 /* Allocated common symbols */ ++#define SHN_MIPS_TEXT 0xff01 /* Allocated test symbols. */ ++#define SHN_MIPS_DATA 0xff02 /* Allocated data symbols. */ ++#define SHN_MIPS_SCOMMON 0xff03 /* Small common symbols */ ++#define SHN_MIPS_SUNDEFINED 0xff04 /* Small undefined symbols */ ++ ++/* Legal values for sh_type field of Elf32_Shdr. */ ++ ++#define SHT_MIPS_LIBLIST 0x70000000 /* Shared objects used in link */ ++#define SHT_MIPS_MSYM 0x70000001 ++#define SHT_MIPS_CONFLICT 0x70000002 /* Conflicting symbols */ ++#define SHT_MIPS_GPTAB 0x70000003 /* Global data area sizes */ ++#define SHT_MIPS_UCODE 0x70000004 /* Reserved for SGI/MIPS compilers */ ++#define SHT_MIPS_DEBUG 0x70000005 /* MIPS ECOFF debugging information*/ ++#define SHT_MIPS_REGINFO 0x70000006 /* Register usage information */ ++#define SHT_MIPS_PACKAGE 0x70000007 ++#define SHT_MIPS_PACKSYM 0x70000008 ++#define SHT_MIPS_RELD 0x70000009 ++#define SHT_MIPS_IFACE 0x7000000b ++#define SHT_MIPS_CONTENT 0x7000000c ++#define SHT_MIPS_OPTIONS 0x7000000d /* Miscellaneous options. */ ++#define SHT_MIPS_SHDR 0x70000010 ++#define SHT_MIPS_FDESC 0x70000011 ++#define SHT_MIPS_EXTSYM 0x70000012 ++#define SHT_MIPS_DENSE 0x70000013 ++#define SHT_MIPS_PDESC 0x70000014 ++#define SHT_MIPS_LOCSYM 0x70000015 ++#define SHT_MIPS_AUXSYM 0x70000016 ++#define SHT_MIPS_OPTSYM 0x70000017 ++#define SHT_MIPS_LOCSTR 0x70000018 ++#define SHT_MIPS_LINE 0x70000019 ++#define SHT_MIPS_RFDESC 0x7000001a ++#define SHT_MIPS_DELTASYM 0x7000001b ++#define SHT_MIPS_DELTAINST 0x7000001c ++#define SHT_MIPS_DELTACLASS 0x7000001d ++#define SHT_MIPS_DWARF 0x7000001e /* DWARF debugging information. */ ++#define SHT_MIPS_DELTADECL 0x7000001f ++#define SHT_MIPS_SYMBOL_LIB 0x70000020 ++#define SHT_MIPS_EVENTS 0x70000021 /* Event section. */ ++#define SHT_MIPS_TRANSLATE 0x70000022 ++#define SHT_MIPS_PIXIE 0x70000023 ++#define SHT_MIPS_XLATE 0x70000024 ++#define SHT_MIPS_XLATE_DEBUG 0x70000025 ++#define SHT_MIPS_WHIRL 0x70000026 ++#define SHT_MIPS_EH_REGION 0x70000027 ++#define SHT_MIPS_XLATE_OLD 0x70000028 ++#define SHT_MIPS_PDR_EXCEPTION 0x70000029 ++ ++/* Legal values for sh_flags field of Elf32_Shdr. */ ++ ++#define SHF_MIPS_GPREL 0x10000000 /* Must be part of global data area */ ++#define SHF_MIPS_MERGE 0x20000000 ++#define SHF_MIPS_ADDR 0x40000000 ++#define SHF_MIPS_STRINGS 0x80000000 ++#define SHF_MIPS_NOSTRIP 0x08000000 ++#define SHF_MIPS_LOCAL 0x04000000 ++#define SHF_MIPS_NAMES 0x02000000 ++#define SHF_MIPS_NODUPE 0x01000000 ++ ++ ++/* Symbol tables. */ ++ ++/* MIPS specific values for `st_other'. */ ++#define STO_MIPS_DEFAULT 0x0 ++#define STO_MIPS_INTERNAL 0x1 ++#define STO_MIPS_HIDDEN 0x2 ++#define STO_MIPS_PROTECTED 0x3 ++#define STO_MIPS_PLT 0x8 ++#define STO_MIPS_SC_ALIGN_UNUSED 0xff ++ ++/* MIPS specific values for `st_info'. */ ++#define STB_MIPS_SPLIT_COMMON 13 ++ ++/* Entries found in sections of type SHT_MIPS_GPTAB. */ ++ ++typedef union ++{ ++ struct ++ { ++ Elf32_Word gt_current_g_value; /* -G value used for compilation */ ++ Elf32_Word gt_unused; /* Not used */ ++ } gt_header; /* First entry in section */ ++ struct ++ { ++ Elf32_Word gt_g_value; /* If this value were used for -G */ ++ Elf32_Word gt_bytes; /* This many bytes would be used */ ++ } gt_entry; /* Subsequent entries in section */ ++} Elf32_gptab; ++ ++/* Entry found in sections of type SHT_MIPS_REGINFO. */ ++ ++typedef struct ++{ ++ Elf32_Word ri_gprmask; /* General registers used */ ++ Elf32_Word ri_cprmask[4]; /* Coprocessor registers used */ ++ Elf32_Sword ri_gp_value; /* $gp register value */ ++} Elf32_RegInfo; ++ ++/* Entries found in sections of type SHT_MIPS_OPTIONS. */ ++ ++typedef struct ++{ ++ unsigned char kind; /* Determines interpretation of the ++ variable part of descriptor. */ ++ unsigned char size; /* Size of descriptor, including header. */ ++ Elf32_Section section; /* Section header index of section affected, ++ 0 for global options. */ ++ Elf32_Word info; /* Kind-specific information. */ ++} Elf_Options; ++ ++/* Values for `kind' field in Elf_Options. */ ++ ++#define ODK_NULL 0 /* Undefined. */ ++#define ODK_REGINFO 1 /* Register usage information. */ ++#define ODK_EXCEPTIONS 2 /* Exception processing options. */ ++#define ODK_PAD 3 /* Section padding options. */ ++#define ODK_HWPATCH 4 /* Hardware workarounds performed */ ++#define ODK_FILL 5 /* record the fill value used by the linker. */ ++#define ODK_TAGS 6 /* reserve space for desktop tools to write. */ ++#define ODK_HWAND 7 /* HW workarounds. 'AND' bits when merging. */ ++#define ODK_HWOR 8 /* HW workarounds. 'OR' bits when merging. */ ++ ++/* Values for `info' in Elf_Options for ODK_EXCEPTIONS entries. */ ++ ++#define OEX_FPU_MIN 0x1f /* FPE's which MUST be enabled. */ ++#define OEX_FPU_MAX 0x1f00 /* FPE's which MAY be enabled. */ ++#define OEX_PAGE0 0x10000 /* page zero must be mapped. */ ++#define OEX_SMM 0x20000 /* Force sequential memory mode? */ ++#define OEX_FPDBUG 0x40000 /* Force floating point debug mode? */ ++#define OEX_PRECISEFP OEX_FPDBUG ++#define OEX_DISMISS 0x80000 /* Dismiss invalid address faults? */ ++ ++#define OEX_FPU_INVAL 0x10 ++#define OEX_FPU_DIV0 0x08 ++#define OEX_FPU_OFLO 0x04 ++#define OEX_FPU_UFLO 0x02 ++#define OEX_FPU_INEX 0x01 ++ ++/* Masks for `info' in Elf_Options for an ODK_HWPATCH entry. */ ++ ++#define OHW_R4KEOP 0x1 /* R4000 end-of-page patch. */ ++#define OHW_R8KPFETCH 0x2 /* may need R8000 prefetch patch. */ ++#define OHW_R5KEOP 0x4 /* R5000 end-of-page patch. */ ++#define OHW_R5KCVTL 0x8 /* R5000 cvt.[ds].l bug. clean=1. */ ++ ++#define OPAD_PREFIX 0x1 ++#define OPAD_POSTFIX 0x2 ++#define OPAD_SYMBOL 0x4 ++ ++/* Entry found in `.options' section. */ ++ ++typedef struct ++{ ++ Elf32_Word hwp_flags1; /* Extra flags. */ ++ Elf32_Word hwp_flags2; /* Extra flags. */ ++} Elf_Options_Hw; ++ ++/* Masks for `info' in ElfOptions for ODK_HWAND and ODK_HWOR entries. */ ++ ++#define OHWA0_R4KEOP_CHECKED 0x00000001 ++#define OHWA1_R4KEOP_CLEAN 0x00000002 ++ ++/* MIPS relocs. */ ++ ++#define R_MIPS_NONE 0 /* No reloc */ ++#define R_MIPS_16 1 /* Direct 16 bit */ ++#define R_MIPS_32 2 /* Direct 32 bit */ ++#define R_MIPS_REL32 3 /* PC relative 32 bit */ ++#define R_MIPS_26 4 /* Direct 26 bit shifted */ ++#define R_MIPS_HI16 5 /* High 16 bit */ ++#define R_MIPS_LO16 6 /* Low 16 bit */ ++#define R_MIPS_GPREL16 7 /* GP relative 16 bit */ ++#define R_MIPS_LITERAL 8 /* 16 bit literal entry */ ++#define R_MIPS_GOT16 9 /* 16 bit GOT entry */ ++#define R_MIPS_PC16 10 /* PC relative 16 bit */ ++#define R_MIPS_CALL16 11 /* 16 bit GOT entry for function */ ++#define R_MIPS_GPREL32 12 /* GP relative 32 bit */ ++ ++#define R_MIPS_SHIFT5 16 ++#define R_MIPS_SHIFT6 17 ++#define R_MIPS_64 18 ++#define R_MIPS_GOT_DISP 19 ++#define R_MIPS_GOT_PAGE 20 ++#define R_MIPS_GOT_OFST 21 ++#define R_MIPS_GOT_HI16 22 ++#define R_MIPS_GOT_LO16 23 ++#define R_MIPS_SUB 24 ++#define R_MIPS_INSERT_A 25 ++#define R_MIPS_INSERT_B 26 ++#define R_MIPS_DELETE 27 ++#define R_MIPS_HIGHER 28 ++#define R_MIPS_HIGHEST 29 ++#define R_MIPS_CALL_HI16 30 ++#define R_MIPS_CALL_LO16 31 ++#define R_MIPS_SCN_DISP 32 ++#define R_MIPS_REL16 33 ++#define R_MIPS_ADD_IMMEDIATE 34 ++#define R_MIPS_PJUMP 35 ++#define R_MIPS_RELGOT 36 ++#define R_MIPS_JALR 37 ++#define R_MIPS_TLS_DTPMOD32 38 /* Module number 32 bit */ ++#define R_MIPS_TLS_DTPREL32 39 /* Module-relative offset 32 bit */ ++#define R_MIPS_TLS_DTPMOD64 40 /* Module number 64 bit */ ++#define R_MIPS_TLS_DTPREL64 41 /* Module-relative offset 64 bit */ ++#define R_MIPS_TLS_GD 42 /* 16 bit GOT offset for GD */ ++#define R_MIPS_TLS_LDM 43 /* 16 bit GOT offset for LDM */ ++#define R_MIPS_TLS_DTPREL_HI16 44 /* Module-relative offset, high 16 bits */ ++#define R_MIPS_TLS_DTPREL_LO16 45 /* Module-relative offset, low 16 bits */ ++#define R_MIPS_TLS_GOTTPREL 46 /* 16 bit GOT offset for IE */ ++#define R_MIPS_TLS_TPREL32 47 /* TP-relative offset, 32 bit */ ++#define R_MIPS_TLS_TPREL64 48 /* TP-relative offset, 64 bit */ ++#define R_MIPS_TLS_TPREL_HI16 49 /* TP-relative offset, high 16 bits */ ++#define R_MIPS_TLS_TPREL_LO16 50 /* TP-relative offset, low 16 bits */ ++#define R_MIPS_GLOB_DAT 51 ++#define R_MIPS_COPY 126 ++#define R_MIPS_JUMP_SLOT 127 ++/* Keep this the last entry. */ ++#define R_MIPS_NUM 128 ++ ++/* Legal values for p_type field of Elf32_Phdr. */ ++ ++#define PT_MIPS_REGINFO 0x70000000 /* Register usage information */ ++#define PT_MIPS_RTPROC 0x70000001 /* Runtime procedure table. */ ++#define PT_MIPS_OPTIONS 0x70000002 ++ ++/* Special program header types. */ ++ ++#define PF_MIPS_LOCAL 0x10000000 ++ ++/* Legal values for d_tag field of Elf32_Dyn. */ ++ ++#define DT_MIPS_RLD_VERSION 0x70000001 /* Runtime linker interface version */ ++#define DT_MIPS_TIME_STAMP 0x70000002 /* Timestamp */ ++#define DT_MIPS_ICHECKSUM 0x70000003 /* Checksum */ ++#define DT_MIPS_IVERSION 0x70000004 /* Version string (string tbl index) */ ++#define DT_MIPS_FLAGS 0x70000005 /* Flags */ ++#define DT_MIPS_BASE_ADDRESS 0x70000006 /* Base address */ ++#define DT_MIPS_MSYM 0x70000007 ++#define DT_MIPS_CONFLICT 0x70000008 /* Address of CONFLICT section */ ++#define DT_MIPS_LIBLIST 0x70000009 /* Address of LIBLIST section */ ++#define DT_MIPS_LOCAL_GOTNO 0x7000000a /* Number of local GOT entries */ ++#define DT_MIPS_CONFLICTNO 0x7000000b /* Number of CONFLICT entries */ ++#define DT_MIPS_LIBLISTNO 0x70000010 /* Number of LIBLIST entries */ ++#define DT_MIPS_SYMTABNO 0x70000011 /* Number of DYNSYM entries */ ++#define DT_MIPS_UNREFEXTNO 0x70000012 /* First external DYNSYM */ ++#define DT_MIPS_GOTSYM 0x70000013 /* First GOT entry in DYNSYM */ ++#define DT_MIPS_HIPAGENO 0x70000014 /* Number of GOT page table entries */ ++#define DT_MIPS_RLD_MAP 0x70000016 /* Address of run time loader map. */ ++#define DT_MIPS_DELTA_CLASS 0x70000017 /* Delta C++ class definition. */ ++#define DT_MIPS_DELTA_CLASS_NO 0x70000018 /* Number of entries in ++ DT_MIPS_DELTA_CLASS. */ ++#define DT_MIPS_DELTA_INSTANCE 0x70000019 /* Delta C++ class instances. */ ++#define DT_MIPS_DELTA_INSTANCE_NO 0x7000001a /* Number of entries in ++ DT_MIPS_DELTA_INSTANCE. */ ++#define DT_MIPS_DELTA_RELOC 0x7000001b /* Delta relocations. */ ++#define DT_MIPS_DELTA_RELOC_NO 0x7000001c /* Number of entries in ++ DT_MIPS_DELTA_RELOC. */ ++#define DT_MIPS_DELTA_SYM 0x7000001d /* Delta symbols that Delta ++ relocations refer to. */ ++#define DT_MIPS_DELTA_SYM_NO 0x7000001e /* Number of entries in ++ DT_MIPS_DELTA_SYM. */ ++#define DT_MIPS_DELTA_CLASSSYM 0x70000020 /* Delta symbols that hold the ++ class declaration. */ ++#define DT_MIPS_DELTA_CLASSSYM_NO 0x70000021 /* Number of entries in ++ DT_MIPS_DELTA_CLASSSYM. */ ++#define DT_MIPS_CXX_FLAGS 0x70000022 /* Flags indicating for C++ flavor. */ ++#define DT_MIPS_PIXIE_INIT 0x70000023 ++#define DT_MIPS_SYMBOL_LIB 0x70000024 ++#define DT_MIPS_LOCALPAGE_GOTIDX 0x70000025 ++#define DT_MIPS_LOCAL_GOTIDX 0x70000026 ++#define DT_MIPS_HIDDEN_GOTIDX 0x70000027 ++#define DT_MIPS_PROTECTED_GOTIDX 0x70000028 ++#define DT_MIPS_OPTIONS 0x70000029 /* Address of .options. */ ++#define DT_MIPS_INTERFACE 0x7000002a /* Address of .interface. */ ++#define DT_MIPS_DYNSTR_ALIGN 0x7000002b ++#define DT_MIPS_INTERFACE_SIZE 0x7000002c /* Size of the .interface section. */ ++#define DT_MIPS_RLD_TEXT_RESOLVE_ADDR 0x7000002d /* Address of rld_text_rsolve ++ function stored in GOT. */ ++#define DT_MIPS_PERF_SUFFIX 0x7000002e /* Default suffix of dso to be added ++ by rld on dlopen() calls. */ ++#define DT_MIPS_COMPACT_SIZE 0x7000002f /* (O32)Size of compact rel section. */ ++#define DT_MIPS_GP_VALUE 0x70000030 /* GP value for aux GOTs. */ ++#define DT_MIPS_AUX_DYNAMIC 0x70000031 /* Address of aux .dynamic. */ ++/* The address of .got.plt in an executable using the new non-PIC ABI. */ ++#define DT_MIPS_PLTGOT 0x70000032 ++/* The base of the PLT in an executable using the new non-PIC ABI if that ++ PLT is writable. For a non-writable PLT, this is omitted or has a zero ++ value. */ ++#define DT_MIPS_RWPLT 0x70000034 ++#define DT_MIPS_NUM 0x35 ++ ++/* Legal values for DT_MIPS_FLAGS Elf32_Dyn entry. */ ++ ++#define RHF_NONE 0 /* No flags */ ++#define RHF_QUICKSTART (1 << 0) /* Use quickstart */ ++#define RHF_NOTPOT (1 << 1) /* Hash size not power of 2 */ ++#define RHF_NO_LIBRARY_REPLACEMENT (1 << 2) /* Ignore LD_LIBRARY_PATH */ ++#define RHF_NO_MOVE (1 << 3) ++#define RHF_SGI_ONLY (1 << 4) ++#define RHF_GUARANTEE_INIT (1 << 5) ++#define RHF_DELTA_C_PLUS_PLUS (1 << 6) ++#define RHF_GUARANTEE_START_INIT (1 << 7) ++#define RHF_PIXIE (1 << 8) ++#define RHF_DEFAULT_DELAY_LOAD (1 << 9) ++#define RHF_REQUICKSTART (1 << 10) ++#define RHF_REQUICKSTARTED (1 << 11) ++#define RHF_CORD (1 << 12) ++#define RHF_NO_UNRES_UNDEF (1 << 13) ++#define RHF_RLD_ORDER_SAFE (1 << 14) ++ ++/* Entries found in sections of type SHT_MIPS_LIBLIST. */ ++ ++typedef struct ++{ ++ Elf32_Word l_name; /* Name (string table index) */ ++ Elf32_Word l_time_stamp; /* Timestamp */ ++ Elf32_Word l_checksum; /* Checksum */ ++ Elf32_Word l_version; /* Interface version */ ++ Elf32_Word l_flags; /* Flags */ ++} Elf32_Lib; ++ ++typedef struct ++{ ++ Elf64_Word l_name; /* Name (string table index) */ ++ Elf64_Word l_time_stamp; /* Timestamp */ ++ Elf64_Word l_checksum; /* Checksum */ ++ Elf64_Word l_version; /* Interface version */ ++ Elf64_Word l_flags; /* Flags */ ++} Elf64_Lib; ++ ++ ++/* Legal values for l_flags. */ ++ ++#define LL_NONE 0 ++#define LL_EXACT_MATCH (1 << 0) /* Require exact match */ ++#define LL_IGNORE_INT_VER (1 << 1) /* Ignore interface version */ ++#define LL_REQUIRE_MINOR (1 << 2) ++#define LL_EXPORTS (1 << 3) ++#define LL_DELAY_LOAD (1 << 4) ++#define LL_DELTA (1 << 5) ++ ++/* Entries found in sections of type SHT_MIPS_CONFLICT. */ ++ ++typedef Elf32_Addr Elf32_Conflict; ++ ++ ++/* HPPA specific definitions. */ ++ ++/* Legal values for e_flags field of Elf32_Ehdr. */ ++ ++#define EF_PARISC_TRAPNIL 0x00010000 /* Trap nil pointer dereference. */ ++#define EF_PARISC_EXT 0x00020000 /* Program uses arch. extensions. */ ++#define EF_PARISC_LSB 0x00040000 /* Program expects little endian. */ ++#define EF_PARISC_WIDE 0x00080000 /* Program expects wide mode. */ ++#define EF_PARISC_NO_KABP 0x00100000 /* No kernel assisted branch ++ prediction. */ ++#define EF_PARISC_LAZYSWAP 0x00400000 /* Allow lazy swapping. */ ++#define EF_PARISC_ARCH 0x0000ffff /* Architecture version. */ ++ ++/* Defined values for `e_flags & EF_PARISC_ARCH' are: */ ++ ++#define EFA_PARISC_1_0 0x020b /* PA-RISC 1.0 big-endian. */ ++#define EFA_PARISC_1_1 0x0210 /* PA-RISC 1.1 big-endian. */ ++#define EFA_PARISC_2_0 0x0214 /* PA-RISC 2.0 big-endian. */ ++ ++/* Additional section indeces. */ ++ ++#define SHN_PARISC_ANSI_COMMON 0xff00 /* Section for tenatively declared ++ symbols in ANSI C. */ ++#define SHN_PARISC_HUGE_COMMON 0xff01 /* Common blocks in huge model. */ ++ ++/* Legal values for sh_type field of Elf32_Shdr. */ ++ ++#define SHT_PARISC_EXT 0x70000000 /* Contains product specific ext. */ ++#define SHT_PARISC_UNWIND 0x70000001 /* Unwind information. */ ++#define SHT_PARISC_DOC 0x70000002 /* Debug info for optimized code. */ ++ ++/* Legal values for sh_flags field of Elf32_Shdr. */ ++ ++#define SHF_PARISC_SHORT 0x20000000 /* Section with short addressing. */ ++#define SHF_PARISC_HUGE 0x40000000 /* Section far from gp. */ ++#define SHF_PARISC_SBP 0x80000000 /* Static branch prediction code. */ ++ ++/* Legal values for ST_TYPE subfield of st_info (symbol type). */ ++ ++#define STT_PARISC_MILLICODE 13 /* Millicode function entry point. */ ++ ++#define STT_HP_OPAQUE (STT_LOOS + 0x1) ++#define STT_HP_STUB (STT_LOOS + 0x2) ++ ++/* HPPA relocs. */ ++ ++#define R_PARISC_NONE 0 /* No reloc. */ ++#define R_PARISC_DIR32 1 /* Direct 32-bit reference. */ ++#define R_PARISC_DIR21L 2 /* Left 21 bits of eff. address. */ ++#define R_PARISC_DIR17R 3 /* Right 17 bits of eff. address. */ ++#define R_PARISC_DIR17F 4 /* 17 bits of eff. address. */ ++#define R_PARISC_DIR14R 6 /* Right 14 bits of eff. address. */ ++#define R_PARISC_PCREL32 9 /* 32-bit rel. address. */ ++#define R_PARISC_PCREL21L 10 /* Left 21 bits of rel. address. */ ++#define R_PARISC_PCREL17R 11 /* Right 17 bits of rel. address. */ ++#define R_PARISC_PCREL17F 12 /* 17 bits of rel. address. */ ++#define R_PARISC_PCREL14R 14 /* Right 14 bits of rel. address. */ ++#define R_PARISC_DPREL21L 18 /* Left 21 bits of rel. address. */ ++#define R_PARISC_DPREL14R 22 /* Right 14 bits of rel. address. */ ++#define R_PARISC_GPREL21L 26 /* GP-relative, left 21 bits. */ ++#define R_PARISC_GPREL14R 30 /* GP-relative, right 14 bits. */ ++#define R_PARISC_LTOFF21L 34 /* LT-relative, left 21 bits. */ ++#define R_PARISC_LTOFF14R 38 /* LT-relative, right 14 bits. */ ++#define R_PARISC_SECREL32 41 /* 32 bits section rel. address. */ ++#define R_PARISC_SEGBASE 48 /* No relocation, set segment base. */ ++#define R_PARISC_SEGREL32 49 /* 32 bits segment rel. address. */ ++#define R_PARISC_PLTOFF21L 50 /* PLT rel. address, left 21 bits. */ ++#define R_PARISC_PLTOFF14R 54 /* PLT rel. address, right 14 bits. */ ++#define R_PARISC_LTOFF_FPTR32 57 /* 32 bits LT-rel. function pointer. */ ++#define R_PARISC_LTOFF_FPTR21L 58 /* LT-rel. fct ptr, left 21 bits. */ ++#define R_PARISC_LTOFF_FPTR14R 62 /* LT-rel. fct ptr, right 14 bits. */ ++#define R_PARISC_FPTR64 64 /* 64 bits function address. */ ++#define R_PARISC_PLABEL32 65 /* 32 bits function address. */ ++#define R_PARISC_PLABEL21L 66 /* Left 21 bits of fdesc address. */ ++#define R_PARISC_PLABEL14R 70 /* Right 14 bits of fdesc address. */ ++#define R_PARISC_PCREL64 72 /* 64 bits PC-rel. address. */ ++#define R_PARISC_PCREL22F 74 /* 22 bits PC-rel. address. */ ++#define R_PARISC_PCREL14WR 75 /* PC-rel. address, right 14 bits. */ ++#define R_PARISC_PCREL14DR 76 /* PC rel. address, right 14 bits. */ ++#define R_PARISC_PCREL16F 77 /* 16 bits PC-rel. address. */ ++#define R_PARISC_PCREL16WF 78 /* 16 bits PC-rel. address. */ ++#define R_PARISC_PCREL16DF 79 /* 16 bits PC-rel. address. */ ++#define R_PARISC_DIR64 80 /* 64 bits of eff. address. */ ++#define R_PARISC_DIR14WR 83 /* 14 bits of eff. address. */ ++#define R_PARISC_DIR14DR 84 /* 14 bits of eff. address. */ ++#define R_PARISC_DIR16F 85 /* 16 bits of eff. address. */ ++#define R_PARISC_DIR16WF 86 /* 16 bits of eff. address. */ ++#define R_PARISC_DIR16DF 87 /* 16 bits of eff. address. */ ++#define R_PARISC_GPREL64 88 /* 64 bits of GP-rel. address. */ ++#define R_PARISC_GPREL14WR 91 /* GP-rel. address, right 14 bits. */ ++#define R_PARISC_GPREL14DR 92 /* GP-rel. address, right 14 bits. */ ++#define R_PARISC_GPREL16F 93 /* 16 bits GP-rel. address. */ ++#define R_PARISC_GPREL16WF 94 /* 16 bits GP-rel. address. */ ++#define R_PARISC_GPREL16DF 95 /* 16 bits GP-rel. address. */ ++#define R_PARISC_LTOFF64 96 /* 64 bits LT-rel. address. */ ++#define R_PARISC_LTOFF14WR 99 /* LT-rel. address, right 14 bits. */ ++#define R_PARISC_LTOFF14DR 100 /* LT-rel. address, right 14 bits. */ ++#define R_PARISC_LTOFF16F 101 /* 16 bits LT-rel. address. */ ++#define R_PARISC_LTOFF16WF 102 /* 16 bits LT-rel. address. */ ++#define R_PARISC_LTOFF16DF 103 /* 16 bits LT-rel. address. */ ++#define R_PARISC_SECREL64 104 /* 64 bits section rel. address. */ ++#define R_PARISC_SEGREL64 112 /* 64 bits segment rel. address. */ ++#define R_PARISC_PLTOFF14WR 115 /* PLT-rel. address, right 14 bits. */ ++#define R_PARISC_PLTOFF14DR 116 /* PLT-rel. address, right 14 bits. */ ++#define R_PARISC_PLTOFF16F 117 /* 16 bits LT-rel. address. */ ++#define R_PARISC_PLTOFF16WF 118 /* 16 bits PLT-rel. address. */ ++#define R_PARISC_PLTOFF16DF 119 /* 16 bits PLT-rel. address. */ ++#define R_PARISC_LTOFF_FPTR64 120 /* 64 bits LT-rel. function ptr. */ ++#define R_PARISC_LTOFF_FPTR14WR 123 /* LT-rel. fct. ptr., right 14 bits. */ ++#define R_PARISC_LTOFF_FPTR14DR 124 /* LT-rel. fct. ptr., right 14 bits. */ ++#define R_PARISC_LTOFF_FPTR16F 125 /* 16 bits LT-rel. function ptr. */ ++#define R_PARISC_LTOFF_FPTR16WF 126 /* 16 bits LT-rel. function ptr. */ ++#define R_PARISC_LTOFF_FPTR16DF 127 /* 16 bits LT-rel. function ptr. */ ++#define R_PARISC_LORESERVE 128 ++#define R_PARISC_COPY 128 /* Copy relocation. */ ++#define R_PARISC_IPLT 129 /* Dynamic reloc, imported PLT */ ++#define R_PARISC_EPLT 130 /* Dynamic reloc, exported PLT */ ++#define R_PARISC_TPREL32 153 /* 32 bits TP-rel. address. */ ++#define R_PARISC_TPREL21L 154 /* TP-rel. address, left 21 bits. */ ++#define R_PARISC_TPREL14R 158 /* TP-rel. address, right 14 bits. */ ++#define R_PARISC_LTOFF_TP21L 162 /* LT-TP-rel. address, left 21 bits. */ ++#define R_PARISC_LTOFF_TP14R 166 /* LT-TP-rel. address, right 14 bits.*/ ++#define R_PARISC_LTOFF_TP14F 167 /* 14 bits LT-TP-rel. address. */ ++#define R_PARISC_TPREL64 216 /* 64 bits TP-rel. address. */ ++#define R_PARISC_TPREL14WR 219 /* TP-rel. address, right 14 bits. */ ++#define R_PARISC_TPREL14DR 220 /* TP-rel. address, right 14 bits. */ ++#define R_PARISC_TPREL16F 221 /* 16 bits TP-rel. address. */ ++#define R_PARISC_TPREL16WF 222 /* 16 bits TP-rel. address. */ ++#define R_PARISC_TPREL16DF 223 /* 16 bits TP-rel. address. */ ++#define R_PARISC_LTOFF_TP64 224 /* 64 bits LT-TP-rel. address. */ ++#define R_PARISC_LTOFF_TP14WR 227 /* LT-TP-rel. address, right 14 bits.*/ ++#define R_PARISC_LTOFF_TP14DR 228 /* LT-TP-rel. address, right 14 bits.*/ ++#define R_PARISC_LTOFF_TP16F 229 /* 16 bits LT-TP-rel. address. */ ++#define R_PARISC_LTOFF_TP16WF 230 /* 16 bits LT-TP-rel. address. */ ++#define R_PARISC_LTOFF_TP16DF 231 /* 16 bits LT-TP-rel. address. */ ++#define R_PARISC_GNU_VTENTRY 232 ++#define R_PARISC_GNU_VTINHERIT 233 ++#define R_PARISC_TLS_GD21L 234 /* GD 21-bit left. */ ++#define R_PARISC_TLS_GD14R 235 /* GD 14-bit right. */ ++#define R_PARISC_TLS_GDCALL 236 /* GD call to __t_g_a. */ ++#define R_PARISC_TLS_LDM21L 237 /* LD module 21-bit left. */ ++#define R_PARISC_TLS_LDM14R 238 /* LD module 14-bit right. */ ++#define R_PARISC_TLS_LDMCALL 239 /* LD module call to __t_g_a. */ ++#define R_PARISC_TLS_LDO21L 240 /* LD offset 21-bit left. */ ++#define R_PARISC_TLS_LDO14R 241 /* LD offset 14-bit right. */ ++#define R_PARISC_TLS_DTPMOD32 242 /* DTP module 32-bit. */ ++#define R_PARISC_TLS_DTPMOD64 243 /* DTP module 64-bit. */ ++#define R_PARISC_TLS_DTPOFF32 244 /* DTP offset 32-bit. */ ++#define R_PARISC_TLS_DTPOFF64 245 /* DTP offset 32-bit. */ ++#define R_PARISC_TLS_LE21L R_PARISC_TPREL21L ++#define R_PARISC_TLS_LE14R R_PARISC_TPREL14R ++#define R_PARISC_TLS_IE21L R_PARISC_LTOFF_TP21L ++#define R_PARISC_TLS_IE14R R_PARISC_LTOFF_TP14R ++#define R_PARISC_TLS_TPREL32 R_PARISC_TPREL32 ++#define R_PARISC_TLS_TPREL64 R_PARISC_TPREL64 ++#define R_PARISC_HIRESERVE 255 ++ ++/* Legal values for p_type field of Elf32_Phdr/Elf64_Phdr. */ ++ ++#define PT_HP_TLS (PT_LOOS + 0x0) ++#define PT_HP_CORE_NONE (PT_LOOS + 0x1) ++#define PT_HP_CORE_VERSION (PT_LOOS + 0x2) ++#define PT_HP_CORE_KERNEL (PT_LOOS + 0x3) ++#define PT_HP_CORE_COMM (PT_LOOS + 0x4) ++#define PT_HP_CORE_PROC (PT_LOOS + 0x5) ++#define PT_HP_CORE_LOADABLE (PT_LOOS + 0x6) ++#define PT_HP_CORE_STACK (PT_LOOS + 0x7) ++#define PT_HP_CORE_SHM (PT_LOOS + 0x8) ++#define PT_HP_CORE_MMF (PT_LOOS + 0x9) ++#define PT_HP_PARALLEL (PT_LOOS + 0x10) ++#define PT_HP_FASTBIND (PT_LOOS + 0x11) ++#define PT_HP_OPT_ANNOT (PT_LOOS + 0x12) ++#define PT_HP_HSL_ANNOT (PT_LOOS + 0x13) ++#define PT_HP_STACK (PT_LOOS + 0x14) ++ ++#define PT_PARISC_ARCHEXT 0x70000000 ++#define PT_PARISC_UNWIND 0x70000001 ++ ++/* Legal values for p_flags field of Elf32_Phdr/Elf64_Phdr. */ ++ ++#define PF_PARISC_SBP 0x08000000 ++ ++#define PF_HP_PAGE_SIZE 0x00100000 ++#define PF_HP_FAR_SHARED 0x00200000 ++#define PF_HP_NEAR_SHARED 0x00400000 ++#define PF_HP_CODE 0x01000000 ++#define PF_HP_MODIFY 0x02000000 ++#define PF_HP_LAZYSWAP 0x04000000 ++#define PF_HP_SBP 0x08000000 ++ ++ ++/* Alpha specific definitions. */ ++ ++/* Legal values for e_flags field of Elf64_Ehdr. */ ++ ++#define EF_ALPHA_32BIT 1 /* All addresses must be < 2GB. */ ++#define EF_ALPHA_CANRELAX 2 /* Relocations for relaxing exist. */ ++ ++/* Legal values for sh_type field of Elf64_Shdr. */ ++ ++/* These two are primerily concerned with ECOFF debugging info. */ ++#define SHT_ALPHA_DEBUG 0x70000001 ++#define SHT_ALPHA_REGINFO 0x70000002 ++ ++/* Legal values for sh_flags field of Elf64_Shdr. */ ++ ++#define SHF_ALPHA_GPREL 0x10000000 ++ ++/* Legal values for st_other field of Elf64_Sym. */ ++#define STO_ALPHA_NOPV 0x80 /* No PV required. */ ++#define STO_ALPHA_STD_GPLOAD 0x88 /* PV only used for initial ldgp. */ ++ ++/* Alpha relocs. */ ++ ++#define R_ALPHA_NONE 0 /* No reloc */ ++#define R_ALPHA_REFLONG 1 /* Direct 32 bit */ ++#define R_ALPHA_REFQUAD 2 /* Direct 64 bit */ ++#define R_ALPHA_GPREL32 3 /* GP relative 32 bit */ ++#define R_ALPHA_LITERAL 4 /* GP relative 16 bit w/optimization */ ++#define R_ALPHA_LITUSE 5 /* Optimization hint for LITERAL */ ++#define R_ALPHA_GPDISP 6 /* Add displacement to GP */ ++#define R_ALPHA_BRADDR 7 /* PC+4 relative 23 bit shifted */ ++#define R_ALPHA_HINT 8 /* PC+4 relative 16 bit shifted */ ++#define R_ALPHA_SREL16 9 /* PC relative 16 bit */ ++#define R_ALPHA_SREL32 10 /* PC relative 32 bit */ ++#define R_ALPHA_SREL64 11 /* PC relative 64 bit */ ++#define R_ALPHA_GPRELHIGH 17 /* GP relative 32 bit, high 16 bits */ ++#define R_ALPHA_GPRELLOW 18 /* GP relative 32 bit, low 16 bits */ ++#define R_ALPHA_GPREL16 19 /* GP relative 16 bit */ ++#define R_ALPHA_COPY 24 /* Copy symbol at runtime */ ++#define R_ALPHA_GLOB_DAT 25 /* Create GOT entry */ ++#define R_ALPHA_JMP_SLOT 26 /* Create PLT entry */ ++#define R_ALPHA_RELATIVE 27 /* Adjust by program base */ ++#define R_ALPHA_TLS_GD_HI 28 ++#define R_ALPHA_TLSGD 29 ++#define R_ALPHA_TLS_LDM 30 ++#define R_ALPHA_DTPMOD64 31 ++#define R_ALPHA_GOTDTPREL 32 ++#define R_ALPHA_DTPREL64 33 ++#define R_ALPHA_DTPRELHI 34 ++#define R_ALPHA_DTPRELLO 35 ++#define R_ALPHA_DTPREL16 36 ++#define R_ALPHA_GOTTPREL 37 ++#define R_ALPHA_TPREL64 38 ++#define R_ALPHA_TPRELHI 39 ++#define R_ALPHA_TPRELLO 40 ++#define R_ALPHA_TPREL16 41 ++/* Keep this the last entry. */ ++#define R_ALPHA_NUM 46 ++ ++/* Magic values of the LITUSE relocation addend. */ ++#define LITUSE_ALPHA_ADDR 0 ++#define LITUSE_ALPHA_BASE 1 ++#define LITUSE_ALPHA_BYTOFF 2 ++#define LITUSE_ALPHA_JSR 3 ++#define LITUSE_ALPHA_TLS_GD 4 ++#define LITUSE_ALPHA_TLS_LDM 5 ++ ++/* Legal values for d_tag of Elf64_Dyn. */ ++#define DT_ALPHA_PLTRO (DT_LOPROC + 0) ++#define DT_ALPHA_NUM 1 ++ ++/* PowerPC specific declarations */ ++ ++/* Values for Elf32/64_Ehdr.e_flags. */ ++#define EF_PPC_EMB 0x80000000 /* PowerPC embedded flag */ ++ ++/* Cygnus local bits below */ ++#define EF_PPC_RELOCATABLE 0x00010000 /* PowerPC -mrelocatable flag*/ ++#define EF_PPC_RELOCATABLE_LIB 0x00008000 /* PowerPC -mrelocatable-lib ++ flag */ ++ ++/* PowerPC relocations defined by the ABIs */ ++#define R_PPC_NONE 0 ++#define R_PPC_ADDR32 1 /* 32bit absolute address */ ++#define R_PPC_ADDR24 2 /* 26bit address, 2 bits ignored. */ ++#define R_PPC_ADDR16 3 /* 16bit absolute address */ ++#define R_PPC_ADDR16_LO 4 /* lower 16bit of absolute address */ ++#define R_PPC_ADDR16_HI 5 /* high 16bit of absolute address */ ++#define R_PPC_ADDR16_HA 6 /* adjusted high 16bit */ ++#define R_PPC_ADDR14 7 /* 16bit address, 2 bits ignored */ ++#define R_PPC_ADDR14_BRTAKEN 8 ++#define R_PPC_ADDR14_BRNTAKEN 9 ++#define R_PPC_REL24 10 /* PC relative 26 bit */ ++#define R_PPC_REL14 11 /* PC relative 16 bit */ ++#define R_PPC_REL14_BRTAKEN 12 ++#define R_PPC_REL14_BRNTAKEN 13 ++#define R_PPC_GOT16 14 ++#define R_PPC_GOT16_LO 15 ++#define R_PPC_GOT16_HI 16 ++#define R_PPC_GOT16_HA 17 ++#define R_PPC_PLTREL24 18 ++#define R_PPC_COPY 19 ++#define R_PPC_GLOB_DAT 20 ++#define R_PPC_JMP_SLOT 21 ++#define R_PPC_RELATIVE 22 ++#define R_PPC_LOCAL24PC 23 ++#define R_PPC_UADDR32 24 ++#define R_PPC_UADDR16 25 ++#define R_PPC_REL32 26 ++#define R_PPC_PLT32 27 ++#define R_PPC_PLTREL32 28 ++#define R_PPC_PLT16_LO 29 ++#define R_PPC_PLT16_HI 30 ++#define R_PPC_PLT16_HA 31 ++#define R_PPC_SDAREL16 32 ++#define R_PPC_SECTOFF 33 ++#define R_PPC_SECTOFF_LO 34 ++#define R_PPC_SECTOFF_HI 35 ++#define R_PPC_SECTOFF_HA 36 ++ ++/* PowerPC relocations defined for the TLS access ABI. */ ++#define R_PPC_TLS 67 /* none (sym+add)@tls */ ++#define R_PPC_DTPMOD32 68 /* word32 (sym+add)@dtpmod */ ++#define R_PPC_TPREL16 69 /* half16* (sym+add)@tprel */ ++#define R_PPC_TPREL16_LO 70 /* half16 (sym+add)@tprel@l */ ++#define R_PPC_TPREL16_HI 71 /* half16 (sym+add)@tprel@h */ ++#define R_PPC_TPREL16_HA 72 /* half16 (sym+add)@tprel@ha */ ++#define R_PPC_TPREL32 73 /* word32 (sym+add)@tprel */ ++#define R_PPC_DTPREL16 74 /* half16* (sym+add)@dtprel */ ++#define R_PPC_DTPREL16_LO 75 /* half16 (sym+add)@dtprel@l */ ++#define R_PPC_DTPREL16_HI 76 /* half16 (sym+add)@dtprel@h */ ++#define R_PPC_DTPREL16_HA 77 /* half16 (sym+add)@dtprel@ha */ ++#define R_PPC_DTPREL32 78 /* word32 (sym+add)@dtprel */ ++#define R_PPC_GOT_TLSGD16 79 /* half16* (sym+add)@got@tlsgd */ ++#define R_PPC_GOT_TLSGD16_LO 80 /* half16 (sym+add)@got@tlsgd@l */ ++#define R_PPC_GOT_TLSGD16_HI 81 /* half16 (sym+add)@got@tlsgd@h */ ++#define R_PPC_GOT_TLSGD16_HA 82 /* half16 (sym+add)@got@tlsgd@ha */ ++#define R_PPC_GOT_TLSLD16 83 /* half16* (sym+add)@got@tlsld */ ++#define R_PPC_GOT_TLSLD16_LO 84 /* half16 (sym+add)@got@tlsld@l */ ++#define R_PPC_GOT_TLSLD16_HI 85 /* half16 (sym+add)@got@tlsld@h */ ++#define R_PPC_GOT_TLSLD16_HA 86 /* half16 (sym+add)@got@tlsld@ha */ ++#define R_PPC_GOT_TPREL16 87 /* half16* (sym+add)@got@tprel */ ++#define R_PPC_GOT_TPREL16_LO 88 /* half16 (sym+add)@got@tprel@l */ ++#define R_PPC_GOT_TPREL16_HI 89 /* half16 (sym+add)@got@tprel@h */ ++#define R_PPC_GOT_TPREL16_HA 90 /* half16 (sym+add)@got@tprel@ha */ ++#define R_PPC_GOT_DTPREL16 91 /* half16* (sym+add)@got@dtprel */ ++#define R_PPC_GOT_DTPREL16_LO 92 /* half16* (sym+add)@got@dtprel@l */ ++#define R_PPC_GOT_DTPREL16_HI 93 /* half16* (sym+add)@got@dtprel@h */ ++#define R_PPC_GOT_DTPREL16_HA 94 /* half16* (sym+add)@got@dtprel@ha */ ++ ++/* The remaining relocs are from the Embedded ELF ABI, and are not ++ in the SVR4 ELF ABI. */ ++#define R_PPC_EMB_NADDR32 101 ++#define R_PPC_EMB_NADDR16 102 ++#define R_PPC_EMB_NADDR16_LO 103 ++#define R_PPC_EMB_NADDR16_HI 104 ++#define R_PPC_EMB_NADDR16_HA 105 ++#define R_PPC_EMB_SDAI16 106 ++#define R_PPC_EMB_SDA2I16 107 ++#define R_PPC_EMB_SDA2REL 108 ++#define R_PPC_EMB_SDA21 109 /* 16 bit offset in SDA */ ++#define R_PPC_EMB_MRKREF 110 ++#define R_PPC_EMB_RELSEC16 111 ++#define R_PPC_EMB_RELST_LO 112 ++#define R_PPC_EMB_RELST_HI 113 ++#define R_PPC_EMB_RELST_HA 114 ++#define R_PPC_EMB_BIT_FLD 115 ++#define R_PPC_EMB_RELSDA 116 /* 16 bit relative offset in SDA */ ++ ++/* Diab tool relocations. */ ++#define R_PPC_DIAB_SDA21_LO 180 /* like EMB_SDA21, but lower 16 bit */ ++#define R_PPC_DIAB_SDA21_HI 181 /* like EMB_SDA21, but high 16 bit */ ++#define R_PPC_DIAB_SDA21_HA 182 /* like EMB_SDA21, adjusted high 16 */ ++#define R_PPC_DIAB_RELSDA_LO 183 /* like EMB_RELSDA, but lower 16 bit */ ++#define R_PPC_DIAB_RELSDA_HI 184 /* like EMB_RELSDA, but high 16 bit */ ++#define R_PPC_DIAB_RELSDA_HA 185 /* like EMB_RELSDA, adjusted high 16 */ ++ ++/* GNU extension to support local ifunc. */ ++#define R_PPC_IRELATIVE 248 ++ ++/* GNU relocs used in PIC code sequences. */ ++#define R_PPC_REL16 249 /* half16 (sym+add-.) */ ++#define R_PPC_REL16_LO 250 /* half16 (sym+add-.)@l */ ++#define R_PPC_REL16_HI 251 /* half16 (sym+add-.)@h */ ++#define R_PPC_REL16_HA 252 /* half16 (sym+add-.)@ha */ ++ ++/* This is a phony reloc to handle any old fashioned TOC16 references ++ that may still be in object files. */ ++#define R_PPC_TOC16 255 ++ ++/* PowerPC specific values for the Dyn d_tag field. */ ++#define DT_PPC_GOT (DT_LOPROC + 0) ++#define DT_PPC_NUM 1 ++ ++/* PowerPC64 relocations defined by the ABIs */ ++#define R_PPC64_NONE R_PPC_NONE ++#define R_PPC64_ADDR32 R_PPC_ADDR32 /* 32bit absolute address */ ++#define R_PPC64_ADDR24 R_PPC_ADDR24 /* 26bit address, word aligned */ ++#define R_PPC64_ADDR16 R_PPC_ADDR16 /* 16bit absolute address */ ++#define R_PPC64_ADDR16_LO R_PPC_ADDR16_LO /* lower 16bits of address */ ++#define R_PPC64_ADDR16_HI R_PPC_ADDR16_HI /* high 16bits of address. */ ++#define R_PPC64_ADDR16_HA R_PPC_ADDR16_HA /* adjusted high 16bits. */ ++#define R_PPC64_ADDR14 R_PPC_ADDR14 /* 16bit address, word aligned */ ++#define R_PPC64_ADDR14_BRTAKEN R_PPC_ADDR14_BRTAKEN ++#define R_PPC64_ADDR14_BRNTAKEN R_PPC_ADDR14_BRNTAKEN ++#define R_PPC64_REL24 R_PPC_REL24 /* PC-rel. 26 bit, word aligned */ ++#define R_PPC64_REL14 R_PPC_REL14 /* PC relative 16 bit */ ++#define R_PPC64_REL14_BRTAKEN R_PPC_REL14_BRTAKEN ++#define R_PPC64_REL14_BRNTAKEN R_PPC_REL14_BRNTAKEN ++#define R_PPC64_GOT16 R_PPC_GOT16 ++#define R_PPC64_GOT16_LO R_PPC_GOT16_LO ++#define R_PPC64_GOT16_HI R_PPC_GOT16_HI ++#define R_PPC64_GOT16_HA R_PPC_GOT16_HA ++ ++#define R_PPC64_COPY R_PPC_COPY ++#define R_PPC64_GLOB_DAT R_PPC_GLOB_DAT ++#define R_PPC64_JMP_SLOT R_PPC_JMP_SLOT ++#define R_PPC64_RELATIVE R_PPC_RELATIVE ++ ++#define R_PPC64_UADDR32 R_PPC_UADDR32 ++#define R_PPC64_UADDR16 R_PPC_UADDR16 ++#define R_PPC64_REL32 R_PPC_REL32 ++#define R_PPC64_PLT32 R_PPC_PLT32 ++#define R_PPC64_PLTREL32 R_PPC_PLTREL32 ++#define R_PPC64_PLT16_LO R_PPC_PLT16_LO ++#define R_PPC64_PLT16_HI R_PPC_PLT16_HI ++#define R_PPC64_PLT16_HA R_PPC_PLT16_HA ++ ++#define R_PPC64_SECTOFF R_PPC_SECTOFF ++#define R_PPC64_SECTOFF_LO R_PPC_SECTOFF_LO ++#define R_PPC64_SECTOFF_HI R_PPC_SECTOFF_HI ++#define R_PPC64_SECTOFF_HA R_PPC_SECTOFF_HA ++#define R_PPC64_ADDR30 37 /* word30 (S + A - P) >> 2 */ ++#define R_PPC64_ADDR64 38 /* doubleword64 S + A */ ++#define R_PPC64_ADDR16_HIGHER 39 /* half16 #higher(S + A) */ ++#define R_PPC64_ADDR16_HIGHERA 40 /* half16 #highera(S + A) */ ++#define R_PPC64_ADDR16_HIGHEST 41 /* half16 #highest(S + A) */ ++#define R_PPC64_ADDR16_HIGHESTA 42 /* half16 #highesta(S + A) */ ++#define R_PPC64_UADDR64 43 /* doubleword64 S + A */ ++#define R_PPC64_REL64 44 /* doubleword64 S + A - P */ ++#define R_PPC64_PLT64 45 /* doubleword64 L + A */ ++#define R_PPC64_PLTREL64 46 /* doubleword64 L + A - P */ ++#define R_PPC64_TOC16 47 /* half16* S + A - .TOC */ ++#define R_PPC64_TOC16_LO 48 /* half16 #lo(S + A - .TOC.) */ ++#define R_PPC64_TOC16_HI 49 /* half16 #hi(S + A - .TOC.) */ ++#define R_PPC64_TOC16_HA 50 /* half16 #ha(S + A - .TOC.) */ ++#define R_PPC64_TOC 51 /* doubleword64 .TOC */ ++#define R_PPC64_PLTGOT16 52 /* half16* M + A */ ++#define R_PPC64_PLTGOT16_LO 53 /* half16 #lo(M + A) */ ++#define R_PPC64_PLTGOT16_HI 54 /* half16 #hi(M + A) */ ++#define R_PPC64_PLTGOT16_HA 55 /* half16 #ha(M + A) */ ++ ++#define R_PPC64_ADDR16_DS 56 /* half16ds* (S + A) >> 2 */ ++#define R_PPC64_ADDR16_LO_DS 57 /* half16ds #lo(S + A) >> 2 */ ++#define R_PPC64_GOT16_DS 58 /* half16ds* (G + A) >> 2 */ ++#define R_PPC64_GOT16_LO_DS 59 /* half16ds #lo(G + A) >> 2 */ ++#define R_PPC64_PLT16_LO_DS 60 /* half16ds #lo(L + A) >> 2 */ ++#define R_PPC64_SECTOFF_DS 61 /* half16ds* (R + A) >> 2 */ ++#define R_PPC64_SECTOFF_LO_DS 62 /* half16ds #lo(R + A) >> 2 */ ++#define R_PPC64_TOC16_DS 63 /* half16ds* (S + A - .TOC.) >> 2 */ ++#define R_PPC64_TOC16_LO_DS 64 /* half16ds #lo(S + A - .TOC.) >> 2 */ ++#define R_PPC64_PLTGOT16_DS 65 /* half16ds* (M + A) >> 2 */ ++#define R_PPC64_PLTGOT16_LO_DS 66 /* half16ds #lo(M + A) >> 2 */ ++ ++/* PowerPC64 relocations defined for the TLS access ABI. */ ++#define R_PPC64_TLS 67 /* none (sym+add)@tls */ ++#define R_PPC64_DTPMOD64 68 /* doubleword64 (sym+add)@dtpmod */ ++#define R_PPC64_TPREL16 69 /* half16* (sym+add)@tprel */ ++#define R_PPC64_TPREL16_LO 70 /* half16 (sym+add)@tprel@l */ ++#define R_PPC64_TPREL16_HI 71 /* half16 (sym+add)@tprel@h */ ++#define R_PPC64_TPREL16_HA 72 /* half16 (sym+add)@tprel@ha */ ++#define R_PPC64_TPREL64 73 /* doubleword64 (sym+add)@tprel */ ++#define R_PPC64_DTPREL16 74 /* half16* (sym+add)@dtprel */ ++#define R_PPC64_DTPREL16_LO 75 /* half16 (sym+add)@dtprel@l */ ++#define R_PPC64_DTPREL16_HI 76 /* half16 (sym+add)@dtprel@h */ ++#define R_PPC64_DTPREL16_HA 77 /* half16 (sym+add)@dtprel@ha */ ++#define R_PPC64_DTPREL64 78 /* doubleword64 (sym+add)@dtprel */ ++#define R_PPC64_GOT_TLSGD16 79 /* half16* (sym+add)@got@tlsgd */ ++#define R_PPC64_GOT_TLSGD16_LO 80 /* half16 (sym+add)@got@tlsgd@l */ ++#define R_PPC64_GOT_TLSGD16_HI 81 /* half16 (sym+add)@got@tlsgd@h */ ++#define R_PPC64_GOT_TLSGD16_HA 82 /* half16 (sym+add)@got@tlsgd@ha */ ++#define R_PPC64_GOT_TLSLD16 83 /* half16* (sym+add)@got@tlsld */ ++#define R_PPC64_GOT_TLSLD16_LO 84 /* half16 (sym+add)@got@tlsld@l */ ++#define R_PPC64_GOT_TLSLD16_HI 85 /* half16 (sym+add)@got@tlsld@h */ ++#define R_PPC64_GOT_TLSLD16_HA 86 /* half16 (sym+add)@got@tlsld@ha */ ++#define R_PPC64_GOT_TPREL16_DS 87 /* half16ds* (sym+add)@got@tprel */ ++#define R_PPC64_GOT_TPREL16_LO_DS 88 /* half16ds (sym+add)@got@tprel@l */ ++#define R_PPC64_GOT_TPREL16_HI 89 /* half16 (sym+add)@got@tprel@h */ ++#define R_PPC64_GOT_TPREL16_HA 90 /* half16 (sym+add)@got@tprel@ha */ ++#define R_PPC64_GOT_DTPREL16_DS 91 /* half16ds* (sym+add)@got@dtprel */ ++#define R_PPC64_GOT_DTPREL16_LO_DS 92 /* half16ds (sym+add)@got@dtprel@l */ ++#define R_PPC64_GOT_DTPREL16_HI 93 /* half16 (sym+add)@got@dtprel@h */ ++#define R_PPC64_GOT_DTPREL16_HA 94 /* half16 (sym+add)@got@dtprel@ha */ ++#define R_PPC64_TPREL16_DS 95 /* half16ds* (sym+add)@tprel */ ++#define R_PPC64_TPREL16_LO_DS 96 /* half16ds (sym+add)@tprel@l */ ++#define R_PPC64_TPREL16_HIGHER 97 /* half16 (sym+add)@tprel@higher */ ++#define R_PPC64_TPREL16_HIGHERA 98 /* half16 (sym+add)@tprel@highera */ ++#define R_PPC64_TPREL16_HIGHEST 99 /* half16 (sym+add)@tprel@highest */ ++#define R_PPC64_TPREL16_HIGHESTA 100 /* half16 (sym+add)@tprel@highesta */ ++#define R_PPC64_DTPREL16_DS 101 /* half16ds* (sym+add)@dtprel */ ++#define R_PPC64_DTPREL16_LO_DS 102 /* half16ds (sym+add)@dtprel@l */ ++#define R_PPC64_DTPREL16_HIGHER 103 /* half16 (sym+add)@dtprel@higher */ ++#define R_PPC64_DTPREL16_HIGHERA 104 /* half16 (sym+add)@dtprel@highera */ ++#define R_PPC64_DTPREL16_HIGHEST 105 /* half16 (sym+add)@dtprel@highest */ ++#define R_PPC64_DTPREL16_HIGHESTA 106 /* half16 (sym+add)@dtprel@highesta */ ++ ++/* GNU extension to support local ifunc. */ ++#define R_PPC64_JMP_IREL 247 ++#define R_PPC64_IRELATIVE 248 ++#define R_PPC64_REL16 249 /* half16 (sym+add-.) */ ++#define R_PPC64_REL16_LO 250 /* half16 (sym+add-.)@l */ ++#define R_PPC64_REL16_HI 251 /* half16 (sym+add-.)@h */ ++#define R_PPC64_REL16_HA 252 /* half16 (sym+add-.)@ha */ ++ ++/* PowerPC64 specific values for the Dyn d_tag field. */ ++#define DT_PPC64_GLINK (DT_LOPROC + 0) ++#define DT_PPC64_OPD (DT_LOPROC + 1) ++#define DT_PPC64_OPDSZ (DT_LOPROC + 2) ++#define DT_PPC64_NUM 3 ++ ++ ++/* ARM specific declarations */ ++ ++/* Processor specific flags for the ELF header e_flags field. */ ++#define EF_ARM_RELEXEC 0x01 ++#define EF_ARM_HASENTRY 0x02 ++#define EF_ARM_INTERWORK 0x04 ++#define EF_ARM_APCS_26 0x08 ++#define EF_ARM_APCS_FLOAT 0x10 ++#define EF_ARM_PIC 0x20 ++#define EF_ARM_ALIGN8 0x40 /* 8-bit structure alignment is in use */ ++#define EF_ARM_NEW_ABI 0x80 ++#define EF_ARM_OLD_ABI 0x100 ++#define EF_ARM_SOFT_FLOAT 0x200 ++#define EF_ARM_VFP_FLOAT 0x400 ++#define EF_ARM_MAVERICK_FLOAT 0x800 ++ ++ ++/* Other constants defined in the ARM ELF spec. version B-01. */ ++/* NB. These conflict with values defined above. */ ++#define EF_ARM_SYMSARESORTED 0x04 ++#define EF_ARM_DYNSYMSUSESEGIDX 0x08 ++#define EF_ARM_MAPSYMSFIRST 0x10 ++#define EF_ARM_EABIMASK 0XFF000000 ++ ++/* Constants defined in AAELF. */ ++#define EF_ARM_BE8 0x00800000 ++#define EF_ARM_LE8 0x00400000 ++ ++#define EF_ARM_EABI_VERSION(flags) ((flags) & EF_ARM_EABIMASK) ++#define EF_ARM_EABI_UNKNOWN 0x00000000 ++#define EF_ARM_EABI_VER1 0x01000000 ++#define EF_ARM_EABI_VER2 0x02000000 ++#define EF_ARM_EABI_VER3 0x03000000 ++#define EF_ARM_EABI_VER4 0x04000000 ++#define EF_ARM_EABI_VER5 0x05000000 ++ ++/* Additional symbol types for Thumb. */ ++#define STT_ARM_TFUNC STT_LOPROC /* A Thumb function. */ ++#define STT_ARM_16BIT STT_HIPROC /* A Thumb label. */ ++ ++/* ARM-specific values for sh_flags */ ++#define SHF_ARM_ENTRYSECT 0x10000000 /* Section contains an entry point */ ++#define SHF_ARM_COMDEF 0x80000000 /* Section may be multiply defined ++ in the input to a link step. */ ++ ++/* ARM-specific program header flags */ ++#define PF_ARM_SB 0x10000000 /* Segment contains the location ++ addressed by the static base. */ ++#define PF_ARM_PI 0x20000000 /* Position-independent segment. */ ++#define PF_ARM_ABS 0x40000000 /* Absolute segment. */ ++ ++/* Processor specific values for the Phdr p_type field. */ ++#define PT_ARM_EXIDX (PT_LOPROC + 1) /* ARM unwind segment. */ ++ ++/* Processor specific values for the Shdr sh_type field. */ ++#define SHT_ARM_EXIDX (SHT_LOPROC + 1) /* ARM unwind section. */ ++#define SHT_ARM_PREEMPTMAP (SHT_LOPROC + 2) /* Preemption details. */ ++#define SHT_ARM_ATTRIBUTES (SHT_LOPROC + 3) /* ARM attributes section. */ ++ ++ ++/* ARM relocs. */ ++ ++#define R_ARM_NONE 0 /* No reloc */ ++#define R_ARM_PC24 1 /* PC relative 26 bit branch */ ++#define R_ARM_ABS32 2 /* Direct 32 bit */ ++#define R_ARM_REL32 3 /* PC relative 32 bit */ ++#define R_ARM_PC13 4 ++#define R_ARM_ABS16 5 /* Direct 16 bit */ ++#define R_ARM_ABS12 6 /* Direct 12 bit */ ++#define R_ARM_THM_ABS5 7 ++#define R_ARM_ABS8 8 /* Direct 8 bit */ ++#define R_ARM_SBREL32 9 ++#define R_ARM_THM_PC22 10 ++#define R_ARM_THM_PC8 11 ++#define R_ARM_AMP_VCALL9 12 ++#define R_ARM_SWI24 13 /* Obsolete static relocation. */ ++#define R_ARM_TLS_DESC 13 /* Dynamic relocation. */ ++#define R_ARM_THM_SWI8 14 ++#define R_ARM_XPC25 15 ++#define R_ARM_THM_XPC22 16 ++#define R_ARM_TLS_DTPMOD32 17 /* ID of module containing symbol */ ++#define R_ARM_TLS_DTPOFF32 18 /* Offset in TLS block */ ++#define R_ARM_TLS_TPOFF32 19 /* Offset in static TLS block */ ++#define R_ARM_COPY 20 /* Copy symbol at runtime */ ++#define R_ARM_GLOB_DAT 21 /* Create GOT entry */ ++#define R_ARM_JUMP_SLOT 22 /* Create PLT entry */ ++#define R_ARM_RELATIVE 23 /* Adjust by program base */ ++#define R_ARM_GOTOFF 24 /* 32 bit offset to GOT */ ++#define R_ARM_GOTPC 25 /* 32 bit PC relative offset to GOT */ ++#define R_ARM_GOT32 26 /* 32 bit GOT entry */ ++#define R_ARM_PLT32 27 /* 32 bit PLT address */ ++#define R_ARM_ALU_PCREL_7_0 32 ++#define R_ARM_ALU_PCREL_15_8 33 ++#define R_ARM_ALU_PCREL_23_15 34 ++#define R_ARM_LDR_SBREL_11_0 35 ++#define R_ARM_ALU_SBREL_19_12 36 ++#define R_ARM_ALU_SBREL_27_20 37 ++#define R_ARM_TLS_GOTDESC 90 ++#define R_ARM_TLS_CALL 91 ++#define R_ARM_TLS_DESCSEQ 92 ++#define R_ARM_THM_TLS_CALL 93 ++#define R_ARM_GNU_VTENTRY 100 ++#define R_ARM_GNU_VTINHERIT 101 ++#define R_ARM_THM_PC11 102 /* thumb unconditional branch */ ++#define R_ARM_THM_PC9 103 /* thumb conditional branch */ ++#define R_ARM_TLS_GD32 104 /* PC-rel 32 bit for global dynamic ++ thread local data */ ++#define R_ARM_TLS_LDM32 105 /* PC-rel 32 bit for local dynamic ++ thread local data */ ++#define R_ARM_TLS_LDO32 106 /* 32 bit offset relative to TLS ++ block */ ++#define R_ARM_TLS_IE32 107 /* PC-rel 32 bit for GOT entry of ++ static TLS block offset */ ++#define R_ARM_TLS_LE32 108 /* 32 bit offset relative to static ++ TLS block */ ++#define R_ARM_THM_TLS_DESCSEQ 129 ++#define R_ARM_IRELATIVE 160 ++#define R_ARM_RXPC25 249 ++#define R_ARM_RSBREL32 250 ++#define R_ARM_THM_RPC22 251 ++#define R_ARM_RREL32 252 ++#define R_ARM_RABS22 253 ++#define R_ARM_RPC24 254 ++#define R_ARM_RBASE 255 ++/* Keep this the last entry. */ ++#define R_ARM_NUM 256 ++ ++/* IA-64 specific declarations. */ ++ ++/* Processor specific flags for the Ehdr e_flags field. */ ++#define EF_IA_64_MASKOS 0x0000000f /* os-specific flags */ ++#define EF_IA_64_ABI64 0x00000010 /* 64-bit ABI */ ++#define EF_IA_64_ARCH 0xff000000 /* arch. version mask */ ++ ++/* Processor specific values for the Phdr p_type field. */ ++#define PT_IA_64_ARCHEXT (PT_LOPROC + 0) /* arch extension bits */ ++#define PT_IA_64_UNWIND (PT_LOPROC + 1) /* ia64 unwind bits */ ++#define PT_IA_64_HP_OPT_ANOT (PT_LOOS + 0x12) ++#define PT_IA_64_HP_HSL_ANOT (PT_LOOS + 0x13) ++#define PT_IA_64_HP_STACK (PT_LOOS + 0x14) ++ ++/* Processor specific flags for the Phdr p_flags field. */ ++#define PF_IA_64_NORECOV 0x80000000 /* spec insns w/o recovery */ ++ ++/* Processor specific values for the Shdr sh_type field. */ ++#define SHT_IA_64_EXT (SHT_LOPROC + 0) /* extension bits */ ++#define SHT_IA_64_UNWIND (SHT_LOPROC + 1) /* unwind bits */ ++ ++/* Processor specific flags for the Shdr sh_flags field. */ ++#define SHF_IA_64_SHORT 0x10000000 /* section near gp */ ++#define SHF_IA_64_NORECOV 0x20000000 /* spec insns w/o recovery */ ++ ++/* Processor specific values for the Dyn d_tag field. */ ++#define DT_IA_64_PLT_RESERVE (DT_LOPROC + 0) ++#define DT_IA_64_NUM 1 ++ ++/* IA-64 relocations. */ ++#define R_IA64_NONE 0x00 /* none */ ++#define R_IA64_IMM14 0x21 /* symbol + addend, add imm14 */ ++#define R_IA64_IMM22 0x22 /* symbol + addend, add imm22 */ ++#define R_IA64_IMM64 0x23 /* symbol + addend, mov imm64 */ ++#define R_IA64_DIR32MSB 0x24 /* symbol + addend, data4 MSB */ ++#define R_IA64_DIR32LSB 0x25 /* symbol + addend, data4 LSB */ ++#define R_IA64_DIR64MSB 0x26 /* symbol + addend, data8 MSB */ ++#define R_IA64_DIR64LSB 0x27 /* symbol + addend, data8 LSB */ ++#define R_IA64_GPREL22 0x2a /* @gprel(sym + add), add imm22 */ ++#define R_IA64_GPREL64I 0x2b /* @gprel(sym + add), mov imm64 */ ++#define R_IA64_GPREL32MSB 0x2c /* @gprel(sym + add), data4 MSB */ ++#define R_IA64_GPREL32LSB 0x2d /* @gprel(sym + add), data4 LSB */ ++#define R_IA64_GPREL64MSB 0x2e /* @gprel(sym + add), data8 MSB */ ++#define R_IA64_GPREL64LSB 0x2f /* @gprel(sym + add), data8 LSB */ ++#define R_IA64_LTOFF22 0x32 /* @ltoff(sym + add), add imm22 */ ++#define R_IA64_LTOFF64I 0x33 /* @ltoff(sym + add), mov imm64 */ ++#define R_IA64_PLTOFF22 0x3a /* @pltoff(sym + add), add imm22 */ ++#define R_IA64_PLTOFF64I 0x3b /* @pltoff(sym + add), mov imm64 */ ++#define R_IA64_PLTOFF64MSB 0x3e /* @pltoff(sym + add), data8 MSB */ ++#define R_IA64_PLTOFF64LSB 0x3f /* @pltoff(sym + add), data8 LSB */ ++#define R_IA64_FPTR64I 0x43 /* @fptr(sym + add), mov imm64 */ ++#define R_IA64_FPTR32MSB 0x44 /* @fptr(sym + add), data4 MSB */ ++#define R_IA64_FPTR32LSB 0x45 /* @fptr(sym + add), data4 LSB */ ++#define R_IA64_FPTR64MSB 0x46 /* @fptr(sym + add), data8 MSB */ ++#define R_IA64_FPTR64LSB 0x47 /* @fptr(sym + add), data8 LSB */ ++#define R_IA64_PCREL60B 0x48 /* @pcrel(sym + add), brl */ ++#define R_IA64_PCREL21B 0x49 /* @pcrel(sym + add), ptb, call */ ++#define R_IA64_PCREL21M 0x4a /* @pcrel(sym + add), chk.s */ ++#define R_IA64_PCREL21F 0x4b /* @pcrel(sym + add), fchkf */ ++#define R_IA64_PCREL32MSB 0x4c /* @pcrel(sym + add), data4 MSB */ ++#define R_IA64_PCREL32LSB 0x4d /* @pcrel(sym + add), data4 LSB */ ++#define R_IA64_PCREL64MSB 0x4e /* @pcrel(sym + add), data8 MSB */ ++#define R_IA64_PCREL64LSB 0x4f /* @pcrel(sym + add), data8 LSB */ ++#define R_IA64_LTOFF_FPTR22 0x52 /* @ltoff(@fptr(s+a)), imm22 */ ++#define R_IA64_LTOFF_FPTR64I 0x53 /* @ltoff(@fptr(s+a)), imm64 */ ++#define R_IA64_LTOFF_FPTR32MSB 0x54 /* @ltoff(@fptr(s+a)), data4 MSB */ ++#define R_IA64_LTOFF_FPTR32LSB 0x55 /* @ltoff(@fptr(s+a)), data4 LSB */ ++#define R_IA64_LTOFF_FPTR64MSB 0x56 /* @ltoff(@fptr(s+a)), data8 MSB */ ++#define R_IA64_LTOFF_FPTR64LSB 0x57 /* @ltoff(@fptr(s+a)), data8 LSB */ ++#define R_IA64_SEGREL32MSB 0x5c /* @segrel(sym + add), data4 MSB */ ++#define R_IA64_SEGREL32LSB 0x5d /* @segrel(sym + add), data4 LSB */ ++#define R_IA64_SEGREL64MSB 0x5e /* @segrel(sym + add), data8 MSB */ ++#define R_IA64_SEGREL64LSB 0x5f /* @segrel(sym + add), data8 LSB */ ++#define R_IA64_SECREL32MSB 0x64 /* @secrel(sym + add), data4 MSB */ ++#define R_IA64_SECREL32LSB 0x65 /* @secrel(sym + add), data4 LSB */ ++#define R_IA64_SECREL64MSB 0x66 /* @secrel(sym + add), data8 MSB */ ++#define R_IA64_SECREL64LSB 0x67 /* @secrel(sym + add), data8 LSB */ ++#define R_IA64_REL32MSB 0x6c /* data 4 + REL */ ++#define R_IA64_REL32LSB 0x6d /* data 4 + REL */ ++#define R_IA64_REL64MSB 0x6e /* data 8 + REL */ ++#define R_IA64_REL64LSB 0x6f /* data 8 + REL */ ++#define R_IA64_LTV32MSB 0x74 /* symbol + addend, data4 MSB */ ++#define R_IA64_LTV32LSB 0x75 /* symbol + addend, data4 LSB */ ++#define R_IA64_LTV64MSB 0x76 /* symbol + addend, data8 MSB */ ++#define R_IA64_LTV64LSB 0x77 /* symbol + addend, data8 LSB */ ++#define R_IA64_PCREL21BI 0x79 /* @pcrel(sym + add), 21bit inst */ ++#define R_IA64_PCREL22 0x7a /* @pcrel(sym + add), 22bit inst */ ++#define R_IA64_PCREL64I 0x7b /* @pcrel(sym + add), 64bit inst */ ++#define R_IA64_IPLTMSB 0x80 /* dynamic reloc, imported PLT, MSB */ ++#define R_IA64_IPLTLSB 0x81 /* dynamic reloc, imported PLT, LSB */ ++#define R_IA64_COPY 0x84 /* copy relocation */ ++#define R_IA64_SUB 0x85 /* Addend and symbol difference */ ++#define R_IA64_LTOFF22X 0x86 /* LTOFF22, relaxable. */ ++#define R_IA64_LDXMOV 0x87 /* Use of LTOFF22X. */ ++#define R_IA64_TPREL14 0x91 /* @tprel(sym + add), imm14 */ ++#define R_IA64_TPREL22 0x92 /* @tprel(sym + add), imm22 */ ++#define R_IA64_TPREL64I 0x93 /* @tprel(sym + add), imm64 */ ++#define R_IA64_TPREL64MSB 0x96 /* @tprel(sym + add), data8 MSB */ ++#define R_IA64_TPREL64LSB 0x97 /* @tprel(sym + add), data8 LSB */ ++#define R_IA64_LTOFF_TPREL22 0x9a /* @ltoff(@tprel(s+a)), imm2 */ ++#define R_IA64_DTPMOD64MSB 0xa6 /* @dtpmod(sym + add), data8 MSB */ ++#define R_IA64_DTPMOD64LSB 0xa7 /* @dtpmod(sym + add), data8 LSB */ ++#define R_IA64_LTOFF_DTPMOD22 0xaa /* @ltoff(@dtpmod(sym + add)), imm22 */ ++#define R_IA64_DTPREL14 0xb1 /* @dtprel(sym + add), imm14 */ ++#define R_IA64_DTPREL22 0xb2 /* @dtprel(sym + add), imm22 */ ++#define R_IA64_DTPREL64I 0xb3 /* @dtprel(sym + add), imm64 */ ++#define R_IA64_DTPREL32MSB 0xb4 /* @dtprel(sym + add), data4 MSB */ ++#define R_IA64_DTPREL32LSB 0xb5 /* @dtprel(sym + add), data4 LSB */ ++#define R_IA64_DTPREL64MSB 0xb6 /* @dtprel(sym + add), data8 MSB */ ++#define R_IA64_DTPREL64LSB 0xb7 /* @dtprel(sym + add), data8 LSB */ ++#define R_IA64_LTOFF_DTPREL22 0xba /* @ltoff(@dtprel(s+a)), imm22 */ ++ ++/* SH specific declarations */ ++ ++/* Processor specific flags for the ELF header e_flags field. */ ++#define EF_SH_MACH_MASK 0x1f ++#define EF_SH_UNKNOWN 0x0 ++#define EF_SH1 0x1 ++#define EF_SH2 0x2 ++#define EF_SH3 0x3 ++#define EF_SH_DSP 0x4 ++#define EF_SH3_DSP 0x5 ++#define EF_SH4AL_DSP 0x6 ++#define EF_SH3E 0x8 ++#define EF_SH4 0x9 ++#define EF_SH2E 0xb ++#define EF_SH4A 0xc ++#define EF_SH2A 0xd ++#define EF_SH4_NOFPU 0x10 ++#define EF_SH4A_NOFPU 0x11 ++#define EF_SH4_NOMMU_NOFPU 0x12 ++#define EF_SH2A_NOFPU 0x13 ++#define EF_SH3_NOMMU 0x14 ++#define EF_SH2A_SH4_NOFPU 0x15 ++#define EF_SH2A_SH3_NOFPU 0x16 ++#define EF_SH2A_SH4 0x17 ++#define EF_SH2A_SH3E 0x18 ++ ++/* SH relocs. */ ++#define R_SH_NONE 0 ++#define R_SH_DIR32 1 ++#define R_SH_REL32 2 ++#define R_SH_DIR8WPN 3 ++#define R_SH_IND12W 4 ++#define R_SH_DIR8WPL 5 ++#define R_SH_DIR8WPZ 6 ++#define R_SH_DIR8BP 7 ++#define R_SH_DIR8W 8 ++#define R_SH_DIR8L 9 ++#define R_SH_SWITCH16 25 ++#define R_SH_SWITCH32 26 ++#define R_SH_USES 27 ++#define R_SH_COUNT 28 ++#define R_SH_ALIGN 29 ++#define R_SH_CODE 30 ++#define R_SH_DATA 31 ++#define R_SH_LABEL 32 ++#define R_SH_SWITCH8 33 ++#define R_SH_GNU_VTINHERIT 34 ++#define R_SH_GNU_VTENTRY 35 ++#define R_SH_TLS_GD_32 144 ++#define R_SH_TLS_LD_32 145 ++#define R_SH_TLS_LDO_32 146 ++#define R_SH_TLS_IE_32 147 ++#define R_SH_TLS_LE_32 148 ++#define R_SH_TLS_DTPMOD32 149 ++#define R_SH_TLS_DTPOFF32 150 ++#define R_SH_TLS_TPOFF32 151 ++#define R_SH_GOT32 160 ++#define R_SH_PLT32 161 ++#define R_SH_COPY 162 ++#define R_SH_GLOB_DAT 163 ++#define R_SH_JMP_SLOT 164 ++#define R_SH_RELATIVE 165 ++#define R_SH_GOTOFF 166 ++#define R_SH_GOTPC 167 ++/* Keep this the last entry. */ ++#define R_SH_NUM 256 ++ ++/* S/390 specific definitions. */ ++ ++/* Valid values for the e_flags field. */ ++ ++#define EF_S390_HIGH_GPRS 0x00000001 /* High GPRs kernel facility needed. */ ++ ++/* Additional s390 relocs */ ++ ++#define R_390_NONE 0 /* No reloc. */ ++#define R_390_8 1 /* Direct 8 bit. */ ++#define R_390_12 2 /* Direct 12 bit. */ ++#define R_390_16 3 /* Direct 16 bit. */ ++#define R_390_32 4 /* Direct 32 bit. */ ++#define R_390_PC32 5 /* PC relative 32 bit. */ ++#define R_390_GOT12 6 /* 12 bit GOT offset. */ ++#define R_390_GOT32 7 /* 32 bit GOT offset. */ ++#define R_390_PLT32 8 /* 32 bit PC relative PLT address. */ ++#define R_390_COPY 9 /* Copy symbol at runtime. */ ++#define R_390_GLOB_DAT 10 /* Create GOT entry. */ ++#define R_390_JMP_SLOT 11 /* Create PLT entry. */ ++#define R_390_RELATIVE 12 /* Adjust by program base. */ ++#define R_390_GOTOFF32 13 /* 32 bit offset to GOT. */ ++#define R_390_GOTPC 14 /* 32 bit PC relative offset to GOT. */ ++#define R_390_GOT16 15 /* 16 bit GOT offset. */ ++#define R_390_PC16 16 /* PC relative 16 bit. */ ++#define R_390_PC16DBL 17 /* PC relative 16 bit shifted by 1. */ ++#define R_390_PLT16DBL 18 /* 16 bit PC rel. PLT shifted by 1. */ ++#define R_390_PC32DBL 19 /* PC relative 32 bit shifted by 1. */ ++#define R_390_PLT32DBL 20 /* 32 bit PC rel. PLT shifted by 1. */ ++#define R_390_GOTPCDBL 21 /* 32 bit PC rel. GOT shifted by 1. */ ++#define R_390_64 22 /* Direct 64 bit. */ ++#define R_390_PC64 23 /* PC relative 64 bit. */ ++#define R_390_GOT64 24 /* 64 bit GOT offset. */ ++#define R_390_PLT64 25 /* 64 bit PC relative PLT address. */ ++#define R_390_GOTENT 26 /* 32 bit PC rel. to GOT entry >> 1. */ ++#define R_390_GOTOFF16 27 /* 16 bit offset to GOT. */ ++#define R_390_GOTOFF64 28 /* 64 bit offset to GOT. */ ++#define R_390_GOTPLT12 29 /* 12 bit offset to jump slot. */ ++#define R_390_GOTPLT16 30 /* 16 bit offset to jump slot. */ ++#define R_390_GOTPLT32 31 /* 32 bit offset to jump slot. */ ++#define R_390_GOTPLT64 32 /* 64 bit offset to jump slot. */ ++#define R_390_GOTPLTENT 33 /* 32 bit rel. offset to jump slot. */ ++#define R_390_PLTOFF16 34 /* 16 bit offset from GOT to PLT. */ ++#define R_390_PLTOFF32 35 /* 32 bit offset from GOT to PLT. */ ++#define R_390_PLTOFF64 36 /* 16 bit offset from GOT to PLT. */ ++#define R_390_TLS_LOAD 37 /* Tag for load insn in TLS code. */ ++#define R_390_TLS_GDCALL 38 /* Tag for function call in general ++ dynamic TLS code. */ ++#define R_390_TLS_LDCALL 39 /* Tag for function call in local ++ dynamic TLS code. */ ++#define R_390_TLS_GD32 40 /* Direct 32 bit for general dynamic ++ thread local data. */ ++#define R_390_TLS_GD64 41 /* Direct 64 bit for general dynamic ++ thread local data. */ ++#define R_390_TLS_GOTIE12 42 /* 12 bit GOT offset for static TLS ++ block offset. */ ++#define R_390_TLS_GOTIE32 43 /* 32 bit GOT offset for static TLS ++ block offset. */ ++#define R_390_TLS_GOTIE64 44 /* 64 bit GOT offset for static TLS ++ block offset. */ ++#define R_390_TLS_LDM32 45 /* Direct 32 bit for local dynamic ++ thread local data in LE code. */ ++#define R_390_TLS_LDM64 46 /* Direct 64 bit for local dynamic ++ thread local data in LE code. */ ++#define R_390_TLS_IE32 47 /* 32 bit address of GOT entry for ++ negated static TLS block offset. */ ++#define R_390_TLS_IE64 48 /* 64 bit address of GOT entry for ++ negated static TLS block offset. */ ++#define R_390_TLS_IEENT 49 /* 32 bit rel. offset to GOT entry for ++ negated static TLS block offset. */ ++#define R_390_TLS_LE32 50 /* 32 bit negated offset relative to ++ static TLS block. */ ++#define R_390_TLS_LE64 51 /* 64 bit negated offset relative to ++ static TLS block. */ ++#define R_390_TLS_LDO32 52 /* 32 bit offset relative to TLS ++ block. */ ++#define R_390_TLS_LDO64 53 /* 64 bit offset relative to TLS ++ block. */ ++#define R_390_TLS_DTPMOD 54 /* ID of module containing symbol. */ ++#define R_390_TLS_DTPOFF 55 /* Offset in TLS block. */ ++#define R_390_TLS_TPOFF 56 /* Negated offset in static TLS ++ block. */ ++#define R_390_20 57 /* Direct 20 bit. */ ++#define R_390_GOT20 58 /* 20 bit GOT offset. */ ++#define R_390_GOTPLT20 59 /* 20 bit offset to jump slot. */ ++#define R_390_TLS_GOTIE20 60 /* 20 bit GOT offset for static TLS ++ block offset. */ ++#define R_390_IRELATIVE 61 /* STT_GNU_IFUNC relocation. */ ++/* Keep this the last entry. */ ++#define R_390_NUM 62 ++ ++ ++/* CRIS relocations. */ ++#define R_CRIS_NONE 0 ++#define R_CRIS_8 1 ++#define R_CRIS_16 2 ++#define R_CRIS_32 3 ++#define R_CRIS_8_PCREL 4 ++#define R_CRIS_16_PCREL 5 ++#define R_CRIS_32_PCREL 6 ++#define R_CRIS_GNU_VTINHERIT 7 ++#define R_CRIS_GNU_VTENTRY 8 ++#define R_CRIS_COPY 9 ++#define R_CRIS_GLOB_DAT 10 ++#define R_CRIS_JUMP_SLOT 11 ++#define R_CRIS_RELATIVE 12 ++#define R_CRIS_16_GOT 13 ++#define R_CRIS_32_GOT 14 ++#define R_CRIS_16_GOTPLT 15 ++#define R_CRIS_32_GOTPLT 16 ++#define R_CRIS_32_GOTREL 17 ++#define R_CRIS_32_PLT_GOTREL 18 ++#define R_CRIS_32_PLT_PCREL 19 ++ ++#define R_CRIS_NUM 20 ++ ++ ++/* AMD x86-64 relocations. */ ++#define R_X86_64_NONE 0 /* No reloc */ ++#define R_X86_64_64 1 /* Direct 64 bit */ ++#define R_X86_64_PC32 2 /* PC relative 32 bit signed */ ++#define R_X86_64_GOT32 3 /* 32 bit GOT entry */ ++#define R_X86_64_PLT32 4 /* 32 bit PLT address */ ++#define R_X86_64_COPY 5 /* Copy symbol at runtime */ ++#define R_X86_64_GLOB_DAT 6 /* Create GOT entry */ ++#define R_X86_64_JUMP_SLOT 7 /* Create PLT entry */ ++#define R_X86_64_RELATIVE 8 /* Adjust by program base */ ++#define R_X86_64_GOTPCREL 9 /* 32 bit signed PC relative ++ offset to GOT */ ++#define R_X86_64_32 10 /* Direct 32 bit zero extended */ ++#define R_X86_64_32S 11 /* Direct 32 bit sign extended */ ++#define R_X86_64_16 12 /* Direct 16 bit zero extended */ ++#define R_X86_64_PC16 13 /* 16 bit sign extended pc relative */ ++#define R_X86_64_8 14 /* Direct 8 bit sign extended */ ++#define R_X86_64_PC8 15 /* 8 bit sign extended pc relative */ ++#define R_X86_64_DTPMOD64 16 /* ID of module containing symbol */ ++#define R_X86_64_DTPOFF64 17 /* Offset in module's TLS block */ ++#define R_X86_64_TPOFF64 18 /* Offset in initial TLS block */ ++#define R_X86_64_TLSGD 19 /* 32 bit signed PC relative offset ++ to two GOT entries for GD symbol */ ++#define R_X86_64_TLSLD 20 /* 32 bit signed PC relative offset ++ to two GOT entries for LD symbol */ ++#define R_X86_64_DTPOFF32 21 /* Offset in TLS block */ ++#define R_X86_64_GOTTPOFF 22 /* 32 bit signed PC relative offset ++ to GOT entry for IE symbol */ ++#define R_X86_64_TPOFF32 23 /* Offset in initial TLS block */ ++#define R_X86_64_PC64 24 /* PC relative 64 bit */ ++#define R_X86_64_GOTOFF64 25 /* 64 bit offset to GOT */ ++#define R_X86_64_GOTPC32 26 /* 32 bit signed pc relative ++ offset to GOT */ ++#define R_X86_64_GOT64 27 /* 64-bit GOT entry offset */ ++#define R_X86_64_GOTPCREL64 28 /* 64-bit PC relative offset ++ to GOT entry */ ++#define R_X86_64_GOTPC64 29 /* 64-bit PC relative offset to GOT */ ++#define R_X86_64_GOTPLT64 30 /* like GOT64, says PLT entry needed */ ++#define R_X86_64_PLTOFF64 31 /* 64-bit GOT relative offset ++ to PLT entry */ ++#define R_X86_64_SIZE32 32 /* Size of symbol plus 32-bit addend */ ++#define R_X86_64_SIZE64 33 /* Size of symbol plus 64-bit addend */ ++#define R_X86_64_GOTPC32_TLSDESC 34 /* GOT offset for TLS descriptor. */ ++#define R_X86_64_TLSDESC_CALL 35 /* Marker for call through TLS ++ descriptor. */ ++#define R_X86_64_TLSDESC 36 /* TLS descriptor. */ ++#define R_X86_64_IRELATIVE 37 /* Adjust indirectly by program base */ ++#define R_X86_64_RELATIVE64 38 /* 64-bit adjust by program base */ ++ ++#define R_X86_64_NUM 39 ++ ++ ++/* AM33 relocations. */ ++#define R_MN10300_NONE 0 /* No reloc. */ ++#define R_MN10300_32 1 /* Direct 32 bit. */ ++#define R_MN10300_16 2 /* Direct 16 bit. */ ++#define R_MN10300_8 3 /* Direct 8 bit. */ ++#define R_MN10300_PCREL32 4 /* PC-relative 32-bit. */ ++#define R_MN10300_PCREL16 5 /* PC-relative 16-bit signed. */ ++#define R_MN10300_PCREL8 6 /* PC-relative 8-bit signed. */ ++#define R_MN10300_GNU_VTINHERIT 7 /* Ancient C++ vtable garbage... */ ++#define R_MN10300_GNU_VTENTRY 8 /* ... collection annotation. */ ++#define R_MN10300_24 9 /* Direct 24 bit. */ ++#define R_MN10300_GOTPC32 10 /* 32-bit PCrel offset to GOT. */ ++#define R_MN10300_GOTPC16 11 /* 16-bit PCrel offset to GOT. */ ++#define R_MN10300_GOTOFF32 12 /* 32-bit offset from GOT. */ ++#define R_MN10300_GOTOFF24 13 /* 24-bit offset from GOT. */ ++#define R_MN10300_GOTOFF16 14 /* 16-bit offset from GOT. */ ++#define R_MN10300_PLT32 15 /* 32-bit PCrel to PLT entry. */ ++#define R_MN10300_PLT16 16 /* 16-bit PCrel to PLT entry. */ ++#define R_MN10300_GOT32 17 /* 32-bit offset to GOT entry. */ ++#define R_MN10300_GOT24 18 /* 24-bit offset to GOT entry. */ ++#define R_MN10300_GOT16 19 /* 16-bit offset to GOT entry. */ ++#define R_MN10300_COPY 20 /* Copy symbol at runtime. */ ++#define R_MN10300_GLOB_DAT 21 /* Create GOT entry. */ ++#define R_MN10300_JMP_SLOT 22 /* Create PLT entry. */ ++#define R_MN10300_RELATIVE 23 /* Adjust by program base. */ ++ ++#define R_MN10300_NUM 24 ++ ++ ++/* M32R relocs. */ ++#define R_M32R_NONE 0 /* No reloc. */ ++#define R_M32R_16 1 /* Direct 16 bit. */ ++#define R_M32R_32 2 /* Direct 32 bit. */ ++#define R_M32R_24 3 /* Direct 24 bit. */ ++#define R_M32R_10_PCREL 4 /* PC relative 10 bit shifted. */ ++#define R_M32R_18_PCREL 5 /* PC relative 18 bit shifted. */ ++#define R_M32R_26_PCREL 6 /* PC relative 26 bit shifted. */ ++#define R_M32R_HI16_ULO 7 /* High 16 bit with unsigned low. */ ++#define R_M32R_HI16_SLO 8 /* High 16 bit with signed low. */ ++#define R_M32R_LO16 9 /* Low 16 bit. */ ++#define R_M32R_SDA16 10 /* 16 bit offset in SDA. */ ++#define R_M32R_GNU_VTINHERIT 11 ++#define R_M32R_GNU_VTENTRY 12 ++/* M32R relocs use SHT_RELA. */ ++#define R_M32R_16_RELA 33 /* Direct 16 bit. */ ++#define R_M32R_32_RELA 34 /* Direct 32 bit. */ ++#define R_M32R_24_RELA 35 /* Direct 24 bit. */ ++#define R_M32R_10_PCREL_RELA 36 /* PC relative 10 bit shifted. */ ++#define R_M32R_18_PCREL_RELA 37 /* PC relative 18 bit shifted. */ ++#define R_M32R_26_PCREL_RELA 38 /* PC relative 26 bit shifted. */ ++#define R_M32R_HI16_ULO_RELA 39 /* High 16 bit with unsigned low */ ++#define R_M32R_HI16_SLO_RELA 40 /* High 16 bit with signed low */ ++#define R_M32R_LO16_RELA 41 /* Low 16 bit */ ++#define R_M32R_SDA16_RELA 42 /* 16 bit offset in SDA */ ++#define R_M32R_RELA_GNU_VTINHERIT 43 ++#define R_M32R_RELA_GNU_VTENTRY 44 ++#define R_M32R_REL32 45 /* PC relative 32 bit. */ ++ ++#define R_M32R_GOT24 48 /* 24 bit GOT entry */ ++#define R_M32R_26_PLTREL 49 /* 26 bit PC relative to PLT shifted */ ++#define R_M32R_COPY 50 /* Copy symbol at runtime */ ++#define R_M32R_GLOB_DAT 51 /* Create GOT entry */ ++#define R_M32R_JMP_SLOT 52 /* Create PLT entry */ ++#define R_M32R_RELATIVE 53 /* Adjust by program base */ ++#define R_M32R_GOTOFF 54 /* 24 bit offset to GOT */ ++#define R_M32R_GOTPC24 55 /* 24 bit PC relative offset to GOT */ ++#define R_M32R_GOT16_HI_ULO 56 /* High 16 bit GOT entry with unsigned ++ low */ ++#define R_M32R_GOT16_HI_SLO 57 /* High 16 bit GOT entry with signed ++ low */ ++#define R_M32R_GOT16_LO 58 /* Low 16 bit GOT entry */ ++#define R_M32R_GOTPC_HI_ULO 59 /* High 16 bit PC relative offset to ++ GOT with unsigned low */ ++#define R_M32R_GOTPC_HI_SLO 60 /* High 16 bit PC relative offset to ++ GOT with signed low */ ++#define R_M32R_GOTPC_LO 61 /* Low 16 bit PC relative offset to ++ GOT */ ++#define R_M32R_GOTOFF_HI_ULO 62 /* High 16 bit offset to GOT ++ with unsigned low */ ++#define R_M32R_GOTOFF_HI_SLO 63 /* High 16 bit offset to GOT ++ with signed low */ ++#define R_M32R_GOTOFF_LO 64 /* Low 16 bit offset to GOT */ ++#define R_M32R_NUM 256 /* Keep this the last entry. */ ++ ++ ++/* TILEPro relocations. */ ++#define R_TILEPRO_NONE 0 /* No reloc */ ++#define R_TILEPRO_32 1 /* Direct 32 bit */ ++#define R_TILEPRO_16 2 /* Direct 16 bit */ ++#define R_TILEPRO_8 3 /* Direct 8 bit */ ++#define R_TILEPRO_32_PCREL 4 /* PC relative 32 bit */ ++#define R_TILEPRO_16_PCREL 5 /* PC relative 16 bit */ ++#define R_TILEPRO_8_PCREL 6 /* PC relative 8 bit */ ++#define R_TILEPRO_LO16 7 /* Low 16 bit */ ++#define R_TILEPRO_HI16 8 /* High 16 bit */ ++#define R_TILEPRO_HA16 9 /* High 16 bit, adjusted */ ++#define R_TILEPRO_COPY 10 /* Copy relocation */ ++#define R_TILEPRO_GLOB_DAT 11 /* Create GOT entry */ ++#define R_TILEPRO_JMP_SLOT 12 /* Create PLT entry */ ++#define R_TILEPRO_RELATIVE 13 /* Adjust by program base */ ++#define R_TILEPRO_BROFF_X1 14 /* X1 pipe branch offset */ ++#define R_TILEPRO_JOFFLONG_X1 15 /* X1 pipe jump offset */ ++#define R_TILEPRO_JOFFLONG_X1_PLT 16 /* X1 pipe jump offset to PLT */ ++#define R_TILEPRO_IMM8_X0 17 /* X0 pipe 8-bit */ ++#define R_TILEPRO_IMM8_Y0 18 /* Y0 pipe 8-bit */ ++#define R_TILEPRO_IMM8_X1 19 /* X1 pipe 8-bit */ ++#define R_TILEPRO_IMM8_Y1 20 /* Y1 pipe 8-bit */ ++#define R_TILEPRO_MT_IMM15_X1 21 /* X1 pipe mtspr */ ++#define R_TILEPRO_MF_IMM15_X1 22 /* X1 pipe mfspr */ ++#define R_TILEPRO_IMM16_X0 23 /* X0 pipe 16-bit */ ++#define R_TILEPRO_IMM16_X1 24 /* X1 pipe 16-bit */ ++#define R_TILEPRO_IMM16_X0_LO 25 /* X0 pipe low 16-bit */ ++#define R_TILEPRO_IMM16_X1_LO 26 /* X1 pipe low 16-bit */ ++#define R_TILEPRO_IMM16_X0_HI 27 /* X0 pipe high 16-bit */ ++#define R_TILEPRO_IMM16_X1_HI 28 /* X1 pipe high 16-bit */ ++#define R_TILEPRO_IMM16_X0_HA 29 /* X0 pipe high 16-bit, adjusted */ ++#define R_TILEPRO_IMM16_X1_HA 30 /* X1 pipe high 16-bit, adjusted */ ++#define R_TILEPRO_IMM16_X0_PCREL 31 /* X0 pipe PC relative 16 bit */ ++#define R_TILEPRO_IMM16_X1_PCREL 32 /* X1 pipe PC relative 16 bit */ ++#define R_TILEPRO_IMM16_X0_LO_PCREL 33 /* X0 pipe PC relative low 16 bit */ ++#define R_TILEPRO_IMM16_X1_LO_PCREL 34 /* X1 pipe PC relative low 16 bit */ ++#define R_TILEPRO_IMM16_X0_HI_PCREL 35 /* X0 pipe PC relative high 16 bit */ ++#define R_TILEPRO_IMM16_X1_HI_PCREL 36 /* X1 pipe PC relative high 16 bit */ ++#define R_TILEPRO_IMM16_X0_HA_PCREL 37 /* X0 pipe PC relative ha() 16 bit */ ++#define R_TILEPRO_IMM16_X1_HA_PCREL 38 /* X1 pipe PC relative ha() 16 bit */ ++#define R_TILEPRO_IMM16_X0_GOT 39 /* X0 pipe 16-bit GOT offset */ ++#define R_TILEPRO_IMM16_X1_GOT 40 /* X1 pipe 16-bit GOT offset */ ++#define R_TILEPRO_IMM16_X0_GOT_LO 41 /* X0 pipe low 16-bit GOT offset */ ++#define R_TILEPRO_IMM16_X1_GOT_LO 42 /* X1 pipe low 16-bit GOT offset */ ++#define R_TILEPRO_IMM16_X0_GOT_HI 43 /* X0 pipe high 16-bit GOT offset */ ++#define R_TILEPRO_IMM16_X1_GOT_HI 44 /* X1 pipe high 16-bit GOT offset */ ++#define R_TILEPRO_IMM16_X0_GOT_HA 45 /* X0 pipe ha() 16-bit GOT offset */ ++#define R_TILEPRO_IMM16_X1_GOT_HA 46 /* X1 pipe ha() 16-bit GOT offset */ ++#define R_TILEPRO_MMSTART_X0 47 /* X0 pipe mm "start" */ ++#define R_TILEPRO_MMEND_X0 48 /* X0 pipe mm "end" */ ++#define R_TILEPRO_MMSTART_X1 49 /* X1 pipe mm "start" */ ++#define R_TILEPRO_MMEND_X1 50 /* X1 pipe mm "end" */ ++#define R_TILEPRO_SHAMT_X0 51 /* X0 pipe shift amount */ ++#define R_TILEPRO_SHAMT_X1 52 /* X1 pipe shift amount */ ++#define R_TILEPRO_SHAMT_Y0 53 /* Y0 pipe shift amount */ ++#define R_TILEPRO_SHAMT_Y1 54 /* Y1 pipe shift amount */ ++#define R_TILEPRO_DEST_IMM8_X1 55 /* X1 pipe destination 8-bit */ ++/* Relocs 56-59 are currently not defined. */ ++#define R_TILEPRO_TLS_GD_CALL 60 /* "jal" for TLS GD */ ++#define R_TILEPRO_IMM8_X0_TLS_GD_ADD 61 /* X0 pipe "addi" for TLS GD */ ++#define R_TILEPRO_IMM8_X1_TLS_GD_ADD 62 /* X1 pipe "addi" for TLS GD */ ++#define R_TILEPRO_IMM8_Y0_TLS_GD_ADD 63 /* Y0 pipe "addi" for TLS GD */ ++#define R_TILEPRO_IMM8_Y1_TLS_GD_ADD 64 /* Y1 pipe "addi" for TLS GD */ ++#define R_TILEPRO_TLS_IE_LOAD 65 /* "lw_tls" for TLS IE */ ++#define R_TILEPRO_IMM16_X0_TLS_GD 66 /* X0 pipe 16-bit TLS GD offset */ ++#define R_TILEPRO_IMM16_X1_TLS_GD 67 /* X1 pipe 16-bit TLS GD offset */ ++#define R_TILEPRO_IMM16_X0_TLS_GD_LO 68 /* X0 pipe low 16-bit TLS GD offset */ ++#define R_TILEPRO_IMM16_X1_TLS_GD_LO 69 /* X1 pipe low 16-bit TLS GD offset */ ++#define R_TILEPRO_IMM16_X0_TLS_GD_HI 70 /* X0 pipe high 16-bit TLS GD offset */ ++#define R_TILEPRO_IMM16_X1_TLS_GD_HI 71 /* X1 pipe high 16-bit TLS GD offset */ ++#define R_TILEPRO_IMM16_X0_TLS_GD_HA 72 /* X0 pipe ha() 16-bit TLS GD offset */ ++#define R_TILEPRO_IMM16_X1_TLS_GD_HA 73 /* X1 pipe ha() 16-bit TLS GD offset */ ++#define R_TILEPRO_IMM16_X0_TLS_IE 74 /* X0 pipe 16-bit TLS IE offset */ ++#define R_TILEPRO_IMM16_X1_TLS_IE 75 /* X1 pipe 16-bit TLS IE offset */ ++#define R_TILEPRO_IMM16_X0_TLS_IE_LO 76 /* X0 pipe low 16-bit TLS IE offset */ ++#define R_TILEPRO_IMM16_X1_TLS_IE_LO 77 /* X1 pipe low 16-bit TLS IE offset */ ++#define R_TILEPRO_IMM16_X0_TLS_IE_HI 78 /* X0 pipe high 16-bit TLS IE offset */ ++#define R_TILEPRO_IMM16_X1_TLS_IE_HI 79 /* X1 pipe high 16-bit TLS IE offset */ ++#define R_TILEPRO_IMM16_X0_TLS_IE_HA 80 /* X0 pipe ha() 16-bit TLS IE offset */ ++#define R_TILEPRO_IMM16_X1_TLS_IE_HA 81 /* X1 pipe ha() 16-bit TLS IE offset */ ++#define R_TILEPRO_TLS_DTPMOD32 82 /* ID of module containing symbol */ ++#define R_TILEPRO_TLS_DTPOFF32 83 /* Offset in TLS block */ ++#define R_TILEPRO_TLS_TPOFF32 84 /* Offset in static TLS block */ ++#define R_TILEPRO_IMM16_X0_TLS_LE 85 /* X0 pipe 16-bit TLS LE offset */ ++#define R_TILEPRO_IMM16_X1_TLS_LE 86 /* X1 pipe 16-bit TLS LE offset */ ++#define R_TILEPRO_IMM16_X0_TLS_LE_LO 87 /* X0 pipe low 16-bit TLS LE offset */ ++#define R_TILEPRO_IMM16_X1_TLS_LE_LO 88 /* X1 pipe low 16-bit TLS LE offset */ ++#define R_TILEPRO_IMM16_X0_TLS_LE_HI 89 /* X0 pipe high 16-bit TLS LE offset */ ++#define R_TILEPRO_IMM16_X1_TLS_LE_HI 90 /* X1 pipe high 16-bit TLS LE offset */ ++#define R_TILEPRO_IMM16_X0_TLS_LE_HA 91 /* X0 pipe ha() 16-bit TLS LE offset */ ++#define R_TILEPRO_IMM16_X1_TLS_LE_HA 92 /* X1 pipe ha() 16-bit TLS LE offset */ ++ ++#define R_TILEPRO_GNU_VTINHERIT 128 /* GNU C++ vtable hierarchy */ ++#define R_TILEPRO_GNU_VTENTRY 129 /* GNU C++ vtable member usage */ ++ ++#define R_TILEPRO_NUM 130 ++ ++ ++/* TILE-Gx relocations. */ ++#define R_TILEGX_NONE 0 /* No reloc */ ++#define R_TILEGX_64 1 /* Direct 64 bit */ ++#define R_TILEGX_32 2 /* Direct 32 bit */ ++#define R_TILEGX_16 3 /* Direct 16 bit */ ++#define R_TILEGX_8 4 /* Direct 8 bit */ ++#define R_TILEGX_64_PCREL 5 /* PC relative 64 bit */ ++#define R_TILEGX_32_PCREL 6 /* PC relative 32 bit */ ++#define R_TILEGX_16_PCREL 7 /* PC relative 16 bit */ ++#define R_TILEGX_8_PCREL 8 /* PC relative 8 bit */ ++#define R_TILEGX_HW0 9 /* hword 0 16-bit */ ++#define R_TILEGX_HW1 10 /* hword 1 16-bit */ ++#define R_TILEGX_HW2 11 /* hword 2 16-bit */ ++#define R_TILEGX_HW3 12 /* hword 3 16-bit */ ++#define R_TILEGX_HW0_LAST 13 /* last hword 0 16-bit */ ++#define R_TILEGX_HW1_LAST 14 /* last hword 1 16-bit */ ++#define R_TILEGX_HW2_LAST 15 /* last hword 2 16-bit */ ++#define R_TILEGX_COPY 16 /* Copy relocation */ ++#define R_TILEGX_GLOB_DAT 17 /* Create GOT entry */ ++#define R_TILEGX_JMP_SLOT 18 /* Create PLT entry */ ++#define R_TILEGX_RELATIVE 19 /* Adjust by program base */ ++#define R_TILEGX_BROFF_X1 20 /* X1 pipe branch offset */ ++#define R_TILEGX_JUMPOFF_X1 21 /* X1 pipe jump offset */ ++#define R_TILEGX_JUMPOFF_X1_PLT 22 /* X1 pipe jump offset to PLT */ ++#define R_TILEGX_IMM8_X0 23 /* X0 pipe 8-bit */ ++#define R_TILEGX_IMM8_Y0 24 /* Y0 pipe 8-bit */ ++#define R_TILEGX_IMM8_X1 25 /* X1 pipe 8-bit */ ++#define R_TILEGX_IMM8_Y1 26 /* Y1 pipe 8-bit */ ++#define R_TILEGX_DEST_IMM8_X1 27 /* X1 pipe destination 8-bit */ ++#define R_TILEGX_MT_IMM14_X1 28 /* X1 pipe mtspr */ ++#define R_TILEGX_MF_IMM14_X1 29 /* X1 pipe mfspr */ ++#define R_TILEGX_MMSTART_X0 30 /* X0 pipe mm "start" */ ++#define R_TILEGX_MMEND_X0 31 /* X0 pipe mm "end" */ ++#define R_TILEGX_SHAMT_X0 32 /* X0 pipe shift amount */ ++#define R_TILEGX_SHAMT_X1 33 /* X1 pipe shift amount */ ++#define R_TILEGX_SHAMT_Y0 34 /* Y0 pipe shift amount */ ++#define R_TILEGX_SHAMT_Y1 35 /* Y1 pipe shift amount */ ++#define R_TILEGX_IMM16_X0_HW0 36 /* X0 pipe hword 0 */ ++#define R_TILEGX_IMM16_X1_HW0 37 /* X1 pipe hword 0 */ ++#define R_TILEGX_IMM16_X0_HW1 38 /* X0 pipe hword 1 */ ++#define R_TILEGX_IMM16_X1_HW1 39 /* X1 pipe hword 1 */ ++#define R_TILEGX_IMM16_X0_HW2 40 /* X0 pipe hword 2 */ ++#define R_TILEGX_IMM16_X1_HW2 41 /* X1 pipe hword 2 */ ++#define R_TILEGX_IMM16_X0_HW3 42 /* X0 pipe hword 3 */ ++#define R_TILEGX_IMM16_X1_HW3 43 /* X1 pipe hword 3 */ ++#define R_TILEGX_IMM16_X0_HW0_LAST 44 /* X0 pipe last hword 0 */ ++#define R_TILEGX_IMM16_X1_HW0_LAST 45 /* X1 pipe last hword 0 */ ++#define R_TILEGX_IMM16_X0_HW1_LAST 46 /* X0 pipe last hword 1 */ ++#define R_TILEGX_IMM16_X1_HW1_LAST 47 /* X1 pipe last hword 1 */ ++#define R_TILEGX_IMM16_X0_HW2_LAST 48 /* X0 pipe last hword 2 */ ++#define R_TILEGX_IMM16_X1_HW2_LAST 49 /* X1 pipe last hword 2 */ ++#define R_TILEGX_IMM16_X0_HW0_PCREL 50 /* X0 pipe PC relative hword 0 */ ++#define R_TILEGX_IMM16_X1_HW0_PCREL 51 /* X1 pipe PC relative hword 0 */ ++#define R_TILEGX_IMM16_X0_HW1_PCREL 52 /* X0 pipe PC relative hword 1 */ ++#define R_TILEGX_IMM16_X1_HW1_PCREL 53 /* X1 pipe PC relative hword 1 */ ++#define R_TILEGX_IMM16_X0_HW2_PCREL 54 /* X0 pipe PC relative hword 2 */ ++#define R_TILEGX_IMM16_X1_HW2_PCREL 55 /* X1 pipe PC relative hword 2 */ ++#define R_TILEGX_IMM16_X0_HW3_PCREL 56 /* X0 pipe PC relative hword 3 */ ++#define R_TILEGX_IMM16_X1_HW3_PCREL 57 /* X1 pipe PC relative hword 3 */ ++#define R_TILEGX_IMM16_X0_HW0_LAST_PCREL 58 /* X0 pipe PC-rel last hword 0 */ ++#define R_TILEGX_IMM16_X1_HW0_LAST_PCREL 59 /* X1 pipe PC-rel last hword 0 */ ++#define R_TILEGX_IMM16_X0_HW1_LAST_PCREL 60 /* X0 pipe PC-rel last hword 1 */ ++#define R_TILEGX_IMM16_X1_HW1_LAST_PCREL 61 /* X1 pipe PC-rel last hword 1 */ ++#define R_TILEGX_IMM16_X0_HW2_LAST_PCREL 62 /* X0 pipe PC-rel last hword 2 */ ++#define R_TILEGX_IMM16_X1_HW2_LAST_PCREL 63 /* X1 pipe PC-rel last hword 2 */ ++#define R_TILEGX_IMM16_X0_HW0_GOT 64 /* X0 pipe hword 0 GOT offset */ ++#define R_TILEGX_IMM16_X1_HW0_GOT 65 /* X1 pipe hword 0 GOT offset */ ++/* Relocs 66-71 are currently not defined. */ ++#define R_TILEGX_IMM16_X0_HW0_LAST_GOT 72 /* X0 pipe last hword 0 GOT offset */ ++#define R_TILEGX_IMM16_X1_HW0_LAST_GOT 73 /* X1 pipe last hword 0 GOT offset */ ++#define R_TILEGX_IMM16_X0_HW1_LAST_GOT 74 /* X0 pipe last hword 1 GOT offset */ ++#define R_TILEGX_IMM16_X1_HW1_LAST_GOT 75 /* X1 pipe last hword 1 GOT offset */ ++/* Relocs 76-77 are currently not defined. */ ++#define R_TILEGX_IMM16_X0_HW0_TLS_GD 78 /* X0 pipe hword 0 TLS GD offset */ ++#define R_TILEGX_IMM16_X1_HW0_TLS_GD 79 /* X1 pipe hword 0 TLS GD offset */ ++#define R_TILEGX_IMM16_X0_HW0_TLS_LE 80 /* X0 pipe hword 0 TLS LE offset */ ++#define R_TILEGX_IMM16_X1_HW0_TLS_LE 81 /* X1 pipe hword 0 TLS LE offset */ ++#define R_TILEGX_IMM16_X0_HW0_LAST_TLS_LE 82 /* X0 pipe last hword 0 LE off */ ++#define R_TILEGX_IMM16_X1_HW0_LAST_TLS_LE 83 /* X1 pipe last hword 0 LE off */ ++#define R_TILEGX_IMM16_X0_HW1_LAST_TLS_LE 84 /* X0 pipe last hword 1 LE off */ ++#define R_TILEGX_IMM16_X1_HW1_LAST_TLS_LE 85 /* X1 pipe last hword 1 LE off */ ++#define R_TILEGX_IMM16_X0_HW0_LAST_TLS_GD 86 /* X0 pipe last hword 0 GD off */ ++#define R_TILEGX_IMM16_X1_HW0_LAST_TLS_GD 87 /* X1 pipe last hword 0 GD off */ ++#define R_TILEGX_IMM16_X0_HW1_LAST_TLS_GD 88 /* X0 pipe last hword 1 GD off */ ++#define R_TILEGX_IMM16_X1_HW1_LAST_TLS_GD 89 /* X1 pipe last hword 1 GD off */ ++/* Relocs 90-91 are currently not defined. */ ++#define R_TILEGX_IMM16_X0_HW0_TLS_IE 92 /* X0 pipe hword 0 TLS IE offset */ ++#define R_TILEGX_IMM16_X1_HW0_TLS_IE 93 /* X1 pipe hword 0 TLS IE offset */ ++/* Relocs 94-99 are currently not defined. */ ++#define R_TILEGX_IMM16_X0_HW0_LAST_TLS_IE 100 /* X0 pipe last hword 0 IE off */ ++#define R_TILEGX_IMM16_X1_HW0_LAST_TLS_IE 101 /* X1 pipe last hword 0 IE off */ ++#define R_TILEGX_IMM16_X0_HW1_LAST_TLS_IE 102 /* X0 pipe last hword 1 IE off */ ++#define R_TILEGX_IMM16_X1_HW1_LAST_TLS_IE 103 /* X1 pipe last hword 1 IE off */ ++/* Relocs 104-105 are currently not defined. */ ++#define R_TILEGX_TLS_DTPMOD64 106 /* 64-bit ID of symbol's module */ ++#define R_TILEGX_TLS_DTPOFF64 107 /* 64-bit offset in TLS block */ ++#define R_TILEGX_TLS_TPOFF64 108 /* 64-bit offset in static TLS block */ ++#define R_TILEGX_TLS_DTPMOD32 109 /* 32-bit ID of symbol's module */ ++#define R_TILEGX_TLS_DTPOFF32 110 /* 32-bit offset in TLS block */ ++#define R_TILEGX_TLS_TPOFF32 111 /* 32-bit offset in static TLS block */ ++#define R_TILEGX_TLS_GD_CALL 112 /* "jal" for TLS GD */ ++#define R_TILEGX_IMM8_X0_TLS_GD_ADD 113 /* X0 pipe "addi" for TLS GD */ ++#define R_TILEGX_IMM8_X1_TLS_GD_ADD 114 /* X1 pipe "addi" for TLS GD */ ++#define R_TILEGX_IMM8_Y0_TLS_GD_ADD 115 /* Y0 pipe "addi" for TLS GD */ ++#define R_TILEGX_IMM8_Y1_TLS_GD_ADD 116 /* Y1 pipe "addi" for TLS GD */ ++#define R_TILEGX_TLS_IE_LOAD 117 /* "ld_tls" for TLS IE */ ++#define R_TILEGX_IMM8_X0_TLS_ADD 118 /* X0 pipe "addi" for TLS GD/IE */ ++#define R_TILEGX_IMM8_X1_TLS_ADD 119 /* X1 pipe "addi" for TLS GD/IE */ ++#define R_TILEGX_IMM8_Y0_TLS_ADD 120 /* Y0 pipe "addi" for TLS GD/IE */ ++#define R_TILEGX_IMM8_Y1_TLS_ADD 121 /* Y1 pipe "addi" for TLS GD/IE */ ++ ++#define R_TILEGX_GNU_VTINHERIT 128 /* GNU C++ vtable hierarchy */ ++#define R_TILEGX_GNU_VTENTRY 129 /* GNU C++ vtable member usage */ ++ ++#define R_TILEGX_NUM 130 ++ ++#endif /* elf.h */ +--- a/scripts/mod/mk_elfconfig.c ++++ b/scripts/mod/mk_elfconfig.c +@@ -2,7 +2,11 @@ + #include <stdio.h> + #include <stdlib.h> + #include <string.h> ++#ifndef __APPLE__ + #include <elf.h> ++#else ++#include "elf.h" ++#endif + + int + main(int argc, char **argv) +--- a/scripts/mod/modpost.h ++++ b/scripts/mod/modpost.h +@@ -8,7 +8,11 @@ + #include <sys/mman.h> + #include <fcntl.h> + #include <unistd.h> ++#if !(defined(__APPLE__) || defined(__CYGWIN__)) + #include <elf.h> ++#else ++#include "elf.h" ++#endif + + #include "elfconfig.h" + diff --git a/pkgs/patches-linux-5.15/211-darwin-uuid-typedef-clash.patch b/pkgs/patches-linux-5.15/211-darwin-uuid-typedef-clash.patch new file mode 100644 index 0000000..50a6227 --- /dev/null +++ b/pkgs/patches-linux-5.15/211-darwin-uuid-typedef-clash.patch @@ -0,0 +1,22 @@ +From e44fc2af1ddc452b6659d08c16973d65c73b7d0a Mon Sep 17 00:00:00 2001 +From: Kevin Darbyshire-Bryant <ldir@darbyshire-bryant.me.uk> +Date: Wed, 5 Feb 2020 18:36:43 +0000 +Subject: [PATCH] file2alias: build on macos + +Signed-off-by: Kevin Darbyshire-Bryant <ldir@darbyshire-bryant.me.uk> +--- + scripts/mod/file2alias.c | 3 +++ + 1 file changed, 3 insertions(+) + +--- a/scripts/mod/file2alias.c ++++ b/scripts/mod/file2alias.c +@@ -38,6 +38,9 @@ typedef struct { + __u8 b[16]; + } guid_t; + ++#ifdef __APPLE__ ++#define uuid_t compat_uuid_t ++#endif + /* backwards compatibility, don't use in new code */ + typedef struct { + __u8 b[16]; diff --git a/pkgs/patches-linux-5.15/212-tools_portability.patch b/pkgs/patches-linux-5.15/212-tools_portability.patch new file mode 100644 index 0000000..b488155 --- /dev/null +++ b/pkgs/patches-linux-5.15/212-tools_portability.patch @@ -0,0 +1,110 @@ +From 48232d3d931c95953ce2ddfe7da7bb164aef6a73 Mon Sep 17 00:00:00 2001 +From: Felix Fietkau <nbd@nbd.name> +Date: Fri, 7 Jul 2017 17:03:16 +0200 +Subject: fix portability of some includes files in tools/ used on the host + +Signed-off-by: Felix Fietkau <nbd@nbd.name> +--- + tools/include/tools/be_byteshift.h | 4 ++++ + tools/include/tools/le_byteshift.h | 4 ++++ + tools/include/tools/linux_types.h | 22 ++++++++++++++++++++++ + 3 files changed, 30 insertions(+) + create mode 100644 tools/include/tools/linux_types.h + +--- a/tools/include/tools/be_byteshift.h ++++ b/tools/include/tools/be_byteshift.h +@@ -2,6 +2,10 @@ + #ifndef _TOOLS_BE_BYTESHIFT_H + #define _TOOLS_BE_BYTESHIFT_H + ++#ifndef __linux__ ++#include "linux_types.h" ++#endif ++ + #include <stdint.h> + + static inline uint16_t __get_unaligned_be16(const uint8_t *p) +--- a/tools/include/tools/le_byteshift.h ++++ b/tools/include/tools/le_byteshift.h +@@ -2,6 +2,10 @@ + #ifndef _TOOLS_LE_BYTESHIFT_H + #define _TOOLS_LE_BYTESHIFT_H + ++#ifndef __linux__ ++#include "linux_types.h" ++#endif ++ + #include <stdint.h> + + static inline uint16_t __get_unaligned_le16(const uint8_t *p) +--- /dev/null ++++ b/tools/include/tools/linux_types.h +@@ -0,0 +1,26 @@ ++#ifndef __LINUX_TYPES_H ++#define __LINUX_TYPES_H ++ ++#include <stdint.h> ++ ++typedef int8_t __s8; ++typedef uint8_t __u8; ++typedef uint8_t __be8; ++typedef uint8_t __le8; ++ ++typedef int16_t __s16; ++typedef uint16_t __u16; ++typedef uint16_t __be16; ++typedef uint16_t __le16; ++ ++typedef int32_t __s32; ++typedef uint32_t __u32; ++typedef uint32_t __be32; ++typedef uint32_t __le32; ++ ++typedef int64_t __s64; ++typedef uint64_t __u64; ++typedef uint64_t __be64; ++typedef uint64_t __le64; ++ ++#endif +--- a/tools/include/linux/types.h ++++ b/tools/include/linux/types.h +@@ -10,8 +10,12 @@ + #define __SANE_USERSPACE_TYPES__ /* For PPC64, to get LL64 types */ + #endif + ++#ifndef __linux__ ++#include <tools/linux_types.h> ++#else + #include <asm/types.h> + #include <asm/posix_types.h> ++#endif + + struct page; + struct kmem_cache; +--- a/tools/perf/pmu-events/jevents.c ++++ b/tools/perf/pmu-events/jevents.c +@@ -1,4 +1,6 @@ ++#ifdef __linux__ + #define _XOPEN_SOURCE 500 /* needed for nftw() */ ++#endif + #define _GNU_SOURCE /* needed for asprintf() */ + + /* Parse event JSON files */ +@@ -35,6 +37,7 @@ + #include <stdlib.h> + #include <errno.h> + #include <string.h> ++#include <strings.h> + #include <ctype.h> + #include <unistd.h> + #include <stdarg.h> +--- a/tools/perf/pmu-events/json.c ++++ b/tools/perf/pmu-events/json.c +@@ -38,7 +38,6 @@ + #include <unistd.h> + #include "jsmn.h" + #include "json.h" +-#include <linux/kernel.h> + + + static char *mapfile(const char *fn, size_t *size) diff --git a/pkgs/patches-linux-5.15/214-spidev_h_portability.patch b/pkgs/patches-linux-5.15/214-spidev_h_portability.patch new file mode 100644 index 0000000..db754a2 --- /dev/null +++ b/pkgs/patches-linux-5.15/214-spidev_h_portability.patch @@ -0,0 +1,24 @@ +From be9be95ff10e16a5b4ad36f903978d0cc5747024 Mon Sep 17 00:00:00 2001 +From: Felix Fietkau <nbd@nbd.name> +Date: Fri, 7 Jul 2017 17:04:08 +0200 +Subject: kernel: fix linux/spi/spidev.h portability issues with musl + +Felix will try to get this define included into musl + +lede-commit: 795e7cf60de19e7a076a46874fab7bb88b43bbff +Signed-off-by: Felix Fietkau <nbd@nbd.name> +--- + include/uapi/linux/spi/spidev.h | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/include/uapi/linux/spi/spidev.h ++++ b/include/uapi/linux/spi/spidev.h +@@ -93,7 +93,7 @@ struct spi_ioc_transfer { + + /* not all platforms use <asm-generic/ioctl.h> or _IOC_TYPECHECK() ... */ + #define SPI_MSGSIZE(N) \ +- ((((N)*(sizeof (struct spi_ioc_transfer))) < (1 << _IOC_SIZEBITS)) \ ++ ((((N)*(sizeof (struct spi_ioc_transfer))) < (1 << 13)) \ + ? ((N)*(sizeof (struct spi_ioc_transfer))) : 0) + #define SPI_IOC_MESSAGE(N) _IOW(SPI_IOC_MAGIC, 0, char[SPI_MSGSIZE(N)]) + diff --git a/pkgs/patches-linux-5.15/220-arm-gc_sections.patch b/pkgs/patches-linux-5.15/220-arm-gc_sections.patch new file mode 100644 index 0000000..0aa3eb8 --- /dev/null +++ b/pkgs/patches-linux-5.15/220-arm-gc_sections.patch @@ -0,0 +1,123 @@ +From e3d8676f5722b7622685581e06e8f53e6138e3ab Mon Sep 17 00:00:00 2001 +From: Felix Fietkau <nbd@nbd.name> +Date: Sat, 15 Jul 2017 23:42:36 +0200 +Subject: use -ffunction-sections, -fdata-sections and --gc-sections + +In combination with kernel symbol export stripping this significantly reduces +the kernel image size. Used on both ARM and MIPS architectures. + +Signed-off-by: Felix Fietkau <nbd@nbd.name> +Signed-off-by: Jonas Gorski <jogo@openwrt.org> +Signed-off-by: Gabor Juhos <juhosg@openwrt.org> +--- +--- a/arch/arm/Kconfig ++++ b/arch/arm/Kconfig +@@ -117,6 +117,7 @@ config ARM + select HAVE_UID16 + select HAVE_VIRT_CPU_ACCOUNTING_GEN + select IRQ_FORCED_THREADING ++ select HAVE_LD_DEAD_CODE_DATA_ELIMINATION + select MODULES_USE_ELF_REL + select NEED_DMA_MAP_STATE + select OF_EARLY_FLATTREE if OF +--- a/arch/arm/boot/compressed/Makefile ++++ b/arch/arm/boot/compressed/Makefile +@@ -92,6 +92,7 @@ endif + ifeq ($(CONFIG_USE_OF),y) + OBJS += $(libfdt_objs) fdt_check_mem_start.o + endif ++KBUILD_CFLAGS_KERNEL := $(patsubst -f%-sections,,$(KBUILD_CFLAGS_KERNEL)) + + # -fstack-protector-strong triggers protection checks in this code, + # but it is being used too early to link to meaningful stack_chk logic. +--- a/arch/arm/kernel/vmlinux.lds.S ++++ b/arch/arm/kernel/vmlinux.lds.S +@@ -75,7 +75,7 @@ SECTIONS + . = ALIGN(4); + __ex_table : AT(ADDR(__ex_table) - LOAD_OFFSET) { + __start___ex_table = .; +- ARM_MMU_KEEP(*(__ex_table)) ++ KEEP(*(__ex_table)) + __stop___ex_table = .; + } + +@@ -100,24 +100,24 @@ SECTIONS + } + .init.arch.info : { + __arch_info_begin = .; +- *(.arch.info.init) ++ KEEP(*(.arch.info.init)) + __arch_info_end = .; + } + .init.tagtable : { + __tagtable_begin = .; +- *(.taglist.init) ++ KEEP(*(.taglist.init)) + __tagtable_end = .; + } + #ifdef CONFIG_SMP_ON_UP + .init.smpalt : { + __smpalt_begin = .; +- *(.alt.smp.init) ++ KEEP(*(.alt.smp.init)) + __smpalt_end = .; + } + #endif + .init.pv_table : { + __pv_table_begin = .; +- *(.pv_table) ++ KEEP(*(.pv_table)) + __pv_table_end = .; + } + +--- a/arch/arm/include/asm/vmlinux.lds.h ++++ b/arch/arm/include/asm/vmlinux.lds.h +@@ -42,13 +42,13 @@ + #define PROC_INFO \ + . = ALIGN(4); \ + __proc_info_begin = .; \ +- *(.proc.info.init) \ ++ KEEP(*(.proc.info.init)) \ + __proc_info_end = .; + + #define IDMAP_TEXT \ + ALIGN_FUNCTION(); \ + __idmap_text_start = .; \ +- *(.idmap.text) \ ++ KEEP(*(.idmap.text)) \ + __idmap_text_end = .; \ + + #define ARM_DISCARD \ +@@ -109,12 +109,12 @@ + . = ALIGN(8); \ + .ARM.unwind_idx : { \ + __start_unwind_idx = .; \ +- *(.ARM.exidx*) \ ++ KEEP(*(.ARM.exidx*)) \ + __stop_unwind_idx = .; \ + } \ + .ARM.unwind_tab : { \ + __start_unwind_tab = .; \ +- *(.ARM.extab*) \ ++ KEEP(*(.ARM.extab*)) \ + __stop_unwind_tab = .; \ + } + +@@ -126,7 +126,7 @@ + __vectors_lma = .; \ + OVERLAY 0xffff0000 : NOCROSSREFS AT(__vectors_lma) { \ + .vectors { \ +- *(.vectors) \ ++ KEEP(*(.vectors)) \ + } \ + .vectors.bhb.loop8 { \ + *(.vectors.bhb.loop8) \ +@@ -144,7 +144,7 @@ + \ + __stubs_lma = .; \ + .stubs ADDR(.vectors) + 0x1000 : AT(__stubs_lma) { \ +- *(.stubs) \ ++ KEEP(*(.stubs)) \ + } \ + ARM_LMA(__stubs, .stubs); \ + . = __stubs_lma + SIZEOF(.stubs); \ diff --git a/pkgs/patches-linux-5.15/221-module_exports.patch b/pkgs/patches-linux-5.15/221-module_exports.patch new file mode 100644 index 0000000..204027d --- /dev/null +++ b/pkgs/patches-linux-5.15/221-module_exports.patch @@ -0,0 +1,126 @@ +From b14784e7883390c20ed3ff904892255404a5914b Mon Sep 17 00:00:00 2001 +From: Felix Fietkau <nbd@nbd.name> +Date: Fri, 7 Jul 2017 17:05:53 +0200 +Subject: add an optional config option for stripping all unnecessary symbol exports from the kernel image + +lede-commit: bb5a40c64b7c4f4848509fa0a6625055fc9e66cc +Signed-off-by: Felix Fietkau <nbd@nbd.name> +--- + include/asm-generic/vmlinux.lds.h | 18 +++++++++++++++--- + include/linux/export.h | 9 ++++++++- + scripts/Makefile.build | 2 +- + 3 files changed, 24 insertions(+), 5 deletions(-) + +--- a/include/asm-generic/vmlinux.lds.h ++++ b/include/asm-generic/vmlinux.lds.h +@@ -81,6 +81,16 @@ + #define RO_EXCEPTION_TABLE + #endif + ++#ifndef SYMTAB_KEEP ++#define SYMTAB_KEEP KEEP(*(SORT(___ksymtab+*))) ++#define SYMTAB_KEEP_GPL KEEP(*(SORT(___ksymtab_gpl+*))) ++#endif ++ ++#ifndef SYMTAB_DISCARD ++#define SYMTAB_DISCARD ++#define SYMTAB_DISCARD_GPL ++#endif ++ + /* Align . to a 8 byte boundary equals to maximum function alignment. */ + #define ALIGN_FUNCTION() . = ALIGN(8) + +@@ -484,14 +494,14 @@ + /* Kernel symbol table: Normal symbols */ \ + __ksymtab : AT(ADDR(__ksymtab) - LOAD_OFFSET) { \ + __start___ksymtab = .; \ +- KEEP(*(SORT(___ksymtab+*))) \ ++ SYMTAB_KEEP \ + __stop___ksymtab = .; \ + } \ + \ + /* Kernel symbol table: GPL-only symbols */ \ + __ksymtab_gpl : AT(ADDR(__ksymtab_gpl) - LOAD_OFFSET) { \ + __start___ksymtab_gpl = .; \ +- KEEP(*(SORT(___ksymtab_gpl+*))) \ ++ SYMTAB_KEEP_GPL \ + __stop___ksymtab_gpl = .; \ + } \ + \ +@@ -511,7 +521,7 @@ + \ + /* Kernel symbol table: strings */ \ + __ksymtab_strings : AT(ADDR(__ksymtab_strings) - LOAD_OFFSET) { \ +- *(__ksymtab_strings) \ ++ *(__ksymtab_strings+*) \ + } \ + \ + /* __*init sections */ \ +@@ -1018,6 +1028,8 @@ + + #define COMMON_DISCARDS \ + SANITIZER_DISCARDS \ ++ SYMTAB_DISCARD \ ++ SYMTAB_DISCARD_GPL \ + *(.discard) \ + *(.discard.*) \ + *(.modinfo) \ +--- a/include/linux/export.h ++++ b/include/linux/export.h +@@ -84,6 +84,12 @@ struct kernel_symbol { + + #else + ++#ifdef MODULE ++#define __EXPORT_SUFFIX(sym) ++#else ++#define __EXPORT_SUFFIX(sym) "+" #sym ++#endif ++ + /* + * For every exported symbol, do the following: + * +@@ -101,7 +107,7 @@ struct kernel_symbol { + extern const char __kstrtab_##sym[]; \ + extern const char __kstrtabns_##sym[]; \ + __CRC_SYMBOL(sym, sec); \ +- asm(" .section \"__ksymtab_strings\",\"aMS\",%progbits,1 \n" \ ++ asm(" .section \"__ksymtab_strings" __EXPORT_SUFFIX(sym) "\",\"aMS\",%progbits,1 \n" \ + "__kstrtab_" #sym ": \n" \ + " .asciz \"" #sym "\" \n" \ + "__kstrtabns_" #sym ": \n" \ +--- a/include/asm-generic/export.h ++++ b/include/asm-generic/export.h +@@ -26,6 +26,12 @@ + #endif + .endm + ++#ifdef MODULE ++#define __EXPORT_SUFFIX(name) ++#else ++#define __EXPORT_SUFFIX(name) + #name ++#endif ++ + /* + * note on .section use: we specify progbits since usage of the "M" (SHF_MERGE) + * section flag requires it. Use '%progbits' instead of '@progbits' since the +@@ -39,7 +45,7 @@ + __ksymtab_\name: + __put \val, __kstrtab_\name + .previous +- .section __ksymtab_strings,"aMS",%progbits,1 ++ .section __ksymtab_strings __EXPORT_SUFFIX(name),"aMS",%progbits,1 + __kstrtab_\name: + .asciz "\name" + .previous +--- a/scripts/Makefile.build ++++ b/scripts/Makefile.build +@@ -397,7 +397,7 @@ targets += $(real-dtb-y) $(lib-y) $(alwa + # Linker scripts preprocessor (.lds.S -> .lds) + # --------------------------------------------------------------------------- + quiet_cmd_cpp_lds_S = LDS $@ +- cmd_cpp_lds_S = $(CPP) $(cpp_flags) -P -U$(ARCH) \ ++ cmd_cpp_lds_S = $(CPP) $(EXTRA_LDSFLAGS) $(cpp_flags) -P -U$(ARCH) \ + -D__ASSEMBLY__ -DLINKER_SCRIPT -o $@ $< + + $(obj)/%.lds: $(src)/%.lds.S FORCE diff --git a/pkgs/patches-linux-5.15/230-openwrt_lzma_options.patch b/pkgs/patches-linux-5.15/230-openwrt_lzma_options.patch new file mode 100644 index 0000000..f9361b0 --- /dev/null +++ b/pkgs/patches-linux-5.15/230-openwrt_lzma_options.patch @@ -0,0 +1,34 @@ +From b3d00b452467f621317953d9e4c6f9ae8dcfd271 Mon Sep 17 00:00:00 2001 +From: Imre Kaloz <kaloz@openwrt.org> +Date: Fri, 7 Jul 2017 17:06:55 +0200 +Subject: use the openwrt lzma options for now + +lede-commit: 548de949f392049420a6a1feeef118b30ab8ea8c +Signed-off-by: Imre Kaloz <kaloz@openwrt.org> +--- + lib/decompress.c | 1 + + scripts/Makefile.lib | 2 +- + usr/gen_initramfs_list.sh | 10 +++++----- + 3 files changed, 7 insertions(+), 6 deletions(-) + +--- a/lib/decompress.c ++++ b/lib/decompress.c +@@ -53,6 +53,7 @@ static const struct compress_format comp + { {0x1f, 0x9e}, "gzip", gunzip }, + { {0x42, 0x5a}, "bzip2", bunzip2 }, + { {0x5d, 0x00}, "lzma", unlzma }, ++ { {0x6d, 0x00}, "lzma-openwrt", unlzma }, + { {0xfd, 0x37}, "xz", unxz }, + { {0x89, 0x4c}, "lzo", unlzo }, + { {0x02, 0x21}, "lz4", unlz4 }, +--- a/scripts/Makefile.lib ++++ b/scripts/Makefile.lib +@@ -402,7 +402,7 @@ quiet_cmd_bzip2 = BZIP2 $@ + # --------------------------------------------------------------------------- + + quiet_cmd_lzma = LZMA $@ +- cmd_lzma = { cat $(real-prereqs) | $(LZMA) -9; $(size_append); } > $@ ++ cmd_lzma = { cat $(real-prereqs) | $(LZMA) e -d20 -lc1 -lp2 -pb2 -eos -si -so; $(size_append); } > $@ + + quiet_cmd_lzo = LZO $@ + cmd_lzo = { cat $(real-prereqs) | $(KLZOP) -9; $(size_append); } > $@ diff --git a/pkgs/patches-linux-5.15/240-remove-unsane-filenames-from-deps_initramfs-list.patch b/pkgs/patches-linux-5.15/240-remove-unsane-filenames-from-deps_initramfs-list.patch new file mode 100644 index 0000000..29cfade --- /dev/null +++ b/pkgs/patches-linux-5.15/240-remove-unsane-filenames-from-deps_initramfs-list.patch @@ -0,0 +1,30 @@ +From: Gabor Juhos <juhosg@openwrt.org> +Subject: usr: sanitize deps_initramfs list + +If any filename in the intramfs dependency +list contains a colon, that causes a kernel +build error like this: + +/devel/openwrt/build_dir/linux-ar71xx_generic/linux-3.6.6/usr/Makefile:58: *** multiple target patterns. Stop. +make[5]: *** [usr] Error 2 + +Fix it by removing such filenames from the +deps_initramfs list. + +Signed-off-by: Gabor Juhos <juhosg@openwrt.org> +Signed-off-by: Felix Fietkau <nbd@nbd.name> +--- + usr/Makefile | 8 +++++--- + 1 file changed, 5 insertions(+), 3 deletions(-) + +--- a/usr/Makefile ++++ b/usr/Makefile +@@ -61,6 +61,8 @@ hostprogs := gen_init_cpio + # The dependency list is generated by gen_initramfs.sh -l + -include $(obj)/.initramfs_data.cpio.d + ++deps_initramfs := $(foreach v,$(deps_initramfs),$(if $(findstring :,$(v)),,$(v))) ++ + # do not try to update files included in initramfs + $(deps_initramfs): ; + diff --git a/pkgs/patches-linux-5.15/249-udp-tunnel-selection.patch b/pkgs/patches-linux-5.15/249-udp-tunnel-selection.patch new file mode 100644 index 0000000..5e15400 --- /dev/null +++ b/pkgs/patches-linux-5.15/249-udp-tunnel-selection.patch @@ -0,0 +1,20 @@ +From 4487708144118bfd5e1b1da7003a486951fb9c5a Mon Sep 17 00:00:00 2001 +From: OpenWrt community <openwrt-devel@lists.openwrt.org> +Date: Wed, 13 Jul 2022 13:31:51 +0200 +Subject: [PATCH] net/ipv4: add udp tunnel tristate string + +--- + net/ipv4/Kconfig | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/net/ipv4/Kconfig ++++ b/net/ipv4/Kconfig +@@ -315,7 +315,7 @@ config NET_IPVTI + on top. + + config NET_UDP_TUNNEL +- tristate ++ tristate "IP: UDP tunneling support" + select NET_IP_TUNNEL + default n + diff --git a/pkgs/patches-linux-5.15/250-netfilter_depends.patch b/pkgs/patches-linux-5.15/250-netfilter_depends.patch new file mode 100644 index 0000000..d9a2b81 --- /dev/null +++ b/pkgs/patches-linux-5.15/250-netfilter_depends.patch @@ -0,0 +1,27 @@ +From: Felix Fietkau <nbd@nbd.name> +Subject: hack: net: remove bogus netfilter dependencies + +lede-commit: 589d2a377dee27d206fc3725325309cf649e4df6 +Signed-off-by: Felix Fietkau <nbd@nbd.name> +--- + net/netfilter/Kconfig | 2 -- + 1 file changed, 2 deletions(-) + +--- a/net/netfilter/Kconfig ++++ b/net/netfilter/Kconfig +@@ -242,7 +242,6 @@ config NF_CONNTRACK_FTP + + config NF_CONNTRACK_H323 + tristate "H.323 protocol support" +- depends on IPV6 || IPV6=n + depends on NETFILTER_ADVANCED + help + H.323 is a VoIP signalling protocol from ITU-T. As one of the most +@@ -1105,7 +1104,6 @@ config NETFILTER_XT_TARGET_SECMARK + + config NETFILTER_XT_TARGET_TCPMSS + tristate '"TCPMSS" target support' +- depends on IPV6 || IPV6=n + default m if NETFILTER_ADVANCED=n + help + This option adds a `TCPMSS' target, which allows you to alter the diff --git a/pkgs/patches-linux-5.15/251-kconfig.patch b/pkgs/patches-linux-5.15/251-kconfig.patch new file mode 100644 index 0000000..5a8a820 --- /dev/null +++ b/pkgs/patches-linux-5.15/251-kconfig.patch @@ -0,0 +1,199 @@ +From da3c50704f14132f4adf80d48e9a4cd5d46e54c9 Mon Sep 17 00:00:00 2001 +From: John Crispin <john@phrozen.org> +Date: Fri, 7 Jul 2017 17:09:21 +0200 +Subject: kconfig: owrt specifc dependencies + +Signed-off-by: John Crispin <john@phrozen.org> +--- + crypto/Kconfig | 10 +++++----- + drivers/bcma/Kconfig | 1 + + drivers/ssb/Kconfig | 3 ++- + lib/Kconfig | 8 ++++---- + net/netfilter/Kconfig | 2 +- + net/wireless/Kconfig | 17 ++++++++++------- + sound/core/Kconfig | 4 ++-- + 7 files changed, 25 insertions(+), 20 deletions(-) + +--- a/crypto/Kconfig ++++ b/crypto/Kconfig +@@ -34,7 +34,7 @@ config CRYPTO_FIPS + this is. + + config CRYPTO_ALGAPI +- tristate ++ tristate "ALGAPI" + select CRYPTO_ALGAPI2 + help + This option provides the API for cryptographic algorithms. +@@ -43,7 +43,7 @@ config CRYPTO_ALGAPI2 + tristate + + config CRYPTO_AEAD +- tristate ++ tristate "AEAD" + select CRYPTO_AEAD2 + select CRYPTO_ALGAPI + +@@ -54,7 +54,7 @@ config CRYPTO_AEAD2 + select CRYPTO_RNG2 + + config CRYPTO_SKCIPHER +- tristate ++ tristate "SKCIPHER" + select CRYPTO_SKCIPHER2 + select CRYPTO_ALGAPI + +@@ -64,7 +64,7 @@ config CRYPTO_SKCIPHER2 + select CRYPTO_RNG2 + + config CRYPTO_HASH +- tristate ++ tristate "HASH" + select CRYPTO_HASH2 + select CRYPTO_ALGAPI + +@@ -73,7 +73,7 @@ config CRYPTO_HASH2 + select CRYPTO_ALGAPI2 + + config CRYPTO_RNG +- tristate ++ tristate "RNG" + select CRYPTO_RNG2 + select CRYPTO_ALGAPI + +--- a/drivers/bcma/Kconfig ++++ b/drivers/bcma/Kconfig +@@ -16,6 +16,7 @@ if BCMA + # Support for Block-I/O. SELECT this from the driver that needs it. + config BCMA_BLOCKIO + bool ++ default y + + config BCMA_HOST_PCI_POSSIBLE + bool +--- a/drivers/ssb/Kconfig ++++ b/drivers/ssb/Kconfig +@@ -29,6 +29,7 @@ config SSB_SPROM + config SSB_BLOCKIO + bool + depends on SSB ++ default y + + config SSB_PCIHOST_POSSIBLE + bool +@@ -49,7 +50,7 @@ config SSB_PCIHOST + config SSB_B43_PCI_BRIDGE + bool + depends on SSB_PCIHOST +- default n ++ default y + + config SSB_PCMCIAHOST_POSSIBLE + bool +--- a/lib/Kconfig ++++ b/lib/Kconfig +@@ -443,16 +443,16 @@ config BCH_CONST_T + # Textsearch support is select'ed if needed + # + config TEXTSEARCH +- bool ++ bool "Textsearch support" + + config TEXTSEARCH_KMP +- tristate ++ tristate "Textsearch KMP" + + config TEXTSEARCH_BM +- tristate ++ tristate "Textsearch BM" + + config TEXTSEARCH_FSM +- tristate ++ tristate "Textsearch FSM" + + config BTREE + bool +--- a/net/netfilter/Kconfig ++++ b/net/netfilter/Kconfig +@@ -11,7 +11,7 @@ config NETFILTER_INGRESS + infrastructure. + + config NETFILTER_NETLINK +- tristate ++ tristate "Netfilter NFNETLINK interface" + + config NETFILTER_FAMILY_BRIDGE + bool +--- a/net/wireless/Kconfig ++++ b/net/wireless/Kconfig +@@ -1,6 +1,6 @@ + # SPDX-License-Identifier: GPL-2.0-only + config WIRELESS_EXT +- bool ++ bool "Wireless extensions" + + config WEXT_CORE + def_bool y +@@ -12,10 +12,10 @@ config WEXT_PROC + depends on WEXT_CORE + + config WEXT_SPY +- bool ++ bool "WEXT_SPY" + + config WEXT_PRIV +- bool ++ bool "WEXT_PRIV" + + config CFG80211 + tristate "cfg80211 - wireless configuration API" +@@ -204,7 +204,7 @@ config CFG80211_WEXT_EXPORT + endif # CFG80211 + + config LIB80211 +- tristate ++ tristate "LIB80211" + default n + help + This options enables a library of common routines used +@@ -213,17 +213,17 @@ config LIB80211 + Drivers should select this themselves if needed. + + config LIB80211_CRYPT_WEP +- tristate ++ tristate "LIB80211_CRYPT_WEP" + select CRYPTO_LIB_ARC4 + + config LIB80211_CRYPT_CCMP +- tristate ++ tristate "LIB80211_CRYPT_CCMP" + select CRYPTO + select CRYPTO_AES + select CRYPTO_CCM + + config LIB80211_CRYPT_TKIP +- tristate ++ tristate "LIB80211_CRYPT_TKIP" + select CRYPTO_LIB_ARC4 + + config LIB80211_DEBUG +--- a/sound/core/Kconfig ++++ b/sound/core/Kconfig +@@ -17,7 +17,7 @@ config SND_DMAENGINE_PCM + tristate + + config SND_HWDEP +- tristate ++ tristate "Sound hardware support" + + config SND_SEQ_DEVICE + tristate +@@ -27,7 +27,7 @@ config SND_RAWMIDI + select SND_SEQ_DEVICE if SND_SEQUENCER != n + + config SND_COMPRESS_OFFLOAD +- tristate ++ tristate "Compression offloading support" + + config SND_JACK + bool diff --git a/pkgs/patches-linux-5.15/252-SATA_PMP.patch b/pkgs/patches-linux-5.15/252-SATA_PMP.patch new file mode 100644 index 0000000..6502d1d --- /dev/null +++ b/pkgs/patches-linux-5.15/252-SATA_PMP.patch @@ -0,0 +1,23 @@ +From 8c817e33be829c7249c2cfd59ff48ad5fac6a31d Mon Sep 17 00:00:00 2001 +From: Sungbo Eo <mans0n@gorani.run> +Date: Fri, 7 Jul 2017 17:09:21 +0200 +Subject: [PATCH] kconfig: solidify SATA_PMP config + +SATA_PMP option in kernel config file disappears for every kernel_oldconfig refresh. +To prevent this, SATA_HOST is now selected automatically when SATA_PMP is enabled. +This patch can be dropped if SATA_MV is ever re-added into the config. +--- + drivers/ata/Kconfig | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/drivers/ata/Kconfig ++++ b/drivers/ata/Kconfig +@@ -112,7 +112,7 @@ config SATA_ZPODD + + config SATA_PMP + bool "SATA Port Multiplier support" +- depends on SATA_HOST ++ select SATA_HOST + default y + help + This option adds support for SATA Port Multipliers diff --git a/pkgs/patches-linux-5.15/253-ksmbd-config.patch b/pkgs/patches-linux-5.15/253-ksmbd-config.patch new file mode 100644 index 0000000..a29cf3b --- /dev/null +++ b/pkgs/patches-linux-5.15/253-ksmbd-config.patch @@ -0,0 +1,32 @@ +From dcd966fa7ca63f38cf7147e1184d13d66e2ca340 Mon Sep 17 00:00:00 2001 +From: OpenWrt community <openwrt-devel@lists.openwrt.org> +Date: Wed, 13 Jul 2022 13:33:30 +0200 +Subject: [PATCH] Kconfig: add tristate for OID and ASNI string + +--- + init/Kconfig | 2 +- + lib/Kconfig | 2 +- + 2 files changed, 2 insertions(+), 2 deletions(-) + +--- a/init/Kconfig ++++ b/init/Kconfig +@@ -2384,7 +2384,7 @@ config PADATA + bool + + config ASN1 +- tristate ++ tristate "ASN1" + help + Build a simple ASN.1 grammar compiler that produces a bytecode output + that can be interpreted by the ASN.1 stream decoder and used to +--- a/lib/Kconfig ++++ b/lib/Kconfig +@@ -614,7 +614,7 @@ config LIBFDT + bool + + config OID_REGISTRY +- tristate ++ tristate "OID" + help + Enable fast lookup object identifier registry. + diff --git a/pkgs/patches-linux-5.15/259-regmap_dynamic.patch b/pkgs/patches-linux-5.15/259-regmap_dynamic.patch new file mode 100644 index 0000000..6be5875 --- /dev/null +++ b/pkgs/patches-linux-5.15/259-regmap_dynamic.patch @@ -0,0 +1,144 @@ +From 811d9e2268a62b830cfe93cd8bc929afcb8b198b Mon Sep 17 00:00:00 2001 +From: Felix Fietkau <nbd@nbd.name> +Date: Sat, 15 Jul 2017 21:12:38 +0200 +Subject: kernel: move regmap bloat out of the kernel image if it is only being used in modules + +lede-commit: 96f39119815028073583e4fca3a9c5fe9141e998 +Signed-off-by: Felix Fietkau <nbd@nbd.name> +--- + drivers/base/regmap/Kconfig | 15 ++++++++++----- + drivers/base/regmap/Makefile | 12 ++++++++---- + drivers/base/regmap/regmap.c | 3 +++ + include/linux/regmap.h | 2 +- + 4 files changed, 22 insertions(+), 10 deletions(-) + +--- a/drivers/base/regmap/Kconfig ++++ b/drivers/base/regmap/Kconfig +@@ -4,10 +4,9 @@ + # subsystems should select the appropriate symbols. + + config REGMAP +- default y if (REGMAP_I2C || REGMAP_SPI || REGMAP_SPMI || REGMAP_W1 || REGMAP_AC97 || REGMAP_MMIO || REGMAP_IRQ || REGMAP_SOUNDWIRE || REGMAP_SOUNDWIRE_MBQ || REGMAP_SCCB || REGMAP_I3C || REGMAP_SPI_AVMM || REGMAP_MDIO) + select IRQ_DOMAIN if REGMAP_IRQ + select MDIO_BUS if REGMAP_MDIO +- bool ++ tristate + + config REGCACHE_COMPRESSED + select LZO_COMPRESS +@@ -15,53 +14,67 @@ config REGCACHE_COMPRESSED + bool + + config REGMAP_AC97 ++ select REGMAP + tristate + + config REGMAP_I2C ++ select REGMAP + tristate + depends on I2C + + config REGMAP_SLIMBUS ++ select REGMAP + tristate + depends on SLIMBUS + + config REGMAP_SPI ++ select REGMAP + tristate + depends on SPI + + config REGMAP_SPMI ++ select REGMAP + tristate + depends on SPMI + + config REGMAP_W1 ++ select REGMAP + tristate + depends on W1 + + config REGMAP_MDIO ++ select REGMAP + tristate + + config REGMAP_MMIO ++ select REGMAP + tristate + + config REGMAP_IRQ ++ select REGMAP + bool + + config REGMAP_SOUNDWIRE ++ select REGMAP + tristate + depends on SOUNDWIRE + + config REGMAP_SOUNDWIRE_MBQ ++ select REGMAP + tristate + depends on SOUNDWIRE + + config REGMAP_SCCB ++ select REGMAP + tristate + depends on I2C + + config REGMAP_I3C ++ select REGMAP + tristate + depends on I3C + + config REGMAP_SPI_AVMM ++ select REGMAP + tristate + depends on SPI +--- a/drivers/base/regmap/Makefile ++++ b/drivers/base/regmap/Makefile +@@ -2,10 +2,14 @@ + # For include/trace/define_trace.h to include trace.h + CFLAGS_regmap.o := -I$(src) + +-obj-$(CONFIG_REGMAP) += regmap.o regcache.o +-obj-$(CONFIG_REGMAP) += regcache-rbtree.o regcache-flat.o +-obj-$(CONFIG_REGCACHE_COMPRESSED) += regcache-lzo.o +-obj-$(CONFIG_DEBUG_FS) += regmap-debugfs.o ++regmap-core-objs = regmap.o regcache.o regcache-rbtree.o regcache-flat.o ++ifdef CONFIG_DEBUG_FS ++regmap-core-objs += regmap-debugfs.o ++endif ++ifdef CONFIG_REGCACHE_COMPRESSED ++regmap-core-objs += regcache-lzo.o ++endif ++obj-$(CONFIG_REGMAP) += regmap-core.o + obj-$(CONFIG_REGMAP_AC97) += regmap-ac97.o + obj-$(CONFIG_REGMAP_I2C) += regmap-i2c.o + obj-$(CONFIG_REGMAP_SLIMBUS) += regmap-slimbus.o +--- a/drivers/base/regmap/regmap.c ++++ b/drivers/base/regmap/regmap.c +@@ -9,6 +9,7 @@ + #include <linux/device.h> + #include <linux/slab.h> + #include <linux/export.h> ++#include <linux/module.h> + #include <linux/mutex.h> + #include <linux/err.h> + #include <linux/property.h> +@@ -3341,3 +3342,5 @@ static int __init regmap_initcall(void) + return 0; + } + postcore_initcall(regmap_initcall); ++ ++MODULE_LICENSE("GPL"); +--- a/include/linux/regmap.h ++++ b/include/linux/regmap.h +@@ -180,7 +180,7 @@ struct reg_sequence { + __ret ?: __tmp; \ + }) + +-#ifdef CONFIG_REGMAP ++#if IS_REACHABLE(CONFIG_REGMAP) + + enum regmap_endian { + /* Unspecified -> 0 -> Backwards compatible default */ diff --git a/pkgs/patches-linux-5.15/260-crypto_test_dependencies.patch b/pkgs/patches-linux-5.15/260-crypto_test_dependencies.patch new file mode 100644 index 0000000..64daa82 --- /dev/null +++ b/pkgs/patches-linux-5.15/260-crypto_test_dependencies.patch @@ -0,0 +1,52 @@ +From fd1799b0bf5efa46dd3e6dfbbf3955564807e508 Mon Sep 17 00:00:00 2001 +From: Felix Fietkau <nbd@nbd.name> +Date: Fri, 7 Jul 2017 17:12:51 +0200 +Subject: kernel: prevent cryptomgr from pulling in useless extra dependencies for tests that are not run + +Reduces kernel size after LZMA by about 5k on MIPS + +lede-commit: 044c316167e076479a344c59905e5b435b84a77f +Signed-off-by: Felix Fietkau <nbd@nbd.name> +--- + crypto/Kconfig | 13 ++++++------- + crypto/algboss.c | 4 ++++ + 2 files changed, 10 insertions(+), 7 deletions(-) + +--- a/crypto/Kconfig ++++ b/crypto/Kconfig +@@ -121,13 +121,13 @@ config CRYPTO_MANAGER + cbc(aes). + + config CRYPTO_MANAGER2 +- def_tristate CRYPTO_MANAGER || (CRYPTO_MANAGER!=n && CRYPTO_ALGAPI=y) +- select CRYPTO_AEAD2 +- select CRYPTO_HASH2 +- select CRYPTO_SKCIPHER2 +- select CRYPTO_AKCIPHER2 +- select CRYPTO_KPP2 +- select CRYPTO_ACOMP2 ++ def_tristate CRYPTO_MANAGER || (CRYPTO_MANAGER!=n && CRYPTO_ALGAPI=y && !CRYPTO_MANAGER_DISABLE_TESTS) ++ select CRYPTO_AEAD2 if !CRYPTO_MANAGER_DISABLE_TESTS ++ select CRYPTO_HASH2 if !CRYPTO_MANAGER_DISABLE_TESTS ++ select CRYPTO_SKCIPHER2 if !CRYPTO_MANAGER_DISABLE_TESTS ++ select CRYPTO_AKCIPHER2 if !CRYPTO_MANAGER_DISABLE_TESTS ++ select CRYPTO_KPP2 if !CRYPTO_MANAGER_DISABLE_TESTS ++ select CRYPTO_ACOMP2 if !CRYPTO_MANAGER_DISABLE_TESTS + + config CRYPTO_USER + tristate "Userspace cryptographic algorithm configuration" +--- a/crypto/algboss.c ++++ b/crypto/algboss.c +@@ -211,8 +211,12 @@ static int cryptomgr_schedule_test(struc + type = alg->cra_flags; + + /* Do not test internal algorithms. */ ++#ifdef CONFIG_CRYPTO_MANAGER_DISABLE_TESTS ++ type |= CRYPTO_ALG_TESTED; ++#else + if (type & CRYPTO_ALG_INTERNAL) + type |= CRYPTO_ALG_TESTED; ++#endif + + param->type = type; + diff --git a/pkgs/patches-linux-5.15/261-enable_wilink_platform_without_drivers.patch b/pkgs/patches-linux-5.15/261-enable_wilink_platform_without_drivers.patch new file mode 100644 index 0000000..cd31f9d --- /dev/null +++ b/pkgs/patches-linux-5.15/261-enable_wilink_platform_without_drivers.patch @@ -0,0 +1,20 @@ +From: Imre Kaloz <kaloz@openwrt.org> +Subject: [PATCH] hack: net: wireless: make the wl12xx glue code available with + compat-wireless, too + +Signed-off-by: Imre Kaloz <kaloz@openwrt.org> +--- + drivers/net/wireless/ti/Kconfig | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/drivers/net/wireless/ti/Kconfig ++++ b/drivers/net/wireless/ti/Kconfig +@@ -20,7 +20,7 @@ source "drivers/net/wireless/ti/wlcore/K + + config WILINK_PLATFORM_DATA + bool "TI WiLink platform data" +- depends on WLCORE_SDIO || WL1251_SDIO ++ depends on WLCORE_SDIO || WL1251_SDIO || ARCH_OMAP2PLUS + default y + help + Small platform data bit needed to pass data to the sdio modules. diff --git a/pkgs/patches-linux-5.15/261-lib-arc4-unhide.patch b/pkgs/patches-linux-5.15/261-lib-arc4-unhide.patch new file mode 100644 index 0000000..ee923c7 --- /dev/null +++ b/pkgs/patches-linux-5.15/261-lib-arc4-unhide.patch @@ -0,0 +1,24 @@ +From 241e5d3f7b0dd3c01f8c7fa83cbc9a3882286d53 Mon Sep 17 00:00:00 2001 +From: OpenWrt community <openwrt-devel@lists.openwrt.org> +Date: Wed, 13 Jul 2022 13:35:18 +0200 +Subject: [PATCH] lib/crypto: add tristate string for ARC4 + +This makes it possible to select CONFIG_CRYPTO_LIB_ARC4 directly. We +need this to be able to compile this into the kernel and make use of it +from backports. + +--- + lib/crypto/Kconfig | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/lib/crypto/Kconfig ++++ b/lib/crypto/Kconfig +@@ -6,7 +6,7 @@ config CRYPTO_LIB_AES + tristate + + config CRYPTO_LIB_ARC4 +- tristate ++ tristate "ARC4 cipher library" + + config CRYPTO_ARCH_HAVE_LIB_BLAKE2S + bool diff --git a/pkgs/patches-linux-5.15/280-rfkill-stubs.patch b/pkgs/patches-linux-5.15/280-rfkill-stubs.patch new file mode 100644 index 0000000..7a650d1 --- /dev/null +++ b/pkgs/patches-linux-5.15/280-rfkill-stubs.patch @@ -0,0 +1,84 @@ +From 236c1acdfef5958010ac9814a9872e0a46fd78ee Mon Sep 17 00:00:00 2001 +From: John Crispin <john@phrozen.org> +Date: Fri, 7 Jul 2017 17:13:44 +0200 +Subject: rfkill: add fake rfkill support + +allow building of modules depending on RFKILL even if RFKILL is not enabled. + +Signed-off-by: John Crispin <john@phrozen.org> +--- + include/linux/rfkill.h | 2 +- + net/Makefile | 2 +- + net/rfkill/Kconfig | 14 +++++++++----- + net/rfkill/Makefile | 2 +- + 4 files changed, 12 insertions(+), 8 deletions(-) + +--- a/include/linux/rfkill.h ++++ b/include/linux/rfkill.h +@@ -64,7 +64,7 @@ struct rfkill_ops { + int (*set_block)(void *data, bool blocked); + }; + +-#if defined(CONFIG_RFKILL) || defined(CONFIG_RFKILL_MODULE) ++#if defined(CONFIG_RFKILL_FULL) || defined(CONFIG_RFKILL_FULL_MODULE) + /** + * rfkill_alloc - Allocate rfkill structure + * @name: name of the struct -- the string is not copied internally +--- a/net/Makefile ++++ b/net/Makefile +@@ -52,7 +52,7 @@ obj-$(CONFIG_TIPC) += tipc/ + obj-$(CONFIG_NETLABEL) += netlabel/ + obj-$(CONFIG_IUCV) += iucv/ + obj-$(CONFIG_SMC) += smc/ +-obj-$(CONFIG_RFKILL) += rfkill/ ++obj-$(CONFIG_RFKILL_FULL) += rfkill/ + obj-$(CONFIG_NET_9P) += 9p/ + obj-$(CONFIG_CAIF) += caif/ + obj-$(CONFIG_DCB) += dcb/ +--- a/net/rfkill/Kconfig ++++ b/net/rfkill/Kconfig +@@ -2,7 +2,11 @@ + # + # RF switch subsystem configuration + # +-menuconfig RFKILL ++config RFKILL ++ bool ++ default y ++ ++menuconfig RFKILL_FULL + tristate "RF switch subsystem support" + help + Say Y here if you want to have control over RF switches +@@ -14,19 +18,19 @@ menuconfig RFKILL + # LED trigger support + config RFKILL_LEDS + bool +- depends on RFKILL ++ depends on RFKILL_FULL + depends on LEDS_TRIGGERS = y || RFKILL = LEDS_TRIGGERS + default y + + config RFKILL_INPUT + bool "RF switch input support" if EXPERT +- depends on RFKILL ++ depends on RFKILL_FULL + depends on INPUT = y || RFKILL = INPUT + default y if !EXPERT + + config RFKILL_GPIO + tristate "GPIO RFKILL driver" +- depends on RFKILL ++ depends on RFKILL_FULL + depends on GPIOLIB || COMPILE_TEST + default n + help +--- a/net/rfkill/Makefile ++++ b/net/rfkill/Makefile +@@ -5,5 +5,5 @@ + + rfkill-y += core.o + rfkill-$(CONFIG_RFKILL_INPUT) += input.o +-obj-$(CONFIG_RFKILL) += rfkill.o ++obj-$(CONFIG_RFKILL_FULL) += rfkill.o + obj-$(CONFIG_RFKILL_GPIO) += rfkill-gpio.o diff --git a/pkgs/patches-linux-5.15/300-MIPS-r4k_cache-use-more-efficient-cache-blast.patch b/pkgs/patches-linux-5.15/300-MIPS-r4k_cache-use-more-efficient-cache-blast.patch new file mode 100644 index 0000000..f21f200 --- /dev/null +++ b/pkgs/patches-linux-5.15/300-MIPS-r4k_cache-use-more-efficient-cache-blast.patch @@ -0,0 +1,64 @@ +From: Ben Menchaca <ben.menchaca@qca.qualcomm.com> +Date: Fri, 7 Jun 2013 18:35:22 -0500 +Subject: MIPS: r4k_cache: use more efficient cache blast + +Optimize the compiler output for larger cache blast cases that are +common for DMA-based networking. + +Signed-off-by: Ben Menchaca <ben.menchaca@qca.qualcomm.com> +Signed-off-by: Felix Fietkau <nbd@nbd.name> +--- +--- a/arch/mips/include/asm/r4kcache.h ++++ b/arch/mips/include/asm/r4kcache.h +@@ -286,14 +286,46 @@ static inline void prot##extra##blast_## + unsigned long end) \ + { \ + unsigned long lsize = cpu_##desc##_line_size(); \ ++ unsigned long lsize_2 = lsize * 2; \ ++ unsigned long lsize_3 = lsize * 3; \ ++ unsigned long lsize_4 = lsize * 4; \ ++ unsigned long lsize_5 = lsize * 5; \ ++ unsigned long lsize_6 = lsize * 6; \ ++ unsigned long lsize_7 = lsize * 7; \ ++ unsigned long lsize_8 = lsize * 8; \ + unsigned long addr = start & ~(lsize - 1); \ +- unsigned long aend = (end - 1) & ~(lsize - 1); \ ++ unsigned long aend = (end + lsize - 1) & ~(lsize - 1); \ ++ int lines = (aend - addr) / lsize; \ + \ +- while (1) { \ ++ while (lines >= 8) { \ ++ prot##cache_op(hitop, addr); \ ++ prot##cache_op(hitop, addr + lsize); \ ++ prot##cache_op(hitop, addr + lsize_2); \ ++ prot##cache_op(hitop, addr + lsize_3); \ ++ prot##cache_op(hitop, addr + lsize_4); \ ++ prot##cache_op(hitop, addr + lsize_5); \ ++ prot##cache_op(hitop, addr + lsize_6); \ ++ prot##cache_op(hitop, addr + lsize_7); \ ++ addr += lsize_8; \ ++ lines -= 8; \ ++ } \ ++ \ ++ if (lines & 0x4) { \ ++ prot##cache_op(hitop, addr); \ ++ prot##cache_op(hitop, addr + lsize); \ ++ prot##cache_op(hitop, addr + lsize_2); \ ++ prot##cache_op(hitop, addr + lsize_3); \ ++ addr += lsize_4; \ ++ } \ ++ \ ++ if (lines & 0x2) { \ ++ prot##cache_op(hitop, addr); \ ++ prot##cache_op(hitop, addr + lsize); \ ++ addr += lsize_2; \ ++ } \ ++ \ ++ if (lines & 0x1) { \ + prot##cache_op(hitop, addr); \ +- if (addr == aend) \ +- break; \ +- addr += lsize; \ + } \ + } + diff --git a/pkgs/patches-linux-5.15/300-mips_expose_boot_raw.patch b/pkgs/patches-linux-5.15/300-mips_expose_boot_raw.patch new file mode 100644 index 0000000..05d0249 --- /dev/null +++ b/pkgs/patches-linux-5.15/300-mips_expose_boot_raw.patch @@ -0,0 +1,40 @@ +From: Mark Miller <mark@mirell.org> +Subject: mips: expose CONFIG_BOOT_RAW + +This exposes the CONFIG_BOOT_RAW symbol in Kconfig. This is needed on +certain Broadcom chipsets running CFE in order to load the kernel. + +Signed-off-by: Mark Miller <mark@mirell.org> +Acked-by: Rob Landley <rob@landley.net> +--- +--- a/arch/mips/Kconfig ++++ b/arch/mips/Kconfig +@@ -1100,9 +1100,6 @@ config FW_ARC + config ARCH_MAY_HAVE_PC_FDC + bool + +-config BOOT_RAW +- bool +- + config CEVT_BCM1480 + bool + +@@ -3182,6 +3179,18 @@ choice + bool "Extend builtin kernel arguments with bootloader arguments" + endchoice + ++config BOOT_RAW ++ bool "Enable the kernel to be executed from the load address" ++ default n ++ help ++ Allow the kernel to be executed from the load address for ++ bootloaders which cannot read the ELF format. This places ++ a jump to start_kernel at the load address. ++ ++ If unsure, say N. ++ ++ ++ + endmenu + + config LOCKDEP_SUPPORT diff --git a/pkgs/patches-linux-5.15/300-mvebu-Mangle-bootloader-s-kernel-arguments.patch b/pkgs/patches-linux-5.15/300-mvebu-Mangle-bootloader-s-kernel-arguments.patch new file mode 100644 index 0000000..071ea67 --- /dev/null +++ b/pkgs/patches-linux-5.15/300-mvebu-Mangle-bootloader-s-kernel-arguments.patch @@ -0,0 +1,208 @@ +From 71270226b14733a4b1f2cde58ea9265caa50b38d Mon Sep 17 00:00:00 2001 +From: Adrian Panella <ianchi74@outlook.com> +Date: Thu, 9 Mar 2017 09:37:17 +0100 +Subject: [PATCH 67/69] generic: Mangle bootloader's kernel arguments + +The command-line arguments provided by the boot loader will be +appended to a new device tree property: bootloader-args. +If there is a property "append-rootblock" in DT under /chosen +and a root= option in bootloaders command line it will be parsed +and added to DT bootargs with the form: <append-rootblock>XX. +Only command line ATAG will be processed, the rest of the ATAGs +sent by bootloader will be ignored. +This is usefull in dual boot systems, to get the current root partition +without afecting the rest of the system. + +Signed-off-by: Adrian Panella <ianchi74@outlook.com> + +This patch has been modified to be mvebu specific. The original patch +did not pass the bootloader cmdline on if no append-rootblock stanza +was found, resulting in blank cmdline and failure to boot. + +Signed-off-by: Michael Gray <michael.gray@lantisproject.com> +--- + arch/arm/Kconfig | 11 ++++ + arch/arm/boot/compressed/atags_to_fdt.c | 85 ++++++++++++++++++++++++- + init/main.c | 16 +++++ + 3 files changed, 111 insertions(+), 1 deletion(-) + +--- a/arch/arm/Kconfig ++++ b/arch/arm/Kconfig +@@ -1727,6 +1727,17 @@ config ARM_ATAG_DTB_COMPAT_CMDLINE_EXTEN + The command-line arguments provided by the boot loader will be + appended to the the device tree bootargs property. + ++config ARM_ATAG_DTB_COMPAT_CMDLINE_MANGLE ++ bool "Append rootblock parsing bootloader's kernel arguments" ++ help ++ The command-line arguments provided by the boot loader will be ++ appended to a new device tree property: bootloader-args. ++ If there is a property "append-rootblock" in DT under /chosen ++ and a root= option in bootloaders command line it will be parsed ++ and added to DT bootargs with the form: <append-rootblock>XX. ++ Only command line ATAG will be processed, the rest of the ATAGs ++ sent by bootloader will be ignored. ++ + endchoice + + config CMDLINE +--- a/arch/arm/boot/compressed/atags_to_fdt.c ++++ b/arch/arm/boot/compressed/atags_to_fdt.c +@@ -5,6 +5,8 @@ + + #if defined(CONFIG_ARM_ATAG_DTB_COMPAT_CMDLINE_EXTEND) + #define do_extend_cmdline 1 ++#elif defined(CONFIG_ARM_ATAG_DTB_COMPAT_CMDLINE_MANGLE) ++#define do_extend_cmdline 1 + #else + #define do_extend_cmdline 0 + #endif +@@ -69,6 +71,72 @@ static uint32_t get_cell_size(const void + return cell_size; + } + ++#if defined(CONFIG_ARM_ATAG_DTB_COMPAT_CMDLINE_MANGLE) ++ ++static char *append_rootblock(char *dest, const char *str, int len, void *fdt) ++{ ++ char *ptr, *end; ++ char *root="root="; ++ int i, l; ++ const char *rootblock; ++ ++ //ARM doesn't have __HAVE_ARCH_STRSTR, so search manually ++ ptr = str - 1; ++ ++ do { ++ //first find an 'r' at the begining or after a space ++ do { ++ ptr++; ++ ptr = strchr(ptr, 'r'); ++ if (!ptr) ++ goto no_append; ++ ++ } while (ptr != str && *(ptr-1) != ' '); ++ ++ //then check for the rest ++ for(i = 1; i <= 4; i++) ++ if(*(ptr+i) != *(root+i)) break; ++ ++ } while (i != 5); ++ ++ end = strchr(ptr, ' '); ++ end = end ? (end - 1) : (strchr(ptr, 0) - 1); ++ ++ //find partition number (assumes format root=/dev/mtdXX | /dev/mtdblockXX | yy:XX ) ++ for( i = 0; end >= ptr && *end >= '0' && *end <= '9'; end--, i++); ++ ptr = end + 1; ++ ++ /* if append-rootblock property is set use it to append to command line */ ++ rootblock = getprop(fdt, "/chosen", "append-rootblock", &l); ++ if (rootblock == NULL) ++ goto no_append; ++ ++ if (*dest != ' ') { ++ *dest = ' '; ++ dest++; ++ len++; ++ } ++ ++ if (len + l + i <= COMMAND_LINE_SIZE) { ++ memcpy(dest, rootblock, l); ++ dest += l - 1; ++ memcpy(dest, ptr, i); ++ dest += i; ++ } ++ ++ return dest; ++ ++no_append: ++ len = strlen(str); ++ if (len + 1 < COMMAND_LINE_SIZE) { ++ memcpy(dest, str, len); ++ dest += len; ++ } ++ ++ return dest; ++} ++#endif ++ + static void merge_fdt_bootargs(void *fdt, const char *fdt_cmdline) + { + char cmdline[COMMAND_LINE_SIZE]; +@@ -88,12 +156,21 @@ static void merge_fdt_bootargs(void *fdt + + /* and append the ATAG_CMDLINE */ + if (fdt_cmdline) { ++ ++#if defined(CONFIG_ARM_ATAG_DTB_COMPAT_CMDLINE_MANGLE) ++ //save original bootloader args ++ //and append ubi.mtd with root partition number to current cmdline ++ setprop_string(fdt, "/chosen", "bootloader-args", fdt_cmdline); ++ ptr = append_rootblock(ptr, fdt_cmdline, len, fdt); ++ ++#else + len = strlen(fdt_cmdline); + if (ptr - cmdline + len + 2 < COMMAND_LINE_SIZE) { + *ptr++ = ' '; + memcpy(ptr, fdt_cmdline, len); + ptr += len; + } ++#endif + } + *ptr = '\0'; + +@@ -168,7 +245,9 @@ int atags_to_fdt(void *atag_list, void * + else + setprop_string(fdt, "/chosen", "bootargs", + atag->u.cmdline.cmdline); +- } else if (atag->hdr.tag == ATAG_MEM) { ++ } ++#ifndef CONFIG_ARM_ATAG_DTB_COMPAT_CMDLINE_MANGLE ++ else if (atag->hdr.tag == ATAG_MEM) { + if (memcount >= sizeof(mem_reg_property)/4) + continue; + if (!atag->u.mem.size) +@@ -212,6 +291,10 @@ int atags_to_fdt(void *atag_list, void * + setprop(fdt, "/memory", "reg", mem_reg_property, + 4 * memcount * memsize); + } ++#else ++ ++ } ++#endif + + return fdt_pack(fdt); + } +--- a/init/main.c ++++ b/init/main.c +@@ -113,6 +113,10 @@ + + #include <kunit/test.h> + ++#if defined(CONFIG_ARM_ATAG_DTB_COMPAT_CMDLINE_MANGLE) ++#include <linux/of.h> ++#endif ++ + static int kernel_init(void *); + + extern void init_IRQ(void); +@@ -990,6 +994,18 @@ asmlinkage __visible void __init __no_sa + page_alloc_init(); + + pr_notice("Kernel command line: %s\n", saved_command_line); ++ ++#if defined(CONFIG_ARM_ATAG_DTB_COMPAT_CMDLINE_MANGLE) ++ //Show bootloader's original command line for reference ++ if(of_chosen) { ++ const char *prop = of_get_property(of_chosen, "bootloader-args", NULL); ++ if(prop) ++ pr_notice("Bootloader command line (ignored): %s\n", prop); ++ else ++ pr_notice("Bootloader command line not present\n"); ++ } ++#endif ++ + /* parameters may set static keys */ + jump_label_init(); + parse_early_param(); diff --git a/pkgs/patches-linux-5.15/300-v5.18-pinctrl-qcom-Return--EINVAL-for-setting-affinity-if-no-IRQ-parent.patch b/pkgs/patches-linux-5.15/300-v5.18-pinctrl-qcom-Return--EINVAL-for-setting-affinity-if-no-IRQ-parent.patch new file mode 100644 index 0000000..18a8752 --- /dev/null +++ b/pkgs/patches-linux-5.15/300-v5.18-pinctrl-qcom-Return--EINVAL-for-setting-affinity-if-no-IRQ-parent.patch @@ -0,0 +1,48 @@ +From: Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org> +To: linus.walleij@linaro.org +Cc: bjorn.andersson@linaro.org, dianders@chromium.org, + linux-arm-msm@vger.kernel.org, linux-gpio@vger.kernel.org, + linux-kernel@vger.kernel.org, + Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org> +Subject: [PATCH] pinctrl: qcom: Return -EINVAL for setting affinity if no IRQ + parent +Date: Thu, 13 Jan 2022 21:56:17 +0530 +Message-Id: <20220113162617.131697-1-manivannan.sadhasivam@linaro.org> + +The MSM GPIO IRQ controller relies on the parent IRQ controller to set the +CPU affinity for the IRQ. And this is only valid if there is any wakeup +parent available and defined in DT. + +For the case of no parent IRQ controller defined in DT, +msm_gpio_irq_set_affinity() and msm_gpio_irq_set_vcpu_affinity() should +return -EINVAL instead of 0 as the affinity can't be set. + +Otherwise, below warning will be printed by genirq: + +genirq: irq_chip msmgpio did not update eff. affinity mask of irq 70 + +Signed-off-by: Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org> +--- + drivers/pinctrl/qcom/pinctrl-msm.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +--- a/drivers/pinctrl/qcom/pinctrl-msm.c ++++ b/drivers/pinctrl/qcom/pinctrl-msm.c +@@ -1157,7 +1157,7 @@ static int msm_gpio_irq_set_affinity(str + if (d->parent_data && test_bit(d->hwirq, pctrl->skip_wake_irqs)) + return irq_chip_set_affinity_parent(d, dest, force); + +- return 0; ++ return -EINVAL; + } + + static int msm_gpio_irq_set_vcpu_affinity(struct irq_data *d, void *vcpu_info) +@@ -1168,7 +1168,7 @@ static int msm_gpio_irq_set_vcpu_affinit + if (d->parent_data && test_bit(d->hwirq, pctrl->skip_wake_irqs)) + return irq_chip_set_vcpu_affinity_parent(d, vcpu_info); + +- return 0; ++ return -EINVAL; + } + + static void msm_gpio_irq_handler(struct irq_desc *desc) diff --git a/pkgs/patches-linux-5.15/301-mips_image_cmdline_hack.patch b/pkgs/patches-linux-5.15/301-mips_image_cmdline_hack.patch new file mode 100644 index 0000000..15e233a --- /dev/null +++ b/pkgs/patches-linux-5.15/301-mips_image_cmdline_hack.patch @@ -0,0 +1,38 @@ +From: John Crispin <john@phrozen.org> +Subject: hack: kernel: add generic image_cmdline hack to MIPS targets + +lede-commit: d59f5b3a987a48508257a0ddbaeadc7909f9f976 +Signed-off-by: Gabor Juhos <juhosg@openwrt.org> +--- + arch/mips/Kconfig | 4 ++++ + arch/mips/kernel/head.S | 6 ++++++ + 2 files changed, 10 insertions(+) + +--- a/arch/mips/Kconfig ++++ b/arch/mips/Kconfig +@@ -1180,6 +1180,10 @@ config MIPS_MSC + config SYNC_R4K + bool + ++config IMAGE_CMDLINE_HACK ++ bool "OpenWrt specific image command line hack" ++ default n ++ + config NO_IOPORT_MAP + def_bool n + +--- a/arch/mips/kernel/head.S ++++ b/arch/mips/kernel/head.S +@@ -79,6 +79,12 @@ FEXPORT(__kernel_entry) + j kernel_entry + #endif /* CONFIG_BOOT_RAW */ + ++#ifdef CONFIG_IMAGE_CMDLINE_HACK ++ .ascii "CMDLINE:" ++EXPORT(__image_cmdline) ++ .fill 0x400 ++#endif /* CONFIG_IMAGE_CMDLINE_HACK */ ++ + __REF + + NESTED(kernel_entry, 16, sp) # kernel entry point diff --git a/pkgs/patches-linux-5.15/301-mvebu-armada-38x-enable-libata-leds.patch b/pkgs/patches-linux-5.15/301-mvebu-armada-38x-enable-libata-leds.patch new file mode 100644 index 0000000..615caac --- /dev/null +++ b/pkgs/patches-linux-5.15/301-mvebu-armada-38x-enable-libata-leds.patch @@ -0,0 +1,10 @@ +--- a/arch/arm/mach-mvebu/Kconfig ++++ b/arch/arm/mach-mvebu/Kconfig +@@ -67,6 +67,7 @@ config MACH_ARMADA_38X + select HAVE_ARM_TWD if SMP + select MACH_MVEBU_V7 + select PINCTRL_ARMADA_38X ++ select ARCH_WANT_LIBATA_LEDS + help + Say 'Y' here if you want your kernel to support boards based + on the Marvell Armada 380/385 SoC with device tree. diff --git a/pkgs/patches-linux-5.15/302-add_powertables.patch b/pkgs/patches-linux-5.15/302-add_powertables.patch new file mode 100644 index 0000000..93ad5de --- /dev/null +++ b/pkgs/patches-linux-5.15/302-add_powertables.patch @@ -0,0 +1,770 @@ +--- a/arch/arm/boot/dts/armada-385-linksys.dtsi ++++ b/arch/arm/boot/dts/armada-385-linksys.dtsi +@@ -214,11 +214,19 @@ + &pcie1 { + /* Marvell 88W8864, 5GHz-only */ + status = "okay"; ++ ++ mwlwifi { ++ marvell,2ghz = <0>; ++ }; + }; + + &pcie2 { + /* Marvell 88W8864, 2GHz-only */ + status = "okay"; ++ ++ mwlwifi { ++ marvell,5ghz = <0>; ++ }; + }; + + &pinctrl { +--- a/arch/arm/boot/dts/armada-385-linksys-caiman.dts ++++ b/arch/arm/boot/dts/armada-385-linksys-caiman.dts +@@ -142,3 +142,205 @@ + }; + }; + }; ++ ++&pcie1 { ++ mwlwifi { ++ marvell,chainmask = <2 2>; ++ marvell,powertable { ++ AU = ++ <36 0 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0 0xf>, ++ <40 0 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0 0xf>, ++ <44 0 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0 0xf>, ++ <48 0 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0 0xf>, ++ <52 0 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0 0xf>, ++ <56 0 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0 0xf>, ++ <60 0 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0 0xf>, ++ <64 0 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0 0xf>, ++ <100 0 0x17 0x17 0x17 0x17 0x17 0x17 0x17 0x15 0x17 0x17 0x17 0x14 0x17 0x17 0x17 0x14 0 0xf>, ++ <104 0 0x17 0x17 0x17 0x17 0x17 0x17 0x17 0x15 0x17 0x17 0x17 0x14 0x17 0x17 0x17 0x14 0 0xf>, ++ <108 0 0x17 0x17 0x17 0x17 0x17 0x17 0x17 0x15 0x17 0x17 0x17 0x14 0x17 0x17 0x17 0x14 0 0xf>, ++ <112 0 0x17 0x17 0x17 0x17 0x17 0x17 0x17 0x15 0x17 0x17 0x17 0x14 0x17 0x17 0x17 0x14 0 0xf>, ++ <116 0 0x17 0x17 0x17 0x17 0x17 0x17 0x17 0x15 0x17 0x17 0x17 0x14 0x17 0x17 0x17 0x14 0 0xf>, ++ <120 0 0x17 0x17 0x17 0x17 0x17 0x17 0x17 0x15 0x17 0x17 0x17 0x14 0x17 0x17 0x17 0x14 0 0xf>, ++ <124 0 0x17 0x17 0x17 0x17 0x17 0x17 0x17 0x15 0x17 0x17 0x17 0x14 0x17 0x17 0x17 0x14 0 0xf>, ++ <128 0 0x17 0x17 0x17 0x17 0x17 0x17 0x17 0x15 0x17 0x17 0x17 0x14 0x17 0x17 0x17 0x14 0 0xf>, ++ <132 0 0x17 0x17 0x17 0x17 0x17 0x17 0x17 0x15 0x17 0x17 0x17 0x14 0x17 0x17 0x17 0x14 0 0xf>, ++ <136 0 0x17 0x17 0x17 0x17 0x17 0x17 0x17 0x15 0x17 0x17 0x17 0x14 0x17 0x17 0x17 0x14 0 0xf>, ++ <140 0 0x17 0x17 0x17 0x17 0x17 0x17 0x17 0x15 0x17 0x17 0x17 0x14 0x17 0x17 0x17 0x14 0 0xf>, ++ <149 0 0x1a 0x1a 0x18 0x17 0x1a 0x1a 0x17 0x15 0x1a 0x1a 0x17 0x14 0x1a 0x1a 0x17 0x14 0 0xf>, ++ <153 0 0x1a 0x1a 0x18 0x17 0x1a 0x1a 0x17 0x15 0x1a 0x1a 0x17 0x14 0x1a 0x1a 0x17 0x14 0 0xf>, ++ <157 0 0x1a 0x1a 0x18 0x17 0x1a 0x1a 0x17 0x15 0x1a 0x1a 0x17 0x14 0x1a 0x1a 0x17 0x14 0 0xf>, ++ <161 0 0x1a 0x1a 0x18 0x17 0x1a 0x1a 0x17 0x15 0x1a 0x1a 0x17 0x14 0x1a 0x1a 0x17 0x14 0 0xf>, ++ <165 0 0x1a 0x1a 0x18 0x17 0x1a 0x1a 0x17 0x15 0x1a 0x1a 0x17 0x14 0x1a 0x1a 0x17 0x14 0 0xf>; ++ CA = ++ <36 0 0xf 0xf 0xf 0xf 0xf 0xf 0xf 0xf 0xf 0xf 0xf 0xf 0xf 0xf 0xf 0xf 0 0xf>, ++ <40 0 0xf 0xf 0xf 0xf 0xf 0xf 0xf 0xf 0xf 0xf 0xf 0xf 0xf 0xf 0xf 0xf 0 0xf>, ++ <44 0 0xf 0xf 0xf 0xf 0xf 0xf 0xf 0xf 0xf 0xf 0xf 0xf 0xf 0xf 0xf 0xf 0 0xf>, ++ <48 0 0xf 0xf 0xf 0xf 0xf 0xf 0xf 0xf 0xf 0xf 0xf 0xf 0xf 0xf 0xf 0xf 0 0xf>, ++ <52 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x12 0x12 0x12 0x12 0x10 0x10 0x10 0x10 0 0xf>, ++ <56 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x12 0x12 0x12 0x12 0x10 0x10 0x10 0x10 0 0xf>, ++ <60 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x12 0x12 0x12 0x12 0x10 0x10 0x10 0x10 0 0xf>, ++ <64 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x12 0x12 0x12 0x12 0x10 0x10 0x10 0x10 0 0xf>, ++ <100 0 0x10 0x10 0x10 0x10 0x11 0x11 0x11 0x11 0x12 0x12 0x12 0x12 0x10 0x10 0x10 0x10 0 0xf>, ++ <104 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x12 0x12 0x12 0x12 0x10 0x10 0x10 0x10 0 0xf>, ++ <108 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0 0xf>, ++ <112 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0 0xf>, ++ <116 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0 0xf>, ++ <120 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0 0xf>, ++ <124 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0 0xf>, ++ <128 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0 0xf>, ++ <132 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0 0xf>, ++ <136 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0 0xf>, ++ <140 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0 0xf>, ++ <149 0 0x1a 0x1a 0x18 0x17 0x19 0x19 0x17 0x15 0x18 0x18 0x17 0x14 0x15 0x15 0x15 0x14 0 0xf>, ++ <153 0 0x1a 0x1a 0x18 0x17 0x1a 0x1a 0x17 0x15 0x1a 0x1a 0x17 0x14 0x15 0x15 0x15 0x14 0 0xf>, ++ <157 0 0x1a 0x1a 0x18 0x17 0x1a 0x1a 0x17 0x15 0x1a 0x1a 0x17 0x14 0x15 0x15 0x15 0x14 0 0xf>, ++ <161 0 0x1a 0x1a 0x18 0x17 0x1a 0x1a 0x17 0x15 0x1a 0x1a 0x17 0x14 0x15 0x15 0x15 0x14 0 0xf>, ++ <165 0 0x1a 0x1a 0x18 0x17 0x1a 0x1a 0x17 0x15 0x1a 0x1a 0x17 0x14 0x15 0x15 0x15 0x14 0 0xf>; ++ CN = ++ <36 0 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0 0xf>, ++ <40 0 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0 0xf>, ++ <44 0 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0 0xf>, ++ <48 0 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0 0xf>, ++ <52 0 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0 0xf>, ++ <56 0 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0 0xf>, ++ <60 0 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0 0xf>, ++ <64 0 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0 0xf>, ++ <100 0 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0 0xf>, ++ <104 0 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0 0xf>, ++ <108 0 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0 0xf>, ++ <112 0 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0 0xf>, ++ <116 0 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0 0xf>, ++ <120 0 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0 0xf>, ++ <124 0 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0 0xf>, ++ <128 0 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0 0xf>, ++ <132 0 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0 0xf>, ++ <136 0 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0 0xf>, ++ <140 0 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0 0xf>, ++ <149 0 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x14 0x14 0x14 0x14 0x11 0x11 0x11 0x11 0 0xf>, ++ <153 0 0x1a 0x1a 0x18 0x17 0x1a 0x1a 0x17 0x15 0x16 0x16 0x16 0x14 0x11 0x11 0x11 0x11 0 0xf>, ++ <157 0 0x1a 0x1a 0x18 0x17 0x1a 0x1a 0x17 0x15 0x16 0x16 0x16 0x14 0x11 0x11 0x11 0x11 0 0xf>, ++ <161 0 0x1a 0x1a 0x18 0x17 0x1a 0x1a 0x17 0x15 0x16 0x16 0x16 0x14 0x11 0x11 0x11 0x11 0 0xf>, ++ <165 0 0x15 0x15 0x15 0x15 0x16 0x16 0x16 0x15 0x16 0x16 0x16 0x14 0x11 0x11 0x11 0x11 0 0xf>; ++ ETSI = ++ <36 0 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0 0xf>, ++ <40 0 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0 0xf>, ++ <44 0 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0 0xf>, ++ <48 0 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0 0xf>, ++ <52 0 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0 0xf>, ++ <56 0 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0 0xf>, ++ <60 0 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0 0xf>, ++ <64 0 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0 0xf>, ++ <100 0 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0 0xf>, ++ <104 0 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0 0xf>, ++ <108 0 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0 0xf>, ++ <112 0 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0 0xf>, ++ <116 0 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0 0xf>, ++ <120 0 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0 0xf>, ++ <124 0 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0 0xf>, ++ <128 0 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0 0xf>, ++ <132 0 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0 0xf>, ++ <136 0 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0 0xf>, ++ <140 0 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0 0xf>, ++ <149 0 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0 0xf>; ++ FCC = ++ <36 0 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x12 0x12 0x12 0x12 0x10 0x10 0x10 0x10 0 0xf>, ++ <40 0 0x19 0x19 0x18 0x17 0x19 0x19 0x17 0x15 0x17 0x17 0x17 0x14 0x10 0x10 0x10 0x10 0 0xf>, ++ <44 0 0x19 0x19 0x18 0x17 0x19 0x19 0x17 0x15 0x17 0x17 0x17 0x14 0x10 0x10 0x10 0x10 0 0xf>, ++ <48 0 0x1a 0x1a 0x18 0x17 0x1a 0x1a 0x17 0x15 0x17 0x17 0x17 0x14 0x10 0x10 0x10 0x10 0 0xf>, ++ <52 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x12 0x12 0x12 0x12 0x10 0x10 0x10 0x10 0 0xf>, ++ <56 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x12 0x12 0x12 0x12 0x10 0x10 0x10 0x10 0 0xf>, ++ <60 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x12 0x12 0x12 0x12 0x10 0x10 0x10 0x10 0 0xf>, ++ <64 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x12 0x12 0x12 0x12 0x10 0x10 0x10 0x10 0 0xf>, ++ <100 0 0x10 0x10 0x10 0x10 0x11 0x11 0x11 0x11 0x12 0x12 0x12 0x12 0x10 0x10 0x10 0x10 0 0xf>, ++ <104 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x12 0x12 0x12 0x12 0x10 0x10 0x10 0x10 0 0xf>, ++ <108 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0 0xf>, ++ <112 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0 0xf>, ++ <116 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0 0xf>, ++ <120 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0 0xf>, ++ <124 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0 0xf>, ++ <128 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0 0xf>, ++ <132 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0 0xf>, ++ <136 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0 0xf>, ++ <140 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0 0xf>, ++ <149 0 0x1a 0x1a 0x18 0x17 0x19 0x19 0x17 0x15 0x18 0x18 0x17 0x14 0x15 0x15 0x15 0x14 0 0xf>, ++ <153 0 0x1a 0x1a 0x18 0x17 0x1a 0x1a 0x17 0x15 0x1a 0x1a 0x17 0x14 0x15 0x15 0x15 0x14 0 0xf>, ++ <157 0 0x1a 0x1a 0x18 0x17 0x1a 0x1a 0x17 0x15 0x1a 0x1a 0x17 0x14 0x15 0x15 0x15 0x14 0 0xf>, ++ <161 0 0x1a 0x1a 0x18 0x17 0x1a 0x1a 0x17 0x15 0x1a 0x1a 0x17 0x14 0x15 0x15 0x15 0x14 0 0xf>, ++ <165 0 0x1a 0x1a 0x18 0x17 0x1a 0x1a 0x17 0x15 0x1a 0x1a 0x17 0x14 0x15 0x15 0x15 0x14 0 0xf>; ++ }; ++ }; ++}; ++ ++&pcie2 { ++ mwlwifi { ++ marvell,chainmask = <2 2>; ++ marvell,powertable { ++ AU = ++ <1 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0x0 0x0 0x0 0x0 0 0xf>, ++ <2 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0x0 0x0 0x0 0x0 0 0xf>, ++ <3 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0x0 0x0 0x0 0x0 0 0xf>, ++ <4 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0x0 0x0 0x0 0x0 0 0xf>, ++ <5 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0x0 0x0 0x0 0x0 0 0xf>, ++ <6 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0x0 0x0 0x0 0x0 0 0xf>, ++ <7 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0x0 0x0 0x0 0x0 0 0xf>, ++ <8 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0x0 0x0 0x0 0x0 0 0xf>, ++ <9 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0x0 0x0 0x0 0x0 0 0xf>, ++ <10 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0x0 0x0 0x0 0x0 0 0xf>, ++ <11 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0x0 0x0 0x0 0x0 0 0xf>; ++ CA = ++ <1 0 0x19 0x14 0x14 0x14 0x13 0x13 0x13 0x13 0x10 0x10 0x10 0x10 0x00 0x00 0x00 0x00 0 0xf>, ++ <2 0 0x1a 0x19 0x18 0x17 0x19 0x19 0x17 0x16 0x14 0x14 0x14 0x14 0x00 0x00 0x00 0x00 0 0xf>, ++ <3 0 0x1a 0x19 0x18 0x17 0x19 0x19 0x17 0x16 0x14 0x14 0x14 0x14 0x00 0x00 0x00 0x00 0 0xf>, ++ <4 0 0x1a 0x19 0x18 0x17 0x19 0x19 0x17 0x16 0x14 0x14 0x14 0x14 0x00 0x00 0x00 0x00 0 0xf>, ++ <5 0 0x1a 0x19 0x18 0x17 0x19 0x19 0x17 0x16 0x14 0x14 0x14 0x14 0x00 0x00 0x00 0x00 0 0xf>, ++ <6 0 0x1a 0x19 0x18 0x17 0x19 0x19 0x17 0x16 0x14 0x14 0x14 0x14 0x00 0x00 0x00 0x00 0 0xf>, ++ <7 0 0x1a 0x19 0x18 0x17 0x19 0x19 0x17 0x16 0x14 0x14 0x14 0x14 0x00 0x00 0x00 0x00 0 0xf>, ++ <8 0 0x1a 0x19 0x18 0x17 0x19 0x19 0x17 0x16 0x14 0x14 0x14 0x14 0x00 0x00 0x00 0x00 0 0xf>, ++ <9 0 0x1a 0x19 0x18 0x17 0x19 0x19 0x17 0x16 0x14 0x14 0x14 0x14 0x00 0x00 0x00 0x00 0 0xf>, ++ <10 0 0x1a 0x19 0x18 0x17 0x19 0x19 0x17 0x16 0x14 0x14 0x14 0x14 0x00 0x00 0x00 0x00 0 0xf>, ++ <11 0 0x19 0x15 0x15 0x15 0x14 0x14 0x14 0x14 0x13 0x13 0x13 0x13 0x00 0x00 0x00 0x00 0 0xf>; ++ CN = ++ <1 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0x0 0x0 0x0 0x0 0 0xf>, ++ <2 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0x0 0x0 0x0 0x0 0 0xf>, ++ <3 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0x0 0x0 0x0 0x0 0 0xf>, ++ <4 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0x0 0x0 0x0 0x0 0 0xf>, ++ <5 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0x0 0x0 0x0 0x0 0 0xf>, ++ <6 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0x0 0x0 0x0 0x0 0 0xf>, ++ <7 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0x0 0x0 0x0 0x0 0 0xf>, ++ <8 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0x0 0x0 0x0 0x0 0 0xf>, ++ <9 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0x0 0x0 0x0 0x0 0 0xf>, ++ <10 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0x0 0x0 0x0 0x0 0 0xf>, ++ <11 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0x0 0x0 0x0 0x0 0 0xf>, ++ <12 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0x0 0x0 0x0 0x0 0 0xf>, ++ <13 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0x0 0x0 0x0 0x0 0 0xf>, ++ <14 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0x0 0x0 0x0 0x0 0 0xf>; ++ ETSI = ++ <1 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0x0 0x0 0x0 0x0 0 0xf>, ++ <2 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0x0 0x0 0x0 0x0 0 0xf>, ++ <3 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0x0 0x0 0x0 0x0 0 0xf>, ++ <4 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0x0 0x0 0x0 0x0 0 0xf>, ++ <5 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0x0 0x0 0x0 0x0 0 0xf>, ++ <6 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0x0 0x0 0x0 0x0 0 0xf>, ++ <7 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0x0 0x0 0x0 0x0 0 0xf>, ++ <8 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0x0 0x0 0x0 0x0 0 0xf>, ++ <9 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0x0 0x0 0x0 0x0 0 0xf>, ++ <10 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0x0 0x0 0x0 0x0 0 0xf>, ++ <11 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0x0 0x0 0x0 0x0 0 0xf>, ++ <12 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0x0 0x0 0x0 0x0 0 0xf>, ++ <13 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0x0 0x0 0x0 0x0 0 0xf>; ++ FCC = ++ <1 0 0x19 0x14 0x14 0x14 0x13 0x13 0x13 0x13 0x10 0x10 0x10 0x10 0x0 0x0 0x0 0x0 0 0xf>, ++ <2 0 0x1a 0x19 0x18 0x17 0x19 0x19 0x17 0x16 0x14 0x14 0x14 0x14 0x0 0x0 0x0 0x0 0 0xf>, ++ <3 0 0x1a 0x19 0x18 0x17 0x19 0x19 0x17 0x16 0x14 0x14 0x14 0x14 0x0 0x0 0x0 0x0 0 0xf>, ++ <4 0 0x1a 0x19 0x18 0x17 0x19 0x19 0x17 0x16 0x14 0x14 0x14 0x14 0x0 0x0 0x0 0x0 0 0xf>, ++ <5 0 0x1a 0x19 0x18 0x17 0x19 0x19 0x17 0x16 0x14 0x14 0x14 0x14 0x0 0x0 0x0 0x0 0 0xf>, ++ <6 0 0x1a 0x19 0x18 0x17 0x19 0x19 0x17 0x16 0x14 0x14 0x14 0x14 0x0 0x0 0x0 0x0 0 0xf>, ++ <7 0 0x1a 0x19 0x18 0x17 0x19 0x19 0x17 0x16 0x14 0x14 0x14 0x14 0x0 0x0 0x0 0x0 0 0xf>, ++ <8 0 0x1a 0x19 0x18 0x17 0x19 0x19 0x17 0x16 0x14 0x14 0x14 0x14 0x0 0x0 0x0 0x0 0 0xf>, ++ <9 0 0x1a 0x19 0x18 0x17 0x19 0x19 0x17 0x16 0x14 0x14 0x14 0x14 0x0 0x0 0x0 0x0 0 0xf>, ++ <10 0 0x1a 0x19 0x18 0x17 0x19 0x19 0x17 0x16 0x14 0x14 0x14 0x14 0x0 0x0 0x0 0x0 0 0xf>, ++ <11 0 0x19 0x15 0x15 0x15 0x14 0x14 0x14 0x14 0x13 0x13 0x13 0x13 0x0 0x0 0x0 0x0 0 0xf>; ++ }; ++ }; ++}; +--- a/arch/arm/boot/dts/armada-385-linksys-cobra.dts ++++ b/arch/arm/boot/dts/armada-385-linksys-cobra.dts +@@ -142,3 +142,205 @@ + }; + }; + }; ++ ++&pcie1 { ++ mwlwifi { ++ marvell,chainmask = <4 4>; ++ marvell,powertable { ++ AU = ++ <36 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>, ++ <40 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>, ++ <44 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>, ++ <48 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>, ++ <52 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>, ++ <56 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>, ++ <60 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>, ++ <64 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>, ++ <100 0 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0 0xf>, ++ <104 0 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0 0xf>, ++ <108 0 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0 0xf>, ++ <112 0 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0 0xf>, ++ <116 0 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0 0xf>, ++ <120 0 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0 0xf>, ++ <124 0 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0 0xf>, ++ <128 0 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0 0xf>, ++ <132 0 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0 0xf>, ++ <136 0 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0 0xf>, ++ <140 0 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0 0xf>, ++ <149 0 0x19 0x19 0x19 0x17 0x19 0x19 0x16 0x15 0x19 0x19 0x16 0x15 0x19 0x19 0x16 0x15 0 0xf>, ++ <153 0 0x19 0x19 0x19 0x17 0x19 0x19 0x16 0x15 0x19 0x19 0x16 0x15 0x19 0x19 0x16 0x15 0 0xf>, ++ <157 0 0x19 0x19 0x19 0x17 0x19 0x19 0x16 0x15 0x19 0x19 0x16 0x15 0x19 0x19 0x16 0x15 0 0xf>, ++ <161 0 0x19 0x19 0x19 0x17 0x19 0x19 0x16 0x15 0x19 0x19 0x16 0x15 0x19 0x19 0x16 0x15 0 0xf>, ++ <165 0 0x19 0x19 0x19 0x17 0x19 0x19 0x16 0x15 0x19 0x19 0x16 0x15 0x19 0x19 0x16 0x15 0 0xf>; ++ CA = ++ <36 0 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0 0xf>, ++ <40 0 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0 0xf>, ++ <44 0 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0 0xf>, ++ <48 0 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0 0xf>, ++ <52 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x12 0x12 0x12 0x12 0x10 0x10 0x10 0x10 0 0xf>, ++ <56 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x12 0x12 0x12 0x12 0x10 0x10 0x10 0x10 0 0xf>, ++ <60 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x12 0x12 0x12 0x12 0x10 0x10 0x10 0x10 0 0xf>, ++ <64 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x12 0x12 0x12 0x12 0x10 0x10 0x10 0x10 0 0xf>, ++ <100 0 0x10 0x10 0x10 0x10 0x11 0x11 0x11 0x11 0x12 0x12 0x12 0x12 0x10 0x10 0x10 0x10 0 0xf>, ++ <104 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x12 0x12 0x12 0x12 0x10 0x10 0x10 0x10 0 0xf>, ++ <108 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0 0xf>, ++ <112 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0 0xf>, ++ <116 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0 0xf>, ++ <120 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0 0xf>, ++ <124 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0 0xf>, ++ <128 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0 0xf>, ++ <132 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0 0xf>, ++ <136 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0 0xf>, ++ <140 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0 0xf>, ++ <149 0 0x16 0x16 0x16 0x16 0x16 0x16 0x16 0x16 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0 0xf>, ++ <153 0 0x16 0x16 0x16 0x16 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0 0xf>, ++ <157 0 0x16 0x16 0x16 0x16 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0 0xf>, ++ <161 0 0x16 0x16 0x16 0x16 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0 0xf>, ++ <165 0 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0 0xf>; ++ CN = ++ <36 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>, ++ <40 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>, ++ <44 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>, ++ <48 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>, ++ <52 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>, ++ <56 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>, ++ <60 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>, ++ <64 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>, ++ <100 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>, ++ <104 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>, ++ <108 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>, ++ <112 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>, ++ <116 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>, ++ <120 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>, ++ <124 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>, ++ <128 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>, ++ <132 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>, ++ <136 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>, ++ <140 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>, ++ <149 0 0x14 0x14 0x14 0x14 0x13 0x13 0x13 0x13 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0 0xf>, ++ <153 0 0x16 0x16 0x16 0x16 0x15 0x15 0x15 0x15 0x14 0x14 0x14 0x14 0x10 0x10 0x10 0x10 0 0xf>, ++ <157 0 0x16 0x16 0x16 0x16 0x15 0x15 0x15 0x15 0x14 0x14 0x14 0x14 0x10 0x10 0x10 0x10 0 0xf>, ++ <161 0 0x16 0x16 0x16 0x16 0x15 0x15 0x15 0x15 0x14 0x14 0x14 0x14 0x10 0x10 0x10 0x10 0 0xf>, ++ <165 0 0x13 0x13 0x13 0x13 0x13 0x13 0x13 0x13 0x14 0x14 0x14 0x14 0x10 0x10 0x10 0x10 0 0xf>; ++ ETSI = ++ <36 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>, ++ <40 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>, ++ <44 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>, ++ <48 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>, ++ <52 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>, ++ <56 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>, ++ <60 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>, ++ <64 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>, ++ <100 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>, ++ <104 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>, ++ <108 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>, ++ <112 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>, ++ <116 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>, ++ <120 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>, ++ <124 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>, ++ <128 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>, ++ <132 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>, ++ <136 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>, ++ <140 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>, ++ <149 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>; ++ FCC = ++ <36 0 0x12 0x12 0x12 0x12 0x12 0x12 0x12 0x12 0x10 0x10 0x10 0x10 0xf 0xf 0xf 0xf 0 0xf>, ++ <40 0 0x16 0x16 0x16 0x16 0x16 0x16 0x16 0x16 0x16 0x16 0x16 0x16 0xf 0xf 0xf 0xf 0 0xf>, ++ <44 0 0x16 0x16 0x16 0x16 0x16 0x16 0x16 0x16 0x16 0x16 0x16 0x16 0xf 0xf 0xf 0xf 0 0xf>, ++ <48 0 0x16 0x16 0x16 0x16 0x16 0x16 0x16 0x16 0x16 0x16 0x16 0x16 0xf 0xf 0xf 0xf 0 0xf>, ++ <52 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x12 0x12 0x12 0x12 0x10 0x10 0x10 0x10 0 0xf>, ++ <56 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x12 0x12 0x12 0x12 0x10 0x10 0x10 0x10 0 0xf>, ++ <60 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x12 0x12 0x12 0x12 0x10 0x10 0x10 0x10 0 0xf>, ++ <64 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x12 0x12 0x12 0x12 0x10 0x10 0x10 0x10 0 0xf>, ++ <100 0 0x10 0x10 0x10 0x10 0x11 0x11 0x11 0x11 0x12 0x12 0x12 0x12 0x10 0x10 0x10 0x10 0 0xf>, ++ <104 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x12 0x12 0x12 0x12 0x10 0x10 0x10 0x10 0 0xf>, ++ <108 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0 0xf>, ++ <112 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0 0xf>, ++ <116 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0 0xf>, ++ <120 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0 0xf>, ++ <124 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0 0xf>, ++ <128 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0 0xf>, ++ <132 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0 0xf>, ++ <136 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0 0xf>, ++ <140 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0 0xf>, ++ <149 0 0x16 0x16 0x16 0x16 0x16 0x16 0x16 0x16 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0 0xf>, ++ <153 0 0x16 0x16 0x16 0x16 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0 0xf>, ++ <157 0 0x16 0x16 0x16 0x16 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0 0xf>, ++ <161 0 0x16 0x16 0x16 0x16 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0 0xf>, ++ <165 0 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0 0xf>; ++ }; ++ }; ++}; ++ ++&pcie2 { ++ mwlwifi { ++ marvell,chainmask = <4 4>; ++ marvell,powertable { ++ AU = ++ <1 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>, ++ <2 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>, ++ <3 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>, ++ <4 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>, ++ <5 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>, ++ <6 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>, ++ <7 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>, ++ <8 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>, ++ <9 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>, ++ <10 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>, ++ <11 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>; ++ CA = ++ <1 0 0x17 0x10 0x10 0x10 0xf 0xf 0xf 0xf 0xe 0xe 0xe 0xe 0x0 0x0 0x0 0x0 0 0xf>, ++ <2 0 0x18 0x16 0x16 0x16 0x16 0x16 0x16 0x14 0x11 0x11 0x11 0x11 0x0 0x0 0x0 0x0 0 0xf>, ++ <3 0 0x18 0x16 0x16 0x16 0x16 0x16 0x16 0x14 0x11 0x11 0x11 0x11 0x0 0x0 0x0 0x0 0 0xf>, ++ <4 0 0x18 0x16 0x16 0x16 0x16 0x16 0x16 0x14 0x11 0x11 0x11 0x11 0x0 0x0 0x0 0x0 0 0xf>, ++ <5 0 0x18 0x16 0x16 0x16 0x16 0x16 0x16 0x14 0x11 0x11 0x11 0x11 0x0 0x0 0x0 0x0 0 0xf>, ++ <6 0 0x18 0x16 0x16 0x16 0x16 0x16 0x16 0x14 0x11 0x11 0x11 0x11 0x0 0x0 0x0 0x0 0 0xf>, ++ <7 0 0x18 0x16 0x16 0x16 0x16 0x16 0x16 0x14 0x11 0x11 0x11 0x11 0x0 0x0 0x0 0x0 0 0xf>, ++ <8 0 0x18 0x16 0x16 0x16 0x16 0x16 0x16 0x14 0x11 0x11 0x11 0x11 0x0 0x0 0x0 0x0 0 0xf>, ++ <9 0 0x18 0x16 0x16 0x16 0x16 0x16 0x16 0x14 0x11 0x11 0x11 0x11 0x0 0x0 0x0 0x0 0 0xf>, ++ <10 0 0x18 0x16 0x16 0x16 0x16 0x16 0x16 0x14 0x11 0x11 0x11 0x11 0x0 0x0 0x0 0x0 0 0xf>, ++ <11 0 0x17 0x12 0x12 0x12 0x13 0x13 0x13 0x13 0xf 0xf 0xf 0xf 0x0 0x0 0x0 0x0 0 0xf>; ++ CN = ++ <1 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>, ++ <2 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>, ++ <3 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>, ++ <4 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>, ++ <5 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>, ++ <6 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>, ++ <7 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>, ++ <8 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>, ++ <9 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>, ++ <10 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>, ++ <11 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>, ++ <12 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>, ++ <13 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>, ++ <14 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>; ++ ETSI = ++ <1 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>, ++ <2 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>, ++ <3 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>, ++ <4 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>, ++ <5 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>, ++ <6 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>, ++ <7 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>, ++ <8 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>, ++ <9 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>, ++ <10 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>, ++ <11 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>, ++ <12 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>, ++ <13 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>; ++ FCC = ++ <1 0 0x17 0x10 0x10 0x10 0xf 0xf 0xf 0xf 0xe 0xe 0xe 0xe 0x0 0x0 0x0 0x0 0 0xf>, ++ <2 0 0x18 0x16 0x16 0x16 0x16 0x16 0x16 0x14 0x11 0x11 0x11 0x11 0x0 0x0 0x0 0x0 0 0xf>, ++ <3 0 0x18 0x16 0x16 0x16 0x16 0x16 0x16 0x14 0x11 0x11 0x11 0x11 0x0 0x0 0x0 0x0 0 0xf>, ++ <4 0 0x18 0x16 0x16 0x16 0x16 0x16 0x16 0x14 0x11 0x11 0x11 0x11 0x0 0x0 0x0 0x0 0 0xf>, ++ <5 0 0x18 0x16 0x16 0x16 0x16 0x16 0x16 0x14 0x11 0x11 0x11 0x11 0x0 0x0 0x0 0x0 0 0xf>, ++ <6 0 0x18 0x16 0x16 0x16 0x16 0x16 0x16 0x14 0x11 0x11 0x11 0x11 0x0 0x0 0x0 0x0 0 0xf>, ++ <7 0 0x18 0x16 0x16 0x16 0x16 0x16 0x16 0x14 0x11 0x11 0x11 0x11 0x0 0x0 0x0 0x0 0 0xf>, ++ <8 0 0x18 0x16 0x16 0x16 0x16 0x16 0x16 0x14 0x11 0x11 0x11 0x11 0x0 0x0 0x0 0x0 0 0xf>, ++ <9 0 0x18 0x16 0x16 0x16 0x16 0x16 0x16 0x14 0x11 0x11 0x11 0x11 0x0 0x0 0x0 0x0 0 0xf>, ++ <10 0 0x18 0x16 0x16 0x16 0x16 0x16 0x16 0x14 0x11 0x11 0x11 0x11 0x0 0x0 0x0 0x0 0 0xf>, ++ <11 0 0x17 0x12 0x12 0x12 0x13 0x13 0x13 0x13 0xf 0xf 0xf 0xf 0x0 0x0 0x0 0x0 0 0xf>; ++ }; ++ }; ++}; +--- a/arch/arm/boot/dts/armada-385-linksys-shelby.dts ++++ b/arch/arm/boot/dts/armada-385-linksys-shelby.dts +@@ -142,3 +142,205 @@ + }; + }; + }; ++ ++&pcie1 { ++ mwlwifi { ++ marvell,chainmask = <4 4>; ++ marvell,powertable { ++ AU = ++ <36 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>, ++ <40 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>, ++ <44 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>, ++ <48 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>, ++ <52 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>, ++ <56 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>, ++ <60 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>, ++ <64 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>, ++ <100 0 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0 0xf>, ++ <104 0 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0 0xf>, ++ <108 0 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0 0xf>, ++ <112 0 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0 0xf>, ++ <116 0 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0 0xf>, ++ <120 0 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0 0xf>, ++ <124 0 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0 0xf>, ++ <128 0 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0 0xf>, ++ <132 0 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0 0xf>, ++ <136 0 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0 0xf>, ++ <140 0 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0 0xf>, ++ <149 0 0x19 0x19 0x19 0x17 0x19 0x19 0x16 0x15 0x19 0x19 0x16 0x15 0x19 0x19 0x16 0x15 0 0xf>, ++ <153 0 0x19 0x19 0x19 0x17 0x19 0x19 0x16 0x15 0x19 0x19 0x16 0x15 0x19 0x19 0x16 0x15 0 0xf>, ++ <157 0 0x19 0x19 0x19 0x17 0x19 0x19 0x16 0x15 0x19 0x19 0x16 0x15 0x19 0x19 0x16 0x15 0 0xf>, ++ <161 0 0x19 0x19 0x19 0x17 0x19 0x19 0x16 0x15 0x19 0x19 0x16 0x15 0x19 0x19 0x16 0x15 0 0xf>, ++ <165 0 0x19 0x19 0x19 0x17 0x19 0x19 0x16 0x15 0x19 0x19 0x16 0x15 0x19 0x19 0x16 0x15 0 0xf>; ++ CA = ++ <36 0 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0 0xf>, ++ <40 0 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0 0xf>, ++ <44 0 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0 0xf>, ++ <48 0 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0 0xf>, ++ <52 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x12 0x12 0x12 0x12 0x10 0x10 0x10 0x10 0 0xf>, ++ <56 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x12 0x12 0x12 0x12 0x10 0x10 0x10 0x10 0 0xf>, ++ <60 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x12 0x12 0x12 0x12 0x10 0x10 0x10 0x10 0 0xf>, ++ <64 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x12 0x12 0x12 0x12 0x10 0x10 0x10 0x10 0 0xf>, ++ <100 0 0x10 0x10 0x10 0x10 0x11 0x11 0x11 0x11 0x12 0x12 0x12 0x12 0x10 0x10 0x10 0x10 0 0xf>, ++ <104 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x12 0x12 0x12 0x12 0x10 0x10 0x10 0x10 0 0xf>, ++ <108 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0 0xf>, ++ <112 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0 0xf>, ++ <116 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0 0xf>, ++ <120 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0 0xf>, ++ <124 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0 0xf>, ++ <128 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0 0xf>, ++ <132 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0 0xf>, ++ <136 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0 0xf>, ++ <140 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0 0xf>, ++ <149 0 0x16 0x16 0x16 0x16 0x16 0x16 0x16 0x16 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0 0xf>, ++ <153 0 0x16 0x16 0x16 0x16 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0 0xf>, ++ <157 0 0x16 0x16 0x16 0x16 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0 0xf>, ++ <161 0 0x16 0x16 0x16 0x16 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0 0xf>, ++ <165 0 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0 0xf>; ++ CN = ++ <36 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>, ++ <40 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>, ++ <44 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>, ++ <48 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>, ++ <52 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>, ++ <56 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>, ++ <60 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>, ++ <64 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>, ++ <100 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>, ++ <104 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>, ++ <108 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>, ++ <112 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>, ++ <116 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>, ++ <120 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>, ++ <124 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>, ++ <128 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>, ++ <132 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>, ++ <136 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>, ++ <140 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>, ++ <149 0 0x14 0x14 0x14 0x14 0x13 0x13 0x13 0x13 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0 0xf>, ++ <153 0 0x16 0x16 0x16 0x16 0x15 0x15 0x15 0x15 0x14 0x14 0x14 0x14 0x10 0x10 0x10 0x10 0 0xf>, ++ <157 0 0x16 0x16 0x16 0x16 0x15 0x15 0x15 0x15 0x14 0x14 0x14 0x14 0x10 0x10 0x10 0x10 0 0xf>, ++ <161 0 0x16 0x16 0x16 0x16 0x15 0x15 0x15 0x15 0x14 0x14 0x14 0x14 0x10 0x10 0x10 0x10 0 0xf>, ++ <165 0 0x13 0x13 0x13 0x13 0x13 0x13 0x13 0x13 0x14 0x14 0x14 0x14 0x10 0x10 0x10 0x10 0 0xf>; ++ ETSI = ++ <36 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>, ++ <40 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>, ++ <44 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>, ++ <48 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>, ++ <52 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>, ++ <56 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>, ++ <60 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>, ++ <64 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>, ++ <100 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>, ++ <104 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>, ++ <108 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>, ++ <112 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>, ++ <116 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>, ++ <120 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>, ++ <124 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>, ++ <128 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>, ++ <132 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>, ++ <136 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>, ++ <140 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>, ++ <149 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>; ++ FCC = ++ <36 0 0x12 0x12 0x12 0x12 0x12 0x12 0x12 0x12 0x10 0x10 0x10 0x10 0xf 0xf 0xf 0xf 0 0xf>, ++ <40 0 0x16 0x16 0x16 0x16 0x16 0x16 0x16 0x16 0x16 0x16 0x16 0x16 0xf 0xf 0xf 0xf 0 0xf>, ++ <44 0 0x16 0x16 0x16 0x16 0x16 0x16 0x16 0x16 0x16 0x16 0x16 0x16 0xf 0xf 0xf 0xf 0 0xf>, ++ <48 0 0x16 0x16 0x16 0x16 0x16 0x16 0x16 0x16 0x16 0x16 0x16 0x16 0xf 0xf 0xf 0xf 0 0xf>, ++ <52 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x12 0x12 0x12 0x12 0x10 0x10 0x10 0x10 0 0xf>, ++ <56 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x12 0x12 0x12 0x12 0x10 0x10 0x10 0x10 0 0xf>, ++ <60 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x12 0x12 0x12 0x12 0x10 0x10 0x10 0x10 0 0xf>, ++ <64 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x12 0x12 0x12 0x12 0x10 0x10 0x10 0x10 0 0xf>, ++ <100 0 0x10 0x10 0x10 0x10 0x11 0x11 0x11 0x11 0x12 0x12 0x12 0x12 0x10 0x10 0x10 0x10 0 0xf>, ++ <104 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x12 0x12 0x12 0x12 0x10 0x10 0x10 0x10 0 0xf>, ++ <108 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0 0xf>, ++ <112 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0 0xf>, ++ <116 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0 0xf>, ++ <120 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0 0xf>, ++ <124 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0 0xf>, ++ <128 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0 0xf>, ++ <132 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0 0xf>, ++ <136 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0 0xf>, ++ <140 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0 0xf>, ++ <149 0 0x16 0x16 0x16 0x16 0x16 0x16 0x16 0x16 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0 0xf>, ++ <153 0 0x16 0x16 0x16 0x16 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0 0xf>, ++ <157 0 0x16 0x16 0x16 0x16 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0 0xf>, ++ <161 0 0x16 0x16 0x16 0x16 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0 0xf>, ++ <165 0 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0 0xf>; ++ }; ++ }; ++}; ++ ++&pcie2 { ++ mwlwifi { ++ marvell,chainmask = <4 4>; ++ marvell,powertable { ++ AU = ++ <1 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>, ++ <2 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>, ++ <3 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>, ++ <4 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>, ++ <5 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>, ++ <6 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>, ++ <7 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>, ++ <8 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>, ++ <9 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>, ++ <10 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>, ++ <11 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>; ++ CA = ++ <1 0 0x17 0x10 0x10 0x10 0xf 0xf 0xf 0xf 0xe 0xe 0xe 0xe 0x0 0x0 0x0 0x0 0 0xf>, ++ <2 0 0x18 0x16 0x16 0x16 0x16 0x16 0x16 0x14 0x11 0x11 0x11 0x11 0x0 0x0 0x0 0x0 0 0xf>, ++ <3 0 0x18 0x16 0x16 0x16 0x16 0x16 0x16 0x14 0x11 0x11 0x11 0x11 0x0 0x0 0x0 0x0 0 0xf>, ++ <4 0 0x18 0x16 0x16 0x16 0x16 0x16 0x16 0x14 0x11 0x11 0x11 0x11 0x0 0x0 0x0 0x0 0 0xf>, ++ <5 0 0x18 0x16 0x16 0x16 0x16 0x16 0x16 0x14 0x11 0x11 0x11 0x11 0x0 0x0 0x0 0x0 0 0xf>, ++ <6 0 0x18 0x16 0x16 0x16 0x16 0x16 0x16 0x14 0x11 0x11 0x11 0x11 0x0 0x0 0x0 0x0 0 0xf>, ++ <7 0 0x18 0x16 0x16 0x16 0x16 0x16 0x16 0x14 0x11 0x11 0x11 0x11 0x0 0x0 0x0 0x0 0 0xf>, ++ <8 0 0x18 0x16 0x16 0x16 0x16 0x16 0x16 0x14 0x11 0x11 0x11 0x11 0x0 0x0 0x0 0x0 0 0xf>, ++ <9 0 0x18 0x16 0x16 0x16 0x16 0x16 0x16 0x14 0x11 0x11 0x11 0x11 0x0 0x0 0x0 0x0 0 0xf>, ++ <10 0 0x18 0x16 0x16 0x16 0x16 0x16 0x16 0x14 0x11 0x11 0x11 0x11 0x0 0x0 0x0 0x0 0 0xf>, ++ <11 0 0x17 0x12 0x12 0x12 0x13 0x13 0x13 0x13 0xf 0xf 0xf 0xf 0x0 0x0 0x0 0x0 0 0xf>; ++ CN = ++ <1 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>, ++ <2 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>, ++ <3 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>, ++ <4 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>, ++ <5 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>, ++ <6 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>, ++ <7 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>, ++ <8 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>, ++ <9 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>, ++ <10 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>, ++ <11 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>, ++ <12 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>, ++ <13 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>, ++ <14 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>; ++ ETSI = ++ <1 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>, ++ <2 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>, ++ <3 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>, ++ <4 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>, ++ <5 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>, ++ <6 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>, ++ <7 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>, ++ <8 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>, ++ <9 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>, ++ <10 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>, ++ <11 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>, ++ <12 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>, ++ <13 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>; ++ FCC = ++ <1 0 0x17 0x10 0x10 0x10 0xf 0xf 0xf 0xf 0xe 0xe 0xe 0xe 0x0 0x0 0x0 0x0 0 0xf>, ++ <2 0 0x18 0x16 0x16 0x16 0x16 0x16 0x16 0x14 0x11 0x11 0x11 0x11 0x0 0x0 0x0 0x0 0 0xf>, ++ <3 0 0x18 0x16 0x16 0x16 0x16 0x16 0x16 0x14 0x11 0x11 0x11 0x11 0x0 0x0 0x0 0x0 0 0xf>, ++ <4 0 0x18 0x16 0x16 0x16 0x16 0x16 0x16 0x14 0x11 0x11 0x11 0x11 0x0 0x0 0x0 0x0 0 0xf>, ++ <5 0 0x18 0x16 0x16 0x16 0x16 0x16 0x16 0x14 0x11 0x11 0x11 0x11 0x0 0x0 0x0 0x0 0 0xf>, ++ <6 0 0x18 0x16 0x16 0x16 0x16 0x16 0x16 0x14 0x11 0x11 0x11 0x11 0x0 0x0 0x0 0x0 0 0xf>, ++ <7 0 0x18 0x16 0x16 0x16 0x16 0x16 0x16 0x14 0x11 0x11 0x11 0x11 0x0 0x0 0x0 0x0 0 0xf>, ++ <8 0 0x18 0x16 0x16 0x16 0x16 0x16 0x16 0x14 0x11 0x11 0x11 0x11 0x0 0x0 0x0 0x0 0 0xf>, ++ <9 0 0x18 0x16 0x16 0x16 0x16 0x16 0x16 0x14 0x11 0x11 0x11 0x11 0x0 0x0 0x0 0x0 0 0xf>, ++ <10 0 0x18 0x16 0x16 0x16 0x16 0x16 0x16 0x14 0x11 0x11 0x11 0x11 0x0 0x0 0x0 0x0 0 0xf>, ++ <11 0 0x17 0x12 0x12 0x12 0x13 0x13 0x13 0x13 0xf 0xf 0xf 0xf 0x0 0x0 0x0 0x0 0 0xf>; ++ }; ++ }; ++}; +--- a/arch/arm/boot/dts/armada-385-linksys-rango.dts ++++ b/arch/arm/boot/dts/armada-385-linksys-rango.dts +@@ -157,6 +157,18 @@ + }; + }; + ++&pcie1 { ++ mwlwifi { ++ marvell,chainmask = <4 4>; ++ }; ++}; ++ ++&pcie2 { ++ mwlwifi { ++ marvell,chainmask = <4 4>; ++ }; ++}; ++ + &sdhci { + pinctrl-names = "default"; + pinctrl-0 = <&sdhci_pins>; +--- a/arch/arm/boot/dts/armada-xp-linksys-mamba.dts ++++ b/arch/arm/boot/dts/armada-xp-linksys-mamba.dts +@@ -225,12 +225,100 @@ + pcie@2,0 { + /* Port 0, Lane 1 */ + status = "okay"; ++ ++ mwlwifi { ++ marvell,5ghz = <0>; ++ marvell,chainmask = <4 4>; ++ marvell,powertable { ++ FCC = ++ <1 0 0x17 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0xf 0xf 0xf 0xf 0x0 0x0 0x0 0x0 0 0xf>, ++ <2 0 0x17 0x16 0x16 0x16 0x16 0x16 0x16 0x14 0x10 0x10 0x10 0x10 0x0 0x0 0x0 0x0 0 0xf>, ++ <3 0 0x17 0x16 0x16 0x16 0x16 0x16 0x16 0x14 0x10 0x10 0x10 0x10 0x0 0x0 0x0 0x0 0 0xf>, ++ <4 0 0x17 0x16 0x16 0x16 0x16 0x16 0x16 0x14 0x10 0x10 0x10 0x10 0x0 0x0 0x0 0x0 0 0xf>, ++ <5 0 0x17 0x16 0x16 0x16 0x16 0x16 0x16 0x14 0x10 0x10 0x10 0x10 0x0 0x0 0x0 0x0 0 0xf>, ++ <6 0 0x17 0x16 0x16 0x16 0x16 0x16 0x16 0x14 0x10 0x10 0x10 0x10 0x0 0x0 0x0 0x0 0 0xf>, ++ <7 0 0x17 0x16 0x16 0x16 0x16 0x16 0x16 0x14 0x10 0x10 0x10 0x10 0x0 0x0 0x0 0x0 0 0xf>, ++ <8 0 0x17 0x16 0x16 0x16 0x16 0x16 0x16 0x14 0x10 0x10 0x10 0x10 0x0 0x0 0x0 0x0 0 0xf>, ++ <9 0 0x17 0x16 0x16 0x16 0x16 0x16 0x16 0x14 0x10 0x10 0x10 0x10 0x0 0x0 0x0 0x0 0 0xf>, ++ <10 0 0x17 0x16 0x16 0x16 0x16 0x16 0x16 0x14 0x10 0x10 0x10 0x10 0x0 0x0 0x0 0x0 0 0xf>, ++ <11 0 0x17 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x0 0x0 0x0 0x0 0 0xf>; ++ ++ ETSI = ++ <1 0 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0x0 0x0 0x0 0x0 0 0xf>, ++ <2 0 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0x0 0x0 0x0 0x0 0 0xf>, ++ <3 0 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0x0 0x0 0x0 0x0 0 0xf>, ++ <4 0 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0x0 0x0 0x0 0x0 0 0xf>, ++ <5 0 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0x0 0x0 0x0 0x0 0 0xf>, ++ <6 0 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0x0 0x0 0x0 0x0 0 0xf>, ++ <7 0 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0x0 0x0 0x0 0x0 0 0xf>, ++ <8 0 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0x0 0x0 0x0 0x0 0 0xf>, ++ <9 0 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0x0 0x0 0x0 0x0 0 0xf>, ++ <10 0 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0x0 0x0 0x0 0x0 0 0xf>, ++ <11 0 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0x0 0x0 0x0 0x0 0 0xf>, ++ <12 0 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0x0 0x0 0x0 0x0 0 0xf>, ++ <13 0 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0x0 0x0 0x0 0x0 0 0xf>; ++ }; ++ }; + }; + + /* Second mini-PCIe port */ + pcie@3,0 { + /* Port 0, Lane 3 */ + status = "okay"; ++ ++ mwlwifi { ++ marvell,2ghz = <0>; ++ marvell,chainmask = <4 4>; ++ marvell,powertable { ++ FCC = ++ <36 0 0x8 0x8 0x8 0x8 0x8 0x8 0x8 0x8 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0 0xf>, ++ <40 0 0x8 0x8 0x8 0x8 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0 0xf>, ++ <44 0 0x8 0x8 0x8 0x8 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0 0xf>, ++ <48 0 0x8 0x8 0x8 0x8 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0 0xf>, ++ <52 0 0xf 0xf 0xf 0xf 0xf 0xf 0xf 0xf 0x12 0x12 0x12 0x12 0x12 0x12 0x12 0x12 0 0xf>, ++ <56 0 0xf 0xf 0xf 0xf 0xf 0xf 0xf 0xf 0x12 0x12 0x12 0x12 0x12 0x12 0x12 0x12 0 0xf>, ++ <60 0 0xf 0xf 0xf 0xf 0xf 0xf 0xf 0xf 0x12 0x12 0x12 0x12 0x12 0x12 0x12 0x12 0 0xf>, ++ <64 0 0xf 0xf 0xf 0xf 0xf 0xf 0xf 0xf 0x12 0x12 0x12 0x12 0x12 0x12 0x12 0x12 0 0xf>, ++ <100 0 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0 0xf>, ++ <104 0 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0 0xf>, ++ <108 0 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0 0xf>, ++ <112 0 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0 0xf>, ++ <116 0 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0 0xf>, ++ <120 0 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0 0xf>, ++ <124 0 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0 0xf>, ++ <128 0 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0 0xf>, ++ <132 0 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0 0xf>, ++ <136 0 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0 0xf>, ++ <140 0 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0 0xf>, ++ <149 0 0x16 0x16 0x16 0x16 0x14 0x14 0x14 0x14 0x15 0x15 0x15 0x15 0x14 0x14 0x14 0x14 0 0xf>, ++ <153 0 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x14 0x14 0x14 0x14 0 0xf>, ++ <157 0 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x14 0x14 0x14 0x14 0 0xf>, ++ <161 0 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x14 0x14 0x14 0x14 0 0xf>, ++ <165 0 0x16 0x16 0x16 0x16 0x16 0x16 0x16 0x16 0x15 0x15 0x15 0x15 0x14 0x14 0x14 0x14 0 0xf>; ++ ++ ETSI = ++ <36 0 0xc 0xc 0xc 0xc 0xc 0xc 0xc 0xc 0xd 0xd 0xd 0xd 0xc 0xc 0xc 0xc 0 0xf>, ++ <40 0 0xc 0xc 0xc 0xc 0xc 0xc 0xc 0xc 0xd 0xd 0xd 0xd 0xc 0xc 0xc 0xc 0 0xf>, ++ <44 0 0xc 0xc 0xc 0xc 0xc 0xc 0xc 0xc 0xd 0xd 0xd 0xd 0xc 0xc 0xc 0xc 0 0xf>, ++ <48 0 0xc 0xc 0xc 0xc 0xc 0xc 0xc 0xc 0xd 0xd 0xd 0xd 0xc 0xc 0xc 0xc 0 0xf>, ++ <52 0 0xc 0xc 0xc 0xc 0xc 0xc 0xc 0xc 0xd 0xd 0xd 0xd 0xc 0xc 0xc 0xc 0 0xf>, ++ <56 0 0xc 0xc 0xc 0xc 0xc 0xc 0xc 0xc 0xd 0xd 0xd 0xd 0xc 0xc 0xc 0xc 0 0xf>, ++ <60 0 0xc 0xc 0xc 0xc 0xc 0xc 0xc 0xc 0xd 0xd 0xd 0xd 0xc 0xc 0xc 0xc 0 0xf>, ++ <64 0 0xc 0xc 0xc 0xc 0xc 0xc 0xc 0xc 0xd 0xd 0xd 0xd 0xc 0xc 0xc 0xc 0 0xf>, ++ <100 0 0xc 0xc 0xc 0xc 0xc 0xc 0xc 0xc 0xd 0xd 0xd 0xd 0xc 0xc 0xc 0xc 0 0xf>, ++ <104 0 0xc 0xc 0xc 0xc 0xc 0xc 0xc 0xc 0xd 0xd 0xd 0xd 0xc 0xc 0xc 0xc 0 0xf>, ++ <108 0 0xc 0xc 0xc 0xc 0xc 0xc 0xc 0xc 0xd 0xd 0xd 0xd 0xc 0xc 0xc 0xc 0 0xf>, ++ <112 0 0xc 0xc 0xc 0xc 0xc 0xc 0xc 0xc 0xd 0xd 0xd 0xd 0xc 0xc 0xc 0xc 0 0xf>, ++ <116 0 0xc 0xc 0xc 0xc 0xc 0xc 0xc 0xc 0xd 0xd 0xd 0xd 0xc 0xc 0xc 0xc 0 0xf>, ++ <120 0 0xc 0xc 0xc 0xc 0xc 0xc 0xc 0xc 0xd 0xd 0xd 0xd 0xc 0xc 0xc 0xc 0 0xf>, ++ <124 0 0xc 0xc 0xc 0xc 0xc 0xc 0xc 0xc 0xd 0xd 0xd 0xd 0xc 0xc 0xc 0xc 0 0xf>, ++ <128 0 0xc 0xc 0xc 0xc 0xc 0xc 0xc 0xc 0xd 0xd 0xd 0xd 0xc 0xc 0xc 0xc 0 0xf>, ++ <132 0 0xc 0xc 0xc 0xc 0xc 0xc 0xc 0xc 0xd 0xd 0xd 0xd 0xc 0xc 0xc 0xc 0 0xf>, ++ <136 0 0xc 0xc 0xc 0xc 0xc 0xc 0xc 0xc 0xd 0xd 0xd 0xd 0xc 0xc 0xc 0xc 0 0xf>, ++ <140 0 0xc 0xc 0xc 0xc 0xc 0xc 0xc 0xc 0xd 0xd 0xd 0xd 0xc 0xc 0xc 0xc 0 0xf>, ++ <149 0 0xc 0xc 0xc 0xc 0xc 0xc 0xc 0xc 0xd 0xd 0xd 0xd 0xc 0xc 0xc 0xc 0 0xf>; ++ }; ++ }; + }; + }; + diff --git a/pkgs/patches-linux-5.15/302-mips_no_branch_likely.patch b/pkgs/patches-linux-5.15/302-mips_no_branch_likely.patch new file mode 100644 index 0000000..271923f --- /dev/null +++ b/pkgs/patches-linux-5.15/302-mips_no_branch_likely.patch @@ -0,0 +1,22 @@ +From: Felix Fietkau <nbd@nbd.name> +Subject: mips: use -mno-branch-likely for kernel and userspace + +saves ~11k kernel size after lzma and ~12k squashfs size in the + +lede-commit: 41a039f46450ffae9483d6216422098669da2900 +Signed-off-by: Felix Fietkau <nbd@nbd.name> +--- + arch/mips/Makefile | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/arch/mips/Makefile ++++ b/arch/mips/Makefile +@@ -95,7 +95,7 @@ all-$(CONFIG_SYS_SUPPORTS_ZBOOT)+= vmlin + # machines may also. Since BFD is incredibly buggy with respect to + # crossformat linking we rely on the elf2ecoff tool for format conversion. + # +-cflags-y += -G 0 -mno-abicalls -fno-pic -pipe ++cflags-y += -G 0 -mno-abicalls -fno-pic -pipe -mno-branch-likely + cflags-y += -msoft-float + LDFLAGS_vmlinux += -G 0 -static -n -nostdlib + KBUILD_AFLAGS_MODULE += -mlong-calls diff --git a/pkgs/patches-linux-5.15/304-revert_i2c_delay.patch b/pkgs/patches-linux-5.15/304-revert_i2c_delay.patch new file mode 100644 index 0000000..930c0f9 --- /dev/null +++ b/pkgs/patches-linux-5.15/304-revert_i2c_delay.patch @@ -0,0 +1,15 @@ +--- a/arch/arm/boot/dts/armada-xp.dtsi ++++ b/arch/arm/boot/dts/armada-xp.dtsi +@@ -237,12 +237,10 @@ + }; + + &i2c0 { +- compatible = "marvell,mv78230-i2c", "marvell,mv64xxx-i2c"; + reg = <0x11000 0x100>; + }; + + &i2c1 { +- compatible = "marvell,mv78230-i2c", "marvell,mv64xxx-i2c"; + reg = <0x11100 0x100>; + }; + diff --git a/pkgs/patches-linux-5.15/305-armada-385-rd-mtd-partitions.patch b/pkgs/patches-linux-5.15/305-armada-385-rd-mtd-partitions.patch new file mode 100644 index 0000000..31bd53b --- /dev/null +++ b/pkgs/patches-linux-5.15/305-armada-385-rd-mtd-partitions.patch @@ -0,0 +1,19 @@ +--- a/arch/arm/boot/dts/armada-388-rd.dts ++++ b/arch/arm/boot/dts/armada-388-rd.dts +@@ -103,6 +103,16 @@ + compatible = "st,m25p128", "jedec,spi-nor"; + reg = <0>; /* Chip select 0 */ + spi-max-frequency = <108000000>; ++ ++ partition@0 { ++ label = "uboot"; ++ reg = <0 0x400000>; ++ }; ++ ++ partition@1 { ++ label = "firmware"; ++ reg = <0x400000 0xc00000>; ++ }; + }; + }; + diff --git a/pkgs/patches-linux-5.15/305-mips_module_reloc.patch b/pkgs/patches-linux-5.15/305-mips_module_reloc.patch new file mode 100644 index 0000000..bbea1f6 --- /dev/null +++ b/pkgs/patches-linux-5.15/305-mips_module_reloc.patch @@ -0,0 +1,370 @@ +From: Felix Fietkau <nbd@nbd.name> +Subject: mips: replace -mlong-calls with -mno-long-calls to make function calls faster in kernel modules to achieve this, try to + +lede-commit: 3b3d64743ba2a874df9d70cd19e242205b0a788c +Signed-off-by: Felix Fietkau <nbd@nbd.name> +--- + arch/mips/Makefile | 5 + + arch/mips/include/asm/module.h | 5 + + arch/mips/kernel/module.c | 279 ++++++++++++++++++++++++++++++++++++++++- + 3 files changed, 284 insertions(+), 5 deletions(-) + +--- a/arch/mips/Makefile ++++ b/arch/mips/Makefile +@@ -98,8 +98,18 @@ all-$(CONFIG_SYS_SUPPORTS_ZBOOT)+= vmlin + cflags-y += -G 0 -mno-abicalls -fno-pic -pipe -mno-branch-likely + cflags-y += -msoft-float + LDFLAGS_vmlinux += -G 0 -static -n -nostdlib ++ifdef CONFIG_64BIT + KBUILD_AFLAGS_MODULE += -mlong-calls + KBUILD_CFLAGS_MODULE += -mlong-calls ++else ++ ifdef CONFIG_DYNAMIC_FTRACE ++ KBUILD_AFLAGS_MODULE += -mlong-calls ++ KBUILD_CFLAGS_MODULE += -mlong-calls ++ else ++ KBUILD_AFLAGS_MODULE += -mno-long-calls ++ KBUILD_CFLAGS_MODULE += -mno-long-calls ++ endif ++endif + + ifeq ($(CONFIG_RELOCATABLE),y) + LDFLAGS_vmlinux += --emit-relocs +--- a/arch/mips/include/asm/module.h ++++ b/arch/mips/include/asm/module.h +@@ -12,6 +12,11 @@ struct mod_arch_specific { + const struct exception_table_entry *dbe_start; + const struct exception_table_entry *dbe_end; + struct mips_hi16 *r_mips_hi16_list; ++ ++ void *phys_plt_tbl; ++ void *virt_plt_tbl; ++ unsigned int phys_plt_offset; ++ unsigned int virt_plt_offset; + }; + + typedef uint8_t Elf64_Byte; /* Type for a 8-bit quantity. */ +--- a/arch/mips/kernel/module.c ++++ b/arch/mips/kernel/module.c +@@ -31,23 +31,261 @@ struct mips_hi16 { + static LIST_HEAD(dbe_list); + static DEFINE_SPINLOCK(dbe_lock); + +-#ifdef MODULE_START ++/* ++ * Get the potential max trampolines size required of the init and ++ * non-init sections. Only used if we cannot find enough contiguous ++ * physically mapped memory to put the module into. ++ */ ++static unsigned int ++get_plt_size(const Elf_Ehdr *hdr, const Elf_Shdr *sechdrs, ++ const char *secstrings, unsigned int symindex, bool is_init) ++{ ++ unsigned long ret = 0; ++ unsigned int i, j; ++ Elf_Sym *syms; ++ ++ /* Everything marked ALLOC (this includes the exported symbols) */ ++ for (i = 1; i < hdr->e_shnum; ++i) { ++ unsigned int info = sechdrs[i].sh_info; ++ ++ if (sechdrs[i].sh_type != SHT_REL ++ && sechdrs[i].sh_type != SHT_RELA) ++ continue; ++ ++ /* Not a valid relocation section? */ ++ if (info >= hdr->e_shnum) ++ continue; ++ ++ /* Don't bother with non-allocated sections */ ++ if (!(sechdrs[info].sh_flags & SHF_ALLOC)) ++ continue; ++ ++ /* If it's called *.init*, and we're not init, we're ++ not interested */ ++ if ((strstr(secstrings + sechdrs[i].sh_name, ".init") != 0) ++ != is_init) ++ continue; ++ ++ syms = (Elf_Sym *) sechdrs[symindex].sh_addr; ++ if (sechdrs[i].sh_type == SHT_REL) { ++ Elf_Mips_Rel *rel = (void *) sechdrs[i].sh_addr; ++ unsigned int size = sechdrs[i].sh_size / sizeof(*rel); ++ ++ for (j = 0; j < size; ++j) { ++ Elf_Sym *sym; ++ ++ if (ELF_MIPS_R_TYPE(rel[j]) != R_MIPS_26) ++ continue; ++ ++ sym = syms + ELF_MIPS_R_SYM(rel[j]); ++ if (!is_init && sym->st_shndx != SHN_UNDEF) ++ continue; ++ ++ ret += 4 * sizeof(int); ++ } ++ } else { ++ Elf_Mips_Rela *rela = (void *) sechdrs[i].sh_addr; ++ unsigned int size = sechdrs[i].sh_size / sizeof(*rela); ++ ++ for (j = 0; j < size; ++j) { ++ Elf_Sym *sym; ++ ++ if (ELF_MIPS_R_TYPE(rela[j]) != R_MIPS_26) ++ continue; ++ ++ sym = syms + ELF_MIPS_R_SYM(rela[j]); ++ if (!is_init && sym->st_shndx != SHN_UNDEF) ++ continue; ++ ++ ret += 4 * sizeof(int); ++ } ++ } ++ } ++ ++ return ret; ++} ++ ++#ifndef MODULE_START ++static void *alloc_phys(unsigned long size) ++{ ++ unsigned order; ++ struct page *page; ++ struct page *p; ++ ++ size = PAGE_ALIGN(size); ++ order = get_order(size); ++ ++ page = alloc_pages(GFP_KERNEL | __GFP_NORETRY | __GFP_NOWARN | ++ __GFP_THISNODE, order); ++ if (!page) ++ return NULL; ++ ++ split_page(page, order); ++ ++ /* mark all pages except for the last one */ ++ for (p = page; p + 1 < page + (size >> PAGE_SHIFT); ++p) ++ set_bit(PG_owner_priv_1, &p->flags); ++ ++ for (p = page + (size >> PAGE_SHIFT); p < page + (1 << order); ++p) ++ __free_page(p); ++ ++ return page_address(page); ++} ++#endif ++ ++static void free_phys(void *ptr) ++{ ++ struct page *page; ++ bool free; ++ ++ page = virt_to_page(ptr); ++ do { ++ free = test_and_clear_bit(PG_owner_priv_1, &page->flags); ++ __free_page(page); ++ page++; ++ } while (free); ++} ++ ++ + void *module_alloc(unsigned long size) + { ++#ifdef MODULE_START + return __vmalloc_node_range(size, 1, MODULE_START, MODULE_END, + GFP_KERNEL, PAGE_KERNEL, 0, NUMA_NO_NODE, + __builtin_return_address(0)); ++#else ++ void *ptr; ++ ++ if (size == 0) ++ return NULL; ++ ++ ptr = alloc_phys(size); ++ ++ /* If we failed to allocate physically contiguous memory, ++ * fall back to regular vmalloc. The module loader code will ++ * create jump tables to handle long jumps */ ++ if (!ptr) ++ return vmalloc(size); ++ ++ return ptr; ++#endif + } ++ ++static inline bool is_phys_addr(void *ptr) ++{ ++#ifdef CONFIG_64BIT ++ return (KSEGX((unsigned long)ptr) == CKSEG0); ++#else ++ return (KSEGX(ptr) == KSEG0); + #endif ++} ++ ++/* Free memory returned from module_alloc */ ++void module_memfree(void *module_region) ++{ ++ if (is_phys_addr(module_region)) ++ free_phys(module_region); ++ else ++ vfree(module_region); ++} ++ ++static void *__module_alloc(int size, bool phys) ++{ ++ void *ptr; ++ ++ if (phys) ++ ptr = kmalloc(size, GFP_KERNEL); ++ else ++ ptr = vmalloc(size); ++ return ptr; ++} ++ ++static void __module_free(void *ptr) ++{ ++ if (is_phys_addr(ptr)) ++ kfree(ptr); ++ else ++ vfree(ptr); ++} ++ ++int module_frob_arch_sections(Elf_Ehdr *hdr, Elf_Shdr *sechdrs, ++ char *secstrings, struct module *mod) ++{ ++ unsigned int symindex = 0; ++ unsigned int core_size, init_size; ++ int i; ++ ++ mod->arch.phys_plt_offset = 0; ++ mod->arch.virt_plt_offset = 0; ++ mod->arch.phys_plt_tbl = NULL; ++ mod->arch.virt_plt_tbl = NULL; ++ ++ if (IS_ENABLED(CONFIG_64BIT)) ++ return 0; ++ ++ for (i = 1; i < hdr->e_shnum; i++) ++ if (sechdrs[i].sh_type == SHT_SYMTAB) ++ symindex = i; ++ ++ core_size = get_plt_size(hdr, sechdrs, secstrings, symindex, false); ++ init_size = get_plt_size(hdr, sechdrs, secstrings, symindex, true); ++ ++ if ((core_size + init_size) == 0) ++ return 0; ++ ++ mod->arch.phys_plt_tbl = __module_alloc(core_size + init_size, 1); ++ if (!mod->arch.phys_plt_tbl) ++ return -ENOMEM; ++ ++ mod->arch.virt_plt_tbl = __module_alloc(core_size + init_size, 0); ++ if (!mod->arch.virt_plt_tbl) { ++ __module_free(mod->arch.phys_plt_tbl); ++ mod->arch.phys_plt_tbl = NULL; ++ return -ENOMEM; ++ } ++ ++ return 0; ++} + + static void apply_r_mips_32(u32 *location, u32 base, Elf_Addr v) + { + *location = base + v; + } + ++static Elf_Addr add_plt_entry_to(unsigned *plt_offset, ++ void *start, Elf_Addr v) ++{ ++ unsigned *tramp = start + *plt_offset; ++ *plt_offset += 4 * sizeof(int); ++ ++ /* adjust carry for addiu */ ++ if (v & 0x00008000) ++ v += 0x10000; ++ ++ tramp[0] = 0x3c190000 | (v >> 16); /* lui t9, hi16 */ ++ tramp[1] = 0x27390000 | (v & 0xffff); /* addiu t9, t9, lo16 */ ++ tramp[2] = 0x03200008; /* jr t9 */ ++ tramp[3] = 0x00000000; /* nop */ ++ ++ return (Elf_Addr) tramp; ++} ++ ++static Elf_Addr add_plt_entry(struct module *me, void *location, Elf_Addr v) ++{ ++ if (is_phys_addr(location)) ++ return add_plt_entry_to(&me->arch.phys_plt_offset, ++ me->arch.phys_plt_tbl, v); ++ else ++ return add_plt_entry_to(&me->arch.virt_plt_offset, ++ me->arch.virt_plt_tbl, v); ++ ++} ++ + static int apply_r_mips_26(struct module *me, u32 *location, u32 base, + Elf_Addr v) + { ++ u32 ofs = base & 0x03ffffff; ++ + if (v % 4) { + pr_err("module %s: dangerous R_MIPS_26 relocation\n", + me->name); +@@ -55,13 +293,17 @@ static int apply_r_mips_26(struct module + } + + if ((v & 0xf0000000) != (((unsigned long)location + 4) & 0xf0000000)) { +- pr_err("module %s: relocation overflow\n", +- me->name); +- return -ENOEXEC; ++ v = add_plt_entry(me, location, v + (ofs << 2)); ++ if (!v) { ++ pr_err("module %s: relocation overflow\n", ++ me->name); ++ return -ENOEXEC; ++ } ++ ofs = 0; + } + + *location = (*location & ~0x03ffffff) | +- ((base + (v >> 2)) & 0x03ffffff); ++ ((ofs + (v >> 2)) & 0x03ffffff); + + return 0; + } +@@ -441,9 +683,36 @@ int module_finalize(const Elf_Ehdr *hdr, + list_add(&me->arch.dbe_list, &dbe_list); + spin_unlock_irq(&dbe_lock); + } ++ ++ /* Get rid of the fixup trampoline if we're running the module ++ * from physically mapped address space */ ++ if (me->arch.phys_plt_offset == 0) { ++ __module_free(me->arch.phys_plt_tbl); ++ me->arch.phys_plt_tbl = NULL; ++ } ++ if (me->arch.virt_plt_offset == 0) { ++ __module_free(me->arch.virt_plt_tbl); ++ me->arch.virt_plt_tbl = NULL; ++ } ++ + return 0; + } + ++void module_arch_freeing_init(struct module *mod) ++{ ++ if (mod->state == MODULE_STATE_LIVE) ++ return; ++ ++ if (mod->arch.phys_plt_tbl) { ++ __module_free(mod->arch.phys_plt_tbl); ++ mod->arch.phys_plt_tbl = NULL; ++ } ++ if (mod->arch.virt_plt_tbl) { ++ __module_free(mod->arch.virt_plt_tbl); ++ mod->arch.virt_plt_tbl = NULL; ++ } ++} ++ + void module_arch_cleanup(struct module *mod) + { + spin_lock_irq(&dbe_lock); diff --git a/pkgs/patches-linux-5.15/306-ARM-mvebu-385-ap-Add-partitions.patch b/pkgs/patches-linux-5.15/306-ARM-mvebu-385-ap-Add-partitions.patch new file mode 100644 index 0000000..2057e31 --- /dev/null +++ b/pkgs/patches-linux-5.15/306-ARM-mvebu-385-ap-Add-partitions.patch @@ -0,0 +1,35 @@ +From 9861f93a59142a3131870df2521eb2deb73026d7 Mon Sep 17 00:00:00 2001 +From: Maxime Ripard <maxime.ripard@free-electrons.com> +Date: Tue, 13 Jan 2015 11:14:09 +0100 +Subject: [PATCH 2/2] ARM: mvebu: 385-ap: Add partitions + +Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com> +--- + arch/arm/boot/dts/armada-385-db-ap.dts | 15 +++++++++++++++ + 1 file changed, 15 insertions(+) + +--- a/arch/arm/boot/dts/armada-385-db-ap.dts ++++ b/arch/arm/boot/dts/armada-385-db-ap.dts +@@ -218,19 +218,19 @@ + #size-cells = <1>; + + partition@0 { +- label = "U-Boot"; ++ label = "u-boot"; + reg = <0x00000000 0x00800000>; + read-only; + }; + + partition@800000 { +- label = "uImage"; ++ label = "kernel"; + reg = <0x00800000 0x00400000>; + read-only; + }; + + partition@c00000 { +- label = "Root"; ++ label = "ubi"; + reg = <0x00c00000 0x3f400000>; + }; + }; diff --git a/pkgs/patches-linux-5.15/307-armada-xp-linksys-mamba-broken-idle.patch b/pkgs/patches-linux-5.15/307-armada-xp-linksys-mamba-broken-idle.patch new file mode 100644 index 0000000..16112d5 --- /dev/null +++ b/pkgs/patches-linux-5.15/307-armada-xp-linksys-mamba-broken-idle.patch @@ -0,0 +1,10 @@ +--- a/arch/arm/boot/dts/armada-xp-linksys-mamba.dts ++++ b/arch/arm/boot/dts/armada-xp-linksys-mamba.dts +@@ -485,3 +485,7 @@ + }; + }; + }; ++ ++&coherencyfab { ++ broken-idle; ++}; diff --git a/pkgs/patches-linux-5.15/307-mips_highmem_offset.patch b/pkgs/patches-linux-5.15/307-mips_highmem_offset.patch new file mode 100644 index 0000000..0529b0c --- /dev/null +++ b/pkgs/patches-linux-5.15/307-mips_highmem_offset.patch @@ -0,0 +1,19 @@ +From: Felix Fietkau <nbd@nbd.name> +Subject: kernel: adjust mips highmem offset to avoid the need for -mlong-calls on systems with >256M RAM + +Signed-off-by: Felix Fietkau <nbd@nbd.name> +--- + arch/mips/include/asm/mach-generic/spaces.h | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/arch/mips/include/asm/mach-generic/spaces.h ++++ b/arch/mips/include/asm/mach-generic/spaces.h +@@ -46,7 +46,7 @@ + * Memory above this physical address will be considered highmem. + */ + #ifndef HIGHMEM_START +-#define HIGHMEM_START _AC(0x20000000, UL) ++#define HIGHMEM_START _AC(0x10000000, UL) + #endif + + #endif /* CONFIG_32BIT */ diff --git a/pkgs/patches-linux-5.15/308-armada-xp-linksys-mamba-wan.patch b/pkgs/patches-linux-5.15/308-armada-xp-linksys-mamba-wan.patch new file mode 100644 index 0000000..4315abc --- /dev/null +++ b/pkgs/patches-linux-5.15/308-armada-xp-linksys-mamba-wan.patch @@ -0,0 +1,11 @@ +--- a/arch/arm/boot/dts/armada-xp-linksys-mamba.dts ++++ b/arch/arm/boot/dts/armada-xp-linksys-mamba.dts +@@ -387,7 +387,7 @@ + + port@4 { + reg = <4>; +- label = "internet"; ++ label = "wan"; + }; + + port@5 { diff --git a/pkgs/patches-linux-5.15/308-mips32r2_tune.patch b/pkgs/patches-linux-5.15/308-mips32r2_tune.patch new file mode 100644 index 0000000..ef92a5d --- /dev/null +++ b/pkgs/patches-linux-5.15/308-mips32r2_tune.patch @@ -0,0 +1,22 @@ +From: Felix Fietkau <nbd@nbd.name> +Subject: kernel: add -mtune=34kc to MIPS CFLAGS when building for mips32r2 + +This provides a good tradeoff across at least 24Kc-74Kc, while also +producing smaller code. + +Signed-off-by: Felix Fietkau <nbd@nbd.name> +--- + arch/mips/Makefile | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/arch/mips/Makefile ++++ b/arch/mips/Makefile +@@ -175,7 +175,7 @@ cflags-$(CONFIG_CPU_VR41XX) += -march=r4 + cflags-$(CONFIG_CPU_R4X00) += -march=r4600 -Wa,--trap + cflags-$(CONFIG_CPU_TX49XX) += -march=r4600 -Wa,--trap + cflags-$(CONFIG_CPU_MIPS32_R1) += -march=mips32 -Wa,--trap +-cflags-$(CONFIG_CPU_MIPS32_R2) += -march=mips32r2 -Wa,--trap ++cflags-$(CONFIG_CPU_MIPS32_R2) += -march=mips32r2 -mtune=34kc -Wa,--trap + cflags-$(CONFIG_CPU_MIPS32_R5) += -march=mips32r5 -Wa,--trap -modd-spreg + cflags-$(CONFIG_CPU_MIPS32_R6) += -march=mips32r6 -Wa,--trap -modd-spreg + cflags-$(CONFIG_CPU_MIPS64_R1) += -march=mips64 -Wa,--trap diff --git a/pkgs/patches-linux-5.15/309-MIPS-Add-CPU-option-reporting-to-proc-cpuinfo.patch b/pkgs/patches-linux-5.15/309-MIPS-Add-CPU-option-reporting-to-proc-cpuinfo.patch new file mode 100644 index 0000000..318c0b1 --- /dev/null +++ b/pkgs/patches-linux-5.15/309-MIPS-Add-CPU-option-reporting-to-proc-cpuinfo.patch @@ -0,0 +1,136 @@ +From 87ec87c2ad615c1a177cd08ef5fa29fc739f6e50 Mon Sep 17 00:00:00 2001 +From: Hauke Mehrtens <hauke@hauke-m.de> +Date: Sun, 23 Dec 2018 18:06:53 +0100 +Subject: [PATCH] MIPS: Add CPU option reporting to /proc/cpuinfo + +Many MIPS CPUs have optional CPU features which are not activates for +all CPU cores. Print the CPU options which are implemented in the core +in /proc/cpuinfo. This makes it possible to see what features are +supported and which are not supported. This should cover all standard +MIPS extensions, before it only printed information about the main MIPS +ASEs. + +Signed-off-by: Hauke Mehrtens <hauke@hauke-m.de> +--- + arch/mips/kernel/proc.c | 116 ++++++++++++++++++++++++++++++++++++++++ + 1 file changed, 116 insertions(+) + +--- a/arch/mips/kernel/proc.c ++++ b/arch/mips/kernel/proc.c +@@ -138,6 +138,116 @@ static int show_cpuinfo(struct seq_file + seq_printf(m, "micromips kernel\t: %s\n", + (read_c0_config3() & MIPS_CONF3_ISA_OE) ? "yes" : "no"); + } ++ ++ seq_printf(m, "Options implemented\t:"); ++ if (cpu_has_tlb) ++ seq_printf(m, "%s", " tlb"); ++ if (cpu_has_ftlb) ++ seq_printf(m, "%s", " ftlb"); ++ if (cpu_has_tlbinv) ++ seq_printf(m, "%s", " tlbinv"); ++ if (cpu_has_segments) ++ seq_printf(m, "%s", " segments"); ++ if (cpu_has_rixiex) ++ seq_printf(m, "%s", " rixiex"); ++ if (cpu_has_ldpte) ++ seq_printf(m, "%s", " ldpte"); ++ if (cpu_has_maar) ++ seq_printf(m, "%s", " maar"); ++ if (cpu_has_rw_llb) ++ seq_printf(m, "%s", " rw_llb"); ++ if (cpu_has_4kex) ++ seq_printf(m, "%s", " 4kex"); ++ if (cpu_has_3k_cache) ++ seq_printf(m, "%s", " 3k_cache"); ++ if (cpu_has_4k_cache) ++ seq_printf(m, "%s", " 4k_cache"); ++ if (cpu_has_tx39_cache) ++ seq_printf(m, "%s", " tx39_cache"); ++ if (cpu_has_octeon_cache) ++ seq_printf(m, "%s", " octeon_cache"); ++ if (cpu_has_fpu) ++ seq_printf(m, "%s", " fpu"); ++ if (cpu_has_32fpr) ++ seq_printf(m, "%s", " 32fpr"); ++ if (cpu_has_cache_cdex_p) ++ seq_printf(m, "%s", " cache_cdex_p"); ++ if (cpu_has_cache_cdex_s) ++ seq_printf(m, "%s", " cache_cdex_s"); ++ if (cpu_has_prefetch) ++ seq_printf(m, "%s", " prefetch"); ++ if (cpu_has_mcheck) ++ seq_printf(m, "%s", " mcheck"); ++ if (cpu_has_ejtag) ++ seq_printf(m, "%s", " ejtag"); ++ if (cpu_has_llsc) ++ seq_printf(m, "%s", " llsc"); ++ if (cpu_has_guestctl0ext) ++ seq_printf(m, "%s", " guestctl0ext"); ++ if (cpu_has_guestctl1) ++ seq_printf(m, "%s", " guestctl1"); ++ if (cpu_has_guestctl2) ++ seq_printf(m, "%s", " guestctl2"); ++ if (cpu_has_guestid) ++ seq_printf(m, "%s", " guestid"); ++ if (cpu_has_drg) ++ seq_printf(m, "%s", " drg"); ++ if (cpu_has_rixi) ++ seq_printf(m, "%s", " rixi"); ++ if (cpu_has_lpa) ++ seq_printf(m, "%s", " lpa"); ++ if (cpu_has_mvh) ++ seq_printf(m, "%s", " mvh"); ++ if (cpu_has_vtag_icache) ++ seq_printf(m, "%s", " vtag_icache"); ++ if (cpu_has_dc_aliases) ++ seq_printf(m, "%s", " dc_aliases"); ++ if (cpu_has_ic_fills_f_dc) ++ seq_printf(m, "%s", " ic_fills_f_dc"); ++ if (cpu_has_pindexed_dcache) ++ seq_printf(m, "%s", " pindexed_dcache"); ++ if (cpu_has_userlocal) ++ seq_printf(m, "%s", " userlocal"); ++ if (cpu_has_nofpuex) ++ seq_printf(m, "%s", " nofpuex"); ++ if (cpu_has_vint) ++ seq_printf(m, "%s", " vint"); ++ if (cpu_has_veic) ++ seq_printf(m, "%s", " veic"); ++ if (cpu_has_inclusive_pcaches) ++ seq_printf(m, "%s", " inclusive_pcaches"); ++ if (cpu_has_perf_cntr_intr_bit) ++ seq_printf(m, "%s", " perf_cntr_intr_bit"); ++ if (cpu_has_ufr) ++ seq_printf(m, "%s", " ufr"); ++ if (cpu_has_fre) ++ seq_printf(m, "%s", " fre"); ++ if (cpu_has_cdmm) ++ seq_printf(m, "%s", " cdmm"); ++ if (cpu_has_small_pages) ++ seq_printf(m, "%s", " small_pages"); ++ if (cpu_has_nan_legacy) ++ seq_printf(m, "%s", " nan_legacy"); ++ if (cpu_has_nan_2008) ++ seq_printf(m, "%s", " nan_2008"); ++ if (cpu_has_ebase_wg) ++ seq_printf(m, "%s", " ebase_wg"); ++ if (cpu_has_badinstr) ++ seq_printf(m, "%s", " badinstr"); ++ if (cpu_has_badinstrp) ++ seq_printf(m, "%s", " badinstrp"); ++ if (cpu_has_contextconfig) ++ seq_printf(m, "%s", " contextconfig"); ++ if (cpu_has_perf) ++ seq_printf(m, "%s", " perf"); ++ if (cpu_has_shared_ftlb_ram) ++ seq_printf(m, "%s", " shared_ftlb_ram"); ++ if (cpu_has_shared_ftlb_entries) ++ seq_printf(m, "%s", " shared_ftlb_entries"); ++ if (cpu_has_mipsmt_pertccounters) ++ seq_printf(m, "%s", " mipsmt_pertccounters"); ++ seq_printf(m, "\n"); ++ + seq_printf(m, "shadow register sets\t: %d\n", + cpu_data[n].srsets); + seq_printf(m, "kscratch registers\t: %d\n", diff --git a/pkgs/patches-linux-5.15/309-linksys-status-led.patch b/pkgs/patches-linux-5.15/309-linksys-status-led.patch new file mode 100644 index 0000000..e5e8357 --- /dev/null +++ b/pkgs/patches-linux-5.15/309-linksys-status-led.patch @@ -0,0 +1,50 @@ +--- a/arch/arm/boot/dts/armada-385-linksys.dtsi ++++ b/arch/arm/boot/dts/armada-385-linksys.dtsi +@@ -14,6 +14,13 @@ + compatible = "linksys,armada385", "marvell,armada385", + "marvell,armada380"; + ++ aliases { ++ led-boot = &led_power; ++ led-failsafe = &led_power; ++ led-running = &led_power; ++ led-upgrade = &led_power; ++ }; ++ + chosen { + stdout-path = "serial0:115200n8"; + }; +@@ -71,7 +78,7 @@ + pinctrl-0 = <&gpio_leds_pins>; + pinctrl-names = "default"; + +- power { ++ led_power: power { + gpios = <&gpio1 23 GPIO_ACTIVE_HIGH>; + default-state = "on"; + }; +--- a/arch/arm/boot/dts/armada-xp-linksys-mamba.dts ++++ b/arch/arm/boot/dts/armada-xp-linksys-mamba.dts +@@ -26,6 +26,13 @@ + compatible = "linksys,mamba", "marvell,armadaxp-mv78230", + "marvell,armadaxp", "marvell,armada-370-xp"; + ++ aliases { ++ led-boot = &led_power; ++ led-failsafe = &led_power; ++ led-running = &led_power; ++ led-upgrade = &led_power; ++ }; ++ + chosen { + bootargs = "console=ttyS0,115200"; + stdout-path = &uart0; +@@ -197,7 +204,7 @@ + pinctrl-0 = <&power_led_pin>; + pinctrl-names = "default"; + +- power { ++ led_power: power { + label = "mamba:white:power"; + gpios = <&gpio1 8 GPIO_ACTIVE_HIGH>; + default-state = "on"; diff --git a/pkgs/patches-linux-5.15/310-arm_module_unresolved_weak_sym.patch b/pkgs/patches-linux-5.15/310-arm_module_unresolved_weak_sym.patch new file mode 100644 index 0000000..191dc6a --- /dev/null +++ b/pkgs/patches-linux-5.15/310-arm_module_unresolved_weak_sym.patch @@ -0,0 +1,22 @@ +From: Felix Fietkau <nbd@nbd.name> +Subject: fix errors in unresolved weak symbols on arm + +lede-commit: 570699d4838a907c3ef9f2819bf19eb72997b32f +Signed-off-by: Felix Fietkau <nbd@nbd.name> +--- + arch/arm/kernel/module.c | 4 ++++ + 1 file changed, 4 insertions(+) + +--- a/arch/arm/kernel/module.c ++++ b/arch/arm/kernel/module.c +@@ -105,6 +105,10 @@ apply_relocate(Elf32_Shdr *sechdrs, cons + return -ENOEXEC; + } + ++ if ((IS_ERR_VALUE(sym->st_value) || !sym->st_value) && ++ ELF_ST_BIND(sym->st_info) == STB_WEAK) ++ continue; ++ + loc = dstsec->sh_addr + rel->r_offset; + + switch (ELF32_R_TYPE(rel->r_info)) { diff --git a/pkgs/patches-linux-5.15/310-linksys-use-eth0-as-cpu-port.patch b/pkgs/patches-linux-5.15/310-linksys-use-eth0-as-cpu-port.patch new file mode 100644 index 0000000..84d49a0 --- /dev/null +++ b/pkgs/patches-linux-5.15/310-linksys-use-eth0-as-cpu-port.patch @@ -0,0 +1,25 @@ +--- a/arch/arm/boot/dts/armada-385-linksys.dtsi ++++ b/arch/arm/boot/dts/armada-385-linksys.dtsi +@@ -116,7 +116,7 @@ + }; + + ð2 { +- status = "okay"; ++ status = "disabled"; + phy-mode = "sgmii"; + buffer-manager = <&bm>; + bm,pool-long = <2>; +@@ -200,10 +200,10 @@ + label = "wan"; + }; + +- port@5 { +- reg = <5>; ++ port@6 { ++ reg = <6>; + label = "cpu"; +- ethernet = <ð2>; ++ ethernet = <ð0>; + + fixed-link { + speed = <1000>; diff --git a/pkgs/patches-linux-5.15/311-adjust-compatible-for-linksys.patch b/pkgs/patches-linux-5.15/311-adjust-compatible-for-linksys.patch new file mode 100644 index 0000000..a5d3e63 --- /dev/null +++ b/pkgs/patches-linux-5.15/311-adjust-compatible-for-linksys.patch @@ -0,0 +1,68 @@ +--- a/arch/arm/boot/dts/armada-385-linksys-rango.dts ++++ b/arch/arm/boot/dts/armada-385-linksys-rango.dts +@@ -12,8 +12,8 @@ + + / { + model = "Linksys WRT3200ACM"; +- compatible = "linksys,rango", "linksys,armada385", "marvell,armada385", +- "marvell,armada380"; ++ compatible = "linksys,wrt3200acm", "linksys,rango", "linksys,armada385", ++ "marvell,armada385", "marvell,armada380"; + }; + + &expander0 { +--- a/arch/arm/boot/dts/armada-xp-linksys-mamba.dts ++++ b/arch/arm/boot/dts/armada-xp-linksys-mamba.dts +@@ -22,9 +22,10 @@ + #include "armada-xp-mv78230.dtsi" + + / { +- model = "Linksys WRT1900AC"; +- compatible = "linksys,mamba", "marvell,armadaxp-mv78230", +- "marvell,armadaxp", "marvell,armada-370-xp"; ++ model = "Linksys WRT1900AC v1"; ++ compatible = "linksys,wrt1900ac-v1", "linksys,mamba", ++ "marvell,armadaxp-mv78230", "marvell,armadaxp", ++ "marvell,armada-370-xp"; + + aliases { + led-boot = &led_power; +--- a/arch/arm/boot/dts/armada-385-linksys-cobra.dts ++++ b/arch/arm/boot/dts/armada-385-linksys-cobra.dts +@@ -9,8 +9,9 @@ + #include "armada-385-linksys.dtsi" + + / { +- model = "Linksys WRT1900ACv2"; +- compatible = "linksys,cobra", "linksys,armada385", "marvell,armada385", ++ model = "Linksys WRT1900AC v2"; ++ compatible = "linksys,wrt1900ac-v2", "linksys,cobra", ++ "linksys,armada385", "marvell,armada385", + "marvell,armada380"; + }; + +--- a/arch/arm/boot/dts/armada-385-linksys-caiman.dts ++++ b/arch/arm/boot/dts/armada-385-linksys-caiman.dts +@@ -10,8 +10,8 @@ + + / { + model = "Linksys WRT1200AC"; +- compatible = "linksys,caiman", "linksys,armada385", "marvell,armada385", +- "marvell,armada380"; ++ compatible = "linksys,wrt1200ac", "linksys,caiman", "linksys,armada385", ++ "marvell,armada385", "marvell,armada380"; + }; + + &expander0 { +--- a/arch/arm/boot/dts/armada-385-linksys-shelby.dts ++++ b/arch/arm/boot/dts/armada-385-linksys-shelby.dts +@@ -10,7 +10,8 @@ + + / { + model = "Linksys WRT1900ACS"; +- compatible = "linksys,shelby", "linksys,armada385", "marvell,armada385", ++ compatible = "linksys,wrt1900acs", "linksys,shelby", ++ "linksys,armada385", "marvell,armada385", + "marvell,armada380"; + }; + diff --git a/pkgs/patches-linux-5.15/312-ARM-dts-armada388-clearfog-emmc-on-clearfog-base.patch b/pkgs/patches-linux-5.15/312-ARM-dts-armada388-clearfog-emmc-on-clearfog-base.patch new file mode 100644 index 0000000..dd2bef7 --- /dev/null +++ b/pkgs/patches-linux-5.15/312-ARM-dts-armada388-clearfog-emmc-on-clearfog-base.patch @@ -0,0 +1,87 @@ +From 8137da20701c776ad3481115305a5e8e410871ba Mon Sep 17 00:00:00 2001 +From: Russell King <rmk+kernel@armlinux.org.uk> +Date: Tue, 29 Nov 2016 10:15:45 +0000 +Subject: ARM: dts: armada388-clearfog: emmc on clearfog base + +Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk> +--- + arch/arm/boot/dts/armada-388-clearfog-base.dts | 1 + + .../dts/armada-38x-solidrun-microsom-emmc.dtsi | 62 ++++++++++++++++++++++ + 2 files changed, 63 insertions(+) + create mode 100644 arch/arm/boot/dts/armada-38x-solidrun-microsom-emmc.dtsi + +--- a/arch/arm/boot/dts/armada-388-clearfog-base.dts ++++ b/arch/arm/boot/dts/armada-388-clearfog-base.dts +@@ -7,6 +7,7 @@ + + /dts-v1/; + #include "armada-388-clearfog.dtsi" ++#include "armada-38x-solidrun-microsom-emmc.dtsi" + + / { + model = "SolidRun Clearfog Base A1"; +--- /dev/null ++++ b/arch/arm/boot/dts/armada-38x-solidrun-microsom-emmc.dtsi +@@ -0,0 +1,62 @@ ++/* ++ * Device Tree file for SolidRun Armada 38x Microsom add-on for eMMC ++ * ++ * Copyright (C) 2015 Russell King ++ * ++ * This board is in development; the contents of this file work with ++ * the A1 rev 2.0 of the board, which does not represent final ++ * production board. Things will change, don't expect this file to ++ * remain compatible info the future. ++ * ++ * This file is dual-licensed: you can use it either under the terms ++ * of the GPL or the X11 license, at your option. Note that this dual ++ * licensing only applies to this file, and not this project as a ++ * whole. ++ * ++ * a) This file is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * version 2 as published by the Free Software Foundation. ++ * ++ * This file is distributed in the hope that it will be useful ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * Or, alternatively ++ * ++ * b) Permission is hereby granted, free of charge, to any person ++ * obtaining a copy of this software and associated documentation ++ * files (the "Software"), to deal in the Software without ++ * restriction, including without limitation the rights to use ++ * copy, modify, merge, publish, distribute, sublicense, and/or ++ * sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following ++ * conditions: ++ * ++ * The above copyright notice and this permission notice shall be ++ * included in all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED , WITHOUT WARRANTY OF ANY KIND ++ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES ++ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND ++ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT ++ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY ++ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING ++ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ++ * OTHER DEALINGS IN THE SOFTWARE. ++ */ ++/ { ++ soc { ++ internal-regs { ++ sdhci@d8000 { ++ bus-width = <4>; ++ no-1-8-v; ++ non-removable; ++ pinctrl-0 = <µsom_sdhci_pins>; ++ pinctrl-names = "default"; ++ status = "okay"; ++ wp-inverted; ++ }; ++ }; ++ }; ++}; diff --git a/pkgs/patches-linux-5.15/313-helios4-dts-status-led-alias.patch b/pkgs/patches-linux-5.15/313-helios4-dts-status-led-alias.patch new file mode 100644 index 0000000..607f436 --- /dev/null +++ b/pkgs/patches-linux-5.15/313-helios4-dts-status-led-alias.patch @@ -0,0 +1,28 @@ +--- a/arch/arm/boot/dts/armada-388-helios4.dts ++++ b/arch/arm/boot/dts/armada-388-helios4.dts +@@ -15,6 +15,13 @@ + model = "Helios4"; + compatible = "kobol,helios4", "marvell,armada388", + "marvell,armada385", "marvell,armada380"; ++ ++ aliases { ++ led-boot = &led_status; ++ led-failsafe = &led_status; ++ led-running = &led_status; ++ led-upgrade = &led_status; ++ }; + + memory { + device_type = "memory"; +@@ -73,10 +80,9 @@ + pinctrl-names = "default"; + pinctrl-0 = <&helios_system_led_pins>; + +- status-led { ++ led_status: status-led { + label = "helios4:green:status"; + gpios = <&gpio0 24 GPIO_ACTIVE_LOW>; +- linux,default-trigger = "heartbeat"; + default-state = "on"; + }; + diff --git a/pkgs/patches-linux-5.15/315-armada-xp-linksys-mamba-resize-kernel.patch b/pkgs/patches-linux-5.15/315-armada-xp-linksys-mamba-resize-kernel.patch new file mode 100644 index 0000000..f1fddce --- /dev/null +++ b/pkgs/patches-linux-5.15/315-armada-xp-linksys-mamba-resize-kernel.patch @@ -0,0 +1,37 @@ +From 258233f00bcd013050efee00c5d9128ef8cd62dd Mon Sep 17 00:00:00 2001 +From: Tad <tad@spotco.us> +Date: Fri, 5 Feb 2021 22:32:11 -0500 +Subject: [PATCH] ARM: dts: armada-xp-linksys-mamba: Increase kernel + partition to 4MB + +Signed-off-by: Tad Davanzo <tad@spotco.us> +--- + arch/arm/boot/dts/armada-xp-linksys-mamba.dts | 8 ++++---- + 1 file changed, 4 insertions(+), 4 deletions(-) + +--- a/arch/arm/boot/dts/armada-xp-linksys-mamba.dts ++++ b/arch/arm/boot/dts/armada-xp-linksys-mamba.dts +@@ -456,9 +456,9 @@ + reg = <0xa00000 0x2800000>; /* 40MB */ + }; + +- partition@d00000 { ++ partition@e00000 { + label = "rootfs1"; +- reg = <0xd00000 0x2500000>; /* 37MB */ ++ reg = <0xe00000 0x2400000>; /* 36MB */ + }; + + /* kernel2 overlaps with rootfs2 by design */ +@@ -467,9 +467,9 @@ + reg = <0x3200000 0x2800000>; /* 40MB */ + }; + +- partition@3500000 { ++ partition@3600000 { + label = "rootfs2"; +- reg = <0x3500000 0x2500000>; /* 37MB */ ++ reg = <0x3600000 0x2400000>; /* 36MB */ + }; + + /* diff --git a/pkgs/patches-linux-5.15/316-armada-370-dts-fix-crypto-engine.patch b/pkgs/patches-linux-5.15/316-armada-370-dts-fix-crypto-engine.patch new file mode 100644 index 0000000..1937887 --- /dev/null +++ b/pkgs/patches-linux-5.15/316-armada-370-dts-fix-crypto-engine.patch @@ -0,0 +1,29 @@ +--- a/arch/arm/boot/dts/armada-370.dtsi ++++ b/arch/arm/boot/dts/armada-370.dtsi +@@ -234,7 +234,7 @@ + clocks = <&gateclk 23>; + clock-names = "cesa0"; + marvell,crypto-srams = <&crypto_sram>; +- marvell,crypto-sram-size = <0x7e0>; ++ marvell,crypto-sram-size = <0x800>; + }; + }; + +@@ -255,12 +255,17 @@ + * cpuidle workaround. + */ + idle-sram@0 { ++ status = "disabled"; + reg = <0x0 0x20>; + }; + }; + }; + }; + ++&coherencyfab { ++ broken-idle; ++}; ++ + /* + * Default UART pinctrl setting without RTS/CTS, can be overwritten on + * board level if a different configuration is used. diff --git a/pkgs/patches-linux-5.15/321-powerpc_crtsavres_prereq.patch b/pkgs/patches-linux-5.15/321-powerpc_crtsavres_prereq.patch new file mode 100644 index 0000000..f1942e2 --- /dev/null +++ b/pkgs/patches-linux-5.15/321-powerpc_crtsavres_prereq.patch @@ -0,0 +1,38 @@ +From 107c0964cb8db7ca28ac5199426414fdab3c274d Mon Sep 17 00:00:00 2001 +From: "Alexandros C. Couloumbis" <alex@ozo.com> +Date: Fri, 7 Jul 2017 17:14:51 +0200 +Subject: hack: arch: powerpc: drop register save/restore library from modules + +Upstream GCC uses a libgcc function for saving/restoring registers. This +makes the code bigger, and upstream kernels need to carry that function +for every single kernel module. Our GCC is patched to avoid those +references, so we can drop the extra bloat for modules. + +lede-commit: e8e1084654f50904e6bf77b70b2de3f137d7b3ec +Signed-off-by: Alexandros C. Couloumbis <alex@ozo.com> +--- + arch/powerpc/Makefile | 1 - + 1 file changed, 1 deletion(-) + +--- a/arch/powerpc/Makefile ++++ b/arch/powerpc/Makefile +@@ -44,19 +44,6 @@ machine-$(CONFIG_PPC64) += 64 + machine-$(CONFIG_CPU_LITTLE_ENDIAN) += le + UTS_MACHINE := $(subst $(space),,$(machine-y)) + +-# XXX This needs to be before we override LD below +-ifdef CONFIG_PPC32 +-KBUILD_LDFLAGS_MODULE += arch/powerpc/lib/crtsavres.o +-else +-ifeq ($(call ld-ifversion, -ge, 22500, y),y) +-# Have the linker provide sfpr if possible. +-# There is a corresponding test in arch/powerpc/lib/Makefile +-KBUILD_LDFLAGS_MODULE += --save-restore-funcs +-else +-KBUILD_LDFLAGS_MODULE += arch/powerpc/lib/crtsavres.o +-endif +-endif +- + ifdef CONFIG_CPU_LITTLE_ENDIAN + KBUILD_CFLAGS += -mlittle-endian + KBUILD_LDFLAGS += -EL diff --git a/pkgs/patches-linux-5.15/330-MIPS-kexec-Accept-command-line-parameters-from-users.patch b/pkgs/patches-linux-5.15/330-MIPS-kexec-Accept-command-line-parameters-from-users.patch new file mode 100644 index 0000000..3a5f5a2 --- /dev/null +++ b/pkgs/patches-linux-5.15/330-MIPS-kexec-Accept-command-line-parameters-from-users.patch @@ -0,0 +1,283 @@ +From: Yousong Zhou <yszhou4tech@gmail.com> +Subject: MIPS: kexec: Accept command line parameters from userspace. + +Signed-off-by: Yousong Zhou <yszhou4tech@gmail.com> +--- + arch/mips/kernel/machine_kexec.c | 153 +++++++++++++++++++++++++++++++----- + arch/mips/kernel/machine_kexec.h | 20 +++++ + arch/mips/kernel/relocate_kernel.S | 21 +++-- + 3 files changed, 167 insertions(+), 27 deletions(-) + create mode 100644 arch/mips/kernel/machine_kexec.h + +--- a/arch/mips/kernel/machine_kexec.c ++++ b/arch/mips/kernel/machine_kexec.c +@@ -9,14 +9,11 @@ + #include <linux/delay.h> + #include <linux/libfdt.h> + ++#include <asm/bootinfo.h> + #include <asm/cacheflush.h> + #include <asm/page.h> +- +-extern const unsigned char relocate_new_kernel[]; +-extern const size_t relocate_new_kernel_size; +- +-extern unsigned long kexec_start_address; +-extern unsigned long kexec_indirection_page; ++#include <linux/uaccess.h> ++#include "machine_kexec.h" + + static unsigned long reboot_code_buffer; + +@@ -30,6 +27,101 @@ void (*_crash_smp_send_stop)(void) = NUL + void (*_machine_kexec_shutdown)(void) = NULL; + void (*_machine_crash_shutdown)(struct pt_regs *regs) = NULL; + ++static void machine_kexec_print_args(void) ++{ ++ unsigned long argc = (int)kexec_args[0]; ++ int i; ++ ++ pr_info("kexec_args[0] (argc): %lu\n", argc); ++ pr_info("kexec_args[1] (argv): %p\n", (void *)kexec_args[1]); ++ pr_info("kexec_args[2] (env ): %p\n", (void *)kexec_args[2]); ++ pr_info("kexec_args[3] (desc): %p\n", (void *)kexec_args[3]); ++ ++ for (i = 0; i < argc; i++) { ++ pr_info("kexec_argv[%d] = %p, %s\n", ++ i, kexec_argv[i], kexec_argv[i]); ++ } ++} ++ ++static void machine_kexec_init_argv(struct kimage *image) ++{ ++ void __user *buf = NULL; ++ size_t bufsz; ++ size_t size; ++ int i; ++ ++ bufsz = 0; ++ for (i = 0; i < image->nr_segments; i++) { ++ struct kexec_segment *seg; ++ ++ seg = &image->segment[i]; ++ if (seg->bufsz < 6) ++ continue; ++ ++ if (strncmp((char *) seg->buf, "kexec ", 6)) ++ continue; ++ ++ buf = seg->buf; ++ bufsz = seg->bufsz; ++ break; ++ } ++ ++ if (!buf) ++ return; ++ ++ size = KEXEC_COMMAND_LINE_SIZE; ++ size = min(size, bufsz); ++ if (size < bufsz) ++ pr_warn("kexec command line truncated to %zd bytes\n", size); ++ ++ /* Copy to kernel space */ ++ if (copy_from_user(kexec_argv_buf, buf, size)) ++ pr_warn("kexec command line copy to kernel space failed\n"); ++ ++ kexec_argv_buf[size - 1] = 0; ++} ++ ++static void machine_kexec_parse_argv(struct kimage *image) ++{ ++ char *reboot_code_buffer; ++ int reloc_delta; ++ char *ptr; ++ int argc; ++ int i; ++ ++ ptr = kexec_argv_buf; ++ argc = 0; ++ ++ /* ++ * convert command line string to array of parameters ++ * (as bootloader does). ++ */ ++ while (ptr && *ptr && (KEXEC_MAX_ARGC > argc)) { ++ if (*ptr == ' ') { ++ *ptr++ = '\0'; ++ continue; ++ } ++ ++ kexec_argv[argc++] = ptr; ++ ptr = strchr(ptr, ' '); ++ } ++ ++ if (!argc) ++ return; ++ ++ kexec_args[0] = argc; ++ kexec_args[1] = (unsigned long)kexec_argv; ++ kexec_args[2] = 0; ++ kexec_args[3] = 0; ++ ++ reboot_code_buffer = page_address(image->control_code_page); ++ reloc_delta = reboot_code_buffer - (char *)kexec_relocate_new_kernel; ++ ++ kexec_args[1] += reloc_delta; ++ for (i = 0; i < argc; i++) ++ kexec_argv[i] += reloc_delta; ++} ++ + static void kexec_image_info(const struct kimage *kimage) + { + unsigned long i; +@@ -99,6 +191,18 @@ machine_kexec_prepare(struct kimage *kim + #endif + + kexec_image_info(kimage); ++ /* ++ * Whenever arguments passed from kexec-tools, Init the arguments as ++ * the original ones to try avoiding booting failure. ++ */ ++ ++ kexec_args[0] = fw_arg0; ++ kexec_args[1] = fw_arg1; ++ kexec_args[2] = fw_arg2; ++ kexec_args[3] = fw_arg3; ++ ++ machine_kexec_init_argv(kimage); ++ machine_kexec_parse_argv(kimage); + + if (_machine_kexec_prepare) + return _machine_kexec_prepare(kimage); +@@ -161,7 +265,7 @@ machine_crash_shutdown(struct pt_regs *r + void kexec_nonboot_cpu_jump(void) + { + local_flush_icache_range((unsigned long)relocated_kexec_smp_wait, +- reboot_code_buffer + relocate_new_kernel_size); ++ reboot_code_buffer + KEXEC_RELOCATE_NEW_KERNEL_SIZE); + + relocated_kexec_smp_wait(NULL); + } +@@ -199,7 +303,7 @@ void kexec_reboot(void) + * machine_kexec() CPU. + */ + local_flush_icache_range(reboot_code_buffer, +- reboot_code_buffer + relocate_new_kernel_size); ++ reboot_code_buffer + KEXEC_RELOCATE_NEW_KERNEL_SIZE); + + do_kexec = (void *)reboot_code_buffer; + do_kexec(); +@@ -212,10 +316,12 @@ machine_kexec(struct kimage *image) + unsigned long *ptr; + + reboot_code_buffer = +- (unsigned long)page_address(image->control_code_page); ++ (unsigned long)page_address(image->control_code_page); ++ pr_info("reboot_code_buffer = %p\n", (void *)reboot_code_buffer); + + kexec_start_address = + (unsigned long) phys_to_virt(image->start); ++ pr_info("kexec_start_address = %p\n", (void *)kexec_start_address); + + if (image->type == KEXEC_TYPE_DEFAULT) { + kexec_indirection_page = +@@ -223,9 +329,19 @@ machine_kexec(struct kimage *image) + } else { + kexec_indirection_page = (unsigned long)&image->head; + } ++ pr_info("kexec_indirection_page = %p\n", (void *)kexec_indirection_page); + +- memcpy((void*)reboot_code_buffer, relocate_new_kernel, +- relocate_new_kernel_size); ++ pr_info("Where is memcpy: %p\n", memcpy); ++ pr_info("kexec_relocate_new_kernel = %p, kexec_relocate_new_kernel_end = %p\n", ++ (void *)kexec_relocate_new_kernel, &kexec_relocate_new_kernel_end); ++ pr_info("Copy %lu bytes from %p to %p\n", KEXEC_RELOCATE_NEW_KERNEL_SIZE, ++ (void *)kexec_relocate_new_kernel, (void *)reboot_code_buffer); ++ memcpy((void*)reboot_code_buffer, kexec_relocate_new_kernel, ++ KEXEC_RELOCATE_NEW_KERNEL_SIZE); ++ ++ pr_info("Before _print_args().\n"); ++ machine_kexec_print_args(); ++ pr_info("Before eval loop.\n"); + + /* + * The generic kexec code builds a page list with physical +@@ -256,7 +372,7 @@ machine_kexec(struct kimage *image) + #ifdef CONFIG_SMP + /* All secondary cpus now may jump to kexec_wait cycle */ + relocated_kexec_smp_wait = reboot_code_buffer + +- (void *)(kexec_smp_wait - relocate_new_kernel); ++ (void *)(kexec_smp_wait - kexec_relocate_new_kernel); + smp_wmb(); + atomic_set(&kexec_ready_to_reboot, 1); + #endif +--- /dev/null ++++ b/arch/mips/kernel/machine_kexec.h +@@ -0,0 +1,20 @@ ++#ifndef _MACHINE_KEXEC_H ++#define _MACHINE_KEXEC_H ++ ++#ifndef __ASSEMBLY__ ++extern const unsigned char kexec_relocate_new_kernel[]; ++extern unsigned long kexec_relocate_new_kernel_end; ++extern unsigned long kexec_start_address; ++extern unsigned long kexec_indirection_page; ++ ++extern char kexec_argv_buf[]; ++extern char *kexec_argv[]; ++ ++#define KEXEC_RELOCATE_NEW_KERNEL_SIZE ((unsigned long)&kexec_relocate_new_kernel_end - (unsigned long)kexec_relocate_new_kernel) ++#endif /* !__ASSEMBLY__ */ ++ ++#define KEXEC_COMMAND_LINE_SIZE 256 ++#define KEXEC_ARGV_SIZE (KEXEC_COMMAND_LINE_SIZE / 16) ++#define KEXEC_MAX_ARGC (KEXEC_ARGV_SIZE / sizeof(long)) ++ ++#endif +--- a/arch/mips/kernel/relocate_kernel.S ++++ b/arch/mips/kernel/relocate_kernel.S +@@ -10,10 +10,11 @@ + #include <asm/mipsregs.h> + #include <asm/stackframe.h> + #include <asm/addrspace.h> ++#include "machine_kexec.h" + + #include <kernel-entry-init.h> + +-LEAF(relocate_new_kernel) ++LEAF(kexec_relocate_new_kernel) + PTR_L a0, arg0 + PTR_L a1, arg1 + PTR_L a2, arg2 +@@ -98,7 +99,7 @@ done: + #endif + /* jump to kexec_start_address */ + j s1 +- END(relocate_new_kernel) ++ END(kexec_relocate_new_kernel) + + #ifdef CONFIG_SMP + /* +@@ -181,9 +182,15 @@ kexec_indirection_page: + PTR_WD 0 + .size kexec_indirection_page, PTRSIZE + +-relocate_new_kernel_end: ++kexec_argv_buf: ++ EXPORT(kexec_argv_buf) ++ .skip KEXEC_COMMAND_LINE_SIZE ++ .size kexec_argv_buf, KEXEC_COMMAND_LINE_SIZE ++ ++kexec_argv: ++ EXPORT(kexec_argv) ++ .skip KEXEC_ARGV_SIZE ++ .size kexec_argv, KEXEC_ARGV_SIZE + +-relocate_new_kernel_size: +- EXPORT(relocate_new_kernel_size) +- PTR_WD relocate_new_kernel_end - relocate_new_kernel +- .size relocate_new_kernel_size, PTRSIZE ++kexec_relocate_new_kernel_end: ++ EXPORT(kexec_relocate_new_kernel_end) diff --git a/pkgs/patches-linux-5.15/332-arc-add-OWRTDTB-section.patch b/pkgs/patches-linux-5.15/332-arc-add-OWRTDTB-section.patch new file mode 100644 index 0000000..30158cf --- /dev/null +++ b/pkgs/patches-linux-5.15/332-arc-add-OWRTDTB-section.patch @@ -0,0 +1,84 @@ +From bb0c3b0175240bf152fd7c644821a0cf9f77c37c Mon Sep 17 00:00:00 2001 +From: Evgeniy Didin <Evgeniy.Didin@synopsys.com> +Date: Fri, 15 Mar 2019 18:53:38 +0300 +Subject: [PATCH] arc add OWRTDTB section + +This change allows OpenWRT to patch resulting kernel binary with +external .dtb. + +That allows us to re-use exactky the same vmlinux on different boards +given its ARC core configurations match (at least cache line sizes etc). + +""patch-dtb" searches for ASCII "OWRTDTB:" strign and copies external +.dtb right after it, keeping the string in place. + +Signed-off-by: Eugeniy Paltsev <Eugeniy.Paltsev@synopsys.com> +Signed-off-by: Alexey Brodkin <abrodkin@synopsys.com> +Signed-off-by: Evgeniy Didin <Evgeniy.Didin@synopsys.com> +--- + arch/arc/kernel/head.S | 10 ++++++++++ + arch/arc/kernel/setup.c | 4 +++- + arch/arc/kernel/vmlinux.lds.S | 13 +++++++++++++ + 3 files changed, 26 insertions(+), 1 deletion(-) + +--- a/arch/arc/kernel/head.S ++++ b/arch/arc/kernel/head.S +@@ -88,6 +88,16 @@ + DSP_EARLY_INIT + .endm + ++ ; Here "patch-dtb" will embed external .dtb ++ ; Note "patch-dtb" searches for ASCII "OWRTDTB:" string ++ ; and pastes .dtb right after it, hense the string precedes ++ ; __image_dtb symbol. ++ .section .owrt, "aw",@progbits ++ .ascii "OWRTDTB:" ++ENTRY(__image_dtb) ++ .fill 0x4000 ++END(__image_dtb) ++ + .section .init.text, "ax",@progbits + + ;---------------------------------------------------------------- +--- a/arch/arc/kernel/setup.c ++++ b/arch/arc/kernel/setup.c +@@ -495,6 +495,8 @@ static inline bool uboot_arg_invalid(uns + /* We always pass 0 as magic from U-boot */ + #define UBOOT_MAGIC_VALUE 0 + ++extern struct boot_param_header __image_dtb; ++ + void __init handle_uboot_args(void) + { + bool use_embedded_dtb = true; +@@ -533,7 +535,7 @@ void __init handle_uboot_args(void) + ignore_uboot_args: + + if (use_embedded_dtb) { +- machine_desc = setup_machine_fdt(__dtb_start); ++ machine_desc = setup_machine_fdt(&__image_dtb); + if (!machine_desc) + panic("Embedded DT invalid\n"); + } +--- a/arch/arc/kernel/vmlinux.lds.S ++++ b/arch/arc/kernel/vmlinux.lds.S +@@ -27,6 +27,19 @@ SECTIONS + + . = CONFIG_LINUX_LINK_BASE; + ++ /* ++ * In OpenWRT we want to patch built binary embedding .dtb of choice. ++ * This is implemented with "patch-dtb" utility which searches for ++ * "OWRTDTB:" string in first 16k of image and if it is found ++ * copies .dtb right after mentioned string. ++ * ++ * Note: "OWRTDTB:" won't be overwritten with .dtb, .dtb will follow it. ++ */ ++ .owrt : { ++ *(.owrt) ++ . = ALIGN(PAGE_SIZE); ++ } ++ + _int_vec_base_lds = .; + .vector : { + *(.vector) diff --git a/pkgs/patches-linux-5.15/333-arc-enable-unaligned-access-in-kernel-mode.patch b/pkgs/patches-linux-5.15/333-arc-enable-unaligned-access-in-kernel-mode.patch new file mode 100644 index 0000000..1848a84 --- /dev/null +++ b/pkgs/patches-linux-5.15/333-arc-enable-unaligned-access-in-kernel-mode.patch @@ -0,0 +1,24 @@ +From: Alexey Brodkin <abrodkin@synopsys.com> +Subject: arc: enable unaligned access in kernel mode + +This enables misaligned access handling even in kernel mode. +Some wireless drivers (ath9k-htc and mt7601u) use misaligned accesses +here and there and to cope with that without fixing stuff in the drivers +we're just gracefully handling it on ARC. + +Signed-off-by: Alexey Brodkin <abrodkin@synopsys.com> +--- + arch/arc/kernel/unaligned.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/arch/arc/kernel/unaligned.c ++++ b/arch/arc/kernel/unaligned.c +@@ -202,7 +202,7 @@ int misaligned_fixup(unsigned long addre + char buf[TASK_COMM_LEN]; + + /* handle user mode only and only if enabled by sysadmin */ +- if (!user_mode(regs) || !unaligned_enabled) ++ if (!unaligned_enabled) + return 1; + + if (no_unaligned_warning) { diff --git a/pkgs/patches-linux-5.15/342-powerpc-Enable-kernel-XZ-compression-option-on-PPC_8.patch b/pkgs/patches-linux-5.15/342-powerpc-Enable-kernel-XZ-compression-option-on-PPC_8.patch new file mode 100644 index 0000000..082b122 --- /dev/null +++ b/pkgs/patches-linux-5.15/342-powerpc-Enable-kernel-XZ-compression-option-on-PPC_8.patch @@ -0,0 +1,25 @@ +From 66770a004afe10df11d3902e16eaa0c2c39436bb Mon Sep 17 00:00:00 2001 +From: Pawel Dembicki <paweldembicki@gmail.com> +Date: Fri, 24 May 2019 17:56:19 +0200 +Subject: [PATCH] powerpc: Enable kernel XZ compression option on PPC_85xx + +Enable kernel XZ compression option on PPC_85xx. Tested with +simpleImage on TP-Link TL-WDR4900 (Freescale P1014 processor). + +Suggested-by: Christian Lamparter <chunkeey@gmail.com> +Signed-off-by: Pawel Dembicki <paweldembicki@gmail.com> +--- + arch/powerpc/Kconfig | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/arch/powerpc/Kconfig ++++ b/arch/powerpc/Kconfig +@@ -221,7 +221,7 @@ config PPC + select HAVE_KERNEL_GZIP + select HAVE_KERNEL_LZMA if DEFAULT_UIMAGE + select HAVE_KERNEL_LZO if DEFAULT_UIMAGE +- select HAVE_KERNEL_XZ if PPC_BOOK3S || 44x ++ select HAVE_KERNEL_XZ if PPC_BOOK3S || 44x || PPC_85xx + select HAVE_KPROBES + select HAVE_KPROBES_ON_FTRACE + select HAVE_KRETPROBES diff --git a/pkgs/patches-linux-5.15/343-netfilter-nft_flow_offload-handle-netdevice-events-f.patch b/pkgs/patches-linux-5.15/343-netfilter-nft_flow_offload-handle-netdevice-events-f.patch new file mode 100644 index 0000000..19ec9d9 --- /dev/null +++ b/pkgs/patches-linux-5.15/343-netfilter-nft_flow_offload-handle-netdevice-events-f.patch @@ -0,0 +1,106 @@ +From: Pablo Neira Ayuso <pablo@netfilter.org> +Date: Thu, 25 Jan 2018 12:58:55 +0100 +Subject: [PATCH] netfilter: nft_flow_offload: handle netdevice events from + nf_flow_table + +Move the code that deals with device events to the core. + +Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org> +--- + +--- a/net/netfilter/nf_flow_table_core.c ++++ b/net/netfilter/nf_flow_table_core.c +@@ -608,13 +608,41 @@ void nf_flow_table_free(struct nf_flowta + } + EXPORT_SYMBOL_GPL(nf_flow_table_free); + ++static int nf_flow_table_netdev_event(struct notifier_block *this, ++ unsigned long event, void *ptr) ++{ ++ struct net_device *dev = netdev_notifier_info_to_dev(ptr); ++ ++ if (event != NETDEV_DOWN) ++ return NOTIFY_DONE; ++ ++ nf_flow_table_cleanup(dev); ++ ++ return NOTIFY_DONE; ++} ++ ++static struct notifier_block flow_offload_netdev_notifier = { ++ .notifier_call = nf_flow_table_netdev_event, ++}; ++ + static int __init nf_flow_table_module_init(void) + { +- return nf_flow_table_offload_init(); ++ int ret; ++ ++ ret = nf_flow_table_offload_init(); ++ if (ret) ++ return ret; ++ ++ ret = register_netdevice_notifier(&flow_offload_netdev_notifier); ++ if (ret) ++ nf_flow_table_offload_exit(); ++ ++ return ret; + } + + static void __exit nf_flow_table_module_exit(void) + { ++ unregister_netdevice_notifier(&flow_offload_netdev_notifier); + nf_flow_table_offload_exit(); + } + +--- a/net/netfilter/nft_flow_offload.c ++++ b/net/netfilter/nft_flow_offload.c +@@ -444,47 +444,14 @@ static struct nft_expr_type nft_flow_off + .owner = THIS_MODULE, + }; + +-static int flow_offload_netdev_event(struct notifier_block *this, +- unsigned long event, void *ptr) +-{ +- struct net_device *dev = netdev_notifier_info_to_dev(ptr); +- +- if (event != NETDEV_DOWN) +- return NOTIFY_DONE; +- +- nf_flow_table_cleanup(dev); +- +- return NOTIFY_DONE; +-} +- +-static struct notifier_block flow_offload_netdev_notifier = { +- .notifier_call = flow_offload_netdev_event, +-}; +- + static int __init nft_flow_offload_module_init(void) + { +- int err; +- +- err = register_netdevice_notifier(&flow_offload_netdev_notifier); +- if (err) +- goto err; +- +- err = nft_register_expr(&nft_flow_offload_type); +- if (err < 0) +- goto register_expr; +- +- return 0; +- +-register_expr: +- unregister_netdevice_notifier(&flow_offload_netdev_notifier); +-err: +- return err; ++ return nft_register_expr(&nft_flow_offload_type); + } + + static void __exit nft_flow_offload_module_exit(void) + { + nft_unregister_expr(&nft_flow_offload_type); +- unregister_netdevice_notifier(&flow_offload_netdev_notifier); + } + + module_init(nft_flow_offload_module_init); diff --git a/pkgs/patches-linux-5.15/400-find_active_root.patch b/pkgs/patches-linux-5.15/400-find_active_root.patch new file mode 100644 index 0000000..5582d20 --- /dev/null +++ b/pkgs/patches-linux-5.15/400-find_active_root.patch @@ -0,0 +1,60 @@ +The WRT1900AC among other Linksys routers uses a dual-firmware layout. +Dynamically rename the active partition to "ubi". + +Signed-off-by: Imre Kaloz <kaloz@openwrt.org> + +--- a/drivers/mtd/parsers/ofpart_core.c ++++ b/drivers/mtd/parsers/ofpart_core.c +@@ -38,6 +38,8 @@ static bool node_has_compatible(struct d + return of_get_property(pp, "compatible", NULL); + } + ++static int mangled_rootblock; ++ + static int parse_fixed_partitions(struct mtd_info *master, + const struct mtd_partition **pparts, + struct mtd_part_parser_data *data) +@@ -48,6 +50,7 @@ static int parse_fixed_partitions(struct + struct device_node *mtd_node; + struct device_node *ofpart_node; + const char *partname; ++ const char *owrtpart = "ubi"; + struct device_node *pp; + int nr_parts, i, ret = 0; + bool dedicated = true; +@@ -133,9 +136,13 @@ static int parse_fixed_partitions(struct + parts[i].size = of_read_number(reg + a_cells, s_cells); + parts[i].of_node = pp; + +- partname = of_get_property(pp, "label", &len); +- if (!partname) +- partname = of_get_property(pp, "name", &len); ++ if (mangled_rootblock && (i == mangled_rootblock)) { ++ partname = owrtpart; ++ } else { ++ partname = of_get_property(pp, "label", &len); ++ if (!partname) ++ partname = of_get_property(pp, "name", &len); ++ } + parts[i].name = partname; + + if (of_get_property(pp, "read-only", &len)) +@@ -252,6 +259,18 @@ static int __init ofpart_parser_init(voi + return 0; + } + ++static int __init active_root(char *str) ++{ ++ get_option(&str, &mangled_rootblock); ++ ++ if (!mangled_rootblock) ++ return 1; ++ ++ return 1; ++} ++ ++__setup("mangled_rootblock=", active_root); ++ + static void __exit ofpart_parser_exit(void) + { + deregister_mtd_parser(&ofpart_parser); diff --git a/pkgs/patches-linux-5.15/400-v5.19-mtd-call-of_platform_populate-for-MTD-partitions.patch b/pkgs/patches-linux-5.15/400-v5.19-mtd-call-of_platform_populate-for-MTD-partitions.patch new file mode 100644 index 0000000..1f3aae1 --- /dev/null +++ b/pkgs/patches-linux-5.15/400-v5.19-mtd-call-of_platform_populate-for-MTD-partitions.patch @@ -0,0 +1,72 @@ +From bcdf0315a61a29eb753a607d3a85a4032de72d94 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <rafal@milecki.pl> +Date: Tue, 10 May 2022 15:12:59 +0200 +Subject: [PATCH] mtd: call of_platform_populate() for MTD partitions +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Until this change MTD subsystem supported handling partitions only with +MTD partitions parsers. That's a specific / limited API designed around +partitions. + +Some MTD partitions may however require different handling. They may +contain specific data that needs to be parsed and somehow extracted. For +that purpose MTD subsystem should allow binding of standard platform +drivers. + +An example can be U-Boot (sub)partition with environment variables. +There exist a "u-boot,env" DT binding for MTD (sub)partition that +requires an NVMEM driver. + +Ref: 5db1c2dbc04c ("dt-bindings: nvmem: add U-Boot environment variables binding") +Signed-off-by: Rafał Miłecki <rafal@milecki.pl> +Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com> +Link: https://lore.kernel.org/linux-mtd/20220510131259.555-1-zajec5@gmail.com +--- + drivers/mtd/mtdpart.c | 9 +++++++++ + 1 file changed, 9 insertions(+) + +--- a/drivers/mtd/mtdpart.c ++++ b/drivers/mtd/mtdpart.c +@@ -17,6 +17,7 @@ + #include <linux/mtd/partitions.h> + #include <linux/err.h> + #include <linux/of.h> ++#include <linux/of_platform.h> + + #include "mtdcore.h" + +@@ -577,10 +578,16 @@ static int mtd_part_of_parse(struct mtd_ + struct mtd_part_parser *parser; + struct device_node *np; + struct property *prop; ++ struct device *dev; + const char *compat; + const char *fixed = "fixed-partitions"; + int ret, err = 0; + ++ dev = &master->dev; ++ /* Use parent device (controller) if the top level MTD is not registered */ ++ if (!IS_ENABLED(CONFIG_MTD_PARTITIONED_MASTER) && !mtd_is_partition(master)) ++ dev = master->dev.parent; ++ + np = mtd_get_of_node(master); + if (mtd_is_partition(master)) + of_node_get(np); +@@ -593,6 +600,7 @@ static int mtd_part_of_parse(struct mtd_ + continue; + ret = mtd_part_do_parse(parser, master, pparts, NULL); + if (ret > 0) { ++ of_platform_populate(np, NULL, NULL, dev); + of_node_put(np); + return ret; + } +@@ -600,6 +608,7 @@ static int mtd_part_of_parse(struct mtd_ + if (ret < 0 && !err) + err = ret; + } ++ of_platform_populate(np, NULL, NULL, dev); + of_node_put(np); + + /* diff --git a/pkgs/patches-linux-5.15/401-v5.20-mtd-parsers-add-support-for-Sercomm-partitions.patch b/pkgs/patches-linux-5.15/401-v5.20-mtd-parsers-add-support-for-Sercomm-partitions.patch new file mode 100644 index 0000000..113a96a --- /dev/null +++ b/pkgs/patches-linux-5.15/401-v5.20-mtd-parsers-add-support-for-Sercomm-partitions.patch @@ -0,0 +1,302 @@ +From 9b78ef0c7997052e9eaa0f7a4513d546fa17358c Mon Sep 17 00:00:00 2001 +From: Mikhail Zhilkin <csharper2005@gmail.com> +Date: Sun, 29 May 2022 11:07:14 +0000 +Subject: [PATCH] mtd: parsers: add support for Sercomm partitions + +This adds an MTD partition parser for the Sercomm partition table that +is used in some Beeline, Netgear and Sercomm routers. + +The Sercomm partition map table contains real partition offsets, which +may differ from device to device depending on the number and location of +bad blocks on NAND. + +Original patch (proposed by NOGUCHI Hiroshi): +Link: https://github.com/openwrt/openwrt/pull/1318#issuecomment-420607394 + +Signed-off-by: NOGUCHI Hiroshi <drvlabo@gmail.com> +Signed-off-by: Mikhail Zhilkin <csharper2005@gmail.com> +Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com> +Link: https://lore.kernel.org/linux-mtd/20220529110714.189732-1-csharper2005@gmail.com +--- + drivers/mtd/parsers/Kconfig | 9 ++ + drivers/mtd/parsers/Makefile | 1 + + drivers/mtd/parsers/scpart.c | 248 +++++++++++++++++++++++++++++++++++ + 3 files changed, 258 insertions(+) + create mode 100644 drivers/mtd/parsers/scpart.c + +--- a/drivers/mtd/parsers/Kconfig ++++ b/drivers/mtd/parsers/Kconfig +@@ -186,3 +186,12 @@ config MTD_QCOMSMEM_PARTS + help + This provides support for parsing partitions from Shared Memory (SMEM) + for NAND and SPI flash on Qualcomm platforms. ++ ++config MTD_SERCOMM_PARTS ++ tristate "Sercomm partition table parser" ++ depends on MTD && RALINK ++ help ++ This provides partitions table parser for devices with Sercomm ++ partition map. This partition table contains real partition ++ offsets, which may differ from device to device depending on the ++ number and location of bad blocks on NAND. +--- a/drivers/mtd/parsers/Makefile ++++ b/drivers/mtd/parsers/Makefile +@@ -10,6 +10,7 @@ ofpart-$(CONFIG_MTD_OF_PARTS_LINKSYS_NS) + obj-$(CONFIG_MTD_PARSER_IMAGETAG) += parser_imagetag.o + obj-$(CONFIG_MTD_AFS_PARTS) += afs.o + obj-$(CONFIG_MTD_PARSER_TRX) += parser_trx.o ++obj-$(CONFIG_MTD_SERCOMM_PARTS) += scpart.o + obj-$(CONFIG_MTD_SHARPSL_PARTS) += sharpslpart.o + obj-$(CONFIG_MTD_REDBOOT_PARTS) += redboot.o + obj-$(CONFIG_MTD_QCOMSMEM_PARTS) += qcomsmempart.o +--- /dev/null ++++ b/drivers/mtd/parsers/scpart.c +@@ -0,0 +1,248 @@ ++// SPDX-License-Identifier: GPL-2.0-or-later ++/* ++ * drivers/mtd/scpart.c: Sercomm Partition Parser ++ * ++ * Copyright (C) 2018 NOGUCHI Hiroshi ++ * Copyright (C) 2022 Mikhail Zhilkin ++ */ ++ ++#include <linux/kernel.h> ++#include <linux/slab.h> ++#include <linux/mtd/mtd.h> ++#include <linux/mtd/partitions.h> ++#include <linux/module.h> ++ ++#define MOD_NAME "scpart" ++ ++#ifdef pr_fmt ++#undef pr_fmt ++#endif ++ ++#define pr_fmt(fmt) MOD_NAME ": " fmt ++ ++#define ID_ALREADY_FOUND 0xffffffffUL ++ ++#define MAP_OFFS_IN_BLK 0x800 ++#define MAP_MIRROR_NUM 2 ++ ++static const char sc_part_magic[] = { ++ 'S', 'C', 'F', 'L', 'M', 'A', 'P', 'O', 'K', '\0', ++}; ++#define PART_MAGIC_LEN sizeof(sc_part_magic) ++ ++/* assumes that all fields are set by CPU native endian */ ++struct sc_part_desc { ++ uint32_t part_id; ++ uint32_t part_offs; ++ uint32_t part_bytes; ++}; ++ ++static uint32_t scpart_desc_is_valid(struct sc_part_desc *pdesc) ++{ ++ return ((pdesc->part_id != 0xffffffffUL) && ++ (pdesc->part_offs != 0xffffffffUL) && ++ (pdesc->part_bytes != 0xffffffffUL)); ++} ++ ++static int scpart_scan_partmap(struct mtd_info *master, loff_t partmap_offs, ++ struct sc_part_desc **ppdesc) ++{ ++ int cnt = 0; ++ int res = 0; ++ int res2; ++ loff_t offs; ++ size_t retlen; ++ struct sc_part_desc *pdesc = NULL; ++ struct sc_part_desc *tmpdesc; ++ uint8_t *buf; ++ ++ buf = kzalloc(master->erasesize, GFP_KERNEL); ++ if (!buf) { ++ res = -ENOMEM; ++ goto out; ++ } ++ ++ res2 = mtd_read(master, partmap_offs, master->erasesize, &retlen, buf); ++ if (res2 || retlen != master->erasesize) { ++ res = -EIO; ++ goto free; ++ } ++ ++ for (offs = MAP_OFFS_IN_BLK; ++ offs < master->erasesize - sizeof(*tmpdesc); ++ offs += sizeof(*tmpdesc)) { ++ tmpdesc = (struct sc_part_desc *)&buf[offs]; ++ if (!scpart_desc_is_valid(tmpdesc)) ++ break; ++ cnt++; ++ } ++ ++ if (cnt > 0) { ++ int bytes = cnt * sizeof(*pdesc); ++ ++ pdesc = kcalloc(cnt, sizeof(*pdesc), GFP_KERNEL); ++ if (!pdesc) { ++ res = -ENOMEM; ++ goto free; ++ } ++ memcpy(pdesc, &(buf[MAP_OFFS_IN_BLK]), bytes); ++ ++ *ppdesc = pdesc; ++ res = cnt; ++ } ++ ++free: ++ kfree(buf); ++ ++out: ++ return res; ++} ++ ++static int scpart_find_partmap(struct mtd_info *master, ++ struct sc_part_desc **ppdesc) ++{ ++ int magic_found = 0; ++ int res = 0; ++ int res2; ++ loff_t offs = 0; ++ size_t retlen; ++ uint8_t rdbuf[PART_MAGIC_LEN]; ++ ++ while ((magic_found < MAP_MIRROR_NUM) && ++ (offs < master->size) && ++ !mtd_block_isbad(master, offs)) { ++ res2 = mtd_read(master, offs, PART_MAGIC_LEN, &retlen, rdbuf); ++ if (res2 || retlen != PART_MAGIC_LEN) { ++ res = -EIO; ++ goto out; ++ } ++ if (!memcmp(rdbuf, sc_part_magic, PART_MAGIC_LEN)) { ++ pr_debug("Signature found at 0x%llx\n", offs); ++ magic_found++; ++ res = scpart_scan_partmap(master, offs, ppdesc); ++ if (res > 0) ++ goto out; ++ } ++ offs += master->erasesize; ++ } ++ ++out: ++ if (res > 0) ++ pr_info("Valid 'SC PART MAP' (%d partitions) found at 0x%llx\n", res, offs); ++ else ++ pr_info("No valid 'SC PART MAP' was found\n"); ++ ++ return res; ++} ++ ++static int scpart_parse(struct mtd_info *master, ++ const struct mtd_partition **pparts, ++ struct mtd_part_parser_data *data) ++{ ++ const char *partname; ++ int n; ++ int nr_scparts; ++ int nr_parts = 0; ++ int res = 0; ++ struct sc_part_desc *scpart_map = NULL; ++ struct mtd_partition *parts = NULL; ++ struct device_node *mtd_node; ++ struct device_node *ofpart_node; ++ struct device_node *pp; ++ ++ mtd_node = mtd_get_of_node(master); ++ if (!mtd_node) { ++ res = -ENOENT; ++ goto out; ++ } ++ ++ ofpart_node = of_get_child_by_name(mtd_node, "partitions"); ++ if (!ofpart_node) { ++ pr_info("%s: 'partitions' subnode not found on %pOF.\n", ++ master->name, mtd_node); ++ res = -ENOENT; ++ goto out; ++ } ++ ++ nr_scparts = scpart_find_partmap(master, &scpart_map); ++ if (nr_scparts <= 0) { ++ pr_info("No any partitions was found in 'SC PART MAP'.\n"); ++ res = -ENOENT; ++ goto free; ++ } ++ ++ parts = kcalloc(of_get_child_count(ofpart_node), sizeof(*parts), ++ GFP_KERNEL); ++ if (!parts) { ++ res = -ENOMEM; ++ goto free; ++ } ++ ++ for_each_child_of_node(ofpart_node, pp) { ++ u32 scpart_id; ++ ++ if (of_property_read_u32(pp, "sercomm,scpart-id", &scpart_id)) ++ continue; ++ ++ for (n = 0 ; n < nr_scparts ; n++) ++ if ((scpart_map[n].part_id != ID_ALREADY_FOUND) && ++ (scpart_id == scpart_map[n].part_id)) ++ break; ++ if (n >= nr_scparts) ++ /* not match */ ++ continue; ++ ++ /* add the partition found in OF into MTD partition array */ ++ parts[nr_parts].offset = scpart_map[n].part_offs; ++ parts[nr_parts].size = scpart_map[n].part_bytes; ++ parts[nr_parts].of_node = pp; ++ ++ if (!of_property_read_string(pp, "label", &partname)) ++ parts[nr_parts].name = partname; ++ if (of_property_read_bool(pp, "read-only")) ++ parts[nr_parts].mask_flags |= MTD_WRITEABLE; ++ if (of_property_read_bool(pp, "lock")) ++ parts[nr_parts].mask_flags |= MTD_POWERUP_LOCK; ++ ++ /* mark as 'done' */ ++ scpart_map[n].part_id = ID_ALREADY_FOUND; ++ ++ nr_parts++; ++ } ++ ++ if (nr_parts > 0) { ++ *pparts = parts; ++ res = nr_parts; ++ } else ++ pr_info("No partition in OF matches partition ID with 'SC PART MAP'.\n"); ++ ++ of_node_put(pp); ++ ++free: ++ kfree(scpart_map); ++ if (res <= 0) ++ kfree(parts); ++ ++out: ++ return res; ++} ++ ++static const struct of_device_id scpart_parser_of_match_table[] = { ++ { .compatible = "sercomm,sc-partitions" }, ++ {}, ++}; ++MODULE_DEVICE_TABLE(of, scpart_parser_of_match_table); ++ ++static struct mtd_part_parser scpart_parser = { ++ .parse_fn = scpart_parse, ++ .name = "scpart", ++ .of_match_table = scpart_parser_of_match_table, ++}; ++module_mtd_part_parser(scpart_parser); ++ ++/* mtd parsers will request the module by parser name */ ++MODULE_ALIAS("scpart"); ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("NOGUCHI Hiroshi <drvlabo@gmail.com>"); ++MODULE_AUTHOR("Mikhail Zhilkin <csharper2005@gmail.com>"); ++MODULE_DESCRIPTION("Sercomm partition parser"); diff --git a/pkgs/patches-linux-5.15/402-mtd-blktrans-call-add-disks-after-mtd-device.patch b/pkgs/patches-linux-5.15/402-mtd-blktrans-call-add-disks-after-mtd-device.patch new file mode 100644 index 0000000..011f790 --- /dev/null +++ b/pkgs/patches-linux-5.15/402-mtd-blktrans-call-add-disks-after-mtd-device.patch @@ -0,0 +1,98 @@ +From 0bccc3722bdd88e8ae995e77ef9f7b77ee4cbdee Mon Sep 17 00:00:00 2001 +From: Daniel Golle <daniel@makrotopia.org> +Date: Wed, 7 Apr 2021 22:45:54 +0100 +Subject: [PATCH 2/2] mtd: blktrans: call add disks after mtd device +To: linux-mtd@lists.infradead.org +Cc: Vignesh Raghavendra <vigneshr@ti.com>, + Richard Weinberger <richard@nod.at>, + Miquel Raynal <miquel.raynal@bootlin.com>, + David Woodhouse <dwmw2@infradead.org> + +Calling device_add_disk while holding mtd_table_mutex leads +to deadlock in case part_bits!=0 as block partition parsers +will try to open the newly created disks, trying to acquire +mutex once again. +Move device_add_disk to additional function called after +add partitions of an MTD device have been added and locks +have been released. + +Signed-off-by: Daniel Golle <daniel@makrotopia.org> +--- + drivers/mtd/mtd_blkdevs.c | 33 ++++++++++++++++++++++++++------- + drivers/mtd/mtdcore.c | 3 +++ + include/linux/mtd/blktrans.h | 1 + + 3 files changed, 30 insertions(+), 7 deletions(-) + +--- a/drivers/mtd/mtd_blkdevs.c ++++ b/drivers/mtd/mtd_blkdevs.c +@@ -384,13 +384,6 @@ int add_mtd_blktrans_dev(struct mtd_blkt + if (new->readonly) + set_disk_ro(gd, 1); + +- device_add_disk(&new->mtd->dev, gd, NULL); +- +- if (new->disk_attributes) { +- ret = sysfs_create_group(&disk_to_dev(gd)->kobj, +- new->disk_attributes); +- WARN_ON(ret); +- } + return 0; + + out_free_tag_set: +@@ -402,6 +395,27 @@ out_list_del: + return ret; + } + ++void register_mtd_blktrans_devs(void) ++{ ++ struct mtd_blktrans_ops *tr; ++ struct mtd_blktrans_dev *dev, *next; ++ int ret; ++ ++ list_for_each_entry(tr, &blktrans_majors, list) { ++ list_for_each_entry_safe(dev, next, &tr->devs, list) { ++ if (disk_live(dev->disk)) ++ continue; ++ ++ device_add_disk(&dev->mtd->dev, dev->disk, NULL); ++ if (dev->disk_attributes) { ++ ret = sysfs_create_group(&disk_to_dev(dev->disk)->kobj, ++ dev->disk_attributes); ++ WARN_ON(ret); ++ } ++ } ++ } ++} ++ + int del_mtd_blktrans_dev(struct mtd_blktrans_dev *old) + { + unsigned long flags; +--- a/drivers/mtd/mtdcore.c ++++ b/drivers/mtd/mtdcore.c +@@ -31,6 +31,7 @@ + + #include <linux/mtd/mtd.h> + #include <linux/mtd/partitions.h> ++#include <linux/mtd/blktrans.h> + + #include "mtdcore.h" + +@@ -1073,6 +1074,8 @@ int mtd_device_parse_register(struct mtd + + ret = mtd_otp_nvmem_add(mtd); + ++ register_mtd_blktrans_devs(); ++ + out: + if (ret && device_is_registered(&mtd->dev)) + del_mtd_device(mtd); +--- a/include/linux/mtd/blktrans.h ++++ b/include/linux/mtd/blktrans.h +@@ -76,6 +76,7 @@ extern int deregister_mtd_blktrans(struc + extern int add_mtd_blktrans_dev(struct mtd_blktrans_dev *dev); + extern int del_mtd_blktrans_dev(struct mtd_blktrans_dev *dev); + extern int mtd_blktrans_cease_background(struct mtd_blktrans_dev *dev); ++extern void register_mtd_blktrans_devs(void); + + /** + * module_mtd_blktrans() - Helper macro for registering a mtd blktrans driver diff --git a/pkgs/patches-linux-5.15/402-mtd-spi-nor-write-support-for-minor-aligned-partitions.patch b/pkgs/patches-linux-5.15/402-mtd-spi-nor-write-support-for-minor-aligned-partitions.patch new file mode 100644 index 0000000..ed9d1c9 --- /dev/null +++ b/pkgs/patches-linux-5.15/402-mtd-spi-nor-write-support-for-minor-aligned-partitions.patch @@ -0,0 +1,245 @@ +From acacdac272927ae1d96e0bca51eb82899671eaea Mon Sep 17 00:00:00 2001 +From: John Thomson <git@johnthomson.fastmail.com.au> +Date: Fri, 25 Dec 2020 18:50:08 +1000 +Subject: [PATCH] mtd: spi-nor: write support for minor aligned partitions +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Do not prevent writing to mtd partitions where a partition boundary sits +on a minor erasesize boundary. +This addresses a FIXME that has been present since the start of the +linux git history: +/* Doesn't start on a boundary of major erase size */ +/* FIXME: Let it be writable if it is on a boundary of + * _minor_ erase size though */ + +Allow a uniform erase region spi-nor device to be configured +to use the non-uniform erase regions code path for an erase with: +CONFIG_MTD_SPI_NOR_USE_VARIABLE_ERASE=y + +On supporting hardware (SECT_4K: majority of current SPI-NOR device) +provide the facility for an erase to use the least number +of SPI-NOR operations, as well as access to 4K erase without +requiring CONFIG_MTD_SPI_NOR_USE_4K_SECTORS + +Introduce erasesize_minor to the mtd struct, +the smallest erasesize supported by the device + +On existing devices, this is useful where write support is wanted +for data on a 4K partition, such as some u-boot-env partitions, +or RouterBoot soft_config, while still netting the performance +benefits of using 64K sectors + +Performance: +time mtd erase firmware +OpenWrt 5.10 ramips MT7621 w25q128jv 0xfc0000 partition length + +Without this patch +MTD_SPI_NOR_USE_4K_SECTORS=y |n +real 2m 11.66s |0m 50.86s +user 0m 0.00s |0m 0.00s +sys 1m 56.20s |0m 50.80s + +With this patch +MTD_SPI_NOR_USE_VARIABLE_ERASE=n|y |4K_SECTORS=y +real 0m 51.68s |0m 50.85s |2m 12.89s +user 0m 0.00s |0m 0.00s |0m 0.01s +sys 0m 46.94s |0m 50.38s |2m 12.46s + +Signed-off-by: John Thomson <git@johnthomson.fastmail.com.au> +Signed-off-by: Thibaut VARÈNE <hacks+kernel@slashdirt.org> + +--- + +checkpatch does not like the printk(KERN_WARNING +these should be changed separately beforehand? + +Changes v1 -> v2: +Added mtdcore sysfs for erasesize_minor +Removed finding minor erasesize for variable erase regions device, +as untested and no responses regarding it. +Moved IF_ENABLED for SPINOR variable erase to guard setting +erasesize_minor in spi-nor/core.c +Removed setting erasesize to minor where partition boundaries require +minor erase to be writable +Simplified minor boundary check by relying on minor being a factor of +major + +Changes RFC -> v1: +Fix uninitialized variable smatch warning +Reported-by: kernel test robot <lkp@intel.com> +Reported-by: Dan Carpenter <dan.carpenter@oracle.com> +--- + drivers/mtd/mtdcore.c | 10 ++++++++++ + drivers/mtd/mtdpart.c | 35 +++++++++++++++++++++++++---------- + drivers/mtd/spi-nor/Kconfig | 10 ++++++++++ + drivers/mtd/spi-nor/core.c | 11 +++++++++-- + include/linux/mtd/mtd.h | 2 ++ + 5 files changed, 56 insertions(+), 12 deletions(-) + +--- a/drivers/mtd/mtdcore.c ++++ b/drivers/mtd/mtdcore.c +@@ -168,6 +168,15 @@ static ssize_t mtd_erasesize_show(struct + } + MTD_DEVICE_ATTR_RO(erasesize); + ++static ssize_t mtd_erasesize_minor_show(struct device *dev, ++ struct device_attribute *attr, char *buf) ++{ ++ struct mtd_info *mtd = dev_get_drvdata(dev); ++ ++ return sysfs_emit(buf, "%lu\n", (unsigned long)mtd->erasesize_minor); ++} ++MTD_DEVICE_ATTR_RO(erasesize_minor); ++ + static ssize_t mtd_writesize_show(struct device *dev, + struct device_attribute *attr, char *buf) + { +@@ -313,6 +322,7 @@ static struct attribute *mtd_attrs[] = { + &dev_attr_flags.attr, + &dev_attr_size.attr, + &dev_attr_erasesize.attr, ++ &dev_attr_erasesize_minor.attr, + &dev_attr_writesize.attr, + &dev_attr_subpagesize.attr, + &dev_attr_oobsize.attr, +--- a/drivers/mtd/mtdpart.c ++++ b/drivers/mtd/mtdpart.c +@@ -41,6 +41,7 @@ static struct mtd_info *allocate_partiti + struct mtd_info *master = mtd_get_master(parent); + int wr_alignment = (parent->flags & MTD_NO_ERASE) ? + master->writesize : master->erasesize; ++ int wr_alignment_minor = 0; + u64 parent_size = mtd_is_partition(parent) ? + parent->part.size : parent->size; + struct mtd_info *child; +@@ -165,6 +166,7 @@ static struct mtd_info *allocate_partiti + } else { + /* Single erase size */ + child->erasesize = master->erasesize; ++ child->erasesize_minor = master->erasesize_minor; + } + + /* +@@ -172,26 +174,39 @@ static struct mtd_info *allocate_partiti + * exposes several regions with different erasesize. Adjust + * wr_alignment accordingly. + */ +- if (!(child->flags & MTD_NO_ERASE)) ++ if (!(child->flags & MTD_NO_ERASE)) { + wr_alignment = child->erasesize; ++ wr_alignment_minor = child->erasesize_minor; ++ } + + tmp = mtd_get_master_ofs(child, 0); + remainder = do_div(tmp, wr_alignment); + if ((child->flags & MTD_WRITEABLE) && remainder) { +- /* Doesn't start on a boundary of major erase size */ +- /* FIXME: Let it be writable if it is on a boundary of +- * _minor_ erase size though */ +- child->flags &= ~MTD_WRITEABLE; +- printk(KERN_WARNING"mtd: partition \"%s\" doesn't start on an erase/write block boundary -- force read-only\n", +- part->name); ++ if (wr_alignment_minor) { ++ /* rely on minor being a factor of major erasesize */ ++ tmp = remainder; ++ remainder = do_div(tmp, wr_alignment_minor); ++ } ++ if (remainder) { ++ child->flags &= ~MTD_WRITEABLE; ++ printk(KERN_WARNING"mtd: partition \"%s\" doesn't start on an erase/write block boundary -- force read-only\n", ++ part->name); ++ } + } + + tmp = mtd_get_master_ofs(child, 0) + child->part.size; + remainder = do_div(tmp, wr_alignment); + if ((child->flags & MTD_WRITEABLE) && remainder) { +- child->flags &= ~MTD_WRITEABLE; +- printk(KERN_WARNING"mtd: partition \"%s\" doesn't end on an erase/write block -- force read-only\n", +- part->name); ++ if (wr_alignment_minor) { ++ tmp = remainder; ++ remainder = do_div(tmp, wr_alignment_minor); ++ } ++ ++ if (remainder) { ++ child->flags &= ~MTD_WRITEABLE; ++ printk(KERN_WARNING"mtd: partition \"%s\" doesn't end on an erase/write block -- force read-only\n", ++ part->name); ++ } + } + + child->size = child->part.size; +--- a/drivers/mtd/spi-nor/Kconfig ++++ b/drivers/mtd/spi-nor/Kconfig +@@ -10,6 +10,16 @@ menuconfig MTD_SPI_NOR + + if MTD_SPI_NOR + ++config MTD_SPI_NOR_USE_VARIABLE_ERASE ++ bool "Disable uniform_erase to allow use of all hardware supported erasesizes" ++ depends on !MTD_SPI_NOR_USE_4K_SECTORS ++ default n ++ help ++ Allow mixed use of all hardware supported erasesizes, ++ by forcing spi_nor to use the multiple eraseregions code path. ++ For example: A 68K erase will use one 64K erase, and one 4K erase ++ on supporting hardware. ++ + config MTD_SPI_NOR_USE_4K_SECTORS + bool "Use small 4096 B erase sectors" + default y +--- a/drivers/mtd/spi-nor/core.c ++++ b/drivers/mtd/spi-nor/core.c +@@ -1271,6 +1271,8 @@ static u8 spi_nor_convert_3to4_erase(u8 + + static bool spi_nor_has_uniform_erase(const struct spi_nor *nor) + { ++ if (IS_ENABLED(CONFIG_MTD_SPI_NOR_USE_VARIABLE_ERASE)) ++ return false; + return !!nor->params->erase_map.uniform_erase_type; + } + +@@ -2388,6 +2390,7 @@ static int spi_nor_select_erase(struct s + { + struct spi_nor_erase_map *map = &nor->params->erase_map; + const struct spi_nor_erase_type *erase = NULL; ++ const struct spi_nor_erase_type *erase_minor = NULL; + struct mtd_info *mtd = &nor->mtd; + u32 wanted_size = nor->info->sector_size; + int i; +@@ -2420,8 +2423,9 @@ static int spi_nor_select_erase(struct s + */ + for (i = SNOR_ERASE_TYPE_MAX - 1; i >= 0; i--) { + if (map->erase_type[i].size) { +- erase = &map->erase_type[i]; +- break; ++ if (!erase) ++ erase = &map->erase_type[i]; ++ erase_minor = &map->erase_type[i]; + } + } + +@@ -2429,6 +2433,9 @@ static int spi_nor_select_erase(struct s + return -EINVAL; + + mtd->erasesize = erase->size; ++ if (IS_ENABLED(CONFIG_MTD_SPI_NOR_USE_VARIABLE_ERASE) && ++ erase_minor && erase_minor->size < erase->size) ++ mtd->erasesize_minor = erase_minor->size; + return 0; + } + +--- a/include/linux/mtd/mtd.h ++++ b/include/linux/mtd/mtd.h +@@ -243,6 +243,8 @@ struct mtd_info { + * information below if they desire + */ + uint32_t erasesize; ++ /* "Minor" (smallest) erase size supported by the whole device */ ++ uint32_t erasesize_minor; + /* Minimal writable flash unit size. In case of NOR flash it is 1 (even + * though individual bits can be cleared), in case of NAND flash it is + * one NAND page (or half, or one-fourths of it), in case of ECC-ed NOR diff --git a/pkgs/patches-linux-5.15/402-v5.20-mtd-next-mtd-core-introduce-of-support-for-dynamic-partitions.patch b/pkgs/patches-linux-5.15/402-v5.20-mtd-next-mtd-core-introduce-of-support-for-dynamic-partitions.patch new file mode 100644 index 0000000..8b8e478 --- /dev/null +++ b/pkgs/patches-linux-5.15/402-v5.20-mtd-next-mtd-core-introduce-of-support-for-dynamic-partitions.patch @@ -0,0 +1,106 @@ +From ad9b10d1eaada169bd764abcab58f08538877e26 Mon Sep 17 00:00:00 2001 +From: Christian Marangi <ansuelsmth@gmail.com> +Date: Wed, 22 Jun 2022 03:06:28 +0200 +Subject: mtd: core: introduce of support for dynamic partitions + +We have many parser that register mtd partitions at runtime. One example +is the cmdlinepart or the smem-part parser where the compatible is defined +in the dts and the partitions gets detected and registered by the +parser. This is problematic for the NVMEM subsystem that requires an OF +node to detect NVMEM cells. + +To fix this problem, introduce an additional logic that will try to +assign an OF node to the MTD if declared. + +On MTD addition, it will be checked if the MTD has an OF node and if +not declared will check if a partition with the same label / node name is +declared in DTS. If an exact match is found, the partition dynamically +allocated by the parser will have a connected OF node. + +The NVMEM subsystem will detect the OF node and register any NVMEM cells +declared statically in the DTS. + +Signed-off-by: Christian Marangi <ansuelsmth@gmail.com> +Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com> +Link: https://lore.kernel.org/linux-mtd/20220622010628.30414-4-ansuelsmth@gmail.com +--- + drivers/mtd/mtdcore.c | 61 +++++++++++++++++++++++++++++++++++++++++++ + 1 file changed, 61 insertions(+) + +--- a/drivers/mtd/mtdcore.c ++++ b/drivers/mtd/mtdcore.c +@@ -564,6 +564,66 @@ static int mtd_nvmem_add(struct mtd_info + return 0; + } + ++static void mtd_check_of_node(struct mtd_info *mtd) ++{ ++ struct device_node *partitions, *parent_dn, *mtd_dn = NULL; ++ const char *pname, *prefix = "partition-"; ++ int plen, mtd_name_len, offset, prefix_len; ++ struct mtd_info *parent; ++ bool found = false; ++ ++ /* Check if MTD already has a device node */ ++ if (dev_of_node(&mtd->dev)) ++ return; ++ ++ /* Check if a partitions node exist */ ++ parent = mtd->parent; ++ parent_dn = dev_of_node(&parent->dev); ++ if (!parent_dn) ++ return; ++ ++ partitions = of_get_child_by_name(parent_dn, "partitions"); ++ if (!partitions) ++ goto exit_parent; ++ ++ prefix_len = strlen(prefix); ++ mtd_name_len = strlen(mtd->name); ++ ++ /* Search if a partition is defined with the same name */ ++ for_each_child_of_node(partitions, mtd_dn) { ++ offset = 0; ++ ++ /* Skip partition with no/wrong prefix */ ++ if (!of_node_name_prefix(mtd_dn, "partition-")) ++ continue; ++ ++ /* Label have priority. Check that first */ ++ if (of_property_read_string(mtd_dn, "label", &pname)) { ++ of_property_read_string(mtd_dn, "name", &pname); ++ offset = prefix_len; ++ } ++ ++ plen = strlen(pname) - offset; ++ if (plen == mtd_name_len && ++ !strncmp(mtd->name, pname + offset, plen)) { ++ found = true; ++ break; ++ } ++ } ++ ++ if (!found) ++ goto exit_partitions; ++ ++ /* Set of_node only for nvmem */ ++ if (of_device_is_compatible(mtd_dn, "nvmem-cells")) ++ mtd_set_of_node(mtd, mtd_dn); ++ ++exit_partitions: ++ of_node_put(partitions); ++exit_parent: ++ of_node_put(parent_dn); ++} ++ + /** + * add_mtd_device - register an MTD device + * @mtd: pointer to new MTD device info structure +@@ -669,6 +729,7 @@ int add_mtd_device(struct mtd_info *mtd) + mtd->dev.devt = MTD_DEVT(i); + dev_set_name(&mtd->dev, "mtd%d", i); + dev_set_drvdata(&mtd->dev, mtd); ++ mtd_check_of_node(mtd); + of_node_get(mtd_get_of_node(mtd)); + error = device_register(&mtd->dev); + if (error) diff --git a/pkgs/patches-linux-5.15/410-block-fit-partition-parser.patch b/pkgs/patches-linux-5.15/410-block-fit-partition-parser.patch new file mode 100644 index 0000000..96c89f4 --- /dev/null +++ b/pkgs/patches-linux-5.15/410-block-fit-partition-parser.patch @@ -0,0 +1,214 @@ +From 69357074558daf6ff24c9f58714935e9e095a865 Mon Sep 17 00:00:00 2001 +From: OpenWrt community <openwrt-devel@lists.openwrt.org> +Date: Wed, 13 Jul 2022 13:37:33 +0200 +Subject: [PATCH] kernel: add block fit partition parser + +--- + block/blk.h | 2 ++ + block/partitions/Kconfig | 7 +++++++ + block/partitions/Makefile | 1 + + block/partitions/check.h | 3 +++ + block/partitions/core.c | 17 +++++++++++++++++ + block/partitions/efi.c | 8 ++++++++ + block/partitions/efi.h | 3 +++ + block/partitions/msdos.c | 10 ++++++++++ + drivers/mtd/mtd_blkdevs.c | 2 ++ + drivers/mtd/ubi/block.c | 3 +++ + include/linux/msdos_partition.h | 1 + + 11 files changed, 57 insertions(+) + +--- a/block/blk.h ++++ b/block/blk.h +@@ -354,6 +354,8 @@ void blk_free_ext_minor(unsigned int min + #define ADDPART_FLAG_NONE 0 + #define ADDPART_FLAG_RAID 1 + #define ADDPART_FLAG_WHOLEDISK 2 ++#define ADDPART_FLAG_READONLY 4 ++#define ADDPART_FLAG_ROOTDEV 8 + int bdev_add_partition(struct gendisk *disk, int partno, sector_t start, + sector_t length); + int bdev_del_partition(struct gendisk *disk, int partno); +--- a/block/partitions/Kconfig ++++ b/block/partitions/Kconfig +@@ -101,6 +101,13 @@ config ATARI_PARTITION + Say Y here if you would like to use hard disks under Linux which + were partitioned under the Atari OS. + ++config FIT_PARTITION ++ bool "Flattened-Image-Tree (FIT) partition support" if PARTITION_ADVANCED ++ default n ++ help ++ Say Y here if your system needs to mount the filesystem part of ++ a Flattened-Image-Tree (FIT) image commonly used with Das U-Boot. ++ + config IBM_PARTITION + bool "IBM disk label and partition support" + depends on PARTITION_ADVANCED && S390 +--- a/block/partitions/Makefile ++++ b/block/partitions/Makefile +@@ -8,6 +8,7 @@ obj-$(CONFIG_ACORN_PARTITION) += acorn.o + obj-$(CONFIG_AMIGA_PARTITION) += amiga.o + obj-$(CONFIG_ATARI_PARTITION) += atari.o + obj-$(CONFIG_AIX_PARTITION) += aix.o ++obj-$(CONFIG_FIT_PARTITION) += fit.o + obj-$(CONFIG_CMDLINE_PARTITION) += cmdline.o + obj-$(CONFIG_MAC_PARTITION) += mac.o + obj-$(CONFIG_LDM_PARTITION) += ldm.o +--- a/block/partitions/check.h ++++ b/block/partitions/check.h +@@ -58,6 +58,7 @@ int amiga_partition(struct parsed_partit + int atari_partition(struct parsed_partitions *state); + int cmdline_partition(struct parsed_partitions *state); + int efi_partition(struct parsed_partitions *state); ++int fit_partition(struct parsed_partitions *state); + int ibm_partition(struct parsed_partitions *); + int karma_partition(struct parsed_partitions *state); + int ldm_partition(struct parsed_partitions *state); +@@ -68,3 +69,5 @@ int sgi_partition(struct parsed_partitio + int sun_partition(struct parsed_partitions *state); + int sysv68_partition(struct parsed_partitions *state); + int ultrix_partition(struct parsed_partitions *state); ++ ++int parse_fit_partitions(struct parsed_partitions *state, u64 start_sector, u64 nr_sectors, int *slot, int add_remain); +--- a/block/partitions/core.c ++++ b/block/partitions/core.c +@@ -12,6 +12,10 @@ + #include <linux/vmalloc.h> + #include <linux/blktrace_api.h> + #include <linux/raid/detect.h> ++#ifdef CONFIG_FIT_PARTITION ++#include <linux/root_dev.h> ++#endif ++ + #include "check.h" + + static int (*check_part[])(struct parsed_partitions *) = { +@@ -48,6 +52,9 @@ static int (*check_part[])(struct parsed + #ifdef CONFIG_EFI_PARTITION + efi_partition, /* this must come before msdos */ + #endif ++#ifdef CONFIG_FIT_PARTITION ++ fit_partition, ++#endif + #ifdef CONFIG_SGI_PARTITION + sgi_partition, + #endif +@@ -408,6 +415,11 @@ static struct block_device *add_partitio + goto out_del; + } + ++#ifdef CONFIG_FIT_PARTITION ++ if (flags & ADDPART_FLAG_READONLY) ++ bdev->bd_read_only = true; ++#endif ++ + /* everything is up and running, commence */ + err = xa_insert(&disk->part_tbl, partno, bdev, GFP_KERNEL); + if (err) +@@ -598,6 +610,11 @@ static bool blk_add_partition(struct gen + (state->parts[p].flags & ADDPART_FLAG_RAID)) + md_autodetect_dev(part->bd_dev); + ++#ifdef CONFIG_FIT_PARTITION ++ if ((state->parts[p].flags & ADDPART_FLAG_ROOTDEV) && ROOT_DEV == 0) ++ ROOT_DEV = part->bd_dev; ++#endif ++ + return true; + } + +--- a/block/partitions/efi.c ++++ b/block/partitions/efi.c +@@ -716,6 +716,9 @@ int efi_partition(struct parsed_partitio + gpt_entry *ptes = NULL; + u32 i; + unsigned ssz = queue_logical_block_size(state->disk->queue) / 512; ++#ifdef CONFIG_FIT_PARTITION ++ u32 extra_slot = 64; ++#endif + + if (!find_valid_gpt(state, &gpt, &ptes) || !gpt || !ptes) { + kfree(gpt); +@@ -749,6 +752,11 @@ int efi_partition(struct parsed_partitio + ARRAY_SIZE(ptes[i].partition_name)); + utf16_le_to_7bit(ptes[i].partition_name, label_max, info->volname); + state->parts[i + 1].has_info = true; ++#ifdef CONFIG_FIT_PARTITION ++ /* If this is a U-Boot FIT volume it may have subpartitions */ ++ if (!efi_guidcmp(ptes[i].partition_type_guid, PARTITION_LINUX_FIT_GUID)) ++ (void) parse_fit_partitions(state, start * ssz, size * ssz, &extra_slot, 1); ++#endif + } + kfree(ptes); + kfree(gpt); +--- a/block/partitions/efi.h ++++ b/block/partitions/efi.h +@@ -52,6 +52,9 @@ + #define PARTITION_LINUX_LVM_GUID \ + EFI_GUID( 0xe6d6d379, 0xf507, 0x44c2, \ + 0xa2, 0x3c, 0x23, 0x8f, 0x2a, 0x3d, 0xf9, 0x28) ++#define PARTITION_LINUX_FIT_GUID \ ++ EFI_GUID( 0xcae9be83, 0xb15f, 0x49cc, \ ++ 0x86, 0x3f, 0x08, 0x1b, 0x74, 0x4a, 0x2d, 0x93) + + typedef struct _gpt_header { + __le64 signature; +--- a/block/partitions/msdos.c ++++ b/block/partitions/msdos.c +@@ -564,6 +564,15 @@ static void parse_minix(struct parsed_pa + #endif /* CONFIG_MINIX_SUBPARTITION */ + } + ++static void parse_fit_mbr(struct parsed_partitions *state, ++ sector_t offset, sector_t size, int origin) ++{ ++#ifdef CONFIG_FIT_PARTITION ++ u32 extra_slot = 64; ++ (void) parse_fit_partitions(state, offset, size, &extra_slot, 1); ++#endif /* CONFIG_FIT_PARTITION */ ++} ++ + static struct { + unsigned char id; + void (*parse)(struct parsed_partitions *, sector_t, sector_t, int); +@@ -575,6 +584,7 @@ static struct { + {UNIXWARE_PARTITION, parse_unixware}, + {SOLARIS_X86_PARTITION, parse_solaris_x86}, + {NEW_SOLARIS_X86_PARTITION, parse_solaris_x86}, ++ {FIT_PARTITION, parse_fit_mbr}, + {0, NULL}, + }; + +--- a/drivers/mtd/mtd_blkdevs.c ++++ b/drivers/mtd/mtd_blkdevs.c +@@ -345,6 +345,8 @@ int add_mtd_blktrans_dev(struct mtd_blkt + gd->first_minor = (new->devnum) << tr->part_bits; + gd->minors = 1 << tr->part_bits; + gd->fops = &mtd_block_ops; ++ if (IS_ENABLED(CONFIG_FIT_PARTITION) && !mtd_type_is_nand(new->mtd)) ++ gd->flags |= GENHD_FL_EXT_DEVT; + + if (tr->part_bits) + if (new->devnum < 26) +--- a/drivers/mtd/ubi/block.c ++++ b/drivers/mtd/ubi/block.c +@@ -428,6 +428,9 @@ int ubiblock_create(struct ubi_volume_in + goto out_cleanup_disk; + } + gd->private_data = dev; ++#ifdef CONFIG_FIT_PARTITION ++ gd->flags |= GENHD_FL_EXT_DEVT; ++#endif + sprintf(gd->disk_name, "ubiblock%d_%d", dev->ubi_num, dev->vol_id); + set_capacity(gd, disk_capacity); + dev->gd = gd; +--- a/include/linux/msdos_partition.h ++++ b/include/linux/msdos_partition.h +@@ -31,6 +31,7 @@ enum msdos_sys_ind { + LINUX_LVM_PARTITION = 0x8e, + LINUX_RAID_PARTITION = 0xfd, /* autodetect RAID partition */ + ++ FIT_PARTITION = 0x2e, /* U-Boot uImage.FIT */ + SOLARIS_X86_PARTITION = 0x82, /* also Linux swap partitions */ + NEW_SOLARIS_X86_PARTITION = 0xbf, + diff --git a/pkgs/patches-linux-5.15/410-mtd-next-mtd-parsers-trx-allow-to-use-on-MediaTek-MIPS-SoCs.patch b/pkgs/patches-linux-5.15/410-mtd-next-mtd-parsers-trx-allow-to-use-on-MediaTek-MIPS-SoCs.patch new file mode 100644 index 0000000..5c49841 --- /dev/null +++ b/pkgs/patches-linux-5.15/410-mtd-next-mtd-parsers-trx-allow-to-use-on-MediaTek-MIPS-SoCs.patch @@ -0,0 +1,33 @@ +From 2365f91c861cbfeef7141c69842848c7b2d3c2db Mon Sep 17 00:00:00 2001 +From: INAGAKI Hiroshi <musashino.open@gmail.com> +Date: Sun, 13 Feb 2022 15:40:44 +0900 +Subject: [PATCH] mtd: parsers: trx: allow to use on MediaTek MIPS SoCs + +Buffalo sells some router devices which have trx-formatted firmware, +based on MediaTek MIPS SoCs. To use parser_trx on those devices, add +"RALINK" to dependency and allow to compile for MediaTek MIPS SoCs. + +examples: + +- WCR-1166DS (MT7628) +- WSR-1166DHP (MT7621) +- WSR-2533DHP (MT7621) + +Signed-off-by: INAGAKI Hiroshi <musashino.open@gmail.com> +Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com> +Link: https://lore.kernel.org/linux-mtd/20220213064045.1781-1-musashino.open@gmail.com +--- + drivers/mtd/parsers/Kconfig | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/drivers/mtd/parsers/Kconfig ++++ b/drivers/mtd/parsers/Kconfig +@@ -115,7 +115,7 @@ config MTD_AFS_PARTS + + config MTD_PARSER_TRX + tristate "Parser for TRX format partitions" +- depends on MTD && (BCM47XX || ARCH_BCM_5301X || ARCH_MEDIATEK || COMPILE_TEST) ++ depends on MTD && (BCM47XX || ARCH_BCM_5301X || ARCH_MEDIATEK || RALINK || COMPILE_TEST) + help + TRX is a firmware format used by Broadcom on their devices. It + may contain up to 3/4 partitions (depending on the version). diff --git a/pkgs/patches-linux-5.15/420-mtd-redboot_space.patch b/pkgs/patches-linux-5.15/420-mtd-redboot_space.patch new file mode 100644 index 0000000..fee1936 --- /dev/null +++ b/pkgs/patches-linux-5.15/420-mtd-redboot_space.patch @@ -0,0 +1,41 @@ +From: Felix Fietkau <nbd@nbd.name> +Subject: add patch for including unpartitioned space in the rootfs partition for redboot devices (if applicable) + +[john@phrozen.org: used by ixp and others] + +lede-commit: 394918851f84e4d00fa16eb900e7700e95091f00 +Signed-off-by: Felix Fietkau <nbd@nbd.name> +--- + drivers/mtd/redboot.c | 19 +++++++++++++------ + 1 file changed, 13 insertions(+), 6 deletions(-) + +--- a/drivers/mtd/parsers/redboot.c ++++ b/drivers/mtd/parsers/redboot.c +@@ -277,14 +277,21 @@ nogood: + #endif + names += strlen(names) + 1; + +-#ifdef CONFIG_MTD_REDBOOT_PARTS_UNALLOCATED + if (fl->next && fl->img->flash_base + fl->img->size + master->erasesize <= fl->next->img->flash_base) { +- i++; +- parts[i].offset = parts[i - 1].size + parts[i - 1].offset; +- parts[i].size = fl->next->img->flash_base - parts[i].offset; +- parts[i].name = nullname; +- } ++ if (!strcmp(parts[i].name, "rootfs")) { ++ parts[i].size = fl->next->img->flash_base; ++ parts[i].size &= ~(master->erasesize - 1); ++ parts[i].size -= parts[i].offset; ++#ifdef CONFIG_MTD_REDBOOT_PARTS_UNALLOCATED ++ nrparts--; ++ } else { ++ i++; ++ parts[i].offset = parts[i-1].size + parts[i-1].offset; ++ parts[i].size = fl->next->img->flash_base - parts[i].offset; ++ parts[i].name = nullname; + #endif ++ } ++ } + tmp_fl = fl; + fl = fl->next; + kfree(tmp_fl); diff --git a/pkgs/patches-linux-5.15/420-mtd-set-rootfs-to-be-root-dev.patch b/pkgs/patches-linux-5.15/420-mtd-set-rootfs-to-be-root-dev.patch new file mode 100644 index 0000000..90254c6 --- /dev/null +++ b/pkgs/patches-linux-5.15/420-mtd-set-rootfs-to-be-root-dev.patch @@ -0,0 +1,39 @@ +From: Gabor Juhos <juhosg@openwrt.org> +Subject: kernel/3.1[02]: move MTD root device setup code to mtdcore + +The current code only allows to automatically set +root device on MTD partitions. Move the code to MTD +core to allow to use it with all MTD devices. + +Signed-off-by: Gabor Juhos <juhosg@openwrt.org> +--- + drivers/mtd/mtdcore.c | 10 ++++++++++ + 1 file changed, 10 insertions(+) + +--- a/drivers/mtd/mtdcore.c ++++ b/drivers/mtd/mtdcore.c +@@ -27,6 +27,7 @@ + #include <linux/reboot.h> + #include <linux/leds.h> + #include <linux/debugfs.h> ++#include <linux/root_dev.h> + #include <linux/nvmem-provider.h> + + #include <linux/mtd/mtd.h> +@@ -768,6 +769,16 @@ int add_mtd_device(struct mtd_info *mtd) + of this try_ nonsense, and no bitching about it + either. :) */ + __module_get(THIS_MODULE); ++ ++ if (!strcmp(mtd->name, "rootfs") && ++ IS_ENABLED(CONFIG_MTD_ROOTFS_ROOT_DEV) && ++ ROOT_DEV == 0) { ++ unsigned int index = mtd->index; ++ pr_notice("mtd: device %d (%s) set to be root filesystem\n", ++ mtd->index, mtd->name); ++ ROOT_DEV = MKDEV(MTD_BLOCK_MAJOR, index); ++ } ++ + return 0; + + fail_nvmem_add: diff --git a/pkgs/patches-linux-5.15/420-v5.19-02-mtd-spinand-gigadevice-add-support-for-GD5FxGQ4xExxG.patch b/pkgs/patches-linux-5.15/420-v5.19-02-mtd-spinand-gigadevice-add-support-for-GD5FxGQ4xExxG.patch new file mode 100644 index 0000000..181c912 --- /dev/null +++ b/pkgs/patches-linux-5.15/420-v5.19-02-mtd-spinand-gigadevice-add-support-for-GD5FxGQ4xExxG.patch @@ -0,0 +1,58 @@ +From 573eec222bc82fb5e724586267fbbb1aed9ffd03 Mon Sep 17 00:00:00 2001 +From: Chuanhong Guo <gch981213@gmail.com> +Date: Sun, 20 Mar 2022 17:59:58 +0800 +Subject: [PATCH 2/5] mtd: spinand: gigadevice: add support for GD5FxGQ4xExxG + +Add support for: + GD5F1GQ4RExxG + GD5F2GQ4{U,R}ExxG + +These chips differ from GD5F1GQ4UExxG only in chip ID, voltage +and capacity. + +Signed-off-by: Chuanhong Guo <gch981213@gmail.com> +Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com> +Link: https://lore.kernel.org/linux-mtd/20220320100001.247905-3-gch981213@gmail.com +--- + drivers/mtd/nand/spi/gigadevice.c | 30 ++++++++++++++++++++++++++++++ + 1 file changed, 30 insertions(+) + +--- a/drivers/mtd/nand/spi/gigadevice.c ++++ b/drivers/mtd/nand/spi/gigadevice.c +@@ -333,6 +333,36 @@ static const struct spinand_info gigadev + SPINAND_HAS_QE_BIT, + SPINAND_ECCINFO(&gd5fxgqx_variant2_ooblayout, + gd5fxgq4uexxg_ecc_get_status)), ++ SPINAND_INFO("GD5F1GQ4RExxG", ++ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_ADDR, 0xc1), ++ NAND_MEMORG(1, 2048, 128, 64, 1024, 20, 1, 1, 1), ++ NAND_ECCREQ(8, 512), ++ SPINAND_INFO_OP_VARIANTS(&read_cache_variants, ++ &write_cache_variants, ++ &update_cache_variants), ++ SPINAND_HAS_QE_BIT, ++ SPINAND_ECCINFO(&gd5fxgqx_variant2_ooblayout, ++ gd5fxgq4uexxg_ecc_get_status)), ++ SPINAND_INFO("GD5F2GQ4UExxG", ++ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_ADDR, 0xd2), ++ NAND_MEMORG(1, 2048, 128, 64, 2048, 40, 1, 1, 1), ++ NAND_ECCREQ(8, 512), ++ SPINAND_INFO_OP_VARIANTS(&read_cache_variants, ++ &write_cache_variants, ++ &update_cache_variants), ++ SPINAND_HAS_QE_BIT, ++ SPINAND_ECCINFO(&gd5fxgqx_variant2_ooblayout, ++ gd5fxgq4uexxg_ecc_get_status)), ++ SPINAND_INFO("GD5F2GQ4RExxG", ++ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_ADDR, 0xc2), ++ NAND_MEMORG(1, 2048, 128, 64, 2048, 40, 1, 1, 1), ++ NAND_ECCREQ(8, 512), ++ SPINAND_INFO_OP_VARIANTS(&read_cache_variants, ++ &write_cache_variants, ++ &update_cache_variants), ++ SPINAND_HAS_QE_BIT, ++ SPINAND_ECCINFO(&gd5fxgqx_variant2_ooblayout, ++ gd5fxgq4uexxg_ecc_get_status)), + SPINAND_INFO("GD5F1GQ4UFxxG", + SPINAND_ID(SPINAND_READID_METHOD_OPCODE, 0xb1, 0x48), + NAND_MEMORG(1, 2048, 128, 64, 1024, 20, 1, 1, 1), diff --git a/pkgs/patches-linux-5.15/420-v5.19-03-mtd-spinand-gigadevice-add-support-for-GD5F1GQ5RExxG.patch b/pkgs/patches-linux-5.15/420-v5.19-03-mtd-spinand-gigadevice-add-support-for-GD5F1GQ5RExxG.patch new file mode 100644 index 0000000..3a1cc9e --- /dev/null +++ b/pkgs/patches-linux-5.15/420-v5.19-03-mtd-spinand-gigadevice-add-support-for-GD5F1GQ5RExxG.patch @@ -0,0 +1,33 @@ +From 620a988813403318023296b61228ee8f3fcdb8e0 Mon Sep 17 00:00:00 2001 +From: Chuanhong Guo <gch981213@gmail.com> +Date: Sun, 20 Mar 2022 17:59:59 +0800 +Subject: [PATCH 3/5] mtd: spinand: gigadevice: add support for GD5F1GQ5RExxG + +This chip is the 1.8v version of GD5F1GQ5UExxG. + +Signed-off-by: Chuanhong Guo <gch981213@gmail.com> +Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com> +Link: https://lore.kernel.org/linux-mtd/20220320100001.247905-4-gch981213@gmail.com +--- + drivers/mtd/nand/spi/gigadevice.c | 10 ++++++++++ + 1 file changed, 10 insertions(+) + +--- a/drivers/mtd/nand/spi/gigadevice.c ++++ b/drivers/mtd/nand/spi/gigadevice.c +@@ -383,6 +383,16 @@ static const struct spinand_info gigadev + SPINAND_HAS_QE_BIT, + SPINAND_ECCINFO(&gd5fxgqx_variant2_ooblayout, + gd5fxgq5xexxg_ecc_get_status)), ++ SPINAND_INFO("GD5F1GQ5RExxG", ++ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x41), ++ NAND_MEMORG(1, 2048, 128, 64, 1024, 20, 1, 1, 1), ++ NAND_ECCREQ(4, 512), ++ SPINAND_INFO_OP_VARIANTS(&read_cache_variants_1gq5, ++ &write_cache_variants, ++ &update_cache_variants), ++ SPINAND_HAS_QE_BIT, ++ SPINAND_ECCINFO(&gd5fxgqx_variant2_ooblayout, ++ gd5fxgq5xexxg_ecc_get_status)), + }; + + static const struct spinand_manufacturer_ops gigadevice_spinand_manuf_ops = { diff --git a/pkgs/patches-linux-5.15/420-v5.19-04-mtd-spinand-gigadevice-add-support-for-GD5F-2-4-GQ5x.patch b/pkgs/patches-linux-5.15/420-v5.19-04-mtd-spinand-gigadevice-add-support-for-GD5F-2-4-GQ5x.patch new file mode 100644 index 0000000..cee9d9d --- /dev/null +++ b/pkgs/patches-linux-5.15/420-v5.19-04-mtd-spinand-gigadevice-add-support-for-GD5F-2-4-GQ5x.patch @@ -0,0 +1,84 @@ +From 194ec04b3a9e7fa97d1fbef296410631bc3cf1c8 Mon Sep 17 00:00:00 2001 +From: Chuanhong Guo <gch981213@gmail.com> +Date: Sun, 20 Mar 2022 18:00:00 +0800 +Subject: [PATCH 4/5] mtd: spinand: gigadevice: add support for GD5F{2, + 4}GQ5xExxG + +Add support for: + GD5F2GQ5{U,R}ExxG + GD5F4GQ6{U,R}ExxG + +These chips uses 4 dummy bytes for quad io and 2 dummy bytes for dual io. +Besides that and memory layout, they are identical to their 1G variant. + +Signed-off-by: Chuanhong Guo <gch981213@gmail.com> +Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com> +Link: https://lore.kernel.org/linux-mtd/20220320100001.247905-5-gch981213@gmail.com +--- + drivers/mtd/nand/spi/gigadevice.c | 48 +++++++++++++++++++++++++++++++ + 1 file changed, 48 insertions(+) + +--- a/drivers/mtd/nand/spi/gigadevice.c ++++ b/drivers/mtd/nand/spi/gigadevice.c +@@ -47,6 +47,14 @@ static SPINAND_OP_VARIANTS(read_cache_va + SPINAND_PAGE_READ_FROM_CACHE_OP(true, 0, 1, NULL, 0), + SPINAND_PAGE_READ_FROM_CACHE_OP(false, 0, 1, NULL, 0)); + ++static SPINAND_OP_VARIANTS(read_cache_variants_2gq5, ++ SPINAND_PAGE_READ_FROM_CACHE_QUADIO_OP(0, 4, NULL, 0), ++ SPINAND_PAGE_READ_FROM_CACHE_X4_OP(0, 1, NULL, 0), ++ SPINAND_PAGE_READ_FROM_CACHE_DUALIO_OP(0, 2, NULL, 0), ++ SPINAND_PAGE_READ_FROM_CACHE_X2_OP(0, 1, NULL, 0), ++ SPINAND_PAGE_READ_FROM_CACHE_OP(true, 0, 1, NULL, 0), ++ SPINAND_PAGE_READ_FROM_CACHE_OP(false, 0, 1, NULL, 0)); ++ + static SPINAND_OP_VARIANTS(write_cache_variants, + SPINAND_PROG_LOAD_X4(true, 0, NULL, 0), + SPINAND_PROG_LOAD(true, 0, NULL, 0)); +@@ -391,6 +399,46 @@ static const struct spinand_info gigadev + &write_cache_variants, + &update_cache_variants), + SPINAND_HAS_QE_BIT, ++ SPINAND_ECCINFO(&gd5fxgqx_variant2_ooblayout, ++ gd5fxgq5xexxg_ecc_get_status)), ++ SPINAND_INFO("GD5F2GQ5UExxG", ++ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x52), ++ NAND_MEMORG(1, 2048, 128, 64, 2048, 40, 1, 1, 1), ++ NAND_ECCREQ(4, 512), ++ SPINAND_INFO_OP_VARIANTS(&read_cache_variants_2gq5, ++ &write_cache_variants, ++ &update_cache_variants), ++ SPINAND_HAS_QE_BIT, ++ SPINAND_ECCINFO(&gd5fxgqx_variant2_ooblayout, ++ gd5fxgq5xexxg_ecc_get_status)), ++ SPINAND_INFO("GD5F2GQ5RExxG", ++ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x42), ++ NAND_MEMORG(1, 2048, 128, 64, 2048, 40, 1, 1, 1), ++ NAND_ECCREQ(4, 512), ++ SPINAND_INFO_OP_VARIANTS(&read_cache_variants_2gq5, ++ &write_cache_variants, ++ &update_cache_variants), ++ SPINAND_HAS_QE_BIT, ++ SPINAND_ECCINFO(&gd5fxgqx_variant2_ooblayout, ++ gd5fxgq5xexxg_ecc_get_status)), ++ SPINAND_INFO("GD5F4GQ6UExxG", ++ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x55), ++ NAND_MEMORG(1, 2048, 128, 64, 2048, 40, 1, 2, 1), ++ NAND_ECCREQ(4, 512), ++ SPINAND_INFO_OP_VARIANTS(&read_cache_variants_2gq5, ++ &write_cache_variants, ++ &update_cache_variants), ++ SPINAND_HAS_QE_BIT, ++ SPINAND_ECCINFO(&gd5fxgqx_variant2_ooblayout, ++ gd5fxgq5xexxg_ecc_get_status)), ++ SPINAND_INFO("GD5F4GQ6RExxG", ++ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x45), ++ NAND_MEMORG(1, 2048, 128, 64, 2048, 40, 1, 2, 1), ++ NAND_ECCREQ(4, 512), ++ SPINAND_INFO_OP_VARIANTS(&read_cache_variants_2gq5, ++ &write_cache_variants, ++ &update_cache_variants), ++ SPINAND_HAS_QE_BIT, + SPINAND_ECCINFO(&gd5fxgqx_variant2_ooblayout, + gd5fxgq5xexxg_ecc_get_status)), + }; diff --git a/pkgs/patches-linux-5.15/420-v5.19-05-mtd-spinand-gigadevice-add-support-for-GD5FxGM7xExxG.patch b/pkgs/patches-linux-5.15/420-v5.19-05-mtd-spinand-gigadevice-add-support-for-GD5FxGM7xExxG.patch new file mode 100644 index 0000000..d63113e --- /dev/null +++ b/pkgs/patches-linux-5.15/420-v5.19-05-mtd-spinand-gigadevice-add-support-for-GD5FxGM7xExxG.patch @@ -0,0 +1,91 @@ +From 54647cd003c08b714474a5b599a147ec6a160486 Mon Sep 17 00:00:00 2001 +From: Chuanhong Guo <gch981213@gmail.com> +Date: Sun, 20 Mar 2022 18:00:01 +0800 +Subject: [PATCH 5/5] mtd: spinand: gigadevice: add support for GD5FxGM7xExxG + +Add support for: + GD5F{1,2}GM7{U,R}ExxG + GD5F4GM8{U,R}ExxG + +These are new 27nm counterparts for the GD5FxGQ4 chips from GigaDevice +with 8b/512b on-die ECC capability. +These chips (and currently supported GD5FxGQ5 chips) have QIO DTR +instruction for reading page cache. It isn't added in this patch because +I don't have a DTR spi controller for testing. + +Signed-off-by: Chuanhong Guo <gch981213@gmail.com> +Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com> +Link: https://lore.kernel.org/linux-mtd/20220320100001.247905-6-gch981213@gmail.com +--- + drivers/mtd/nand/spi/gigadevice.c | 60 +++++++++++++++++++++++++++++++ + 1 file changed, 60 insertions(+) + +--- a/drivers/mtd/nand/spi/gigadevice.c ++++ b/drivers/mtd/nand/spi/gigadevice.c +@@ -441,6 +441,66 @@ static const struct spinand_info gigadev + SPINAND_HAS_QE_BIT, + SPINAND_ECCINFO(&gd5fxgqx_variant2_ooblayout, + gd5fxgq5xexxg_ecc_get_status)), ++ SPINAND_INFO("GD5F1GM7UExxG", ++ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x91), ++ NAND_MEMORG(1, 2048, 128, 64, 1024, 20, 1, 1, 1), ++ NAND_ECCREQ(8, 512), ++ SPINAND_INFO_OP_VARIANTS(&read_cache_variants_1gq5, ++ &write_cache_variants, ++ &update_cache_variants), ++ SPINAND_HAS_QE_BIT, ++ SPINAND_ECCINFO(&gd5fxgqx_variant2_ooblayout, ++ gd5fxgq4uexxg_ecc_get_status)), ++ SPINAND_INFO("GD5F1GM7RExxG", ++ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x81), ++ NAND_MEMORG(1, 2048, 128, 64, 1024, 20, 1, 1, 1), ++ NAND_ECCREQ(8, 512), ++ SPINAND_INFO_OP_VARIANTS(&read_cache_variants_1gq5, ++ &write_cache_variants, ++ &update_cache_variants), ++ SPINAND_HAS_QE_BIT, ++ SPINAND_ECCINFO(&gd5fxgqx_variant2_ooblayout, ++ gd5fxgq4uexxg_ecc_get_status)), ++ SPINAND_INFO("GD5F2GM7UExxG", ++ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x92), ++ NAND_MEMORG(1, 2048, 128, 64, 2048, 40, 1, 1, 1), ++ NAND_ECCREQ(8, 512), ++ SPINAND_INFO_OP_VARIANTS(&read_cache_variants_1gq5, ++ &write_cache_variants, ++ &update_cache_variants), ++ SPINAND_HAS_QE_BIT, ++ SPINAND_ECCINFO(&gd5fxgqx_variant2_ooblayout, ++ gd5fxgq4uexxg_ecc_get_status)), ++ SPINAND_INFO("GD5F2GM7RExxG", ++ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x82), ++ NAND_MEMORG(1, 2048, 128, 64, 2048, 40, 1, 1, 1), ++ NAND_ECCREQ(8, 512), ++ SPINAND_INFO_OP_VARIANTS(&read_cache_variants_1gq5, ++ &write_cache_variants, ++ &update_cache_variants), ++ SPINAND_HAS_QE_BIT, ++ SPINAND_ECCINFO(&gd5fxgqx_variant2_ooblayout, ++ gd5fxgq4uexxg_ecc_get_status)), ++ SPINAND_INFO("GD5F4GM8UExxG", ++ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x95), ++ NAND_MEMORG(1, 2048, 128, 64, 4096, 80, 1, 1, 1), ++ NAND_ECCREQ(8, 512), ++ SPINAND_INFO_OP_VARIANTS(&read_cache_variants_1gq5, ++ &write_cache_variants, ++ &update_cache_variants), ++ SPINAND_HAS_QE_BIT, ++ SPINAND_ECCINFO(&gd5fxgqx_variant2_ooblayout, ++ gd5fxgq4uexxg_ecc_get_status)), ++ SPINAND_INFO("GD5F4GM8RExxG", ++ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x85), ++ NAND_MEMORG(1, 2048, 128, 64, 4096, 80, 1, 1, 1), ++ NAND_ECCREQ(8, 512), ++ SPINAND_INFO_OP_VARIANTS(&read_cache_variants_1gq5, ++ &write_cache_variants, ++ &update_cache_variants), ++ SPINAND_HAS_QE_BIT, ++ SPINAND_ECCINFO(&gd5fxgqx_variant2_ooblayout, ++ gd5fxgq4uexxg_ecc_get_status)), + }; + + static const struct spinand_manufacturer_ops gigadevice_spinand_manuf_ops = { diff --git a/pkgs/patches-linux-5.15/421-drivers-mtd-parsers-add-nvmem-support-to-cmdlinepart.patch b/pkgs/patches-linux-5.15/421-drivers-mtd-parsers-add-nvmem-support-to-cmdlinepart.patch new file mode 100644 index 0000000..965a331 --- /dev/null +++ b/pkgs/patches-linux-5.15/421-drivers-mtd-parsers-add-nvmem-support-to-cmdlinepart.patch @@ -0,0 +1,120 @@ +From 6fa9e3678eb002246df1280322b6a024853950a5 Mon Sep 17 00:00:00 2001 +From: Ansuel Smith <ansuelsmth@gmail.com> +Date: Mon, 11 Oct 2021 00:53:14 +0200 +Subject: [PATCH] drivers: mtd: parsers: add nvmem support to cmdlinepart + +Assuming cmdlinepart is only one level deep partition scheme and that +static partition are also defined in DTS, we can assign an of_node for +partition declared from bootargs. cmdlinepart have priority than +fiexed-partition parser so in this specific case the parser doesn't +assign an of_node. Fix this by searching a defined of_node using a +similar fixed_partition parser and if a partition is found with the same +label, check that it has the same offset and size and return the DT +of_node to correctly use NVMEM cells. + +Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com> +--- + drivers/mtd/parsers/cmdlinepart.c | 71 +++++++++++++++++++++++++++++++ + 1 file changed, 71 insertions(+) + +--- a/drivers/mtd/parsers/cmdlinepart.c ++++ b/drivers/mtd/parsers/cmdlinepart.c +@@ -43,6 +43,7 @@ + #include <linux/mtd/partitions.h> + #include <linux/module.h> + #include <linux/err.h> ++#include <linux/of.h> + + /* debug macro */ + #if 0 +@@ -323,6 +324,68 @@ static int mtdpart_setup_real(char *s) + return 0; + } + ++static int search_fixed_partition(struct mtd_info *master, ++ struct mtd_partition *target_part, ++ struct mtd_partition *fixed_part) ++{ ++ struct device_node *mtd_node; ++ struct device_node *ofpart_node; ++ struct device_node *pp; ++ struct mtd_partition part; ++ const char *partname; ++ ++ mtd_node = mtd_get_of_node(master); ++ if (!mtd_node) ++ return -EINVAL; ++ ++ ofpart_node = of_get_child_by_name(mtd_node, "partitions"); ++ ++ for_each_child_of_node(ofpart_node, pp) { ++ const __be32 *reg; ++ int len; ++ int a_cells, s_cells; ++ ++ reg = of_get_property(pp, "reg", &len); ++ if (!reg) { ++ pr_debug("%s: ofpart partition %pOF (%pOF) missing reg property.\n", ++ master->name, pp, ++ mtd_node); ++ continue; ++ } ++ ++ a_cells = of_n_addr_cells(pp); ++ s_cells = of_n_size_cells(pp); ++ if (len / 4 != a_cells + s_cells) { ++ pr_debug("%s: ofpart partition %pOF (%pOF) error parsing reg property.\n", ++ master->name, pp, ++ mtd_node); ++ continue; ++ } ++ ++ part.offset = of_read_number(reg, a_cells); ++ part.size = of_read_number(reg + a_cells, s_cells); ++ part.of_node = pp; ++ ++ partname = of_get_property(pp, "label", &len); ++ if (!partname) ++ partname = of_get_property(pp, "name", &len); ++ part.name = partname; ++ ++ if (!strncmp(target_part->name, part.name, len)) { ++ if (part.offset != target_part->offset) ++ return -EINVAL; ++ ++ if (part.size != target_part->size) ++ return -EINVAL; ++ ++ memcpy(fixed_part, &part, sizeof(struct mtd_partition)); ++ return 0; ++ } ++ } ++ ++ return -EINVAL; ++} ++ + /* + * Main function to be called from the MTD mapping driver/device to + * obtain the partitioning information. At this point the command line +@@ -338,6 +401,7 @@ static int parse_cmdline_partitions(stru + int i, err; + struct cmdline_mtd_partition *part; + const char *mtd_id = master->name; ++ struct mtd_partition fixed_part; + + /* parse command line */ + if (!cmdline_parsed) { +@@ -382,6 +446,13 @@ static int parse_cmdline_partitions(stru + sizeof(*part->parts) * (part->num_parts - i)); + i--; + } ++ ++ err = search_fixed_partition(master, &part->parts[i], &fixed_part); ++ if (!err) { ++ part->parts[i].of_node = fixed_part.of_node; ++ pr_info("Found partition defined in DT for %s. Assigning OF node to support nvmem.", ++ part->parts[i].name); ++ } + } + + *pparts = kmemdup(part->parts, sizeof(*part->parts) * part->num_parts, diff --git a/pkgs/patches-linux-5.15/430-mtd-add-myloader-partition-parser.patch b/pkgs/patches-linux-5.15/430-mtd-add-myloader-partition-parser.patch new file mode 100644 index 0000000..0889c9a --- /dev/null +++ b/pkgs/patches-linux-5.15/430-mtd-add-myloader-partition-parser.patch @@ -0,0 +1,229 @@ +From: Florian Fainelli <f.fainelli@gmail.com> +Subject: Add myloader partition table parser + +[john@phozen.org: shoud be upstreamable] + +lede-commit: d8bf22859b51faa09d22c056fe221a45d2f7a3b8 +Signed-off-by: Florian Fainelli <f.fainelli@gmail.com> +[adjust for kernel 5.4, add myloader.c to patch] +Signed-off-by: Adrian Schmutzler <freifunk@adrianschmutzler.de> + +--- a/drivers/mtd/parsers/Kconfig ++++ b/drivers/mtd/parsers/Kconfig +@@ -57,6 +57,22 @@ config MTD_CMDLINE_PARTS + + If unsure, say 'N'. + ++config MTD_MYLOADER_PARTS ++ tristate "MyLoader partition parsing" ++ depends on ADM5120 || ATH25 || ATH79 ++ help ++ MyLoader is a bootloader which allows the user to define partitions ++ in flash devices, by putting a table in the second erase block ++ on the device, similar to a partition table. This table gives the ++ offsets and lengths of the user defined partitions. ++ ++ If you need code which can detect and parse these tables, and ++ register MTD 'partitions' corresponding to each image detected, ++ enable this option. ++ ++ You will still need the parsing functions to be called by the driver ++ for your particular device. It won't happen automatically. ++ + config MTD_OF_PARTS + tristate "OpenFirmware (device tree) partitioning parser" + default y +--- a/drivers/mtd/parsers/Makefile ++++ b/drivers/mtd/parsers/Makefile +@@ -3,6 +3,7 @@ obj-$(CONFIG_MTD_AR7_PARTS) += ar7part. + obj-$(CONFIG_MTD_BCM47XX_PARTS) += bcm47xxpart.o + obj-$(CONFIG_MTD_BCM63XX_PARTS) += bcm63xxpart.o + obj-$(CONFIG_MTD_CMDLINE_PARTS) += cmdlinepart.o ++obj-$(CONFIG_MTD_MYLOADER_PARTS) += myloader.o + obj-$(CONFIG_MTD_OF_PARTS) += ofpart.o + ofpart-y += ofpart_core.o + ofpart-$(CONFIG_MTD_OF_PARTS_BCM4908) += ofpart_bcm4908.o +--- /dev/null ++++ b/drivers/mtd/parsers/myloader.c +@@ -0,0 +1,181 @@ ++/* ++ * Parse MyLoader-style flash partition tables and produce a Linux partition ++ * array to match. ++ * ++ * Copyright (C) 2007-2009 Gabor Juhos <juhosg@openwrt.org> ++ * ++ * This file was based on drivers/mtd/redboot.c ++ * Author: Red Hat, Inc. - David Woodhouse <dwmw2@cambridge.redhat.com> ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 as published ++ * by the Free Software Foundation. ++ */ ++ ++#include <linux/kernel.h> ++#include <linux/module.h> ++#include <linux/version.h> ++#include <linux/slab.h> ++#include <linux/init.h> ++#include <linux/vmalloc.h> ++#include <linux/mtd/mtd.h> ++#include <linux/mtd/partitions.h> ++#include <linux/byteorder/generic.h> ++#include <linux/myloader.h> ++ ++#define BLOCK_LEN_MIN 0x10000 ++#define PART_NAME_LEN 32 ++ ++struct part_data { ++ struct mylo_partition_table tab; ++ char names[MYLO_MAX_PARTITIONS][PART_NAME_LEN]; ++}; ++ ++static int myloader_parse_partitions(struct mtd_info *master, ++ const struct mtd_partition **pparts, ++ struct mtd_part_parser_data *data) ++{ ++ struct part_data *buf; ++ struct mylo_partition_table *tab; ++ struct mylo_partition *part; ++ struct mtd_partition *mtd_parts; ++ struct mtd_partition *mtd_part; ++ int num_parts; ++ int ret, i; ++ size_t retlen; ++ char *names; ++ unsigned long offset; ++ unsigned long blocklen; ++ ++ buf = vmalloc(sizeof(*buf)); ++ if (!buf) { ++ return -ENOMEM; ++ goto out; ++ } ++ tab = &buf->tab; ++ ++ blocklen = master->erasesize; ++ if (blocklen < BLOCK_LEN_MIN) ++ blocklen = BLOCK_LEN_MIN; ++ ++ offset = blocklen; ++ ++ /* Find the partition table */ ++ for (i = 0; i < 4; i++, offset += blocklen) { ++ printk(KERN_DEBUG "%s: searching for MyLoader partition table" ++ " at offset 0x%lx\n", master->name, offset); ++ ++ ret = mtd_read(master, offset, sizeof(*buf), &retlen, ++ (void *)buf); ++ if (ret) ++ goto out_free_buf; ++ ++ if (retlen != sizeof(*buf)) { ++ ret = -EIO; ++ goto out_free_buf; ++ } ++ ++ /* Check for Partition Table magic number */ ++ if (tab->magic == le32_to_cpu(MYLO_MAGIC_PARTITIONS)) ++ break; ++ ++ } ++ ++ if (tab->magic != le32_to_cpu(MYLO_MAGIC_PARTITIONS)) { ++ printk(KERN_DEBUG "%s: no MyLoader partition table found\n", ++ master->name); ++ ret = 0; ++ goto out_free_buf; ++ } ++ ++ /* The MyLoader and the Partition Table is always present */ ++ num_parts = 2; ++ ++ /* Detect number of used partitions */ ++ for (i = 0; i < MYLO_MAX_PARTITIONS; i++) { ++ part = &tab->partitions[i]; ++ ++ if (le16_to_cpu(part->type) == PARTITION_TYPE_FREE) ++ continue; ++ ++ num_parts++; ++ } ++ ++ mtd_parts = kzalloc((num_parts * sizeof(*mtd_part) + ++ num_parts * PART_NAME_LEN), GFP_KERNEL); ++ ++ if (!mtd_parts) { ++ ret = -ENOMEM; ++ goto out_free_buf; ++ } ++ ++ mtd_part = mtd_parts; ++ names = (char *)&mtd_parts[num_parts]; ++ ++ strncpy(names, "myloader", PART_NAME_LEN); ++ mtd_part->name = names; ++ mtd_part->offset = 0; ++ mtd_part->size = offset; ++ mtd_part->mask_flags = MTD_WRITEABLE; ++ mtd_part++; ++ names += PART_NAME_LEN; ++ ++ strncpy(names, "partition_table", PART_NAME_LEN); ++ mtd_part->name = names; ++ mtd_part->offset = offset; ++ mtd_part->size = blocklen; ++ mtd_part->mask_flags = MTD_WRITEABLE; ++ mtd_part++; ++ names += PART_NAME_LEN; ++ ++ for (i = 0; i < MYLO_MAX_PARTITIONS; i++) { ++ part = &tab->partitions[i]; ++ ++ if (le16_to_cpu(part->type) == PARTITION_TYPE_FREE) ++ continue; ++ ++ if ((buf->names[i][0]) && (buf->names[i][0] != '\xff')) ++ strncpy(names, buf->names[i], PART_NAME_LEN); ++ else ++ snprintf(names, PART_NAME_LEN, "partition%d", i); ++ ++ mtd_part->offset = le32_to_cpu(part->addr); ++ mtd_part->size = le32_to_cpu(part->size); ++ mtd_part->name = names; ++ mtd_part++; ++ names += PART_NAME_LEN; ++ } ++ ++ *pparts = mtd_parts; ++ ret = num_parts; ++ ++ out_free_buf: ++ vfree(buf); ++ out: ++ return ret; ++} ++ ++static struct mtd_part_parser myloader_mtd_parser = { ++ .owner = THIS_MODULE, ++ .parse_fn = myloader_parse_partitions, ++ .name = "MyLoader", ++}; ++ ++static int __init myloader_mtd_parser_init(void) ++{ ++ register_mtd_parser(&myloader_mtd_parser); ++ ++ return 0; ++} ++ ++static void __exit myloader_mtd_parser_exit(void) ++{ ++ deregister_mtd_parser(&myloader_mtd_parser); ++} ++ ++module_init(myloader_mtd_parser_init); ++module_exit(myloader_mtd_parser_exit); ++ ++MODULE_AUTHOR("Gabor Juhos <juhosg@openwrt.org>"); ++MODULE_DESCRIPTION("Parsing code for MyLoader partition tables"); ++MODULE_LICENSE("GPL v2"); diff --git a/pkgs/patches-linux-5.15/430-mtk-bmt-support.patch b/pkgs/patches-linux-5.15/430-mtk-bmt-support.patch new file mode 100644 index 0000000..2a83f46 --- /dev/null +++ b/pkgs/patches-linux-5.15/430-mtk-bmt-support.patch @@ -0,0 +1,33 @@ +From ac84397efb3b3868c71c10ad7521161773228a17 Mon Sep 17 00:00:00 2001 +From: OpenWrt community <openwrt-devel@lists.openwrt.org> +Date: Wed, 13 Jul 2022 13:41:44 +0200 +Subject: [PATCH] mtd/nand: add MediaTek NAND bad block managment table + +--- + drivers/mtd/nand/Kconfig | 4 ++++ + drivers/mtd/nand/Makefile | 1 + + 2 files changed, 5 insertions(+) + +--- a/drivers/mtd/nand/Kconfig ++++ b/drivers/mtd/nand/Kconfig +@@ -46,6 +46,10 @@ config MTD_NAND_ECC_SW_BCH + ECC codes. They are used with NAND devices requiring more than 1 bit + of error correction. + ++config MTD_NAND_MTK_BMT ++ bool "Support MediaTek NAND Bad-block Management Table" ++ default n ++ + endmenu + + endmenu +--- a/drivers/mtd/nand/Makefile ++++ b/drivers/mtd/nand/Makefile +@@ -2,6 +2,7 @@ + + nandcore-objs := core.o bbt.o + obj-$(CONFIG_MTD_NAND_CORE) += nandcore.o ++obj-$(CONFIG_MTD_NAND_MTK_BMT) += mtk_bmt.o mtk_bmt_v2.o mtk_bmt_bbt.o mtk_bmt_nmbm.o + + obj-y += onenand/ + obj-y += raw/ diff --git a/pkgs/patches-linux-5.15/431-mtd-bcm47xxpart-check-for-bad-blocks-when-calculatin.patch b/pkgs/patches-linux-5.15/431-mtd-bcm47xxpart-check-for-bad-blocks-when-calculatin.patch new file mode 100644 index 0000000..bcea45d --- /dev/null +++ b/pkgs/patches-linux-5.15/431-mtd-bcm47xxpart-check-for-bad-blocks-when-calculatin.patch @@ -0,0 +1,68 @@ +From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <zajec5@gmail.com> +Subject: [PATCH] mtd: bcm47xxpart: check for bad blocks when calculating offsets + +Signed-off-by: Rafał Miłecki <zajec5@gmail.com> +--- + +--- a/drivers/mtd/parsers/parser_trx.c ++++ b/drivers/mtd/parsers/parser_trx.c +@@ -25,6 +25,33 @@ struct trx_header { + uint32_t offset[3]; + } __packed; + ++/* ++ * Calculate real end offset (address) for a given amount of data. It checks ++ * all blocks skipping bad ones. ++ */ ++static size_t parser_trx_real_offset(struct mtd_info *mtd, size_t bytes) ++{ ++ size_t real_offset = 0; ++ ++ if (mtd_block_isbad(mtd, real_offset)) ++ pr_warn("Base offset shouldn't be at bad block"); ++ ++ while (bytes >= mtd->erasesize) { ++ bytes -= mtd->erasesize; ++ real_offset += mtd->erasesize; ++ while (mtd_block_isbad(mtd, real_offset)) { ++ real_offset += mtd->erasesize; ++ ++ if (real_offset >= mtd->size) ++ return real_offset - mtd->erasesize; ++ } ++ } ++ ++ real_offset += bytes; ++ ++ return real_offset; ++} ++ + static const char *parser_trx_data_part_name(struct mtd_info *master, + size_t offset) + { +@@ -86,21 +113,21 @@ static int parser_trx_parse(struct mtd_i + if (trx.offset[2]) { + part = &parts[curr_part++]; + part->name = "loader"; +- part->offset = trx.offset[i]; ++ part->offset = parser_trx_real_offset(mtd, trx.offset[i]); + i++; + } + + if (trx.offset[i]) { + part = &parts[curr_part++]; + part->name = "linux"; +- part->offset = trx.offset[i]; ++ part->offset = parser_trx_real_offset(mtd, trx.offset[i]); + i++; + } + + if (trx.offset[i]) { + part = &parts[curr_part++]; +- part->name = parser_trx_data_part_name(mtd, trx.offset[i]); +- part->offset = trx.offset[i]; ++ part->offset = parser_trx_real_offset(mtd, trx.offset[i]); ++ part->name = parser_trx_data_part_name(mtd, part->offset); + i++; + } + diff --git a/pkgs/patches-linux-5.15/432-mtd-bcm47xxpart-detect-T_Meter-partition.patch b/pkgs/patches-linux-5.15/432-mtd-bcm47xxpart-detect-T_Meter-partition.patch new file mode 100644 index 0000000..852654d --- /dev/null +++ b/pkgs/patches-linux-5.15/432-mtd-bcm47xxpart-detect-T_Meter-partition.patch @@ -0,0 +1,37 @@ +From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <zajec5@gmail.com> +Subject: mtd: bcm47xxpart: detect T_Meter partition + +It can be found on many Netgear devices. It consists of many 0x30 blocks +starting with 4D 54. + +Signed-off-by: Rafał Miłecki <zajec5@gmail.com> +--- + drivers/mtd/bcm47xxpart.c | 10 ++++++++++ + 1 file changed, 10 insertions(+) + +--- a/drivers/mtd/parsers/bcm47xxpart.c ++++ b/drivers/mtd/parsers/bcm47xxpart.c +@@ -35,6 +35,7 @@ + #define NVRAM_HEADER 0x48534C46 /* FLSH */ + #define POT_MAGIC1 0x54544f50 /* POTT */ + #define POT_MAGIC2 0x504f /* OP */ ++#define T_METER_MAGIC 0x4D540000 /* MT */ + #define ML_MAGIC1 0x39685a42 + #define ML_MAGIC2 0x26594131 + #define TRX_MAGIC 0x30524448 +@@ -178,6 +179,15 @@ static int bcm47xxpart_parse(struct mtd_ + MTD_WRITEABLE); + continue; + } ++ ++ /* T_Meter */ ++ if ((le32_to_cpu(buf[0x000 / 4]) & 0xFFFF0000) == T_METER_MAGIC && ++ (le32_to_cpu(buf[0x030 / 4]) & 0xFFFF0000) == T_METER_MAGIC && ++ (le32_to_cpu(buf[0x060 / 4]) & 0xFFFF0000) == T_METER_MAGIC) { ++ bcm47xxpart_add_part(&parts[curr_part++], "T_Meter", offset, ++ MTD_WRITEABLE); ++ continue; ++ } + + /* TRX */ + if (buf[0x000 / 4] == TRX_MAGIC) { diff --git a/pkgs/patches-linux-5.15/460-mtd-cfi_cmdset_0002-no-erase_suspend.patch b/pkgs/patches-linux-5.15/460-mtd-cfi_cmdset_0002-no-erase_suspend.patch new file mode 100644 index 0000000..0be74a5 --- /dev/null +++ b/pkgs/patches-linux-5.15/460-mtd-cfi_cmdset_0002-no-erase_suspend.patch @@ -0,0 +1,25 @@ +From: Felix Fietkau <nbd@nbd.name> +Subject: kernel: disable cfi cmdset 0002 erase suspend + +on some platforms, erase suspend leads to data corruption and lockups when write +ops collide with erase ops. this has been observed on the buffalo wzr-hp-g300nh. +rather than play whack-a-mole with a hard to reproduce issue on a variety of devices, +simply disable erase suspend, as it will usually not produce any useful gain on +the small filesystems used on embedded hardware. + +Signed-off-by: Felix Fietkau <nbd@nbd.name> +--- + drivers/mtd/chips/cfi_cmdset_0002.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/drivers/mtd/chips/cfi_cmdset_0002.c ++++ b/drivers/mtd/chips/cfi_cmdset_0002.c +@@ -907,7 +907,7 @@ static int get_chip(struct map_info *map + return 0; + + case FL_ERASING: +- if (!cfip || !(cfip->EraseSuspend & (0x1|0x2)) || ++ if (1 /* no suspend */ || !cfip || !(cfip->EraseSuspend & (0x1|0x2)) || + !(mode == FL_READY || mode == FL_POINT || + (mode == FL_WRITING && (cfip->EraseSuspend & 0x2)))) + goto sleep; diff --git a/pkgs/patches-linux-5.15/461-mtd-cfi_cmdset_0002-add-buffer-write-cmd-timeout.patch b/pkgs/patches-linux-5.15/461-mtd-cfi_cmdset_0002-add-buffer-write-cmd-timeout.patch new file mode 100644 index 0000000..ca56de8 --- /dev/null +++ b/pkgs/patches-linux-5.15/461-mtd-cfi_cmdset_0002-add-buffer-write-cmd-timeout.patch @@ -0,0 +1,17 @@ +From: George Kashperko <george@znau.edu.ua> +Subject: Issue map read after Write Buffer Load command to ensure chip is ready to receive data. + +Signed-off-by: George Kashperko <george@znau.edu.ua> +--- + drivers/mtd/chips/cfi_cmdset_0002.c | 1 + + 1 file changed, 1 insertion(+) +--- a/drivers/mtd/chips/cfi_cmdset_0002.c ++++ b/drivers/mtd/chips/cfi_cmdset_0002.c +@@ -2051,6 +2051,7 @@ static int __xipram do_write_buffer(stru + + /* Write Buffer Load */ + map_write(map, CMD(0x25), cmd_adr); ++ (void) map_read(map, cmd_adr); + + chip->state = FL_WRITING_TO_BUFFER; + diff --git a/pkgs/patches-linux-5.15/465-m25p80-mx-disable-software-protection.patch b/pkgs/patches-linux-5.15/465-m25p80-mx-disable-software-protection.patch new file mode 100644 index 0000000..f58d545 --- /dev/null +++ b/pkgs/patches-linux-5.15/465-m25p80-mx-disable-software-protection.patch @@ -0,0 +1,18 @@ +From: Felix Fietkau <nbd@nbd.name> +Subject: Disable software protection bits for Macronix flashes. + +Signed-off-by: Felix Fietkau <nbd@nbd.name> +--- + drivers/mtd/spi-nor/spi-nor.c | 1 + + 1 file changed, 1 insertion(+) + +--- a/drivers/mtd/spi-nor/macronix.c ++++ b/drivers/mtd/spi-nor/macronix.c +@@ -93,6 +93,7 @@ static void macronix_default_init(struct + { + nor->params->quad_enable = spi_nor_sr1_bit6_quad_enable; + nor->params->set_4byte_addr_mode = spi_nor_set_4byte_addr_mode; ++ nor->flags |= SNOR_F_HAS_LOCK; + } + + static const struct spi_nor_fixups macronix_fixups = { diff --git a/pkgs/patches-linux-5.15/476-mtd-spi-nor-add-eon-en25q128.patch b/pkgs/patches-linux-5.15/476-mtd-spi-nor-add-eon-en25q128.patch new file mode 100644 index 0000000..325fca6 --- /dev/null +++ b/pkgs/patches-linux-5.15/476-mtd-spi-nor-add-eon-en25q128.patch @@ -0,0 +1,18 @@ +From: Piotr Dymacz <pepe2k@gmail.com> +Subject: kernel/mtd: add support for EON EN25Q128 + +Signed-off-by: Piotr Dymacz <pepe2k@gmail.com> +--- + drivers/mtd/spi-nor/spi-nor.c | 1 + + 1 file changed, 1 insertion(+) + +--- a/drivers/mtd/spi-nor/eon.c ++++ b/drivers/mtd/spi-nor/eon.c +@@ -15,6 +15,7 @@ static const struct flash_info eon_parts + { "en25q32b", INFO(0x1c3016, 0, 64 * 1024, 64, 0) }, + { "en25p64", INFO(0x1c2017, 0, 64 * 1024, 128, 0) }, + { "en25q64", INFO(0x1c3017, 0, 64 * 1024, 128, SECT_4K) }, ++ { "en25q128", INFO(0x1c3018, 0, 64 * 1024, 256, SECT_4K) }, + { "en25q80a", INFO(0x1c3014, 0, 64 * 1024, 16, + SECT_4K | SPI_NOR_DUAL_READ) }, + { "en25qh16", INFO(0x1c7015, 0, 64 * 1024, 32, diff --git a/pkgs/patches-linux-5.15/479-mtd-spi-nor-add-xtx-xt25f128b.patch b/pkgs/patches-linux-5.15/479-mtd-spi-nor-add-xtx-xt25f128b.patch new file mode 100644 index 0000000..58a336a --- /dev/null +++ b/pkgs/patches-linux-5.15/479-mtd-spi-nor-add-xtx-xt25f128b.patch @@ -0,0 +1,79 @@ +From patchwork Thu Feb 6 17:19:41 2020 +Content-Type: text/plain; charset="utf-8" +MIME-Version: 1.0 +Content-Transfer-Encoding: 7bit +X-Patchwork-Submitter: Daniel Golle <daniel@makrotopia.org> +X-Patchwork-Id: 1234465 +Date: Thu, 6 Feb 2020 19:19:41 +0200 +From: Daniel Golle <daniel@makrotopia.org> +To: linux-mtd@lists.infradead.org +Subject: [PATCH v2] mtd: spi-nor: Add support for xt25f128b chip +Message-ID: <20200206171941.GA2398@makrotopia.org> +MIME-Version: 1.0 +Content-Disposition: inline +List-Subscribe: <http://lists.infradead.org/mailman/listinfo/linux-mtd>, + <mailto:linux-mtd-request@lists.infradead.org?subject=subscribe> +Cc: Eitan Cohen <eitan@neot-semadar.com>, Piotr Dymacz <pepe2k@gmail.com>, + Tudor Ambarus <tudor.ambarus@microchip.com> +Sender: "linux-mtd" <linux-mtd-bounces@lists.infradead.org> +Errors-To: linux-mtd-bounces+incoming=patchwork.ozlabs.org@lists.infradead.org + +Add XT25F128B made by XTX Technology (Shenzhen) Limited. +This chip supports dual and quad read and uniform 4K-byte erase. +Verified on Teltonika RUT955 which comes with XT25F128B in recent +versions of the device. + +Signed-off-by: Daniel Golle <daniel@makrotopia.org> +Signed-off-by: Felix Fietkau <nbd@nbd.name> +--- + drivers/mtd/spi-nor/spi-nor.c | 4 ++++ + 1 file changed, 4 insertions(+) + +--- a/drivers/mtd/spi-nor/Makefile ++++ b/drivers/mtd/spi-nor/Makefile +@@ -17,6 +17,7 @@ spi-nor-objs += sst.o + spi-nor-objs += winbond.o + spi-nor-objs += xilinx.o + spi-nor-objs += xmc.o ++spi-nor-objs += xtx.o + obj-$(CONFIG_MTD_SPI_NOR) += spi-nor.o + + obj-$(CONFIG_MTD_SPI_NOR) += controllers/ +--- /dev/null ++++ b/drivers/mtd/spi-nor/xtx.c +@@ -0,0 +1,15 @@ ++// SPDX-License-Identifier: GPL-2.0 ++#include <linux/mtd/spi-nor.h> ++ ++#include "core.h" ++ ++static const struct flash_info xtx_parts[] = { ++ /* XTX Technology (Shenzhen) Limited */ ++ { "xt25f128b", INFO(0x0B4018, 0, 64 * 1024, 256, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) }, ++}; ++ ++const struct spi_nor_manufacturer spi_nor_xtx = { ++ .name = "xtx", ++ .parts = xtx_parts, ++ .nparts = ARRAY_SIZE(xtx_parts), ++}; +--- a/drivers/mtd/spi-nor/core.c ++++ b/drivers/mtd/spi-nor/core.c +@@ -1857,6 +1857,7 @@ static const struct spi_nor_manufacturer + &spi_nor_winbond, + &spi_nor_xilinx, + &spi_nor_xmc, ++ &spi_nor_xtx, + }; + + static const struct flash_info * +--- a/drivers/mtd/spi-nor/core.h ++++ b/drivers/mtd/spi-nor/core.h +@@ -489,6 +489,7 @@ extern const struct spi_nor_manufacturer + extern const struct spi_nor_manufacturer spi_nor_winbond; + extern const struct spi_nor_manufacturer spi_nor_xilinx; + extern const struct spi_nor_manufacturer spi_nor_xmc; ++extern const struct spi_nor_manufacturer spi_nor_xtx; + + extern const struct attribute_group *spi_nor_sysfs_groups[]; + diff --git a/pkgs/patches-linux-5.15/481-mtd-spi-nor-add-support-for-Gigadevice-GD25D05.patch b/pkgs/patches-linux-5.15/481-mtd-spi-nor-add-support-for-Gigadevice-GD25D05.patch new file mode 100644 index 0000000..c32cde5 --- /dev/null +++ b/pkgs/patches-linux-5.15/481-mtd-spi-nor-add-support-for-Gigadevice-GD25D05.patch @@ -0,0 +1,22 @@ +From d68b4aa22e8c625685bfad642dd7337948dc0ad1 Mon Sep 17 00:00:00 2001 +From: Koen Vandeputte <koen.vandeputte@ncentric.com> +Date: Mon, 6 Jan 2020 13:07:56 +0100 +Subject: [PATCH] mtd: spi-nor: add support for Gigadevice GD25D05 + +Signed-off-by: Koen Vandeputte <koen.vandeputte@ncentric.com> +--- + drivers/mtd/spi-nor/spi-nor.c | 5 +++++ + 1 file changed, 5 insertions(+) + +--- a/drivers/mtd/spi-nor/gigadevice.c ++++ b/drivers/mtd/spi-nor/gigadevice.c +@@ -24,6 +24,9 @@ static struct spi_nor_fixups gd25q256_fi + }; + + static const struct flash_info gigadevice_parts[] = { ++ { "gd25q05", INFO(0xc84010, 0, 64 * 1024, 1, ++ SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | ++ SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB) }, + { "gd25q16", INFO(0xc84015, 0, 64 * 1024, 32, + SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | + SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB) }, diff --git a/pkgs/patches-linux-5.15/482-mtd-spi-nor-add-gd25q512.patch b/pkgs/patches-linux-5.15/482-mtd-spi-nor-add-gd25q512.patch new file mode 100644 index 0000000..96848e1 --- /dev/null +++ b/pkgs/patches-linux-5.15/482-mtd-spi-nor-add-gd25q512.patch @@ -0,0 +1,25 @@ +From f8943df3beb0d3f9754bb35320c3a378727175a8 Mon Sep 17 00:00:00 2001 +From: OpenWrt community <openwrt-devel@lists.openwrt.org> +Date: Thu, 14 Jul 2022 08:38:07 +0200 +Subject: [PATCH] spi-nor/gigadevic: add gd25q512 + +--- + drivers/mtd/spi-nor/gigadevice.c | 3 +++ + 1 file changed, 3 insertions(+) + +diff --git a/drivers/mtd/spi-nor/gigadevice.c b/drivers/mtd/spi-nor/gigadevice.c +index e52ed19d864f..d25292ec1eb9 100644 +--- a/drivers/mtd/spi-nor/gigadevice.c ++++ b/drivers/mtd/spi-nor/gigadevice.c +@@ -53,6 +53,9 @@ static const struct flash_info gigadevice_parts[] = { + SPI_NOR_4B_OPCODES | SPI_NOR_HAS_LOCK | + SPI_NOR_HAS_TB | SPI_NOR_TB_SR_BIT6) + .fixups = &gd25q256_fixups }, ++ { "gd25q512", INFO(0xc84020, 0, 64 * 1024, 1024, ++ SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | ++ SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB | SPI_NOR_4B_OPCODES) }, + }; + + const struct spi_nor_manufacturer spi_nor_gigadevice = { +-- + diff --git a/pkgs/patches-linux-5.15/483-mtd-spinand-add-support-for-xtx-xt26g0xa.patch b/pkgs/patches-linux-5.15/483-mtd-spinand-add-support-for-xtx-xt26g0xa.patch new file mode 100644 index 0000000..a73c9fa --- /dev/null +++ b/pkgs/patches-linux-5.15/483-mtd-spinand-add-support-for-xtx-xt26g0xa.patch @@ -0,0 +1,178 @@ +From a07e31adf2753cad2fd9790db5bfc047c81e8152 Mon Sep 17 00:00:00 2001 +From: Felix Matouschek <felix@matouschek.org> +Date: Fri, 2 Jul 2021 20:31:23 +0200 +Subject: [PATCH] mtd: spinand: Add support for XTX XT26G0xA + +Add support for XTX Technology XT26G01AXXXXX, XTX26G02AXXXXX and +XTX26G04AXXXXX SPI NAND. + +These are 3V, 1G/2G/4Gbit serial SLC NAND flash devices with on-die ECC +(8bit strength per 512bytes). + +Tested on Teltonika RUTX10 flashed with OpenWrt. + +Datasheets available at +http://www.xtxtech.com/download/?AId=225 +https://datasheet.lcsc.com/szlcsc/2005251034_XTX-XT26G01AWSEGA_C558841.pdf + +Signed-off-by: Felix Matouschek <felix@matouschek.org> +--- + drivers/mtd/nand/spi/Makefile | 2 +- + drivers/mtd/nand/spi/core.c | 1 + + drivers/mtd/nand/spi/xtx.c | 122 ++++++++++++++++++++++++++++++++++ + include/linux/mtd/spinand.h | 1 + + 4 files changed, 125 insertions(+), 1 deletion(-) + create mode 100644 drivers/mtd/nand/spi/xtx.c + +--- a/drivers/mtd/nand/spi/Makefile ++++ b/drivers/mtd/nand/spi/Makefile +@@ -1,3 +1,3 @@ + # SPDX-License-Identifier: GPL-2.0 +-spinand-objs := core.o gigadevice.o macronix.o micron.o paragon.o toshiba.o winbond.o ++spinand-objs := core.o gigadevice.o macronix.o micron.o paragon.o toshiba.o winbond.o xtx.o + obj-$(CONFIG_MTD_SPI_NAND) += spinand.o +--- a/drivers/mtd/nand/spi/core.c ++++ b/drivers/mtd/nand/spi/core.c +@@ -902,6 +902,7 @@ static const struct spinand_manufacturer + ¶gon_spinand_manufacturer, + &toshiba_spinand_manufacturer, + &winbond_spinand_manufacturer, ++ &xtx_spinand_manufacturer, + }; + + static int spinand_manufacturer_match(struct spinand_device *spinand, +--- /dev/null ++++ b/drivers/mtd/nand/spi/xtx.c +@@ -0,0 +1,122 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Author: ++ * Felix Matouschek <felix@matouschek.org> ++ */ ++ ++#include <linux/device.h> ++#include <linux/kernel.h> ++#include <linux/mtd/spinand.h> ++ ++#define SPINAND_MFR_XTX 0x0B ++ ++#define XT26G0XA_STATUS_ECC_MASK GENMASK(5, 2) ++#define XT26G0XA_STATUS_ECC_NO_DETECTED (0 << 2) ++#define XT26G0XA_STATUS_ECC_8_CORRECTED (3 << 4) ++#define XT26G0XA_STATUS_ECC_UNCOR_ERROR (2 << 4) ++ ++static SPINAND_OP_VARIANTS(read_cache_variants, ++ SPINAND_PAGE_READ_FROM_CACHE_QUADIO_OP(0, 1, NULL, 0), ++ SPINAND_PAGE_READ_FROM_CACHE_X4_OP(0, 1, NULL, 0), ++ SPINAND_PAGE_READ_FROM_CACHE_DUALIO_OP(0, 1, NULL, 0), ++ SPINAND_PAGE_READ_FROM_CACHE_X2_OP(0, 1, NULL, 0), ++ SPINAND_PAGE_READ_FROM_CACHE_OP(true, 0, 1, NULL, 0), ++ SPINAND_PAGE_READ_FROM_CACHE_OP(false, 0, 1, NULL, 0)); ++ ++static SPINAND_OP_VARIANTS(write_cache_variants, ++ SPINAND_PROG_LOAD_X4(true, 0, NULL, 0), ++ SPINAND_PROG_LOAD(true, 0, NULL, 0)); ++ ++static SPINAND_OP_VARIANTS(update_cache_variants, ++ SPINAND_PROG_LOAD_X4(false, 0, NULL, 0), ++ SPINAND_PROG_LOAD(false, 0, NULL, 0)); ++ ++static int xt26g0xa_ooblayout_ecc(struct mtd_info *mtd, int section, ++ struct mtd_oob_region *region) ++{ ++ if (section) ++ return -ERANGE; ++ ++ region->offset = 48; ++ region->length = 16; ++ ++ return 0; ++} ++ ++static int xt26g0xa_ooblayout_free(struct mtd_info *mtd, int section, ++ struct mtd_oob_region *region) ++{ ++ if (section) ++ return -ERANGE; ++ ++ region->offset = 1; ++ region->length = 47; ++ ++ return 0; ++} ++ ++static const struct mtd_ooblayout_ops xt26g0xa_ooblayout = { ++ .ecc = xt26g0xa_ooblayout_ecc, ++ .free = xt26g0xa_ooblayout_free, ++}; ++ ++static int xt26g0xa_ecc_get_status(struct spinand_device *spinand, ++ u8 status) ++{ ++ switch (status & XT26G0XA_STATUS_ECC_MASK) { ++ case XT26G0XA_STATUS_ECC_NO_DETECTED: ++ return 0; ++ case XT26G0XA_STATUS_ECC_8_CORRECTED: ++ return 8; ++ case XT26G0XA_STATUS_ECC_UNCOR_ERROR: ++ return -EBADMSG; ++ default: /* (1 << 2) through (7 << 2) are 1-7 corrected errors */ ++ return (status & XT26G0XA_STATUS_ECC_MASK) >> 2; ++ } ++ ++ return -EINVAL; ++} ++ ++static const struct spinand_info xtx_spinand_table[] = { ++ SPINAND_INFO("XT26G01A", ++ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_ADDR, 0xE1), ++ NAND_MEMORG(1, 2048, 64, 64, 1024, 20, 1, 1, 1), ++ NAND_ECCREQ(8, 512), ++ SPINAND_INFO_OP_VARIANTS(&read_cache_variants, ++ &write_cache_variants, ++ &update_cache_variants), ++ SPINAND_HAS_QE_BIT, ++ SPINAND_ECCINFO(&xt26g0xa_ooblayout, ++ xt26g0xa_ecc_get_status)), ++ SPINAND_INFO("XT26G02A", ++ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_ADDR, 0xE2), ++ NAND_MEMORG(1, 2048, 64, 64, 2048, 40, 1, 1, 1), ++ NAND_ECCREQ(8, 512), ++ SPINAND_INFO_OP_VARIANTS(&read_cache_variants, ++ &write_cache_variants, ++ &update_cache_variants), ++ SPINAND_HAS_QE_BIT, ++ SPINAND_ECCINFO(&xt26g0xa_ooblayout, ++ xt26g0xa_ecc_get_status)), ++ SPINAND_INFO("XT26G04A", ++ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_ADDR, 0xE3), ++ NAND_MEMORG(1, 2048, 64, 128, 2048, 40, 1, 1, 1), ++ NAND_ECCREQ(8, 512), ++ SPINAND_INFO_OP_VARIANTS(&read_cache_variants, ++ &write_cache_variants, ++ &update_cache_variants), ++ SPINAND_HAS_QE_BIT, ++ SPINAND_ECCINFO(&xt26g0xa_ooblayout, ++ xt26g0xa_ecc_get_status)), ++}; ++ ++static const struct spinand_manufacturer_ops xtx_spinand_manuf_ops = { ++}; ++ ++const struct spinand_manufacturer xtx_spinand_manufacturer = { ++ .id = SPINAND_MFR_XTX, ++ .name = "XTX", ++ .chips = xtx_spinand_table, ++ .nchips = ARRAY_SIZE(xtx_spinand_table), ++ .ops = &xtx_spinand_manuf_ops, ++}; +--- a/include/linux/mtd/spinand.h ++++ b/include/linux/mtd/spinand.h +@@ -266,6 +266,7 @@ extern const struct spinand_manufacturer + extern const struct spinand_manufacturer paragon_spinand_manufacturer; + extern const struct spinand_manufacturer toshiba_spinand_manufacturer; + extern const struct spinand_manufacturer winbond_spinand_manufacturer; ++extern const struct spinand_manufacturer xtx_spinand_manufacturer; + + /** + * struct spinand_op_variants - SPI NAND operation variants diff --git a/pkgs/patches-linux-5.15/484-mtd-spi-nor-add-esmt-f25l16pa.patch b/pkgs/patches-linux-5.15/484-mtd-spi-nor-add-esmt-f25l16pa.patch new file mode 100644 index 0000000..d5b7259 --- /dev/null +++ b/pkgs/patches-linux-5.15/484-mtd-spi-nor-add-esmt-f25l16pa.patch @@ -0,0 +1,27 @@ +From 87363cc0e522de3294ea6ae10fb468d2a8d6fb2f Mon Sep 17 00:00:00 2001 +From: OpenWrt community <openwrt-devel@lists.openwrt.org> +Date: Wed, 13 Jul 2022 12:17:21 +0200 +Subject: [PATCH] spi-nor/esmt.c: add esmt f25l16pa + +This fixes support for Dongwon T&I DW02-412H which uses F25L16PA(2S) +flash. + +--- + drivers/mtd/spi-nor/esmt.c | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/drivers/mtd/spi-nor/esmt.c b/drivers/mtd/spi-nor/esmt.c +index cfc9218c1053..d1750807f005 100644 +--- a/drivers/mtd/spi-nor/esmt.c ++++ b/drivers/mtd/spi-nor/esmt.c +@@ -10,6 +10,8 @@ + + static const struct flash_info esmt_parts[] = { + /* ESMT */ ++ { "f25l16pa-2s", INFO(0x8c2115, 0, 64 * 1024, 32, ++ SECT_4K | SPI_NOR_HAS_LOCK) }, + { "f25l32pa", INFO(0x8c2016, 0, 64 * 1024, 64, + SECT_4K | SPI_NOR_HAS_LOCK | SPI_NOR_SWP_IS_VOLATILE) }, + { "f25l32qa", INFO(0x8c4116, 0, 64 * 1024, 64, +-- + diff --git a/pkgs/patches-linux-5.15/485-mtd-spi-nor-add-xmc-xm25qh128c.patch b/pkgs/patches-linux-5.15/485-mtd-spi-nor-add-xmc-xm25qh128c.patch new file mode 100644 index 0000000..2823464 --- /dev/null +++ b/pkgs/patches-linux-5.15/485-mtd-spi-nor-add-xmc-xm25qh128c.patch @@ -0,0 +1,28 @@ +From f6b33d850f7f12555df2fa0e3349b33427bf5890 Mon Sep 17 00:00:00 2001 +From: OpenWrt community <openwrt-devel@lists.openwrt.org> +Date: Wed, 13 Jul 2022 12:19:01 +0200 +Subject: [PATCH] spi-nor/xmc.c: add xm25qh128c + +The XMC XM25QH128C is a 16MB SPI NOR chip. The patch is verified on +Ruijie RG-EW3200GX PRO. +Datasheet available at https://www.xmcwh.com/uploads/435/XM25QH128C.pdf + +--- + drivers/mtd/spi-nor/xmc.c | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/drivers/mtd/spi-nor/xmc.c b/drivers/mtd/spi-nor/xmc.c +index 2c7773b68993..99ee43a654c4 100644 +--- a/drivers/mtd/spi-nor/xmc.c ++++ b/drivers/mtd/spi-nor/xmc.c +@@ -14,6 +14,8 @@ static const struct flash_info xmc_parts[] = { + SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) }, + { "XM25QH128A", INFO(0x207018, 0, 64 * 1024, 256, + SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) }, ++ { "XM25QH128C", INFO(0x204018, 0, 64 * 1024, 256, ++ SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) }, + }; + + const struct spi_nor_manufacturer spi_nor_xmc = { +-- + diff --git a/pkgs/patches-linux-5.15/486-01-mtd-spinand-add-support-for-ESMT-F50x1G41LB.patch b/pkgs/patches-linux-5.15/486-01-mtd-spinand-add-support-for-ESMT-F50x1G41LB.patch new file mode 100644 index 0000000..c170fed --- /dev/null +++ b/pkgs/patches-linux-5.15/486-01-mtd-spinand-add-support-for-ESMT-F50x1G41LB.patch @@ -0,0 +1,143 @@ +From a43b844cb40bf1b783055fdc81b7f991e21e7e76 Mon Sep 17 00:00:00 2001 +From: Chuanhong Guo <gch981213@gmail.com> +Date: Wed, 13 Apr 2022 11:58:17 +0800 +Subject: [PATCH] mtd: spinand: add support for ESMT F50x1G41LB + +This patch adds support for ESMT F50L1G41LB and F50D1G41LB. +It seems that ESMT likes to use random JEDEC ID from other vendors. +Their 1G chips uses 0xc8 from GigaDevice and 2G/4G chips uses 0x2c from +Micron. For this reason, the ESMT entry is named esmt_c8 with explicit +JEDEC ID in variable name. + +Datasheets: +https://www.esmt.com.tw/upload/pdf/ESMT/datasheets/F50L1G41LB(2M).pdf +https://www.esmt.com.tw/upload/pdf/ESMT/datasheets/F50D1G41LB(2M).pdf + +Signed-off-by: Chuanhong Guo <gch981213@gmail.com> +--- + drivers/mtd/nand/spi/Makefile | 2 +- + drivers/mtd/nand/spi/core.c | 1 + + drivers/mtd/nand/spi/esmt.c | 89 +++++++++++++++++++++++++++++++++++ + include/linux/mtd/spinand.h | 1 + + 4 files changed, 92 insertions(+), 1 deletion(-) + create mode 100644 drivers/mtd/nand/spi/esmt.c + +--- a/drivers/mtd/nand/spi/Makefile ++++ b/drivers/mtd/nand/spi/Makefile +@@ -1,3 +1,3 @@ + # SPDX-License-Identifier: GPL-2.0 +-spinand-objs := core.o gigadevice.o macronix.o micron.o paragon.o toshiba.o winbond.o xtx.o ++spinand-objs := core.o esmt.o gigadevice.o macronix.o micron.o paragon.o toshiba.o winbond.o xtx.o + obj-$(CONFIG_MTD_SPI_NAND) += spinand.o +--- a/drivers/mtd/nand/spi/core.c ++++ b/drivers/mtd/nand/spi/core.c +@@ -896,6 +896,7 @@ static const struct nand_ops spinand_ops + }; + + static const struct spinand_manufacturer *spinand_manufacturers[] = { ++ &esmt_c8_spinand_manufacturer, + &gigadevice_spinand_manufacturer, + ¯onix_spinand_manufacturer, + µn_spinand_manufacturer, +--- /dev/null ++++ b/drivers/mtd/nand/spi/esmt.c +@@ -0,0 +1,89 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Author: ++ * Chuanhong Guo <gch981213@gmail.com> ++ */ ++ ++#include <linux/device.h> ++#include <linux/kernel.h> ++#include <linux/mtd/spinand.h> ++ ++/* ESMT uses GigaDevice 0xc8 JECDEC ID on some SPI NANDs */ ++#define SPINAND_MFR_ESMT_C8 0xc8 ++ ++static SPINAND_OP_VARIANTS(read_cache_variants, ++ SPINAND_PAGE_READ_FROM_CACHE_QUADIO_OP(0, 2, NULL, 0), ++ SPINAND_PAGE_READ_FROM_CACHE_X4_OP(0, 1, NULL, 0), ++ SPINAND_PAGE_READ_FROM_CACHE_DUALIO_OP(0, 1, NULL, 0), ++ SPINAND_PAGE_READ_FROM_CACHE_X2_OP(0, 1, NULL, 0), ++ SPINAND_PAGE_READ_FROM_CACHE_OP(true, 0, 1, NULL, 0), ++ SPINAND_PAGE_READ_FROM_CACHE_OP(false, 0, 1, NULL, 0)); ++ ++static SPINAND_OP_VARIANTS(write_cache_variants, ++ SPINAND_PROG_LOAD_X4(true, 0, NULL, 0), ++ SPINAND_PROG_LOAD(true, 0, NULL, 0)); ++ ++static SPINAND_OP_VARIANTS(update_cache_variants, ++ SPINAND_PROG_LOAD_X4(false, 0, NULL, 0), ++ SPINAND_PROG_LOAD(false, 0, NULL, 0)); ++ ++static int f50l1g41lb_ooblayout_ecc(struct mtd_info *mtd, int section, ++ struct mtd_oob_region *region) ++{ ++ if (section > 3) ++ return -ERANGE; ++ ++ region->offset = 16 * section + 8; ++ region->length = 8; ++ ++ return 0; ++} ++ ++static int f50l1g41lb_ooblayout_free(struct mtd_info *mtd, int section, ++ struct mtd_oob_region *region) ++{ ++ if (section > 3) ++ return -ERANGE; ++ ++ region->offset = 16 * section + 2; ++ region->length = 6; ++ ++ return 0; ++} ++ ++static const struct mtd_ooblayout_ops f50l1g41lb_ooblayout = { ++ .ecc = f50l1g41lb_ooblayout_ecc, ++ .free = f50l1g41lb_ooblayout_free, ++}; ++ ++static const struct spinand_info esmt_c8_spinand_table[] = { ++ SPINAND_INFO("F50L1G41LB", ++ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_ADDR, 0x01), ++ NAND_MEMORG(1, 2048, 64, 64, 1024, 20, 1, 1, 1), ++ NAND_ECCREQ(1, 512), ++ SPINAND_INFO_OP_VARIANTS(&read_cache_variants, ++ &write_cache_variants, ++ &update_cache_variants), ++ 0, ++ SPINAND_ECCINFO(&f50l1g41lb_ooblayout, NULL)), ++ SPINAND_INFO("F50D1G41LB", ++ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_ADDR, 0x11), ++ NAND_MEMORG(1, 2048, 64, 64, 1024, 20, 1, 1, 1), ++ NAND_ECCREQ(1, 512), ++ SPINAND_INFO_OP_VARIANTS(&read_cache_variants, ++ &write_cache_variants, ++ &update_cache_variants), ++ 0, ++ SPINAND_ECCINFO(&f50l1g41lb_ooblayout, NULL)), ++}; ++ ++static const struct spinand_manufacturer_ops esmt_spinand_manuf_ops = { ++}; ++ ++const struct spinand_manufacturer esmt_c8_spinand_manufacturer = { ++ .id = SPINAND_MFR_ESMT_C8, ++ .name = "ESMT", ++ .chips = esmt_c8_spinand_table, ++ .nchips = ARRAY_SIZE(esmt_c8_spinand_table), ++ .ops = &esmt_spinand_manuf_ops, ++}; +--- a/include/linux/mtd/spinand.h ++++ b/include/linux/mtd/spinand.h +@@ -260,6 +260,7 @@ struct spinand_manufacturer { + }; + + /* SPI NAND manufacturers */ ++extern const struct spinand_manufacturer esmt_c8_spinand_manufacturer; + extern const struct spinand_manufacturer gigadevice_spinand_manufacturer; + extern const struct spinand_manufacturer macronix_spinand_manufacturer; + extern const struct spinand_manufacturer micron_spinand_manufacturer; diff --git a/pkgs/patches-linux-5.15/490-ubi-auto-attach-mtd-device-named-ubi-or-data-on-boot.patch b/pkgs/patches-linux-5.15/490-ubi-auto-attach-mtd-device-named-ubi-or-data-on-boot.patch new file mode 100644 index 0000000..b120548 --- /dev/null +++ b/pkgs/patches-linux-5.15/490-ubi-auto-attach-mtd-device-named-ubi-or-data-on-boot.patch @@ -0,0 +1,97 @@ +From: Daniel Golle <daniel@makrotopia.org> +Subject: ubi: auto-attach mtd device named "ubi" or "data" on boot + +Signed-off-by: Daniel Golle <daniel@makrotopia.org> +--- + drivers/mtd/ubi/build.c | 36 ++++++++++++++++++++++++++++++++++++ + 1 file changed, 36 insertions(+) + +--- a/drivers/mtd/ubi/build.c ++++ b/drivers/mtd/ubi/build.c +@@ -1184,6 +1184,73 @@ static struct mtd_info * __init open_mtd + return mtd; + } + ++/* ++ * This function tries attaching mtd partitions named either "ubi" or "data" ++ * during boot. ++ */ ++static void __init ubi_auto_attach(void) ++{ ++ int err; ++ struct mtd_info *mtd; ++ loff_t offset = 0; ++ size_t len; ++ char magic[4]; ++ ++ /* try attaching mtd device named "ubi" or "data" */ ++ mtd = open_mtd_device("ubi"); ++ if (IS_ERR(mtd)) ++ mtd = open_mtd_device("data"); ++ ++ if (IS_ERR(mtd)) ++ return; ++ ++ /* get the first not bad block */ ++ if (mtd_can_have_bb(mtd)) ++ while (mtd_block_isbad(mtd, offset)) { ++ offset += mtd->erasesize; ++ ++ if (offset > mtd->size) { ++ pr_err("UBI error: Failed to find a non-bad " ++ "block on mtd%d\n", mtd->index); ++ goto cleanup; ++ } ++ } ++ ++ /* check if the read from flash was successful */ ++ err = mtd_read(mtd, offset, 4, &len, (void *) magic); ++ if ((err && !mtd_is_bitflip(err)) || len != 4) { ++ pr_err("UBI error: unable to read from mtd%d\n", mtd->index); ++ goto cleanup; ++ } ++ ++ /* check for a valid ubi magic */ ++ if (strncmp(magic, "UBI#", 4)) { ++ pr_err("UBI error: no valid UBI magic found inside mtd%d\n", mtd->index); ++ goto cleanup; ++ } ++ ++ /* don't auto-add media types where UBI doesn't makes sense */ ++ if (mtd->type != MTD_NANDFLASH && ++ mtd->type != MTD_NORFLASH && ++ mtd->type != MTD_DATAFLASH && ++ mtd->type != MTD_MLCNANDFLASH) ++ goto cleanup; ++ ++ mutex_lock(&ubi_devices_mutex); ++ pr_notice("UBI: auto-attach mtd%d\n", mtd->index); ++ err = ubi_attach_mtd_dev(mtd, UBI_DEV_NUM_AUTO, 0, 0); ++ mutex_unlock(&ubi_devices_mutex); ++ if (err < 0) { ++ pr_err("UBI error: cannot attach mtd%d\n", mtd->index); ++ goto cleanup; ++ } ++ ++ return; ++ ++cleanup: ++ put_mtd_device(mtd); ++} ++ + static int __init ubi_init(void) + { + int err, i, k; +@@ -1267,6 +1334,12 @@ static int __init ubi_init(void) + } + } + ++ /* auto-attach mtd devices only if built-in to the kernel and no ubi.mtd ++ * parameter was given */ ++ if (IS_ENABLED(CONFIG_MTD_ROOTFS_ROOT_DEV) && ++ !ubi_is_module() && !mtd_devs) ++ ubi_auto_attach(); ++ + err = ubiblock_init(); + if (err) { + pr_err("UBI error: block: cannot initialize, error %d\n", err); diff --git a/pkgs/patches-linux-5.15/491-ubi-auto-create-ubiblock-device-for-rootfs.patch b/pkgs/patches-linux-5.15/491-ubi-auto-create-ubiblock-device-for-rootfs.patch new file mode 100644 index 0000000..ae53770 --- /dev/null +++ b/pkgs/patches-linux-5.15/491-ubi-auto-create-ubiblock-device-for-rootfs.patch @@ -0,0 +1,69 @@ +From: Daniel Golle <daniel@makrotopia.org> +Subject: ubi: auto-create ubiblock device for rootfs + +Signed-off-by: Daniel Golle <daniel@makrotopia.org> +--- + drivers/mtd/ubi/block.c | 42 ++++++++++++++++++++++++++++++++++++++++++ + 1 file changed, 42 insertions(+) + +--- a/drivers/mtd/ubi/block.c ++++ b/drivers/mtd/ubi/block.c +@@ -642,6 +642,47 @@ static void __init ubiblock_create_from_ + } + } + ++#define UBIFS_NODE_MAGIC 0x06101831 ++static inline int ubi_vol_is_ubifs(struct ubi_volume_desc *desc) ++{ ++ int ret; ++ uint32_t magic_of, magic; ++ ret = ubi_read(desc, 0, (char *)&magic_of, 0, 4); ++ if (ret) ++ return 0; ++ magic = le32_to_cpu(magic_of); ++ return magic == UBIFS_NODE_MAGIC; ++} ++ ++static void __init ubiblock_create_auto_rootfs(void) ++{ ++ int ubi_num, ret, is_ubifs; ++ struct ubi_volume_desc *desc; ++ struct ubi_volume_info vi; ++ ++ for (ubi_num = 0; ubi_num < UBI_MAX_DEVICES; ubi_num++) { ++ desc = ubi_open_volume_nm(ubi_num, "rootfs", UBI_READONLY); ++ if (IS_ERR(desc)) ++ desc = ubi_open_volume_nm(ubi_num, "fit", UBI_READONLY);; ++ ++ if (IS_ERR(desc)) ++ continue; ++ ++ ubi_get_volume_info(desc, &vi); ++ is_ubifs = ubi_vol_is_ubifs(desc); ++ ubi_close_volume(desc); ++ if (is_ubifs) ++ break; ++ ++ ret = ubiblock_create(&vi); ++ if (ret) ++ pr_err("UBI error: block: can't add '%s' volume, err=%d\n", ++ vi.name, ret); ++ /* always break if we get here */ ++ break; ++ } ++} ++ + static void ubiblock_remove_all(void) + { + struct ubiblock *next; +@@ -674,6 +715,10 @@ int __init ubiblock_init(void) + */ + ubiblock_create_from_param(); + ++ /* auto-attach "rootfs" volume if existing and non-ubifs */ ++ if (IS_ENABLED(CONFIG_MTD_ROOTFS_ROOT_DEV)) ++ ubiblock_create_auto_rootfs(); ++ + /* + * Block devices are only created upon user requests, so we ignore + * existing volumes. diff --git a/pkgs/patches-linux-5.15/492-try-auto-mounting-ubi0-rootfs-in-init-do_mounts.c.patch b/pkgs/patches-linux-5.15/492-try-auto-mounting-ubi0-rootfs-in-init-do_mounts.c.patch new file mode 100644 index 0000000..cf41c8c --- /dev/null +++ b/pkgs/patches-linux-5.15/492-try-auto-mounting-ubi0-rootfs-in-init-do_mounts.c.patch @@ -0,0 +1,53 @@ +From: Daniel Golle <daniel@makrotopia.org> +Subject: try auto-mounting ubi0:rootfs in init/do_mounts.c + +Signed-off-by: Daniel Golle <daniel@makrotopia.org> +--- + init/do_mounts.c | 26 +++++++++++++++++++++++++- + 1 file changed, 25 insertions(+), 1 deletion(-) + +--- a/init/do_mounts.c ++++ b/init/do_mounts.c +@@ -447,7 +447,30 @@ retry: + out: + put_page(page); + } +- ++ ++#ifdef CONFIG_MTD_ROOTFS_ROOT_DEV ++static int __init mount_ubi_rootfs(void) ++{ ++ int flags = MS_SILENT; ++ int err, tried = 0; ++ ++ while (tried < 2) { ++ err = do_mount_root("ubi0:rootfs", "ubifs", flags, \ ++ root_mount_data); ++ switch (err) { ++ case -EACCES: ++ flags |= MS_RDONLY; ++ tried++; ++ break; ++ default: ++ return err; ++ } ++ } ++ ++ return -EINVAL; ++} ++#endif ++ + #ifdef CONFIG_ROOT_NFS + + #define NFSROOT_TIMEOUT_MIN 5 +@@ -580,6 +603,10 @@ void __init mount_root(void) + return; + } + #endif ++#ifdef CONFIG_MTD_ROOTFS_ROOT_DEV ++ if (!mount_ubi_rootfs()) ++ return; ++#endif + if (ROOT_DEV == 0 && root_device_name && root_fs_names) { + if (mount_nodev_root() == 0) + return; diff --git a/pkgs/patches-linux-5.15/493-ubi-set-ROOT_DEV-to-ubiblock-rootfs-if-unset.patch b/pkgs/patches-linux-5.15/493-ubi-set-ROOT_DEV-to-ubiblock-rootfs-if-unset.patch new file mode 100644 index 0000000..266a633 --- /dev/null +++ b/pkgs/patches-linux-5.15/493-ubi-set-ROOT_DEV-to-ubiblock-rootfs-if-unset.patch @@ -0,0 +1,34 @@ +From: Daniel Golle <daniel@makrotopia.org> +Subject: ubi: set ROOT_DEV to ubiblock "rootfs" if unset + +Signed-off-by: Daniel Golle <daniel@makrotopia.org> +--- + drivers/mtd/ubi/block.c | 10 ++++++++++ + 1 file changed, 10 insertions(+) + +--- a/drivers/mtd/ubi/block.c ++++ b/drivers/mtd/ubi/block.c +@@ -42,6 +42,7 @@ + #include <linux/scatterlist.h> + #include <linux/idr.h> + #include <asm/div64.h> ++#include <linux/root_dev.h> + + #include "ubi-media.h" + #include "ubi.h" +@@ -451,6 +452,15 @@ int ubiblock_create(struct ubi_volume_in + dev_info(disk_to_dev(dev->gd), "created from ubi%d:%d(%s)", + dev->ubi_num, dev->vol_id, vi->name); + mutex_unlock(&devices_mutex); ++ ++ if (!strcmp(vi->name, "rootfs") && ++ IS_ENABLED(CONFIG_MTD_ROOTFS_ROOT_DEV) && ++ ROOT_DEV == 0) { ++ pr_notice("ubiblock: device ubiblock%d_%d (%s) set to be root filesystem\n", ++ dev->ubi_num, dev->vol_id, vi->name); ++ ROOT_DEV = MKDEV(gd->major, gd->first_minor); ++ } ++ + return 0; + + out_remove_minor: diff --git a/pkgs/patches-linux-5.15/494-mtd-ubi-add-EOF-marker-support.patch b/pkgs/patches-linux-5.15/494-mtd-ubi-add-EOF-marker-support.patch new file mode 100644 index 0000000..4134317 --- /dev/null +++ b/pkgs/patches-linux-5.15/494-mtd-ubi-add-EOF-marker-support.patch @@ -0,0 +1,60 @@ +From: Gabor Juhos <juhosg@openwrt.org> +Subject: mtd: add EOF marker support to the UBI layer + +Signed-off-by: Gabor Juhos <juhosg@openwrt.org> +--- + drivers/mtd/ubi/attach.c | 25 ++++++++++++++++++++++--- + drivers/mtd/ubi/ubi.h | 1 + + 2 files changed, 23 insertions(+), 3 deletions(-) + +--- a/drivers/mtd/ubi/attach.c ++++ b/drivers/mtd/ubi/attach.c +@@ -926,6 +926,13 @@ static bool vol_ignored(int vol_id) + #endif + } + ++static bool ec_hdr_has_eof(struct ubi_ec_hdr *ech) ++{ ++ return ech->padding1[0] == 'E' && ++ ech->padding1[1] == 'O' && ++ ech->padding1[2] == 'F'; ++} ++ + /** + * scan_peb - scan and process UBI headers of a PEB. + * @ubi: UBI device description object +@@ -958,9 +965,21 @@ static int scan_peb(struct ubi_device *u + return 0; + } + +- err = ubi_io_read_ec_hdr(ubi, pnum, ech, 0); +- if (err < 0) +- return err; ++ if (!ai->eof_found) { ++ err = ubi_io_read_ec_hdr(ubi, pnum, ech, 0); ++ if (err < 0) ++ return err; ++ ++ if (ec_hdr_has_eof(ech)) { ++ pr_notice("UBI: EOF marker found, PEBs from %d will be erased\n", ++ pnum); ++ ai->eof_found = true; ++ } ++ } ++ ++ if (ai->eof_found) ++ err = UBI_IO_FF_BITFLIPS; ++ + switch (err) { + case 0: + break; +--- a/drivers/mtd/ubi/ubi.h ++++ b/drivers/mtd/ubi/ubi.h +@@ -778,6 +778,7 @@ struct ubi_attach_info { + int mean_ec; + uint64_t ec_sum; + int ec_count; ++ bool eof_found; + struct kmem_cache *aeb_slab_cache; + struct ubi_ec_hdr *ech; + struct ubi_vid_io_buf *vidb; diff --git a/pkgs/patches-linux-5.15/495-mtd-core-add-get_mtd_device_by_node.patch b/pkgs/patches-linux-5.15/495-mtd-core-add-get_mtd_device_by_node.patch new file mode 100644 index 0000000..431c807 --- /dev/null +++ b/pkgs/patches-linux-5.15/495-mtd-core-add-get_mtd_device_by_node.patch @@ -0,0 +1,75 @@ +From 1bd1b740f208d1cf4071932cc51860d37266c402 Mon Sep 17 00:00:00 2001 +From: Bernhard Frauendienst <kernel@nospam.obeliks.de> +Date: Sat, 1 Sep 2018 00:30:11 +0200 +Subject: [PATCH 495/497] mtd: core: add get_mtd_device_by_node + +Add function to retrieve a mtd device by its OF node. Since drivers can +assign arbitrary names to mtd devices in the absence of a label +property, there is no other reliable way to retrieve a mtd device for a +given OF node. + +Signed-off-by: Bernhard Frauendienst <kernel@nospam.obeliks.de> +Reviewed-by: Miquel Raynal <miquel.raynal@bootlin.com> +--- + drivers/mtd/mtdcore.c | 38 ++++++++++++++++++++++++++++++++++++++ + include/linux/mtd/mtd.h | 2 ++ + 2 files changed, 40 insertions(+) + +--- a/drivers/mtd/mtdcore.c ++++ b/drivers/mtd/mtdcore.c +@@ -1274,6 +1274,44 @@ out_unlock: + } + EXPORT_SYMBOL_GPL(get_mtd_device_nm); + ++/** ++ * get_mtd_device_by_node - obtain a validated handle for an MTD device ++ * by of_node ++ * @of_node: OF node of MTD device to open ++ * ++ * This function returns MTD device description structure in case of ++ * success and an error code in case of failure. ++ */ ++struct mtd_info *get_mtd_device_by_node(const struct device_node *of_node) ++{ ++ int err = -ENODEV; ++ struct mtd_info *mtd = NULL, *other; ++ ++ mutex_lock(&mtd_table_mutex); ++ ++ mtd_for_each_device(other) { ++ if (of_node == other->dev.of_node) { ++ mtd = other; ++ break; ++ } ++ } ++ ++ if (!mtd) ++ goto out_unlock; ++ ++ err = __get_mtd_device(mtd); ++ if (err) ++ goto out_unlock; ++ ++ mutex_unlock(&mtd_table_mutex); ++ return mtd; ++ ++out_unlock: ++ mutex_unlock(&mtd_table_mutex); ++ return ERR_PTR(err); ++} ++EXPORT_SYMBOL_GPL(get_mtd_device_by_node); ++ + void put_mtd_device(struct mtd_info *mtd) + { + mutex_lock(&mtd_table_mutex); +--- a/include/linux/mtd/mtd.h ++++ b/include/linux/mtd/mtd.h +@@ -703,6 +703,8 @@ extern struct mtd_info *get_mtd_device(s + extern int __get_mtd_device(struct mtd_info *mtd); + extern void __put_mtd_device(struct mtd_info *mtd); + extern struct mtd_info *get_mtd_device_nm(const char *name); ++extern struct mtd_info *get_mtd_device_by_node( ++ const struct device_node *of_node); + extern void put_mtd_device(struct mtd_info *mtd); + + static inline uint64_t mtdpart_get_offset(const struct mtd_info *mtd) diff --git a/pkgs/patches-linux-5.15/496-dt-bindings-add-bindings-for-mtd-concat-devices.patch b/pkgs/patches-linux-5.15/496-dt-bindings-add-bindings-for-mtd-concat-devices.patch new file mode 100644 index 0000000..01f3b9e --- /dev/null +++ b/pkgs/patches-linux-5.15/496-dt-bindings-add-bindings-for-mtd-concat-devices.patch @@ -0,0 +1,52 @@ +From 5734c6669fba7ddb5ef491ccff7159d15dba0b59 Mon Sep 17 00:00:00 2001 +From: Bernhard Frauendienst <kernel@nospam.obeliks.de> +Date: Wed, 5 Sep 2018 01:32:51 +0200 +Subject: [PATCH 496/497] dt-bindings: add bindings for mtd-concat devices + +Document virtual mtd-concat device bindings. + +Signed-off-by: Bernhard Frauendienst <kernel@nospam.obeliks.de> +--- + .../devicetree/bindings/mtd/mtd-concat.txt | 36 +++++++++++++++++++ + 1 file changed, 36 insertions(+) + create mode 100644 Documentation/devicetree/bindings/mtd/mtd-concat.txt + +--- /dev/null ++++ b/Documentation/devicetree/bindings/mtd/mtd-concat.txt +@@ -0,0 +1,36 @@ ++Virtual MTD concat device ++ ++Requires properties: ++- devices: list of phandles to mtd nodes that should be concatenated ++ ++Example: ++ ++&spi { ++ flash0: flash@0 { ++ ... ++ }; ++ flash1: flash@1 { ++ ... ++ }; ++}; ++ ++flash { ++ compatible = "mtd-concat"; ++ ++ devices = <&flash0 &flash1>; ++ ++ partitions { ++ compatible = "fixed-partitions"; ++ ++ partition@0 { ++ label = "boot"; ++ reg = <0x0000000 0x0040000>; ++ read-only; ++ }; ++ ++ partition@40000 { ++ label = "firmware"; ++ reg = <0x0040000 0x1fc0000>; ++ }; ++ } ++} diff --git a/pkgs/patches-linux-5.15/497-mtd-mtdconcat-add-dt-driver-for-concat-devices.patch b/pkgs/patches-linux-5.15/497-mtd-mtdconcat-add-dt-driver-for-concat-devices.patch new file mode 100644 index 0000000..3216801 --- /dev/null +++ b/pkgs/patches-linux-5.15/497-mtd-mtdconcat-add-dt-driver-for-concat-devices.patch @@ -0,0 +1,216 @@ +From e53f712d8eac71f54399b61038ccf87d2cee99d7 Mon Sep 17 00:00:00 2001 +From: Bernhard Frauendienst <kernel@nospam.obeliks.de> +Date: Sat, 25 Aug 2018 12:35:22 +0200 +Subject: [PATCH 497/497] mtd: mtdconcat: add dt driver for concat devices + +Some mtd drivers like physmap variants have support for concatenating +multiple mtd devices, but there is no generic way to define such a +concat device from within the device tree. + +This is useful for some SoC boards that use multiple flash chips as +memory banks of a single mtd device, with partitions spanning chip +borders. + +This commit adds a driver for creating virtual mtd-concat devices. They +must have a compatible = "mtd-concat" line, and define a list of devices +to concat in the 'devices' property, for example: + +flash { + compatible = "mtd-concat"; + + devices = <&flash0 &flash1>; + + partitions { + ... + }; +}; + +The driver is added to the very end of the mtd Makefile to increase the +likelyhood of all child devices already being loaded at the time of +probing, preventing unnecessary deferred probes. + +Signed-off-by: Bernhard Frauendienst <kernel@nospam.obeliks.de> +--- + drivers/mtd/Kconfig | 2 + + drivers/mtd/Makefile | 3 + + drivers/mtd/composite/Kconfig | 12 +++ + drivers/mtd/composite/Makefile | 6 ++ + drivers/mtd/composite/virt_concat.c | 128 ++++++++++++++++++++++++++++ + 5 files changed, 151 insertions(+) + create mode 100644 drivers/mtd/composite/Kconfig + create mode 100644 drivers/mtd/composite/Makefile + create mode 100644 drivers/mtd/composite/virt_concat.c + +--- a/drivers/mtd/Kconfig ++++ b/drivers/mtd/Kconfig +@@ -241,4 +241,6 @@ source "drivers/mtd/ubi/Kconfig" + + source "drivers/mtd/hyperbus/Kconfig" + ++source "drivers/mtd/composite/Kconfig" ++ + endif # MTD +--- a/drivers/mtd/Makefile ++++ b/drivers/mtd/Makefile +@@ -33,3 +33,6 @@ obj-y += chips/ lpddr/ maps/ devices/ n + obj-$(CONFIG_MTD_SPI_NOR) += spi-nor/ + obj-$(CONFIG_MTD_UBI) += ubi/ + obj-$(CONFIG_MTD_HYPERBUS) += hyperbus/ ++ ++# Composite drivers must be loaded last ++obj-y += composite/ +--- /dev/null ++++ b/drivers/mtd/composite/Kconfig +@@ -0,0 +1,12 @@ ++menu "Composite MTD device drivers" ++ depends on MTD!=n ++ ++config MTD_VIRT_CONCAT ++ tristate "Virtual concat MTD device" ++ help ++ This driver allows creation of a virtual MTD concat device, which ++ concatenates multiple underlying MTD devices to a single device. ++ This is required by some SoC boards where multiple memory banks are ++ used as one device with partitions spanning across device boundaries. ++ ++endmenu +--- /dev/null ++++ b/drivers/mtd/composite/Makefile +@@ -0,0 +1,6 @@ ++# SPDX-License-Identifier: GPL-2.0 ++# ++# linux/drivers/mtd/composite/Makefile ++# ++ ++obj-$(CONFIG_MTD_VIRT_CONCAT) += virt_concat.o +--- /dev/null ++++ b/drivers/mtd/composite/virt_concat.c +@@ -0,0 +1,128 @@ ++// SPDX-License-Identifier: GPL-2.0+ ++/* ++ * Virtual concat MTD device driver ++ * ++ * Copyright (C) 2018 Bernhard Frauendienst ++ * Author: Bernhard Frauendienst, kernel@nospam.obeliks.de ++ */ ++ ++#include <linux/module.h> ++#include <linux/device.h> ++#include <linux/mtd/concat.h> ++#include <linux/mtd/mtd.h> ++#include <linux/mtd/partitions.h> ++#include <linux/of.h> ++#include <linux/of_platform.h> ++#include <linux/slab.h> ++ ++/* ++ * struct of_virt_concat - platform device driver data. ++ * @cmtd the final mtd_concat device ++ * @num_devices the number of devices in @devices ++ * @devices points to an array of devices already loaded ++ */ ++struct of_virt_concat { ++ struct mtd_info *cmtd; ++ int num_devices; ++ struct mtd_info **devices; ++}; ++ ++static int virt_concat_remove(struct platform_device *pdev) ++{ ++ struct of_virt_concat *info; ++ int i; ++ ++ info = platform_get_drvdata(pdev); ++ if (!info) ++ return 0; ++ ++ // unset data for when this is called after a probe error ++ platform_set_drvdata(pdev, NULL); ++ ++ if (info->cmtd) { ++ mtd_device_unregister(info->cmtd); ++ mtd_concat_destroy(info->cmtd); ++ } ++ ++ if (info->devices) { ++ for (i = 0; i < info->num_devices; i++) ++ put_mtd_device(info->devices[i]); ++ } ++ ++ return 0; ++} ++ ++static int virt_concat_probe(struct platform_device *pdev) ++{ ++ struct device_node *node = pdev->dev.of_node; ++ struct of_phandle_iterator it; ++ struct of_virt_concat *info; ++ struct mtd_info *mtd; ++ int err = 0, count; ++ ++ count = of_count_phandle_with_args(node, "devices", NULL); ++ if (count <= 0) ++ return -EINVAL; ++ ++ info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL); ++ if (!info) ++ return -ENOMEM; ++ info->devices = devm_kcalloc(&pdev->dev, count, ++ sizeof(*(info->devices)), GFP_KERNEL); ++ if (!info->devices) { ++ err = -ENOMEM; ++ goto err_remove; ++ } ++ ++ platform_set_drvdata(pdev, info); ++ ++ of_for_each_phandle(&it, err, node, "devices", NULL, 0) { ++ mtd = get_mtd_device_by_node(it.node); ++ if (IS_ERR(mtd)) { ++ of_node_put(it.node); ++ err = -EPROBE_DEFER; ++ goto err_remove; ++ } ++ ++ info->devices[info->num_devices++] = mtd; ++ } ++ ++ info->cmtd = mtd_concat_create(info->devices, info->num_devices, ++ dev_name(&pdev->dev)); ++ if (!info->cmtd) { ++ err = -ENXIO; ++ goto err_remove; ++ } ++ ++ info->cmtd->dev.parent = &pdev->dev; ++ mtd_set_of_node(info->cmtd, node); ++ mtd_device_register(info->cmtd, NULL, 0); ++ ++ return 0; ++ ++err_remove: ++ virt_concat_remove(pdev); ++ ++ return err; ++} ++ ++static const struct of_device_id virt_concat_of_match[] = { ++ { .compatible = "mtd-concat", }, ++ { /* sentinel */ } ++}; ++MODULE_DEVICE_TABLE(of, virt_concat_of_match); ++ ++static struct platform_driver virt_concat_driver = { ++ .probe = virt_concat_probe, ++ .remove = virt_concat_remove, ++ .driver = { ++ .name = "virt-mtdconcat", ++ .of_match_table = virt_concat_of_match, ++ }, ++}; ++ ++module_platform_driver(virt_concat_driver); ++ ++MODULE_LICENSE("GPL v2"); ++MODULE_AUTHOR("Bernhard Frauendienst <kernel@nospam.obeliks.de>"); ++MODULE_DESCRIPTION("Virtual concat MTD device driver"); diff --git a/pkgs/patches-linux-5.15/498-mtd-spi-nor-locking-support-for-MX25L6405D.patch b/pkgs/patches-linux-5.15/498-mtd-spi-nor-locking-support-for-MX25L6405D.patch new file mode 100644 index 0000000..81de764 --- /dev/null +++ b/pkgs/patches-linux-5.15/498-mtd-spi-nor-locking-support-for-MX25L6405D.patch @@ -0,0 +1,34 @@ +From 8bf2ce6ea4ee840b70f55a27f80e1cd308051b13 Mon Sep 17 00:00:00 2001 +From: Nick Hainke <vincent@systemli.org> +Date: Mon, 27 Dec 2021 00:38:13 +0100 +Subject: [PATCH 1/2] mtd: spi-nor: locking support for MX25L6405D + +Macronix MX25L6405D supports locking with four block-protection bits. +Currently, the driver only sets three bits. If the bootloader does not +sustain the flash chip in an unlocked state, the flash might be +non-writeable. Add the corresponding flag to enable locking support with +four bits in the status register. + +Tested on Nanostation M2 XM. + +Similar to commit 7ea40b54e83b ("mtd: spi-nor: enable locking support for +MX25L12805D") + +Signed-off-by: David Bauer <mail@david-bauer.net> +Signed-off-by: Nick Hainke <vincent@systemli.org> +--- + drivers/mtd/spi-nor/macronix.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +--- a/drivers/mtd/spi-nor/macronix.c ++++ b/drivers/mtd/spi-nor/macronix.c +@@ -41,7 +41,8 @@ static const struct flash_info macronix_ + { "mx25l1606e", INFO(0xc22015, 0, 64 * 1024, 32, SECT_4K) }, + { "mx25l3205d", INFO(0xc22016, 0, 64 * 1024, 64, SECT_4K) }, + { "mx25l3255e", INFO(0xc29e16, 0, 64 * 1024, 64, SECT_4K) }, +- { "mx25l6405d", INFO(0xc22017, 0, 64 * 1024, 128, SECT_4K) }, ++ { "mx25l6405d", INFO(0xc22017, 0, 64 * 1024, 128, SECT_4K | ++ SPI_NOR_HAS_LOCK | SPI_NOR_4BIT_BP) }, + { "mx25u2033e", INFO(0xc22532, 0, 64 * 1024, 4, SECT_4K) }, + { "mx25u3235f", INFO(0xc22536, 0, 64 * 1024, 64, + SECT_4K | SPI_NOR_DUAL_READ | diff --git a/pkgs/patches-linux-5.15/499-mtd-spi-nor-disable-16-bit-sr-for-macronix.patch b/pkgs/patches-linux-5.15/499-mtd-spi-nor-disable-16-bit-sr-for-macronix.patch new file mode 100644 index 0000000..ec14f63 --- /dev/null +++ b/pkgs/patches-linux-5.15/499-mtd-spi-nor-disable-16-bit-sr-for-macronix.patch @@ -0,0 +1,30 @@ +From 245224608b5368c10407da07557e546743d3c489 Mon Sep 17 00:00:00 2001 +From: Nick Hainke <vincent@systemli.org> +Date: Mon, 27 Dec 2021 09:33:13 +0100 +Subject: [PATCH 2/2] mtd: spi-nor: disable 16-bit-sr for macronix + +Macronix flash chips seem to consist of only one status register. +These chips will not work with the "16-bit Write Status (01h) Command". +Disable SNOR_F_HAS_16BIT_SR for all Macronix chips. + +Tested with MX25L6405D. + +Fixes: 39d1e3340c73 ("mtd: spi-nor: Fix clearing of QE bit on +lock()/unlock()") + +Signed-off-by: David Bauer <mail@david-bauer.net> +Signed-off-by: Nick Hainke <vincent@systemli.org> +--- + drivers/mtd/spi-nor/macronix.c | 1 + + 1 file changed, 1 insertion(+) + +--- a/drivers/mtd/spi-nor/macronix.c ++++ b/drivers/mtd/spi-nor/macronix.c +@@ -94,6 +94,7 @@ static void macronix_default_init(struct + { + nor->params->quad_enable = spi_nor_sr1_bit6_quad_enable; + nor->params->set_4byte_addr_mode = spi_nor_set_4byte_addr_mode; ++ nor->flags &= ~SNOR_F_HAS_16BIT_SR; + nor->flags |= SNOR_F_HAS_LOCK; + } + diff --git a/pkgs/patches-linux-5.15/500-fs_cdrom_dependencies.patch b/pkgs/patches-linux-5.15/500-fs_cdrom_dependencies.patch new file mode 100644 index 0000000..b46865f --- /dev/null +++ b/pkgs/patches-linux-5.15/500-fs_cdrom_dependencies.patch @@ -0,0 +1,62 @@ +From af7b91bcecce0eae24e90acd35d96ecee73e1407 Mon Sep 17 00:00:00 2001 +From: OpenWrt community <openwrt-devel@lists.openwrt.org> +Date: Wed, 13 Jul 2022 12:21:15 +0200 +Subject: [PATCH] fs: add cdrom dependency + +--- + fs/hfs/Kconfig | 1 + + fs/hfsplus/Kconfig | 1 + + fs/isofs/Kconfig | 1 + + fs/udf/Kconfig | 1 + + 4 files changed, 4 insertions(+) + +diff --git a/fs/hfs/Kconfig b/fs/hfs/Kconfig +index 129926b5142d..e0d2c647aa07 100644 +--- a/fs/hfs/Kconfig ++++ b/fs/hfs/Kconfig +@@ -2,6 +2,7 @@ + config HFS_FS + tristate "Apple Macintosh file system support" + depends on BLOCK ++ select CDROM + select NLS + help + If you say Y here, you will be able to mount Macintosh-formatted +diff --git a/fs/hfsplus/Kconfig b/fs/hfsplus/Kconfig +index 7d4229aecec0..648d91d1881f 100644 +--- a/fs/hfsplus/Kconfig ++++ b/fs/hfsplus/Kconfig +@@ -2,6 +2,7 @@ + config HFSPLUS_FS + tristate "Apple Extended HFS file system support" + depends on BLOCK ++ select CDROM + select NLS + select NLS_UTF8 + help +diff --git a/fs/isofs/Kconfig b/fs/isofs/Kconfig +index 08ffd37b9bb8..f74680379207 100644 +--- a/fs/isofs/Kconfig ++++ b/fs/isofs/Kconfig +@@ -1,6 +1,7 @@ + # SPDX-License-Identifier: GPL-2.0-only + config ISO9660_FS + tristate "ISO 9660 CDROM file system support" ++ select CDROM + help + This is the standard file system used on CD-ROMs. It was previously + known as "High Sierra File System" and is called "hsfs" on other +diff --git a/fs/udf/Kconfig b/fs/udf/Kconfig +index 26e1a49f3ba7..3f85a084d2b5 100644 +--- a/fs/udf/Kconfig ++++ b/fs/udf/Kconfig +@@ -1,6 +1,7 @@ + # SPDX-License-Identifier: GPL-2.0-only + config UDF_FS + tristate "UDF file system support" ++ select CDROM + select CRC_ITU_T + select NLS + help +-- + diff --git a/pkgs/patches-linux-5.15/530-jffs2_make_lzma_available.patch b/pkgs/patches-linux-5.15/530-jffs2_make_lzma_available.patch new file mode 100644 index 0000000..fd6ca78 --- /dev/null +++ b/pkgs/patches-linux-5.15/530-jffs2_make_lzma_available.patch @@ -0,0 +1,5180 @@ +From: Alexandros C. Couloumbis <alex@ozo.com> +Subject: fs: add jffs2/lzma support (not activated by default yet) + +lede-commit: c2c88d315fa0e881f8b19da07b62859b915b11b2 +Signed-off-by: Alexandros C. Couloumbis <alex@ozo.com> +--- + fs/jffs2/Kconfig | 9 + + fs/jffs2/Makefile | 3 + + fs/jffs2/compr.c | 6 + + fs/jffs2/compr.h | 10 +- + fs/jffs2/compr_lzma.c | 128 +++ + fs/jffs2/super.c | 33 +- + include/linux/lzma.h | 62 ++ + include/linux/lzma/LzFind.h | 115 +++ + include/linux/lzma/LzHash.h | 54 + + include/linux/lzma/LzmaDec.h | 231 +++++ + include/linux/lzma/LzmaEnc.h | 80 ++ + include/linux/lzma/Types.h | 226 +++++ + include/uapi/linux/jffs2.h | 1 + + lib/Kconfig | 6 + + lib/Makefile | 12 + + lib/lzma/LzFind.c | 761 ++++++++++++++ + lib/lzma/LzmaDec.c | 999 +++++++++++++++++++ + lib/lzma/LzmaEnc.c | 2271 ++++++++++++++++++++++++++++++++++++++++++ + lib/lzma/Makefile | 7 + + 19 files changed, 5008 insertions(+), 6 deletions(-) + create mode 100644 fs/jffs2/compr_lzma.c + create mode 100644 include/linux/lzma.h + create mode 100644 include/linux/lzma/LzFind.h + create mode 100644 include/linux/lzma/LzHash.h + create mode 100644 include/linux/lzma/LzmaDec.h + create mode 100644 include/linux/lzma/LzmaEnc.h + create mode 100644 include/linux/lzma/Types.h + create mode 100644 lib/lzma/LzFind.c + create mode 100644 lib/lzma/LzmaDec.c + create mode 100644 lib/lzma/LzmaEnc.c + create mode 100644 lib/lzma/Makefile + +--- a/fs/jffs2/Kconfig ++++ b/fs/jffs2/Kconfig +@@ -136,6 +136,15 @@ config JFFS2_LZO + This feature was added in July, 2007. Say 'N' if you need + compatibility with older bootloaders or kernels. + ++config JFFS2_LZMA ++ bool "JFFS2 LZMA compression support" if JFFS2_COMPRESSION_OPTIONS ++ select LZMA_COMPRESS ++ select LZMA_DECOMPRESS ++ depends on JFFS2_FS ++ default n ++ help ++ JFFS2 wrapper to the LZMA C SDK ++ + config JFFS2_RTIME + bool "JFFS2 RTIME compression support" if JFFS2_COMPRESSION_OPTIONS + depends on JFFS2_FS +--- a/fs/jffs2/Makefile ++++ b/fs/jffs2/Makefile +@@ -19,4 +19,7 @@ jffs2-$(CONFIG_JFFS2_RUBIN) += compr_rub + jffs2-$(CONFIG_JFFS2_RTIME) += compr_rtime.o + jffs2-$(CONFIG_JFFS2_ZLIB) += compr_zlib.o + jffs2-$(CONFIG_JFFS2_LZO) += compr_lzo.o ++jffs2-$(CONFIG_JFFS2_LZMA) += compr_lzma.o + jffs2-$(CONFIG_JFFS2_SUMMARY) += summary.o ++ ++CFLAGS_compr_lzma.o += -Iinclude/linux -Ilib/lzma +--- a/fs/jffs2/compr.c ++++ b/fs/jffs2/compr.c +@@ -378,6 +378,9 @@ int __init jffs2_compressors_init(void) + #ifdef CONFIG_JFFS2_LZO + jffs2_lzo_init(); + #endif ++#ifdef CONFIG_JFFS2_LZMA ++ jffs2_lzma_init(); ++#endif + /* Setting default compression mode */ + #ifdef CONFIG_JFFS2_CMODE_NONE + jffs2_compression_mode = JFFS2_COMPR_MODE_NONE; +@@ -401,6 +404,9 @@ int __init jffs2_compressors_init(void) + int jffs2_compressors_exit(void) + { + /* Unregistering compressors */ ++#ifdef CONFIG_JFFS2_LZMA ++ jffs2_lzma_exit(); ++#endif + #ifdef CONFIG_JFFS2_LZO + jffs2_lzo_exit(); + #endif +--- a/fs/jffs2/compr.h ++++ b/fs/jffs2/compr.h +@@ -29,9 +29,9 @@ + #define JFFS2_DYNRUBIN_PRIORITY 20 + #define JFFS2_LZARI_PRIORITY 30 + #define JFFS2_RTIME_PRIORITY 50 +-#define JFFS2_ZLIB_PRIORITY 60 +-#define JFFS2_LZO_PRIORITY 80 +- ++#define JFFS2_LZMA_PRIORITY 70 ++#define JFFS2_ZLIB_PRIORITY 80 ++#define JFFS2_LZO_PRIORITY 90 + + #define JFFS2_RUBINMIPS_DISABLED /* RUBINs will be used only */ + #define JFFS2_DYNRUBIN_DISABLED /* for decompression */ +@@ -101,5 +101,9 @@ void jffs2_zlib_exit(void); + int jffs2_lzo_init(void); + void jffs2_lzo_exit(void); + #endif ++#ifdef CONFIG_JFFS2_LZMA ++int jffs2_lzma_init(void); ++void jffs2_lzma_exit(void); ++#endif + + #endif /* __JFFS2_COMPR_H__ */ +--- /dev/null ++++ b/fs/jffs2/compr_lzma.c +@@ -0,0 +1,128 @@ ++/* ++ * JFFS2 -- Journalling Flash File System, Version 2. ++ * ++ * For licensing information, see the file 'LICENCE' in this directory. ++ * ++ * JFFS2 wrapper to the LZMA C SDK ++ * ++ */ ++ ++#include <linux/lzma.h> ++#include "compr.h" ++ ++#ifdef __KERNEL__ ++ static DEFINE_MUTEX(deflate_mutex); ++#endif ++ ++CLzmaEncHandle *p; ++Byte propsEncoded[LZMA_PROPS_SIZE]; ++SizeT propsSize = sizeof(propsEncoded); ++ ++STATIC void lzma_free_workspace(void) ++{ ++ LzmaEnc_Destroy(p, &lzma_alloc, &lzma_alloc); ++} ++ ++STATIC int INIT lzma_alloc_workspace(CLzmaEncProps *props) ++{ ++ if ((p = (CLzmaEncHandle *)LzmaEnc_Create(&lzma_alloc)) == NULL) ++ { ++ PRINT_ERROR("Failed to allocate lzma deflate workspace\n"); ++ return -ENOMEM; ++ } ++ ++ if (LzmaEnc_SetProps(p, props) != SZ_OK) ++ { ++ lzma_free_workspace(); ++ return -1; ++ } ++ ++ if (LzmaEnc_WriteProperties(p, propsEncoded, &propsSize) != SZ_OK) ++ { ++ lzma_free_workspace(); ++ return -1; ++ } ++ ++ return 0; ++} ++ ++STATIC int jffs2_lzma_compress(unsigned char *data_in, unsigned char *cpage_out, ++ uint32_t *sourcelen, uint32_t *dstlen) ++{ ++ SizeT compress_size = (SizeT)(*dstlen); ++ int ret; ++ ++ #ifdef __KERNEL__ ++ mutex_lock(&deflate_mutex); ++ #endif ++ ++ ret = LzmaEnc_MemEncode(p, cpage_out, &compress_size, data_in, *sourcelen, ++ 0, NULL, &lzma_alloc, &lzma_alloc); ++ ++ #ifdef __KERNEL__ ++ mutex_unlock(&deflate_mutex); ++ #endif ++ ++ if (ret != SZ_OK) ++ return -1; ++ ++ *dstlen = (uint32_t)compress_size; ++ ++ return 0; ++} ++ ++STATIC int jffs2_lzma_decompress(unsigned char *data_in, unsigned char *cpage_out, ++ uint32_t srclen, uint32_t destlen) ++{ ++ int ret; ++ SizeT dl = (SizeT)destlen; ++ SizeT sl = (SizeT)srclen; ++ ELzmaStatus status; ++ ++ ret = LzmaDecode(cpage_out, &dl, data_in, &sl, propsEncoded, ++ propsSize, LZMA_FINISH_ANY, &status, &lzma_alloc); ++ ++ if (ret != SZ_OK || status == LZMA_STATUS_NOT_FINISHED || dl != (SizeT)destlen) ++ return -1; ++ ++ return 0; ++} ++ ++static struct jffs2_compressor jffs2_lzma_comp = { ++ .priority = JFFS2_LZMA_PRIORITY, ++ .name = "lzma", ++ .compr = JFFS2_COMPR_LZMA, ++ .compress = &jffs2_lzma_compress, ++ .decompress = &jffs2_lzma_decompress, ++ .disabled = 0, ++}; ++ ++int INIT jffs2_lzma_init(void) ++{ ++ int ret; ++ CLzmaEncProps props; ++ LzmaEncProps_Init(&props); ++ ++ props.dictSize = LZMA_BEST_DICT(0x2000); ++ props.level = LZMA_BEST_LEVEL; ++ props.lc = LZMA_BEST_LC; ++ props.lp = LZMA_BEST_LP; ++ props.pb = LZMA_BEST_PB; ++ props.fb = LZMA_BEST_FB; ++ ++ ret = lzma_alloc_workspace(&props); ++ if (ret < 0) ++ return ret; ++ ++ ret = jffs2_register_compressor(&jffs2_lzma_comp); ++ if (ret) ++ lzma_free_workspace(); ++ ++ return ret; ++} ++ ++void jffs2_lzma_exit(void) ++{ ++ jffs2_unregister_compressor(&jffs2_lzma_comp); ++ lzma_free_workspace(); ++} +--- a/fs/jffs2/super.c ++++ b/fs/jffs2/super.c +@@ -374,14 +374,41 @@ static int __init init_jffs2_fs(void) + BUILD_BUG_ON(sizeof(struct jffs2_raw_inode) != 68); + BUILD_BUG_ON(sizeof(struct jffs2_raw_summary) != 32); + +- pr_info("version 2.2." ++ pr_info("version 2.2" + #ifdef CONFIG_JFFS2_FS_WRITEBUFFER + " (NAND)" + #endif + #ifdef CONFIG_JFFS2_SUMMARY +- " (SUMMARY) " ++ " (SUMMARY)" + #endif +- " © 2001-2006 Red Hat, Inc.\n"); ++#ifdef CONFIG_JFFS2_ZLIB ++ " (ZLIB)" ++#endif ++#ifdef CONFIG_JFFS2_LZO ++ " (LZO)" ++#endif ++#ifdef CONFIG_JFFS2_LZMA ++ " (LZMA)" ++#endif ++#ifdef CONFIG_JFFS2_RTIME ++ " (RTIME)" ++#endif ++#ifdef CONFIG_JFFS2_RUBIN ++ " (RUBIN)" ++#endif ++#ifdef CONFIG_JFFS2_CMODE_NONE ++ " (CMODE_NONE)" ++#endif ++#ifdef CONFIG_JFFS2_CMODE_PRIORITY ++ " (CMODE_PRIORITY)" ++#endif ++#ifdef CONFIG_JFFS2_CMODE_SIZE ++ " (CMODE_SIZE)" ++#endif ++#ifdef CONFIG_JFFS2_CMODE_FAVOURLZO ++ " (CMODE_FAVOURLZO)" ++#endif ++ " (c) 2001-2006 Red Hat, Inc.\n"); + + jffs2_inode_cachep = kmem_cache_create("jffs2_i", + sizeof(struct jffs2_inode_info), +--- /dev/null ++++ b/include/linux/lzma.h +@@ -0,0 +1,62 @@ ++#ifndef __LZMA_H__ ++#define __LZMA_H__ ++ ++#ifdef __KERNEL__ ++ #include <linux/kernel.h> ++ #include <linux/sched.h> ++ #include <linux/slab.h> ++ #include <linux/vmalloc.h> ++ #include <linux/init.h> ++ #define LZMA_MALLOC vmalloc ++ #define LZMA_FREE vfree ++ #define PRINT_ERROR(msg) printk(KERN_WARNING #msg) ++ #define INIT __init ++ #define STATIC static ++#else ++ #include <stdint.h> ++ #include <stdlib.h> ++ #include <stdio.h> ++ #include <unistd.h> ++ #include <string.h> ++ #include <asm/types.h> ++ #include <errno.h> ++ #include <linux/jffs2.h> ++ #ifndef PAGE_SIZE ++ extern int page_size; ++ #define PAGE_SIZE page_size ++ #endif ++ #define LZMA_MALLOC malloc ++ #define LZMA_FREE free ++ #define PRINT_ERROR(msg) fprintf(stderr, msg) ++ #define INIT ++ #define STATIC ++#endif ++ ++#include "lzma/LzmaDec.h" ++#include "lzma/LzmaEnc.h" ++ ++#define LZMA_BEST_LEVEL (9) ++#define LZMA_BEST_LC (0) ++#define LZMA_BEST_LP (0) ++#define LZMA_BEST_PB (0) ++#define LZMA_BEST_FB (273) ++ ++#define LZMA_BEST_DICT(n) (((int)((n) / 2)) * 2) ++ ++static void *p_lzma_malloc(void *p, size_t size) ++{ ++ if (size == 0) ++ return NULL; ++ ++ return LZMA_MALLOC(size); ++} ++ ++static void p_lzma_free(void *p, void *address) ++{ ++ if (address != NULL) ++ LZMA_FREE(address); ++} ++ ++static ISzAlloc lzma_alloc = {p_lzma_malloc, p_lzma_free}; ++ ++#endif +--- /dev/null ++++ b/include/linux/lzma/LzFind.h +@@ -0,0 +1,115 @@ ++/* LzFind.h -- Match finder for LZ algorithms ++2009-04-22 : Igor Pavlov : Public domain */ ++ ++#ifndef __LZ_FIND_H ++#define __LZ_FIND_H ++ ++#include "Types.h" ++ ++#ifdef __cplusplus ++extern "C" { ++#endif ++ ++typedef UInt32 CLzRef; ++ ++typedef struct _CMatchFinder ++{ ++ Byte *buffer; ++ UInt32 pos; ++ UInt32 posLimit; ++ UInt32 streamPos; ++ UInt32 lenLimit; ++ ++ UInt32 cyclicBufferPos; ++ UInt32 cyclicBufferSize; /* it must be = (historySize + 1) */ ++ ++ UInt32 matchMaxLen; ++ CLzRef *hash; ++ CLzRef *son; ++ UInt32 hashMask; ++ UInt32 cutValue; ++ ++ Byte *bufferBase; ++ ISeqInStream *stream; ++ int streamEndWasReached; ++ ++ UInt32 blockSize; ++ UInt32 keepSizeBefore; ++ UInt32 keepSizeAfter; ++ ++ UInt32 numHashBytes; ++ int directInput; ++ size_t directInputRem; ++ int btMode; ++ int bigHash; ++ UInt32 historySize; ++ UInt32 fixedHashSize; ++ UInt32 hashSizeSum; ++ UInt32 numSons; ++ SRes result; ++ UInt32 crc[256]; ++} CMatchFinder; ++ ++#define Inline_MatchFinder_GetPointerToCurrentPos(p) ((p)->buffer) ++#define Inline_MatchFinder_GetIndexByte(p, index) ((p)->buffer[(Int32)(index)]) ++ ++#define Inline_MatchFinder_GetNumAvailableBytes(p) ((p)->streamPos - (p)->pos) ++ ++int MatchFinder_NeedMove(CMatchFinder *p); ++Byte *MatchFinder_GetPointerToCurrentPos(CMatchFinder *p); ++void MatchFinder_MoveBlock(CMatchFinder *p); ++void MatchFinder_ReadIfRequired(CMatchFinder *p); ++ ++void MatchFinder_Construct(CMatchFinder *p); ++ ++/* Conditions: ++ historySize <= 3 GB ++ keepAddBufferBefore + matchMaxLen + keepAddBufferAfter < 511MB ++*/ ++int MatchFinder_Create(CMatchFinder *p, UInt32 historySize, ++ UInt32 keepAddBufferBefore, UInt32 matchMaxLen, UInt32 keepAddBufferAfter, ++ ISzAlloc *alloc); ++void MatchFinder_Free(CMatchFinder *p, ISzAlloc *alloc); ++void MatchFinder_Normalize3(UInt32 subValue, CLzRef *items, UInt32 numItems); ++void MatchFinder_ReduceOffsets(CMatchFinder *p, UInt32 subValue); ++ ++UInt32 * GetMatchesSpec1(UInt32 lenLimit, UInt32 curMatch, UInt32 pos, const Byte *buffer, CLzRef *son, ++ UInt32 _cyclicBufferPos, UInt32 _cyclicBufferSize, UInt32 _cutValue, ++ UInt32 *distances, UInt32 maxLen); ++ ++/* ++Conditions: ++ Mf_GetNumAvailableBytes_Func must be called before each Mf_GetMatchLen_Func. ++ Mf_GetPointerToCurrentPos_Func's result must be used only before any other function ++*/ ++ ++typedef void (*Mf_Init_Func)(void *object); ++typedef Byte (*Mf_GetIndexByte_Func)(void *object, Int32 index); ++typedef UInt32 (*Mf_GetNumAvailableBytes_Func)(void *object); ++typedef const Byte * (*Mf_GetPointerToCurrentPos_Func)(void *object); ++typedef UInt32 (*Mf_GetMatches_Func)(void *object, UInt32 *distances); ++typedef void (*Mf_Skip_Func)(void *object, UInt32); ++ ++typedef struct _IMatchFinder ++{ ++ Mf_Init_Func Init; ++ Mf_GetIndexByte_Func GetIndexByte; ++ Mf_GetNumAvailableBytes_Func GetNumAvailableBytes; ++ Mf_GetPointerToCurrentPos_Func GetPointerToCurrentPos; ++ Mf_GetMatches_Func GetMatches; ++ Mf_Skip_Func Skip; ++} IMatchFinder; ++ ++void MatchFinder_CreateVTable(CMatchFinder *p, IMatchFinder *vTable); ++ ++void MatchFinder_Init(CMatchFinder *p); ++UInt32 Bt3Zip_MatchFinder_GetMatches(CMatchFinder *p, UInt32 *distances); ++UInt32 Hc3Zip_MatchFinder_GetMatches(CMatchFinder *p, UInt32 *distances); ++void Bt3Zip_MatchFinder_Skip(CMatchFinder *p, UInt32 num); ++void Hc3Zip_MatchFinder_Skip(CMatchFinder *p, UInt32 num); ++ ++#ifdef __cplusplus ++} ++#endif ++ ++#endif +--- /dev/null ++++ b/include/linux/lzma/LzHash.h +@@ -0,0 +1,54 @@ ++/* LzHash.h -- HASH functions for LZ algorithms ++2009-02-07 : Igor Pavlov : Public domain */ ++ ++#ifndef __LZ_HASH_H ++#define __LZ_HASH_H ++ ++#define kHash2Size (1 << 10) ++#define kHash3Size (1 << 16) ++#define kHash4Size (1 << 20) ++ ++#define kFix3HashSize (kHash2Size) ++#define kFix4HashSize (kHash2Size + kHash3Size) ++#define kFix5HashSize (kHash2Size + kHash3Size + kHash4Size) ++ ++#define HASH2_CALC hashValue = cur[0] | ((UInt32)cur[1] << 8); ++ ++#define HASH3_CALC { \ ++ UInt32 temp = p->crc[cur[0]] ^ cur[1]; \ ++ hash2Value = temp & (kHash2Size - 1); \ ++ hashValue = (temp ^ ((UInt32)cur[2] << 8)) & p->hashMask; } ++ ++#define HASH4_CALC { \ ++ UInt32 temp = p->crc[cur[0]] ^ cur[1]; \ ++ hash2Value = temp & (kHash2Size - 1); \ ++ hash3Value = (temp ^ ((UInt32)cur[2] << 8)) & (kHash3Size - 1); \ ++ hashValue = (temp ^ ((UInt32)cur[2] << 8) ^ (p->crc[cur[3]] << 5)) & p->hashMask; } ++ ++#define HASH5_CALC { \ ++ UInt32 temp = p->crc[cur[0]] ^ cur[1]; \ ++ hash2Value = temp & (kHash2Size - 1); \ ++ hash3Value = (temp ^ ((UInt32)cur[2] << 8)) & (kHash3Size - 1); \ ++ hash4Value = (temp ^ ((UInt32)cur[2] << 8) ^ (p->crc[cur[3]] << 5)); \ ++ hashValue = (hash4Value ^ (p->crc[cur[4]] << 3)) & p->hashMask; \ ++ hash4Value &= (kHash4Size - 1); } ++ ++/* #define HASH_ZIP_CALC hashValue = ((cur[0] | ((UInt32)cur[1] << 8)) ^ p->crc[cur[2]]) & 0xFFFF; */ ++#define HASH_ZIP_CALC hashValue = ((cur[2] | ((UInt32)cur[0] << 8)) ^ p->crc[cur[1]]) & 0xFFFF; ++ ++ ++#define MT_HASH2_CALC \ ++ hash2Value = (p->crc[cur[0]] ^ cur[1]) & (kHash2Size - 1); ++ ++#define MT_HASH3_CALC { \ ++ UInt32 temp = p->crc[cur[0]] ^ cur[1]; \ ++ hash2Value = temp & (kHash2Size - 1); \ ++ hash3Value = (temp ^ ((UInt32)cur[2] << 8)) & (kHash3Size - 1); } ++ ++#define MT_HASH4_CALC { \ ++ UInt32 temp = p->crc[cur[0]] ^ cur[1]; \ ++ hash2Value = temp & (kHash2Size - 1); \ ++ hash3Value = (temp ^ ((UInt32)cur[2] << 8)) & (kHash3Size - 1); \ ++ hash4Value = (temp ^ ((UInt32)cur[2] << 8) ^ (p->crc[cur[3]] << 5)) & (kHash4Size - 1); } ++ ++#endif +--- /dev/null ++++ b/include/linux/lzma/LzmaDec.h +@@ -0,0 +1,231 @@ ++/* LzmaDec.h -- LZMA Decoder ++2009-02-07 : Igor Pavlov : Public domain */ ++ ++#ifndef __LZMA_DEC_H ++#define __LZMA_DEC_H ++ ++#include "Types.h" ++ ++#ifdef __cplusplus ++extern "C" { ++#endif ++ ++/* #define _LZMA_PROB32 */ ++/* _LZMA_PROB32 can increase the speed on some CPUs, ++ but memory usage for CLzmaDec::probs will be doubled in that case */ ++ ++#ifdef _LZMA_PROB32 ++#define CLzmaProb UInt32 ++#else ++#define CLzmaProb UInt16 ++#endif ++ ++ ++/* ---------- LZMA Properties ---------- */ ++ ++#define LZMA_PROPS_SIZE 5 ++ ++typedef struct _CLzmaProps ++{ ++ unsigned lc, lp, pb; ++ UInt32 dicSize; ++} CLzmaProps; ++ ++/* LzmaProps_Decode - decodes properties ++Returns: ++ SZ_OK ++ SZ_ERROR_UNSUPPORTED - Unsupported properties ++*/ ++ ++SRes LzmaProps_Decode(CLzmaProps *p, const Byte *data, unsigned size); ++ ++ ++/* ---------- LZMA Decoder state ---------- */ ++ ++/* LZMA_REQUIRED_INPUT_MAX = number of required input bytes for worst case. ++ Num bits = log2((2^11 / 31) ^ 22) + 26 < 134 + 26 = 160; */ ++ ++#define LZMA_REQUIRED_INPUT_MAX 20 ++ ++typedef struct ++{ ++ CLzmaProps prop; ++ CLzmaProb *probs; ++ Byte *dic; ++ const Byte *buf; ++ UInt32 range, code; ++ SizeT dicPos; ++ SizeT dicBufSize; ++ UInt32 processedPos; ++ UInt32 checkDicSize; ++ unsigned state; ++ UInt32 reps[4]; ++ unsigned remainLen; ++ int needFlush; ++ int needInitState; ++ UInt32 numProbs; ++ unsigned tempBufSize; ++ Byte tempBuf[LZMA_REQUIRED_INPUT_MAX]; ++} CLzmaDec; ++ ++#define LzmaDec_Construct(p) { (p)->dic = 0; (p)->probs = 0; } ++ ++void LzmaDec_Init(CLzmaDec *p); ++ ++/* There are two types of LZMA streams: ++ 0) Stream with end mark. That end mark adds about 6 bytes to compressed size. ++ 1) Stream without end mark. You must know exact uncompressed size to decompress such stream. */ ++ ++typedef enum ++{ ++ LZMA_FINISH_ANY, /* finish at any point */ ++ LZMA_FINISH_END /* block must be finished at the end */ ++} ELzmaFinishMode; ++ ++/* ELzmaFinishMode has meaning only if the decoding reaches output limit !!! ++ ++ You must use LZMA_FINISH_END, when you know that current output buffer ++ covers last bytes of block. In other cases you must use LZMA_FINISH_ANY. ++ ++ If LZMA decoder sees end marker before reaching output limit, it returns SZ_OK, ++ and output value of destLen will be less than output buffer size limit. ++ You can check status result also. ++ ++ You can use multiple checks to test data integrity after full decompression: ++ 1) Check Result and "status" variable. ++ 2) Check that output(destLen) = uncompressedSize, if you know real uncompressedSize. ++ 3) Check that output(srcLen) = compressedSize, if you know real compressedSize. ++ You must use correct finish mode in that case. */ ++ ++typedef enum ++{ ++ LZMA_STATUS_NOT_SPECIFIED, /* use main error code instead */ ++ LZMA_STATUS_FINISHED_WITH_MARK, /* stream was finished with end mark. */ ++ LZMA_STATUS_NOT_FINISHED, /* stream was not finished */ ++ LZMA_STATUS_NEEDS_MORE_INPUT, /* you must provide more input bytes */ ++ LZMA_STATUS_MAYBE_FINISHED_WITHOUT_MARK /* there is probability that stream was finished without end mark */ ++} ELzmaStatus; ++ ++/* ELzmaStatus is used only as output value for function call */ ++ ++ ++/* ---------- Interfaces ---------- */ ++ ++/* There are 3 levels of interfaces: ++ 1) Dictionary Interface ++ 2) Buffer Interface ++ 3) One Call Interface ++ You can select any of these interfaces, but don't mix functions from different ++ groups for same object. */ ++ ++ ++/* There are two variants to allocate state for Dictionary Interface: ++ 1) LzmaDec_Allocate / LzmaDec_Free ++ 2) LzmaDec_AllocateProbs / LzmaDec_FreeProbs ++ You can use variant 2, if you set dictionary buffer manually. ++ For Buffer Interface you must always use variant 1. ++ ++LzmaDec_Allocate* can return: ++ SZ_OK ++ SZ_ERROR_MEM - Memory allocation error ++ SZ_ERROR_UNSUPPORTED - Unsupported properties ++*/ ++ ++SRes LzmaDec_AllocateProbs(CLzmaDec *p, const Byte *props, unsigned propsSize, ISzAlloc *alloc); ++void LzmaDec_FreeProbs(CLzmaDec *p, ISzAlloc *alloc); ++ ++SRes LzmaDec_Allocate(CLzmaDec *state, const Byte *prop, unsigned propsSize, ISzAlloc *alloc); ++void LzmaDec_Free(CLzmaDec *state, ISzAlloc *alloc); ++ ++/* ---------- Dictionary Interface ---------- */ ++ ++/* You can use it, if you want to eliminate the overhead for data copying from ++ dictionary to some other external buffer. ++ You must work with CLzmaDec variables directly in this interface. ++ ++ STEPS: ++ LzmaDec_Constr() ++ LzmaDec_Allocate() ++ for (each new stream) ++ { ++ LzmaDec_Init() ++ while (it needs more decompression) ++ { ++ LzmaDec_DecodeToDic() ++ use data from CLzmaDec::dic and update CLzmaDec::dicPos ++ } ++ } ++ LzmaDec_Free() ++*/ ++ ++/* LzmaDec_DecodeToDic ++ ++ The decoding to internal dictionary buffer (CLzmaDec::dic). ++ You must manually update CLzmaDec::dicPos, if it reaches CLzmaDec::dicBufSize !!! ++ ++finishMode: ++ It has meaning only if the decoding reaches output limit (dicLimit). ++ LZMA_FINISH_ANY - Decode just dicLimit bytes. ++ LZMA_FINISH_END - Stream must be finished after dicLimit. ++ ++Returns: ++ SZ_OK ++ status: ++ LZMA_STATUS_FINISHED_WITH_MARK ++ LZMA_STATUS_NOT_FINISHED ++ LZMA_STATUS_NEEDS_MORE_INPUT ++ LZMA_STATUS_MAYBE_FINISHED_WITHOUT_MARK ++ SZ_ERROR_DATA - Data error ++*/ ++ ++SRes LzmaDec_DecodeToDic(CLzmaDec *p, SizeT dicLimit, ++ const Byte *src, SizeT *srcLen, ELzmaFinishMode finishMode, ELzmaStatus *status); ++ ++ ++/* ---------- Buffer Interface ---------- */ ++ ++/* It's zlib-like interface. ++ See LzmaDec_DecodeToDic description for information about STEPS and return results, ++ but you must use LzmaDec_DecodeToBuf instead of LzmaDec_DecodeToDic and you don't need ++ to work with CLzmaDec variables manually. ++ ++finishMode: ++ It has meaning only if the decoding reaches output limit (*destLen). ++ LZMA_FINISH_ANY - Decode just destLen bytes. ++ LZMA_FINISH_END - Stream must be finished after (*destLen). ++*/ ++ ++SRes LzmaDec_DecodeToBuf(CLzmaDec *p, Byte *dest, SizeT *destLen, ++ const Byte *src, SizeT *srcLen, ELzmaFinishMode finishMode, ELzmaStatus *status); ++ ++ ++/* ---------- One Call Interface ---------- */ ++ ++/* LzmaDecode ++ ++finishMode: ++ It has meaning only if the decoding reaches output limit (*destLen). ++ LZMA_FINISH_ANY - Decode just destLen bytes. ++ LZMA_FINISH_END - Stream must be finished after (*destLen). ++ ++Returns: ++ SZ_OK ++ status: ++ LZMA_STATUS_FINISHED_WITH_MARK ++ LZMA_STATUS_NOT_FINISHED ++ LZMA_STATUS_MAYBE_FINISHED_WITHOUT_MARK ++ SZ_ERROR_DATA - Data error ++ SZ_ERROR_MEM - Memory allocation error ++ SZ_ERROR_UNSUPPORTED - Unsupported properties ++ SZ_ERROR_INPUT_EOF - It needs more bytes in input buffer (src). ++*/ ++ ++SRes LzmaDecode(Byte *dest, SizeT *destLen, const Byte *src, SizeT *srcLen, ++ const Byte *propData, unsigned propSize, ELzmaFinishMode finishMode, ++ ELzmaStatus *status, ISzAlloc *alloc); ++ ++#ifdef __cplusplus ++} ++#endif ++ ++#endif +--- /dev/null ++++ b/include/linux/lzma/LzmaEnc.h +@@ -0,0 +1,80 @@ ++/* LzmaEnc.h -- LZMA Encoder ++2009-02-07 : Igor Pavlov : Public domain */ ++ ++#ifndef __LZMA_ENC_H ++#define __LZMA_ENC_H ++ ++#include "Types.h" ++ ++#ifdef __cplusplus ++extern "C" { ++#endif ++ ++#define LZMA_PROPS_SIZE 5 ++ ++typedef struct _CLzmaEncProps ++{ ++ int level; /* 0 <= level <= 9 */ ++ UInt32 dictSize; /* (1 << 12) <= dictSize <= (1 << 27) for 32-bit version ++ (1 << 12) <= dictSize <= (1 << 30) for 64-bit version ++ default = (1 << 24) */ ++ int lc; /* 0 <= lc <= 8, default = 3 */ ++ int lp; /* 0 <= lp <= 4, default = 0 */ ++ int pb; /* 0 <= pb <= 4, default = 2 */ ++ int algo; /* 0 - fast, 1 - normal, default = 1 */ ++ int fb; /* 5 <= fb <= 273, default = 32 */ ++ int btMode; /* 0 - hashChain Mode, 1 - binTree mode - normal, default = 1 */ ++ int numHashBytes; /* 2, 3 or 4, default = 4 */ ++ UInt32 mc; /* 1 <= mc <= (1 << 30), default = 32 */ ++ unsigned writeEndMark; /* 0 - do not write EOPM, 1 - write EOPM, default = 0 */ ++ int numThreads; /* 1 or 2, default = 2 */ ++} CLzmaEncProps; ++ ++void LzmaEncProps_Init(CLzmaEncProps *p); ++void LzmaEncProps_Normalize(CLzmaEncProps *p); ++UInt32 LzmaEncProps_GetDictSize(const CLzmaEncProps *props2); ++ ++ ++/* ---------- CLzmaEncHandle Interface ---------- */ ++ ++/* LzmaEnc_* functions can return the following exit codes: ++Returns: ++ SZ_OK - OK ++ SZ_ERROR_MEM - Memory allocation error ++ SZ_ERROR_PARAM - Incorrect paramater in props ++ SZ_ERROR_WRITE - Write callback error. ++ SZ_ERROR_PROGRESS - some break from progress callback ++ SZ_ERROR_THREAD - errors in multithreading functions (only for Mt version) ++*/ ++ ++typedef void * CLzmaEncHandle; ++ ++CLzmaEncHandle LzmaEnc_Create(ISzAlloc *alloc); ++void LzmaEnc_Destroy(CLzmaEncHandle p, ISzAlloc *alloc, ISzAlloc *allocBig); ++SRes LzmaEnc_SetProps(CLzmaEncHandle p, const CLzmaEncProps *props); ++SRes LzmaEnc_WriteProperties(CLzmaEncHandle p, Byte *properties, SizeT *size); ++SRes LzmaEnc_Encode(CLzmaEncHandle p, ISeqOutStream *outStream, ISeqInStream *inStream, ++ ICompressProgress *progress, ISzAlloc *alloc, ISzAlloc *allocBig); ++SRes LzmaEnc_MemEncode(CLzmaEncHandle p, Byte *dest, SizeT *destLen, const Byte *src, SizeT srcLen, ++ int writeEndMark, ICompressProgress *progress, ISzAlloc *alloc, ISzAlloc *allocBig); ++ ++/* ---------- One Call Interface ---------- */ ++ ++/* LzmaEncode ++Return code: ++ SZ_OK - OK ++ SZ_ERROR_MEM - Memory allocation error ++ SZ_ERROR_PARAM - Incorrect paramater ++ SZ_ERROR_OUTPUT_EOF - output buffer overflow ++ SZ_ERROR_THREAD - errors in multithreading functions (only for Mt version) ++*/ ++ ++SRes LzmaEncode(Byte *dest, SizeT *destLen, const Byte *src, SizeT srcLen, ++ const CLzmaEncProps *props, Byte *propsEncoded, SizeT *propsSize, int writeEndMark, ++ ICompressProgress *progress, ISzAlloc *alloc, ISzAlloc *allocBig); ++ ++#ifdef __cplusplus ++} ++#endif ++ ++#endif +--- /dev/null ++++ b/include/linux/lzma/Types.h +@@ -0,0 +1,226 @@ ++/* Types.h -- Basic types ++2009-11-23 : Igor Pavlov : Public domain */ ++ ++#ifndef __7Z_TYPES_H ++#define __7Z_TYPES_H ++ ++#include <stddef.h> ++ ++#ifdef _WIN32 ++#include <windows.h> ++#endif ++ ++#ifndef EXTERN_C_BEGIN ++#ifdef __cplusplus ++#define EXTERN_C_BEGIN extern "C" { ++#define EXTERN_C_END } ++#else ++#define EXTERN_C_BEGIN ++#define EXTERN_C_END ++#endif ++#endif ++ ++EXTERN_C_BEGIN ++ ++#define SZ_OK 0 ++ ++#define SZ_ERROR_DATA 1 ++#define SZ_ERROR_MEM 2 ++#define SZ_ERROR_CRC 3 ++#define SZ_ERROR_UNSUPPORTED 4 ++#define SZ_ERROR_PARAM 5 ++#define SZ_ERROR_INPUT_EOF 6 ++#define SZ_ERROR_OUTPUT_EOF 7 ++#define SZ_ERROR_READ 8 ++#define SZ_ERROR_WRITE 9 ++#define SZ_ERROR_PROGRESS 10 ++#define SZ_ERROR_FAIL 11 ++#define SZ_ERROR_THREAD 12 ++ ++#define SZ_ERROR_ARCHIVE 16 ++#define SZ_ERROR_NO_ARCHIVE 17 ++ ++typedef int SRes; ++ ++#ifdef _WIN32 ++typedef DWORD WRes; ++#else ++typedef int WRes; ++#endif ++ ++#ifndef RINOK ++#define RINOK(x) { int __result__ = (x); if (__result__ != 0) return __result__; } ++#endif ++ ++typedef unsigned char Byte; ++typedef short Int16; ++typedef unsigned short UInt16; ++ ++#ifdef _LZMA_UINT32_IS_ULONG ++typedef long Int32; ++typedef unsigned long UInt32; ++#else ++typedef int Int32; ++typedef unsigned int UInt32; ++#endif ++ ++#ifdef _SZ_NO_INT_64 ++ ++/* define _SZ_NO_INT_64, if your compiler doesn't support 64-bit integers. ++ NOTES: Some code will work incorrectly in that case! */ ++ ++typedef long Int64; ++typedef unsigned long UInt64; ++ ++#else ++ ++#if defined(_MSC_VER) || defined(__BORLANDC__) ++typedef __int64 Int64; ++typedef unsigned __int64 UInt64; ++#else ++typedef long long int Int64; ++typedef unsigned long long int UInt64; ++#endif ++ ++#endif ++ ++#ifdef _LZMA_NO_SYSTEM_SIZE_T ++typedef UInt32 SizeT; ++#else ++typedef size_t SizeT; ++#endif ++ ++typedef int Bool; ++#define True 1 ++#define False 0 ++ ++ ++#ifdef _WIN32 ++#define MY_STD_CALL __stdcall ++#else ++#define MY_STD_CALL ++#endif ++ ++#ifdef _MSC_VER ++ ++#if _MSC_VER >= 1300 ++#define MY_NO_INLINE __declspec(noinline) ++#else ++#define MY_NO_INLINE ++#endif ++ ++#define MY_CDECL __cdecl ++#define MY_FAST_CALL __fastcall ++ ++#else ++ ++#define MY_CDECL ++#define MY_FAST_CALL ++ ++#endif ++ ++ ++/* The following interfaces use first parameter as pointer to structure */ ++ ++typedef struct ++{ ++ SRes (*Read)(void *p, void *buf, size_t *size); ++ /* if (input(*size) != 0 && output(*size) == 0) means end_of_stream. ++ (output(*size) < input(*size)) is allowed */ ++} ISeqInStream; ++ ++/* it can return SZ_ERROR_INPUT_EOF */ ++SRes SeqInStream_Read(ISeqInStream *stream, void *buf, size_t size); ++SRes SeqInStream_Read2(ISeqInStream *stream, void *buf, size_t size, SRes errorType); ++SRes SeqInStream_ReadByte(ISeqInStream *stream, Byte *buf); ++ ++typedef struct ++{ ++ size_t (*Write)(void *p, const void *buf, size_t size); ++ /* Returns: result - the number of actually written bytes. ++ (result < size) means error */ ++} ISeqOutStream; ++ ++typedef enum ++{ ++ SZ_SEEK_SET = 0, ++ SZ_SEEK_CUR = 1, ++ SZ_SEEK_END = 2 ++} ESzSeek; ++ ++typedef struct ++{ ++ SRes (*Read)(void *p, void *buf, size_t *size); /* same as ISeqInStream::Read */ ++ SRes (*Seek)(void *p, Int64 *pos, ESzSeek origin); ++} ISeekInStream; ++ ++typedef struct ++{ ++ SRes (*Look)(void *p, void **buf, size_t *size); ++ /* if (input(*size) != 0 && output(*size) == 0) means end_of_stream. ++ (output(*size) > input(*size)) is not allowed ++ (output(*size) < input(*size)) is allowed */ ++ SRes (*Skip)(void *p, size_t offset); ++ /* offset must be <= output(*size) of Look */ ++ ++ SRes (*Read)(void *p, void *buf, size_t *size); ++ /* reads directly (without buffer). It's same as ISeqInStream::Read */ ++ SRes (*Seek)(void *p, Int64 *pos, ESzSeek origin); ++} ILookInStream; ++ ++SRes LookInStream_LookRead(ILookInStream *stream, void *buf, size_t *size); ++SRes LookInStream_SeekTo(ILookInStream *stream, UInt64 offset); ++ ++/* reads via ILookInStream::Read */ ++SRes LookInStream_Read2(ILookInStream *stream, void *buf, size_t size, SRes errorType); ++SRes LookInStream_Read(ILookInStream *stream, void *buf, size_t size); ++ ++#define LookToRead_BUF_SIZE (1 << 14) ++ ++typedef struct ++{ ++ ILookInStream s; ++ ISeekInStream *realStream; ++ size_t pos; ++ size_t size; ++ Byte buf[LookToRead_BUF_SIZE]; ++} CLookToRead; ++ ++void LookToRead_CreateVTable(CLookToRead *p, int lookahead); ++void LookToRead_Init(CLookToRead *p); ++ ++typedef struct ++{ ++ ISeqInStream s; ++ ILookInStream *realStream; ++} CSecToLook; ++ ++void SecToLook_CreateVTable(CSecToLook *p); ++ ++typedef struct ++{ ++ ISeqInStream s; ++ ILookInStream *realStream; ++} CSecToRead; ++ ++void SecToRead_CreateVTable(CSecToRead *p); ++ ++typedef struct ++{ ++ SRes (*Progress)(void *p, UInt64 inSize, UInt64 outSize); ++ /* Returns: result. (result != SZ_OK) means break. ++ Value (UInt64)(Int64)-1 for size means unknown value. */ ++} ICompressProgress; ++ ++typedef struct ++{ ++ void *(*Alloc)(void *p, size_t size); ++ void (*Free)(void *p, void *address); /* address can be 0 */ ++} ISzAlloc; ++ ++#define IAlloc_Alloc(p, size) (p)->Alloc((p), size) ++#define IAlloc_Free(p, a) (p)->Free((p), a) ++ ++EXTERN_C_END ++ ++#endif +--- a/include/uapi/linux/jffs2.h ++++ b/include/uapi/linux/jffs2.h +@@ -46,6 +46,7 @@ + #define JFFS2_COMPR_DYNRUBIN 0x05 + #define JFFS2_COMPR_ZLIB 0x06 + #define JFFS2_COMPR_LZO 0x07 ++#define JFFS2_COMPR_LZMA 0x08 + /* Compatibility flags. */ + #define JFFS2_COMPAT_MASK 0xc000 /* What do to if an unknown nodetype is found */ + #define JFFS2_NODE_ACCURATE 0x2000 +--- a/lib/Kconfig ++++ b/lib/Kconfig +@@ -340,6 +340,12 @@ config ZSTD_DECOMPRESS + + source "lib/xz/Kconfig" + ++config LZMA_COMPRESS ++ tristate ++ ++config LZMA_DECOMPRESS ++ tristate ++ + # + # These all provide a common interface (hence the apparent duplication with + # ZLIB_INFLATE; DECOMPRESS_GZIP is just a wrapper.) +--- a/lib/Makefile ++++ b/lib/Makefile +@@ -135,6 +135,16 @@ CFLAGS_kobject.o += -DDEBUG + CFLAGS_kobject_uevent.o += -DDEBUG + endif + ++ifdef CONFIG_JFFS2_ZLIB ++ CONFIG_ZLIB_INFLATE:=y ++ CONFIG_ZLIB_DEFLATE:=y ++endif ++ ++ifdef CONFIG_JFFS2_LZMA ++ CONFIG_LZMA_DECOMPRESS:=y ++ CONFIG_LZMA_COMPRESS:=y ++endif ++ + obj-$(CONFIG_DEBUG_INFO_REDUCED) += debug_info.o + CFLAGS_debug_info.o += $(call cc-option, -femit-struct-debug-detailed=any) + +@@ -192,6 +202,8 @@ obj-$(CONFIG_ZSTD_COMPRESS) += zstd/ + obj-$(CONFIG_ZSTD_DECOMPRESS) += zstd/ + obj-$(CONFIG_XZ_DEC) += xz/ + obj-$(CONFIG_RAID6_PQ) += raid6/ ++obj-$(CONFIG_LZMA_COMPRESS) += lzma/ ++obj-$(CONFIG_LZMA_DECOMPRESS) += lzma/ + + lib-$(CONFIG_DECOMPRESS_GZIP) += decompress_inflate.o + lib-$(CONFIG_DECOMPRESS_BZIP2) += decompress_bunzip2.o +--- /dev/null ++++ b/lib/lzma/LzFind.c +@@ -0,0 +1,761 @@ ++/* LzFind.c -- Match finder for LZ algorithms ++2009-04-22 : Igor Pavlov : Public domain */ ++ ++#include <string.h> ++ ++#include "LzFind.h" ++#include "LzHash.h" ++ ++#define kEmptyHashValue 0 ++#define kMaxValForNormalize ((UInt32)0xFFFFFFFF) ++#define kNormalizeStepMin (1 << 10) /* it must be power of 2 */ ++#define kNormalizeMask (~(kNormalizeStepMin - 1)) ++#define kMaxHistorySize ((UInt32)3 << 30) ++ ++#define kStartMaxLen 3 ++ ++static void LzInWindow_Free(CMatchFinder *p, ISzAlloc *alloc) ++{ ++ if (!p->directInput) ++ { ++ alloc->Free(alloc, p->bufferBase); ++ p->bufferBase = 0; ++ } ++} ++ ++/* keepSizeBefore + keepSizeAfter + keepSizeReserv must be < 4G) */ ++ ++static int LzInWindow_Create(CMatchFinder *p, UInt32 keepSizeReserv, ISzAlloc *alloc) ++{ ++ UInt32 blockSize = p->keepSizeBefore + p->keepSizeAfter + keepSizeReserv; ++ if (p->directInput) ++ { ++ p->blockSize = blockSize; ++ return 1; ++ } ++ if (p->bufferBase == 0 || p->blockSize != blockSize) ++ { ++ LzInWindow_Free(p, alloc); ++ p->blockSize = blockSize; ++ p->bufferBase = (Byte *)alloc->Alloc(alloc, (size_t)blockSize); ++ } ++ return (p->bufferBase != 0); ++} ++ ++Byte *MatchFinder_GetPointerToCurrentPos(CMatchFinder *p) { return p->buffer; } ++Byte MatchFinder_GetIndexByte(CMatchFinder *p, Int32 index) { return p->buffer[index]; } ++ ++UInt32 MatchFinder_GetNumAvailableBytes(CMatchFinder *p) { return p->streamPos - p->pos; } ++ ++void MatchFinder_ReduceOffsets(CMatchFinder *p, UInt32 subValue) ++{ ++ p->posLimit -= subValue; ++ p->pos -= subValue; ++ p->streamPos -= subValue; ++} ++ ++static void MatchFinder_ReadBlock(CMatchFinder *p) ++{ ++ if (p->streamEndWasReached || p->result != SZ_OK) ++ return; ++ if (p->directInput) ++ { ++ UInt32 curSize = 0xFFFFFFFF - p->streamPos; ++ if (curSize > p->directInputRem) ++ curSize = (UInt32)p->directInputRem; ++ p->directInputRem -= curSize; ++ p->streamPos += curSize; ++ if (p->directInputRem == 0) ++ p->streamEndWasReached = 1; ++ return; ++ } ++ for (;;) ++ { ++ Byte *dest = p->buffer + (p->streamPos - p->pos); ++ size_t size = (p->bufferBase + p->blockSize - dest); ++ if (size == 0) ++ return; ++ p->result = p->stream->Read(p->stream, dest, &size); ++ if (p->result != SZ_OK) ++ return; ++ if (size == 0) ++ { ++ p->streamEndWasReached = 1; ++ return; ++ } ++ p->streamPos += (UInt32)size; ++ if (p->streamPos - p->pos > p->keepSizeAfter) ++ return; ++ } ++} ++ ++void MatchFinder_MoveBlock(CMatchFinder *p) ++{ ++ memmove(p->bufferBase, ++ p->buffer - p->keepSizeBefore, ++ (size_t)(p->streamPos - p->pos + p->keepSizeBefore)); ++ p->buffer = p->bufferBase + p->keepSizeBefore; ++} ++ ++int MatchFinder_NeedMove(CMatchFinder *p) ++{ ++ if (p->directInput) ++ return 0; ++ /* if (p->streamEndWasReached) return 0; */ ++ return ((size_t)(p->bufferBase + p->blockSize - p->buffer) <= p->keepSizeAfter); ++} ++ ++void MatchFinder_ReadIfRequired(CMatchFinder *p) ++{ ++ if (p->streamEndWasReached) ++ return; ++ if (p->keepSizeAfter >= p->streamPos - p->pos) ++ MatchFinder_ReadBlock(p); ++} ++ ++static void MatchFinder_CheckAndMoveAndRead(CMatchFinder *p) ++{ ++ if (MatchFinder_NeedMove(p)) ++ MatchFinder_MoveBlock(p); ++ MatchFinder_ReadBlock(p); ++} ++ ++static void MatchFinder_SetDefaultSettings(CMatchFinder *p) ++{ ++ p->cutValue = 32; ++ p->btMode = 1; ++ p->numHashBytes = 4; ++ p->bigHash = 0; ++} ++ ++#define kCrcPoly 0xEDB88320 ++ ++void MatchFinder_Construct(CMatchFinder *p) ++{ ++ UInt32 i; ++ p->bufferBase = 0; ++ p->directInput = 0; ++ p->hash = 0; ++ MatchFinder_SetDefaultSettings(p); ++ ++ for (i = 0; i < 256; i++) ++ { ++ UInt32 r = i; ++ int j; ++ for (j = 0; j < 8; j++) ++ r = (r >> 1) ^ (kCrcPoly & ~((r & 1) - 1)); ++ p->crc[i] = r; ++ } ++} ++ ++static void MatchFinder_FreeThisClassMemory(CMatchFinder *p, ISzAlloc *alloc) ++{ ++ alloc->Free(alloc, p->hash); ++ p->hash = 0; ++} ++ ++void MatchFinder_Free(CMatchFinder *p, ISzAlloc *alloc) ++{ ++ MatchFinder_FreeThisClassMemory(p, alloc); ++ LzInWindow_Free(p, alloc); ++} ++ ++static CLzRef* AllocRefs(UInt32 num, ISzAlloc *alloc) ++{ ++ size_t sizeInBytes = (size_t)num * sizeof(CLzRef); ++ if (sizeInBytes / sizeof(CLzRef) != num) ++ return 0; ++ return (CLzRef *)alloc->Alloc(alloc, sizeInBytes); ++} ++ ++int MatchFinder_Create(CMatchFinder *p, UInt32 historySize, ++ UInt32 keepAddBufferBefore, UInt32 matchMaxLen, UInt32 keepAddBufferAfter, ++ ISzAlloc *alloc) ++{ ++ UInt32 sizeReserv; ++ if (historySize > kMaxHistorySize) ++ { ++ MatchFinder_Free(p, alloc); ++ return 0; ++ } ++ sizeReserv = historySize >> 1; ++ if (historySize > ((UInt32)2 << 30)) ++ sizeReserv = historySize >> 2; ++ sizeReserv += (keepAddBufferBefore + matchMaxLen + keepAddBufferAfter) / 2 + (1 << 19); ++ ++ p->keepSizeBefore = historySize + keepAddBufferBefore + 1; ++ p->keepSizeAfter = matchMaxLen + keepAddBufferAfter; ++ /* we need one additional byte, since we use MoveBlock after pos++ and before dictionary using */ ++ if (LzInWindow_Create(p, sizeReserv, alloc)) ++ { ++ UInt32 newCyclicBufferSize = historySize + 1; ++ UInt32 hs; ++ p->matchMaxLen = matchMaxLen; ++ { ++ p->fixedHashSize = 0; ++ if (p->numHashBytes == 2) ++ hs = (1 << 16) - 1; ++ else ++ { ++ hs = historySize - 1; ++ hs |= (hs >> 1); ++ hs |= (hs >> 2); ++ hs |= (hs >> 4); ++ hs |= (hs >> 8); ++ hs >>= 1; ++ hs |= 0xFFFF; /* don't change it! It's required for Deflate */ ++ if (hs > (1 << 24)) ++ { ++ if (p->numHashBytes == 3) ++ hs = (1 << 24) - 1; ++ else ++ hs >>= 1; ++ } ++ } ++ p->hashMask = hs; ++ hs++; ++ if (p->numHashBytes > 2) p->fixedHashSize += kHash2Size; ++ if (p->numHashBytes > 3) p->fixedHashSize += kHash3Size; ++ if (p->numHashBytes > 4) p->fixedHashSize += kHash4Size; ++ hs += p->fixedHashSize; ++ } ++ ++ { ++ UInt32 prevSize = p->hashSizeSum + p->numSons; ++ UInt32 newSize; ++ p->historySize = historySize; ++ p->hashSizeSum = hs; ++ p->cyclicBufferSize = newCyclicBufferSize; ++ p->numSons = (p->btMode ? newCyclicBufferSize * 2 : newCyclicBufferSize); ++ newSize = p->hashSizeSum + p->numSons; ++ if (p->hash != 0 && prevSize == newSize) ++ return 1; ++ MatchFinder_FreeThisClassMemory(p, alloc); ++ p->hash = AllocRefs(newSize, alloc); ++ if (p->hash != 0) ++ { ++ p->son = p->hash + p->hashSizeSum; ++ return 1; ++ } ++ } ++ } ++ MatchFinder_Free(p, alloc); ++ return 0; ++} ++ ++static void MatchFinder_SetLimits(CMatchFinder *p) ++{ ++ UInt32 limit = kMaxValForNormalize - p->pos; ++ UInt32 limit2 = p->cyclicBufferSize - p->cyclicBufferPos; ++ if (limit2 < limit) ++ limit = limit2; ++ limit2 = p->streamPos - p->pos; ++ if (limit2 <= p->keepSizeAfter) ++ { ++ if (limit2 > 0) ++ limit2 = 1; ++ } ++ else ++ limit2 -= p->keepSizeAfter; ++ if (limit2 < limit) ++ limit = limit2; ++ { ++ UInt32 lenLimit = p->streamPos - p->pos; ++ if (lenLimit > p->matchMaxLen) ++ lenLimit = p->matchMaxLen; ++ p->lenLimit = lenLimit; ++ } ++ p->posLimit = p->pos + limit; ++} ++ ++void MatchFinder_Init(CMatchFinder *p) ++{ ++ UInt32 i; ++ for (i = 0; i < p->hashSizeSum; i++) ++ p->hash[i] = kEmptyHashValue; ++ p->cyclicBufferPos = 0; ++ p->buffer = p->bufferBase; ++ p->pos = p->streamPos = p->cyclicBufferSize; ++ p->result = SZ_OK; ++ p->streamEndWasReached = 0; ++ MatchFinder_ReadBlock(p); ++ MatchFinder_SetLimits(p); ++} ++ ++static UInt32 MatchFinder_GetSubValue(CMatchFinder *p) ++{ ++ return (p->pos - p->historySize - 1) & kNormalizeMask; ++} ++ ++void MatchFinder_Normalize3(UInt32 subValue, CLzRef *items, UInt32 numItems) ++{ ++ UInt32 i; ++ for (i = 0; i < numItems; i++) ++ { ++ UInt32 value = items[i]; ++ if (value <= subValue) ++ value = kEmptyHashValue; ++ else ++ value -= subValue; ++ items[i] = value; ++ } ++} ++ ++static void MatchFinder_Normalize(CMatchFinder *p) ++{ ++ UInt32 subValue = MatchFinder_GetSubValue(p); ++ MatchFinder_Normalize3(subValue, p->hash, p->hashSizeSum + p->numSons); ++ MatchFinder_ReduceOffsets(p, subValue); ++} ++ ++static void MatchFinder_CheckLimits(CMatchFinder *p) ++{ ++ if (p->pos == kMaxValForNormalize) ++ MatchFinder_Normalize(p); ++ if (!p->streamEndWasReached && p->keepSizeAfter == p->streamPos - p->pos) ++ MatchFinder_CheckAndMoveAndRead(p); ++ if (p->cyclicBufferPos == p->cyclicBufferSize) ++ p->cyclicBufferPos = 0; ++ MatchFinder_SetLimits(p); ++} ++ ++static UInt32 * Hc_GetMatchesSpec(UInt32 lenLimit, UInt32 curMatch, UInt32 pos, const Byte *cur, CLzRef *son, ++ UInt32 _cyclicBufferPos, UInt32 _cyclicBufferSize, UInt32 cutValue, ++ UInt32 *distances, UInt32 maxLen) ++{ ++ son[_cyclicBufferPos] = curMatch; ++ for (;;) ++ { ++ UInt32 delta = pos - curMatch; ++ if (cutValue-- == 0 || delta >= _cyclicBufferSize) ++ return distances; ++ { ++ const Byte *pb = cur - delta; ++ curMatch = son[_cyclicBufferPos - delta + ((delta > _cyclicBufferPos) ? _cyclicBufferSize : 0)]; ++ if (pb[maxLen] == cur[maxLen] && *pb == *cur) ++ { ++ UInt32 len = 0; ++ while (++len != lenLimit) ++ if (pb[len] != cur[len]) ++ break; ++ if (maxLen < len) ++ { ++ *distances++ = maxLen = len; ++ *distances++ = delta - 1; ++ if (len == lenLimit) ++ return distances; ++ } ++ } ++ } ++ } ++} ++ ++UInt32 * GetMatchesSpec1(UInt32 lenLimit, UInt32 curMatch, UInt32 pos, const Byte *cur, CLzRef *son, ++ UInt32 _cyclicBufferPos, UInt32 _cyclicBufferSize, UInt32 cutValue, ++ UInt32 *distances, UInt32 maxLen) ++{ ++ CLzRef *ptr0 = son + (_cyclicBufferPos << 1) + 1; ++ CLzRef *ptr1 = son + (_cyclicBufferPos << 1); ++ UInt32 len0 = 0, len1 = 0; ++ for (;;) ++ { ++ UInt32 delta = pos - curMatch; ++ if (cutValue-- == 0 || delta >= _cyclicBufferSize) ++ { ++ *ptr0 = *ptr1 = kEmptyHashValue; ++ return distances; ++ } ++ { ++ CLzRef *pair = son + ((_cyclicBufferPos - delta + ((delta > _cyclicBufferPos) ? _cyclicBufferSize : 0)) << 1); ++ const Byte *pb = cur - delta; ++ UInt32 len = (len0 < len1 ? len0 : len1); ++ if (pb[len] == cur[len]) ++ { ++ if (++len != lenLimit && pb[len] == cur[len]) ++ while (++len != lenLimit) ++ if (pb[len] != cur[len]) ++ break; ++ if (maxLen < len) ++ { ++ *distances++ = maxLen = len; ++ *distances++ = delta - 1; ++ if (len == lenLimit) ++ { ++ *ptr1 = pair[0]; ++ *ptr0 = pair[1]; ++ return distances; ++ } ++ } ++ } ++ if (pb[len] < cur[len]) ++ { ++ *ptr1 = curMatch; ++ ptr1 = pair + 1; ++ curMatch = *ptr1; ++ len1 = len; ++ } ++ else ++ { ++ *ptr0 = curMatch; ++ ptr0 = pair; ++ curMatch = *ptr0; ++ len0 = len; ++ } ++ } ++ } ++} ++ ++static void SkipMatchesSpec(UInt32 lenLimit, UInt32 curMatch, UInt32 pos, const Byte *cur, CLzRef *son, ++ UInt32 _cyclicBufferPos, UInt32 _cyclicBufferSize, UInt32 cutValue) ++{ ++ CLzRef *ptr0 = son + (_cyclicBufferPos << 1) + 1; ++ CLzRef *ptr1 = son + (_cyclicBufferPos << 1); ++ UInt32 len0 = 0, len1 = 0; ++ for (;;) ++ { ++ UInt32 delta = pos - curMatch; ++ if (cutValue-- == 0 || delta >= _cyclicBufferSize) ++ { ++ *ptr0 = *ptr1 = kEmptyHashValue; ++ return; ++ } ++ { ++ CLzRef *pair = son + ((_cyclicBufferPos - delta + ((delta > _cyclicBufferPos) ? _cyclicBufferSize : 0)) << 1); ++ const Byte *pb = cur - delta; ++ UInt32 len = (len0 < len1 ? len0 : len1); ++ if (pb[len] == cur[len]) ++ { ++ while (++len != lenLimit) ++ if (pb[len] != cur[len]) ++ break; ++ { ++ if (len == lenLimit) ++ { ++ *ptr1 = pair[0]; ++ *ptr0 = pair[1]; ++ return; ++ } ++ } ++ } ++ if (pb[len] < cur[len]) ++ { ++ *ptr1 = curMatch; ++ ptr1 = pair + 1; ++ curMatch = *ptr1; ++ len1 = len; ++ } ++ else ++ { ++ *ptr0 = curMatch; ++ ptr0 = pair; ++ curMatch = *ptr0; ++ len0 = len; ++ } ++ } ++ } ++} ++ ++#define MOVE_POS \ ++ ++p->cyclicBufferPos; \ ++ p->buffer++; \ ++ if (++p->pos == p->posLimit) MatchFinder_CheckLimits(p); ++ ++#define MOVE_POS_RET MOVE_POS return offset; ++ ++static void MatchFinder_MovePos(CMatchFinder *p) { MOVE_POS; } ++ ++#define GET_MATCHES_HEADER2(minLen, ret_op) \ ++ UInt32 lenLimit; UInt32 hashValue; const Byte *cur; UInt32 curMatch; \ ++ lenLimit = p->lenLimit; { if (lenLimit < minLen) { MatchFinder_MovePos(p); ret_op; }} \ ++ cur = p->buffer; ++ ++#define GET_MATCHES_HEADER(minLen) GET_MATCHES_HEADER2(minLen, return 0) ++#define SKIP_HEADER(minLen) GET_MATCHES_HEADER2(minLen, continue) ++ ++#define MF_PARAMS(p) p->pos, p->buffer, p->son, p->cyclicBufferPos, p->cyclicBufferSize, p->cutValue ++ ++#define GET_MATCHES_FOOTER(offset, maxLen) \ ++ offset = (UInt32)(GetMatchesSpec1(lenLimit, curMatch, MF_PARAMS(p), \ ++ distances + offset, maxLen) - distances); MOVE_POS_RET; ++ ++#define SKIP_FOOTER \ ++ SkipMatchesSpec(lenLimit, curMatch, MF_PARAMS(p)); MOVE_POS; ++ ++static UInt32 Bt2_MatchFinder_GetMatches(CMatchFinder *p, UInt32 *distances) ++{ ++ UInt32 offset; ++ GET_MATCHES_HEADER(2) ++ HASH2_CALC; ++ curMatch = p->hash[hashValue]; ++ p->hash[hashValue] = p->pos; ++ offset = 0; ++ GET_MATCHES_FOOTER(offset, 1) ++} ++ ++UInt32 Bt3Zip_MatchFinder_GetMatches(CMatchFinder *p, UInt32 *distances) ++{ ++ UInt32 offset; ++ GET_MATCHES_HEADER(3) ++ HASH_ZIP_CALC; ++ curMatch = p->hash[hashValue]; ++ p->hash[hashValue] = p->pos; ++ offset = 0; ++ GET_MATCHES_FOOTER(offset, 2) ++} ++ ++static UInt32 Bt3_MatchFinder_GetMatches(CMatchFinder *p, UInt32 *distances) ++{ ++ UInt32 hash2Value, delta2, maxLen, offset; ++ GET_MATCHES_HEADER(3) ++ ++ HASH3_CALC; ++ ++ delta2 = p->pos - p->hash[hash2Value]; ++ curMatch = p->hash[kFix3HashSize + hashValue]; ++ ++ p->hash[hash2Value] = ++ p->hash[kFix3HashSize + hashValue] = p->pos; ++ ++ ++ maxLen = 2; ++ offset = 0; ++ if (delta2 < p->cyclicBufferSize && *(cur - delta2) == *cur) ++ { ++ for (; maxLen != lenLimit; maxLen++) ++ if (cur[(ptrdiff_t)maxLen - delta2] != cur[maxLen]) ++ break; ++ distances[0] = maxLen; ++ distances[1] = delta2 - 1; ++ offset = 2; ++ if (maxLen == lenLimit) ++ { ++ SkipMatchesSpec(lenLimit, curMatch, MF_PARAMS(p)); ++ MOVE_POS_RET; ++ } ++ } ++ GET_MATCHES_FOOTER(offset, maxLen) ++} ++ ++static UInt32 Bt4_MatchFinder_GetMatches(CMatchFinder *p, UInt32 *distances) ++{ ++ UInt32 hash2Value, hash3Value, delta2, delta3, maxLen, offset; ++ GET_MATCHES_HEADER(4) ++ ++ HASH4_CALC; ++ ++ delta2 = p->pos - p->hash[ hash2Value]; ++ delta3 = p->pos - p->hash[kFix3HashSize + hash3Value]; ++ curMatch = p->hash[kFix4HashSize + hashValue]; ++ ++ p->hash[ hash2Value] = ++ p->hash[kFix3HashSize + hash3Value] = ++ p->hash[kFix4HashSize + hashValue] = p->pos; ++ ++ maxLen = 1; ++ offset = 0; ++ if (delta2 < p->cyclicBufferSize && *(cur - delta2) == *cur) ++ { ++ distances[0] = maxLen = 2; ++ distances[1] = delta2 - 1; ++ offset = 2; ++ } ++ if (delta2 != delta3 && delta3 < p->cyclicBufferSize && *(cur - delta3) == *cur) ++ { ++ maxLen = 3; ++ distances[offset + 1] = delta3 - 1; ++ offset += 2; ++ delta2 = delta3; ++ } ++ if (offset != 0) ++ { ++ for (; maxLen != lenLimit; maxLen++) ++ if (cur[(ptrdiff_t)maxLen - delta2] != cur[maxLen]) ++ break; ++ distances[offset - 2] = maxLen; ++ if (maxLen == lenLimit) ++ { ++ SkipMatchesSpec(lenLimit, curMatch, MF_PARAMS(p)); ++ MOVE_POS_RET; ++ } ++ } ++ if (maxLen < 3) ++ maxLen = 3; ++ GET_MATCHES_FOOTER(offset, maxLen) ++} ++ ++static UInt32 Hc4_MatchFinder_GetMatches(CMatchFinder *p, UInt32 *distances) ++{ ++ UInt32 hash2Value, hash3Value, delta2, delta3, maxLen, offset; ++ GET_MATCHES_HEADER(4) ++ ++ HASH4_CALC; ++ ++ delta2 = p->pos - p->hash[ hash2Value]; ++ delta3 = p->pos - p->hash[kFix3HashSize + hash3Value]; ++ curMatch = p->hash[kFix4HashSize + hashValue]; ++ ++ p->hash[ hash2Value] = ++ p->hash[kFix3HashSize + hash3Value] = ++ p->hash[kFix4HashSize + hashValue] = p->pos; ++ ++ maxLen = 1; ++ offset = 0; ++ if (delta2 < p->cyclicBufferSize && *(cur - delta2) == *cur) ++ { ++ distances[0] = maxLen = 2; ++ distances[1] = delta2 - 1; ++ offset = 2; ++ } ++ if (delta2 != delta3 && delta3 < p->cyclicBufferSize && *(cur - delta3) == *cur) ++ { ++ maxLen = 3; ++ distances[offset + 1] = delta3 - 1; ++ offset += 2; ++ delta2 = delta3; ++ } ++ if (offset != 0) ++ { ++ for (; maxLen != lenLimit; maxLen++) ++ if (cur[(ptrdiff_t)maxLen - delta2] != cur[maxLen]) ++ break; ++ distances[offset - 2] = maxLen; ++ if (maxLen == lenLimit) ++ { ++ p->son[p->cyclicBufferPos] = curMatch; ++ MOVE_POS_RET; ++ } ++ } ++ if (maxLen < 3) ++ maxLen = 3; ++ offset = (UInt32)(Hc_GetMatchesSpec(lenLimit, curMatch, MF_PARAMS(p), ++ distances + offset, maxLen) - (distances)); ++ MOVE_POS_RET ++} ++ ++UInt32 Hc3Zip_MatchFinder_GetMatches(CMatchFinder *p, UInt32 *distances) ++{ ++ UInt32 offset; ++ GET_MATCHES_HEADER(3) ++ HASH_ZIP_CALC; ++ curMatch = p->hash[hashValue]; ++ p->hash[hashValue] = p->pos; ++ offset = (UInt32)(Hc_GetMatchesSpec(lenLimit, curMatch, MF_PARAMS(p), ++ distances, 2) - (distances)); ++ MOVE_POS_RET ++} ++ ++static void Bt2_MatchFinder_Skip(CMatchFinder *p, UInt32 num) ++{ ++ do ++ { ++ SKIP_HEADER(2) ++ HASH2_CALC; ++ curMatch = p->hash[hashValue]; ++ p->hash[hashValue] = p->pos; ++ SKIP_FOOTER ++ } ++ while (--num != 0); ++} ++ ++void Bt3Zip_MatchFinder_Skip(CMatchFinder *p, UInt32 num) ++{ ++ do ++ { ++ SKIP_HEADER(3) ++ HASH_ZIP_CALC; ++ curMatch = p->hash[hashValue]; ++ p->hash[hashValue] = p->pos; ++ SKIP_FOOTER ++ } ++ while (--num != 0); ++} ++ ++static void Bt3_MatchFinder_Skip(CMatchFinder *p, UInt32 num) ++{ ++ do ++ { ++ UInt32 hash2Value; ++ SKIP_HEADER(3) ++ HASH3_CALC; ++ curMatch = p->hash[kFix3HashSize + hashValue]; ++ p->hash[hash2Value] = ++ p->hash[kFix3HashSize + hashValue] = p->pos; ++ SKIP_FOOTER ++ } ++ while (--num != 0); ++} ++ ++static void Bt4_MatchFinder_Skip(CMatchFinder *p, UInt32 num) ++{ ++ do ++ { ++ UInt32 hash2Value, hash3Value; ++ SKIP_HEADER(4) ++ HASH4_CALC; ++ curMatch = p->hash[kFix4HashSize + hashValue]; ++ p->hash[ hash2Value] = ++ p->hash[kFix3HashSize + hash3Value] = p->pos; ++ p->hash[kFix4HashSize + hashValue] = p->pos; ++ SKIP_FOOTER ++ } ++ while (--num != 0); ++} ++ ++static void Hc4_MatchFinder_Skip(CMatchFinder *p, UInt32 num) ++{ ++ do ++ { ++ UInt32 hash2Value, hash3Value; ++ SKIP_HEADER(4) ++ HASH4_CALC; ++ curMatch = p->hash[kFix4HashSize + hashValue]; ++ p->hash[ hash2Value] = ++ p->hash[kFix3HashSize + hash3Value] = ++ p->hash[kFix4HashSize + hashValue] = p->pos; ++ p->son[p->cyclicBufferPos] = curMatch; ++ MOVE_POS ++ } ++ while (--num != 0); ++} ++ ++void Hc3Zip_MatchFinder_Skip(CMatchFinder *p, UInt32 num) ++{ ++ do ++ { ++ SKIP_HEADER(3) ++ HASH_ZIP_CALC; ++ curMatch = p->hash[hashValue]; ++ p->hash[hashValue] = p->pos; ++ p->son[p->cyclicBufferPos] = curMatch; ++ MOVE_POS ++ } ++ while (--num != 0); ++} ++ ++void MatchFinder_CreateVTable(CMatchFinder *p, IMatchFinder *vTable) ++{ ++ vTable->Init = (Mf_Init_Func)MatchFinder_Init; ++ vTable->GetIndexByte = (Mf_GetIndexByte_Func)MatchFinder_GetIndexByte; ++ vTable->GetNumAvailableBytes = (Mf_GetNumAvailableBytes_Func)MatchFinder_GetNumAvailableBytes; ++ vTable->GetPointerToCurrentPos = (Mf_GetPointerToCurrentPos_Func)MatchFinder_GetPointerToCurrentPos; ++ if (!p->btMode) ++ { ++ vTable->GetMatches = (Mf_GetMatches_Func)Hc4_MatchFinder_GetMatches; ++ vTable->Skip = (Mf_Skip_Func)Hc4_MatchFinder_Skip; ++ } ++ else if (p->numHashBytes == 2) ++ { ++ vTable->GetMatches = (Mf_GetMatches_Func)Bt2_MatchFinder_GetMatches; ++ vTable->Skip = (Mf_Skip_Func)Bt2_MatchFinder_Skip; ++ } ++ else if (p->numHashBytes == 3) ++ { ++ vTable->GetMatches = (Mf_GetMatches_Func)Bt3_MatchFinder_GetMatches; ++ vTable->Skip = (Mf_Skip_Func)Bt3_MatchFinder_Skip; ++ } ++ else ++ { ++ vTable->GetMatches = (Mf_GetMatches_Func)Bt4_MatchFinder_GetMatches; ++ vTable->Skip = (Mf_Skip_Func)Bt4_MatchFinder_Skip; ++ } ++} +--- /dev/null ++++ b/lib/lzma/LzmaDec.c +@@ -0,0 +1,999 @@ ++/* LzmaDec.c -- LZMA Decoder ++2009-09-20 : Igor Pavlov : Public domain */ ++ ++#include "LzmaDec.h" ++ ++#include <string.h> ++ ++#define kNumTopBits 24 ++#define kTopValue ((UInt32)1 << kNumTopBits) ++ ++#define kNumBitModelTotalBits 11 ++#define kBitModelTotal (1 << kNumBitModelTotalBits) ++#define kNumMoveBits 5 ++ ++#define RC_INIT_SIZE 5 ++ ++#define NORMALIZE if (range < kTopValue) { range <<= 8; code = (code << 8) | (*buf++); } ++ ++#define IF_BIT_0(p) ttt = *(p); NORMALIZE; bound = (range >> kNumBitModelTotalBits) * ttt; if (code < bound) ++#define UPDATE_0(p) range = bound; *(p) = (CLzmaProb)(ttt + ((kBitModelTotal - ttt) >> kNumMoveBits)); ++#define UPDATE_1(p) range -= bound; code -= bound; *(p) = (CLzmaProb)(ttt - (ttt >> kNumMoveBits)); ++#define GET_BIT2(p, i, A0, A1) IF_BIT_0(p) \ ++ { UPDATE_0(p); i = (i + i); A0; } else \ ++ { UPDATE_1(p); i = (i + i) + 1; A1; } ++#define GET_BIT(p, i) GET_BIT2(p, i, ; , ;) ++ ++#define TREE_GET_BIT(probs, i) { GET_BIT((probs + i), i); } ++#define TREE_DECODE(probs, limit, i) \ ++ { i = 1; do { TREE_GET_BIT(probs, i); } while (i < limit); i -= limit; } ++ ++/* #define _LZMA_SIZE_OPT */ ++ ++#ifdef _LZMA_SIZE_OPT ++#define TREE_6_DECODE(probs, i) TREE_DECODE(probs, (1 << 6), i) ++#else ++#define TREE_6_DECODE(probs, i) \ ++ { i = 1; \ ++ TREE_GET_BIT(probs, i); \ ++ TREE_GET_BIT(probs, i); \ ++ TREE_GET_BIT(probs, i); \ ++ TREE_GET_BIT(probs, i); \ ++ TREE_GET_BIT(probs, i); \ ++ TREE_GET_BIT(probs, i); \ ++ i -= 0x40; } ++#endif ++ ++#define NORMALIZE_CHECK if (range < kTopValue) { if (buf >= bufLimit) return DUMMY_ERROR; range <<= 8; code = (code << 8) | (*buf++); } ++ ++#define IF_BIT_0_CHECK(p) ttt = *(p); NORMALIZE_CHECK; bound = (range >> kNumBitModelTotalBits) * ttt; if (code < bound) ++#define UPDATE_0_CHECK range = bound; ++#define UPDATE_1_CHECK range -= bound; code -= bound; ++#define GET_BIT2_CHECK(p, i, A0, A1) IF_BIT_0_CHECK(p) \ ++ { UPDATE_0_CHECK; i = (i + i); A0; } else \ ++ { UPDATE_1_CHECK; i = (i + i) + 1; A1; } ++#define GET_BIT_CHECK(p, i) GET_BIT2_CHECK(p, i, ; , ;) ++#define TREE_DECODE_CHECK(probs, limit, i) \ ++ { i = 1; do { GET_BIT_CHECK(probs + i, i) } while (i < limit); i -= limit; } ++ ++ ++#define kNumPosBitsMax 4 ++#define kNumPosStatesMax (1 << kNumPosBitsMax) ++ ++#define kLenNumLowBits 3 ++#define kLenNumLowSymbols (1 << kLenNumLowBits) ++#define kLenNumMidBits 3 ++#define kLenNumMidSymbols (1 << kLenNumMidBits) ++#define kLenNumHighBits 8 ++#define kLenNumHighSymbols (1 << kLenNumHighBits) ++ ++#define LenChoice 0 ++#define LenChoice2 (LenChoice + 1) ++#define LenLow (LenChoice2 + 1) ++#define LenMid (LenLow + (kNumPosStatesMax << kLenNumLowBits)) ++#define LenHigh (LenMid + (kNumPosStatesMax << kLenNumMidBits)) ++#define kNumLenProbs (LenHigh + kLenNumHighSymbols) ++ ++ ++#define kNumStates 12 ++#define kNumLitStates 7 ++ ++#define kStartPosModelIndex 4 ++#define kEndPosModelIndex 14 ++#define kNumFullDistances (1 << (kEndPosModelIndex >> 1)) ++ ++#define kNumPosSlotBits 6 ++#define kNumLenToPosStates 4 ++ ++#define kNumAlignBits 4 ++#define kAlignTableSize (1 << kNumAlignBits) ++ ++#define kMatchMinLen 2 ++#define kMatchSpecLenStart (kMatchMinLen + kLenNumLowSymbols + kLenNumMidSymbols + kLenNumHighSymbols) ++ ++#define IsMatch 0 ++#define IsRep (IsMatch + (kNumStates << kNumPosBitsMax)) ++#define IsRepG0 (IsRep + kNumStates) ++#define IsRepG1 (IsRepG0 + kNumStates) ++#define IsRepG2 (IsRepG1 + kNumStates) ++#define IsRep0Long (IsRepG2 + kNumStates) ++#define PosSlot (IsRep0Long + (kNumStates << kNumPosBitsMax)) ++#define SpecPos (PosSlot + (kNumLenToPosStates << kNumPosSlotBits)) ++#define Align (SpecPos + kNumFullDistances - kEndPosModelIndex) ++#define LenCoder (Align + kAlignTableSize) ++#define RepLenCoder (LenCoder + kNumLenProbs) ++#define Literal (RepLenCoder + kNumLenProbs) ++ ++#define LZMA_BASE_SIZE 1846 ++#define LZMA_LIT_SIZE 768 ++ ++#define LzmaProps_GetNumProbs(p) ((UInt32)LZMA_BASE_SIZE + (LZMA_LIT_SIZE << ((p)->lc + (p)->lp))) ++ ++#if Literal != LZMA_BASE_SIZE ++StopCompilingDueBUG ++#endif ++ ++#define LZMA_DIC_MIN (1 << 12) ++ ++/* First LZMA-symbol is always decoded. ++And it decodes new LZMA-symbols while (buf < bufLimit), but "buf" is without last normalization ++Out: ++ Result: ++ SZ_OK - OK ++ SZ_ERROR_DATA - Error ++ p->remainLen: ++ < kMatchSpecLenStart : normal remain ++ = kMatchSpecLenStart : finished ++ = kMatchSpecLenStart + 1 : Flush marker ++ = kMatchSpecLenStart + 2 : State Init Marker ++*/ ++ ++static int MY_FAST_CALL LzmaDec_DecodeReal(CLzmaDec *p, SizeT limit, const Byte *bufLimit) ++{ ++ CLzmaProb *probs = p->probs; ++ ++ unsigned state = p->state; ++ UInt32 rep0 = p->reps[0], rep1 = p->reps[1], rep2 = p->reps[2], rep3 = p->reps[3]; ++ unsigned pbMask = ((unsigned)1 << (p->prop.pb)) - 1; ++ unsigned lpMask = ((unsigned)1 << (p->prop.lp)) - 1; ++ unsigned lc = p->prop.lc; ++ ++ Byte *dic = p->dic; ++ SizeT dicBufSize = p->dicBufSize; ++ SizeT dicPos = p->dicPos; ++ ++ UInt32 processedPos = p->processedPos; ++ UInt32 checkDicSize = p->checkDicSize; ++ unsigned len = 0; ++ ++ const Byte *buf = p->buf; ++ UInt32 range = p->range; ++ UInt32 code = p->code; ++ ++ do ++ { ++ CLzmaProb *prob; ++ UInt32 bound; ++ unsigned ttt; ++ unsigned posState = processedPos & pbMask; ++ ++ prob = probs + IsMatch + (state << kNumPosBitsMax) + posState; ++ IF_BIT_0(prob) ++ { ++ unsigned symbol; ++ UPDATE_0(prob); ++ prob = probs + Literal; ++ if (checkDicSize != 0 || processedPos != 0) ++ prob += (LZMA_LIT_SIZE * (((processedPos & lpMask) << lc) + ++ (dic[(dicPos == 0 ? dicBufSize : dicPos) - 1] >> (8 - lc)))); ++ ++ if (state < kNumLitStates) ++ { ++ state -= (state < 4) ? state : 3; ++ symbol = 1; ++ do { GET_BIT(prob + symbol, symbol) } while (symbol < 0x100); ++ } ++ else ++ { ++ unsigned matchByte = p->dic[(dicPos - rep0) + ((dicPos < rep0) ? dicBufSize : 0)]; ++ unsigned offs = 0x100; ++ state -= (state < 10) ? 3 : 6; ++ symbol = 1; ++ do ++ { ++ unsigned bit; ++ CLzmaProb *probLit; ++ matchByte <<= 1; ++ bit = (matchByte & offs); ++ probLit = prob + offs + bit + symbol; ++ GET_BIT2(probLit, symbol, offs &= ~bit, offs &= bit) ++ } ++ while (symbol < 0x100); ++ } ++ dic[dicPos++] = (Byte)symbol; ++ processedPos++; ++ continue; ++ } ++ else ++ { ++ UPDATE_1(prob); ++ prob = probs + IsRep + state; ++ IF_BIT_0(prob) ++ { ++ UPDATE_0(prob); ++ state += kNumStates; ++ prob = probs + LenCoder; ++ } ++ else ++ { ++ UPDATE_1(prob); ++ if (checkDicSize == 0 && processedPos == 0) ++ return SZ_ERROR_DATA; ++ prob = probs + IsRepG0 + state; ++ IF_BIT_0(prob) ++ { ++ UPDATE_0(prob); ++ prob = probs + IsRep0Long + (state << kNumPosBitsMax) + posState; ++ IF_BIT_0(prob) ++ { ++ UPDATE_0(prob); ++ dic[dicPos] = dic[(dicPos - rep0) + ((dicPos < rep0) ? dicBufSize : 0)]; ++ dicPos++; ++ processedPos++; ++ state = state < kNumLitStates ? 9 : 11; ++ continue; ++ } ++ UPDATE_1(prob); ++ } ++ else ++ { ++ UInt32 distance; ++ UPDATE_1(prob); ++ prob = probs + IsRepG1 + state; ++ IF_BIT_0(prob) ++ { ++ UPDATE_0(prob); ++ distance = rep1; ++ } ++ else ++ { ++ UPDATE_1(prob); ++ prob = probs + IsRepG2 + state; ++ IF_BIT_0(prob) ++ { ++ UPDATE_0(prob); ++ distance = rep2; ++ } ++ else ++ { ++ UPDATE_1(prob); ++ distance = rep3; ++ rep3 = rep2; ++ } ++ rep2 = rep1; ++ } ++ rep1 = rep0; ++ rep0 = distance; ++ } ++ state = state < kNumLitStates ? 8 : 11; ++ prob = probs + RepLenCoder; ++ } ++ { ++ unsigned limit, offset; ++ CLzmaProb *probLen = prob + LenChoice; ++ IF_BIT_0(probLen) ++ { ++ UPDATE_0(probLen); ++ probLen = prob + LenLow + (posState << kLenNumLowBits); ++ offset = 0; ++ limit = (1 << kLenNumLowBits); ++ } ++ else ++ { ++ UPDATE_1(probLen); ++ probLen = prob + LenChoice2; ++ IF_BIT_0(probLen) ++ { ++ UPDATE_0(probLen); ++ probLen = prob + LenMid + (posState << kLenNumMidBits); ++ offset = kLenNumLowSymbols; ++ limit = (1 << kLenNumMidBits); ++ } ++ else ++ { ++ UPDATE_1(probLen); ++ probLen = prob + LenHigh; ++ offset = kLenNumLowSymbols + kLenNumMidSymbols; ++ limit = (1 << kLenNumHighBits); ++ } ++ } ++ TREE_DECODE(probLen, limit, len); ++ len += offset; ++ } ++ ++ if (state >= kNumStates) ++ { ++ UInt32 distance; ++ prob = probs + PosSlot + ++ ((len < kNumLenToPosStates ? len : kNumLenToPosStates - 1) << kNumPosSlotBits); ++ TREE_6_DECODE(prob, distance); ++ if (distance >= kStartPosModelIndex) ++ { ++ unsigned posSlot = (unsigned)distance; ++ int numDirectBits = (int)(((distance >> 1) - 1)); ++ distance = (2 | (distance & 1)); ++ if (posSlot < kEndPosModelIndex) ++ { ++ distance <<= numDirectBits; ++ prob = probs + SpecPos + distance - posSlot - 1; ++ { ++ UInt32 mask = 1; ++ unsigned i = 1; ++ do ++ { ++ GET_BIT2(prob + i, i, ; , distance |= mask); ++ mask <<= 1; ++ } ++ while (--numDirectBits != 0); ++ } ++ } ++ else ++ { ++ numDirectBits -= kNumAlignBits; ++ do ++ { ++ NORMALIZE ++ range >>= 1; ++ ++ { ++ UInt32 t; ++ code -= range; ++ t = (0 - ((UInt32)code >> 31)); /* (UInt32)((Int32)code >> 31) */ ++ distance = (distance << 1) + (t + 1); ++ code += range & t; ++ } ++ /* ++ distance <<= 1; ++ if (code >= range) ++ { ++ code -= range; ++ distance |= 1; ++ } ++ */ ++ } ++ while (--numDirectBits != 0); ++ prob = probs + Align; ++ distance <<= kNumAlignBits; ++ { ++ unsigned i = 1; ++ GET_BIT2(prob + i, i, ; , distance |= 1); ++ GET_BIT2(prob + i, i, ; , distance |= 2); ++ GET_BIT2(prob + i, i, ; , distance |= 4); ++ GET_BIT2(prob + i, i, ; , distance |= 8); ++ } ++ if (distance == (UInt32)0xFFFFFFFF) ++ { ++ len += kMatchSpecLenStart; ++ state -= kNumStates; ++ break; ++ } ++ } ++ } ++ rep3 = rep2; ++ rep2 = rep1; ++ rep1 = rep0; ++ rep0 = distance + 1; ++ if (checkDicSize == 0) ++ { ++ if (distance >= processedPos) ++ return SZ_ERROR_DATA; ++ } ++ else if (distance >= checkDicSize) ++ return SZ_ERROR_DATA; ++ state = (state < kNumStates + kNumLitStates) ? kNumLitStates : kNumLitStates + 3; ++ } ++ ++ len += kMatchMinLen; ++ ++ if (limit == dicPos) ++ return SZ_ERROR_DATA; ++ { ++ SizeT rem = limit - dicPos; ++ unsigned curLen = ((rem < len) ? (unsigned)rem : len); ++ SizeT pos = (dicPos - rep0) + ((dicPos < rep0) ? dicBufSize : 0); ++ ++ processedPos += curLen; ++ ++ len -= curLen; ++ if (pos + curLen <= dicBufSize) ++ { ++ Byte *dest = dic + dicPos; ++ ptrdiff_t src = (ptrdiff_t)pos - (ptrdiff_t)dicPos; ++ const Byte *lim = dest + curLen; ++ dicPos += curLen; ++ do ++ *(dest) = (Byte)*(dest + src); ++ while (++dest != lim); ++ } ++ else ++ { ++ do ++ { ++ dic[dicPos++] = dic[pos]; ++ if (++pos == dicBufSize) ++ pos = 0; ++ } ++ while (--curLen != 0); ++ } ++ } ++ } ++ } ++ while (dicPos < limit && buf < bufLimit); ++ NORMALIZE; ++ p->buf = buf; ++ p->range = range; ++ p->code = code; ++ p->remainLen = len; ++ p->dicPos = dicPos; ++ p->processedPos = processedPos; ++ p->reps[0] = rep0; ++ p->reps[1] = rep1; ++ p->reps[2] = rep2; ++ p->reps[3] = rep3; ++ p->state = state; ++ ++ return SZ_OK; ++} ++ ++static void MY_FAST_CALL LzmaDec_WriteRem(CLzmaDec *p, SizeT limit) ++{ ++ if (p->remainLen != 0 && p->remainLen < kMatchSpecLenStart) ++ { ++ Byte *dic = p->dic; ++ SizeT dicPos = p->dicPos; ++ SizeT dicBufSize = p->dicBufSize; ++ unsigned len = p->remainLen; ++ UInt32 rep0 = p->reps[0]; ++ if (limit - dicPos < len) ++ len = (unsigned)(limit - dicPos); ++ ++ if (p->checkDicSize == 0 && p->prop.dicSize - p->processedPos <= len) ++ p->checkDicSize = p->prop.dicSize; ++ ++ p->processedPos += len; ++ p->remainLen -= len; ++ while (len-- != 0) ++ { ++ dic[dicPos] = dic[(dicPos - rep0) + ((dicPos < rep0) ? dicBufSize : 0)]; ++ dicPos++; ++ } ++ p->dicPos = dicPos; ++ } ++} ++ ++static int MY_FAST_CALL LzmaDec_DecodeReal2(CLzmaDec *p, SizeT limit, const Byte *bufLimit) ++{ ++ do ++ { ++ SizeT limit2 = limit; ++ if (p->checkDicSize == 0) ++ { ++ UInt32 rem = p->prop.dicSize - p->processedPos; ++ if (limit - p->dicPos > rem) ++ limit2 = p->dicPos + rem; ++ } ++ RINOK(LzmaDec_DecodeReal(p, limit2, bufLimit)); ++ if (p->processedPos >= p->prop.dicSize) ++ p->checkDicSize = p->prop.dicSize; ++ LzmaDec_WriteRem(p, limit); ++ } ++ while (p->dicPos < limit && p->buf < bufLimit && p->remainLen < kMatchSpecLenStart); ++ ++ if (p->remainLen > kMatchSpecLenStart) ++ { ++ p->remainLen = kMatchSpecLenStart; ++ } ++ return 0; ++} ++ ++typedef enum ++{ ++ DUMMY_ERROR, /* unexpected end of input stream */ ++ DUMMY_LIT, ++ DUMMY_MATCH, ++ DUMMY_REP ++} ELzmaDummy; ++ ++static ELzmaDummy LzmaDec_TryDummy(const CLzmaDec *p, const Byte *buf, SizeT inSize) ++{ ++ UInt32 range = p->range; ++ UInt32 code = p->code; ++ const Byte *bufLimit = buf + inSize; ++ CLzmaProb *probs = p->probs; ++ unsigned state = p->state; ++ ELzmaDummy res; ++ ++ { ++ CLzmaProb *prob; ++ UInt32 bound; ++ unsigned ttt; ++ unsigned posState = (p->processedPos) & ((1 << p->prop.pb) - 1); ++ ++ prob = probs + IsMatch + (state << kNumPosBitsMax) + posState; ++ IF_BIT_0_CHECK(prob) ++ { ++ UPDATE_0_CHECK ++ ++ /* if (bufLimit - buf >= 7) return DUMMY_LIT; */ ++ ++ prob = probs + Literal; ++ if (p->checkDicSize != 0 || p->processedPos != 0) ++ prob += (LZMA_LIT_SIZE * ++ ((((p->processedPos) & ((1 << (p->prop.lp)) - 1)) << p->prop.lc) + ++ (p->dic[(p->dicPos == 0 ? p->dicBufSize : p->dicPos) - 1] >> (8 - p->prop.lc)))); ++ ++ if (state < kNumLitStates) ++ { ++ unsigned symbol = 1; ++ do { GET_BIT_CHECK(prob + symbol, symbol) } while (symbol < 0x100); ++ } ++ else ++ { ++ unsigned matchByte = p->dic[p->dicPos - p->reps[0] + ++ ((p->dicPos < p->reps[0]) ? p->dicBufSize : 0)]; ++ unsigned offs = 0x100; ++ unsigned symbol = 1; ++ do ++ { ++ unsigned bit; ++ CLzmaProb *probLit; ++ matchByte <<= 1; ++ bit = (matchByte & offs); ++ probLit = prob + offs + bit + symbol; ++ GET_BIT2_CHECK(probLit, symbol, offs &= ~bit, offs &= bit) ++ } ++ while (symbol < 0x100); ++ } ++ res = DUMMY_LIT; ++ } ++ else ++ { ++ unsigned len; ++ UPDATE_1_CHECK; ++ ++ prob = probs + IsRep + state; ++ IF_BIT_0_CHECK(prob) ++ { ++ UPDATE_0_CHECK; ++ state = 0; ++ prob = probs + LenCoder; ++ res = DUMMY_MATCH; ++ } ++ else ++ { ++ UPDATE_1_CHECK; ++ res = DUMMY_REP; ++ prob = probs + IsRepG0 + state; ++ IF_BIT_0_CHECK(prob) ++ { ++ UPDATE_0_CHECK; ++ prob = probs + IsRep0Long + (state << kNumPosBitsMax) + posState; ++ IF_BIT_0_CHECK(prob) ++ { ++ UPDATE_0_CHECK; ++ NORMALIZE_CHECK; ++ return DUMMY_REP; ++ } ++ else ++ { ++ UPDATE_1_CHECK; ++ } ++ } ++ else ++ { ++ UPDATE_1_CHECK; ++ prob = probs + IsRepG1 + state; ++ IF_BIT_0_CHECK(prob) ++ { ++ UPDATE_0_CHECK; ++ } ++ else ++ { ++ UPDATE_1_CHECK; ++ prob = probs + IsRepG2 + state; ++ IF_BIT_0_CHECK(prob) ++ { ++ UPDATE_0_CHECK; ++ } ++ else ++ { ++ UPDATE_1_CHECK; ++ } ++ } ++ } ++ state = kNumStates; ++ prob = probs + RepLenCoder; ++ } ++ { ++ unsigned limit, offset; ++ CLzmaProb *probLen = prob + LenChoice; ++ IF_BIT_0_CHECK(probLen) ++ { ++ UPDATE_0_CHECK; ++ probLen = prob + LenLow + (posState << kLenNumLowBits); ++ offset = 0; ++ limit = 1 << kLenNumLowBits; ++ } ++ else ++ { ++ UPDATE_1_CHECK; ++ probLen = prob + LenChoice2; ++ IF_BIT_0_CHECK(probLen) ++ { ++ UPDATE_0_CHECK; ++ probLen = prob + LenMid + (posState << kLenNumMidBits); ++ offset = kLenNumLowSymbols; ++ limit = 1 << kLenNumMidBits; ++ } ++ else ++ { ++ UPDATE_1_CHECK; ++ probLen = prob + LenHigh; ++ offset = kLenNumLowSymbols + kLenNumMidSymbols; ++ limit = 1 << kLenNumHighBits; ++ } ++ } ++ TREE_DECODE_CHECK(probLen, limit, len); ++ len += offset; ++ } ++ ++ if (state < 4) ++ { ++ unsigned posSlot; ++ prob = probs + PosSlot + ++ ((len < kNumLenToPosStates ? len : kNumLenToPosStates - 1) << ++ kNumPosSlotBits); ++ TREE_DECODE_CHECK(prob, 1 << kNumPosSlotBits, posSlot); ++ if (posSlot >= kStartPosModelIndex) ++ { ++ int numDirectBits = ((posSlot >> 1) - 1); ++ ++ /* if (bufLimit - buf >= 8) return DUMMY_MATCH; */ ++ ++ if (posSlot < kEndPosModelIndex) ++ { ++ prob = probs + SpecPos + ((2 | (posSlot & 1)) << numDirectBits) - posSlot - 1; ++ } ++ else ++ { ++ numDirectBits -= kNumAlignBits; ++ do ++ { ++ NORMALIZE_CHECK ++ range >>= 1; ++ code -= range & (((code - range) >> 31) - 1); ++ /* if (code >= range) code -= range; */ ++ } ++ while (--numDirectBits != 0); ++ prob = probs + Align; ++ numDirectBits = kNumAlignBits; ++ } ++ { ++ unsigned i = 1; ++ do ++ { ++ GET_BIT_CHECK(prob + i, i); ++ } ++ while (--numDirectBits != 0); ++ } ++ } ++ } ++ } ++ } ++ NORMALIZE_CHECK; ++ return res; ++} ++ ++ ++static void LzmaDec_InitRc(CLzmaDec *p, const Byte *data) ++{ ++ p->code = ((UInt32)data[1] << 24) | ((UInt32)data[2] << 16) | ((UInt32)data[3] << 8) | ((UInt32)data[4]); ++ p->range = 0xFFFFFFFF; ++ p->needFlush = 0; ++} ++ ++void LzmaDec_InitDicAndState(CLzmaDec *p, Bool initDic, Bool initState) ++{ ++ p->needFlush = 1; ++ p->remainLen = 0; ++ p->tempBufSize = 0; ++ ++ if (initDic) ++ { ++ p->processedPos = 0; ++ p->checkDicSize = 0; ++ p->needInitState = 1; ++ } ++ if (initState) ++ p->needInitState = 1; ++} ++ ++void LzmaDec_Init(CLzmaDec *p) ++{ ++ p->dicPos = 0; ++ LzmaDec_InitDicAndState(p, True, True); ++} ++ ++static void LzmaDec_InitStateReal(CLzmaDec *p) ++{ ++ UInt32 numProbs = Literal + ((UInt32)LZMA_LIT_SIZE << (p->prop.lc + p->prop.lp)); ++ UInt32 i; ++ CLzmaProb *probs = p->probs; ++ for (i = 0; i < numProbs; i++) ++ probs[i] = kBitModelTotal >> 1; ++ p->reps[0] = p->reps[1] = p->reps[2] = p->reps[3] = 1; ++ p->state = 0; ++ p->needInitState = 0; ++} ++ ++SRes LzmaDec_DecodeToDic(CLzmaDec *p, SizeT dicLimit, const Byte *src, SizeT *srcLen, ++ ELzmaFinishMode finishMode, ELzmaStatus *status) ++{ ++ SizeT inSize = *srcLen; ++ (*srcLen) = 0; ++ LzmaDec_WriteRem(p, dicLimit); ++ ++ *status = LZMA_STATUS_NOT_SPECIFIED; ++ ++ while (p->remainLen != kMatchSpecLenStart) ++ { ++ int checkEndMarkNow; ++ ++ if (p->needFlush != 0) ++ { ++ for (; inSize > 0 && p->tempBufSize < RC_INIT_SIZE; (*srcLen)++, inSize--) ++ p->tempBuf[p->tempBufSize++] = *src++; ++ if (p->tempBufSize < RC_INIT_SIZE) ++ { ++ *status = LZMA_STATUS_NEEDS_MORE_INPUT; ++ return SZ_OK; ++ } ++ if (p->tempBuf[0] != 0) ++ return SZ_ERROR_DATA; ++ ++ LzmaDec_InitRc(p, p->tempBuf); ++ p->tempBufSize = 0; ++ } ++ ++ checkEndMarkNow = 0; ++ if (p->dicPos >= dicLimit) ++ { ++ if (p->remainLen == 0 && p->code == 0) ++ { ++ *status = LZMA_STATUS_MAYBE_FINISHED_WITHOUT_MARK; ++ return SZ_OK; ++ } ++ if (finishMode == LZMA_FINISH_ANY) ++ { ++ *status = LZMA_STATUS_NOT_FINISHED; ++ return SZ_OK; ++ } ++ if (p->remainLen != 0) ++ { ++ *status = LZMA_STATUS_NOT_FINISHED; ++ return SZ_ERROR_DATA; ++ } ++ checkEndMarkNow = 1; ++ } ++ ++ if (p->needInitState) ++ LzmaDec_InitStateReal(p); ++ ++ if (p->tempBufSize == 0) ++ { ++ SizeT processed; ++ const Byte *bufLimit; ++ if (inSize < LZMA_REQUIRED_INPUT_MAX || checkEndMarkNow) ++ { ++ int dummyRes = LzmaDec_TryDummy(p, src, inSize); ++ if (dummyRes == DUMMY_ERROR) ++ { ++ memcpy(p->tempBuf, src, inSize); ++ p->tempBufSize = (unsigned)inSize; ++ (*srcLen) += inSize; ++ *status = LZMA_STATUS_NEEDS_MORE_INPUT; ++ return SZ_OK; ++ } ++ if (checkEndMarkNow && dummyRes != DUMMY_MATCH) ++ { ++ *status = LZMA_STATUS_NOT_FINISHED; ++ return SZ_ERROR_DATA; ++ } ++ bufLimit = src; ++ } ++ else ++ bufLimit = src + inSize - LZMA_REQUIRED_INPUT_MAX; ++ p->buf = src; ++ if (LzmaDec_DecodeReal2(p, dicLimit, bufLimit) != 0) ++ return SZ_ERROR_DATA; ++ processed = (SizeT)(p->buf - src); ++ (*srcLen) += processed; ++ src += processed; ++ inSize -= processed; ++ } ++ else ++ { ++ unsigned rem = p->tempBufSize, lookAhead = 0; ++ while (rem < LZMA_REQUIRED_INPUT_MAX && lookAhead < inSize) ++ p->tempBuf[rem++] = src[lookAhead++]; ++ p->tempBufSize = rem; ++ if (rem < LZMA_REQUIRED_INPUT_MAX || checkEndMarkNow) ++ { ++ int dummyRes = LzmaDec_TryDummy(p, p->tempBuf, rem); ++ if (dummyRes == DUMMY_ERROR) ++ { ++ (*srcLen) += lookAhead; ++ *status = LZMA_STATUS_NEEDS_MORE_INPUT; ++ return SZ_OK; ++ } ++ if (checkEndMarkNow && dummyRes != DUMMY_MATCH) ++ { ++ *status = LZMA_STATUS_NOT_FINISHED; ++ return SZ_ERROR_DATA; ++ } ++ } ++ p->buf = p->tempBuf; ++ if (LzmaDec_DecodeReal2(p, dicLimit, p->buf) != 0) ++ return SZ_ERROR_DATA; ++ lookAhead -= (rem - (unsigned)(p->buf - p->tempBuf)); ++ (*srcLen) += lookAhead; ++ src += lookAhead; ++ inSize -= lookAhead; ++ p->tempBufSize = 0; ++ } ++ } ++ if (p->code == 0) ++ *status = LZMA_STATUS_FINISHED_WITH_MARK; ++ return (p->code == 0) ? SZ_OK : SZ_ERROR_DATA; ++} ++ ++SRes LzmaDec_DecodeToBuf(CLzmaDec *p, Byte *dest, SizeT *destLen, const Byte *src, SizeT *srcLen, ELzmaFinishMode finishMode, ELzmaStatus *status) ++{ ++ SizeT outSize = *destLen; ++ SizeT inSize = *srcLen; ++ *srcLen = *destLen = 0; ++ for (;;) ++ { ++ SizeT inSizeCur = inSize, outSizeCur, dicPos; ++ ELzmaFinishMode curFinishMode; ++ SRes res; ++ if (p->dicPos == p->dicBufSize) ++ p->dicPos = 0; ++ dicPos = p->dicPos; ++ if (outSize > p->dicBufSize - dicPos) ++ { ++ outSizeCur = p->dicBufSize; ++ curFinishMode = LZMA_FINISH_ANY; ++ } ++ else ++ { ++ outSizeCur = dicPos + outSize; ++ curFinishMode = finishMode; ++ } ++ ++ res = LzmaDec_DecodeToDic(p, outSizeCur, src, &inSizeCur, curFinishMode, status); ++ src += inSizeCur; ++ inSize -= inSizeCur; ++ *srcLen += inSizeCur; ++ outSizeCur = p->dicPos - dicPos; ++ memcpy(dest, p->dic + dicPos, outSizeCur); ++ dest += outSizeCur; ++ outSize -= outSizeCur; ++ *destLen += outSizeCur; ++ if (res != 0) ++ return res; ++ if (outSizeCur == 0 || outSize == 0) ++ return SZ_OK; ++ } ++} ++ ++void LzmaDec_FreeProbs(CLzmaDec *p, ISzAlloc *alloc) ++{ ++ alloc->Free(alloc, p->probs); ++ p->probs = 0; ++} ++ ++static void LzmaDec_FreeDict(CLzmaDec *p, ISzAlloc *alloc) ++{ ++ alloc->Free(alloc, p->dic); ++ p->dic = 0; ++} ++ ++void LzmaDec_Free(CLzmaDec *p, ISzAlloc *alloc) ++{ ++ LzmaDec_FreeProbs(p, alloc); ++ LzmaDec_FreeDict(p, alloc); ++} ++ ++SRes LzmaProps_Decode(CLzmaProps *p, const Byte *data, unsigned size) ++{ ++ UInt32 dicSize; ++ Byte d; ++ ++ if (size < LZMA_PROPS_SIZE) ++ return SZ_ERROR_UNSUPPORTED; ++ else ++ dicSize = data[1] | ((UInt32)data[2] << 8) | ((UInt32)data[3] << 16) | ((UInt32)data[4] << 24); ++ ++ if (dicSize < LZMA_DIC_MIN) ++ dicSize = LZMA_DIC_MIN; ++ p->dicSize = dicSize; ++ ++ d = data[0]; ++ if (d >= (9 * 5 * 5)) ++ return SZ_ERROR_UNSUPPORTED; ++ ++ p->lc = d % 9; ++ d /= 9; ++ p->pb = d / 5; ++ p->lp = d % 5; ++ ++ return SZ_OK; ++} ++ ++static SRes LzmaDec_AllocateProbs2(CLzmaDec *p, const CLzmaProps *propNew, ISzAlloc *alloc) ++{ ++ UInt32 numProbs = LzmaProps_GetNumProbs(propNew); ++ if (p->probs == 0 || numProbs != p->numProbs) ++ { ++ LzmaDec_FreeProbs(p, alloc); ++ p->probs = (CLzmaProb *)alloc->Alloc(alloc, numProbs * sizeof(CLzmaProb)); ++ p->numProbs = numProbs; ++ if (p->probs == 0) ++ return SZ_ERROR_MEM; ++ } ++ return SZ_OK; ++} ++ ++SRes LzmaDec_AllocateProbs(CLzmaDec *p, const Byte *props, unsigned propsSize, ISzAlloc *alloc) ++{ ++ CLzmaProps propNew; ++ RINOK(LzmaProps_Decode(&propNew, props, propsSize)); ++ RINOK(LzmaDec_AllocateProbs2(p, &propNew, alloc)); ++ p->prop = propNew; ++ return SZ_OK; ++} ++ ++SRes LzmaDec_Allocate(CLzmaDec *p, const Byte *props, unsigned propsSize, ISzAlloc *alloc) ++{ ++ CLzmaProps propNew; ++ SizeT dicBufSize; ++ RINOK(LzmaProps_Decode(&propNew, props, propsSize)); ++ RINOK(LzmaDec_AllocateProbs2(p, &propNew, alloc)); ++ dicBufSize = propNew.dicSize; ++ if (p->dic == 0 || dicBufSize != p->dicBufSize) ++ { ++ LzmaDec_FreeDict(p, alloc); ++ p->dic = (Byte *)alloc->Alloc(alloc, dicBufSize); ++ if (p->dic == 0) ++ { ++ LzmaDec_FreeProbs(p, alloc); ++ return SZ_ERROR_MEM; ++ } ++ } ++ p->dicBufSize = dicBufSize; ++ p->prop = propNew; ++ return SZ_OK; ++} ++ ++SRes LzmaDecode(Byte *dest, SizeT *destLen, const Byte *src, SizeT *srcLen, ++ const Byte *propData, unsigned propSize, ELzmaFinishMode finishMode, ++ ELzmaStatus *status, ISzAlloc *alloc) ++{ ++ CLzmaDec p; ++ SRes res; ++ SizeT inSize = *srcLen; ++ SizeT outSize = *destLen; ++ *srcLen = *destLen = 0; ++ if (inSize < RC_INIT_SIZE) ++ return SZ_ERROR_INPUT_EOF; ++ ++ LzmaDec_Construct(&p); ++ res = LzmaDec_AllocateProbs(&p, propData, propSize, alloc); ++ if (res != 0) ++ return res; ++ p.dic = dest; ++ p.dicBufSize = outSize; ++ ++ LzmaDec_Init(&p); ++ ++ *srcLen = inSize; ++ res = LzmaDec_DecodeToDic(&p, outSize, src, srcLen, finishMode, status); ++ ++ if (res == SZ_OK && *status == LZMA_STATUS_NEEDS_MORE_INPUT) ++ res = SZ_ERROR_INPUT_EOF; ++ ++ (*destLen) = p.dicPos; ++ LzmaDec_FreeProbs(&p, alloc); ++ return res; ++} +--- /dev/null ++++ b/lib/lzma/LzmaEnc.c +@@ -0,0 +1,2271 @@ ++/* LzmaEnc.c -- LZMA Encoder ++2009-11-24 : Igor Pavlov : Public domain */ ++ ++#include <string.h> ++ ++/* #define SHOW_STAT */ ++/* #define SHOW_STAT2 */ ++ ++#if defined(SHOW_STAT) || defined(SHOW_STAT2) ++#include <stdio.h> ++#endif ++ ++#include "LzmaEnc.h" ++ ++/* disable MT */ ++#define _7ZIP_ST ++ ++#include "LzFind.h" ++#ifndef _7ZIP_ST ++#include "LzFindMt.h" ++#endif ++ ++#ifdef SHOW_STAT ++static int ttt = 0; ++#endif ++ ++#define kBlockSizeMax ((1 << LZMA_NUM_BLOCK_SIZE_BITS) - 1) ++ ++#define kBlockSize (9 << 10) ++#define kUnpackBlockSize (1 << 18) ++#define kMatchArraySize (1 << 21) ++#define kMatchRecordMaxSize ((LZMA_MATCH_LEN_MAX * 2 + 3) * LZMA_MATCH_LEN_MAX) ++ ++#define kNumMaxDirectBits (31) ++ ++#define kNumTopBits 24 ++#define kTopValue ((UInt32)1 << kNumTopBits) ++ ++#define kNumBitModelTotalBits 11 ++#define kBitModelTotal (1 << kNumBitModelTotalBits) ++#define kNumMoveBits 5 ++#define kProbInitValue (kBitModelTotal >> 1) ++ ++#define kNumMoveReducingBits 4 ++#define kNumBitPriceShiftBits 4 ++#define kBitPrice (1 << kNumBitPriceShiftBits) ++ ++void LzmaEncProps_Init(CLzmaEncProps *p) ++{ ++ p->level = 5; ++ p->dictSize = p->mc = 0; ++ p->lc = p->lp = p->pb = p->algo = p->fb = p->btMode = p->numHashBytes = p->numThreads = -1; ++ p->writeEndMark = 0; ++} ++ ++void LzmaEncProps_Normalize(CLzmaEncProps *p) ++{ ++ int level = p->level; ++ if (level < 0) level = 5; ++ p->level = level; ++ if (p->dictSize == 0) p->dictSize = (level <= 5 ? (1 << (level * 2 + 14)) : (level == 6 ? (1 << 25) : (1 << 26))); ++ if (p->lc < 0) p->lc = 3; ++ if (p->lp < 0) p->lp = 0; ++ if (p->pb < 0) p->pb = 2; ++ if (p->algo < 0) p->algo = (level < 5 ? 0 : 1); ++ if (p->fb < 0) p->fb = (level < 7 ? 32 : 64); ++ if (p->btMode < 0) p->btMode = (p->algo == 0 ? 0 : 1); ++ if (p->numHashBytes < 0) p->numHashBytes = 4; ++ if (p->mc == 0) p->mc = (16 + (p->fb >> 1)) >> (p->btMode ? 0 : 1); ++ if (p->numThreads < 0) ++ p->numThreads = ++ #ifndef _7ZIP_ST ++ ((p->btMode && p->algo) ? 2 : 1); ++ #else ++ 1; ++ #endif ++} ++ ++UInt32 LzmaEncProps_GetDictSize(const CLzmaEncProps *props2) ++{ ++ CLzmaEncProps props = *props2; ++ LzmaEncProps_Normalize(&props); ++ return props.dictSize; ++} ++ ++/* #define LZMA_LOG_BSR */ ++/* Define it for Intel's CPU */ ++ ++ ++#ifdef LZMA_LOG_BSR ++ ++#define kDicLogSizeMaxCompress 30 ++ ++#define BSR2_RET(pos, res) { unsigned long i; _BitScanReverse(&i, (pos)); res = (i + i) + ((pos >> (i - 1)) & 1); } ++ ++UInt32 GetPosSlot1(UInt32 pos) ++{ ++ UInt32 res; ++ BSR2_RET(pos, res); ++ return res; ++} ++#define GetPosSlot2(pos, res) { BSR2_RET(pos, res); } ++#define GetPosSlot(pos, res) { if (pos < 2) res = pos; else BSR2_RET(pos, res); } ++ ++#else ++ ++#define kNumLogBits (9 + (int)sizeof(size_t) / 2) ++#define kDicLogSizeMaxCompress ((kNumLogBits - 1) * 2 + 7) ++ ++void LzmaEnc_FastPosInit(Byte *g_FastPos) ++{ ++ int c = 2, slotFast; ++ g_FastPos[0] = 0; ++ g_FastPos[1] = 1; ++ ++ for (slotFast = 2; slotFast < kNumLogBits * 2; slotFast++) ++ { ++ UInt32 k = (1 << ((slotFast >> 1) - 1)); ++ UInt32 j; ++ for (j = 0; j < k; j++, c++) ++ g_FastPos[c] = (Byte)slotFast; ++ } ++} ++ ++#define BSR2_RET(pos, res) { UInt32 i = 6 + ((kNumLogBits - 1) & \ ++ (0 - (((((UInt32)1 << (kNumLogBits + 6)) - 1) - pos) >> 31))); \ ++ res = p->g_FastPos[pos >> i] + (i * 2); } ++/* ++#define BSR2_RET(pos, res) { res = (pos < (1 << (kNumLogBits + 6))) ? \ ++ p->g_FastPos[pos >> 6] + 12 : \ ++ p->g_FastPos[pos >> (6 + kNumLogBits - 1)] + (6 + (kNumLogBits - 1)) * 2; } ++*/ ++ ++#define GetPosSlot1(pos) p->g_FastPos[pos] ++#define GetPosSlot2(pos, res) { BSR2_RET(pos, res); } ++#define GetPosSlot(pos, res) { if (pos < kNumFullDistances) res = p->g_FastPos[pos]; else BSR2_RET(pos, res); } ++ ++#endif ++ ++ ++#define LZMA_NUM_REPS 4 ++ ++typedef unsigned CState; ++ ++typedef struct ++{ ++ UInt32 price; ++ ++ CState state; ++ int prev1IsChar; ++ int prev2; ++ ++ UInt32 posPrev2; ++ UInt32 backPrev2; ++ ++ UInt32 posPrev; ++ UInt32 backPrev; ++ UInt32 backs[LZMA_NUM_REPS]; ++} COptimal; ++ ++#define kNumOpts (1 << 12) ++ ++#define kNumLenToPosStates 4 ++#define kNumPosSlotBits 6 ++#define kDicLogSizeMin 0 ++#define kDicLogSizeMax 32 ++#define kDistTableSizeMax (kDicLogSizeMax * 2) ++ ++ ++#define kNumAlignBits 4 ++#define kAlignTableSize (1 << kNumAlignBits) ++#define kAlignMask (kAlignTableSize - 1) ++ ++#define kStartPosModelIndex 4 ++#define kEndPosModelIndex 14 ++#define kNumPosModels (kEndPosModelIndex - kStartPosModelIndex) ++ ++#define kNumFullDistances (1 << (kEndPosModelIndex >> 1)) ++ ++#ifdef _LZMA_PROB32 ++#define CLzmaProb UInt32 ++#else ++#define CLzmaProb UInt16 ++#endif ++ ++#define LZMA_PB_MAX 4 ++#define LZMA_LC_MAX 8 ++#define LZMA_LP_MAX 4 ++ ++#define LZMA_NUM_PB_STATES_MAX (1 << LZMA_PB_MAX) ++ ++ ++#define kLenNumLowBits 3 ++#define kLenNumLowSymbols (1 << kLenNumLowBits) ++#define kLenNumMidBits 3 ++#define kLenNumMidSymbols (1 << kLenNumMidBits) ++#define kLenNumHighBits 8 ++#define kLenNumHighSymbols (1 << kLenNumHighBits) ++ ++#define kLenNumSymbolsTotal (kLenNumLowSymbols + kLenNumMidSymbols + kLenNumHighSymbols) ++ ++#define LZMA_MATCH_LEN_MIN 2 ++#define LZMA_MATCH_LEN_MAX (LZMA_MATCH_LEN_MIN + kLenNumSymbolsTotal - 1) ++ ++#define kNumStates 12 ++ ++typedef struct ++{ ++ CLzmaProb choice; ++ CLzmaProb choice2; ++ CLzmaProb low[LZMA_NUM_PB_STATES_MAX << kLenNumLowBits]; ++ CLzmaProb mid[LZMA_NUM_PB_STATES_MAX << kLenNumMidBits]; ++ CLzmaProb high[kLenNumHighSymbols]; ++} CLenEnc; ++ ++typedef struct ++{ ++ CLenEnc p; ++ UInt32 prices[LZMA_NUM_PB_STATES_MAX][kLenNumSymbolsTotal]; ++ UInt32 tableSize; ++ UInt32 counters[LZMA_NUM_PB_STATES_MAX]; ++} CLenPriceEnc; ++ ++typedef struct ++{ ++ UInt32 range; ++ Byte cache; ++ UInt64 low; ++ UInt64 cacheSize; ++ Byte *buf; ++ Byte *bufLim; ++ Byte *bufBase; ++ ISeqOutStream *outStream; ++ UInt64 processed; ++ SRes res; ++} CRangeEnc; ++ ++typedef struct ++{ ++ CLzmaProb *litProbs; ++ ++ CLzmaProb isMatch[kNumStates][LZMA_NUM_PB_STATES_MAX]; ++ CLzmaProb isRep[kNumStates]; ++ CLzmaProb isRepG0[kNumStates]; ++ CLzmaProb isRepG1[kNumStates]; ++ CLzmaProb isRepG2[kNumStates]; ++ CLzmaProb isRep0Long[kNumStates][LZMA_NUM_PB_STATES_MAX]; ++ ++ CLzmaProb posSlotEncoder[kNumLenToPosStates][1 << kNumPosSlotBits]; ++ CLzmaProb posEncoders[kNumFullDistances - kEndPosModelIndex]; ++ CLzmaProb posAlignEncoder[1 << kNumAlignBits]; ++ ++ CLenPriceEnc lenEnc; ++ CLenPriceEnc repLenEnc; ++ ++ UInt32 reps[LZMA_NUM_REPS]; ++ UInt32 state; ++} CSaveState; ++ ++typedef struct ++{ ++ IMatchFinder matchFinder; ++ void *matchFinderObj; ++ ++ #ifndef _7ZIP_ST ++ Bool mtMode; ++ CMatchFinderMt matchFinderMt; ++ #endif ++ ++ CMatchFinder matchFinderBase; ++ ++ #ifndef _7ZIP_ST ++ Byte pad[128]; ++ #endif ++ ++ UInt32 optimumEndIndex; ++ UInt32 optimumCurrentIndex; ++ ++ UInt32 longestMatchLength; ++ UInt32 numPairs; ++ UInt32 numAvail; ++ COptimal opt[kNumOpts]; ++ ++ #ifndef LZMA_LOG_BSR ++ Byte g_FastPos[1 << kNumLogBits]; ++ #endif ++ ++ UInt32 ProbPrices[kBitModelTotal >> kNumMoveReducingBits]; ++ UInt32 matches[LZMA_MATCH_LEN_MAX * 2 + 2 + 1]; ++ UInt32 numFastBytes; ++ UInt32 additionalOffset; ++ UInt32 reps[LZMA_NUM_REPS]; ++ UInt32 state; ++ ++ UInt32 posSlotPrices[kNumLenToPosStates][kDistTableSizeMax]; ++ UInt32 distancesPrices[kNumLenToPosStates][kNumFullDistances]; ++ UInt32 alignPrices[kAlignTableSize]; ++ UInt32 alignPriceCount; ++ ++ UInt32 distTableSize; ++ ++ unsigned lc, lp, pb; ++ unsigned lpMask, pbMask; ++ ++ CLzmaProb *litProbs; ++ ++ CLzmaProb isMatch[kNumStates][LZMA_NUM_PB_STATES_MAX]; ++ CLzmaProb isRep[kNumStates]; ++ CLzmaProb isRepG0[kNumStates]; ++ CLzmaProb isRepG1[kNumStates]; ++ CLzmaProb isRepG2[kNumStates]; ++ CLzmaProb isRep0Long[kNumStates][LZMA_NUM_PB_STATES_MAX]; ++ ++ CLzmaProb posSlotEncoder[kNumLenToPosStates][1 << kNumPosSlotBits]; ++ CLzmaProb posEncoders[kNumFullDistances - kEndPosModelIndex]; ++ CLzmaProb posAlignEncoder[1 << kNumAlignBits]; ++ ++ CLenPriceEnc lenEnc; ++ CLenPriceEnc repLenEnc; ++ ++ unsigned lclp; ++ ++ Bool fastMode; ++ ++ CRangeEnc rc; ++ ++ Bool writeEndMark; ++ UInt64 nowPos64; ++ UInt32 matchPriceCount; ++ Bool finished; ++ Bool multiThread; ++ ++ SRes result; ++ UInt32 dictSize; ++ UInt32 matchFinderCycles; ++ ++ int needInit; ++ ++ CSaveState saveState; ++} CLzmaEnc; ++ ++void LzmaEnc_SaveState(CLzmaEncHandle pp) ++{ ++ CLzmaEnc *p = (CLzmaEnc *)pp; ++ CSaveState *dest = &p->saveState; ++ int i; ++ dest->lenEnc = p->lenEnc; ++ dest->repLenEnc = p->repLenEnc; ++ dest->state = p->state; ++ ++ for (i = 0; i < kNumStates; i++) ++ { ++ memcpy(dest->isMatch[i], p->isMatch[i], sizeof(p->isMatch[i])); ++ memcpy(dest->isRep0Long[i], p->isRep0Long[i], sizeof(p->isRep0Long[i])); ++ } ++ for (i = 0; i < kNumLenToPosStates; i++) ++ memcpy(dest->posSlotEncoder[i], p->posSlotEncoder[i], sizeof(p->posSlotEncoder[i])); ++ memcpy(dest->isRep, p->isRep, sizeof(p->isRep)); ++ memcpy(dest->isRepG0, p->isRepG0, sizeof(p->isRepG0)); ++ memcpy(dest->isRepG1, p->isRepG1, sizeof(p->isRepG1)); ++ memcpy(dest->isRepG2, p->isRepG2, sizeof(p->isRepG2)); ++ memcpy(dest->posEncoders, p->posEncoders, sizeof(p->posEncoders)); ++ memcpy(dest->posAlignEncoder, p->posAlignEncoder, sizeof(p->posAlignEncoder)); ++ memcpy(dest->reps, p->reps, sizeof(p->reps)); ++ memcpy(dest->litProbs, p->litProbs, (0x300 << p->lclp) * sizeof(CLzmaProb)); ++} ++ ++void LzmaEnc_RestoreState(CLzmaEncHandle pp) ++{ ++ CLzmaEnc *dest = (CLzmaEnc *)pp; ++ const CSaveState *p = &dest->saveState; ++ int i; ++ dest->lenEnc = p->lenEnc; ++ dest->repLenEnc = p->repLenEnc; ++ dest->state = p->state; ++ ++ for (i = 0; i < kNumStates; i++) ++ { ++ memcpy(dest->isMatch[i], p->isMatch[i], sizeof(p->isMatch[i])); ++ memcpy(dest->isRep0Long[i], p->isRep0Long[i], sizeof(p->isRep0Long[i])); ++ } ++ for (i = 0; i < kNumLenToPosStates; i++) ++ memcpy(dest->posSlotEncoder[i], p->posSlotEncoder[i], sizeof(p->posSlotEncoder[i])); ++ memcpy(dest->isRep, p->isRep, sizeof(p->isRep)); ++ memcpy(dest->isRepG0, p->isRepG0, sizeof(p->isRepG0)); ++ memcpy(dest->isRepG1, p->isRepG1, sizeof(p->isRepG1)); ++ memcpy(dest->isRepG2, p->isRepG2, sizeof(p->isRepG2)); ++ memcpy(dest->posEncoders, p->posEncoders, sizeof(p->posEncoders)); ++ memcpy(dest->posAlignEncoder, p->posAlignEncoder, sizeof(p->posAlignEncoder)); ++ memcpy(dest->reps, p->reps, sizeof(p->reps)); ++ memcpy(dest->litProbs, p->litProbs, (0x300 << dest->lclp) * sizeof(CLzmaProb)); ++} ++ ++SRes LzmaEnc_SetProps(CLzmaEncHandle pp, const CLzmaEncProps *props2) ++{ ++ CLzmaEnc *p = (CLzmaEnc *)pp; ++ CLzmaEncProps props = *props2; ++ LzmaEncProps_Normalize(&props); ++ ++ if (props.lc > LZMA_LC_MAX || props.lp > LZMA_LP_MAX || props.pb > LZMA_PB_MAX || ++ props.dictSize > (1 << kDicLogSizeMaxCompress) || props.dictSize > (1 << 30)) ++ return SZ_ERROR_PARAM; ++ p->dictSize = props.dictSize; ++ p->matchFinderCycles = props.mc; ++ { ++ unsigned fb = props.fb; ++ if (fb < 5) ++ fb = 5; ++ if (fb > LZMA_MATCH_LEN_MAX) ++ fb = LZMA_MATCH_LEN_MAX; ++ p->numFastBytes = fb; ++ } ++ p->lc = props.lc; ++ p->lp = props.lp; ++ p->pb = props.pb; ++ p->fastMode = (props.algo == 0); ++ p->matchFinderBase.btMode = props.btMode; ++ { ++ UInt32 numHashBytes = 4; ++ if (props.btMode) ++ { ++ if (props.numHashBytes < 2) ++ numHashBytes = 2; ++ else if (props.numHashBytes < 4) ++ numHashBytes = props.numHashBytes; ++ } ++ p->matchFinderBase.numHashBytes = numHashBytes; ++ } ++ ++ p->matchFinderBase.cutValue = props.mc; ++ ++ p->writeEndMark = props.writeEndMark; ++ ++ #ifndef _7ZIP_ST ++ /* ++ if (newMultiThread != _multiThread) ++ { ++ ReleaseMatchFinder(); ++ _multiThread = newMultiThread; ++ } ++ */ ++ p->multiThread = (props.numThreads > 1); ++ #endif ++ ++ return SZ_OK; ++} ++ ++static const int kLiteralNextStates[kNumStates] = {0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 4, 5}; ++static const int kMatchNextStates[kNumStates] = {7, 7, 7, 7, 7, 7, 7, 10, 10, 10, 10, 10}; ++static const int kRepNextStates[kNumStates] = {8, 8, 8, 8, 8, 8, 8, 11, 11, 11, 11, 11}; ++static const int kShortRepNextStates[kNumStates]= {9, 9, 9, 9, 9, 9, 9, 11, 11, 11, 11, 11}; ++ ++#define IsCharState(s) ((s) < 7) ++ ++#define GetLenToPosState(len) (((len) < kNumLenToPosStates + 1) ? (len) - 2 : kNumLenToPosStates - 1) ++ ++#define kInfinityPrice (1 << 30) ++ ++static void RangeEnc_Construct(CRangeEnc *p) ++{ ++ p->outStream = 0; ++ p->bufBase = 0; ++} ++ ++#define RangeEnc_GetProcessed(p) ((p)->processed + ((p)->buf - (p)->bufBase) + (p)->cacheSize) ++ ++#define RC_BUF_SIZE (1 << 16) ++static int RangeEnc_Alloc(CRangeEnc *p, ISzAlloc *alloc) ++{ ++ if (p->bufBase == 0) ++ { ++ p->bufBase = (Byte *)alloc->Alloc(alloc, RC_BUF_SIZE); ++ if (p->bufBase == 0) ++ return 0; ++ p->bufLim = p->bufBase + RC_BUF_SIZE; ++ } ++ return 1; ++} ++ ++static void RangeEnc_Free(CRangeEnc *p, ISzAlloc *alloc) ++{ ++ alloc->Free(alloc, p->bufBase); ++ p->bufBase = 0; ++} ++ ++static void RangeEnc_Init(CRangeEnc *p) ++{ ++ /* Stream.Init(); */ ++ p->low = 0; ++ p->range = 0xFFFFFFFF; ++ p->cacheSize = 1; ++ p->cache = 0; ++ ++ p->buf = p->bufBase; ++ ++ p->processed = 0; ++ p->res = SZ_OK; ++} ++ ++static void RangeEnc_FlushStream(CRangeEnc *p) ++{ ++ size_t num; ++ if (p->res != SZ_OK) ++ return; ++ num = p->buf - p->bufBase; ++ if (num != p->outStream->Write(p->outStream, p->bufBase, num)) ++ p->res = SZ_ERROR_WRITE; ++ p->processed += num; ++ p->buf = p->bufBase; ++} ++ ++static void MY_FAST_CALL RangeEnc_ShiftLow(CRangeEnc *p) ++{ ++ if ((UInt32)p->low < (UInt32)0xFF000000 || (int)(p->low >> 32) != 0) ++ { ++ Byte temp = p->cache; ++ do ++ { ++ Byte *buf = p->buf; ++ *buf++ = (Byte)(temp + (Byte)(p->low >> 32)); ++ p->buf = buf; ++ if (buf == p->bufLim) ++ RangeEnc_FlushStream(p); ++ temp = 0xFF; ++ } ++ while (--p->cacheSize != 0); ++ p->cache = (Byte)((UInt32)p->low >> 24); ++ } ++ p->cacheSize++; ++ p->low = (UInt32)p->low << 8; ++} ++ ++static void RangeEnc_FlushData(CRangeEnc *p) ++{ ++ int i; ++ for (i = 0; i < 5; i++) ++ RangeEnc_ShiftLow(p); ++} ++ ++static void RangeEnc_EncodeDirectBits(CRangeEnc *p, UInt32 value, int numBits) ++{ ++ do ++ { ++ p->range >>= 1; ++ p->low += p->range & (0 - ((value >> --numBits) & 1)); ++ if (p->range < kTopValue) ++ { ++ p->range <<= 8; ++ RangeEnc_ShiftLow(p); ++ } ++ } ++ while (numBits != 0); ++} ++ ++static void RangeEnc_EncodeBit(CRangeEnc *p, CLzmaProb *prob, UInt32 symbol) ++{ ++ UInt32 ttt = *prob; ++ UInt32 newBound = (p->range >> kNumBitModelTotalBits) * ttt; ++ if (symbol == 0) ++ { ++ p->range = newBound; ++ ttt += (kBitModelTotal - ttt) >> kNumMoveBits; ++ } ++ else ++ { ++ p->low += newBound; ++ p->range -= newBound; ++ ttt -= ttt >> kNumMoveBits; ++ } ++ *prob = (CLzmaProb)ttt; ++ if (p->range < kTopValue) ++ { ++ p->range <<= 8; ++ RangeEnc_ShiftLow(p); ++ } ++} ++ ++static void LitEnc_Encode(CRangeEnc *p, CLzmaProb *probs, UInt32 symbol) ++{ ++ symbol |= 0x100; ++ do ++ { ++ RangeEnc_EncodeBit(p, probs + (symbol >> 8), (symbol >> 7) & 1); ++ symbol <<= 1; ++ } ++ while (symbol < 0x10000); ++} ++ ++static void LitEnc_EncodeMatched(CRangeEnc *p, CLzmaProb *probs, UInt32 symbol, UInt32 matchByte) ++{ ++ UInt32 offs = 0x100; ++ symbol |= 0x100; ++ do ++ { ++ matchByte <<= 1; ++ RangeEnc_EncodeBit(p, probs + (offs + (matchByte & offs) + (symbol >> 8)), (symbol >> 7) & 1); ++ symbol <<= 1; ++ offs &= ~(matchByte ^ symbol); ++ } ++ while (symbol < 0x10000); ++} ++ ++void LzmaEnc_InitPriceTables(UInt32 *ProbPrices) ++{ ++ UInt32 i; ++ for (i = (1 << kNumMoveReducingBits) / 2; i < kBitModelTotal; i += (1 << kNumMoveReducingBits)) ++ { ++ const int kCyclesBits = kNumBitPriceShiftBits; ++ UInt32 w = i; ++ UInt32 bitCount = 0; ++ int j; ++ for (j = 0; j < kCyclesBits; j++) ++ { ++ w = w * w; ++ bitCount <<= 1; ++ while (w >= ((UInt32)1 << 16)) ++ { ++ w >>= 1; ++ bitCount++; ++ } ++ } ++ ProbPrices[i >> kNumMoveReducingBits] = ((kNumBitModelTotalBits << kCyclesBits) - 15 - bitCount); ++ } ++} ++ ++ ++#define GET_PRICE(prob, symbol) \ ++ p->ProbPrices[((prob) ^ (((-(int)(symbol))) & (kBitModelTotal - 1))) >> kNumMoveReducingBits]; ++ ++#define GET_PRICEa(prob, symbol) \ ++ ProbPrices[((prob) ^ ((-((int)(symbol))) & (kBitModelTotal - 1))) >> kNumMoveReducingBits]; ++ ++#define GET_PRICE_0(prob) p->ProbPrices[(prob) >> kNumMoveReducingBits] ++#define GET_PRICE_1(prob) p->ProbPrices[((prob) ^ (kBitModelTotal - 1)) >> kNumMoveReducingBits] ++ ++#define GET_PRICE_0a(prob) ProbPrices[(prob) >> kNumMoveReducingBits] ++#define GET_PRICE_1a(prob) ProbPrices[((prob) ^ (kBitModelTotal - 1)) >> kNumMoveReducingBits] ++ ++static UInt32 LitEnc_GetPrice(const CLzmaProb *probs, UInt32 symbol, UInt32 *ProbPrices) ++{ ++ UInt32 price = 0; ++ symbol |= 0x100; ++ do ++ { ++ price += GET_PRICEa(probs[symbol >> 8], (symbol >> 7) & 1); ++ symbol <<= 1; ++ } ++ while (symbol < 0x10000); ++ return price; ++} ++ ++static UInt32 LitEnc_GetPriceMatched(const CLzmaProb *probs, UInt32 symbol, UInt32 matchByte, UInt32 *ProbPrices) ++{ ++ UInt32 price = 0; ++ UInt32 offs = 0x100; ++ symbol |= 0x100; ++ do ++ { ++ matchByte <<= 1; ++ price += GET_PRICEa(probs[offs + (matchByte & offs) + (symbol >> 8)], (symbol >> 7) & 1); ++ symbol <<= 1; ++ offs &= ~(matchByte ^ symbol); ++ } ++ while (symbol < 0x10000); ++ return price; ++} ++ ++ ++static void RcTree_Encode(CRangeEnc *rc, CLzmaProb *probs, int numBitLevels, UInt32 symbol) ++{ ++ UInt32 m = 1; ++ int i; ++ for (i = numBitLevels; i != 0;) ++ { ++ UInt32 bit; ++ i--; ++ bit = (symbol >> i) & 1; ++ RangeEnc_EncodeBit(rc, probs + m, bit); ++ m = (m << 1) | bit; ++ } ++} ++ ++static void RcTree_ReverseEncode(CRangeEnc *rc, CLzmaProb *probs, int numBitLevels, UInt32 symbol) ++{ ++ UInt32 m = 1; ++ int i; ++ for (i = 0; i < numBitLevels; i++) ++ { ++ UInt32 bit = symbol & 1; ++ RangeEnc_EncodeBit(rc, probs + m, bit); ++ m = (m << 1) | bit; ++ symbol >>= 1; ++ } ++} ++ ++static UInt32 RcTree_GetPrice(const CLzmaProb *probs, int numBitLevels, UInt32 symbol, UInt32 *ProbPrices) ++{ ++ UInt32 price = 0; ++ symbol |= (1 << numBitLevels); ++ while (symbol != 1) ++ { ++ price += GET_PRICEa(probs[symbol >> 1], symbol & 1); ++ symbol >>= 1; ++ } ++ return price; ++} ++ ++static UInt32 RcTree_ReverseGetPrice(const CLzmaProb *probs, int numBitLevels, UInt32 symbol, UInt32 *ProbPrices) ++{ ++ UInt32 price = 0; ++ UInt32 m = 1; ++ int i; ++ for (i = numBitLevels; i != 0; i--) ++ { ++ UInt32 bit = symbol & 1; ++ symbol >>= 1; ++ price += GET_PRICEa(probs[m], bit); ++ m = (m << 1) | bit; ++ } ++ return price; ++} ++ ++ ++static void LenEnc_Init(CLenEnc *p) ++{ ++ unsigned i; ++ p->choice = p->choice2 = kProbInitValue; ++ for (i = 0; i < (LZMA_NUM_PB_STATES_MAX << kLenNumLowBits); i++) ++ p->low[i] = kProbInitValue; ++ for (i = 0; i < (LZMA_NUM_PB_STATES_MAX << kLenNumMidBits); i++) ++ p->mid[i] = kProbInitValue; ++ for (i = 0; i < kLenNumHighSymbols; i++) ++ p->high[i] = kProbInitValue; ++} ++ ++static void LenEnc_Encode(CLenEnc *p, CRangeEnc *rc, UInt32 symbol, UInt32 posState) ++{ ++ if (symbol < kLenNumLowSymbols) ++ { ++ RangeEnc_EncodeBit(rc, &p->choice, 0); ++ RcTree_Encode(rc, p->low + (posState << kLenNumLowBits), kLenNumLowBits, symbol); ++ } ++ else ++ { ++ RangeEnc_EncodeBit(rc, &p->choice, 1); ++ if (symbol < kLenNumLowSymbols + kLenNumMidSymbols) ++ { ++ RangeEnc_EncodeBit(rc, &p->choice2, 0); ++ RcTree_Encode(rc, p->mid + (posState << kLenNumMidBits), kLenNumMidBits, symbol - kLenNumLowSymbols); ++ } ++ else ++ { ++ RangeEnc_EncodeBit(rc, &p->choice2, 1); ++ RcTree_Encode(rc, p->high, kLenNumHighBits, symbol - kLenNumLowSymbols - kLenNumMidSymbols); ++ } ++ } ++} ++ ++static void LenEnc_SetPrices(CLenEnc *p, UInt32 posState, UInt32 numSymbols, UInt32 *prices, UInt32 *ProbPrices) ++{ ++ UInt32 a0 = GET_PRICE_0a(p->choice); ++ UInt32 a1 = GET_PRICE_1a(p->choice); ++ UInt32 b0 = a1 + GET_PRICE_0a(p->choice2); ++ UInt32 b1 = a1 + GET_PRICE_1a(p->choice2); ++ UInt32 i = 0; ++ for (i = 0; i < kLenNumLowSymbols; i++) ++ { ++ if (i >= numSymbols) ++ return; ++ prices[i] = a0 + RcTree_GetPrice(p->low + (posState << kLenNumLowBits), kLenNumLowBits, i, ProbPrices); ++ } ++ for (; i < kLenNumLowSymbols + kLenNumMidSymbols; i++) ++ { ++ if (i >= numSymbols) ++ return; ++ prices[i] = b0 + RcTree_GetPrice(p->mid + (posState << kLenNumMidBits), kLenNumMidBits, i - kLenNumLowSymbols, ProbPrices); ++ } ++ for (; i < numSymbols; i++) ++ prices[i] = b1 + RcTree_GetPrice(p->high, kLenNumHighBits, i - kLenNumLowSymbols - kLenNumMidSymbols, ProbPrices); ++} ++ ++static void MY_FAST_CALL LenPriceEnc_UpdateTable(CLenPriceEnc *p, UInt32 posState, UInt32 *ProbPrices) ++{ ++ LenEnc_SetPrices(&p->p, posState, p->tableSize, p->prices[posState], ProbPrices); ++ p->counters[posState] = p->tableSize; ++} ++ ++static void LenPriceEnc_UpdateTables(CLenPriceEnc *p, UInt32 numPosStates, UInt32 *ProbPrices) ++{ ++ UInt32 posState; ++ for (posState = 0; posState < numPosStates; posState++) ++ LenPriceEnc_UpdateTable(p, posState, ProbPrices); ++} ++ ++static void LenEnc_Encode2(CLenPriceEnc *p, CRangeEnc *rc, UInt32 symbol, UInt32 posState, Bool updatePrice, UInt32 *ProbPrices) ++{ ++ LenEnc_Encode(&p->p, rc, symbol, posState); ++ if (updatePrice) ++ if (--p->counters[posState] == 0) ++ LenPriceEnc_UpdateTable(p, posState, ProbPrices); ++} ++ ++ ++ ++ ++static void MovePos(CLzmaEnc *p, UInt32 num) ++{ ++ #ifdef SHOW_STAT ++ ttt += num; ++ printf("\n MovePos %d", num); ++ #endif ++ if (num != 0) ++ { ++ p->additionalOffset += num; ++ p->matchFinder.Skip(p->matchFinderObj, num); ++ } ++} ++ ++static UInt32 ReadMatchDistances(CLzmaEnc *p, UInt32 *numDistancePairsRes) ++{ ++ UInt32 lenRes = 0, numPairs; ++ p->numAvail = p->matchFinder.GetNumAvailableBytes(p->matchFinderObj); ++ numPairs = p->matchFinder.GetMatches(p->matchFinderObj, p->matches); ++ #ifdef SHOW_STAT ++ printf("\n i = %d numPairs = %d ", ttt, numPairs / 2); ++ ttt++; ++ { ++ UInt32 i; ++ for (i = 0; i < numPairs; i += 2) ++ printf("%2d %6d | ", p->matches[i], p->matches[i + 1]); ++ } ++ #endif ++ if (numPairs > 0) ++ { ++ lenRes = p->matches[numPairs - 2]; ++ if (lenRes == p->numFastBytes) ++ { ++ const Byte *pby = p->matchFinder.GetPointerToCurrentPos(p->matchFinderObj) - 1; ++ UInt32 distance = p->matches[numPairs - 1] + 1; ++ UInt32 numAvail = p->numAvail; ++ if (numAvail > LZMA_MATCH_LEN_MAX) ++ numAvail = LZMA_MATCH_LEN_MAX; ++ { ++ const Byte *pby2 = pby - distance; ++ for (; lenRes < numAvail && pby[lenRes] == pby2[lenRes]; lenRes++); ++ } ++ } ++ } ++ p->additionalOffset++; ++ *numDistancePairsRes = numPairs; ++ return lenRes; ++} ++ ++ ++#define MakeAsChar(p) (p)->backPrev = (UInt32)(-1); (p)->prev1IsChar = False; ++#define MakeAsShortRep(p) (p)->backPrev = 0; (p)->prev1IsChar = False; ++#define IsShortRep(p) ((p)->backPrev == 0) ++ ++static UInt32 GetRepLen1Price(CLzmaEnc *p, UInt32 state, UInt32 posState) ++{ ++ return ++ GET_PRICE_0(p->isRepG0[state]) + ++ GET_PRICE_0(p->isRep0Long[state][posState]); ++} ++ ++static UInt32 GetPureRepPrice(CLzmaEnc *p, UInt32 repIndex, UInt32 state, UInt32 posState) ++{ ++ UInt32 price; ++ if (repIndex == 0) ++ { ++ price = GET_PRICE_0(p->isRepG0[state]); ++ price += GET_PRICE_1(p->isRep0Long[state][posState]); ++ } ++ else ++ { ++ price = GET_PRICE_1(p->isRepG0[state]); ++ if (repIndex == 1) ++ price += GET_PRICE_0(p->isRepG1[state]); ++ else ++ { ++ price += GET_PRICE_1(p->isRepG1[state]); ++ price += GET_PRICE(p->isRepG2[state], repIndex - 2); ++ } ++ } ++ return price; ++} ++ ++static UInt32 GetRepPrice(CLzmaEnc *p, UInt32 repIndex, UInt32 len, UInt32 state, UInt32 posState) ++{ ++ return p->repLenEnc.prices[posState][len - LZMA_MATCH_LEN_MIN] + ++ GetPureRepPrice(p, repIndex, state, posState); ++} ++ ++static UInt32 Backward(CLzmaEnc *p, UInt32 *backRes, UInt32 cur) ++{ ++ UInt32 posMem = p->opt[cur].posPrev; ++ UInt32 backMem = p->opt[cur].backPrev; ++ p->optimumEndIndex = cur; ++ do ++ { ++ if (p->opt[cur].prev1IsChar) ++ { ++ MakeAsChar(&p->opt[posMem]) ++ p->opt[posMem].posPrev = posMem - 1; ++ if (p->opt[cur].prev2) ++ { ++ p->opt[posMem - 1].prev1IsChar = False; ++ p->opt[posMem - 1].posPrev = p->opt[cur].posPrev2; ++ p->opt[posMem - 1].backPrev = p->opt[cur].backPrev2; ++ } ++ } ++ { ++ UInt32 posPrev = posMem; ++ UInt32 backCur = backMem; ++ ++ backMem = p->opt[posPrev].backPrev; ++ posMem = p->opt[posPrev].posPrev; ++ ++ p->opt[posPrev].backPrev = backCur; ++ p->opt[posPrev].posPrev = cur; ++ cur = posPrev; ++ } ++ } ++ while (cur != 0); ++ *backRes = p->opt[0].backPrev; ++ p->optimumCurrentIndex = p->opt[0].posPrev; ++ return p->optimumCurrentIndex; ++} ++ ++#define LIT_PROBS(pos, prevByte) (p->litProbs + ((((pos) & p->lpMask) << p->lc) + ((prevByte) >> (8 - p->lc))) * 0x300) ++ ++static UInt32 GetOptimum(CLzmaEnc *p, UInt32 position, UInt32 *backRes) ++{ ++ UInt32 numAvail, mainLen, numPairs, repMaxIndex, i, posState, lenEnd, len, cur; ++ UInt32 matchPrice, repMatchPrice, normalMatchPrice; ++ UInt32 reps[LZMA_NUM_REPS], repLens[LZMA_NUM_REPS]; ++ UInt32 *matches; ++ const Byte *data; ++ Byte curByte, matchByte; ++ if (p->optimumEndIndex != p->optimumCurrentIndex) ++ { ++ const COptimal *opt = &p->opt[p->optimumCurrentIndex]; ++ UInt32 lenRes = opt->posPrev - p->optimumCurrentIndex; ++ *backRes = opt->backPrev; ++ p->optimumCurrentIndex = opt->posPrev; ++ return lenRes; ++ } ++ p->optimumCurrentIndex = p->optimumEndIndex = 0; ++ ++ if (p->additionalOffset == 0) ++ mainLen = ReadMatchDistances(p, &numPairs); ++ else ++ { ++ mainLen = p->longestMatchLength; ++ numPairs = p->numPairs; ++ } ++ ++ numAvail = p->numAvail; ++ if (numAvail < 2) ++ { ++ *backRes = (UInt32)(-1); ++ return 1; ++ } ++ if (numAvail > LZMA_MATCH_LEN_MAX) ++ numAvail = LZMA_MATCH_LEN_MAX; ++ ++ data = p->matchFinder.GetPointerToCurrentPos(p->matchFinderObj) - 1; ++ repMaxIndex = 0; ++ for (i = 0; i < LZMA_NUM_REPS; i++) ++ { ++ UInt32 lenTest; ++ const Byte *data2; ++ reps[i] = p->reps[i]; ++ data2 = data - (reps[i] + 1); ++ if (data[0] != data2[0] || data[1] != data2[1]) ++ { ++ repLens[i] = 0; ++ continue; ++ } ++ for (lenTest = 2; lenTest < numAvail && data[lenTest] == data2[lenTest]; lenTest++); ++ repLens[i] = lenTest; ++ if (lenTest > repLens[repMaxIndex]) ++ repMaxIndex = i; ++ } ++ if (repLens[repMaxIndex] >= p->numFastBytes) ++ { ++ UInt32 lenRes; ++ *backRes = repMaxIndex; ++ lenRes = repLens[repMaxIndex]; ++ MovePos(p, lenRes - 1); ++ return lenRes; ++ } ++ ++ matches = p->matches; ++ if (mainLen >= p->numFastBytes) ++ { ++ *backRes = matches[numPairs - 1] + LZMA_NUM_REPS; ++ MovePos(p, mainLen - 1); ++ return mainLen; ++ } ++ curByte = *data; ++ matchByte = *(data - (reps[0] + 1)); ++ ++ if (mainLen < 2 && curByte != matchByte && repLens[repMaxIndex] < 2) ++ { ++ *backRes = (UInt32)-1; ++ return 1; ++ } ++ ++ p->opt[0].state = (CState)p->state; ++ ++ posState = (position & p->pbMask); ++ ++ { ++ const CLzmaProb *probs = LIT_PROBS(position, *(data - 1)); ++ p->opt[1].price = GET_PRICE_0(p->isMatch[p->state][posState]) + ++ (!IsCharState(p->state) ? ++ LitEnc_GetPriceMatched(probs, curByte, matchByte, p->ProbPrices) : ++ LitEnc_GetPrice(probs, curByte, p->ProbPrices)); ++ } ++ ++ MakeAsChar(&p->opt[1]); ++ ++ matchPrice = GET_PRICE_1(p->isMatch[p->state][posState]); ++ repMatchPrice = matchPrice + GET_PRICE_1(p->isRep[p->state]); ++ ++ if (matchByte == curByte) ++ { ++ UInt32 shortRepPrice = repMatchPrice + GetRepLen1Price(p, p->state, posState); ++ if (shortRepPrice < p->opt[1].price) ++ { ++ p->opt[1].price = shortRepPrice; ++ MakeAsShortRep(&p->opt[1]); ++ } ++ } ++ lenEnd = ((mainLen >= repLens[repMaxIndex]) ? mainLen : repLens[repMaxIndex]); ++ ++ if (lenEnd < 2) ++ { ++ *backRes = p->opt[1].backPrev; ++ return 1; ++ } ++ ++ p->opt[1].posPrev = 0; ++ for (i = 0; i < LZMA_NUM_REPS; i++) ++ p->opt[0].backs[i] = reps[i]; ++ ++ len = lenEnd; ++ do ++ p->opt[len--].price = kInfinityPrice; ++ while (len >= 2); ++ ++ for (i = 0; i < LZMA_NUM_REPS; i++) ++ { ++ UInt32 repLen = repLens[i]; ++ UInt32 price; ++ if (repLen < 2) ++ continue; ++ price = repMatchPrice + GetPureRepPrice(p, i, p->state, posState); ++ do ++ { ++ UInt32 curAndLenPrice = price + p->repLenEnc.prices[posState][repLen - 2]; ++ COptimal *opt = &p->opt[repLen]; ++ if (curAndLenPrice < opt->price) ++ { ++ opt->price = curAndLenPrice; ++ opt->posPrev = 0; ++ opt->backPrev = i; ++ opt->prev1IsChar = False; ++ } ++ } ++ while (--repLen >= 2); ++ } ++ ++ normalMatchPrice = matchPrice + GET_PRICE_0(p->isRep[p->state]); ++ ++ len = ((repLens[0] >= 2) ? repLens[0] + 1 : 2); ++ if (len <= mainLen) ++ { ++ UInt32 offs = 0; ++ while (len > matches[offs]) ++ offs += 2; ++ for (; ; len++) ++ { ++ COptimal *opt; ++ UInt32 distance = matches[offs + 1]; ++ ++ UInt32 curAndLenPrice = normalMatchPrice + p->lenEnc.prices[posState][len - LZMA_MATCH_LEN_MIN]; ++ UInt32 lenToPosState = GetLenToPosState(len); ++ if (distance < kNumFullDistances) ++ curAndLenPrice += p->distancesPrices[lenToPosState][distance]; ++ else ++ { ++ UInt32 slot; ++ GetPosSlot2(distance, slot); ++ curAndLenPrice += p->alignPrices[distance & kAlignMask] + p->posSlotPrices[lenToPosState][slot]; ++ } ++ opt = &p->opt[len]; ++ if (curAndLenPrice < opt->price) ++ { ++ opt->price = curAndLenPrice; ++ opt->posPrev = 0; ++ opt->backPrev = distance + LZMA_NUM_REPS; ++ opt->prev1IsChar = False; ++ } ++ if (len == matches[offs]) ++ { ++ offs += 2; ++ if (offs == numPairs) ++ break; ++ } ++ } ++ } ++ ++ cur = 0; ++ ++ #ifdef SHOW_STAT2 ++ if (position >= 0) ++ { ++ unsigned i; ++ printf("\n pos = %4X", position); ++ for (i = cur; i <= lenEnd; i++) ++ printf("\nprice[%4X] = %d", position - cur + i, p->opt[i].price); ++ } ++ #endif ++ ++ for (;;) ++ { ++ UInt32 numAvailFull, newLen, numPairs, posPrev, state, posState, startLen; ++ UInt32 curPrice, curAnd1Price, matchPrice, repMatchPrice; ++ Bool nextIsChar; ++ Byte curByte, matchByte; ++ const Byte *data; ++ COptimal *curOpt; ++ COptimal *nextOpt; ++ ++ cur++; ++ if (cur == lenEnd) ++ return Backward(p, backRes, cur); ++ ++ newLen = ReadMatchDistances(p, &numPairs); ++ if (newLen >= p->numFastBytes) ++ { ++ p->numPairs = numPairs; ++ p->longestMatchLength = newLen; ++ return Backward(p, backRes, cur); ++ } ++ position++; ++ curOpt = &p->opt[cur]; ++ posPrev = curOpt->posPrev; ++ if (curOpt->prev1IsChar) ++ { ++ posPrev--; ++ if (curOpt->prev2) ++ { ++ state = p->opt[curOpt->posPrev2].state; ++ if (curOpt->backPrev2 < LZMA_NUM_REPS) ++ state = kRepNextStates[state]; ++ else ++ state = kMatchNextStates[state]; ++ } ++ else ++ state = p->opt[posPrev].state; ++ state = kLiteralNextStates[state]; ++ } ++ else ++ state = p->opt[posPrev].state; ++ if (posPrev == cur - 1) ++ { ++ if (IsShortRep(curOpt)) ++ state = kShortRepNextStates[state]; ++ else ++ state = kLiteralNextStates[state]; ++ } ++ else ++ { ++ UInt32 pos; ++ const COptimal *prevOpt; ++ if (curOpt->prev1IsChar && curOpt->prev2) ++ { ++ posPrev = curOpt->posPrev2; ++ pos = curOpt->backPrev2; ++ state = kRepNextStates[state]; ++ } ++ else ++ { ++ pos = curOpt->backPrev; ++ if (pos < LZMA_NUM_REPS) ++ state = kRepNextStates[state]; ++ else ++ state = kMatchNextStates[state]; ++ } ++ prevOpt = &p->opt[posPrev]; ++ if (pos < LZMA_NUM_REPS) ++ { ++ UInt32 i; ++ reps[0] = prevOpt->backs[pos]; ++ for (i = 1; i <= pos; i++) ++ reps[i] = prevOpt->backs[i - 1]; ++ for (; i < LZMA_NUM_REPS; i++) ++ reps[i] = prevOpt->backs[i]; ++ } ++ else ++ { ++ UInt32 i; ++ reps[0] = (pos - LZMA_NUM_REPS); ++ for (i = 1; i < LZMA_NUM_REPS; i++) ++ reps[i] = prevOpt->backs[i - 1]; ++ } ++ } ++ curOpt->state = (CState)state; ++ ++ curOpt->backs[0] = reps[0]; ++ curOpt->backs[1] = reps[1]; ++ curOpt->backs[2] = reps[2]; ++ curOpt->backs[3] = reps[3]; ++ ++ curPrice = curOpt->price; ++ nextIsChar = False; ++ data = p->matchFinder.GetPointerToCurrentPos(p->matchFinderObj) - 1; ++ curByte = *data; ++ matchByte = *(data - (reps[0] + 1)); ++ ++ posState = (position & p->pbMask); ++ ++ curAnd1Price = curPrice + GET_PRICE_0(p->isMatch[state][posState]); ++ { ++ const CLzmaProb *probs = LIT_PROBS(position, *(data - 1)); ++ curAnd1Price += ++ (!IsCharState(state) ? ++ LitEnc_GetPriceMatched(probs, curByte, matchByte, p->ProbPrices) : ++ LitEnc_GetPrice(probs, curByte, p->ProbPrices)); ++ } ++ ++ nextOpt = &p->opt[cur + 1]; ++ ++ if (curAnd1Price < nextOpt->price) ++ { ++ nextOpt->price = curAnd1Price; ++ nextOpt->posPrev = cur; ++ MakeAsChar(nextOpt); ++ nextIsChar = True; ++ } ++ ++ matchPrice = curPrice + GET_PRICE_1(p->isMatch[state][posState]); ++ repMatchPrice = matchPrice + GET_PRICE_1(p->isRep[state]); ++ ++ if (matchByte == curByte && !(nextOpt->posPrev < cur && nextOpt->backPrev == 0)) ++ { ++ UInt32 shortRepPrice = repMatchPrice + GetRepLen1Price(p, state, posState); ++ if (shortRepPrice <= nextOpt->price) ++ { ++ nextOpt->price = shortRepPrice; ++ nextOpt->posPrev = cur; ++ MakeAsShortRep(nextOpt); ++ nextIsChar = True; ++ } ++ } ++ numAvailFull = p->numAvail; ++ { ++ UInt32 temp = kNumOpts - 1 - cur; ++ if (temp < numAvailFull) ++ numAvailFull = temp; ++ } ++ ++ if (numAvailFull < 2) ++ continue; ++ numAvail = (numAvailFull <= p->numFastBytes ? numAvailFull : p->numFastBytes); ++ ++ if (!nextIsChar && matchByte != curByte) /* speed optimization */ ++ { ++ /* try Literal + rep0 */ ++ UInt32 temp; ++ UInt32 lenTest2; ++ const Byte *data2 = data - (reps[0] + 1); ++ UInt32 limit = p->numFastBytes + 1; ++ if (limit > numAvailFull) ++ limit = numAvailFull; ++ ++ for (temp = 1; temp < limit && data[temp] == data2[temp]; temp++); ++ lenTest2 = temp - 1; ++ if (lenTest2 >= 2) ++ { ++ UInt32 state2 = kLiteralNextStates[state]; ++ UInt32 posStateNext = (position + 1) & p->pbMask; ++ UInt32 nextRepMatchPrice = curAnd1Price + ++ GET_PRICE_1(p->isMatch[state2][posStateNext]) + ++ GET_PRICE_1(p->isRep[state2]); ++ /* for (; lenTest2 >= 2; lenTest2--) */ ++ { ++ UInt32 curAndLenPrice; ++ COptimal *opt; ++ UInt32 offset = cur + 1 + lenTest2; ++ while (lenEnd < offset) ++ p->opt[++lenEnd].price = kInfinityPrice; ++ curAndLenPrice = nextRepMatchPrice + GetRepPrice(p, 0, lenTest2, state2, posStateNext); ++ opt = &p->opt[offset]; ++ if (curAndLenPrice < opt->price) ++ { ++ opt->price = curAndLenPrice; ++ opt->posPrev = cur + 1; ++ opt->backPrev = 0; ++ opt->prev1IsChar = True; ++ opt->prev2 = False; ++ } ++ } ++ } ++ } ++ ++ startLen = 2; /* speed optimization */ ++ { ++ UInt32 repIndex; ++ for (repIndex = 0; repIndex < LZMA_NUM_REPS; repIndex++) ++ { ++ UInt32 lenTest; ++ UInt32 lenTestTemp; ++ UInt32 price; ++ const Byte *data2 = data - (reps[repIndex] + 1); ++ if (data[0] != data2[0] || data[1] != data2[1]) ++ continue; ++ for (lenTest = 2; lenTest < numAvail && data[lenTest] == data2[lenTest]; lenTest++); ++ while (lenEnd < cur + lenTest) ++ p->opt[++lenEnd].price = kInfinityPrice; ++ lenTestTemp = lenTest; ++ price = repMatchPrice + GetPureRepPrice(p, repIndex, state, posState); ++ do ++ { ++ UInt32 curAndLenPrice = price + p->repLenEnc.prices[posState][lenTest - 2]; ++ COptimal *opt = &p->opt[cur + lenTest]; ++ if (curAndLenPrice < opt->price) ++ { ++ opt->price = curAndLenPrice; ++ opt->posPrev = cur; ++ opt->backPrev = repIndex; ++ opt->prev1IsChar = False; ++ } ++ } ++ while (--lenTest >= 2); ++ lenTest = lenTestTemp; ++ ++ if (repIndex == 0) ++ startLen = lenTest + 1; ++ ++ /* if (_maxMode) */ ++ { ++ UInt32 lenTest2 = lenTest + 1; ++ UInt32 limit = lenTest2 + p->numFastBytes; ++ UInt32 nextRepMatchPrice; ++ if (limit > numAvailFull) ++ limit = numAvailFull; ++ for (; lenTest2 < limit && data[lenTest2] == data2[lenTest2]; lenTest2++); ++ lenTest2 -= lenTest + 1; ++ if (lenTest2 >= 2) ++ { ++ UInt32 state2 = kRepNextStates[state]; ++ UInt32 posStateNext = (position + lenTest) & p->pbMask; ++ UInt32 curAndLenCharPrice = ++ price + p->repLenEnc.prices[posState][lenTest - 2] + ++ GET_PRICE_0(p->isMatch[state2][posStateNext]) + ++ LitEnc_GetPriceMatched(LIT_PROBS(position + lenTest, data[lenTest - 1]), ++ data[lenTest], data2[lenTest], p->ProbPrices); ++ state2 = kLiteralNextStates[state2]; ++ posStateNext = (position + lenTest + 1) & p->pbMask; ++ nextRepMatchPrice = curAndLenCharPrice + ++ GET_PRICE_1(p->isMatch[state2][posStateNext]) + ++ GET_PRICE_1(p->isRep[state2]); ++ ++ /* for (; lenTest2 >= 2; lenTest2--) */ ++ { ++ UInt32 curAndLenPrice; ++ COptimal *opt; ++ UInt32 offset = cur + lenTest + 1 + lenTest2; ++ while (lenEnd < offset) ++ p->opt[++lenEnd].price = kInfinityPrice; ++ curAndLenPrice = nextRepMatchPrice + GetRepPrice(p, 0, lenTest2, state2, posStateNext); ++ opt = &p->opt[offset]; ++ if (curAndLenPrice < opt->price) ++ { ++ opt->price = curAndLenPrice; ++ opt->posPrev = cur + lenTest + 1; ++ opt->backPrev = 0; ++ opt->prev1IsChar = True; ++ opt->prev2 = True; ++ opt->posPrev2 = cur; ++ opt->backPrev2 = repIndex; ++ } ++ } ++ } ++ } ++ } ++ } ++ /* for (UInt32 lenTest = 2; lenTest <= newLen; lenTest++) */ ++ if (newLen > numAvail) ++ { ++ newLen = numAvail; ++ for (numPairs = 0; newLen > matches[numPairs]; numPairs += 2); ++ matches[numPairs] = newLen; ++ numPairs += 2; ++ } ++ if (newLen >= startLen) ++ { ++ UInt32 normalMatchPrice = matchPrice + GET_PRICE_0(p->isRep[state]); ++ UInt32 offs, curBack, posSlot; ++ UInt32 lenTest; ++ while (lenEnd < cur + newLen) ++ p->opt[++lenEnd].price = kInfinityPrice; ++ ++ offs = 0; ++ while (startLen > matches[offs]) ++ offs += 2; ++ curBack = matches[offs + 1]; ++ GetPosSlot2(curBack, posSlot); ++ for (lenTest = /*2*/ startLen; ; lenTest++) ++ { ++ UInt32 curAndLenPrice = normalMatchPrice + p->lenEnc.prices[posState][lenTest - LZMA_MATCH_LEN_MIN]; ++ UInt32 lenToPosState = GetLenToPosState(lenTest); ++ COptimal *opt; ++ if (curBack < kNumFullDistances) ++ curAndLenPrice += p->distancesPrices[lenToPosState][curBack]; ++ else ++ curAndLenPrice += p->posSlotPrices[lenToPosState][posSlot] + p->alignPrices[curBack & kAlignMask]; ++ ++ opt = &p->opt[cur + lenTest]; ++ if (curAndLenPrice < opt->price) ++ { ++ opt->price = curAndLenPrice; ++ opt->posPrev = cur; ++ opt->backPrev = curBack + LZMA_NUM_REPS; ++ opt->prev1IsChar = False; ++ } ++ ++ if (/*_maxMode && */lenTest == matches[offs]) ++ { ++ /* Try Match + Literal + Rep0 */ ++ const Byte *data2 = data - (curBack + 1); ++ UInt32 lenTest2 = lenTest + 1; ++ UInt32 limit = lenTest2 + p->numFastBytes; ++ UInt32 nextRepMatchPrice; ++ if (limit > numAvailFull) ++ limit = numAvailFull; ++ for (; lenTest2 < limit && data[lenTest2] == data2[lenTest2]; lenTest2++); ++ lenTest2 -= lenTest + 1; ++ if (lenTest2 >= 2) ++ { ++ UInt32 state2 = kMatchNextStates[state]; ++ UInt32 posStateNext = (position + lenTest) & p->pbMask; ++ UInt32 curAndLenCharPrice = curAndLenPrice + ++ GET_PRICE_0(p->isMatch[state2][posStateNext]) + ++ LitEnc_GetPriceMatched(LIT_PROBS(position + lenTest, data[lenTest - 1]), ++ data[lenTest], data2[lenTest], p->ProbPrices); ++ state2 = kLiteralNextStates[state2]; ++ posStateNext = (posStateNext + 1) & p->pbMask; ++ nextRepMatchPrice = curAndLenCharPrice + ++ GET_PRICE_1(p->isMatch[state2][posStateNext]) + ++ GET_PRICE_1(p->isRep[state2]); ++ ++ /* for (; lenTest2 >= 2; lenTest2--) */ ++ { ++ UInt32 offset = cur + lenTest + 1 + lenTest2; ++ UInt32 curAndLenPrice; ++ COptimal *opt; ++ while (lenEnd < offset) ++ p->opt[++lenEnd].price = kInfinityPrice; ++ curAndLenPrice = nextRepMatchPrice + GetRepPrice(p, 0, lenTest2, state2, posStateNext); ++ opt = &p->opt[offset]; ++ if (curAndLenPrice < opt->price) ++ { ++ opt->price = curAndLenPrice; ++ opt->posPrev = cur + lenTest + 1; ++ opt->backPrev = 0; ++ opt->prev1IsChar = True; ++ opt->prev2 = True; ++ opt->posPrev2 = cur; ++ opt->backPrev2 = curBack + LZMA_NUM_REPS; ++ } ++ } ++ } ++ offs += 2; ++ if (offs == numPairs) ++ break; ++ curBack = matches[offs + 1]; ++ if (curBack >= kNumFullDistances) ++ GetPosSlot2(curBack, posSlot); ++ } ++ } ++ } ++ } ++} ++ ++#define ChangePair(smallDist, bigDist) (((bigDist) >> 7) > (smallDist)) ++ ++static UInt32 GetOptimumFast(CLzmaEnc *p, UInt32 *backRes) ++{ ++ UInt32 numAvail, mainLen, mainDist, numPairs, repIndex, repLen, i; ++ const Byte *data; ++ const UInt32 *matches; ++ ++ if (p->additionalOffset == 0) ++ mainLen = ReadMatchDistances(p, &numPairs); ++ else ++ { ++ mainLen = p->longestMatchLength; ++ numPairs = p->numPairs; ++ } ++ ++ numAvail = p->numAvail; ++ *backRes = (UInt32)-1; ++ if (numAvail < 2) ++ return 1; ++ if (numAvail > LZMA_MATCH_LEN_MAX) ++ numAvail = LZMA_MATCH_LEN_MAX; ++ data = p->matchFinder.GetPointerToCurrentPos(p->matchFinderObj) - 1; ++ ++ repLen = repIndex = 0; ++ for (i = 0; i < LZMA_NUM_REPS; i++) ++ { ++ UInt32 len; ++ const Byte *data2 = data - (p->reps[i] + 1); ++ if (data[0] != data2[0] || data[1] != data2[1]) ++ continue; ++ for (len = 2; len < numAvail && data[len] == data2[len]; len++); ++ if (len >= p->numFastBytes) ++ { ++ *backRes = i; ++ MovePos(p, len - 1); ++ return len; ++ } ++ if (len > repLen) ++ { ++ repIndex = i; ++ repLen = len; ++ } ++ } ++ ++ matches = p->matches; ++ if (mainLen >= p->numFastBytes) ++ { ++ *backRes = matches[numPairs - 1] + LZMA_NUM_REPS; ++ MovePos(p, mainLen - 1); ++ return mainLen; ++ } ++ ++ mainDist = 0; /* for GCC */ ++ if (mainLen >= 2) ++ { ++ mainDist = matches[numPairs - 1]; ++ while (numPairs > 2 && mainLen == matches[numPairs - 4] + 1) ++ { ++ if (!ChangePair(matches[numPairs - 3], mainDist)) ++ break; ++ numPairs -= 2; ++ mainLen = matches[numPairs - 2]; ++ mainDist = matches[numPairs - 1]; ++ } ++ if (mainLen == 2 && mainDist >= 0x80) ++ mainLen = 1; ++ } ++ ++ if (repLen >= 2 && ( ++ (repLen + 1 >= mainLen) || ++ (repLen + 2 >= mainLen && mainDist >= (1 << 9)) || ++ (repLen + 3 >= mainLen && mainDist >= (1 << 15)))) ++ { ++ *backRes = repIndex; ++ MovePos(p, repLen - 1); ++ return repLen; ++ } ++ ++ if (mainLen < 2 || numAvail <= 2) ++ return 1; ++ ++ p->longestMatchLength = ReadMatchDistances(p, &p->numPairs); ++ if (p->longestMatchLength >= 2) ++ { ++ UInt32 newDistance = matches[p->numPairs - 1]; ++ if ((p->longestMatchLength >= mainLen && newDistance < mainDist) || ++ (p->longestMatchLength == mainLen + 1 && !ChangePair(mainDist, newDistance)) || ++ (p->longestMatchLength > mainLen + 1) || ++ (p->longestMatchLength + 1 >= mainLen && mainLen >= 3 && ChangePair(newDistance, mainDist))) ++ return 1; ++ } ++ ++ data = p->matchFinder.GetPointerToCurrentPos(p->matchFinderObj) - 1; ++ for (i = 0; i < LZMA_NUM_REPS; i++) ++ { ++ UInt32 len, limit; ++ const Byte *data2 = data - (p->reps[i] + 1); ++ if (data[0] != data2[0] || data[1] != data2[1]) ++ continue; ++ limit = mainLen - 1; ++ for (len = 2; len < limit && data[len] == data2[len]; len++); ++ if (len >= limit) ++ return 1; ++ } ++ *backRes = mainDist + LZMA_NUM_REPS; ++ MovePos(p, mainLen - 2); ++ return mainLen; ++} ++ ++static void WriteEndMarker(CLzmaEnc *p, UInt32 posState) ++{ ++ UInt32 len; ++ RangeEnc_EncodeBit(&p->rc, &p->isMatch[p->state][posState], 1); ++ RangeEnc_EncodeBit(&p->rc, &p->isRep[p->state], 0); ++ p->state = kMatchNextStates[p->state]; ++ len = LZMA_MATCH_LEN_MIN; ++ LenEnc_Encode2(&p->lenEnc, &p->rc, len - LZMA_MATCH_LEN_MIN, posState, !p->fastMode, p->ProbPrices); ++ RcTree_Encode(&p->rc, p->posSlotEncoder[GetLenToPosState(len)], kNumPosSlotBits, (1 << kNumPosSlotBits) - 1); ++ RangeEnc_EncodeDirectBits(&p->rc, (((UInt32)1 << 30) - 1) >> kNumAlignBits, 30 - kNumAlignBits); ++ RcTree_ReverseEncode(&p->rc, p->posAlignEncoder, kNumAlignBits, kAlignMask); ++} ++ ++static SRes CheckErrors(CLzmaEnc *p) ++{ ++ if (p->result != SZ_OK) ++ return p->result; ++ if (p->rc.res != SZ_OK) ++ p->result = SZ_ERROR_WRITE; ++ if (p->matchFinderBase.result != SZ_OK) ++ p->result = SZ_ERROR_READ; ++ if (p->result != SZ_OK) ++ p->finished = True; ++ return p->result; ++} ++ ++static SRes Flush(CLzmaEnc *p, UInt32 nowPos) ++{ ++ /* ReleaseMFStream(); */ ++ p->finished = True; ++ if (p->writeEndMark) ++ WriteEndMarker(p, nowPos & p->pbMask); ++ RangeEnc_FlushData(&p->rc); ++ RangeEnc_FlushStream(&p->rc); ++ return CheckErrors(p); ++} ++ ++static void FillAlignPrices(CLzmaEnc *p) ++{ ++ UInt32 i; ++ for (i = 0; i < kAlignTableSize; i++) ++ p->alignPrices[i] = RcTree_ReverseGetPrice(p->posAlignEncoder, kNumAlignBits, i, p->ProbPrices); ++ p->alignPriceCount = 0; ++} ++ ++static void FillDistancesPrices(CLzmaEnc *p) ++{ ++ UInt32 tempPrices[kNumFullDistances]; ++ UInt32 i, lenToPosState; ++ for (i = kStartPosModelIndex; i < kNumFullDistances; i++) ++ { ++ UInt32 posSlot = GetPosSlot1(i); ++ UInt32 footerBits = ((posSlot >> 1) - 1); ++ UInt32 base = ((2 | (posSlot & 1)) << footerBits); ++ tempPrices[i] = RcTree_ReverseGetPrice(p->posEncoders + base - posSlot - 1, footerBits, i - base, p->ProbPrices); ++ } ++ ++ for (lenToPosState = 0; lenToPosState < kNumLenToPosStates; lenToPosState++) ++ { ++ UInt32 posSlot; ++ const CLzmaProb *encoder = p->posSlotEncoder[lenToPosState]; ++ UInt32 *posSlotPrices = p->posSlotPrices[lenToPosState]; ++ for (posSlot = 0; posSlot < p->distTableSize; posSlot++) ++ posSlotPrices[posSlot] = RcTree_GetPrice(encoder, kNumPosSlotBits, posSlot, p->ProbPrices); ++ for (posSlot = kEndPosModelIndex; posSlot < p->distTableSize; posSlot++) ++ posSlotPrices[posSlot] += ((((posSlot >> 1) - 1) - kNumAlignBits) << kNumBitPriceShiftBits); ++ ++ { ++ UInt32 *distancesPrices = p->distancesPrices[lenToPosState]; ++ UInt32 i; ++ for (i = 0; i < kStartPosModelIndex; i++) ++ distancesPrices[i] = posSlotPrices[i]; ++ for (; i < kNumFullDistances; i++) ++ distancesPrices[i] = posSlotPrices[GetPosSlot1(i)] + tempPrices[i]; ++ } ++ } ++ p->matchPriceCount = 0; ++} ++ ++void LzmaEnc_Construct(CLzmaEnc *p) ++{ ++ RangeEnc_Construct(&p->rc); ++ MatchFinder_Construct(&p->matchFinderBase); ++ #ifndef _7ZIP_ST ++ MatchFinderMt_Construct(&p->matchFinderMt); ++ p->matchFinderMt.MatchFinder = &p->matchFinderBase; ++ #endif ++ ++ { ++ CLzmaEncProps props; ++ LzmaEncProps_Init(&props); ++ LzmaEnc_SetProps(p, &props); ++ } ++ ++ #ifndef LZMA_LOG_BSR ++ LzmaEnc_FastPosInit(p->g_FastPos); ++ #endif ++ ++ LzmaEnc_InitPriceTables(p->ProbPrices); ++ p->litProbs = 0; ++ p->saveState.litProbs = 0; ++} ++ ++CLzmaEncHandle LzmaEnc_Create(ISzAlloc *alloc) ++{ ++ void *p; ++ p = alloc->Alloc(alloc, sizeof(CLzmaEnc)); ++ if (p != 0) ++ LzmaEnc_Construct((CLzmaEnc *)p); ++ return p; ++} ++ ++void LzmaEnc_FreeLits(CLzmaEnc *p, ISzAlloc *alloc) ++{ ++ alloc->Free(alloc, p->litProbs); ++ alloc->Free(alloc, p->saveState.litProbs); ++ p->litProbs = 0; ++ p->saveState.litProbs = 0; ++} ++ ++void LzmaEnc_Destruct(CLzmaEnc *p, ISzAlloc *alloc, ISzAlloc *allocBig) ++{ ++ #ifndef _7ZIP_ST ++ MatchFinderMt_Destruct(&p->matchFinderMt, allocBig); ++ #endif ++ MatchFinder_Free(&p->matchFinderBase, allocBig); ++ LzmaEnc_FreeLits(p, alloc); ++ RangeEnc_Free(&p->rc, alloc); ++} ++ ++void LzmaEnc_Destroy(CLzmaEncHandle p, ISzAlloc *alloc, ISzAlloc *allocBig) ++{ ++ LzmaEnc_Destruct((CLzmaEnc *)p, alloc, allocBig); ++ alloc->Free(alloc, p); ++} ++ ++static SRes LzmaEnc_CodeOneBlock(CLzmaEnc *p, Bool useLimits, UInt32 maxPackSize, UInt32 maxUnpackSize) ++{ ++ UInt32 nowPos32, startPos32; ++ if (p->needInit) ++ { ++ p->matchFinder.Init(p->matchFinderObj); ++ p->needInit = 0; ++ } ++ ++ if (p->finished) ++ return p->result; ++ RINOK(CheckErrors(p)); ++ ++ nowPos32 = (UInt32)p->nowPos64; ++ startPos32 = nowPos32; ++ ++ if (p->nowPos64 == 0) ++ { ++ UInt32 numPairs; ++ Byte curByte; ++ if (p->matchFinder.GetNumAvailableBytes(p->matchFinderObj) == 0) ++ return Flush(p, nowPos32); ++ ReadMatchDistances(p, &numPairs); ++ RangeEnc_EncodeBit(&p->rc, &p->isMatch[p->state][0], 0); ++ p->state = kLiteralNextStates[p->state]; ++ curByte = p->matchFinder.GetIndexByte(p->matchFinderObj, 0 - p->additionalOffset); ++ LitEnc_Encode(&p->rc, p->litProbs, curByte); ++ p->additionalOffset--; ++ nowPos32++; ++ } ++ ++ if (p->matchFinder.GetNumAvailableBytes(p->matchFinderObj) != 0) ++ for (;;) ++ { ++ UInt32 pos, len, posState; ++ ++ if (p->fastMode) ++ len = GetOptimumFast(p, &pos); ++ else ++ len = GetOptimum(p, nowPos32, &pos); ++ ++ #ifdef SHOW_STAT2 ++ printf("\n pos = %4X, len = %d pos = %d", nowPos32, len, pos); ++ #endif ++ ++ posState = nowPos32 & p->pbMask; ++ if (len == 1 && pos == (UInt32)-1) ++ { ++ Byte curByte; ++ CLzmaProb *probs; ++ const Byte *data; ++ ++ RangeEnc_EncodeBit(&p->rc, &p->isMatch[p->state][posState], 0); ++ data = p->matchFinder.GetPointerToCurrentPos(p->matchFinderObj) - p->additionalOffset; ++ curByte = *data; ++ probs = LIT_PROBS(nowPos32, *(data - 1)); ++ if (IsCharState(p->state)) ++ LitEnc_Encode(&p->rc, probs, curByte); ++ else ++ LitEnc_EncodeMatched(&p->rc, probs, curByte, *(data - p->reps[0] - 1)); ++ p->state = kLiteralNextStates[p->state]; ++ } ++ else ++ { ++ RangeEnc_EncodeBit(&p->rc, &p->isMatch[p->state][posState], 1); ++ if (pos < LZMA_NUM_REPS) ++ { ++ RangeEnc_EncodeBit(&p->rc, &p->isRep[p->state], 1); ++ if (pos == 0) ++ { ++ RangeEnc_EncodeBit(&p->rc, &p->isRepG0[p->state], 0); ++ RangeEnc_EncodeBit(&p->rc, &p->isRep0Long[p->state][posState], ((len == 1) ? 0 : 1)); ++ } ++ else ++ { ++ UInt32 distance = p->reps[pos]; ++ RangeEnc_EncodeBit(&p->rc, &p->isRepG0[p->state], 1); ++ if (pos == 1) ++ RangeEnc_EncodeBit(&p->rc, &p->isRepG1[p->state], 0); ++ else ++ { ++ RangeEnc_EncodeBit(&p->rc, &p->isRepG1[p->state], 1); ++ RangeEnc_EncodeBit(&p->rc, &p->isRepG2[p->state], pos - 2); ++ if (pos == 3) ++ p->reps[3] = p->reps[2]; ++ p->reps[2] = p->reps[1]; ++ } ++ p->reps[1] = p->reps[0]; ++ p->reps[0] = distance; ++ } ++ if (len == 1) ++ p->state = kShortRepNextStates[p->state]; ++ else ++ { ++ LenEnc_Encode2(&p->repLenEnc, &p->rc, len - LZMA_MATCH_LEN_MIN, posState, !p->fastMode, p->ProbPrices); ++ p->state = kRepNextStates[p->state]; ++ } ++ } ++ else ++ { ++ UInt32 posSlot; ++ RangeEnc_EncodeBit(&p->rc, &p->isRep[p->state], 0); ++ p->state = kMatchNextStates[p->state]; ++ LenEnc_Encode2(&p->lenEnc, &p->rc, len - LZMA_MATCH_LEN_MIN, posState, !p->fastMode, p->ProbPrices); ++ pos -= LZMA_NUM_REPS; ++ GetPosSlot(pos, posSlot); ++ RcTree_Encode(&p->rc, p->posSlotEncoder[GetLenToPosState(len)], kNumPosSlotBits, posSlot); ++ ++ if (posSlot >= kStartPosModelIndex) ++ { ++ UInt32 footerBits = ((posSlot >> 1) - 1); ++ UInt32 base = ((2 | (posSlot & 1)) << footerBits); ++ UInt32 posReduced = pos - base; ++ ++ if (posSlot < kEndPosModelIndex) ++ RcTree_ReverseEncode(&p->rc, p->posEncoders + base - posSlot - 1, footerBits, posReduced); ++ else ++ { ++ RangeEnc_EncodeDirectBits(&p->rc, posReduced >> kNumAlignBits, footerBits - kNumAlignBits); ++ RcTree_ReverseEncode(&p->rc, p->posAlignEncoder, kNumAlignBits, posReduced & kAlignMask); ++ p->alignPriceCount++; ++ } ++ } ++ p->reps[3] = p->reps[2]; ++ p->reps[2] = p->reps[1]; ++ p->reps[1] = p->reps[0]; ++ p->reps[0] = pos; ++ p->matchPriceCount++; ++ } ++ } ++ p->additionalOffset -= len; ++ nowPos32 += len; ++ if (p->additionalOffset == 0) ++ { ++ UInt32 processed; ++ if (!p->fastMode) ++ { ++ if (p->matchPriceCount >= (1 << 7)) ++ FillDistancesPrices(p); ++ if (p->alignPriceCount >= kAlignTableSize) ++ FillAlignPrices(p); ++ } ++ if (p->matchFinder.GetNumAvailableBytes(p->matchFinderObj) == 0) ++ break; ++ processed = nowPos32 - startPos32; ++ if (useLimits) ++ { ++ if (processed + kNumOpts + 300 >= maxUnpackSize || ++ RangeEnc_GetProcessed(&p->rc) + kNumOpts * 2 >= maxPackSize) ++ break; ++ } ++ else if (processed >= (1 << 15)) ++ { ++ p->nowPos64 += nowPos32 - startPos32; ++ return CheckErrors(p); ++ } ++ } ++ } ++ p->nowPos64 += nowPos32 - startPos32; ++ return Flush(p, nowPos32); ++} ++ ++#define kBigHashDicLimit ((UInt32)1 << 24) ++ ++static SRes LzmaEnc_Alloc(CLzmaEnc *p, UInt32 keepWindowSize, ISzAlloc *alloc, ISzAlloc *allocBig) ++{ ++ UInt32 beforeSize = kNumOpts; ++ Bool btMode; ++ if (!RangeEnc_Alloc(&p->rc, alloc)) ++ return SZ_ERROR_MEM; ++ btMode = (p->matchFinderBase.btMode != 0); ++ #ifndef _7ZIP_ST ++ p->mtMode = (p->multiThread && !p->fastMode && btMode); ++ #endif ++ ++ { ++ unsigned lclp = p->lc + p->lp; ++ if (p->litProbs == 0 || p->saveState.litProbs == 0 || p->lclp != lclp) ++ { ++ LzmaEnc_FreeLits(p, alloc); ++ p->litProbs = (CLzmaProb *)alloc->Alloc(alloc, (0x300 << lclp) * sizeof(CLzmaProb)); ++ p->saveState.litProbs = (CLzmaProb *)alloc->Alloc(alloc, (0x300 << lclp) * sizeof(CLzmaProb)); ++ if (p->litProbs == 0 || p->saveState.litProbs == 0) ++ { ++ LzmaEnc_FreeLits(p, alloc); ++ return SZ_ERROR_MEM; ++ } ++ p->lclp = lclp; ++ } ++ } ++ ++ p->matchFinderBase.bigHash = (p->dictSize > kBigHashDicLimit); ++ ++ if (beforeSize + p->dictSize < keepWindowSize) ++ beforeSize = keepWindowSize - p->dictSize; ++ ++ #ifndef _7ZIP_ST ++ if (p->mtMode) ++ { ++ RINOK(MatchFinderMt_Create(&p->matchFinderMt, p->dictSize, beforeSize, p->numFastBytes, LZMA_MATCH_LEN_MAX, allocBig)); ++ p->matchFinderObj = &p->matchFinderMt; ++ MatchFinderMt_CreateVTable(&p->matchFinderMt, &p->matchFinder); ++ } ++ else ++ #endif ++ { ++ if (!MatchFinder_Create(&p->matchFinderBase, p->dictSize, beforeSize, p->numFastBytes, LZMA_MATCH_LEN_MAX, allocBig)) ++ return SZ_ERROR_MEM; ++ p->matchFinderObj = &p->matchFinderBase; ++ MatchFinder_CreateVTable(&p->matchFinderBase, &p->matchFinder); ++ } ++ return SZ_OK; ++} ++ ++void LzmaEnc_Init(CLzmaEnc *p) ++{ ++ UInt32 i; ++ p->state = 0; ++ for (i = 0 ; i < LZMA_NUM_REPS; i++) ++ p->reps[i] = 0; ++ ++ RangeEnc_Init(&p->rc); ++ ++ ++ for (i = 0; i < kNumStates; i++) ++ { ++ UInt32 j; ++ for (j = 0; j < LZMA_NUM_PB_STATES_MAX; j++) ++ { ++ p->isMatch[i][j] = kProbInitValue; ++ p->isRep0Long[i][j] = kProbInitValue; ++ } ++ p->isRep[i] = kProbInitValue; ++ p->isRepG0[i] = kProbInitValue; ++ p->isRepG1[i] = kProbInitValue; ++ p->isRepG2[i] = kProbInitValue; ++ } ++ ++ { ++ UInt32 num = 0x300 << (p->lp + p->lc); ++ for (i = 0; i < num; i++) ++ p->litProbs[i] = kProbInitValue; ++ } ++ ++ { ++ for (i = 0; i < kNumLenToPosStates; i++) ++ { ++ CLzmaProb *probs = p->posSlotEncoder[i]; ++ UInt32 j; ++ for (j = 0; j < (1 << kNumPosSlotBits); j++) ++ probs[j] = kProbInitValue; ++ } ++ } ++ { ++ for (i = 0; i < kNumFullDistances - kEndPosModelIndex; i++) ++ p->posEncoders[i] = kProbInitValue; ++ } ++ ++ LenEnc_Init(&p->lenEnc.p); ++ LenEnc_Init(&p->repLenEnc.p); ++ ++ for (i = 0; i < (1 << kNumAlignBits); i++) ++ p->posAlignEncoder[i] = kProbInitValue; ++ ++ p->optimumEndIndex = 0; ++ p->optimumCurrentIndex = 0; ++ p->additionalOffset = 0; ++ ++ p->pbMask = (1 << p->pb) - 1; ++ p->lpMask = (1 << p->lp) - 1; ++} ++ ++void LzmaEnc_InitPrices(CLzmaEnc *p) ++{ ++ if (!p->fastMode) ++ { ++ FillDistancesPrices(p); ++ FillAlignPrices(p); ++ } ++ ++ p->lenEnc.tableSize = ++ p->repLenEnc.tableSize = ++ p->numFastBytes + 1 - LZMA_MATCH_LEN_MIN; ++ LenPriceEnc_UpdateTables(&p->lenEnc, 1 << p->pb, p->ProbPrices); ++ LenPriceEnc_UpdateTables(&p->repLenEnc, 1 << p->pb, p->ProbPrices); ++} ++ ++static SRes LzmaEnc_AllocAndInit(CLzmaEnc *p, UInt32 keepWindowSize, ISzAlloc *alloc, ISzAlloc *allocBig) ++{ ++ UInt32 i; ++ for (i = 0; i < (UInt32)kDicLogSizeMaxCompress; i++) ++ if (p->dictSize <= ((UInt32)1 << i)) ++ break; ++ p->distTableSize = i * 2; ++ ++ p->finished = False; ++ p->result = SZ_OK; ++ RINOK(LzmaEnc_Alloc(p, keepWindowSize, alloc, allocBig)); ++ LzmaEnc_Init(p); ++ LzmaEnc_InitPrices(p); ++ p->nowPos64 = 0; ++ return SZ_OK; ++} ++ ++static SRes LzmaEnc_Prepare(CLzmaEncHandle pp, ISeqOutStream *outStream, ISeqInStream *inStream, ++ ISzAlloc *alloc, ISzAlloc *allocBig) ++{ ++ CLzmaEnc *p = (CLzmaEnc *)pp; ++ p->matchFinderBase.stream = inStream; ++ p->needInit = 1; ++ p->rc.outStream = outStream; ++ return LzmaEnc_AllocAndInit(p, 0, alloc, allocBig); ++} ++ ++SRes LzmaEnc_PrepareForLzma2(CLzmaEncHandle pp, ++ ISeqInStream *inStream, UInt32 keepWindowSize, ++ ISzAlloc *alloc, ISzAlloc *allocBig) ++{ ++ CLzmaEnc *p = (CLzmaEnc *)pp; ++ p->matchFinderBase.stream = inStream; ++ p->needInit = 1; ++ return LzmaEnc_AllocAndInit(p, keepWindowSize, alloc, allocBig); ++} ++ ++static void LzmaEnc_SetInputBuf(CLzmaEnc *p, const Byte *src, SizeT srcLen) ++{ ++ p->matchFinderBase.directInput = 1; ++ p->matchFinderBase.bufferBase = (Byte *)src; ++ p->matchFinderBase.directInputRem = srcLen; ++} ++ ++SRes LzmaEnc_MemPrepare(CLzmaEncHandle pp, const Byte *src, SizeT srcLen, ++ UInt32 keepWindowSize, ISzAlloc *alloc, ISzAlloc *allocBig) ++{ ++ CLzmaEnc *p = (CLzmaEnc *)pp; ++ LzmaEnc_SetInputBuf(p, src, srcLen); ++ p->needInit = 1; ++ ++ return LzmaEnc_AllocAndInit(p, keepWindowSize, alloc, allocBig); ++} ++ ++void LzmaEnc_Finish(CLzmaEncHandle pp) ++{ ++ #ifndef _7ZIP_ST ++ CLzmaEnc *p = (CLzmaEnc *)pp; ++ if (p->mtMode) ++ MatchFinderMt_ReleaseStream(&p->matchFinderMt); ++ #else ++ pp = pp; ++ #endif ++} ++ ++typedef struct ++{ ++ ISeqOutStream funcTable; ++ Byte *data; ++ SizeT rem; ++ Bool overflow; ++} CSeqOutStreamBuf; ++ ++static size_t MyWrite(void *pp, const void *data, size_t size) ++{ ++ CSeqOutStreamBuf *p = (CSeqOutStreamBuf *)pp; ++ if (p->rem < size) ++ { ++ size = p->rem; ++ p->overflow = True; ++ } ++ memcpy(p->data, data, size); ++ p->rem -= size; ++ p->data += size; ++ return size; ++} ++ ++ ++UInt32 LzmaEnc_GetNumAvailableBytes(CLzmaEncHandle pp) ++{ ++ const CLzmaEnc *p = (CLzmaEnc *)pp; ++ return p->matchFinder.GetNumAvailableBytes(p->matchFinderObj); ++} ++ ++const Byte *LzmaEnc_GetCurBuf(CLzmaEncHandle pp) ++{ ++ const CLzmaEnc *p = (CLzmaEnc *)pp; ++ return p->matchFinder.GetPointerToCurrentPos(p->matchFinderObj) - p->additionalOffset; ++} ++ ++SRes LzmaEnc_CodeOneMemBlock(CLzmaEncHandle pp, Bool reInit, ++ Byte *dest, size_t *destLen, UInt32 desiredPackSize, UInt32 *unpackSize) ++{ ++ CLzmaEnc *p = (CLzmaEnc *)pp; ++ UInt64 nowPos64; ++ SRes res; ++ CSeqOutStreamBuf outStream; ++ ++ outStream.funcTable.Write = MyWrite; ++ outStream.data = dest; ++ outStream.rem = *destLen; ++ outStream.overflow = False; ++ ++ p->writeEndMark = False; ++ p->finished = False; ++ p->result = SZ_OK; ++ ++ if (reInit) ++ LzmaEnc_Init(p); ++ LzmaEnc_InitPrices(p); ++ nowPos64 = p->nowPos64; ++ RangeEnc_Init(&p->rc); ++ p->rc.outStream = &outStream.funcTable; ++ ++ res = LzmaEnc_CodeOneBlock(p, True, desiredPackSize, *unpackSize); ++ ++ *unpackSize = (UInt32)(p->nowPos64 - nowPos64); ++ *destLen -= outStream.rem; ++ if (outStream.overflow) ++ return SZ_ERROR_OUTPUT_EOF; ++ ++ return res; ++} ++ ++static SRes LzmaEnc_Encode2(CLzmaEnc *p, ICompressProgress *progress) ++{ ++ SRes res = SZ_OK; ++ ++ #ifndef _7ZIP_ST ++ Byte allocaDummy[0x300]; ++ int i = 0; ++ for (i = 0; i < 16; i++) ++ allocaDummy[i] = (Byte)i; ++ #endif ++ ++ for (;;) ++ { ++ res = LzmaEnc_CodeOneBlock(p, False, 0, 0); ++ if (res != SZ_OK || p->finished != 0) ++ break; ++ if (progress != 0) ++ { ++ res = progress->Progress(progress, p->nowPos64, RangeEnc_GetProcessed(&p->rc)); ++ if (res != SZ_OK) ++ { ++ res = SZ_ERROR_PROGRESS; ++ break; ++ } ++ } ++ } ++ LzmaEnc_Finish(p); ++ return res; ++} ++ ++SRes LzmaEnc_Encode(CLzmaEncHandle pp, ISeqOutStream *outStream, ISeqInStream *inStream, ICompressProgress *progress, ++ ISzAlloc *alloc, ISzAlloc *allocBig) ++{ ++ RINOK(LzmaEnc_Prepare(pp, outStream, inStream, alloc, allocBig)); ++ return LzmaEnc_Encode2((CLzmaEnc *)pp, progress); ++} ++ ++SRes LzmaEnc_WriteProperties(CLzmaEncHandle pp, Byte *props, SizeT *size) ++{ ++ CLzmaEnc *p = (CLzmaEnc *)pp; ++ int i; ++ UInt32 dictSize = p->dictSize; ++ if (*size < LZMA_PROPS_SIZE) ++ return SZ_ERROR_PARAM; ++ *size = LZMA_PROPS_SIZE; ++ props[0] = (Byte)((p->pb * 5 + p->lp) * 9 + p->lc); ++ ++ for (i = 11; i <= 30; i++) ++ { ++ if (dictSize <= ((UInt32)2 << i)) ++ { ++ dictSize = (2 << i); ++ break; ++ } ++ if (dictSize <= ((UInt32)3 << i)) ++ { ++ dictSize = (3 << i); ++ break; ++ } ++ } ++ ++ for (i = 0; i < 4; i++) ++ props[1 + i] = (Byte)(dictSize >> (8 * i)); ++ return SZ_OK; ++} ++ ++SRes LzmaEnc_MemEncode(CLzmaEncHandle pp, Byte *dest, SizeT *destLen, const Byte *src, SizeT srcLen, ++ int writeEndMark, ICompressProgress *progress, ISzAlloc *alloc, ISzAlloc *allocBig) ++{ ++ SRes res; ++ CLzmaEnc *p = (CLzmaEnc *)pp; ++ ++ CSeqOutStreamBuf outStream; ++ ++ LzmaEnc_SetInputBuf(p, src, srcLen); ++ ++ outStream.funcTable.Write = MyWrite; ++ outStream.data = dest; ++ outStream.rem = *destLen; ++ outStream.overflow = False; ++ ++ p->writeEndMark = writeEndMark; ++ ++ p->rc.outStream = &outStream.funcTable; ++ res = LzmaEnc_MemPrepare(pp, src, srcLen, 0, alloc, allocBig); ++ if (res == SZ_OK) ++ res = LzmaEnc_Encode2(p, progress); ++ ++ *destLen -= outStream.rem; ++ if (outStream.overflow) ++ return SZ_ERROR_OUTPUT_EOF; ++ return res; ++} ++ ++SRes LzmaEncode(Byte *dest, SizeT *destLen, const Byte *src, SizeT srcLen, ++ const CLzmaEncProps *props, Byte *propsEncoded, SizeT *propsSize, int writeEndMark, ++ ICompressProgress *progress, ISzAlloc *alloc, ISzAlloc *allocBig) ++{ ++ CLzmaEnc *p = (CLzmaEnc *)LzmaEnc_Create(alloc); ++ SRes res; ++ if (p == 0) ++ return SZ_ERROR_MEM; ++ ++ res = LzmaEnc_SetProps(p, props); ++ if (res == SZ_OK) ++ { ++ res = LzmaEnc_WriteProperties(p, propsEncoded, propsSize); ++ if (res == SZ_OK) ++ res = LzmaEnc_MemEncode(p, dest, destLen, src, srcLen, ++ writeEndMark, progress, alloc, allocBig); ++ } ++ ++ LzmaEnc_Destroy(p, alloc, allocBig); ++ return res; ++} +--- /dev/null ++++ b/lib/lzma/Makefile +@@ -0,0 +1,7 @@ ++lzma_compress-objs := LzFind.o LzmaEnc.o ++lzma_decompress-objs := LzmaDec.o ++ ++obj-$(CONFIG_LZMA_COMPRESS) += lzma_compress.o ++obj-$(CONFIG_LZMA_DECOMPRESS) += lzma_decompress.o ++ ++EXTRA_CFLAGS += -Iinclude/linux -Iinclude/linux/lzma -include types.h diff --git a/pkgs/patches-linux-5.15/531-debloat_lzma.patch b/pkgs/patches-linux-5.15/531-debloat_lzma.patch new file mode 100644 index 0000000..2f70eee --- /dev/null +++ b/pkgs/patches-linux-5.15/531-debloat_lzma.patch @@ -0,0 +1,1040 @@ +From 3fd297761ac246c54d7723c57fca95c112b99465 Mon Sep 17 00:00:00 2001 +From: Felix Fietkau <nbd@nbd.name> +Date: Sat, 15 Jul 2017 21:15:44 +0200 +Subject: lzma: de-bloat the lzma library used by jffs2 + +lede-commit: 3fd1dd08fbcbb78b34efefd32c3032e5c99108d6 +Signed-off-by: Felix Fietkau <nbd@nbd.name> +--- + include/linux/lzma/LzFind.h | 17 --- + include/linux/lzma/LzmaDec.h | 101 --------------- + include/linux/lzma/LzmaEnc.h | 20 --- + lib/lzma/LzFind.c | 287 ++++--------------------------------------- + lib/lzma/LzmaDec.c | 86 +------------ + lib/lzma/LzmaEnc.c | 172 ++------------------------ + 6 files changed, 42 insertions(+), 641 deletions(-) + +--- a/include/linux/lzma/LzFind.h ++++ b/include/linux/lzma/LzFind.h +@@ -55,11 +55,6 @@ typedef struct _CMatchFinder + + #define Inline_MatchFinder_GetNumAvailableBytes(p) ((p)->streamPos - (p)->pos) + +-int MatchFinder_NeedMove(CMatchFinder *p); +-Byte *MatchFinder_GetPointerToCurrentPos(CMatchFinder *p); +-void MatchFinder_MoveBlock(CMatchFinder *p); +-void MatchFinder_ReadIfRequired(CMatchFinder *p); +- + void MatchFinder_Construct(CMatchFinder *p); + + /* Conditions: +@@ -70,12 +65,6 @@ int MatchFinder_Create(CMatchFinder *p, + UInt32 keepAddBufferBefore, UInt32 matchMaxLen, UInt32 keepAddBufferAfter, + ISzAlloc *alloc); + void MatchFinder_Free(CMatchFinder *p, ISzAlloc *alloc); +-void MatchFinder_Normalize3(UInt32 subValue, CLzRef *items, UInt32 numItems); +-void MatchFinder_ReduceOffsets(CMatchFinder *p, UInt32 subValue); +- +-UInt32 * GetMatchesSpec1(UInt32 lenLimit, UInt32 curMatch, UInt32 pos, const Byte *buffer, CLzRef *son, +- UInt32 _cyclicBufferPos, UInt32 _cyclicBufferSize, UInt32 _cutValue, +- UInt32 *distances, UInt32 maxLen); + + /* + Conditions: +@@ -102,12 +91,6 @@ typedef struct _IMatchFinder + + void MatchFinder_CreateVTable(CMatchFinder *p, IMatchFinder *vTable); + +-void MatchFinder_Init(CMatchFinder *p); +-UInt32 Bt3Zip_MatchFinder_GetMatches(CMatchFinder *p, UInt32 *distances); +-UInt32 Hc3Zip_MatchFinder_GetMatches(CMatchFinder *p, UInt32 *distances); +-void Bt3Zip_MatchFinder_Skip(CMatchFinder *p, UInt32 num); +-void Hc3Zip_MatchFinder_Skip(CMatchFinder *p, UInt32 num); +- + #ifdef __cplusplus + } + #endif +--- a/include/linux/lzma/LzmaDec.h ++++ b/include/linux/lzma/LzmaDec.h +@@ -31,14 +31,6 @@ typedef struct _CLzmaProps + UInt32 dicSize; + } CLzmaProps; + +-/* LzmaProps_Decode - decodes properties +-Returns: +- SZ_OK +- SZ_ERROR_UNSUPPORTED - Unsupported properties +-*/ +- +-SRes LzmaProps_Decode(CLzmaProps *p, const Byte *data, unsigned size); +- + + /* ---------- LZMA Decoder state ---------- */ + +@@ -70,8 +62,6 @@ typedef struct + + #define LzmaDec_Construct(p) { (p)->dic = 0; (p)->probs = 0; } + +-void LzmaDec_Init(CLzmaDec *p); +- + /* There are two types of LZMA streams: + 0) Stream with end mark. That end mark adds about 6 bytes to compressed size. + 1) Stream without end mark. You must know exact uncompressed size to decompress such stream. */ +@@ -108,97 +98,6 @@ typedef enum + + /* ELzmaStatus is used only as output value for function call */ + +- +-/* ---------- Interfaces ---------- */ +- +-/* There are 3 levels of interfaces: +- 1) Dictionary Interface +- 2) Buffer Interface +- 3) One Call Interface +- You can select any of these interfaces, but don't mix functions from different +- groups for same object. */ +- +- +-/* There are two variants to allocate state for Dictionary Interface: +- 1) LzmaDec_Allocate / LzmaDec_Free +- 2) LzmaDec_AllocateProbs / LzmaDec_FreeProbs +- You can use variant 2, if you set dictionary buffer manually. +- For Buffer Interface you must always use variant 1. +- +-LzmaDec_Allocate* can return: +- SZ_OK +- SZ_ERROR_MEM - Memory allocation error +- SZ_ERROR_UNSUPPORTED - Unsupported properties +-*/ +- +-SRes LzmaDec_AllocateProbs(CLzmaDec *p, const Byte *props, unsigned propsSize, ISzAlloc *alloc); +-void LzmaDec_FreeProbs(CLzmaDec *p, ISzAlloc *alloc); +- +-SRes LzmaDec_Allocate(CLzmaDec *state, const Byte *prop, unsigned propsSize, ISzAlloc *alloc); +-void LzmaDec_Free(CLzmaDec *state, ISzAlloc *alloc); +- +-/* ---------- Dictionary Interface ---------- */ +- +-/* You can use it, if you want to eliminate the overhead for data copying from +- dictionary to some other external buffer. +- You must work with CLzmaDec variables directly in this interface. +- +- STEPS: +- LzmaDec_Constr() +- LzmaDec_Allocate() +- for (each new stream) +- { +- LzmaDec_Init() +- while (it needs more decompression) +- { +- LzmaDec_DecodeToDic() +- use data from CLzmaDec::dic and update CLzmaDec::dicPos +- } +- } +- LzmaDec_Free() +-*/ +- +-/* LzmaDec_DecodeToDic +- +- The decoding to internal dictionary buffer (CLzmaDec::dic). +- You must manually update CLzmaDec::dicPos, if it reaches CLzmaDec::dicBufSize !!! +- +-finishMode: +- It has meaning only if the decoding reaches output limit (dicLimit). +- LZMA_FINISH_ANY - Decode just dicLimit bytes. +- LZMA_FINISH_END - Stream must be finished after dicLimit. +- +-Returns: +- SZ_OK +- status: +- LZMA_STATUS_FINISHED_WITH_MARK +- LZMA_STATUS_NOT_FINISHED +- LZMA_STATUS_NEEDS_MORE_INPUT +- LZMA_STATUS_MAYBE_FINISHED_WITHOUT_MARK +- SZ_ERROR_DATA - Data error +-*/ +- +-SRes LzmaDec_DecodeToDic(CLzmaDec *p, SizeT dicLimit, +- const Byte *src, SizeT *srcLen, ELzmaFinishMode finishMode, ELzmaStatus *status); +- +- +-/* ---------- Buffer Interface ---------- */ +- +-/* It's zlib-like interface. +- See LzmaDec_DecodeToDic description for information about STEPS and return results, +- but you must use LzmaDec_DecodeToBuf instead of LzmaDec_DecodeToDic and you don't need +- to work with CLzmaDec variables manually. +- +-finishMode: +- It has meaning only if the decoding reaches output limit (*destLen). +- LZMA_FINISH_ANY - Decode just destLen bytes. +- LZMA_FINISH_END - Stream must be finished after (*destLen). +-*/ +- +-SRes LzmaDec_DecodeToBuf(CLzmaDec *p, Byte *dest, SizeT *destLen, +- const Byte *src, SizeT *srcLen, ELzmaFinishMode finishMode, ELzmaStatus *status); +- +- + /* ---------- One Call Interface ---------- */ + + /* LzmaDecode +--- a/include/linux/lzma/LzmaEnc.h ++++ b/include/linux/lzma/LzmaEnc.h +@@ -31,9 +31,6 @@ typedef struct _CLzmaEncProps + } CLzmaEncProps; + + void LzmaEncProps_Init(CLzmaEncProps *p); +-void LzmaEncProps_Normalize(CLzmaEncProps *p); +-UInt32 LzmaEncProps_GetDictSize(const CLzmaEncProps *props2); +- + + /* ---------- CLzmaEncHandle Interface ---------- */ + +@@ -53,26 +50,9 @@ CLzmaEncHandle LzmaEnc_Create(ISzAlloc * + void LzmaEnc_Destroy(CLzmaEncHandle p, ISzAlloc *alloc, ISzAlloc *allocBig); + SRes LzmaEnc_SetProps(CLzmaEncHandle p, const CLzmaEncProps *props); + SRes LzmaEnc_WriteProperties(CLzmaEncHandle p, Byte *properties, SizeT *size); +-SRes LzmaEnc_Encode(CLzmaEncHandle p, ISeqOutStream *outStream, ISeqInStream *inStream, +- ICompressProgress *progress, ISzAlloc *alloc, ISzAlloc *allocBig); + SRes LzmaEnc_MemEncode(CLzmaEncHandle p, Byte *dest, SizeT *destLen, const Byte *src, SizeT srcLen, + int writeEndMark, ICompressProgress *progress, ISzAlloc *alloc, ISzAlloc *allocBig); + +-/* ---------- One Call Interface ---------- */ +- +-/* LzmaEncode +-Return code: +- SZ_OK - OK +- SZ_ERROR_MEM - Memory allocation error +- SZ_ERROR_PARAM - Incorrect paramater +- SZ_ERROR_OUTPUT_EOF - output buffer overflow +- SZ_ERROR_THREAD - errors in multithreading functions (only for Mt version) +-*/ +- +-SRes LzmaEncode(Byte *dest, SizeT *destLen, const Byte *src, SizeT srcLen, +- const CLzmaEncProps *props, Byte *propsEncoded, SizeT *propsSize, int writeEndMark, +- ICompressProgress *progress, ISzAlloc *alloc, ISzAlloc *allocBig); +- + #ifdef __cplusplus + } + #endif +--- a/lib/lzma/LzFind.c ++++ b/lib/lzma/LzFind.c +@@ -14,9 +14,15 @@ + + #define kStartMaxLen 3 + ++#if 0 ++#define DIRECT_INPUT p->directInput ++#else ++#define DIRECT_INPUT 1 ++#endif ++ + static void LzInWindow_Free(CMatchFinder *p, ISzAlloc *alloc) + { +- if (!p->directInput) ++ if (!DIRECT_INPUT) + { + alloc->Free(alloc, p->bufferBase); + p->bufferBase = 0; +@@ -28,7 +34,7 @@ static void LzInWindow_Free(CMatchFinder + static int LzInWindow_Create(CMatchFinder *p, UInt32 keepSizeReserv, ISzAlloc *alloc) + { + UInt32 blockSize = p->keepSizeBefore + p->keepSizeAfter + keepSizeReserv; +- if (p->directInput) ++ if (DIRECT_INPUT) + { + p->blockSize = blockSize; + return 1; +@@ -42,12 +48,12 @@ static int LzInWindow_Create(CMatchFinde + return (p->bufferBase != 0); + } + +-Byte *MatchFinder_GetPointerToCurrentPos(CMatchFinder *p) { return p->buffer; } +-Byte MatchFinder_GetIndexByte(CMatchFinder *p, Int32 index) { return p->buffer[index]; } ++static Byte *MatchFinder_GetPointerToCurrentPos(CMatchFinder *p) { return p->buffer; } ++static Byte MatchFinder_GetIndexByte(CMatchFinder *p, Int32 index) { return p->buffer[index]; } + +-UInt32 MatchFinder_GetNumAvailableBytes(CMatchFinder *p) { return p->streamPos - p->pos; } ++static UInt32 MatchFinder_GetNumAvailableBytes(CMatchFinder *p) { return p->streamPos - p->pos; } + +-void MatchFinder_ReduceOffsets(CMatchFinder *p, UInt32 subValue) ++static void MatchFinder_ReduceOffsets(CMatchFinder *p, UInt32 subValue) + { + p->posLimit -= subValue; + p->pos -= subValue; +@@ -58,7 +64,7 @@ static void MatchFinder_ReadBlock(CMatch + { + if (p->streamEndWasReached || p->result != SZ_OK) + return; +- if (p->directInput) ++ if (DIRECT_INPUT) + { + UInt32 curSize = 0xFFFFFFFF - p->streamPos; + if (curSize > p->directInputRem) +@@ -89,7 +95,7 @@ static void MatchFinder_ReadBlock(CMatch + } + } + +-void MatchFinder_MoveBlock(CMatchFinder *p) ++static void MatchFinder_MoveBlock(CMatchFinder *p) + { + memmove(p->bufferBase, + p->buffer - p->keepSizeBefore, +@@ -97,22 +103,14 @@ void MatchFinder_MoveBlock(CMatchFinder + p->buffer = p->bufferBase + p->keepSizeBefore; + } + +-int MatchFinder_NeedMove(CMatchFinder *p) ++static int MatchFinder_NeedMove(CMatchFinder *p) + { +- if (p->directInput) ++ if (DIRECT_INPUT) + return 0; + /* if (p->streamEndWasReached) return 0; */ + return ((size_t)(p->bufferBase + p->blockSize - p->buffer) <= p->keepSizeAfter); + } + +-void MatchFinder_ReadIfRequired(CMatchFinder *p) +-{ +- if (p->streamEndWasReached) +- return; +- if (p->keepSizeAfter >= p->streamPos - p->pos) +- MatchFinder_ReadBlock(p); +-} +- + static void MatchFinder_CheckAndMoveAndRead(CMatchFinder *p) + { + if (MatchFinder_NeedMove(p)) +@@ -268,7 +266,7 @@ static void MatchFinder_SetLimits(CMatch + p->posLimit = p->pos + limit; + } + +-void MatchFinder_Init(CMatchFinder *p) ++static void MatchFinder_Init(CMatchFinder *p) + { + UInt32 i; + for (i = 0; i < p->hashSizeSum; i++) +@@ -287,7 +285,7 @@ static UInt32 MatchFinder_GetSubValue(CM + return (p->pos - p->historySize - 1) & kNormalizeMask; + } + +-void MatchFinder_Normalize3(UInt32 subValue, CLzRef *items, UInt32 numItems) ++static void MatchFinder_Normalize3(UInt32 subValue, CLzRef *items, UInt32 numItems) + { + UInt32 i; + for (i = 0; i < numItems; i++) +@@ -319,38 +317,7 @@ static void MatchFinder_CheckLimits(CMat + MatchFinder_SetLimits(p); + } + +-static UInt32 * Hc_GetMatchesSpec(UInt32 lenLimit, UInt32 curMatch, UInt32 pos, const Byte *cur, CLzRef *son, +- UInt32 _cyclicBufferPos, UInt32 _cyclicBufferSize, UInt32 cutValue, +- UInt32 *distances, UInt32 maxLen) +-{ +- son[_cyclicBufferPos] = curMatch; +- for (;;) +- { +- UInt32 delta = pos - curMatch; +- if (cutValue-- == 0 || delta >= _cyclicBufferSize) +- return distances; +- { +- const Byte *pb = cur - delta; +- curMatch = son[_cyclicBufferPos - delta + ((delta > _cyclicBufferPos) ? _cyclicBufferSize : 0)]; +- if (pb[maxLen] == cur[maxLen] && *pb == *cur) +- { +- UInt32 len = 0; +- while (++len != lenLimit) +- if (pb[len] != cur[len]) +- break; +- if (maxLen < len) +- { +- *distances++ = maxLen = len; +- *distances++ = delta - 1; +- if (len == lenLimit) +- return distances; +- } +- } +- } +- } +-} +- +-UInt32 * GetMatchesSpec1(UInt32 lenLimit, UInt32 curMatch, UInt32 pos, const Byte *cur, CLzRef *son, ++static UInt32 * GetMatchesSpec1(UInt32 lenLimit, UInt32 curMatch, UInt32 pos, const Byte *cur, CLzRef *son, + UInt32 _cyclicBufferPos, UInt32 _cyclicBufferSize, UInt32 cutValue, + UInt32 *distances, UInt32 maxLen) + { +@@ -460,10 +427,10 @@ static void SkipMatchesSpec(UInt32 lenLi + p->buffer++; \ + if (++p->pos == p->posLimit) MatchFinder_CheckLimits(p); + +-#define MOVE_POS_RET MOVE_POS return offset; +- + static void MatchFinder_MovePos(CMatchFinder *p) { MOVE_POS; } + ++#define MOVE_POS_RET MatchFinder_MovePos(p); return offset; ++ + #define GET_MATCHES_HEADER2(minLen, ret_op) \ + UInt32 lenLimit; UInt32 hashValue; const Byte *cur; UInt32 curMatch; \ + lenLimit = p->lenLimit; { if (lenLimit < minLen) { MatchFinder_MovePos(p); ret_op; }} \ +@@ -479,62 +446,7 @@ static void MatchFinder_MovePos(CMatchFi + distances + offset, maxLen) - distances); MOVE_POS_RET; + + #define SKIP_FOOTER \ +- SkipMatchesSpec(lenLimit, curMatch, MF_PARAMS(p)); MOVE_POS; +- +-static UInt32 Bt2_MatchFinder_GetMatches(CMatchFinder *p, UInt32 *distances) +-{ +- UInt32 offset; +- GET_MATCHES_HEADER(2) +- HASH2_CALC; +- curMatch = p->hash[hashValue]; +- p->hash[hashValue] = p->pos; +- offset = 0; +- GET_MATCHES_FOOTER(offset, 1) +-} +- +-UInt32 Bt3Zip_MatchFinder_GetMatches(CMatchFinder *p, UInt32 *distances) +-{ +- UInt32 offset; +- GET_MATCHES_HEADER(3) +- HASH_ZIP_CALC; +- curMatch = p->hash[hashValue]; +- p->hash[hashValue] = p->pos; +- offset = 0; +- GET_MATCHES_FOOTER(offset, 2) +-} +- +-static UInt32 Bt3_MatchFinder_GetMatches(CMatchFinder *p, UInt32 *distances) +-{ +- UInt32 hash2Value, delta2, maxLen, offset; +- GET_MATCHES_HEADER(3) +- +- HASH3_CALC; +- +- delta2 = p->pos - p->hash[hash2Value]; +- curMatch = p->hash[kFix3HashSize + hashValue]; +- +- p->hash[hash2Value] = +- p->hash[kFix3HashSize + hashValue] = p->pos; +- +- +- maxLen = 2; +- offset = 0; +- if (delta2 < p->cyclicBufferSize && *(cur - delta2) == *cur) +- { +- for (; maxLen != lenLimit; maxLen++) +- if (cur[(ptrdiff_t)maxLen - delta2] != cur[maxLen]) +- break; +- distances[0] = maxLen; +- distances[1] = delta2 - 1; +- offset = 2; +- if (maxLen == lenLimit) +- { +- SkipMatchesSpec(lenLimit, curMatch, MF_PARAMS(p)); +- MOVE_POS_RET; +- } +- } +- GET_MATCHES_FOOTER(offset, maxLen) +-} ++ SkipMatchesSpec(lenLimit, curMatch, MF_PARAMS(p)); MatchFinder_MovePos(p); + + static UInt32 Bt4_MatchFinder_GetMatches(CMatchFinder *p, UInt32 *distances) + { +@@ -583,108 +495,6 @@ static UInt32 Bt4_MatchFinder_GetMatches + GET_MATCHES_FOOTER(offset, maxLen) + } + +-static UInt32 Hc4_MatchFinder_GetMatches(CMatchFinder *p, UInt32 *distances) +-{ +- UInt32 hash2Value, hash3Value, delta2, delta3, maxLen, offset; +- GET_MATCHES_HEADER(4) +- +- HASH4_CALC; +- +- delta2 = p->pos - p->hash[ hash2Value]; +- delta3 = p->pos - p->hash[kFix3HashSize + hash3Value]; +- curMatch = p->hash[kFix4HashSize + hashValue]; +- +- p->hash[ hash2Value] = +- p->hash[kFix3HashSize + hash3Value] = +- p->hash[kFix4HashSize + hashValue] = p->pos; +- +- maxLen = 1; +- offset = 0; +- if (delta2 < p->cyclicBufferSize && *(cur - delta2) == *cur) +- { +- distances[0] = maxLen = 2; +- distances[1] = delta2 - 1; +- offset = 2; +- } +- if (delta2 != delta3 && delta3 < p->cyclicBufferSize && *(cur - delta3) == *cur) +- { +- maxLen = 3; +- distances[offset + 1] = delta3 - 1; +- offset += 2; +- delta2 = delta3; +- } +- if (offset != 0) +- { +- for (; maxLen != lenLimit; maxLen++) +- if (cur[(ptrdiff_t)maxLen - delta2] != cur[maxLen]) +- break; +- distances[offset - 2] = maxLen; +- if (maxLen == lenLimit) +- { +- p->son[p->cyclicBufferPos] = curMatch; +- MOVE_POS_RET; +- } +- } +- if (maxLen < 3) +- maxLen = 3; +- offset = (UInt32)(Hc_GetMatchesSpec(lenLimit, curMatch, MF_PARAMS(p), +- distances + offset, maxLen) - (distances)); +- MOVE_POS_RET +-} +- +-UInt32 Hc3Zip_MatchFinder_GetMatches(CMatchFinder *p, UInt32 *distances) +-{ +- UInt32 offset; +- GET_MATCHES_HEADER(3) +- HASH_ZIP_CALC; +- curMatch = p->hash[hashValue]; +- p->hash[hashValue] = p->pos; +- offset = (UInt32)(Hc_GetMatchesSpec(lenLimit, curMatch, MF_PARAMS(p), +- distances, 2) - (distances)); +- MOVE_POS_RET +-} +- +-static void Bt2_MatchFinder_Skip(CMatchFinder *p, UInt32 num) +-{ +- do +- { +- SKIP_HEADER(2) +- HASH2_CALC; +- curMatch = p->hash[hashValue]; +- p->hash[hashValue] = p->pos; +- SKIP_FOOTER +- } +- while (--num != 0); +-} +- +-void Bt3Zip_MatchFinder_Skip(CMatchFinder *p, UInt32 num) +-{ +- do +- { +- SKIP_HEADER(3) +- HASH_ZIP_CALC; +- curMatch = p->hash[hashValue]; +- p->hash[hashValue] = p->pos; +- SKIP_FOOTER +- } +- while (--num != 0); +-} +- +-static void Bt3_MatchFinder_Skip(CMatchFinder *p, UInt32 num) +-{ +- do +- { +- UInt32 hash2Value; +- SKIP_HEADER(3) +- HASH3_CALC; +- curMatch = p->hash[kFix3HashSize + hashValue]; +- p->hash[hash2Value] = +- p->hash[kFix3HashSize + hashValue] = p->pos; +- SKIP_FOOTER +- } +- while (--num != 0); +-} +- + static void Bt4_MatchFinder_Skip(CMatchFinder *p, UInt32 num) + { + do +@@ -701,61 +511,12 @@ static void Bt4_MatchFinder_Skip(CMatchF + while (--num != 0); + } + +-static void Hc4_MatchFinder_Skip(CMatchFinder *p, UInt32 num) +-{ +- do +- { +- UInt32 hash2Value, hash3Value; +- SKIP_HEADER(4) +- HASH4_CALC; +- curMatch = p->hash[kFix4HashSize + hashValue]; +- p->hash[ hash2Value] = +- p->hash[kFix3HashSize + hash3Value] = +- p->hash[kFix4HashSize + hashValue] = p->pos; +- p->son[p->cyclicBufferPos] = curMatch; +- MOVE_POS +- } +- while (--num != 0); +-} +- +-void Hc3Zip_MatchFinder_Skip(CMatchFinder *p, UInt32 num) +-{ +- do +- { +- SKIP_HEADER(3) +- HASH_ZIP_CALC; +- curMatch = p->hash[hashValue]; +- p->hash[hashValue] = p->pos; +- p->son[p->cyclicBufferPos] = curMatch; +- MOVE_POS +- } +- while (--num != 0); +-} +- + void MatchFinder_CreateVTable(CMatchFinder *p, IMatchFinder *vTable) + { + vTable->Init = (Mf_Init_Func)MatchFinder_Init; + vTable->GetIndexByte = (Mf_GetIndexByte_Func)MatchFinder_GetIndexByte; + vTable->GetNumAvailableBytes = (Mf_GetNumAvailableBytes_Func)MatchFinder_GetNumAvailableBytes; + vTable->GetPointerToCurrentPos = (Mf_GetPointerToCurrentPos_Func)MatchFinder_GetPointerToCurrentPos; +- if (!p->btMode) +- { +- vTable->GetMatches = (Mf_GetMatches_Func)Hc4_MatchFinder_GetMatches; +- vTable->Skip = (Mf_Skip_Func)Hc4_MatchFinder_Skip; +- } +- else if (p->numHashBytes == 2) +- { +- vTable->GetMatches = (Mf_GetMatches_Func)Bt2_MatchFinder_GetMatches; +- vTable->Skip = (Mf_Skip_Func)Bt2_MatchFinder_Skip; +- } +- else if (p->numHashBytes == 3) +- { +- vTable->GetMatches = (Mf_GetMatches_Func)Bt3_MatchFinder_GetMatches; +- vTable->Skip = (Mf_Skip_Func)Bt3_MatchFinder_Skip; +- } +- else +- { +- vTable->GetMatches = (Mf_GetMatches_Func)Bt4_MatchFinder_GetMatches; +- vTable->Skip = (Mf_Skip_Func)Bt4_MatchFinder_Skip; +- } ++ vTable->GetMatches = (Mf_GetMatches_Func)Bt4_MatchFinder_GetMatches; ++ vTable->Skip = (Mf_Skip_Func)Bt4_MatchFinder_Skip; + } +--- a/lib/lzma/LzmaDec.c ++++ b/lib/lzma/LzmaDec.c +@@ -682,7 +682,7 @@ static void LzmaDec_InitRc(CLzmaDec *p, + p->needFlush = 0; + } + +-void LzmaDec_InitDicAndState(CLzmaDec *p, Bool initDic, Bool initState) ++static void LzmaDec_InitDicAndState(CLzmaDec *p, Bool initDic, Bool initState) + { + p->needFlush = 1; + p->remainLen = 0; +@@ -698,7 +698,7 @@ void LzmaDec_InitDicAndState(CLzmaDec *p + p->needInitState = 1; + } + +-void LzmaDec_Init(CLzmaDec *p) ++static void LzmaDec_Init(CLzmaDec *p) + { + p->dicPos = 0; + LzmaDec_InitDicAndState(p, True, True); +@@ -716,7 +716,7 @@ static void LzmaDec_InitStateReal(CLzmaD + p->needInitState = 0; + } + +-SRes LzmaDec_DecodeToDic(CLzmaDec *p, SizeT dicLimit, const Byte *src, SizeT *srcLen, ++static SRes LzmaDec_DecodeToDic(CLzmaDec *p, SizeT dicLimit, const Byte *src, SizeT *srcLen, + ELzmaFinishMode finishMode, ELzmaStatus *status) + { + SizeT inSize = *srcLen; +@@ -837,65 +837,13 @@ SRes LzmaDec_DecodeToDic(CLzmaDec *p, Si + return (p->code == 0) ? SZ_OK : SZ_ERROR_DATA; + } + +-SRes LzmaDec_DecodeToBuf(CLzmaDec *p, Byte *dest, SizeT *destLen, const Byte *src, SizeT *srcLen, ELzmaFinishMode finishMode, ELzmaStatus *status) +-{ +- SizeT outSize = *destLen; +- SizeT inSize = *srcLen; +- *srcLen = *destLen = 0; +- for (;;) +- { +- SizeT inSizeCur = inSize, outSizeCur, dicPos; +- ELzmaFinishMode curFinishMode; +- SRes res; +- if (p->dicPos == p->dicBufSize) +- p->dicPos = 0; +- dicPos = p->dicPos; +- if (outSize > p->dicBufSize - dicPos) +- { +- outSizeCur = p->dicBufSize; +- curFinishMode = LZMA_FINISH_ANY; +- } +- else +- { +- outSizeCur = dicPos + outSize; +- curFinishMode = finishMode; +- } +- +- res = LzmaDec_DecodeToDic(p, outSizeCur, src, &inSizeCur, curFinishMode, status); +- src += inSizeCur; +- inSize -= inSizeCur; +- *srcLen += inSizeCur; +- outSizeCur = p->dicPos - dicPos; +- memcpy(dest, p->dic + dicPos, outSizeCur); +- dest += outSizeCur; +- outSize -= outSizeCur; +- *destLen += outSizeCur; +- if (res != 0) +- return res; +- if (outSizeCur == 0 || outSize == 0) +- return SZ_OK; +- } +-} +- +-void LzmaDec_FreeProbs(CLzmaDec *p, ISzAlloc *alloc) ++static void LzmaDec_FreeProbs(CLzmaDec *p, ISzAlloc *alloc) + { + alloc->Free(alloc, p->probs); + p->probs = 0; + } + +-static void LzmaDec_FreeDict(CLzmaDec *p, ISzAlloc *alloc) +-{ +- alloc->Free(alloc, p->dic); +- p->dic = 0; +-} +- +-void LzmaDec_Free(CLzmaDec *p, ISzAlloc *alloc) +-{ +- LzmaDec_FreeProbs(p, alloc); +- LzmaDec_FreeDict(p, alloc); +-} +- +-SRes LzmaProps_Decode(CLzmaProps *p, const Byte *data, unsigned size) ++static SRes LzmaProps_Decode(CLzmaProps *p, const Byte *data, unsigned size) + { + UInt32 dicSize; + Byte d; +@@ -935,7 +883,7 @@ static SRes LzmaDec_AllocateProbs2(CLzma + return SZ_OK; + } + +-SRes LzmaDec_AllocateProbs(CLzmaDec *p, const Byte *props, unsigned propsSize, ISzAlloc *alloc) ++static SRes LzmaDec_AllocateProbs(CLzmaDec *p, const Byte *props, unsigned propsSize, ISzAlloc *alloc) + { + CLzmaProps propNew; + RINOK(LzmaProps_Decode(&propNew, props, propsSize)); +@@ -943,28 +891,6 @@ SRes LzmaDec_AllocateProbs(CLzmaDec *p, + p->prop = propNew; + return SZ_OK; + } +- +-SRes LzmaDec_Allocate(CLzmaDec *p, const Byte *props, unsigned propsSize, ISzAlloc *alloc) +-{ +- CLzmaProps propNew; +- SizeT dicBufSize; +- RINOK(LzmaProps_Decode(&propNew, props, propsSize)); +- RINOK(LzmaDec_AllocateProbs2(p, &propNew, alloc)); +- dicBufSize = propNew.dicSize; +- if (p->dic == 0 || dicBufSize != p->dicBufSize) +- { +- LzmaDec_FreeDict(p, alloc); +- p->dic = (Byte *)alloc->Alloc(alloc, dicBufSize); +- if (p->dic == 0) +- { +- LzmaDec_FreeProbs(p, alloc); +- return SZ_ERROR_MEM; +- } +- } +- p->dicBufSize = dicBufSize; +- p->prop = propNew; +- return SZ_OK; +-} + + SRes LzmaDecode(Byte *dest, SizeT *destLen, const Byte *src, SizeT *srcLen, + const Byte *propData, unsigned propSize, ELzmaFinishMode finishMode, +--- a/lib/lzma/LzmaEnc.c ++++ b/lib/lzma/LzmaEnc.c +@@ -53,7 +53,7 @@ void LzmaEncProps_Init(CLzmaEncProps *p) + p->writeEndMark = 0; + } + +-void LzmaEncProps_Normalize(CLzmaEncProps *p) ++static void LzmaEncProps_Normalize(CLzmaEncProps *p) + { + int level = p->level; + if (level < 0) level = 5; +@@ -76,7 +76,7 @@ void LzmaEncProps_Normalize(CLzmaEncProp + #endif + } + +-UInt32 LzmaEncProps_GetDictSize(const CLzmaEncProps *props2) ++static UInt32 __maybe_unused LzmaEncProps_GetDictSize(const CLzmaEncProps *props2) + { + CLzmaEncProps props = *props2; + LzmaEncProps_Normalize(&props); +@@ -93,7 +93,7 @@ UInt32 LzmaEncProps_GetDictSize(const CL + + #define BSR2_RET(pos, res) { unsigned long i; _BitScanReverse(&i, (pos)); res = (i + i) + ((pos >> (i - 1)) & 1); } + +-UInt32 GetPosSlot1(UInt32 pos) ++static UInt32 GetPosSlot1(UInt32 pos) + { + UInt32 res; + BSR2_RET(pos, res); +@@ -107,7 +107,7 @@ UInt32 GetPosSlot1(UInt32 pos) + #define kNumLogBits (9 + (int)sizeof(size_t) / 2) + #define kDicLogSizeMaxCompress ((kNumLogBits - 1) * 2 + 7) + +-void LzmaEnc_FastPosInit(Byte *g_FastPos) ++static void LzmaEnc_FastPosInit(Byte *g_FastPos) + { + int c = 2, slotFast; + g_FastPos[0] = 0; +@@ -339,58 +339,6 @@ typedef struct + CSaveState saveState; + } CLzmaEnc; + +-void LzmaEnc_SaveState(CLzmaEncHandle pp) +-{ +- CLzmaEnc *p = (CLzmaEnc *)pp; +- CSaveState *dest = &p->saveState; +- int i; +- dest->lenEnc = p->lenEnc; +- dest->repLenEnc = p->repLenEnc; +- dest->state = p->state; +- +- for (i = 0; i < kNumStates; i++) +- { +- memcpy(dest->isMatch[i], p->isMatch[i], sizeof(p->isMatch[i])); +- memcpy(dest->isRep0Long[i], p->isRep0Long[i], sizeof(p->isRep0Long[i])); +- } +- for (i = 0; i < kNumLenToPosStates; i++) +- memcpy(dest->posSlotEncoder[i], p->posSlotEncoder[i], sizeof(p->posSlotEncoder[i])); +- memcpy(dest->isRep, p->isRep, sizeof(p->isRep)); +- memcpy(dest->isRepG0, p->isRepG0, sizeof(p->isRepG0)); +- memcpy(dest->isRepG1, p->isRepG1, sizeof(p->isRepG1)); +- memcpy(dest->isRepG2, p->isRepG2, sizeof(p->isRepG2)); +- memcpy(dest->posEncoders, p->posEncoders, sizeof(p->posEncoders)); +- memcpy(dest->posAlignEncoder, p->posAlignEncoder, sizeof(p->posAlignEncoder)); +- memcpy(dest->reps, p->reps, sizeof(p->reps)); +- memcpy(dest->litProbs, p->litProbs, (0x300 << p->lclp) * sizeof(CLzmaProb)); +-} +- +-void LzmaEnc_RestoreState(CLzmaEncHandle pp) +-{ +- CLzmaEnc *dest = (CLzmaEnc *)pp; +- const CSaveState *p = &dest->saveState; +- int i; +- dest->lenEnc = p->lenEnc; +- dest->repLenEnc = p->repLenEnc; +- dest->state = p->state; +- +- for (i = 0; i < kNumStates; i++) +- { +- memcpy(dest->isMatch[i], p->isMatch[i], sizeof(p->isMatch[i])); +- memcpy(dest->isRep0Long[i], p->isRep0Long[i], sizeof(p->isRep0Long[i])); +- } +- for (i = 0; i < kNumLenToPosStates; i++) +- memcpy(dest->posSlotEncoder[i], p->posSlotEncoder[i], sizeof(p->posSlotEncoder[i])); +- memcpy(dest->isRep, p->isRep, sizeof(p->isRep)); +- memcpy(dest->isRepG0, p->isRepG0, sizeof(p->isRepG0)); +- memcpy(dest->isRepG1, p->isRepG1, sizeof(p->isRepG1)); +- memcpy(dest->isRepG2, p->isRepG2, sizeof(p->isRepG2)); +- memcpy(dest->posEncoders, p->posEncoders, sizeof(p->posEncoders)); +- memcpy(dest->posAlignEncoder, p->posAlignEncoder, sizeof(p->posAlignEncoder)); +- memcpy(dest->reps, p->reps, sizeof(p->reps)); +- memcpy(dest->litProbs, p->litProbs, (0x300 << dest->lclp) * sizeof(CLzmaProb)); +-} +- + SRes LzmaEnc_SetProps(CLzmaEncHandle pp, const CLzmaEncProps *props2) + { + CLzmaEnc *p = (CLzmaEnc *)pp; +@@ -600,7 +548,7 @@ static void LitEnc_EncodeMatched(CRangeE + while (symbol < 0x10000); + } + +-void LzmaEnc_InitPriceTables(UInt32 *ProbPrices) ++static void LzmaEnc_InitPriceTables(UInt32 *ProbPrices) + { + UInt32 i; + for (i = (1 << kNumMoveReducingBits) / 2; i < kBitModelTotal; i += (1 << kNumMoveReducingBits)) +@@ -1676,7 +1624,7 @@ static void FillDistancesPrices(CLzmaEnc + p->matchPriceCount = 0; + } + +-void LzmaEnc_Construct(CLzmaEnc *p) ++static void LzmaEnc_Construct(CLzmaEnc *p) + { + RangeEnc_Construct(&p->rc); + MatchFinder_Construct(&p->matchFinderBase); +@@ -1709,7 +1657,7 @@ CLzmaEncHandle LzmaEnc_Create(ISzAlloc * + return p; + } + +-void LzmaEnc_FreeLits(CLzmaEnc *p, ISzAlloc *alloc) ++static void LzmaEnc_FreeLits(CLzmaEnc *p, ISzAlloc *alloc) + { + alloc->Free(alloc, p->litProbs); + alloc->Free(alloc, p->saveState.litProbs); +@@ -1717,7 +1665,7 @@ void LzmaEnc_FreeLits(CLzmaEnc *p, ISzAl + p->saveState.litProbs = 0; + } + +-void LzmaEnc_Destruct(CLzmaEnc *p, ISzAlloc *alloc, ISzAlloc *allocBig) ++static void LzmaEnc_Destruct(CLzmaEnc *p, ISzAlloc *alloc, ISzAlloc *allocBig) + { + #ifndef _7ZIP_ST + MatchFinderMt_Destruct(&p->matchFinderMt, allocBig); +@@ -1947,7 +1895,7 @@ static SRes LzmaEnc_Alloc(CLzmaEnc *p, U + return SZ_OK; + } + +-void LzmaEnc_Init(CLzmaEnc *p) ++static void LzmaEnc_Init(CLzmaEnc *p) + { + UInt32 i; + p->state = 0; +@@ -2005,7 +1953,7 @@ void LzmaEnc_Init(CLzmaEnc *p) + p->lpMask = (1 << p->lp) - 1; + } + +-void LzmaEnc_InitPrices(CLzmaEnc *p) ++static void LzmaEnc_InitPrices(CLzmaEnc *p) + { + if (!p->fastMode) + { +@@ -2037,26 +1985,6 @@ static SRes LzmaEnc_AllocAndInit(CLzmaEn + return SZ_OK; + } + +-static SRes LzmaEnc_Prepare(CLzmaEncHandle pp, ISeqOutStream *outStream, ISeqInStream *inStream, +- ISzAlloc *alloc, ISzAlloc *allocBig) +-{ +- CLzmaEnc *p = (CLzmaEnc *)pp; +- p->matchFinderBase.stream = inStream; +- p->needInit = 1; +- p->rc.outStream = outStream; +- return LzmaEnc_AllocAndInit(p, 0, alloc, allocBig); +-} +- +-SRes LzmaEnc_PrepareForLzma2(CLzmaEncHandle pp, +- ISeqInStream *inStream, UInt32 keepWindowSize, +- ISzAlloc *alloc, ISzAlloc *allocBig) +-{ +- CLzmaEnc *p = (CLzmaEnc *)pp; +- p->matchFinderBase.stream = inStream; +- p->needInit = 1; +- return LzmaEnc_AllocAndInit(p, keepWindowSize, alloc, allocBig); +-} +- + static void LzmaEnc_SetInputBuf(CLzmaEnc *p, const Byte *src, SizeT srcLen) + { + p->matchFinderBase.directInput = 1; +@@ -2064,7 +1992,7 @@ static void LzmaEnc_SetInputBuf(CLzmaEnc + p->matchFinderBase.directInputRem = srcLen; + } + +-SRes LzmaEnc_MemPrepare(CLzmaEncHandle pp, const Byte *src, SizeT srcLen, ++static SRes LzmaEnc_MemPrepare(CLzmaEncHandle pp, const Byte *src, SizeT srcLen, + UInt32 keepWindowSize, ISzAlloc *alloc, ISzAlloc *allocBig) + { + CLzmaEnc *p = (CLzmaEnc *)pp; +@@ -2074,7 +2002,7 @@ SRes LzmaEnc_MemPrepare(CLzmaEncHandle p + return LzmaEnc_AllocAndInit(p, keepWindowSize, alloc, allocBig); + } + +-void LzmaEnc_Finish(CLzmaEncHandle pp) ++static void LzmaEnc_Finish(CLzmaEncHandle pp) + { + #ifndef _7ZIP_ST + CLzmaEnc *p = (CLzmaEnc *)pp; +@@ -2107,53 +2035,6 @@ static size_t MyWrite(void *pp, const vo + return size; + } + +- +-UInt32 LzmaEnc_GetNumAvailableBytes(CLzmaEncHandle pp) +-{ +- const CLzmaEnc *p = (CLzmaEnc *)pp; +- return p->matchFinder.GetNumAvailableBytes(p->matchFinderObj); +-} +- +-const Byte *LzmaEnc_GetCurBuf(CLzmaEncHandle pp) +-{ +- const CLzmaEnc *p = (CLzmaEnc *)pp; +- return p->matchFinder.GetPointerToCurrentPos(p->matchFinderObj) - p->additionalOffset; +-} +- +-SRes LzmaEnc_CodeOneMemBlock(CLzmaEncHandle pp, Bool reInit, +- Byte *dest, size_t *destLen, UInt32 desiredPackSize, UInt32 *unpackSize) +-{ +- CLzmaEnc *p = (CLzmaEnc *)pp; +- UInt64 nowPos64; +- SRes res; +- CSeqOutStreamBuf outStream; +- +- outStream.funcTable.Write = MyWrite; +- outStream.data = dest; +- outStream.rem = *destLen; +- outStream.overflow = False; +- +- p->writeEndMark = False; +- p->finished = False; +- p->result = SZ_OK; +- +- if (reInit) +- LzmaEnc_Init(p); +- LzmaEnc_InitPrices(p); +- nowPos64 = p->nowPos64; +- RangeEnc_Init(&p->rc); +- p->rc.outStream = &outStream.funcTable; +- +- res = LzmaEnc_CodeOneBlock(p, True, desiredPackSize, *unpackSize); +- +- *unpackSize = (UInt32)(p->nowPos64 - nowPos64); +- *destLen -= outStream.rem; +- if (outStream.overflow) +- return SZ_ERROR_OUTPUT_EOF; +- +- return res; +-} +- + static SRes LzmaEnc_Encode2(CLzmaEnc *p, ICompressProgress *progress) + { + SRes res = SZ_OK; +@@ -2184,13 +2065,6 @@ static SRes LzmaEnc_Encode2(CLzmaEnc *p, + return res; + } + +-SRes LzmaEnc_Encode(CLzmaEncHandle pp, ISeqOutStream *outStream, ISeqInStream *inStream, ICompressProgress *progress, +- ISzAlloc *alloc, ISzAlloc *allocBig) +-{ +- RINOK(LzmaEnc_Prepare(pp, outStream, inStream, alloc, allocBig)); +- return LzmaEnc_Encode2((CLzmaEnc *)pp, progress); +-} +- + SRes LzmaEnc_WriteProperties(CLzmaEncHandle pp, Byte *props, SizeT *size) + { + CLzmaEnc *p = (CLzmaEnc *)pp; +@@ -2247,25 +2121,3 @@ SRes LzmaEnc_MemEncode(CLzmaEncHandle pp + return SZ_ERROR_OUTPUT_EOF; + return res; + } +- +-SRes LzmaEncode(Byte *dest, SizeT *destLen, const Byte *src, SizeT srcLen, +- const CLzmaEncProps *props, Byte *propsEncoded, SizeT *propsSize, int writeEndMark, +- ICompressProgress *progress, ISzAlloc *alloc, ISzAlloc *allocBig) +-{ +- CLzmaEnc *p = (CLzmaEnc *)LzmaEnc_Create(alloc); +- SRes res; +- if (p == 0) +- return SZ_ERROR_MEM; +- +- res = LzmaEnc_SetProps(p, props); +- if (res == SZ_OK) +- { +- res = LzmaEnc_WriteProperties(p, propsEncoded, propsSize); +- if (res == SZ_OK) +- res = LzmaEnc_MemEncode(p, dest, destLen, src, srcLen, +- writeEndMark, progress, alloc, allocBig); +- } +- +- LzmaEnc_Destroy(p, alloc, allocBig); +- return res; +-} diff --git a/pkgs/patches-linux-5.15/532-jffs2_eofdetect.patch b/pkgs/patches-linux-5.15/532-jffs2_eofdetect.patch new file mode 100644 index 0000000..744fbd0 --- /dev/null +++ b/pkgs/patches-linux-5.15/532-jffs2_eofdetect.patch @@ -0,0 +1,65 @@ +From: Felix Fietkau <nbd@nbd.name> +Subject: fs: jffs2: EOF marker + +Signed-off-by: Felix Fietkau <nbd@nbd.name> +--- + fs/jffs2/build.c | 10 ++++++++++ + fs/jffs2/scan.c | 21 +++++++++++++++++++-- + 2 files changed, 29 insertions(+), 2 deletions(-) + +--- a/fs/jffs2/build.c ++++ b/fs/jffs2/build.c +@@ -117,6 +117,16 @@ static int jffs2_build_filesystem(struct + dbg_fsbuild("scanned flash completely\n"); + jffs2_dbg_dump_block_lists_nolock(c); + ++ if (c->flags & (1 << 7)) { ++ printk("%s(): unlocking the mtd device... ", __func__); ++ mtd_unlock(c->mtd, 0, c->mtd->size); ++ printk("done.\n"); ++ ++ printk("%s(): erasing all blocks after the end marker... ", __func__); ++ jffs2_erase_pending_blocks(c, -1); ++ printk("done.\n"); ++ } ++ + dbg_fsbuild("pass 1 starting\n"); + c->flags |= JFFS2_SB_FLAG_BUILDING; + /* Now scan the directory tree, increasing nlink according to every dirent found. */ +--- a/fs/jffs2/scan.c ++++ b/fs/jffs2/scan.c +@@ -148,8 +148,14 @@ int jffs2_scan_medium(struct jffs2_sb_in + /* reset summary info for next eraseblock scan */ + jffs2_sum_reset_collected(s); + +- ret = jffs2_scan_eraseblock(c, jeb, buf_size?flashbuf:(flashbuf+jeb->offset), +- buf_size, s); ++ if (c->flags & (1 << 7)) { ++ if (mtd_block_isbad(c->mtd, jeb->offset)) ++ ret = BLK_STATE_BADBLOCK; ++ else ++ ret = BLK_STATE_ALLFF; ++ } else ++ ret = jffs2_scan_eraseblock(c, jeb, buf_size?flashbuf:(flashbuf+jeb->offset), ++ buf_size, s); + + if (ret < 0) + goto out; +@@ -567,6 +573,17 @@ full_scan: + return err; + } + ++ if ((buf[0] == 0xde) && ++ (buf[1] == 0xad) && ++ (buf[2] == 0xc0) && ++ (buf[3] == 0xde)) { ++ /* end of filesystem. erase everything after this point */ ++ printk("%s(): End of filesystem marker found at 0x%x\n", __func__, jeb->offset); ++ c->flags |= (1 << 7); ++ ++ return BLK_STATE_ALLFF; ++ } ++ + /* We temporarily use 'ofs' as a pointer into the buffer/jeb */ + ofs = 0; + max_ofs = EMPTY_SCAN_SIZE(c->sector_size); diff --git a/pkgs/patches-linux-5.15/600-netfilter_conntrack_flush.patch b/pkgs/patches-linux-5.15/600-netfilter_conntrack_flush.patch new file mode 100644 index 0000000..a88e3d7 --- /dev/null +++ b/pkgs/patches-linux-5.15/600-netfilter_conntrack_flush.patch @@ -0,0 +1,88 @@ +From: Felix Fietkau <nbd@nbd.name> +Subject: netfilter: add support for flushing conntrack via /proc + +lede-commit 8193bbe59a74d34d6a26d4a8cb857b1952905314 +Signed-off-by: Felix Fietkau <nbd@nbd.name> +--- + net/netfilter/nf_conntrack_standalone.c | 59 ++++++++++++++++++++++++++++++++- + 1 file changed, 58 insertions(+), 1 deletion(-) + +--- a/net/netfilter/nf_conntrack_standalone.c ++++ b/net/netfilter/nf_conntrack_standalone.c +@@ -9,6 +9,7 @@ + #include <linux/percpu.h> + #include <linux/netdevice.h> + #include <linux/security.h> ++#include <linux/inet.h> + #include <net/net_namespace.h> + #ifdef CONFIG_SYSCTL + #include <linux/sysctl.h> +@@ -462,6 +463,56 @@ static int ct_cpu_seq_show(struct seq_fi + return 0; + } + ++struct kill_request { ++ u16 family; ++ union nf_inet_addr addr; ++}; ++ ++static int kill_matching(struct nf_conn *i, void *data) ++{ ++ struct kill_request *kr = data; ++ struct nf_conntrack_tuple *t1 = &i->tuplehash[IP_CT_DIR_ORIGINAL].tuple; ++ struct nf_conntrack_tuple *t2 = &i->tuplehash[IP_CT_DIR_REPLY].tuple; ++ ++ if (!kr->family) ++ return 1; ++ ++ if (t1->src.l3num != kr->family) ++ return 0; ++ ++ return (nf_inet_addr_cmp(&kr->addr, &t1->src.u3) || ++ nf_inet_addr_cmp(&kr->addr, &t1->dst.u3) || ++ nf_inet_addr_cmp(&kr->addr, &t2->src.u3) || ++ nf_inet_addr_cmp(&kr->addr, &t2->dst.u3)); ++} ++ ++static int ct_file_write(struct file *file, char *buf, size_t count) ++{ ++ struct seq_file *seq = file->private_data; ++ struct net *net = seq_file_net(seq); ++ struct kill_request kr = { }; ++ ++ if (count == 0) ++ return 0; ++ ++ if (count >= INET6_ADDRSTRLEN) ++ count = INET6_ADDRSTRLEN - 1; ++ ++ if (strnchr(buf, count, ':')) { ++ kr.family = AF_INET6; ++ if (!in6_pton(buf, count, (void *)&kr.addr, '\n', NULL)) ++ return -EINVAL; ++ } else if (strnchr(buf, count, '.')) { ++ kr.family = AF_INET; ++ if (!in4_pton(buf, count, (void *)&kr.addr, '\n', NULL)) ++ return -EINVAL; ++ } ++ ++ nf_ct_iterate_cleanup_net(net, kill_matching, &kr, 0, 0); ++ ++ return 0; ++} ++ + static const struct seq_operations ct_cpu_seq_ops = { + .start = ct_cpu_seq_start, + .next = ct_cpu_seq_next, +@@ -475,8 +526,9 @@ static int nf_conntrack_standalone_init_ + kuid_t root_uid; + kgid_t root_gid; + +- pde = proc_create_net("nf_conntrack", 0440, net->proc_net, &ct_seq_ops, +- sizeof(struct ct_iter_state)); ++ pde = proc_create_net_data_write("nf_conntrack", 0440, net->proc_net, ++ &ct_seq_ops, &ct_file_write, ++ sizeof(struct ct_iter_state), NULL); + if (!pde) + goto out_nf_conntrack; + diff --git a/pkgs/patches-linux-5.15/610-netfilter_match_bypass_default_checks.patch b/pkgs/patches-linux-5.15/610-netfilter_match_bypass_default_checks.patch new file mode 100644 index 0000000..4577031 --- /dev/null +++ b/pkgs/patches-linux-5.15/610-netfilter_match_bypass_default_checks.patch @@ -0,0 +1,110 @@ +From: Felix Fietkau <nbd@nbd.name> +Subject: kernel: add a new version of my netfilter speedup patches for linux 2.6.39 and 3.0 + +Signed-off-by: Felix Fietkau <nbd@nbd.name> +--- + include/uapi/linux/netfilter_ipv4/ip_tables.h | 1 + + net/ipv4/netfilter/ip_tables.c | 37 +++++++++++++++++++++++++++ + 2 files changed, 38 insertions(+) + +--- a/include/uapi/linux/netfilter_ipv4/ip_tables.h ++++ b/include/uapi/linux/netfilter_ipv4/ip_tables.h +@@ -89,6 +89,7 @@ struct ipt_ip { + #define IPT_F_FRAG 0x01 /* Set if rule is a fragment rule */ + #define IPT_F_GOTO 0x02 /* Set if jump is a goto */ + #define IPT_F_MASK 0x03 /* All possible flag bits mask. */ ++#define IPT_F_NO_DEF_MATCH 0x80 /* Internal: no default match rules present */ + + /* Values for "inv" field in struct ipt_ip. */ + #define IPT_INV_VIA_IN 0x01 /* Invert the sense of IN IFACE. */ +--- a/net/ipv4/netfilter/ip_tables.c ++++ b/net/ipv4/netfilter/ip_tables.c +@@ -50,6 +50,9 @@ ip_packet_match(const struct iphdr *ip, + { + unsigned long ret; + ++ if (ipinfo->flags & IPT_F_NO_DEF_MATCH) ++ return true; ++ + if (NF_INVF(ipinfo, IPT_INV_SRCIP, + (ip->saddr & ipinfo->smsk.s_addr) != ipinfo->src.s_addr) || + NF_INVF(ipinfo, IPT_INV_DSTIP, +@@ -80,6 +83,29 @@ ip_packet_match(const struct iphdr *ip, + return true; + } + ++static void ++ip_checkdefault(struct ipt_ip *ip) ++{ ++ static const char iface_mask[IFNAMSIZ] = {}; ++ ++ if (ip->invflags || ip->flags & IPT_F_FRAG) ++ return; ++ ++ if (memcmp(ip->iniface_mask, iface_mask, IFNAMSIZ) != 0) ++ return; ++ ++ if (memcmp(ip->outiface_mask, iface_mask, IFNAMSIZ) != 0) ++ return; ++ ++ if (ip->smsk.s_addr || ip->dmsk.s_addr) ++ return; ++ ++ if (ip->proto) ++ return; ++ ++ ip->flags |= IPT_F_NO_DEF_MATCH; ++} ++ + static bool + ip_checkentry(const struct ipt_ip *ip) + { +@@ -524,6 +550,8 @@ find_check_entry(struct ipt_entry *e, st + struct xt_mtchk_param mtpar; + struct xt_entry_match *ematch; + ++ ip_checkdefault(&e->ip); ++ + if (!xt_percpu_counter_alloc(alloc_state, &e->counters)) + return -ENOMEM; + +@@ -818,6 +846,7 @@ copy_entries_to_user(unsigned int total_ + const struct xt_table_info *private = table->private; + int ret = 0; + const void *loc_cpu_entry; ++ u8 flags; + + counters = alloc_counters(table); + if (IS_ERR(counters)) +@@ -845,6 +874,14 @@ copy_entries_to_user(unsigned int total_ + goto free_counters; + } + ++ flags = e->ip.flags & IPT_F_MASK; ++ if (copy_to_user(userptr + off ++ + offsetof(struct ipt_entry, ip.flags), ++ &flags, sizeof(flags)) != 0) { ++ ret = -EFAULT; ++ goto free_counters; ++ } ++ + for (i = sizeof(struct ipt_entry); + i < e->target_offset; + i += m->u.match_size) { +@@ -1223,12 +1260,15 @@ compat_copy_entry_to_user(struct ipt_ent + compat_uint_t origsize; + const struct xt_entry_match *ematch; + int ret = 0; ++ u8 flags = e->ip.flags & IPT_F_MASK; + + origsize = *size; + ce = *dstptr; + if (copy_to_user(ce, e, sizeof(struct ipt_entry)) != 0 || + copy_to_user(&ce->counters, &counters[i], +- sizeof(counters[i])) != 0) ++ sizeof(counters[i])) != 0 || ++ copy_to_user(&ce->ip.flags, &flags, ++ sizeof(flags)) != 0) + return -EFAULT; + + *dstptr += sizeof(struct compat_ipt_entry); diff --git a/pkgs/patches-linux-5.15/611-netfilter_match_bypass_default_table.patch b/pkgs/patches-linux-5.15/611-netfilter_match_bypass_default_table.patch new file mode 100644 index 0000000..baf738a --- /dev/null +++ b/pkgs/patches-linux-5.15/611-netfilter_match_bypass_default_table.patch @@ -0,0 +1,106 @@ +From: Felix Fietkau <nbd@nbd.name> +Subject: netfilter: match bypass default table + +Signed-off-by: Felix Fietkau <nbd@nbd.name> +--- + net/ipv4/netfilter/ip_tables.c | 79 +++++++++++++++++++++++++++++++----------- + 1 file changed, 58 insertions(+), 21 deletions(-) + +--- a/net/ipv4/netfilter/ip_tables.c ++++ b/net/ipv4/netfilter/ip_tables.c +@@ -246,6 +246,33 @@ struct ipt_entry *ipt_next_entry(const s + return (void *)entry + entry->next_offset; + } + ++static bool ++ipt_handle_default_rule(struct ipt_entry *e, unsigned int *verdict) ++{ ++ struct xt_entry_target *t; ++ struct xt_standard_target *st; ++ ++ if (e->target_offset != sizeof(struct ipt_entry)) ++ return false; ++ ++ if (!(e->ip.flags & IPT_F_NO_DEF_MATCH)) ++ return false; ++ ++ t = ipt_get_target(e); ++ if (t->u.kernel.target->target) ++ return false; ++ ++ st = (struct xt_standard_target *) t; ++ if (st->verdict == XT_RETURN) ++ return false; ++ ++ if (st->verdict >= 0) ++ return false; ++ ++ *verdict = (unsigned)(-st->verdict) - 1; ++ return true; ++} ++ + /* Returns one of the generic firewall policies, like NF_ACCEPT. */ + unsigned int + ipt_do_table(struct sk_buff *skb, +@@ -266,27 +293,28 @@ ipt_do_table(struct sk_buff *skb, + unsigned int addend; + + /* Initialization */ ++ WARN_ON(!(table->valid_hooks & (1 << hook))); ++ local_bh_disable(); ++ private = READ_ONCE(table->private); /* Address dependency. */ ++ cpu = smp_processor_id(); ++ table_base = private->entries; ++ ++ e = get_entry(table_base, private->hook_entry[hook]); ++ if (ipt_handle_default_rule(e, &verdict)) { ++ struct xt_counters *counter; ++ ++ counter = xt_get_this_cpu_counter(&e->counters); ++ ADD_COUNTER(*counter, skb->len, 1); ++ local_bh_enable(); ++ return verdict; ++ } ++ + stackidx = 0; + ip = ip_hdr(skb); + indev = state->in ? state->in->name : nulldevname; + outdev = state->out ? state->out->name : nulldevname; +- /* We handle fragments by dealing with the first fragment as +- * if it was a normal packet. All other fragments are treated +- * normally, except that they will NEVER match rules that ask +- * things we don't know, ie. tcp syn flag or ports). If the +- * rule is also a fragment-specific rule, non-fragments won't +- * match it. */ +- acpar.fragoff = ntohs(ip->frag_off) & IP_OFFSET; +- acpar.thoff = ip_hdrlen(skb); +- acpar.hotdrop = false; +- acpar.state = state; + +- WARN_ON(!(table->valid_hooks & (1 << hook))); +- local_bh_disable(); + addend = xt_write_recseq_begin(); +- private = READ_ONCE(table->private); /* Address dependency. */ +- cpu = smp_processor_id(); +- table_base = private->entries; + jumpstack = (struct ipt_entry **)private->jumpstack[cpu]; + + /* Switch to alternate jumpstack if we're being invoked via TEE. +@@ -299,7 +327,16 @@ ipt_do_table(struct sk_buff *skb, + if (static_key_false(&xt_tee_enabled)) + jumpstack += private->stacksize * __this_cpu_read(nf_skb_duplicated); + +- e = get_entry(table_base, private->hook_entry[hook]); ++ /* We handle fragments by dealing with the first fragment as ++ * if it was a normal packet. All other fragments are treated ++ * normally, except that they will NEVER match rules that ask ++ * things we don't know, ie. tcp syn flag or ports). If the ++ * rule is also a fragment-specific rule, non-fragments won't ++ * match it. */ ++ acpar.fragoff = ntohs(ip->frag_off) & IP_OFFSET; ++ acpar.thoff = ip_hdrlen(skb); ++ acpar.hotdrop = false; ++ acpar.state = state; + + do { + const struct xt_entry_target *t; diff --git a/pkgs/patches-linux-5.15/612-netfilter_match_reduce_memory_access.patch b/pkgs/patches-linux-5.15/612-netfilter_match_reduce_memory_access.patch new file mode 100644 index 0000000..79da677 --- /dev/null +++ b/pkgs/patches-linux-5.15/612-netfilter_match_reduce_memory_access.patch @@ -0,0 +1,22 @@ +From: Felix Fietkau <nbd@nbd.name> +Subject: netfilter: reduce match memory access + +Signed-off-by: Felix Fietkau <nbd@nbd.name> +--- + net/ipv4/netfilter/ip_tables.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +--- a/net/ipv4/netfilter/ip_tables.c ++++ b/net/ipv4/netfilter/ip_tables.c +@@ -53,9 +53,9 @@ ip_packet_match(const struct iphdr *ip, + if (ipinfo->flags & IPT_F_NO_DEF_MATCH) + return true; + +- if (NF_INVF(ipinfo, IPT_INV_SRCIP, ++ if (NF_INVF(ipinfo, IPT_INV_SRCIP, ipinfo->smsk.s_addr && + (ip->saddr & ipinfo->smsk.s_addr) != ipinfo->src.s_addr) || +- NF_INVF(ipinfo, IPT_INV_DSTIP, ++ NF_INVF(ipinfo, IPT_INV_DSTIP, ipinfo->dmsk.s_addr && + (ip->daddr & ipinfo->dmsk.s_addr) != ipinfo->dst.s_addr)) + return false; + diff --git a/pkgs/patches-linux-5.15/613-netfilter_optional_tcp_window_check.patch b/pkgs/patches-linux-5.15/613-netfilter_optional_tcp_window_check.patch new file mode 100644 index 0000000..fd9e63a --- /dev/null +++ b/pkgs/patches-linux-5.15/613-netfilter_optional_tcp_window_check.patch @@ -0,0 +1,83 @@ +From: Felix Fietkau <nbd@nbd.name> +Subject: netfilter: optional tcp window check + +Signed-off-by: Felix Fietkau <nbd@nbd.name> +Signed-off-by: Christian 'Ansuel' Marangi <ansuelsmth@gmail.com> +--- + net/netfilter/nf_conntrack_proto_tcp.c | 13 +++++++++++++ + 1 file changed, 13 insertions(+) + +--- a/net/netfilter/nf_conntrack_proto_tcp.c ++++ b/net/netfilter/nf_conntrack_proto_tcp.c +@@ -465,6 +465,9 @@ static bool tcp_in_window(struct nf_conn + s32 receiver_offset; + bool res, in_recv_win; + ++ if (tn->tcp_no_window_check) ++ return true; ++ + /* + * Get the required data from the packet. + */ +@@ -1160,7 +1163,7 @@ int nf_conntrack_tcp_packet(struct nf_co + IP_CT_TCP_FLAG_DATA_UNACKNOWLEDGED && + timeouts[new_state] > timeouts[TCP_CONNTRACK_UNACK]) + timeout = timeouts[TCP_CONNTRACK_UNACK]; +- else if (ct->proto.tcp.last_win == 0 && ++ else if (!tn->tcp_no_window_check && ct->proto.tcp.last_win == 0 && + timeouts[new_state] > timeouts[TCP_CONNTRACK_RETRANS]) + timeout = timeouts[TCP_CONNTRACK_RETRANS]; + else +@@ -1476,6 +1479,9 @@ void nf_conntrack_tcp_init_net(struct ne + */ + tn->tcp_be_liberal = 0; + ++ /* Skip Windows Check */ ++ tn->tcp_no_window_check = 0; ++ + /* If it's non-zero, we turn off RST sequence number check */ + tn->tcp_ignore_invalid_rst = 0; + +--- a/net/netfilter/nf_conntrack_standalone.c ++++ b/net/netfilter/nf_conntrack_standalone.c +@@ -633,6 +633,7 @@ enum nf_ct_sysctl_index { + #endif + NF_SYSCTL_CT_PROTO_TCP_LOOSE, + NF_SYSCTL_CT_PROTO_TCP_LIBERAL, ++ NF_SYSCTL_CT_PROTO_TCP_NO_WINDOW_CHECK, + NF_SYSCTL_CT_PROTO_TCP_IGNORE_INVALID_RST, + NF_SYSCTL_CT_PROTO_TCP_MAX_RETRANS, + NF_SYSCTL_CT_PROTO_TIMEOUT_UDP, +@@ -849,6 +850,14 @@ static struct ctl_table nf_ct_sysctl_tab + .extra1 = SYSCTL_ZERO, + .extra2 = SYSCTL_ONE, + }, ++ [NF_SYSCTL_CT_PROTO_TCP_NO_WINDOW_CHECK] = { ++ .procname = "nf_conntrack_tcp_no_window_check", ++ .maxlen = sizeof(u8), ++ .mode = 0644, ++ .proc_handler = proc_dou8vec_minmax, ++ .extra1 = SYSCTL_ZERO, ++ .extra2 = SYSCTL_ONE, ++ }, + [NF_SYSCTL_CT_PROTO_TCP_IGNORE_INVALID_RST] = { + .procname = "nf_conntrack_tcp_ignore_invalid_rst", + .maxlen = sizeof(u8), +@@ -1065,6 +1074,7 @@ static void nf_conntrack_standalone_init + + XASSIGN(LOOSE, &tn->tcp_loose); + XASSIGN(LIBERAL, &tn->tcp_be_liberal); ++ XASSIGN(NO_WINDOW_CHECK, &tn->tcp_no_window_check); + XASSIGN(MAX_RETRANS, &tn->tcp_max_retrans); + XASSIGN(IGNORE_INVALID_RST, &tn->tcp_ignore_invalid_rst); + #undef XASSIGN +--- a/include/net/netns/conntrack.h ++++ b/include/net/netns/conntrack.h +@@ -26,6 +26,7 @@ struct nf_tcp_net { + unsigned int timeouts[TCP_CONNTRACK_TIMEOUT_MAX]; + u8 tcp_loose; + u8 tcp_be_liberal; ++ u8 tcp_no_window_check; + u8 tcp_max_retrans; + u8 tcp_ignore_invalid_rst; + #if IS_ENABLED(CONFIG_NF_FLOW_TABLE) diff --git a/pkgs/patches-linux-5.15/620-net_sched-codel-do-not-defer-queue-length-update.patch b/pkgs/patches-linux-5.15/620-net_sched-codel-do-not-defer-queue-length-update.patch new file mode 100644 index 0000000..4b4825a --- /dev/null +++ b/pkgs/patches-linux-5.15/620-net_sched-codel-do-not-defer-queue-length-update.patch @@ -0,0 +1,86 @@ +From: Konstantin Khlebnikov <khlebnikov@yandex-team.ru> +Date: Mon, 21 Aug 2017 11:14:14 +0300 +Subject: [PATCH] net_sched/codel: do not defer queue length update + +When codel wants to drop last packet in ->dequeue() it cannot call +qdisc_tree_reduce_backlog() right away - it will notify parent qdisc +about zero qlen and HTB/HFSC will deactivate class. The same class will +be deactivated second time by caller of ->dequeue(). Currently codel and +fq_codel defer update. This triggers warning in HFSC when it's qlen != 0 +but there is no active classes. + +This patch update parent queue length immediately: just temporary increase +qlen around qdisc_tree_reduce_backlog() to prevent first class deactivation +if we have skb to return. + +This might open another problem in HFSC - now operation peek could fail and +deactivate parent class. + +Signed-off-by: Konstantin Khlebnikov <khlebnikov@yandex-team.ru> +Link: https://bugzilla.kernel.org/show_bug.cgi?id=109581 +--- + +--- a/net/sched/sch_codel.c ++++ b/net/sched/sch_codel.c +@@ -95,11 +95,17 @@ static struct sk_buff *codel_qdisc_deque + &q->stats, qdisc_pkt_len, codel_get_enqueue_time, + drop_func, dequeue_func); + +- /* We cant call qdisc_tree_reduce_backlog() if our qlen is 0, +- * or HTB crashes. Defer it for next round. ++ /* If our qlen is 0 qdisc_tree_reduce_backlog() will deactivate ++ * parent class, dequeue in parent qdisc will do the same if we ++ * return skb. Temporary increment qlen if we have skb. + */ +- if (q->stats.drop_count && sch->q.qlen) { +- qdisc_tree_reduce_backlog(sch, q->stats.drop_count, q->stats.drop_len); ++ if (q->stats.drop_count) { ++ if (skb) ++ sch->q.qlen++; ++ qdisc_tree_reduce_backlog(sch, q->stats.drop_count, ++ q->stats.drop_len); ++ if (skb) ++ sch->q.qlen--; + q->stats.drop_count = 0; + q->stats.drop_len = 0; + } +--- a/net/sched/sch_fq_codel.c ++++ b/net/sched/sch_fq_codel.c +@@ -304,6 +304,21 @@ begin: + &flow->cvars, &q->cstats, qdisc_pkt_len, + codel_get_enqueue_time, drop_func, dequeue_func); + ++ /* If our qlen is 0 qdisc_tree_reduce_backlog() will deactivate ++ * parent class, dequeue in parent qdisc will do the same if we ++ * return skb. Temporary increment qlen if we have skb. ++ */ ++ if (q->cstats.drop_count) { ++ if (skb) ++ sch->q.qlen++; ++ qdisc_tree_reduce_backlog(sch, q->cstats.drop_count, ++ q->cstats.drop_len); ++ if (skb) ++ sch->q.qlen--; ++ q->cstats.drop_count = 0; ++ q->cstats.drop_len = 0; ++ } ++ + if (!skb) { + /* force a pass through old_flows to prevent starvation */ + if ((head == &q->new_flows) && !list_empty(&q->old_flows)) +@@ -314,15 +329,6 @@ begin: + } + qdisc_bstats_update(sch, skb); + flow->deficit -= qdisc_pkt_len(skb); +- /* We cant call qdisc_tree_reduce_backlog() if our qlen is 0, +- * or HTB crashes. Defer it for next round. +- */ +- if (q->cstats.drop_count && sch->q.qlen) { +- qdisc_tree_reduce_backlog(sch, q->cstats.drop_count, +- q->cstats.drop_len); +- q->cstats.drop_count = 0; +- q->cstats.drop_len = 0; +- } + return skb; + } + diff --git a/pkgs/patches-linux-5.15/630-packet_socket_type.patch b/pkgs/patches-linux-5.15/630-packet_socket_type.patch new file mode 100644 index 0000000..c61935f --- /dev/null +++ b/pkgs/patches-linux-5.15/630-packet_socket_type.patch @@ -0,0 +1,138 @@ +From: Felix Fietkau <nbd@nbd.name> +Subject: net: add an optimization for dealing with raw sockets + +lede-commit: 4898039703d7315f0f3431c860123338ec3be0f6 +Signed-off-by: Felix Fietkau <nbd@nbd.name> +--- + include/uapi/linux/if_packet.h | 3 +++ + net/packet/af_packet.c | 34 +++++++++++++++++++++++++++------- + net/packet/internal.h | 1 + + 3 files changed, 31 insertions(+), 7 deletions(-) + +--- a/include/uapi/linux/if_packet.h ++++ b/include/uapi/linux/if_packet.h +@@ -33,6 +33,8 @@ struct sockaddr_ll { + #define PACKET_KERNEL 7 /* To kernel space */ + /* Unused, PACKET_FASTROUTE and PACKET_LOOPBACK are invisible to user space */ + #define PACKET_FASTROUTE 6 /* Fastrouted frame */ ++#define PACKET_MASK_ANY 0xffffffff /* mask for packet type bits */ ++ + + /* Packet socket options */ + +@@ -59,6 +61,7 @@ struct sockaddr_ll { + #define PACKET_ROLLOVER_STATS 21 + #define PACKET_FANOUT_DATA 22 + #define PACKET_IGNORE_OUTGOING 23 ++#define PACKET_RECV_TYPE 24 + + #define PACKET_FANOUT_HASH 0 + #define PACKET_FANOUT_LB 1 +--- a/net/packet/af_packet.c ++++ b/net/packet/af_packet.c +@@ -1825,6 +1825,7 @@ static int packet_rcv_spkt(struct sk_buf + { + struct sock *sk; + struct sockaddr_pkt *spkt; ++ struct packet_sock *po; + + /* + * When we registered the protocol we saved the socket in the data +@@ -1832,6 +1833,7 @@ static int packet_rcv_spkt(struct sk_buf + */ + + sk = pt->af_packet_priv; ++ po = pkt_sk(sk); + + /* + * Yank back the headers [hope the device set this +@@ -1844,7 +1846,7 @@ static int packet_rcv_spkt(struct sk_buf + * so that this procedure is noop. + */ + +- if (skb->pkt_type == PACKET_LOOPBACK) ++ if (!(po->pkt_type & (1 << skb->pkt_type))) + goto out; + + if (!net_eq(dev_net(dev), sock_net(sk))) +@@ -2082,12 +2084,12 @@ static int packet_rcv(struct sk_buff *sk + unsigned int snaplen, res; + bool is_drop_n_account = false; + +- if (skb->pkt_type == PACKET_LOOPBACK) +- goto drop; +- + sk = pt->af_packet_priv; + po = pkt_sk(sk); + ++ if (!(po->pkt_type & (1 << skb->pkt_type))) ++ goto drop; ++ + if (!net_eq(dev_net(dev), sock_net(sk))) + goto drop; + +@@ -2213,12 +2215,12 @@ static int tpacket_rcv(struct sk_buff *s + BUILD_BUG_ON(TPACKET_ALIGN(sizeof(*h.h2)) != 32); + BUILD_BUG_ON(TPACKET_ALIGN(sizeof(*h.h3)) != 48); + +- if (skb->pkt_type == PACKET_LOOPBACK) +- goto drop; +- + sk = pt->af_packet_priv; + po = pkt_sk(sk); + ++ if (!(po->pkt_type & (1 << skb->pkt_type))) ++ goto drop; ++ + if (!net_eq(dev_net(dev), sock_net(sk))) + goto drop; + +@@ -3330,6 +3332,7 @@ static int packet_create(struct net *net + mutex_init(&po->pg_vec_lock); + po->rollover = NULL; + po->prot_hook.func = packet_rcv; ++ po->pkt_type = PACKET_MASK_ANY & ~(1 << PACKET_LOOPBACK); + + if (sock->type == SOCK_PACKET) + po->prot_hook.func = packet_rcv_spkt; +@@ -3971,6 +3974,16 @@ packet_setsockopt(struct socket *sock, i + po->xmit = val ? packet_direct_xmit : dev_queue_xmit; + return 0; + } ++ case PACKET_RECV_TYPE: ++ { ++ unsigned int val; ++ if (optlen != sizeof(val)) ++ return -EINVAL; ++ if (copy_from_sockptr(&val, optval, sizeof(val))) ++ return -EFAULT; ++ po->pkt_type = val & ~BIT(PACKET_LOOPBACK); ++ return 0; ++ } + default: + return -ENOPROTOOPT; + } +@@ -4027,6 +4040,13 @@ static int packet_getsockopt(struct sock + case PACKET_VNET_HDR: + val = po->has_vnet_hdr; + break; ++ case PACKET_RECV_TYPE: ++ if (len > sizeof(unsigned int)) ++ len = sizeof(unsigned int); ++ val = po->pkt_type; ++ ++ data = &val; ++ break; + case PACKET_VERSION: + val = po->tp_version; + break; +--- a/net/packet/internal.h ++++ b/net/packet/internal.h +@@ -137,6 +137,7 @@ struct packet_sock { + int (*xmit)(struct sk_buff *skb); + struct packet_type prot_hook ____cacheline_aligned_in_smp; + atomic_t tp_drops ____cacheline_aligned_in_smp; ++ unsigned int pkt_type; + }; + + static inline struct packet_sock *pkt_sk(struct sock *sk) diff --git a/pkgs/patches-linux-5.15/645-netfilter-connmark-introduce-set-dscpmark.patch b/pkgs/patches-linux-5.15/645-netfilter-connmark-introduce-set-dscpmark.patch new file mode 100644 index 0000000..2d3fe01 --- /dev/null +++ b/pkgs/patches-linux-5.15/645-netfilter-connmark-introduce-set-dscpmark.patch @@ -0,0 +1,212 @@ +From eda40b8c8c82e0f2789d6bc8bf63846dce2e8f32 Mon Sep 17 00:00:00 2001 +From: Kevin Darbyshire-Bryant <ldir@darbyshire-bryant.me.uk> +Date: Sat, 23 Mar 2019 09:29:49 +0000 +Subject: [PATCH] netfilter: connmark: introduce set-dscpmark + +set-dscpmark is a method of storing the DSCP of an ip packet into +conntrack mark. In combination with a suitable tc filter action +(act_ctinfo) DSCP values are able to be stored in the mark on egress and +restored on ingress across links that otherwise alter or bleach DSCP. + +This is useful for qdiscs such as CAKE which are able to shape according +to policies based on DSCP. + +Ingress classification is traditionally a challenging task since +iptables rules haven't yet run and tc filter/eBPF programs are pre-NAT +lookups, hence are unable to see internal IPv4 addresses as used on the +typical home masquerading gateway. + +x_tables CONNMARK set-dscpmark target solves the problem of storing the +DSCP to the conntrack mark in a way suitable for the new act_ctinfo tc +action to restore. + +The set-dscpmark option accepts 2 parameters, a 32bit 'dscpmask' and a +32bit 'statemask'. The dscp mask must be 6 contiguous bits and +represents the area where the DSCP will be stored in the connmark. The +state mask is a minimum 1 bit length mask that must not overlap with the +dscpmask. It represents a flag which is set when the DSCP has been +stored in the conntrack mark. This is useful to implement a 'one shot' +iptables based classification where the 'complicated' iptables rules are +only run once to classify the connection on initial (egress) packet and +subsequent packets are all marked/restored with the same DSCP. A state +mask of zero disables the setting of a status bit/s. + +example syntax with a suitably modified iptables user space application: + +iptables -A QOS_MARK_eth0 -t mangle -j CONNMARK --set-dscpmark 0xfc000000/0x01000000 + +Would store the DSCP in the top 6 bits of the 32bit mark field, and use +the LSB of the top byte as the 'DSCP has been stored' marker. + +|----0xFC----conntrack mark----000000---| +| Bits 31-26 | bit 25 | bit24 |~~~ Bit 0| +| DSCP | unused | flag |unused | +|-----------------------0x01---000000---| + ^ ^ + | | + ---| Conditional flag + | set this when dscp +|-ip diffserv-| stored in mark +| 6 bits | +|-------------| + +an identically configured tc action to restore looks like: + +tc filter show dev eth0 ingress +filter parent ffff: protocol all pref 10 u32 chain 0 +filter parent ffff: protocol all pref 10 u32 chain 0 fh 800: ht divisor 1 +filter parent ffff: protocol all pref 10 u32 chain 0 fh 800::800 order 2048 key ht 800 bkt 0 flowid 1: not_in_hw + match 00000000/00000000 at 0 + action order 1: ctinfo zone 0 pipe + index 2 ref 1 bind 1 dscp 0xfc000000/0x1000000 + + action order 2: mirred (Egress Redirect to device ifb4eth0) stolen + index 1 ref 1 bind 1 + +|----0xFC----conntrack mark----000000---| +| Bits 31-26 | bit 25 | bit24 |~~~ Bit 0| +| DSCP | unused | flag |unused | +|-----------------------0x01---000000---| + | | + | | + ---| Conditional flag + v only restore if set +|-ip diffserv-| +| 6 bits | +|-------------| + +Signed-off-by: Kevin Darbyshire-Bryant <ldir@darbyshire-bryant.me.uk> +--- + include/uapi/linux/netfilter/xt_connmark.h | 10 ++++ + net/netfilter/xt_connmark.c | 55 ++++++++++++++++++---- + 2 files changed, 57 insertions(+), 8 deletions(-) + +--- a/include/uapi/linux/netfilter/xt_connmark.h ++++ b/include/uapi/linux/netfilter/xt_connmark.h +@@ -20,6 +20,11 @@ enum { + }; + + enum { ++ XT_CONNMARK_VALUE = (1 << 0), ++ XT_CONNMARK_DSCP = (1 << 1) ++}; ++ ++enum { + D_SHIFT_LEFT = 0, + D_SHIFT_RIGHT, + }; +@@ -34,6 +39,11 @@ struct xt_connmark_tginfo2 { + __u8 shift_dir, shift_bits, mode; + }; + ++struct xt_connmark_tginfo3 { ++ __u32 ctmark, ctmask, nfmask; ++ __u8 shift_dir, shift_bits, mode, func; ++}; ++ + struct xt_connmark_mtinfo1 { + __u32 mark, mask; + __u8 invert; +--- a/net/netfilter/xt_connmark.c ++++ b/net/netfilter/xt_connmark.c +@@ -24,12 +24,13 @@ MODULE_ALIAS("ipt_connmark"); + MODULE_ALIAS("ip6t_connmark"); + + static unsigned int +-connmark_tg_shift(struct sk_buff *skb, const struct xt_connmark_tginfo2 *info) ++connmark_tg_shift(struct sk_buff *skb, const struct xt_connmark_tginfo3 *info) + { + enum ip_conntrack_info ctinfo; + u_int32_t new_targetmark; + struct nf_conn *ct; + u_int32_t newmark; ++ u_int8_t dscp; + + ct = nf_ct_get(skb, &ctinfo); + if (ct == NULL) +@@ -37,12 +38,24 @@ connmark_tg_shift(struct sk_buff *skb, c + + switch (info->mode) { + case XT_CONNMARK_SET: +- newmark = (ct->mark & ~info->ctmask) ^ info->ctmark; +- if (info->shift_dir == D_SHIFT_RIGHT) +- newmark >>= info->shift_bits; +- else +- newmark <<= info->shift_bits; ++ newmark = ct->mark; ++ if (info->func & XT_CONNMARK_VALUE) { ++ newmark = (newmark & ~info->ctmask) ^ info->ctmark; ++ if (info->shift_dir == D_SHIFT_RIGHT) ++ newmark >>= info->shift_bits; ++ else ++ newmark <<= info->shift_bits; ++ } else if (info->func & XT_CONNMARK_DSCP) { ++ if (skb->protocol == htons(ETH_P_IP)) ++ dscp = ipv4_get_dsfield(ip_hdr(skb)) >> 2; ++ else if (skb->protocol == htons(ETH_P_IPV6)) ++ dscp = ipv6_get_dsfield(ipv6_hdr(skb)) >> 2; ++ else /* protocol doesn't have diffserv */ ++ break; + ++ newmark = (newmark & ~info->ctmark) | ++ (info->ctmask | (dscp << info->shift_bits)); ++ } + if (ct->mark != newmark) { + ct->mark = newmark; + nf_conntrack_event_cache(IPCT_MARK, ct); +@@ -81,20 +94,36 @@ static unsigned int + connmark_tg(struct sk_buff *skb, const struct xt_action_param *par) + { + const struct xt_connmark_tginfo1 *info = par->targinfo; +- const struct xt_connmark_tginfo2 info2 = { ++ const struct xt_connmark_tginfo3 info3 = { + .ctmark = info->ctmark, + .ctmask = info->ctmask, + .nfmask = info->nfmask, + .mode = info->mode, ++ .func = XT_CONNMARK_VALUE + }; + +- return connmark_tg_shift(skb, &info2); ++ return connmark_tg_shift(skb, &info3); + } + + static unsigned int + connmark_tg_v2(struct sk_buff *skb, const struct xt_action_param *par) + { + const struct xt_connmark_tginfo2 *info = par->targinfo; ++ const struct xt_connmark_tginfo3 info3 = { ++ .ctmark = info->ctmark, ++ .ctmask = info->ctmask, ++ .nfmask = info->nfmask, ++ .mode = info->mode, ++ .func = XT_CONNMARK_VALUE ++ }; ++ ++ return connmark_tg_shift(skb, &info3); ++} ++ ++static unsigned int ++connmark_tg_v3(struct sk_buff *skb, const struct xt_action_param *par) ++{ ++ const struct xt_connmark_tginfo3 *info = par->targinfo; + + return connmark_tg_shift(skb, info); + } +@@ -165,6 +194,16 @@ static struct xt_target connmark_tg_reg[ + .targetsize = sizeof(struct xt_connmark_tginfo2), + .destroy = connmark_tg_destroy, + .me = THIS_MODULE, ++ }, ++ { ++ .name = "CONNMARK", ++ .revision = 3, ++ .family = NFPROTO_UNSPEC, ++ .checkentry = connmark_tg_check, ++ .target = connmark_tg_v3, ++ .targetsize = sizeof(struct xt_connmark_tginfo3), ++ .destroy = connmark_tg_destroy, ++ .me = THIS_MODULE, + } + }; + diff --git a/pkgs/patches-linux-5.15/650-netfilter-add-xt_FLOWOFFLOAD-target.patch b/pkgs/patches-linux-5.15/650-netfilter-add-xt_FLOWOFFLOAD-target.patch new file mode 100644 index 0000000..961a930 --- /dev/null +++ b/pkgs/patches-linux-5.15/650-netfilter-add-xt_FLOWOFFLOAD-target.patch @@ -0,0 +1,860 @@ +From: Felix Fietkau <nbd@nbd.name> +Date: Tue, 20 Feb 2018 15:56:02 +0100 +Subject: [PATCH] netfilter: add xt_FLOWOFFLOAD target + +Signed-off-by: Felix Fietkau <nbd@nbd.name> +--- + create mode 100644 net/netfilter/xt_OFFLOAD.c + +--- a/net/ipv4/netfilter/Kconfig ++++ b/net/ipv4/netfilter/Kconfig +@@ -56,8 +56,6 @@ config NF_TABLES_ARP + help + This option enables the ARP support for nf_tables. + +-endif # NF_TABLES +- + config NF_FLOW_TABLE_IPV4 + tristate "Netfilter flow table IPv4 module" + depends on NF_FLOW_TABLE +@@ -66,6 +64,8 @@ config NF_FLOW_TABLE_IPV4 + + To compile it as a module, choose M here. + ++endif # NF_TABLES ++ + config NF_DUP_IPV4 + tristate "Netfilter IPv4 packet duplication to alternate destination" + depends on !NF_CONNTRACK || NF_CONNTRACK +--- a/net/ipv6/netfilter/Kconfig ++++ b/net/ipv6/netfilter/Kconfig +@@ -45,7 +45,6 @@ config NFT_FIB_IPV6 + multicast or blackhole. + + endif # NF_TABLES_IPV6 +-endif # NF_TABLES + + config NF_FLOW_TABLE_IPV6 + tristate "Netfilter flow table IPv6 module" +@@ -55,6 +54,8 @@ config NF_FLOW_TABLE_IPV6 + + To compile it as a module, choose M here. + ++endif # NF_TABLES ++ + config NF_DUP_IPV6 + tristate "Netfilter IPv6 packet duplication to alternate destination" + depends on !NF_CONNTRACK || NF_CONNTRACK +--- a/net/netfilter/Kconfig ++++ b/net/netfilter/Kconfig +@@ -707,8 +707,6 @@ config NFT_REJECT_NETDEV + + endif # NF_TABLES_NETDEV + +-endif # NF_TABLES +- + config NF_FLOW_TABLE_INET + tristate "Netfilter flow table mixed IPv4/IPv6 module" + depends on NF_FLOW_TABLE +@@ -717,11 +715,12 @@ config NF_FLOW_TABLE_INET + + To compile it as a module, choose M here. + ++endif # NF_TABLES ++ + config NF_FLOW_TABLE + tristate "Netfilter flow table module" + depends on NETFILTER_INGRESS + depends on NF_CONNTRACK +- depends on NF_TABLES + help + This option adds the flow table core infrastructure. + +@@ -1010,6 +1009,15 @@ config NETFILTER_XT_TARGET_NOTRACK + depends on NETFILTER_ADVANCED + select NETFILTER_XT_TARGET_CT + ++config NETFILTER_XT_TARGET_FLOWOFFLOAD ++ tristate '"FLOWOFFLOAD" target support' ++ depends on NF_FLOW_TABLE ++ depends on NETFILTER_INGRESS ++ help ++ This option adds a `FLOWOFFLOAD' target, which uses the nf_flow_offload ++ module to speed up processing of packets by bypassing the usual ++ netfilter chains ++ + config NETFILTER_XT_TARGET_RATEEST + tristate '"RATEEST" target support' + depends on NETFILTER_ADVANCED +--- a/net/netfilter/Makefile ++++ b/net/netfilter/Makefile +@@ -143,6 +143,7 @@ obj-$(CONFIG_NETFILTER_XT_TARGET_CLASSIF + obj-$(CONFIG_NETFILTER_XT_TARGET_CONNSECMARK) += xt_CONNSECMARK.o + obj-$(CONFIG_NETFILTER_XT_TARGET_CT) += xt_CT.o + obj-$(CONFIG_NETFILTER_XT_TARGET_DSCP) += xt_DSCP.o ++obj-$(CONFIG_NETFILTER_XT_TARGET_FLOWOFFLOAD) += xt_FLOWOFFLOAD.o + obj-$(CONFIG_NETFILTER_XT_TARGET_HL) += xt_HL.o + obj-$(CONFIG_NETFILTER_XT_TARGET_HMARK) += xt_HMARK.o + obj-$(CONFIG_NETFILTER_XT_TARGET_LED) += xt_LED.o +--- /dev/null ++++ b/net/netfilter/xt_FLOWOFFLOAD.c +@@ -0,0 +1,697 @@ ++/* ++ * Copyright (C) 2018-2021 Felix Fietkau <nbd@nbd.name> ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++#include <linux/module.h> ++#include <linux/init.h> ++#include <linux/netfilter.h> ++#include <linux/netfilter/xt_FLOWOFFLOAD.h> ++#include <linux/if_vlan.h> ++#include <net/ip.h> ++#include <net/netfilter/nf_conntrack.h> ++#include <net/netfilter/nf_conntrack_extend.h> ++#include <net/netfilter/nf_conntrack_helper.h> ++#include <net/netfilter/nf_flow_table.h> ++ ++struct xt_flowoffload_hook { ++ struct hlist_node list; ++ struct nf_hook_ops ops; ++ struct net *net; ++ bool registered; ++ bool used; ++}; ++ ++struct xt_flowoffload_table { ++ struct nf_flowtable ft; ++ struct hlist_head hooks; ++ struct delayed_work work; ++}; ++ ++struct nf_forward_info { ++ const struct net_device *indev; ++ const struct net_device *outdev; ++ const struct net_device *hw_outdev; ++ struct id { ++ __u16 id; ++ __be16 proto; ++ } encap[NF_FLOW_TABLE_ENCAP_MAX]; ++ u8 num_encaps; ++ u8 ingress_vlans; ++ u8 h_source[ETH_ALEN]; ++ u8 h_dest[ETH_ALEN]; ++ enum flow_offload_xmit_type xmit_type; ++}; ++ ++static DEFINE_SPINLOCK(hooks_lock); ++ ++struct xt_flowoffload_table flowtable[2]; ++ ++static unsigned int ++xt_flowoffload_net_hook(void *priv, struct sk_buff *skb, ++ const struct nf_hook_state *state) ++{ ++ struct vlan_ethhdr *veth; ++ __be16 proto; ++ ++ switch (skb->protocol) { ++ case htons(ETH_P_8021Q): ++ veth = (struct vlan_ethhdr *)skb_mac_header(skb); ++ proto = veth->h_vlan_encapsulated_proto; ++ break; ++ case htons(ETH_P_PPP_SES): ++ proto = nf_flow_pppoe_proto(skb); ++ break; ++ default: ++ proto = skb->protocol; ++ break; ++ } ++ ++ switch (proto) { ++ case htons(ETH_P_IP): ++ return nf_flow_offload_ip_hook(priv, skb, state); ++ case htons(ETH_P_IPV6): ++ return nf_flow_offload_ipv6_hook(priv, skb, state); ++ } ++ ++ return NF_ACCEPT; ++} ++ ++static int ++xt_flowoffload_create_hook(struct xt_flowoffload_table *table, ++ struct net_device *dev) ++{ ++ struct xt_flowoffload_hook *hook; ++ struct nf_hook_ops *ops; ++ ++ hook = kzalloc(sizeof(*hook), GFP_ATOMIC); ++ if (!hook) ++ return -ENOMEM; ++ ++ ops = &hook->ops; ++ ops->pf = NFPROTO_NETDEV; ++ ops->hooknum = NF_NETDEV_INGRESS; ++ ops->priority = 10; ++ ops->priv = &table->ft; ++ ops->hook = xt_flowoffload_net_hook; ++ ops->dev = dev; ++ ++ hlist_add_head(&hook->list, &table->hooks); ++ mod_delayed_work(system_power_efficient_wq, &table->work, 0); ++ ++ return 0; ++} ++ ++static struct xt_flowoffload_hook * ++flow_offload_lookup_hook(struct xt_flowoffload_table *table, ++ struct net_device *dev) ++{ ++ struct xt_flowoffload_hook *hook; ++ ++ hlist_for_each_entry(hook, &table->hooks, list) { ++ if (hook->ops.dev == dev) ++ return hook; ++ } ++ ++ return NULL; ++} ++ ++static void ++xt_flowoffload_check_device(struct xt_flowoffload_table *table, ++ struct net_device *dev) ++{ ++ struct xt_flowoffload_hook *hook; ++ ++ if (!dev) ++ return; ++ ++ spin_lock_bh(&hooks_lock); ++ hook = flow_offload_lookup_hook(table, dev); ++ if (hook) ++ hook->used = true; ++ else ++ xt_flowoffload_create_hook(table, dev); ++ spin_unlock_bh(&hooks_lock); ++} ++ ++static void ++xt_flowoffload_register_hooks(struct xt_flowoffload_table *table) ++{ ++ struct xt_flowoffload_hook *hook; ++ ++restart: ++ hlist_for_each_entry(hook, &table->hooks, list) { ++ if (hook->registered) ++ continue; ++ ++ hook->registered = true; ++ hook->net = dev_net(hook->ops.dev); ++ spin_unlock_bh(&hooks_lock); ++ nf_register_net_hook(hook->net, &hook->ops); ++ if (table->ft.flags & NF_FLOWTABLE_HW_OFFLOAD) ++ table->ft.type->setup(&table->ft, hook->ops.dev, ++ FLOW_BLOCK_BIND); ++ spin_lock_bh(&hooks_lock); ++ goto restart; ++ } ++ ++} ++ ++static bool ++xt_flowoffload_cleanup_hooks(struct xt_flowoffload_table *table) ++{ ++ struct xt_flowoffload_hook *hook; ++ bool active = false; ++ ++restart: ++ spin_lock_bh(&hooks_lock); ++ hlist_for_each_entry(hook, &table->hooks, list) { ++ if (hook->used || !hook->registered) { ++ active = true; ++ continue; ++ } ++ ++ hlist_del(&hook->list); ++ spin_unlock_bh(&hooks_lock); ++ if (table->ft.flags & NF_FLOWTABLE_HW_OFFLOAD) ++ table->ft.type->setup(&table->ft, hook->ops.dev, ++ FLOW_BLOCK_UNBIND); ++ nf_unregister_net_hook(hook->net, &hook->ops); ++ kfree(hook); ++ goto restart; ++ } ++ spin_unlock_bh(&hooks_lock); ++ ++ return active; ++} ++ ++static void ++xt_flowoffload_check_hook(struct nf_flowtable *flowtable, ++ struct flow_offload *flow, void *data) ++{ ++ struct xt_flowoffload_table *table; ++ struct flow_offload_tuple *tuple0 = &flow->tuplehash[0].tuple; ++ struct flow_offload_tuple *tuple1 = &flow->tuplehash[1].tuple; ++ struct xt_flowoffload_hook *hook; ++ ++ table = container_of(flowtable, struct xt_flowoffload_table, ft); ++ ++ spin_lock_bh(&hooks_lock); ++ hlist_for_each_entry(hook, &table->hooks, list) { ++ if (hook->ops.dev->ifindex != tuple0->iifidx && ++ hook->ops.dev->ifindex != tuple1->iifidx) ++ continue; ++ ++ hook->used = true; ++ } ++ spin_unlock_bh(&hooks_lock); ++} ++ ++static void ++xt_flowoffload_hook_work(struct work_struct *work) ++{ ++ struct xt_flowoffload_table *table; ++ struct xt_flowoffload_hook *hook; ++ int err; ++ ++ table = container_of(work, struct xt_flowoffload_table, work.work); ++ ++ spin_lock_bh(&hooks_lock); ++ xt_flowoffload_register_hooks(table); ++ hlist_for_each_entry(hook, &table->hooks, list) ++ hook->used = false; ++ spin_unlock_bh(&hooks_lock); ++ ++ err = nf_flow_table_iterate(&table->ft, xt_flowoffload_check_hook, ++ NULL); ++ if (err && err != -EAGAIN) ++ goto out; ++ ++ if (!xt_flowoffload_cleanup_hooks(table)) ++ return; ++ ++out: ++ queue_delayed_work(system_power_efficient_wq, &table->work, HZ); ++} ++ ++static bool ++xt_flowoffload_skip(struct sk_buff *skb, int family) ++{ ++ if (skb_sec_path(skb)) ++ return true; ++ ++ if (family == NFPROTO_IPV4) { ++ const struct ip_options *opt = &(IPCB(skb)->opt); ++ ++ if (unlikely(opt->optlen)) ++ return true; ++ } ++ ++ return false; ++} ++ ++static enum flow_offload_xmit_type nf_xmit_type(struct dst_entry *dst) ++{ ++ if (dst_xfrm(dst)) ++ return FLOW_OFFLOAD_XMIT_XFRM; ++ ++ return FLOW_OFFLOAD_XMIT_NEIGH; ++} ++ ++static void nf_default_forward_path(struct nf_flow_route *route, ++ struct dst_entry *dst_cache, ++ enum ip_conntrack_dir dir, ++ struct net_device **dev) ++{ ++ dev[!dir] = dst_cache->dev; ++ route->tuple[!dir].in.ifindex = dst_cache->dev->ifindex; ++ route->tuple[dir].dst = dst_cache; ++ route->tuple[dir].xmit_type = nf_xmit_type(dst_cache); ++} ++ ++static bool nf_is_valid_ether_device(const struct net_device *dev) ++{ ++ if (!dev || (dev->flags & IFF_LOOPBACK) || dev->type != ARPHRD_ETHER || ++ dev->addr_len != ETH_ALEN || !is_valid_ether_addr(dev->dev_addr)) ++ return false; ++ ++ return true; ++} ++ ++static void nf_dev_path_info(const struct net_device_path_stack *stack, ++ struct nf_forward_info *info, ++ unsigned char *ha) ++{ ++ const struct net_device_path *path; ++ int i; ++ ++ memcpy(info->h_dest, ha, ETH_ALEN); ++ ++ for (i = 0; i < stack->num_paths; i++) { ++ path = &stack->path[i]; ++ switch (path->type) { ++ case DEV_PATH_ETHERNET: ++ case DEV_PATH_DSA: ++ case DEV_PATH_VLAN: ++ case DEV_PATH_PPPOE: ++ info->indev = path->dev; ++ if (is_zero_ether_addr(info->h_source)) ++ memcpy(info->h_source, path->dev->dev_addr, ETH_ALEN); ++ ++ if (path->type == DEV_PATH_ETHERNET) ++ break; ++ if (path->type == DEV_PATH_DSA) { ++ i = stack->num_paths; ++ break; ++ } ++ ++ /* DEV_PATH_VLAN and DEV_PATH_PPPOE */ ++ if (info->num_encaps >= NF_FLOW_TABLE_ENCAP_MAX) { ++ info->indev = NULL; ++ break; ++ } ++ if (!info->outdev) ++ info->outdev = path->dev; ++ info->encap[info->num_encaps].id = path->encap.id; ++ info->encap[info->num_encaps].proto = path->encap.proto; ++ info->num_encaps++; ++ if (path->type == DEV_PATH_PPPOE) ++ memcpy(info->h_dest, path->encap.h_dest, ETH_ALEN); ++ break; ++ case DEV_PATH_BRIDGE: ++ if (is_zero_ether_addr(info->h_source)) ++ memcpy(info->h_source, path->dev->dev_addr, ETH_ALEN); ++ ++ switch (path->bridge.vlan_mode) { ++ case DEV_PATH_BR_VLAN_UNTAG_HW: ++ info->ingress_vlans |= BIT(info->num_encaps - 1); ++ break; ++ case DEV_PATH_BR_VLAN_TAG: ++ info->encap[info->num_encaps].id = path->bridge.vlan_id; ++ info->encap[info->num_encaps].proto = path->bridge.vlan_proto; ++ info->num_encaps++; ++ break; ++ case DEV_PATH_BR_VLAN_UNTAG: ++ info->num_encaps--; ++ break; ++ case DEV_PATH_BR_VLAN_KEEP: ++ break; ++ } ++ break; ++ default: ++ info->indev = NULL; ++ break; ++ } ++ } ++ if (!info->outdev) ++ info->outdev = info->indev; ++ ++ info->hw_outdev = info->indev; ++ ++ if (nf_is_valid_ether_device(info->indev)) ++ info->xmit_type = FLOW_OFFLOAD_XMIT_DIRECT; ++} ++ ++static int nf_dev_fill_forward_path(const struct nf_flow_route *route, ++ const struct dst_entry *dst_cache, ++ const struct nf_conn *ct, ++ enum ip_conntrack_dir dir, u8 *ha, ++ struct net_device_path_stack *stack) ++{ ++ const void *daddr = &ct->tuplehash[!dir].tuple.src.u3; ++ struct net_device *dev = dst_cache->dev; ++ struct neighbour *n; ++ u8 nud_state; ++ ++ if (!nf_is_valid_ether_device(dev)) ++ goto out; ++ ++ n = dst_neigh_lookup(dst_cache, daddr); ++ if (!n) ++ return -1; ++ ++ read_lock_bh(&n->lock); ++ nud_state = n->nud_state; ++ ether_addr_copy(ha, n->ha); ++ read_unlock_bh(&n->lock); ++ neigh_release(n); ++ ++ if (!(nud_state & NUD_VALID)) ++ return -1; ++ ++out: ++ return dev_fill_forward_path(dev, ha, stack); ++} ++ ++static void nf_dev_forward_path(struct nf_flow_route *route, ++ const struct nf_conn *ct, ++ enum ip_conntrack_dir dir, ++ struct net_device **devs) ++{ ++ const struct dst_entry *dst = route->tuple[dir].dst; ++ struct net_device_path_stack stack; ++ struct nf_forward_info info = {}; ++ unsigned char ha[ETH_ALEN]; ++ int i; ++ ++ if (nf_dev_fill_forward_path(route, dst, ct, dir, ha, &stack) >= 0) ++ nf_dev_path_info(&stack, &info, ha); ++ ++ devs[!dir] = (struct net_device *)info.indev; ++ if (!info.indev) ++ return; ++ ++ route->tuple[!dir].in.ifindex = info.indev->ifindex; ++ for (i = 0; i < info.num_encaps; i++) { ++ route->tuple[!dir].in.encap[i].id = info.encap[i].id; ++ route->tuple[!dir].in.encap[i].proto = info.encap[i].proto; ++ } ++ route->tuple[!dir].in.num_encaps = info.num_encaps; ++ route->tuple[!dir].in.ingress_vlans = info.ingress_vlans; ++ ++ if (info.xmit_type == FLOW_OFFLOAD_XMIT_DIRECT) { ++ memcpy(route->tuple[dir].out.h_source, info.h_source, ETH_ALEN); ++ memcpy(route->tuple[dir].out.h_dest, info.h_dest, ETH_ALEN); ++ route->tuple[dir].out.ifindex = info.outdev->ifindex; ++ route->tuple[dir].out.hw_ifindex = info.hw_outdev->ifindex; ++ route->tuple[dir].xmit_type = info.xmit_type; ++ } ++} ++ ++static int ++xt_flowoffload_route(struct sk_buff *skb, const struct nf_conn *ct, ++ const struct xt_action_param *par, ++ struct nf_flow_route *route, enum ip_conntrack_dir dir, ++ struct net_device **devs) ++{ ++ struct dst_entry *this_dst = skb_dst(skb); ++ struct dst_entry *other_dst = NULL; ++ struct flowi fl; ++ ++ memset(&fl, 0, sizeof(fl)); ++ switch (xt_family(par)) { ++ case NFPROTO_IPV4: ++ fl.u.ip4.daddr = ct->tuplehash[dir].tuple.src.u3.ip; ++ fl.u.ip4.flowi4_oif = xt_in(par)->ifindex; ++ break; ++ case NFPROTO_IPV6: ++ fl.u.ip6.saddr = ct->tuplehash[!dir].tuple.dst.u3.in6; ++ fl.u.ip6.daddr = ct->tuplehash[dir].tuple.src.u3.in6; ++ fl.u.ip6.flowi6_oif = xt_in(par)->ifindex; ++ break; ++ } ++ ++ nf_route(xt_net(par), &other_dst, &fl, false, xt_family(par)); ++ if (!other_dst) ++ return -ENOENT; ++ ++ nf_default_forward_path(route, this_dst, dir, devs); ++ nf_default_forward_path(route, other_dst, !dir, devs); ++ ++ if (route->tuple[dir].xmit_type == FLOW_OFFLOAD_XMIT_NEIGH && ++ route->tuple[!dir].xmit_type == FLOW_OFFLOAD_XMIT_NEIGH) { ++ nf_dev_forward_path(route, ct, dir, devs); ++ nf_dev_forward_path(route, ct, !dir, devs); ++ } ++ ++ return 0; ++} ++ ++static unsigned int ++flowoffload_tg(struct sk_buff *skb, const struct xt_action_param *par) ++{ ++ struct xt_flowoffload_table *table; ++ const struct xt_flowoffload_target_info *info = par->targinfo; ++ struct tcphdr _tcph, *tcph = NULL; ++ enum ip_conntrack_info ctinfo; ++ enum ip_conntrack_dir dir; ++ struct nf_flow_route route = {}; ++ struct flow_offload *flow = NULL; ++ struct net_device *devs[2] = {}; ++ struct nf_conn *ct; ++ struct net *net; ++ ++ if (xt_flowoffload_skip(skb, xt_family(par))) ++ return XT_CONTINUE; ++ ++ ct = nf_ct_get(skb, &ctinfo); ++ if (ct == NULL) ++ return XT_CONTINUE; ++ ++ switch (ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.protonum) { ++ case IPPROTO_TCP: ++ if (ct->proto.tcp.state != TCP_CONNTRACK_ESTABLISHED) ++ return XT_CONTINUE; ++ ++ tcph = skb_header_pointer(skb, par->thoff, ++ sizeof(_tcph), &_tcph); ++ if (unlikely(!tcph || tcph->fin || tcph->rst)) ++ return XT_CONTINUE; ++ break; ++ case IPPROTO_UDP: ++ break; ++ default: ++ return XT_CONTINUE; ++ } ++ ++ if (nf_ct_ext_exist(ct, NF_CT_EXT_HELPER) || ++ ct->status & (IPS_SEQ_ADJUST | IPS_NAT_CLASH)) ++ return XT_CONTINUE; ++ ++ if (!nf_ct_is_confirmed(ct)) ++ return XT_CONTINUE; ++ ++ devs[dir] = xt_out(par); ++ devs[!dir] = xt_in(par); ++ ++ if (!devs[dir] || !devs[!dir]) ++ return XT_CONTINUE; ++ ++ if (test_and_set_bit(IPS_OFFLOAD_BIT, &ct->status)) ++ return XT_CONTINUE; ++ ++ dir = CTINFO2DIR(ctinfo); ++ ++ if (xt_flowoffload_route(skb, ct, par, &route, dir, devs) < 0) ++ goto err_flow_route; ++ ++ flow = flow_offload_alloc(ct); ++ if (!flow) ++ goto err_flow_alloc; ++ ++ if (flow_offload_route_init(flow, &route) < 0) ++ goto err_flow_add; ++ ++ if (tcph) { ++ ct->proto.tcp.seen[0].flags |= IP_CT_TCP_FLAG_BE_LIBERAL; ++ ct->proto.tcp.seen[1].flags |= IP_CT_TCP_FLAG_BE_LIBERAL; ++ } ++ ++ table = &flowtable[!!(info->flags & XT_FLOWOFFLOAD_HW)]; ++ ++ net = read_pnet(&table->ft.net); ++ if (!net) ++ write_pnet(&table->ft.net, xt_net(par)); ++ ++ if (flow_offload_add(&table->ft, flow) < 0) ++ goto err_flow_add; ++ ++ xt_flowoffload_check_device(table, devs[0]); ++ xt_flowoffload_check_device(table, devs[1]); ++ ++ dst_release(route.tuple[!dir].dst); ++ ++ return XT_CONTINUE; ++ ++err_flow_add: ++ flow_offload_free(flow); ++err_flow_alloc: ++ dst_release(route.tuple[!dir].dst); ++err_flow_route: ++ clear_bit(IPS_OFFLOAD_BIT, &ct->status); ++ ++ return XT_CONTINUE; ++} ++ ++static int flowoffload_chk(const struct xt_tgchk_param *par) ++{ ++ struct xt_flowoffload_target_info *info = par->targinfo; ++ ++ if (info->flags & ~XT_FLOWOFFLOAD_MASK) ++ return -EINVAL; ++ ++ return 0; ++} ++ ++static struct xt_target offload_tg_reg __read_mostly = { ++ .family = NFPROTO_UNSPEC, ++ .name = "FLOWOFFLOAD", ++ .revision = 0, ++ .targetsize = sizeof(struct xt_flowoffload_target_info), ++ .usersize = sizeof(struct xt_flowoffload_target_info), ++ .checkentry = flowoffload_chk, ++ .target = flowoffload_tg, ++ .me = THIS_MODULE, ++}; ++ ++static int flow_offload_netdev_event(struct notifier_block *this, ++ unsigned long event, void *ptr) ++{ ++ struct xt_flowoffload_hook *hook0, *hook1; ++ struct net_device *dev = netdev_notifier_info_to_dev(ptr); ++ ++ if (event != NETDEV_UNREGISTER) ++ return NOTIFY_DONE; ++ ++ spin_lock_bh(&hooks_lock); ++ hook0 = flow_offload_lookup_hook(&flowtable[0], dev); ++ if (hook0) ++ hlist_del(&hook0->list); ++ ++ hook1 = flow_offload_lookup_hook(&flowtable[1], dev); ++ if (hook1) ++ hlist_del(&hook1->list); ++ spin_unlock_bh(&hooks_lock); ++ ++ if (hook0) { ++ nf_unregister_net_hook(hook0->net, &hook0->ops); ++ kfree(hook0); ++ } ++ ++ if (hook1) { ++ nf_unregister_net_hook(hook1->net, &hook1->ops); ++ kfree(hook1); ++ } ++ ++ nf_flow_table_cleanup(dev); ++ ++ return NOTIFY_DONE; ++} ++ ++static struct notifier_block flow_offload_netdev_notifier = { ++ .notifier_call = flow_offload_netdev_event, ++}; ++ ++static int nf_flow_rule_route_inet(struct net *net, ++ const struct flow_offload *flow, ++ enum flow_offload_tuple_dir dir, ++ struct nf_flow_rule *flow_rule) ++{ ++ const struct flow_offload_tuple *flow_tuple = &flow->tuplehash[dir].tuple; ++ int err; ++ ++ switch (flow_tuple->l3proto) { ++ case NFPROTO_IPV4: ++ err = nf_flow_rule_route_ipv4(net, flow, dir, flow_rule); ++ break; ++ case NFPROTO_IPV6: ++ err = nf_flow_rule_route_ipv6(net, flow, dir, flow_rule); ++ break; ++ default: ++ err = -1; ++ break; ++ } ++ ++ return err; ++} ++ ++static struct nf_flowtable_type flowtable_inet = { ++ .family = NFPROTO_INET, ++ .init = nf_flow_table_init, ++ .setup = nf_flow_table_offload_setup, ++ .action = nf_flow_rule_route_inet, ++ .free = nf_flow_table_free, ++ .hook = xt_flowoffload_net_hook, ++ .owner = THIS_MODULE, ++}; ++ ++static int init_flowtable(struct xt_flowoffload_table *tbl) ++{ ++ INIT_DELAYED_WORK(&tbl->work, xt_flowoffload_hook_work); ++ tbl->ft.type = &flowtable_inet; ++ ++ return nf_flow_table_init(&tbl->ft); ++} ++ ++static int __init xt_flowoffload_tg_init(void) ++{ ++ int ret; ++ ++ register_netdevice_notifier(&flow_offload_netdev_notifier); ++ ++ ret = init_flowtable(&flowtable[0]); ++ if (ret) ++ return ret; ++ ++ ret = init_flowtable(&flowtable[1]); ++ if (ret) ++ goto cleanup; ++ ++ flowtable[1].ft.flags = NF_FLOWTABLE_HW_OFFLOAD; ++ ++ ret = xt_register_target(&offload_tg_reg); ++ if (ret) ++ goto cleanup2; ++ ++ return 0; ++ ++cleanup2: ++ nf_flow_table_free(&flowtable[1].ft); ++cleanup: ++ nf_flow_table_free(&flowtable[0].ft); ++ return ret; ++} ++ ++static void __exit xt_flowoffload_tg_exit(void) ++{ ++ xt_unregister_target(&offload_tg_reg); ++ unregister_netdevice_notifier(&flow_offload_netdev_notifier); ++ nf_flow_table_free(&flowtable[0].ft); ++ nf_flow_table_free(&flowtable[1].ft); ++} ++ ++MODULE_LICENSE("GPL"); ++module_init(xt_flowoffload_tg_init); ++module_exit(xt_flowoffload_tg_exit); +--- a/net/netfilter/nf_flow_table_core.c ++++ b/net/netfilter/nf_flow_table_core.c +@@ -7,7 +7,6 @@ + #include <linux/netdevice.h> + #include <net/ip.h> + #include <net/ip6_route.h> +-#include <net/netfilter/nf_tables.h> + #include <net/netfilter/nf_flow_table.h> + #include <net/netfilter/nf_conntrack.h> + #include <net/netfilter/nf_conntrack_core.h> +@@ -380,8 +379,7 @@ flow_offload_lookup(struct nf_flowtable + } + EXPORT_SYMBOL_GPL(flow_offload_lookup); + +-static int +-nf_flow_table_iterate(struct nf_flowtable *flow_table, ++int nf_flow_table_iterate(struct nf_flowtable *flow_table, + void (*iter)(struct nf_flowtable *flowtable, + struct flow_offload *flow, void *data), + void *data) +@@ -435,6 +433,7 @@ static void nf_flow_offload_gc_step(stru + nf_flow_offload_stats(flow_table, flow); + } + } ++EXPORT_SYMBOL_GPL(nf_flow_table_iterate); + + void nf_flow_table_gc_run(struct nf_flowtable *flow_table) + { +--- /dev/null ++++ b/include/uapi/linux/netfilter/xt_FLOWOFFLOAD.h +@@ -0,0 +1,17 @@ ++/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ ++#ifndef _XT_FLOWOFFLOAD_H ++#define _XT_FLOWOFFLOAD_H ++ ++#include <linux/types.h> ++ ++enum { ++ XT_FLOWOFFLOAD_HW = 1 << 0, ++ ++ XT_FLOWOFFLOAD_MASK = XT_FLOWOFFLOAD_HW ++}; ++ ++struct xt_flowoffload_target_info { ++ __u32 flags; ++}; ++ ++#endif /* _XT_FLOWOFFLOAD_H */ +--- a/include/net/netfilter/nf_flow_table.h ++++ b/include/net/netfilter/nf_flow_table.h +@@ -276,6 +276,11 @@ void nf_flow_table_free(struct nf_flowta + + void flow_offload_teardown(struct flow_offload *flow); + ++int nf_flow_table_iterate(struct nf_flowtable *flow_table, ++ void (*iter)(struct nf_flowtable *flowtable, ++ struct flow_offload *flow, void *data), ++ void *data); ++ + void nf_flow_snat_port(const struct flow_offload *flow, + struct sk_buff *skb, unsigned int thoff, + u8 protocol, enum flow_offload_tuple_dir dir); diff --git a/pkgs/patches-linux-5.15/651-wireless_mesh_header.patch b/pkgs/patches-linux-5.15/651-wireless_mesh_header.patch new file mode 100644 index 0000000..12a031e --- /dev/null +++ b/pkgs/patches-linux-5.15/651-wireless_mesh_header.patch @@ -0,0 +1,24 @@ +From 6d3bc769657b0ee7c7506dad9911111c4226a7ea Mon Sep 17 00:00:00 2001 +From: Imre Kaloz <kaloz@openwrt.org> +Date: Fri, 7 Jul 2017 17:21:05 +0200 +Subject: mac80211: increase wireless mesh header size + +lede-commit 3d4466cfd8f75f717efdb1f96fdde3c70d865fc1 +Signed-off-by: Imre Kaloz <kaloz@openwrt.org> +--- + include/linux/netdevice.h | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +--- a/include/linux/netdevice.h ++++ b/include/linux/netdevice.h +@@ -145,8 +145,8 @@ static inline bool dev_xmit_complete(int + + #if defined(CONFIG_HYPERV_NET) + # define LL_MAX_HEADER 128 +-#elif defined(CONFIG_WLAN) || IS_ENABLED(CONFIG_AX25) +-# if defined(CONFIG_MAC80211_MESH) ++#elif defined(CONFIG_WLAN) || IS_ENABLED(CONFIG_AX25) || 1 ++# if defined(CONFIG_MAC80211_MESH) || 1 + # define LL_MAX_HEADER 128 + # else + # define LL_MAX_HEADER 96 diff --git a/pkgs/patches-linux-5.15/655-increase_skb_pad.patch b/pkgs/patches-linux-5.15/655-increase_skb_pad.patch new file mode 100644 index 0000000..9d03fe5 --- /dev/null +++ b/pkgs/patches-linux-5.15/655-increase_skb_pad.patch @@ -0,0 +1,20 @@ +From: Felix Fietkau <nbd@nbd.name> +Subject: kernel: add a few patches for avoiding unnecessary skb reallocations - significantly improves ethernet<->wireless performance + +lede-commit: 6f89cffc9add6939d44a6b54cf9a5e77849aa7fd +Signed-off-by: Felix Fietkau <nbd@nbd.name> +--- + include/linux/skbuff.h | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/include/linux/skbuff.h ++++ b/include/linux/skbuff.h +@@ -2737,7 +2737,7 @@ static inline int pskb_network_may_pull( + * NET_IP_ALIGN(2) + ethernet_header(14) + IP_header(20/40) + ports(8) + */ + #ifndef NET_SKB_PAD +-#define NET_SKB_PAD max(32, L1_CACHE_BYTES) ++#define NET_SKB_PAD max(64, L1_CACHE_BYTES) + #endif + + int ___pskb_trim(struct sk_buff *skb, unsigned int len); diff --git a/pkgs/patches-linux-5.15/660-fq_codel_defaults.patch b/pkgs/patches-linux-5.15/660-fq_codel_defaults.patch new file mode 100644 index 0000000..5541c0b --- /dev/null +++ b/pkgs/patches-linux-5.15/660-fq_codel_defaults.patch @@ -0,0 +1,27 @@ +From a6ccb238939b25851474a279b20367fd24a0e816 Mon Sep 17 00:00:00 2001 +From: Felix Fietkau <nbd@nbd.name> +Date: Fri, 7 Jul 2017 17:21:53 +0200 +Subject: hack: net: fq_codel: tune defaults for small devices + +Assume that x86_64 devices always have a big memory and do not need this +optimization compared to devices with only 32 MB or 64 MB RAM. + +Signed-off-by: Felix Fietkau <nbd@nbd.name> +--- + net/sched/sch_fq_codel.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/net/sched/sch_fq_codel.c ++++ b/net/sched/sch_fq_codel.c +@@ -469,7 +469,11 @@ static int fq_codel_init(struct Qdisc *s + + sch->limit = 10*1024; + q->flows_cnt = 1024; ++#ifdef CONFIG_X86_64 + q->memory_limit = 32 << 20; /* 32 MBytes */ ++#else ++ q->memory_limit = 4 << 20; /* 4 MBytes */ ++#endif + q->drop_batch_size = 64; + q->quantum = psched_mtu(qdisc_dev(sch)); + INIT_LIST_HEAD(&q->new_flows); diff --git a/pkgs/patches-linux-5.15/661-kernel-ct-size-the-hashtable-more-adequately.patch b/pkgs/patches-linux-5.15/661-kernel-ct-size-the-hashtable-more-adequately.patch new file mode 100644 index 0000000..134e210 --- /dev/null +++ b/pkgs/patches-linux-5.15/661-kernel-ct-size-the-hashtable-more-adequately.patch @@ -0,0 +1,25 @@ +From 804fbb3f2ec9283f7b778e057a68bfff440a0be6 Mon Sep 17 00:00:00 2001 +From: Rui Salvaterra <rsalvaterra@gmail.com> +Date: Wed, 30 Mar 2022 22:51:55 +0100 +Subject: [PATCH] kernel: ct: size the hashtable more adequately + +To set the default size of the connection tracking hash table, a divider of +16384 becomes inadequate for a router handling lots of connections. Divide by +2048 instead, making the default size scale better with the available RAM. + +Signed-off-by: Rui Salvaterra <rsalvaterra@gmail.com> +--- + net/netfilter/nf_conntrack_core.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/net/netfilter/nf_conntrack_core.c ++++ b/net/netfilter/nf_conntrack_core.c +@@ -2727,7 +2727,7 @@ int nf_conntrack_init_start(void) + + if (!nf_conntrack_htable_size) { + nf_conntrack_htable_size +- = (((nr_pages << PAGE_SHIFT) / 16384) ++ = (((nr_pages << PAGE_SHIFT) / 2048) + / sizeof(struct hlist_head)); + if (BITS_PER_LONG >= 64 && + nr_pages > (4 * (1024 * 1024 * 1024 / PAGE_SIZE))) diff --git a/pkgs/patches-linux-5.15/666-Add-support-for-MAP-E-FMRs-mesh-mode.patch b/pkgs/patches-linux-5.15/666-Add-support-for-MAP-E-FMRs-mesh-mode.patch new file mode 100644 index 0000000..09efa1e --- /dev/null +++ b/pkgs/patches-linux-5.15/666-Add-support-for-MAP-E-FMRs-mesh-mode.patch @@ -0,0 +1,511 @@ +From: Steven Barth <steven@midlink.org> +Subject: Add support for MAP-E FMRs (mesh mode) + +MAP-E FMRs (draft-ietf-softwire-map-10) are rules for IPv4-communication +between MAP CEs (mesh mode) without the need to forward such data to a +border relay. This is similar to how 6rd works but for IPv4 over IPv6. + +Signed-off-by: Steven Barth <cyrus@openwrt.org> +--- + include/net/ip6_tunnel.h | 13 ++ + include/uapi/linux/if_tunnel.h | 13 ++ + net/ipv6/ip6_tunnel.c | 276 +++++++++++++++++++++++++++++++++++++++-- + 3 files changed, 291 insertions(+), 11 deletions(-) + +--- a/include/net/ip6_tunnel.h ++++ b/include/net/ip6_tunnel.h +@@ -18,6 +18,18 @@ + /* determine capability on a per-packet basis */ + #define IP6_TNL_F_CAP_PER_PACKET 0x40000 + ++/* IPv6 tunnel FMR */ ++struct __ip6_tnl_fmr { ++ struct __ip6_tnl_fmr *next; /* next fmr in list */ ++ struct in6_addr ip6_prefix; ++ struct in_addr ip4_prefix; ++ ++ __u8 ip6_prefix_len; ++ __u8 ip4_prefix_len; ++ __u8 ea_len; ++ __u8 offset; ++}; ++ + struct __ip6_tnl_parm { + char name[IFNAMSIZ]; /* name of tunnel device */ + int link; /* ifindex of underlying L2 interface */ +@@ -29,6 +41,7 @@ struct __ip6_tnl_parm { + __u32 flags; /* tunnel flags */ + struct in6_addr laddr; /* local tunnel end-point address */ + struct in6_addr raddr; /* remote tunnel end-point address */ ++ struct __ip6_tnl_fmr *fmrs; /* FMRs */ + + __be16 i_flags; + __be16 o_flags; +--- a/include/uapi/linux/if_tunnel.h ++++ b/include/uapi/linux/if_tunnel.h +@@ -77,10 +77,23 @@ enum { + IFLA_IPTUN_ENCAP_DPORT, + IFLA_IPTUN_COLLECT_METADATA, + IFLA_IPTUN_FWMARK, ++ IFLA_IPTUN_FMRS, + __IFLA_IPTUN_MAX, + }; + #define IFLA_IPTUN_MAX (__IFLA_IPTUN_MAX - 1) + ++enum { ++ IFLA_IPTUN_FMR_UNSPEC, ++ IFLA_IPTUN_FMR_IP6_PREFIX, ++ IFLA_IPTUN_FMR_IP4_PREFIX, ++ IFLA_IPTUN_FMR_IP6_PREFIX_LEN, ++ IFLA_IPTUN_FMR_IP4_PREFIX_LEN, ++ IFLA_IPTUN_FMR_EA_LEN, ++ IFLA_IPTUN_FMR_OFFSET, ++ __IFLA_IPTUN_FMR_MAX, ++}; ++#define IFLA_IPTUN_FMR_MAX (__IFLA_IPTUN_FMR_MAX - 1) ++ + enum tunnel_encap_types { + TUNNEL_ENCAP_NONE, + TUNNEL_ENCAP_FOU, +--- a/net/ipv6/ip6_tunnel.c ++++ b/net/ipv6/ip6_tunnel.c +@@ -11,6 +11,9 @@ + * linux/net/ipv6/sit.c and linux/net/ipv4/ipip.c + * + * RFC 2473 ++ * ++ * Changes: ++ * Steven Barth <cyrus@openwrt.org>: MAP-E FMR support + */ + + #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt +@@ -67,9 +70,9 @@ static bool log_ecn_error = true; + module_param(log_ecn_error, bool, 0644); + MODULE_PARM_DESC(log_ecn_error, "Log packets received with corrupted ECN"); + +-static u32 HASH(const struct in6_addr *addr1, const struct in6_addr *addr2) ++static u32 HASH(const struct in6_addr *addr) + { +- u32 hash = ipv6_addr_hash(addr1) ^ ipv6_addr_hash(addr2); ++ u32 hash = ipv6_addr_hash(addr); + + return hash_32(hash, IP6_TUNNEL_HASH_SIZE_SHIFT); + } +@@ -114,17 +117,33 @@ static struct ip6_tnl * + ip6_tnl_lookup(struct net *net, int link, + const struct in6_addr *remote, const struct in6_addr *local) + { +- unsigned int hash = HASH(remote, local); ++ unsigned int hash = HASH(local); + struct ip6_tnl *t, *cand = NULL; + struct ip6_tnl_net *ip6n = net_generic(net, ip6_tnl_net_id); + struct in6_addr any; + + for_each_ip6_tunnel_rcu(ip6n->tnls_r_l[hash]) { + if (!ipv6_addr_equal(local, &t->parms.laddr) || +- !ipv6_addr_equal(remote, &t->parms.raddr) || + !(t->dev->flags & IFF_UP)) + continue; + ++ if (!ipv6_addr_equal(remote, &t->parms.raddr)) { ++ struct __ip6_tnl_fmr *fmr; ++ bool found = false; ++ ++ for (fmr = t->parms.fmrs; fmr; fmr = fmr->next) { ++ if (!ipv6_prefix_equal(remote, &fmr->ip6_prefix, ++ fmr->ip6_prefix_len)) ++ continue; ++ ++ found = true; ++ break; ++ } ++ ++ if (!found) ++ continue; ++ } ++ + if (link == t->parms.link) + return t; + else +@@ -132,7 +151,7 @@ ip6_tnl_lookup(struct net *net, int link + } + + memset(&any, 0, sizeof(any)); +- hash = HASH(&any, local); ++ hash = HASH(local); + for_each_ip6_tunnel_rcu(ip6n->tnls_r_l[hash]) { + if (!ipv6_addr_equal(local, &t->parms.laddr) || + !ipv6_addr_any(&t->parms.raddr) || +@@ -145,7 +164,7 @@ ip6_tnl_lookup(struct net *net, int link + cand = t; + } + +- hash = HASH(remote, &any); ++ hash = HASH(&any); + for_each_ip6_tunnel_rcu(ip6n->tnls_r_l[hash]) { + if (!ipv6_addr_equal(remote, &t->parms.raddr) || + !ipv6_addr_any(&t->parms.laddr) || +@@ -194,7 +213,7 @@ ip6_tnl_bucket(struct ip6_tnl_net *ip6n, + + if (!ipv6_addr_any(remote) || !ipv6_addr_any(local)) { + prio = 1; +- h = HASH(remote, local); ++ h = HASH(local); + } + return &ip6n->tnls[prio][h]; + } +@@ -378,6 +397,12 @@ ip6_tnl_dev_uninit(struct net_device *de + struct net *net = t->net; + struct ip6_tnl_net *ip6n = net_generic(net, ip6_tnl_net_id); + ++ while (t->parms.fmrs) { ++ struct __ip6_tnl_fmr *next = t->parms.fmrs->next; ++ kfree(t->parms.fmrs); ++ t->parms.fmrs = next; ++ } ++ + if (dev == ip6n->fb_tnl_dev) + RCU_INIT_POINTER(ip6n->tnls_wc[0], NULL); + else +@@ -790,6 +815,107 @@ int ip6_tnl_rcv_ctl(struct ip6_tnl *t, + } + EXPORT_SYMBOL_GPL(ip6_tnl_rcv_ctl); + ++/** ++ * ip4ip6_fmr_calc - calculate target / source IPv6-address based on FMR ++ * @dest: destination IPv6 address buffer ++ * @skb: received socket buffer ++ * @fmr: MAP FMR ++ * @xmit: Calculate for xmit or rcv ++ **/ ++static void ip4ip6_fmr_calc(struct in6_addr *dest, ++ const struct iphdr *iph, const uint8_t *end, ++ const struct __ip6_tnl_fmr *fmr, bool xmit) ++{ ++ int psidlen = fmr->ea_len - (32 - fmr->ip4_prefix_len); ++ u8 *portp = NULL; ++ bool use_dest_addr; ++ const struct iphdr *dsth = iph; ++ ++ if ((u8*)dsth >= end) ++ return; ++ ++ /* find significant IP header */ ++ if (iph->protocol == IPPROTO_ICMP) { ++ struct icmphdr *ih = (struct icmphdr*)(((u8*)dsth) + dsth->ihl * 4); ++ if (ih && ((u8*)&ih[1]) <= end && ( ++ ih->type == ICMP_DEST_UNREACH || ++ ih->type == ICMP_SOURCE_QUENCH || ++ ih->type == ICMP_TIME_EXCEEDED || ++ ih->type == ICMP_PARAMETERPROB || ++ ih->type == ICMP_REDIRECT)) ++ dsth = (const struct iphdr*)&ih[1]; ++ } ++ ++ /* in xmit-path use dest port by default and source port only if ++ this is an ICMP reply to something else; vice versa in rcv-path */ ++ use_dest_addr = (xmit && dsth == iph) || (!xmit && dsth != iph); ++ ++ /* get dst port */ ++ if (((u8*)&dsth[1]) <= end && ( ++ dsth->protocol == IPPROTO_UDP || ++ dsth->protocol == IPPROTO_TCP || ++ dsth->protocol == IPPROTO_SCTP || ++ dsth->protocol == IPPROTO_DCCP)) { ++ /* for UDP, TCP, SCTP and DCCP source and dest port ++ follow IPv4 header directly */ ++ portp = ((u8*)dsth) + dsth->ihl * 4; ++ ++ if (use_dest_addr) ++ portp += sizeof(u16); ++ } else if (iph->protocol == IPPROTO_ICMP) { ++ struct icmphdr *ih = (struct icmphdr*)(((u8*)dsth) + dsth->ihl * 4); ++ ++ /* use icmp identifier as port */ ++ if (((u8*)&ih) <= end && ( ++ (use_dest_addr && ( ++ ih->type == ICMP_ECHOREPLY || ++ ih->type == ICMP_TIMESTAMPREPLY || ++ ih->type == ICMP_INFO_REPLY || ++ ih->type == ICMP_ADDRESSREPLY)) || ++ (!use_dest_addr && ( ++ ih->type == ICMP_ECHO || ++ ih->type == ICMP_TIMESTAMP || ++ ih->type == ICMP_INFO_REQUEST || ++ ih->type == ICMP_ADDRESS) ++ ))) ++ portp = (u8*)&ih->un.echo.id; ++ } ++ ++ if ((portp && &portp[2] <= end) || psidlen == 0) { ++ int frombyte = fmr->ip6_prefix_len / 8; ++ int fromrem = fmr->ip6_prefix_len % 8; ++ int bytes = sizeof(struct in6_addr) - frombyte; ++ const u32 *addr = (use_dest_addr) ? &iph->daddr : &iph->saddr; ++ u64 eabits = ((u64)ntohl(*addr)) << (32 + fmr->ip4_prefix_len); ++ u64 t = 0; ++ ++ /* extract PSID from port and add it to eabits */ ++ u16 psidbits = 0; ++ if (psidlen > 0) { ++ psidbits = ((u16)portp[0]) << 8 | ((u16)portp[1]); ++ psidbits >>= 16 - psidlen - fmr->offset; ++ psidbits = (u16)(psidbits << (16 - psidlen)); ++ eabits |= ((u64)psidbits) << (48 - (fmr->ea_len - psidlen)); ++ } ++ ++ /* rewrite destination address */ ++ *dest = fmr->ip6_prefix; ++ memcpy(&dest->s6_addr[10], addr, sizeof(*addr)); ++ dest->s6_addr16[7] = htons(psidbits >> (16 - psidlen)); ++ ++ if (bytes > sizeof(u64)) ++ bytes = sizeof(u64); ++ ++ /* insert eabits */ ++ memcpy(&t, &dest->s6_addr[frombyte], bytes); ++ t = be64_to_cpu(t) & ~(((((u64)1) << fmr->ea_len) - 1) ++ << (64 - fmr->ea_len - fromrem)); ++ t = cpu_to_be64(t | (eabits >> fromrem)); ++ memcpy(&dest->s6_addr[frombyte], &t, bytes); ++ } ++} ++ ++ + static int __ip6_tnl_rcv(struct ip6_tnl *tunnel, struct sk_buff *skb, + const struct tnl_ptk_info *tpi, + struct metadata_dst *tun_dst, +@@ -843,6 +969,27 @@ static int __ip6_tnl_rcv(struct ip6_tnl + skb_reset_network_header(skb); + memset(skb->cb, 0, sizeof(struct inet6_skb_parm)); + ++ if (tpi->proto == htons(ETH_P_IP) && tunnel->parms.fmrs && ++ !ipv6_addr_equal(&ipv6h->saddr, &tunnel->parms.raddr)) { ++ /* Packet didn't come from BR, so lookup FMR */ ++ struct __ip6_tnl_fmr *fmr; ++ struct in6_addr expected = tunnel->parms.raddr; ++ for (fmr = tunnel->parms.fmrs; fmr; fmr = fmr->next) ++ if (ipv6_prefix_equal(&ipv6h->saddr, ++ &fmr->ip6_prefix, fmr->ip6_prefix_len)) ++ break; ++ ++ /* Check that IPv6 matches IPv4 source to prevent spoofing */ ++ if (fmr) ++ ip4ip6_fmr_calc(&expected, ip_hdr(skb), ++ skb_tail_pointer(skb), fmr, false); ++ ++ if (!ipv6_addr_equal(&ipv6h->saddr, &expected)) { ++ rcu_read_unlock(); ++ goto drop; ++ } ++ } ++ + __skb_tunnel_rx(skb, tunnel->dev, tunnel->net); + + err = dscp_ecn_decapsulate(tunnel, ipv6h, skb); +@@ -994,6 +1141,7 @@ static void init_tel_txopt(struct ipv6_t + opt->ops.opt_nflen = 8; + } + ++ + /** + * ip6_tnl_addr_conflict - compare packet addresses to tunnel's own + * @t: the outgoing tunnel device +@@ -1274,6 +1422,7 @@ ipxip6_tnl_xmit(struct sk_buff *skb, str + u8 protocol) + { + struct ip6_tnl *t = netdev_priv(dev); ++ struct __ip6_tnl_fmr *fmr; + struct ipv6hdr *ipv6h; + const struct iphdr *iph; + int encap_limit = -1; +@@ -1373,6 +1522,18 @@ ipxip6_tnl_xmit(struct sk_buff *skb, str + fl6.flowi6_uid = sock_net_uid(dev_net(dev), NULL); + dsfield = INET_ECN_encapsulate(dsfield, orig_dsfield); + ++ /* try to find matching FMR */ ++ for (fmr = t->parms.fmrs; fmr; fmr = fmr->next) { ++ unsigned mshift = 32 - fmr->ip4_prefix_len; ++ if (ntohl(fmr->ip4_prefix.s_addr) >> mshift == ++ ntohl(ip_hdr(skb)->daddr) >> mshift) ++ break; ++ } ++ ++ /* change dstaddr according to FMR */ ++ if (fmr) ++ ip4ip6_fmr_calc(&fl6.daddr, ip_hdr(skb), skb_tail_pointer(skb), fmr, true); ++ + if (iptunnel_handle_offloads(skb, SKB_GSO_IPXIP6)) + return -1; + +@@ -1525,6 +1686,14 @@ ip6_tnl_change(struct ip6_tnl *t, const + t->parms.link = p->link; + t->parms.proto = p->proto; + t->parms.fwmark = p->fwmark; ++ ++ while (t->parms.fmrs) { ++ struct __ip6_tnl_fmr *next = t->parms.fmrs->next; ++ kfree(t->parms.fmrs); ++ t->parms.fmrs = next; ++ } ++ t->parms.fmrs = p->fmrs; ++ + dst_cache_reset(&t->dst_cache); + ip6_tnl_link_config(t); + return 0; +@@ -1563,6 +1732,7 @@ ip6_tnl_parm_from_user(struct __ip6_tnl_ + p->flowinfo = u->flowinfo; + p->link = u->link; + p->proto = u->proto; ++ p->fmrs = NULL; + memcpy(p->name, u->name, sizeof(u->name)); + } + +@@ -1949,6 +2119,15 @@ static int ip6_tnl_validate(struct nlatt + return 0; + } + ++static const struct nla_policy ip6_tnl_fmr_policy[IFLA_IPTUN_FMR_MAX + 1] = { ++ [IFLA_IPTUN_FMR_IP6_PREFIX] = { .len = sizeof(struct in6_addr) }, ++ [IFLA_IPTUN_FMR_IP4_PREFIX] = { .len = sizeof(struct in_addr) }, ++ [IFLA_IPTUN_FMR_IP6_PREFIX_LEN] = { .type = NLA_U8 }, ++ [IFLA_IPTUN_FMR_IP4_PREFIX_LEN] = { .type = NLA_U8 }, ++ [IFLA_IPTUN_FMR_EA_LEN] = { .type = NLA_U8 }, ++ [IFLA_IPTUN_FMR_OFFSET] = { .type = NLA_U8 } ++}; ++ + static void ip6_tnl_netlink_parms(struct nlattr *data[], + struct __ip6_tnl_parm *parms) + { +@@ -1986,6 +2165,46 @@ static void ip6_tnl_netlink_parms(struct + + if (data[IFLA_IPTUN_FWMARK]) + parms->fwmark = nla_get_u32(data[IFLA_IPTUN_FWMARK]); ++ ++ if (data[IFLA_IPTUN_FMRS]) { ++ unsigned rem; ++ struct nlattr *fmr; ++ nla_for_each_nested(fmr, data[IFLA_IPTUN_FMRS], rem) { ++ struct nlattr *fmrd[IFLA_IPTUN_FMR_MAX + 1], *c; ++ struct __ip6_tnl_fmr *nfmr; ++ ++ nla_parse_nested(fmrd, IFLA_IPTUN_FMR_MAX, ++ fmr, ip6_tnl_fmr_policy, NULL); ++ ++ if (!(nfmr = kzalloc(sizeof(*nfmr), GFP_KERNEL))) ++ continue; ++ ++ nfmr->offset = 6; ++ ++ if ((c = fmrd[IFLA_IPTUN_FMR_IP6_PREFIX])) ++ nla_memcpy(&nfmr->ip6_prefix, fmrd[IFLA_IPTUN_FMR_IP6_PREFIX], ++ sizeof(nfmr->ip6_prefix)); ++ ++ if ((c = fmrd[IFLA_IPTUN_FMR_IP4_PREFIX])) ++ nla_memcpy(&nfmr->ip4_prefix, fmrd[IFLA_IPTUN_FMR_IP4_PREFIX], ++ sizeof(nfmr->ip4_prefix)); ++ ++ if ((c = fmrd[IFLA_IPTUN_FMR_IP6_PREFIX_LEN])) ++ nfmr->ip6_prefix_len = nla_get_u8(c); ++ ++ if ((c = fmrd[IFLA_IPTUN_FMR_IP4_PREFIX_LEN])) ++ nfmr->ip4_prefix_len = nla_get_u8(c); ++ ++ if ((c = fmrd[IFLA_IPTUN_FMR_EA_LEN])) ++ nfmr->ea_len = nla_get_u8(c); ++ ++ if ((c = fmrd[IFLA_IPTUN_FMR_OFFSET])) ++ nfmr->offset = nla_get_u8(c); ++ ++ nfmr->next = parms->fmrs; ++ parms->fmrs = nfmr; ++ } ++ } + } + + static bool ip6_tnl_netlink_encap_parms(struct nlattr *data[], +@@ -2101,6 +2320,12 @@ static void ip6_tnl_dellink(struct net_d + + static size_t ip6_tnl_get_size(const struct net_device *dev) + { ++ const struct ip6_tnl *t = netdev_priv(dev); ++ struct __ip6_tnl_fmr *c; ++ int fmrs = 0; ++ for (c = t->parms.fmrs; c; c = c->next) ++ ++fmrs; ++ + return + /* IFLA_IPTUN_LINK */ + nla_total_size(4) + +@@ -2130,6 +2355,24 @@ static size_t ip6_tnl_get_size(const str + nla_total_size(0) + + /* IFLA_IPTUN_FWMARK */ + nla_total_size(4) + ++ /* IFLA_IPTUN_FMRS */ ++ nla_total_size(0) + ++ ( ++ /* nest */ ++ nla_total_size(0) + ++ /* IFLA_IPTUN_FMR_IP6_PREFIX */ ++ nla_total_size(sizeof(struct in6_addr)) + ++ /* IFLA_IPTUN_FMR_IP4_PREFIX */ ++ nla_total_size(sizeof(struct in_addr)) + ++ /* IFLA_IPTUN_FMR_EA_LEN */ ++ nla_total_size(1) + ++ /* IFLA_IPTUN_FMR_IP6_PREFIX_LEN */ ++ nla_total_size(1) + ++ /* IFLA_IPTUN_FMR_IP4_PREFIX_LEN */ ++ nla_total_size(1) + ++ /* IFLA_IPTUN_FMR_OFFSET */ ++ nla_total_size(1) ++ ) * fmrs + + 0; + } + +@@ -2137,6 +2380,9 @@ static int ip6_tnl_fill_info(struct sk_b + { + struct ip6_tnl *tunnel = netdev_priv(dev); + struct __ip6_tnl_parm *parm = &tunnel->parms; ++ struct __ip6_tnl_fmr *c; ++ int fmrcnt = 0; ++ struct nlattr *fmrs; + + if (nla_put_u32(skb, IFLA_IPTUN_LINK, parm->link) || + nla_put_in6_addr(skb, IFLA_IPTUN_LOCAL, &parm->laddr) || +@@ -2146,9 +2392,27 @@ static int ip6_tnl_fill_info(struct sk_b + nla_put_be32(skb, IFLA_IPTUN_FLOWINFO, parm->flowinfo) || + nla_put_u32(skb, IFLA_IPTUN_FLAGS, parm->flags) || + nla_put_u8(skb, IFLA_IPTUN_PROTO, parm->proto) || +- nla_put_u32(skb, IFLA_IPTUN_FWMARK, parm->fwmark)) ++ nla_put_u32(skb, IFLA_IPTUN_FWMARK, parm->fwmark) || ++ !(fmrs = nla_nest_start(skb, IFLA_IPTUN_FMRS))) + goto nla_put_failure; + ++ for (c = parm->fmrs; c; c = c->next) { ++ struct nlattr *fmr = nla_nest_start(skb, ++fmrcnt); ++ if (!fmr || ++ nla_put(skb, IFLA_IPTUN_FMR_IP6_PREFIX, ++ sizeof(c->ip6_prefix), &c->ip6_prefix) || ++ nla_put(skb, IFLA_IPTUN_FMR_IP4_PREFIX, ++ sizeof(c->ip4_prefix), &c->ip4_prefix) || ++ nla_put_u8(skb, IFLA_IPTUN_FMR_IP6_PREFIX_LEN, c->ip6_prefix_len) || ++ nla_put_u8(skb, IFLA_IPTUN_FMR_IP4_PREFIX_LEN, c->ip4_prefix_len) || ++ nla_put_u8(skb, IFLA_IPTUN_FMR_EA_LEN, c->ea_len) || ++ nla_put_u8(skb, IFLA_IPTUN_FMR_OFFSET, c->offset)) ++ goto nla_put_failure; ++ ++ nla_nest_end(skb, fmr); ++ } ++ nla_nest_end(skb, fmrs); ++ + if (nla_put_u16(skb, IFLA_IPTUN_ENCAP_TYPE, tunnel->encap.type) || + nla_put_be16(skb, IFLA_IPTUN_ENCAP_SPORT, tunnel->encap.sport) || + nla_put_be16(skb, IFLA_IPTUN_ENCAP_DPORT, tunnel->encap.dport) || +@@ -2188,6 +2452,7 @@ static const struct nla_policy ip6_tnl_p + [IFLA_IPTUN_ENCAP_DPORT] = { .type = NLA_U16 }, + [IFLA_IPTUN_COLLECT_METADATA] = { .type = NLA_FLAG }, + [IFLA_IPTUN_FWMARK] = { .type = NLA_U32 }, ++ [IFLA_IPTUN_FMRS] = { .type = NLA_NESTED }, + }; + + static struct rtnl_link_ops ip6_link_ops __read_mostly = { diff --git a/pkgs/patches-linux-5.15/670-ipv6-allow-rejecting-with-source-address-failed-policy.patch b/pkgs/patches-linux-5.15/670-ipv6-allow-rejecting-with-source-address-failed-policy.patch new file mode 100644 index 0000000..6896076 --- /dev/null +++ b/pkgs/patches-linux-5.15/670-ipv6-allow-rejecting-with-source-address-failed-policy.patch @@ -0,0 +1,263 @@ +From: Jonas Gorski <jogo@openwrt.org> +Subject: ipv6: allow rejecting with "source address failed policy" + +RFC6204 L-14 requires rejecting traffic from invalid addresses with +ICMPv6 Destination Unreachable, Code 5 (Source address failed ingress/ +egress policy) on the LAN side, so add an appropriate rule for that. + +Signed-off-by: Jonas Gorski <jogo@openwrt.org> +--- + include/net/netns/ipv6.h | 1 + + include/uapi/linux/fib_rules.h | 4 +++ + include/uapi/linux/rtnetlink.h | 1 + + net/ipv4/fib_semantics.c | 4 +++ + net/ipv4/fib_trie.c | 1 + + net/ipv4/ipmr.c | 1 + + net/ipv6/fib6_rules.c | 4 +++ + net/ipv6/ip6mr.c | 2 ++ + net/ipv6/route.c | 58 +++++++++++++++++++++++++++++++++++++++++- + 9 files changed, 75 insertions(+), 1 deletion(-) + +--- a/include/net/netns/ipv6.h ++++ b/include/net/netns/ipv6.h +@@ -85,6 +85,7 @@ struct netns_ipv6 { + unsigned int fib6_routes_require_src; + #endif + struct rt6_info *ip6_prohibit_entry; ++ struct rt6_info *ip6_policy_failed_entry; + struct rt6_info *ip6_blk_hole_entry; + struct fib6_table *fib6_local_tbl; + struct fib_rules_ops *fib6_rules_ops; +--- a/include/uapi/linux/fib_rules.h ++++ b/include/uapi/linux/fib_rules.h +@@ -82,6 +82,10 @@ enum { + FR_ACT_BLACKHOLE, /* Drop without notification */ + FR_ACT_UNREACHABLE, /* Drop with ENETUNREACH */ + FR_ACT_PROHIBIT, /* Drop with EACCES */ ++ FR_ACT_RES9, ++ FR_ACT_RES10, ++ FR_ACT_RES11, ++ FR_ACT_POLICY_FAILED, /* Drop with EACCES */ + __FR_ACT_MAX, + }; + +--- a/include/uapi/linux/rtnetlink.h ++++ b/include/uapi/linux/rtnetlink.h +@@ -256,6 +256,7 @@ enum { + RTN_THROW, /* Not in this table */ + RTN_NAT, /* Translate this address */ + RTN_XRESOLVE, /* Use external resolver */ ++ RTN_POLICY_FAILED, /* Failed ingress/egress policy */ + __RTN_MAX + }; + +--- a/net/ipv4/fib_semantics.c ++++ b/net/ipv4/fib_semantics.c +@@ -142,6 +142,10 @@ const struct fib_prop fib_props[RTN_MAX + .error = -EINVAL, + .scope = RT_SCOPE_NOWHERE, + }, ++ [RTN_POLICY_FAILED] = { ++ .error = -EACCES, ++ .scope = RT_SCOPE_UNIVERSE, ++ }, + }; + + static void rt_fibinfo_free(struct rtable __rcu **rtp) +--- a/net/ipv4/fib_trie.c ++++ b/net/ipv4/fib_trie.c +@@ -2767,6 +2767,7 @@ static const char *const rtn_type_names[ + [RTN_THROW] = "THROW", + [RTN_NAT] = "NAT", + [RTN_XRESOLVE] = "XRESOLVE", ++ [RTN_POLICY_FAILED] = "POLICY_FAILED", + }; + + static inline const char *rtn_type(char *buf, size_t len, unsigned int t) +--- a/net/ipv4/ipmr.c ++++ b/net/ipv4/ipmr.c +@@ -175,6 +175,7 @@ static int ipmr_rule_action(struct fib_r + case FR_ACT_UNREACHABLE: + return -ENETUNREACH; + case FR_ACT_PROHIBIT: ++ case FR_ACT_POLICY_FAILED: + return -EACCES; + case FR_ACT_BLACKHOLE: + default: +--- a/net/ipv6/fib6_rules.c ++++ b/net/ipv6/fib6_rules.c +@@ -220,6 +220,10 @@ static int __fib6_rule_action(struct fib + err = -EACCES; + rt = net->ipv6.ip6_prohibit_entry; + goto discard_pkt; ++ case FR_ACT_POLICY_FAILED: ++ err = -EACCES; ++ rt = net->ipv6.ip6_policy_failed_entry; ++ goto discard_pkt; + } + + tb_id = fib_rule_get_table(rule, arg); +--- a/net/ipv6/ip6mr.c ++++ b/net/ipv6/ip6mr.c +@@ -163,6 +163,8 @@ static int ip6mr_rule_action(struct fib_ + return -ENETUNREACH; + case FR_ACT_PROHIBIT: + return -EACCES; ++ case FR_ACT_POLICY_FAILED: ++ return -EACCES; + case FR_ACT_BLACKHOLE: + default: + return -EINVAL; +--- a/net/ipv6/route.c ++++ b/net/ipv6/route.c +@@ -97,6 +97,8 @@ static int ip6_pkt_discard(struct sk_bu + static int ip6_pkt_discard_out(struct net *net, struct sock *sk, struct sk_buff *skb); + static int ip6_pkt_prohibit(struct sk_buff *skb); + static int ip6_pkt_prohibit_out(struct net *net, struct sock *sk, struct sk_buff *skb); ++static int ip6_pkt_policy_failed(struct sk_buff *skb); ++static int ip6_pkt_policy_failed_out(struct net *net, struct sock *sk, struct sk_buff *skb); + static void ip6_link_failure(struct sk_buff *skb); + static void ip6_rt_update_pmtu(struct dst_entry *dst, struct sock *sk, + struct sk_buff *skb, u32 mtu, +@@ -312,6 +314,18 @@ static const struct rt6_info ip6_prohibi + .rt6i_flags = (RTF_REJECT | RTF_NONEXTHOP), + }; + ++static const struct rt6_info ip6_policy_failed_entry_template = { ++ .dst = { ++ .__refcnt = ATOMIC_INIT(1), ++ .__use = 1, ++ .obsolete = DST_OBSOLETE_FORCE_CHK, ++ .error = -EACCES, ++ .input = ip6_pkt_policy_failed, ++ .output = ip6_pkt_policy_failed_out, ++ }, ++ .rt6i_flags = (RTF_REJECT | RTF_NONEXTHOP), ++}; ++ + static const struct rt6_info ip6_blk_hole_entry_template = { + .dst = { + .__refcnt = ATOMIC_INIT(1), +@@ -1033,6 +1047,7 @@ static const int fib6_prop[RTN_MAX + 1] + [RTN_BLACKHOLE] = -EINVAL, + [RTN_UNREACHABLE] = -EHOSTUNREACH, + [RTN_PROHIBIT] = -EACCES, ++ [RTN_POLICY_FAILED] = -EACCES, + [RTN_THROW] = -EAGAIN, + [RTN_NAT] = -EINVAL, + [RTN_XRESOLVE] = -EINVAL, +@@ -1068,6 +1083,10 @@ static void ip6_rt_init_dst_reject(struc + rt->dst.output = ip6_pkt_prohibit_out; + rt->dst.input = ip6_pkt_prohibit; + break; ++ case RTN_POLICY_FAILED: ++ rt->dst.output = ip6_pkt_policy_failed_out; ++ rt->dst.input = ip6_pkt_policy_failed; ++ break; + case RTN_THROW: + case RTN_UNREACHABLE: + default: +@@ -4560,6 +4579,17 @@ static int ip6_pkt_prohibit_out(struct n + return ip6_pkt_drop(skb, ICMPV6_ADM_PROHIBITED, IPSTATS_MIB_OUTNOROUTES); + } + ++static int ip6_pkt_policy_failed(struct sk_buff *skb) ++{ ++ return ip6_pkt_drop(skb, ICMPV6_POLICY_FAIL, IPSTATS_MIB_INNOROUTES); ++} ++ ++static int ip6_pkt_policy_failed_out(struct net *net, struct sock *sk, struct sk_buff *skb) ++{ ++ skb->dev = skb_dst(skb)->dev; ++ return ip6_pkt_drop(skb, ICMPV6_POLICY_FAIL, IPSTATS_MIB_OUTNOROUTES); ++} ++ + /* + * Allocate a dst for local (unicast / anycast) address. + */ +@@ -5047,7 +5077,8 @@ static int rtm_to_fib6_config(struct sk_ + if (rtm->rtm_type == RTN_UNREACHABLE || + rtm->rtm_type == RTN_BLACKHOLE || + rtm->rtm_type == RTN_PROHIBIT || +- rtm->rtm_type == RTN_THROW) ++ rtm->rtm_type == RTN_THROW || ++ rtm->rtm_type == RTN_POLICY_FAILED) + cfg->fc_flags |= RTF_REJECT; + + if (rtm->rtm_type == RTN_LOCAL) +@@ -6300,6 +6331,8 @@ static int ip6_route_dev_notify(struct n + #ifdef CONFIG_IPV6_MULTIPLE_TABLES + net->ipv6.ip6_prohibit_entry->dst.dev = dev; + net->ipv6.ip6_prohibit_entry->rt6i_idev = in6_dev_get(dev); ++ net->ipv6.ip6_policy_failed_entry->dst.dev = dev; ++ net->ipv6.ip6_policy_failed_entry->rt6i_idev = in6_dev_get(dev); + net->ipv6.ip6_blk_hole_entry->dst.dev = dev; + net->ipv6.ip6_blk_hole_entry->rt6i_idev = in6_dev_get(dev); + #endif +@@ -6311,6 +6344,7 @@ static int ip6_route_dev_notify(struct n + in6_dev_put_clear(&net->ipv6.ip6_null_entry->rt6i_idev); + #ifdef CONFIG_IPV6_MULTIPLE_TABLES + in6_dev_put_clear(&net->ipv6.ip6_prohibit_entry->rt6i_idev); ++ in6_dev_put_clear(&net->ipv6.ip6_policy_failed_entry->rt6i_idev); + in6_dev_put_clear(&net->ipv6.ip6_blk_hole_entry->rt6i_idev); + #endif + } +@@ -6502,6 +6536,8 @@ static int __net_init ip6_route_net_init + + #ifdef CONFIG_IPV6_MULTIPLE_TABLES + net->ipv6.fib6_has_custom_rules = false; ++ ++ + net->ipv6.ip6_prohibit_entry = kmemdup(&ip6_prohibit_entry_template, + sizeof(*net->ipv6.ip6_prohibit_entry), + GFP_KERNEL); +@@ -6512,11 +6548,21 @@ static int __net_init ip6_route_net_init + ip6_template_metrics, true); + INIT_LIST_HEAD(&net->ipv6.ip6_prohibit_entry->rt6i_uncached); + ++ net->ipv6.ip6_policy_failed_entry = ++ kmemdup(&ip6_policy_failed_entry_template, ++ sizeof(*net->ipv6.ip6_policy_failed_entry), GFP_KERNEL); ++ if (!net->ipv6.ip6_policy_failed_entry) ++ goto out_ip6_prohibit_entry; ++ net->ipv6.ip6_policy_failed_entry->dst.ops = &net->ipv6.ip6_dst_ops; ++ dst_init_metrics(&net->ipv6.ip6_policy_failed_entry->dst, ++ ip6_template_metrics, true); ++ INIT_LIST_HEAD(&net->ipv6.ip6_policy_failed_entry->rt6i_uncached); ++ + net->ipv6.ip6_blk_hole_entry = kmemdup(&ip6_blk_hole_entry_template, + sizeof(*net->ipv6.ip6_blk_hole_entry), + GFP_KERNEL); + if (!net->ipv6.ip6_blk_hole_entry) +- goto out_ip6_prohibit_entry; ++ goto out_ip6_policy_failed_entry; + net->ipv6.ip6_blk_hole_entry->dst.ops = &net->ipv6.ip6_dst_ops; + dst_init_metrics(&net->ipv6.ip6_blk_hole_entry->dst, + ip6_template_metrics, true); +@@ -6543,6 +6589,8 @@ out: + return ret; + + #ifdef CONFIG_IPV6_MULTIPLE_TABLES ++out_ip6_policy_failed_entry: ++ kfree(net->ipv6.ip6_policy_failed_entry); + out_ip6_prohibit_entry: + kfree(net->ipv6.ip6_prohibit_entry); + out_ip6_null_entry: +@@ -6562,6 +6610,7 @@ static void __net_exit ip6_route_net_exi + kfree(net->ipv6.ip6_null_entry); + #ifdef CONFIG_IPV6_MULTIPLE_TABLES + kfree(net->ipv6.ip6_prohibit_entry); ++ kfree(net->ipv6.ip6_policy_failed_entry); + kfree(net->ipv6.ip6_blk_hole_entry); + #endif + dst_entries_destroy(&net->ipv6.ip6_dst_ops); +@@ -6639,6 +6688,9 @@ void __init ip6_route_init_special_entri + init_net.ipv6.ip6_prohibit_entry->rt6i_idev = in6_dev_get(init_net.loopback_dev); + init_net.ipv6.ip6_blk_hole_entry->dst.dev = init_net.loopback_dev; + init_net.ipv6.ip6_blk_hole_entry->rt6i_idev = in6_dev_get(init_net.loopback_dev); ++ init_net.ipv6.ip6_policy_failed_entry->dst.dev = init_net.loopback_dev; ++ init_net.ipv6.ip6_policy_failed_entry->rt6i_idev = ++ in6_dev_get(init_net.loopback_dev); + #endif + } + diff --git a/pkgs/patches-linux-5.15/671-net-provide-defines-for-_POLICY_FAILED-until-all-cod.patch b/pkgs/patches-linux-5.15/671-net-provide-defines-for-_POLICY_FAILED-until-all-cod.patch new file mode 100644 index 0000000..bea43b2 --- /dev/null +++ b/pkgs/patches-linux-5.15/671-net-provide-defines-for-_POLICY_FAILED-until-all-cod.patch @@ -0,0 +1,50 @@ +From: Jonas Gorski <jogo@openwrt.org> +Subject: net: provide defines for _POLICY_FAILED until all code is updated + +Upstream introduced ICMPV6_POLICY_FAIL for code 5 of destination +unreachable, conflicting with our name. + +Add appropriate defines to allow our code to build with the new +name until we have updated our local patches for older kernels +and userspace packages. + +Signed-off-by: Jonas Gorski <jogo@openwrt.org> +--- + include/uapi/linux/fib_rules.h | 2 ++ + include/uapi/linux/icmpv6.h | 2 ++ + include/uapi/linux/rtnetlink.h | 2 ++ + 3 files changed, 6 insertions(+) + +--- a/include/uapi/linux/fib_rules.h ++++ b/include/uapi/linux/fib_rules.h +@@ -89,6 +89,8 @@ enum { + __FR_ACT_MAX, + }; + ++#define FR_ACT_FAILED_POLICY FR_ACT_POLICY_FAILED ++ + #define FR_ACT_MAX (__FR_ACT_MAX - 1) + + #endif +--- a/include/uapi/linux/icmpv6.h ++++ b/include/uapi/linux/icmpv6.h +@@ -126,6 +126,8 @@ struct icmp6hdr { + #define ICMPV6_POLICY_FAIL 5 + #define ICMPV6_REJECT_ROUTE 6 + ++#define ICMPV6_FAILED_POLICY ICMPV6_POLICY_FAIL ++ + /* + * Codes for Time Exceeded + */ +--- a/include/uapi/linux/rtnetlink.h ++++ b/include/uapi/linux/rtnetlink.h +@@ -260,6 +260,8 @@ enum { + __RTN_MAX + }; + ++#define RTN_FAILED_POLICY RTN_POLICY_FAILED ++ + #define RTN_MAX (__RTN_MAX - 1) + + diff --git a/pkgs/patches-linux-5.15/680-NET-skip-GRO-for-foreign-MAC-addresses.patch b/pkgs/patches-linux-5.15/680-NET-skip-GRO-for-foreign-MAC-addresses.patch new file mode 100644 index 0000000..8b4cd10 --- /dev/null +++ b/pkgs/patches-linux-5.15/680-NET-skip-GRO-for-foreign-MAC-addresses.patch @@ -0,0 +1,149 @@ +From: Felix Fietkau <nbd@nbd.name> +Subject: net: replace GRO optimization patch with a new one that supports VLANs/bridges with different MAC addresses + +Signed-off-by: Felix Fietkau <nbd@nbd.name> +--- + include/linux/netdevice.h | 2 ++ + include/linux/skbuff.h | 3 ++- + net/core/dev.c | 48 +++++++++++++++++++++++++++++++++++++++++++++++ + net/ethernet/eth.c | 18 +++++++++++++++++- + 4 files changed, 69 insertions(+), 2 deletions(-) + +--- a/include/linux/netdevice.h ++++ b/include/linux/netdevice.h +@@ -2075,6 +2075,8 @@ struct net_device { + struct netdev_hw_addr_list mc; + struct netdev_hw_addr_list dev_addrs; + ++ unsigned char local_addr_mask[MAX_ADDR_LEN]; ++ + #ifdef CONFIG_SYSFS + struct kset *queues_kset; + #endif +--- a/include/linux/skbuff.h ++++ b/include/linux/skbuff.h +@@ -855,6 +855,7 @@ struct sk_buff { + #ifdef CONFIG_IPV6_NDISC_NODETYPE + __u8 ndisc_nodetype:2; + #endif ++ __u8 gro_skip:1; + + __u8 ipvs_property:1; + __u8 inner_protocol_type:1; +--- a/net/core/dev.c ++++ b/net/core/dev.c +@@ -6061,6 +6061,9 @@ static enum gro_result dev_gro_receive(s + int same_flow; + int grow; + ++ if (skb->gro_skip) ++ goto normal; ++ + if (netif_elide_gro(skb->dev)) + goto normal; + +@@ -8075,6 +8078,48 @@ static void __netdev_adjacent_dev_unlink + &upper_dev->adj_list.lower); + } + ++static void __netdev_addr_mask(unsigned char *mask, const unsigned char *addr, ++ struct net_device *dev) ++{ ++ int i; ++ ++ for (i = 0; i < dev->addr_len; i++) ++ mask[i] |= addr[i] ^ dev->dev_addr[i]; ++} ++ ++static void __netdev_upper_mask(unsigned char *mask, struct net_device *dev, ++ struct net_device *lower) ++{ ++ struct net_device *cur; ++ struct list_head *iter; ++ ++ netdev_for_each_upper_dev_rcu(dev, cur, iter) { ++ __netdev_addr_mask(mask, cur->dev_addr, lower); ++ __netdev_upper_mask(mask, cur, lower); ++ } ++} ++ ++static void __netdev_update_addr_mask(struct net_device *dev) ++{ ++ unsigned char mask[MAX_ADDR_LEN]; ++ struct net_device *cur; ++ struct list_head *iter; ++ ++ memset(mask, 0, sizeof(mask)); ++ __netdev_upper_mask(mask, dev, dev); ++ memcpy(dev->local_addr_mask, mask, dev->addr_len); ++ ++ netdev_for_each_lower_dev(dev, cur, iter) ++ __netdev_update_addr_mask(cur); ++} ++ ++static void netdev_update_addr_mask(struct net_device *dev) ++{ ++ rcu_read_lock(); ++ __netdev_update_addr_mask(dev); ++ rcu_read_unlock(); ++} ++ + static int __netdev_upper_dev_link(struct net_device *dev, + struct net_device *upper_dev, bool master, + void *upper_priv, void *upper_info, +@@ -8126,6 +8171,7 @@ static int __netdev_upper_dev_link(struc + if (ret) + return ret; + ++ netdev_update_addr_mask(dev); + ret = call_netdevice_notifiers_info(NETDEV_CHANGEUPPER, + &changeupper_info.info); + ret = notifier_to_errno(ret); +@@ -8222,6 +8268,7 @@ static void __netdev_upper_dev_unlink(st + + __netdev_adjacent_dev_unlink_neighbour(dev, upper_dev); + ++ netdev_update_addr_mask(dev); + call_netdevice_notifiers_info(NETDEV_CHANGEUPPER, + &changeupper_info.info); + +@@ -9041,6 +9088,7 @@ int dev_set_mac_address(struct net_devic + if (err) + return err; + dev->addr_assign_type = NET_ADDR_SET; ++ netdev_update_addr_mask(dev); + call_netdevice_notifiers(NETDEV_CHANGEADDR, dev); + add_device_randomness(dev->dev_addr, dev->addr_len); + return 0; +--- a/net/ethernet/eth.c ++++ b/net/ethernet/eth.c +@@ -142,6 +142,18 @@ u32 eth_get_headlen(const struct net_dev + } + EXPORT_SYMBOL(eth_get_headlen); + ++static inline bool ++eth_check_local_mask(const void *addr1, const void *addr2, const void *mask) ++{ ++ const u16 *a1 = addr1; ++ const u16 *a2 = addr2; ++ const u16 *m = mask; ++ ++ return (((a1[0] ^ a2[0]) & ~m[0]) | ++ ((a1[1] ^ a2[1]) & ~m[1]) | ++ ((a1[2] ^ a2[2]) & ~m[2])); ++} ++ + /** + * eth_type_trans - determine the packet's protocol ID. + * @skb: received socket data +@@ -173,6 +185,10 @@ __be16 eth_type_trans(struct sk_buff *sk + } else { + skb->pkt_type = PACKET_OTHERHOST; + } ++ ++ if (eth_check_local_mask(eth->h_dest, dev->dev_addr, ++ dev->local_addr_mask)) ++ skb->gro_skip = 1; + } + + /* diff --git a/pkgs/patches-linux-5.15/682-of_net-add-mac-address-increment-support.patch b/pkgs/patches-linux-5.15/682-of_net-add-mac-address-increment-support.patch new file mode 100644 index 0000000..fe6fadd --- /dev/null +++ b/pkgs/patches-linux-5.15/682-of_net-add-mac-address-increment-support.patch @@ -0,0 +1,89 @@ +From 844c273286f328acf0dab5fbd5d864366b4904dc Mon Sep 17 00:00:00 2001 +From: Ansuel Smith <ansuelsmth@gmail.com> +Date: Tue, 30 Mar 2021 18:21:14 +0200 +Subject: [PATCH] of_net: add mac-address-increment support + +Lots of embedded devices use the mac-address of other interface +extracted from nvmem cells and increments it by one or two. Add two +bindings to integrate this and directly use the right mac-address for +the interface. Some example are some routers that use the gmac +mac-address stored in the art partition and increments it by one for the +wifi. mac-address-increment-byte bindings is used to tell what byte of +the mac-address has to be increased (if not defined the last byte is +increased) and mac-address-increment tells how much the byte decided +early has to be increased. + +Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com> +--- + net/core/of_net.c | 43 +++++++++++++++++++++++++++++++++++++++---- + 1 file changed, 39 insertions(+), 4 deletions(-) + +--- a/net/core/of_net.c ++++ b/net/core/of_net.c +@@ -119,27 +119,62 @@ static int of_get_mac_addr_nvmem(struct + * this case, the real MAC is in 'local-mac-address', and 'mac-address' exists + * but is all zeros. + * ++ * DT can tell the system to increment the mac-address after is extracted by ++ * using: ++ * - mac-address-increment-byte to decide what byte to increase ++ * (if not defined is increased the last byte) ++ * - mac-address-increment to decide how much to increase. The value WILL ++ * overflow to other bytes if the increment is over 255 or the total ++ * increment will exceed 255 of the current byte. ++ * (example 00:01:02:03:04:ff + 1 == 00:01:02:03:05:00) ++ * (example 00:01:02:03:04:fe + 5 == 00:01:02:03:05:03) ++ * + * Return: 0 on success and errno in case of error. + */ + int of_get_mac_address(struct device_node *np, u8 *addr) + { ++ u32 inc_idx, mac_inc, mac_val; + int ret; + ++ /* Check first if the increment byte is present and valid. ++ * If not set assume to increment the last byte if found. ++ */ ++ if (of_property_read_u32(np, "mac-address-increment-byte", &inc_idx)) ++ inc_idx = 5; ++ if (inc_idx < 3 || inc_idx > 5) ++ return -EINVAL; ++ + if (!np) + return -ENODEV; + + ret = of_get_mac_addr(np, "mac-address", addr); + if (!ret) +- return 0; ++ goto found; + + ret = of_get_mac_addr(np, "local-mac-address", addr); + if (!ret) +- return 0; ++ goto found; + + ret = of_get_mac_addr(np, "address", addr); + if (!ret) +- return 0; ++ goto found; ++ ++ ret = of_get_mac_addr_nvmem(np, addr); ++ if (ret) ++ return ret; ++ ++found: ++ if (!of_property_read_u32(np, "mac-address-increment", &mac_inc)) { ++ /* Convert to a contiguous value */ ++ mac_val = (addr[3] << 16) + (addr[4] << 8) + addr[5]; ++ mac_val += mac_inc << 8 * (5-inc_idx); ++ ++ /* Apply the incremented value handling overflow case */ ++ addr[3] = (mac_val >> 16) & 0xff; ++ addr[4] = (mac_val >> 8) & 0xff; ++ addr[5] = (mac_val >> 0) & 0xff; ++ } + +- return of_get_mac_addr_nvmem(np, addr); ++ return ret; + } + EXPORT_SYMBOL(of_get_mac_address); diff --git a/pkgs/patches-linux-5.15/683-of_net-add-mac-address-to-of-tree.patch b/pkgs/patches-linux-5.15/683-of_net-add-mac-address-to-of-tree.patch new file mode 100644 index 0000000..742d58b --- /dev/null +++ b/pkgs/patches-linux-5.15/683-of_net-add-mac-address-to-of-tree.patch @@ -0,0 +1,59 @@ +From 8585756342caa6d27008d1ad0c18023e4211a40a Mon Sep 17 00:00:00 2001 +From: OpenWrt community <openwrt-devel@lists.openwrt.org> +Date: Wed, 13 Jul 2022 12:22:48 +0200 +Subject: [PATCH] of/of_net: write back netdev MAC-address to device-tree + +The label-mac logic relies on the mac-address property of a netdev +devices of-node. However, the mac address can also be stored as a +different property or read from e.g. an mtd device. + +Create this node when reading a mac-address from OF if it does not +already exist and copy the mac-address used for the device to this +property. This way, the MAC address can be accessed using procfs. + +--- + net/core/of_net.c | 22 ++++++++++++++++++++++ + 1 file changed, 22 insertions(+) + +diff --git a/net/core/of_net.c b/net/core/of_net.c +index 71c38b532f72..43b28c8ddff9 100644 +--- a/net/core/of_net.c ++++ b/net/core/of_net.c +@@ -95,6 +95,27 @@ static int of_get_mac_addr_nvmem(struct device_node *np, u8 *addr) + return 0; + } + ++static int of_add_mac_address(struct device_node *np, u8* addr) ++{ ++ struct property *prop; ++ ++ prop = kzalloc(sizeof(*prop), GFP_KERNEL); ++ if (!prop) ++ return -ENOMEM; ++ ++ prop->name = "mac-address"; ++ prop->length = ETH_ALEN; ++ prop->value = kmemdup(addr, ETH_ALEN, GFP_KERNEL); ++ if (!prop->value || of_update_property(np, prop)) ++ goto free; ++ ++ return 0; ++free: ++ kfree(prop->value); ++ kfree(prop); ++ return -ENOMEM; ++} ++ + /** + * of_get_mac_address() + * @np: Caller's Device Node +@@ -175,6 +196,7 @@ int of_get_mac_address(struct device_node *np, u8 *addr) + addr[5] = (mac_val >> 0) & 0xff; + } + ++ of_add_mac_address(np, addr); + return ret; + } + EXPORT_SYMBOL(of_get_mac_address); +-- + diff --git a/pkgs/patches-linux-5.15/700-mvneta-tx-queue-workaround.patch b/pkgs/patches-linux-5.15/700-mvneta-tx-queue-workaround.patch new file mode 100644 index 0000000..6c07a29 --- /dev/null +++ b/pkgs/patches-linux-5.15/700-mvneta-tx-queue-workaround.patch @@ -0,0 +1,38 @@ +The hardware queue scheduling is apparently configured with fixed +priorities, which creates a nasty fairness issue where traffic from one +CPU can starve traffic from all other CPUs. + +Work around this issue by forcing all tx packets to go through one CPU, +until this issue is fixed properly. + +Signed-off-by: Felix Fietkau <nbd@nbd.name> +--- +--- a/drivers/net/ethernet/marvell/mvneta.c ++++ b/drivers/net/ethernet/marvell/mvneta.c +@@ -4987,6 +4987,16 @@ static int mvneta_setup_tc(struct net_de + } + } + ++#ifndef CONFIG_ARM64 ++static u16 mvneta_select_queue(struct net_device *dev, struct sk_buff *skb, ++ struct net_device *sb_dev) ++{ ++ /* XXX: hardware queue scheduling is broken, ++ * use only one queue until it is fixed */ ++ return 0; ++} ++#endif ++ + static const struct net_device_ops mvneta_netdev_ops = { + .ndo_open = mvneta_open, + .ndo_stop = mvneta_stop, +@@ -4997,6 +5007,9 @@ static const struct net_device_ops mvnet + .ndo_fix_features = mvneta_fix_features, + .ndo_get_stats64 = mvneta_get_stats64, + .ndo_eth_ioctl = mvneta_ioctl, ++#ifndef CONFIG_ARM64 ++ .ndo_select_queue = mvneta_select_queue, ++#endif + .ndo_bpf = mvneta_xdp, + .ndo_xdp_xmit = mvneta_xdp_xmit, + .ndo_setup_tc = mvneta_setup_tc, diff --git a/pkgs/patches-linux-5.15/700-net-next-net-dsa-introduce-tagger-owned-storage-for-private.patch b/pkgs/patches-linux-5.15/700-net-next-net-dsa-introduce-tagger-owned-storage-for-private.patch new file mode 100644 index 0000000..fe47c17 --- /dev/null +++ b/pkgs/patches-linux-5.15/700-net-next-net-dsa-introduce-tagger-owned-storage-for-private.patch @@ -0,0 +1,279 @@ +From dc452a471dbae8aca8257c565174212620880093 Mon Sep 17 00:00:00 2001 +From: Vladimir Oltean <vladimir.oltean@nxp.com> +Date: Fri, 10 Dec 2021 01:34:37 +0200 +Subject: net: dsa: introduce tagger-owned storage for private and shared data + +Ansuel is working on register access over Ethernet for the qca8k switch +family. This requires the qca8k tagging protocol driver to receive +frames which aren't intended for the network stack, but instead for the +qca8k switch driver itself. + +The dp->priv is currently the prevailing method for passing data back +and forth between the tagging protocol driver and the switch driver. +However, this method is riddled with caveats. + +The DSA design allows in principle for any switch driver to return any +protocol it desires in ->get_tag_protocol(). The dsa_loop driver can be +modified to do just that. But in the current design, the memory behind +dp->priv has to be allocated by the switch driver, so if the tagging +protocol is paired to an unexpected switch driver, we may end up in NULL +pointer dereferences inside the kernel, or worse (a switch driver may +allocate dp->priv according to the expectations of a different tagger). + +The latter possibility is even more plausible considering that DSA +switches can dynamically change tagging protocols in certain cases +(dsa <-> edsa, ocelot <-> ocelot-8021q), and the current design lends +itself to mistakes that are all too easy to make. + +This patch proposes that the tagging protocol driver should manage its +own memory, instead of relying on the switch driver to do so. +After analyzing the different in-tree needs, it can be observed that the +required tagger storage is per switch, therefore a ds->tagger_data +pointer is introduced. In principle, per-port storage could also be +introduced, although there is no need for it at the moment. Future +changes will replace the current usage of dp->priv with ds->tagger_data. + +We define a "binding" event between the DSA switch tree and the tagging +protocol. During this binding event, the tagging protocol's ->connect() +method is called first, and this may allocate some memory for each +switch of the tree. Then a cross-chip notifier is emitted for the +switches within that tree, and they are given the opportunity to fix up +the tagger's memory (for example, they might set up some function +pointers that represent virtual methods for consuming packets). +Because the memory is owned by the tagger, there exists a ->disconnect() +method for the tagger (which is the place to free the resources), but +there doesn't exist a ->disconnect() method for the switch driver. +This is part of the design. The switch driver should make minimal use of +the public part of the tagger data, and only after type-checking it +using the supplied "proto" argument. + +In the code there are in fact two binding events, one is the initial +event in dsa_switch_setup_tag_protocol(). At this stage, the cross chip +notifier chains aren't initialized, so we call each switch's connect() +method by hand. Then there is dsa_tree_bind_tag_proto() during +dsa_tree_change_tag_proto(), and here we have an old protocol and a new +one. We first connect to the new one before disconnecting from the old +one, to simplify error handling a bit and to ensure we remain in a valid +state at all times. + +Co-developed-by: Ansuel Smith <ansuelsmth@gmail.com> +Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com> +Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com> +Signed-off-by: David S. Miller <davem@davemloft.net> +--- + include/net/dsa.h | 12 +++++++++ + net/dsa/dsa2.c | 73 +++++++++++++++++++++++++++++++++++++++++++++++++++--- + net/dsa/dsa_priv.h | 1 + + net/dsa/switch.c | 14 +++++++++++ + 4 files changed, 96 insertions(+), 4 deletions(-) + +--- a/include/net/dsa.h ++++ b/include/net/dsa.h +@@ -80,12 +80,15 @@ enum dsa_tag_protocol { + }; + + struct dsa_switch; ++struct dsa_switch_tree; + + struct dsa_device_ops { + struct sk_buff *(*xmit)(struct sk_buff *skb, struct net_device *dev); + struct sk_buff *(*rcv)(struct sk_buff *skb, struct net_device *dev); + void (*flow_dissect)(const struct sk_buff *skb, __be16 *proto, + int *offset); ++ int (*connect)(struct dsa_switch_tree *dst); ++ void (*disconnect)(struct dsa_switch_tree *dst); + unsigned int needed_headroom; + unsigned int needed_tailroom; + const char *name; +@@ -329,6 +332,8 @@ struct dsa_switch { + */ + void *priv; + ++ void *tagger_data; ++ + /* + * Configuration data for this switch. + */ +@@ -584,6 +589,13 @@ struct dsa_switch_ops { + enum dsa_tag_protocol mprot); + int (*change_tag_protocol)(struct dsa_switch *ds, int port, + enum dsa_tag_protocol proto); ++ /* ++ * Method for switch drivers to connect to the tagging protocol driver ++ * in current use. The switch driver can provide handlers for certain ++ * types of packets for switch management. ++ */ ++ int (*connect_tag_protocol)(struct dsa_switch *ds, ++ enum dsa_tag_protocol proto); + + /* Optional switch-wide initialization and destruction methods */ + int (*setup)(struct dsa_switch *ds); +--- a/net/dsa/dsa2.c ++++ b/net/dsa/dsa2.c +@@ -230,8 +230,12 @@ static struct dsa_switch_tree *dsa_tree_ + + static void dsa_tree_free(struct dsa_switch_tree *dst) + { +- if (dst->tag_ops) ++ if (dst->tag_ops) { ++ if (dst->tag_ops->disconnect) ++ dst->tag_ops->disconnect(dst); ++ + dsa_tag_driver_put(dst->tag_ops); ++ } + list_del(&dst->list); + kfree(dst); + } +@@ -805,7 +809,7 @@ static int dsa_switch_setup_tag_protocol + int port, err; + + if (tag_ops->proto == dst->default_proto) +- return 0; ++ goto connect; + + for (port = 0; port < ds->num_ports; port++) { + if (!dsa_is_cpu_port(ds, port)) +@@ -821,6 +825,17 @@ static int dsa_switch_setup_tag_protocol + } + } + ++connect: ++ if (ds->ops->connect_tag_protocol) { ++ err = ds->ops->connect_tag_protocol(ds, tag_ops->proto); ++ if (err) { ++ dev_err(ds->dev, ++ "Unable to connect to tag protocol \"%s\": %pe\n", ++ tag_ops->name, ERR_PTR(err)); ++ return err; ++ } ++ } ++ + return 0; + } + +@@ -1132,6 +1147,46 @@ static void dsa_tree_teardown(struct dsa + dst->setup = false; + } + ++static int dsa_tree_bind_tag_proto(struct dsa_switch_tree *dst, ++ const struct dsa_device_ops *tag_ops) ++{ ++ const struct dsa_device_ops *old_tag_ops = dst->tag_ops; ++ struct dsa_notifier_tag_proto_info info; ++ int err; ++ ++ dst->tag_ops = tag_ops; ++ ++ /* Notify the new tagger about the connection to this tree */ ++ if (tag_ops->connect) { ++ err = tag_ops->connect(dst); ++ if (err) ++ goto out_revert; ++ } ++ ++ /* Notify the switches from this tree about the connection ++ * to the new tagger ++ */ ++ info.tag_ops = tag_ops; ++ err = dsa_tree_notify(dst, DSA_NOTIFIER_TAG_PROTO_CONNECT, &info); ++ if (err && err != -EOPNOTSUPP) ++ goto out_disconnect; ++ ++ /* Notify the old tagger about the disconnection from this tree */ ++ if (old_tag_ops->disconnect) ++ old_tag_ops->disconnect(dst); ++ ++ return 0; ++ ++out_disconnect: ++ /* Revert the new tagger's connection to this tree */ ++ if (tag_ops->disconnect) ++ tag_ops->disconnect(dst); ++out_revert: ++ dst->tag_ops = old_tag_ops; ++ ++ return err; ++} ++ + /* Since the dsa/tagging sysfs device attribute is per master, the assumption + * is that all DSA switches within a tree share the same tagger, otherwise + * they would have formed disjoint trees (different "dsa,member" values). +@@ -1164,12 +1219,15 @@ int dsa_tree_change_tag_proto(struct dsa + goto out_unlock; + } + ++ /* Notify the tag protocol change */ + info.tag_ops = tag_ops; + err = dsa_tree_notify(dst, DSA_NOTIFIER_TAG_PROTO, &info); + if (err) +- goto out_unwind_tagger; ++ return err; + +- dst->tag_ops = tag_ops; ++ err = dsa_tree_bind_tag_proto(dst, tag_ops); ++ if (err) ++ goto out_unwind_tagger; + + rtnl_unlock(); + +@@ -1257,6 +1315,7 @@ static int dsa_port_parse_cpu(struct dsa + struct dsa_switch_tree *dst = ds->dst; + const struct dsa_device_ops *tag_ops; + enum dsa_tag_protocol default_proto; ++ int err; + + /* Find out which protocol the switch would prefer. */ + default_proto = dsa_get_tag_protocol(dp, master); +@@ -1304,6 +1363,12 @@ static int dsa_port_parse_cpu(struct dsa + */ + dsa_tag_driver_put(tag_ops); + } else { ++ if (tag_ops->connect) { ++ err = tag_ops->connect(dst); ++ if (err) ++ return err; ++ } ++ + dst->tag_ops = tag_ops; + } + +--- a/net/dsa/dsa_priv.h ++++ b/net/dsa/dsa_priv.h +@@ -37,6 +37,7 @@ enum { + DSA_NOTIFIER_VLAN_DEL, + DSA_NOTIFIER_MTU, + DSA_NOTIFIER_TAG_PROTO, ++ DSA_NOTIFIER_TAG_PROTO_CONNECT, + DSA_NOTIFIER_MRP_ADD, + DSA_NOTIFIER_MRP_DEL, + DSA_NOTIFIER_MRP_ADD_RING_ROLE, +--- a/net/dsa/switch.c ++++ b/net/dsa/switch.c +@@ -616,6 +616,17 @@ static int dsa_switch_change_tag_proto(s + return 0; + } + ++static int dsa_switch_connect_tag_proto(struct dsa_switch *ds, ++ struct dsa_notifier_tag_proto_info *info) ++{ ++ const struct dsa_device_ops *tag_ops = info->tag_ops; ++ ++ if (!ds->ops->connect_tag_protocol) ++ return -EOPNOTSUPP; ++ ++ return ds->ops->connect_tag_protocol(ds, tag_ops->proto); ++} ++ + static int dsa_switch_mrp_add(struct dsa_switch *ds, + struct dsa_notifier_mrp_info *info) + { +@@ -735,6 +746,9 @@ static int dsa_switch_event(struct notif + case DSA_NOTIFIER_TAG_PROTO: + err = dsa_switch_change_tag_proto(ds, info); + break; ++ case DSA_NOTIFIER_TAG_PROTO_CONNECT: ++ err = dsa_switch_connect_tag_proto(ds, info); ++ break; + case DSA_NOTIFIER_MRP_ADD: + err = dsa_switch_mrp_add(ds, info); + break; diff --git a/pkgs/patches-linux-5.15/701-net-dsa-make-tagging-protocols-connect-to-individual-switches.patch b/pkgs/patches-linux-5.15/701-net-dsa-make-tagging-protocols-connect-to-individual-switches.patch new file mode 100644 index 0000000..f682260 --- /dev/null +++ b/pkgs/patches-linux-5.15/701-net-dsa-make-tagging-protocols-connect-to-individual-switches.patch @@ -0,0 +1,274 @@ +From 7f2973149c22e7a6fee4c0c9fa6b8e4108e9c208 Mon Sep 17 00:00:00 2001 +From: Vladimir Oltean <vladimir.oltean@nxp.com> +Date: Tue, 14 Dec 2021 03:45:36 +0200 +Subject: net: dsa: make tagging protocols connect to individual switches from + a tree + +On the NXP Bluebox 3 board which uses a multi-switch setup with sja1105, +the mechanism through which the tagger connects to the switch tree is +broken, due to improper DSA code design. At the time when tag_ops->connect() +is called in dsa_port_parse_cpu(), DSA hasn't finished "touching" all +the ports, so it doesn't know how large the tree is and how many ports +it has. It has just seen the first CPU port by this time. As a result, +this function will call the tagger's ->connect method too early, and the +tagger will connect only to the first switch from the tree. + +This could be perhaps addressed a bit more simply by just moving the +tag_ops->connect(dst) call a bit later (for example in dsa_tree_setup), +but there is already a design inconsistency at present: on the switch +side, the notification is on a per-switch basis, but on the tagger side, +it is on a per-tree basis. Furthermore, the persistent storage itself is +per switch (ds->tagger_data). And the tagger connect and disconnect +procedures (at least the ones that exist currently) could see a fair bit +of simplification if they didn't have to iterate through the switches of +a tree. + +To fix the issue, this change transforms tag_ops->connect(dst) into +tag_ops->connect(ds) and moves it somewhere where we already iterate +over all switches of a tree. That is in dsa_switch_setup_tag_protocol(), +which is a good placement because we already have there the connection +call to the switch side of things. + +As for the dsa_tree_bind_tag_proto() method (called from the code path +that changes the tag protocol), things are a bit more complicated +because we receive the tree as argument, yet when we unwind on errors, +it would be nice to not call tag_ops->disconnect(ds) where we didn't +previously call tag_ops->connect(ds). We didn't have this problem before +because the tag_ops connection operations passed the entire dst before, +and this is more fine grained now. To solve the error rewind case using +the new API, we have to create yet one more cross-chip notifier for +disconnection, and stay connected with the old tag protocol to all the +switches in the tree until we've succeeded to connect with the new one +as well. So if something fails half way, the whole tree is still +connected to the old tagger. But there may still be leaks if the tagger +fails to connect to the 2nd out of 3 switches in a tree: somebody needs +to tell the tagger to disconnect from the first switch. Nothing comes +for free, and this was previously handled privately by the tagging +protocol driver before, but now we need to emit a disconnect cross-chip +notifier for that, because DSA has to take care of the unwind path. We +assume that the tagging protocol has connected to a switch if it has set +ds->tagger_data to something, otherwise we avoid calling its +disconnection method in the error rewind path. + +The rest of the changes are in the tagging protocol drivers, and have to +do with the replacement of dst with ds. The iteration is removed and the +error unwind path is simplified, as mentioned above. + +Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com> +Signed-off-by: David S. Miller <davem@davemloft.net> +--- + include/net/dsa.h | 5 ++-- + net/dsa/dsa2.c | 44 +++++++++++++----------------- + net/dsa/dsa_priv.h | 1 + + net/dsa/switch.c | 52 ++++++++++++++++++++++++++++++++--- + net/dsa/tag_ocelot_8021q.c | 53 +++++++++++------------------------- + net/dsa/tag_sja1105.c | 67 ++++++++++++++++------------------------------ + 6 files changed, 109 insertions(+), 113 deletions(-) + +--- a/include/net/dsa.h ++++ b/include/net/dsa.h +@@ -80,15 +80,14 @@ enum dsa_tag_protocol { + }; + + struct dsa_switch; +-struct dsa_switch_tree; + + struct dsa_device_ops { + struct sk_buff *(*xmit)(struct sk_buff *skb, struct net_device *dev); + struct sk_buff *(*rcv)(struct sk_buff *skb, struct net_device *dev); + void (*flow_dissect)(const struct sk_buff *skb, __be16 *proto, + int *offset); +- int (*connect)(struct dsa_switch_tree *dst); +- void (*disconnect)(struct dsa_switch_tree *dst); ++ int (*connect)(struct dsa_switch *ds); ++ void (*disconnect)(struct dsa_switch *ds); + unsigned int needed_headroom; + unsigned int needed_tailroom; + const char *name; +--- a/net/dsa/dsa2.c ++++ b/net/dsa/dsa2.c +@@ -230,12 +230,8 @@ static struct dsa_switch_tree *dsa_tree_ + + static void dsa_tree_free(struct dsa_switch_tree *dst) + { +- if (dst->tag_ops) { +- if (dst->tag_ops->disconnect) +- dst->tag_ops->disconnect(dst); +- ++ if (dst->tag_ops) + dsa_tag_driver_put(dst->tag_ops); +- } + list_del(&dst->list); + kfree(dst); + } +@@ -826,17 +822,29 @@ static int dsa_switch_setup_tag_protocol + } + + connect: ++ if (tag_ops->connect) { ++ err = tag_ops->connect(ds); ++ if (err) ++ return err; ++ } ++ + if (ds->ops->connect_tag_protocol) { + err = ds->ops->connect_tag_protocol(ds, tag_ops->proto); + if (err) { + dev_err(ds->dev, + "Unable to connect to tag protocol \"%s\": %pe\n", + tag_ops->name, ERR_PTR(err)); +- return err; ++ goto disconnect; + } + } + + return 0; ++ ++disconnect: ++ if (tag_ops->disconnect) ++ tag_ops->disconnect(ds); ++ ++ return err; + } + + static int dsa_switch_setup(struct dsa_switch *ds) +@@ -1156,13 +1164,6 @@ static int dsa_tree_bind_tag_proto(struc + + dst->tag_ops = tag_ops; + +- /* Notify the new tagger about the connection to this tree */ +- if (tag_ops->connect) { +- err = tag_ops->connect(dst); +- if (err) +- goto out_revert; +- } +- + /* Notify the switches from this tree about the connection + * to the new tagger + */ +@@ -1172,16 +1173,14 @@ static int dsa_tree_bind_tag_proto(struc + goto out_disconnect; + + /* Notify the old tagger about the disconnection from this tree */ +- if (old_tag_ops->disconnect) +- old_tag_ops->disconnect(dst); ++ info.tag_ops = old_tag_ops; ++ dsa_tree_notify(dst, DSA_NOTIFIER_TAG_PROTO_DISCONNECT, &info); + + return 0; + + out_disconnect: +- /* Revert the new tagger's connection to this tree */ +- if (tag_ops->disconnect) +- tag_ops->disconnect(dst); +-out_revert: ++ info.tag_ops = tag_ops; ++ dsa_tree_notify(dst, DSA_NOTIFIER_TAG_PROTO_DISCONNECT, &info); + dst->tag_ops = old_tag_ops; + + return err; +@@ -1315,7 +1314,6 @@ static int dsa_port_parse_cpu(struct dsa + struct dsa_switch_tree *dst = ds->dst; + const struct dsa_device_ops *tag_ops; + enum dsa_tag_protocol default_proto; +- int err; + + /* Find out which protocol the switch would prefer. */ + default_proto = dsa_get_tag_protocol(dp, master); +@@ -1363,12 +1361,6 @@ static int dsa_port_parse_cpu(struct dsa + */ + dsa_tag_driver_put(tag_ops); + } else { +- if (tag_ops->connect) { +- err = tag_ops->connect(dst); +- if (err) +- return err; +- } +- + dst->tag_ops = tag_ops; + } + +--- a/net/dsa/dsa_priv.h ++++ b/net/dsa/dsa_priv.h +@@ -38,6 +38,7 @@ enum { + DSA_NOTIFIER_MTU, + DSA_NOTIFIER_TAG_PROTO, + DSA_NOTIFIER_TAG_PROTO_CONNECT, ++ DSA_NOTIFIER_TAG_PROTO_DISCONNECT, + DSA_NOTIFIER_MRP_ADD, + DSA_NOTIFIER_MRP_DEL, + DSA_NOTIFIER_MRP_ADD_RING_ROLE, +--- a/net/dsa/switch.c ++++ b/net/dsa/switch.c +@@ -616,15 +616,58 @@ static int dsa_switch_change_tag_proto(s + return 0; + } + +-static int dsa_switch_connect_tag_proto(struct dsa_switch *ds, +- struct dsa_notifier_tag_proto_info *info) ++/* We use the same cross-chip notifiers to inform both the tagger side, as well ++ * as the switch side, of connection and disconnection events. ++ * Since ds->tagger_data is owned by the tagger, it isn't a hard error if the ++ * switch side doesn't support connecting to this tagger, and therefore, the ++ * fact that we don't disconnect the tagger side doesn't constitute a memory ++ * leak: the tagger will still operate with persistent per-switch memory, just ++ * with the switch side unconnected to it. What does constitute a hard error is ++ * when the switch side supports connecting but fails. ++ */ ++static int ++dsa_switch_connect_tag_proto(struct dsa_switch *ds, ++ struct dsa_notifier_tag_proto_info *info) + { + const struct dsa_device_ops *tag_ops = info->tag_ops; ++ int err; ++ ++ /* Notify the new tagger about the connection to this switch */ ++ if (tag_ops->connect) { ++ err = tag_ops->connect(ds); ++ if (err) ++ return err; ++ } + + if (!ds->ops->connect_tag_protocol) + return -EOPNOTSUPP; + +- return ds->ops->connect_tag_protocol(ds, tag_ops->proto); ++ /* Notify the switch about the connection to the new tagger */ ++ err = ds->ops->connect_tag_protocol(ds, tag_ops->proto); ++ if (err) { ++ /* Revert the new tagger's connection to this tree */ ++ if (tag_ops->disconnect) ++ tag_ops->disconnect(ds); ++ return err; ++ } ++ ++ return 0; ++} ++ ++static int ++dsa_switch_disconnect_tag_proto(struct dsa_switch *ds, ++ struct dsa_notifier_tag_proto_info *info) ++{ ++ const struct dsa_device_ops *tag_ops = info->tag_ops; ++ ++ /* Notify the tagger about the disconnection from this switch */ ++ if (tag_ops->disconnect && ds->tagger_data) ++ tag_ops->disconnect(ds); ++ ++ /* No need to notify the switch, since it shouldn't have any ++ * resources to tear down ++ */ ++ return 0; + } + + static int dsa_switch_mrp_add(struct dsa_switch *ds, +@@ -749,6 +792,9 @@ static int dsa_switch_event(struct notif + case DSA_NOTIFIER_TAG_PROTO_CONNECT: + err = dsa_switch_connect_tag_proto(ds, info); + break; ++ case DSA_NOTIFIER_TAG_PROTO_DISCONNECT: ++ err = dsa_switch_disconnect_tag_proto(ds, info); ++ break; + case DSA_NOTIFIER_MRP_ADD: + err = dsa_switch_mrp_add(ds, info); + break; diff --git a/pkgs/patches-linux-5.15/702-net-next-ethernet-marvell-mvnetaMQPrioOffload.patch b/pkgs/patches-linux-5.15/702-net-next-ethernet-marvell-mvnetaMQPrioOffload.patch new file mode 100644 index 0000000..96f5042 --- /dev/null +++ b/pkgs/patches-linux-5.15/702-net-next-ethernet-marvell-mvnetaMQPrioOffload.patch @@ -0,0 +1,66 @@ +From 75fa71e3acadbb4ab5eda18505277eb9a1f69b23 Mon Sep 17 00:00:00 2001 +From: Maxime Chevallier <maxime.chevallier@bootlin.com> +Date: Fri, 26 Nov 2021 12:20:53 +0100 +Subject: net: mvneta: Use struct tc_mqprio_qopt_offload for MQPrio + configuration + +The struct tc_mqprio_qopt_offload is a container for struct tc_mqprio_qopt, +that allows passing extra parameters, such as traffic shaping. This commit +converts the current mqprio code to that new struct. + +Signed-off-by: Maxime Chevallier <maxime.chevallier@bootlin.com> +Signed-off-by: David S. Miller <davem@davemloft.net> +--- + drivers/net/ethernet/marvell/mvneta.c | 17 ++++++++++------- + 1 file changed, 10 insertions(+), 7 deletions(-) + +(limited to 'drivers/net/ethernet/marvell/mvneta.c') + +--- a/drivers/net/ethernet/marvell/mvneta.c ++++ b/drivers/net/ethernet/marvell/mvneta.c +@@ -38,6 +38,7 @@ + #include <net/ipv6.h> + #include <net/tso.h> + #include <net/page_pool.h> ++#include <net/pkt_cls.h> + #include <linux/bpf_trace.h> + + /* Registers */ +@@ -4947,14 +4948,14 @@ static void mvneta_setup_rx_prio_map(str + } + + static int mvneta_setup_mqprio(struct net_device *dev, +- struct tc_mqprio_qopt *qopt) ++ struct tc_mqprio_qopt_offload *mqprio) + { + struct mvneta_port *pp = netdev_priv(dev); + u8 num_tc; + int i; + +- qopt->hw = TC_MQPRIO_HW_OFFLOAD_TCS; +- num_tc = qopt->num_tc; ++ mqprio->qopt.hw = TC_MQPRIO_HW_OFFLOAD_TCS; ++ num_tc = mqprio->qopt.num_tc; + + if (num_tc > rxq_number) + return -EINVAL; +@@ -4965,13 +4966,15 @@ static int mvneta_setup_mqprio(struct ne + return 0; + } + +- memcpy(pp->prio_tc_map, qopt->prio_tc_map, sizeof(pp->prio_tc_map)); ++ memcpy(pp->prio_tc_map, mqprio->qopt.prio_tc_map, ++ sizeof(pp->prio_tc_map)); + + mvneta_setup_rx_prio_map(pp); + +- netdev_set_num_tc(dev, qopt->num_tc); +- for (i = 0; i < qopt->num_tc; i++) +- netdev_set_tc_queue(dev, i, qopt->count[i], qopt->offset[i]); ++ netdev_set_num_tc(dev, mqprio->qopt.num_tc); ++ for (i = 0; i < mqprio->qopt.num_tc; i++) ++ netdev_set_tc_queue(dev, i, mqprio->qopt.count[i], ++ mqprio->qopt.offset[i]); + + return 0; + } diff --git a/pkgs/patches-linux-5.15/703-net-next-ethernet-marvell-mvnetaMQPrioFlag.patch b/pkgs/patches-linux-5.15/703-net-next-ethernet-marvell-mvnetaMQPrioFlag.patch new file mode 100644 index 0000000..997f301 --- /dev/null +++ b/pkgs/patches-linux-5.15/703-net-next-ethernet-marvell-mvnetaMQPrioFlag.patch @@ -0,0 +1,30 @@ +From e7ca75fe6662f78bfeb0112671c812e4c7b8e214 Mon Sep 17 00:00:00 2001 +From: Maxime Chevallier <maxime.chevallier@bootlin.com> +Date: Fri, 26 Nov 2021 12:20:54 +0100 +Subject: net: mvneta: Don't force-set the offloading flag + +The qopt->hw flag is set by the TC code according to the offloading mode +asked by user. Don't force-set it in the driver, but instead read it to +make sure we do what's asked. + +Signed-off-by: Maxime Chevallier <maxime.chevallier@bootlin.com> +Signed-off-by: David S. Miller <davem@davemloft.net> +--- + drivers/net/ethernet/marvell/mvneta.c | 4 +++- + 1 file changed, 3 insertions(+), 1 deletion(-) + +(limited to 'drivers/net/ethernet/marvell/mvneta.c') + +--- a/drivers/net/ethernet/marvell/mvneta.c ++++ b/drivers/net/ethernet/marvell/mvneta.c +@@ -4954,7 +4954,9 @@ static int mvneta_setup_mqprio(struct ne + u8 num_tc; + int i; + +- mqprio->qopt.hw = TC_MQPRIO_HW_OFFLOAD_TCS; ++ if (mqprio->qopt.hw != TC_MQPRIO_HW_OFFLOAD_TCS) ++ return 0; ++ + num_tc = mqprio->qopt.num_tc; + + if (num_tc > rxq_number) diff --git a/pkgs/patches-linux-5.15/703-phy-add-detach-callback-to-struct-phy_driver.patch b/pkgs/patches-linux-5.15/703-phy-add-detach-callback-to-struct-phy_driver.patch new file mode 100644 index 0000000..21c322b --- /dev/null +++ b/pkgs/patches-linux-5.15/703-phy-add-detach-callback-to-struct-phy_driver.patch @@ -0,0 +1,38 @@ +From: Gabor Juhos <juhosg@openwrt.org> +Subject: generic: add detach callback to struct phy_driver + +lede-commit: fe61fc2d7d0b3fb348b502f68f98243b3ddf5867 + +Signed-off-by: Gabor Juhos <juhosg@openwrt.org> +--- + drivers/net/phy/phy_device.c | 3 +++ + include/linux/phy.h | 6 ++++++ + 2 files changed, 9 insertions(+) + +--- a/drivers/net/phy/phy_device.c ++++ b/drivers/net/phy/phy_device.c +@@ -1738,6 +1738,9 @@ void phy_detach(struct phy_device *phyde + struct module *ndev_owner = NULL; + struct mii_bus *bus; + ++ if (phydev->drv && phydev->drv->detach) ++ phydev->drv->detach(phydev); ++ + if (phydev->sysfs_links) { + if (dev) + sysfs_remove_link(&dev->dev.kobj, "phydev"); +--- a/include/linux/phy.h ++++ b/include/linux/phy.h +@@ -789,6 +789,12 @@ struct phy_driver { + /** @handle_interrupt: Override default interrupt handling */ + irqreturn_t (*handle_interrupt)(struct phy_device *phydev); + ++ /* ++ * Called before an ethernet device is detached ++ * from the PHY. ++ */ ++ void (*detach)(struct phy_device *phydev); ++ + /** @remove: Clears up any memory if needed */ + void (*remove)(struct phy_device *phydev); + diff --git a/pkgs/patches-linux-5.15/704-net-next-ethernet-marvell-mvnetaMQPrioQueue.patch b/pkgs/patches-linux-5.15/704-net-next-ethernet-marvell-mvnetaMQPrioQueue.patch new file mode 100644 index 0000000..73563a5 --- /dev/null +++ b/pkgs/patches-linux-5.15/704-net-next-ethernet-marvell-mvnetaMQPrioQueue.patch @@ -0,0 +1,97 @@ +From e9f7099d0730341b24c057acbf545dd019581db6 Mon Sep 17 00:00:00 2001 +From: Maxime Chevallier <maxime.chevallier@bootlin.com> +Date: Fri, 26 Nov 2021 12:20:55 +0100 +Subject: net: mvneta: Allow having more than one queue per TC + +The current mqprio implementation assumed that we are only using one +queue per TC. Use the offset and count parameters to allow using +multiple queues per TC. In that case, the controller will use a standard +round-robin algorithm to pick queues assigned to the same TC, with the +same priority. + +This only applies to VLAN priorities in ingress traffic, each TC +corresponding to a vlan priority. + +Signed-off-by: Maxime Chevallier <maxime.chevallier@bootlin.com> +Signed-off-by: David S. Miller <davem@davemloft.net> +--- + drivers/net/ethernet/marvell/mvneta.c | 35 ++++++++++++++++++++--------------- + 1 file changed, 20 insertions(+), 15 deletions(-) + +(limited to 'drivers/net/ethernet/marvell/mvneta.c') + +--- a/drivers/net/ethernet/marvell/mvneta.c ++++ b/drivers/net/ethernet/marvell/mvneta.c +@@ -493,7 +493,6 @@ struct mvneta_port { + u8 mcast_count[256]; + u16 tx_ring_size; + u16 rx_ring_size; +- u8 prio_tc_map[8]; + + phy_interface_t phy_interface; + struct device_node *dn; +@@ -4936,13 +4935,12 @@ static void mvneta_clear_rx_prio_map(str + mvreg_write(pp, MVNETA_VLAN_PRIO_TO_RXQ, 0); + } + +-static void mvneta_setup_rx_prio_map(struct mvneta_port *pp) ++static void mvneta_map_vlan_prio_to_rxq(struct mvneta_port *pp, u8 pri, u8 rxq) + { +- u32 val = 0; +- int i; ++ u32 val = mvreg_read(pp, MVNETA_VLAN_PRIO_TO_RXQ); + +- for (i = 0; i < rxq_number; i++) +- val |= MVNETA_VLAN_PRIO_RXQ_MAP(i, pp->prio_tc_map[i]); ++ val &= ~MVNETA_VLAN_PRIO_RXQ_MAP(pri, 0x7); ++ val |= MVNETA_VLAN_PRIO_RXQ_MAP(pri, rxq); + + mvreg_write(pp, MVNETA_VLAN_PRIO_TO_RXQ, val); + } +@@ -4951,8 +4949,8 @@ static int mvneta_setup_mqprio(struct ne + struct tc_mqprio_qopt_offload *mqprio) + { + struct mvneta_port *pp = netdev_priv(dev); ++ int rxq, tc; + u8 num_tc; +- int i; + + if (mqprio->qopt.hw != TC_MQPRIO_HW_OFFLOAD_TCS) + return 0; +@@ -4962,21 +4960,28 @@ static int mvneta_setup_mqprio(struct ne + if (num_tc > rxq_number) + return -EINVAL; + ++ mvneta_clear_rx_prio_map(pp); ++ + if (!num_tc) { +- mvneta_clear_rx_prio_map(pp); + netdev_reset_tc(dev); + return 0; + } + +- memcpy(pp->prio_tc_map, mqprio->qopt.prio_tc_map, +- sizeof(pp->prio_tc_map)); ++ netdev_set_num_tc(dev, mqprio->qopt.num_tc); + +- mvneta_setup_rx_prio_map(pp); ++ for (tc = 0; tc < mqprio->qopt.num_tc; tc++) { ++ netdev_set_tc_queue(dev, tc, mqprio->qopt.count[tc], ++ mqprio->qopt.offset[tc]); ++ ++ for (rxq = mqprio->qopt.offset[tc]; ++ rxq < mqprio->qopt.count[tc] + mqprio->qopt.offset[tc]; ++ rxq++) { ++ if (rxq >= rxq_number) ++ return -EINVAL; + +- netdev_set_num_tc(dev, mqprio->qopt.num_tc); +- for (i = 0; i < mqprio->qopt.num_tc; i++) +- netdev_set_tc_queue(dev, i, mqprio->qopt.count[i], +- mqprio->qopt.offset[i]); ++ mvneta_map_vlan_prio_to_rxq(pp, tc, rxq); ++ } ++ } + + return 0; + } diff --git a/pkgs/patches-linux-5.15/705-net-dsa-tag_mtk-add-padding-for-tx-packets.patch b/pkgs/patches-linux-5.15/705-net-dsa-tag_mtk-add-padding-for-tx-packets.patch new file mode 100644 index 0000000..e27ac35 --- /dev/null +++ b/pkgs/patches-linux-5.15/705-net-dsa-tag_mtk-add-padding-for-tx-packets.patch @@ -0,0 +1,29 @@ +From: Felix Fietkau <nbd@nbd.name> +Date: Fri, 6 May 2022 21:38:42 +0200 +Subject: [PATCH] net: dsa: tag_mtk: add padding for tx packets + +Padding for transmitted packets needs to account for the special tag. +With not enough padding, garbage bytes are inserted by the switch at the +end of small packets. + +Fixes: 5cd8985a1909 ("net-next: dsa: add Mediatek tag RX/TX handler") +Signed-off-by: Felix Fietkau <nbd@nbd.name> +--- + +--- a/net/dsa/tag_mtk.c ++++ b/net/dsa/tag_mtk.c +@@ -25,6 +25,14 @@ static struct sk_buff *mtk_tag_xmit(stru + u8 xmit_tpid; + u8 *mtk_tag; + ++ /* The Ethernet switch we are interfaced with needs packets to be at ++ * least 64 bytes (including FCS) otherwise their padding might be ++ * corrupted. With tags enabled, we need to make sure that packets are ++ * at least 68 bytes (including FCS and tag). ++ */ ++ if (__skb_put_padto(skb, ETH_ZLEN + MTK_HDR_LEN, false)) ++ return NULL; ++ + /* Build the special tag after the MAC Source Address. If VLAN header + * is present, it's required that VLAN header and special tag is + * being combined. Only in this way we can allow the switch can parse diff --git a/pkgs/patches-linux-5.15/705-net-next-ethernet-marvell-mvnetaMQPrioTCOffload.patch b/pkgs/patches-linux-5.15/705-net-next-ethernet-marvell-mvnetaMQPrioTCOffload.patch new file mode 100644 index 0000000..7b837d0 --- /dev/null +++ b/pkgs/patches-linux-5.15/705-net-next-ethernet-marvell-mvnetaMQPrioTCOffload.patch @@ -0,0 +1,182 @@ +From 2551dc9e398c37a15e52122d385c29a8b06be45f Mon Sep 17 00:00:00 2001 +From: Maxime Chevallier <maxime.chevallier@bootlin.com> +Date: Fri, 26 Nov 2021 12:20:56 +0100 +Subject: net: mvneta: Add TC traffic shaping offload + +The mvneta controller is able to do some tocken-bucket per-queue traffic +shaping. This commit adds support for setting these using the TC mqprio +interface. + +The token-bucket parameters are customisable, but the current +implementation configures them to have a 10kbps resolution for the +rate limitation, since it allows to cover the whole range of max_rate +values from 10kbps to 5Gbps with 10kbps increments. + +Signed-off-by: Maxime Chevallier <maxime.chevallier@bootlin.com> +Signed-off-by: David S. Miller <davem@davemloft.net> +--- + drivers/net/ethernet/marvell/mvneta.c | 120 +++++++++++++++++++++++++++++++++- + 1 file changed, 119 insertions(+), 1 deletion(-) + +(limited to 'drivers/net/ethernet/marvell/mvneta.c') + +--- a/drivers/net/ethernet/marvell/mvneta.c ++++ b/drivers/net/ethernet/marvell/mvneta.c +@@ -248,12 +248,39 @@ + #define MVNETA_TXQ_SENT_DESC_MASK 0x3fff0000 + #define MVNETA_PORT_TX_RESET 0x3cf0 + #define MVNETA_PORT_TX_DMA_RESET BIT(0) ++#define MVNETA_TXQ_CMD1_REG 0x3e00 ++#define MVNETA_TXQ_CMD1_BW_LIM_SEL_V1 BIT(3) ++#define MVNETA_TXQ_CMD1_BW_LIM_EN BIT(0) ++#define MVNETA_REFILL_NUM_CLK_REG 0x3e08 ++#define MVNETA_REFILL_MAX_NUM_CLK 0x0000ffff + #define MVNETA_TX_MTU 0x3e0c + #define MVNETA_TX_TOKEN_SIZE 0x3e14 + #define MVNETA_TX_TOKEN_SIZE_MAX 0xffffffff ++#define MVNETA_TXQ_BUCKET_REFILL_REG(q) (0x3e20 + ((q) << 2)) ++#define MVNETA_TXQ_BUCKET_REFILL_PERIOD_MASK 0x3ff00000 ++#define MVNETA_TXQ_BUCKET_REFILL_PERIOD_SHIFT 20 ++#define MVNETA_TXQ_BUCKET_REFILL_VALUE_MAX 0x0007ffff + #define MVNETA_TXQ_TOKEN_SIZE_REG(q) (0x3e40 + ((q) << 2)) + #define MVNETA_TXQ_TOKEN_SIZE_MAX 0x7fffffff + ++/* The values of the bucket refill base period and refill period are taken from ++ * the reference manual, and adds up to a base resolution of 10Kbps. This allows ++ * to cover all rate-limit values from 10Kbps up to 5Gbps ++ */ ++ ++/* Base period for the rate limit algorithm */ ++#define MVNETA_TXQ_BUCKET_REFILL_BASE_PERIOD_NS 100 ++ ++/* Number of Base Period to wait between each bucket refill */ ++#define MVNETA_TXQ_BUCKET_REFILL_PERIOD 1000 ++ ++/* The base resolution for rate limiting, in bps. Any max_rate value should be ++ * a multiple of that value. ++ */ ++#define MVNETA_TXQ_RATE_LIMIT_RESOLUTION (NSEC_PER_SEC / \ ++ (MVNETA_TXQ_BUCKET_REFILL_BASE_PERIOD_NS * \ ++ MVNETA_TXQ_BUCKET_REFILL_PERIOD)) ++ + #define MVNETA_LPI_CTRL_0 0x2cc0 + #define MVNETA_LPI_CTRL_1 0x2cc4 + #define MVNETA_LPI_REQUEST_ENABLE BIT(0) +@@ -4945,11 +4972,74 @@ static void mvneta_map_vlan_prio_to_rxq( + mvreg_write(pp, MVNETA_VLAN_PRIO_TO_RXQ, val); + } + ++static int mvneta_enable_per_queue_rate_limit(struct mvneta_port *pp) ++{ ++ unsigned long core_clk_rate; ++ u32 refill_cycles; ++ u32 val; ++ ++ core_clk_rate = clk_get_rate(pp->clk); ++ if (!core_clk_rate) ++ return -EINVAL; ++ ++ refill_cycles = MVNETA_TXQ_BUCKET_REFILL_BASE_PERIOD_NS / ++ (NSEC_PER_SEC / core_clk_rate); ++ ++ if (refill_cycles > MVNETA_REFILL_MAX_NUM_CLK) ++ return -EINVAL; ++ ++ /* Enable bw limit algorithm version 3 */ ++ val = mvreg_read(pp, MVNETA_TXQ_CMD1_REG); ++ val &= ~(MVNETA_TXQ_CMD1_BW_LIM_SEL_V1 | MVNETA_TXQ_CMD1_BW_LIM_EN); ++ mvreg_write(pp, MVNETA_TXQ_CMD1_REG, val); ++ ++ /* Set the base refill rate */ ++ mvreg_write(pp, MVNETA_REFILL_NUM_CLK_REG, refill_cycles); ++ ++ return 0; ++} ++ ++static void mvneta_disable_per_queue_rate_limit(struct mvneta_port *pp) ++{ ++ u32 val = mvreg_read(pp, MVNETA_TXQ_CMD1_REG); ++ ++ val |= (MVNETA_TXQ_CMD1_BW_LIM_SEL_V1 | MVNETA_TXQ_CMD1_BW_LIM_EN); ++ mvreg_write(pp, MVNETA_TXQ_CMD1_REG, val); ++} ++ ++static int mvneta_setup_queue_rates(struct mvneta_port *pp, int queue, ++ u64 min_rate, u64 max_rate) ++{ ++ u32 refill_val, rem; ++ u32 val = 0; ++ ++ /* Convert to from Bps to bps */ ++ max_rate *= 8; ++ ++ if (min_rate) ++ return -EINVAL; ++ ++ refill_val = div_u64_rem(max_rate, MVNETA_TXQ_RATE_LIMIT_RESOLUTION, ++ &rem); ++ ++ if (rem || !refill_val || ++ refill_val > MVNETA_TXQ_BUCKET_REFILL_VALUE_MAX) ++ return -EINVAL; ++ ++ val = refill_val; ++ val |= (MVNETA_TXQ_BUCKET_REFILL_PERIOD << ++ MVNETA_TXQ_BUCKET_REFILL_PERIOD_SHIFT); ++ ++ mvreg_write(pp, MVNETA_TXQ_BUCKET_REFILL_REG(queue), val); ++ ++ return 0; ++} ++ + static int mvneta_setup_mqprio(struct net_device *dev, + struct tc_mqprio_qopt_offload *mqprio) + { + struct mvneta_port *pp = netdev_priv(dev); +- int rxq, tc; ++ int rxq, txq, tc, ret; + u8 num_tc; + + if (mqprio->qopt.hw != TC_MQPRIO_HW_OFFLOAD_TCS) +@@ -4963,6 +5053,7 @@ static int mvneta_setup_mqprio(struct ne + mvneta_clear_rx_prio_map(pp); + + if (!num_tc) { ++ mvneta_disable_per_queue_rate_limit(pp); + netdev_reset_tc(dev); + return 0; + } +@@ -4983,6 +5074,33 @@ static int mvneta_setup_mqprio(struct ne + } + } + ++ if (mqprio->shaper != TC_MQPRIO_SHAPER_BW_RATE) { ++ mvneta_disable_per_queue_rate_limit(pp); ++ return 0; ++ } ++ ++ if (mqprio->qopt.num_tc > txq_number) ++ return -EINVAL; ++ ++ ret = mvneta_enable_per_queue_rate_limit(pp); ++ if (ret) ++ return ret; ++ ++ for (tc = 0; tc < mqprio->qopt.num_tc; tc++) { ++ for (txq = mqprio->qopt.offset[tc]; ++ txq < mqprio->qopt.count[tc] + mqprio->qopt.offset[tc]; ++ txq++) { ++ if (txq >= txq_number) ++ return -EINVAL; ++ ++ ret = mvneta_setup_queue_rates(pp, txq, ++ mqprio->min_rate[tc], ++ mqprio->max_rate[tc]); ++ if (ret) ++ return ret; ++ } ++ } ++ + return 0; + } + diff --git a/pkgs/patches-linux-5.15/710-bridge-add-knob-for-filtering-rx-tx-BPDU-pack.patch b/pkgs/patches-linux-5.15/710-bridge-add-knob-for-filtering-rx-tx-BPDU-pack.patch new file mode 100644 index 0000000..2a2ca7f --- /dev/null +++ b/pkgs/patches-linux-5.15/710-bridge-add-knob-for-filtering-rx-tx-BPDU-pack.patch @@ -0,0 +1,174 @@ +From: Felix Fietkau <nbd@nbd.name> +Date: Fri, 27 Aug 2021 12:22:32 +0200 +Subject: [PATCH] bridge: add knob for filtering rx/tx BPDU packets on a port + +Some devices (e.g. wireless APs) can't have devices behind them be part of +a bridge topology with redundant links, due to address limitations. +Additionally, broadcast traffic on these devices is somewhat expensive, due to +the low data rate and wakeups of clients in powersave mode. +This knob can be used to ensure that BPDU packets are never sent or forwarded +to/from these devices + +Signed-off-by: Felix Fietkau <nbd@nbd.name> +--- + +--- a/include/linux/if_bridge.h ++++ b/include/linux/if_bridge.h +@@ -58,6 +58,7 @@ struct br_ip_list { + #define BR_MRP_LOST_CONT BIT(18) + #define BR_MRP_LOST_IN_CONT BIT(19) + #define BR_TX_FWD_OFFLOAD BIT(20) ++#define BR_BPDU_FILTER BIT(21) + + #define BR_DEFAULT_AGEING_TIME (300 * HZ) + +--- a/net/bridge/br_forward.c ++++ b/net/bridge/br_forward.c +@@ -199,6 +199,7 @@ out: + void br_flood(struct net_bridge *br, struct sk_buff *skb, + enum br_pkt_type pkt_type, bool local_rcv, bool local_orig) + { ++ const unsigned char *dest = eth_hdr(skb)->h_dest; + struct net_bridge_port *prev = NULL; + struct net_bridge_port *p; + +@@ -214,6 +215,10 @@ void br_flood(struct net_bridge *br, str + case BR_PKT_MULTICAST: + if (!(p->flags & BR_MCAST_FLOOD) && skb->dev != br->dev) + continue; ++ if ((p->flags & BR_BPDU_FILTER) && ++ unlikely(is_link_local_ether_addr(dest) && ++ dest[5] == 0)) ++ continue; + break; + case BR_PKT_BROADCAST: + if (!(p->flags & BR_BCAST_FLOOD) && skb->dev != br->dev) +--- a/net/bridge/br_input.c ++++ b/net/bridge/br_input.c +@@ -326,6 +326,8 @@ static rx_handler_result_t br_handle_fra + fwd_mask |= p->group_fwd_mask; + switch (dest[5]) { + case 0x00: /* Bridge Group Address */ ++ if (p->flags & BR_BPDU_FILTER) ++ goto drop; + /* If STP is turned off, + then must forward to keep loop detection */ + if (p->br->stp_enabled == BR_NO_STP || +--- a/net/bridge/br_sysfs_if.c ++++ b/net/bridge/br_sysfs_if.c +@@ -240,6 +240,7 @@ BRPORT_ATTR_FLAG(multicast_flood, BR_MCA + BRPORT_ATTR_FLAG(broadcast_flood, BR_BCAST_FLOOD); + BRPORT_ATTR_FLAG(neigh_suppress, BR_NEIGH_SUPPRESS); + BRPORT_ATTR_FLAG(isolated, BR_ISOLATED); ++BRPORT_ATTR_FLAG(bpdu_filter, BR_BPDU_FILTER); + + #ifdef CONFIG_BRIDGE_IGMP_SNOOPING + static ssize_t show_multicast_router(struct net_bridge_port *p, char *buf) +@@ -292,6 +293,7 @@ static const struct brport_attribute *br + &brport_attr_group_fwd_mask, + &brport_attr_neigh_suppress, + &brport_attr_isolated, ++ &brport_attr_bpdu_filter, + &brport_attr_backup_port, + NULL + }; +--- a/net/bridge/br_stp_bpdu.c ++++ b/net/bridge/br_stp_bpdu.c +@@ -80,7 +80,8 @@ void br_send_config_bpdu(struct net_brid + { + unsigned char buf[35]; + +- if (p->br->stp_enabled != BR_KERNEL_STP) ++ if (p->br->stp_enabled != BR_KERNEL_STP || ++ (p->flags & BR_BPDU_FILTER)) + return; + + buf[0] = 0; +@@ -127,7 +128,8 @@ void br_send_tcn_bpdu(struct net_bridge_ + { + unsigned char buf[4]; + +- if (p->br->stp_enabled != BR_KERNEL_STP) ++ if (p->br->stp_enabled != BR_KERNEL_STP || ++ (p->flags & BR_BPDU_FILTER)) + return; + + buf[0] = 0; +@@ -172,6 +174,9 @@ void br_stp_rcv(const struct stp_proto * + if (!(br->dev->flags & IFF_UP)) + goto out; + ++ if (p->flags & BR_BPDU_FILTER) ++ goto out; ++ + if (p->state == BR_STATE_DISABLED) + goto out; + +--- a/include/uapi/linux/if_link.h ++++ b/include/uapi/linux/if_link.h +@@ -536,6 +536,7 @@ enum { + IFLA_BRPORT_MRP_IN_OPEN, + IFLA_BRPORT_MCAST_EHT_HOSTS_LIMIT, + IFLA_BRPORT_MCAST_EHT_HOSTS_CNT, ++ IFLA_BRPORT_BPDU_FILTER, + __IFLA_BRPORT_MAX + }; + #define IFLA_BRPORT_MAX (__IFLA_BRPORT_MAX - 1) +--- a/net/bridge/br_netlink.c ++++ b/net/bridge/br_netlink.c +@@ -184,6 +184,7 @@ static inline size_t br_port_info_size(v + + nla_total_size(1) /* IFLA_BRPORT_VLAN_TUNNEL */ + + nla_total_size(1) /* IFLA_BRPORT_NEIGH_SUPPRESS */ + + nla_total_size(1) /* IFLA_BRPORT_ISOLATED */ ++ + nla_total_size(1) /* IFLA_BRPORT_BPDU_FILTER */ + + nla_total_size(sizeof(struct ifla_bridge_id)) /* IFLA_BRPORT_ROOT_ID */ + + nla_total_size(sizeof(struct ifla_bridge_id)) /* IFLA_BRPORT_BRIDGE_ID */ + + nla_total_size(sizeof(u16)) /* IFLA_BRPORT_DESIGNATED_PORT */ +@@ -269,7 +270,8 @@ static int br_port_fill_attrs(struct sk_ + BR_MRP_LOST_CONT)) || + nla_put_u8(skb, IFLA_BRPORT_MRP_IN_OPEN, + !!(p->flags & BR_MRP_LOST_IN_CONT)) || +- nla_put_u8(skb, IFLA_BRPORT_ISOLATED, !!(p->flags & BR_ISOLATED))) ++ nla_put_u8(skb, IFLA_BRPORT_ISOLATED, !!(p->flags & BR_ISOLATED)) || ++ nla_put_u8(skb, IFLA_BRPORT_BPDU_FILTER, !!(p->flags & BR_BPDU_FILTER))) + return -EMSGSIZE; + + timerval = br_timer_value(&p->message_age_timer); +@@ -829,6 +831,7 @@ static const struct nla_policy br_port_p + [IFLA_BRPORT_ISOLATED] = { .type = NLA_U8 }, + [IFLA_BRPORT_BACKUP_PORT] = { .type = NLA_U32 }, + [IFLA_BRPORT_MCAST_EHT_HOSTS_LIMIT] = { .type = NLA_U32 }, ++ [IFLA_BRPORT_BPDU_FILTER] = { .type = NLA_U8 }, + }; + + /* Change the state of the port and notify spanning tree */ +@@ -893,6 +896,7 @@ static int br_setport(struct net_bridge_ + br_set_port_flag(p, tb, IFLA_BRPORT_VLAN_TUNNEL, BR_VLAN_TUNNEL); + br_set_port_flag(p, tb, IFLA_BRPORT_NEIGH_SUPPRESS, BR_NEIGH_SUPPRESS); + br_set_port_flag(p, tb, IFLA_BRPORT_ISOLATED, BR_ISOLATED); ++ br_set_port_flag(p, tb, IFLA_BRPORT_BPDU_FILTER, BR_BPDU_FILTER); + + changed_mask = old_flags ^ p->flags; + +--- a/net/core/rtnetlink.c ++++ b/net/core/rtnetlink.c +@@ -55,7 +55,7 @@ + #include <net/net_namespace.h> + + #define RTNL_MAX_TYPE 50 +-#define RTNL_SLAVE_MAX_TYPE 40 ++#define RTNL_SLAVE_MAX_TYPE 41 + + struct rtnl_link { + rtnl_doit_func doit; +@@ -4700,7 +4700,9 @@ int ndo_dflt_bridge_getlink(struct sk_bu + brport_nla_put_flag(skb, flags, mask, + IFLA_BRPORT_MCAST_FLOOD, BR_MCAST_FLOOD) || + brport_nla_put_flag(skb, flags, mask, +- IFLA_BRPORT_BCAST_FLOOD, BR_BCAST_FLOOD)) { ++ IFLA_BRPORT_BCAST_FLOOD, BR_BCAST_FLOOD) || ++ brport_nla_put_flag(skb, flags, mask, ++ IFLA_BRPORT_BPDU_FILTER, BR_BPDU_FILTER)) { + nla_nest_cancel(skb, protinfo); + goto nla_put_failure; + } diff --git a/pkgs/patches-linux-5.15/711-net-dsa-mv88e6xxx-disable-ATU-violation.patch b/pkgs/patches-linux-5.15/711-net-dsa-mv88e6xxx-disable-ATU-violation.patch new file mode 100644 index 0000000..ffa44f0 --- /dev/null +++ b/pkgs/patches-linux-5.15/711-net-dsa-mv88e6xxx-disable-ATU-violation.patch @@ -0,0 +1,21 @@ +From ebd924d773223593142d417c41d4ee6fa16f1805 Mon Sep 17 00:00:00 2001 +From: OpenWrt community <openwrt-devel@lists.openwrt.org> +Date: Wed, 13 Jul 2022 13:45:56 +0200 +Subject: [PATCH] net/dsa/mv88e6xxx: disable ATU violation + +--- + drivers/net/dsa/mv88e6xxx/chip.c | 3 +++ + 1 file changed, 3 insertions(+) + +--- a/drivers/net/dsa/mv88e6xxx/chip.c ++++ b/drivers/net/dsa/mv88e6xxx/chip.c +@@ -2980,6 +2980,9 @@ static int mv88e6xxx_setup_port(struct m + else + reg = 1 << port; + ++ /* Disable ATU member violation interrupt */ ++ reg |= MV88E6XXX_PORT_ASSOC_VECTOR_IGNORE_WRONG; ++ + err = mv88e6xxx_port_write(chip, port, MV88E6XXX_PORT_ASSOC_VECTOR, + reg); + if (err) diff --git a/pkgs/patches-linux-5.15/720-net-phy-add-aqr-phys.patch b/pkgs/patches-linux-5.15/720-net-phy-add-aqr-phys.patch new file mode 100644 index 0000000..65d8f47 --- /dev/null +++ b/pkgs/patches-linux-5.15/720-net-phy-add-aqr-phys.patch @@ -0,0 +1,142 @@ +From: Birger Koblitz <git@birger-koblitz.de> +Date: Sun, 5 Sep 2021 15:13:10 +0200 +Subject: [PATCH] kernel: Add AQR113C and AQR813 support + +This hack adds support for the Aquantia 4th generation, 10GBit +PHYs AQR113C and AQR813. + +Signed-off-by: Birger Koblitz <git@birger-koblitz.de> + +--- a/drivers/net/phy/aquantia_main.c ++++ b/drivers/net/phy/aquantia_main.c +@@ -20,8 +20,10 @@ + #define PHY_ID_AQR105 0x03a1b4a2 + #define PHY_ID_AQR106 0x03a1b4d0 + #define PHY_ID_AQR107 0x03a1b4e0 ++#define PHY_ID_AQR113C 0x31c31c12 + #define PHY_ID_AQCS109 0x03a1b5c2 + #define PHY_ID_AQR405 0x03a1b4b0 ++#define PHY_ID_AQR813 0x31c31cb2 + + #define MDIO_PHYXS_VEND_IF_STATUS 0xe812 + #define MDIO_PHYXS_VEND_IF_STATUS_TYPE_MASK GENMASK(7, 3) +@@ -372,6 +374,49 @@ static int aqr107_read_rate(struct phy_d + return 0; + } + ++static int aqr113c_read_status(struct phy_device *phydev) ++{ ++ int val, ret; ++ ++ ret = aqr_read_status(phydev); ++ if (ret) ++ return ret; ++ ++ if (!phydev->link || phydev->autoneg == AUTONEG_DISABLE) ++ return 0; ++ ++ // On AQR113C, the speed returned by aqr_read_status is wrong ++ aqr107_read_rate(phydev); ++ ++ val = phy_read_mmd(phydev, MDIO_MMD_PHYXS, MDIO_PHYXS_VEND_IF_STATUS); ++ if (val < 0) ++ return val; ++ ++ switch (FIELD_GET(MDIO_PHYXS_VEND_IF_STATUS_TYPE_MASK, val)) { ++ case MDIO_PHYXS_VEND_IF_STATUS_TYPE_KR: ++ phydev->interface = PHY_INTERFACE_MODE_10GKR; ++ break; ++ case MDIO_PHYXS_VEND_IF_STATUS_TYPE_XFI: ++ phydev->interface = PHY_INTERFACE_MODE_10GBASER; ++ break; ++ case MDIO_PHYXS_VEND_IF_STATUS_TYPE_USXGMII: ++ phydev->interface = PHY_INTERFACE_MODE_USXGMII; ++ break; ++ case MDIO_PHYXS_VEND_IF_STATUS_TYPE_SGMII: ++ phydev->interface = PHY_INTERFACE_MODE_SGMII; ++ break; ++ case MDIO_PHYXS_VEND_IF_STATUS_TYPE_OCSGMII: ++ phydev->interface = PHY_INTERFACE_MODE_2500BASEX; ++ break; ++ default: ++ phydev->interface = PHY_INTERFACE_MODE_NA; ++ break; ++ } ++ ++ /* Read downshifted rate from vendor register */ ++ return aqr107_read_rate(phydev); ++} ++ + static int aqr107_read_status(struct phy_device *phydev) + { + int val, ret; +@@ -502,7 +547,7 @@ static void aqr107_chip_info(struct phy_ + build_id = FIELD_GET(VEND1_GLOBAL_RSVD_STAT1_FW_BUILD_ID, val); + prov_id = FIELD_GET(VEND1_GLOBAL_RSVD_STAT1_PROV_ID, val); + +- phydev_dbg(phydev, "FW %u.%u, Build %u, Provisioning %u\n", ++ phydev_info(phydev, "FW %u.%u, Build %u, Provisioning %u\n", + fw_major, fw_minor, build_id, prov_id); + } + +@@ -674,6 +719,24 @@ static struct phy_driver aqr_driver[] = + .link_change_notify = aqr107_link_change_notify, + }, + { ++ PHY_ID_MATCH_MODEL(PHY_ID_AQR113C), ++ .name = "Aquantia AQR113C", ++ .probe = aqr107_probe, ++ .config_init = aqr107_config_init, ++ .config_aneg = aqr_config_aneg, ++ .config_intr = aqr_config_intr, ++ .handle_interrupt = aqr_handle_interrupt, ++ .read_status = aqr113c_read_status, ++ .get_tunable = aqr107_get_tunable, ++ .set_tunable = aqr107_set_tunable, ++ .suspend = aqr107_suspend, ++ .resume = aqr107_resume, ++ .get_sset_count = aqr107_get_sset_count, ++ .get_strings = aqr107_get_strings, ++ .get_stats = aqr107_get_stats, ++ .link_change_notify = aqr107_link_change_notify, ++}, ++{ + PHY_ID_MATCH_MODEL(PHY_ID_AQCS109), + .name = "Aquantia AQCS109", + .probe = aqr107_probe, +@@ -699,6 +762,24 @@ static struct phy_driver aqr_driver[] = + .handle_interrupt = aqr_handle_interrupt, + .read_status = aqr_read_status, + }, ++{ ++ PHY_ID_MATCH_MODEL(PHY_ID_AQR813), ++ .name = "Aquantia AQR813", ++ .probe = aqr107_probe, ++ .config_init = aqr107_config_init, ++ .config_aneg = aqr_config_aneg, ++ .config_intr = aqr_config_intr, ++ .handle_interrupt = aqr_handle_interrupt, ++ .read_status = aqr113c_read_status, ++ .get_tunable = aqr107_get_tunable, ++ .set_tunable = aqr107_set_tunable, ++ .suspend = aqr107_suspend, ++ .resume = aqr107_resume, ++ .get_sset_count = aqr107_get_sset_count, ++ .get_strings = aqr107_get_strings, ++ .get_stats = aqr107_get_stats, ++ .link_change_notify = aqr107_link_change_notify, ++}, + }; + + module_phy_driver(aqr_driver); +@@ -709,8 +790,10 @@ static struct mdio_device_id __maybe_unu + { PHY_ID_MATCH_MODEL(PHY_ID_AQR105) }, + { PHY_ID_MATCH_MODEL(PHY_ID_AQR106) }, + { PHY_ID_MATCH_MODEL(PHY_ID_AQR107) }, ++ { PHY_ID_MATCH_MODEL(PHY_ID_AQR113C) }, + { PHY_ID_MATCH_MODEL(PHY_ID_AQCS109) }, + { PHY_ID_MATCH_MODEL(PHY_ID_AQR405) }, ++ { PHY_ID_MATCH_MODEL(PHY_ID_AQR813) }, + { } + }; + diff --git a/pkgs/patches-linux-5.15/721-net-add-packet-mangeling.patch b/pkgs/patches-linux-5.15/721-net-add-packet-mangeling.patch new file mode 100644 index 0000000..206e16c --- /dev/null +++ b/pkgs/patches-linux-5.15/721-net-add-packet-mangeling.patch @@ -0,0 +1,178 @@ +From ffe387740bbe88dd88bbe04d6375902708003d6e Mon Sep 17 00:00:00 2001 +From: Felix Fietkau <nbd@nbd.name> +Date: Fri, 7 Jul 2017 17:25:00 +0200 +Subject: net: add packet mangeling + +ar8216 switches have a hardware bug, which renders normal 802.1q support +unusable. Packet mangling is required to fix up the vlan for incoming +packets. + +Signed-off-by: Felix Fietkau <nbd@nbd.name> +--- + include/linux/netdevice.h | 11 +++++++++++ + include/linux/skbuff.h | 14 ++++---------- + net/Kconfig | 6 ++++++ + net/core/dev.c | 20 +++++++++++++++----- + net/core/skbuff.c | 17 +++++++++++++++++ + net/ethernet/eth.c | 6 ++++++ + 6 files changed, 59 insertions(+), 15 deletions(-) + +--- a/include/linux/netdevice.h ++++ b/include/linux/netdevice.h +@@ -1669,6 +1669,10 @@ enum netdev_priv_flags { + IFF_TX_SKB_NO_LINEAR = BIT_ULL(31), + }; + ++enum netdev_extra_priv_flags { ++ IFF_NO_IP_ALIGN = 1<<0, ++}; ++ + #define IFF_802_1Q_VLAN IFF_802_1Q_VLAN + #define IFF_EBRIDGE IFF_EBRIDGE + #define IFF_BONDING IFF_BONDING +@@ -1701,6 +1705,7 @@ enum netdev_priv_flags { + #define IFF_L3MDEV_RX_HANDLER IFF_L3MDEV_RX_HANDLER + #define IFF_LIVE_RENAME_OK IFF_LIVE_RENAME_OK + #define IFF_TX_SKB_NO_LINEAR IFF_TX_SKB_NO_LINEAR ++#define IFF_NO_IP_ALIGN IFF_NO_IP_ALIGN + + /* Specifies the type of the struct net_device::ml_priv pointer */ + enum netdev_ml_priv_type { +@@ -2002,6 +2007,7 @@ struct net_device { + /* Read-mostly cache-line for fast-path access */ + unsigned int flags; + unsigned int priv_flags; ++ unsigned int extra_priv_flags; + const struct net_device_ops *netdev_ops; + int ifindex; + unsigned short gflags; +@@ -2062,6 +2068,11 @@ struct net_device { + const struct tlsdev_ops *tlsdev_ops; + #endif + ++#ifdef CONFIG_ETHERNET_PACKET_MANGLE ++ void (*eth_mangle_rx)(struct net_device *dev, struct sk_buff *skb); ++ struct sk_buff *(*eth_mangle_tx)(struct net_device *dev, struct sk_buff *skb); ++#endif ++ + const struct header_ops *header_ops; + + unsigned char operstate; +@@ -2136,6 +2147,10 @@ struct net_device { + struct mctp_dev __rcu *mctp_ptr; + #endif + ++#ifdef CONFIG_ETHERNET_PACKET_MANGLE ++ void *phy_ptr; /* PHY device specific data */ ++#endif ++ + /* + * Cache lines mostly used on receive path (including eth_type_trans()) + */ +--- a/include/linux/skbuff.h ++++ b/include/linux/skbuff.h +@@ -2852,6 +2852,10 @@ static inline int pskb_trim(struct sk_bu + return (len < skb->len) ? __pskb_trim(skb, len) : 0; + } + ++extern struct sk_buff *__netdev_alloc_skb_ip_align(struct net_device *dev, ++ unsigned int length, gfp_t gfp); ++ ++ + /** + * pskb_trim_unique - remove end from a paged unique (not cloned) buffer + * @skb: buffer to alter +@@ -3002,16 +3006,6 @@ static inline struct sk_buff *dev_alloc_ + } + + +-static inline struct sk_buff *__netdev_alloc_skb_ip_align(struct net_device *dev, +- unsigned int length, gfp_t gfp) +-{ +- struct sk_buff *skb = __netdev_alloc_skb(dev, length + NET_IP_ALIGN, gfp); +- +- if (NET_IP_ALIGN && skb) +- skb_reserve(skb, NET_IP_ALIGN); +- return skb; +-} +- + static inline struct sk_buff *netdev_alloc_skb_ip_align(struct net_device *dev, + unsigned int length) + { +--- a/net/Kconfig ++++ b/net/Kconfig +@@ -26,6 +26,12 @@ menuconfig NET + + if NET + ++config ETHERNET_PACKET_MANGLE ++ bool ++ help ++ This option can be selected by phy drivers that need to mangle ++ packets going in or out of an ethernet device. ++ + config WANT_COMPAT_NETLINK_MESSAGES + bool + help +--- a/net/core/dev.c ++++ b/net/core/dev.c +@@ -3588,6 +3588,11 @@ static int xmit_one(struct sk_buff *skb, + if (dev_nit_active(dev)) + dev_queue_xmit_nit(skb, dev); + ++#ifdef CONFIG_ETHERNET_PACKET_MANGLE ++ if (dev->eth_mangle_tx && !(skb = dev->eth_mangle_tx(dev, skb))) ++ return NETDEV_TX_OK; ++#endif ++ + len = skb->len; + PRANDOM_ADD_NOISE(skb, dev, txq, len + jiffies); + trace_net_dev_start_xmit(skb, dev); +--- a/net/core/skbuff.c ++++ b/net/core/skbuff.c +@@ -61,6 +61,7 @@ + #include <linux/if_vlan.h> + #include <linux/mpls.h> + #include <linux/kcov.h> ++#include <linux/if.h> + + #include <net/protocol.h> + #include <net/dst.h> +@@ -602,6 +603,22 @@ skb_fail: + } + EXPORT_SYMBOL(__napi_alloc_skb); + ++struct sk_buff *__netdev_alloc_skb_ip_align(struct net_device *dev, ++ unsigned int length, gfp_t gfp) ++{ ++ struct sk_buff *skb = __netdev_alloc_skb(dev, length + NET_IP_ALIGN, gfp); ++ ++#ifdef CONFIG_ETHERNET_PACKET_MANGLE ++ if (dev && (dev->extra_priv_flags & IFF_NO_IP_ALIGN)) ++ return skb; ++#endif ++ ++ if (NET_IP_ALIGN && skb) ++ skb_reserve(skb, NET_IP_ALIGN); ++ return skb; ++} ++EXPORT_SYMBOL(__netdev_alloc_skb_ip_align); ++ + void skb_add_rx_frag(struct sk_buff *skb, int i, struct page *page, int off, + int size, unsigned int truesize) + { +--- a/net/ethernet/eth.c ++++ b/net/ethernet/eth.c +@@ -170,6 +170,12 @@ __be16 eth_type_trans(struct sk_buff *sk + const struct ethhdr *eth; + + skb->dev = dev; ++ ++#ifdef CONFIG_ETHERNET_PACKET_MANGLE ++ if (dev->eth_mangle_rx) ++ dev->eth_mangle_rx(dev, skb); ++#endif ++ + skb_reset_mac_header(skb); + + eth = (struct ethhdr *)skb->data; diff --git a/pkgs/patches-linux-5.15/722-net-phy-aquantia-enable-AQR112-and-AQR412.patch b/pkgs/patches-linux-5.15/722-net-phy-aquantia-enable-AQR112-and-AQR412.patch new file mode 100644 index 0000000..f190185 --- /dev/null +++ b/pkgs/patches-linux-5.15/722-net-phy-aquantia-enable-AQR112-and-AQR412.patch @@ -0,0 +1,154 @@ +From 5f62951fba63a9f9cfff564209426bdea5fcc371 Mon Sep 17 00:00:00 2001 +From: Alex Marginean <alexandru.marginean@nxp.com> +Date: Tue, 27 Aug 2019 15:16:56 +0300 +Subject: [PATCH] drivers: net: phy: aquantia: enable AQR112 and AQR412 + +Adds support for AQR112 and AQR412 which is mostly based on existing code +with the addition of code configuring the protocol on system side. +This allows changing the system side protocol without having to deploy a +different firmware on the PHY. + +Signed-off-by: Alex Marginean <alexandru.marginean@nxp.com> +--- + drivers/net/phy/aquantia_main.c | 88 +++++++++++++++++++++++++++++++++++++++++ + 1 file changed, 88 insertions(+) + +--- a/drivers/net/phy/aquantia_main.c ++++ b/drivers/net/phy/aquantia_main.c +@@ -20,9 +20,11 @@ + #define PHY_ID_AQR105 0x03a1b4a2 + #define PHY_ID_AQR106 0x03a1b4d0 + #define PHY_ID_AQR107 0x03a1b4e0 ++#define PHY_ID_AQR112 0x03a1b662 + #define PHY_ID_AQR113C 0x31c31c12 + #define PHY_ID_AQCS109 0x03a1b5c2 + #define PHY_ID_AQR405 0x03a1b4b0 ++#define PHY_ID_AQR412 0x03a1b712 + #define PHY_ID_AQR813 0x31c31cb2 + + #define MDIO_PHYXS_VEND_IF_STATUS 0xe812 +@@ -126,6 +128,29 @@ + #define VEND1_GLOBAL_INT_VEND_MASK_GLOBAL2 BIT(1) + #define VEND1_GLOBAL_INT_VEND_MASK_GLOBAL3 BIT(0) + ++/* registers in MDIO_MMD_VEND1 region */ ++#define AQUANTIA_VND1_GLOBAL_SC 0x000 ++#define AQUANTIA_VND1_GLOBAL_SC_LP BIT(0xb) ++ ++/* global start rate, the protocol associated with this speed is used by default ++ * on SI. ++ */ ++#define AQUANTIA_VND1_GSTART_RATE 0x31a ++#define AQUANTIA_VND1_GSTART_RATE_OFF 0 ++#define AQUANTIA_VND1_GSTART_RATE_100M 1 ++#define AQUANTIA_VND1_GSTART_RATE_1G 2 ++#define AQUANTIA_VND1_GSTART_RATE_10G 3 ++#define AQUANTIA_VND1_GSTART_RATE_2_5G 4 ++#define AQUANTIA_VND1_GSTART_RATE_5G 5 ++ ++/* SYSCFG registers for 100M, 1G, 2.5G, 5G, 10G */ ++#define AQUANTIA_VND1_GSYSCFG_BASE 0x31b ++#define AQUANTIA_VND1_GSYSCFG_100M 0 ++#define AQUANTIA_VND1_GSYSCFG_1G 1 ++#define AQUANTIA_VND1_GSYSCFG_2_5G 2 ++#define AQUANTIA_VND1_GSYSCFG_5G 3 ++#define AQUANTIA_VND1_GSYSCFG_10G 4 ++ + struct aqr107_hw_stat { + const char *name; + int reg; +@@ -257,6 +282,51 @@ static int aqr_config_aneg(struct phy_de + return genphy_c45_check_and_restart_aneg(phydev, changed); + } + ++static struct { ++ u16 syscfg; ++ int cnt; ++ u16 start_rate; ++} aquantia_syscfg[PHY_INTERFACE_MODE_MAX] = { ++ [PHY_INTERFACE_MODE_SGMII] = {0x04b, AQUANTIA_VND1_GSYSCFG_1G, ++ AQUANTIA_VND1_GSTART_RATE_1G}, ++ [PHY_INTERFACE_MODE_2500BASEX] = {0x144, AQUANTIA_VND1_GSYSCFG_2_5G, ++ AQUANTIA_VND1_GSTART_RATE_2_5G}, ++ [PHY_INTERFACE_MODE_XGMII] = {0x100, AQUANTIA_VND1_GSYSCFG_10G, ++ AQUANTIA_VND1_GSTART_RATE_10G}, ++ [PHY_INTERFACE_MODE_USXGMII] = {0x080, AQUANTIA_VND1_GSYSCFG_10G, ++ AQUANTIA_VND1_GSTART_RATE_10G}, ++}; ++ ++/* Sets up protocol on system side before calling aqr_config_aneg */ ++static int aqr_config_aneg_set_prot(struct phy_device *phydev) ++{ ++ int if_type = phydev->interface; ++ int i; ++ ++ if (!aquantia_syscfg[if_type].cnt) ++ return 0; ++ ++ /* set PHY in low power mode so we can configure protocols */ ++ phy_write_mmd(phydev, MDIO_MMD_VEND1, AQUANTIA_VND1_GLOBAL_SC, ++ AQUANTIA_VND1_GLOBAL_SC_LP); ++ mdelay(10); ++ ++ /* set the default rate to enable the SI link */ ++ phy_write_mmd(phydev, MDIO_MMD_VEND1, AQUANTIA_VND1_GSTART_RATE, ++ aquantia_syscfg[if_type].start_rate); ++ ++ for (i = 0; i <= aquantia_syscfg[if_type].cnt; i++) ++ phy_write_mmd(phydev, MDIO_MMD_VEND1, ++ AQUANTIA_VND1_GSYSCFG_BASE + i, ++ aquantia_syscfg[if_type].syscfg); ++ ++ /* wake PHY back up */ ++ phy_write_mmd(phydev, MDIO_MMD_VEND1, AQUANTIA_VND1_GLOBAL_SC, 0); ++ mdelay(10); ++ ++ return aqr_config_aneg(phydev); ++} ++ + static int aqr_config_intr(struct phy_device *phydev) + { + bool en = phydev->interrupts == PHY_INTERRUPT_ENABLED; +@@ -780,6 +850,30 @@ static struct phy_driver aqr_driver[] = + .get_stats = aqr107_get_stats, + .link_change_notify = aqr107_link_change_notify, + }, ++{ ++ PHY_ID_MATCH_MODEL(PHY_ID_AQR112), ++ .name = "Aquantia AQR112", ++ .probe = aqr107_probe, ++ .config_aneg = aqr_config_aneg_set_prot, ++ .config_intr = aqr_config_intr, ++ .handle_interrupt = aqr_handle_interrupt, ++ .read_status = aqr107_read_status, ++ .get_sset_count = aqr107_get_sset_count, ++ .get_strings = aqr107_get_strings, ++ .get_stats = aqr107_get_stats, ++}, ++{ ++ PHY_ID_MATCH_MODEL(PHY_ID_AQR412), ++ .name = "Aquantia AQR412", ++ .probe = aqr107_probe, ++ .config_aneg = aqr_config_aneg_set_prot, ++ .config_intr = aqr_config_intr, ++ .handle_interrupt = aqr_handle_interrupt, ++ .read_status = aqr107_read_status, ++ .get_sset_count = aqr107_get_sset_count, ++ .get_strings = aqr107_get_strings, ++ .get_stats = aqr107_get_stats, ++}, + }; + + module_phy_driver(aqr_driver); +@@ -790,9 +884,11 @@ static struct mdio_device_id __maybe_unu + { PHY_ID_MATCH_MODEL(PHY_ID_AQR105) }, + { PHY_ID_MATCH_MODEL(PHY_ID_AQR106) }, + { PHY_ID_MATCH_MODEL(PHY_ID_AQR107) }, ++ { PHY_ID_MATCH_MODEL(PHY_ID_AQR112) }, + { PHY_ID_MATCH_MODEL(PHY_ID_AQR113C) }, + { PHY_ID_MATCH_MODEL(PHY_ID_AQCS109) }, + { PHY_ID_MATCH_MODEL(PHY_ID_AQR405) }, ++ { PHY_ID_MATCH_MODEL(PHY_ID_AQR412) }, + { PHY_ID_MATCH_MODEL(PHY_ID_AQR813) }, + { } + }; diff --git a/pkgs/patches-linux-5.15/723-net-phy-aquantia-fix-system-side-protocol-mi.patch b/pkgs/patches-linux-5.15/723-net-phy-aquantia-fix-system-side-protocol-mi.patch new file mode 100644 index 0000000..d99b495 --- /dev/null +++ b/pkgs/patches-linux-5.15/723-net-phy-aquantia-fix-system-side-protocol-mi.patch @@ -0,0 +1,34 @@ +From 5f008cb22f60da4e10375f22266c1a4e20b1252e Mon Sep 17 00:00:00 2001 +From: Alex Marginean <alexandru.marginean@nxp.com> +Date: Fri, 20 Sep 2019 18:22:52 +0300 +Subject: [PATCH] drivers: net: phy: aquantia: fix system side protocol + misconfiguration + +Do not set up protocols for speeds that are not supported by FW. Enabling +these protocols leads to link issues on system side. + +Signed-off-by: Alex Marginean <alexandru.marginean@nxp.com> +--- + drivers/net/phy/aquantia_main.c | 8 +++++++- + 1 file changed, 7 insertions(+), 1 deletion(-) + +--- a/drivers/net/phy/aquantia_main.c ++++ b/drivers/net/phy/aquantia_main.c +@@ -315,10 +315,16 @@ static int aqr_config_aneg_set_prot(stru + phy_write_mmd(phydev, MDIO_MMD_VEND1, AQUANTIA_VND1_GSTART_RATE, + aquantia_syscfg[if_type].start_rate); + +- for (i = 0; i <= aquantia_syscfg[if_type].cnt; i++) ++ for (i = 0; i <= aquantia_syscfg[if_type].cnt; i++) { ++ u16 reg = phy_read_mmd(phydev, MDIO_MMD_VEND1, ++ AQUANTIA_VND1_GSYSCFG_BASE + i); ++ if (!reg) ++ continue; ++ + phy_write_mmd(phydev, MDIO_MMD_VEND1, + AQUANTIA_VND1_GSYSCFG_BASE + i, + aquantia_syscfg[if_type].syscfg); ++ } + + /* wake PHY back up */ + phy_write_mmd(phydev, MDIO_MMD_VEND1, AQUANTIA_VND1_GLOBAL_SC, 0); diff --git a/pkgs/patches-linux-5.15/724-net-phy-aquantia-Add-AQR113-driver-support.patch b/pkgs/patches-linux-5.15/724-net-phy-aquantia-Add-AQR113-driver-support.patch new file mode 100644 index 0000000..634288c --- /dev/null +++ b/pkgs/patches-linux-5.15/724-net-phy-aquantia-Add-AQR113-driver-support.patch @@ -0,0 +1,43 @@ +From 2e677e4ae8f8330f68013163b060d0fda3a43095 Mon Sep 17 00:00:00 2001 +From: "Langer, Thomas" <tlanger@maxlinear.com> +Date: Fri, 9 Jul 2021 17:36:46 +0200 +Subject: [PATCH] PONRTSYS-8842: aquantia: Add AQR113 driver support + +Add a new entry for AQR113 PHY_ID +--- + drivers/net/phy/aquantia_main.c | 10 ++++++++++ + 1 file changed, 10 insertions(+) + +--- a/drivers/net/phy/aquantia_main.c ++++ b/drivers/net/phy/aquantia_main.c +@@ -21,6 +21,7 @@ + #define PHY_ID_AQR106 0x03a1b4d0 + #define PHY_ID_AQR107 0x03a1b4e0 + #define PHY_ID_AQR112 0x03a1b662 ++#define PHY_ID_AQR113 0x31c31c40 + #define PHY_ID_AQR113C 0x31c31c12 + #define PHY_ID_AQCS109 0x03a1b5c2 + #define PHY_ID_AQR405 0x03a1b4b0 +@@ -869,6 +870,14 @@ static struct phy_driver aqr_driver[] = + .get_stats = aqr107_get_stats, + }, + { ++ PHY_ID_MATCH_MODEL(PHY_ID_AQR113), ++ .name = "Aquantia AQR113", ++ .config_aneg = aqr_config_aneg, ++ .config_intr = aqr_config_intr, ++ .handle_interrupt = aqr_handle_interrupt, ++ .read_status = aqr107_read_status, ++}, ++{ + PHY_ID_MATCH_MODEL(PHY_ID_AQR412), + .name = "Aquantia AQR412", + .probe = aqr107_probe, +@@ -891,6 +900,7 @@ static struct mdio_device_id __maybe_unu + { PHY_ID_MATCH_MODEL(PHY_ID_AQR106) }, + { PHY_ID_MATCH_MODEL(PHY_ID_AQR107) }, + { PHY_ID_MATCH_MODEL(PHY_ID_AQR112) }, ++ { PHY_ID_MATCH_MODEL(PHY_ID_AQR113) }, + { PHY_ID_MATCH_MODEL(PHY_ID_AQR113C) }, + { PHY_ID_MATCH_MODEL(PHY_ID_AQCS109) }, + { PHY_ID_MATCH_MODEL(PHY_ID_AQR405) }, diff --git a/pkgs/patches-linux-5.15/725-net-phy-aquantia-add-PHY_IDs-for-AQR112-variants.patch b/pkgs/patches-linux-5.15/725-net-phy-aquantia-add-PHY_IDs-for-AQR112-variants.patch new file mode 100644 index 0000000..f7ccc45 --- /dev/null +++ b/pkgs/patches-linux-5.15/725-net-phy-aquantia-add-PHY_IDs-for-AQR112-variants.patch @@ -0,0 +1,63 @@ +From 3b92ee7b7899b6beffb2b484c58326e36612a873 Mon Sep 17 00:00:00 2001 +From: Daniel Golle <daniel@makrotopia.org> +Date: Thu, 23 Dec 2021 14:52:56 +0000 +Subject: [PATCH] net: phy: aquantia: add PHY_ID for AQR112R + +As advised by Ian Chang this PHY is used in Puzzle devices. + +Signed-off-by: Daniel Golle <daniel@makrotopia.org> +--- + drivers/net/phy/aquantia_main.c | 10 ++++++++++ + 1 file changed, 10 insertions(+) + +--- a/drivers/net/phy/aquantia_main.c ++++ b/drivers/net/phy/aquantia_main.c +@@ -21,6 +21,8 @@ + #define PHY_ID_AQR106 0x03a1b4d0 + #define PHY_ID_AQR107 0x03a1b4e0 + #define PHY_ID_AQR112 0x03a1b662 ++#define PHY_ID_AQR112C 0x03a1b790 ++#define PHY_ID_AQR112R 0x31c31d12 + #define PHY_ID_AQR113 0x31c31c40 + #define PHY_ID_AQR113C 0x31c31c12 + #define PHY_ID_AQCS109 0x03a1b5c2 +@@ -870,6 +872,30 @@ static struct phy_driver aqr_driver[] = + .get_stats = aqr107_get_stats, + }, + { ++ PHY_ID_MATCH_MODEL(PHY_ID_AQR112C), ++ .name = "Aquantia AQR112C", ++ .probe = aqr107_probe, ++ .config_aneg = aqr_config_aneg_set_prot, ++ .config_intr = aqr_config_intr, ++ .handle_interrupt = aqr_handle_interrupt, ++ .read_status = aqr107_read_status, ++ .get_sset_count = aqr107_get_sset_count, ++ .get_strings = aqr107_get_strings, ++ .get_stats = aqr107_get_stats, ++}, ++{ ++ PHY_ID_MATCH_MODEL(PHY_ID_AQR112R), ++ .name = "Aquantia AQR112R", ++ .probe = aqr107_probe, ++ .config_aneg = aqr_config_aneg_set_prot, ++ .config_intr = aqr_config_intr, ++ .handle_interrupt = aqr_handle_interrupt, ++ .read_status = aqr107_read_status, ++ .get_sset_count = aqr107_get_sset_count, ++ .get_strings = aqr107_get_strings, ++ .get_stats = aqr107_get_stats, ++}, ++{ + PHY_ID_MATCH_MODEL(PHY_ID_AQR113), + .name = "Aquantia AQR113", + .config_aneg = aqr_config_aneg, +@@ -900,6 +926,8 @@ static struct mdio_device_id __maybe_unu + { PHY_ID_MATCH_MODEL(PHY_ID_AQR106) }, + { PHY_ID_MATCH_MODEL(PHY_ID_AQR107) }, + { PHY_ID_MATCH_MODEL(PHY_ID_AQR112) }, ++ { PHY_ID_MATCH_MODEL(PHY_ID_AQR112C) }, ++ { PHY_ID_MATCH_MODEL(PHY_ID_AQR112R) }, + { PHY_ID_MATCH_MODEL(PHY_ID_AQR113) }, + { PHY_ID_MATCH_MODEL(PHY_ID_AQR113C) }, + { PHY_ID_MATCH_MODEL(PHY_ID_AQCS109) }, diff --git a/pkgs/patches-linux-5.15/734-v5.16-0001-net-bgmac-improve-handling-PHY.patch b/pkgs/patches-linux-5.15/734-v5.16-0001-net-bgmac-improve-handling-PHY.patch new file mode 100644 index 0000000..6788a2e --- /dev/null +++ b/pkgs/patches-linux-5.15/734-v5.16-0001-net-bgmac-improve-handling-PHY.patch @@ -0,0 +1,84 @@ +From b5375509184dc23d2b7fa0c5ed8763899ccc9674 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <rafal@milecki.pl> +Date: Sat, 2 Oct 2021 19:58:11 +0200 +Subject: [PATCH] net: bgmac: improve handling PHY +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +1. Use info from DT if available + +It allows describing for example a fixed link. It's more accurate than +just guessing there may be one (depending on a chipset). + +2. Verify PHY ID before trying to connect PHY + +PHY addr 0x1e (30) is special in Broadcom routers and means a switch +connected as MDIO devices instead of a real PHY. Don't try connecting to +it. + +Signed-off-by: Rafał Miłecki <rafal@milecki.pl> +Signed-off-by: David S. Miller <davem@davemloft.net> +--- + drivers/net/ethernet/broadcom/bgmac-bcma.c | 33 ++++++++++++++-------- + 1 file changed, 21 insertions(+), 12 deletions(-) + +--- a/drivers/net/ethernet/broadcom/bgmac-bcma.c ++++ b/drivers/net/ethernet/broadcom/bgmac-bcma.c +@@ -11,6 +11,7 @@ + #include <linux/bcma/bcma.h> + #include <linux/brcmphy.h> + #include <linux/etherdevice.h> ++#include <linux/of_mdio.h> + #include <linux/of_net.h> + #include "bgmac.h" + +@@ -86,17 +87,28 @@ static int bcma_phy_connect(struct bgmac + struct phy_device *phy_dev; + char bus_id[MII_BUS_ID_SIZE + 3]; + ++ /* DT info should be the most accurate */ ++ phy_dev = of_phy_get_and_connect(bgmac->net_dev, bgmac->dev->of_node, ++ bgmac_adjust_link); ++ if (phy_dev) ++ return 0; ++ + /* Connect to the PHY */ +- snprintf(bus_id, sizeof(bus_id), PHY_ID_FMT, bgmac->mii_bus->id, +- bgmac->phyaddr); +- phy_dev = phy_connect(bgmac->net_dev, bus_id, bgmac_adjust_link, +- PHY_INTERFACE_MODE_MII); +- if (IS_ERR(phy_dev)) { +- dev_err(bgmac->dev, "PHY connection failed\n"); +- return PTR_ERR(phy_dev); ++ if (bgmac->mii_bus && bgmac->phyaddr != BGMAC_PHY_NOREGS) { ++ snprintf(bus_id, sizeof(bus_id), PHY_ID_FMT, bgmac->mii_bus->id, ++ bgmac->phyaddr); ++ phy_dev = phy_connect(bgmac->net_dev, bus_id, bgmac_adjust_link, ++ PHY_INTERFACE_MODE_MII); ++ if (IS_ERR(phy_dev)) { ++ dev_err(bgmac->dev, "PHY connection failed\n"); ++ return PTR_ERR(phy_dev); ++ } ++ ++ return 0; + } + +- return 0; ++ /* Assume a fixed link to the switch port */ ++ return bgmac_phy_connect_direct(bgmac); + } + + static const struct bcma_device_id bgmac_bcma_tbl[] = { +@@ -297,10 +309,7 @@ static int bgmac_probe(struct bcma_devic + bgmac->cco_ctl_maskset = bcma_bgmac_cco_ctl_maskset; + bgmac->get_bus_clock = bcma_bgmac_get_bus_clock; + bgmac->cmn_maskset32 = bcma_bgmac_cmn_maskset32; +- if (bgmac->mii_bus) +- bgmac->phy_connect = bcma_phy_connect; +- else +- bgmac->phy_connect = bgmac_phy_connect_direct; ++ bgmac->phy_connect = bcma_phy_connect; + + err = bgmac_enet_probe(bgmac); + if (err) diff --git a/pkgs/patches-linux-5.15/734-v5.16-0002-net-bgmac-support-MDIO-described-in-DT.patch b/pkgs/patches-linux-5.15/734-v5.16-0002-net-bgmac-support-MDIO-described-in-DT.patch new file mode 100644 index 0000000..f134828 --- /dev/null +++ b/pkgs/patches-linux-5.15/734-v5.16-0002-net-bgmac-support-MDIO-described-in-DT.patch @@ -0,0 +1,54 @@ +From 45c9d966688e7fad7f24bfc450547d91e4304d0b Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <rafal@milecki.pl> +Date: Sat, 2 Oct 2021 19:58:12 +0200 +Subject: [PATCH] net: bgmac: support MDIO described in DT +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Check ethernet controller DT node for "mdio" subnode and use it with +of_mdiobus_register() when present. That allows specifying MDIO and its +PHY devices in a standard DT based way. + +This is required for BCM53573 SoC support. That family is sometimes +called Northstar (by marketing?) but is quite different from it. It uses +different CPU(s) and many different hw blocks. + +One of shared blocks in BCM53573 is Ethernet controller. Switch however +is not SRAB accessible (as it Northstar) but is MDIO attached. + +Signed-off-by: Rafał Miłecki <rafal@milecki.pl> +Signed-off-by: David S. Miller <davem@davemloft.net> +--- + drivers/net/ethernet/broadcom/bgmac-bcma-mdio.c | 6 +++++- + 1 file changed, 5 insertions(+), 1 deletion(-) + +--- a/drivers/net/ethernet/broadcom/bgmac-bcma-mdio.c ++++ b/drivers/net/ethernet/broadcom/bgmac-bcma-mdio.c +@@ -10,6 +10,7 @@ + + #include <linux/bcma/bcma.h> + #include <linux/brcmphy.h> ++#include <linux/of_mdio.h> + #include "bgmac.h" + + static bool bcma_mdio_wait_value(struct bcma_device *core, u16 reg, u32 mask, +@@ -211,6 +212,7 @@ struct mii_bus *bcma_mdio_mii_register(s + { + struct bcma_device *core = bgmac->bcma.core; + struct mii_bus *mii_bus; ++ struct device_node *np; + int err; + + mii_bus = mdiobus_alloc(); +@@ -229,7 +231,9 @@ struct mii_bus *bcma_mdio_mii_register(s + mii_bus->parent = &core->dev; + mii_bus->phy_mask = ~(1 << bgmac->phyaddr); + +- err = mdiobus_register(mii_bus); ++ np = of_get_child_by_name(core->dev.of_node, "mdio"); ++ ++ err = of_mdiobus_register(mii_bus, np); + if (err) { + dev_err(&core->dev, "Registration of mii bus failed\n"); + goto err_free_bus; diff --git a/pkgs/patches-linux-5.15/742-v5.16-net-phy-at803x-add-support-for-qca-8327-internal-phy.patch b/pkgs/patches-linux-5.15/742-v5.16-net-phy-at803x-add-support-for-qca-8327-internal-phy.patch new file mode 100644 index 0000000..8f000ba --- /dev/null +++ b/pkgs/patches-linux-5.15/742-v5.16-net-phy-at803x-add-support-for-qca-8327-internal-phy.patch @@ -0,0 +1,48 @@ +From 0ccf8511182436183c031e8a2f740ae91a02c625 Mon Sep 17 00:00:00 2001 +From: Ansuel Smith <ansuelsmth@gmail.com> +Date: Tue, 14 Sep 2021 14:33:45 +0200 +Subject: net: phy: at803x: add support for qca 8327 internal phy + +Add support for qca8327 internal phy needed for correct init of the +switch port. It does use the same qca8337 function and reg just with a +different id. + +Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com> +Tested-by: Rosen Penev <rosenp@gmail.com> +Tested-by: Andrew Lunn <andrew@lunn.ch> +Signed-off-by: David S. Miller <davem@davemloft.net> +--- + drivers/net/phy/at803x.c | 15 +++++++++++++++ + 1 file changed, 15 insertions(+) + +--- a/drivers/net/phy/at803x.c ++++ b/drivers/net/phy/at803x.c +@@ -1412,6 +1412,19 @@ static struct phy_driver at803x_driver[] + .get_sset_count = at803x_get_sset_count, + .get_strings = at803x_get_strings, + .get_stats = at803x_get_stats, ++}, { ++ /* QCA8327 */ ++ .phy_id = QCA8327_PHY_ID, ++ .phy_id_mask = QCA8K_PHY_ID_MASK, ++ .name = "QCA PHY 8327", ++ /* PHY_GBIT_FEATURES */ ++ .probe = at803x_probe, ++ .flags = PHY_IS_INTERNAL, ++ .config_init = qca83xx_config_init, ++ .soft_reset = genphy_soft_reset, ++ .get_sset_count = at803x_get_sset_count, ++ .get_strings = at803x_get_strings, ++ .get_stats = at803x_get_stats, + }, }; + + module_phy_driver(at803x_driver); +@@ -1422,6 +1435,8 @@ static struct mdio_device_id __maybe_unu + { PHY_ID_MATCH_EXACT(ATH8032_PHY_ID) }, + { PHY_ID_MATCH_EXACT(ATH8035_PHY_ID) }, + { PHY_ID_MATCH_EXACT(ATH9331_PHY_ID) }, ++ { PHY_ID_MATCH_EXACT(QCA8337_PHY_ID) }, ++ { PHY_ID_MATCH_EXACT(QCA8327_PHY_ID) }, + { } + }; + diff --git a/pkgs/patches-linux-5.15/743-v5.16-0001-net-dsa-b53-Include-all-ports-in-enabled_ports.patch b/pkgs/patches-linux-5.15/743-v5.16-0001-net-dsa-b53-Include-all-ports-in-enabled_ports.patch new file mode 100644 index 0000000..eb84b45 --- /dev/null +++ b/pkgs/patches-linux-5.15/743-v5.16-0001-net-dsa-b53-Include-all-ports-in-enabled_ports.patch @@ -0,0 +1,131 @@ +From 983d96a9116a328668601555d96736261d33170c Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <rafal@milecki.pl> +Date: Thu, 16 Sep 2021 14:03:51 +0200 +Subject: [PATCH] net: dsa: b53: Include all ports in "enabled_ports" +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Make "enabled_ports" bitfield contain all available switch ports +including a CPU port. This way there is no need for fixup during +initialization. + +For BCM53010, BCM53018 and BCM53019 include also other available ports. + +Signed-off-by: Rafał Miłecki <rafal@milecki.pl> +Reviewed-by: Florian Fainelli <f.fainelli@gmail.com> +Tested-by: Florian Fainelli <f.fainelli@gmail.com> +Signed-off-by: Jakub Kicinski <kuba@kernel.org> +--- + drivers/net/dsa/b53/b53_common.c | 23 +++++++++++------------ + 1 file changed, 11 insertions(+), 12 deletions(-) + +--- a/drivers/net/dsa/b53/b53_common.c ++++ b/drivers/net/dsa/b53/b53_common.c +@@ -2302,7 +2302,7 @@ static const struct b53_chip_data b53_sw + .chip_id = BCM5325_DEVICE_ID, + .dev_name = "BCM5325", + .vlans = 16, +- .enabled_ports = 0x1f, ++ .enabled_ports = 0x3f, + .arl_bins = 2, + .arl_buckets = 1024, + .imp_port = 5, +@@ -2313,7 +2313,7 @@ static const struct b53_chip_data b53_sw + .chip_id = BCM5365_DEVICE_ID, + .dev_name = "BCM5365", + .vlans = 256, +- .enabled_ports = 0x1f, ++ .enabled_ports = 0x3f, + .arl_bins = 2, + .arl_buckets = 1024, + .imp_port = 5, +@@ -2324,7 +2324,7 @@ static const struct b53_chip_data b53_sw + .chip_id = BCM5389_DEVICE_ID, + .dev_name = "BCM5389", + .vlans = 4096, +- .enabled_ports = 0x1f, ++ .enabled_ports = 0x11f, + .arl_bins = 4, + .arl_buckets = 1024, + .imp_port = 8, +@@ -2338,7 +2338,7 @@ static const struct b53_chip_data b53_sw + .chip_id = BCM5395_DEVICE_ID, + .dev_name = "BCM5395", + .vlans = 4096, +- .enabled_ports = 0x1f, ++ .enabled_ports = 0x11f, + .arl_bins = 4, + .arl_buckets = 1024, + .imp_port = 8, +@@ -2352,7 +2352,7 @@ static const struct b53_chip_data b53_sw + .chip_id = BCM5397_DEVICE_ID, + .dev_name = "BCM5397", + .vlans = 4096, +- .enabled_ports = 0x1f, ++ .enabled_ports = 0x11f, + .arl_bins = 4, + .arl_buckets = 1024, + .imp_port = 8, +@@ -2366,7 +2366,7 @@ static const struct b53_chip_data b53_sw + .chip_id = BCM5398_DEVICE_ID, + .dev_name = "BCM5398", + .vlans = 4096, +- .enabled_ports = 0x7f, ++ .enabled_ports = 0x17f, + .arl_bins = 4, + .arl_buckets = 1024, + .imp_port = 8, +@@ -2380,7 +2380,7 @@ static const struct b53_chip_data b53_sw + .chip_id = BCM53115_DEVICE_ID, + .dev_name = "BCM53115", + .vlans = 4096, +- .enabled_ports = 0x1f, ++ .enabled_ports = 0x11f, + .arl_bins = 4, + .arl_buckets = 1024, + .vta_regs = B53_VTA_REGS, +@@ -2394,7 +2394,7 @@ static const struct b53_chip_data b53_sw + .chip_id = BCM53125_DEVICE_ID, + .dev_name = "BCM53125", + .vlans = 4096, +- .enabled_ports = 0xff, ++ .enabled_ports = 0x1ff, + .arl_bins = 4, + .arl_buckets = 1024, + .imp_port = 8, +@@ -2436,7 +2436,7 @@ static const struct b53_chip_data b53_sw + .chip_id = BCM53010_DEVICE_ID, + .dev_name = "BCM53010", + .vlans = 4096, +- .enabled_ports = 0x1f, ++ .enabled_ports = 0x1bf, + .arl_bins = 4, + .arl_buckets = 1024, + .imp_port = 8, +@@ -2478,7 +2478,7 @@ static const struct b53_chip_data b53_sw + .chip_id = BCM53018_DEVICE_ID, + .dev_name = "BCM53018", + .vlans = 4096, +- .enabled_ports = 0x1f, ++ .enabled_ports = 0x1bf, + .arl_bins = 4, + .arl_buckets = 1024, + .imp_port = 8, +@@ -2492,7 +2492,7 @@ static const struct b53_chip_data b53_sw + .chip_id = BCM53019_DEVICE_ID, + .dev_name = "BCM53019", + .vlans = 4096, +- .enabled_ports = 0x1f, ++ .enabled_ports = 0x1bf, + .arl_bins = 4, + .arl_buckets = 1024, + .imp_port = 8, +@@ -2634,7 +2634,6 @@ static int b53_switch_init(struct b53_de + dev->cpu_port = 5; + } + +- dev->enabled_ports |= BIT(dev->cpu_port); + dev->num_ports = fls(dev->enabled_ports); + + dev->ds->num_ports = min_t(unsigned int, dev->num_ports, DSA_MAX_PORTS); diff --git a/pkgs/patches-linux-5.15/743-v5.16-0002-net-dsa-b53-Drop-BCM5301x-workaround-for-a-wrong-CPU.patch b/pkgs/patches-linux-5.15/743-v5.16-0002-net-dsa-b53-Drop-BCM5301x-workaround-for-a-wrong-CPU.patch new file mode 100644 index 0000000..23805a9 --- /dev/null +++ b/pkgs/patches-linux-5.15/743-v5.16-0002-net-dsa-b53-Drop-BCM5301x-workaround-for-a-wrong-CPU.patch @@ -0,0 +1,42 @@ +From b290c6384afabbca5ae6e2af72fb1b2bc37922be Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <rafal@milecki.pl> +Date: Thu, 16 Sep 2021 14:03:52 +0200 +Subject: [PATCH] net: dsa: b53: Drop BCM5301x workaround for a wrong CPU/IMP + port +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +On BCM5301x port 8 requires a fixed link when used. + +Years ago when b53 was an OpenWrt downstream driver (with configuration +based on sometimes bugged NVRAM) there was a need for a fixup. In case +of forcing fixed link for (incorrectly specified) port 5 the code had to +actually setup port 8 link. + +For upstream b53 driver with setup based on DT there is no need for that +workaround. In DT we have and require correct ports setup. + +Signed-off-by: Rafał Miłecki <rafal@milecki.pl> +Reviewed-by: Florian Fainelli <f.fainelli@gmail.com> +Tested-by: Florian Fainelli <f.fainelli@gmail.com> +Signed-off-by: Jakub Kicinski <kuba@kernel.org> +--- + drivers/net/dsa/b53/b53_common.c | 6 ------ + 1 file changed, 6 deletions(-) + +--- a/drivers/net/dsa/b53/b53_common.c ++++ b/drivers/net/dsa/b53/b53_common.c +@@ -1291,12 +1291,6 @@ static void b53_adjust_link(struct dsa_s + return; + } + } +- } else if (is5301x(dev)) { +- if (port != dev->cpu_port) { +- b53_force_port_config(dev, dev->cpu_port, 2000, +- DUPLEX_FULL, true, true); +- b53_force_link(dev, dev->cpu_port, 1); +- } + } + + /* Re-negotiate EEE if it was enabled already */ diff --git a/pkgs/patches-linux-5.15/743-v5.16-0003-net-dsa-b53-Improve-flow-control-setup-on-BCM5301x.patch b/pkgs/patches-linux-5.15/743-v5.16-0003-net-dsa-b53-Improve-flow-control-setup-on-BCM5301x.patch new file mode 100644 index 0000000..941fa23 --- /dev/null +++ b/pkgs/patches-linux-5.15/743-v5.16-0003-net-dsa-b53-Improve-flow-control-setup-on-BCM5301x.patch @@ -0,0 +1,32 @@ +From 3ff26b29230c54fea2353b63124c589b61953e14 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <rafal@milecki.pl> +Date: Thu, 16 Sep 2021 14:03:53 +0200 +Subject: [PATCH] net: dsa: b53: Improve flow control setup on BCM5301x +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +According to the Broadcom's reference driver flow control needs to be +enabled for any CPU switch port (5, 7 or 8 - depending on which one is +used). Current code makes it work only for the port 5. Use +dsa_is_cpu_port() which solved that problem. + +Signed-off-by: Rafał Miłecki <rafal@milecki.pl> +Reviewed-by: Florian Fainelli <f.fainelli@gmail.com> +Tested-by: Florian Fainelli <f.fainelli@gmail.com> +Signed-off-by: Jakub Kicinski <kuba@kernel.org> +--- + drivers/net/dsa/b53/b53_common.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/drivers/net/dsa/b53/b53_common.c ++++ b/drivers/net/dsa/b53/b53_common.c +@@ -1222,7 +1222,7 @@ static void b53_adjust_link(struct dsa_s + return; + + /* Enable flow control on BCM5301x's CPU port */ +- if (is5301x(dev) && port == dev->cpu_port) ++ if (is5301x(dev) && dsa_is_cpu_port(ds, port)) + tx_pause = rx_pause = true; + + if (phydev->pause) { diff --git a/pkgs/patches-linux-5.15/743-v5.16-0004-net-dsa-b53-Drop-unused-cpu_port-field.patch b/pkgs/patches-linux-5.15/743-v5.16-0004-net-dsa-b53-Drop-unused-cpu_port-field.patch new file mode 100644 index 0000000..746a1e3 --- /dev/null +++ b/pkgs/patches-linux-5.15/743-v5.16-0004-net-dsa-b53-Drop-unused-cpu_port-field.patch @@ -0,0 +1,205 @@ +From 7d5af56418d7d01e43247a33b6fe6492ea871923 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <rafal@milecki.pl> +Date: Thu, 16 Sep 2021 14:03:54 +0200 +Subject: [PATCH] net: dsa: b53: Drop unused "cpu_port" field +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +It's set but never used anymore. + +Signed-off-by: Rafał Miłecki <rafal@milecki.pl> +Reviewed-by: Florian Fainelli <f.fainelli@gmail.com> +Tested-by: Florian Fainelli <f.fainelli@gmail.com> +Signed-off-by: Jakub Kicinski <kuba@kernel.org> +--- + drivers/net/dsa/b53/b53_common.c | 28 ---------------------------- + drivers/net/dsa/b53/b53_priv.h | 1 - + 2 files changed, 29 deletions(-) + +--- a/drivers/net/dsa/b53/b53_common.c ++++ b/drivers/net/dsa/b53/b53_common.c +@@ -2300,7 +2300,6 @@ static const struct b53_chip_data b53_sw + .arl_bins = 2, + .arl_buckets = 1024, + .imp_port = 5, +- .cpu_port = B53_CPU_PORT_25, + .duplex_reg = B53_DUPLEX_STAT_FE, + }, + { +@@ -2311,7 +2310,6 @@ static const struct b53_chip_data b53_sw + .arl_bins = 2, + .arl_buckets = 1024, + .imp_port = 5, +- .cpu_port = B53_CPU_PORT_25, + .duplex_reg = B53_DUPLEX_STAT_FE, + }, + { +@@ -2322,7 +2320,6 @@ static const struct b53_chip_data b53_sw + .arl_bins = 4, + .arl_buckets = 1024, + .imp_port = 8, +- .cpu_port = B53_CPU_PORT, + .vta_regs = B53_VTA_REGS, + .duplex_reg = B53_DUPLEX_STAT_GE, + .jumbo_pm_reg = B53_JUMBO_PORT_MASK, +@@ -2336,7 +2333,6 @@ static const struct b53_chip_data b53_sw + .arl_bins = 4, + .arl_buckets = 1024, + .imp_port = 8, +- .cpu_port = B53_CPU_PORT, + .vta_regs = B53_VTA_REGS, + .duplex_reg = B53_DUPLEX_STAT_GE, + .jumbo_pm_reg = B53_JUMBO_PORT_MASK, +@@ -2350,7 +2346,6 @@ static const struct b53_chip_data b53_sw + .arl_bins = 4, + .arl_buckets = 1024, + .imp_port = 8, +- .cpu_port = B53_CPU_PORT, + .vta_regs = B53_VTA_REGS_9798, + .duplex_reg = B53_DUPLEX_STAT_GE, + .jumbo_pm_reg = B53_JUMBO_PORT_MASK, +@@ -2364,7 +2359,6 @@ static const struct b53_chip_data b53_sw + .arl_bins = 4, + .arl_buckets = 1024, + .imp_port = 8, +- .cpu_port = B53_CPU_PORT, + .vta_regs = B53_VTA_REGS_9798, + .duplex_reg = B53_DUPLEX_STAT_GE, + .jumbo_pm_reg = B53_JUMBO_PORT_MASK, +@@ -2379,7 +2373,6 @@ static const struct b53_chip_data b53_sw + .arl_buckets = 1024, + .vta_regs = B53_VTA_REGS, + .imp_port = 8, +- .cpu_port = B53_CPU_PORT, + .duplex_reg = B53_DUPLEX_STAT_GE, + .jumbo_pm_reg = B53_JUMBO_PORT_MASK, + .jumbo_size_reg = B53_JUMBO_MAX_SIZE, +@@ -2392,7 +2385,6 @@ static const struct b53_chip_data b53_sw + .arl_bins = 4, + .arl_buckets = 1024, + .imp_port = 8, +- .cpu_port = B53_CPU_PORT, + .vta_regs = B53_VTA_REGS, + .duplex_reg = B53_DUPLEX_STAT_GE, + .jumbo_pm_reg = B53_JUMBO_PORT_MASK, +@@ -2406,7 +2398,6 @@ static const struct b53_chip_data b53_sw + .arl_bins = 4, + .arl_buckets = 1024, + .imp_port = 8, +- .cpu_port = B53_CPU_PORT, + .vta_regs = B53_VTA_REGS, + .duplex_reg = B53_DUPLEX_STAT_GE, + .jumbo_pm_reg = B53_JUMBO_PORT_MASK, +@@ -2420,7 +2411,6 @@ static const struct b53_chip_data b53_sw + .arl_bins = 4, + .arl_buckets = 1024, + .imp_port = 8, +- .cpu_port = B53_CPU_PORT, + .vta_regs = B53_VTA_REGS_63XX, + .duplex_reg = B53_DUPLEX_STAT_63XX, + .jumbo_pm_reg = B53_JUMBO_PORT_MASK_63XX, +@@ -2434,7 +2424,6 @@ static const struct b53_chip_data b53_sw + .arl_bins = 4, + .arl_buckets = 1024, + .imp_port = 8, +- .cpu_port = B53_CPU_PORT_25, /* TODO: auto detect */ + .vta_regs = B53_VTA_REGS, + .duplex_reg = B53_DUPLEX_STAT_GE, + .jumbo_pm_reg = B53_JUMBO_PORT_MASK, +@@ -2448,7 +2437,6 @@ static const struct b53_chip_data b53_sw + .arl_bins = 4, + .arl_buckets = 1024, + .imp_port = 8, +- .cpu_port = B53_CPU_PORT_25, /* TODO: auto detect */ + .vta_regs = B53_VTA_REGS, + .duplex_reg = B53_DUPLEX_STAT_GE, + .jumbo_pm_reg = B53_JUMBO_PORT_MASK, +@@ -2462,7 +2450,6 @@ static const struct b53_chip_data b53_sw + .arl_bins = 4, + .arl_buckets = 1024, + .imp_port = 8, +- .cpu_port = B53_CPU_PORT_25, /* TODO: auto detect */ + .vta_regs = B53_VTA_REGS, + .duplex_reg = B53_DUPLEX_STAT_GE, + .jumbo_pm_reg = B53_JUMBO_PORT_MASK, +@@ -2476,7 +2463,6 @@ static const struct b53_chip_data b53_sw + .arl_bins = 4, + .arl_buckets = 1024, + .imp_port = 8, +- .cpu_port = B53_CPU_PORT_25, /* TODO: auto detect */ + .vta_regs = B53_VTA_REGS, + .duplex_reg = B53_DUPLEX_STAT_GE, + .jumbo_pm_reg = B53_JUMBO_PORT_MASK, +@@ -2490,7 +2476,6 @@ static const struct b53_chip_data b53_sw + .arl_bins = 4, + .arl_buckets = 1024, + .imp_port = 8, +- .cpu_port = B53_CPU_PORT_25, /* TODO: auto detect */ + .vta_regs = B53_VTA_REGS, + .duplex_reg = B53_DUPLEX_STAT_GE, + .jumbo_pm_reg = B53_JUMBO_PORT_MASK, +@@ -2504,7 +2489,6 @@ static const struct b53_chip_data b53_sw + .arl_bins = 4, + .arl_buckets = 1024, + .imp_port = 8, +- .cpu_port = B53_CPU_PORT, + .vta_regs = B53_VTA_REGS, + .duplex_reg = B53_DUPLEX_STAT_GE, + .jumbo_pm_reg = B53_JUMBO_PORT_MASK, +@@ -2518,7 +2502,6 @@ static const struct b53_chip_data b53_sw + .arl_bins = 4, + .arl_buckets = 1024, + .imp_port = 8, +- .cpu_port = B53_CPU_PORT, + .vta_regs = B53_VTA_REGS, + .duplex_reg = B53_DUPLEX_STAT_GE, + .jumbo_pm_reg = B53_JUMBO_PORT_MASK, +@@ -2547,7 +2530,6 @@ static const struct b53_chip_data b53_sw + .arl_bins = 4, + .arl_buckets = 1024, + .imp_port = 8, +- .cpu_port = B53_CPU_PORT, + .vta_regs = B53_VTA_REGS, + .duplex_reg = B53_DUPLEX_STAT_GE, + .jumbo_pm_reg = B53_JUMBO_PORT_MASK, +@@ -2561,7 +2543,6 @@ static const struct b53_chip_data b53_sw + .arl_bins = 4, + .arl_buckets = 256, + .imp_port = 8, +- .cpu_port = B53_CPU_PORT, + .vta_regs = B53_VTA_REGS, + .duplex_reg = B53_DUPLEX_STAT_GE, + .jumbo_pm_reg = B53_JUMBO_PORT_MASK, +@@ -2587,7 +2568,6 @@ static int b53_switch_init(struct b53_de + dev->vta_regs[2] = chip->vta_regs[2]; + dev->jumbo_pm_reg = chip->jumbo_pm_reg; + dev->imp_port = chip->imp_port; +- dev->cpu_port = chip->cpu_port; + dev->num_vlans = chip->vlans; + dev->num_arl_bins = chip->arl_bins; + dev->num_arl_buckets = chip->arl_buckets; +@@ -2619,13 +2599,6 @@ static int b53_switch_init(struct b53_de + break; + #endif + } +- } else if (dev->chip_id == BCM53115_DEVICE_ID) { +- u64 strap_value; +- +- b53_read48(dev, B53_STAT_PAGE, B53_STRAP_VALUE, &strap_value); +- /* use second IMP port if GMII is enabled */ +- if (strap_value & SV_GMII_CTRL_115) +- dev->cpu_port = 5; + } + + dev->num_ports = fls(dev->enabled_ports); +--- a/drivers/net/dsa/b53/b53_priv.h ++++ b/drivers/net/dsa/b53/b53_priv.h +@@ -124,7 +124,6 @@ struct b53_device { + /* used ports mask */ + u16 enabled_ports; + unsigned int imp_port; +- unsigned int cpu_port; + + /* connect specific data */ + u8 current_page; diff --git a/pkgs/patches-linux-5.15/745-v5.16-01-net-phy-at803x-add-support-for-qca-8327-A-variant.patch b/pkgs/patches-linux-5.15/745-v5.16-01-net-phy-at803x-add-support-for-qca-8327-A-variant.patch new file mode 100644 index 0000000..99d91df --- /dev/null +++ b/pkgs/patches-linux-5.15/745-v5.16-01-net-phy-at803x-add-support-for-qca-8327-A-variant.patch @@ -0,0 +1,65 @@ +From b4df02b562f4aa14ff6811f30e1b4d2159585c59 Mon Sep 17 00:00:00 2001 +From: Ansuel Smith <ansuelsmth@gmail.com> +Date: Sun, 19 Sep 2021 18:28:15 +0200 +Subject: net: phy: at803x: add support for qca 8327 A variant internal phy + +For qca8327 internal phy there are 2 different switch variant with 2 +different phy id. Add this missing variant so the internal phy can be +correctly identified and fixed. + +Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com> +Reviewed-by: Andrew Lunn <andrew@lunn.ch> +Signed-off-by: David S. Miller <davem@davemloft.net> +--- + drivers/net/phy/at803x.c | 25 ++++++++++++++++++++----- + 1 file changed, 20 insertions(+), 5 deletions(-) + +--- a/drivers/net/phy/at803x.c ++++ b/drivers/net/phy/at803x.c +@@ -150,7 +150,8 @@ + #define ATH8035_PHY_ID 0x004dd072 + #define AT8030_PHY_ID_MASK 0xffffffef + +-#define QCA8327_PHY_ID 0x004dd034 ++#define QCA8327_A_PHY_ID 0x004dd033 ++#define QCA8327_B_PHY_ID 0x004dd034 + #define QCA8337_PHY_ID 0x004dd036 + #define QCA8K_PHY_ID_MASK 0xffffffff + +@@ -1413,10 +1414,23 @@ static struct phy_driver at803x_driver[] + .get_strings = at803x_get_strings, + .get_stats = at803x_get_stats, + }, { +- /* QCA8327 */ +- .phy_id = QCA8327_PHY_ID, ++ /* QCA8327-A from switch QCA8327-AL1A */ ++ .phy_id = QCA8327_A_PHY_ID, + .phy_id_mask = QCA8K_PHY_ID_MASK, +- .name = "QCA PHY 8327", ++ .name = "QCA PHY 8327-A", ++ /* PHY_GBIT_FEATURES */ ++ .probe = at803x_probe, ++ .flags = PHY_IS_INTERNAL, ++ .config_init = qca83xx_config_init, ++ .soft_reset = genphy_soft_reset, ++ .get_sset_count = at803x_get_sset_count, ++ .get_strings = at803x_get_strings, ++ .get_stats = at803x_get_stats, ++}, { ++ /* QCA8327-B from switch QCA8327-BL1A */ ++ .phy_id = QCA8327_B_PHY_ID, ++ .phy_id_mask = QCA8K_PHY_ID_MASK, ++ .name = "QCA PHY 8327-B", + /* PHY_GBIT_FEATURES */ + .probe = at803x_probe, + .flags = PHY_IS_INTERNAL, +@@ -1436,7 +1450,8 @@ static struct mdio_device_id __maybe_unu + { PHY_ID_MATCH_EXACT(ATH8035_PHY_ID) }, + { PHY_ID_MATCH_EXACT(ATH9331_PHY_ID) }, + { PHY_ID_MATCH_EXACT(QCA8337_PHY_ID) }, +- { PHY_ID_MATCH_EXACT(QCA8327_PHY_ID) }, ++ { PHY_ID_MATCH_EXACT(QCA8327_A_PHY_ID) }, ++ { PHY_ID_MATCH_EXACT(QCA8327_B_PHY_ID) }, + { } + }; + diff --git a/pkgs/patches-linux-5.15/745-v5.16-02-net-phy-at803x-add-resume-suspend-function-to-qca83x.patch b/pkgs/patches-linux-5.15/745-v5.16-02-net-phy-at803x-add-resume-suspend-function-to-qca83x.patch new file mode 100644 index 0000000..cd83fac --- /dev/null +++ b/pkgs/patches-linux-5.15/745-v5.16-02-net-phy-at803x-add-resume-suspend-function-to-qca83x.patch @@ -0,0 +1,45 @@ +From 15b9df4ece17d084f14eb0ca1cf05f2ad497e425 Mon Sep 17 00:00:00 2001 +From: Ansuel Smith <ansuelsmth@gmail.com> +Date: Sun, 19 Sep 2021 18:28:16 +0200 +Subject: net: phy: at803x: add resume/suspend function to qca83xx phy + +Add resume/suspend function to qca83xx internal phy. +We can't use the at803x generic function as the documentation lacks of +any support for WoL regs. + +Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com> +Reviewed-by: Andrew Lunn <andrew@lunn.ch> +Signed-off-by: David S. Miller <davem@davemloft.net> +--- + drivers/net/phy/at803x.c | 6 ++++++ + 1 file changed, 6 insertions(+) + +--- a/drivers/net/phy/at803x.c ++++ b/drivers/net/phy/at803x.c +@@ -1413,6 +1413,8 @@ static struct phy_driver at803x_driver[] + .get_sset_count = at803x_get_sset_count, + .get_strings = at803x_get_strings, + .get_stats = at803x_get_stats, ++ .suspend = genphy_suspend, ++ .resume = genphy_resume, + }, { + /* QCA8327-A from switch QCA8327-AL1A */ + .phy_id = QCA8327_A_PHY_ID, +@@ -1426,6 +1428,8 @@ static struct phy_driver at803x_driver[] + .get_sset_count = at803x_get_sset_count, + .get_strings = at803x_get_strings, + .get_stats = at803x_get_stats, ++ .suspend = genphy_suspend, ++ .resume = genphy_resume, + }, { + /* QCA8327-B from switch QCA8327-BL1A */ + .phy_id = QCA8327_B_PHY_ID, +@@ -1439,6 +1443,8 @@ static struct phy_driver at803x_driver[] + .get_sset_count = at803x_get_sset_count, + .get_strings = at803x_get_strings, + .get_stats = at803x_get_stats, ++ .suspend = genphy_suspend, ++ .resume = genphy_resume, + }, }; + + module_phy_driver(at803x_driver); diff --git a/pkgs/patches-linux-5.15/745-v5.16-03-net-phy-at803x-fix-spacing-and-improve-name-for-83xx.patch b/pkgs/patches-linux-5.15/745-v5.16-03-net-phy-at803x-fix-spacing-and-improve-name-for-83xx.patch new file mode 100644 index 0000000..586d895 --- /dev/null +++ b/pkgs/patches-linux-5.15/745-v5.16-03-net-phy-at803x-fix-spacing-and-improve-name-for-83xx.patch @@ -0,0 +1,95 @@ +From d44fd8604a4ab92119adb35f05fd87612af722b5 Mon Sep 17 00:00:00 2001 +From: Ansuel Smith <ansuelsmth@gmail.com> +Date: Sun, 19 Sep 2021 18:28:17 +0200 +Subject: net: phy: at803x: fix spacing and improve name for 83xx phy + +Fix spacing and improve name for 83xx phy following other phy in the +same driver. + +Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com> +Reviewed-by: Andrew Lunn <andrew@lunn.ch> +Signed-off-by: David S. Miller <davem@davemloft.net> +--- + drivers/net/phy/at803x.c | 60 ++++++++++++++++++++++++------------------------ + 1 file changed, 30 insertions(+), 30 deletions(-) + +--- a/drivers/net/phy/at803x.c ++++ b/drivers/net/phy/at803x.c +@@ -1402,47 +1402,47 @@ static struct phy_driver at803x_driver[] + .config_aneg = at803x_config_aneg, + }, { + /* QCA8337 */ +- .phy_id = QCA8337_PHY_ID, +- .phy_id_mask = QCA8K_PHY_ID_MASK, +- .name = "QCA PHY 8337", ++ .phy_id = QCA8337_PHY_ID, ++ .phy_id_mask = QCA8K_PHY_ID_MASK, ++ .name = "Qualcomm Atheros 8337 internal PHY", + /* PHY_GBIT_FEATURES */ +- .probe = at803x_probe, +- .flags = PHY_IS_INTERNAL, +- .config_init = qca83xx_config_init, +- .soft_reset = genphy_soft_reset, +- .get_sset_count = at803x_get_sset_count, +- .get_strings = at803x_get_strings, +- .get_stats = at803x_get_stats, ++ .probe = at803x_probe, ++ .flags = PHY_IS_INTERNAL, ++ .config_init = qca83xx_config_init, ++ .soft_reset = genphy_soft_reset, ++ .get_sset_count = at803x_get_sset_count, ++ .get_strings = at803x_get_strings, ++ .get_stats = at803x_get_stats, + .suspend = genphy_suspend, + .resume = genphy_resume, + }, { + /* QCA8327-A from switch QCA8327-AL1A */ +- .phy_id = QCA8327_A_PHY_ID, +- .phy_id_mask = QCA8K_PHY_ID_MASK, +- .name = "QCA PHY 8327-A", ++ .phy_id = QCA8327_A_PHY_ID, ++ .phy_id_mask = QCA8K_PHY_ID_MASK, ++ .name = "Qualcomm Atheros 8327-A internal PHY", + /* PHY_GBIT_FEATURES */ +- .probe = at803x_probe, +- .flags = PHY_IS_INTERNAL, +- .config_init = qca83xx_config_init, +- .soft_reset = genphy_soft_reset, +- .get_sset_count = at803x_get_sset_count, +- .get_strings = at803x_get_strings, +- .get_stats = at803x_get_stats, ++ .probe = at803x_probe, ++ .flags = PHY_IS_INTERNAL, ++ .config_init = qca83xx_config_init, ++ .soft_reset = genphy_soft_reset, ++ .get_sset_count = at803x_get_sset_count, ++ .get_strings = at803x_get_strings, ++ .get_stats = at803x_get_stats, + .suspend = genphy_suspend, + .resume = genphy_resume, + }, { + /* QCA8327-B from switch QCA8327-BL1A */ +- .phy_id = QCA8327_B_PHY_ID, +- .phy_id_mask = QCA8K_PHY_ID_MASK, +- .name = "QCA PHY 8327-B", ++ .phy_id = QCA8327_B_PHY_ID, ++ .phy_id_mask = QCA8K_PHY_ID_MASK, ++ .name = "Qualcomm Atheros 8327-B internal PHY", + /* PHY_GBIT_FEATURES */ +- .probe = at803x_probe, +- .flags = PHY_IS_INTERNAL, +- .config_init = qca83xx_config_init, +- .soft_reset = genphy_soft_reset, +- .get_sset_count = at803x_get_sset_count, +- .get_strings = at803x_get_strings, +- .get_stats = at803x_get_stats, ++ .probe = at803x_probe, ++ .flags = PHY_IS_INTERNAL, ++ .config_init = qca83xx_config_init, ++ .soft_reset = genphy_soft_reset, ++ .get_sset_count = at803x_get_sset_count, ++ .get_strings = at803x_get_strings, ++ .get_stats = at803x_get_stats, + .suspend = genphy_suspend, + .resume = genphy_resume, + }, }; diff --git a/pkgs/patches-linux-5.15/746-v5.16-01-net-phy-at803x-fix-resume-for-QCA8327-phy.patch b/pkgs/patches-linux-5.15/746-v5.16-01-net-phy-at803x-fix-resume-for-QCA8327-phy.patch new file mode 100644 index 0000000..09797ae --- /dev/null +++ b/pkgs/patches-linux-5.15/746-v5.16-01-net-phy-at803x-fix-resume-for-QCA8327-phy.patch @@ -0,0 +1,131 @@ +From ba3c01ee02ed0d821c9f241f179bbc9457542b8f Mon Sep 17 00:00:00 2001 +From: Ansuel Smith <ansuelsmth@gmail.com> +Date: Sun, 10 Oct 2021 00:46:15 +0200 +Subject: net: phy: at803x: fix resume for QCA8327 phy + +From Documentation phy resume triggers phy reset and restart +auto-negotiation. Add a dedicated function to wait reset to finish as +it was notice a regression where port sometime are not reliable after a +suspend/resume session. The reset wait logic is copied from phy_poll_reset. +Add dedicated suspend function to use genphy_suspend only with QCA8337 +phy and set only additional debug settings for QCA8327. With more test +it was reported that QCA8327 doesn't proprely support this mode and +using this cause the unreliability of the switch ports, especially the +malfunction of the port0. + +Fixes: 15b9df4ece17 ("net: phy: at803x: add resume/suspend function to qca83xx phy") +Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com> +Signed-off-by: David S. Miller <davem@davemloft.net> +--- + drivers/net/phy/at803x.c | 69 +++++++++++++++++++++++++++++++++++++++++++----- + 1 file changed, 63 insertions(+), 6 deletions(-) + +--- a/drivers/net/phy/at803x.c ++++ b/drivers/net/phy/at803x.c +@@ -92,9 +92,14 @@ + #define AT803X_DEBUG_REG_5 0x05 + #define AT803X_DEBUG_TX_CLK_DLY_EN BIT(8) + ++#define AT803X_DEBUG_REG_HIB_CTRL 0x0b ++#define AT803X_DEBUG_HIB_CTRL_SEL_RST_80U BIT(10) ++#define AT803X_DEBUG_HIB_CTRL_EN_ANY_CHANGE BIT(13) ++ + #define AT803X_DEBUG_REG_3C 0x3C + + #define AT803X_DEBUG_REG_3D 0x3D ++#define AT803X_DEBUG_GATE_CLK_IN1000 BIT(6) + + #define AT803X_DEBUG_REG_1F 0x1F + #define AT803X_DEBUG_PLL_ON BIT(2) +@@ -1304,6 +1309,58 @@ static int qca83xx_config_init(struct ph + return 0; + } + ++static int qca83xx_resume(struct phy_device *phydev) ++{ ++ int ret, val; ++ ++ /* Skip reset if not suspended */ ++ if (!phydev->suspended) ++ return 0; ++ ++ /* Reinit the port, reset values set by suspend */ ++ qca83xx_config_init(phydev); ++ ++ /* Reset the port on port resume */ ++ phy_set_bits(phydev, MII_BMCR, BMCR_RESET | BMCR_ANENABLE); ++ ++ /* On resume from suspend the switch execute a reset and ++ * restart auto-negotiation. Wait for reset to complete. ++ */ ++ ret = phy_read_poll_timeout(phydev, MII_BMCR, val, !(val & BMCR_RESET), ++ 50000, 600000, true); ++ if (ret) ++ return ret; ++ ++ msleep(1); ++ ++ return 0; ++} ++ ++static int qca83xx_suspend(struct phy_device *phydev) ++{ ++ u16 mask = 0; ++ ++ /* Only QCA8337 support actual suspend. ++ * QCA8327 cause port unreliability when phy suspend ++ * is set. ++ */ ++ if (phydev->drv->phy_id == QCA8337_PHY_ID) { ++ genphy_suspend(phydev); ++ } else { ++ mask |= ~(BMCR_SPEED1000 | BMCR_FULLDPLX); ++ phy_modify(phydev, MII_BMCR, mask, 0); ++ } ++ ++ at803x_debug_reg_mask(phydev, AT803X_DEBUG_REG_3D, ++ AT803X_DEBUG_GATE_CLK_IN1000, 0); ++ ++ at803x_debug_reg_mask(phydev, AT803X_DEBUG_REG_HIB_CTRL, ++ AT803X_DEBUG_HIB_CTRL_EN_ANY_CHANGE | ++ AT803X_DEBUG_HIB_CTRL_SEL_RST_80U, 0); ++ ++ return 0; ++} ++ + static struct phy_driver at803x_driver[] = { + { + /* Qualcomm Atheros AR8035 */ +@@ -1413,8 +1470,8 @@ static struct phy_driver at803x_driver[] + .get_sset_count = at803x_get_sset_count, + .get_strings = at803x_get_strings, + .get_stats = at803x_get_stats, +- .suspend = genphy_suspend, +- .resume = genphy_resume, ++ .suspend = qca83xx_suspend, ++ .resume = qca83xx_resume, + }, { + /* QCA8327-A from switch QCA8327-AL1A */ + .phy_id = QCA8327_A_PHY_ID, +@@ -1428,8 +1485,8 @@ static struct phy_driver at803x_driver[] + .get_sset_count = at803x_get_sset_count, + .get_strings = at803x_get_strings, + .get_stats = at803x_get_stats, +- .suspend = genphy_suspend, +- .resume = genphy_resume, ++ .suspend = qca83xx_suspend, ++ .resume = qca83xx_resume, + }, { + /* QCA8327-B from switch QCA8327-BL1A */ + .phy_id = QCA8327_B_PHY_ID, +@@ -1443,8 +1500,8 @@ static struct phy_driver at803x_driver[] + .get_sset_count = at803x_get_sset_count, + .get_strings = at803x_get_strings, + .get_stats = at803x_get_stats, +- .suspend = genphy_suspend, +- .resume = genphy_resume, ++ .suspend = qca83xx_suspend, ++ .resume = qca83xx_resume, + }, }; + + module_phy_driver(at803x_driver); diff --git a/pkgs/patches-linux-5.15/746-v5.16-02-net-phy-at803x-add-DAC-amplitude-fix-for-8327-phy.patch b/pkgs/patches-linux-5.15/746-v5.16-02-net-phy-at803x-add-DAC-amplitude-fix-for-8327-phy.patch new file mode 100644 index 0000000..c504c37 --- /dev/null +++ b/pkgs/patches-linux-5.15/746-v5.16-02-net-phy-at803x-add-DAC-amplitude-fix-for-8327-phy.patch @@ -0,0 +1,91 @@ +From 1ca8311949aec5c9447645731ef1c6bc5bd71350 Mon Sep 17 00:00:00 2001 +From: Ansuel Smith <ansuelsmth@gmail.com> +Date: Sun, 10 Oct 2021 00:46:16 +0200 +Subject: net: phy: at803x: add DAC amplitude fix for 8327 phy + +QCA8327 internal phy require DAC amplitude adjustement set to +6% with +100m speed. Also add additional define to report a change of the same +reg in QCA8337. (different scope it does set 1000m voltage) +Add link_change_notify function to set the proper amplitude adjustement +on PHY_RUNNING state and disable on any other state. + +Fixes: b4df02b562f4 ("net: phy: at803x: add support for qca 8327 A variant internal phy") +Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com> +Signed-off-by: David S. Miller <davem@davemloft.net> +--- + drivers/net/phy/at803x.c | 33 +++++++++++++++++++++++++++++++++ + 1 file changed, 33 insertions(+) + +--- a/drivers/net/phy/at803x.c ++++ b/drivers/net/phy/at803x.c +@@ -87,6 +87,8 @@ + #define AT803X_PSSR_MR_AN_COMPLETE 0x0200 + + #define AT803X_DEBUG_REG_0 0x00 ++#define QCA8327_DEBUG_MANU_CTRL_EN BIT(2) ++#define QCA8337_DEBUG_MANU_CTRL_EN GENMASK(3, 2) + #define AT803X_DEBUG_RX_CLK_DLY_EN BIT(15) + + #define AT803X_DEBUG_REG_5 0x05 +@@ -1306,9 +1308,37 @@ static int qca83xx_config_init(struct ph + break; + } + ++ /* QCA8327 require DAC amplitude adjustment for 100m set to +6%. ++ * Disable on init and enable only with 100m speed following ++ * qca original source code. ++ */ ++ if (phydev->drv->phy_id == QCA8327_A_PHY_ID || ++ phydev->drv->phy_id == QCA8327_B_PHY_ID) ++ at803x_debug_reg_mask(phydev, AT803X_DEBUG_REG_0, ++ QCA8327_DEBUG_MANU_CTRL_EN, 0); ++ + return 0; + } + ++static void qca83xx_link_change_notify(struct phy_device *phydev) ++{ ++ /* QCA8337 doesn't require DAC Amplitude adjustement */ ++ if (phydev->drv->phy_id == QCA8337_PHY_ID) ++ return; ++ ++ /* Set DAC Amplitude adjustment to +6% for 100m on link running */ ++ if (phydev->state == PHY_RUNNING) { ++ if (phydev->speed == SPEED_100) ++ at803x_debug_reg_mask(phydev, AT803X_DEBUG_REG_0, ++ QCA8327_DEBUG_MANU_CTRL_EN, ++ QCA8327_DEBUG_MANU_CTRL_EN); ++ } else { ++ /* Reset DAC Amplitude adjustment */ ++ at803x_debug_reg_mask(phydev, AT803X_DEBUG_REG_0, ++ QCA8327_DEBUG_MANU_CTRL_EN, 0); ++ } ++} ++ + static int qca83xx_resume(struct phy_device *phydev) + { + int ret, val; +@@ -1463,6 +1493,7 @@ static struct phy_driver at803x_driver[] + .phy_id_mask = QCA8K_PHY_ID_MASK, + .name = "Qualcomm Atheros 8337 internal PHY", + /* PHY_GBIT_FEATURES */ ++ .link_change_notify = qca83xx_link_change_notify, + .probe = at803x_probe, + .flags = PHY_IS_INTERNAL, + .config_init = qca83xx_config_init, +@@ -1478,6 +1509,7 @@ static struct phy_driver at803x_driver[] + .phy_id_mask = QCA8K_PHY_ID_MASK, + .name = "Qualcomm Atheros 8327-A internal PHY", + /* PHY_GBIT_FEATURES */ ++ .link_change_notify = qca83xx_link_change_notify, + .probe = at803x_probe, + .flags = PHY_IS_INTERNAL, + .config_init = qca83xx_config_init, +@@ -1493,6 +1525,7 @@ static struct phy_driver at803x_driver[] + .phy_id_mask = QCA8K_PHY_ID_MASK, + .name = "Qualcomm Atheros 8327-B internal PHY", + /* PHY_GBIT_FEATURES */ ++ .link_change_notify = qca83xx_link_change_notify, + .probe = at803x_probe, + .flags = PHY_IS_INTERNAL, + .config_init = qca83xx_config_init, diff --git a/pkgs/patches-linux-5.15/746-v5.16-03-net-phy-at803x-enable-prefer-master-for-83xx-interna.patch b/pkgs/patches-linux-5.15/746-v5.16-03-net-phy-at803x-enable-prefer-master-for-83xx-interna.patch new file mode 100644 index 0000000..9f88059 --- /dev/null +++ b/pkgs/patches-linux-5.15/746-v5.16-03-net-phy-at803x-enable-prefer-master-for-83xx-interna.patch @@ -0,0 +1,27 @@ +From 9d1c29b4028557a496be9c5eb2b4b86063700636 Mon Sep 17 00:00:00 2001 +From: Ansuel Smith <ansuelsmth@gmail.com> +Date: Sun, 10 Oct 2021 00:46:17 +0200 +Subject: net: phy: at803x: enable prefer master for 83xx internal phy + +From original QCA source code the port was set to prefer master as port +type in 1000BASE-T mode. Apply the same settings also here. + +Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com> +Reviewed-by: Andrew Lunn <andrew@lunn.ch> +Signed-off-by: David S. Miller <davem@davemloft.net> +--- + drivers/net/phy/at803x.c | 3 +++ + 1 file changed, 3 insertions(+) + +--- a/drivers/net/phy/at803x.c ++++ b/drivers/net/phy/at803x.c +@@ -1317,6 +1317,9 @@ static int qca83xx_config_init(struct ph + at803x_debug_reg_mask(phydev, AT803X_DEBUG_REG_0, + QCA8327_DEBUG_MANU_CTRL_EN, 0); + ++ /* Following original QCA sourcecode set port to prefer master */ ++ phy_set_bits(phydev, MII_CTRL1000, CTL1000_PREFER_MASTER); ++ + return 0; + } + diff --git a/pkgs/patches-linux-5.15/746-v5.16-04-net-phy-at803x-better-describe-debug-regs.patch b/pkgs/patches-linux-5.15/746-v5.16-04-net-phy-at803x-better-describe-debug-regs.patch new file mode 100644 index 0000000..89e9b3f --- /dev/null +++ b/pkgs/patches-linux-5.15/746-v5.16-04-net-phy-at803x-better-describe-debug-regs.patch @@ -0,0 +1,127 @@ +From 67999555ff42e91de7654488d9a7735bd9e84555 Mon Sep 17 00:00:00 2001 +From: Ansuel Smith <ansuelsmth@gmail.com> +Date: Sun, 10 Oct 2021 00:46:18 +0200 +Subject: net: phy: at803x: better describe debug regs + +Give a name to known debug regs from Documentation instead of using +unknown hex values. + +Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com> +Reviewed-by: Andrew Lunn <andrew@lunn.ch> +Signed-off-by: David S. Miller <davem@davemloft.net> +--- + drivers/net/phy/at803x.c | 30 +++++++++++++++--------------- + 1 file changed, 15 insertions(+), 15 deletions(-) + +--- a/drivers/net/phy/at803x.c ++++ b/drivers/net/phy/at803x.c +@@ -86,12 +86,12 @@ + #define AT803X_PSSR 0x11 /*PHY-Specific Status Register*/ + #define AT803X_PSSR_MR_AN_COMPLETE 0x0200 + +-#define AT803X_DEBUG_REG_0 0x00 ++#define AT803X_DEBUG_ANALOG_TEST_CTRL 0x00 + #define QCA8327_DEBUG_MANU_CTRL_EN BIT(2) + #define QCA8337_DEBUG_MANU_CTRL_EN GENMASK(3, 2) + #define AT803X_DEBUG_RX_CLK_DLY_EN BIT(15) + +-#define AT803X_DEBUG_REG_5 0x05 ++#define AT803X_DEBUG_SYSTEM_CTRL_MODE 0x05 + #define AT803X_DEBUG_TX_CLK_DLY_EN BIT(8) + + #define AT803X_DEBUG_REG_HIB_CTRL 0x0b +@@ -100,7 +100,7 @@ + + #define AT803X_DEBUG_REG_3C 0x3C + +-#define AT803X_DEBUG_REG_3D 0x3D ++#define AT803X_DEBUG_REG_GREEN 0x3D + #define AT803X_DEBUG_GATE_CLK_IN1000 BIT(6) + + #define AT803X_DEBUG_REG_1F 0x1F +@@ -284,25 +284,25 @@ static int at803x_read_page(struct phy_d + + static int at803x_enable_rx_delay(struct phy_device *phydev) + { +- return at803x_debug_reg_mask(phydev, AT803X_DEBUG_REG_0, 0, ++ return at803x_debug_reg_mask(phydev, AT803X_DEBUG_ANALOG_TEST_CTRL, 0, + AT803X_DEBUG_RX_CLK_DLY_EN); + } + + static int at803x_enable_tx_delay(struct phy_device *phydev) + { +- return at803x_debug_reg_mask(phydev, AT803X_DEBUG_REG_5, 0, ++ return at803x_debug_reg_mask(phydev, AT803X_DEBUG_SYSTEM_CTRL_MODE, 0, + AT803X_DEBUG_TX_CLK_DLY_EN); + } + + static int at803x_disable_rx_delay(struct phy_device *phydev) + { +- return at803x_debug_reg_mask(phydev, AT803X_DEBUG_REG_0, ++ return at803x_debug_reg_mask(phydev, AT803X_DEBUG_ANALOG_TEST_CTRL, + AT803X_DEBUG_RX_CLK_DLY_EN, 0); + } + + static int at803x_disable_tx_delay(struct phy_device *phydev) + { +- return at803x_debug_reg_mask(phydev, AT803X_DEBUG_REG_5, ++ return at803x_debug_reg_mask(phydev, AT803X_DEBUG_SYSTEM_CTRL_MODE, + AT803X_DEBUG_TX_CLK_DLY_EN, 0); + } + +@@ -1292,9 +1292,9 @@ static int qca83xx_config_init(struct ph + switch (switch_revision) { + case 1: + /* For 100M waveform */ +- at803x_debug_reg_write(phydev, AT803X_DEBUG_REG_0, 0x02ea); ++ at803x_debug_reg_write(phydev, AT803X_DEBUG_ANALOG_TEST_CTRL, 0x02ea); + /* Turn on Gigabit clock */ +- at803x_debug_reg_write(phydev, AT803X_DEBUG_REG_3D, 0x68a0); ++ at803x_debug_reg_write(phydev, AT803X_DEBUG_REG_GREEN, 0x68a0); + break; + + case 2: +@@ -1302,8 +1302,8 @@ static int qca83xx_config_init(struct ph + fallthrough; + case 4: + phy_write_mmd(phydev, MDIO_MMD_PCS, MDIO_AZ_DEBUG, 0x803f); +- at803x_debug_reg_write(phydev, AT803X_DEBUG_REG_3D, 0x6860); +- at803x_debug_reg_write(phydev, AT803X_DEBUG_REG_5, 0x2c46); ++ at803x_debug_reg_write(phydev, AT803X_DEBUG_REG_GREEN, 0x6860); ++ at803x_debug_reg_write(phydev, AT803X_DEBUG_SYSTEM_CTRL_MODE, 0x2c46); + at803x_debug_reg_write(phydev, AT803X_DEBUG_REG_3C, 0x6000); + break; + } +@@ -1314,7 +1314,7 @@ static int qca83xx_config_init(struct ph + */ + if (phydev->drv->phy_id == QCA8327_A_PHY_ID || + phydev->drv->phy_id == QCA8327_B_PHY_ID) +- at803x_debug_reg_mask(phydev, AT803X_DEBUG_REG_0, ++ at803x_debug_reg_mask(phydev, AT803X_DEBUG_ANALOG_TEST_CTRL, + QCA8327_DEBUG_MANU_CTRL_EN, 0); + + /* Following original QCA sourcecode set port to prefer master */ +@@ -1332,12 +1332,12 @@ static void qca83xx_link_change_notify(s + /* Set DAC Amplitude adjustment to +6% for 100m on link running */ + if (phydev->state == PHY_RUNNING) { + if (phydev->speed == SPEED_100) +- at803x_debug_reg_mask(phydev, AT803X_DEBUG_REG_0, ++ at803x_debug_reg_mask(phydev, AT803X_DEBUG_ANALOG_TEST_CTRL, + QCA8327_DEBUG_MANU_CTRL_EN, + QCA8327_DEBUG_MANU_CTRL_EN); + } else { + /* Reset DAC Amplitude adjustment */ +- at803x_debug_reg_mask(phydev, AT803X_DEBUG_REG_0, ++ at803x_debug_reg_mask(phydev, AT803X_DEBUG_ANALOG_TEST_CTRL, + QCA8327_DEBUG_MANU_CTRL_EN, 0); + } + } +@@ -1384,7 +1384,7 @@ static int qca83xx_suspend(struct phy_de + phy_modify(phydev, MII_BMCR, mask, 0); + } + +- at803x_debug_reg_mask(phydev, AT803X_DEBUG_REG_3D, ++ at803x_debug_reg_mask(phydev, AT803X_DEBUG_REG_GREEN, + AT803X_DEBUG_GATE_CLK_IN1000, 0); + + at803x_debug_reg_mask(phydev, AT803X_DEBUG_REG_HIB_CTRL, diff --git a/pkgs/patches-linux-5.15/747-v5.16-01-dsa-qca8k-add-mac-power-sel-support.patch b/pkgs/patches-linux-5.15/747-v5.16-01-dsa-qca8k-add-mac-power-sel-support.patch new file mode 100644 index 0000000..c8d424d --- /dev/null +++ b/pkgs/patches-linux-5.15/747-v5.16-01-dsa-qca8k-add-mac-power-sel-support.patch @@ -0,0 +1,80 @@ +From d8b6f5bae6d3b648a67b6958cb98e4e97256d652 Mon Sep 17 00:00:00 2001 +From: Ansuel Smith <ansuelsmth@gmail.com> +Date: Thu, 14 Oct 2021 00:39:06 +0200 +Subject: dsa: qca8k: add mac_power_sel support + +Add missing mac power sel support needed for ipq8064/5 SoC that require +1.8v for the internal regulator port instead of the default 1.5v. +If other device needs this, consider adding a dedicated binding to +support this. + +Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com> +Reviewed-by: Vladimir Oltean <olteanv@gmail.com> +Reviewed-by: Florian Fainelli <f.fainelli@gmail.com> +Signed-off-by: David S. Miller <davem@davemloft.net> +--- + drivers/net/dsa/qca8k.c | 31 +++++++++++++++++++++++++++++++ + drivers/net/dsa/qca8k.h | 5 +++++ + 2 files changed, 36 insertions(+) + +--- a/drivers/net/dsa/qca8k.c ++++ b/drivers/net/dsa/qca8k.c +@@ -951,6 +951,33 @@ qca8k_setup_of_rgmii_delay(struct qca8k_ + } + + static int ++qca8k_setup_mac_pwr_sel(struct qca8k_priv *priv) ++{ ++ u32 mask = 0; ++ int ret = 0; ++ ++ /* SoC specific settings for ipq8064. ++ * If more device require this consider adding ++ * a dedicated binding. ++ */ ++ if (of_machine_is_compatible("qcom,ipq8064")) ++ mask |= QCA8K_MAC_PWR_RGMII0_1_8V; ++ ++ /* SoC specific settings for ipq8065 */ ++ if (of_machine_is_compatible("qcom,ipq8065")) ++ mask |= QCA8K_MAC_PWR_RGMII1_1_8V; ++ ++ if (mask) { ++ ret = qca8k_rmw(priv, QCA8K_REG_MAC_PWR_SEL, ++ QCA8K_MAC_PWR_RGMII0_1_8V | ++ QCA8K_MAC_PWR_RGMII1_1_8V, ++ mask); ++ } ++ ++ return ret; ++} ++ ++static int + qca8k_setup(struct dsa_switch *ds) + { + struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv; +@@ -979,6 +1006,10 @@ qca8k_setup(struct dsa_switch *ds) + if (ret) + return ret; + ++ ret = qca8k_setup_mac_pwr_sel(priv); ++ if (ret) ++ return ret; ++ + /* Enable CPU Port */ + ret = qca8k_reg_set(priv, QCA8K_REG_GLOBAL_FW_CTRL0, + QCA8K_GLOBAL_FW_CTRL0_CPU_PORT_EN); +--- a/drivers/net/dsa/qca8k.h ++++ b/drivers/net/dsa/qca8k.h +@@ -100,6 +100,11 @@ + #define QCA8K_SGMII_MODE_CTRL_PHY (1 << 22) + #define QCA8K_SGMII_MODE_CTRL_MAC (2 << 22) + ++/* MAC_PWR_SEL registers */ ++#define QCA8K_REG_MAC_PWR_SEL 0x0e4 ++#define QCA8K_MAC_PWR_RGMII1_1_8V BIT(18) ++#define QCA8K_MAC_PWR_RGMII0_1_8V BIT(19) ++ + /* EEE control registers */ + #define QCA8K_REG_EEE_CTRL 0x100 + #define QCA8K_REG_EEE_CTRL_LPI_EN(_i) ((_i + 1) * 2) diff --git a/pkgs/patches-linux-5.15/747-v5.16-02-dt-bindings-net-dsa-qca8k-Add-SGMII-clock-phase-prop.patch b/pkgs/patches-linux-5.15/747-v5.16-02-dt-bindings-net-dsa-qca8k-Add-SGMII-clock-phase-prop.patch new file mode 100644 index 0000000..bd768ec --- /dev/null +++ b/pkgs/patches-linux-5.15/747-v5.16-02-dt-bindings-net-dsa-qca8k-Add-SGMII-clock-phase-prop.patch @@ -0,0 +1,30 @@ +From fdbf35df9c091db9c46e57e9938e3f7a4f603a7c Mon Sep 17 00:00:00 2001 +From: Ansuel Smith <ansuelsmth@gmail.com> +Date: Thu, 14 Oct 2021 00:39:07 +0200 +Subject: dt-bindings: net: dsa: qca8k: Add SGMII clock phase properties + +Add names and descriptions of additional PORT0_PAD_CTRL properties. +qca,sgmii-(rx|tx)clk-falling-edge are for setting the respective clock +phase to failling edge. + +Co-developed-by: Matthew Hagan <mnhagan88@gmail.com> +Signed-off-by: Matthew Hagan <mnhagan88@gmail.com> +Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com> +Signed-off-by: David S. Miller <davem@davemloft.net> +--- + Documentation/devicetree/bindings/net/dsa/qca8k.txt | 4 ++++ + 1 file changed, 4 insertions(+) + +--- a/Documentation/devicetree/bindings/net/dsa/qca8k.txt ++++ b/Documentation/devicetree/bindings/net/dsa/qca8k.txt +@@ -37,6 +37,10 @@ A CPU port node has the following option + managed entity. See + Documentation/devicetree/bindings/net/fixed-link.txt + for details. ++- qca,sgmii-rxclk-falling-edge: Set the receive clock phase to falling edge. ++ Mostly used in qca8327 with CPU port 0 set to ++ sgmii. ++- qca,sgmii-txclk-falling-edge: Set the transmit clock phase to falling edge. + + For QCA8K the 'fixed-link' sub-node supports only the following properties: + diff --git a/pkgs/patches-linux-5.15/747-v5.16-03-net-dsa-qca8k-add-support-for-sgmii-falling-edge.patch b/pkgs/patches-linux-5.15/747-v5.16-03-net-dsa-qca8k-add-support-for-sgmii-falling-edge.patch new file mode 100644 index 0000000..e464452 --- /dev/null +++ b/pkgs/patches-linux-5.15/747-v5.16-03-net-dsa-qca8k-add-support-for-sgmii-falling-edge.patch @@ -0,0 +1,127 @@ +From 6c43809bf1bee76c434e365a26546a92a5fbec14 Mon Sep 17 00:00:00 2001 +From: Ansuel Smith <ansuelsmth@gmail.com> +Date: Thu, 14 Oct 2021 00:39:08 +0200 +Subject: net: dsa: qca8k: add support for sgmii falling edge + +Add support for this in the qca8k driver. Also add support for SGMII +rx/tx clock falling edge. This is only present for pad0, pad5 and +pad6 have these bit reserved from Documentation. Add a comment that this +is hardcoded to PAD0 as qca8327/28/34/37 have an unique sgmii line and +setting falling in port0 applies to both configuration with sgmii used +for port0 or port6. + +Co-developed-by: Matthew Hagan <mnhagan88@gmail.com> +Signed-off-by: Matthew Hagan <mnhagan88@gmail.com> +Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com> +Signed-off-by: David S. Miller <davem@davemloft.net> +--- + drivers/net/dsa/qca8k.c | 63 +++++++++++++++++++++++++++++++++++++++++++++++++ + drivers/net/dsa/qca8k.h | 4 ++++ + 2 files changed, 67 insertions(+) + +--- a/drivers/net/dsa/qca8k.c ++++ b/drivers/net/dsa/qca8k.c +@@ -978,6 +978,42 @@ qca8k_setup_mac_pwr_sel(struct qca8k_pri + } + + static int ++qca8k_parse_port_config(struct qca8k_priv *priv) ++{ ++ struct device_node *port_dn; ++ phy_interface_t mode; ++ struct dsa_port *dp; ++ int port, ret; ++ ++ /* We have 2 CPU port. Check them */ ++ for (port = 0; port < QCA8K_NUM_PORTS; port++) { ++ /* Skip every other port */ ++ if (port != 0 && port != 6) ++ continue; ++ ++ dp = dsa_to_port(priv->ds, port); ++ port_dn = dp->dn; ++ ++ if (!of_device_is_available(port_dn)) ++ continue; ++ ++ ret = of_get_phy_mode(port_dn, &mode); ++ if (ret) ++ continue; ++ ++ if (mode == PHY_INTERFACE_MODE_SGMII) { ++ if (of_property_read_bool(port_dn, "qca,sgmii-txclk-falling-edge")) ++ priv->sgmii_tx_clk_falling_edge = true; ++ ++ if (of_property_read_bool(port_dn, "qca,sgmii-rxclk-falling-edge")) ++ priv->sgmii_rx_clk_falling_edge = true; ++ } ++ } ++ ++ return 0; ++} ++ ++static int + qca8k_setup(struct dsa_switch *ds) + { + struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv; +@@ -990,6 +1026,11 @@ qca8k_setup(struct dsa_switch *ds) + return -EINVAL; + } + ++ /* Parse CPU port config to be later used in phy_link mac_config */ ++ ret = qca8k_parse_port_config(priv); ++ if (ret) ++ return ret; ++ + mutex_init(&priv->reg_mutex); + + /* Start by setting up the register mapping */ +@@ -1274,6 +1315,28 @@ qca8k_phylink_mac_config(struct dsa_swit + } + + qca8k_write(priv, QCA8K_REG_SGMII_CTRL, val); ++ ++ /* For qca8327/qca8328/qca8334/qca8338 sgmii is unique and ++ * falling edge is set writing in the PORT0 PAD reg ++ */ ++ if (priv->switch_id == QCA8K_ID_QCA8327 || ++ priv->switch_id == QCA8K_ID_QCA8337) ++ reg = QCA8K_REG_PORT0_PAD_CTRL; ++ ++ val = 0; ++ ++ /* SGMII Clock phase configuration */ ++ if (priv->sgmii_rx_clk_falling_edge) ++ val |= QCA8K_PORT0_PAD_SGMII_RXCLK_FALLING_EDGE; ++ ++ if (priv->sgmii_tx_clk_falling_edge) ++ val |= QCA8K_PORT0_PAD_SGMII_TXCLK_FALLING_EDGE; ++ ++ if (val) ++ ret = qca8k_rmw(priv, reg, ++ QCA8K_PORT0_PAD_SGMII_RXCLK_FALLING_EDGE | ++ QCA8K_PORT0_PAD_SGMII_TXCLK_FALLING_EDGE, ++ val); + break; + default: + dev_err(ds->dev, "xMII mode %s not supported for port %d\n", +--- a/drivers/net/dsa/qca8k.h ++++ b/drivers/net/dsa/qca8k.h +@@ -35,6 +35,8 @@ + #define QCA8K_MASK_CTRL_DEVICE_ID_MASK GENMASK(15, 8) + #define QCA8K_MASK_CTRL_DEVICE_ID(x) ((x) >> 8) + #define QCA8K_REG_PORT0_PAD_CTRL 0x004 ++#define QCA8K_PORT0_PAD_SGMII_RXCLK_FALLING_EDGE BIT(19) ++#define QCA8K_PORT0_PAD_SGMII_TXCLK_FALLING_EDGE BIT(18) + #define QCA8K_REG_PORT5_PAD_CTRL 0x008 + #define QCA8K_REG_PORT6_PAD_CTRL 0x00c + #define QCA8K_PORT_PAD_RGMII_EN BIT(26) +@@ -260,6 +262,8 @@ struct qca8k_priv { + u8 switch_revision; + u8 rgmii_tx_delay; + u8 rgmii_rx_delay; ++ bool sgmii_rx_clk_falling_edge; ++ bool sgmii_tx_clk_falling_edge; + bool legacy_phy_port_mapping; + struct regmap *regmap; + struct mii_bus *bus; diff --git a/pkgs/patches-linux-5.15/747-v5.16-04-dt-bindings-net-dsa-qca8k-Document-support-for-CPU-p.patch b/pkgs/patches-linux-5.15/747-v5.16-04-dt-bindings-net-dsa-qca8k-Document-support-for-CPU-p.patch new file mode 100644 index 0000000..606ac0a --- /dev/null +++ b/pkgs/patches-linux-5.15/747-v5.16-04-dt-bindings-net-dsa-qca8k-Document-support-for-CPU-p.patch @@ -0,0 +1,29 @@ +From 731d613338ec6de482053ffa3f71be2325b0f8eb Mon Sep 17 00:00:00 2001 +From: Ansuel Smith <ansuelsmth@gmail.com> +Date: Thu, 14 Oct 2021 00:39:09 +0200 +Subject: dt-bindings: net: dsa: qca8k: Document support for CPU port 6 + +The switch now support CPU port to be set 6 instead of be hardcoded to +0. Document support for it and describe logic selection. + +Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com> +Signed-off-by: David S. Miller <davem@davemloft.net> +--- + Documentation/devicetree/bindings/net/dsa/qca8k.txt | 6 +++++- + 1 file changed, 5 insertions(+), 1 deletion(-) + +--- a/Documentation/devicetree/bindings/net/dsa/qca8k.txt ++++ b/Documentation/devicetree/bindings/net/dsa/qca8k.txt +@@ -29,7 +29,11 @@ the mdio MASTER is used as communication + Don't use mixed external and internal mdio-bus configurations, as this is + not supported by the hardware. + +-The CPU port of this switch is always port 0. ++This switch support 2 CPU port. Normally and advised configuration is with ++CPU port set to port 0. It is also possible to set the CPU port to port 6 ++if the device requires it. The driver will configure the switch to the defined ++port. With both CPU port declared the first CPU port is selected as primary ++and the secondary CPU ignored. + + A CPU port node has the following optional node: + diff --git a/pkgs/patches-linux-5.15/747-v5.16-05-net-dsa-qca8k-add-support-for-cpu-port-6.patch b/pkgs/patches-linux-5.15/747-v5.16-05-net-dsa-qca8k-add-support-for-cpu-port-6.patch new file mode 100644 index 0000000..320db8f --- /dev/null +++ b/pkgs/patches-linux-5.15/747-v5.16-05-net-dsa-qca8k-add-support-for-cpu-port-6.patch @@ -0,0 +1,153 @@ +From 3fcf734aa482487df83cf8f18608438fcf59127f Mon Sep 17 00:00:00 2001 +From: Ansuel Smith <ansuelsmth@gmail.com> +Date: Thu, 14 Oct 2021 00:39:10 +0200 +Subject: net: dsa: qca8k: add support for cpu port 6 + +Currently CPU port is always hardcoded to port 0. This switch have 2 CPU +ports. The original intention of this driver seems to be use the +mac06_exchange bit to swap MAC0 with MAC6 in the strange configuration +where device have connected only the CPU port 6. To skip the +introduction of a new binding, rework the driver to address the +secondary CPU port as primary and drop any reference of hardcoded port. +With configuration of mac06 exchange, just skip the definition of port0 +and define the CPU port as a secondary. The driver will autoconfigure +the switch to use that as the primary CPU port. + +Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com> +Signed-off-by: David S. Miller <davem@davemloft.net> +--- + drivers/net/dsa/qca8k.c | 51 ++++++++++++++++++++++++++++++++++--------------- + drivers/net/dsa/qca8k.h | 2 -- + 2 files changed, 36 insertions(+), 17 deletions(-) + +--- a/drivers/net/dsa/qca8k.c ++++ b/drivers/net/dsa/qca8k.c +@@ -977,6 +977,22 @@ qca8k_setup_mac_pwr_sel(struct qca8k_pri + return ret; + } + ++static int qca8k_find_cpu_port(struct dsa_switch *ds) ++{ ++ struct qca8k_priv *priv = ds->priv; ++ ++ /* 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; ++ ++ return -EINVAL; ++} ++ + static int + qca8k_parse_port_config(struct qca8k_priv *priv) + { +@@ -1017,13 +1033,13 @@ static int + qca8k_setup(struct dsa_switch *ds) + { + struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv; +- int ret, i; ++ int cpu_port, ret, i; + u32 mask; + +- /* Make sure that port 0 is the cpu port */ +- if (!dsa_is_cpu_port(ds, 0)) { +- dev_err(priv->dev, "port 0 is not the CPU port"); +- return -EINVAL; ++ cpu_port = qca8k_find_cpu_port(ds); ++ if (cpu_port < 0) { ++ dev_err(priv->dev, "No cpu port configured in both cpu port0 and port6"); ++ return cpu_port; + } + + /* Parse CPU port config to be later used in phy_link mac_config */ +@@ -1065,7 +1081,7 @@ qca8k_setup(struct dsa_switch *ds) + dev_warn(priv->dev, "mib init failed"); + + /* Enable QCA header mode on the cpu port */ +- ret = qca8k_write(priv, QCA8K_REG_PORT_HDR_CTRL(QCA8K_CPU_PORT), ++ ret = qca8k_write(priv, QCA8K_REG_PORT_HDR_CTRL(cpu_port), + QCA8K_PORT_HDR_CTRL_ALL << QCA8K_PORT_HDR_CTRL_TX_S | + QCA8K_PORT_HDR_CTRL_ALL << QCA8K_PORT_HDR_CTRL_RX_S); + if (ret) { +@@ -1087,10 +1103,10 @@ qca8k_setup(struct dsa_switch *ds) + + /* Forward all unknown frames to CPU port for Linux processing */ + ret = qca8k_write(priv, QCA8K_REG_GLOBAL_FW_CTRL1, +- BIT(0) << QCA8K_GLOBAL_FW_CTRL1_IGMP_DP_S | +- BIT(0) << QCA8K_GLOBAL_FW_CTRL1_BC_DP_S | +- BIT(0) << QCA8K_GLOBAL_FW_CTRL1_MC_DP_S | +- BIT(0) << QCA8K_GLOBAL_FW_CTRL1_UC_DP_S); ++ BIT(cpu_port) << QCA8K_GLOBAL_FW_CTRL1_IGMP_DP_S | ++ BIT(cpu_port) << QCA8K_GLOBAL_FW_CTRL1_BC_DP_S | ++ BIT(cpu_port) << QCA8K_GLOBAL_FW_CTRL1_MC_DP_S | ++ BIT(cpu_port) << QCA8K_GLOBAL_FW_CTRL1_UC_DP_S); + if (ret) + return ret; + +@@ -1098,7 +1114,7 @@ qca8k_setup(struct dsa_switch *ds) + for (i = 0; i < QCA8K_NUM_PORTS; i++) { + /* CPU port gets connected to all user ports of the switch */ + if (dsa_is_cpu_port(ds, i)) { +- ret = qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(QCA8K_CPU_PORT), ++ ret = qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(cpu_port), + QCA8K_PORT_LOOKUP_MEMBER, dsa_user_ports(ds)); + if (ret) + return ret; +@@ -1110,7 +1126,7 @@ qca8k_setup(struct dsa_switch *ds) + + ret = qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(i), + QCA8K_PORT_LOOKUP_MEMBER, +- BIT(QCA8K_CPU_PORT)); ++ BIT(cpu_port)); + if (ret) + return ret; + +@@ -1616,9 +1632,12 @@ static int + qca8k_port_bridge_join(struct dsa_switch *ds, int port, struct net_device *br) + { + struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv; +- int port_mask = BIT(QCA8K_CPU_PORT); ++ int port_mask, cpu_port; + int i, ret; + ++ cpu_port = dsa_to_port(ds, port)->cpu_dp->index; ++ port_mask = BIT(cpu_port); ++ + for (i = 1; i < QCA8K_NUM_PORTS; i++) { + if (dsa_to_port(ds, i)->bridge_dev != br) + continue; +@@ -1645,7 +1664,9 @@ static void + qca8k_port_bridge_leave(struct dsa_switch *ds, int port, struct net_device *br) + { + struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv; +- int i; ++ int cpu_port, i; ++ ++ cpu_port = dsa_to_port(ds, port)->cpu_dp->index; + + for (i = 1; i < QCA8K_NUM_PORTS; i++) { + if (dsa_to_port(ds, i)->bridge_dev != br) +@@ -1662,7 +1683,7 @@ qca8k_port_bridge_leave(struct dsa_switc + * this port + */ + qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(port), +- QCA8K_PORT_LOOKUP_MEMBER, BIT(QCA8K_CPU_PORT)); ++ QCA8K_PORT_LOOKUP_MEMBER, BIT(cpu_port)); + } + + static int +--- a/drivers/net/dsa/qca8k.h ++++ b/drivers/net/dsa/qca8k.h +@@ -24,8 +24,6 @@ + + #define QCA8K_NUM_FDB_RECORDS 2048 + +-#define QCA8K_CPU_PORT 0 +- + #define QCA8K_PORT_VID_DEF 1 + + /* Global control registers */ diff --git a/pkgs/patches-linux-5.15/747-v5.16-06-net-dsa-qca8k-rework-rgmii-delay-logic-and-scan-for-.patch b/pkgs/patches-linux-5.15/747-v5.16-06-net-dsa-qca8k-rework-rgmii-delay-logic-and-scan-for-.patch new file mode 100644 index 0000000..de20176 --- /dev/null +++ b/pkgs/patches-linux-5.15/747-v5.16-06-net-dsa-qca8k-rework-rgmii-delay-logic-and-scan-for-.patch @@ -0,0 +1,295 @@ +From 5654ec78dd7e64b1e04777b24007344329e6a63b Mon Sep 17 00:00:00 2001 +From: Ansuel Smith <ansuelsmth@gmail.com> +Date: Thu, 14 Oct 2021 00:39:11 +0200 +Subject: net: dsa: qca8k: rework rgmii delay logic and scan for cpu port 6 + +Future proof commit. This switch have 2 CPU ports and one valid +configuration is first CPU port set to sgmii and second CPU port set to +rgmii-id. The current implementation detects delay only for CPU port +zero set to rgmii and doesn't count any delay set in a secondary CPU +port. Drop the current delay scan function and move it to the sgmii +parser function to generalize and implicitly add support for secondary +CPU port set to rgmii-id. Introduce new logic where delay is enabled +also with internal delay binding declared and rgmii set as PHY mode. + +Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com> +Signed-off-by: David S. Miller <davem@davemloft.net> +--- + drivers/net/dsa/qca8k.c | 165 ++++++++++++++++++++++++------------------------ + drivers/net/dsa/qca8k.h | 10 ++- + 2 files changed, 89 insertions(+), 86 deletions(-) + +--- a/drivers/net/dsa/qca8k.c ++++ b/drivers/net/dsa/qca8k.c +@@ -889,68 +889,6 @@ qca8k_setup_mdio_bus(struct qca8k_priv * + } + + static int +-qca8k_setup_of_rgmii_delay(struct qca8k_priv *priv) +-{ +- struct device_node *port_dn; +- phy_interface_t mode; +- struct dsa_port *dp; +- u32 val; +- +- /* CPU port is already checked */ +- dp = dsa_to_port(priv->ds, 0); +- +- port_dn = dp->dn; +- +- /* Check if port 0 is set to the correct type */ +- of_get_phy_mode(port_dn, &mode); +- if (mode != PHY_INTERFACE_MODE_RGMII_ID && +- mode != PHY_INTERFACE_MODE_RGMII_RXID && +- mode != PHY_INTERFACE_MODE_RGMII_TXID) { +- return 0; +- } +- +- switch (mode) { +- case PHY_INTERFACE_MODE_RGMII_ID: +- case PHY_INTERFACE_MODE_RGMII_RXID: +- if (of_property_read_u32(port_dn, "rx-internal-delay-ps", &val)) +- val = 2; +- else +- /* Switch regs accept value in ns, convert ps to ns */ +- val = val / 1000; +- +- if (val > QCA8K_MAX_DELAY) { +- dev_err(priv->dev, "rgmii rx delay is limited to a max value of 3ns, setting to the max value"); +- val = 3; +- } +- +- priv->rgmii_rx_delay = val; +- /* Stop here if we need to check only for rx delay */ +- if (mode != PHY_INTERFACE_MODE_RGMII_ID) +- break; +- +- fallthrough; +- case PHY_INTERFACE_MODE_RGMII_TXID: +- if (of_property_read_u32(port_dn, "tx-internal-delay-ps", &val)) +- val = 1; +- else +- /* Switch regs accept value in ns, convert ps to ns */ +- val = val / 1000; +- +- if (val > QCA8K_MAX_DELAY) { +- dev_err(priv->dev, "rgmii tx delay is limited to a max value of 3ns, setting to the max value"); +- val = 3; +- } +- +- priv->rgmii_tx_delay = val; +- break; +- default: +- return 0; +- } +- +- return 0; +-} +- +-static int + qca8k_setup_mac_pwr_sel(struct qca8k_priv *priv) + { + u32 mask = 0; +@@ -996,19 +934,21 @@ static int qca8k_find_cpu_port(struct ds + static int + qca8k_parse_port_config(struct qca8k_priv *priv) + { ++ int port, cpu_port_index = 0, ret; + struct device_node *port_dn; + phy_interface_t mode; + struct dsa_port *dp; +- int port, ret; ++ u32 delay; + + /* We have 2 CPU port. Check them */ +- for (port = 0; port < QCA8K_NUM_PORTS; port++) { ++ for (port = 0; port < QCA8K_NUM_PORTS && cpu_port_index < QCA8K_NUM_CPU_PORTS; port++) { + /* Skip every other port */ + if (port != 0 && port != 6) + continue; + + dp = dsa_to_port(priv->ds, port); + port_dn = dp->dn; ++ cpu_port_index++; + + if (!of_device_is_available(port_dn)) + continue; +@@ -1017,12 +957,54 @@ qca8k_parse_port_config(struct qca8k_pri + if (ret) + continue; + +- if (mode == PHY_INTERFACE_MODE_SGMII) { ++ switch (mode) { ++ case PHY_INTERFACE_MODE_RGMII: ++ case PHY_INTERFACE_MODE_RGMII_ID: ++ case PHY_INTERFACE_MODE_RGMII_TXID: ++ case PHY_INTERFACE_MODE_RGMII_RXID: ++ delay = 0; ++ ++ if (!of_property_read_u32(port_dn, "tx-internal-delay-ps", &delay)) ++ /* Switch regs accept value in ns, convert ps to ns */ ++ delay = delay / 1000; ++ else if (mode == PHY_INTERFACE_MODE_RGMII_ID || ++ mode == PHY_INTERFACE_MODE_RGMII_TXID) ++ delay = 1; ++ ++ if (delay > QCA8K_MAX_DELAY) { ++ dev_err(priv->dev, "rgmii tx delay is limited to a max value of 3ns, setting to the max value"); ++ delay = 3; ++ } ++ ++ priv->rgmii_tx_delay[cpu_port_index] = delay; ++ ++ delay = 0; ++ ++ if (!of_property_read_u32(port_dn, "rx-internal-delay-ps", &delay)) ++ /* Switch regs accept value in ns, convert ps to ns */ ++ delay = delay / 1000; ++ else if (mode == PHY_INTERFACE_MODE_RGMII_ID || ++ mode == PHY_INTERFACE_MODE_RGMII_RXID) ++ delay = 2; ++ ++ if (delay > QCA8K_MAX_DELAY) { ++ dev_err(priv->dev, "rgmii rx delay is limited to a max value of 3ns, setting to the max value"); ++ delay = 3; ++ } ++ ++ priv->rgmii_rx_delay[cpu_port_index] = delay; ++ ++ break; ++ case PHY_INTERFACE_MODE_SGMII: + if (of_property_read_bool(port_dn, "qca,sgmii-txclk-falling-edge")) + priv->sgmii_tx_clk_falling_edge = true; + + if (of_property_read_bool(port_dn, "qca,sgmii-rxclk-falling-edge")) + priv->sgmii_rx_clk_falling_edge = true; ++ ++ break; ++ default: ++ continue; + } + } + +@@ -1059,10 +1041,6 @@ qca8k_setup(struct dsa_switch *ds) + if (ret) + return ret; + +- ret = qca8k_setup_of_rgmii_delay(priv); +- if (ret) +- return ret; +- + ret = qca8k_setup_mac_pwr_sel(priv); + if (ret) + return ret; +@@ -1229,8 +1207,8 @@ qca8k_phylink_mac_config(struct dsa_swit + const struct phylink_link_state *state) + { + struct qca8k_priv *priv = ds->priv; +- u32 reg, val; +- int ret; ++ int cpu_port_index, ret; ++ u32 reg, val, delay; + + switch (port) { + case 0: /* 1st CPU port */ +@@ -1242,6 +1220,7 @@ qca8k_phylink_mac_config(struct dsa_swit + return; + + reg = QCA8K_REG_PORT0_PAD_CTRL; ++ cpu_port_index = QCA8K_CPU_PORT0; + break; + case 1: + case 2: +@@ -1260,6 +1239,7 @@ qca8k_phylink_mac_config(struct dsa_swit + return; + + reg = QCA8K_REG_PORT6_PAD_CTRL; ++ cpu_port_index = QCA8K_CPU_PORT6; + break; + default: + dev_err(ds->dev, "%s: unsupported port: %i\n", __func__, port); +@@ -1274,23 +1254,40 @@ qca8k_phylink_mac_config(struct dsa_swit + + switch (state->interface) { + case PHY_INTERFACE_MODE_RGMII: +- /* RGMII mode means no delay so don't enable the delay */ +- qca8k_write(priv, reg, QCA8K_PORT_PAD_RGMII_EN); +- break; + case PHY_INTERFACE_MODE_RGMII_ID: + case PHY_INTERFACE_MODE_RGMII_TXID: + case PHY_INTERFACE_MODE_RGMII_RXID: +- /* RGMII_ID needs internal delay. This is enabled through +- * PORT5_PAD_CTRL for all ports, rather than individual port +- * registers ++ val = QCA8K_PORT_PAD_RGMII_EN; ++ ++ /* Delay can be declared in 3 different way. ++ * Mode to rgmii and internal-delay standard binding defined ++ * rgmii-id or rgmii-tx/rx phy mode set. ++ * The parse logic set a delay different than 0 only when one ++ * of the 3 different way is used. In all other case delay is ++ * not enabled. With ID or TX/RXID delay is enabled and set ++ * to the default and recommended value. ++ */ ++ if (priv->rgmii_tx_delay[cpu_port_index]) { ++ delay = priv->rgmii_tx_delay[cpu_port_index]; ++ ++ val |= QCA8K_PORT_PAD_RGMII_TX_DELAY(delay) | ++ QCA8K_PORT_PAD_RGMII_TX_DELAY_EN; ++ } ++ ++ if (priv->rgmii_rx_delay[cpu_port_index]) { ++ delay = priv->rgmii_rx_delay[cpu_port_index]; ++ ++ val |= QCA8K_PORT_PAD_RGMII_RX_DELAY(delay) | ++ QCA8K_PORT_PAD_RGMII_RX_DELAY_EN; ++ } ++ ++ /* Set RGMII delay based on the selected values */ ++ qca8k_write(priv, reg, val); ++ ++ /* QCA8337 requires to set rgmii rx delay for all ports. ++ * This is enabled through PORT5_PAD_CTRL for all ports, ++ * rather than individual port registers. + */ +- qca8k_write(priv, reg, +- QCA8K_PORT_PAD_RGMII_EN | +- QCA8K_PORT_PAD_RGMII_TX_DELAY(priv->rgmii_tx_delay) | +- QCA8K_PORT_PAD_RGMII_RX_DELAY(priv->rgmii_rx_delay) | +- QCA8K_PORT_PAD_RGMII_TX_DELAY_EN | +- QCA8K_PORT_PAD_RGMII_RX_DELAY_EN); +- /* QCA8337 requires to set rgmii rx delay */ + if (priv->switch_id == QCA8K_ID_QCA8337) + qca8k_write(priv, QCA8K_REG_PORT5_PAD_CTRL, + QCA8K_PORT_PAD_RGMII_RX_DELAY_EN); +--- a/drivers/net/dsa/qca8k.h ++++ b/drivers/net/dsa/qca8k.h +@@ -13,6 +13,7 @@ + #include <linux/gpio.h> + + #define QCA8K_NUM_PORTS 7 ++#define QCA8K_NUM_CPU_PORTS 2 + #define QCA8K_MAX_MTU 9000 + + #define PHY_ID_QCA8327 0x004dd034 +@@ -255,13 +256,18 @@ struct qca8k_match_data { + u8 id; + }; + ++enum { ++ QCA8K_CPU_PORT0, ++ QCA8K_CPU_PORT6, ++}; ++ + struct qca8k_priv { + u8 switch_id; + u8 switch_revision; +- u8 rgmii_tx_delay; +- u8 rgmii_rx_delay; + bool sgmii_rx_clk_falling_edge; + bool sgmii_tx_clk_falling_edge; ++ u8 rgmii_rx_delay[QCA8K_NUM_CPU_PORTS]; /* 0: CPU port0, 1: CPU port6 */ ++ u8 rgmii_tx_delay[QCA8K_NUM_CPU_PORTS]; /* 0: CPU port0, 1: CPU port6 */ + bool legacy_phy_port_mapping; + struct regmap *regmap; + struct mii_bus *bus; diff --git a/pkgs/patches-linux-5.15/747-v5.16-07-dt-bindings-net-dsa-qca8k-Document-qca-sgmii-enable-.patch b/pkgs/patches-linux-5.15/747-v5.16-07-dt-bindings-net-dsa-qca8k-Document-qca-sgmii-enable-.patch new file mode 100644 index 0000000..8abd264 --- /dev/null +++ b/pkgs/patches-linux-5.15/747-v5.16-07-dt-bindings-net-dsa-qca8k-Document-qca-sgmii-enable-.patch @@ -0,0 +1,33 @@ +From 13ad5ccc093ff448b99ac7e138e91e78796adb48 Mon Sep 17 00:00:00 2001 +From: Ansuel Smith <ansuelsmth@gmail.com> +Date: Thu, 14 Oct 2021 00:39:12 +0200 +Subject: dt-bindings: net: dsa: qca8k: Document qca,sgmii-enable-pll + +Document qca,sgmii-enable-pll binding used in the CPU nodes to +enable SGMII PLL on MAC config. + +Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com> +Signed-off-by: David S. Miller <davem@davemloft.net> +--- + Documentation/devicetree/bindings/net/dsa/qca8k.txt | 10 ++++++++++ + 1 file changed, 10 insertions(+) + +--- a/Documentation/devicetree/bindings/net/dsa/qca8k.txt ++++ b/Documentation/devicetree/bindings/net/dsa/qca8k.txt +@@ -45,6 +45,16 @@ A CPU port node has the following option + Mostly used in qca8327 with CPU port 0 set to + sgmii. + - qca,sgmii-txclk-falling-edge: Set the transmit clock phase to falling edge. ++- qca,sgmii-enable-pll : For SGMII CPU port, explicitly enable PLL, TX and RX ++ chain along with Signal Detection. ++ This should NOT be enabled for qca8327. If enabled with ++ qca8327 the sgmii port won't correctly init and an err ++ is printed. ++ This can be required for qca8337 switch with revision 2. ++ A warning is displayed when used with revision greater ++ 2. ++ With CPU port set to sgmii and qca8337 it is advised ++ to set this unless a communication problem is observed. + + For QCA8K the 'fixed-link' sub-node supports only the following properties: + diff --git a/pkgs/patches-linux-5.15/747-v5.16-08-net-dsa-qca8k-add-explicit-SGMII-PLL-enable.patch b/pkgs/patches-linux-5.15/747-v5.16-08-net-dsa-qca8k-add-explicit-SGMII-PLL-enable.patch new file mode 100644 index 0000000..2b5a84a --- /dev/null +++ b/pkgs/patches-linux-5.15/747-v5.16-08-net-dsa-qca8k-add-explicit-SGMII-PLL-enable.patch @@ -0,0 +1,65 @@ +From bbc4799e8bb6c397e3b3fec13de68e179f5db9ff Mon Sep 17 00:00:00 2001 +From: Ansuel Smith <ansuelsmth@gmail.com> +Date: Thu, 14 Oct 2021 00:39:13 +0200 +Subject: net: dsa: qca8k: add explicit SGMII PLL enable + +Support enabling PLL on the SGMII CPU port. Some device require this +special configuration or no traffic is transmitted and the switch +doesn't work at all. A dedicated binding is added to the CPU node +port to apply the correct reg on mac config. +Fail to correctly configure sgmii with qca8327 switch and warn if pll is +used on qca8337 with a revision greater than 1. + +Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com> +Reviewed-by: Florian Fainelli <f.fainelli@gmail.com> +Signed-off-by: David S. Miller <davem@davemloft.net> +--- + drivers/net/dsa/qca8k.c | 19 +++++++++++++++++-- + drivers/net/dsa/qca8k.h | 1 + + 2 files changed, 18 insertions(+), 2 deletions(-) + +--- a/drivers/net/dsa/qca8k.c ++++ b/drivers/net/dsa/qca8k.c +@@ -1002,6 +1002,18 @@ qca8k_parse_port_config(struct qca8k_pri + if (of_property_read_bool(port_dn, "qca,sgmii-rxclk-falling-edge")) + priv->sgmii_rx_clk_falling_edge = true; + ++ if (of_property_read_bool(port_dn, "qca,sgmii-enable-pll")) { ++ priv->sgmii_enable_pll = true; ++ ++ if (priv->switch_id == QCA8K_ID_QCA8327) { ++ dev_err(priv->dev, "SGMII PLL should NOT be enabled for qca8327. Aborting enabling"); ++ priv->sgmii_enable_pll = false; ++ } ++ ++ if (priv->switch_revision < 2) ++ dev_warn(priv->dev, "SGMII PLL should NOT be enabled for qca8337 with revision 2 or more."); ++ } ++ + break; + default: + continue; +@@ -1312,8 +1324,11 @@ qca8k_phylink_mac_config(struct dsa_swit + if (ret) + return; + +- val |= QCA8K_SGMII_EN_PLL | QCA8K_SGMII_EN_RX | +- QCA8K_SGMII_EN_TX | QCA8K_SGMII_EN_SD; ++ val |= QCA8K_SGMII_EN_SD; ++ ++ if (priv->sgmii_enable_pll) ++ val |= QCA8K_SGMII_EN_PLL | QCA8K_SGMII_EN_RX | ++ QCA8K_SGMII_EN_TX; + + if (dsa_is_cpu_port(ds, port)) { + /* CPU port, we're talking to the CPU MAC, be a PHY */ +--- a/drivers/net/dsa/qca8k.h ++++ b/drivers/net/dsa/qca8k.h +@@ -266,6 +266,7 @@ struct qca8k_priv { + u8 switch_revision; + bool sgmii_rx_clk_falling_edge; + bool sgmii_tx_clk_falling_edge; ++ bool sgmii_enable_pll; + u8 rgmii_rx_delay[QCA8K_NUM_CPU_PORTS]; /* 0: CPU port0, 1: CPU port6 */ + u8 rgmii_tx_delay[QCA8K_NUM_CPU_PORTS]; /* 0: CPU port0, 1: CPU port6 */ + bool legacy_phy_port_mapping; diff --git a/pkgs/patches-linux-5.15/747-v5.16-09-dt-bindings-net-dsa-qca8k-Document-qca-led-open-drai.patch b/pkgs/patches-linux-5.15/747-v5.16-09-dt-bindings-net-dsa-qca8k-Document-qca-led-open-drai.patch new file mode 100644 index 0000000..38dc954 --- /dev/null +++ b/pkgs/patches-linux-5.15/747-v5.16-09-dt-bindings-net-dsa-qca8k-Document-qca-led-open-drai.patch @@ -0,0 +1,37 @@ +From 924087c5c3d41553700b0eb83ca2a53b91643dca Mon Sep 17 00:00:00 2001 +From: Ansuel Smith <ansuelsmth@gmail.com> +Date: Thu, 14 Oct 2021 00:39:14 +0200 +Subject: dt-bindings: net: dsa: qca8k: Document qca,led-open-drain binding + +Document new binding qca,ignore-power-on-sel used to ignore +power on strapping and use sw regs instead. +Document qca,led-open.drain to set led to open drain mode, the +qca,ignore-power-on-sel is mandatory with this enabled or an error will +be reported. + +Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com> +Signed-off-by: David S. Miller <davem@davemloft.net> +--- + Documentation/devicetree/bindings/net/dsa/qca8k.txt | 11 +++++++++++ + 1 file changed, 11 insertions(+) + +--- a/Documentation/devicetree/bindings/net/dsa/qca8k.txt ++++ b/Documentation/devicetree/bindings/net/dsa/qca8k.txt +@@ -13,6 +13,17 @@ Required properties: + Optional properties: + + - reset-gpios: GPIO to be used to reset the whole device ++- qca,ignore-power-on-sel: Ignore power on pin strapping to configure led open ++ drain or eeprom presence. This is needed for broken ++ devices that have wrong configuration or when the oem ++ decided to not use pin strapping and fallback to sw ++ regs. ++- qca,led-open-drain: Set leds to open-drain mode. This requires the ++ qca,ignore-power-on-sel to be set or the driver will fail ++ to probe. This is needed if the oem doesn't use pin ++ strapping to set this mode and prefers to set it using sw ++ regs. The pin strapping related to led open drain mode is ++ the pin B68 for QCA832x and B49 for QCA833x + + Subnodes: + diff --git a/pkgs/patches-linux-5.15/747-v5.16-10-net-dsa-qca8k-add-support-for-pws-config-reg.patch b/pkgs/patches-linux-5.15/747-v5.16-10-net-dsa-qca8k-add-support-for-pws-config-reg.patch new file mode 100644 index 0000000..aa5d92a --- /dev/null +++ b/pkgs/patches-linux-5.15/747-v5.16-10-net-dsa-qca8k-add-support-for-pws-config-reg.patch @@ -0,0 +1,92 @@ +From 362bb238d8bf1470424214a8a5968d9c6cce68fa Mon Sep 17 00:00:00 2001 +From: Ansuel Smith <ansuelsmth@gmail.com> +Date: Thu, 14 Oct 2021 00:39:15 +0200 +Subject: net: dsa: qca8k: add support for pws config reg + +Some qca8327 switch require to force the ignore of power on sel +strapping. Some switch require to set the led open drain mode in regs +instead of using strapping. While most of the device implements this +using the correct way using pin strapping, there are still some broken +device that require to be set using sw regs. +Introduce a new binding and support these special configuration. +As led open drain require to ignore pin strapping to work, the probe +fails with EINVAL error with incorrect configuration. + +Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com> +Reviewed-by: Florian Fainelli <f.fainelli@gmail.com> +Signed-off-by: David S. Miller <davem@davemloft.net> +--- + drivers/net/dsa/qca8k.c | 39 +++++++++++++++++++++++++++++++++++++++ + drivers/net/dsa/qca8k.h | 6 ++++++ + 2 files changed, 45 insertions(+) + +--- a/drivers/net/dsa/qca8k.c ++++ b/drivers/net/dsa/qca8k.c +@@ -932,6 +932,41 @@ static int qca8k_find_cpu_port(struct ds + } + + static int ++qca8k_setup_of_pws_reg(struct qca8k_priv *priv) ++{ ++ struct device_node *node = priv->dev->of_node; ++ u32 val = 0; ++ int ret; ++ ++ /* QCA8327 require to set to the correct mode. ++ * His bigger brother QCA8328 have the 172 pin layout. ++ * Should be applied by default but we set this just to make sure. ++ */ ++ if (priv->switch_id == QCA8K_ID_QCA8327) { ++ ret = qca8k_rmw(priv, QCA8K_REG_PWS, QCA8327_PWS_PACKAGE148_EN, ++ QCA8327_PWS_PACKAGE148_EN); ++ if (ret) ++ return ret; ++ } ++ ++ if (of_property_read_bool(node, "qca,ignore-power-on-sel")) ++ val |= QCA8K_PWS_POWER_ON_SEL; ++ ++ if (of_property_read_bool(node, "qca,led-open-drain")) { ++ if (!(val & QCA8K_PWS_POWER_ON_SEL)) { ++ dev_err(priv->dev, "qca,led-open-drain require qca,ignore-power-on-sel to be set."); ++ return -EINVAL; ++ } ++ ++ val |= QCA8K_PWS_LED_OPEN_EN_CSR; ++ } ++ ++ return qca8k_rmw(priv, QCA8K_REG_PWS, ++ QCA8K_PWS_LED_OPEN_EN_CSR | QCA8K_PWS_POWER_ON_SEL, ++ val); ++} ++ ++static int + qca8k_parse_port_config(struct qca8k_priv *priv) + { + int port, cpu_port_index = 0, ret; +@@ -1053,6 +1088,10 @@ qca8k_setup(struct dsa_switch *ds) + if (ret) + return ret; + ++ ret = qca8k_setup_of_pws_reg(priv); ++ if (ret) ++ return ret; ++ + ret = qca8k_setup_mac_pwr_sel(priv); + if (ret) + return ret; +--- a/drivers/net/dsa/qca8k.h ++++ b/drivers/net/dsa/qca8k.h +@@ -46,6 +46,12 @@ + #define QCA8K_MAX_DELAY 3 + #define QCA8K_PORT_PAD_SGMII_EN BIT(7) + #define QCA8K_REG_PWS 0x010 ++#define QCA8K_PWS_POWER_ON_SEL BIT(31) ++/* This reg is only valid for QCA832x and toggle the package ++ * type from 176 pin (by default) to 148 pin used on QCA8327 ++ */ ++#define QCA8327_PWS_PACKAGE148_EN BIT(30) ++#define QCA8K_PWS_LED_OPEN_EN_CSR BIT(24) + #define QCA8K_PWS_SERDES_AEN_DIS BIT(7) + #define QCA8K_REG_MODULE_EN 0x030 + #define QCA8K_MODULE_EN_MIB BIT(0) diff --git a/pkgs/patches-linux-5.15/747-v5.16-11-dt-bindings-net-dsa-qca8k-document-support-for-qca83.patch b/pkgs/patches-linux-5.15/747-v5.16-11-dt-bindings-net-dsa-qca8k-document-support-for-qca83.patch new file mode 100644 index 0000000..1bfb00c --- /dev/null +++ b/pkgs/patches-linux-5.15/747-v5.16-11-dt-bindings-net-dsa-qca8k-document-support-for-qca83.patch @@ -0,0 +1,32 @@ +From ed7988d77fbfb79366b68f9e7fa60a6080da23d4 Mon Sep 17 00:00:00 2001 +From: Ansuel Smith <ansuelsmth@gmail.com> +Date: Thu, 14 Oct 2021 00:39:16 +0200 +Subject: dt-bindings: net: dsa: qca8k: document support for qca8328 + +QCA8328 is the bigger brother of qca8327. Document the new compatible +binding and add some information to understand the various switch +compatible. + +Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com> +Reviewed-by: Florian Fainelli <f.fainelli@gmail.com> +Signed-off-by: David S. Miller <davem@davemloft.net> +--- + Documentation/devicetree/bindings/net/dsa/qca8k.txt | 7 ++++--- + 1 file changed, 4 insertions(+), 3 deletions(-) + +--- a/Documentation/devicetree/bindings/net/dsa/qca8k.txt ++++ b/Documentation/devicetree/bindings/net/dsa/qca8k.txt +@@ -3,9 +3,10 @@ + Required properties: + + - compatible: should be one of: +- "qca,qca8327" +- "qca,qca8334" +- "qca,qca8337" ++ "qca,qca8328": referenced as AR8328(N)-AK1(A/B) QFN 176 pin package ++ "qca,qca8327": referenced as AR8327(N)-AL1A DR-QFN 148 pin package ++ "qca,qca8334": referenced as QCA8334-AL3C QFN 88 pin package ++ "qca,qca8337": referenced as QCA8337N-AL3(B/C) DR-QFN 148 pin package + + - #size-cells: must be 0 + - #address-cells: must be 1 diff --git a/pkgs/patches-linux-5.15/747-v5.16-12-net-dsa-qca8k-add-support-for-QCA8328.patch b/pkgs/patches-linux-5.15/747-v5.16-12-net-dsa-qca8k-add-support-for-QCA8328.patch new file mode 100644 index 0000000..70f227f --- /dev/null +++ b/pkgs/patches-linux-5.15/747-v5.16-12-net-dsa-qca8k-add-support-for-QCA8328.patch @@ -0,0 +1,78 @@ +From f477d1c8bdbef4f400718238e350f16f521d2a3e Mon Sep 17 00:00:00 2001 +From: Ansuel Smith <ansuelsmth@gmail.com> +Date: Thu, 14 Oct 2021 00:39:17 +0200 +Subject: net: dsa: qca8k: add support for QCA8328 + +QCA8328 switch is the bigger brother of the qca8327. Same regs different +chip. Change the function to set the correct pin layout and introduce a +new match_data to differentiate the 2 switch as they have the same ID +and their internal PHY have the same ID. + +Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com> +Reviewed-by: Florian Fainelli <f.fainelli@gmail.com> +Signed-off-by: David S. Miller <davem@davemloft.net> +--- + drivers/net/dsa/qca8k.c | 19 ++++++++++++++++--- + drivers/net/dsa/qca8k.h | 1 + + 2 files changed, 17 insertions(+), 3 deletions(-) + +--- a/drivers/net/dsa/qca8k.c ++++ b/drivers/net/dsa/qca8k.c +@@ -935,6 +935,7 @@ static int + qca8k_setup_of_pws_reg(struct qca8k_priv *priv) + { + struct device_node *node = priv->dev->of_node; ++ const struct qca8k_match_data *data; + u32 val = 0; + int ret; + +@@ -943,8 +944,14 @@ qca8k_setup_of_pws_reg(struct qca8k_priv + * Should be applied by default but we set this just to make sure. + */ + if (priv->switch_id == QCA8K_ID_QCA8327) { ++ data = of_device_get_match_data(priv->dev); ++ ++ /* Set the correct package of 148 pin for QCA8327 */ ++ if (data->reduced_package) ++ val |= QCA8327_PWS_PACKAGE148_EN; ++ + ret = qca8k_rmw(priv, QCA8K_REG_PWS, QCA8327_PWS_PACKAGE148_EN, +- QCA8327_PWS_PACKAGE148_EN); ++ val); + if (ret) + return ret; + } +@@ -2105,7 +2112,12 @@ static int qca8k_resume(struct device *d + static SIMPLE_DEV_PM_OPS(qca8k_pm_ops, + qca8k_suspend, qca8k_resume); + +-static const struct qca8k_match_data qca832x = { ++static const struct qca8k_match_data qca8327 = { ++ .id = QCA8K_ID_QCA8327, ++ .reduced_package = true, ++}; ++ ++static const struct qca8k_match_data qca8328 = { + .id = QCA8K_ID_QCA8327, + }; + +@@ -2114,7 +2126,8 @@ static const struct qca8k_match_data qca + }; + + static const struct of_device_id qca8k_of_match[] = { +- { .compatible = "qca,qca8327", .data = &qca832x }, ++ { .compatible = "qca,qca8327", .data = &qca8327 }, ++ { .compatible = "qca,qca8328", .data = &qca8328 }, + { .compatible = "qca,qca8334", .data = &qca833x }, + { .compatible = "qca,qca8337", .data = &qca833x }, + { /* sentinel */ }, +--- a/drivers/net/dsa/qca8k.h ++++ b/drivers/net/dsa/qca8k.h +@@ -260,6 +260,7 @@ struct ar8xxx_port_status { + + struct qca8k_match_data { + u8 id; ++ bool reduced_package; + }; + + enum { diff --git a/pkgs/patches-linux-5.15/747-v5.16-13-net-dsa-qca8k-set-internal-delay-also-for-sgmii.patch b/pkgs/patches-linux-5.15/747-v5.16-13-net-dsa-qca8k-set-internal-delay-also-for-sgmii.patch new file mode 100644 index 0000000..27f94dc --- /dev/null +++ b/pkgs/patches-linux-5.15/747-v5.16-13-net-dsa-qca8k-set-internal-delay-also-for-sgmii.patch @@ -0,0 +1,159 @@ +From cef08115846e581f80ff99abf7bf218da1840616 Mon Sep 17 00:00:00 2001 +From: Ansuel Smith <ansuelsmth@gmail.com> +Date: Thu, 14 Oct 2021 00:39:18 +0200 +Subject: net: dsa: qca8k: set internal delay also for sgmii + +QCA original code report port instability and sa that SGMII also require +to set internal delay. Generalize the rgmii delay function and apply the +advised value if they are not defined in DT. + +Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com> +Signed-off-by: David S. Miller <davem@davemloft.net> +--- + drivers/net/dsa/qca8k.c | 88 +++++++++++++++++++++++++++++++++---------------- + drivers/net/dsa/qca8k.h | 2 ++ + 2 files changed, 62 insertions(+), 28 deletions(-) + +--- a/drivers/net/dsa/qca8k.c ++++ b/drivers/net/dsa/qca8k.c +@@ -1004,6 +1004,7 @@ qca8k_parse_port_config(struct qca8k_pri + case PHY_INTERFACE_MODE_RGMII_ID: + case PHY_INTERFACE_MODE_RGMII_TXID: + case PHY_INTERFACE_MODE_RGMII_RXID: ++ case PHY_INTERFACE_MODE_SGMII: + delay = 0; + + if (!of_property_read_u32(port_dn, "tx-internal-delay-ps", &delay)) +@@ -1036,8 +1037,13 @@ qca8k_parse_port_config(struct qca8k_pri + + priv->rgmii_rx_delay[cpu_port_index] = delay; + +- break; +- case PHY_INTERFACE_MODE_SGMII: ++ /* Skip sgmii parsing for rgmii* mode */ ++ if (mode == PHY_INTERFACE_MODE_RGMII || ++ mode == PHY_INTERFACE_MODE_RGMII_ID || ++ mode == PHY_INTERFACE_MODE_RGMII_TXID || ++ mode == PHY_INTERFACE_MODE_RGMII_RXID) ++ break; ++ + if (of_property_read_bool(port_dn, "qca,sgmii-txclk-falling-edge")) + priv->sgmii_tx_clk_falling_edge = true; + +@@ -1261,12 +1267,53 @@ qca8k_setup(struct dsa_switch *ds) + } + + static void ++qca8k_mac_config_setup_internal_delay(struct qca8k_priv *priv, int cpu_port_index, ++ u32 reg) ++{ ++ u32 delay, val = 0; ++ int ret; ++ ++ /* Delay can be declared in 3 different way. ++ * Mode to rgmii and internal-delay standard binding defined ++ * rgmii-id or rgmii-tx/rx phy mode set. ++ * The parse logic set a delay different than 0 only when one ++ * of the 3 different way is used. In all other case delay is ++ * not enabled. With ID or TX/RXID delay is enabled and set ++ * to the default and recommended value. ++ */ ++ if (priv->rgmii_tx_delay[cpu_port_index]) { ++ delay = priv->rgmii_tx_delay[cpu_port_index]; ++ ++ val |= QCA8K_PORT_PAD_RGMII_TX_DELAY(delay) | ++ QCA8K_PORT_PAD_RGMII_TX_DELAY_EN; ++ } ++ ++ if (priv->rgmii_rx_delay[cpu_port_index]) { ++ delay = priv->rgmii_rx_delay[cpu_port_index]; ++ ++ val |= QCA8K_PORT_PAD_RGMII_RX_DELAY(delay) | ++ QCA8K_PORT_PAD_RGMII_RX_DELAY_EN; ++ } ++ ++ /* Set RGMII delay based on the selected values */ ++ ret = qca8k_rmw(priv, reg, ++ QCA8K_PORT_PAD_RGMII_TX_DELAY_MASK | ++ QCA8K_PORT_PAD_RGMII_RX_DELAY_MASK | ++ QCA8K_PORT_PAD_RGMII_TX_DELAY_EN | ++ QCA8K_PORT_PAD_RGMII_RX_DELAY_EN, ++ val); ++ if (ret) ++ dev_err(priv->dev, "Failed to set internal delay for CPU port%d", ++ cpu_port_index == QCA8K_CPU_PORT0 ? 0 : 6); ++} ++ ++static void + qca8k_phylink_mac_config(struct dsa_switch *ds, int port, unsigned int mode, + const struct phylink_link_state *state) + { + struct qca8k_priv *priv = ds->priv; + int cpu_port_index, ret; +- u32 reg, val, delay; ++ u32 reg, val; + + switch (port) { + case 0: /* 1st CPU port */ +@@ -1315,32 +1362,10 @@ qca8k_phylink_mac_config(struct dsa_swit + case PHY_INTERFACE_MODE_RGMII_ID: + case PHY_INTERFACE_MODE_RGMII_TXID: + case PHY_INTERFACE_MODE_RGMII_RXID: +- val = QCA8K_PORT_PAD_RGMII_EN; +- +- /* Delay can be declared in 3 different way. +- * Mode to rgmii and internal-delay standard binding defined +- * rgmii-id or rgmii-tx/rx phy mode set. +- * The parse logic set a delay different than 0 only when one +- * of the 3 different way is used. In all other case delay is +- * not enabled. With ID or TX/RXID delay is enabled and set +- * to the default and recommended value. +- */ +- if (priv->rgmii_tx_delay[cpu_port_index]) { +- delay = priv->rgmii_tx_delay[cpu_port_index]; +- +- val |= QCA8K_PORT_PAD_RGMII_TX_DELAY(delay) | +- QCA8K_PORT_PAD_RGMII_TX_DELAY_EN; +- } +- +- if (priv->rgmii_rx_delay[cpu_port_index]) { +- delay = priv->rgmii_rx_delay[cpu_port_index]; +- +- val |= QCA8K_PORT_PAD_RGMII_RX_DELAY(delay) | +- QCA8K_PORT_PAD_RGMII_RX_DELAY_EN; +- } ++ qca8k_write(priv, reg, QCA8K_PORT_PAD_RGMII_EN); + +- /* Set RGMII delay based on the selected values */ +- qca8k_write(priv, reg, val); ++ /* Configure rgmii delay */ ++ qca8k_mac_config_setup_internal_delay(priv, cpu_port_index, reg); + + /* QCA8337 requires to set rgmii rx delay for all ports. + * This is enabled through PORT5_PAD_CTRL for all ports, +@@ -1411,6 +1436,13 @@ qca8k_phylink_mac_config(struct dsa_swit + QCA8K_PORT0_PAD_SGMII_RXCLK_FALLING_EDGE | + QCA8K_PORT0_PAD_SGMII_TXCLK_FALLING_EDGE, + val); ++ ++ /* From original code is reported port instability as SGMII also ++ * require delay set. Apply advised values here or take them from DT. ++ */ ++ if (state->interface == PHY_INTERFACE_MODE_SGMII) ++ qca8k_mac_config_setup_internal_delay(priv, cpu_port_index, reg); ++ + break; + default: + dev_err(ds->dev, "xMII mode %s not supported for port %d\n", +--- a/drivers/net/dsa/qca8k.h ++++ b/drivers/net/dsa/qca8k.h +@@ -39,7 +39,9 @@ + #define QCA8K_REG_PORT5_PAD_CTRL 0x008 + #define QCA8K_REG_PORT6_PAD_CTRL 0x00c + #define QCA8K_PORT_PAD_RGMII_EN BIT(26) ++#define QCA8K_PORT_PAD_RGMII_TX_DELAY_MASK GENMASK(23, 22) + #define QCA8K_PORT_PAD_RGMII_TX_DELAY(x) ((x) << 22) ++#define QCA8K_PORT_PAD_RGMII_RX_DELAY_MASK GENMASK(21, 20) + #define QCA8K_PORT_PAD_RGMII_RX_DELAY(x) ((x) << 20) + #define QCA8K_PORT_PAD_RGMII_TX_DELAY_EN BIT(25) + #define QCA8K_PORT_PAD_RGMII_RX_DELAY_EN BIT(24) diff --git a/pkgs/patches-linux-5.15/747-v5.16-14-net-dsa-qca8k-move-port-config-to-dedicated-struct.patch b/pkgs/patches-linux-5.15/747-v5.16-14-net-dsa-qca8k-move-port-config-to-dedicated-struct.patch new file mode 100644 index 0000000..b991798 --- /dev/null +++ b/pkgs/patches-linux-5.15/747-v5.16-14-net-dsa-qca8k-move-port-config-to-dedicated-struct.patch @@ -0,0 +1,124 @@ +From fd0bb28c547f7c8affb1691128cece38f5b626a1 Mon Sep 17 00:00:00 2001 +From: Ansuel Smith <ansuelsmth@gmail.com> +Date: Thu, 14 Oct 2021 00:39:19 +0200 +Subject: net: dsa: qca8k: move port config to dedicated struct + +Move ports related config to dedicated struct to keep things organized. + +Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com> +Reviewed-by: Florian Fainelli <f.fainelli@gmail.com> +Signed-off-by: David S. Miller <davem@davemloft.net> +--- + drivers/net/dsa/qca8k.c | 26 +++++++++++++------------- + drivers/net/dsa/qca8k.h | 10 +++++++--- + 2 files changed, 20 insertions(+), 16 deletions(-) + +--- a/drivers/net/dsa/qca8k.c ++++ b/drivers/net/dsa/qca8k.c +@@ -1019,7 +1019,7 @@ qca8k_parse_port_config(struct qca8k_pri + delay = 3; + } + +- priv->rgmii_tx_delay[cpu_port_index] = delay; ++ priv->ports_config.rgmii_tx_delay[cpu_port_index] = delay; + + delay = 0; + +@@ -1035,7 +1035,7 @@ qca8k_parse_port_config(struct qca8k_pri + delay = 3; + } + +- priv->rgmii_rx_delay[cpu_port_index] = delay; ++ priv->ports_config.rgmii_rx_delay[cpu_port_index] = delay; + + /* Skip sgmii parsing for rgmii* mode */ + if (mode == PHY_INTERFACE_MODE_RGMII || +@@ -1045,17 +1045,17 @@ qca8k_parse_port_config(struct qca8k_pri + break; + + if (of_property_read_bool(port_dn, "qca,sgmii-txclk-falling-edge")) +- priv->sgmii_tx_clk_falling_edge = true; ++ priv->ports_config.sgmii_tx_clk_falling_edge = true; + + if (of_property_read_bool(port_dn, "qca,sgmii-rxclk-falling-edge")) +- priv->sgmii_rx_clk_falling_edge = true; ++ priv->ports_config.sgmii_rx_clk_falling_edge = true; + + if (of_property_read_bool(port_dn, "qca,sgmii-enable-pll")) { +- priv->sgmii_enable_pll = true; ++ priv->ports_config.sgmii_enable_pll = true; + + if (priv->switch_id == QCA8K_ID_QCA8327) { + dev_err(priv->dev, "SGMII PLL should NOT be enabled for qca8327. Aborting enabling"); +- priv->sgmii_enable_pll = false; ++ priv->ports_config.sgmii_enable_pll = false; + } + + if (priv->switch_revision < 2) +@@ -1281,15 +1281,15 @@ qca8k_mac_config_setup_internal_delay(st + * not enabled. With ID or TX/RXID delay is enabled and set + * to the default and recommended value. + */ +- if (priv->rgmii_tx_delay[cpu_port_index]) { +- delay = priv->rgmii_tx_delay[cpu_port_index]; ++ if (priv->ports_config.rgmii_tx_delay[cpu_port_index]) { ++ delay = priv->ports_config.rgmii_tx_delay[cpu_port_index]; + + val |= QCA8K_PORT_PAD_RGMII_TX_DELAY(delay) | + QCA8K_PORT_PAD_RGMII_TX_DELAY_EN; + } + +- if (priv->rgmii_rx_delay[cpu_port_index]) { +- delay = priv->rgmii_rx_delay[cpu_port_index]; ++ if (priv->ports_config.rgmii_rx_delay[cpu_port_index]) { ++ delay = priv->ports_config.rgmii_rx_delay[cpu_port_index]; + + val |= QCA8K_PORT_PAD_RGMII_RX_DELAY(delay) | + QCA8K_PORT_PAD_RGMII_RX_DELAY_EN; +@@ -1397,7 +1397,7 @@ qca8k_phylink_mac_config(struct dsa_swit + + val |= QCA8K_SGMII_EN_SD; + +- if (priv->sgmii_enable_pll) ++ if (priv->ports_config.sgmii_enable_pll) + val |= QCA8K_SGMII_EN_PLL | QCA8K_SGMII_EN_RX | + QCA8K_SGMII_EN_TX; + +@@ -1425,10 +1425,10 @@ qca8k_phylink_mac_config(struct dsa_swit + val = 0; + + /* SGMII Clock phase configuration */ +- if (priv->sgmii_rx_clk_falling_edge) ++ if (priv->ports_config.sgmii_rx_clk_falling_edge) + val |= QCA8K_PORT0_PAD_SGMII_RXCLK_FALLING_EDGE; + +- if (priv->sgmii_tx_clk_falling_edge) ++ if (priv->ports_config.sgmii_tx_clk_falling_edge) + val |= QCA8K_PORT0_PAD_SGMII_TXCLK_FALLING_EDGE; + + if (val) +--- a/drivers/net/dsa/qca8k.h ++++ b/drivers/net/dsa/qca8k.h +@@ -270,15 +270,19 @@ enum { + QCA8K_CPU_PORT6, + }; + +-struct qca8k_priv { +- u8 switch_id; +- u8 switch_revision; ++struct qca8k_ports_config { + bool sgmii_rx_clk_falling_edge; + bool sgmii_tx_clk_falling_edge; + bool sgmii_enable_pll; + u8 rgmii_rx_delay[QCA8K_NUM_CPU_PORTS]; /* 0: CPU port0, 1: CPU port6 */ + u8 rgmii_tx_delay[QCA8K_NUM_CPU_PORTS]; /* 0: CPU port0, 1: CPU port6 */ ++}; ++ ++struct qca8k_priv { ++ u8 switch_id; ++ u8 switch_revision; + bool legacy_phy_port_mapping; ++ struct qca8k_ports_config ports_config; + struct regmap *regmap; + struct mii_bus *bus; + struct ar8xxx_port_status port_sts[QCA8K_NUM_PORTS]; diff --git a/pkgs/patches-linux-5.15/747-v5.16-15-dt-bindings-net-ipq8064-mdio-fix-warning-with-new-qc.patch b/pkgs/patches-linux-5.15/747-v5.16-15-dt-bindings-net-ipq8064-mdio-fix-warning-with-new-qc.patch new file mode 100644 index 0000000..f7cb514 --- /dev/null +++ b/pkgs/patches-linux-5.15/747-v5.16-15-dt-bindings-net-ipq8064-mdio-fix-warning-with-new-qc.patch @@ -0,0 +1,26 @@ +From e52073a8e3086046a098b8a7cbeb282ff0cdb424 Mon Sep 17 00:00:00 2001 +From: Ansuel Smith <ansuelsmth@gmail.com> +Date: Thu, 14 Oct 2021 00:39:20 +0200 +Subject: dt-bindings: net: ipq8064-mdio: fix warning with new qca8k switch + +Fix warning now that we have qca8k switch Documentation using yaml. + +Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com> +Signed-off-by: David S. Miller <davem@davemloft.net> +--- + Documentation/devicetree/bindings/net/qcom,ipq8064-mdio.yaml | 5 ++++- + 1 file changed, 4 insertions(+), 1 deletion(-) + +--- a/Documentation/devicetree/bindings/net/qcom,ipq8064-mdio.yaml ++++ b/Documentation/devicetree/bindings/net/qcom,ipq8064-mdio.yaml +@@ -51,6 +51,9 @@ examples: + switch@10 { + compatible = "qca,qca8337"; + reg = <0x10>; +- /* ... */ ++ ++ ports { ++ /* ... */ ++ }; + }; + }; diff --git a/pkgs/patches-linux-5.15/747-v5.16-16-dt-bindings-net-dsa-qca8k-convert-to-YAML-schema.patch b/pkgs/patches-linux-5.15/747-v5.16-16-dt-bindings-net-dsa-qca8k-convert-to-YAML-schema.patch new file mode 100644 index 0000000..b9bce97 --- /dev/null +++ b/pkgs/patches-linux-5.15/747-v5.16-16-dt-bindings-net-dsa-qca8k-convert-to-YAML-schema.patch @@ -0,0 +1,631 @@ +From d291fbb8245d5ba04979fed85575860a5cea7196 Mon Sep 17 00:00:00 2001 +From: Matthew Hagan <mnhagan88@gmail.com> +Date: Thu, 14 Oct 2021 00:39:21 +0200 +Subject: dt-bindings: net: dsa: qca8k: convert to YAML schema + +Convert the qca8k bindings to YAML format. + +Signed-off-by: Matthew Hagan <mnhagan88@gmail.com> +Co-developed-by: Ansuel Smith <ansuelsmth@gmail.com> +Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com> +Signed-off-by: David S. Miller <davem@davemloft.net> +--- + .../devicetree/bindings/net/dsa/qca8k.txt | 245 -------------- + .../devicetree/bindings/net/dsa/qca8k.yaml | 362 +++++++++++++++++++++ + 2 files changed, 362 insertions(+), 245 deletions(-) + delete mode 100644 Documentation/devicetree/bindings/net/dsa/qca8k.txt + create mode 100644 Documentation/devicetree/bindings/net/dsa/qca8k.yaml + +--- a/Documentation/devicetree/bindings/net/dsa/qca8k.txt ++++ /dev/null +@@ -1,245 +0,0 @@ +-* Qualcomm Atheros QCA8xxx switch family +- +-Required properties: +- +-- compatible: should be one of: +- "qca,qca8328": referenced as AR8328(N)-AK1(A/B) QFN 176 pin package +- "qca,qca8327": referenced as AR8327(N)-AL1A DR-QFN 148 pin package +- "qca,qca8334": referenced as QCA8334-AL3C QFN 88 pin package +- "qca,qca8337": referenced as QCA8337N-AL3(B/C) DR-QFN 148 pin package +- +-- #size-cells: must be 0 +-- #address-cells: must be 1 +- +-Optional properties: +- +-- reset-gpios: GPIO to be used to reset the whole device +-- qca,ignore-power-on-sel: Ignore power on pin strapping to configure led open +- drain or eeprom presence. This is needed for broken +- devices that have wrong configuration or when the oem +- decided to not use pin strapping and fallback to sw +- regs. +-- qca,led-open-drain: Set leds to open-drain mode. This requires the +- qca,ignore-power-on-sel to be set or the driver will fail +- to probe. This is needed if the oem doesn't use pin +- strapping to set this mode and prefers to set it using sw +- regs. The pin strapping related to led open drain mode is +- the pin B68 for QCA832x and B49 for QCA833x +- +-Subnodes: +- +-The integrated switch subnode should be specified according to the binding +-described in dsa/dsa.txt. If the QCA8K switch is connect to a SoC's external +-mdio-bus each subnode describing a port needs to have a valid phandle +-referencing the internal PHY it is connected to. This is because there's no +-N:N mapping of port and PHY id. +-To declare the internal mdio-bus configuration, declare a mdio node in the +-switch node and declare the phandle for the port referencing the internal +-PHY is connected to. In this config a internal mdio-bus is registered and +-the mdio MASTER is used as communication. +- +-Don't use mixed external and internal mdio-bus configurations, as this is +-not supported by the hardware. +- +-This switch support 2 CPU port. Normally and advised configuration is with +-CPU port set to port 0. It is also possible to set the CPU port to port 6 +-if the device requires it. The driver will configure the switch to the defined +-port. With both CPU port declared the first CPU port is selected as primary +-and the secondary CPU ignored. +- +-A CPU port node has the following optional node: +- +-- fixed-link : Fixed-link subnode describing a link to a non-MDIO +- managed entity. See +- Documentation/devicetree/bindings/net/fixed-link.txt +- for details. +-- qca,sgmii-rxclk-falling-edge: Set the receive clock phase to falling edge. +- Mostly used in qca8327 with CPU port 0 set to +- sgmii. +-- qca,sgmii-txclk-falling-edge: Set the transmit clock phase to falling edge. +-- qca,sgmii-enable-pll : For SGMII CPU port, explicitly enable PLL, TX and RX +- chain along with Signal Detection. +- This should NOT be enabled for qca8327. If enabled with +- qca8327 the sgmii port won't correctly init and an err +- is printed. +- This can be required for qca8337 switch with revision 2. +- A warning is displayed when used with revision greater +- 2. +- With CPU port set to sgmii and qca8337 it is advised +- to set this unless a communication problem is observed. +- +-For QCA8K the 'fixed-link' sub-node supports only the following properties: +- +-- 'speed' (integer, mandatory), to indicate the link speed. Accepted +- values are 10, 100 and 1000 +-- 'full-duplex' (boolean, optional), to indicate that full duplex is +- used. When absent, half duplex is assumed. +- +-Examples: +- +-for the external mdio-bus configuration: +- +- &mdio0 { +- phy_port1: phy@0 { +- reg = <0>; +- }; +- +- phy_port2: phy@1 { +- reg = <1>; +- }; +- +- phy_port3: phy@2 { +- reg = <2>; +- }; +- +- phy_port4: phy@3 { +- reg = <3>; +- }; +- +- phy_port5: phy@4 { +- reg = <4>; +- }; +- +- switch@10 { +- compatible = "qca,qca8337"; +- #address-cells = <1>; +- #size-cells = <0>; +- +- reset-gpios = <&gpio 42 GPIO_ACTIVE_LOW>; +- reg = <0x10>; +- +- ports { +- #address-cells = <1>; +- #size-cells = <0>; +- port@0 { +- reg = <0>; +- label = "cpu"; +- ethernet = <&gmac1>; +- phy-mode = "rgmii"; +- fixed-link { +- speed = 1000; +- full-duplex; +- }; +- }; +- +- port@1 { +- reg = <1>; +- label = "lan1"; +- phy-handle = <&phy_port1>; +- }; +- +- port@2 { +- reg = <2>; +- label = "lan2"; +- phy-handle = <&phy_port2>; +- }; +- +- port@3 { +- reg = <3>; +- label = "lan3"; +- phy-handle = <&phy_port3>; +- }; +- +- port@4 { +- reg = <4>; +- label = "lan4"; +- phy-handle = <&phy_port4>; +- }; +- +- port@5 { +- reg = <5>; +- label = "wan"; +- phy-handle = <&phy_port5>; +- }; +- }; +- }; +- }; +- +-for the internal master mdio-bus configuration: +- +- &mdio0 { +- switch@10 { +- compatible = "qca,qca8337"; +- #address-cells = <1>; +- #size-cells = <0>; +- +- reset-gpios = <&gpio 42 GPIO_ACTIVE_LOW>; +- reg = <0x10>; +- +- ports { +- #address-cells = <1>; +- #size-cells = <0>; +- +- port@0 { +- reg = <0>; +- label = "cpu"; +- ethernet = <&gmac1>; +- phy-mode = "rgmii"; +- fixed-link { +- speed = 1000; +- full-duplex; +- }; +- }; +- +- port@1 { +- reg = <1>; +- label = "lan1"; +- phy-mode = "internal"; +- phy-handle = <&phy_port1>; +- }; +- +- port@2 { +- reg = <2>; +- label = "lan2"; +- phy-mode = "internal"; +- phy-handle = <&phy_port2>; +- }; +- +- port@3 { +- reg = <3>; +- label = "lan3"; +- phy-mode = "internal"; +- phy-handle = <&phy_port3>; +- }; +- +- port@4 { +- reg = <4>; +- label = "lan4"; +- phy-mode = "internal"; +- phy-handle = <&phy_port4>; +- }; +- +- port@5 { +- reg = <5>; +- label = "wan"; +- phy-mode = "internal"; +- phy-handle = <&phy_port5>; +- }; +- }; +- +- mdio { +- #address-cells = <1>; +- #size-cells = <0>; +- +- phy_port1: phy@0 { +- reg = <0>; +- }; +- +- phy_port2: phy@1 { +- reg = <1>; +- }; +- +- phy_port3: phy@2 { +- reg = <2>; +- }; +- +- phy_port4: phy@3 { +- reg = <3>; +- }; +- +- phy_port5: phy@4 { +- reg = <4>; +- }; +- }; +- }; +- }; +--- /dev/null ++++ b/Documentation/devicetree/bindings/net/dsa/qca8k.yaml +@@ -0,0 +1,362 @@ ++# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) ++%YAML 1.2 ++--- ++$id: http://devicetree.org/schemas/net/dsa/qca8k.yaml# ++$schema: http://devicetree.org/meta-schemas/core.yaml# ++ ++title: Qualcomm Atheros QCA83xx switch family ++ ++maintainers: ++ - John Crispin <john@phrozen.org> ++ ++description: ++ If the QCA8K switch is connect to an SoC's external mdio-bus, each subnode ++ describing a port needs to have a valid phandle referencing the internal PHY ++ it is connected to. This is because there is no N:N mapping of port and PHY ++ ID. To declare the internal mdio-bus configuration, declare an MDIO node in ++ the switch node and declare the phandle for the port, referencing the internal ++ PHY it is connected to. In this config, an internal mdio-bus is registered and ++ the MDIO master is used for communication. Mixed external and internal ++ mdio-bus configurations are not supported by the hardware. ++ ++properties: ++ compatible: ++ oneOf: ++ - enum: ++ - qca,qca8327 ++ - qca,qca8328 ++ - qca,qca8334 ++ - qca,qca8337 ++ description: | ++ qca,qca8328: referenced as AR8328(N)-AK1(A/B) QFN 176 pin package ++ qca,qca8327: referenced as AR8327(N)-AL1A DR-QFN 148 pin package ++ qca,qca8334: referenced as QCA8334-AL3C QFN 88 pin package ++ qca,qca8337: referenced as QCA8337N-AL3(B/C) DR-QFN 148 pin package ++ ++ reg: ++ maxItems: 1 ++ ++ reset-gpios: ++ description: ++ GPIO to be used to reset the whole device ++ maxItems: 1 ++ ++ qca,ignore-power-on-sel: ++ $ref: /schemas/types.yaml#/definitions/flag ++ description: ++ Ignore power-on pin strapping to configure LED open-drain or EEPROM ++ presence. This is needed for devices with incorrect configuration or when ++ the OEM has decided not to use pin strapping and falls back to SW regs. ++ ++ qca,led-open-drain: ++ $ref: /schemas/types.yaml#/definitions/flag ++ description: ++ Set LEDs to open-drain mode. This requires the qca,ignore-power-on-sel to ++ be set, otherwise the driver will fail at probe. This is required if the ++ OEM does not use pin strapping to set this mode and prefers to set it ++ using SW regs. The pin strappings related to LED open-drain mode are ++ B68 on the QCA832x and B49 on the QCA833x. ++ ++ mdio: ++ type: object ++ description: Qca8k switch have an internal mdio to access switch port. ++ If this is not present, the legacy mapping is used and the ++ internal mdio access is used. ++ With the legacy mapping the reg corresponding to the internal ++ mdio is the switch reg with an offset of -1. ++ ++ properties: ++ '#address-cells': ++ const: 1 ++ '#size-cells': ++ const: 0 ++ ++ patternProperties: ++ "^(ethernet-)?phy@[0-4]$": ++ type: object ++ ++ allOf: ++ - $ref: "http://devicetree.org/schemas/net/mdio.yaml#" ++ ++ properties: ++ reg: ++ maxItems: 1 ++ ++ required: ++ - reg ++ ++patternProperties: ++ "^(ethernet-)?ports$": ++ type: object ++ properties: ++ '#address-cells': ++ const: 1 ++ '#size-cells': ++ const: 0 ++ ++ patternProperties: ++ "^(ethernet-)?port@[0-6]$": ++ type: object ++ description: Ethernet switch ports ++ ++ properties: ++ reg: ++ description: Port number ++ ++ label: ++ description: ++ Describes the label associated with this port, which will become ++ the netdev name ++ $ref: /schemas/types.yaml#/definitions/string ++ ++ link: ++ description: ++ Should be a list of phandles to other switch's DSA port. This ++ port is used as the outgoing port towards the phandle ports. The ++ full routing information must be given, not just the one hop ++ routes to neighbouring switches ++ $ref: /schemas/types.yaml#/definitions/phandle-array ++ ++ ethernet: ++ description: ++ Should be a phandle to a valid Ethernet device node. This host ++ device is what the switch port is connected to ++ $ref: /schemas/types.yaml#/definitions/phandle ++ ++ phy-handle: true ++ ++ phy-mode: true ++ ++ fixed-link: true ++ ++ mac-address: true ++ ++ sfp: true ++ ++ qca,sgmii-rxclk-falling-edge: ++ $ref: /schemas/types.yaml#/definitions/flag ++ description: ++ Set the receive clock phase to falling edge. Mostly commonly used on ++ the QCA8327 with CPU port 0 set to SGMII. ++ ++ qca,sgmii-txclk-falling-edge: ++ $ref: /schemas/types.yaml#/definitions/flag ++ description: ++ Set the transmit clock phase to falling edge. ++ ++ qca,sgmii-enable-pll: ++ $ref: /schemas/types.yaml#/definitions/flag ++ description: ++ For SGMII CPU port, explicitly enable PLL, TX and RX chain along with ++ Signal Detection. On the QCA8327 this should not be enabled, otherwise ++ the SGMII port will not initialize. When used on the QCA8337, revision 3 ++ or greater, a warning will be displayed. When the CPU port is set to ++ SGMII on the QCA8337, it is advised to set this unless a communication ++ issue is observed. ++ ++ required: ++ - reg ++ ++ additionalProperties: false ++ ++oneOf: ++ - required: ++ - ports ++ - required: ++ - ethernet-ports ++ ++required: ++ - compatible ++ - reg ++ ++additionalProperties: true ++ ++examples: ++ - | ++ #include <dt-bindings/gpio/gpio.h> ++ ++ mdio { ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ external_phy_port1: ethernet-phy@0 { ++ reg = <0>; ++ }; ++ ++ external_phy_port2: ethernet-phy@1 { ++ reg = <1>; ++ }; ++ ++ external_phy_port3: ethernet-phy@2 { ++ reg = <2>; ++ }; ++ ++ external_phy_port4: ethernet-phy@3 { ++ reg = <3>; ++ }; ++ ++ external_phy_port5: ethernet-phy@4 { ++ reg = <4>; ++ }; ++ ++ switch@10 { ++ compatible = "qca,qca8337"; ++ #address-cells = <1>; ++ #size-cells = <0>; ++ reset-gpios = <&gpio 42 GPIO_ACTIVE_LOW>; ++ reg = <0x10>; ++ ++ ports { ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ port@0 { ++ reg = <0>; ++ label = "cpu"; ++ ethernet = <&gmac1>; ++ phy-mode = "rgmii"; ++ ++ fixed-link { ++ speed = <1000>; ++ full-duplex; ++ }; ++ }; ++ ++ port@1 { ++ reg = <1>; ++ label = "lan1"; ++ phy-handle = <&external_phy_port1>; ++ }; ++ ++ port@2 { ++ reg = <2>; ++ label = "lan2"; ++ phy-handle = <&external_phy_port2>; ++ }; ++ ++ port@3 { ++ reg = <3>; ++ label = "lan3"; ++ phy-handle = <&external_phy_port3>; ++ }; ++ ++ port@4 { ++ reg = <4>; ++ label = "lan4"; ++ phy-handle = <&external_phy_port4>; ++ }; ++ ++ port@5 { ++ reg = <5>; ++ label = "wan"; ++ phy-handle = <&external_phy_port5>; ++ }; ++ }; ++ }; ++ }; ++ - | ++ #include <dt-bindings/gpio/gpio.h> ++ ++ mdio { ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ switch@10 { ++ compatible = "qca,qca8337"; ++ #address-cells = <1>; ++ #size-cells = <0>; ++ reset-gpios = <&gpio 42 GPIO_ACTIVE_LOW>; ++ reg = <0x10>; ++ ++ ports { ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ port@0 { ++ reg = <0>; ++ label = "cpu"; ++ ethernet = <&gmac1>; ++ phy-mode = "rgmii"; ++ ++ fixed-link { ++ speed = <1000>; ++ full-duplex; ++ }; ++ }; ++ ++ port@1 { ++ reg = <1>; ++ label = "lan1"; ++ phy-mode = "internal"; ++ phy-handle = <&internal_phy_port1>; ++ }; ++ ++ port@2 { ++ reg = <2>; ++ label = "lan2"; ++ phy-mode = "internal"; ++ phy-handle = <&internal_phy_port2>; ++ }; ++ ++ port@3 { ++ reg = <3>; ++ label = "lan3"; ++ phy-mode = "internal"; ++ phy-handle = <&internal_phy_port3>; ++ }; ++ ++ port@4 { ++ reg = <4>; ++ label = "lan4"; ++ phy-mode = "internal"; ++ phy-handle = <&internal_phy_port4>; ++ }; ++ ++ port@5 { ++ reg = <5>; ++ label = "wan"; ++ phy-mode = "internal"; ++ phy-handle = <&internal_phy_port5>; ++ }; ++ ++ port@6 { ++ reg = <0>; ++ label = "cpu"; ++ ethernet = <&gmac1>; ++ phy-mode = "sgmii"; ++ ++ qca,sgmii-rxclk-falling-edge; ++ ++ fixed-link { ++ speed = <1000>; ++ full-duplex; ++ }; ++ }; ++ }; ++ ++ mdio { ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ internal_phy_port1: ethernet-phy@0 { ++ reg = <0>; ++ }; ++ ++ internal_phy_port2: ethernet-phy@1 { ++ reg = <1>; ++ }; ++ ++ internal_phy_port3: ethernet-phy@2 { ++ reg = <2>; ++ }; ++ ++ internal_phy_port4: ethernet-phy@3 { ++ reg = <3>; ++ }; ++ ++ internal_phy_port5: ethernet-phy@4 { ++ reg = <4>; ++ }; ++ }; ++ }; ++ }; diff --git a/pkgs/patches-linux-5.15/748-v5.16-net-dsa-qca8k-fix-delay-applied-to-wrong-cpu-in-parse-p.patch b/pkgs/patches-linux-5.15/748-v5.16-net-dsa-qca8k-fix-delay-applied-to-wrong-cpu-in-parse-p.patch new file mode 100644 index 0000000..a510cfd --- /dev/null +++ b/pkgs/patches-linux-5.15/748-v5.16-net-dsa-qca8k-fix-delay-applied-to-wrong-cpu-in-parse-p.patch @@ -0,0 +1,28 @@ +From 06dd34a628ae5b6a839b757e746de165d6789ca8 Mon Sep 17 00:00:00 2001 +From: Ansuel Smith <ansuelsmth@gmail.com> +Date: Sun, 17 Oct 2021 16:56:46 +0200 +Subject: net: dsa: qca8k: fix delay applied to wrong cpu in parse_port_config + +Fix delay settings applied to wrong cpu in parse_port_config. The delay +values is set to the wrong index as the cpu_port_index is incremented +too early. Start the cpu_port_index to -1 so the correct value is +applied to address also the case with invalid phy mode and not available +port. + +Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com> +Signed-off-by: David S. Miller <davem@davemloft.net> +--- + drivers/net/dsa/qca8k.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/drivers/net/dsa/qca8k.c ++++ b/drivers/net/dsa/qca8k.c +@@ -976,7 +976,7 @@ qca8k_setup_of_pws_reg(struct qca8k_priv + static int + qca8k_parse_port_config(struct qca8k_priv *priv) + { +- int port, cpu_port_index = 0, ret; ++ int port, cpu_port_index = -1, ret; + struct device_node *port_dn; + phy_interface_t mode; + struct dsa_port *dp; diff --git a/pkgs/patches-linux-5.15/749-v5.16-net-dsa-qca8k-tidy-for-loop-in-setup-and-add-cpu-port-c.patch b/pkgs/patches-linux-5.15/749-v5.16-net-dsa-qca8k-tidy-for-loop-in-setup-and-add-cpu-port-c.patch new file mode 100644 index 0000000..71fa302 --- /dev/null +++ b/pkgs/patches-linux-5.15/749-v5.16-net-dsa-qca8k-tidy-for-loop-in-setup-and-add-cpu-port-c.patch @@ -0,0 +1,151 @@ +From 040e926f5813a5f4cc18dbff7c942d1e52f368f2 Mon Sep 17 00:00:00 2001 +From: Ansuel Smith <ansuelsmth@gmail.com> +Date: Tue, 19 Oct 2021 02:08:50 +0200 +Subject: net: dsa: qca8k: tidy for loop in setup and add cpu port check + +Tidy and organize qca8k setup function from multiple for loop. +Change for loop in bridge leave/join to scan all port and skip cpu port. +No functional change intended. + +Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com> +Signed-off-by: David S. Miller <davem@davemloft.net> +--- + drivers/net/dsa/qca8k.c | 74 +++++++++++++++++++++++++++++-------------------- + 1 file changed, 44 insertions(+), 30 deletions(-) + +--- a/drivers/net/dsa/qca8k.c ++++ b/drivers/net/dsa/qca8k.c +@@ -1122,28 +1122,34 @@ qca8k_setup(struct dsa_switch *ds) + if (ret) + dev_warn(priv->dev, "mib init failed"); + +- /* Enable QCA header mode on the cpu port */ +- ret = qca8k_write(priv, QCA8K_REG_PORT_HDR_CTRL(cpu_port), +- QCA8K_PORT_HDR_CTRL_ALL << QCA8K_PORT_HDR_CTRL_TX_S | +- QCA8K_PORT_HDR_CTRL_ALL << QCA8K_PORT_HDR_CTRL_RX_S); +- if (ret) { +- dev_err(priv->dev, "failed enabling QCA header mode"); +- return ret; +- } +- +- /* Disable forwarding by default on all ports */ ++ /* Initial setup of all ports */ + for (i = 0; i < QCA8K_NUM_PORTS; i++) { ++ /* Disable forwarding by default on all ports */ + ret = qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(i), + QCA8K_PORT_LOOKUP_MEMBER, 0); + if (ret) + return ret; +- } + +- /* Disable MAC by default on all ports */ +- for (i = 1; i < QCA8K_NUM_PORTS; i++) +- qca8k_port_set_status(priv, i, 0); ++ /* Enable QCA header mode on all cpu ports */ ++ if (dsa_is_cpu_port(ds, i)) { ++ ret = qca8k_write(priv, QCA8K_REG_PORT_HDR_CTRL(i), ++ QCA8K_PORT_HDR_CTRL_ALL << QCA8K_PORT_HDR_CTRL_TX_S | ++ QCA8K_PORT_HDR_CTRL_ALL << QCA8K_PORT_HDR_CTRL_RX_S); ++ if (ret) { ++ dev_err(priv->dev, "failed enabling QCA header mode"); ++ return ret; ++ } ++ } ++ ++ /* Disable MAC by default on all user ports */ ++ if (dsa_is_user_port(ds, i)) ++ qca8k_port_set_status(priv, i, 0); ++ } + +- /* Forward all unknown frames to CPU port for Linux processing */ ++ /* Forward all unknown frames to CPU port for Linux processing ++ * Notice that in multi-cpu config only one port should be set ++ * for igmp, unknown, multicast and broadcast packet ++ */ + ret = qca8k_write(priv, QCA8K_REG_GLOBAL_FW_CTRL1, + BIT(cpu_port) << QCA8K_GLOBAL_FW_CTRL1_IGMP_DP_S | + BIT(cpu_port) << QCA8K_GLOBAL_FW_CTRL1_BC_DP_S | +@@ -1152,11 +1158,13 @@ qca8k_setup(struct dsa_switch *ds) + if (ret) + return ret; + +- /* Setup connection between CPU port & user ports */ ++ /* Setup connection between CPU port & user ports ++ * Configure specific switch configuration for ports ++ */ + for (i = 0; i < QCA8K_NUM_PORTS; i++) { + /* CPU port gets connected to all user ports of the switch */ + if (dsa_is_cpu_port(ds, i)) { +- ret = qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(cpu_port), ++ ret = qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(i), + QCA8K_PORT_LOOKUP_MEMBER, dsa_user_ports(ds)); + if (ret) + return ret; +@@ -1193,16 +1201,14 @@ qca8k_setup(struct dsa_switch *ds) + if (ret) + return ret; + } +- } + +- /* The port 5 of the qca8337 have some problem in flood condition. The +- * original legacy driver had some specific buffer and priority settings +- * for the different port suggested by the QCA switch team. Add this +- * missing settings to improve switch stability under load condition. +- * This problem is limited to qca8337 and other qca8k switch are not affected. +- */ +- if (priv->switch_id == QCA8K_ID_QCA8337) { +- for (i = 0; i < QCA8K_NUM_PORTS; i++) { ++ /* The port 5 of the qca8337 have some problem in flood condition. The ++ * original legacy driver had some specific buffer and priority settings ++ * for the different port suggested by the QCA switch team. Add this ++ * missing settings to improve switch stability under load condition. ++ * This problem is limited to qca8337 and other qca8k switch are not affected. ++ */ ++ if (priv->switch_id == QCA8K_ID_QCA8337) { + switch (i) { + /* The 2 CPU port and port 5 requires some different + * priority than any other ports. +@@ -1238,6 +1244,12 @@ qca8k_setup(struct dsa_switch *ds) + QCA8K_PORT_HOL_CTRL1_WRED_EN, + mask); + } ++ ++ /* Set initial MTU for every port. ++ * We have only have a general MTU setting. So track ++ * every port and set the max across all port. ++ */ ++ priv->port_mtu[i] = ETH_FRAME_LEN + ETH_FCS_LEN; + } + + /* Special GLOBAL_FC_THRESH value are needed for ar8327 switch */ +@@ -1251,8 +1263,6 @@ qca8k_setup(struct dsa_switch *ds) + } + + /* Setup our port MTUs to match power on defaults */ +- for (i = 0; i < QCA8K_NUM_PORTS; i++) +- priv->port_mtu[i] = ETH_FRAME_LEN + ETH_FCS_LEN; + ret = qca8k_write(priv, QCA8K_MAX_FRAME_SIZE, ETH_FRAME_LEN + ETH_FCS_LEN); + if (ret) + dev_warn(priv->dev, "failed setting MTU settings"); +@@ -1728,7 +1738,9 @@ qca8k_port_bridge_join(struct dsa_switch + cpu_port = dsa_to_port(ds, port)->cpu_dp->index; + port_mask = BIT(cpu_port); + +- for (i = 1; i < QCA8K_NUM_PORTS; i++) { ++ for (i = 0; i < QCA8K_NUM_PORTS; i++) { ++ if (dsa_is_cpu_port(ds, i)) ++ continue; + if (dsa_to_port(ds, i)->bridge_dev != br) + continue; + /* Add this port to the portvlan mask of the other ports +@@ -1758,7 +1770,9 @@ qca8k_port_bridge_leave(struct dsa_switc + + cpu_port = dsa_to_port(ds, port)->cpu_dp->index; + +- for (i = 1; i < QCA8K_NUM_PORTS; i++) { ++ for (i = 0; i < QCA8K_NUM_PORTS; i++) { ++ if (dsa_is_cpu_port(ds, i)) ++ continue; + if (dsa_to_port(ds, i)->bridge_dev != br) + continue; + /* Remove this port to the portvlan mask of the other ports diff --git a/pkgs/patches-linux-5.15/750-v5.16-net-dsa-qca8k-make-sure-pad0-mac06-exchange-is-disabled.patch b/pkgs/patches-linux-5.15/750-v5.16-net-dsa-qca8k-make-sure-pad0-mac06-exchange-is-disabled.patch new file mode 100644 index 0000000..4a61703 --- /dev/null +++ b/pkgs/patches-linux-5.15/750-v5.16-net-dsa-qca8k-make-sure-pad0-mac06-exchange-is-disabled.patch @@ -0,0 +1,47 @@ +From 5f15d392dcb4aa250a63d6f2c5adfc26c0aedc78 Mon Sep 17 00:00:00 2001 +From: Ansuel Smith <ansuelsmth@gmail.com> +Date: Tue, 2 Nov 2021 19:30:41 +0100 +Subject: net: dsa: qca8k: make sure PAD0 MAC06 exchange is disabled + +Some device set MAC06 exchange in the bootloader. This cause some +problem as we don't support this strange mode and we just set the port6 +as the primary CPU port. With MAC06 exchange, PAD0 reg configure port6 +instead of port0. Add an extra check and explicitly disable MAC06 exchange +to correctly configure the port PAD config. + +Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com> +Fixes: 3fcf734aa482 ("net: dsa: qca8k: add support for cpu port 6") +Reviewed-by: Vladimir Oltean <olteanv@gmail.com> +Signed-off-by: David S. Miller <davem@davemloft.net> +--- + drivers/net/dsa/qca8k.c | 8 ++++++++ + drivers/net/dsa/qca8k.h | 1 + + 2 files changed, 9 insertions(+) + +--- a/drivers/net/dsa/qca8k.c ++++ b/drivers/net/dsa/qca8k.c +@@ -1109,6 +1109,14 @@ qca8k_setup(struct dsa_switch *ds) + if (ret) + return ret; + ++ /* Make sure MAC06 is disabled */ ++ ret = qca8k_reg_clear(priv, QCA8K_REG_PORT0_PAD_CTRL, ++ QCA8K_PORT0_PAD_MAC06_EXCHANGE_EN); ++ if (ret) { ++ dev_err(priv->dev, "failed disabling MAC06 exchange"); ++ return ret; ++ } ++ + /* Enable CPU Port */ + ret = qca8k_reg_set(priv, QCA8K_REG_GLOBAL_FW_CTRL0, + QCA8K_GLOBAL_FW_CTRL0_CPU_PORT_EN); +--- a/drivers/net/dsa/qca8k.h ++++ b/drivers/net/dsa/qca8k.h +@@ -34,6 +34,7 @@ + #define QCA8K_MASK_CTRL_DEVICE_ID_MASK GENMASK(15, 8) + #define QCA8K_MASK_CTRL_DEVICE_ID(x) ((x) >> 8) + #define QCA8K_REG_PORT0_PAD_CTRL 0x004 ++#define QCA8K_PORT0_PAD_MAC06_EXCHANGE_EN BIT(31) + #define QCA8K_PORT0_PAD_SGMII_RXCLK_FALLING_EDGE BIT(19) + #define QCA8K_PORT0_PAD_SGMII_TXCLK_FALLING_EDGE BIT(18) + #define QCA8K_REG_PORT5_PAD_CTRL 0x008 diff --git a/pkgs/patches-linux-5.15/751-v5.16-net-dsa-qca8k-fix-internal-delay-applied-to-the-wrong-PAD.patch b/pkgs/patches-linux-5.15/751-v5.16-net-dsa-qca8k-fix-internal-delay-applied-to-the-wrong-PAD.patch new file mode 100644 index 0000000..df9518d --- /dev/null +++ b/pkgs/patches-linux-5.15/751-v5.16-net-dsa-qca8k-fix-internal-delay-applied-to-the-wrong-PAD.patch @@ -0,0 +1,48 @@ +From 3b00a07c2443745d62babfe08dbb2ad8e649526e Mon Sep 17 00:00:00 2001 +From: Ansuel Smith <ansuelsmth@gmail.com> +Date: Fri, 19 Nov 2021 03:03:49 +0100 +Subject: [PATCH] net: dsa: qca8k: fix internal delay applied to the wrong PAD + config + +With SGMII phy the internal delay is always applied to the PAD0 config. +This is caused by the falling edge configuration that hardcode the reg +to PAD0 (as the falling edge bits are present only in PAD0 reg) +Move the delay configuration before the reg overwrite to correctly apply +the delay. + +Fixes: cef08115846e ("net: dsa: qca8k: set internal delay also for sgmii") +Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com> +Reviewed-by: Vladimir Oltean <olteanv@gmail.com> +Signed-off-by: David S. Miller <davem@davemloft.net> +--- + drivers/net/dsa/qca8k.c | 12 ++++++------ + 1 file changed, 6 insertions(+), 6 deletions(-) + +--- a/drivers/net/dsa/qca8k.c ++++ b/drivers/net/dsa/qca8k.c +@@ -1433,6 +1433,12 @@ qca8k_phylink_mac_config(struct dsa_swit + + qca8k_write(priv, QCA8K_REG_SGMII_CTRL, val); + ++ /* From original code is reported port instability as SGMII also ++ * require delay set. Apply advised values here or take them from DT. ++ */ ++ if (state->interface == PHY_INTERFACE_MODE_SGMII) ++ qca8k_mac_config_setup_internal_delay(priv, cpu_port_index, reg); ++ + /* For qca8327/qca8328/qca8334/qca8338 sgmii is unique and + * falling edge is set writing in the PORT0 PAD reg + */ +@@ -1455,12 +1461,6 @@ qca8k_phylink_mac_config(struct dsa_swit + QCA8K_PORT0_PAD_SGMII_TXCLK_FALLING_EDGE, + val); + +- /* From original code is reported port instability as SGMII also +- * require delay set. Apply advised values here or take them from DT. +- */ +- if (state->interface == PHY_INTERFACE_MODE_SGMII) +- qca8k_mac_config_setup_internal_delay(priv, cpu_port_index, reg); +- + break; + default: + dev_err(ds->dev, "xMII mode %s not supported for port %d\n", diff --git a/pkgs/patches-linux-5.15/752-v5.16-net-dsa-qca8k-fix-MTU-calculation.patch b/pkgs/patches-linux-5.15/752-v5.16-net-dsa-qca8k-fix-MTU-calculation.patch new file mode 100644 index 0000000..7348d93 --- /dev/null +++ b/pkgs/patches-linux-5.15/752-v5.16-net-dsa-qca8k-fix-MTU-calculation.patch @@ -0,0 +1,46 @@ +From 65258b9d8cde45689bdc86ca39b50f01f983733b Mon Sep 17 00:00:00 2001 +From: Robert Marko <robert.marko@sartura.hr> +Date: Fri, 19 Nov 2021 03:03:50 +0100 +Subject: [PATCH] net: dsa: qca8k: fix MTU calculation + +qca8k has a global MTU, so its tracking the MTU per port to make sure +that the largest MTU gets applied. +Since it uses the frame size instead of MTU the driver MTU change function +will then add the size of Ethernet header and checksum on top of MTU. + +The driver currently populates the per port MTU size as Ethernet frame +length + checksum which equals 1518. + +The issue is that then MTU change function will go through all of the +ports, find the largest MTU and apply the Ethernet header + checksum on +top of it again, so for a desired MTU of 1500 you will end up with 1536. + +This is obviously incorrect, so to correct it populate the per port struct +MTU with just the MTU and not include the Ethernet header + checksum size +as those will be added by the MTU change function. + +Fixes: f58d2598cf70 ("net: dsa: qca8k: implement the port MTU callbacks") +Signed-off-by: Robert Marko <robert.marko@sartura.hr> +Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com> +Reviewed-by: Vladimir Oltean <olteanv@gmail.com> +Signed-off-by: David S. Miller <davem@davemloft.net> +--- + drivers/net/dsa/qca8k.c | 6 +++++- + 1 file changed, 5 insertions(+), 1 deletion(-) + +--- a/drivers/net/dsa/qca8k.c ++++ b/drivers/net/dsa/qca8k.c +@@ -1256,8 +1256,12 @@ qca8k_setup(struct dsa_switch *ds) + /* Set initial MTU for every port. + * We have only have a general MTU setting. So track + * every port and set the max across all port. ++ * Set per port MTU to 1500 as the MTU change function ++ * will add the overhead and if its set to 1518 then it ++ * will apply the overhead again and we will end up with ++ * MTU of 1536 instead of 1518 + */ +- priv->port_mtu[i] = ETH_FRAME_LEN + ETH_FCS_LEN; ++ priv->port_mtu[i] = ETH_DATA_LEN; + } + + /* Special GLOBAL_FC_THRESH value are needed for ar8327 switch */ diff --git a/pkgs/patches-linux-5.15/753-net-next-net-dsa-qca8k-remove-redundant-check-in-parse_port_config.patch b/pkgs/patches-linux-5.15/753-net-next-net-dsa-qca8k-remove-redundant-check-in-parse_port_config.patch new file mode 100644 index 0000000..f477b1b --- /dev/null +++ b/pkgs/patches-linux-5.15/753-net-next-net-dsa-qca8k-remove-redundant-check-in-parse_port_config.patch @@ -0,0 +1,29 @@ +From b9133f3ef5a2659730cf47a74bd0a9259f1cf8ff Mon Sep 17 00:00:00 2001 +From: Ansuel Smith <ansuelsmth@gmail.com> +Date: Mon, 22 Nov 2021 16:23:40 +0100 +Subject: net: dsa: qca8k: remove redundant check in parse_port_config + +The very next check for port 0 and 6 already makes sure we don't go out +of bounds with the ports_config delay table. +Remove the redundant check. + +Reported-by: kernel test robot <lkp@intel.com> +Reported-by: Dan Carpenter <dan.carpenter@oracle.com> +Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com> +Reviewed-by: Vladimir Oltean <olteanv@gmail.com> +Signed-off-by: David S. Miller <davem@davemloft.net> +--- + drivers/net/dsa/qca8k.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/drivers/net/dsa/qca8k.c ++++ b/drivers/net/dsa/qca8k.c +@@ -983,7 +983,7 @@ qca8k_parse_port_config(struct qca8k_pri + u32 delay; + + /* We have 2 CPU port. Check them */ +- for (port = 0; port < QCA8K_NUM_PORTS && cpu_port_index < QCA8K_NUM_CPU_PORTS; port++) { ++ for (port = 0; port < QCA8K_NUM_PORTS; port++) { + /* Skip every other port */ + if (port != 0 && port != 6) + continue; diff --git a/pkgs/patches-linux-5.15/754-net-next-net-dsa-qca8k-convert-to-GENMASK_FIELD_PREP_FIELD_GET.patch b/pkgs/patches-linux-5.15/754-net-next-net-dsa-qca8k-convert-to-GENMASK_FIELD_PREP_FIELD_GET.patch new file mode 100644 index 0000000..408a59d --- /dev/null +++ b/pkgs/patches-linux-5.15/754-net-next-net-dsa-qca8k-convert-to-GENMASK_FIELD_PREP_FIELD_GET.patch @@ -0,0 +1,507 @@ +From 90ae68bfc2ffcb54a4ba4f64edbeb84a80cbb57c Mon Sep 17 00:00:00 2001 +From: Ansuel Smith <ansuelsmth@gmail.com> +Date: Mon, 22 Nov 2021 16:23:41 +0100 +Subject: net: dsa: qca8k: convert to GENMASK/FIELD_PREP/FIELD_GET + +Convert and try to standardize bit fields using +GENMASK/FIELD_PREP/FIELD_GET macros. Rework some logic to support the +standard macro and tidy things up. No functional change intended. + +Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com> +Signed-off-by: David S. Miller <davem@davemloft.net> +--- + drivers/net/dsa/qca8k.c | 98 +++++++++++++++---------------- + drivers/net/dsa/qca8k.h | 153 ++++++++++++++++++++++++++---------------------- + 2 files changed, 130 insertions(+), 121 deletions(-) + +--- a/drivers/net/dsa/qca8k.c ++++ b/drivers/net/dsa/qca8k.c +@@ -9,6 +9,7 @@ + #include <linux/module.h> + #include <linux/phy.h> + #include <linux/netdevice.h> ++#include <linux/bitfield.h> + #include <net/dsa.h> + #include <linux/of_net.h> + #include <linux/of_mdio.h> +@@ -319,18 +320,18 @@ qca8k_fdb_read(struct qca8k_priv *priv, + } + + /* vid - 83:72 */ +- fdb->vid = (reg[2] >> QCA8K_ATU_VID_S) & QCA8K_ATU_VID_M; ++ fdb->vid = FIELD_GET(QCA8K_ATU_VID_MASK, reg[2]); + /* aging - 67:64 */ +- fdb->aging = reg[2] & QCA8K_ATU_STATUS_M; ++ fdb->aging = FIELD_GET(QCA8K_ATU_STATUS_MASK, reg[2]); + /* portmask - 54:48 */ +- fdb->port_mask = (reg[1] >> QCA8K_ATU_PORT_S) & QCA8K_ATU_PORT_M; ++ fdb->port_mask = FIELD_GET(QCA8K_ATU_PORT_MASK, reg[1]); + /* mac - 47:0 */ +- fdb->mac[0] = (reg[1] >> QCA8K_ATU_ADDR0_S) & 0xff; +- fdb->mac[1] = reg[1] & 0xff; +- fdb->mac[2] = (reg[0] >> QCA8K_ATU_ADDR2_S) & 0xff; +- fdb->mac[3] = (reg[0] >> QCA8K_ATU_ADDR3_S) & 0xff; +- fdb->mac[4] = (reg[0] >> QCA8K_ATU_ADDR4_S) & 0xff; +- fdb->mac[5] = reg[0] & 0xff; ++ fdb->mac[0] = FIELD_GET(QCA8K_ATU_ADDR0_MASK, reg[1]); ++ fdb->mac[1] = FIELD_GET(QCA8K_ATU_ADDR1_MASK, reg[1]); ++ fdb->mac[2] = FIELD_GET(QCA8K_ATU_ADDR2_MASK, reg[0]); ++ fdb->mac[3] = FIELD_GET(QCA8K_ATU_ADDR3_MASK, reg[0]); ++ fdb->mac[4] = FIELD_GET(QCA8K_ATU_ADDR4_MASK, reg[0]); ++ fdb->mac[5] = FIELD_GET(QCA8K_ATU_ADDR5_MASK, reg[0]); + + return 0; + } +@@ -343,18 +344,18 @@ qca8k_fdb_write(struct qca8k_priv *priv, + int i; + + /* vid - 83:72 */ +- reg[2] = (vid & QCA8K_ATU_VID_M) << QCA8K_ATU_VID_S; ++ reg[2] = FIELD_PREP(QCA8K_ATU_VID_MASK, vid); + /* aging - 67:64 */ +- reg[2] |= aging & QCA8K_ATU_STATUS_M; ++ reg[2] |= FIELD_PREP(QCA8K_ATU_STATUS_MASK, aging); + /* portmask - 54:48 */ +- reg[1] = (port_mask & QCA8K_ATU_PORT_M) << QCA8K_ATU_PORT_S; ++ reg[1] = FIELD_PREP(QCA8K_ATU_PORT_MASK, port_mask); + /* mac - 47:0 */ +- reg[1] |= mac[0] << QCA8K_ATU_ADDR0_S; +- reg[1] |= mac[1]; +- reg[0] |= mac[2] << QCA8K_ATU_ADDR2_S; +- reg[0] |= mac[3] << QCA8K_ATU_ADDR3_S; +- reg[0] |= mac[4] << QCA8K_ATU_ADDR4_S; +- reg[0] |= mac[5]; ++ reg[1] |= FIELD_PREP(QCA8K_ATU_ADDR0_MASK, mac[0]); ++ reg[1] |= FIELD_PREP(QCA8K_ATU_ADDR1_MASK, mac[1]); ++ reg[0] |= FIELD_PREP(QCA8K_ATU_ADDR2_MASK, mac[2]); ++ reg[0] |= FIELD_PREP(QCA8K_ATU_ADDR3_MASK, mac[3]); ++ reg[0] |= FIELD_PREP(QCA8K_ATU_ADDR4_MASK, mac[4]); ++ reg[0] |= FIELD_PREP(QCA8K_ATU_ADDR5_MASK, mac[5]); + + /* load the array into the ARL table */ + for (i = 0; i < 3; i++) +@@ -372,7 +373,7 @@ qca8k_fdb_access(struct qca8k_priv *priv + reg |= cmd; + if (port >= 0) { + reg |= QCA8K_ATU_FUNC_PORT_EN; +- reg |= (port & QCA8K_ATU_FUNC_PORT_M) << QCA8K_ATU_FUNC_PORT_S; ++ reg |= FIELD_PREP(QCA8K_ATU_FUNC_PORT_MASK, port); + } + + /* Write the function register triggering the table access */ +@@ -454,7 +455,7 @@ qca8k_vlan_access(struct qca8k_priv *pri + /* Set the command and VLAN index */ + reg = QCA8K_VTU_FUNC1_BUSY; + reg |= cmd; +- reg |= vid << QCA8K_VTU_FUNC1_VID_S; ++ reg |= FIELD_PREP(QCA8K_VTU_FUNC1_VID_MASK, vid); + + /* Write the function register triggering the table access */ + ret = qca8k_write(priv, QCA8K_REG_VTU_FUNC1, reg); +@@ -500,13 +501,11 @@ qca8k_vlan_add(struct qca8k_priv *priv, + if (ret < 0) + goto out; + reg |= QCA8K_VTU_FUNC0_VALID | QCA8K_VTU_FUNC0_IVL_EN; +- reg &= ~(QCA8K_VTU_FUNC0_EG_MODE_MASK << QCA8K_VTU_FUNC0_EG_MODE_S(port)); ++ reg &= ~QCA8K_VTU_FUNC0_EG_MODE_PORT_MASK(port); + if (untagged) +- reg |= QCA8K_VTU_FUNC0_EG_MODE_UNTAG << +- QCA8K_VTU_FUNC0_EG_MODE_S(port); ++ reg |= QCA8K_VTU_FUNC0_EG_MODE_PORT_UNTAG(port); + else +- reg |= QCA8K_VTU_FUNC0_EG_MODE_TAG << +- QCA8K_VTU_FUNC0_EG_MODE_S(port); ++ reg |= QCA8K_VTU_FUNC0_EG_MODE_PORT_TAG(port); + + ret = qca8k_write(priv, QCA8K_REG_VTU_FUNC0, reg); + if (ret) +@@ -534,15 +533,13 @@ qca8k_vlan_del(struct qca8k_priv *priv, + ret = qca8k_read(priv, QCA8K_REG_VTU_FUNC0, ®); + if (ret < 0) + goto out; +- reg &= ~(3 << QCA8K_VTU_FUNC0_EG_MODE_S(port)); +- reg |= QCA8K_VTU_FUNC0_EG_MODE_NOT << +- QCA8K_VTU_FUNC0_EG_MODE_S(port); ++ reg &= ~QCA8K_VTU_FUNC0_EG_MODE_PORT_MASK(port); ++ reg |= QCA8K_VTU_FUNC0_EG_MODE_PORT_NOT(port); + + /* Check if we're the last member to be removed */ + del = true; + for (i = 0; i < QCA8K_NUM_PORTS; i++) { +- mask = QCA8K_VTU_FUNC0_EG_MODE_NOT; +- mask <<= QCA8K_VTU_FUNC0_EG_MODE_S(i); ++ mask = QCA8K_VTU_FUNC0_EG_MODE_PORT_NOT(i); + + if ((reg & mask) != mask) { + del = false; +@@ -1014,7 +1011,7 @@ qca8k_parse_port_config(struct qca8k_pri + mode == PHY_INTERFACE_MODE_RGMII_TXID) + delay = 1; + +- if (delay > QCA8K_MAX_DELAY) { ++ if (!FIELD_FIT(QCA8K_PORT_PAD_RGMII_TX_DELAY_MASK, delay)) { + dev_err(priv->dev, "rgmii tx delay is limited to a max value of 3ns, setting to the max value"); + delay = 3; + } +@@ -1030,7 +1027,7 @@ qca8k_parse_port_config(struct qca8k_pri + mode == PHY_INTERFACE_MODE_RGMII_RXID) + delay = 2; + +- if (delay > QCA8K_MAX_DELAY) { ++ if (!FIELD_FIT(QCA8K_PORT_PAD_RGMII_RX_DELAY_MASK, delay)) { + dev_err(priv->dev, "rgmii rx delay is limited to a max value of 3ns, setting to the max value"); + delay = 3; + } +@@ -1141,8 +1138,8 @@ qca8k_setup(struct dsa_switch *ds) + /* Enable QCA header mode on all cpu ports */ + if (dsa_is_cpu_port(ds, i)) { + ret = qca8k_write(priv, QCA8K_REG_PORT_HDR_CTRL(i), +- QCA8K_PORT_HDR_CTRL_ALL << QCA8K_PORT_HDR_CTRL_TX_S | +- QCA8K_PORT_HDR_CTRL_ALL << QCA8K_PORT_HDR_CTRL_RX_S); ++ FIELD_PREP(QCA8K_PORT_HDR_CTRL_TX_MASK, QCA8K_PORT_HDR_CTRL_ALL) | ++ FIELD_PREP(QCA8K_PORT_HDR_CTRL_RX_MASK, QCA8K_PORT_HDR_CTRL_ALL)); + if (ret) { + dev_err(priv->dev, "failed enabling QCA header mode"); + return ret; +@@ -1159,10 +1156,10 @@ qca8k_setup(struct dsa_switch *ds) + * for igmp, unknown, multicast and broadcast packet + */ + ret = qca8k_write(priv, QCA8K_REG_GLOBAL_FW_CTRL1, +- BIT(cpu_port) << QCA8K_GLOBAL_FW_CTRL1_IGMP_DP_S | +- BIT(cpu_port) << QCA8K_GLOBAL_FW_CTRL1_BC_DP_S | +- BIT(cpu_port) << QCA8K_GLOBAL_FW_CTRL1_MC_DP_S | +- BIT(cpu_port) << QCA8K_GLOBAL_FW_CTRL1_UC_DP_S); ++ FIELD_PREP(QCA8K_GLOBAL_FW_CTRL1_IGMP_DP_MASK, BIT(cpu_port)) | ++ FIELD_PREP(QCA8K_GLOBAL_FW_CTRL1_BC_DP_MASK, BIT(cpu_port)) | ++ FIELD_PREP(QCA8K_GLOBAL_FW_CTRL1_MC_DP_MASK, BIT(cpu_port)) | ++ FIELD_PREP(QCA8K_GLOBAL_FW_CTRL1_UC_DP_MASK, BIT(cpu_port))); + if (ret) + return ret; + +@@ -1180,8 +1177,6 @@ qca8k_setup(struct dsa_switch *ds) + + /* Individual user ports get connected to CPU port only */ + if (dsa_is_user_port(ds, i)) { +- int shift = 16 * (i % 2); +- + ret = qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(i), + QCA8K_PORT_LOOKUP_MEMBER, + BIT(cpu_port)); +@@ -1198,8 +1193,8 @@ qca8k_setup(struct dsa_switch *ds) + * default egress vid + */ + ret = qca8k_rmw(priv, QCA8K_EGRESS_VLAN(i), +- 0xfff << shift, +- QCA8K_PORT_VID_DEF << shift); ++ QCA8K_EGREES_VLAN_PORT_MASK(i), ++ QCA8K_EGREES_VLAN_PORT(i, QCA8K_PORT_VID_DEF)); + if (ret) + return ret; + +@@ -1246,7 +1241,7 @@ qca8k_setup(struct dsa_switch *ds) + QCA8K_PORT_HOL_CTRL1_EG_PORT_BUF_EN | + QCA8K_PORT_HOL_CTRL1_WRED_EN; + qca8k_rmw(priv, QCA8K_REG_PORT_HOL_CTRL1(i), +- QCA8K_PORT_HOL_CTRL1_ING_BUF | ++ QCA8K_PORT_HOL_CTRL1_ING_BUF_MASK | + QCA8K_PORT_HOL_CTRL1_EG_PRI_BUF_EN | + QCA8K_PORT_HOL_CTRL1_EG_PORT_BUF_EN | + QCA8K_PORT_HOL_CTRL1_WRED_EN, +@@ -1269,8 +1264,8 @@ qca8k_setup(struct dsa_switch *ds) + mask = QCA8K_GLOBAL_FC_GOL_XON_THRES(288) | + QCA8K_GLOBAL_FC_GOL_XOFF_THRES(496); + qca8k_rmw(priv, QCA8K_REG_GLOBAL_FC_THRESH, +- QCA8K_GLOBAL_FC_GOL_XON_THRES_S | +- QCA8K_GLOBAL_FC_GOL_XOFF_THRES_S, ++ QCA8K_GLOBAL_FC_GOL_XON_THRES_MASK | ++ QCA8K_GLOBAL_FC_GOL_XOFF_THRES_MASK, + mask); + } + +@@ -1916,11 +1911,11 @@ qca8k_port_vlan_filtering(struct dsa_swi + + if (vlan_filtering) { + ret = qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(port), +- QCA8K_PORT_LOOKUP_VLAN_MODE, ++ QCA8K_PORT_LOOKUP_VLAN_MODE_MASK, + QCA8K_PORT_LOOKUP_VLAN_MODE_SECURE); + } else { + ret = qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(port), +- QCA8K_PORT_LOOKUP_VLAN_MODE, ++ QCA8K_PORT_LOOKUP_VLAN_MODE_MASK, + QCA8K_PORT_LOOKUP_VLAN_MODE_NONE); + } + +@@ -1944,10 +1939,9 @@ qca8k_port_vlan_add(struct dsa_switch *d + } + + if (pvid) { +- int shift = 16 * (port % 2); +- + ret = qca8k_rmw(priv, QCA8K_EGRESS_VLAN(port), +- 0xfff << shift, vlan->vid << shift); ++ QCA8K_EGREES_VLAN_PORT_MASK(port), ++ QCA8K_EGREES_VLAN_PORT(port, vlan->vid)); + if (ret) + return ret; + +@@ -2041,7 +2035,7 @@ static int qca8k_read_switch_id(struct q + if (ret < 0) + return -ENODEV; + +- id = QCA8K_MASK_CTRL_DEVICE_ID(val & QCA8K_MASK_CTRL_DEVICE_ID_MASK); ++ id = QCA8K_MASK_CTRL_DEVICE_ID(val); + if (id != data->id) { + dev_err(priv->dev, "Switch id detected %x but expected %x", id, data->id); + return -ENODEV; +@@ -2050,7 +2044,7 @@ static int qca8k_read_switch_id(struct q + priv->switch_id = id; + + /* Save revision to communicate to the internal PHY driver */ +- priv->switch_revision = (val & QCA8K_MASK_CTRL_REV_ID_MASK); ++ priv->switch_revision = QCA8K_MASK_CTRL_REV_ID(val); + + return 0; + } +--- a/drivers/net/dsa/qca8k.h ++++ b/drivers/net/dsa/qca8k.h +@@ -30,9 +30,9 @@ + /* Global control registers */ + #define QCA8K_REG_MASK_CTRL 0x000 + #define QCA8K_MASK_CTRL_REV_ID_MASK GENMASK(7, 0) +-#define QCA8K_MASK_CTRL_REV_ID(x) ((x) >> 0) ++#define QCA8K_MASK_CTRL_REV_ID(x) FIELD_GET(QCA8K_MASK_CTRL_REV_ID_MASK, x) + #define QCA8K_MASK_CTRL_DEVICE_ID_MASK GENMASK(15, 8) +-#define QCA8K_MASK_CTRL_DEVICE_ID(x) ((x) >> 8) ++#define QCA8K_MASK_CTRL_DEVICE_ID(x) FIELD_GET(QCA8K_MASK_CTRL_DEVICE_ID_MASK, x) + #define QCA8K_REG_PORT0_PAD_CTRL 0x004 + #define QCA8K_PORT0_PAD_MAC06_EXCHANGE_EN BIT(31) + #define QCA8K_PORT0_PAD_SGMII_RXCLK_FALLING_EDGE BIT(19) +@@ -41,12 +41,11 @@ + #define QCA8K_REG_PORT6_PAD_CTRL 0x00c + #define QCA8K_PORT_PAD_RGMII_EN BIT(26) + #define QCA8K_PORT_PAD_RGMII_TX_DELAY_MASK GENMASK(23, 22) +-#define QCA8K_PORT_PAD_RGMII_TX_DELAY(x) ((x) << 22) ++#define QCA8K_PORT_PAD_RGMII_TX_DELAY(x) FIELD_PREP(QCA8K_PORT_PAD_RGMII_TX_DELAY_MASK, x) + #define QCA8K_PORT_PAD_RGMII_RX_DELAY_MASK GENMASK(21, 20) +-#define QCA8K_PORT_PAD_RGMII_RX_DELAY(x) ((x) << 20) ++#define QCA8K_PORT_PAD_RGMII_RX_DELAY(x) FIELD_PREP(QCA8K_PORT_PAD_RGMII_RX_DELAY_MASK, x) + #define QCA8K_PORT_PAD_RGMII_TX_DELAY_EN BIT(25) + #define QCA8K_PORT_PAD_RGMII_RX_DELAY_EN BIT(24) +-#define QCA8K_MAX_DELAY 3 + #define QCA8K_PORT_PAD_SGMII_EN BIT(7) + #define QCA8K_REG_PWS 0x010 + #define QCA8K_PWS_POWER_ON_SEL BIT(31) +@@ -68,10 +67,12 @@ + #define QCA8K_MDIO_MASTER_READ BIT(27) + #define QCA8K_MDIO_MASTER_WRITE 0 + #define QCA8K_MDIO_MASTER_SUP_PRE BIT(26) +-#define QCA8K_MDIO_MASTER_PHY_ADDR(x) ((x) << 21) +-#define QCA8K_MDIO_MASTER_REG_ADDR(x) ((x) << 16) +-#define QCA8K_MDIO_MASTER_DATA(x) (x) ++#define QCA8K_MDIO_MASTER_PHY_ADDR_MASK GENMASK(25, 21) ++#define QCA8K_MDIO_MASTER_PHY_ADDR(x) FIELD_PREP(QCA8K_MDIO_MASTER_PHY_ADDR_MASK, x) ++#define QCA8K_MDIO_MASTER_REG_ADDR_MASK GENMASK(20, 16) ++#define QCA8K_MDIO_MASTER_REG_ADDR(x) FIELD_PREP(QCA8K_MDIO_MASTER_REG_ADDR_MASK, x) + #define QCA8K_MDIO_MASTER_DATA_MASK GENMASK(15, 0) ++#define QCA8K_MDIO_MASTER_DATA(x) FIELD_PREP(QCA8K_MDIO_MASTER_DATA_MASK, x) + #define QCA8K_MDIO_MASTER_MAX_PORTS 5 + #define QCA8K_MDIO_MASTER_MAX_REG 32 + #define QCA8K_GOL_MAC_ADDR0 0x60 +@@ -93,9 +94,7 @@ + #define QCA8K_PORT_STATUS_FLOW_AUTO BIT(12) + #define QCA8K_REG_PORT_HDR_CTRL(_i) (0x9c + (_i * 4)) + #define QCA8K_PORT_HDR_CTRL_RX_MASK GENMASK(3, 2) +-#define QCA8K_PORT_HDR_CTRL_RX_S 2 + #define QCA8K_PORT_HDR_CTRL_TX_MASK GENMASK(1, 0) +-#define QCA8K_PORT_HDR_CTRL_TX_S 0 + #define QCA8K_PORT_HDR_CTRL_ALL 2 + #define QCA8K_PORT_HDR_CTRL_MGMT 1 + #define QCA8K_PORT_HDR_CTRL_NONE 0 +@@ -105,10 +104,11 @@ + #define QCA8K_SGMII_EN_TX BIT(3) + #define QCA8K_SGMII_EN_SD BIT(4) + #define QCA8K_SGMII_CLK125M_DELAY BIT(7) +-#define QCA8K_SGMII_MODE_CTRL_MASK (BIT(22) | BIT(23)) +-#define QCA8K_SGMII_MODE_CTRL_BASEX (0 << 22) +-#define QCA8K_SGMII_MODE_CTRL_PHY (1 << 22) +-#define QCA8K_SGMII_MODE_CTRL_MAC (2 << 22) ++#define QCA8K_SGMII_MODE_CTRL_MASK GENMASK(23, 22) ++#define QCA8K_SGMII_MODE_CTRL(x) FIELD_PREP(QCA8K_SGMII_MODE_CTRL_MASK, x) ++#define QCA8K_SGMII_MODE_CTRL_BASEX QCA8K_SGMII_MODE_CTRL(0x0) ++#define QCA8K_SGMII_MODE_CTRL_PHY QCA8K_SGMII_MODE_CTRL(0x1) ++#define QCA8K_SGMII_MODE_CTRL_MAC QCA8K_SGMII_MODE_CTRL(0x2) + + /* MAC_PWR_SEL registers */ + #define QCA8K_REG_MAC_PWR_SEL 0x0e4 +@@ -121,100 +121,115 @@ + + /* ACL registers */ + #define QCA8K_REG_PORT_VLAN_CTRL0(_i) (0x420 + (_i * 8)) +-#define QCA8K_PORT_VLAN_CVID(x) (x << 16) +-#define QCA8K_PORT_VLAN_SVID(x) x ++#define QCA8K_PORT_VLAN_CVID_MASK GENMASK(27, 16) ++#define QCA8K_PORT_VLAN_CVID(x) FIELD_PREP(QCA8K_PORT_VLAN_CVID_MASK, x) ++#define QCA8K_PORT_VLAN_SVID_MASK GENMASK(11, 0) ++#define QCA8K_PORT_VLAN_SVID(x) FIELD_PREP(QCA8K_PORT_VLAN_SVID_MASK, x) + #define QCA8K_REG_PORT_VLAN_CTRL1(_i) (0x424 + (_i * 8)) + #define QCA8K_REG_IPV4_PRI_BASE_ADDR 0x470 + #define QCA8K_REG_IPV4_PRI_ADDR_MASK 0x474 + + /* Lookup registers */ + #define QCA8K_REG_ATU_DATA0 0x600 +-#define QCA8K_ATU_ADDR2_S 24 +-#define QCA8K_ATU_ADDR3_S 16 +-#define QCA8K_ATU_ADDR4_S 8 ++#define QCA8K_ATU_ADDR2_MASK GENMASK(31, 24) ++#define QCA8K_ATU_ADDR3_MASK GENMASK(23, 16) ++#define QCA8K_ATU_ADDR4_MASK GENMASK(15, 8) ++#define QCA8K_ATU_ADDR5_MASK GENMASK(7, 0) + #define QCA8K_REG_ATU_DATA1 0x604 +-#define QCA8K_ATU_PORT_M 0x7f +-#define QCA8K_ATU_PORT_S 16 +-#define QCA8K_ATU_ADDR0_S 8 ++#define QCA8K_ATU_PORT_MASK GENMASK(22, 16) ++#define QCA8K_ATU_ADDR0_MASK GENMASK(15, 8) ++#define QCA8K_ATU_ADDR1_MASK GENMASK(7, 0) + #define QCA8K_REG_ATU_DATA2 0x608 +-#define QCA8K_ATU_VID_M 0xfff +-#define QCA8K_ATU_VID_S 8 +-#define QCA8K_ATU_STATUS_M 0xf ++#define QCA8K_ATU_VID_MASK GENMASK(19, 8) ++#define QCA8K_ATU_STATUS_MASK GENMASK(3, 0) + #define QCA8K_ATU_STATUS_STATIC 0xf + #define QCA8K_REG_ATU_FUNC 0x60c + #define QCA8K_ATU_FUNC_BUSY BIT(31) + #define QCA8K_ATU_FUNC_PORT_EN BIT(14) + #define QCA8K_ATU_FUNC_MULTI_EN BIT(13) + #define QCA8K_ATU_FUNC_FULL BIT(12) +-#define QCA8K_ATU_FUNC_PORT_M 0xf +-#define QCA8K_ATU_FUNC_PORT_S 8 ++#define QCA8K_ATU_FUNC_PORT_MASK GENMASK(11, 8) + #define QCA8K_REG_VTU_FUNC0 0x610 + #define QCA8K_VTU_FUNC0_VALID BIT(20) + #define QCA8K_VTU_FUNC0_IVL_EN BIT(19) +-#define QCA8K_VTU_FUNC0_EG_MODE_S(_i) (4 + (_i) * 2) +-#define QCA8K_VTU_FUNC0_EG_MODE_MASK 3 +-#define QCA8K_VTU_FUNC0_EG_MODE_UNMOD 0 +-#define QCA8K_VTU_FUNC0_EG_MODE_UNTAG 1 +-#define QCA8K_VTU_FUNC0_EG_MODE_TAG 2 +-#define QCA8K_VTU_FUNC0_EG_MODE_NOT 3 ++/* QCA8K_VTU_FUNC0_EG_MODE_MASK GENMASK(17, 4) ++ * It does contain VLAN_MODE for each port [5:4] for port0, ++ * [7:6] for port1 ... [17:16] for port6. Use virtual port ++ * define to handle this. ++ */ ++#define QCA8K_VTU_FUNC0_EG_MODE_PORT_SHIFT(_i) (4 + (_i) * 2) ++#define QCA8K_VTU_FUNC0_EG_MODE_MASK GENMASK(1, 0) ++#define QCA8K_VTU_FUNC0_EG_MODE_PORT_MASK(_i) (GENMASK(1, 0) << QCA8K_VTU_FUNC0_EG_MODE_PORT_SHIFT(_i)) ++#define QCA8K_VTU_FUNC0_EG_MODE_UNMOD FIELD_PREP(QCA8K_VTU_FUNC0_EG_MODE_MASK, 0x0) ++#define QCA8K_VTU_FUNC0_EG_MODE_PORT_UNMOD(_i) (QCA8K_VTU_FUNC0_EG_MODE_UNMOD << QCA8K_VTU_FUNC0_EG_MODE_PORT_SHIFT(_i)) ++#define QCA8K_VTU_FUNC0_EG_MODE_UNTAG FIELD_PREP(QCA8K_VTU_FUNC0_EG_MODE_MASK, 0x1) ++#define QCA8K_VTU_FUNC0_EG_MODE_PORT_UNTAG(_i) (QCA8K_VTU_FUNC0_EG_MODE_UNTAG << QCA8K_VTU_FUNC0_EG_MODE_PORT_SHIFT(_i)) ++#define QCA8K_VTU_FUNC0_EG_MODE_TAG FIELD_PREP(QCA8K_VTU_FUNC0_EG_MODE_MASK, 0x2) ++#define QCA8K_VTU_FUNC0_EG_MODE_PORT_TAG(_i) (QCA8K_VTU_FUNC0_EG_MODE_TAG << QCA8K_VTU_FUNC0_EG_MODE_PORT_SHIFT(_i)) ++#define QCA8K_VTU_FUNC0_EG_MODE_NOT FIELD_PREP(QCA8K_VTU_FUNC0_EG_MODE_MASK, 0x3) ++#define QCA8K_VTU_FUNC0_EG_MODE_PORT_NOT(_i) (QCA8K_VTU_FUNC0_EG_MODE_NOT << QCA8K_VTU_FUNC0_EG_MODE_PORT_SHIFT(_i)) + #define QCA8K_REG_VTU_FUNC1 0x614 + #define QCA8K_VTU_FUNC1_BUSY BIT(31) +-#define QCA8K_VTU_FUNC1_VID_S 16 ++#define QCA8K_VTU_FUNC1_VID_MASK GENMASK(27, 16) + #define QCA8K_VTU_FUNC1_FULL BIT(4) + #define QCA8K_REG_GLOBAL_FW_CTRL0 0x620 + #define QCA8K_GLOBAL_FW_CTRL0_CPU_PORT_EN BIT(10) + #define QCA8K_REG_GLOBAL_FW_CTRL1 0x624 +-#define QCA8K_GLOBAL_FW_CTRL1_IGMP_DP_S 24 +-#define QCA8K_GLOBAL_FW_CTRL1_BC_DP_S 16 +-#define QCA8K_GLOBAL_FW_CTRL1_MC_DP_S 8 +-#define QCA8K_GLOBAL_FW_CTRL1_UC_DP_S 0 ++#define QCA8K_GLOBAL_FW_CTRL1_IGMP_DP_MASK GENMASK(30, 24) ++#define QCA8K_GLOBAL_FW_CTRL1_BC_DP_MASK GENMASK(22, 16) ++#define QCA8K_GLOBAL_FW_CTRL1_MC_DP_MASK GENMASK(14, 8) ++#define QCA8K_GLOBAL_FW_CTRL1_UC_DP_MASK GENMASK(6, 0) + #define QCA8K_PORT_LOOKUP_CTRL(_i) (0x660 + (_i) * 0xc) + #define QCA8K_PORT_LOOKUP_MEMBER GENMASK(6, 0) +-#define QCA8K_PORT_LOOKUP_VLAN_MODE GENMASK(9, 8) +-#define QCA8K_PORT_LOOKUP_VLAN_MODE_NONE (0 << 8) +-#define QCA8K_PORT_LOOKUP_VLAN_MODE_FALLBACK (1 << 8) +-#define QCA8K_PORT_LOOKUP_VLAN_MODE_CHECK (2 << 8) +-#define QCA8K_PORT_LOOKUP_VLAN_MODE_SECURE (3 << 8) ++#define QCA8K_PORT_LOOKUP_VLAN_MODE_MASK GENMASK(9, 8) ++#define QCA8K_PORT_LOOKUP_VLAN_MODE(x) FIELD_PREP(QCA8K_PORT_LOOKUP_VLAN_MODE_MASK, x) ++#define QCA8K_PORT_LOOKUP_VLAN_MODE_NONE QCA8K_PORT_LOOKUP_VLAN_MODE(0x0) ++#define QCA8K_PORT_LOOKUP_VLAN_MODE_FALLBACK QCA8K_PORT_LOOKUP_VLAN_MODE(0x1) ++#define QCA8K_PORT_LOOKUP_VLAN_MODE_CHECK QCA8K_PORT_LOOKUP_VLAN_MODE(0x2) ++#define QCA8K_PORT_LOOKUP_VLAN_MODE_SECURE QCA8K_PORT_LOOKUP_VLAN_MODE(0x3) + #define QCA8K_PORT_LOOKUP_STATE_MASK GENMASK(18, 16) +-#define QCA8K_PORT_LOOKUP_STATE_DISABLED (0 << 16) +-#define QCA8K_PORT_LOOKUP_STATE_BLOCKING (1 << 16) +-#define QCA8K_PORT_LOOKUP_STATE_LISTENING (2 << 16) +-#define QCA8K_PORT_LOOKUP_STATE_LEARNING (3 << 16) +-#define QCA8K_PORT_LOOKUP_STATE_FORWARD (4 << 16) +-#define QCA8K_PORT_LOOKUP_STATE GENMASK(18, 16) ++#define QCA8K_PORT_LOOKUP_STATE(x) FIELD_PREP(QCA8K_PORT_LOOKUP_STATE_MASK, x) ++#define QCA8K_PORT_LOOKUP_STATE_DISABLED QCA8K_PORT_LOOKUP_STATE(0x0) ++#define QCA8K_PORT_LOOKUP_STATE_BLOCKING QCA8K_PORT_LOOKUP_STATE(0x1) ++#define QCA8K_PORT_LOOKUP_STATE_LISTENING QCA8K_PORT_LOOKUP_STATE(0x2) ++#define QCA8K_PORT_LOOKUP_STATE_LEARNING QCA8K_PORT_LOOKUP_STATE(0x3) ++#define QCA8K_PORT_LOOKUP_STATE_FORWARD QCA8K_PORT_LOOKUP_STATE(0x4) + #define QCA8K_PORT_LOOKUP_LEARN BIT(20) + + #define QCA8K_REG_GLOBAL_FC_THRESH 0x800 +-#define QCA8K_GLOBAL_FC_GOL_XON_THRES(x) ((x) << 16) +-#define QCA8K_GLOBAL_FC_GOL_XON_THRES_S GENMASK(24, 16) +-#define QCA8K_GLOBAL_FC_GOL_XOFF_THRES(x) ((x) << 0) +-#define QCA8K_GLOBAL_FC_GOL_XOFF_THRES_S GENMASK(8, 0) ++#define QCA8K_GLOBAL_FC_GOL_XON_THRES_MASK GENMASK(24, 16) ++#define QCA8K_GLOBAL_FC_GOL_XON_THRES(x) FIELD_PREP(QCA8K_GLOBAL_FC_GOL_XON_THRES_MASK, x) ++#define QCA8K_GLOBAL_FC_GOL_XOFF_THRES_MASK GENMASK(8, 0) ++#define QCA8K_GLOBAL_FC_GOL_XOFF_THRES(x) FIELD_PREP(QCA8K_GLOBAL_FC_GOL_XOFF_THRES_MASK, x) + + #define QCA8K_REG_PORT_HOL_CTRL0(_i) (0x970 + (_i) * 0x8) +-#define QCA8K_PORT_HOL_CTRL0_EG_PRI0_BUF GENMASK(3, 0) +-#define QCA8K_PORT_HOL_CTRL0_EG_PRI0(x) ((x) << 0) +-#define QCA8K_PORT_HOL_CTRL0_EG_PRI1_BUF GENMASK(7, 4) +-#define QCA8K_PORT_HOL_CTRL0_EG_PRI1(x) ((x) << 4) +-#define QCA8K_PORT_HOL_CTRL0_EG_PRI2_BUF GENMASK(11, 8) +-#define QCA8K_PORT_HOL_CTRL0_EG_PRI2(x) ((x) << 8) +-#define QCA8K_PORT_HOL_CTRL0_EG_PRI3_BUF GENMASK(15, 12) +-#define QCA8K_PORT_HOL_CTRL0_EG_PRI3(x) ((x) << 12) +-#define QCA8K_PORT_HOL_CTRL0_EG_PRI4_BUF GENMASK(19, 16) +-#define QCA8K_PORT_HOL_CTRL0_EG_PRI4(x) ((x) << 16) +-#define QCA8K_PORT_HOL_CTRL0_EG_PRI5_BUF GENMASK(23, 20) +-#define QCA8K_PORT_HOL_CTRL0_EG_PRI5(x) ((x) << 20) +-#define QCA8K_PORT_HOL_CTRL0_EG_PORT_BUF GENMASK(29, 24) +-#define QCA8K_PORT_HOL_CTRL0_EG_PORT(x) ((x) << 24) ++#define QCA8K_PORT_HOL_CTRL0_EG_PRI0_BUF_MASK GENMASK(3, 0) ++#define QCA8K_PORT_HOL_CTRL0_EG_PRI0(x) FIELD_PREP(QCA8K_PORT_HOL_CTRL0_EG_PRI0_BUF_MASK, x) ++#define QCA8K_PORT_HOL_CTRL0_EG_PRI1_BUF_MASK GENMASK(7, 4) ++#define QCA8K_PORT_HOL_CTRL0_EG_PRI1(x) FIELD_PREP(QCA8K_PORT_HOL_CTRL0_EG_PRI1_BUF_MASK, x) ++#define QCA8K_PORT_HOL_CTRL0_EG_PRI2_BUF_MASK GENMASK(11, 8) ++#define QCA8K_PORT_HOL_CTRL0_EG_PRI2(x) FIELD_PREP(QCA8K_PORT_HOL_CTRL0_EG_PRI2_BUF_MASK, x) ++#define QCA8K_PORT_HOL_CTRL0_EG_PRI3_BUF_MASK GENMASK(15, 12) ++#define QCA8K_PORT_HOL_CTRL0_EG_PRI3(x) FIELD_PREP(QCA8K_PORT_HOL_CTRL0_EG_PRI3_BUF_MASK, x) ++#define QCA8K_PORT_HOL_CTRL0_EG_PRI4_BUF_MASK GENMASK(19, 16) ++#define QCA8K_PORT_HOL_CTRL0_EG_PRI4(x) FIELD_PREP(QCA8K_PORT_HOL_CTRL0_EG_PRI4_BUF_MASK, x) ++#define QCA8K_PORT_HOL_CTRL0_EG_PRI5_BUF_MASK GENMASK(23, 20) ++#define QCA8K_PORT_HOL_CTRL0_EG_PRI5(x) FIELD_PREP(QCA8K_PORT_HOL_CTRL0_EG_PRI5_BUF_MASK, x) ++#define QCA8K_PORT_HOL_CTRL0_EG_PORT_BUF_MASK GENMASK(29, 24) ++#define QCA8K_PORT_HOL_CTRL0_EG_PORT(x) FIELD_PREP(QCA8K_PORT_HOL_CTRL0_EG_PORT_BUF_MASK, x) + + #define QCA8K_REG_PORT_HOL_CTRL1(_i) (0x974 + (_i) * 0x8) +-#define QCA8K_PORT_HOL_CTRL1_ING_BUF GENMASK(3, 0) +-#define QCA8K_PORT_HOL_CTRL1_ING(x) ((x) << 0) ++#define QCA8K_PORT_HOL_CTRL1_ING_BUF_MASK GENMASK(3, 0) ++#define QCA8K_PORT_HOL_CTRL1_ING(x) FIELD_PREP(QCA8K_PORT_HOL_CTRL1_ING_BUF_MASK, x) + #define QCA8K_PORT_HOL_CTRL1_EG_PRI_BUF_EN BIT(6) + #define QCA8K_PORT_HOL_CTRL1_EG_PORT_BUF_EN BIT(7) + #define QCA8K_PORT_HOL_CTRL1_WRED_EN BIT(8) + #define QCA8K_PORT_HOL_CTRL1_EG_MIRROR_EN BIT(16) + + /* Pkt edit registers */ ++#define QCA8K_EGREES_VLAN_PORT_SHIFT(_i) (16 * ((_i) % 2)) ++#define QCA8K_EGREES_VLAN_PORT_MASK(_i) (GENMASK(11, 0) << QCA8K_EGREES_VLAN_PORT_SHIFT(_i)) ++#define QCA8K_EGREES_VLAN_PORT(_i, x) ((x) << QCA8K_EGREES_VLAN_PORT_SHIFT(_i)) + #define QCA8K_EGRESS_VLAN(x) (0x0c70 + (4 * (x / 2))) + + /* L3 registers */ diff --git a/pkgs/patches-linux-5.15/755-net-next-net-dsa-qca8k-remove-extra-mutex_init-in-qca8k_setup.patch b/pkgs/patches-linux-5.15/755-net-next-net-dsa-qca8k-remove-extra-mutex_init-in-qca8k_setup.patch new file mode 100644 index 0000000..8c39b8e --- /dev/null +++ b/pkgs/patches-linux-5.15/755-net-next-net-dsa-qca8k-remove-extra-mutex_init-in-qca8k_setup.patch @@ -0,0 +1,25 @@ +From 994c28b6f971fa5db8ae977daea37eee87d93d51 Mon Sep 17 00:00:00 2001 +From: Ansuel Smith <ansuelsmth@gmail.com> +Date: Mon, 22 Nov 2021 16:23:42 +0100 +Subject: net: dsa: qca8k: remove extra mutex_init in qca8k_setup + +Mutex is already init in sw_probe. Remove the extra init in qca8k_setup. + +Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com> +Reviewed-by: Vladimir Oltean <olteanv@gmail.com> +Signed-off-by: David S. Miller <davem@davemloft.net> +--- + drivers/net/dsa/qca8k.c | 2 -- + 1 file changed, 2 deletions(-) + +--- a/drivers/net/dsa/qca8k.c ++++ b/drivers/net/dsa/qca8k.c +@@ -1086,8 +1086,6 @@ qca8k_setup(struct dsa_switch *ds) + if (ret) + return ret; + +- mutex_init(&priv->reg_mutex); +- + /* Start by setting up the register mapping */ + priv->regmap = devm_regmap_init(ds->dev, NULL, priv, + &qca8k_regmap_config); diff --git a/pkgs/patches-linux-5.15/756-net-next-net-dsa-qca8k-move-regmap-init-in-probe-and-set-it.patch b/pkgs/patches-linux-5.15/756-net-next-net-dsa-qca8k-move-regmap-init-in-probe-and-set-it.patch new file mode 100644 index 0000000..f873b70 --- /dev/null +++ b/pkgs/patches-linux-5.15/756-net-next-net-dsa-qca8k-move-regmap-init-in-probe-and-set-it.patch @@ -0,0 +1,46 @@ +From 36b8af12f424e7a7f60a935c60a0fd4aa0822378 Mon Sep 17 00:00:00 2001 +From: Ansuel Smith <ansuelsmth@gmail.com> +Date: Mon, 22 Nov 2021 16:23:43 +0100 +Subject: net: dsa: qca8k: move regmap init in probe and set it mandatory + +In preparation for regmap conversion, move regmap init in the probe +function and make it mandatory as any read/write/rmw operation will be +converted to regmap API. + +Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com> +Reviewed-by: Vladimir Oltean <olteanv@gmail.com> +Signed-off-by: David S. Miller <davem@davemloft.net> +--- + drivers/net/dsa/qca8k.c | 14 ++++++++------ + 1 file changed, 8 insertions(+), 6 deletions(-) + +--- a/drivers/net/dsa/qca8k.c ++++ b/drivers/net/dsa/qca8k.c +@@ -1086,12 +1086,6 @@ qca8k_setup(struct dsa_switch *ds) + if (ret) + return ret; + +- /* Start by setting up the register mapping */ +- priv->regmap = devm_regmap_init(ds->dev, NULL, priv, +- &qca8k_regmap_config); +- if (IS_ERR(priv->regmap)) +- dev_warn(priv->dev, "regmap initialization failed"); +- + ret = qca8k_setup_mdio_bus(priv); + if (ret) + return ret; +@@ -2077,6 +2071,14 @@ qca8k_sw_probe(struct mdio_device *mdiod + gpiod_set_value_cansleep(priv->reset_gpio, 0); + } + ++ /* Start by setting up the register mapping */ ++ priv->regmap = devm_regmap_init(&mdiodev->dev, NULL, priv, ++ &qca8k_regmap_config); ++ if (IS_ERR(priv->regmap)) { ++ dev_err(priv->dev, "regmap initialization failed"); ++ return PTR_ERR(priv->regmap); ++ } ++ + /* Check the detected switch id */ + ret = qca8k_read_switch_id(priv); + if (ret) diff --git a/pkgs/patches-linux-5.15/757-net-next-net-dsa-qca8k-initial-conversion-to-regmap-heper.patch b/pkgs/patches-linux-5.15/757-net-next-net-dsa-qca8k-initial-conversion-to-regmap-heper.patch new file mode 100644 index 0000000..4ca9c8b --- /dev/null +++ b/pkgs/patches-linux-5.15/757-net-next-net-dsa-qca8k-initial-conversion-to-regmap-heper.patch @@ -0,0 +1,249 @@ +From 8b5f3f29a81a71934d004e21a1292c1148b05926 Mon Sep 17 00:00:00 2001 +From: Ansuel Smith <ansuelsmth@gmail.com> +Date: Mon, 22 Nov 2021 16:23:44 +0100 +Subject: net: dsa: qca8k: initial conversion to regmap helper + +Convert any qca8k set/clear/pool to regmap helper and add +missing config to regmap_config struct. +Read/write/rmw operation are reworked to use the regmap helper +internally to keep the delta of this patch low. These additional +function will then be dropped when the code split will be proposed. + +Ipq40xx SoC have the internal switch based on the qca8k regmap but use +mmio for read/write/rmw operation instead of mdio. +In preparation for the support of this internal switch, convert the +driver to regmap API to later split the driver to common and specific +code. The overhead introduced by the use of regamp API is marginal as the +internal mdio will bypass it by using its direct access and regmap will be +used only by configuration functions or fdb access. + +Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com> +Signed-off-by: David S. Miller <davem@davemloft.net> +--- + drivers/net/dsa/qca8k.c | 107 +++++++++++++++++++++--------------------------- + 1 file changed, 47 insertions(+), 60 deletions(-) + +--- a/drivers/net/dsa/qca8k.c ++++ b/drivers/net/dsa/qca8k.c +@@ -10,6 +10,7 @@ + #include <linux/phy.h> + #include <linux/netdevice.h> + #include <linux/bitfield.h> ++#include <linux/regmap.h> + #include <net/dsa.h> + #include <linux/of_net.h> + #include <linux/of_mdio.h> +@@ -152,6 +153,25 @@ qca8k_set_page(struct mii_bus *bus, u16 + static int + qca8k_read(struct qca8k_priv *priv, u32 reg, u32 *val) + { ++ return regmap_read(priv->regmap, reg, val); ++} ++ ++static int ++qca8k_write(struct qca8k_priv *priv, u32 reg, u32 val) ++{ ++ return regmap_write(priv->regmap, reg, val); ++} ++ ++static int ++qca8k_rmw(struct qca8k_priv *priv, u32 reg, u32 mask, u32 write_val) ++{ ++ return regmap_update_bits(priv->regmap, reg, mask, write_val); ++} ++ ++static int ++qca8k_regmap_read(void *ctx, uint32_t reg, uint32_t *val) ++{ ++ struct qca8k_priv *priv = (struct qca8k_priv *)ctx; + struct mii_bus *bus = priv->bus; + u16 r1, r2, page; + int ret; +@@ -172,8 +192,9 @@ exit: + } + + static int +-qca8k_write(struct qca8k_priv *priv, u32 reg, u32 val) ++qca8k_regmap_write(void *ctx, uint32_t reg, uint32_t val) + { ++ struct qca8k_priv *priv = (struct qca8k_priv *)ctx; + struct mii_bus *bus = priv->bus; + u16 r1, r2, page; + int ret; +@@ -194,8 +215,9 @@ exit: + } + + static int +-qca8k_rmw(struct qca8k_priv *priv, u32 reg, u32 mask, u32 write_val) ++qca8k_regmap_update_bits(void *ctx, uint32_t reg, uint32_t mask, uint32_t write_val) + { ++ struct qca8k_priv *priv = (struct qca8k_priv *)ctx; + struct mii_bus *bus = priv->bus; + u16 r1, r2, page; + u32 val; +@@ -223,34 +245,6 @@ exit: + return ret; + } + +-static int +-qca8k_reg_set(struct qca8k_priv *priv, u32 reg, u32 val) +-{ +- return qca8k_rmw(priv, reg, 0, val); +-} +- +-static int +-qca8k_reg_clear(struct qca8k_priv *priv, u32 reg, u32 val) +-{ +- return qca8k_rmw(priv, reg, val, 0); +-} +- +-static int +-qca8k_regmap_read(void *ctx, uint32_t reg, uint32_t *val) +-{ +- struct qca8k_priv *priv = (struct qca8k_priv *)ctx; +- +- return qca8k_read(priv, reg, val); +-} +- +-static int +-qca8k_regmap_write(void *ctx, uint32_t reg, uint32_t val) +-{ +- struct qca8k_priv *priv = (struct qca8k_priv *)ctx; +- +- return qca8k_write(priv, reg, val); +-} +- + static const struct regmap_range qca8k_readable_ranges[] = { + regmap_reg_range(0x0000, 0x00e4), /* Global control */ + regmap_reg_range(0x0100, 0x0168), /* EEE control */ +@@ -282,26 +276,19 @@ static struct regmap_config qca8k_regmap + .max_register = 0x16ac, /* end MIB - Port6 range */ + .reg_read = qca8k_regmap_read, + .reg_write = qca8k_regmap_write, ++ .reg_update_bits = qca8k_regmap_update_bits, + .rd_table = &qca8k_readable_table, ++ .disable_locking = true, /* Locking is handled by qca8k read/write */ ++ .cache_type = REGCACHE_NONE, /* Explicitly disable CACHE */ + }; + + static int + qca8k_busy_wait(struct qca8k_priv *priv, u32 reg, u32 mask) + { +- int ret, ret1; + u32 val; + +- ret = read_poll_timeout(qca8k_read, ret1, !(val & mask), +- 0, QCA8K_BUSY_WAIT_TIMEOUT * USEC_PER_MSEC, false, +- priv, reg, &val); +- +- /* Check if qca8k_read has failed for a different reason +- * before returning -ETIMEDOUT +- */ +- if (ret < 0 && ret1 < 0) +- return ret1; +- +- return ret; ++ return regmap_read_poll_timeout(priv->regmap, reg, val, !(val & mask), 0, ++ QCA8K_BUSY_WAIT_TIMEOUT * USEC_PER_MSEC); + } + + static int +@@ -568,7 +555,7 @@ qca8k_mib_init(struct qca8k_priv *priv) + int ret; + + mutex_lock(&priv->reg_mutex); +- ret = qca8k_reg_set(priv, QCA8K_REG_MIB, QCA8K_MIB_FLUSH | QCA8K_MIB_BUSY); ++ ret = regmap_set_bits(priv->regmap, QCA8K_REG_MIB, QCA8K_MIB_FLUSH | QCA8K_MIB_BUSY); + if (ret) + goto exit; + +@@ -576,7 +563,7 @@ qca8k_mib_init(struct qca8k_priv *priv) + if (ret) + goto exit; + +- ret = qca8k_reg_set(priv, QCA8K_REG_MIB, QCA8K_MIB_CPU_KEEP); ++ ret = regmap_set_bits(priv->regmap, QCA8K_REG_MIB, QCA8K_MIB_CPU_KEEP); + if (ret) + goto exit; + +@@ -597,9 +584,9 @@ qca8k_port_set_status(struct qca8k_priv + mask |= QCA8K_PORT_STATUS_LINK_AUTO; + + if (enable) +- qca8k_reg_set(priv, QCA8K_REG_PORT_STATUS(port), mask); ++ regmap_set_bits(priv->regmap, QCA8K_REG_PORT_STATUS(port), mask); + else +- qca8k_reg_clear(priv, QCA8K_REG_PORT_STATUS(port), mask); ++ regmap_clear_bits(priv->regmap, QCA8K_REG_PORT_STATUS(port), mask); + } + + static u32 +@@ -861,8 +848,8 @@ qca8k_setup_mdio_bus(struct qca8k_priv * + * a dt-overlay and driver reload changed the configuration + */ + +- return qca8k_reg_clear(priv, QCA8K_MDIO_MASTER_CTRL, +- QCA8K_MDIO_MASTER_EN); ++ return regmap_clear_bits(priv->regmap, QCA8K_MDIO_MASTER_CTRL, ++ QCA8K_MDIO_MASTER_EN); + } + + /* Check if the devicetree declare the port:phy mapping */ +@@ -1099,16 +1086,16 @@ qca8k_setup(struct dsa_switch *ds) + return ret; + + /* Make sure MAC06 is disabled */ +- ret = qca8k_reg_clear(priv, QCA8K_REG_PORT0_PAD_CTRL, +- QCA8K_PORT0_PAD_MAC06_EXCHANGE_EN); ++ ret = regmap_clear_bits(priv->regmap, QCA8K_REG_PORT0_PAD_CTRL, ++ QCA8K_PORT0_PAD_MAC06_EXCHANGE_EN); + if (ret) { + dev_err(priv->dev, "failed disabling MAC06 exchange"); + return ret; + } + + /* Enable CPU Port */ +- ret = qca8k_reg_set(priv, QCA8K_REG_GLOBAL_FW_CTRL0, +- QCA8K_GLOBAL_FW_CTRL0_CPU_PORT_EN); ++ ret = regmap_set_bits(priv->regmap, QCA8K_REG_GLOBAL_FW_CTRL0, ++ QCA8K_GLOBAL_FW_CTRL0_CPU_PORT_EN); + if (ret) { + dev_err(priv->dev, "failed enabling CPU port"); + return ret; +@@ -1176,8 +1163,8 @@ qca8k_setup(struct dsa_switch *ds) + return ret; + + /* Enable ARP Auto-learning by default */ +- ret = qca8k_reg_set(priv, QCA8K_PORT_LOOKUP_CTRL(i), +- QCA8K_PORT_LOOKUP_LEARN); ++ ret = regmap_set_bits(priv->regmap, QCA8K_PORT_LOOKUP_CTRL(i), ++ QCA8K_PORT_LOOKUP_LEARN); + if (ret) + return ret; + +@@ -1745,9 +1732,9 @@ qca8k_port_bridge_join(struct dsa_switch + /* Add this port to the portvlan mask of the other ports + * in the bridge + */ +- ret = qca8k_reg_set(priv, +- QCA8K_PORT_LOOKUP_CTRL(i), +- BIT(port)); ++ ret = regmap_set_bits(priv->regmap, ++ QCA8K_PORT_LOOKUP_CTRL(i), ++ BIT(port)); + if (ret) + return ret; + if (i != port) +@@ -1777,9 +1764,9 @@ qca8k_port_bridge_leave(struct dsa_switc + /* Remove this port to the portvlan mask of the other ports + * in the bridge + */ +- qca8k_reg_clear(priv, +- QCA8K_PORT_LOOKUP_CTRL(i), +- BIT(port)); ++ regmap_clear_bits(priv->regmap, ++ QCA8K_PORT_LOOKUP_CTRL(i), ++ BIT(port)); + } + + /* Set the cpu port to be the only one in the portvlan mask of diff --git a/pkgs/patches-linux-5.15/758-net-next-net-dsa-qca8k-add-additional-MIB-counter-and-.patch b/pkgs/patches-linux-5.15/758-net-next-net-dsa-qca8k-add-additional-MIB-counter-and-.patch new file mode 100644 index 0000000..1465d1f --- /dev/null +++ b/pkgs/patches-linux-5.15/758-net-next-net-dsa-qca8k-add-additional-MIB-counter-and-.patch @@ -0,0 +1,120 @@ +From c126f118b330ccf0db0dda4a4bd6c729865a205f Mon Sep 17 00:00:00 2001 +From: Ansuel Smith <ansuelsmth@gmail.com> +Date: Mon, 22 Nov 2021 16:23:45 +0100 +Subject: net: dsa: qca8k: add additional MIB counter and make it dynamic + +We are currently missing 2 additionals MIB counter present in QCA833x +switch. +QC832x switch have 39 MIB counter and QCA833X have 41 MIB counter. +Add the additional MIB counter and rework the MIB function to print the +correct supported counter from the match_data struct. + +Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com> +Reviewed-by: Vladimir Oltean <olteanv@gmail.com> +Signed-off-by: David S. Miller <davem@davemloft.net> +--- + drivers/net/dsa/qca8k.c | 23 ++++++++++++++++++++--- + drivers/net/dsa/qca8k.h | 4 ++++ + 2 files changed, 24 insertions(+), 3 deletions(-) + +--- a/drivers/net/dsa/qca8k.c ++++ b/drivers/net/dsa/qca8k.c +@@ -70,6 +70,8 @@ static const struct qca8k_mib_desc ar832 + MIB_DESC(1, 0x9c, "TxExcDefer"), + MIB_DESC(1, 0xa0, "TxDefer"), + MIB_DESC(1, 0xa4, "TxLateCol"), ++ MIB_DESC(1, 0xa8, "RXUnicast"), ++ MIB_DESC(1, 0xac, "TXUnicast"), + }; + + /* The 32bit switch registers are accessed indirectly. To achieve this we need +@@ -1605,12 +1607,16 @@ qca8k_phylink_mac_link_up(struct dsa_swi + static void + qca8k_get_strings(struct dsa_switch *ds, int port, u32 stringset, uint8_t *data) + { ++ const struct qca8k_match_data *match_data; ++ struct qca8k_priv *priv = ds->priv; + int i; + + if (stringset != ETH_SS_STATS) + return; + +- for (i = 0; i < ARRAY_SIZE(ar8327_mib); i++) ++ match_data = of_device_get_match_data(priv->dev); ++ ++ for (i = 0; i < match_data->mib_count; i++) + strncpy(data + i * ETH_GSTRING_LEN, ar8327_mib[i].name, + ETH_GSTRING_LEN); + } +@@ -1620,12 +1626,15 @@ qca8k_get_ethtool_stats(struct dsa_switc + uint64_t *data) + { + struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv; ++ const struct qca8k_match_data *match_data; + const struct qca8k_mib_desc *mib; + u32 reg, i, val; + u32 hi = 0; + int ret; + +- for (i = 0; i < ARRAY_SIZE(ar8327_mib); i++) { ++ match_data = of_device_get_match_data(priv->dev); ++ ++ for (i = 0; i < match_data->mib_count; i++) { + mib = &ar8327_mib[i]; + reg = QCA8K_PORT_MIB_COUNTER(port) + mib->offset; + +@@ -1648,10 +1657,15 @@ qca8k_get_ethtool_stats(struct dsa_switc + static int + qca8k_get_sset_count(struct dsa_switch *ds, int port, int sset) + { ++ const struct qca8k_match_data *match_data; ++ struct qca8k_priv *priv = ds->priv; ++ + if (sset != ETH_SS_STATS) + return 0; + +- return ARRAY_SIZE(ar8327_mib); ++ match_data = of_device_get_match_data(priv->dev); ++ ++ return match_data->mib_count; + } + + static int +@@ -2154,14 +2168,17 @@ static SIMPLE_DEV_PM_OPS(qca8k_pm_ops, + static const struct qca8k_match_data qca8327 = { + .id = QCA8K_ID_QCA8327, + .reduced_package = true, ++ .mib_count = QCA8K_QCA832X_MIB_COUNT, + }; + + static const struct qca8k_match_data qca8328 = { + .id = QCA8K_ID_QCA8327, ++ .mib_count = QCA8K_QCA832X_MIB_COUNT, + }; + + static const struct qca8k_match_data qca833x = { + .id = QCA8K_ID_QCA8337, ++ .mib_count = QCA8K_QCA833X_MIB_COUNT, + }; + + static const struct of_device_id qca8k_of_match[] = { +--- a/drivers/net/dsa/qca8k.h ++++ b/drivers/net/dsa/qca8k.h +@@ -21,6 +21,9 @@ + #define PHY_ID_QCA8337 0x004dd036 + #define QCA8K_ID_QCA8337 0x13 + ++#define QCA8K_QCA832X_MIB_COUNT 39 ++#define QCA8K_QCA833X_MIB_COUNT 41 ++ + #define QCA8K_BUSY_WAIT_TIMEOUT 2000 + + #define QCA8K_NUM_FDB_RECORDS 2048 +@@ -279,6 +282,7 @@ struct ar8xxx_port_status { + struct qca8k_match_data { + u8 id; + bool reduced_package; ++ u8 mib_count; + }; + + enum { diff --git a/pkgs/patches-linux-5.15/759-net-next-net-dsa-qca8k-add-support-for-port-fast-aging.patch b/pkgs/patches-linux-5.15/759-net-next-net-dsa-qca8k-add-support-for-port-fast-aging.patch new file mode 100644 index 0000000..973446e --- /dev/null +++ b/pkgs/patches-linux-5.15/759-net-next-net-dsa-qca8k-add-support-for-port-fast-aging.patch @@ -0,0 +1,53 @@ +From 4592538bfb0d5d3c3c8a1d7071724d081412ac91 Mon Sep 17 00:00:00 2001 +From: Ansuel Smith <ansuelsmth@gmail.com> +Date: Mon, 22 Nov 2021 16:23:46 +0100 +Subject: net: dsa: qca8k: add support for port fast aging + +The switch supports fast aging by flushing any rule in the ARL +table for a specific port. + +Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com> +Reviewed-by: Vladimir Oltean <olteanv@gmail.com> +Signed-off-by: David S. Miller <davem@davemloft.net> +--- + drivers/net/dsa/qca8k.c | 11 +++++++++++ + drivers/net/dsa/qca8k.h | 1 + + 2 files changed, 12 insertions(+) + +--- a/drivers/net/dsa/qca8k.c ++++ b/drivers/net/dsa/qca8k.c +@@ -1790,6 +1790,16 @@ qca8k_port_bridge_leave(struct dsa_switc + QCA8K_PORT_LOOKUP_MEMBER, BIT(cpu_port)); + } + ++static void ++qca8k_port_fast_age(struct dsa_switch *ds, int port) ++{ ++ struct qca8k_priv *priv = ds->priv; ++ ++ mutex_lock(&priv->reg_mutex); ++ qca8k_fdb_access(priv, QCA8K_FDB_FLUSH_PORT, port); ++ mutex_unlock(&priv->reg_mutex); ++} ++ + static int + qca8k_port_enable(struct dsa_switch *ds, int port, + struct phy_device *phy) +@@ -1998,6 +2008,7 @@ static const struct dsa_switch_ops qca8k + .port_stp_state_set = qca8k_port_stp_state_set, + .port_bridge_join = qca8k_port_bridge_join, + .port_bridge_leave = qca8k_port_bridge_leave, ++ .port_fast_age = qca8k_port_fast_age, + .port_fdb_add = qca8k_port_fdb_add, + .port_fdb_del = qca8k_port_fdb_del, + .port_fdb_dump = qca8k_port_fdb_dump, +--- a/drivers/net/dsa/qca8k.h ++++ b/drivers/net/dsa/qca8k.h +@@ -262,6 +262,7 @@ enum qca8k_fdb_cmd { + QCA8K_FDB_FLUSH = 1, + QCA8K_FDB_LOAD = 2, + QCA8K_FDB_PURGE = 3, ++ QCA8K_FDB_FLUSH_PORT = 5, + QCA8K_FDB_NEXT = 6, + QCA8K_FDB_SEARCH = 7, + }; diff --git a/pkgs/patches-linux-5.15/760-net-next-net-dsa-qca8k-add-set_ageing_time-support.patch b/pkgs/patches-linux-5.15/760-net-next-net-dsa-qca8k-add-set_ageing_time-support.patch new file mode 100644 index 0000000..2953006 --- /dev/null +++ b/pkgs/patches-linux-5.15/760-net-next-net-dsa-qca8k-add-set_ageing_time-support.patch @@ -0,0 +1,78 @@ +From 6a3bdc5209f45d2af83aa92433ab6e5cf2297aa4 Mon Sep 17 00:00:00 2001 +From: Ansuel Smith <ansuelsmth@gmail.com> +Date: Mon, 22 Nov 2021 16:23:47 +0100 +Subject: net: dsa: qca8k: add set_ageing_time support + +qca8k support setting ageing time in step of 7s. Add support for it and +set the max value accepted of 7645m. +Documentation talks about support for 10000m but that values doesn't +make sense as the value doesn't match the max value in the reg. + +Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com> +Reviewed-by: Vladimir Oltean <olteanv@gmail.com> +Signed-off-by: David S. Miller <davem@davemloft.net> +--- + drivers/net/dsa/qca8k.c | 25 +++++++++++++++++++++++++ + drivers/net/dsa/qca8k.h | 3 +++ + 2 files changed, 28 insertions(+) + +--- a/drivers/net/dsa/qca8k.c ++++ b/drivers/net/dsa/qca8k.c +@@ -1261,6 +1261,10 @@ qca8k_setup(struct dsa_switch *ds) + /* We don't have interrupts for link changes, so we need to poll */ + ds->pcs_poll = true; + ++ /* Set min a max ageing value supported */ ++ ds->ageing_time_min = 7000; ++ ds->ageing_time_max = 458745000; ++ + return 0; + } + +@@ -1801,6 +1805,26 @@ qca8k_port_fast_age(struct dsa_switch *d + } + + static int ++qca8k_set_ageing_time(struct dsa_switch *ds, unsigned int msecs) ++{ ++ struct qca8k_priv *priv = ds->priv; ++ unsigned int secs = msecs / 1000; ++ u32 val; ++ ++ /* AGE_TIME reg is set in 7s step */ ++ val = secs / 7; ++ ++ /* Handle case with 0 as val to NOT disable ++ * learning ++ */ ++ if (!val) ++ val = 1; ++ ++ return regmap_update_bits(priv->regmap, QCA8K_REG_ATU_CTRL, QCA8K_ATU_AGE_TIME_MASK, ++ QCA8K_ATU_AGE_TIME(val)); ++} ++ ++static int + qca8k_port_enable(struct dsa_switch *ds, int port, + struct phy_device *phy) + { +@@ -1999,6 +2023,7 @@ static const struct dsa_switch_ops qca8k + .get_strings = qca8k_get_strings, + .get_ethtool_stats = qca8k_get_ethtool_stats, + .get_sset_count = qca8k_get_sset_count, ++ .set_ageing_time = qca8k_set_ageing_time, + .get_mac_eee = qca8k_get_mac_eee, + .set_mac_eee = qca8k_set_mac_eee, + .port_enable = qca8k_port_enable, +--- a/drivers/net/dsa/qca8k.h ++++ b/drivers/net/dsa/qca8k.h +@@ -175,6 +175,9 @@ + #define QCA8K_VTU_FUNC1_BUSY BIT(31) + #define QCA8K_VTU_FUNC1_VID_MASK GENMASK(27, 16) + #define QCA8K_VTU_FUNC1_FULL BIT(4) ++#define QCA8K_REG_ATU_CTRL 0x618 ++#define QCA8K_ATU_AGE_TIME_MASK GENMASK(15, 0) ++#define QCA8K_ATU_AGE_TIME(x) FIELD_PREP(QCA8K_ATU_AGE_TIME_MASK, (x)) + #define QCA8K_REG_GLOBAL_FW_CTRL0 0x620 + #define QCA8K_GLOBAL_FW_CTRL0_CPU_PORT_EN BIT(10) + #define QCA8K_REG_GLOBAL_FW_CTRL1 0x624 diff --git a/pkgs/patches-linux-5.15/760-net-usb-r8152-add-LED-configuration-from-OF.patch b/pkgs/patches-linux-5.15/760-net-usb-r8152-add-LED-configuration-from-OF.patch new file mode 100644 index 0000000..c54332f --- /dev/null +++ b/pkgs/patches-linux-5.15/760-net-usb-r8152-add-LED-configuration-from-OF.patch @@ -0,0 +1,74 @@ +From 82985725e071f2a5735052f18e109a32aeac3a0b Mon Sep 17 00:00:00 2001 +From: David Bauer <mail@david-bauer.net> +Date: Sun, 26 Jul 2020 02:38:31 +0200 +Subject: [PATCH] net: usb: r8152: add LED configuration from OF + +This adds the ability to configure the LED configuration register using +OF. This way, the correct value for board specific LED configuration can +be determined. + +Signed-off-by: David Bauer <mail@david-bauer.net> +--- + drivers/net/usb/r8152.c | 23 +++++++++++++++++++++++ + 1 file changed, 23 insertions(+) + +--- a/drivers/net/usb/r8152.c ++++ b/drivers/net/usb/r8152.c +@@ -11,6 +11,7 @@ + #include <linux/mii.h> + #include <linux/ethtool.h> + #include <linux/usb.h> ++#include <linux/of.h> + #include <linux/crc32.h> + #include <linux/if_vlan.h> + #include <linux/uaccess.h> +@@ -6861,6 +6862,22 @@ static void rtl_tally_reset(struct r8152 + ocp_write_word(tp, MCU_TYPE_PLA, PLA_RSTTALLY, ocp_data); + } + ++static int r8152_led_configuration(struct r8152 *tp) ++{ ++ u32 led_data; ++ int ret; ++ ++ ret = of_property_read_u32(tp->udev->dev.of_node, "realtek,led-data", ++ &led_data); ++ ++ if (ret) ++ return ret; ++ ++ ocp_write_word(tp, MCU_TYPE_PLA, PLA_LEDSEL, led_data); ++ ++ return 0; ++} ++ + static void r8152b_init(struct r8152 *tp) + { + u32 ocp_data; +@@ -6902,6 +6919,8 @@ static void r8152b_init(struct r8152 *tp + ocp_data = ocp_read_word(tp, MCU_TYPE_USB, USB_USB_CTRL); + ocp_data &= ~(RX_AGG_DISABLE | RX_ZERO_EN); + ocp_write_word(tp, MCU_TYPE_USB, USB_USB_CTRL, ocp_data); ++ ++ r8152_led_configuration(tp); + } + + static void r8153_init(struct r8152 *tp) +@@ -7042,6 +7061,8 @@ static void r8153_init(struct r8152 *tp) + tp->coalesce = COALESCE_SLOW; + break; + } ++ ++ r8152_led_configuration(tp); + } + + static void r8153b_init(struct r8152 *tp) +@@ -7124,6 +7145,8 @@ static void r8153b_init(struct r8152 *tp + rtl_tally_reset(tp); + + tp->coalesce = 15000; /* 15 us */ ++ ++ r8152_led_configuration(tp); + } + + static void r8153c_init(struct r8152 *tp) diff --git a/pkgs/patches-linux-5.15/761-dt-bindings-net-add-RTL8152-binding-documentation.patch b/pkgs/patches-linux-5.15/761-dt-bindings-net-add-RTL8152-binding-documentation.patch new file mode 100644 index 0000000..be262b9 --- /dev/null +++ b/pkgs/patches-linux-5.15/761-dt-bindings-net-add-RTL8152-binding-documentation.patch @@ -0,0 +1,54 @@ +From 3ee05f4aa64fc86af3be5bc176ba5808de9260a7 Mon Sep 17 00:00:00 2001 +From: David Bauer <mail@david-bauer.net> +Date: Sun, 26 Jul 2020 15:30:33 +0200 +Subject: [PATCH] dt-bindings: net: add RTL8152 binding documentation + +Add binding documentation for the Realtek RTL8152 / RTL8153 USB ethernet +adapters. + +Signed-off-by: David Bauer <mail@david-bauer.net> +--- + .../bindings/net/realtek,rtl8152.yaml | 36 +++++++++++++++++++ + 1 file changed, 36 insertions(+) + create mode 100644 Documentation/devicetree/bindings/net/realtek,rtl8152.yaml + +--- /dev/null ++++ b/Documentation/devicetree/bindings/net/realtek,rtl8152.yaml +@@ -0,0 +1,36 @@ ++# SPDX-License-Identifier: GPL-2.0 ++%YAML 1.2 ++--- ++$id: http://devicetree.org/schemas/net/realtek,rtl8152.yaml# ++$schema: http://devicetree.org/meta-schemas/core.yaml# ++ ++title: Realtek RTL8152/RTL8153 series USB ethernet ++ ++maintainers: ++ - David Bauer <mail@david-bauer.net> ++ ++properties: ++ compatible: ++ oneOf: ++ - items: ++ - enum: ++ - realtek,rtl8152 ++ - realtek,rtl8153 ++ ++ reg: ++ description: The device number on the USB bus ++ ++ realtek,led-data: ++ description: Value to be written to the LED configuration register. ++ ++required: ++ - compatible ++ - reg ++ ++examples: ++ - | ++ usb-eth@2 { ++ compatible = "realtek,rtl8153"; ++ reg = <2>; ++ realtek,led-data = <0x87>; ++ }; +\ No newline at end of file diff --git a/pkgs/patches-linux-5.15/761-net-next-net-dsa-qca8k-add-support-for-mdb_add-del.patch b/pkgs/patches-linux-5.15/761-net-next-net-dsa-qca8k-add-support-for-mdb_add-del.patch new file mode 100644 index 0000000..fa022d7 --- /dev/null +++ b/pkgs/patches-linux-5.15/761-net-next-net-dsa-qca8k-add-support-for-mdb_add-del.patch @@ -0,0 +1,142 @@ +From ba8f870dfa635113ce6e8095a5eb1835ecde2e9e Mon Sep 17 00:00:00 2001 +From: Ansuel Smith <ansuelsmth@gmail.com> +Date: Mon, 22 Nov 2021 16:23:48 +0100 +Subject: net: dsa: qca8k: add support for mdb_add/del + +Add support for mdb add/del function. The ARL table is used to insert +the rule. The rule will be searched, deleted and reinserted with the +port mask updated. The function will check if the rule has to be updated +or insert directly with no deletion of the old rule. +If every port is removed from the port mask, the rule is removed. +The rule is set STATIC in the ARL table (aka it doesn't age) to not be +flushed by fast age function. + +Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com> +Reviewed-by: Vladimir Oltean <olteanv@gmail.com> +Signed-off-by: David S. Miller <davem@davemloft.net> +--- + drivers/net/dsa/qca8k.c | 99 +++++++++++++++++++++++++++++++++++++++++++++++++ + 1 file changed, 99 insertions(+) + +--- a/drivers/net/dsa/qca8k.c ++++ b/drivers/net/dsa/qca8k.c +@@ -436,6 +436,81 @@ qca8k_fdb_flush(struct qca8k_priv *priv) + } + + static int ++qca8k_fdb_search_and_insert(struct qca8k_priv *priv, u8 port_mask, ++ const u8 *mac, u16 vid) ++{ ++ struct qca8k_fdb fdb = { 0 }; ++ int ret; ++ ++ mutex_lock(&priv->reg_mutex); ++ ++ qca8k_fdb_write(priv, vid, 0, mac, 0); ++ ret = qca8k_fdb_access(priv, QCA8K_FDB_SEARCH, -1); ++ if (ret < 0) ++ goto exit; ++ ++ ret = qca8k_fdb_read(priv, &fdb); ++ if (ret < 0) ++ goto exit; ++ ++ /* Rule exist. Delete first */ ++ if (!fdb.aging) { ++ ret = qca8k_fdb_access(priv, QCA8K_FDB_PURGE, -1); ++ if (ret) ++ goto exit; ++ } ++ ++ /* Add port to fdb portmask */ ++ fdb.port_mask |= port_mask; ++ ++ qca8k_fdb_write(priv, vid, fdb.port_mask, mac, fdb.aging); ++ ret = qca8k_fdb_access(priv, QCA8K_FDB_LOAD, -1); ++ ++exit: ++ mutex_unlock(&priv->reg_mutex); ++ return ret; ++} ++ ++static int ++qca8k_fdb_search_and_del(struct qca8k_priv *priv, u8 port_mask, ++ const u8 *mac, u16 vid) ++{ ++ struct qca8k_fdb fdb = { 0 }; ++ int ret; ++ ++ mutex_lock(&priv->reg_mutex); ++ ++ qca8k_fdb_write(priv, vid, 0, mac, 0); ++ ret = qca8k_fdb_access(priv, QCA8K_FDB_SEARCH, -1); ++ if (ret < 0) ++ goto exit; ++ ++ /* Rule doesn't exist. Why delete? */ ++ if (!fdb.aging) { ++ ret = -EINVAL; ++ goto exit; ++ } ++ ++ ret = qca8k_fdb_access(priv, QCA8K_FDB_PURGE, -1); ++ if (ret) ++ goto exit; ++ ++ /* Only port in the rule is this port. Don't re insert */ ++ if (fdb.port_mask == port_mask) ++ goto exit; ++ ++ /* Remove port from port mask */ ++ fdb.port_mask &= ~port_mask; ++ ++ qca8k_fdb_write(priv, vid, fdb.port_mask, mac, fdb.aging); ++ ret = qca8k_fdb_access(priv, QCA8K_FDB_LOAD, -1); ++ ++exit: ++ mutex_unlock(&priv->reg_mutex); ++ return ret; ++} ++ ++static int + qca8k_vlan_access(struct qca8k_priv *priv, enum qca8k_vlan_cmd cmd, u16 vid) + { + u32 reg; +@@ -1930,6 +2005,28 @@ qca8k_port_fdb_dump(struct dsa_switch *d + } + + static int ++qca8k_port_mdb_add(struct dsa_switch *ds, int port, ++ const struct switchdev_obj_port_mdb *mdb) ++{ ++ struct qca8k_priv *priv = ds->priv; ++ const u8 *addr = mdb->addr; ++ u16 vid = mdb->vid; ++ ++ return qca8k_fdb_search_and_insert(priv, BIT(port), addr, vid); ++} ++ ++static int ++qca8k_port_mdb_del(struct dsa_switch *ds, int port, ++ const struct switchdev_obj_port_mdb *mdb) ++{ ++ struct qca8k_priv *priv = ds->priv; ++ const u8 *addr = mdb->addr; ++ u16 vid = mdb->vid; ++ ++ return qca8k_fdb_search_and_del(priv, BIT(port), addr, vid); ++} ++ ++static int + qca8k_port_vlan_filtering(struct dsa_switch *ds, int port, bool vlan_filtering, + struct netlink_ext_ack *extack) + { +@@ -2037,6 +2134,8 @@ static const struct dsa_switch_ops qca8k + .port_fdb_add = qca8k_port_fdb_add, + .port_fdb_del = qca8k_port_fdb_del, + .port_fdb_dump = qca8k_port_fdb_dump, ++ .port_mdb_add = qca8k_port_mdb_add, ++ .port_mdb_del = qca8k_port_mdb_del, + .port_vlan_filtering = qca8k_port_vlan_filtering, + .port_vlan_add = qca8k_port_vlan_add, + .port_vlan_del = qca8k_port_vlan_del, diff --git a/pkgs/patches-linux-5.15/762-net-next-net-dsa-qca8k-add-support-for-mirror-mode.patch b/pkgs/patches-linux-5.15/762-net-next-net-dsa-qca8k-add-support-for-mirror-mode.patch new file mode 100644 index 0000000..69e9b38 --- /dev/null +++ b/pkgs/patches-linux-5.15/762-net-next-net-dsa-qca8k-add-support-for-mirror-mode.patch @@ -0,0 +1,155 @@ +From 2c1bdbc7e7560d7de754cad277d968d56bb1899e Mon Sep 17 00:00:00 2001 +From: Ansuel Smith <ansuelsmth@gmail.com> +Date: Tue, 23 Nov 2021 03:59:10 +0100 +Subject: net: dsa: qca8k: add support for mirror mode + +The switch supports mirror mode. Only one port can set as mirror port and +every other port can set to both ingress and egress mode. The mirror +port is disabled and reverted to normal operation once every port is +removed from sending packet to it. + +Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com> +Signed-off-by: David S. Miller <davem@davemloft.net> +--- + drivers/net/dsa/qca8k.c | 95 +++++++++++++++++++++++++++++++++++++++++++++++++ + drivers/net/dsa/qca8k.h | 4 +++ + 2 files changed, 99 insertions(+) + +--- a/drivers/net/dsa/qca8k.c ++++ b/drivers/net/dsa/qca8k.c +@@ -2027,6 +2027,99 @@ qca8k_port_mdb_del(struct dsa_switch *ds + } + + static int ++qca8k_port_mirror_add(struct dsa_switch *ds, int port, ++ struct dsa_mall_mirror_tc_entry *mirror, ++ bool ingress) ++{ ++ struct qca8k_priv *priv = ds->priv; ++ int monitor_port, ret; ++ u32 reg, val; ++ ++ /* Check for existent entry */ ++ if ((ingress ? priv->mirror_rx : priv->mirror_tx) & BIT(port)) ++ return -EEXIST; ++ ++ ret = regmap_read(priv->regmap, QCA8K_REG_GLOBAL_FW_CTRL0, &val); ++ if (ret) ++ return ret; ++ ++ /* QCA83xx can have only one port set to mirror mode. ++ * Check that the correct port is requested and return error otherwise. ++ * When no mirror port is set, the values is set to 0xF ++ */ ++ monitor_port = FIELD_GET(QCA8K_GLOBAL_FW_CTRL0_MIRROR_PORT_NUM, val); ++ if (monitor_port != 0xF && monitor_port != mirror->to_local_port) ++ return -EEXIST; ++ ++ /* Set the monitor port */ ++ val = FIELD_PREP(QCA8K_GLOBAL_FW_CTRL0_MIRROR_PORT_NUM, ++ mirror->to_local_port); ++ ret = regmap_update_bits(priv->regmap, QCA8K_REG_GLOBAL_FW_CTRL0, ++ QCA8K_GLOBAL_FW_CTRL0_MIRROR_PORT_NUM, val); ++ if (ret) ++ return ret; ++ ++ if (ingress) { ++ reg = QCA8K_PORT_LOOKUP_CTRL(port); ++ val = QCA8K_PORT_LOOKUP_ING_MIRROR_EN; ++ } else { ++ reg = QCA8K_REG_PORT_HOL_CTRL1(port); ++ val = QCA8K_PORT_HOL_CTRL1_EG_MIRROR_EN; ++ } ++ ++ ret = regmap_update_bits(priv->regmap, reg, val, val); ++ if (ret) ++ return ret; ++ ++ /* Track mirror port for tx and rx to decide when the ++ * mirror port has to be disabled. ++ */ ++ if (ingress) ++ priv->mirror_rx |= BIT(port); ++ else ++ priv->mirror_tx |= BIT(port); ++ ++ return 0; ++} ++ ++static void ++qca8k_port_mirror_del(struct dsa_switch *ds, int port, ++ struct dsa_mall_mirror_tc_entry *mirror) ++{ ++ struct qca8k_priv *priv = ds->priv; ++ u32 reg, val; ++ int ret; ++ ++ if (mirror->ingress) { ++ reg = QCA8K_PORT_LOOKUP_CTRL(port); ++ val = QCA8K_PORT_LOOKUP_ING_MIRROR_EN; ++ } else { ++ reg = QCA8K_REG_PORT_HOL_CTRL1(port); ++ val = QCA8K_PORT_HOL_CTRL1_EG_MIRROR_EN; ++ } ++ ++ ret = regmap_clear_bits(priv->regmap, reg, val); ++ if (ret) ++ goto err; ++ ++ if (mirror->ingress) ++ priv->mirror_rx &= ~BIT(port); ++ else ++ priv->mirror_tx &= ~BIT(port); ++ ++ /* No port set to send packet to mirror port. Disable mirror port */ ++ if (!priv->mirror_rx && !priv->mirror_tx) { ++ val = FIELD_PREP(QCA8K_GLOBAL_FW_CTRL0_MIRROR_PORT_NUM, 0xF); ++ ret = regmap_update_bits(priv->regmap, QCA8K_REG_GLOBAL_FW_CTRL0, ++ QCA8K_GLOBAL_FW_CTRL0_MIRROR_PORT_NUM, val); ++ if (ret) ++ goto err; ++ } ++err: ++ dev_err(priv->dev, "Failed to del mirror port from %d", port); ++} ++ ++static int + qca8k_port_vlan_filtering(struct dsa_switch *ds, int port, bool vlan_filtering, + struct netlink_ext_ack *extack) + { +@@ -2136,6 +2229,8 @@ static const struct dsa_switch_ops qca8k + .port_fdb_dump = qca8k_port_fdb_dump, + .port_mdb_add = qca8k_port_mdb_add, + .port_mdb_del = qca8k_port_mdb_del, ++ .port_mirror_add = qca8k_port_mirror_add, ++ .port_mirror_del = qca8k_port_mirror_del, + .port_vlan_filtering = qca8k_port_vlan_filtering, + .port_vlan_add = qca8k_port_vlan_add, + .port_vlan_del = qca8k_port_vlan_del, +--- a/drivers/net/dsa/qca8k.h ++++ b/drivers/net/dsa/qca8k.h +@@ -180,6 +180,7 @@ + #define QCA8K_ATU_AGE_TIME(x) FIELD_PREP(QCA8K_ATU_AGE_TIME_MASK, (x)) + #define QCA8K_REG_GLOBAL_FW_CTRL0 0x620 + #define QCA8K_GLOBAL_FW_CTRL0_CPU_PORT_EN BIT(10) ++#define QCA8K_GLOBAL_FW_CTRL0_MIRROR_PORT_NUM GENMASK(7, 4) + #define QCA8K_REG_GLOBAL_FW_CTRL1 0x624 + #define QCA8K_GLOBAL_FW_CTRL1_IGMP_DP_MASK GENMASK(30, 24) + #define QCA8K_GLOBAL_FW_CTRL1_BC_DP_MASK GENMASK(22, 16) +@@ -201,6 +202,7 @@ + #define QCA8K_PORT_LOOKUP_STATE_LEARNING QCA8K_PORT_LOOKUP_STATE(0x3) + #define QCA8K_PORT_LOOKUP_STATE_FORWARD QCA8K_PORT_LOOKUP_STATE(0x4) + #define QCA8K_PORT_LOOKUP_LEARN BIT(20) ++#define QCA8K_PORT_LOOKUP_ING_MIRROR_EN BIT(25) + + #define QCA8K_REG_GLOBAL_FC_THRESH 0x800 + #define QCA8K_GLOBAL_FC_GOL_XON_THRES_MASK GENMASK(24, 16) +@@ -305,6 +307,8 @@ struct qca8k_ports_config { + struct qca8k_priv { + u8 switch_id; + u8 switch_revision; ++ u8 mirror_rx; ++ u8 mirror_tx; + bool legacy_phy_port_mapping; + struct qca8k_ports_config ports_config; + struct regmap *regmap; diff --git a/pkgs/patches-linux-5.15/763-net-next-net-dsa-qca8k-add-LAG-support.patch b/pkgs/patches-linux-5.15/763-net-next-net-dsa-qca8k-add-LAG-support.patch new file mode 100644 index 0000000..bfc77db --- /dev/null +++ b/pkgs/patches-linux-5.15/763-net-next-net-dsa-qca8k-add-LAG-support.patch @@ -0,0 +1,288 @@ +From def975307c01191b6f0170048c3724b0ed3348af Mon Sep 17 00:00:00 2001 +From: Ansuel Smith <ansuelsmth@gmail.com> +Date: Tue, 23 Nov 2021 03:59:11 +0100 +Subject: net: dsa: qca8k: add LAG support + +Add LAG support to this switch. In Documentation this is described as +trunk mode. A max of 4 LAGs are supported and each can support up to 4 +port. The current tx mode supported is Hash mode with both L2 and L2+3 +mode. +When no port are present in the trunk, the trunk is disabled in the +switch. +When a port is disconnected, the traffic is redirected to the other +available port. +The hash mode is global and each LAG require to have the same hash mode +set. To change the hash mode when multiple LAG are configured, it's +required to remove each LAG and set the desired hash mode to the last. +An error is printed when it's asked to set a not supported hadh mode. + +Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com> +Signed-off-by: David S. Miller <davem@davemloft.net> +--- + drivers/net/dsa/qca8k.c | 177 ++++++++++++++++++++++++++++++++++++++++++++++++ + drivers/net/dsa/qca8k.h | 33 +++++++++ + 2 files changed, 210 insertions(+) + +--- a/drivers/net/dsa/qca8k.c ++++ b/drivers/net/dsa/qca8k.c +@@ -1340,6 +1340,9 @@ qca8k_setup(struct dsa_switch *ds) + ds->ageing_time_min = 7000; + ds->ageing_time_max = 458745000; + ++ /* Set max number of LAGs supported */ ++ ds->num_lag_ids = QCA8K_NUM_LAGS; ++ + return 0; + } + +@@ -2207,6 +2210,178 @@ qca8k_get_tag_protocol(struct dsa_switch + return DSA_TAG_PROTO_QCA; + } + ++static bool ++qca8k_lag_can_offload(struct dsa_switch *ds, ++ struct net_device *lag, ++ struct netdev_lag_upper_info *info) ++{ ++ struct dsa_port *dp; ++ int id, members = 0; ++ ++ id = dsa_lag_id(ds->dst, lag); ++ if (id < 0 || id >= ds->num_lag_ids) ++ return false; ++ ++ dsa_lag_foreach_port(dp, ds->dst, lag) ++ /* Includes the port joining the LAG */ ++ members++; ++ ++ if (members > QCA8K_NUM_PORTS_FOR_LAG) ++ return false; ++ ++ if (info->tx_type != NETDEV_LAG_TX_TYPE_HASH) ++ return false; ++ ++ if (info->hash_type != NETDEV_LAG_HASH_L2 || ++ info->hash_type != NETDEV_LAG_HASH_L23) ++ return false; ++ ++ return true; ++} ++ ++static int ++qca8k_lag_setup_hash(struct dsa_switch *ds, ++ struct net_device *lag, ++ struct netdev_lag_upper_info *info) ++{ ++ struct qca8k_priv *priv = ds->priv; ++ bool unique_lag = true; ++ int i, id; ++ u32 hash; ++ ++ id = dsa_lag_id(ds->dst, lag); ++ ++ switch (info->hash_type) { ++ case NETDEV_LAG_HASH_L23: ++ hash |= QCA8K_TRUNK_HASH_SIP_EN; ++ hash |= QCA8K_TRUNK_HASH_DIP_EN; ++ fallthrough; ++ case NETDEV_LAG_HASH_L2: ++ hash |= QCA8K_TRUNK_HASH_SA_EN; ++ hash |= QCA8K_TRUNK_HASH_DA_EN; ++ break; ++ default: /* We should NEVER reach this */ ++ return -EOPNOTSUPP; ++ } ++ ++ /* Check if we are the unique configured LAG */ ++ dsa_lags_foreach_id(i, ds->dst) ++ if (i != id && dsa_lag_dev(ds->dst, i)) { ++ unique_lag = false; ++ break; ++ } ++ ++ /* Hash Mode is global. Make sure the same Hash Mode ++ * is set to all the 4 possible lag. ++ * If we are the unique LAG we can set whatever hash ++ * mode we want. ++ * To change hash mode it's needed to remove all LAG ++ * and change the mode with the latest. ++ */ ++ if (unique_lag) { ++ priv->lag_hash_mode = hash; ++ } else if (priv->lag_hash_mode != hash) { ++ netdev_err(lag, "Error: Mismateched Hash Mode across different lag is not supported\n"); ++ return -EOPNOTSUPP; ++ } ++ ++ return regmap_update_bits(priv->regmap, QCA8K_TRUNK_HASH_EN_CTRL, ++ QCA8K_TRUNK_HASH_MASK, hash); ++} ++ ++static int ++qca8k_lag_refresh_portmap(struct dsa_switch *ds, int port, ++ struct net_device *lag, bool delete) ++{ ++ struct qca8k_priv *priv = ds->priv; ++ int ret, id, i; ++ u32 val; ++ ++ id = dsa_lag_id(ds->dst, lag); ++ ++ /* Read current port member */ ++ ret = regmap_read(priv->regmap, QCA8K_REG_GOL_TRUNK_CTRL0, &val); ++ if (ret) ++ return ret; ++ ++ /* Shift val to the correct trunk */ ++ val >>= QCA8K_REG_GOL_TRUNK_SHIFT(id); ++ val &= QCA8K_REG_GOL_TRUNK_MEMBER_MASK; ++ if (delete) ++ val &= ~BIT(port); ++ else ++ val |= BIT(port); ++ ++ /* Update port member. With empty portmap disable trunk */ ++ ret = regmap_update_bits(priv->regmap, QCA8K_REG_GOL_TRUNK_CTRL0, ++ QCA8K_REG_GOL_TRUNK_MEMBER(id) | ++ QCA8K_REG_GOL_TRUNK_EN(id), ++ !val << QCA8K_REG_GOL_TRUNK_SHIFT(id) | ++ val << QCA8K_REG_GOL_TRUNK_SHIFT(id)); ++ ++ /* Search empty member if adding or port on deleting */ ++ for (i = 0; i < QCA8K_NUM_PORTS_FOR_LAG; i++) { ++ ret = regmap_read(priv->regmap, QCA8K_REG_GOL_TRUNK_CTRL(id), &val); ++ if (ret) ++ return ret; ++ ++ val >>= QCA8K_REG_GOL_TRUNK_ID_MEM_ID_SHIFT(id, i); ++ val &= QCA8K_REG_GOL_TRUNK_ID_MEM_ID_MASK; ++ ++ if (delete) { ++ /* If port flagged to be disabled assume this member is ++ * empty ++ */ ++ if (val != QCA8K_REG_GOL_TRUNK_ID_MEM_ID_EN_MASK) ++ continue; ++ ++ val &= QCA8K_REG_GOL_TRUNK_ID_MEM_ID_PORT_MASK; ++ if (val != port) ++ continue; ++ } else { ++ /* If port flagged to be enabled assume this member is ++ * already set ++ */ ++ if (val == QCA8K_REG_GOL_TRUNK_ID_MEM_ID_EN_MASK) ++ continue; ++ } ++ ++ /* We have found the member to add/remove */ ++ break; ++ } ++ ++ /* Set port in the correct port mask or disable port if in delete mode */ ++ return regmap_update_bits(priv->regmap, QCA8K_REG_GOL_TRUNK_CTRL(id), ++ QCA8K_REG_GOL_TRUNK_ID_MEM_ID_EN(id, i) | ++ QCA8K_REG_GOL_TRUNK_ID_MEM_ID_PORT(id, i), ++ !delete << QCA8K_REG_GOL_TRUNK_ID_MEM_ID_SHIFT(id, i) | ++ port << QCA8K_REG_GOL_TRUNK_ID_MEM_ID_SHIFT(id, i)); ++} ++ ++static int ++qca8k_port_lag_join(struct dsa_switch *ds, int port, ++ struct net_device *lag, ++ struct netdev_lag_upper_info *info) ++{ ++ int ret; ++ ++ if (!qca8k_lag_can_offload(ds, lag, info)) ++ return -EOPNOTSUPP; ++ ++ ret = qca8k_lag_setup_hash(ds, lag, info); ++ if (ret) ++ return ret; ++ ++ return qca8k_lag_refresh_portmap(ds, port, lag, false); ++} ++ ++static int ++qca8k_port_lag_leave(struct dsa_switch *ds, int port, ++ struct net_device *lag) ++{ ++ return qca8k_lag_refresh_portmap(ds, port, lag, true); ++} ++ + static const struct dsa_switch_ops qca8k_switch_ops = { + .get_tag_protocol = qca8k_get_tag_protocol, + .setup = qca8k_setup, +@@ -2240,6 +2415,8 @@ static const struct dsa_switch_ops qca8k + .phylink_mac_link_down = qca8k_phylink_mac_link_down, + .phylink_mac_link_up = qca8k_phylink_mac_link_up, + .get_phy_flags = qca8k_get_phy_flags, ++ .port_lag_join = qca8k_port_lag_join, ++ .port_lag_leave = qca8k_port_lag_leave, + }; + + static int qca8k_read_switch_id(struct qca8k_priv *priv) +--- a/drivers/net/dsa/qca8k.h ++++ b/drivers/net/dsa/qca8k.h +@@ -15,6 +15,8 @@ + #define QCA8K_NUM_PORTS 7 + #define QCA8K_NUM_CPU_PORTS 2 + #define QCA8K_MAX_MTU 9000 ++#define QCA8K_NUM_LAGS 4 ++#define QCA8K_NUM_PORTS_FOR_LAG 4 + + #define PHY_ID_QCA8327 0x004dd034 + #define QCA8K_ID_QCA8327 0x12 +@@ -122,6 +124,14 @@ + #define QCA8K_REG_EEE_CTRL 0x100 + #define QCA8K_REG_EEE_CTRL_LPI_EN(_i) ((_i + 1) * 2) + ++/* TRUNK_HASH_EN registers */ ++#define QCA8K_TRUNK_HASH_EN_CTRL 0x270 ++#define QCA8K_TRUNK_HASH_SIP_EN BIT(3) ++#define QCA8K_TRUNK_HASH_DIP_EN BIT(2) ++#define QCA8K_TRUNK_HASH_SA_EN BIT(1) ++#define QCA8K_TRUNK_HASH_DA_EN BIT(0) ++#define QCA8K_TRUNK_HASH_MASK GENMASK(3, 0) ++ + /* ACL registers */ + #define QCA8K_REG_PORT_VLAN_CTRL0(_i) (0x420 + (_i * 8)) + #define QCA8K_PORT_VLAN_CVID_MASK GENMASK(27, 16) +@@ -204,6 +214,28 @@ + #define QCA8K_PORT_LOOKUP_LEARN BIT(20) + #define QCA8K_PORT_LOOKUP_ING_MIRROR_EN BIT(25) + ++#define QCA8K_REG_GOL_TRUNK_CTRL0 0x700 ++/* 4 max trunk first ++ * first 6 bit for member bitmap ++ * 7th bit is to enable trunk port ++ */ ++#define QCA8K_REG_GOL_TRUNK_SHIFT(_i) ((_i) * 8) ++#define QCA8K_REG_GOL_TRUNK_EN_MASK BIT(7) ++#define QCA8K_REG_GOL_TRUNK_EN(_i) (QCA8K_REG_GOL_TRUNK_EN_MASK << QCA8K_REG_GOL_TRUNK_SHIFT(_i)) ++#define QCA8K_REG_GOL_TRUNK_MEMBER_MASK GENMASK(6, 0) ++#define QCA8K_REG_GOL_TRUNK_MEMBER(_i) (QCA8K_REG_GOL_TRUNK_MEMBER_MASK << QCA8K_REG_GOL_TRUNK_SHIFT(_i)) ++/* 0x704 for TRUNK 0-1 --- 0x708 for TRUNK 2-3 */ ++#define QCA8K_REG_GOL_TRUNK_CTRL(_i) (0x704 + (((_i) / 2) * 4)) ++#define QCA8K_REG_GOL_TRUNK_ID_MEM_ID_MASK GENMASK(3, 0) ++#define QCA8K_REG_GOL_TRUNK_ID_MEM_ID_EN_MASK BIT(3) ++#define QCA8K_REG_GOL_TRUNK_ID_MEM_ID_PORT_MASK GENMASK(2, 0) ++#define QCA8K_REG_GOL_TRUNK_ID_SHIFT(_i) (((_i) / 2) * 16) ++#define QCA8K_REG_GOL_MEM_ID_SHIFT(_i) ((_i) * 4) ++/* Complex shift: FIRST shift for port THEN shift for trunk */ ++#define QCA8K_REG_GOL_TRUNK_ID_MEM_ID_SHIFT(_i, _j) (QCA8K_REG_GOL_MEM_ID_SHIFT(_j) + QCA8K_REG_GOL_TRUNK_ID_SHIFT(_i)) ++#define QCA8K_REG_GOL_TRUNK_ID_MEM_ID_EN(_i, _j) (QCA8K_REG_GOL_TRUNK_ID_MEM_ID_EN_MASK << QCA8K_REG_GOL_TRUNK_ID_MEM_ID_SHIFT(_i, _j)) ++#define QCA8K_REG_GOL_TRUNK_ID_MEM_ID_PORT(_i, _j) (QCA8K_REG_GOL_TRUNK_ID_MEM_ID_PORT_MASK << QCA8K_REG_GOL_TRUNK_ID_MEM_ID_SHIFT(_i, _j)) ++ + #define QCA8K_REG_GLOBAL_FC_THRESH 0x800 + #define QCA8K_GLOBAL_FC_GOL_XON_THRES_MASK GENMASK(24, 16) + #define QCA8K_GLOBAL_FC_GOL_XON_THRES(x) FIELD_PREP(QCA8K_GLOBAL_FC_GOL_XON_THRES_MASK, x) +@@ -309,6 +341,7 @@ struct qca8k_priv { + u8 switch_revision; + u8 mirror_rx; + u8 mirror_tx; ++ u8 lag_hash_mode; + bool legacy_phy_port_mapping; + struct qca8k_ports_config ports_config; + struct regmap *regmap; diff --git a/pkgs/patches-linux-5.15/764-net-next-net-dsa-qca8k-fix-warning-in-LAG-feature.patch b/pkgs/patches-linux-5.15/764-net-next-net-dsa-qca8k-fix-warning-in-LAG-feature.patch new file mode 100644 index 0000000..8c0a990 --- /dev/null +++ b/pkgs/patches-linux-5.15/764-net-next-net-dsa-qca8k-fix-warning-in-LAG-feature.patch @@ -0,0 +1,40 @@ +From 0898ca67b86e14207d4feb3f3fea8b87cec5aab1 Mon Sep 17 00:00:00 2001 +From: Ansuel Smith <ansuelsmth@gmail.com> +Date: Tue, 23 Nov 2021 16:44:46 +0100 +Subject: net: dsa: qca8k: fix warning in LAG feature + +Fix warning reported by bot. +Make sure hash is init to 0 and fix wrong logic for hash_type in +qca8k_lag_can_offload. + +Reported-by: kernel test robot <lkp@intel.com> +Fixes: def975307c01 ("net: dsa: qca8k: add LAG support") +Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com> +Reviewed-by: Florian Fainelli <f.fainelli@gmail.com> +Link: https://lore.kernel.org/r/20211123154446.31019-1-ansuelsmth@gmail.com +Signed-off-by: Jakub Kicinski <kuba@kernel.org> +--- + drivers/net/dsa/qca8k.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +--- a/drivers/net/dsa/qca8k.c ++++ b/drivers/net/dsa/qca8k.c +@@ -2232,7 +2232,7 @@ qca8k_lag_can_offload(struct dsa_switch + if (info->tx_type != NETDEV_LAG_TX_TYPE_HASH) + return false; + +- if (info->hash_type != NETDEV_LAG_HASH_L2 || ++ if (info->hash_type != NETDEV_LAG_HASH_L2 && + info->hash_type != NETDEV_LAG_HASH_L23) + return false; + +@@ -2246,8 +2246,8 @@ qca8k_lag_setup_hash(struct dsa_switch * + { + struct qca8k_priv *priv = ds->priv; + bool unique_lag = true; ++ u32 hash = 0; + int i, id; +- u32 hash; + + id = dsa_lag_id(ds->dst, lag); + diff --git a/pkgs/patches-linux-5.15/765-1-net-next-net-dsa-reorder-PHY-initialization-with-MTU-setup-in.patch b/pkgs/patches-linux-5.15/765-1-net-next-net-dsa-reorder-PHY-initialization-with-MTU-setup-in.patch new file mode 100644 index 0000000..1786bf0 --- /dev/null +++ b/pkgs/patches-linux-5.15/765-1-net-next-net-dsa-reorder-PHY-initialization-with-MTU-setup-in.patch @@ -0,0 +1,52 @@ +From 904e112ad431492b34f235f59738e8312802bbf9 Mon Sep 17 00:00:00 2001 +From: Vladimir Oltean <vladimir.oltean@nxp.com> +Date: Thu, 6 Jan 2022 01:11:12 +0200 +Subject: [PATCH 1/6] net: dsa: reorder PHY initialization with MTU setup in + slave.c + +In dsa_slave_create() there are 2 sections that take rtnl_lock(): +MTU change and netdev registration. They are separated by PHY +initialization. + +There isn't any strict ordering requirement except for the fact that +netdev registration should be last. Therefore, we can perform the MTU +change a bit later, after the PHY setup. A future change will then be +able to merge the two rtnl_lock sections into one. + +Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com> +Reviewed-by: Florian Fainelli <f.fainelli@gmail.com> +Signed-off-by: David S. Miller <davem@davemloft.net> +--- + net/dsa/slave.c | 14 +++++++------- + 1 file changed, 7 insertions(+), 7 deletions(-) + +--- a/net/dsa/slave.c ++++ b/net/dsa/slave.c +@@ -1986,13 +1986,6 @@ int dsa_slave_create(struct dsa_port *po + port->slave = slave_dev; + dsa_slave_setup_tagger(slave_dev); + +- rtnl_lock(); +- ret = dsa_slave_change_mtu(slave_dev, ETH_DATA_LEN); +- rtnl_unlock(); +- if (ret && ret != -EOPNOTSUPP) +- dev_warn(ds->dev, "nonfatal error %d setting MTU to %d on port %d\n", +- ret, ETH_DATA_LEN, port->index); +- + netif_carrier_off(slave_dev); + + ret = dsa_slave_phy_setup(slave_dev); +@@ -2004,6 +1997,13 @@ int dsa_slave_create(struct dsa_port *po + } + + rtnl_lock(); ++ ret = dsa_slave_change_mtu(slave_dev, ETH_DATA_LEN); ++ rtnl_unlock(); ++ if (ret && ret != -EOPNOTSUPP) ++ dev_warn(ds->dev, "nonfatal error %d setting MTU to %d on port %d\n", ++ ret, ETH_DATA_LEN, port->index); ++ ++ rtnl_lock(); + + ret = register_netdevice(slave_dev); + if (ret) { diff --git a/pkgs/patches-linux-5.15/765-2-net-next-net-dsa-merge-rtnl_lock-sections-in-dsa_slave_create.patch b/pkgs/patches-linux-5.15/765-2-net-next-net-dsa-merge-rtnl_lock-sections-in-dsa_slave_create.patch new file mode 100644 index 0000000..c2493a0 --- /dev/null +++ b/pkgs/patches-linux-5.15/765-2-net-next-net-dsa-merge-rtnl_lock-sections-in-dsa_slave_create.patch @@ -0,0 +1,34 @@ +From e31dbd3b6aba585231cd84a87adeb22e7c6a8c19 Mon Sep 17 00:00:00 2001 +From: Vladimir Oltean <vladimir.oltean@nxp.com> +Date: Thu, 6 Jan 2022 01:11:13 +0200 +Subject: [PATCH 2/6] net: dsa: merge rtnl_lock sections in dsa_slave_create + +Currently dsa_slave_create() has two sequences of rtnl_lock/rtnl_unlock +in a row. Remove the rtnl_unlock() and rtnl_lock() in between, such that +the operation can execute slighly faster. + +Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com> +Reviewed-by: Florian Fainelli <f.fainelli@gmail.com> +Signed-off-by: David S. Miller <davem@davemloft.net> +--- + net/dsa/slave.c | 4 +--- + 1 file changed, 1 insertion(+), 3 deletions(-) + +--- a/net/dsa/slave.c ++++ b/net/dsa/slave.c +@@ -1997,14 +1997,12 @@ int dsa_slave_create(struct dsa_port *po + } + + rtnl_lock(); ++ + ret = dsa_slave_change_mtu(slave_dev, ETH_DATA_LEN); +- rtnl_unlock(); + if (ret && ret != -EOPNOTSUPP) + dev_warn(ds->dev, "nonfatal error %d setting MTU to %d on port %d\n", + ret, ETH_DATA_LEN, port->index); + +- rtnl_lock(); +- + ret = register_netdevice(slave_dev); + if (ret) { + netdev_err(master, "error %d registering interface %s\n", diff --git a/pkgs/patches-linux-5.15/765-3-net-next-net-dsa-stop-updating-master-MTU-from-master.c.patch b/pkgs/patches-linux-5.15/765-3-net-next-net-dsa-stop-updating-master-MTU-from-master.c.patch new file mode 100644 index 0000000..d1126de --- /dev/null +++ b/pkgs/patches-linux-5.15/765-3-net-next-net-dsa-stop-updating-master-MTU-from-master.c.patch @@ -0,0 +1,91 @@ +From a1ff94c2973c43bc1e2677ac63ebb15b1d1ff846 Mon Sep 17 00:00:00 2001 +From: Vladimir Oltean <vladimir.oltean@nxp.com> +Date: Thu, 6 Jan 2022 01:11:14 +0200 +Subject: [PATCH 3/6] net: dsa: stop updating master MTU from master.c + +At present there are two paths for changing the MTU of the DSA master. + +The first is: + +dsa_tree_setup +-> dsa_tree_setup_ports + -> dsa_port_setup + -> dsa_slave_create + -> dsa_slave_change_mtu + -> dev_set_mtu(master) + +The second is: + +dsa_tree_setup +-> dsa_tree_setup_master + -> dsa_master_setup + -> dev_set_mtu(dev) + +So the dev_set_mtu() call from dsa_master_setup() has been effectively +superseded by the dsa_slave_change_mtu(slave_dev, ETH_DATA_LEN) that is +done from dsa_slave_create() for each user port. The later function also +updates the master MTU according to the largest user port MTU from the +tree. Therefore, updating the master MTU through a separate code path +isn't needed. + +Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com> +Reviewed-by: Florian Fainelli <f.fainelli@gmail.com> +Signed-off-by: David S. Miller <davem@davemloft.net> +--- + net/dsa/master.c | 25 +------------------------ + 1 file changed, 1 insertion(+), 24 deletions(-) + +--- a/net/dsa/master.c ++++ b/net/dsa/master.c +@@ -330,28 +330,13 @@ static const struct attribute_group dsa_ + .attrs = dsa_slave_attrs, + }; + +-static void dsa_master_reset_mtu(struct net_device *dev) +-{ +- int err; +- +- rtnl_lock(); +- err = dev_set_mtu(dev, ETH_DATA_LEN); +- if (err) +- netdev_dbg(dev, +- "Unable to reset MTU to exclude DSA overheads\n"); +- rtnl_unlock(); +-} +- + static struct lock_class_key dsa_master_addr_list_lock_key; + + int dsa_master_setup(struct net_device *dev, struct dsa_port *cpu_dp) + { +- const struct dsa_device_ops *tag_ops = cpu_dp->tag_ops; + struct dsa_switch *ds = cpu_dp->ds; + struct device_link *consumer_link; +- int mtu, ret; +- +- mtu = ETH_DATA_LEN + dsa_tag_protocol_overhead(tag_ops); ++ int ret; + + /* The DSA master must use SET_NETDEV_DEV for this to work. */ + consumer_link = device_link_add(ds->dev, dev->dev.parent, +@@ -361,13 +346,6 @@ int dsa_master_setup(struct net_device * + "Failed to create a device link to DSA switch %s\n", + dev_name(ds->dev)); + +- rtnl_lock(); +- ret = dev_set_mtu(dev, mtu); +- rtnl_unlock(); +- if (ret) +- netdev_warn(dev, "error %d setting MTU to %d to include DSA overhead\n", +- ret, mtu); +- + /* If we use a tagging format that doesn't have an ethertype + * field, make sure that all packets from this point on get + * sent to the tag format's receive function. +@@ -405,7 +383,6 @@ void dsa_master_teardown(struct net_devi + sysfs_remove_group(&dev->dev.kobj, &dsa_group); + dsa_netdev_ops_set(dev, NULL); + dsa_master_ethtool_teardown(dev); +- dsa_master_reset_mtu(dev); + dsa_master_set_promiscuity(dev, -1); + + dev->dsa_ptr = NULL; diff --git a/pkgs/patches-linux-5.15/765-4-net-next-net-dsa-hold-rtnl_mutex-when-calling-dsa_master_-set.patch b/pkgs/patches-linux-5.15/765-4-net-next-net-dsa-hold-rtnl_mutex-when-calling-dsa_master_-set.patch new file mode 100644 index 0000000..67d4340 --- /dev/null +++ b/pkgs/patches-linux-5.15/765-4-net-next-net-dsa-hold-rtnl_mutex-when-calling-dsa_master_-set.patch @@ -0,0 +1,78 @@ +From c146f9bc195a9dc3ad7fd000a14540e7c9df952d Mon Sep 17 00:00:00 2001 +From: Vladimir Oltean <vladimir.oltean@nxp.com> +Date: Thu, 6 Jan 2022 01:11:15 +0200 +Subject: [PATCH 4/6] net: dsa: hold rtnl_mutex when calling + dsa_master_{setup,teardown} + +DSA needs to simulate master tracking events when a binding is first +with a DSA master established and torn down, in order to give drivers +the simplifying guarantee that ->master_state_change calls are made +only when the master's readiness state to pass traffic changes. +master_state_change() provide a operational bool that DSA driver can use +to understand if DSA master is operational or not. +To avoid races, we need to block the reception of +NETDEV_UP/NETDEV_CHANGE/NETDEV_GOING_DOWN events in the netdev notifier +chain while we are changing the master's dev->dsa_ptr (this changes what +netdev_uses_dsa(dev) reports). + +The dsa_master_setup() and dsa_master_teardown() functions optionally +require the rtnl_mutex to be held, if the tagger needs the master to be +promiscuous, these functions call dev_set_promiscuity(). Move the +rtnl_lock() from that function and make it top-level. + +Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com> +Reviewed-by: Florian Fainelli <f.fainelli@gmail.com> +Signed-off-by: David S. Miller <davem@davemloft.net> +--- + net/dsa/dsa2.c | 8 ++++++++ + net/dsa/master.c | 4 ++-- + 2 files changed, 10 insertions(+), 2 deletions(-) + +--- a/net/dsa/dsa2.c ++++ b/net/dsa/dsa2.c +@@ -1034,6 +1034,8 @@ static int dsa_tree_setup_master(struct + struct dsa_port *dp; + int err; + ++ rtnl_lock(); ++ + list_for_each_entry(dp, &dst->ports, list) { + if (dsa_port_is_cpu(dp)) { + err = dsa_master_setup(dp->master, dp); +@@ -1042,6 +1044,8 @@ static int dsa_tree_setup_master(struct + } + } + ++ rtnl_unlock(); ++ + return 0; + } + +@@ -1049,9 +1053,13 @@ static void dsa_tree_teardown_master(str + { + struct dsa_port *dp; + ++ rtnl_lock(); ++ + list_for_each_entry(dp, &dst->ports, list) + if (dsa_port_is_cpu(dp)) + dsa_master_teardown(dp->master); ++ ++ rtnl_unlock(); + } + + static int dsa_tree_setup_lags(struct dsa_switch_tree *dst) +--- a/net/dsa/master.c ++++ b/net/dsa/master.c +@@ -267,9 +267,9 @@ static void dsa_master_set_promiscuity(s + if (!ops->promisc_on_master) + return; + +- rtnl_lock(); ++ ASSERT_RTNL(); ++ + dev_set_promiscuity(dev, inc); +- rtnl_unlock(); + } + + static ssize_t tagging_show(struct device *d, struct device_attribute *attr, diff --git a/pkgs/patches-linux-5.15/765-5-net-next-net-dsa-first-set-up-shared-ports-then-non-shared-po.patch b/pkgs/patches-linux-5.15/765-5-net-next-net-dsa-first-set-up-shared-ports-then-non-shared-po.patch new file mode 100644 index 0000000..e6472c6 --- /dev/null +++ b/pkgs/patches-linux-5.15/765-5-net-next-net-dsa-first-set-up-shared-ports-then-non-shared-po.patch @@ -0,0 +1,118 @@ +From 1e3f407f3cacc5dcfe27166c412ed9bc263d82bf Mon Sep 17 00:00:00 2001 +From: Vladimir Oltean <vladimir.oltean@nxp.com> +Date: Thu, 6 Jan 2022 01:11:16 +0200 +Subject: [PATCH 5/6] net: dsa: first set up shared ports, then non-shared + ports + +After commit a57d8c217aad ("net: dsa: flush switchdev workqueue before +tearing down CPU/DSA ports"), the port setup and teardown procedure +became asymmetric. + +The fact of the matter is that user ports need the shared ports to be up +before they can be used for CPU-initiated termination. And since we +register net devices for the user ports, those won't be functional until +we also call the setup for the shared (CPU, DSA) ports. But we may do +that later, depending on the port numbering scheme of the hardware we +are dealing with. + +It just makes sense that all shared ports are brought up before any user +port is. I can't pinpoint any issue due to the current behavior, but +let's change it nonetheless, for consistency's sake. + +Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com> +Signed-off-by: David S. Miller <davem@davemloft.net> +--- + net/dsa/dsa2.c | 50 +++++++++++++++++++++++++++++++++++++------------- + 1 file changed, 37 insertions(+), 13 deletions(-) + +--- a/net/dsa/dsa2.c ++++ b/net/dsa/dsa2.c +@@ -999,23 +999,28 @@ static void dsa_tree_teardown_switches(s + dsa_switch_teardown(dp->ds); + } + +-static int dsa_tree_setup_switches(struct dsa_switch_tree *dst) ++/* Bring shared ports up first, then non-shared ports */ ++static int dsa_tree_setup_ports(struct dsa_switch_tree *dst) + { + struct dsa_port *dp; +- int err; ++ int err = 0; + + list_for_each_entry(dp, &dst->ports, list) { +- err = dsa_switch_setup(dp->ds); +- if (err) +- goto teardown; ++ if (dsa_port_is_dsa(dp) || dsa_port_is_cpu(dp)) { ++ err = dsa_port_setup(dp); ++ if (err) ++ goto teardown; ++ } + } + + list_for_each_entry(dp, &dst->ports, list) { +- err = dsa_port_setup(dp); +- if (err) { +- err = dsa_port_reinit_as_unused(dp); +- if (err) +- goto teardown; ++ if (dsa_port_is_user(dp) || dsa_port_is_unused(dp)) { ++ err = dsa_port_setup(dp); ++ if (err) { ++ err = dsa_port_reinit_as_unused(dp); ++ if (err) ++ goto teardown; ++ } + } + } + +@@ -1024,7 +1029,21 @@ static int dsa_tree_setup_switches(struc + teardown: + dsa_tree_teardown_ports(dst); + +- dsa_tree_teardown_switches(dst); ++ return err; ++} ++ ++static int dsa_tree_setup_switches(struct dsa_switch_tree *dst) ++{ ++ struct dsa_port *dp; ++ int err = 0; ++ ++ list_for_each_entry(dp, &dst->ports, list) { ++ err = dsa_switch_setup(dp->ds); ++ if (err) { ++ dsa_tree_teardown_switches(dst); ++ break; ++ } ++ } + + return err; + } +@@ -1111,10 +1130,14 @@ static int dsa_tree_setup(struct dsa_swi + if (err) + goto teardown_cpu_ports; + +- err = dsa_tree_setup_master(dst); ++ err = dsa_tree_setup_ports(dst); + if (err) + goto teardown_switches; + ++ err = dsa_tree_setup_master(dst); ++ if (err) ++ goto teardown_ports; ++ + err = dsa_tree_setup_lags(dst); + if (err) + goto teardown_master; +@@ -1127,8 +1150,9 @@ static int dsa_tree_setup(struct dsa_swi + + teardown_master: + dsa_tree_teardown_master(dst); +-teardown_switches: ++teardown_ports: + dsa_tree_teardown_ports(dst); ++teardown_switches: + dsa_tree_teardown_switches(dst); + teardown_cpu_ports: + dsa_tree_teardown_cpu_ports(dst); diff --git a/pkgs/patches-linux-5.15/765-6-net-next-net-dsa-setup-master-before-ports.patch b/pkgs/patches-linux-5.15/765-6-net-next-net-dsa-setup-master-before-ports.patch new file mode 100644 index 0000000..93cad0c --- /dev/null +++ b/pkgs/patches-linux-5.15/765-6-net-next-net-dsa-setup-master-before-ports.patch @@ -0,0 +1,115 @@ +From 11fd667dac315ea3f2469961f6d2869271a46cae Mon Sep 17 00:00:00 2001 +From: Vladimir Oltean <vladimir.oltean@nxp.com> +Date: Thu, 6 Jan 2022 01:11:17 +0200 +Subject: [PATCH 6/6] net: dsa: setup master before ports + +It is said that as soon as a network interface is registered, all its +resources should have already been prepared, so that it is available for +sending and receiving traffic. One of the resources needed by a DSA +slave interface is the master. + +dsa_tree_setup +-> dsa_tree_setup_ports + -> dsa_port_setup + -> dsa_slave_create + -> register_netdevice +-> dsa_tree_setup_master + -> dsa_master_setup + -> sets up master->dsa_ptr, which enables reception + +Therefore, there is a short period of time after register_netdevice() +during which the master isn't prepared to pass traffic to the DSA layer +(master->dsa_ptr is checked by eth_type_trans). Same thing during +unregistration, there is a time frame in which packets might be missed. + +Note that this change opens us to another race: dsa_master_find_slave() +will get invoked potentially earlier than the slave creation, and later +than the slave deletion. Since dp->slave starts off as a NULL pointer, +the earlier calls aren't a problem, but the later calls are. To avoid +use-after-free, we should zeroize dp->slave before calling +dsa_slave_destroy(). + +In practice I cannot really test real life improvements brought by this +change, since in my systems, netdevice creation races with PHY autoneg +which takes a few seconds to complete, and that masks quite a few races. +Effects might be noticeable in a setup with fixed links all the way to +an external system. + +Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com> +Signed-off-by: David S. Miller <davem@davemloft.net> +--- + net/dsa/dsa2.c | 23 +++++++++++++---------- + 1 file changed, 13 insertions(+), 10 deletions(-) + +--- a/net/dsa/dsa2.c ++++ b/net/dsa/dsa2.c +@@ -545,6 +545,7 @@ static void dsa_port_teardown(struct dsa + struct devlink_port *dlp = &dp->devlink_port; + struct dsa_switch *ds = dp->ds; + struct dsa_mac_addr *a, *tmp; ++ struct net_device *slave; + + if (!dp->setup) + return; +@@ -566,9 +567,11 @@ static void dsa_port_teardown(struct dsa + dsa_port_link_unregister_of(dp); + break; + case DSA_PORT_TYPE_USER: +- if (dp->slave) { +- dsa_slave_destroy(dp->slave); ++ slave = dp->slave; ++ ++ if (slave) { + dp->slave = NULL; ++ dsa_slave_destroy(slave); + } + break; + } +@@ -1130,17 +1133,17 @@ static int dsa_tree_setup(struct dsa_swi + if (err) + goto teardown_cpu_ports; + +- err = dsa_tree_setup_ports(dst); ++ err = dsa_tree_setup_master(dst); + if (err) + goto teardown_switches; + +- err = dsa_tree_setup_master(dst); ++ err = dsa_tree_setup_ports(dst); + if (err) +- goto teardown_ports; ++ goto teardown_master; + + err = dsa_tree_setup_lags(dst); + if (err) +- goto teardown_master; ++ goto teardown_ports; + + dst->setup = true; + +@@ -1148,10 +1151,10 @@ static int dsa_tree_setup(struct dsa_swi + + return 0; + +-teardown_master: +- dsa_tree_teardown_master(dst); + teardown_ports: + dsa_tree_teardown_ports(dst); ++teardown_master: ++ dsa_tree_teardown_master(dst); + teardown_switches: + dsa_tree_teardown_switches(dst); + teardown_cpu_ports: +@@ -1169,10 +1172,10 @@ static void dsa_tree_teardown(struct dsa + + dsa_tree_teardown_lags(dst); + +- dsa_tree_teardown_master(dst); +- + dsa_tree_teardown_ports(dst); + ++ dsa_tree_teardown_master(dst); ++ + dsa_tree_teardown_switches(dst); + + dsa_tree_teardown_cpu_ports(dst); diff --git a/pkgs/patches-linux-5.15/766-01-net-dsa-provide-switch-operations-for-tracking-the-m.patch b/pkgs/patches-linux-5.15/766-01-net-dsa-provide-switch-operations-for-tracking-the-m.patch new file mode 100644 index 0000000..d73b745 --- /dev/null +++ b/pkgs/patches-linux-5.15/766-01-net-dsa-provide-switch-operations-for-tracking-the-m.patch @@ -0,0 +1,254 @@ +From 295ab96f478d0fa56393e85406f19a867e26ce22 Mon Sep 17 00:00:00 2001 +From: Vladimir Oltean <vladimir.oltean@nxp.com> +Date: Wed, 2 Feb 2022 01:03:20 +0100 +Subject: [PATCH 01/16] net: dsa: provide switch operations for tracking the + master state + +Certain drivers may need to send management traffic to the switch for +things like register access, FDB dump, etc, to accelerate what their +slow bus (SPI, I2C, MDIO) can already do. + +Ethernet is faster (especially in bulk transactions) but is also more +unreliable, since the user may decide to bring the DSA master down (or +not bring it up), therefore severing the link between the host and the +attached switch. + +Drivers needing Ethernet-based register access already should have +fallback logic to the slow bus if the Ethernet method fails, but that +fallback may be based on a timeout, and the I/O to the switch may slow +down to a halt if the master is down, because every Ethernet packet will +have to time out. The driver also doesn't have the option to turn off +Ethernet-based I/O momentarily, because it wouldn't know when to turn it +back on. + +Which is where this change comes in. By tracking NETDEV_CHANGE, +NETDEV_UP and NETDEV_GOING_DOWN events on the DSA master, we should know +the exact interval of time during which this interface is reliably +available for traffic. Provide this information to switches so they can +use it as they wish. + +An helper is added dsa_port_master_is_operational() to check if a master +port is operational. + +Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com> +Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com> +Reviewed-by: Florian Fainelli <f.fainelli@gmail.com> +Signed-off-by: David S. Miller <davem@davemloft.net> +--- + include/net/dsa.h | 17 +++++++++++++++++ + net/dsa/dsa2.c | 46 ++++++++++++++++++++++++++++++++++++++++++++++ + net/dsa/dsa_priv.h | 13 +++++++++++++ + net/dsa/slave.c | 32 ++++++++++++++++++++++++++++++++ + net/dsa/switch.c | 15 +++++++++++++++ + 5 files changed, 123 insertions(+) + +--- a/include/net/dsa.h ++++ b/include/net/dsa.h +@@ -291,6 +291,10 @@ struct dsa_port { + struct list_head mdbs; + + bool setup; ++ /* Master state bits, valid only on CPU ports */ ++ u8 master_admin_up:1; ++ u8 master_oper_up:1; ++ + }; + + /* TODO: ideally DSA ports would have a single dp->link_dp member, +@@ -456,6 +460,12 @@ static inline bool dsa_port_is_unused(st + return dp->type == DSA_PORT_TYPE_UNUSED; + } + ++static inline bool dsa_port_master_is_operational(struct dsa_port *dp) ++{ ++ return dsa_port_is_cpu(dp) && dp->master_admin_up && ++ dp->master_oper_up; ++} ++ + static inline bool dsa_is_unused_port(struct dsa_switch *ds, int p) + { + return dsa_to_port(ds, p)->type == DSA_PORT_TYPE_UNUSED; +@@ -916,6 +926,13 @@ struct dsa_switch_ops { + int (*tag_8021q_vlan_add)(struct dsa_switch *ds, int port, u16 vid, + u16 flags); + int (*tag_8021q_vlan_del)(struct dsa_switch *ds, int port, u16 vid); ++ ++ /* ++ * DSA master tracking operations ++ */ ++ void (*master_state_change)(struct dsa_switch *ds, ++ const struct net_device *master, ++ bool operational); + }; + + #define DSA_DEVLINK_PARAM_DRIVER(_id, _name, _type, _cmodes) \ +--- a/net/dsa/dsa2.c ++++ b/net/dsa/dsa2.c +@@ -1275,6 +1275,52 @@ out_unlock: + return err; + } + ++static void dsa_tree_master_state_change(struct dsa_switch_tree *dst, ++ struct net_device *master) ++{ ++ struct dsa_notifier_master_state_info info; ++ struct dsa_port *cpu_dp = master->dsa_ptr; ++ ++ info.master = master; ++ info.operational = dsa_port_master_is_operational(cpu_dp); ++ ++ dsa_tree_notify(dst, DSA_NOTIFIER_MASTER_STATE_CHANGE, &info); ++} ++ ++void dsa_tree_master_admin_state_change(struct dsa_switch_tree *dst, ++ struct net_device *master, ++ bool up) ++{ ++ struct dsa_port *cpu_dp = master->dsa_ptr; ++ bool notify = false; ++ ++ if ((dsa_port_master_is_operational(cpu_dp)) != ++ (up && cpu_dp->master_oper_up)) ++ notify = true; ++ ++ cpu_dp->master_admin_up = up; ++ ++ if (notify) ++ dsa_tree_master_state_change(dst, master); ++} ++ ++void dsa_tree_master_oper_state_change(struct dsa_switch_tree *dst, ++ struct net_device *master, ++ bool up) ++{ ++ struct dsa_port *cpu_dp = master->dsa_ptr; ++ bool notify = false; ++ ++ if ((dsa_port_master_is_operational(cpu_dp)) != ++ (cpu_dp->master_admin_up && up)) ++ notify = true; ++ ++ cpu_dp->master_oper_up = up; ++ ++ if (notify) ++ dsa_tree_master_state_change(dst, master); ++} ++ + static struct dsa_port *dsa_port_touch(struct dsa_switch *ds, int index) + { + struct dsa_switch_tree *dst = ds->dst; +--- a/net/dsa/dsa_priv.h ++++ b/net/dsa/dsa_priv.h +@@ -45,6 +45,7 @@ enum { + DSA_NOTIFIER_MRP_DEL_RING_ROLE, + DSA_NOTIFIER_TAG_8021Q_VLAN_ADD, + DSA_NOTIFIER_TAG_8021Q_VLAN_DEL, ++ DSA_NOTIFIER_MASTER_STATE_CHANGE, + }; + + /* DSA_NOTIFIER_AGEING_TIME */ +@@ -127,6 +128,12 @@ struct dsa_notifier_tag_8021q_vlan_info + u16 vid; + }; + ++/* DSA_NOTIFIER_MASTER_STATE_CHANGE */ ++struct dsa_notifier_master_state_info { ++ const struct net_device *master; ++ bool operational; ++}; ++ + struct dsa_switchdev_event_work { + struct dsa_switch *ds; + int port; +@@ -548,6 +555,12 @@ int dsa_tree_change_tag_proto(struct dsa + struct net_device *master, + const struct dsa_device_ops *tag_ops, + const struct dsa_device_ops *old_tag_ops); ++void dsa_tree_master_admin_state_change(struct dsa_switch_tree *dst, ++ struct net_device *master, ++ bool up); ++void dsa_tree_master_oper_state_change(struct dsa_switch_tree *dst, ++ struct net_device *master, ++ bool up); + int dsa_bridge_num_get(const struct net_device *bridge_dev, int max); + void dsa_bridge_num_put(const struct net_device *bridge_dev, int bridge_num); + +--- a/net/dsa/slave.c ++++ b/net/dsa/slave.c +@@ -2320,6 +2320,36 @@ static int dsa_slave_netdevice_event(str + err = dsa_port_lag_change(dp, info->lower_state_info); + return notifier_from_errno(err); + } ++ case NETDEV_CHANGE: ++ case NETDEV_UP: { ++ /* Track state of master port. ++ * DSA driver may require the master port (and indirectly ++ * the tagger) to be available for some special operation. ++ */ ++ if (netdev_uses_dsa(dev)) { ++ struct dsa_port *cpu_dp = dev->dsa_ptr; ++ struct dsa_switch_tree *dst = cpu_dp->ds->dst; ++ ++ /* Track when the master port is UP */ ++ dsa_tree_master_oper_state_change(dst, dev, ++ netif_oper_up(dev)); ++ ++ /* Track when the master port is ready and can accept ++ * packet. ++ * NETDEV_UP event is not enough to flag a port as ready. ++ * We also have to wait for linkwatch_do_dev to dev_activate ++ * and emit a NETDEV_CHANGE event. ++ * We check if a master port is ready by checking if the dev ++ * have a qdisc assigned and is not noop. ++ */ ++ dsa_tree_master_admin_state_change(dst, dev, ++ !qdisc_tx_is_noop(dev)); ++ ++ return NOTIFY_OK; ++ } ++ ++ return NOTIFY_DONE; ++ } + case NETDEV_GOING_DOWN: { + struct dsa_port *dp, *cpu_dp; + struct dsa_switch_tree *dst; +@@ -2331,6 +2361,8 @@ static int dsa_slave_netdevice_event(str + cpu_dp = dev->dsa_ptr; + dst = cpu_dp->ds->dst; + ++ dsa_tree_master_admin_state_change(dst, dev, false); ++ + list_for_each_entry(dp, &dst->ports, list) { + if (!dsa_is_user_port(dp->ds, dp->index)) + continue; +--- a/net/dsa/switch.c ++++ b/net/dsa/switch.c +@@ -722,6 +722,18 @@ dsa_switch_mrp_del_ring_role(struct dsa_ + return 0; + } + ++static int ++dsa_switch_master_state_change(struct dsa_switch *ds, ++ struct dsa_notifier_master_state_info *info) ++{ ++ if (!ds->ops->master_state_change) ++ return 0; ++ ++ ds->ops->master_state_change(ds, info->master, info->operational); ++ ++ return 0; ++} ++ + static int dsa_switch_event(struct notifier_block *nb, + unsigned long event, void *info) + { +@@ -813,6 +825,9 @@ static int dsa_switch_event(struct notif + case DSA_NOTIFIER_TAG_8021Q_VLAN_DEL: + err = dsa_switch_tag_8021q_vlan_del(ds, info); + break; ++ case DSA_NOTIFIER_MASTER_STATE_CHANGE: ++ err = dsa_switch_master_state_change(ds, info); ++ break; + default: + err = -EOPNOTSUPP; + break; diff --git a/pkgs/patches-linux-5.15/766-02-net-dsa-replay-master-state-events-in-dsa_tree_-setu.patch b/pkgs/patches-linux-5.15/766-02-net-dsa-replay-master-state-events-in-dsa_tree_-setu.patch new file mode 100644 index 0000000..6478d58 --- /dev/null +++ b/pkgs/patches-linux-5.15/766-02-net-dsa-replay-master-state-events-in-dsa_tree_-setu.patch @@ -0,0 +1,89 @@ +From e83d56537859849f2223b90749e554831b1f3c27 Mon Sep 17 00:00:00 2001 +From: Vladimir Oltean <vladimir.oltean@nxp.com> +Date: Wed, 2 Feb 2022 01:03:21 +0100 +Subject: [PATCH 02/16] net: dsa: replay master state events in + dsa_tree_{setup,teardown}_master + +In order for switch driver to be able to make simple and reliable use of +the master tracking operations, they must also be notified of the +initial state of the DSA master, not just of the changes. This is +because they might enable certain features only during the time when +they know that the DSA master is up and running. + +Therefore, this change explicitly checks the state of the DSA master +under the same rtnl_mutex as we were holding during the +dsa_master_setup() and dsa_master_teardown() call. The idea being that +if the DSA master became operational in between the moment in which it +became a DSA master (dsa_master_setup set dev->dsa_ptr) and the moment +when we checked for the master being up, there is a chance that we +would emit a ->master_state_change() call with no actual state change. +We need to avoid that by serializing the concurrent netdevice event with +us. If the netdevice event started before, we force it to finish before +we begin, because we take rtnl_lock before making netdev_uses_dsa() +return true. So we also handle that early event and do nothing on it. +Similarly, if the dev_open() attempt is concurrent with us, it will +attempt to take the rtnl_mutex, but we're holding it. We'll see that +the master flag IFF_UP isn't set, then when we release the rtnl_mutex +we'll process the NETDEV_UP notifier. + +Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com> +Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com> +Reviewed-by: Florian Fainelli <f.fainelli@gmail.com> +Signed-off-by: David S. Miller <davem@davemloft.net> +--- + net/dsa/dsa2.c | 28 ++++++++++++++++++++++++---- + 1 file changed, 24 insertions(+), 4 deletions(-) + +--- a/net/dsa/dsa2.c ++++ b/net/dsa/dsa2.c +@@ -15,6 +15,7 @@ + #include <linux/of.h> + #include <linux/of_net.h> + #include <net/devlink.h> ++#include <net/sch_generic.h> + + #include "dsa_priv.h" + +@@ -1060,9 +1061,18 @@ static int dsa_tree_setup_master(struct + + list_for_each_entry(dp, &dst->ports, list) { + if (dsa_port_is_cpu(dp)) { +- err = dsa_master_setup(dp->master, dp); ++ struct net_device *master = dp->master; ++ bool admin_up = (master->flags & IFF_UP) && ++ !qdisc_tx_is_noop(master); ++ ++ err = dsa_master_setup(master, dp); + if (err) + return err; ++ ++ /* Replay master state event */ ++ dsa_tree_master_admin_state_change(dst, master, admin_up); ++ dsa_tree_master_oper_state_change(dst, master, ++ netif_oper_up(master)); + } + } + +@@ -1077,9 +1087,19 @@ static void dsa_tree_teardown_master(str + + rtnl_lock(); + +- list_for_each_entry(dp, &dst->ports, list) +- if (dsa_port_is_cpu(dp)) +- dsa_master_teardown(dp->master); ++ list_for_each_entry(dp, &dst->ports, list) { ++ if (dsa_port_is_cpu(dp)) { ++ struct net_device *master = dp->master; ++ ++ /* Synthesizing an "admin down" state is sufficient for ++ * the switches to get a notification if the master is ++ * currently up and running. ++ */ ++ dsa_tree_master_admin_state_change(dst, master, false); ++ ++ dsa_master_teardown(master); ++ } ++ } + + rtnl_unlock(); + } diff --git a/pkgs/patches-linux-5.15/766-03-net-dsa-tag_qca-convert-to-FIELD-macro.patch b/pkgs/patches-linux-5.15/766-03-net-dsa-tag_qca-convert-to-FIELD-macro.patch new file mode 100644 index 0000000..82c94b3 --- /dev/null +++ b/pkgs/patches-linux-5.15/766-03-net-dsa-tag_qca-convert-to-FIELD-macro.patch @@ -0,0 +1,86 @@ +From 6b0458299297ca4ab6fb295800e29a4e501d50c1 Mon Sep 17 00:00:00 2001 +From: Ansuel Smith <ansuelsmth@gmail.com> +Date: Wed, 2 Feb 2022 01:03:22 +0100 +Subject: [PATCH 03/16] net: dsa: tag_qca: convert to FIELD macro + +Convert driver to FIELD macro to drop redundant define. + +Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com> +Reviewed-by: Vladimir Oltean <olteanv@gmail.com> +Reviewed-by: Florian Fainelli <f.fainelli@gmail.com> +Signed-off-by: David S. Miller <davem@davemloft.net> +--- + net/dsa/tag_qca.c | 34 +++++++++++++++------------------- + 1 file changed, 15 insertions(+), 19 deletions(-) + +--- a/net/dsa/tag_qca.c ++++ b/net/dsa/tag_qca.c +@@ -4,29 +4,24 @@ + */ + + #include <linux/etherdevice.h> ++#include <linux/bitfield.h> + + #include "dsa_priv.h" + + #define QCA_HDR_LEN 2 + #define QCA_HDR_VERSION 0x2 + +-#define QCA_HDR_RECV_VERSION_MASK GENMASK(15, 14) +-#define QCA_HDR_RECV_VERSION_S 14 +-#define QCA_HDR_RECV_PRIORITY_MASK GENMASK(13, 11) +-#define QCA_HDR_RECV_PRIORITY_S 11 +-#define QCA_HDR_RECV_TYPE_MASK GENMASK(10, 6) +-#define QCA_HDR_RECV_TYPE_S 6 ++#define QCA_HDR_RECV_VERSION GENMASK(15, 14) ++#define QCA_HDR_RECV_PRIORITY GENMASK(13, 11) ++#define QCA_HDR_RECV_TYPE GENMASK(10, 6) + #define QCA_HDR_RECV_FRAME_IS_TAGGED BIT(3) +-#define QCA_HDR_RECV_SOURCE_PORT_MASK GENMASK(2, 0) ++#define QCA_HDR_RECV_SOURCE_PORT GENMASK(2, 0) + +-#define QCA_HDR_XMIT_VERSION_MASK GENMASK(15, 14) +-#define QCA_HDR_XMIT_VERSION_S 14 +-#define QCA_HDR_XMIT_PRIORITY_MASK GENMASK(13, 11) +-#define QCA_HDR_XMIT_PRIORITY_S 11 +-#define QCA_HDR_XMIT_CONTROL_MASK GENMASK(10, 8) +-#define QCA_HDR_XMIT_CONTROL_S 8 ++#define QCA_HDR_XMIT_VERSION GENMASK(15, 14) ++#define QCA_HDR_XMIT_PRIORITY GENMASK(13, 11) ++#define QCA_HDR_XMIT_CONTROL GENMASK(10, 8) + #define QCA_HDR_XMIT_FROM_CPU BIT(7) +-#define QCA_HDR_XMIT_DP_BIT_MASK GENMASK(6, 0) ++#define QCA_HDR_XMIT_DP_BIT GENMASK(6, 0) + + static struct sk_buff *qca_tag_xmit(struct sk_buff *skb, struct net_device *dev) + { +@@ -40,8 +35,9 @@ static struct sk_buff *qca_tag_xmit(stru + phdr = dsa_etype_header_pos_tx(skb); + + /* Set the version field, and set destination port information */ +- hdr = QCA_HDR_VERSION << QCA_HDR_XMIT_VERSION_S | +- QCA_HDR_XMIT_FROM_CPU | BIT(dp->index); ++ hdr = FIELD_PREP(QCA_HDR_XMIT_VERSION, QCA_HDR_VERSION); ++ hdr |= QCA_HDR_XMIT_FROM_CPU; ++ hdr |= FIELD_PREP(QCA_HDR_XMIT_DP_BIT, BIT(dp->index)); + + *phdr = htons(hdr); + +@@ -62,7 +58,7 @@ static struct sk_buff *qca_tag_rcv(struc + hdr = ntohs(*phdr); + + /* Make sure the version is correct */ +- ver = (hdr & QCA_HDR_RECV_VERSION_MASK) >> QCA_HDR_RECV_VERSION_S; ++ ver = FIELD_GET(QCA_HDR_RECV_VERSION, hdr); + if (unlikely(ver != QCA_HDR_VERSION)) + return NULL; + +@@ -71,7 +67,7 @@ static struct sk_buff *qca_tag_rcv(struc + dsa_strip_etype_header(skb, QCA_HDR_LEN); + + /* Get source port information */ +- port = (hdr & QCA_HDR_RECV_SOURCE_PORT_MASK); ++ port = FIELD_GET(QCA_HDR_RECV_SOURCE_PORT, hdr); + + skb->dev = dsa_master_find_slave(dev, 0, port); + if (!skb->dev) diff --git a/pkgs/patches-linux-5.15/766-04-net-dsa-tag_qca-move-define-to-include-linux-dsa.patch b/pkgs/patches-linux-5.15/766-04-net-dsa-tag_qca-move-define-to-include-linux-dsa.patch new file mode 100644 index 0000000..c1e74ce --- /dev/null +++ b/pkgs/patches-linux-5.15/766-04-net-dsa-tag_qca-move-define-to-include-linux-dsa.patch @@ -0,0 +1,71 @@ +From 3ec762fb13c7e7273800b94c80db1c2cc37590d1 Mon Sep 17 00:00:00 2001 +From: Ansuel Smith <ansuelsmth@gmail.com> +Date: Wed, 2 Feb 2022 01:03:23 +0100 +Subject: [PATCH 04/16] net: dsa: tag_qca: move define to include linux/dsa + +Move tag_qca define to include dir linux/dsa as the qca8k require access +to the tagger define to support in-band mdio read/write using ethernet +packet. + +Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com> +Reviewed-by: Vladimir Oltean <olteanv@gmail.com> +Reviewed-by: Florian Fainelli <f.fainelli@gmail.com> +Signed-off-by: David S. Miller <davem@davemloft.net> +--- + include/linux/dsa/tag_qca.h | 21 +++++++++++++++++++++ + net/dsa/tag_qca.c | 16 +--------------- + 2 files changed, 22 insertions(+), 15 deletions(-) + create mode 100644 include/linux/dsa/tag_qca.h + +--- /dev/null ++++ b/include/linux/dsa/tag_qca.h +@@ -0,0 +1,21 @@ ++/* SPDX-License-Identifier: GPL-2.0 */ ++ ++#ifndef __TAG_QCA_H ++#define __TAG_QCA_H ++ ++#define QCA_HDR_LEN 2 ++#define QCA_HDR_VERSION 0x2 ++ ++#define QCA_HDR_RECV_VERSION GENMASK(15, 14) ++#define QCA_HDR_RECV_PRIORITY GENMASK(13, 11) ++#define QCA_HDR_RECV_TYPE GENMASK(10, 6) ++#define QCA_HDR_RECV_FRAME_IS_TAGGED BIT(3) ++#define QCA_HDR_RECV_SOURCE_PORT GENMASK(2, 0) ++ ++#define QCA_HDR_XMIT_VERSION GENMASK(15, 14) ++#define QCA_HDR_XMIT_PRIORITY GENMASK(13, 11) ++#define QCA_HDR_XMIT_CONTROL GENMASK(10, 8) ++#define QCA_HDR_XMIT_FROM_CPU BIT(7) ++#define QCA_HDR_XMIT_DP_BIT GENMASK(6, 0) ++ ++#endif /* __TAG_QCA_H */ +--- a/net/dsa/tag_qca.c ++++ b/net/dsa/tag_qca.c +@@ -5,24 +5,10 @@ + + #include <linux/etherdevice.h> + #include <linux/bitfield.h> ++#include <linux/dsa/tag_qca.h> + + #include "dsa_priv.h" + +-#define QCA_HDR_LEN 2 +-#define QCA_HDR_VERSION 0x2 +- +-#define QCA_HDR_RECV_VERSION GENMASK(15, 14) +-#define QCA_HDR_RECV_PRIORITY GENMASK(13, 11) +-#define QCA_HDR_RECV_TYPE GENMASK(10, 6) +-#define QCA_HDR_RECV_FRAME_IS_TAGGED BIT(3) +-#define QCA_HDR_RECV_SOURCE_PORT GENMASK(2, 0) +- +-#define QCA_HDR_XMIT_VERSION GENMASK(15, 14) +-#define QCA_HDR_XMIT_PRIORITY GENMASK(13, 11) +-#define QCA_HDR_XMIT_CONTROL GENMASK(10, 8) +-#define QCA_HDR_XMIT_FROM_CPU BIT(7) +-#define QCA_HDR_XMIT_DP_BIT GENMASK(6, 0) +- + static struct sk_buff *qca_tag_xmit(struct sk_buff *skb, struct net_device *dev) + { + struct dsa_port *dp = dsa_slave_to_port(dev); diff --git a/pkgs/patches-linux-5.15/766-05-net-dsa-tag_qca-enable-promisc_on_master-flag.patch b/pkgs/patches-linux-5.15/766-05-net-dsa-tag_qca-enable-promisc_on_master-flag.patch new file mode 100644 index 0000000..9394a0d --- /dev/null +++ b/pkgs/patches-linux-5.15/766-05-net-dsa-tag_qca-enable-promisc_on_master-flag.patch @@ -0,0 +1,27 @@ +From 101c04c3463b87061e6a3d4f72c1bc57670685a6 Mon Sep 17 00:00:00 2001 +From: Ansuel Smith <ansuelsmth@gmail.com> +Date: Wed, 2 Feb 2022 01:03:24 +0100 +Subject: [PATCH 05/16] net: dsa: tag_qca: enable promisc_on_master flag + +Ethernet MDIO packets are non-standard and DSA master expects the first +6 octets to be the MAC DA. To address these kind of packet, enable +promisc_on_master flag for the tagger. + +Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com> +Reviewed-by: Vladimir Oltean <olteanv@gmail.com> +Reviewed-by: Florian Fainelli <f.fainelli@gmail.com> +Signed-off-by: David S. Miller <davem@davemloft.net> +--- + net/dsa/tag_qca.c | 1 + + 1 file changed, 1 insertion(+) + +--- a/net/dsa/tag_qca.c ++++ b/net/dsa/tag_qca.c +@@ -68,6 +68,7 @@ static const struct dsa_device_ops qca_n + .xmit = qca_tag_xmit, + .rcv = qca_tag_rcv, + .needed_headroom = QCA_HDR_LEN, ++ .promisc_on_master = true, + }; + + MODULE_LICENSE("GPL"); diff --git a/pkgs/patches-linux-5.15/766-06-net-dsa-tag_qca-add-define-for-handling-mgmt-Etherne.patch b/pkgs/patches-linux-5.15/766-06-net-dsa-tag_qca-add-define-for-handling-mgmt-Etherne.patch new file mode 100644 index 0000000..459454e --- /dev/null +++ b/pkgs/patches-linux-5.15/766-06-net-dsa-tag_qca-add-define-for-handling-mgmt-Etherne.patch @@ -0,0 +1,110 @@ +From c2ee8181fddb293d296477f60b3eb4fa3ce4e1a6 Mon Sep 17 00:00:00 2001 +From: Ansuel Smith <ansuelsmth@gmail.com> +Date: Wed, 2 Feb 2022 01:03:25 +0100 +Subject: [PATCH 06/16] net: dsa: tag_qca: add define for handling mgmt + Ethernet packet + +Add all the required define to prepare support for mgmt read/write in +Ethernet packet. Any packet of this type has to be dropped as the only +use of these special packet is receive ack for an mgmt write request or +receive data for an mgmt read request. +A struct is used that emulates the Ethernet header but is used for a +different purpose. + +Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com> +Reviewed-by: Florian Fainelli <f.fainelli@gmail.com> +Signed-off-by: David S. Miller <davem@davemloft.net> +--- + include/linux/dsa/tag_qca.h | 44 +++++++++++++++++++++++++++++++++++++ + net/dsa/tag_qca.c | 15 ++++++++++--- + 2 files changed, 56 insertions(+), 3 deletions(-) + +--- a/include/linux/dsa/tag_qca.h ++++ b/include/linux/dsa/tag_qca.h +@@ -12,10 +12,54 @@ + #define QCA_HDR_RECV_FRAME_IS_TAGGED BIT(3) + #define QCA_HDR_RECV_SOURCE_PORT GENMASK(2, 0) + ++/* Packet type for recv */ ++#define QCA_HDR_RECV_TYPE_NORMAL 0x0 ++#define QCA_HDR_RECV_TYPE_MIB 0x1 ++#define QCA_HDR_RECV_TYPE_RW_REG_ACK 0x2 ++ + #define QCA_HDR_XMIT_VERSION GENMASK(15, 14) + #define QCA_HDR_XMIT_PRIORITY GENMASK(13, 11) + #define QCA_HDR_XMIT_CONTROL GENMASK(10, 8) + #define QCA_HDR_XMIT_FROM_CPU BIT(7) + #define QCA_HDR_XMIT_DP_BIT GENMASK(6, 0) + ++/* Packet type for xmit */ ++#define QCA_HDR_XMIT_TYPE_NORMAL 0x0 ++#define QCA_HDR_XMIT_TYPE_RW_REG 0x1 ++ ++/* Check code for a valid mgmt packet. Switch will ignore the packet ++ * with this wrong. ++ */ ++#define QCA_HDR_MGMT_CHECK_CODE_VAL 0x5 ++ ++/* Specific define for in-band MDIO read/write with Ethernet packet */ ++#define QCA_HDR_MGMT_SEQ_LEN 4 /* 4 byte for the seq */ ++#define QCA_HDR_MGMT_COMMAND_LEN 4 /* 4 byte for the command */ ++#define QCA_HDR_MGMT_DATA1_LEN 4 /* First 4 byte for the mdio data */ ++#define QCA_HDR_MGMT_HEADER_LEN (QCA_HDR_MGMT_SEQ_LEN + \ ++ QCA_HDR_MGMT_COMMAND_LEN + \ ++ QCA_HDR_MGMT_DATA1_LEN) ++ ++#define QCA_HDR_MGMT_DATA2_LEN 12 /* Other 12 byte for the mdio data */ ++#define QCA_HDR_MGMT_PADDING_LEN 34 /* Padding to reach the min Ethernet packet */ ++ ++#define QCA_HDR_MGMT_PKT_LEN (QCA_HDR_MGMT_HEADER_LEN + \ ++ QCA_HDR_LEN + \ ++ QCA_HDR_MGMT_DATA2_LEN + \ ++ QCA_HDR_MGMT_PADDING_LEN) ++ ++#define QCA_HDR_MGMT_SEQ_NUM GENMASK(31, 0) /* 63, 32 */ ++#define QCA_HDR_MGMT_CHECK_CODE GENMASK(31, 29) /* 31, 29 */ ++#define QCA_HDR_MGMT_CMD BIT(28) /* 28 */ ++#define QCA_HDR_MGMT_LENGTH GENMASK(23, 20) /* 23, 20 */ ++#define QCA_HDR_MGMT_ADDR GENMASK(18, 0) /* 18, 0 */ ++ ++/* Special struct emulating a Ethernet header */ ++struct qca_mgmt_ethhdr { ++ u32 command; /* command bit 31:0 */ ++ u32 seq; /* seq 63:32 */ ++ u32 mdio_data; /* first 4byte mdio */ ++ __be16 hdr; /* qca hdr */ ++} __packed; ++ + #endif /* __TAG_QCA_H */ +--- a/net/dsa/tag_qca.c ++++ b/net/dsa/tag_qca.c +@@ -32,10 +32,12 @@ static struct sk_buff *qca_tag_xmit(stru + + static struct sk_buff *qca_tag_rcv(struct sk_buff *skb, struct net_device *dev) + { +- u8 ver; +- u16 hdr; +- int port; ++ u8 ver, pk_type; + __be16 *phdr; ++ int port; ++ u16 hdr; ++ ++ BUILD_BUG_ON(sizeof(struct qca_mgmt_ethhdr) != QCA_HDR_MGMT_HEADER_LEN + QCA_HDR_LEN); + + if (unlikely(!pskb_may_pull(skb, QCA_HDR_LEN))) + return NULL; +@@ -48,6 +50,13 @@ static struct sk_buff *qca_tag_rcv(struc + if (unlikely(ver != QCA_HDR_VERSION)) + return NULL; + ++ /* Get pk type */ ++ pk_type = FIELD_GET(QCA_HDR_RECV_TYPE, hdr); ++ ++ /* Ethernet MDIO read/write packet */ ++ if (pk_type == QCA_HDR_RECV_TYPE_RW_REG_ACK) ++ return NULL; ++ + /* Remove QCA tag and recalculate checksum */ + skb_pull_rcsum(skb, QCA_HDR_LEN); + dsa_strip_etype_header(skb, QCA_HDR_LEN); diff --git a/pkgs/patches-linux-5.15/766-07-net-dsa-tag_qca-add-define-for-handling-MIB-packet.patch b/pkgs/patches-linux-5.15/766-07-net-dsa-tag_qca-add-define-for-handling-MIB-packet.patch new file mode 100644 index 0000000..7e5dc65 --- /dev/null +++ b/pkgs/patches-linux-5.15/766-07-net-dsa-tag_qca-add-define-for-handling-MIB-packet.patch @@ -0,0 +1,45 @@ +From 18be654a4345f7d937b4bfbad74bea8093e3a93c Mon Sep 17 00:00:00 2001 +From: Ansuel Smith <ansuelsmth@gmail.com> +Date: Wed, 2 Feb 2022 01:03:26 +0100 +Subject: [PATCH 07/16] net: dsa: tag_qca: add define for handling MIB packet + +Add struct to correctly parse a mib Ethernet packet. + +Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com> +Reviewed-by: Florian Fainelli <f.fainelli@gmail.com> +Signed-off-by: David S. Miller <davem@davemloft.net> +--- + include/linux/dsa/tag_qca.h | 10 ++++++++++ + net/dsa/tag_qca.c | 4 ++++ + 2 files changed, 14 insertions(+) + +--- a/include/linux/dsa/tag_qca.h ++++ b/include/linux/dsa/tag_qca.h +@@ -62,4 +62,14 @@ struct qca_mgmt_ethhdr { + __be16 hdr; /* qca hdr */ + } __packed; + ++enum mdio_cmd { ++ MDIO_WRITE = 0x0, ++ MDIO_READ ++}; ++ ++struct mib_ethhdr { ++ u32 data[3]; /* first 3 mib counter */ ++ __be16 hdr; /* qca hdr */ ++} __packed; ++ + #endif /* __TAG_QCA_H */ +--- a/net/dsa/tag_qca.c ++++ b/net/dsa/tag_qca.c +@@ -57,6 +57,10 @@ static struct sk_buff *qca_tag_rcv(struc + if (pk_type == QCA_HDR_RECV_TYPE_RW_REG_ACK) + return NULL; + ++ /* Ethernet MIB counter packet */ ++ if (pk_type == QCA_HDR_RECV_TYPE_MIB) ++ return NULL; ++ + /* Remove QCA tag and recalculate checksum */ + skb_pull_rcsum(skb, QCA_HDR_LEN); + dsa_strip_etype_header(skb, QCA_HDR_LEN); diff --git a/pkgs/patches-linux-5.15/766-08-net-dsa-tag_qca-add-support-for-handling-mgmt-and-MI.patch b/pkgs/patches-linux-5.15/766-08-net-dsa-tag_qca-add-support-for-handling-mgmt-and-MI.patch new file mode 100644 index 0000000..ad25da3 --- /dev/null +++ b/pkgs/patches-linux-5.15/766-08-net-dsa-tag_qca-add-support-for-handling-mgmt-and-MI.patch @@ -0,0 +1,116 @@ +From 31eb6b4386ad91930417e3f5c8157a4b5e31cbd5 Mon Sep 17 00:00:00 2001 +From: Ansuel Smith <ansuelsmth@gmail.com> +Date: Wed, 2 Feb 2022 01:03:27 +0100 +Subject: [PATCH 08/16] net: dsa: tag_qca: add support for handling mgmt and + MIB Ethernet packet + +Add connect/disconnect helper to assign private struct to the DSA switch. +Add support for Ethernet mgmt and MIB if the DSA driver provide an handler +to correctly parse and elaborate the data. + +Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com> +Reviewed-by: Vladimir Oltean <olteanv@gmail.com> +Reviewed-by: Florian Fainelli <f.fainelli@gmail.com> +Signed-off-by: David S. Miller <davem@davemloft.net> +--- + include/linux/dsa/tag_qca.h | 7 +++++++ + net/dsa/tag_qca.c | 39 ++++++++++++++++++++++++++++++++++--- + 2 files changed, 43 insertions(+), 3 deletions(-) + +--- a/include/linux/dsa/tag_qca.h ++++ b/include/linux/dsa/tag_qca.h +@@ -72,4 +72,11 @@ struct mib_ethhdr { + __be16 hdr; /* qca hdr */ + } __packed; + ++struct qca_tagger_data { ++ void (*rw_reg_ack_handler)(struct dsa_switch *ds, ++ struct sk_buff *skb); ++ void (*mib_autocast_handler)(struct dsa_switch *ds, ++ struct sk_buff *skb); ++}; ++ + #endif /* __TAG_QCA_H */ +--- a/net/dsa/tag_qca.c ++++ b/net/dsa/tag_qca.c +@@ -5,6 +5,7 @@ + + #include <linux/etherdevice.h> + #include <linux/bitfield.h> ++#include <net/dsa.h> + #include <linux/dsa/tag_qca.h> + + #include "dsa_priv.h" +@@ -32,6 +33,9 @@ static struct sk_buff *qca_tag_xmit(stru + + static struct sk_buff *qca_tag_rcv(struct sk_buff *skb, struct net_device *dev) + { ++ struct qca_tagger_data *tagger_data; ++ struct dsa_port *dp = dev->dsa_ptr; ++ struct dsa_switch *ds = dp->ds; + u8 ver, pk_type; + __be16 *phdr; + int port; +@@ -39,6 +43,8 @@ static struct sk_buff *qca_tag_rcv(struc + + BUILD_BUG_ON(sizeof(struct qca_mgmt_ethhdr) != QCA_HDR_MGMT_HEADER_LEN + QCA_HDR_LEN); + ++ tagger_data = ds->tagger_data; ++ + if (unlikely(!pskb_may_pull(skb, QCA_HDR_LEN))) + return NULL; + +@@ -53,13 +59,19 @@ static struct sk_buff *qca_tag_rcv(struc + /* Get pk type */ + pk_type = FIELD_GET(QCA_HDR_RECV_TYPE, hdr); + +- /* Ethernet MDIO read/write packet */ +- if (pk_type == QCA_HDR_RECV_TYPE_RW_REG_ACK) ++ /* Ethernet mgmt read/write packet */ ++ if (pk_type == QCA_HDR_RECV_TYPE_RW_REG_ACK) { ++ if (likely(tagger_data->rw_reg_ack_handler)) ++ tagger_data->rw_reg_ack_handler(ds, skb); + return NULL; ++ } + + /* Ethernet MIB counter packet */ +- if (pk_type == QCA_HDR_RECV_TYPE_MIB) ++ if (pk_type == QCA_HDR_RECV_TYPE_MIB) { ++ if (likely(tagger_data->mib_autocast_handler)) ++ tagger_data->mib_autocast_handler(ds, skb); + return NULL; ++ } + + /* Remove QCA tag and recalculate checksum */ + skb_pull_rcsum(skb, QCA_HDR_LEN); +@@ -75,9 +87,30 @@ static struct sk_buff *qca_tag_rcv(struc + return skb; + } + ++static int qca_tag_connect(struct dsa_switch *ds) ++{ ++ struct qca_tagger_data *tagger_data; ++ ++ tagger_data = kzalloc(sizeof(*tagger_data), GFP_KERNEL); ++ if (!tagger_data) ++ return -ENOMEM; ++ ++ ds->tagger_data = tagger_data; ++ ++ return 0; ++} ++ ++static void qca_tag_disconnect(struct dsa_switch *ds) ++{ ++ kfree(ds->tagger_data); ++ ds->tagger_data = NULL; ++} ++ + static const struct dsa_device_ops qca_netdev_ops = { + .name = "qca", + .proto = DSA_TAG_PROTO_QCA, ++ .connect = qca_tag_connect, ++ .disconnect = qca_tag_disconnect, + .xmit = qca_tag_xmit, + .rcv = qca_tag_rcv, + .needed_headroom = QCA_HDR_LEN, diff --git a/pkgs/patches-linux-5.15/766-09-net-dsa-qca8k-add-tracking-state-of-master-port.patch b/pkgs/patches-linux-5.15/766-09-net-dsa-qca8k-add-tracking-state-of-master-port.patch new file mode 100644 index 0000000..ff8fdca --- /dev/null +++ b/pkgs/patches-linux-5.15/766-09-net-dsa-qca8k-add-tracking-state-of-master-port.patch @@ -0,0 +1,67 @@ +From cddbec19466a1dfb4d45ddd507d9f09f991d54ae Mon Sep 17 00:00:00 2001 +From: Ansuel Smith <ansuelsmth@gmail.com> +Date: Wed, 2 Feb 2022 01:03:28 +0100 +Subject: [PATCH 09/16] net: dsa: qca8k: add tracking state of master port + +MDIO/MIB Ethernet require the master port and the tagger availabale to +correctly work. Use the new api master_state_change to track when master +is operational or not and set a bool in qca8k_priv. +We cache the first cached master available and we check if other cpu +port are operational when the cached one goes down. +This cached master will later be used by mdio read/write and mib request to +correctly use the working function. + +qca8k implementation for MDIO/MIB Ethernet is bad. CPU port0 is the only +one that answers with the ack packet or sends MIB Ethernet packets. For +this reason the master_state_change ignore CPU port6 and only checks +CPU port0 if it's operational and enables this mode. + +Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com> +Reviewed-by: Florian Fainelli <f.fainelli@gmail.com> +Signed-off-by: David S. Miller <davem@davemloft.net> +--- + drivers/net/dsa/qca8k.c | 15 +++++++++++++++ + drivers/net/dsa/qca8k.h | 1 + + 2 files changed, 16 insertions(+) + +--- a/drivers/net/dsa/qca8k.c ++++ b/drivers/net/dsa/qca8k.c +@@ -2382,6 +2382,20 @@ qca8k_port_lag_leave(struct dsa_switch * + return qca8k_lag_refresh_portmap(ds, port, lag, true); + } + ++static void ++qca8k_master_change(struct dsa_switch *ds, const struct net_device *master, ++ bool operational) ++{ ++ struct dsa_port *dp = master->dsa_ptr; ++ struct qca8k_priv *priv = ds->priv; ++ ++ /* Ethernet MIB/MDIO is only supported for CPU port 0 */ ++ if (dp->index != 0) ++ return; ++ ++ priv->mgmt_master = operational ? (struct net_device *)master : NULL; ++} ++ + static const struct dsa_switch_ops qca8k_switch_ops = { + .get_tag_protocol = qca8k_get_tag_protocol, + .setup = qca8k_setup, +@@ -2417,6 +2431,7 @@ static const struct dsa_switch_ops qca8k + .get_phy_flags = qca8k_get_phy_flags, + .port_lag_join = qca8k_port_lag_join, + .port_lag_leave = qca8k_port_lag_leave, ++ .master_state_change = qca8k_master_change, + }; + + static int qca8k_read_switch_id(struct qca8k_priv *priv) +--- a/drivers/net/dsa/qca8k.h ++++ b/drivers/net/dsa/qca8k.h +@@ -353,6 +353,7 @@ struct qca8k_priv { + struct dsa_switch_ops ops; + struct gpio_desc *reset_gpio; + unsigned int port_mtu[QCA8K_NUM_PORTS]; ++ struct net_device *mgmt_master; /* Track if mdio/mib Ethernet is available */ + }; + + struct qca8k_mib_desc { diff --git a/pkgs/patches-linux-5.15/766-10-net-dsa-qca8k-add-support-for-mgmt-read-write-in-Eth.patch b/pkgs/patches-linux-5.15/766-10-net-dsa-qca8k-add-support-for-mgmt-read-write-in-Eth.patch new file mode 100644 index 0000000..43656ad --- /dev/null +++ b/pkgs/patches-linux-5.15/766-10-net-dsa-qca8k-add-support-for-mgmt-read-write-in-Eth.patch @@ -0,0 +1,363 @@ +From 5950c7c0a68c915b336c70f79388626e2d576ab7 Mon Sep 17 00:00:00 2001 +From: Ansuel Smith <ansuelsmth@gmail.com> +Date: Wed, 2 Feb 2022 01:03:29 +0100 +Subject: [PATCH 10/16] net: dsa: qca8k: add support for mgmt read/write in + Ethernet packet + +Add qca8k side support for mgmt read/write in Ethernet packet. +qca8k supports some specially crafted Ethernet packet that can be used +for mgmt read/write instead of the legacy method uart/internal mdio. +This add support for the qca8k side to craft the packet and enqueue it. +Each port and the qca8k_priv have a special struct to put data in it. +The completion API is used to wait for the packet to be received back +with the requested data. + +The various steps are: +1. Craft the special packet with the qca hdr set to mgmt read/write + mode. +2. Set the lock in the dedicated mgmt struct. +3. Increment the seq number and set it in the mgmt pkt +4. Reinit the completion. +5. Enqueue the packet. +6. Wait the packet to be received. +7. Use the data set by the tagger to complete the mdio operation. + +If the completion timeouts or the ack value is not true, the legacy +mdio way is used. + +It has to be considered that in the initial setup mdio is still used and +mdio is still used until DSA is ready to accept and tag packet. + +tag_proto_connect() is used to fill the required handler for the tagger +to correctly parse and elaborate the special Ethernet mdio packet. + +Locking is added to qca8k_master_change() to make sure no mgmt Ethernet +are in progress. + +Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com> +Signed-off-by: David S. Miller <davem@davemloft.net> +--- + drivers/net/dsa/qca8k.c | 225 ++++++++++++++++++++++++++++++++++++++++ + drivers/net/dsa/qca8k.h | 13 +++ + 2 files changed, 238 insertions(+) + +--- a/drivers/net/dsa/qca8k.c ++++ b/drivers/net/dsa/qca8k.c +@@ -20,6 +20,7 @@ + #include <linux/phylink.h> + #include <linux/gpio/consumer.h> + #include <linux/etherdevice.h> ++#include <linux/dsa/tag_qca.h> + + #include "qca8k.h" + +@@ -170,6 +171,194 @@ qca8k_rmw(struct qca8k_priv *priv, u32 r + return regmap_update_bits(priv->regmap, reg, mask, write_val); + } + ++static void qca8k_rw_reg_ack_handler(struct dsa_switch *ds, struct sk_buff *skb) ++{ ++ struct qca8k_mgmt_eth_data *mgmt_eth_data; ++ struct qca8k_priv *priv = ds->priv; ++ struct qca_mgmt_ethhdr *mgmt_ethhdr; ++ u8 len, cmd; ++ ++ mgmt_ethhdr = (struct qca_mgmt_ethhdr *)skb_mac_header(skb); ++ mgmt_eth_data = &priv->mgmt_eth_data; ++ ++ cmd = FIELD_GET(QCA_HDR_MGMT_CMD, mgmt_ethhdr->command); ++ len = FIELD_GET(QCA_HDR_MGMT_LENGTH, mgmt_ethhdr->command); ++ ++ /* Make sure the seq match the requested packet */ ++ if (mgmt_ethhdr->seq == mgmt_eth_data->seq) ++ mgmt_eth_data->ack = true; ++ ++ if (cmd == MDIO_READ) { ++ mgmt_eth_data->data[0] = mgmt_ethhdr->mdio_data; ++ ++ /* Get the rest of the 12 byte of data */ ++ if (len > QCA_HDR_MGMT_DATA1_LEN) ++ memcpy(mgmt_eth_data->data + 1, skb->data, ++ QCA_HDR_MGMT_DATA2_LEN); ++ } ++ ++ complete(&mgmt_eth_data->rw_done); ++} ++ ++static struct sk_buff *qca8k_alloc_mdio_header(enum mdio_cmd cmd, u32 reg, u32 *val, ++ int priority) ++{ ++ struct qca_mgmt_ethhdr *mgmt_ethhdr; ++ struct sk_buff *skb; ++ u16 hdr; ++ ++ skb = dev_alloc_skb(QCA_HDR_MGMT_PKT_LEN); ++ if (!skb) ++ return NULL; ++ ++ skb_reset_mac_header(skb); ++ skb_set_network_header(skb, skb->len); ++ ++ mgmt_ethhdr = skb_push(skb, QCA_HDR_MGMT_HEADER_LEN + QCA_HDR_LEN); ++ ++ hdr = FIELD_PREP(QCA_HDR_XMIT_VERSION, QCA_HDR_VERSION); ++ hdr |= FIELD_PREP(QCA_HDR_XMIT_PRIORITY, priority); ++ hdr |= QCA_HDR_XMIT_FROM_CPU; ++ hdr |= FIELD_PREP(QCA_HDR_XMIT_DP_BIT, BIT(0)); ++ hdr |= FIELD_PREP(QCA_HDR_XMIT_CONTROL, QCA_HDR_XMIT_TYPE_RW_REG); ++ ++ mgmt_ethhdr->command = FIELD_PREP(QCA_HDR_MGMT_ADDR, reg); ++ mgmt_ethhdr->command |= FIELD_PREP(QCA_HDR_MGMT_LENGTH, 4); ++ mgmt_ethhdr->command |= FIELD_PREP(QCA_HDR_MGMT_CMD, cmd); ++ mgmt_ethhdr->command |= FIELD_PREP(QCA_HDR_MGMT_CHECK_CODE, ++ QCA_HDR_MGMT_CHECK_CODE_VAL); ++ ++ if (cmd == MDIO_WRITE) ++ mgmt_ethhdr->mdio_data = *val; ++ ++ mgmt_ethhdr->hdr = htons(hdr); ++ ++ skb_put_zero(skb, QCA_HDR_MGMT_DATA2_LEN + QCA_HDR_MGMT_PADDING_LEN); ++ ++ return skb; ++} ++ ++static void qca8k_mdio_header_fill_seq_num(struct sk_buff *skb, u32 seq_num) ++{ ++ struct qca_mgmt_ethhdr *mgmt_ethhdr; ++ ++ mgmt_ethhdr = (struct qca_mgmt_ethhdr *)skb->data; ++ mgmt_ethhdr->seq = FIELD_PREP(QCA_HDR_MGMT_SEQ_NUM, seq_num); ++} ++ ++static int qca8k_read_eth(struct qca8k_priv *priv, u32 reg, u32 *val) ++{ ++ struct qca8k_mgmt_eth_data *mgmt_eth_data = &priv->mgmt_eth_data; ++ struct sk_buff *skb; ++ bool ack; ++ int ret; ++ ++ skb = qca8k_alloc_mdio_header(MDIO_READ, reg, NULL, ++ QCA8K_ETHERNET_MDIO_PRIORITY); ++ if (!skb) ++ return -ENOMEM; ++ ++ mutex_lock(&mgmt_eth_data->mutex); ++ ++ /* Check mgmt_master if is operational */ ++ if (!priv->mgmt_master) { ++ kfree_skb(skb); ++ mutex_unlock(&mgmt_eth_data->mutex); ++ return -EINVAL; ++ } ++ ++ skb->dev = priv->mgmt_master; ++ ++ reinit_completion(&mgmt_eth_data->rw_done); ++ ++ /* Increment seq_num and set it in the mdio pkt */ ++ mgmt_eth_data->seq++; ++ qca8k_mdio_header_fill_seq_num(skb, mgmt_eth_data->seq); ++ mgmt_eth_data->ack = false; ++ ++ dev_queue_xmit(skb); ++ ++ ret = wait_for_completion_timeout(&mgmt_eth_data->rw_done, ++ msecs_to_jiffies(QCA8K_ETHERNET_TIMEOUT)); ++ ++ *val = mgmt_eth_data->data[0]; ++ ack = mgmt_eth_data->ack; ++ ++ mutex_unlock(&mgmt_eth_data->mutex); ++ ++ if (ret <= 0) ++ return -ETIMEDOUT; ++ ++ if (!ack) ++ return -EINVAL; ++ ++ return 0; ++} ++ ++static int qca8k_write_eth(struct qca8k_priv *priv, u32 reg, u32 val) ++{ ++ struct qca8k_mgmt_eth_data *mgmt_eth_data = &priv->mgmt_eth_data; ++ struct sk_buff *skb; ++ bool ack; ++ int ret; ++ ++ skb = qca8k_alloc_mdio_header(MDIO_WRITE, reg, &val, ++ QCA8K_ETHERNET_MDIO_PRIORITY); ++ if (!skb) ++ return -ENOMEM; ++ ++ mutex_lock(&mgmt_eth_data->mutex); ++ ++ /* Check mgmt_master if is operational */ ++ if (!priv->mgmt_master) { ++ kfree_skb(skb); ++ mutex_unlock(&mgmt_eth_data->mutex); ++ return -EINVAL; ++ } ++ ++ skb->dev = priv->mgmt_master; ++ ++ reinit_completion(&mgmt_eth_data->rw_done); ++ ++ /* Increment seq_num and set it in the mdio pkt */ ++ mgmt_eth_data->seq++; ++ qca8k_mdio_header_fill_seq_num(skb, mgmt_eth_data->seq); ++ mgmt_eth_data->ack = false; ++ ++ dev_queue_xmit(skb); ++ ++ ret = wait_for_completion_timeout(&mgmt_eth_data->rw_done, ++ msecs_to_jiffies(QCA8K_ETHERNET_TIMEOUT)); ++ ++ ack = mgmt_eth_data->ack; ++ ++ mutex_unlock(&mgmt_eth_data->mutex); ++ ++ if (ret <= 0) ++ return -ETIMEDOUT; ++ ++ if (!ack) ++ return -EINVAL; ++ ++ return 0; ++} ++ ++static int ++qca8k_regmap_update_bits_eth(struct qca8k_priv *priv, u32 reg, u32 mask, u32 write_val) ++{ ++ u32 val = 0; ++ int ret; ++ ++ ret = qca8k_read_eth(priv, reg, &val); ++ if (ret) ++ return ret; ++ ++ val &= ~mask; ++ val |= write_val; ++ ++ return qca8k_write_eth(priv, reg, val); ++} ++ + static int + qca8k_regmap_read(void *ctx, uint32_t reg, uint32_t *val) + { +@@ -178,6 +367,9 @@ qca8k_regmap_read(void *ctx, uint32_t re + u16 r1, r2, page; + int ret; + ++ if (!qca8k_read_eth(priv, reg, val)) ++ return 0; ++ + qca8k_split_addr(reg, &r1, &r2, &page); + + mutex_lock_nested(&bus->mdio_lock, MDIO_MUTEX_NESTED); +@@ -201,6 +393,9 @@ qca8k_regmap_write(void *ctx, uint32_t r + u16 r1, r2, page; + int ret; + ++ if (!qca8k_write_eth(priv, reg, val)) ++ return 0; ++ + qca8k_split_addr(reg, &r1, &r2, &page); + + mutex_lock_nested(&bus->mdio_lock, MDIO_MUTEX_NESTED); +@@ -225,6 +420,9 @@ qca8k_regmap_update_bits(void *ctx, uint + u32 val; + int ret; + ++ if (!qca8k_regmap_update_bits_eth(priv, reg, mask, write_val)) ++ return 0; ++ + qca8k_split_addr(reg, &r1, &r2, &page); + + mutex_lock_nested(&bus->mdio_lock, MDIO_MUTEX_NESTED); +@@ -2393,7 +2591,30 @@ qca8k_master_change(struct dsa_switch *d + if (dp->index != 0) + return; + ++ mutex_lock(&priv->mgmt_eth_data.mutex); ++ + priv->mgmt_master = operational ? (struct net_device *)master : NULL; ++ ++ mutex_unlock(&priv->mgmt_eth_data.mutex); ++} ++ ++static int qca8k_connect_tag_protocol(struct dsa_switch *ds, ++ enum dsa_tag_protocol proto) ++{ ++ struct qca_tagger_data *tagger_data; ++ ++ switch (proto) { ++ case DSA_TAG_PROTO_QCA: ++ tagger_data = ds->tagger_data; ++ ++ tagger_data->rw_reg_ack_handler = qca8k_rw_reg_ack_handler; ++ ++ break; ++ default: ++ return -EOPNOTSUPP; ++ } ++ ++ return 0; + } + + static const struct dsa_switch_ops qca8k_switch_ops = { +@@ -2432,6 +2653,7 @@ static const struct dsa_switch_ops qca8k + .port_lag_join = qca8k_port_lag_join, + .port_lag_leave = qca8k_port_lag_leave, + .master_state_change = qca8k_master_change, ++ .connect_tag_protocol = qca8k_connect_tag_protocol, + }; + + static int qca8k_read_switch_id(struct qca8k_priv *priv) +@@ -2511,6 +2733,9 @@ qca8k_sw_probe(struct mdio_device *mdiod + if (!priv->ds) + return -ENOMEM; + ++ mutex_init(&priv->mgmt_eth_data.mutex); ++ init_completion(&priv->mgmt_eth_data.rw_done); ++ + priv->ds->dev = &mdiodev->dev; + priv->ds->num_ports = QCA8K_NUM_PORTS; + priv->ds->priv = priv; +--- a/drivers/net/dsa/qca8k.h ++++ b/drivers/net/dsa/qca8k.h +@@ -11,6 +11,10 @@ + #include <linux/delay.h> + #include <linux/regmap.h> + #include <linux/gpio.h> ++#include <linux/dsa/tag_qca.h> ++ ++#define QCA8K_ETHERNET_MDIO_PRIORITY 7 ++#define QCA8K_ETHERNET_TIMEOUT 100 + + #define QCA8K_NUM_PORTS 7 + #define QCA8K_NUM_CPU_PORTS 2 +@@ -328,6 +332,14 @@ enum { + QCA8K_CPU_PORT6, + }; + ++struct qca8k_mgmt_eth_data { ++ struct completion rw_done; ++ struct mutex mutex; /* Enforce one mdio read/write at time */ ++ bool ack; ++ u32 seq; ++ u32 data[4]; ++}; ++ + struct qca8k_ports_config { + bool sgmii_rx_clk_falling_edge; + bool sgmii_tx_clk_falling_edge; +@@ -354,6 +366,7 @@ struct qca8k_priv { + struct gpio_desc *reset_gpio; + unsigned int port_mtu[QCA8K_NUM_PORTS]; + struct net_device *mgmt_master; /* Track if mdio/mib Ethernet is available */ ++ struct qca8k_mgmt_eth_data mgmt_eth_data; + }; + + struct qca8k_mib_desc { diff --git a/pkgs/patches-linux-5.15/766-11-net-dsa-qca8k-add-support-for-mib-autocast-in-Ethern.patch b/pkgs/patches-linux-5.15/766-11-net-dsa-qca8k-add-support-for-mib-autocast-in-Ethern.patch new file mode 100644 index 0000000..c4bc2b3 --- /dev/null +++ b/pkgs/patches-linux-5.15/766-11-net-dsa-qca8k-add-support-for-mib-autocast-in-Ethern.patch @@ -0,0 +1,226 @@ +From 5c957c7ca78cce5e4b96866722b0115bd758d945 Mon Sep 17 00:00:00 2001 +From: Ansuel Smith <ansuelsmth@gmail.com> +Date: Wed, 2 Feb 2022 01:03:30 +0100 +Subject: [PATCH 11/16] net: dsa: qca8k: add support for mib autocast in + Ethernet packet + +The switch can autocast MIB counter using Ethernet packet. +Add support for this and provide a handler for the tagger. +The switch will send packet with MIB counter for each port, the switch +will use completion API to wait for the correct packet to be received +and will complete the task only when each packet is received. +Although the handler will drop all the other packet, we still have to +consume each MIB packet to complete the request. This is done to prevent +mixed data with concurrent ethtool request. + +connect_tag_protocol() is used to add the handler to the tag_qca tagger, +master_state_change() use the MIB lock to make sure no MIB Ethernet is +in progress. + +Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com> +Signed-off-by: David S. Miller <davem@davemloft.net> +--- + drivers/net/dsa/qca8k.c | 106 +++++++++++++++++++++++++++++++++++++++- + drivers/net/dsa/qca8k.h | 17 ++++++- + 2 files changed, 121 insertions(+), 2 deletions(-) + +--- a/drivers/net/dsa/qca8k.c ++++ b/drivers/net/dsa/qca8k.c +@@ -830,7 +830,10 @@ qca8k_mib_init(struct qca8k_priv *priv) + int ret; + + mutex_lock(&priv->reg_mutex); +- ret = regmap_set_bits(priv->regmap, QCA8K_REG_MIB, QCA8K_MIB_FLUSH | QCA8K_MIB_BUSY); ++ ret = regmap_update_bits(priv->regmap, QCA8K_REG_MIB, ++ QCA8K_MIB_FUNC | QCA8K_MIB_BUSY, ++ FIELD_PREP(QCA8K_MIB_FUNC, QCA8K_MIB_FLUSH) | ++ QCA8K_MIB_BUSY); + if (ret) + goto exit; + +@@ -1901,6 +1904,97 @@ qca8k_get_strings(struct dsa_switch *ds, + ETH_GSTRING_LEN); + } + ++static void qca8k_mib_autocast_handler(struct dsa_switch *ds, struct sk_buff *skb) ++{ ++ const struct qca8k_match_data *match_data; ++ struct qca8k_mib_eth_data *mib_eth_data; ++ struct qca8k_priv *priv = ds->priv; ++ const struct qca8k_mib_desc *mib; ++ struct mib_ethhdr *mib_ethhdr; ++ int i, mib_len, offset = 0; ++ u64 *data; ++ u8 port; ++ ++ mib_ethhdr = (struct mib_ethhdr *)skb_mac_header(skb); ++ mib_eth_data = &priv->mib_eth_data; ++ ++ /* The switch autocast every port. Ignore other packet and ++ * parse only the requested one. ++ */ ++ port = FIELD_GET(QCA_HDR_RECV_SOURCE_PORT, ntohs(mib_ethhdr->hdr)); ++ if (port != mib_eth_data->req_port) ++ goto exit; ++ ++ match_data = device_get_match_data(priv->dev); ++ data = mib_eth_data->data; ++ ++ for (i = 0; i < match_data->mib_count; i++) { ++ mib = &ar8327_mib[i]; ++ ++ /* First 3 mib are present in the skb head */ ++ if (i < 3) { ++ data[i] = mib_ethhdr->data[i]; ++ continue; ++ } ++ ++ mib_len = sizeof(uint32_t); ++ ++ /* Some mib are 64 bit wide */ ++ if (mib->size == 2) ++ mib_len = sizeof(uint64_t); ++ ++ /* Copy the mib value from packet to the */ ++ memcpy(data + i, skb->data + offset, mib_len); ++ ++ /* Set the offset for the next mib */ ++ offset += mib_len; ++ } ++ ++exit: ++ /* Complete on receiving all the mib packet */ ++ if (refcount_dec_and_test(&mib_eth_data->port_parsed)) ++ complete(&mib_eth_data->rw_done); ++} ++ ++static int ++qca8k_get_ethtool_stats_eth(struct dsa_switch *ds, int port, u64 *data) ++{ ++ struct dsa_port *dp = dsa_to_port(ds, port); ++ struct qca8k_mib_eth_data *mib_eth_data; ++ struct qca8k_priv *priv = ds->priv; ++ int ret; ++ ++ mib_eth_data = &priv->mib_eth_data; ++ ++ mutex_lock(&mib_eth_data->mutex); ++ ++ reinit_completion(&mib_eth_data->rw_done); ++ ++ mib_eth_data->req_port = dp->index; ++ mib_eth_data->data = data; ++ refcount_set(&mib_eth_data->port_parsed, QCA8K_NUM_PORTS); ++ ++ mutex_lock(&priv->reg_mutex); ++ ++ /* Send mib autocast request */ ++ ret = regmap_update_bits(priv->regmap, QCA8K_REG_MIB, ++ QCA8K_MIB_FUNC | QCA8K_MIB_BUSY, ++ FIELD_PREP(QCA8K_MIB_FUNC, QCA8K_MIB_CAST) | ++ QCA8K_MIB_BUSY); ++ ++ mutex_unlock(&priv->reg_mutex); ++ ++ if (ret) ++ goto exit; ++ ++ ret = wait_for_completion_timeout(&mib_eth_data->rw_done, QCA8K_ETHERNET_TIMEOUT); ++ ++exit: ++ mutex_unlock(&mib_eth_data->mutex); ++ ++ return ret; ++} ++ + static void + qca8k_get_ethtool_stats(struct dsa_switch *ds, int port, + uint64_t *data) +@@ -1912,6 +2006,10 @@ qca8k_get_ethtool_stats(struct dsa_switc + u32 hi = 0; + int ret; + ++ if (priv->mgmt_master && ++ qca8k_get_ethtool_stats_eth(ds, port, data) > 0) ++ return; ++ + match_data = of_device_get_match_data(priv->dev); + + for (i = 0; i < match_data->mib_count; i++) { +@@ -2592,9 +2690,11 @@ qca8k_master_change(struct dsa_switch *d + return; + + mutex_lock(&priv->mgmt_eth_data.mutex); ++ mutex_lock(&priv->mib_eth_data.mutex); + + priv->mgmt_master = operational ? (struct net_device *)master : NULL; + ++ mutex_unlock(&priv->mib_eth_data.mutex); + mutex_unlock(&priv->mgmt_eth_data.mutex); + } + +@@ -2608,6 +2708,7 @@ static int qca8k_connect_tag_protocol(st + tagger_data = ds->tagger_data; + + tagger_data->rw_reg_ack_handler = qca8k_rw_reg_ack_handler; ++ tagger_data->mib_autocast_handler = qca8k_mib_autocast_handler; + + break; + default: +@@ -2736,6 +2837,9 @@ qca8k_sw_probe(struct mdio_device *mdiod + mutex_init(&priv->mgmt_eth_data.mutex); + init_completion(&priv->mgmt_eth_data.rw_done); + ++ mutex_init(&priv->mib_eth_data.mutex); ++ init_completion(&priv->mib_eth_data.rw_done); ++ + priv->ds->dev = &mdiodev->dev; + priv->ds->num_ports = QCA8K_NUM_PORTS; + priv->ds->priv = priv; +--- a/drivers/net/dsa/qca8k.h ++++ b/drivers/net/dsa/qca8k.h +@@ -67,7 +67,7 @@ + #define QCA8K_REG_MODULE_EN 0x030 + #define QCA8K_MODULE_EN_MIB BIT(0) + #define QCA8K_REG_MIB 0x034 +-#define QCA8K_MIB_FLUSH BIT(24) ++#define QCA8K_MIB_FUNC GENMASK(26, 24) + #define QCA8K_MIB_CPU_KEEP BIT(20) + #define QCA8K_MIB_BUSY BIT(17) + #define QCA8K_MDIO_MASTER_CTRL 0x3c +@@ -317,6 +317,12 @@ enum qca8k_vlan_cmd { + QCA8K_VLAN_READ = 6, + }; + ++enum qca8k_mid_cmd { ++ QCA8K_MIB_FLUSH = 1, ++ QCA8K_MIB_FLUSH_PORT = 2, ++ QCA8K_MIB_CAST = 3, ++}; ++ + struct ar8xxx_port_status { + int enabled; + }; +@@ -340,6 +346,14 @@ struct qca8k_mgmt_eth_data { + u32 data[4]; + }; + ++struct qca8k_mib_eth_data { ++ struct completion rw_done; ++ struct mutex mutex; /* Process one command at time */ ++ refcount_t port_parsed; /* Counter to track parsed port */ ++ u8 req_port; ++ u64 *data; /* pointer to ethtool data */ ++}; ++ + struct qca8k_ports_config { + bool sgmii_rx_clk_falling_edge; + bool sgmii_tx_clk_falling_edge; +@@ -367,6 +381,7 @@ struct qca8k_priv { + unsigned int port_mtu[QCA8K_NUM_PORTS]; + struct net_device *mgmt_master; /* Track if mdio/mib Ethernet is available */ + struct qca8k_mgmt_eth_data mgmt_eth_data; ++ struct qca8k_mib_eth_data mib_eth_data; + }; + + struct qca8k_mib_desc { diff --git a/pkgs/patches-linux-5.15/766-12-net-dsa-qca8k-add-support-for-phy-read-write-with-mg.patch b/pkgs/patches-linux-5.15/766-12-net-dsa-qca8k-add-support-for-phy-read-write-with-mg.patch new file mode 100644 index 0000000..f5899eb --- /dev/null +++ b/pkgs/patches-linux-5.15/766-12-net-dsa-qca8k-add-support-for-phy-read-write-with-mg.patch @@ -0,0 +1,287 @@ +From 2cd5485663847d468dc207b3ff85fb1fab44d97f Mon Sep 17 00:00:00 2001 +From: Ansuel Smith <ansuelsmth@gmail.com> +Date: Wed, 2 Feb 2022 01:03:31 +0100 +Subject: [PATCH 12/16] net: dsa: qca8k: add support for phy read/write with + mgmt Ethernet + +Use mgmt Ethernet also for phy read/write if availabale. Use a different +seq number to make sure we receive the correct packet. +On any error, we fallback to the legacy mdio read/write. + +Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com> +Signed-off-by: David S. Miller <davem@davemloft.net> +--- + drivers/net/dsa/qca8k.c | 216 ++++++++++++++++++++++++++++++++++++++++ + drivers/net/dsa/qca8k.h | 1 + + 2 files changed, 217 insertions(+) + +--- a/drivers/net/dsa/qca8k.c ++++ b/drivers/net/dsa/qca8k.c +@@ -867,6 +867,199 @@ qca8k_port_set_status(struct qca8k_priv + regmap_clear_bits(priv->regmap, QCA8K_REG_PORT_STATUS(port), mask); + } + ++static int ++qca8k_phy_eth_busy_wait(struct qca8k_mgmt_eth_data *mgmt_eth_data, ++ struct sk_buff *read_skb, u32 *val) ++{ ++ struct sk_buff *skb = skb_copy(read_skb, GFP_KERNEL); ++ bool ack; ++ int ret; ++ ++ reinit_completion(&mgmt_eth_data->rw_done); ++ ++ /* Increment seq_num and set it in the copy pkt */ ++ mgmt_eth_data->seq++; ++ qca8k_mdio_header_fill_seq_num(skb, mgmt_eth_data->seq); ++ mgmt_eth_data->ack = false; ++ ++ dev_queue_xmit(skb); ++ ++ ret = wait_for_completion_timeout(&mgmt_eth_data->rw_done, ++ QCA8K_ETHERNET_TIMEOUT); ++ ++ ack = mgmt_eth_data->ack; ++ ++ if (ret <= 0) ++ return -ETIMEDOUT; ++ ++ if (!ack) ++ return -EINVAL; ++ ++ *val = mgmt_eth_data->data[0]; ++ ++ return 0; ++} ++ ++static int ++qca8k_phy_eth_command(struct qca8k_priv *priv, bool read, int phy, ++ int regnum, u16 data) ++{ ++ struct sk_buff *write_skb, *clear_skb, *read_skb; ++ struct qca8k_mgmt_eth_data *mgmt_eth_data; ++ u32 write_val, clear_val = 0, val; ++ struct net_device *mgmt_master; ++ int ret, ret1; ++ bool ack; ++ ++ if (regnum >= QCA8K_MDIO_MASTER_MAX_REG) ++ return -EINVAL; ++ ++ mgmt_eth_data = &priv->mgmt_eth_data; ++ ++ write_val = QCA8K_MDIO_MASTER_BUSY | QCA8K_MDIO_MASTER_EN | ++ QCA8K_MDIO_MASTER_PHY_ADDR(phy) | ++ QCA8K_MDIO_MASTER_REG_ADDR(regnum); ++ ++ if (read) { ++ write_val |= QCA8K_MDIO_MASTER_READ; ++ } else { ++ write_val |= QCA8K_MDIO_MASTER_WRITE; ++ write_val |= QCA8K_MDIO_MASTER_DATA(data); ++ } ++ ++ /* Prealloc all the needed skb before the lock */ ++ write_skb = qca8k_alloc_mdio_header(MDIO_WRITE, QCA8K_MDIO_MASTER_CTRL, ++ &write_val, QCA8K_ETHERNET_PHY_PRIORITY); ++ if (!write_skb) ++ return -ENOMEM; ++ ++ clear_skb = qca8k_alloc_mdio_header(MDIO_WRITE, QCA8K_MDIO_MASTER_CTRL, ++ &clear_val, QCA8K_ETHERNET_PHY_PRIORITY); ++ if (!write_skb) { ++ ret = -ENOMEM; ++ goto err_clear_skb; ++ } ++ ++ read_skb = qca8k_alloc_mdio_header(MDIO_READ, QCA8K_MDIO_MASTER_CTRL, ++ &clear_val, QCA8K_ETHERNET_PHY_PRIORITY); ++ if (!write_skb) { ++ ret = -ENOMEM; ++ goto err_read_skb; ++ } ++ ++ /* Actually start the request: ++ * 1. Send mdio master packet ++ * 2. Busy Wait for mdio master command ++ * 3. Get the data if we are reading ++ * 4. Reset the mdio master (even with error) ++ */ ++ mutex_lock(&mgmt_eth_data->mutex); ++ ++ /* Check if mgmt_master is operational */ ++ mgmt_master = priv->mgmt_master; ++ if (!mgmt_master) { ++ mutex_unlock(&mgmt_eth_data->mutex); ++ ret = -EINVAL; ++ goto err_mgmt_master; ++ } ++ ++ read_skb->dev = mgmt_master; ++ clear_skb->dev = mgmt_master; ++ write_skb->dev = mgmt_master; ++ ++ reinit_completion(&mgmt_eth_data->rw_done); ++ ++ /* Increment seq_num and set it in the write pkt */ ++ mgmt_eth_data->seq++; ++ qca8k_mdio_header_fill_seq_num(write_skb, mgmt_eth_data->seq); ++ mgmt_eth_data->ack = false; ++ ++ dev_queue_xmit(write_skb); ++ ++ ret = wait_for_completion_timeout(&mgmt_eth_data->rw_done, ++ QCA8K_ETHERNET_TIMEOUT); ++ ++ ack = mgmt_eth_data->ack; ++ ++ if (ret <= 0) { ++ ret = -ETIMEDOUT; ++ kfree_skb(read_skb); ++ goto exit; ++ } ++ ++ if (!ack) { ++ ret = -EINVAL; ++ kfree_skb(read_skb); ++ goto exit; ++ } ++ ++ ret = read_poll_timeout(qca8k_phy_eth_busy_wait, ret1, ++ !(val & QCA8K_MDIO_MASTER_BUSY), 0, ++ QCA8K_BUSY_WAIT_TIMEOUT * USEC_PER_MSEC, false, ++ mgmt_eth_data, read_skb, &val); ++ ++ if (ret < 0 && ret1 < 0) { ++ ret = ret1; ++ goto exit; ++ } ++ ++ if (read) { ++ reinit_completion(&mgmt_eth_data->rw_done); ++ ++ /* Increment seq_num and set it in the read pkt */ ++ mgmt_eth_data->seq++; ++ qca8k_mdio_header_fill_seq_num(read_skb, mgmt_eth_data->seq); ++ mgmt_eth_data->ack = false; ++ ++ dev_queue_xmit(read_skb); ++ ++ ret = wait_for_completion_timeout(&mgmt_eth_data->rw_done, ++ QCA8K_ETHERNET_TIMEOUT); ++ ++ ack = mgmt_eth_data->ack; ++ ++ if (ret <= 0) { ++ ret = -ETIMEDOUT; ++ goto exit; ++ } ++ ++ if (!ack) { ++ ret = -EINVAL; ++ goto exit; ++ } ++ ++ ret = mgmt_eth_data->data[0] & QCA8K_MDIO_MASTER_DATA_MASK; ++ } else { ++ kfree_skb(read_skb); ++ } ++exit: ++ reinit_completion(&mgmt_eth_data->rw_done); ++ ++ /* Increment seq_num and set it in the clear pkt */ ++ mgmt_eth_data->seq++; ++ qca8k_mdio_header_fill_seq_num(clear_skb, mgmt_eth_data->seq); ++ mgmt_eth_data->ack = false; ++ ++ dev_queue_xmit(clear_skb); ++ ++ wait_for_completion_timeout(&mgmt_eth_data->rw_done, ++ QCA8K_ETHERNET_TIMEOUT); ++ ++ mutex_unlock(&mgmt_eth_data->mutex); ++ ++ return ret; ++ ++ /* Error handling before lock */ ++err_mgmt_master: ++ kfree_skb(read_skb); ++err_read_skb: ++ kfree_skb(clear_skb); ++err_clear_skb: ++ kfree_skb(write_skb); ++ ++ return ret; ++} ++ + static u32 + qca8k_port_to_phy(int port) + { +@@ -989,6 +1182,12 @@ qca8k_internal_mdio_write(struct mii_bus + { + struct qca8k_priv *priv = slave_bus->priv; + struct mii_bus *bus = priv->bus; ++ int ret; ++ ++ /* Use mdio Ethernet when available, fallback to legacy one on error */ ++ ret = qca8k_phy_eth_command(priv, false, phy, regnum, data); ++ if (!ret) ++ return 0; + + return qca8k_mdio_write(bus, phy, regnum, data); + } +@@ -998,6 +1197,12 @@ qca8k_internal_mdio_read(struct mii_bus + { + struct qca8k_priv *priv = slave_bus->priv; + struct mii_bus *bus = priv->bus; ++ int ret; ++ ++ /* Use mdio Ethernet when available, fallback to legacy one on error */ ++ ret = qca8k_phy_eth_command(priv, true, phy, regnum, 0); ++ if (ret >= 0) ++ return ret; + + return qca8k_mdio_read(bus, phy, regnum); + } +@@ -1006,6 +1211,7 @@ static int + qca8k_phy_write(struct dsa_switch *ds, int port, int regnum, u16 data) + { + struct qca8k_priv *priv = ds->priv; ++ int ret; + + /* Check if the legacy mapping should be used and the + * port is not correctly mapped to the right PHY in the +@@ -1014,6 +1220,11 @@ qca8k_phy_write(struct dsa_switch *ds, i + if (priv->legacy_phy_port_mapping) + port = qca8k_port_to_phy(port) % PHY_MAX_ADDR; + ++ /* Use mdio Ethernet when available, fallback to legacy one on error */ ++ ret = qca8k_phy_eth_command(priv, false, port, regnum, 0); ++ if (!ret) ++ return ret; ++ + return qca8k_mdio_write(priv->bus, port, regnum, data); + } + +@@ -1030,6 +1241,11 @@ qca8k_phy_read(struct dsa_switch *ds, in + if (priv->legacy_phy_port_mapping) + port = qca8k_port_to_phy(port) % PHY_MAX_ADDR; + ++ /* Use mdio Ethernet when available, fallback to legacy one on error */ ++ ret = qca8k_phy_eth_command(priv, true, port, regnum, 0); ++ if (ret >= 0) ++ return ret; ++ + ret = qca8k_mdio_read(priv->bus, port, regnum); + + if (ret < 0) +--- a/drivers/net/dsa/qca8k.h ++++ b/drivers/net/dsa/qca8k.h +@@ -14,6 +14,7 @@ + #include <linux/dsa/tag_qca.h> + + #define QCA8K_ETHERNET_MDIO_PRIORITY 7 ++#define QCA8K_ETHERNET_PHY_PRIORITY 6 + #define QCA8K_ETHERNET_TIMEOUT 100 + + #define QCA8K_NUM_PORTS 7 diff --git a/pkgs/patches-linux-5.15/766-13-net-dsa-qca8k-move-page-cache-to-driver-priv.patch b/pkgs/patches-linux-5.15/766-13-net-dsa-qca8k-move-page-cache-to-driver-priv.patch new file mode 100644 index 0000000..dc81609 --- /dev/null +++ b/pkgs/patches-linux-5.15/766-13-net-dsa-qca8k-move-page-cache-to-driver-priv.patch @@ -0,0 +1,208 @@ +From 4264350acb75430d5021a1d7de56a33faf69a097 Mon Sep 17 00:00:00 2001 +From: Ansuel Smith <ansuelsmth@gmail.com> +Date: Wed, 2 Feb 2022 01:03:32 +0100 +Subject: [PATCH 13/16] net: dsa: qca8k: move page cache to driver priv + +There can be multiple qca8k switch on the same system. Move the static +qca8k_current_page to qca8k_priv and make it specific for each switch. + +Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com> +Reviewed-by: Florian Fainelli <f.fainelli@gmail.com> +Signed-off-by: David S. Miller <davem@davemloft.net> +--- + drivers/net/dsa/qca8k.c | 42 ++++++++++++++++++++--------------------- + drivers/net/dsa/qca8k.h | 9 +++++++++ + 2 files changed, 29 insertions(+), 22 deletions(-) + +--- a/drivers/net/dsa/qca8k.c ++++ b/drivers/net/dsa/qca8k.c +@@ -75,12 +75,6 @@ static const struct qca8k_mib_desc ar832 + MIB_DESC(1, 0xac, "TXUnicast"), + }; + +-/* The 32bit switch registers are accessed indirectly. To achieve this we need +- * to set the page of the register. Track the last page that was set to reduce +- * mdio writes +- */ +-static u16 qca8k_current_page = 0xffff; +- + static void + qca8k_split_addr(u32 regaddr, u16 *r1, u16 *r2, u16 *page) + { +@@ -134,11 +128,13 @@ qca8k_mii_write32(struct mii_bus *bus, i + } + + static int +-qca8k_set_page(struct mii_bus *bus, u16 page) ++qca8k_set_page(struct qca8k_priv *priv, u16 page) + { ++ u16 *cached_page = &priv->mdio_cache.page; ++ struct mii_bus *bus = priv->bus; + int ret; + +- if (page == qca8k_current_page) ++ if (page == *cached_page) + return 0; + + ret = bus->write(bus, 0x18, 0, page); +@@ -148,7 +144,7 @@ qca8k_set_page(struct mii_bus *bus, u16 + return ret; + } + +- qca8k_current_page = page; ++ *cached_page = page; + usleep_range(1000, 2000); + return 0; + } +@@ -374,7 +370,7 @@ qca8k_regmap_read(void *ctx, uint32_t re + + mutex_lock_nested(&bus->mdio_lock, MDIO_MUTEX_NESTED); + +- ret = qca8k_set_page(bus, page); ++ ret = qca8k_set_page(priv, page); + if (ret < 0) + goto exit; + +@@ -400,7 +396,7 @@ qca8k_regmap_write(void *ctx, uint32_t r + + mutex_lock_nested(&bus->mdio_lock, MDIO_MUTEX_NESTED); + +- ret = qca8k_set_page(bus, page); ++ ret = qca8k_set_page(priv, page); + if (ret < 0) + goto exit; + +@@ -427,7 +423,7 @@ qca8k_regmap_update_bits(void *ctx, uint + + mutex_lock_nested(&bus->mdio_lock, MDIO_MUTEX_NESTED); + +- ret = qca8k_set_page(bus, page); ++ ret = qca8k_set_page(priv, page); + if (ret < 0) + goto exit; + +@@ -1098,8 +1094,9 @@ qca8k_mdio_busy_wait(struct mii_bus *bus + } + + static int +-qca8k_mdio_write(struct mii_bus *bus, int phy, int regnum, u16 data) ++qca8k_mdio_write(struct qca8k_priv *priv, int phy, int regnum, u16 data) + { ++ struct mii_bus *bus = priv->bus; + u16 r1, r2, page; + u32 val; + int ret; +@@ -1116,7 +1113,7 @@ qca8k_mdio_write(struct mii_bus *bus, in + + mutex_lock_nested(&bus->mdio_lock, MDIO_MUTEX_NESTED); + +- ret = qca8k_set_page(bus, page); ++ ret = qca8k_set_page(priv, page); + if (ret) + goto exit; + +@@ -1135,8 +1132,9 @@ exit: + } + + static int +-qca8k_mdio_read(struct mii_bus *bus, int phy, int regnum) ++qca8k_mdio_read(struct qca8k_priv *priv, int phy, int regnum) + { ++ struct mii_bus *bus = priv->bus; + u16 r1, r2, page; + u32 val; + int ret; +@@ -1152,7 +1150,7 @@ qca8k_mdio_read(struct mii_bus *bus, int + + mutex_lock_nested(&bus->mdio_lock, MDIO_MUTEX_NESTED); + +- ret = qca8k_set_page(bus, page); ++ ret = qca8k_set_page(priv, page); + if (ret) + goto exit; + +@@ -1181,7 +1179,6 @@ static int + qca8k_internal_mdio_write(struct mii_bus *slave_bus, int phy, int regnum, u16 data) + { + struct qca8k_priv *priv = slave_bus->priv; +- struct mii_bus *bus = priv->bus; + int ret; + + /* Use mdio Ethernet when available, fallback to legacy one on error */ +@@ -1189,14 +1186,13 @@ qca8k_internal_mdio_write(struct mii_bus + if (!ret) + return 0; + +- return qca8k_mdio_write(bus, phy, regnum, data); ++ return qca8k_mdio_write(priv, phy, regnum, data); + } + + static int + qca8k_internal_mdio_read(struct mii_bus *slave_bus, int phy, int regnum) + { + struct qca8k_priv *priv = slave_bus->priv; +- struct mii_bus *bus = priv->bus; + int ret; + + /* Use mdio Ethernet when available, fallback to legacy one on error */ +@@ -1204,7 +1200,7 @@ qca8k_internal_mdio_read(struct mii_bus + if (ret >= 0) + return ret; + +- return qca8k_mdio_read(bus, phy, regnum); ++ return qca8k_mdio_read(priv, phy, regnum); + } + + static int +@@ -1225,7 +1221,7 @@ qca8k_phy_write(struct dsa_switch *ds, i + if (!ret) + return ret; + +- return qca8k_mdio_write(priv->bus, port, regnum, data); ++ return qca8k_mdio_write(priv, port, regnum, data); + } + + static int +@@ -1246,7 +1242,7 @@ qca8k_phy_read(struct dsa_switch *ds, in + if (ret >= 0) + return ret; + +- ret = qca8k_mdio_read(priv->bus, port, regnum); ++ ret = qca8k_mdio_read(priv, port, regnum); + + if (ret < 0) + return 0xffff; +@@ -3041,6 +3037,8 @@ qca8k_sw_probe(struct mdio_device *mdiod + return PTR_ERR(priv->regmap); + } + ++ priv->mdio_cache.page = 0xffff; ++ + /* Check the detected switch id */ + ret = qca8k_read_switch_id(priv); + if (ret) +--- a/drivers/net/dsa/qca8k.h ++++ b/drivers/net/dsa/qca8k.h +@@ -363,6 +363,14 @@ struct qca8k_ports_config { + u8 rgmii_tx_delay[QCA8K_NUM_CPU_PORTS]; /* 0: CPU port0, 1: CPU port6 */ + }; + ++struct qca8k_mdio_cache { ++/* The 32bit switch registers are accessed indirectly. To achieve this we need ++ * to set the page of the register. Track the last page that was set to reduce ++ * mdio writes ++ */ ++ u16 page; ++}; ++ + struct qca8k_priv { + u8 switch_id; + u8 switch_revision; +@@ -383,6 +391,7 @@ struct qca8k_priv { + struct net_device *mgmt_master; /* Track if mdio/mib Ethernet is available */ + struct qca8k_mgmt_eth_data mgmt_eth_data; + struct qca8k_mib_eth_data mib_eth_data; ++ struct qca8k_mdio_cache mdio_cache; + }; + + struct qca8k_mib_desc { diff --git a/pkgs/patches-linux-5.15/766-14-net-dsa-qca8k-cache-lo-and-hi-for-mdio-write.patch b/pkgs/patches-linux-5.15/766-14-net-dsa-qca8k-cache-lo-and-hi-for-mdio-write.patch new file mode 100644 index 0000000..2d48373 --- /dev/null +++ b/pkgs/patches-linux-5.15/766-14-net-dsa-qca8k-cache-lo-and-hi-for-mdio-write.patch @@ -0,0 +1,164 @@ +From 2481d206fae7884cd07014fd1318e63af35e99eb Mon Sep 17 00:00:00 2001 +From: Ansuel Smith <ansuelsmth@gmail.com> +Date: Wed, 2 Feb 2022 01:03:33 +0100 +Subject: [PATCH 14/16] net: dsa: qca8k: cache lo and hi for mdio write + +From Documentation, we can cache lo and hi the same way we do with the +page. This massively reduce the mdio write as 3/4 of the time as we only +require to write the lo or hi part for a mdio write. + +Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com> +Reviewed-by: Florian Fainelli <f.fainelli@gmail.com> +Signed-off-by: David S. Miller <davem@davemloft.net> +--- + drivers/net/dsa/qca8k.c | 61 +++++++++++++++++++++++++++++++++-------- + drivers/net/dsa/qca8k.h | 5 ++++ + 2 files changed, 54 insertions(+), 12 deletions(-) + +--- a/drivers/net/dsa/qca8k.c ++++ b/drivers/net/dsa/qca8k.c +@@ -89,6 +89,44 @@ qca8k_split_addr(u32 regaddr, u16 *r1, u + } + + static int ++qca8k_set_lo(struct qca8k_priv *priv, int phy_id, u32 regnum, u16 lo) ++{ ++ u16 *cached_lo = &priv->mdio_cache.lo; ++ struct mii_bus *bus = priv->bus; ++ int ret; ++ ++ if (lo == *cached_lo) ++ return 0; ++ ++ ret = bus->write(bus, phy_id, regnum, lo); ++ if (ret < 0) ++ dev_err_ratelimited(&bus->dev, ++ "failed to write qca8k 32bit lo register\n"); ++ ++ *cached_lo = lo; ++ return 0; ++} ++ ++static int ++qca8k_set_hi(struct qca8k_priv *priv, int phy_id, u32 regnum, u16 hi) ++{ ++ u16 *cached_hi = &priv->mdio_cache.hi; ++ struct mii_bus *bus = priv->bus; ++ int ret; ++ ++ if (hi == *cached_hi) ++ return 0; ++ ++ ret = bus->write(bus, phy_id, regnum, hi); ++ if (ret < 0) ++ dev_err_ratelimited(&bus->dev, ++ "failed to write qca8k 32bit hi register\n"); ++ ++ *cached_hi = hi; ++ return 0; ++} ++ ++static int + qca8k_mii_read32(struct mii_bus *bus, int phy_id, u32 regnum, u32 *val) + { + int ret; +@@ -111,7 +149,7 @@ qca8k_mii_read32(struct mii_bus *bus, in + } + + static void +-qca8k_mii_write32(struct mii_bus *bus, int phy_id, u32 regnum, u32 val) ++qca8k_mii_write32(struct qca8k_priv *priv, int phy_id, u32 regnum, u32 val) + { + u16 lo, hi; + int ret; +@@ -119,12 +157,9 @@ qca8k_mii_write32(struct mii_bus *bus, i + lo = val & 0xffff; + hi = (u16)(val >> 16); + +- ret = bus->write(bus, phy_id, regnum, lo); ++ ret = qca8k_set_lo(priv, phy_id, regnum, lo); + if (ret >= 0) +- ret = bus->write(bus, phy_id, regnum + 1, hi); +- if (ret < 0) +- dev_err_ratelimited(&bus->dev, +- "failed to write qca8k 32bit register\n"); ++ ret = qca8k_set_hi(priv, phy_id, regnum + 1, hi); + } + + static int +@@ -400,7 +435,7 @@ qca8k_regmap_write(void *ctx, uint32_t r + if (ret < 0) + goto exit; + +- qca8k_mii_write32(bus, 0x10 | r2, r1, val); ++ qca8k_mii_write32(priv, 0x10 | r2, r1, val); + + exit: + mutex_unlock(&bus->mdio_lock); +@@ -433,7 +468,7 @@ qca8k_regmap_update_bits(void *ctx, uint + + val &= ~mask; + val |= write_val; +- qca8k_mii_write32(bus, 0x10 | r2, r1, val); ++ qca8k_mii_write32(priv, 0x10 | r2, r1, val); + + exit: + mutex_unlock(&bus->mdio_lock); +@@ -1117,14 +1152,14 @@ qca8k_mdio_write(struct qca8k_priv *priv + if (ret) + goto exit; + +- qca8k_mii_write32(bus, 0x10 | r2, r1, val); ++ qca8k_mii_write32(priv, 0x10 | r2, r1, val); + + ret = qca8k_mdio_busy_wait(bus, QCA8K_MDIO_MASTER_CTRL, + QCA8K_MDIO_MASTER_BUSY); + + exit: + /* even if the busy_wait timeouts try to clear the MASTER_EN */ +- qca8k_mii_write32(bus, 0x10 | r2, r1, 0); ++ qca8k_mii_write32(priv, 0x10 | r2, r1, 0); + + mutex_unlock(&bus->mdio_lock); + +@@ -1154,7 +1189,7 @@ qca8k_mdio_read(struct qca8k_priv *priv, + if (ret) + goto exit; + +- qca8k_mii_write32(bus, 0x10 | r2, r1, val); ++ qca8k_mii_write32(priv, 0x10 | r2, r1, val); + + ret = qca8k_mdio_busy_wait(bus, QCA8K_MDIO_MASTER_CTRL, + QCA8K_MDIO_MASTER_BUSY); +@@ -1165,7 +1200,7 @@ qca8k_mdio_read(struct qca8k_priv *priv, + + exit: + /* even if the busy_wait timeouts try to clear the MASTER_EN */ +- qca8k_mii_write32(bus, 0x10 | r2, r1, 0); ++ qca8k_mii_write32(priv, 0x10 | r2, r1, 0); + + mutex_unlock(&bus->mdio_lock); + +@@ -3038,6 +3073,8 @@ qca8k_sw_probe(struct mdio_device *mdiod + } + + priv->mdio_cache.page = 0xffff; ++ priv->mdio_cache.lo = 0xffff; ++ priv->mdio_cache.hi = 0xffff; + + /* Check the detected switch id */ + ret = qca8k_read_switch_id(priv); +--- a/drivers/net/dsa/qca8k.h ++++ b/drivers/net/dsa/qca8k.h +@@ -369,6 +369,11 @@ struct qca8k_mdio_cache { + * mdio writes + */ + u16 page; ++/* lo and hi can also be cached and from Documentation we can skip one ++ * extra mdio write if lo or hi is didn't change. ++ */ ++ u16 lo; ++ u16 hi; + }; + + struct qca8k_priv { diff --git a/pkgs/patches-linux-5.15/766-15-net-dsa-qca8k-add-support-for-larger-read-write-size.patch b/pkgs/patches-linux-5.15/766-15-net-dsa-qca8k-add-support-for-larger-read-write-size.patch new file mode 100644 index 0000000..5acd13d --- /dev/null +++ b/pkgs/patches-linux-5.15/766-15-net-dsa-qca8k-add-support-for-larger-read-write-size.patch @@ -0,0 +1,206 @@ +From 90386223f44e2a751d7e9e9ac8f78ea33358a891 Mon Sep 17 00:00:00 2001 +From: Ansuel Smith <ansuelsmth@gmail.com> +Date: Wed, 2 Feb 2022 01:03:34 +0100 +Subject: [PATCH 15/16] net: dsa: qca8k: add support for larger read/write size + with mgmt Ethernet + +mgmt Ethernet packet can read/write up to 16byte at times. The len reg +is limited to 15 (0xf). The switch actually sends and accepts data in 4 +different steps of len values. +Len steps: +- 0: nothing +- 1-4: first 4 byte +- 5-6: first 12 byte +- 7-15: all 16 byte + +In the alloc skb function we check if the len is 16 and we fix it to a +len of 15. It the read/write function interest to extract the real asked +data. The tagger handler will always copy the fully 16byte with a READ +command. This is useful for some big regs like the fdb reg that are +more than 4byte of data. This permits to introduce a bulk function that +will send and request the entire entry in one go. +Write function is changed and it does now require to pass the pointer to +val to also handle array val. + +Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com> +Signed-off-by: David S. Miller <davem@davemloft.net> +--- + drivers/net/dsa/qca8k.c | 61 +++++++++++++++++++++++++++-------------- + 1 file changed, 41 insertions(+), 20 deletions(-) + +--- a/drivers/net/dsa/qca8k.c ++++ b/drivers/net/dsa/qca8k.c +@@ -222,7 +222,9 @@ static void qca8k_rw_reg_ack_handler(str + if (cmd == MDIO_READ) { + mgmt_eth_data->data[0] = mgmt_ethhdr->mdio_data; + +- /* Get the rest of the 12 byte of data */ ++ /* Get the rest of the 12 byte of data. ++ * The read/write function will extract the requested data. ++ */ + if (len > QCA_HDR_MGMT_DATA1_LEN) + memcpy(mgmt_eth_data->data + 1, skb->data, + QCA_HDR_MGMT_DATA2_LEN); +@@ -232,16 +234,30 @@ static void qca8k_rw_reg_ack_handler(str + } + + static struct sk_buff *qca8k_alloc_mdio_header(enum mdio_cmd cmd, u32 reg, u32 *val, +- int priority) ++ int priority, unsigned int len) + { + struct qca_mgmt_ethhdr *mgmt_ethhdr; ++ unsigned int real_len; + struct sk_buff *skb; ++ u32 *data2; + u16 hdr; + + skb = dev_alloc_skb(QCA_HDR_MGMT_PKT_LEN); + if (!skb) + return NULL; + ++ /* Max value for len reg is 15 (0xf) but the switch actually return 16 byte ++ * Actually for some reason the steps are: ++ * 0: nothing ++ * 1-4: first 4 byte ++ * 5-6: first 12 byte ++ * 7-15: all 16 byte ++ */ ++ if (len == 16) ++ real_len = 15; ++ else ++ real_len = len; ++ + skb_reset_mac_header(skb); + skb_set_network_header(skb, skb->len); + +@@ -254,7 +270,7 @@ static struct sk_buff *qca8k_alloc_mdio_ + hdr |= FIELD_PREP(QCA_HDR_XMIT_CONTROL, QCA_HDR_XMIT_TYPE_RW_REG); + + mgmt_ethhdr->command = FIELD_PREP(QCA_HDR_MGMT_ADDR, reg); +- mgmt_ethhdr->command |= FIELD_PREP(QCA_HDR_MGMT_LENGTH, 4); ++ mgmt_ethhdr->command |= FIELD_PREP(QCA_HDR_MGMT_LENGTH, real_len); + mgmt_ethhdr->command |= FIELD_PREP(QCA_HDR_MGMT_CMD, cmd); + mgmt_ethhdr->command |= FIELD_PREP(QCA_HDR_MGMT_CHECK_CODE, + QCA_HDR_MGMT_CHECK_CODE_VAL); +@@ -264,7 +280,9 @@ static struct sk_buff *qca8k_alloc_mdio_ + + mgmt_ethhdr->hdr = htons(hdr); + +- skb_put_zero(skb, QCA_HDR_MGMT_DATA2_LEN + QCA_HDR_MGMT_PADDING_LEN); ++ data2 = skb_put_zero(skb, QCA_HDR_MGMT_DATA2_LEN + QCA_HDR_MGMT_PADDING_LEN); ++ if (cmd == MDIO_WRITE && len > QCA_HDR_MGMT_DATA1_LEN) ++ memcpy(data2, val + 1, len - QCA_HDR_MGMT_DATA1_LEN); + + return skb; + } +@@ -277,7 +295,7 @@ static void qca8k_mdio_header_fill_seq_n + mgmt_ethhdr->seq = FIELD_PREP(QCA_HDR_MGMT_SEQ_NUM, seq_num); + } + +-static int qca8k_read_eth(struct qca8k_priv *priv, u32 reg, u32 *val) ++static int qca8k_read_eth(struct qca8k_priv *priv, u32 reg, u32 *val, int len) + { + struct qca8k_mgmt_eth_data *mgmt_eth_data = &priv->mgmt_eth_data; + struct sk_buff *skb; +@@ -285,7 +303,7 @@ static int qca8k_read_eth(struct qca8k_p + int ret; + + skb = qca8k_alloc_mdio_header(MDIO_READ, reg, NULL, +- QCA8K_ETHERNET_MDIO_PRIORITY); ++ QCA8K_ETHERNET_MDIO_PRIORITY, len); + if (!skb) + return -ENOMEM; + +@@ -313,6 +331,9 @@ static int qca8k_read_eth(struct qca8k_p + msecs_to_jiffies(QCA8K_ETHERNET_TIMEOUT)); + + *val = mgmt_eth_data->data[0]; ++ if (len > QCA_HDR_MGMT_DATA1_LEN) ++ memcpy(val + 1, mgmt_eth_data->data + 1, len - QCA_HDR_MGMT_DATA1_LEN); ++ + ack = mgmt_eth_data->ack; + + mutex_unlock(&mgmt_eth_data->mutex); +@@ -326,15 +347,15 @@ static int qca8k_read_eth(struct qca8k_p + return 0; + } + +-static int qca8k_write_eth(struct qca8k_priv *priv, u32 reg, u32 val) ++static int qca8k_write_eth(struct qca8k_priv *priv, u32 reg, u32 *val, int len) + { + struct qca8k_mgmt_eth_data *mgmt_eth_data = &priv->mgmt_eth_data; + struct sk_buff *skb; + bool ack; + int ret; + +- skb = qca8k_alloc_mdio_header(MDIO_WRITE, reg, &val, +- QCA8K_ETHERNET_MDIO_PRIORITY); ++ skb = qca8k_alloc_mdio_header(MDIO_WRITE, reg, val, ++ QCA8K_ETHERNET_MDIO_PRIORITY, len); + if (!skb) + return -ENOMEM; + +@@ -380,14 +401,14 @@ qca8k_regmap_update_bits_eth(struct qca8 + u32 val = 0; + int ret; + +- ret = qca8k_read_eth(priv, reg, &val); ++ ret = qca8k_read_eth(priv, reg, &val, sizeof(val)); + if (ret) + return ret; + + val &= ~mask; + val |= write_val; + +- return qca8k_write_eth(priv, reg, val); ++ return qca8k_write_eth(priv, reg, &val, sizeof(val)); + } + + static int +@@ -398,7 +419,7 @@ qca8k_regmap_read(void *ctx, uint32_t re + u16 r1, r2, page; + int ret; + +- if (!qca8k_read_eth(priv, reg, val)) ++ if (!qca8k_read_eth(priv, reg, val, sizeof(val))) + return 0; + + qca8k_split_addr(reg, &r1, &r2, &page); +@@ -424,7 +445,7 @@ qca8k_regmap_write(void *ctx, uint32_t r + u16 r1, r2, page; + int ret; + +- if (!qca8k_write_eth(priv, reg, val)) ++ if (!qca8k_write_eth(priv, reg, &val, sizeof(val))) + return 0; + + qca8k_split_addr(reg, &r1, &r2, &page); +@@ -959,21 +980,21 @@ qca8k_phy_eth_command(struct qca8k_priv + } + + /* Prealloc all the needed skb before the lock */ +- write_skb = qca8k_alloc_mdio_header(MDIO_WRITE, QCA8K_MDIO_MASTER_CTRL, +- &write_val, QCA8K_ETHERNET_PHY_PRIORITY); ++ write_skb = qca8k_alloc_mdio_header(MDIO_WRITE, QCA8K_MDIO_MASTER_CTRL, &write_val, ++ QCA8K_ETHERNET_PHY_PRIORITY, sizeof(write_val)); + if (!write_skb) + return -ENOMEM; + +- clear_skb = qca8k_alloc_mdio_header(MDIO_WRITE, QCA8K_MDIO_MASTER_CTRL, +- &clear_val, QCA8K_ETHERNET_PHY_PRIORITY); ++ clear_skb = qca8k_alloc_mdio_header(MDIO_WRITE, QCA8K_MDIO_MASTER_CTRL, &clear_val, ++ QCA8K_ETHERNET_PHY_PRIORITY, sizeof(clear_val)); + if (!write_skb) { + ret = -ENOMEM; + goto err_clear_skb; + } + +- read_skb = qca8k_alloc_mdio_header(MDIO_READ, QCA8K_MDIO_MASTER_CTRL, +- &clear_val, QCA8K_ETHERNET_PHY_PRIORITY); +- if (!write_skb) { ++ read_skb = qca8k_alloc_mdio_header(MDIO_READ, QCA8K_MDIO_MASTER_CTRL, &clear_val, ++ QCA8K_ETHERNET_PHY_PRIORITY, sizeof(clear_val)); ++ if (!read_skb) { + ret = -ENOMEM; + goto err_read_skb; + } diff --git a/pkgs/patches-linux-5.15/766-16-net-dsa-qca8k-introduce-qca8k_bulk_read-write-functi.patch b/pkgs/patches-linux-5.15/766-16-net-dsa-qca8k-introduce-qca8k_bulk_read-write-functi.patch new file mode 100644 index 0000000..f26c6b9 --- /dev/null +++ b/pkgs/patches-linux-5.15/766-16-net-dsa-qca8k-introduce-qca8k_bulk_read-write-functi.patch @@ -0,0 +1,104 @@ +From 4f3701fc599820568ba4395070d34e4248800fc0 Mon Sep 17 00:00:00 2001 +From: Ansuel Smith <ansuelsmth@gmail.com> +Date: Wed, 2 Feb 2022 01:03:35 +0100 +Subject: [PATCH 16/16] net: dsa: qca8k: introduce qca8k_bulk_read/write + function + +Introduce qca8k_bulk_read/write() function to use mgmt Ethernet way to +read/write packet in bulk. Make use of this new function in the fdb +function and while at it reduce the reg for fdb_read from 4 to 3 as the +max bit for the ARL(fdb) table is 83 bits. + +Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com> +Signed-off-by: David S. Miller <davem@davemloft.net> +--- + drivers/net/dsa/qca8k.c | 55 ++++++++++++++++++++++++++++++++--------- + 1 file changed, 43 insertions(+), 12 deletions(-) + +--- a/drivers/net/dsa/qca8k.c ++++ b/drivers/net/dsa/qca8k.c +@@ -412,6 +412,43 @@ qca8k_regmap_update_bits_eth(struct qca8 + } + + static int ++qca8k_bulk_read(struct qca8k_priv *priv, u32 reg, u32 *val, int len) ++{ ++ int i, count = len / sizeof(u32), ret; ++ ++ if (priv->mgmt_master && !qca8k_read_eth(priv, reg, val, len)) ++ return 0; ++ ++ for (i = 0; i < count; i++) { ++ ret = regmap_read(priv->regmap, reg + (i * 4), val + i); ++ if (ret < 0) ++ return ret; ++ } ++ ++ return 0; ++} ++ ++static int ++qca8k_bulk_write(struct qca8k_priv *priv, u32 reg, u32 *val, int len) ++{ ++ int i, count = len / sizeof(u32), ret; ++ u32 tmp; ++ ++ if (priv->mgmt_master && !qca8k_write_eth(priv, reg, val, len)) ++ return 0; ++ ++ for (i = 0; i < count; i++) { ++ tmp = val[i]; ++ ++ ret = regmap_write(priv->regmap, reg + (i * 4), tmp); ++ if (ret < 0) ++ return ret; ++ } ++ ++ return 0; ++} ++ ++static int + qca8k_regmap_read(void *ctx, uint32_t reg, uint32_t *val) + { + struct qca8k_priv *priv = (struct qca8k_priv *)ctx; +@@ -546,17 +583,13 @@ qca8k_busy_wait(struct qca8k_priv *priv, + static int + qca8k_fdb_read(struct qca8k_priv *priv, struct qca8k_fdb *fdb) + { +- u32 reg[4], val; +- int i, ret; ++ u32 reg[3]; ++ int ret; + + /* load the ARL table into an array */ +- for (i = 0; i < 4; i++) { +- ret = qca8k_read(priv, QCA8K_REG_ATU_DATA0 + (i * 4), &val); +- if (ret < 0) +- return ret; +- +- reg[i] = val; +- } ++ ret = qca8k_bulk_read(priv, QCA8K_REG_ATU_DATA0, reg, sizeof(reg)); ++ if (ret) ++ return ret; + + /* vid - 83:72 */ + fdb->vid = FIELD_GET(QCA8K_ATU_VID_MASK, reg[2]); +@@ -580,7 +613,6 @@ qca8k_fdb_write(struct qca8k_priv *priv, + u8 aging) + { + u32 reg[3] = { 0 }; +- int i; + + /* vid - 83:72 */ + reg[2] = FIELD_PREP(QCA8K_ATU_VID_MASK, vid); +@@ -597,8 +629,7 @@ qca8k_fdb_write(struct qca8k_priv *priv, + reg[0] |= FIELD_PREP(QCA8K_ATU_ADDR5_MASK, mac[5]); + + /* load the array into the ARL table */ +- for (i = 0; i < 3; i++) +- qca8k_write(priv, QCA8K_REG_ATU_DATA0 + (i * 4), reg[i]); ++ qca8k_bulk_write(priv, QCA8K_REG_ATU_DATA0, reg, sizeof(reg)); + } + + static int diff --git a/pkgs/patches-linux-5.15/768-net-dsa-mv88e6xxx-Request-assisted-learning-on-CPU-port.patch b/pkgs/patches-linux-5.15/768-net-dsa-mv88e6xxx-Request-assisted-learning-on-CPU-port.patch new file mode 100644 index 0000000..662b956 --- /dev/null +++ b/pkgs/patches-linux-5.15/768-net-dsa-mv88e6xxx-Request-assisted-learning-on-CPU-port.patch @@ -0,0 +1,27 @@ +From: Tobias Waldekranz <tobias@waldekranz.com> +Subject: [RFC net-next 7/7] net: dsa: mv88e6xxx: Request assisted learning on CPU port +Date: Sat, 16 Jan 2021 02:25:15 +0100 +Archived-At: <https://lore.kernel.org/netdev/20210116012515.3152-8-tobias@waldekranz.com/> + +While the hardware is capable of performing learning on the CPU port, +it requires alot of additions to the bridge's forwarding path in order +to handle multi-destination traffic correctly. + +Until that is in place, opt for the next best thing and let DSA sync +the relevant addresses down to the hardware FDB. + +Signed-off-by: Tobias Waldekranz <tobias@waldekranz.com> +--- + drivers/net/dsa/mv88e6xxx/chip.c | 1 + + 1 file changed, 1 insertion(+) + +--- a/drivers/net/dsa/mv88e6xxx/chip.c ++++ b/drivers/net/dsa/mv88e6xxx/chip.c +@@ -6320,6 +6320,7 @@ static int mv88e6xxx_register_switch(str + ds->ops = &mv88e6xxx_switch_ops; + ds->ageing_time_min = chip->info->age_time_coeff; + ds->ageing_time_max = chip->info->age_time_coeff * U8_MAX; ++ ds->assisted_learning_on_cpu_port = true; + + /* Some chips support up to 32, but that requires enabling the + * 5-bit port mode, which we do not support. 640k^W16 ought to diff --git a/pkgs/patches-linux-5.15/773-bgmac-add-srab-switch.patch b/pkgs/patches-linux-5.15/773-bgmac-add-srab-switch.patch new file mode 100644 index 0000000..1e4fc44 --- /dev/null +++ b/pkgs/patches-linux-5.15/773-bgmac-add-srab-switch.patch @@ -0,0 +1,98 @@ +From 3cb240533ab787899dc7f17aa7d6c5b4810e2e58 Mon Sep 17 00:00:00 2001 +From: Hauke Mehrtens <hauke@hauke-m.de> +Date: Fri, 7 Jul 2017 17:26:01 +0200 +Subject: bcm53xx: bgmac: use srab switch driver + +use the srab switch driver on these SoCs. + +Signed-off-by: Hauke Mehrtens <hauke@hauke-m.de> +--- + drivers/net/ethernet/broadcom/bgmac-bcma.c | 1 + + drivers/net/ethernet/broadcom/bgmac.c | 24 ++++++++++++++++++++++++ + drivers/net/ethernet/broadcom/bgmac.h | 4 ++++ + 3 files changed, 29 insertions(+) + +--- a/drivers/net/ethernet/broadcom/bgmac-bcma.c ++++ b/drivers/net/ethernet/broadcom/bgmac-bcma.c +@@ -280,6 +280,7 @@ static int bgmac_probe(struct bcma_devic + bgmac->feature_flags |= BGMAC_FEAT_CLKCTLST; + bgmac->feature_flags |= BGMAC_FEAT_NO_RESET; + bgmac->feature_flags |= BGMAC_FEAT_FORCE_SPEED_2500; ++ bgmac->feature_flags |= BGMAC_FEAT_SRAB; + break; + default: + bgmac->feature_flags |= BGMAC_FEAT_CLKCTLST; +--- a/drivers/net/ethernet/broadcom/bgmac.c ++++ b/drivers/net/ethernet/broadcom/bgmac.c +@@ -12,6 +12,7 @@ + #include <linux/bcma/bcma.h> + #include <linux/etherdevice.h> + #include <linux/interrupt.h> ++#include <linux/platform_data/b53.h> + #include <linux/bcm47xx_nvram.h> + #include <linux/phy.h> + #include <linux/phy_fixed.h> +@@ -1408,6 +1409,17 @@ static const struct ethtool_ops bgmac_et + .set_link_ksettings = phy_ethtool_set_link_ksettings, + }; + ++static struct b53_platform_data bgmac_b53_pdata = { ++}; ++ ++static struct platform_device bgmac_b53_dev = { ++ .name = "b53-srab-switch", ++ .id = -1, ++ .dev = { ++ .platform_data = &bgmac_b53_pdata, ++ }, ++}; ++ + /************************************************** + * MII + **************************************************/ +@@ -1542,6 +1554,14 @@ int bgmac_enet_probe(struct bgmac *bgmac + /* Omit FCS from max MTU size */ + net_dev->max_mtu = BGMAC_RX_MAX_FRAME_SIZE - ETH_FCS_LEN; + ++ if ((bgmac->feature_flags & BGMAC_FEAT_SRAB) && !bgmac_b53_pdata.regs) { ++ bgmac_b53_pdata.regs = ioremap(0x18007000, 0x1000); ++ ++ err = platform_device_register(&bgmac_b53_dev); ++ if (!err) ++ bgmac->b53_device = &bgmac_b53_dev; ++ } ++ + err = register_netdev(bgmac->net_dev); + if (err) { + dev_err(bgmac->dev, "Cannot register net device\n"); +@@ -1564,6 +1584,10 @@ EXPORT_SYMBOL_GPL(bgmac_enet_probe); + + void bgmac_enet_remove(struct bgmac *bgmac) + { ++ if (bgmac->b53_device) ++ platform_device_unregister(&bgmac_b53_dev); ++ bgmac->b53_device = NULL; ++ + unregister_netdev(bgmac->net_dev); + phy_disconnect(bgmac->net_dev->phydev); + netif_napi_del(&bgmac->napi); +--- a/drivers/net/ethernet/broadcom/bgmac.h ++++ b/drivers/net/ethernet/broadcom/bgmac.h +@@ -390,6 +390,7 @@ + #define BGMAC_FEAT_CC4_IF_SW_TYPE_RGMII BIT(18) + #define BGMAC_FEAT_CC7_IF_TYPE_RGMII BIT(19) + #define BGMAC_FEAT_IDM_MASK BIT(20) ++#define BGMAC_FEAT_SRAB BIT(21) + + struct bgmac_slot_info { + union { +@@ -495,6 +496,9 @@ struct bgmac { + void (*cmn_maskset32)(struct bgmac *bgmac, u16 offset, u32 mask, + u32 set); + int (*phy_connect)(struct bgmac *bgmac); ++ ++ /* platform device for associated switch */ ++ struct platform_device *b53_device; + }; + + struct bgmac *bgmac_alloc(struct device *dev); diff --git a/pkgs/patches-linux-5.15/773-v5.18-1-net-dsa-Move-VLAN-filtering-syncing-out-of-dsa_switc.patch b/pkgs/patches-linux-5.15/773-v5.18-1-net-dsa-Move-VLAN-filtering-syncing-out-of-dsa_switc.patch new file mode 100644 index 0000000..44093ea --- /dev/null +++ b/pkgs/patches-linux-5.15/773-v5.18-1-net-dsa-Move-VLAN-filtering-syncing-out-of-dsa_switc.patch @@ -0,0 +1,77 @@ +From 381a730182f1d174e1950cd4e63e885b1c302051 Mon Sep 17 00:00:00 2001 +From: Tobias Waldekranz <tobias@waldekranz.com> +Date: Mon, 24 Jan 2022 22:09:43 +0100 +Subject: net: dsa: Move VLAN filtering syncing out of dsa_switch_bridge_leave + +Most of dsa_switch_bridge_leave was, in fact, dealing with the syncing +of VLAN filtering for switches on which that is a global +setting. Separate the two phases to prepare for the cross-chip related +bugfix in the following commit. + +Signed-off-by: Tobias Waldekranz <tobias@waldekranz.com> +Reviewed-by: Vladimir Oltean <olteanv@gmail.com> +Signed-off-by: David S. Miller <davem@davemloft.net> +--- + net/dsa/switch.c | 38 +++++++++++++++++++++++++------------- + 1 file changed, 25 insertions(+), 13 deletions(-) + +--- a/net/dsa/switch.c ++++ b/net/dsa/switch.c +@@ -113,25 +113,14 @@ static int dsa_switch_bridge_join(struct + return dsa_tag_8021q_bridge_join(ds, info); + } + +-static int dsa_switch_bridge_leave(struct dsa_switch *ds, +- struct dsa_notifier_bridge_info *info) ++static int dsa_switch_sync_vlan_filtering(struct dsa_switch *ds, ++ struct dsa_notifier_bridge_info *info) + { +- struct dsa_switch_tree *dst = ds->dst; + struct netlink_ext_ack extack = {0}; + bool change_vlan_filtering = false; + bool vlan_filtering; + int err, port; + +- if (dst->index == info->tree_index && ds->index == info->sw_index && +- ds->ops->port_bridge_leave) +- ds->ops->port_bridge_leave(ds, info->port, info->br); +- +- if ((dst->index != info->tree_index || ds->index != info->sw_index) && +- ds->ops->crosschip_bridge_leave) +- ds->ops->crosschip_bridge_leave(ds, info->tree_index, +- info->sw_index, info->port, +- info->br); +- + if (ds->needs_standalone_vlan_filtering && !br_vlan_enabled(info->br)) { + change_vlan_filtering = true; + vlan_filtering = true; +@@ -172,6 +161,29 @@ static int dsa_switch_bridge_leave(struc + return err; + } + ++ return 0; ++} ++ ++static int dsa_switch_bridge_leave(struct dsa_switch *ds, ++ struct dsa_notifier_bridge_info *info) ++{ ++ struct dsa_switch_tree *dst = ds->dst; ++ int err; ++ ++ if (dst->index == info->tree_index && ds->index == info->sw_index && ++ ds->ops->port_bridge_leave) ++ ds->ops->port_bridge_leave(ds, info->port, info->br); ++ ++ if ((dst->index != info->tree_index || ds->index != info->sw_index) && ++ ds->ops->crosschip_bridge_leave) ++ ds->ops->crosschip_bridge_leave(ds, info->tree_index, ++ info->sw_index, info->port, ++ info->br); ++ ++ err = dsa_switch_sync_vlan_filtering(ds, info); ++ if (err) ++ return err; ++ + return dsa_tag_8021q_bridge_leave(ds, info); + } + diff --git a/pkgs/patches-linux-5.15/773-v5.18-2-net-dsa-Avoid-cross-chip-syncing-of-VLAN-filtering.patch b/pkgs/patches-linux-5.15/773-v5.18-2-net-dsa-Avoid-cross-chip-syncing-of-VLAN-filtering.patch new file mode 100644 index 0000000..cdddbcf --- /dev/null +++ b/pkgs/patches-linux-5.15/773-v5.18-2-net-dsa-Avoid-cross-chip-syncing-of-VLAN-filtering.patch @@ -0,0 +1,52 @@ +From 108dc8741c203e9d6ce4e973367f1bac20c7192b Mon Sep 17 00:00:00 2001 +From: Tobias Waldekranz <tobias@waldekranz.com> +Date: Mon, 24 Jan 2022 22:09:44 +0100 +Subject: net: dsa: Avoid cross-chip syncing of VLAN filtering + +Changes to VLAN filtering are not applicable to cross-chip +notifications. + +On a system like this: + +.-----. .-----. .-----. +| sw1 +---+ sw2 +---+ sw3 | +'-1-2-' '-1-2-' '-1-2-' + +Before this change, upon sw1p1 leaving a bridge, a call to +dsa_port_vlan_filtering would also be made to sw2p1 and sw3p1. + +In this scenario: + +.---------. .-----. .-----. +| sw1 +---+ sw2 +---+ sw3 | +'-1-2-3-4-' '-1-2-' '-1-2-' + +When sw1p4 would leave a bridge, dsa_port_vlan_filtering would be +called for sw2 and sw3 with a non-existing port - leading to array +out-of-bounds accesses and crashes on mv88e6xxx. + +Fixes: d371b7c92d19 ("net: dsa: Unset vlan_filtering when ports leave the bridge") +Signed-off-by: Tobias Waldekranz <tobias@waldekranz.com> +Reviewed-by: Vladimir Oltean <olteanv@gmail.com> +Signed-off-by: David S. Miller <davem@davemloft.net> +--- + net/dsa/switch.c | 8 +++++--- + 1 file changed, 5 insertions(+), 3 deletions(-) + +--- a/net/dsa/switch.c ++++ b/net/dsa/switch.c +@@ -180,9 +180,11 @@ static int dsa_switch_bridge_leave(struc + info->sw_index, info->port, + info->br); + +- err = dsa_switch_sync_vlan_filtering(ds, info); +- if (err) +- return err; ++ if (ds->dst->index == info->tree_index && ds->index == info->sw_index) { ++ err = dsa_switch_sync_vlan_filtering(ds, info); ++ if (err) ++ return err; ++ } + + return dsa_tag_8021q_bridge_leave(ds, info); + } diff --git a/pkgs/patches-linux-5.15/774-v5.16-01-net-dsa-rtl8366rb-Support-bridge-offloading.patch b/pkgs/patches-linux-5.15/774-v5.16-01-net-dsa-rtl8366rb-Support-bridge-offloading.patch new file mode 100644 index 0000000..78570c5 --- /dev/null +++ b/pkgs/patches-linux-5.15/774-v5.16-01-net-dsa-rtl8366rb-Support-bridge-offloading.patch @@ -0,0 +1,141 @@ +From c9111895fd38dadf125e07be627778a9950d8d77 Mon Sep 17 00:00:00 2001 +From: DENG Qingfang <dqfext@gmail.com> +Date: Sun, 26 Sep 2021 00:59:24 +0200 +Subject: [PATCH 01/11] net: dsa: rtl8366rb: Support bridge offloading +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Use port isolation registers to configure bridge offloading. + +Tested on the D-Link DIR-685, switching between ports and +sniffing ports to make sure no packets leak. + +Cc: Vladimir Oltean <olteanv@gmail.com> +Cc: Mauri Sandberg <sandberg@mailfence.com> +Reviewed-by: Vladimir Oltean <olteanv@gmail.com> +Reviewed-by: Alvin Šipraga <alsi@bang-olufsen.dk> +Reviewed-by: Florian Fainelli <f.fainelli@gmail.com> +Signed-off-by: DENG Qingfang <dqfext@gmail.com> +Signed-off-by: Linus Walleij <linus.walleij@linaro.org> +Signed-off-by: David S. Miller <davem@davemloft.net> +--- + drivers/net/dsa/rtl8366rb.c | 86 +++++++++++++++++++++++++++++++++++++ + 1 file changed, 86 insertions(+) + +--- a/drivers/net/dsa/rtl8366rb.c ++++ b/drivers/net/dsa/rtl8366rb.c +@@ -300,6 +300,13 @@ + #define RTL8366RB_INTERRUPT_STATUS_REG 0x0442 + #define RTL8366RB_NUM_INTERRUPT 14 /* 0..13 */ + ++/* Port isolation registers */ ++#define RTL8366RB_PORT_ISO_BASE 0x0F08 ++#define RTL8366RB_PORT_ISO(pnum) (RTL8366RB_PORT_ISO_BASE + (pnum)) ++#define RTL8366RB_PORT_ISO_EN BIT(0) ++#define RTL8366RB_PORT_ISO_PORTS_MASK GENMASK(7, 1) ++#define RTL8366RB_PORT_ISO_PORTS(pmask) ((pmask) << 1) ++ + /* bits 0..5 enable force when cleared */ + #define RTL8366RB_MAC_FORCE_CTRL_REG 0x0F11 + +@@ -835,6 +842,21 @@ static int rtl8366rb_setup(struct dsa_sw + if (ret) + return ret; + ++ /* Isolate all user ports so they can only send packets to itself and the CPU port */ ++ for (i = 0; i < RTL8366RB_PORT_NUM_CPU; i++) { ++ ret = regmap_write(smi->map, RTL8366RB_PORT_ISO(i), ++ RTL8366RB_PORT_ISO_PORTS(BIT(RTL8366RB_PORT_NUM_CPU)) | ++ RTL8366RB_PORT_ISO_EN); ++ if (ret) ++ return ret; ++ } ++ /* CPU port can send packets to all ports */ ++ ret = regmap_write(smi->map, RTL8366RB_PORT_ISO(RTL8366RB_PORT_NUM_CPU), ++ RTL8366RB_PORT_ISO_PORTS(dsa_user_ports(ds)) | ++ RTL8366RB_PORT_ISO_EN); ++ if (ret) ++ return ret; ++ + /* Set up the "green ethernet" feature */ + ret = rtl8366rb_jam_table(rtl8366rb_green_jam, + ARRAY_SIZE(rtl8366rb_green_jam), smi, false); +@@ -1127,6 +1149,68 @@ rtl8366rb_port_disable(struct dsa_switch + rb8366rb_set_port_led(smi, port, false); + } + ++static int ++rtl8366rb_port_bridge_join(struct dsa_switch *ds, int port, ++ struct net_device *bridge) ++{ ++ struct realtek_smi *smi = ds->priv; ++ unsigned int port_bitmap = 0; ++ int ret, i; ++ ++ /* Loop over all other ports than the current one */ ++ for (i = 0; i < RTL8366RB_PORT_NUM_CPU; i++) { ++ /* Current port handled last */ ++ if (i == port) ++ continue; ++ /* Not on this bridge */ ++ if (dsa_to_port(ds, i)->bridge_dev != bridge) ++ continue; ++ /* Join this port to each other port on the bridge */ ++ ret = regmap_update_bits(smi->map, RTL8366RB_PORT_ISO(i), ++ RTL8366RB_PORT_ISO_PORTS(BIT(port)), ++ RTL8366RB_PORT_ISO_PORTS(BIT(port))); ++ if (ret) ++ dev_err(smi->dev, "failed to join port %d\n", port); ++ ++ port_bitmap |= BIT(i); ++ } ++ ++ /* Set the bits for the ports we can access */ ++ return regmap_update_bits(smi->map, RTL8366RB_PORT_ISO(port), ++ RTL8366RB_PORT_ISO_PORTS(port_bitmap), ++ RTL8366RB_PORT_ISO_PORTS(port_bitmap)); ++} ++ ++static void ++rtl8366rb_port_bridge_leave(struct dsa_switch *ds, int port, ++ struct net_device *bridge) ++{ ++ struct realtek_smi *smi = ds->priv; ++ unsigned int port_bitmap = 0; ++ int ret, i; ++ ++ /* Loop over all other ports than this one */ ++ for (i = 0; i < RTL8366RB_PORT_NUM_CPU; i++) { ++ /* Current port handled last */ ++ if (i == port) ++ continue; ++ /* Not on this bridge */ ++ if (dsa_to_port(ds, i)->bridge_dev != bridge) ++ continue; ++ /* Remove this port from any other port on the bridge */ ++ ret = regmap_update_bits(smi->map, RTL8366RB_PORT_ISO(i), ++ RTL8366RB_PORT_ISO_PORTS(BIT(port)), 0); ++ if (ret) ++ dev_err(smi->dev, "failed to leave port %d\n", port); ++ ++ port_bitmap |= BIT(i); ++ } ++ ++ /* Clear the bits for the ports we can not access, leave ourselves */ ++ regmap_update_bits(smi->map, RTL8366RB_PORT_ISO(port), ++ RTL8366RB_PORT_ISO_PORTS(port_bitmap), 0); ++} ++ + static int rtl8366rb_change_mtu(struct dsa_switch *ds, int port, int new_mtu) + { + struct realtek_smi *smi = ds->priv; +@@ -1510,6 +1594,8 @@ static const struct dsa_switch_ops rtl83 + .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 = rtl8366_vlan_filtering, + .port_vlan_add = rtl8366_vlan_add, + .port_vlan_del = rtl8366_vlan_del, diff --git a/pkgs/patches-linux-5.15/774-v5.16-02-net-dsa-rtl8366-Drop-custom-VLAN-set-up.patch b/pkgs/patches-linux-5.15/774-v5.16-02-net-dsa-rtl8366-Drop-custom-VLAN-set-up.patch new file mode 100644 index 0000000..e61349a --- /dev/null +++ b/pkgs/patches-linux-5.15/774-v5.16-02-net-dsa-rtl8366-Drop-custom-VLAN-set-up.patch @@ -0,0 +1,118 @@ +From 96cf10a8e7297065459473c081a6fb6432a22312 Mon Sep 17 00:00:00 2001 +From: Linus Walleij <linus.walleij@linaro.org> +Date: Sun, 26 Sep 2021 00:59:25 +0200 +Subject: [PATCH 02/11] net: dsa: rtl8366: Drop custom VLAN set-up +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +This hacky default VLAN setup was done in order to direct +packets to the right ports and provide port isolation, both +which we now support properly using custom tags and proper +bridge port isolation. + +We can drop the custom VLAN code and leave all VLAN handling +alone, as users expect things to be. We can also drop +ds->configure_vlan_while_not_filtering = false; and let +the core deal with any VLANs it wants. + +Cc: Mauri Sandberg <sandberg@mailfence.com> +Cc: DENG Qingfang <dqfext@gmail.com> +Reviewed-by: Vladimir Oltean <olteanv@gmail.com> +Reviewed-by: Alvin Šipraga <alsi@bang-olufsen.dk> +Reviewed-by: Florian Fainelli <f.fainelli@gmail.com> +Signed-off-by: Linus Walleij <linus.walleij@linaro.org> +Signed-off-by: David S. Miller <davem@davemloft.net> +--- + drivers/net/dsa/realtek-smi-core.h | 1 - + drivers/net/dsa/rtl8366.c | 48 ------------------------------ + drivers/net/dsa/rtl8366rb.c | 4 +-- + 3 files changed, 1 insertion(+), 52 deletions(-) + +--- a/drivers/net/dsa/realtek-smi-core.h ++++ b/drivers/net/dsa/realtek-smi-core.h +@@ -129,7 +129,6 @@ int rtl8366_set_pvid(struct realtek_smi + int rtl8366_enable_vlan4k(struct realtek_smi *smi, bool enable); + int rtl8366_enable_vlan(struct realtek_smi *smi, bool enable); + int rtl8366_reset_vlan(struct realtek_smi *smi); +-int rtl8366_init_vlan(struct realtek_smi *smi); + int rtl8366_vlan_filtering(struct dsa_switch *ds, int port, bool vlan_filtering, + struct netlink_ext_ack *extack); + int rtl8366_vlan_add(struct dsa_switch *ds, int port, +--- a/drivers/net/dsa/rtl8366.c ++++ b/drivers/net/dsa/rtl8366.c +@@ -292,54 +292,6 @@ int rtl8366_reset_vlan(struct realtek_sm + } + EXPORT_SYMBOL_GPL(rtl8366_reset_vlan); + +-int rtl8366_init_vlan(struct realtek_smi *smi) +-{ +- int port; +- int ret; +- +- ret = rtl8366_reset_vlan(smi); +- if (ret) +- return ret; +- +- /* Loop over the available ports, for each port, associate +- * it with the VLAN (port+1) +- */ +- for (port = 0; port < smi->num_ports; port++) { +- u32 mask; +- +- if (port == smi->cpu_port) +- /* For the CPU port, make all ports members of this +- * VLAN. +- */ +- mask = GENMASK((int)smi->num_ports - 1, 0); +- else +- /* For all other ports, enable itself plus the +- * CPU port. +- */ +- mask = BIT(port) | BIT(smi->cpu_port); +- +- /* For each port, set the port as member of VLAN (port+1) +- * and untagged, except for the CPU port: the CPU port (5) is +- * member of VLAN 6 and so are ALL the other ports as well. +- * Use filter 0 (no filter). +- */ +- dev_info(smi->dev, "VLAN%d port mask for port %d, %08x\n", +- (port + 1), port, mask); +- ret = rtl8366_set_vlan(smi, (port + 1), mask, mask, 0); +- if (ret) +- return ret; +- +- dev_info(smi->dev, "VLAN%d port %d, PVID set to %d\n", +- (port + 1), port, (port + 1)); +- ret = rtl8366_set_pvid(smi, port, (port + 1)); +- if (ret) +- return ret; +- } +- +- return rtl8366_enable_vlan(smi, true); +-} +-EXPORT_SYMBOL_GPL(rtl8366_init_vlan); +- + int rtl8366_vlan_filtering(struct dsa_switch *ds, int port, bool vlan_filtering, + struct netlink_ext_ack *extack) + { +--- a/drivers/net/dsa/rtl8366rb.c ++++ b/drivers/net/dsa/rtl8366rb.c +@@ -985,7 +985,7 @@ static int rtl8366rb_setup(struct dsa_sw + return ret; + } + +- ret = rtl8366_init_vlan(smi); ++ ret = rtl8366_reset_vlan(smi); + if (ret) + return ret; + +@@ -999,8 +999,6 @@ static int rtl8366rb_setup(struct dsa_sw + return -ENODEV; + } + +- ds->configure_vlan_while_not_filtering = false; +- + return 0; + } + diff --git a/pkgs/patches-linux-5.15/774-v5.16-03-net-dsa-rtl8366rb-Rewrite-weird-VLAN-filering-enable.patch b/pkgs/patches-linux-5.15/774-v5.16-03-net-dsa-rtl8366rb-Rewrite-weird-VLAN-filering-enable.patch new file mode 100644 index 0000000..2b19752 --- /dev/null +++ b/pkgs/patches-linux-5.15/774-v5.16-03-net-dsa-rtl8366rb-Rewrite-weird-VLAN-filering-enable.patch @@ -0,0 +1,270 @@ +From 7028f54b620f8df344b18e46e4a78e266091ab45 Mon Sep 17 00:00:00 2001 +From: Linus Walleij <linus.walleij@linaro.org> +Date: Sun, 26 Sep 2021 00:59:26 +0200 +Subject: [PATCH 03/11] net: dsa: rtl8366rb: Rewrite weird VLAN filering + enablement +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +While we were defining one VLAN per port for isolating the ports +the port_vlan_filtering() callback was implemented to enable a +VLAN on the port + 1. This function makes no sense, not only is +it incomplete as it only enables the VLAN, it doesn't do what +the callback is supposed to do, which is to selectively enable +and disable filtering on a certain port. + +Implement the correct callback: we have two registers dealing +with filtering on the RTL9366RB, so we implement an ASIC-specific +callback and implement filering using the register bit that makes +the switch drop frames if the port is not in the VLAN member set. + +The DSA documentation Documentation/networking/switchdev.rst states: + + When the bridge has VLAN filtering enabled and a PVID is not + configured on the ingress port, untagged and 802.1p tagged + packets must be dropped. When the bridge has VLAN filtering + enabled and a PVID exists on the ingress port, untagged and + priority-tagged packets must be accepted and forwarded according + to the bridge's port membership of the PVID VLAN. When the + bridge has VLAN filtering disabled, the presence/lack of a + PVID should not influence the packet forwarding decision. + +To comply with this, we add two arrays of bool in the RTL8366RB +state that keeps track of if filtering and PVID is enabled or +not for each port. We then add code such that whenever filtering +or PVID changes, we update the filter according to the +specification. + +Cc: Vladimir Oltean <olteanv@gmail.com> +Cc: Mauri Sandberg <sandberg@mailfence.com> +Cc: Alvin Šipraga <alsi@bang-olufsen.dk> +Cc: Florian Fainelli <f.fainelli@gmail.com> +Cc: DENG Qingfang <dqfext@gmail.com> +Signed-off-by: Linus Walleij <linus.walleij@linaro.org> +Signed-off-by: David S. Miller <davem@davemloft.net> +--- + drivers/net/dsa/realtek-smi-core.h | 2 - + drivers/net/dsa/rtl8366.c | 35 ---------- + drivers/net/dsa/rtl8366rb.c | 102 +++++++++++++++++++++++++++-- + 3 files changed, 95 insertions(+), 44 deletions(-) + +--- a/drivers/net/dsa/realtek-smi-core.h ++++ b/drivers/net/dsa/realtek-smi-core.h +@@ -129,8 +129,6 @@ int rtl8366_set_pvid(struct realtek_smi + int rtl8366_enable_vlan4k(struct realtek_smi *smi, bool enable); + int rtl8366_enable_vlan(struct realtek_smi *smi, bool enable); + int rtl8366_reset_vlan(struct realtek_smi *smi); +-int rtl8366_vlan_filtering(struct dsa_switch *ds, int port, bool vlan_filtering, +- struct netlink_ext_ack *extack); + int rtl8366_vlan_add(struct dsa_switch *ds, int port, + const struct switchdev_obj_port_vlan *vlan, + struct netlink_ext_ack *extack); +--- a/drivers/net/dsa/rtl8366.c ++++ b/drivers/net/dsa/rtl8366.c +@@ -292,41 +292,6 @@ int rtl8366_reset_vlan(struct realtek_sm + } + EXPORT_SYMBOL_GPL(rtl8366_reset_vlan); + +-int rtl8366_vlan_filtering(struct dsa_switch *ds, int port, bool vlan_filtering, +- struct netlink_ext_ack *extack) +-{ +- struct realtek_smi *smi = ds->priv; +- struct rtl8366_vlan_4k vlan4k; +- int ret; +- +- /* Use VLAN nr port + 1 since VLAN0 is not valid */ +- if (!smi->ops->is_vlan_valid(smi, port + 1)) +- return -EINVAL; +- +- dev_info(smi->dev, "%s filtering on port %d\n", +- vlan_filtering ? "enable" : "disable", +- port); +- +- /* TODO: +- * The hardware support filter ID (FID) 0..7, I have no clue how to +- * support this in the driver when the callback only says on/off. +- */ +- ret = smi->ops->get_vlan_4k(smi, port + 1, &vlan4k); +- if (ret) +- return ret; +- +- /* Just set the filter to FID 1 for now then */ +- ret = rtl8366_set_vlan(smi, port + 1, +- vlan4k.member, +- vlan4k.untag, +- 1); +- if (ret) +- return ret; +- +- return 0; +-} +-EXPORT_SYMBOL_GPL(rtl8366_vlan_filtering); +- + int rtl8366_vlan_add(struct dsa_switch *ds, int port, + const struct switchdev_obj_port_vlan *vlan, + struct netlink_ext_ack *extack) +--- a/drivers/net/dsa/rtl8366rb.c ++++ b/drivers/net/dsa/rtl8366rb.c +@@ -143,6 +143,21 @@ + #define RTL8366RB_PHY_NO_OFFSET 9 + #define RTL8366RB_PHY_NO_MASK (0x1f << 9) + ++/* VLAN Ingress Control Register 1, one bit per port. ++ * bit 0 .. 5 will make the switch drop ingress frames without ++ * VID such as untagged or priority-tagged frames for respective ++ * port. ++ * bit 6 .. 11 will make the switch drop ingress frames carrying ++ * a C-tag with VID != 0 for respective port. ++ */ ++#define RTL8366RB_VLAN_INGRESS_CTRL1_REG 0x037E ++#define RTL8366RB_VLAN_INGRESS_CTRL1_DROP(port) (BIT((port)) | BIT((port) + 6)) ++ ++/* VLAN Ingress Control Register 2, one bit per port. ++ * bit0 .. bit5 will make the switch drop all ingress frames with ++ * a VLAN classification that does not include the port is in its ++ * member set. ++ */ + #define RTL8366RB_VLAN_INGRESS_CTRL2_REG 0x037f + + /* LED control registers */ +@@ -321,9 +336,13 @@ + /** + * struct rtl8366rb - RTL8366RB-specific data + * @max_mtu: per-port max MTU setting ++ * @pvid_enabled: if PVID is set for respective port ++ * @vlan_filtering: if VLAN filtering is enabled for respective port + */ + struct rtl8366rb { + unsigned int max_mtu[RTL8366RB_NUM_PORTS]; ++ bool pvid_enabled[RTL8366RB_NUM_PORTS]; ++ bool vlan_filtering[RTL8366RB_NUM_PORTS]; + }; + + static struct rtl8366_mib_counter rtl8366rb_mib_counters[] = { +@@ -933,11 +952,13 @@ static int rtl8366rb_setup(struct dsa_sw + if (ret) + return ret; + +- /* Discard VLAN tagged packets if the port is not a member of +- * the VLAN with which the packets is associated. +- */ ++ /* Accept all packets by default, we enable filtering on-demand */ ++ ret = regmap_write(smi->map, RTL8366RB_VLAN_INGRESS_CTRL1_REG, ++ 0); ++ if (ret) ++ return ret; + ret = regmap_write(smi->map, RTL8366RB_VLAN_INGRESS_CTRL2_REG, +- RTL8366RB_PORT_ALL); ++ 0); + if (ret) + return ret; + +@@ -1209,6 +1230,53 @@ rtl8366rb_port_bridge_leave(struct dsa_s + RTL8366RB_PORT_ISO_PORTS(port_bitmap), 0); + } + ++/** ++ * rtl8366rb_drop_untagged() - make the switch drop untagged and C-tagged frames ++ * @smi: SMI state container ++ * @port: the port to drop untagged and C-tagged frames on ++ * @drop: whether to drop or pass untagged and C-tagged frames ++ */ ++static int rtl8366rb_drop_untagged(struct realtek_smi *smi, int port, bool drop) ++{ ++ return regmap_update_bits(smi->map, RTL8366RB_VLAN_INGRESS_CTRL1_REG, ++ RTL8366RB_VLAN_INGRESS_CTRL1_DROP(port), ++ drop ? RTL8366RB_VLAN_INGRESS_CTRL1_DROP(port) : 0); ++} ++ ++static int rtl8366rb_vlan_filtering(struct dsa_switch *ds, int port, ++ bool vlan_filtering, ++ struct netlink_ext_ack *extack) ++{ ++ struct realtek_smi *smi = ds->priv; ++ struct rtl8366rb *rb; ++ int ret; ++ ++ rb = smi->chip_data; ++ ++ dev_dbg(smi->dev, "port %d: %s VLAN filtering\n", port, ++ vlan_filtering ? "enable" : "disable"); ++ ++ /* If the port is not in the member set, the frame will be dropped */ ++ ret = regmap_update_bits(smi->map, RTL8366RB_VLAN_INGRESS_CTRL2_REG, ++ BIT(port), vlan_filtering ? BIT(port) : 0); ++ if (ret) ++ return ret; ++ ++ /* Keep track if filtering is enabled on each port */ ++ rb->vlan_filtering[port] = vlan_filtering; ++ ++ /* If VLAN filtering is enabled and PVID is also enabled, we must ++ * not drop any untagged or C-tagged frames. If we turn off VLAN ++ * filtering on a port, we need ti accept any frames. ++ */ ++ if (vlan_filtering) ++ ret = rtl8366rb_drop_untagged(smi, port, !rb->pvid_enabled[port]); ++ else ++ ret = rtl8366rb_drop_untagged(smi, port, false); ++ ++ return ret; ++} ++ + static int rtl8366rb_change_mtu(struct dsa_switch *ds, int port, int new_mtu) + { + struct realtek_smi *smi = ds->priv; +@@ -1420,14 +1488,34 @@ static int rtl8366rb_get_mc_index(struct + + static int rtl8366rb_set_mc_index(struct realtek_smi *smi, int port, int index) + { ++ struct rtl8366rb *rb; ++ bool pvid_enabled; ++ int ret; ++ ++ rb = smi->chip_data; ++ pvid_enabled = !!index; ++ + if (port >= smi->num_ports || index >= RTL8366RB_NUM_VLANS) + return -EINVAL; + +- return regmap_update_bits(smi->map, RTL8366RB_PORT_VLAN_CTRL_REG(port), ++ ret = regmap_update_bits(smi->map, RTL8366RB_PORT_VLAN_CTRL_REG(port), + RTL8366RB_PORT_VLAN_CTRL_MASK << + RTL8366RB_PORT_VLAN_CTRL_SHIFT(port), + (index & RTL8366RB_PORT_VLAN_CTRL_MASK) << + RTL8366RB_PORT_VLAN_CTRL_SHIFT(port)); ++ if (ret) ++ return ret; ++ ++ rb->pvid_enabled[port] = pvid_enabled; ++ ++ /* If VLAN filtering is enabled and PVID is also enabled, we must ++ * not drop any untagged or C-tagged frames. Make sure to update the ++ * filtering setting. ++ */ ++ if (rb->vlan_filtering[port]) ++ ret = rtl8366rb_drop_untagged(smi, port, !pvid_enabled); ++ ++ return ret; + } + + static bool rtl8366rb_is_vlan_valid(struct realtek_smi *smi, unsigned int vlan) +@@ -1437,7 +1525,7 @@ static bool rtl8366rb_is_vlan_valid(stru + if (smi->vlan4k_enabled) + max = RTL8366RB_NUM_VIDS - 1; + +- if (vlan == 0 || vlan > max) ++ if (vlan > max) + return false; + + return true; +@@ -1594,7 +1682,7 @@ static const struct dsa_switch_ops rtl83 + .get_sset_count = rtl8366_get_sset_count, + .port_bridge_join = rtl8366rb_port_bridge_join, + .port_bridge_leave = rtl8366rb_port_bridge_leave, +- .port_vlan_filtering = rtl8366_vlan_filtering, ++ .port_vlan_filtering = rtl8366rb_vlan_filtering, + .port_vlan_add = rtl8366_vlan_add, + .port_vlan_del = rtl8366_vlan_del, + .port_enable = rtl8366rb_port_enable, diff --git a/pkgs/patches-linux-5.15/774-v5.16-06-net-dsa-rtl8366-Drop-and-depromote-pointless-prints.patch b/pkgs/patches-linux-5.15/774-v5.16-06-net-dsa-rtl8366-Drop-and-depromote-pointless-prints.patch new file mode 100644 index 0000000..b56032c --- /dev/null +++ b/pkgs/patches-linux-5.15/774-v5.16-06-net-dsa-rtl8366-Drop-and-depromote-pointless-prints.patch @@ -0,0 +1,51 @@ +From ddb59a5dc42714999c335dab4bf256125ba3120c Mon Sep 17 00:00:00 2001 +From: Linus Walleij <linus.walleij@linaro.org> +Date: Sun, 26 Sep 2021 00:59:29 +0200 +Subject: [PATCH 06/11] net: dsa: rtl8366: Drop and depromote pointless prints +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +We don't need a message for every VLAN association, dbg +is fine. The message about adding the DSA or CPU +port to a VLAN is directly misleading, this is perfectly +fine. + +Cc: Vladimir Oltean <olteanv@gmail.com> +Cc: Mauri Sandberg <sandberg@mailfence.com> +Cc: DENG Qingfang <dqfext@gmail.com> +Reviewed-by: Alvin Šipraga <alsi@bang-olufsen.dk> +Reviewed-by: Florian Fainelli <f.fainelli@gmail.com> +Signed-off-by: Linus Walleij <linus.walleij@linaro.org> +Signed-off-by: David S. Miller <davem@davemloft.net> +--- + drivers/net/dsa/rtl8366.c | 11 ++++------- + 1 file changed, 4 insertions(+), 7 deletions(-) + +--- a/drivers/net/dsa/rtl8366.c ++++ b/drivers/net/dsa/rtl8366.c +@@ -318,12 +318,9 @@ int rtl8366_vlan_add(struct dsa_switch * + return ret; + } + +- dev_info(smi->dev, "add VLAN %d on port %d, %s, %s\n", +- vlan->vid, port, untagged ? "untagged" : "tagged", +- pvid ? " PVID" : "no PVID"); +- +- if (dsa_is_dsa_port(ds, port) || dsa_is_cpu_port(ds, port)) +- dev_err(smi->dev, "port is DSA or CPU port\n"); ++ dev_dbg(smi->dev, "add VLAN %d on port %d, %s, %s\n", ++ vlan->vid, port, untagged ? "untagged" : "tagged", ++ pvid ? "PVID" : "no PVID"); + + member |= BIT(port); + +@@ -356,7 +353,7 @@ int rtl8366_vlan_del(struct dsa_switch * + struct realtek_smi *smi = ds->priv; + int ret, i; + +- dev_info(smi->dev, "del VLAN %04x on port %d\n", vlan->vid, port); ++ dev_dbg(smi->dev, "del VLAN %d on port %d\n", vlan->vid, port); + + for (i = 0; i < smi->num_vlan_mc; i++) { + struct rtl8366_vlan_mc vlanmc; diff --git a/pkgs/patches-linux-5.15/774-v5.16-07-net-dsa-rtl8366rb-Use-core-filtering-tracking.patch b/pkgs/patches-linux-5.15/774-v5.16-07-net-dsa-rtl8366rb-Use-core-filtering-tracking.patch new file mode 100644 index 0000000..8cd1df9 --- /dev/null +++ b/pkgs/patches-linux-5.15/774-v5.16-07-net-dsa-rtl8366rb-Use-core-filtering-tracking.patch @@ -0,0 +1,61 @@ +From 5c9b66f3c8a3f72fa2a58e89a57c6d7afd550bf0 Mon Sep 17 00:00:00 2001 +From: Linus Walleij <linus.walleij@linaro.org> +Date: Wed, 29 Sep 2021 13:23:22 +0200 +Subject: [PATCH 07/11] net: dsa: rtl8366rb: Use core filtering tracking +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +We added a state variable to track whether a certain port +was VLAN filtering or not, but we can just inquire the DSA +core about this. + +Cc: Vladimir Oltean <olteanv@gmail.com> +Cc: Mauri Sandberg <sandberg@mailfence.com> +Cc: DENG Qingfang <dqfext@gmail.com> +Cc: Alvin Šipraga <alsi@bang-olufsen.dk> +Cc: Reviewed-by: Florian Fainelli <f.fainelli@gmail.com> +Signed-off-by: Linus Walleij <linus.walleij@linaro.org> +Signed-off-by: David S. Miller <davem@davemloft.net> +--- + drivers/net/dsa/rtl8366rb.c | 9 ++------- + 1 file changed, 2 insertions(+), 7 deletions(-) + +--- a/drivers/net/dsa/rtl8366rb.c ++++ b/drivers/net/dsa/rtl8366rb.c +@@ -337,12 +337,10 @@ + * struct rtl8366rb - RTL8366RB-specific data + * @max_mtu: per-port max MTU setting + * @pvid_enabled: if PVID is set for respective port +- * @vlan_filtering: if VLAN filtering is enabled for respective port + */ + struct rtl8366rb { + unsigned int max_mtu[RTL8366RB_NUM_PORTS]; + bool pvid_enabled[RTL8366RB_NUM_PORTS]; +- bool vlan_filtering[RTL8366RB_NUM_PORTS]; + }; + + static struct rtl8366_mib_counter rtl8366rb_mib_counters[] = { +@@ -1262,12 +1260,9 @@ static int rtl8366rb_vlan_filtering(stru + if (ret) + return ret; + +- /* Keep track if filtering is enabled on each port */ +- rb->vlan_filtering[port] = vlan_filtering; +- + /* If VLAN filtering is enabled and PVID is also enabled, we must + * not drop any untagged or C-tagged frames. If we turn off VLAN +- * filtering on a port, we need ti accept any frames. ++ * filtering on a port, we need to accept any frames. + */ + if (vlan_filtering) + ret = rtl8366rb_drop_untagged(smi, port, !rb->pvid_enabled[port]); +@@ -1512,7 +1507,7 @@ static int rtl8366rb_set_mc_index(struct + * not drop any untagged or C-tagged frames. Make sure to update the + * filtering setting. + */ +- if (rb->vlan_filtering[port]) ++ if (dsa_port_is_vlan_filtering(dsa_to_port(smi->ds, port))) + ret = rtl8366rb_drop_untagged(smi, port, !pvid_enabled); + + return ret; diff --git a/pkgs/patches-linux-5.15/774-v5.16-08-net-dsa-rtl8366rb-Support-disabling-learning.patch b/pkgs/patches-linux-5.15/774-v5.16-08-net-dsa-rtl8366rb-Support-disabling-learning.patch new file mode 100644 index 0000000..8306eb5 --- /dev/null +++ b/pkgs/patches-linux-5.15/774-v5.16-08-net-dsa-rtl8366rb-Support-disabling-learning.patch @@ -0,0 +1,115 @@ +From 831a3d26bea0d14f8563eecf96def660a74a3000 Mon Sep 17 00:00:00 2001 +From: Linus Walleij <linus.walleij@linaro.org> +Date: Tue, 5 Oct 2021 21:47:02 +0200 +Subject: [PATCH 08/11] net: dsa: rtl8366rb: Support disabling learning +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +The RTL8366RB hardware supports disabling learning per-port +so let's make use of this feature. Rename some unfortunately +named registers in the process. + +Suggested-by: Vladimir Oltean <olteanv@gmail.com> +Cc: Alvin Šipraga <alsi@bang-olufsen.dk> +Cc: Mauri Sandberg <sandberg@mailfence.com> +Cc: Florian Fainelli <f.fainelli@gmail.com> +Cc: DENG Qingfang <dqfext@gmail.com> +Reviewed-by: Vladimir Oltean <olteanv@gmail.com> +Signed-off-by: Linus Walleij <linus.walleij@linaro.org> +Signed-off-by: David S. Miller <davem@davemloft.net> +--- + drivers/net/dsa/rtl8366rb.c | 50 ++++++++++++++++++++++++++++++++----- + 1 file changed, 44 insertions(+), 6 deletions(-) + +--- a/drivers/net/dsa/rtl8366rb.c ++++ b/drivers/net/dsa/rtl8366rb.c +@@ -14,6 +14,7 @@ + + #include <linux/bitops.h> + #include <linux/etherdevice.h> ++#include <linux/if_bridge.h> + #include <linux/interrupt.h> + #include <linux/irqdomain.h> + #include <linux/irqchip/chained_irq.h> +@@ -42,9 +43,12 @@ + /* Port Enable Control register */ + #define RTL8366RB_PECR 0x0001 + +-/* Switch Security Control registers */ +-#define RTL8366RB_SSCR0 0x0002 +-#define RTL8366RB_SSCR1 0x0003 ++/* Switch per-port learning disablement register */ ++#define RTL8366RB_PORT_LEARNDIS_CTRL 0x0002 ++ ++/* Security control, actually aging register */ ++#define RTL8366RB_SECURITY_CTRL 0x0003 ++ + #define RTL8366RB_SSCR2 0x0004 + #define RTL8366RB_SSCR2_DROP_UNKNOWN_DA BIT(0) + +@@ -927,13 +931,14 @@ static int rtl8366rb_setup(struct dsa_sw + /* layer 2 size, see rtl8366rb_change_mtu() */ + rb->max_mtu[i] = 1532; + +- /* Enable learning for all ports */ +- ret = regmap_write(smi->map, RTL8366RB_SSCR0, 0); ++ /* Disable learning for all ports */ ++ ret = regmap_write(smi->map, RTL8366RB_PORT_LEARNDIS_CTRL, ++ RTL8366RB_PORT_ALL); + if (ret) + return ret; + + /* Enable auto ageing for all ports */ +- ret = regmap_write(smi->map, RTL8366RB_SSCR1, 0); ++ ret = regmap_write(smi->map, RTL8366RB_SECURITY_CTRL, 0); + if (ret) + return ret; + +@@ -1272,6 +1277,37 @@ static int rtl8366rb_vlan_filtering(stru + return ret; + } + ++static int ++rtl8366rb_port_pre_bridge_flags(struct dsa_switch *ds, int port, ++ struct switchdev_brport_flags flags, ++ struct netlink_ext_ack *extack) ++{ ++ /* We support enabling/disabling learning */ ++ if (flags.mask & ~(BR_LEARNING)) ++ return -EINVAL; ++ ++ return 0; ++} ++ ++static int ++rtl8366rb_port_bridge_flags(struct dsa_switch *ds, int port, ++ struct switchdev_brport_flags flags, ++ struct netlink_ext_ack *extack) ++{ ++ struct realtek_smi *smi = ds->priv; ++ int ret; ++ ++ if (flags.mask & BR_LEARNING) { ++ ret = regmap_update_bits(smi->map, RTL8366RB_PORT_LEARNDIS_CTRL, ++ BIT(port), ++ (flags.val & BR_LEARNING) ? 0 : BIT(port)); ++ if (ret) ++ return ret; ++ } ++ ++ return 0; ++} ++ + static int rtl8366rb_change_mtu(struct dsa_switch *ds, int port, int new_mtu) + { + struct realtek_smi *smi = ds->priv; +@@ -1682,6 +1718,8 @@ static const struct dsa_switch_ops rtl83 + .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_change_mtu = rtl8366rb_change_mtu, + .port_max_mtu = rtl8366rb_max_mtu, + }; diff --git a/pkgs/patches-linux-5.15/774-v5.16-09-net-dsa-rtl8366rb-Support-fast-aging.patch b/pkgs/patches-linux-5.15/774-v5.16-09-net-dsa-rtl8366rb-Support-fast-aging.patch new file mode 100644 index 0000000..a74108e --- /dev/null +++ b/pkgs/patches-linux-5.15/774-v5.16-09-net-dsa-rtl8366rb-Support-fast-aging.patch @@ -0,0 +1,57 @@ +From 8eb13420eb9ab4a4e2ebd612bf5dc9dba0039236 Mon Sep 17 00:00:00 2001 +From: Linus Walleij <linus.walleij@linaro.org> +Date: Tue, 5 Oct 2021 21:47:03 +0200 +Subject: [PATCH 09/11] net: dsa: rtl8366rb: Support fast aging +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +This implements fast aging per-port using the special "security" +register, which will flush any learned L2 LUT entries on a port. + +The vendor API just enabled setting and clearing this bit, so +we set it to age out any entries on the port and then we clear +it again. + +Suggested-by: Vladimir Oltean <olteanv@gmail.com> +Cc: Mauri Sandberg <sandberg@mailfence.com> +Cc: DENG Qingfang <dqfext@gmail.com> +Cc: Florian Fainelli <f.fainelli@gmail.com> +Reviewed-by: Alvin Šipraga <alsi@bang-olufsen.dk> +Signed-off-by: Linus Walleij <linus.walleij@linaro.org> +Reviewed-by: Vladimir Oltean <olteanv@gmail.com> +Signed-off-by: David S. Miller <davem@davemloft.net> +--- + drivers/net/dsa/rtl8366rb.c | 14 ++++++++++++++ + 1 file changed, 14 insertions(+) + +--- a/drivers/net/dsa/rtl8366rb.c ++++ b/drivers/net/dsa/rtl8366rb.c +@@ -1308,6 +1308,19 @@ rtl8366rb_port_bridge_flags(struct dsa_s + return 0; + } + ++static void ++rtl8366rb_port_fast_age(struct dsa_switch *ds, int port) ++{ ++ struct realtek_smi *smi = ds->priv; ++ ++ /* This will age out any learned L2 entries */ ++ regmap_update_bits(smi->map, RTL8366RB_SECURITY_CTRL, ++ BIT(port), BIT(port)); ++ /* Restore the normal state of things */ ++ regmap_update_bits(smi->map, RTL8366RB_SECURITY_CTRL, ++ BIT(port), 0); ++} ++ + static int rtl8366rb_change_mtu(struct dsa_switch *ds, int port, int new_mtu) + { + struct realtek_smi *smi = ds->priv; +@@ -1720,6 +1733,7 @@ static const struct dsa_switch_ops rtl83 + .port_disable = rtl8366rb_port_disable, + .port_pre_bridge_flags = rtl8366rb_port_pre_bridge_flags, + .port_bridge_flags = rtl8366rb_port_bridge_flags, ++ .port_fast_age = rtl8366rb_port_fast_age, + .port_change_mtu = rtl8366rb_change_mtu, + .port_max_mtu = rtl8366rb_max_mtu, + }; diff --git a/pkgs/patches-linux-5.15/774-v5.16-10-net-dsa-rtl8366rb-Support-setting-STP-state.patch b/pkgs/patches-linux-5.15/774-v5.16-10-net-dsa-rtl8366rb-Support-setting-STP-state.patch new file mode 100644 index 0000000..e787ce9 --- /dev/null +++ b/pkgs/patches-linux-5.15/774-v5.16-10-net-dsa-rtl8366rb-Support-setting-STP-state.patch @@ -0,0 +1,107 @@ +From 90c855471a89d3e05ecf5b6464bd04abf2c83b70 Mon Sep 17 00:00:00 2001 +From: Linus Walleij <linus.walleij@linaro.org> +Date: Tue, 5 Oct 2021 21:47:04 +0200 +Subject: [PATCH 10/11] net: dsa: rtl8366rb: Support setting STP state +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +This adds support for setting the STP state to the RTL8366RB +DSA switch. This rids the following message from the kernel on +e.g. OpenWrt: + +DSA: failed to set STP state 3 (-95) + +Since the RTL8366RB has one STP state register per FID with +two bit per port in each, we simply loop over all the FIDs +and set the state on all of them. + +Cc: Vladimir Oltean <olteanv@gmail.com> +Cc: Alvin Šipraga <alsi@bang-olufsen.dk> +Cc: Mauri Sandberg <sandberg@mailfence.com> +Cc: DENG Qingfang <dqfext@gmail.com> +Signed-off-by: Linus Walleij <linus.walleij@linaro.org> +Reviewed-by: Vladimir Oltean <olteanv@gmail.com> +Signed-off-by: David S. Miller <davem@davemloft.net> +--- + drivers/net/dsa/rtl8366rb.c | 48 +++++++++++++++++++++++++++++++++++++ + 1 file changed, 48 insertions(+) + +--- a/drivers/net/dsa/rtl8366rb.c ++++ b/drivers/net/dsa/rtl8366rb.c +@@ -110,6 +110,18 @@ + + #define RTL8366RB_POWER_SAVING_REG 0x0021 + ++/* Spanning tree status (STP) control, two bits per port per FID */ ++#define RTL8366RB_STP_STATE_BASE 0x0050 /* 0x0050..0x0057 */ ++#define RTL8366RB_STP_STATE_DISABLED 0x0 ++#define RTL8366RB_STP_STATE_BLOCKING 0x1 ++#define RTL8366RB_STP_STATE_LEARNING 0x2 ++#define RTL8366RB_STP_STATE_FORWARDING 0x3 ++#define RTL8366RB_STP_MASK GENMASK(1, 0) ++#define RTL8366RB_STP_STATE(port, state) \ ++ ((state) << ((port) * 2)) ++#define RTL8366RB_STP_STATE_MASK(port) \ ++ RTL8366RB_STP_STATE((port), RTL8366RB_STP_MASK) ++ + /* CPU port control reg */ + #define RTL8368RB_CPU_CTRL_REG 0x0061 + #define RTL8368RB_CPU_PORTS_MSK 0x00FF +@@ -234,6 +246,7 @@ + #define RTL8366RB_NUM_LEDGROUPS 4 + #define RTL8366RB_NUM_VIDS 4096 + #define RTL8366RB_PRIORITYMAX 7 ++#define RTL8366RB_NUM_FIDS 8 + #define RTL8366RB_FIDMAX 7 + + #define RTL8366RB_PORT_1 BIT(0) /* In userspace port 0 */ +@@ -1309,6 +1322,40 @@ rtl8366rb_port_bridge_flags(struct dsa_s + } + + static void ++rtl8366rb_port_stp_state_set(struct dsa_switch *ds, int port, u8 state) ++{ ++ struct realtek_smi *smi = ds->priv; ++ u32 val; ++ int i; ++ ++ switch (state) { ++ case BR_STATE_DISABLED: ++ val = RTL8366RB_STP_STATE_DISABLED; ++ break; ++ case BR_STATE_BLOCKING: ++ case BR_STATE_LISTENING: ++ val = RTL8366RB_STP_STATE_BLOCKING; ++ break; ++ case BR_STATE_LEARNING: ++ val = RTL8366RB_STP_STATE_LEARNING; ++ break; ++ case BR_STATE_FORWARDING: ++ val = RTL8366RB_STP_STATE_FORWARDING; ++ break; ++ default: ++ dev_err(smi->dev, "unknown bridge state requested\n"); ++ return; ++ }; ++ ++ /* Set the same status for the port on all the FIDs */ ++ for (i = 0; i < RTL8366RB_NUM_FIDS; i++) { ++ regmap_update_bits(smi->map, RTL8366RB_STP_STATE_BASE + i, ++ RTL8366RB_STP_STATE_MASK(port), ++ RTL8366RB_STP_STATE(port, val)); ++ } ++} ++ ++static void + rtl8366rb_port_fast_age(struct dsa_switch *ds, int port) + { + struct realtek_smi *smi = ds->priv; +@@ -1733,6 +1780,7 @@ static const struct dsa_switch_ops rtl83 + .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, diff --git a/pkgs/patches-linux-5.15/775-v5.16-1-net-phy-add-phy_interface_t-bitmap-support.patch b/pkgs/patches-linux-5.15/775-v5.16-1-net-phy-add-phy_interface_t-bitmap-support.patch new file mode 100644 index 0000000..69f6a5d --- /dev/null +++ b/pkgs/patches-linux-5.15/775-v5.16-1-net-phy-add-phy_interface_t-bitmap-support.patch @@ -0,0 +1,66 @@ +From 8e20f591f204f8db7f1182918f8e2285d3f589e0 Mon Sep 17 00:00:00 2001 +From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk> +Date: Tue, 26 Oct 2021 11:06:01 +0100 +Subject: [PATCH 1/3] net: phy: add phy_interface_t bitmap support + +Add support for a bitmap for phy interface modes, which includes: +- a macro to declare the interface bitmap +- an inline helper to zero the interface bitmap +- an inline helper to detect an empty interface bitmap +- inline helpers to do a bitwise AND and OR operations on two interface + bitmaps + +Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk> +Signed-off-by: David S. Miller <davem@davemloft.net> +--- + include/linux/phy.h | 34 ++++++++++++++++++++++++++++++++++ + 1 file changed, 34 insertions(+) + +diff --git a/include/linux/phy.h b/include/linux/phy.h +index 04e90423fa88..96e43fbb2dd8 100644 +--- a/include/linux/phy.h ++++ b/include/linux/phy.h +@@ -155,6 +155,40 @@ typedef enum { + PHY_INTERFACE_MODE_MAX, + } phy_interface_t; + ++/* PHY interface mode bitmap handling */ ++#define DECLARE_PHY_INTERFACE_MASK(name) \ ++ DECLARE_BITMAP(name, PHY_INTERFACE_MODE_MAX) ++ ++static inline void phy_interface_zero(unsigned long *intf) ++{ ++ bitmap_zero(intf, PHY_INTERFACE_MODE_MAX); ++} ++ ++static inline bool phy_interface_empty(const unsigned long *intf) ++{ ++ return bitmap_empty(intf, PHY_INTERFACE_MODE_MAX); ++} ++ ++static inline void phy_interface_and(unsigned long *dst, const unsigned long *a, ++ const unsigned long *b) ++{ ++ bitmap_and(dst, a, b, PHY_INTERFACE_MODE_MAX); ++} ++ ++static inline void phy_interface_or(unsigned long *dst, const unsigned long *a, ++ const unsigned long *b) ++{ ++ bitmap_or(dst, a, b, PHY_INTERFACE_MODE_MAX); ++} ++ ++static inline void phy_interface_set_rgmii(unsigned long *intf) ++{ ++ __set_bit(PHY_INTERFACE_MODE_RGMII, intf); ++ __set_bit(PHY_INTERFACE_MODE_RGMII_ID, intf); ++ __set_bit(PHY_INTERFACE_MODE_RGMII_RXID, intf); ++ __set_bit(PHY_INTERFACE_MODE_RGMII_TXID, intf); ++} ++ + /* + * phy_supported_speeds - return all speeds currently supported by a PHY device + */ +-- +2.35.1 + diff --git a/pkgs/patches-linux-5.15/775-v5.16-2-net-phylink-add-MAC-phy_interface_t-bitmap.patch b/pkgs/patches-linux-5.15/775-v5.16-2-net-phylink-add-MAC-phy_interface_t-bitmap.patch new file mode 100644 index 0000000..2141a3c --- /dev/null +++ b/pkgs/patches-linux-5.15/775-v5.16-2-net-phylink-add-MAC-phy_interface_t-bitmap.patch @@ -0,0 +1,29 @@ +From 38c310eb46f5f80213a92093af11af270c209a76 Mon Sep 17 00:00:00 2001 +From: Russell King <rmk+kernel@armlinux.org.uk> +Date: Tue, 26 Oct 2021 11:06:06 +0100 +Subject: [PATCH 2/3] net: phylink: add MAC phy_interface_t bitmap + +Add a phy_interface_t bitmap so the MAC driver can specifiy which PHY +interface modes it supports. + +Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk> +Signed-off-by: David S. Miller <davem@davemloft.net> +--- + include/linux/phylink.h | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/include/linux/phylink.h b/include/linux/phylink.h +index f7b5ed06a815..bc4b866cd99b 100644 +--- a/include/linux/phylink.h ++++ b/include/linux/phylink.h +@@ -76,6 +76,7 @@ struct phylink_config { + bool ovr_an_inband; + void (*get_fixed_state)(struct phylink_config *config, + struct phylink_link_state *state); ++ DECLARE_PHY_INTERFACE_MASK(supported_interfaces); + }; + + /** +-- +2.35.1 + diff --git a/pkgs/patches-linux-5.15/775-v5.16-3-net-phylink-use-supported_interfaces-for-phylink-val.patch b/pkgs/patches-linux-5.15/775-v5.16-3-net-phylink-use-supported_interfaces-for-phylink-val.patch new file mode 100644 index 0000000..fad5044 --- /dev/null +++ b/pkgs/patches-linux-5.15/775-v5.16-3-net-phylink-use-supported_interfaces-for-phylink-val.patch @@ -0,0 +1,105 @@ +From d25f3a74f30aace819163dfa54f2a4b8ca1dc932 Mon Sep 17 00:00:00 2001 +From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk> +Date: Tue, 26 Oct 2021 11:06:11 +0100 +Subject: [PATCH 3/3] net: phylink: use supported_interfaces for phylink + validation + +If the network device supplies a supported interface bitmap, we can use +that during phylink's validation to simplify MAC drivers in two ways by +using the supported_interfaces bitmap to: + +1. reject unsupported interfaces before calling into the MAC driver. +2. generate the set of all supported link modes across all supported + interfaces (used mainly for SFP, but also some 10G PHYs.) + +Suggested-by: Sean Anderson <sean.anderson@seco.com> +Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk> +Signed-off-by: David S. Miller <davem@davemloft.net> +--- + drivers/net/phy/phylink.c | 36 ++++++++++++++++++++++++++++++++++++ + include/linux/phylink.h | 12 ++++++++++-- + 2 files changed, 46 insertions(+), 2 deletions(-) + +diff --git a/drivers/net/phy/phylink.c b/drivers/net/phy/phylink.c +index 14c7d73790b4..6da245dacca4 100644 +--- a/drivers/net/phy/phylink.c ++++ b/drivers/net/phy/phylink.c +@@ -155,9 +155,45 @@ static const char *phylink_an_mode_str(unsigned int mode) + return mode < ARRAY_SIZE(modestr) ? modestr[mode] : "unknown"; + } + ++static int phylink_validate_any(struct phylink *pl, unsigned long *supported, ++ struct phylink_link_state *state) ++{ ++ __ETHTOOL_DECLARE_LINK_MODE_MASK(all_adv) = { 0, }; ++ __ETHTOOL_DECLARE_LINK_MODE_MASK(all_s) = { 0, }; ++ __ETHTOOL_DECLARE_LINK_MODE_MASK(s); ++ struct phylink_link_state t; ++ int intf; ++ ++ for (intf = 0; intf < PHY_INTERFACE_MODE_MAX; intf++) { ++ if (test_bit(intf, pl->config->supported_interfaces)) { ++ linkmode_copy(s, supported); ++ ++ t = *state; ++ t.interface = intf; ++ pl->mac_ops->validate(pl->config, s, &t); ++ linkmode_or(all_s, all_s, s); ++ linkmode_or(all_adv, all_adv, t.advertising); ++ } ++ } ++ ++ linkmode_copy(supported, all_s); ++ linkmode_copy(state->advertising, all_adv); ++ ++ return phylink_is_empty_linkmode(supported) ? -EINVAL : 0; ++} ++ + static int phylink_validate(struct phylink *pl, unsigned long *supported, + struct phylink_link_state *state) + { ++ if (!phy_interface_empty(pl->config->supported_interfaces)) { ++ if (state->interface == PHY_INTERFACE_MODE_NA) ++ return phylink_validate_any(pl, supported, state); ++ ++ if (!test_bit(state->interface, ++ pl->config->supported_interfaces)) ++ return -EINVAL; ++ } ++ + pl->mac_ops->validate(pl->config, supported, state); + + return phylink_is_empty_linkmode(supported) ? -EINVAL : 0; +diff --git a/include/linux/phylink.h b/include/linux/phylink.h +index bc4b866cd99b..f037470b6fb3 100644 +--- a/include/linux/phylink.h ++++ b/include/linux/phylink.h +@@ -67,6 +67,8 @@ enum phylink_op_type { + * @ovr_an_inband: if true, override PCS to MLO_AN_INBAND + * @get_fixed_state: callback to execute to determine the fixed link state, + * if MAC link is at %MLO_AN_FIXED mode. ++ * @supported_interfaces: bitmap describing which PHY_INTERFACE_MODE_xxx ++ * are supported by the MAC/PCS. + */ + struct phylink_config { + struct device *dev; +@@ -134,8 +136,14 @@ struct phylink_mac_ops { + * based on @state->advertising and/or @state->speed and update + * @state->interface accordingly. See phylink_helper_basex_speed(). + * +- * When @state->interface is %PHY_INTERFACE_MODE_NA, phylink expects the +- * MAC driver to return all supported link modes. ++ * When @config->supported_interfaces has been set, phylink will iterate ++ * over the supported interfaces to determine the full capability of the ++ * MAC. The validation function must not print errors if @state->interface ++ * is set to an unexpected value. ++ * ++ * When @config->supported_interfaces is empty, phylink will call this ++ * function with @state->interface set to %PHY_INTERFACE_MODE_NA, and ++ * expects the MAC driver to return all supported link modes. + * + * If the @state->interface mode is not supported, then the @supported + * mask must be cleared. +-- +2.35.1 + diff --git a/pkgs/patches-linux-5.15/776-v5.16-1-net-convert-users-of-bitmap_foo-to-linkmode_foo.patch b/pkgs/patches-linux-5.15/776-v5.16-1-net-convert-users-of-bitmap_foo-to-linkmode_foo.patch new file mode 100644 index 0000000..23d6f89 --- /dev/null +++ b/pkgs/patches-linux-5.15/776-v5.16-1-net-convert-users-of-bitmap_foo-to-linkmode_foo.patch @@ -0,0 +1,1009 @@ +From 1cd141887a4d4576e6cbfebf8b1445b2be768900 Mon Sep 17 00:00:00 2001 +From: Sean Anderson <sean.anderson@seco.com> +Date: Fri, 22 Oct 2021 18:41:04 -0400 +Subject: [PATCH 1/4] net: convert users of bitmap_foo() to linkmode_foo() + +This converts instances of + bitmap_foo(args..., __ETHTOOL_LINK_MODE_MASK_NBITS) +to + linkmode_foo(args...) + +I manually fixed up some lines to prevent them from being excessively +long. Otherwise, this change was generated with the following semantic +patch: + +// Generated with +// echo linux/linkmode.h > includes +// git grep -Flf includes include/ | cut -f 2- -d / | cat includes - \ +// | sort | uniq | tee new_includes | wc -l && mv new_includes includes +// and repeating until the number stopped going up +@i@ +@@ + +( + #include <linux/acpi_mdio.h> +| + #include <linux/brcmphy.h> +| + #include <linux/dsa/loop.h> +| + #include <linux/dsa/sja1105.h> +| + #include <linux/ethtool.h> +| + #include <linux/ethtool_netlink.h> +| + #include <linux/fec.h> +| + #include <linux/fs_enet_pd.h> +| + #include <linux/fsl/enetc_mdio.h> +| + #include <linux/fwnode_mdio.h> +| + #include <linux/linkmode.h> +| + #include <linux/lsm_audit.h> +| + #include <linux/mdio-bitbang.h> +| + #include <linux/mdio.h> +| + #include <linux/mdio-mux.h> +| + #include <linux/mii.h> +| + #include <linux/mii_timestamper.h> +| + #include <linux/mlx5/accel.h> +| + #include <linux/mlx5/cq.h> +| + #include <linux/mlx5/device.h> +| + #include <linux/mlx5/driver.h> +| + #include <linux/mlx5/eswitch.h> +| + #include <linux/mlx5/fs.h> +| + #include <linux/mlx5/port.h> +| + #include <linux/mlx5/qp.h> +| + #include <linux/mlx5/rsc_dump.h> +| + #include <linux/mlx5/transobj.h> +| + #include <linux/mlx5/vport.h> +| + #include <linux/of_mdio.h> +| + #include <linux/of_net.h> +| + #include <linux/pcs-lynx.h> +| + #include <linux/pcs/pcs-xpcs.h> +| + #include <linux/phy.h> +| + #include <linux/phy_led_triggers.h> +| + #include <linux/phylink.h> +| + #include <linux/platform_data/bcmgenet.h> +| + #include <linux/platform_data/xilinx-ll-temac.h> +| + #include <linux/pxa168_eth.h> +| + #include <linux/qed/qed_eth_if.h> +| + #include <linux/qed/qed_fcoe_if.h> +| + #include <linux/qed/qed_if.h> +| + #include <linux/qed/qed_iov_if.h> +| + #include <linux/qed/qed_iscsi_if.h> +| + #include <linux/qed/qed_ll2_if.h> +| + #include <linux/qed/qed_nvmetcp_if.h> +| + #include <linux/qed/qed_rdma_if.h> +| + #include <linux/sfp.h> +| + #include <linux/sh_eth.h> +| + #include <linux/smsc911x.h> +| + #include <linux/soc/nxp/lpc32xx-misc.h> +| + #include <linux/stmmac.h> +| + #include <linux/sunrpc/svc_rdma.h> +| + #include <linux/sxgbe_platform.h> +| + #include <net/cfg80211.h> +| + #include <net/dsa.h> +| + #include <net/mac80211.h> +| + #include <net/selftests.h> +| + #include <rdma/ib_addr.h> +| + #include <rdma/ib_cache.h> +| + #include <rdma/ib_cm.h> +| + #include <rdma/ib_hdrs.h> +| + #include <rdma/ib_mad.h> +| + #include <rdma/ib_marshall.h> +| + #include <rdma/ib_pack.h> +| + #include <rdma/ib_pma.h> +| + #include <rdma/ib_sa.h> +| + #include <rdma/ib_smi.h> +| + #include <rdma/ib_umem.h> +| + #include <rdma/ib_umem_odp.h> +| + #include <rdma/ib_verbs.h> +| + #include <rdma/iw_cm.h> +| + #include <rdma/mr_pool.h> +| + #include <rdma/opa_addr.h> +| + #include <rdma/opa_port_info.h> +| + #include <rdma/opa_smi.h> +| + #include <rdma/opa_vnic.h> +| + #include <rdma/rdma_cm.h> +| + #include <rdma/rdma_cm_ib.h> +| + #include <rdma/rdmavt_cq.h> +| + #include <rdma/rdma_vt.h> +| + #include <rdma/rdmavt_qp.h> +| + #include <rdma/rw.h> +| + #include <rdma/tid_rdma_defs.h> +| + #include <rdma/uverbs_ioctl.h> +| + #include <rdma/uverbs_named_ioctl.h> +| + #include <rdma/uverbs_std_types.h> +| + #include <rdma/uverbs_types.h> +| + #include <soc/mscc/ocelot.h> +| + #include <soc/mscc/ocelot_ptp.h> +| + #include <soc/mscc/ocelot_vcap.h> +| + #include <trace/events/ib_mad.h> +| + #include <trace/events/rdma_core.h> +| + #include <trace/events/rdma.h> +| + #include <trace/events/rpcrdma.h> +| + #include <uapi/linux/ethtool.h> +| + #include <uapi/linux/ethtool_netlink.h> +| + #include <uapi/linux/mdio.h> +| + #include <uapi/linux/mii.h> +) + +@depends on i@ +expression list args; +@@ + +( +- bitmap_zero(args, __ETHTOOL_LINK_MODE_MASK_NBITS) ++ linkmode_zero(args) +| +- bitmap_copy(args, __ETHTOOL_LINK_MODE_MASK_NBITS) ++ linkmode_copy(args) +| +- bitmap_and(args, __ETHTOOL_LINK_MODE_MASK_NBITS) ++ linkmode_and(args) +| +- bitmap_or(args, __ETHTOOL_LINK_MODE_MASK_NBITS) ++ linkmode_or(args) +| +- bitmap_empty(args, ETHTOOL_LINK_MODE_MASK_NBITS) ++ linkmode_empty(args) +| +- bitmap_andnot(args, __ETHTOOL_LINK_MODE_MASK_NBITS) ++ linkmode_andnot(args) +| +- bitmap_equal(args, __ETHTOOL_LINK_MODE_MASK_NBITS) ++ linkmode_equal(args) +| +- bitmap_intersects(args, __ETHTOOL_LINK_MODE_MASK_NBITS) ++ linkmode_intersects(args) +| +- bitmap_subset(args, __ETHTOOL_LINK_MODE_MASK_NBITS) ++ linkmode_subset(args) +) + +Add missing linux/mii.h include to mellanox. -DaveM + +Signed-off-by: Sean Anderson <sean.anderson@seco.com> +Signed-off-by: David S. Miller <davem@davemloft.net> +--- + drivers/net/dsa/b53/b53_common.c | 6 ++---- + drivers/net/dsa/bcm_sf2.c | 8 +++---- + drivers/net/dsa/hirschmann/hellcreek.c | 6 ++---- + drivers/net/dsa/lantiq_gswip.c | 14 ++++++------- + drivers/net/dsa/microchip/ksz8795.c | 8 +++---- + drivers/net/dsa/mv88e6xxx/chip.c | 5 ++--- + drivers/net/dsa/ocelot/felix_vsc9959.c | 8 +++---- + drivers/net/dsa/ocelot/seville_vsc9953.c | 8 +++---- + drivers/net/dsa/qca/ar9331.c | 10 ++++----- + drivers/net/dsa/sja1105/sja1105_main.c | 7 +++---- + drivers/net/dsa/xrs700x/xrs700x.c | 8 +++---- + drivers/net/ethernet/amd/xgbe/xgbe-ethtool.c | 8 +++---- + drivers/net/ethernet/atheros/ag71xx.c | 8 +++---- + drivers/net/ethernet/cadence/macb_main.c | 11 +++++----- + .../net/ethernet/freescale/enetc/enetc_pf.c | 8 +++---- + .../net/ethernet/huawei/hinic/hinic_ethtool.c | 10 ++++----- + .../net/ethernet/intel/ixgbe/ixgbe_ethtool.c | 5 ++--- + drivers/net/ethernet/marvell/mvneta.c | 10 ++++----- + .../net/ethernet/marvell/mvpp2/mvpp2_main.c | 7 +++---- + .../marvell/octeontx2/nic/otx2_ethtool.c | 5 ++--- + drivers/net/ethernet/marvell/pxa168_eth.c | 3 +-- + .../net/ethernet/mellanox/mlx4/en_ethtool.c | 21 +++++++------------ + .../microchip/sparx5/sparx5_phylink.c | 7 +++---- + drivers/net/ethernet/mscc/ocelot_net.c | 7 +++---- + .../ethernet/pensando/ionic/ionic_ethtool.c | 3 +-- + .../net/ethernet/xilinx/xilinx_axienet_main.c | 8 +++---- + drivers/net/pcs/pcs-xpcs.c | 2 +- + drivers/net/phy/sfp-bus.c | 2 +- + net/ethtool/ioctl.c | 7 +++---- + 29 files changed, 87 insertions(+), 133 deletions(-) + +diff --git a/drivers/net/dsa/b53/b53_common.c b/drivers/net/dsa/b53/b53_common.c +index a967de4fcc90..b32f25b42323 100644 +--- a/drivers/net/dsa/b53/b53_common.c ++++ b/drivers/net/dsa/b53/b53_common.c +@@ -1343,10 +1343,8 @@ void b53_phylink_validate(struct dsa_switch *ds, int port, + phylink_set(mask, 100baseT_Full); + } + +- bitmap_and(supported, supported, mask, +- __ETHTOOL_LINK_MODE_MASK_NBITS); +- bitmap_and(state->advertising, state->advertising, mask, +- __ETHTOOL_LINK_MODE_MASK_NBITS); ++ linkmode_and(supported, supported, mask); ++ linkmode_and(state->advertising, state->advertising, mask); + + phylink_helper_basex_speed(state); + } +diff --git a/drivers/net/dsa/bcm_sf2.c b/drivers/net/dsa/bcm_sf2.c +index d76b2377d66e..564f797a32aa 100644 +--- a/drivers/net/dsa/bcm_sf2.c ++++ b/drivers/net/dsa/bcm_sf2.c +@@ -686,7 +686,7 @@ static void bcm_sf2_sw_validate(struct dsa_switch *ds, int port, + state->interface != PHY_INTERFACE_MODE_GMII && + state->interface != PHY_INTERFACE_MODE_INTERNAL && + state->interface != PHY_INTERFACE_MODE_MOCA) { +- bitmap_zero(supported, __ETHTOOL_LINK_MODE_MASK_NBITS); ++ linkmode_zero(supported); + if (port != core_readl(priv, CORE_IMP0_PRT_ID)) + dev_err(ds->dev, + "Unsupported interface: %d for port %d\n", +@@ -714,10 +714,8 @@ static void bcm_sf2_sw_validate(struct dsa_switch *ds, int port, + phylink_set(mask, 100baseT_Half); + phylink_set(mask, 100baseT_Full); + +- bitmap_and(supported, supported, mask, +- __ETHTOOL_LINK_MODE_MASK_NBITS); +- bitmap_and(state->advertising, state->advertising, mask, +- __ETHTOOL_LINK_MODE_MASK_NBITS); ++ linkmode_and(supported, supported, mask); ++ linkmode_and(state->advertising, state->advertising, mask); + } + + static void bcm_sf2_sw_mac_config(struct dsa_switch *ds, int port, +diff --git a/drivers/net/dsa/hirschmann/hellcreek.c b/drivers/net/dsa/hirschmann/hellcreek.c +index 950a54ec4b59..b2bab460d2e9 100644 +--- a/drivers/net/dsa/hirschmann/hellcreek.c ++++ b/drivers/net/dsa/hirschmann/hellcreek.c +@@ -1476,10 +1476,8 @@ static void hellcreek_phylink_validate(struct dsa_switch *ds, int port, + else + phylink_set(mask, 1000baseT_Full); + +- bitmap_and(supported, supported, mask, +- __ETHTOOL_LINK_MODE_MASK_NBITS); +- bitmap_and(state->advertising, state->advertising, mask, +- __ETHTOOL_LINK_MODE_MASK_NBITS); ++ linkmode_and(supported, supported, mask); ++ linkmode_and(state->advertising, state->advertising, mask); + } + + static int +diff --git a/drivers/net/dsa/lantiq_gswip.c b/drivers/net/dsa/lantiq_gswip.c +index 2240a3d35122..4599e958fa05 100644 +--- a/drivers/net/dsa/lantiq_gswip.c ++++ b/drivers/net/dsa/lantiq_gswip.c +@@ -1452,10 +1452,8 @@ static void gswip_phylink_set_capab(unsigned long *supported, + phylink_set(mask, 100baseT_Half); + phylink_set(mask, 100baseT_Full); + +- bitmap_and(supported, supported, mask, +- __ETHTOOL_LINK_MODE_MASK_NBITS); +- bitmap_and(state->advertising, state->advertising, mask, +- __ETHTOOL_LINK_MODE_MASK_NBITS); ++ linkmode_and(supported, supported, mask); ++ linkmode_and(state->advertising, state->advertising, mask); + } + + static void gswip_xrx200_phylink_validate(struct dsa_switch *ds, int port, +@@ -1483,7 +1481,7 @@ static void gswip_xrx200_phylink_validate(struct dsa_switch *ds, int port, + goto unsupported; + break; + default: +- bitmap_zero(supported, __ETHTOOL_LINK_MODE_MASK_NBITS); ++ linkmode_zero(supported); + dev_err(ds->dev, "Unsupported port: %i\n", port); + return; + } +@@ -1493,7 +1491,7 @@ static void gswip_xrx200_phylink_validate(struct dsa_switch *ds, int port, + return; + + unsupported: +- bitmap_zero(supported, __ETHTOOL_LINK_MODE_MASK_NBITS); ++ linkmode_zero(supported); + dev_err(ds->dev, "Unsupported interface '%s' for port %d\n", + phy_modes(state->interface), port); + } +@@ -1523,7 +1521,7 @@ static void gswip_xrx300_phylink_validate(struct dsa_switch *ds, int port, + goto unsupported; + break; + default: +- bitmap_zero(supported, __ETHTOOL_LINK_MODE_MASK_NBITS); ++ linkmode_zero(supported); + dev_err(ds->dev, "Unsupported port: %i\n", port); + return; + } +@@ -1533,7 +1531,7 @@ static void gswip_xrx300_phylink_validate(struct dsa_switch *ds, int port, + return; + + unsupported: +- bitmap_zero(supported, __ETHTOOL_LINK_MODE_MASK_NBITS); ++ linkmode_zero(supported); + dev_err(ds->dev, "Unsupported interface '%s' for port %d\n", + phy_modes(state->interface), port); + } +diff --git a/drivers/net/dsa/microchip/ksz8795.c b/drivers/net/dsa/microchip/ksz8795.c +index c5142f86a3c7..43fc3087aeb3 100644 +--- a/drivers/net/dsa/microchip/ksz8795.c ++++ b/drivers/net/dsa/microchip/ksz8795.c +@@ -1542,15 +1542,13 @@ static void ksz8_validate(struct dsa_switch *ds, int port, + phylink_set(mask, 100baseT_Half); + phylink_set(mask, 100baseT_Full); + +- bitmap_and(supported, supported, mask, +- __ETHTOOL_LINK_MODE_MASK_NBITS); +- bitmap_and(state->advertising, state->advertising, mask, +- __ETHTOOL_LINK_MODE_MASK_NBITS); ++ linkmode_and(supported, supported, mask); ++ linkmode_and(state->advertising, state->advertising, mask); + + return; + + unsupported: +- bitmap_zero(supported, __ETHTOOL_LINK_MODE_MASK_NBITS); ++ linkmode_zero(supported); + dev_err(ds->dev, "Unsupported interface: %s, port: %d\n", + phy_modes(state->interface), port); + } +diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/chip.c +index cad98ab421d7..d2751f73246b 100644 +--- a/drivers/net/dsa/mv88e6xxx/chip.c ++++ b/drivers/net/dsa/mv88e6xxx/chip.c +@@ -683,9 +683,8 @@ static void mv88e6xxx_validate(struct dsa_switch *ds, int port, + if (chip->info->ops->phylink_validate) + chip->info->ops->phylink_validate(chip, port, mask, state); + +- bitmap_and(supported, supported, mask, __ETHTOOL_LINK_MODE_MASK_NBITS); +- bitmap_and(state->advertising, state->advertising, mask, +- __ETHTOOL_LINK_MODE_MASK_NBITS); ++ linkmode_and(supported, supported, mask); ++ linkmode_and(state->advertising, state->advertising, mask); + + /* We can only operate at 2500BaseX or 1000BaseX. If requested + * to advertise both, only report advertising at 2500BaseX. +diff --git a/drivers/net/dsa/ocelot/felix_vsc9959.c b/drivers/net/dsa/ocelot/felix_vsc9959.c +index 5ba7e5c820dd..aa3b7655a89f 100644 +--- a/drivers/net/dsa/ocelot/felix_vsc9959.c ++++ b/drivers/net/dsa/ocelot/felix_vsc9959.c +@@ -944,7 +944,7 @@ static void vsc9959_phylink_validate(struct ocelot *ocelot, int port, + + if (state->interface != PHY_INTERFACE_MODE_NA && + state->interface != ocelot_port->phy_mode) { +- bitmap_zero(supported, __ETHTOOL_LINK_MODE_MASK_NBITS); ++ linkmode_zero(supported); + return; + } + +@@ -966,10 +966,8 @@ static void vsc9959_phylink_validate(struct ocelot *ocelot, int port, + phylink_set(mask, 2500baseX_Full); + } + +- bitmap_and(supported, supported, mask, +- __ETHTOOL_LINK_MODE_MASK_NBITS); +- bitmap_and(state->advertising, state->advertising, mask, +- __ETHTOOL_LINK_MODE_MASK_NBITS); ++ linkmode_and(supported, supported, mask); ++ linkmode_and(state->advertising, state->advertising, mask); + } + + static int vsc9959_prevalidate_phy_mode(struct ocelot *ocelot, int port, +diff --git a/drivers/net/dsa/ocelot/seville_vsc9953.c b/drivers/net/dsa/ocelot/seville_vsc9953.c +index 05e4e75c0107..40d6d1f2c724 100644 +--- a/drivers/net/dsa/ocelot/seville_vsc9953.c ++++ b/drivers/net/dsa/ocelot/seville_vsc9953.c +@@ -1000,7 +1000,7 @@ static void vsc9953_phylink_validate(struct ocelot *ocelot, int port, + + if (state->interface != PHY_INTERFACE_MODE_NA && + state->interface != ocelot_port->phy_mode) { +- bitmap_zero(supported, __ETHTOOL_LINK_MODE_MASK_NBITS); ++ linkmode_zero(supported); + return; + } + +@@ -1019,10 +1019,8 @@ static void vsc9953_phylink_validate(struct ocelot *ocelot, int port, + phylink_set(mask, 2500baseX_Full); + } + +- bitmap_and(supported, supported, mask, +- __ETHTOOL_LINK_MODE_MASK_NBITS); +- bitmap_and(state->advertising, state->advertising, mask, +- __ETHTOOL_LINK_MODE_MASK_NBITS); ++ linkmode_and(supported, supported, mask); ++ linkmode_and(state->advertising, state->advertising, mask); + } + + static int vsc9953_prevalidate_phy_mode(struct ocelot *ocelot, int port, +diff --git a/drivers/net/dsa/qca/ar9331.c b/drivers/net/dsa/qca/ar9331.c +index 5d476f452396..c39de2a4c1fe 100644 +--- a/drivers/net/dsa/qca/ar9331.c ++++ b/drivers/net/dsa/qca/ar9331.c +@@ -522,7 +522,7 @@ static void ar9331_sw_phylink_validate(struct dsa_switch *ds, int port, + goto unsupported; + break; + default: +- bitmap_zero(supported, __ETHTOOL_LINK_MODE_MASK_NBITS); ++ linkmode_zero(supported); + dev_err(ds->dev, "Unsupported port: %i\n", port); + return; + } +@@ -536,15 +536,13 @@ static void ar9331_sw_phylink_validate(struct dsa_switch *ds, int port, + phylink_set(mask, 100baseT_Half); + phylink_set(mask, 100baseT_Full); + +- bitmap_and(supported, supported, mask, +- __ETHTOOL_LINK_MODE_MASK_NBITS); +- bitmap_and(state->advertising, state->advertising, mask, +- __ETHTOOL_LINK_MODE_MASK_NBITS); ++ linkmode_and(supported, supported, mask); ++ linkmode_and(state->advertising, state->advertising, mask); + + return; + + unsupported: +- bitmap_zero(supported, __ETHTOOL_LINK_MODE_MASK_NBITS); ++ linkmode_zero(supported); + dev_err(ds->dev, "Unsupported interface: %d, port: %d\n", + state->interface, port); + } +diff --git a/drivers/net/dsa/sja1105/sja1105_main.c b/drivers/net/dsa/sja1105/sja1105_main.c +index 1a2a7536ff8a..3e33d3b6fc59 100644 +--- a/drivers/net/dsa/sja1105/sja1105_main.c ++++ b/drivers/net/dsa/sja1105/sja1105_main.c +@@ -1360,7 +1360,7 @@ static void sja1105_phylink_validate(struct dsa_switch *ds, int port, + */ + if (state->interface != PHY_INTERFACE_MODE_NA && + sja1105_phy_mode_mismatch(priv, port, state->interface)) { +- bitmap_zero(supported, __ETHTOOL_LINK_MODE_MASK_NBITS); ++ linkmode_zero(supported); + return; + } + +@@ -1380,9 +1380,8 @@ static void sja1105_phylink_validate(struct dsa_switch *ds, int port, + phylink_set(mask, 2500baseX_Full); + } + +- bitmap_and(supported, supported, mask, __ETHTOOL_LINK_MODE_MASK_NBITS); +- bitmap_and(state->advertising, state->advertising, mask, +- __ETHTOOL_LINK_MODE_MASK_NBITS); ++ linkmode_and(supported, supported, mask); ++ linkmode_and(state->advertising, state->advertising, mask); + } + + static int +diff --git a/drivers/net/dsa/xrs700x/xrs700x.c b/drivers/net/dsa/xrs700x/xrs700x.c +index cf363d5a3002..e79a336c6eb2 100644 +--- a/drivers/net/dsa/xrs700x/xrs700x.c ++++ b/drivers/net/dsa/xrs700x/xrs700x.c +@@ -457,7 +457,7 @@ static void xrs700x_phylink_validate(struct dsa_switch *ds, int port, + phylink_set(mask, 1000baseT_Full); + break; + default: +- bitmap_zero(supported, __ETHTOOL_LINK_MODE_MASK_NBITS); ++ linkmode_zero(supported); + dev_err(ds->dev, "Unsupported port: %i\n", port); + return; + } +@@ -468,10 +468,8 @@ static void xrs700x_phylink_validate(struct dsa_switch *ds, int port, + phylink_set(mask, 10baseT_Full); + phylink_set(mask, 100baseT_Full); + +- bitmap_and(supported, supported, mask, +- __ETHTOOL_LINK_MODE_MASK_NBITS); +- bitmap_and(state->advertising, state->advertising, mask, +- __ETHTOOL_LINK_MODE_MASK_NBITS); ++ linkmode_and(supported, supported, mask); ++ linkmode_and(state->advertising, state->advertising, mask); + } + + static void xrs700x_mac_link_up(struct dsa_switch *ds, int port, +diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-ethtool.c b/drivers/net/ethernet/amd/xgbe/xgbe-ethtool.c +index bafc51c34e0b..94879cf8b420 100644 +--- a/drivers/net/ethernet/amd/xgbe/xgbe-ethtool.c ++++ b/drivers/net/ethernet/amd/xgbe/xgbe-ethtool.c +@@ -369,9 +369,8 @@ static int xgbe_set_link_ksettings(struct net_device *netdev, + __ETHTOOL_LINK_MODE_MASK_NBITS, cmd->link_modes.advertising, + __ETHTOOL_LINK_MODE_MASK_NBITS, lks->link_modes.supported); + +- bitmap_and(advertising, +- cmd->link_modes.advertising, lks->link_modes.supported, +- __ETHTOOL_LINK_MODE_MASK_NBITS); ++ linkmode_and(advertising, cmd->link_modes.advertising, ++ lks->link_modes.supported); + + if ((cmd->base.autoneg == AUTONEG_ENABLE) && + bitmap_empty(advertising, __ETHTOOL_LINK_MODE_MASK_NBITS)) { +@@ -384,8 +383,7 @@ static int xgbe_set_link_ksettings(struct net_device *netdev, + pdata->phy.autoneg = cmd->base.autoneg; + pdata->phy.speed = speed; + pdata->phy.duplex = cmd->base.duplex; +- bitmap_copy(lks->link_modes.advertising, advertising, +- __ETHTOOL_LINK_MODE_MASK_NBITS); ++ linkmode_copy(lks->link_modes.advertising, advertising); + + if (cmd->base.autoneg == AUTONEG_ENABLE) + XGBE_SET_ADV(lks, Autoneg); +diff --git a/drivers/net/ethernet/atheros/ag71xx.c b/drivers/net/ethernet/atheros/ag71xx.c +index 416a5c99db5a..d754bfc4f18f 100644 +--- a/drivers/net/ethernet/atheros/ag71xx.c ++++ b/drivers/net/ethernet/atheros/ag71xx.c +@@ -1082,14 +1082,12 @@ static void ag71xx_mac_validate(struct phylink_config *config, + phylink_set(mask, 1000baseX_Full); + } + +- bitmap_and(supported, supported, mask, +- __ETHTOOL_LINK_MODE_MASK_NBITS); +- bitmap_and(state->advertising, state->advertising, mask, +- __ETHTOOL_LINK_MODE_MASK_NBITS); ++ linkmode_and(supported, supported, mask); ++ linkmode_and(state->advertising, state->advertising, mask); + + return; + unsupported: +- bitmap_zero(supported, __ETHTOOL_LINK_MODE_MASK_NBITS); ++ linkmode_zero(supported); + } + + static void ag71xx_mac_pcs_get_state(struct phylink_config *config, +diff --git a/drivers/net/ethernet/cadence/macb_main.c b/drivers/net/ethernet/cadence/macb_main.c +index 3ca3f9d0fd9b..c6f28b084646 100644 +--- a/drivers/net/ethernet/cadence/macb_main.c ++++ b/drivers/net/ethernet/cadence/macb_main.c +@@ -523,21 +523,21 @@ static void macb_validate(struct phylink_config *config, + state->interface != PHY_INTERFACE_MODE_SGMII && + state->interface != PHY_INTERFACE_MODE_10GBASER && + !phy_interface_mode_is_rgmii(state->interface)) { +- bitmap_zero(supported, __ETHTOOL_LINK_MODE_MASK_NBITS); ++ linkmode_zero(supported); + return; + } + + if (!macb_is_gem(bp) && + (state->interface == PHY_INTERFACE_MODE_GMII || + phy_interface_mode_is_rgmii(state->interface))) { +- bitmap_zero(supported, __ETHTOOL_LINK_MODE_MASK_NBITS); ++ linkmode_zero(supported); + return; + } + + if (state->interface == PHY_INTERFACE_MODE_10GBASER && + !(bp->caps & MACB_CAPS_HIGH_SPEED && + bp->caps & MACB_CAPS_PCS)) { +- bitmap_zero(supported, __ETHTOOL_LINK_MODE_MASK_NBITS); ++ linkmode_zero(supported); + return; + } + +@@ -576,9 +576,8 @@ static void macb_validate(struct phylink_config *config, + phylink_set(mask, 1000baseT_Half); + } + out: +- bitmap_and(supported, supported, mask, __ETHTOOL_LINK_MODE_MASK_NBITS); +- bitmap_and(state->advertising, state->advertising, mask, +- __ETHTOOL_LINK_MODE_MASK_NBITS); ++ linkmode_and(supported, supported, mask); ++ linkmode_and(state->advertising, state->advertising, mask); + } + + static void macb_usx_pcs_link_up(struct phylink_pcs *pcs, unsigned int mode, +diff --git a/drivers/net/ethernet/freescale/enetc/enetc_pf.c b/drivers/net/ethernet/freescale/enetc/enetc_pf.c +index d522bd5c90b4..1c318a932460 100644 +--- a/drivers/net/ethernet/freescale/enetc/enetc_pf.c ++++ b/drivers/net/ethernet/freescale/enetc/enetc_pf.c +@@ -940,7 +940,7 @@ static void enetc_pl_mac_validate(struct phylink_config *config, + state->interface != PHY_INTERFACE_MODE_2500BASEX && + state->interface != PHY_INTERFACE_MODE_USXGMII && + !phy_interface_mode_is_rgmii(state->interface)) { +- bitmap_zero(supported, __ETHTOOL_LINK_MODE_MASK_NBITS); ++ linkmode_zero(supported); + return; + } + +@@ -963,10 +963,8 @@ static void enetc_pl_mac_validate(struct phylink_config *config, + phylink_set(mask, 2500baseX_Full); + } + +- bitmap_and(supported, supported, mask, +- __ETHTOOL_LINK_MODE_MASK_NBITS); +- bitmap_and(state->advertising, state->advertising, mask, +- __ETHTOOL_LINK_MODE_MASK_NBITS); ++ linkmode_and(supported, supported, mask); ++ linkmode_and(state->advertising, state->advertising, mask); + } + + static void enetc_pl_mac_config(struct phylink_config *config, +diff --git a/drivers/net/ethernet/huawei/hinic/hinic_ethtool.c b/drivers/net/ethernet/huawei/hinic/hinic_ethtool.c +index b431c300ef1b..a85667078b72 100644 +--- a/drivers/net/ethernet/huawei/hinic/hinic_ethtool.c ++++ b/drivers/net/ethernet/huawei/hinic/hinic_ethtool.c +@@ -322,12 +322,10 @@ static int hinic_get_link_ksettings(struct net_device *netdev, + } + } + +- bitmap_copy(link_ksettings->link_modes.supported, +- (unsigned long *)&settings.supported, +- __ETHTOOL_LINK_MODE_MASK_NBITS); +- bitmap_copy(link_ksettings->link_modes.advertising, +- (unsigned long *)&settings.advertising, +- __ETHTOOL_LINK_MODE_MASK_NBITS); ++ linkmode_copy(link_ksettings->link_modes.supported, ++ (unsigned long *)&settings.supported); ++ linkmode_copy(link_ksettings->link_modes.advertising, ++ (unsigned long *)&settings.advertising); + + return 0; + } +diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_ethtool.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_ethtool.c +index beda8e0ef7d4..8362822316a9 100644 +--- a/drivers/net/ethernet/intel/ixgbe/ixgbe_ethtool.c ++++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_ethtool.c +@@ -467,9 +467,8 @@ static int ixgbe_set_link_ksettings(struct net_device *netdev, + * this function does not support duplex forcing, but can + * limit the advertising of the adapter to the specified speed + */ +- if (!bitmap_subset(cmd->link_modes.advertising, +- cmd->link_modes.supported, +- __ETHTOOL_LINK_MODE_MASK_NBITS)) ++ if (!linkmode_subset(cmd->link_modes.advertising, ++ cmd->link_modes.supported)) + return -EINVAL; + + /* only allow one speed at a time if no autoneg */ +diff --git a/drivers/net/ethernet/marvell/mvneta.c b/drivers/net/ethernet/marvell/mvneta.c +index 7eb13fbf58e3..f2e959997e48 100644 +--- a/drivers/net/ethernet/marvell/mvneta.c ++++ b/drivers/net/ethernet/marvell/mvneta.c +@@ -3862,14 +3862,14 @@ static void mvneta_validate(struct phylink_config *config, + */ + if (phy_interface_mode_is_8023z(state->interface)) { + if (!phylink_test(state->advertising, Autoneg)) { +- bitmap_zero(supported, __ETHTOOL_LINK_MODE_MASK_NBITS); ++ linkmode_zero(supported); + return; + } + } else if (state->interface != PHY_INTERFACE_MODE_NA && + state->interface != PHY_INTERFACE_MODE_QSGMII && + state->interface != PHY_INTERFACE_MODE_SGMII && + !phy_interface_mode_is_rgmii(state->interface)) { +- bitmap_zero(supported, __ETHTOOL_LINK_MODE_MASK_NBITS); ++ linkmode_zero(supported); + return; + } + +@@ -3898,10 +3898,8 @@ static void mvneta_validate(struct phylink_config *config, + phylink_set(mask, 100baseT_Full); + } + +- bitmap_and(supported, supported, mask, +- __ETHTOOL_LINK_MODE_MASK_NBITS); +- bitmap_and(state->advertising, state->advertising, mask, +- __ETHTOOL_LINK_MODE_MASK_NBITS); ++ linkmode_and(supported, supported, mask); ++ linkmode_and(state->advertising, state->advertising, mask); + + /* We can only operate at 2500BaseX or 1000BaseX. If requested + * to advertise both, only report advertising at 2500BaseX. +diff --git a/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c b/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c +index 2baa909290b3..c963115558cb 100644 +--- a/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c ++++ b/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c +@@ -6347,15 +6347,14 @@ static void mvpp2_phylink_validate(struct phylink_config *config, + goto empty_set; + } + +- bitmap_and(supported, supported, mask, __ETHTOOL_LINK_MODE_MASK_NBITS); +- bitmap_and(state->advertising, state->advertising, mask, +- __ETHTOOL_LINK_MODE_MASK_NBITS); ++ linkmode_and(supported, supported, mask); ++ linkmode_and(state->advertising, state->advertising, mask); + + phylink_helper_basex_speed(state); + return; + + empty_set: +- bitmap_zero(supported, __ETHTOOL_LINK_MODE_MASK_NBITS); ++ linkmode_zero(supported); + } + + static void mvpp2_xlg_config(struct mvpp2_port *port, unsigned int mode, +diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_ethtool.c b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_ethtool.c +index dbfa3bc39e34..4c882ceaeb1f 100644 +--- a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_ethtool.c ++++ b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_ethtool.c +@@ -1168,9 +1168,8 @@ static int otx2_set_link_ksettings(struct net_device *netdev, + otx2_get_link_ksettings(netdev, &cur_ks); + + /* Check requested modes against supported modes by hardware */ +- if (!bitmap_subset(cmd->link_modes.advertising, +- cur_ks.link_modes.supported, +- __ETHTOOL_LINK_MODE_MASK_NBITS)) ++ if (!linkmode_subset(cmd->link_modes.advertising, ++ cur_ks.link_modes.supported)) + return -EINVAL; + + mutex_lock(&mbox->lock); +diff --git a/drivers/net/ethernet/marvell/pxa168_eth.c b/drivers/net/ethernet/marvell/pxa168_eth.c +index fab53c9b8380..572061164f1f 100644 +--- a/drivers/net/ethernet/marvell/pxa168_eth.c ++++ b/drivers/net/ethernet/marvell/pxa168_eth.c +@@ -977,8 +977,7 @@ static int pxa168_init_phy(struct net_device *dev) + cmd.base.phy_address = pep->phy_addr; + cmd.base.speed = pep->phy_speed; + cmd.base.duplex = pep->phy_duplex; +- bitmap_copy(cmd.link_modes.advertising, PHY_BASIC_FEATURES, +- __ETHTOOL_LINK_MODE_MASK_NBITS); ++ linkmode_copy(cmd.link_modes.advertising, PHY_BASIC_FEATURES); + cmd.base.autoneg = AUTONEG_ENABLE; + + if (cmd.base.speed != 0) +diff --git a/drivers/net/ethernet/mellanox/mlx4/en_ethtool.c b/drivers/net/ethernet/mellanox/mlx4/en_ethtool.c +index c3cffb32fb06..4be2bc8f74f1 100644 +--- a/drivers/net/ethernet/mellanox/mlx4/en_ethtool.c ++++ b/drivers/net/ethernet/mellanox/mlx4/en_ethtool.c +@@ -39,6 +39,7 @@ + #include <linux/in.h> + #include <net/ip.h> + #include <linux/bitmap.h> ++#include <linux/mii.h> + + #include "mlx4_en.h" + #include "en_port.h" +@@ -643,10 +644,8 @@ static unsigned long *ptys2ethtool_link_mode(struct ptys2ethtool_config *cfg, + unsigned int i; \ + cfg = &ptys2ethtool_map[reg_]; \ + cfg->speed = speed_; \ +- bitmap_zero(cfg->supported, \ +- __ETHTOOL_LINK_MODE_MASK_NBITS); \ +- bitmap_zero(cfg->advertised, \ +- __ETHTOOL_LINK_MODE_MASK_NBITS); \ ++ linkmode_zero(cfg->supported); \ ++ linkmode_zero(cfg->advertised); \ + for (i = 0 ; i < ARRAY_SIZE(modes) ; ++i) { \ + __set_bit(modes[i], cfg->supported); \ + __set_bit(modes[i], cfg->advertised); \ +@@ -702,10 +701,8 @@ static void ptys2ethtool_update_link_modes(unsigned long *link_modes, + int i; + for (i = 0; i < MLX4_LINK_MODES_SZ; i++) { + if (eth_proto & MLX4_PROT_MASK(i)) +- bitmap_or(link_modes, link_modes, +- ptys2ethtool_link_mode(&ptys2ethtool_map[i], +- report), +- __ETHTOOL_LINK_MODE_MASK_NBITS); ++ linkmode_or(link_modes, link_modes, ++ ptys2ethtool_link_mode(&ptys2ethtool_map[i], report)); + } + } + +@@ -716,11 +713,9 @@ static u32 ethtool2ptys_link_modes(const unsigned long *link_modes, + u32 ptys_modes = 0; + + for (i = 0; i < MLX4_LINK_MODES_SZ; i++) { +- if (bitmap_intersects( +- ptys2ethtool_link_mode(&ptys2ethtool_map[i], +- report), +- link_modes, +- __ETHTOOL_LINK_MODE_MASK_NBITS)) ++ ulong *map_mode = ptys2ethtool_link_mode(&ptys2ethtool_map[i], ++ report); ++ if (linkmode_intersects(map_mode, link_modes)) + ptys_modes |= 1 << i; + } + return ptys_modes; +diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_phylink.c b/drivers/net/ethernet/microchip/sparx5/sparx5_phylink.c +index af70e2795125..fb74752de0ca 100644 +--- a/drivers/net/ethernet/microchip/sparx5/sparx5_phylink.c ++++ b/drivers/net/ethernet/microchip/sparx5/sparx5_phylink.c +@@ -92,12 +92,11 @@ static void sparx5_phylink_validate(struct phylink_config *config, + } + break; + default: +- bitmap_zero(supported, __ETHTOOL_LINK_MODE_MASK_NBITS); ++ linkmode_zero(supported); + return; + } +- bitmap_and(supported, supported, mask, __ETHTOOL_LINK_MODE_MASK_NBITS); +- bitmap_and(state->advertising, state->advertising, mask, +- __ETHTOOL_LINK_MODE_MASK_NBITS); ++ linkmode_and(supported, supported, mask); ++ linkmode_and(state->advertising, state->advertising, mask); + } + + static void sparx5_phylink_mac_config(struct phylink_config *config, +diff --git a/drivers/net/ethernet/mscc/ocelot_net.c b/drivers/net/ethernet/mscc/ocelot_net.c +index c08c56e07b1d..6a8e391ecadd 100644 +--- a/drivers/net/ethernet/mscc/ocelot_net.c ++++ b/drivers/net/ethernet/mscc/ocelot_net.c +@@ -1509,7 +1509,7 @@ static void vsc7514_phylink_validate(struct phylink_config *config, + + if (state->interface != PHY_INTERFACE_MODE_NA && + state->interface != ocelot_port->phy_mode) { +- bitmap_zero(supported, __ETHTOOL_LINK_MODE_MASK_NBITS); ++ linkmode_zero(supported); + return; + } + +@@ -1528,9 +1528,8 @@ static void vsc7514_phylink_validate(struct phylink_config *config, + phylink_set(mask, 2500baseT_Full); + phylink_set(mask, 2500baseX_Full); + +- bitmap_and(supported, supported, mask, __ETHTOOL_LINK_MODE_MASK_NBITS); +- bitmap_and(state->advertising, state->advertising, mask, +- __ETHTOOL_LINK_MODE_MASK_NBITS); ++ linkmode_and(supported, supported, mask); ++ linkmode_and(state->advertising, state->advertising, mask); + } + + static void vsc7514_phylink_mac_config(struct phylink_config *config, +diff --git a/drivers/net/ethernet/pensando/ionic/ionic_ethtool.c b/drivers/net/ethernet/pensando/ionic/ionic_ethtool.c +index 3de1a03839e2..620fc13b5781 100644 +--- a/drivers/net/ethernet/pensando/ionic/ionic_ethtool.c ++++ b/drivers/net/ethernet/pensando/ionic/ionic_ethtool.c +@@ -228,8 +228,7 @@ static int ionic_get_link_ksettings(struct net_device *netdev, + break; + } + +- bitmap_copy(ks->link_modes.advertising, ks->link_modes.supported, +- __ETHTOOL_LINK_MODE_MASK_NBITS); ++ linkmode_copy(ks->link_modes.advertising, ks->link_modes.supported); + + ethtool_link_ksettings_add_link_mode(ks, supported, FEC_BASER); + ethtool_link_ksettings_add_link_mode(ks, supported, FEC_RS); +diff --git a/drivers/net/ethernet/xilinx/xilinx_axienet_main.c b/drivers/net/ethernet/xilinx/xilinx_axienet_main.c +index fbbbcfe0e891..4cf0994f624c 100644 +--- a/drivers/net/ethernet/xilinx/xilinx_axienet_main.c ++++ b/drivers/net/ethernet/xilinx/xilinx_axienet_main.c +@@ -1565,7 +1565,7 @@ static void axienet_validate(struct phylink_config *config, + netdev_warn(ndev, "Cannot use PHY mode %s, supported: %s\n", + phy_modes(state->interface), + phy_modes(lp->phy_mode)); +- bitmap_zero(supported, __ETHTOOL_LINK_MODE_MASK_NBITS); ++ linkmode_zero(supported); + return; + } + } +@@ -1598,10 +1598,8 @@ static void axienet_validate(struct phylink_config *config, + break; + } + +- bitmap_and(supported, supported, mask, +- __ETHTOOL_LINK_MODE_MASK_NBITS); +- bitmap_and(state->advertising, state->advertising, mask, +- __ETHTOOL_LINK_MODE_MASK_NBITS); ++ linkmode_and(supported, supported, mask); ++ linkmode_and(state->advertising, state->advertising, mask); + } + + static void axienet_mac_pcs_get_state(struct phylink_config *config, +diff --git a/drivers/net/pcs/pcs-xpcs.c b/drivers/net/pcs/pcs-xpcs.c +index fd4cbf8a55ad..ec75cf81c3af 100644 +--- a/drivers/net/pcs/pcs-xpcs.c ++++ b/drivers/net/pcs/pcs-xpcs.c +@@ -646,7 +646,7 @@ void xpcs_validate(struct dw_xpcs *xpcs, unsigned long *supported, + if (state->interface == PHY_INTERFACE_MODE_NA) + return; + +- bitmap_zero(xpcs_supported, __ETHTOOL_LINK_MODE_MASK_NBITS); ++ linkmode_zero(xpcs_supported); + + compat = xpcs_find_compat(xpcs->id, state->interface); + +diff --git a/drivers/net/phy/sfp-bus.c b/drivers/net/phy/sfp-bus.c +index 4566348a6d05..0a9099c77694 100644 +--- a/drivers/net/phy/sfp-bus.c ++++ b/drivers/net/phy/sfp-bus.c +@@ -286,7 +286,7 @@ void sfp_parse_support(struct sfp_bus *bus, const struct sfp_eeprom_id *id, + if (bus->sfp_quirk && bus->sfp_quirk->modes) + bus->sfp_quirk->modes(id, modes); + +- bitmap_or(support, support, modes, __ETHTOOL_LINK_MODE_MASK_NBITS); ++ linkmode_or(support, support, modes); + + phylink_set(support, Autoneg); + phylink_set(support, Pause); +diff --git a/net/ethtool/ioctl.c b/net/ethtool/ioctl.c +index e4983f473a3c..b4aa3bbcf3c7 100644 +--- a/net/ethtool/ioctl.c ++++ b/net/ethtool/ioctl.c +@@ -335,7 +335,7 @@ EXPORT_SYMBOL(ethtool_intersect_link_masks); + void ethtool_convert_legacy_u32_to_link_mode(unsigned long *dst, + u32 legacy_u32) + { +- bitmap_zero(dst, __ETHTOOL_LINK_MODE_MASK_NBITS); ++ linkmode_zero(dst); + dst[0] = legacy_u32; + } + EXPORT_SYMBOL(ethtool_convert_legacy_u32_to_link_mode); +@@ -350,11 +350,10 @@ bool ethtool_convert_link_mode_to_legacy_u32(u32 *legacy_u32, + if (__ETHTOOL_LINK_MODE_MASK_NBITS > 32) { + __ETHTOOL_DECLARE_LINK_MODE_MASK(ext); + +- bitmap_zero(ext, __ETHTOOL_LINK_MODE_MASK_NBITS); ++ linkmode_zero(ext); + bitmap_fill(ext, 32); + bitmap_complement(ext, ext, __ETHTOOL_LINK_MODE_MASK_NBITS); +- if (bitmap_intersects(ext, src, +- __ETHTOOL_LINK_MODE_MASK_NBITS)) { ++ if (linkmode_intersects(ext, src)) { + /* src mask goes beyond bit 31 */ + retval = false; + } +-- +2.35.1 + diff --git a/pkgs/patches-linux-5.15/776-v5.16-2-net-mvneta-populate-supported_interfaces-member.patch b/pkgs/patches-linux-5.15/776-v5.16-2-net-mvneta-populate-supported_interfaces-member.patch new file mode 100644 index 0000000..b755bd7 --- /dev/null +++ b/pkgs/patches-linux-5.15/776-v5.16-2-net-mvneta-populate-supported_interfaces-member.patch @@ -0,0 +1,53 @@ +From c7c29985c548c24efe90e337c575b475bc5e8c13 Mon Sep 17 00:00:00 2001 +From: Russell King <rmk+kernel@armlinux.org.uk> +Date: Wed, 27 Oct 2021 10:03:43 +0100 +Subject: [PATCH 2/4] net: mvneta: populate supported_interfaces member + +Populate the phy_interface_t bitmap for the Marvell mvneta driver with +interfaces modes supported by the MAC. + +Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk> +Signed-off-by: David S. Miller <davem@davemloft.net> +--- + drivers/net/ethernet/marvell/mvneta.c | 25 +++++++++++++++++++++++++ + 1 file changed, 25 insertions(+) + +diff --git a/drivers/net/ethernet/marvell/mvneta.c b/drivers/net/ethernet/marvell/mvneta.c +index f2e959997e48..1056b4845ec4 100644 +--- a/drivers/net/ethernet/marvell/mvneta.c ++++ b/drivers/net/ethernet/marvell/mvneta.c +@@ -5308,6 +5308,31 @@ static int mvneta_probe(struct platform_device *pdev) + + pp->phylink_config.dev = &dev->dev; + pp->phylink_config.type = PHYLINK_NETDEV; ++ phy_interface_set_rgmii(pp->phylink_config.supported_interfaces); ++ __set_bit(PHY_INTERFACE_MODE_QSGMII, ++ pp->phylink_config.supported_interfaces); ++ if (comphy) { ++ /* If a COMPHY is present, we can support any of the serdes ++ * modes and switch between them. ++ */ ++ __set_bit(PHY_INTERFACE_MODE_SGMII, ++ pp->phylink_config.supported_interfaces); ++ __set_bit(PHY_INTERFACE_MODE_1000BASEX, ++ pp->phylink_config.supported_interfaces); ++ __set_bit(PHY_INTERFACE_MODE_2500BASEX, ++ pp->phylink_config.supported_interfaces); ++ } else if (phy_mode == PHY_INTERFACE_MODE_2500BASEX) { ++ /* No COMPHY, with only 2500BASE-X mode supported */ ++ __set_bit(PHY_INTERFACE_MODE_2500BASEX, ++ pp->phylink_config.supported_interfaces); ++ } else if (phy_mode == PHY_INTERFACE_MODE_1000BASEX || ++ phy_mode == PHY_INTERFACE_MODE_SGMII) { ++ /* No COMPHY, we can switch between 1000BASE-X and SGMII */ ++ __set_bit(PHY_INTERFACE_MODE_1000BASEX, ++ pp->phylink_config.supported_interfaces); ++ __set_bit(PHY_INTERFACE_MODE_SGMII, ++ pp->phylink_config.supported_interfaces); ++ } + + phylink = phylink_create(&pp->phylink_config, pdev->dev.fwnode, + phy_mode, &mvneta_phylink_ops); +-- +2.35.1 + diff --git a/pkgs/patches-linux-5.15/776-v5.16-3-net-mvneta-remove-interface-checks-in-mvneta_validat.patch b/pkgs/patches-linux-5.15/776-v5.16-3-net-mvneta-remove-interface-checks-in-mvneta_validat.patch new file mode 100644 index 0000000..14c34f2 --- /dev/null +++ b/pkgs/patches-linux-5.15/776-v5.16-3-net-mvneta-remove-interface-checks-in-mvneta_validat.patch @@ -0,0 +1,40 @@ +From 10639f59c410911bb4a14e903d109dd426f2858c Mon Sep 17 00:00:00 2001 +From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk> +Date: Wed, 27 Oct 2021 10:03:48 +0100 +Subject: [PATCH 3/4] net: mvneta: remove interface checks in mvneta_validate() + +As phylink checks the interface mode against the supported_interfaces +bitmap, we no longer need to validate the interface mode in the +validation function. Remove this to simplify it. + +Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk> +Signed-off-by: David S. Miller <davem@davemloft.net> +--- + drivers/net/ethernet/marvell/mvneta.c | 11 ++--------- + 1 file changed, 2 insertions(+), 9 deletions(-) + +diff --git a/drivers/net/ethernet/marvell/mvneta.c b/drivers/net/ethernet/marvell/mvneta.c +index 1056b4845ec4..c13d4beb570f 100644 +--- a/drivers/net/ethernet/marvell/mvneta.c ++++ b/drivers/net/ethernet/marvell/mvneta.c +@@ -3860,15 +3860,8 @@ static void mvneta_validate(struct phylink_config *config, + * "Bit 2 Field InBandAnEn In-band Auto-Negotiation enable. ... + * When <PortType> = 1 (1000BASE-X) this field must be set to 1." + */ +- if (phy_interface_mode_is_8023z(state->interface)) { +- if (!phylink_test(state->advertising, Autoneg)) { +- linkmode_zero(supported); +- return; +- } +- } else if (state->interface != PHY_INTERFACE_MODE_NA && +- state->interface != PHY_INTERFACE_MODE_QSGMII && +- state->interface != PHY_INTERFACE_MODE_SGMII && +- !phy_interface_mode_is_rgmii(state->interface)) { ++ if (phy_interface_mode_is_8023z(state->interface) && ++ !phylink_test(state->advertising, Autoneg)) { + linkmode_zero(supported); + return; + } +-- +2.35.1 + diff --git a/pkgs/patches-linux-5.15/776-v5.16-4-net-mvneta-drop-use-of-phylink_helper_basex_speed.patch b/pkgs/patches-linux-5.15/776-v5.16-4-net-mvneta-drop-use-of-phylink_helper_basex_speed.patch new file mode 100644 index 0000000..0b8fbbd --- /dev/null +++ b/pkgs/patches-linux-5.15/776-v5.16-4-net-mvneta-drop-use-of-phylink_helper_basex_speed.patch @@ -0,0 +1,60 @@ +From 0e09bb9ae49bcb121c353861f440e7a9ede3b378 Mon Sep 17 00:00:00 2001 +From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk> +Date: Wed, 27 Oct 2021 10:03:53 +0100 +Subject: [PATCH 4/4] net: mvneta: drop use of phylink_helper_basex_speed() + +Now that we have a better method to select SFP interface modes, we +no longer need to use phylink_helper_basex_speed() in a driver's +validation function, and we can also get rid of our hack to indicate +both 1000base-X and 2500base-X if the comphy is present to make that +work. Remove this hack and use of phylink_helper_basex_speed(). + +Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk> +Signed-off-by: David S. Miller <davem@davemloft.net> +--- + drivers/net/ethernet/marvell/mvneta.c | 12 +++--------- + 1 file changed, 3 insertions(+), 9 deletions(-) + +diff --git a/drivers/net/ethernet/marvell/mvneta.c b/drivers/net/ethernet/marvell/mvneta.c +index c13d4beb570f..51fe2de0215e 100644 +--- a/drivers/net/ethernet/marvell/mvneta.c ++++ b/drivers/net/ethernet/marvell/mvneta.c +@@ -3851,8 +3851,6 @@ static void mvneta_validate(struct phylink_config *config, + unsigned long *supported, + struct phylink_link_state *state) + { +- struct net_device *ndev = to_net_dev(config->dev); +- struct mvneta_port *pp = netdev_priv(ndev); + __ETHTOOL_DECLARE_LINK_MODE_MASK(mask) = { 0, }; + + /* We only support QSGMII, SGMII, 802.3z and RGMII modes. +@@ -3874,11 +3872,12 @@ static void mvneta_validate(struct phylink_config *config, + phylink_set(mask, Pause); + + /* Half-duplex at speeds higher than 100Mbit is unsupported */ +- if (pp->comphy || state->interface != PHY_INTERFACE_MODE_2500BASEX) { ++ if (state->interface != PHY_INTERFACE_MODE_2500BASEX) { + phylink_set(mask, 1000baseT_Full); + phylink_set(mask, 1000baseX_Full); + } +- if (pp->comphy || state->interface == PHY_INTERFACE_MODE_2500BASEX) { ++ ++ if (state->interface == PHY_INTERFACE_MODE_2500BASEX) { + phylink_set(mask, 2500baseT_Full); + phylink_set(mask, 2500baseX_Full); + } +@@ -3893,11 +3892,6 @@ static void mvneta_validate(struct phylink_config *config, + + linkmode_and(supported, supported, mask); + linkmode_and(state->advertising, state->advertising, mask); +- +- /* We can only operate at 2500BaseX or 1000BaseX. If requested +- * to advertise both, only report advertising at 2500BaseX. +- */ +- phylink_helper_basex_speed(state); + } + + static void mvneta_mac_pcs_get_state(struct phylink_config *config, +-- +2.35.1 + diff --git a/pkgs/patches-linux-5.15/777-v5.16-1-net-dpaa2-mac-add-support-for-more-ethtool-10G-link-.patch b/pkgs/patches-linux-5.15/777-v5.16-1-net-dpaa2-mac-add-support-for-more-ethtool-10G-link-.patch new file mode 100644 index 0000000..a54e4c5 --- /dev/null +++ b/pkgs/patches-linux-5.15/777-v5.16-1-net-dpaa2-mac-add-support-for-more-ethtool-10G-link-.patch @@ -0,0 +1,45 @@ +From bc4b596770b44bbea030175bb2c48be4b08df543 Mon Sep 17 00:00:00 2001 +From: Russell King <rmk+kernel@armlinux.org.uk> +Date: Fri, 17 Sep 2021 14:41:17 +0100 +Subject: [PATCH 1/3] net: dpaa2-mac: add support for more ethtool 10G link + modes +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Phylink documentation says: + Note that the PHY may be able to transform from one connection + technology to another, so, eg, don't clear 1000BaseX just + because the MAC is unable to BaseX mode. This is more about + clearing unsupported speeds and duplex settings. The port modes + should not be cleared; phylink_set_port_modes() will help with this. + +So add the missing 10G modes. + +Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk> +Acked-by: Marek Behún <kabel@kernel.org> +Acked-by: Ioana Ciornei <ioana.ciornei@nxp.com> +Signed-off-by: David S. Miller <davem@davemloft.net> +--- + drivers/net/ethernet/freescale/dpaa2/dpaa2-mac.c | 5 +++++ + 1 file changed, 5 insertions(+) + +diff --git a/drivers/net/ethernet/freescale/dpaa2/dpaa2-mac.c b/drivers/net/ethernet/freescale/dpaa2/dpaa2-mac.c +index ae6d382d8735..543c1f202420 100644 +--- a/drivers/net/ethernet/freescale/dpaa2/dpaa2-mac.c ++++ b/drivers/net/ethernet/freescale/dpaa2/dpaa2-mac.c +@@ -140,6 +140,11 @@ static void dpaa2_mac_validate(struct phylink_config *config, + case PHY_INTERFACE_MODE_10GBASER: + case PHY_INTERFACE_MODE_USXGMII: + phylink_set(mask, 10000baseT_Full); ++ phylink_set(mask, 10000baseCR_Full); ++ phylink_set(mask, 10000baseSR_Full); ++ phylink_set(mask, 10000baseLR_Full); ++ phylink_set(mask, 10000baseLRM_Full); ++ phylink_set(mask, 10000baseER_Full); + if (state->interface == PHY_INTERFACE_MODE_10GBASER) + break; + phylink_set(mask, 5000baseT_Full); +-- +2.35.1 + diff --git a/pkgs/patches-linux-5.15/777-v5.16-2-net-phylink-add-phylink_set_10g_modes-helper.patch b/pkgs/patches-linux-5.15/777-v5.16-2-net-phylink-add-phylink_set_10g_modes-helper.patch new file mode 100644 index 0000000..83c5a4c --- /dev/null +++ b/pkgs/patches-linux-5.15/777-v5.16-2-net-phylink-add-phylink_set_10g_modes-helper.patch @@ -0,0 +1,52 @@ +From 429a7a12a7a11fd5df24915934bc4d01d521ae96 Mon Sep 17 00:00:00 2001 +From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk> +Date: Mon, 4 Oct 2021 12:03:28 +0100 +Subject: [PATCH 2/3] net: phylink: add phylink_set_10g_modes() helper + +Add a helper for setting 10Gigabit modes, so we have one central +place that sets all appropriate 10G modes for a driver. + +Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk> +Signed-off-by: David S. Miller <davem@davemloft.net> +--- + drivers/net/phy/phylink.c | 11 +++++++++++ + include/linux/phylink.h | 1 + + 2 files changed, 12 insertions(+) + +diff --git a/drivers/net/phy/phylink.c b/drivers/net/phy/phylink.c +index 4861924303ee..0720f05ecb02 100644 +--- a/drivers/net/phy/phylink.c ++++ b/drivers/net/phy/phylink.c +@@ -133,6 +133,17 @@ void phylink_set_port_modes(unsigned long *mask) + } + EXPORT_SYMBOL_GPL(phylink_set_port_modes); + ++void phylink_set_10g_modes(unsigned long *mask) ++{ ++ phylink_set(mask, 10000baseT_Full); ++ phylink_set(mask, 10000baseCR_Full); ++ phylink_set(mask, 10000baseSR_Full); ++ phylink_set(mask, 10000baseLR_Full); ++ phylink_set(mask, 10000baseLRM_Full); ++ phylink_set(mask, 10000baseER_Full); ++} ++EXPORT_SYMBOL_GPL(phylink_set_10g_modes); ++ + static int phylink_is_empty_linkmode(const unsigned long *linkmode) + { + __ETHTOOL_DECLARE_LINK_MODE_MASK(tmp) = { 0, }; +diff --git a/include/linux/phylink.h b/include/linux/phylink.h +index 174343ccd685..f037470b6fb3 100644 +--- a/include/linux/phylink.h ++++ b/include/linux/phylink.h +@@ -493,6 +493,7 @@ int phylink_speed_up(struct phylink *pl); + #define phylink_test(bm, mode) __phylink_do_bit(test_bit, bm, mode) + + void phylink_set_port_modes(unsigned long *bits); ++void phylink_set_10g_modes(unsigned long *mask); + void phylink_helper_basex_speed(struct phylink_link_state *state); + + void phylink_mii_c22_pcs_get_state(struct mdio_device *pcs, +-- +2.35.1 + diff --git a/pkgs/patches-linux-5.15/777-v5.16-3-net-ethernet-use-phylink_set_10g_modes.patch b/pkgs/patches-linux-5.15/777-v5.16-3-net-ethernet-use-phylink_set_10g_modes.patch new file mode 100644 index 0000000..0095d64 --- /dev/null +++ b/pkgs/patches-linux-5.15/777-v5.16-3-net-ethernet-use-phylink_set_10g_modes.patch @@ -0,0 +1,74 @@ +From 8259f96b710a2dd78e85bb46f12372dc0a3e75a0 Mon Sep 17 00:00:00 2001 +From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk> +Date: Mon, 4 Oct 2021 12:03:33 +0100 +Subject: [PATCH 3/3] net: ethernet: use phylink_set_10g_modes() + +Update three drivers to use the new phylink_set_10g_modes() helper: +Cadence macb, Freescale DPAA2 and Marvell PP2. + +Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk> +Signed-off-by: David S. Miller <davem@davemloft.net> +--- + drivers/net/ethernet/cadence/macb_main.c | 7 +------ + drivers/net/ethernet/freescale/dpaa2/dpaa2-mac.c | 7 +------ + drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c | 7 +------ + 3 files changed, 3 insertions(+), 18 deletions(-) + +diff --git a/drivers/net/ethernet/cadence/macb_main.c b/drivers/net/ethernet/cadence/macb_main.c +index c6f28b084646..97941cdfe1a1 100644 +--- a/drivers/net/ethernet/cadence/macb_main.c ++++ b/drivers/net/ethernet/cadence/macb_main.c +@@ -548,13 +548,8 @@ static void macb_validate(struct phylink_config *config, + if (bp->caps & MACB_CAPS_GIGABIT_MODE_AVAILABLE && + (state->interface == PHY_INTERFACE_MODE_NA || + state->interface == PHY_INTERFACE_MODE_10GBASER)) { +- phylink_set(mask, 10000baseCR_Full); +- phylink_set(mask, 10000baseER_Full); ++ phylink_set_10g_modes(mask); + phylink_set(mask, 10000baseKR_Full); +- phylink_set(mask, 10000baseLR_Full); +- phylink_set(mask, 10000baseLRM_Full); +- phylink_set(mask, 10000baseSR_Full); +- phylink_set(mask, 10000baseT_Full); + if (state->interface != PHY_INTERFACE_MODE_NA) + goto out; + } +diff --git a/drivers/net/ethernet/freescale/dpaa2/dpaa2-mac.c b/drivers/net/ethernet/freescale/dpaa2/dpaa2-mac.c +index 543c1f202420..ef8f0a055024 100644 +--- a/drivers/net/ethernet/freescale/dpaa2/dpaa2-mac.c ++++ b/drivers/net/ethernet/freescale/dpaa2/dpaa2-mac.c +@@ -139,12 +139,7 @@ static void dpaa2_mac_validate(struct phylink_config *config, + case PHY_INTERFACE_MODE_NA: + case PHY_INTERFACE_MODE_10GBASER: + case PHY_INTERFACE_MODE_USXGMII: +- phylink_set(mask, 10000baseT_Full); +- phylink_set(mask, 10000baseCR_Full); +- phylink_set(mask, 10000baseSR_Full); +- phylink_set(mask, 10000baseLR_Full); +- phylink_set(mask, 10000baseLRM_Full); +- phylink_set(mask, 10000baseER_Full); ++ phylink_set_10g_modes(mask); + if (state->interface == PHY_INTERFACE_MODE_10GBASER) + break; + phylink_set(mask, 5000baseT_Full); +diff --git a/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c b/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c +index c963115558cb..3c267a94f1ca 100644 +--- a/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c ++++ b/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c +@@ -6305,12 +6305,7 @@ static void mvpp2_phylink_validate(struct phylink_config *config, + case PHY_INTERFACE_MODE_XAUI: + case PHY_INTERFACE_MODE_NA: + if (mvpp2_port_supports_xlg(port)) { +- phylink_set(mask, 10000baseT_Full); +- phylink_set(mask, 10000baseCR_Full); +- phylink_set(mask, 10000baseSR_Full); +- phylink_set(mask, 10000baseLR_Full); +- phylink_set(mask, 10000baseLRM_Full); +- phylink_set(mask, 10000baseER_Full); ++ phylink_set_10g_modes(mask); + phylink_set(mask, 10000baseKR_Full); + } + if (state->interface != PHY_INTERFACE_MODE_NA) +-- +2.35.1 + diff --git a/pkgs/patches-linux-5.15/779-v5.16-1-net-mvpp2-populate-supported_interfaces-member.patch b/pkgs/patches-linux-5.15/779-v5.16-1-net-mvpp2-populate-supported_interfaces-member.patch new file mode 100644 index 0000000..28c7134 --- /dev/null +++ b/pkgs/patches-linux-5.15/779-v5.16-1-net-mvpp2-populate-supported_interfaces-member.patch @@ -0,0 +1,62 @@ +From 7d3f249f553251b6167feb8259b85699239eb64a Mon Sep 17 00:00:00 2001 +From: Russell King <rmk+kernel@armlinux.org.uk> +Date: Wed, 27 Oct 2021 10:49:14 +0100 +Subject: [PATCH 1/4] net: mvpp2: populate supported_interfaces member + +Populate the phy interface mode bitmap for the Marvell mvpp2 driver +with interfaces modes supported by the MAC. + +Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk> +Signed-off-by: David S. Miller <davem@davemloft.net> +--- + .../net/ethernet/marvell/mvpp2/mvpp2_main.c | 34 +++++++++++++++++++ + 1 file changed, 34 insertions(+) + +diff --git a/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c b/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c +index 3c267a94f1ca..d765559f7bd0 100644 +--- a/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c ++++ b/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c +@@ -6941,6 +6941,40 @@ static int mvpp2_port_probe(struct platform_device *pdev, + port->phylink_config.dev = &dev->dev; + port->phylink_config.type = PHYLINK_NETDEV; + ++ if (mvpp2_port_supports_xlg(port)) { ++ __set_bit(PHY_INTERFACE_MODE_10GBASER, ++ port->phylink_config.supported_interfaces); ++ __set_bit(PHY_INTERFACE_MODE_XAUI, ++ port->phylink_config.supported_interfaces); ++ } ++ ++ if (mvpp2_port_supports_rgmii(port)) ++ phy_interface_set_rgmii(port->phylink_config.supported_interfaces); ++ ++ if (comphy) { ++ /* If a COMPHY is present, we can support any of the ++ * serdes modes and switch between them. ++ */ ++ __set_bit(PHY_INTERFACE_MODE_SGMII, ++ port->phylink_config.supported_interfaces); ++ __set_bit(PHY_INTERFACE_MODE_1000BASEX, ++ port->phylink_config.supported_interfaces); ++ __set_bit(PHY_INTERFACE_MODE_2500BASEX, ++ port->phylink_config.supported_interfaces); ++ } else if (phy_mode == PHY_INTERFACE_MODE_2500BASEX) { ++ /* No COMPHY, with only 2500BASE-X mode supported */ ++ __set_bit(PHY_INTERFACE_MODE_2500BASEX, ++ port->phylink_config.supported_interfaces); ++ } else if (phy_mode == PHY_INTERFACE_MODE_1000BASEX || ++ phy_mode == PHY_INTERFACE_MODE_SGMII) { ++ /* No COMPHY, we can switch between 1000BASE-X and SGMII ++ */ ++ __set_bit(PHY_INTERFACE_MODE_1000BASEX, ++ port->phylink_config.supported_interfaces); ++ __set_bit(PHY_INTERFACE_MODE_SGMII, ++ port->phylink_config.supported_interfaces); ++ } ++ + phylink = phylink_create(&port->phylink_config, port_fwnode, + phy_mode, &mvpp2_phylink_ops); + if (IS_ERR(phylink)) { +-- +2.35.1 + diff --git a/pkgs/patches-linux-5.15/779-v5.16-2-net-mvpp2-remove-interface-checks-in-mvpp2_phylink_v.patch b/pkgs/patches-linux-5.15/779-v5.16-2-net-mvpp2-remove-interface-checks-in-mvpp2_phylink_v.patch new file mode 100644 index 0000000..1ac1b4b --- /dev/null +++ b/pkgs/patches-linux-5.15/779-v5.16-2-net-mvpp2-remove-interface-checks-in-mvpp2_phylink_v.patch @@ -0,0 +1,63 @@ +From 7e6f41d50ea0af9f04c17937c4060ddcafa1bc90 Mon Sep 17 00:00:00 2001 +From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk> +Date: Wed, 27 Oct 2021 10:49:19 +0100 +Subject: [PATCH 2/4] net: mvpp2: remove interface checks in + mvpp2_phylink_validate() + +As phylink checks the interface mode against the supported_interfaces +bitmap, we no longer need to validate the interface mode in the +validation function. Remove this to simplify it. + +Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk> +Signed-off-by: David S. Miller <davem@davemloft.net> +--- + .../net/ethernet/marvell/mvpp2/mvpp2_main.c | 33 ++++--------------- + 1 file changed, 7 insertions(+), 26 deletions(-) + +diff --git a/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c b/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c +index d765559f7bd0..9ee1b7ef6edf 100644 +--- a/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c ++++ b/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c +@@ -6265,32 +6265,13 @@ static void mvpp2_phylink_validate(struct phylink_config *config, + struct mvpp2_port *port = mvpp2_phylink_to_port(config); + __ETHTOOL_DECLARE_LINK_MODE_MASK(mask) = { 0, }; + +- /* Invalid combinations */ +- switch (state->interface) { +- case PHY_INTERFACE_MODE_10GBASER: +- case PHY_INTERFACE_MODE_XAUI: +- if (!mvpp2_port_supports_xlg(port)) +- goto empty_set; +- break; +- case PHY_INTERFACE_MODE_RGMII: +- case PHY_INTERFACE_MODE_RGMII_ID: +- case PHY_INTERFACE_MODE_RGMII_RXID: +- case PHY_INTERFACE_MODE_RGMII_TXID: +- if (!mvpp2_port_supports_rgmii(port)) +- goto empty_set; +- break; +- case PHY_INTERFACE_MODE_1000BASEX: +- case PHY_INTERFACE_MODE_2500BASEX: +- /* When in 802.3z mode, we must have AN enabled: +- * Bit 2 Field InBandAnEn In-band Auto-Negotiation enable. ... +- * When <PortType> = 1 (1000BASE-X) this field must be set to 1. +- */ +- if (!phylink_test(state->advertising, Autoneg)) +- goto empty_set; +- break; +- default: +- break; +- } ++ /* When in 802.3z mode, we must have AN enabled: ++ * Bit 2 Field InBandAnEn In-band Auto-Negotiation enable. ... ++ * When <PortType> = 1 (1000BASE-X) this field must be set to 1. ++ */ ++ if (phy_interface_mode_is_8023z(state->interface) && ++ !phylink_test(state->advertising, Autoneg)) ++ goto empty_set; + + phylink_set(mask, Autoneg); + phylink_set_port_modes(mask); +-- +2.35.1 + diff --git a/pkgs/patches-linux-5.15/779-v5.16-3-net-mvpp2-drop-use-of-phylink_helper_basex_speed.patch b/pkgs/patches-linux-5.15/779-v5.16-3-net-mvpp2-drop-use-of-phylink_helper_basex_speed.patch new file mode 100644 index 0000000..0d3add4 --- /dev/null +++ b/pkgs/patches-linux-5.15/779-v5.16-3-net-mvpp2-drop-use-of-phylink_helper_basex_speed.patch @@ -0,0 +1,58 @@ +From 5159f776d9ca2bf038499a551292231740df8122 Mon Sep 17 00:00:00 2001 +From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk> +Date: Wed, 27 Oct 2021 10:49:24 +0100 +Subject: [PATCH 3/4] net: mvpp2: drop use of phylink_helper_basex_speed() + +Now that we have a better method to select SFP interface modes, we +no longer need to use phylink_helper_basex_speed() in a driver's +validation function, and we can also get rid of our hack to indicate +both 1000base-X and 2500base-X if the comphy is present to make that +work. Remove this hack and use of phylink_helper_basex_speed(). + +Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk> +Signed-off-by: David S. Miller <davem@davemloft.net> +--- + .../net/ethernet/marvell/mvpp2/mvpp2_main.c | 19 +++++++------------ + 1 file changed, 7 insertions(+), 12 deletions(-) + +diff --git a/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c b/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c +index 9ee1b7ef6edf..a354aa8d7b9a 100644 +--- a/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c ++++ b/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c +@@ -6307,17 +6307,14 @@ static void mvpp2_phylink_validate(struct phylink_config *config, + break; + fallthrough; + case PHY_INTERFACE_MODE_1000BASEX: ++ phylink_set(mask, 1000baseT_Full); ++ phylink_set(mask, 1000baseX_Full); ++ if (state->interface != PHY_INTERFACE_MODE_NA) ++ break; ++ fallthrough; + case PHY_INTERFACE_MODE_2500BASEX: +- if (port->comphy || +- state->interface != PHY_INTERFACE_MODE_2500BASEX) { +- phylink_set(mask, 1000baseT_Full); +- phylink_set(mask, 1000baseX_Full); +- } +- if (port->comphy || +- state->interface == PHY_INTERFACE_MODE_2500BASEX) { +- phylink_set(mask, 2500baseT_Full); +- phylink_set(mask, 2500baseX_Full); +- } ++ phylink_set(mask, 2500baseT_Full); ++ phylink_set(mask, 2500baseX_Full); + break; + default: + goto empty_set; +@@ -6325,8 +6322,6 @@ static void mvpp2_phylink_validate(struct phylink_config *config, + + linkmode_and(supported, supported, mask); + linkmode_and(state->advertising, state->advertising, mask); +- +- phylink_helper_basex_speed(state); + return; + + empty_set: +-- +2.35.1 + diff --git a/pkgs/patches-linux-5.15/779-v5.16-4-net-mvpp2-clean-up-mvpp2_phylink_validate.patch b/pkgs/patches-linux-5.15/779-v5.16-4-net-mvpp2-clean-up-mvpp2_phylink_validate.patch new file mode 100644 index 0000000..fdd4d20 --- /dev/null +++ b/pkgs/patches-linux-5.15/779-v5.16-4-net-mvpp2-clean-up-mvpp2_phylink_validate.patch @@ -0,0 +1,64 @@ +From 4792da1beeaa5c782b660bc793d331501dc4216b Mon Sep 17 00:00:00 2001 +From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk> +Date: Wed, 27 Oct 2021 10:49:29 +0100 +Subject: [PATCH 4/4] net: mvpp2: clean up mvpp2_phylink_validate() + +mvpp2_phylink_validate() no longer needs to check for +PHY_INTERFACE_MODE_NA as phylink will walk the supported interface +types to discover the link mode capabilities. Remove these checks. + +Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk> +Signed-off-by: David S. Miller <davem@davemloft.net> +--- + drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c | 17 +++++++---------- + 1 file changed, 7 insertions(+), 10 deletions(-) + +diff --git a/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c b/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c +index a354aa8d7b9a..be0dcba4649b 100644 +--- a/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c ++++ b/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c +@@ -6284,14 +6284,12 @@ static void mvpp2_phylink_validate(struct phylink_config *config, + switch (state->interface) { + case PHY_INTERFACE_MODE_10GBASER: + case PHY_INTERFACE_MODE_XAUI: +- case PHY_INTERFACE_MODE_NA: + if (mvpp2_port_supports_xlg(port)) { + phylink_set_10g_modes(mask); + phylink_set(mask, 10000baseKR_Full); + } +- if (state->interface != PHY_INTERFACE_MODE_NA) +- break; +- fallthrough; ++ break; ++ + case PHY_INTERFACE_MODE_RGMII: + case PHY_INTERFACE_MODE_RGMII_ID: + case PHY_INTERFACE_MODE_RGMII_RXID: +@@ -6303,19 +6301,18 @@ static void mvpp2_phylink_validate(struct phylink_config *config, + phylink_set(mask, 100baseT_Full); + phylink_set(mask, 1000baseT_Full); + phylink_set(mask, 1000baseX_Full); +- if (state->interface != PHY_INTERFACE_MODE_NA) +- break; +- fallthrough; ++ break; ++ + case PHY_INTERFACE_MODE_1000BASEX: + phylink_set(mask, 1000baseT_Full); + phylink_set(mask, 1000baseX_Full); +- if (state->interface != PHY_INTERFACE_MODE_NA) +- break; +- fallthrough; ++ break; ++ + case PHY_INTERFACE_MODE_2500BASEX: + phylink_set(mask, 2500baseT_Full); + phylink_set(mask, 2500baseX_Full); + break; ++ + default: + goto empty_set; + } +-- +2.35.1 + diff --git a/pkgs/patches-linux-5.15/780-ARM-kirkwood-add-missing-linux-if_ether.h-for-ETH_AL.patch b/pkgs/patches-linux-5.15/780-ARM-kirkwood-add-missing-linux-if_ether.h-for-ETH_AL.patch new file mode 100644 index 0000000..fcf7892 --- /dev/null +++ b/pkgs/patches-linux-5.15/780-ARM-kirkwood-add-missing-linux-if_ether.h-for-ETH_AL.patch @@ -0,0 +1,61 @@ +From patchwork Thu Aug 5 22:23:30 2021 +Content-Type: text/plain; charset="utf-8" +MIME-Version: 1.0 +Content-Transfer-Encoding: 7bit +X-Patchwork-Submitter: Daniel Golle <daniel@makrotopia.org> +X-Patchwork-Id: 12422209 +Date: Thu, 5 Aug 2021 23:23:30 +0100 +From: Daniel Golle <daniel@makrotopia.org> +To: linux-arm-kernel@lists.infradead.org, netdev@vger.kernel.org, + linux-kernel@vger.kernel.org +Cc: "David S. Miller" <davem@davemloft.net>, Andrew Lunn <andrew@lunn.ch>, + Michael Walle <michael@walle.cc> +Subject: [PATCH] ARM: kirkwood: add missing <linux/if_ether.h> for ETH_ALEN +Message-ID: <YQxk4jrbm31NM1US@makrotopia.org> +MIME-Version: 1.0 +Content-Disposition: inline +X-BeenThere: linux-arm-kernel@lists.infradead.org +X-Mailman-Version: 2.1.34 +Precedence: list +List-Id: <linux-arm-kernel.lists.infradead.org> +List-Archive: <http://lists.infradead.org/pipermail/linux-arm-kernel/> +Sender: "linux-arm-kernel" <linux-arm-kernel-bounces@lists.infradead.org> + +After commit 83216e3988cd1 ("of: net: pass the dst buffer to +of_get_mac_address()") build fails for kirkwood as ETH_ALEN is not +defined. + +arch/arm/mach-mvebu/kirkwood.c: In function 'kirkwood_dt_eth_fixup': +arch/arm/mach-mvebu/kirkwood.c:87:13: error: 'ETH_ALEN' undeclared (first use in this function); did you mean 'ESTALE'? + u8 tmpmac[ETH_ALEN]; + ^~~~~~~~ + ESTALE +arch/arm/mach-mvebu/kirkwood.c:87:13: note: each undeclared identifier is reported only once for each function it appears in +arch/arm/mach-mvebu/kirkwood.c:87:6: warning: unused variable 'tmpmac' [-Wunused-variable] + u8 tmpmac[ETH_ALEN]; + ^~~~~~ +make[5]: *** [scripts/Makefile.build:262: arch/arm/mach-mvebu/kirkwood.o] Error 1 +make[5]: *** Waiting for unfinished jobs.... + +Add missing #include <linux/if_ether.h> to fix this. + +Cc: David S. Miller <davem@davemloft.net> +Cc: Andrew Lunn <andrew@lunn.ch> +Cc: Michael Walle <michael@walle.cc> +Reported-by: https://buildbot.openwrt.org/master/images/#/builders/56/builds/220/steps/44/logs/stdio +Fixes: 83216e3988cd1 ("of: net: pass the dst buffer to of_get_mac_address()") +Signed-off-by: Daniel Golle <daniel@makrotopia.org> +--- + arch/arm/mach-mvebu/kirkwood.c | 1 + + 1 file changed, 1 insertion(+) + +--- a/arch/arm/mach-mvebu/kirkwood.c ++++ b/arch/arm/mach-mvebu/kirkwood.c +@@ -14,6 +14,7 @@ + #include <linux/kernel.h> + #include <linux/init.h> + #include <linux/mbus.h> ++#include <linux/if_ether.h> + #include <linux/of.h> + #include <linux/of_address.h> + #include <linux/of_net.h> diff --git a/pkgs/patches-linux-5.15/780-usb-net-MeigLink_modem_support.patch b/pkgs/patches-linux-5.15/780-usb-net-MeigLink_modem_support.patch new file mode 100644 index 0000000..096f143 --- /dev/null +++ b/pkgs/patches-linux-5.15/780-usb-net-MeigLink_modem_support.patch @@ -0,0 +1,43 @@ +From f81700b6bb2eda3756247bce472d8eaf6f466f61 Mon Sep 17 00:00:00 2001 +From: OpenWrt community <openwrt-devel@lists.openwrt.org> +Date: Wed, 13 Jul 2022 13:49:26 +0200 +Subject: [PATCH] net/usb/qmi_wwan: add MeigLink modem support + +--- + drivers/net/usb/qmi_wwan.c | 1 + + drivers/usb/serial/option.c | 7 +++++++ + 2 files changed, 8 insertions(+) + +--- a/drivers/net/usb/qmi_wwan.c ++++ b/drivers/net/usb/qmi_wwan.c +@@ -1086,6 +1086,7 @@ static const struct usb_device_id produc + {QMI_MATCH_FF_FF_FF(0x2c7c, 0x0620)}, /* Quectel EM160R-GL */ + {QMI_MATCH_FF_FF_FF(0x2c7c, 0x0800)}, /* Quectel RM500Q-GL */ + {QMI_MATCH_FF_FF_FF(0x2c7c, 0x0801)}, /* Quectel RM520N */ ++ {QMI_MATCH_FF_FF_FF(0x05c6, 0xf601)}, /* MeigLink SLM750 */ + + /* 3. Combined interface devices matching on interface number */ + {QMI_FIXED_INTF(0x0408, 0xea42, 4)}, /* Yota / Megafon M100-1 */ +--- a/drivers/usb/serial/option.c ++++ b/drivers/usb/serial/option.c +@@ -243,6 +243,8 @@ static void option_instat_callback(struc + #define UBLOX_PRODUCT_R6XX 0x90fa + /* These Yuga products use Qualcomm's vendor ID */ + #define YUGA_PRODUCT_CLM920_NC5 0x9625 ++/* These MeigLink products use Qualcomm's vendor ID */ ++#define MEIGLINK_PRODUCT_SLM750 0xf601 + + #define QUECTEL_VENDOR_ID 0x2c7c + /* These Quectel products use Quectel's vendor ID */ +@@ -1140,6 +1142,11 @@ static const struct usb_device_id option + { USB_DEVICE_AND_INTERFACE_INFO(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_EG95, 0xff, 0, 0) }, + { USB_DEVICE(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_BG96), + .driver_info = RSVD(4) }, ++ /* Meiglink products using Qualcomm vendor ID */ ++ // Works OK. In case of some issues check macros that are used by Quectel Products ++ { USB_DEVICE_AND_INTERFACE_INFO(QUALCOMM_VENDOR_ID, MEIGLINK_PRODUCT_SLM750, 0xff, 0xff, 0xff), ++ .driver_info = NUMEP2 }, ++ { USB_DEVICE_AND_INTERFACE_INFO(QUALCOMM_VENDOR_ID, MEIGLINK_PRODUCT_SLM750, 0xff, 0, 0) }, + { USB_DEVICE_AND_INTERFACE_INFO(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_EP06, 0xff, 0xff, 0xff), + .driver_info = RSVD(1) | RSVD(2) | RSVD(3) | RSVD(4) | NUMEP2 }, + { USB_DEVICE_AND_INTERFACE_INFO(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_EP06, 0xff, 0, 0) }, diff --git a/pkgs/patches-linux-5.15/780-v5.16-1-net-phylink-add-generic-validate-implementation.patch b/pkgs/patches-linux-5.15/780-v5.16-1-net-phylink-add-generic-validate-implementation.patch new file mode 100644 index 0000000..22a25f4 --- /dev/null +++ b/pkgs/patches-linux-5.15/780-v5.16-1-net-phylink-add-generic-validate-implementation.patch @@ -0,0 +1,348 @@ +From 792d0a8e185e3ef101e2644503a0828246c8a5b3 Mon Sep 17 00:00:00 2001 +From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk> +Date: Mon, 15 Nov 2021 10:00:27 +0000 +Subject: [PATCH 1/3] net: phylink: add generic validate implementation + +Add a generic validate() implementation using the supported_interfaces +and a bitmask of MAC pause/speed/duplex capabilities. This allows us +to entirely eliminate many driver private validate() implementations. + +We expose the underlying phylink_get_linkmodes() function so that +drivers which have special needs can still benefit from conversion. + +Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk> +Signed-off-by: David S. Miller <davem@davemloft.net> +--- + drivers/net/phy/phylink.c | 252 ++++++++++++++++++++++++++++++++++++++ + include/linux/phylink.h | 31 +++++ + 2 files changed, 283 insertions(+) + +diff --git a/drivers/net/phy/phylink.c b/drivers/net/phy/phylink.c +index 0720f05ecb02..3e1d7dea616d 100644 +--- a/drivers/net/phy/phylink.c ++++ b/drivers/net/phy/phylink.c +@@ -167,6 +167,258 @@ static const char *phylink_an_mode_str(unsigned int mode) + return mode < ARRAY_SIZE(modestr) ? modestr[mode] : "unknown"; + } + ++static void phylink_caps_to_linkmodes(unsigned long *linkmodes, ++ unsigned long caps) ++{ ++ if (caps & MAC_SYM_PAUSE) ++ __set_bit(ETHTOOL_LINK_MODE_Pause_BIT, linkmodes); ++ ++ if (caps & MAC_ASYM_PAUSE) ++ __set_bit(ETHTOOL_LINK_MODE_Asym_Pause_BIT, linkmodes); ++ ++ if (caps & MAC_10HD) ++ __set_bit(ETHTOOL_LINK_MODE_10baseT_Half_BIT, linkmodes); ++ ++ if (caps & MAC_10FD) ++ __set_bit(ETHTOOL_LINK_MODE_10baseT_Full_BIT, linkmodes); ++ ++ if (caps & MAC_100HD) { ++ __set_bit(ETHTOOL_LINK_MODE_100baseT_Half_BIT, linkmodes); ++ __set_bit(ETHTOOL_LINK_MODE_100baseFX_Half_BIT, linkmodes); ++ } ++ ++ if (caps & MAC_100FD) { ++ __set_bit(ETHTOOL_LINK_MODE_100baseT_Full_BIT, linkmodes); ++ __set_bit(ETHTOOL_LINK_MODE_100baseT1_Full_BIT, linkmodes); ++ __set_bit(ETHTOOL_LINK_MODE_100baseFX_Full_BIT, linkmodes); ++ } ++ ++ if (caps & MAC_1000HD) ++ __set_bit(ETHTOOL_LINK_MODE_1000baseT_Half_BIT, linkmodes); ++ ++ if (caps & MAC_1000FD) { ++ __set_bit(ETHTOOL_LINK_MODE_1000baseT_Full_BIT, linkmodes); ++ __set_bit(ETHTOOL_LINK_MODE_1000baseX_Full_BIT, linkmodes); ++ __set_bit(ETHTOOL_LINK_MODE_1000baseT1_Full_BIT, linkmodes); ++ } ++ ++ if (caps & MAC_2500FD) { ++ __set_bit(ETHTOOL_LINK_MODE_2500baseT_Full_BIT, linkmodes); ++ __set_bit(ETHTOOL_LINK_MODE_2500baseX_Full_BIT, linkmodes); ++ } ++ ++ if (caps & MAC_5000FD) ++ __set_bit(ETHTOOL_LINK_MODE_5000baseT_Full_BIT, linkmodes); ++ ++ if (caps & MAC_10000FD) { ++ __set_bit(ETHTOOL_LINK_MODE_10000baseT_Full_BIT, linkmodes); ++ __set_bit(ETHTOOL_LINK_MODE_10000baseKX4_Full_BIT, linkmodes); ++ __set_bit(ETHTOOL_LINK_MODE_10000baseKR_Full_BIT, linkmodes); ++ __set_bit(ETHTOOL_LINK_MODE_10000baseR_FEC_BIT, linkmodes); ++ __set_bit(ETHTOOL_LINK_MODE_10000baseCR_Full_BIT, linkmodes); ++ __set_bit(ETHTOOL_LINK_MODE_10000baseSR_Full_BIT, linkmodes); ++ __set_bit(ETHTOOL_LINK_MODE_10000baseLR_Full_BIT, linkmodes); ++ __set_bit(ETHTOOL_LINK_MODE_10000baseLRM_Full_BIT, linkmodes); ++ __set_bit(ETHTOOL_LINK_MODE_10000baseER_Full_BIT, linkmodes); ++ } ++ ++ if (caps & MAC_25000FD) { ++ __set_bit(ETHTOOL_LINK_MODE_25000baseCR_Full_BIT, linkmodes); ++ __set_bit(ETHTOOL_LINK_MODE_25000baseKR_Full_BIT, linkmodes); ++ __set_bit(ETHTOOL_LINK_MODE_25000baseSR_Full_BIT, linkmodes); ++ } ++ ++ if (caps & MAC_40000FD) { ++ __set_bit(ETHTOOL_LINK_MODE_40000baseKR4_Full_BIT, linkmodes); ++ __set_bit(ETHTOOL_LINK_MODE_40000baseCR4_Full_BIT, linkmodes); ++ __set_bit(ETHTOOL_LINK_MODE_40000baseSR4_Full_BIT, linkmodes); ++ __set_bit(ETHTOOL_LINK_MODE_40000baseLR4_Full_BIT, linkmodes); ++ } ++ ++ if (caps & MAC_50000FD) { ++ __set_bit(ETHTOOL_LINK_MODE_50000baseCR2_Full_BIT, linkmodes); ++ __set_bit(ETHTOOL_LINK_MODE_50000baseKR2_Full_BIT, linkmodes); ++ __set_bit(ETHTOOL_LINK_MODE_50000baseSR2_Full_BIT, linkmodes); ++ __set_bit(ETHTOOL_LINK_MODE_50000baseKR_Full_BIT, linkmodes); ++ __set_bit(ETHTOOL_LINK_MODE_50000baseSR_Full_BIT, linkmodes); ++ __set_bit(ETHTOOL_LINK_MODE_50000baseCR_Full_BIT, linkmodes); ++ __set_bit(ETHTOOL_LINK_MODE_50000baseLR_ER_FR_Full_BIT, ++ linkmodes); ++ __set_bit(ETHTOOL_LINK_MODE_50000baseDR_Full_BIT, linkmodes); ++ } ++ ++ if (caps & MAC_56000FD) { ++ __set_bit(ETHTOOL_LINK_MODE_56000baseKR4_Full_BIT, linkmodes); ++ __set_bit(ETHTOOL_LINK_MODE_56000baseCR4_Full_BIT, linkmodes); ++ __set_bit(ETHTOOL_LINK_MODE_56000baseSR4_Full_BIT, linkmodes); ++ __set_bit(ETHTOOL_LINK_MODE_56000baseLR4_Full_BIT, linkmodes); ++ } ++ ++ if (caps & MAC_100000FD) { ++ __set_bit(ETHTOOL_LINK_MODE_100000baseKR4_Full_BIT, linkmodes); ++ __set_bit(ETHTOOL_LINK_MODE_100000baseSR4_Full_BIT, linkmodes); ++ __set_bit(ETHTOOL_LINK_MODE_100000baseCR4_Full_BIT, linkmodes); ++ __set_bit(ETHTOOL_LINK_MODE_100000baseLR4_ER4_Full_BIT, ++ linkmodes); ++ __set_bit(ETHTOOL_LINK_MODE_100000baseKR2_Full_BIT, linkmodes); ++ __set_bit(ETHTOOL_LINK_MODE_100000baseSR2_Full_BIT, linkmodes); ++ __set_bit(ETHTOOL_LINK_MODE_100000baseCR2_Full_BIT, linkmodes); ++ __set_bit(ETHTOOL_LINK_MODE_100000baseLR2_ER2_FR2_Full_BIT, ++ linkmodes); ++ __set_bit(ETHTOOL_LINK_MODE_100000baseDR2_Full_BIT, linkmodes); ++ __set_bit(ETHTOOL_LINK_MODE_100000baseKR_Full_BIT, linkmodes); ++ __set_bit(ETHTOOL_LINK_MODE_100000baseSR_Full_BIT, linkmodes); ++ __set_bit(ETHTOOL_LINK_MODE_100000baseLR_ER_FR_Full_BIT, ++ linkmodes); ++ __set_bit(ETHTOOL_LINK_MODE_100000baseCR_Full_BIT, linkmodes); ++ __set_bit(ETHTOOL_LINK_MODE_100000baseDR_Full_BIT, linkmodes); ++ } ++ ++ if (caps & MAC_200000FD) { ++ __set_bit(ETHTOOL_LINK_MODE_200000baseKR4_Full_BIT, linkmodes); ++ __set_bit(ETHTOOL_LINK_MODE_200000baseSR4_Full_BIT, linkmodes); ++ __set_bit(ETHTOOL_LINK_MODE_200000baseLR4_ER4_FR4_Full_BIT, ++ linkmodes); ++ __set_bit(ETHTOOL_LINK_MODE_200000baseDR4_Full_BIT, linkmodes); ++ __set_bit(ETHTOOL_LINK_MODE_200000baseCR4_Full_BIT, linkmodes); ++ __set_bit(ETHTOOL_LINK_MODE_200000baseKR2_Full_BIT, linkmodes); ++ __set_bit(ETHTOOL_LINK_MODE_200000baseSR2_Full_BIT, linkmodes); ++ __set_bit(ETHTOOL_LINK_MODE_200000baseLR2_ER2_FR2_Full_BIT, ++ linkmodes); ++ __set_bit(ETHTOOL_LINK_MODE_200000baseDR2_Full_BIT, linkmodes); ++ __set_bit(ETHTOOL_LINK_MODE_200000baseCR2_Full_BIT, linkmodes); ++ } ++ ++ if (caps & MAC_400000FD) { ++ __set_bit(ETHTOOL_LINK_MODE_400000baseKR8_Full_BIT, linkmodes); ++ __set_bit(ETHTOOL_LINK_MODE_400000baseSR8_Full_BIT, linkmodes); ++ __set_bit(ETHTOOL_LINK_MODE_400000baseLR8_ER8_FR8_Full_BIT, ++ linkmodes); ++ __set_bit(ETHTOOL_LINK_MODE_400000baseDR8_Full_BIT, linkmodes); ++ __set_bit(ETHTOOL_LINK_MODE_400000baseCR8_Full_BIT, linkmodes); ++ __set_bit(ETHTOOL_LINK_MODE_400000baseKR4_Full_BIT, linkmodes); ++ __set_bit(ETHTOOL_LINK_MODE_400000baseSR4_Full_BIT, linkmodes); ++ __set_bit(ETHTOOL_LINK_MODE_400000baseLR4_ER4_FR4_Full_BIT, ++ linkmodes); ++ __set_bit(ETHTOOL_LINK_MODE_400000baseDR4_Full_BIT, linkmodes); ++ __set_bit(ETHTOOL_LINK_MODE_400000baseCR4_Full_BIT, linkmodes); ++ } ++} ++ ++/** ++ * phylink_get_linkmodes() - get acceptable link modes ++ * @linkmodes: ethtool linkmode mask (must be already initialised) ++ * @interface: phy interface mode defined by &typedef phy_interface_t ++ * @mac_capabilities: bitmask of MAC capabilities ++ * ++ * Set all possible pause, speed and duplex linkmodes in @linkmodes that ++ * are supported by the @interface mode and @mac_capabilities. @linkmodes ++ * must have been initialised previously. ++ */ ++void phylink_get_linkmodes(unsigned long *linkmodes, phy_interface_t interface, ++ unsigned long mac_capabilities) ++{ ++ unsigned long caps = MAC_SYM_PAUSE | MAC_ASYM_PAUSE; ++ ++ switch (interface) { ++ case PHY_INTERFACE_MODE_USXGMII: ++ caps |= MAC_10000FD | MAC_5000FD | MAC_2500FD; ++ fallthrough; ++ ++ case PHY_INTERFACE_MODE_RGMII_TXID: ++ case PHY_INTERFACE_MODE_RGMII_RXID: ++ case PHY_INTERFACE_MODE_RGMII_ID: ++ case PHY_INTERFACE_MODE_RGMII: ++ case PHY_INTERFACE_MODE_QSGMII: ++ case PHY_INTERFACE_MODE_SGMII: ++ case PHY_INTERFACE_MODE_GMII: ++ caps |= MAC_1000HD | MAC_1000FD; ++ fallthrough; ++ ++ case PHY_INTERFACE_MODE_REVRMII: ++ case PHY_INTERFACE_MODE_RMII: ++ case PHY_INTERFACE_MODE_REVMII: ++ case PHY_INTERFACE_MODE_MII: ++ caps |= MAC_10HD | MAC_10FD; ++ fallthrough; ++ ++ case PHY_INTERFACE_MODE_100BASEX: ++ caps |= MAC_100HD | MAC_100FD; ++ break; ++ ++ case PHY_INTERFACE_MODE_TBI: ++ case PHY_INTERFACE_MODE_MOCA: ++ case PHY_INTERFACE_MODE_RTBI: ++ case PHY_INTERFACE_MODE_1000BASEX: ++ caps |= MAC_1000HD; ++ fallthrough; ++ case PHY_INTERFACE_MODE_TRGMII: ++ caps |= MAC_1000FD; ++ break; ++ ++ case PHY_INTERFACE_MODE_2500BASEX: ++ caps |= MAC_2500FD; ++ break; ++ ++ case PHY_INTERFACE_MODE_5GBASER: ++ caps |= MAC_5000FD; ++ break; ++ ++ case PHY_INTERFACE_MODE_XGMII: ++ case PHY_INTERFACE_MODE_RXAUI: ++ case PHY_INTERFACE_MODE_XAUI: ++ case PHY_INTERFACE_MODE_10GBASER: ++ case PHY_INTERFACE_MODE_10GKR: ++ caps |= MAC_10000FD; ++ break; ++ ++ case PHY_INTERFACE_MODE_25GBASER: ++ caps |= MAC_25000FD; ++ break; ++ ++ case PHY_INTERFACE_MODE_XLGMII: ++ caps |= MAC_40000FD; ++ break; ++ ++ case PHY_INTERFACE_MODE_INTERNAL: ++ caps |= ~0; ++ break; ++ ++ case PHY_INTERFACE_MODE_NA: ++ case PHY_INTERFACE_MODE_MAX: ++ case PHY_INTERFACE_MODE_SMII: ++ break; ++ } ++ ++ phylink_caps_to_linkmodes(linkmodes, caps & mac_capabilities); ++} ++EXPORT_SYMBOL_GPL(phylink_get_linkmodes); ++ ++/** ++ * phylink_generic_validate() - generic validate() callback implementation ++ * @config: a pointer to a &struct phylink_config. ++ * @supported: ethtool bitmask for supported link modes. ++ * @state: a pointer to a &struct phylink_link_state. ++ * ++ * Generic implementation of the validate() callback that MAC drivers can ++ * use when they pass the range of supported interfaces and MAC capabilities. ++ * This makes use of phylink_get_linkmodes(). ++ */ ++void phylink_generic_validate(struct phylink_config *config, ++ unsigned long *supported, ++ struct phylink_link_state *state) ++{ ++ __ETHTOOL_DECLARE_LINK_MODE_MASK(mask) = { 0, }; ++ ++ phylink_set_port_modes(mask); ++ phylink_set(mask, Autoneg); ++ phylink_get_linkmodes(mask, state->interface, config->mac_capabilities); ++ ++ linkmode_and(supported, supported, mask); ++ linkmode_and(state->advertising, state->advertising, mask); ++} ++EXPORT_SYMBOL_GPL(phylink_generic_validate); ++ + static int phylink_validate_any(struct phylink *pl, unsigned long *supported, + struct phylink_link_state *state) + { +diff --git a/include/linux/phylink.h b/include/linux/phylink.h +index f037470b6fb3..3563820a1765 100644 +--- a/include/linux/phylink.h ++++ b/include/linux/phylink.h +@@ -20,6 +20,29 @@ enum { + MLO_AN_PHY = 0, /* Conventional PHY */ + MLO_AN_FIXED, /* Fixed-link mode */ + MLO_AN_INBAND, /* In-band protocol */ ++ ++ MAC_SYM_PAUSE = BIT(0), ++ MAC_ASYM_PAUSE = BIT(1), ++ MAC_10HD = BIT(2), ++ MAC_10FD = BIT(3), ++ MAC_10 = MAC_10HD | MAC_10FD, ++ MAC_100HD = BIT(4), ++ MAC_100FD = BIT(5), ++ MAC_100 = MAC_100HD | MAC_100FD, ++ MAC_1000HD = BIT(6), ++ MAC_1000FD = BIT(7), ++ MAC_1000 = MAC_1000HD | MAC_1000FD, ++ MAC_2500FD = BIT(8), ++ MAC_5000FD = BIT(9), ++ MAC_10000FD = BIT(10), ++ MAC_20000FD = BIT(11), ++ MAC_25000FD = BIT(12), ++ MAC_40000FD = BIT(13), ++ MAC_50000FD = BIT(14), ++ MAC_56000FD = BIT(15), ++ MAC_100000FD = BIT(16), ++ MAC_200000FD = BIT(17), ++ MAC_400000FD = BIT(18), + }; + + static inline bool phylink_autoneg_inband(unsigned int mode) +@@ -69,6 +92,7 @@ enum phylink_op_type { + * if MAC link is at %MLO_AN_FIXED mode. + * @supported_interfaces: bitmap describing which PHY_INTERFACE_MODE_xxx + * are supported by the MAC/PCS. ++ * @mac_capabilities: MAC pause/speed/duplex capabilities. + */ + struct phylink_config { + struct device *dev; +@@ -79,6 +103,7 @@ struct phylink_config { + void (*get_fixed_state)(struct phylink_config *config, + struct phylink_link_state *state); + DECLARE_PHY_INTERFACE_MASK(supported_interfaces); ++ unsigned long mac_capabilities; + }; + + /** +@@ -442,6 +467,12 @@ void pcs_link_up(struct phylink_pcs *pcs, unsigned int mode, + phy_interface_t interface, int speed, int duplex); + #endif + ++void phylink_get_linkmodes(unsigned long *linkmodes, phy_interface_t interface, ++ unsigned long mac_capabilities); ++void phylink_generic_validate(struct phylink_config *config, ++ unsigned long *supported, ++ struct phylink_link_state *state); ++ + struct phylink *phylink_create(struct phylink_config *, struct fwnode_handle *, + phy_interface_t iface, + const struct phylink_mac_ops *mac_ops); +-- +2.35.1 + diff --git a/pkgs/patches-linux-5.15/780-v5.16-2-net-mvneta-use-phylink_generic_validate.patch b/pkgs/patches-linux-5.15/780-v5.16-2-net-mvneta-use-phylink_generic_validate.patch new file mode 100644 index 0000000..af0e06f --- /dev/null +++ b/pkgs/patches-linux-5.15/780-v5.16-2-net-mvneta-use-phylink_generic_validate.patch @@ -0,0 +1,77 @@ +From 50ba86adc61e95d49215c1c089697190a2666e3a Mon Sep 17 00:00:00 2001 +From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk> +Date: Mon, 15 Nov 2021 10:00:32 +0000 +Subject: [PATCH 2/3] net: mvneta: use phylink_generic_validate() + +Convert mvneta to use phylink_generic_validate() for the bulk of its +validate() implementation. This network adapter has a restriction +that for 802.3z links, autonegotiation must be enabled. + +Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk> +Signed-off-by: David S. Miller <davem@davemloft.net> +--- + drivers/net/ethernet/marvell/mvneta.c | 34 ++++----------------------- + 1 file changed, 4 insertions(+), 30 deletions(-) + +diff --git a/drivers/net/ethernet/marvell/mvneta.c b/drivers/net/ethernet/marvell/mvneta.c +index 51fe2de0215e..191eef5c26a7 100644 +--- a/drivers/net/ethernet/marvell/mvneta.c ++++ b/drivers/net/ethernet/marvell/mvneta.c +@@ -3851,8 +3851,6 @@ static void mvneta_validate(struct phylink_config *config, + unsigned long *supported, + struct phylink_link_state *state) + { +- __ETHTOOL_DECLARE_LINK_MODE_MASK(mask) = { 0, }; +- + /* We only support QSGMII, SGMII, 802.3z and RGMII modes. + * When in 802.3z mode, we must have AN enabled: + * "Bit 2 Field InBandAnEn In-band Auto-Negotiation enable. ... +@@ -3864,34 +3862,7 @@ static void mvneta_validate(struct phylink_config *config, + return; + } + +- /* Allow all the expected bits */ +- phylink_set(mask, Autoneg); +- phylink_set_port_modes(mask); +- +- /* Asymmetric pause is unsupported */ +- phylink_set(mask, Pause); +- +- /* Half-duplex at speeds higher than 100Mbit is unsupported */ +- if (state->interface != PHY_INTERFACE_MODE_2500BASEX) { +- phylink_set(mask, 1000baseT_Full); +- phylink_set(mask, 1000baseX_Full); +- } +- +- if (state->interface == PHY_INTERFACE_MODE_2500BASEX) { +- phylink_set(mask, 2500baseT_Full); +- phylink_set(mask, 2500baseX_Full); +- } +- +- if (!phy_interface_mode_is_8023z(state->interface)) { +- /* 10M and 100M are only supported in non-802.3z mode */ +- phylink_set(mask, 10baseT_Half); +- phylink_set(mask, 10baseT_Full); +- phylink_set(mask, 100baseT_Half); +- phylink_set(mask, 100baseT_Full); +- } +- +- linkmode_and(supported, supported, mask); +- linkmode_and(state->advertising, state->advertising, mask); ++ phylink_generic_validate(config, supported, state); + } + + static void mvneta_mac_pcs_get_state(struct phylink_config *config, +@@ -5295,6 +5266,9 @@ static int mvneta_probe(struct platform_device *pdev) + + pp->phylink_config.dev = &dev->dev; + pp->phylink_config.type = PHYLINK_NETDEV; ++ pp->phylink_config.mac_capabilities = MAC_SYM_PAUSE | MAC_10 | ++ MAC_100 | MAC_1000FD | MAC_2500FD; ++ + phy_interface_set_rgmii(pp->phylink_config.supported_interfaces); + __set_bit(PHY_INTERFACE_MODE_QSGMII, + pp->phylink_config.supported_interfaces); +-- +2.35.1 + diff --git a/pkgs/patches-linux-5.15/780-v5.16-3-net-mvpp2-use-phylink_generic_validate.patch b/pkgs/patches-linux-5.15/780-v5.16-3-net-mvpp2-use-phylink_generic_validate.patch new file mode 100644 index 0000000..a41299d --- /dev/null +++ b/pkgs/patches-linux-5.15/780-v5.16-3-net-mvpp2-use-phylink_generic_validate.patch @@ -0,0 +1,107 @@ +From 7356c6e175210a721310f75915248c3148e3147d Mon Sep 17 00:00:00 2001 +From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk> +Date: Mon, 15 Nov 2021 10:00:37 +0000 +Subject: [PATCH 3/3] net: mvpp2: use phylink_generic_validate() + +Convert mvpp2 to use phylink_generic_validate() for the bulk of its +validate() implementation. This network adapter has a restriction +that for 802.3z links, autonegotiation must be enabled. + +Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk> +Signed-off-by: David S. Miller <davem@davemloft.net> +--- + .../net/ethernet/marvell/mvpp2/mvpp2_main.c | 58 +++---------------- + 1 file changed, 9 insertions(+), 49 deletions(-) + +diff --git a/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c b/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c +index be0dcba4649b..2b8293c0c2f9 100644 +--- a/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c ++++ b/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c +@@ -6262,9 +6262,6 @@ static void mvpp2_phylink_validate(struct phylink_config *config, + unsigned long *supported, + struct phylink_link_state *state) + { +- struct mvpp2_port *port = mvpp2_phylink_to_port(config); +- __ETHTOOL_DECLARE_LINK_MODE_MASK(mask) = { 0, }; +- + /* When in 802.3z mode, we must have AN enabled: + * Bit 2 Field InBandAnEn In-band Auto-Negotiation enable. ... + * When <PortType> = 1 (1000BASE-X) this field must be set to 1. +@@ -6273,52 +6270,7 @@ static void mvpp2_phylink_validate(struct phylink_config *config, + !phylink_test(state->advertising, Autoneg)) + goto empty_set; + +- phylink_set(mask, Autoneg); +- phylink_set_port_modes(mask); +- +- if (port->priv->global_tx_fc) { +- phylink_set(mask, Pause); +- phylink_set(mask, Asym_Pause); +- } +- +- switch (state->interface) { +- case PHY_INTERFACE_MODE_10GBASER: +- case PHY_INTERFACE_MODE_XAUI: +- if (mvpp2_port_supports_xlg(port)) { +- phylink_set_10g_modes(mask); +- phylink_set(mask, 10000baseKR_Full); +- } +- break; +- +- case PHY_INTERFACE_MODE_RGMII: +- case PHY_INTERFACE_MODE_RGMII_ID: +- case PHY_INTERFACE_MODE_RGMII_RXID: +- case PHY_INTERFACE_MODE_RGMII_TXID: +- case PHY_INTERFACE_MODE_SGMII: +- phylink_set(mask, 10baseT_Half); +- phylink_set(mask, 10baseT_Full); +- phylink_set(mask, 100baseT_Half); +- phylink_set(mask, 100baseT_Full); +- phylink_set(mask, 1000baseT_Full); +- phylink_set(mask, 1000baseX_Full); +- break; +- +- case PHY_INTERFACE_MODE_1000BASEX: +- phylink_set(mask, 1000baseT_Full); +- phylink_set(mask, 1000baseX_Full); +- break; +- +- case PHY_INTERFACE_MODE_2500BASEX: +- phylink_set(mask, 2500baseT_Full); +- phylink_set(mask, 2500baseX_Full); +- break; +- +- default: +- goto empty_set; +- } +- +- linkmode_and(supported, supported, mask); +- linkmode_and(state->advertising, state->advertising, mask); ++ phylink_generic_validate(config, supported, state); + return; + + empty_set: +@@ -6913,12 +6865,20 @@ static int mvpp2_port_probe(struct platform_device *pdev, + if (!mvpp2_use_acpi_compat_mode(port_fwnode)) { + port->phylink_config.dev = &dev->dev; + port->phylink_config.type = PHYLINK_NETDEV; ++ port->phylink_config.mac_capabilities = ++ MAC_2500FD | MAC_1000FD | MAC_100 | MAC_10; ++ ++ if (port->priv->global_tx_fc) ++ port->phylink_config.mac_capabilities |= ++ MAC_SYM_PAUSE | MAC_ASYM_PAUSE; + + if (mvpp2_port_supports_xlg(port)) { + __set_bit(PHY_INTERFACE_MODE_10GBASER, + port->phylink_config.supported_interfaces); + __set_bit(PHY_INTERFACE_MODE_XAUI, + port->phylink_config.supported_interfaces); ++ port->phylink_config.mac_capabilities |= ++ MAC_10000FD; + } + + if (mvpp2_port_supports_rgmii(port)) +-- +2.35.1 + diff --git a/pkgs/patches-linux-5.15/781-v5.17-1-net-dsa-populate-supported_interfaces-member.patch b/pkgs/patches-linux-5.15/781-v5.17-1-net-dsa-populate-supported_interfaces-member.patch new file mode 100644 index 0000000..d4bcbb3 --- /dev/null +++ b/pkgs/patches-linux-5.15/781-v5.17-1-net-dsa-populate-supported_interfaces-member.patch @@ -0,0 +1,72 @@ +From e89cf45caa992df5baee7ce07431d9598ac1defc Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Marek=20Beh=C3=BAn?= <kabel@kernel.org> +Date: Thu, 28 Oct 2021 18:00:14 +0100 +Subject: [PATCH 1/6] net: dsa: populate supported_interfaces member +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Add a new DSA switch operation, phylink_get_interfaces, which should +fill in which PHY_INTERFACE_MODE_* are supported by given port. + +Use this before phylink_create() to fill phylinks supported_interfaces +member, allowing phylink to determine which PHY_INTERFACE_MODEs are +supported. + +Signed-off-by: Marek Behún <kabel@kernel.org> +[tweaked patch and description to add more complete support -- rmk] +Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk> +Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk> +Signed-off-by: David S. Miller <davem@davemloft.net> +--- + include/net/dsa.h | 2 ++ + net/dsa/port.c | 4 ++++ + net/dsa/slave.c | 4 ++++ + 3 files changed, 10 insertions(+) + +diff --git a/include/net/dsa.h b/include/net/dsa.h +index 206ff3c4ce47..77d0d9a8e2ab 100644 +--- a/include/net/dsa.h ++++ b/include/net/dsa.h +@@ -636,6 +636,8 @@ struct dsa_switch_ops { + /* + * PHYLINK integration + */ ++ void (*phylink_get_interfaces)(struct dsa_switch *ds, int port, ++ unsigned long *supported_interfaces); + void (*phylink_validate)(struct dsa_switch *ds, int port, + unsigned long *supported, + struct phylink_link_state *state); +diff --git a/net/dsa/port.c b/net/dsa/port.c +index a21015d6bd36..d1502403aeb8 100644 +--- a/net/dsa/port.c ++++ b/net/dsa/port.c +@@ -1172,6 +1172,10 @@ static int dsa_port_phylink_register(struct dsa_port *dp) + dp->pl_config.type = PHYLINK_DEV; + dp->pl_config.pcs_poll = ds->pcs_poll; + ++ if (ds->ops->phylink_get_interfaces) ++ ds->ops->phylink_get_interfaces(ds, dp->index, ++ dp->pl_config.supported_interfaces); ++ + dp->pl = phylink_create(&dp->pl_config, of_fwnode_handle(port_dn), + mode, &dsa_port_phylink_mac_ops); + if (IS_ERR(dp->pl)) { +diff --git a/net/dsa/slave.c b/net/dsa/slave.c +index 4d581bd22cd5..525140370950 100644 +--- a/net/dsa/slave.c ++++ b/net/dsa/slave.c +@@ -1837,6 +1837,10 @@ static int dsa_slave_phy_setup(struct net_device *slave_dev) + dp->pl_config.poll_fixed_state = true; + } + ++ if (ds->ops->phylink_get_interfaces) ++ ds->ops->phylink_get_interfaces(ds, dp->index, ++ dp->pl_config.supported_interfaces); ++ + dp->pl = phylink_create(&dp->pl_config, of_fwnode_handle(port_dn), mode, + &dsa_port_phylink_mac_ops); + if (IS_ERR(dp->pl)) { +-- +2.35.1 + diff --git a/pkgs/patches-linux-5.15/781-v5.17-2-net-dsa-consolidate-phylink-creation.patch b/pkgs/patches-linux-5.15/781-v5.17-2-net-dsa-consolidate-phylink-creation.patch new file mode 100644 index 0000000..26606a5 --- /dev/null +++ b/pkgs/patches-linux-5.15/781-v5.17-2-net-dsa-consolidate-phylink-creation.patch @@ -0,0 +1,158 @@ +From b4668037f74e9f105675fd4dc7da468702d3ea18 Mon Sep 17 00:00:00 2001 +From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk> +Date: Tue, 30 Nov 2021 13:09:55 +0000 +Subject: [PATCH 2/6] net: dsa: consolidate phylink creation +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +The code in port.c and slave.c creating the phylink instance is very +similar - let's consolidate this into a single function. + +Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk> +Reviewed-by: Marek Behún <kabel@kernel.org> +Reviewed-by: Andrew Lunn <andrew@lunn.ch> +Signed-off-by: Jakub Kicinski <kuba@kernel.org> +--- + net/dsa/dsa_priv.h | 2 +- + net/dsa/port.c | 44 ++++++++++++++++++++++++++++---------------- + net/dsa/slave.c | 19 +++---------------- + 3 files changed, 32 insertions(+), 33 deletions(-) + +diff --git a/net/dsa/dsa_priv.h b/net/dsa/dsa_priv.h +index f0963d85b134..4a18e874779b 100644 +--- a/net/dsa/dsa_priv.h ++++ b/net/dsa/dsa_priv.h +@@ -267,13 +267,13 @@ int dsa_port_mrp_add_ring_role(const struct dsa_port *dp, + const struct switchdev_obj_ring_role_mrp *mrp); + int dsa_port_mrp_del_ring_role(const struct dsa_port *dp, + const struct switchdev_obj_ring_role_mrp *mrp); ++int dsa_port_phylink_create(struct dsa_port *dp); + int dsa_port_link_register_of(struct dsa_port *dp); + void dsa_port_link_unregister_of(struct dsa_port *dp); + int dsa_port_hsr_join(struct dsa_port *dp, struct net_device *hsr); + void dsa_port_hsr_leave(struct dsa_port *dp, struct net_device *hsr); + int dsa_port_tag_8021q_vlan_add(struct dsa_port *dp, u16 vid, bool broadcast); + void dsa_port_tag_8021q_vlan_del(struct dsa_port *dp, u16 vid, bool broadcast); +-extern const struct phylink_mac_ops dsa_port_phylink_mac_ops; + + static inline bool dsa_port_offloads_bridge_port(struct dsa_port *dp, + const struct net_device *dev) +diff --git a/net/dsa/port.c b/net/dsa/port.c +index d1502403aeb8..4d227c503837 100644 +--- a/net/dsa/port.c ++++ b/net/dsa/port.c +@@ -1076,7 +1076,7 @@ static void dsa_port_phylink_mac_link_up(struct phylink_config *config, + speed, duplex, tx_pause, rx_pause); + } + +-const struct phylink_mac_ops dsa_port_phylink_mac_ops = { ++static const struct phylink_mac_ops dsa_port_phylink_mac_ops = { + .validate = dsa_port_phylink_validate, + .mac_pcs_get_state = dsa_port_phylink_mac_pcs_get_state, + .mac_config = dsa_port_phylink_mac_config, +@@ -1085,6 +1085,30 @@ const struct phylink_mac_ops dsa_port_phylink_mac_ops = { + .mac_link_up = dsa_port_phylink_mac_link_up, + }; + ++int dsa_port_phylink_create(struct dsa_port *dp) ++{ ++ struct dsa_switch *ds = dp->ds; ++ phy_interface_t mode; ++ int err; ++ ++ err = of_get_phy_mode(dp->dn, &mode); ++ if (err) ++ mode = PHY_INTERFACE_MODE_NA; ++ ++ if (ds->ops->phylink_get_interfaces) ++ ds->ops->phylink_get_interfaces(ds, dp->index, ++ dp->pl_config.supported_interfaces); ++ ++ dp->pl = phylink_create(&dp->pl_config, of_fwnode_handle(dp->dn), ++ mode, &dsa_port_phylink_mac_ops); ++ if (IS_ERR(dp->pl)) { ++ pr_err("error creating PHYLINK: %ld\n", PTR_ERR(dp->pl)); ++ return PTR_ERR(dp->pl); ++ } ++ ++ return 0; ++} ++ + static int dsa_port_setup_phy_of(struct dsa_port *dp, bool enable) + { + struct dsa_switch *ds = dp->ds; +@@ -1161,27 +1185,15 @@ static int dsa_port_phylink_register(struct dsa_port *dp) + { + struct dsa_switch *ds = dp->ds; + struct device_node *port_dn = dp->dn; +- phy_interface_t mode; + int err; + +- err = of_get_phy_mode(port_dn, &mode); +- if (err) +- mode = PHY_INTERFACE_MODE_NA; +- + dp->pl_config.dev = ds->dev; + dp->pl_config.type = PHYLINK_DEV; + dp->pl_config.pcs_poll = ds->pcs_poll; + +- if (ds->ops->phylink_get_interfaces) +- ds->ops->phylink_get_interfaces(ds, dp->index, +- dp->pl_config.supported_interfaces); +- +- dp->pl = phylink_create(&dp->pl_config, of_fwnode_handle(port_dn), +- mode, &dsa_port_phylink_mac_ops); +- if (IS_ERR(dp->pl)) { +- pr_err("error creating PHYLINK: %ld\n", PTR_ERR(dp->pl)); +- return PTR_ERR(dp->pl); +- } ++ err = dsa_port_phylink_create(dp); ++ if (err) ++ return err; + + err = phylink_of_phy_connect(dp->pl, port_dn, 0); + if (err && err != -ENODEV) { +diff --git a/net/dsa/slave.c b/net/dsa/slave.c +index 525140370950..0520b4a2d898 100644 +--- a/net/dsa/slave.c ++++ b/net/dsa/slave.c +@@ -1817,14 +1817,9 @@ static int dsa_slave_phy_setup(struct net_device *slave_dev) + struct dsa_port *dp = dsa_slave_to_port(slave_dev); + struct device_node *port_dn = dp->dn; + struct dsa_switch *ds = dp->ds; +- phy_interface_t mode; + u32 phy_flags = 0; + int ret; + +- ret = of_get_phy_mode(port_dn, &mode); +- if (ret) +- mode = PHY_INTERFACE_MODE_NA; +- + dp->pl_config.dev = &slave_dev->dev; + dp->pl_config.type = PHYLINK_NETDEV; + +@@ -1837,17 +1832,9 @@ static int dsa_slave_phy_setup(struct net_device *slave_dev) + dp->pl_config.poll_fixed_state = true; + } + +- if (ds->ops->phylink_get_interfaces) +- ds->ops->phylink_get_interfaces(ds, dp->index, +- dp->pl_config.supported_interfaces); +- +- dp->pl = phylink_create(&dp->pl_config, of_fwnode_handle(port_dn), mode, +- &dsa_port_phylink_mac_ops); +- if (IS_ERR(dp->pl)) { +- netdev_err(slave_dev, +- "error creating PHYLINK: %ld\n", PTR_ERR(dp->pl)); +- return PTR_ERR(dp->pl); +- } ++ ret = dsa_port_phylink_create(dp); ++ if (ret) ++ return ret; + + if (ds->ops->get_phy_flags) + phy_flags = ds->ops->get_phy_flags(ds, dp->index); +-- +2.35.1 + diff --git a/pkgs/patches-linux-5.15/781-v5.17-3-net-dsa-replace-phylink_get_interfaces-with-phylink_.patch b/pkgs/patches-linux-5.15/781-v5.17-3-net-dsa-replace-phylink_get_interfaces-with-phylink_.patch new file mode 100644 index 0000000..d2f1bd8 --- /dev/null +++ b/pkgs/patches-linux-5.15/781-v5.17-3-net-dsa-replace-phylink_get_interfaces-with-phylink_.patch @@ -0,0 +1,58 @@ +From 31509e304cd5cbdeaecf12cb2de78b6ffe2ac022 Mon Sep 17 00:00:00 2001 +From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk> +Date: Tue, 30 Nov 2021 13:10:01 +0000 +Subject: [PATCH 3/6] net: dsa: replace phylink_get_interfaces() with + phylink_get_caps() +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Phylink needs slightly more information than phylink_get_interfaces() +allows us to get from the DSA drivers - we need the MAC capabilities. +Replace the phylink_get_interfaces() method with phylink_get_caps() to +allow DSA drivers to fill in the phylink_config MAC capabilities field +as well. + +Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk> +Reviewed-by: Marek Behún <kabel@kernel.org> +Reviewed-by: Andrew Lunn <andrew@lunn.ch> +Signed-off-by: Jakub Kicinski <kuba@kernel.org> +--- + include/net/dsa.h | 4 ++-- + net/dsa/port.c | 5 ++--- + 2 files changed, 4 insertions(+), 5 deletions(-) + +diff --git a/include/net/dsa.h b/include/net/dsa.h +index 77d0d9a8e2ab..1b130566ae3a 100644 +--- a/include/net/dsa.h ++++ b/include/net/dsa.h +@@ -636,8 +636,8 @@ struct dsa_switch_ops { + /* + * PHYLINK integration + */ +- void (*phylink_get_interfaces)(struct dsa_switch *ds, int port, +- unsigned long *supported_interfaces); ++ void (*phylink_get_caps)(struct dsa_switch *ds, int port, ++ struct phylink_config *config); + void (*phylink_validate)(struct dsa_switch *ds, int port, + unsigned long *supported, + struct phylink_link_state *state); +diff --git a/net/dsa/port.c b/net/dsa/port.c +index 4d227c503837..eed1b5397dc7 100644 +--- a/net/dsa/port.c ++++ b/net/dsa/port.c +@@ -1095,9 +1095,8 @@ int dsa_port_phylink_create(struct dsa_port *dp) + if (err) + mode = PHY_INTERFACE_MODE_NA; + +- if (ds->ops->phylink_get_interfaces) +- ds->ops->phylink_get_interfaces(ds, dp->index, +- dp->pl_config.supported_interfaces); ++ if (ds->ops->phylink_get_caps) ++ ds->ops->phylink_get_caps(ds, dp->index, &dp->pl_config); + + dp->pl = phylink_create(&dp->pl_config, of_fwnode_handle(dp->dn), + mode, &dsa_port_phylink_mac_ops); +-- +2.35.1 + diff --git a/pkgs/patches-linux-5.15/781-v5.17-4-net-dsa-support-use-of-phylink_generic_validate.patch b/pkgs/patches-linux-5.15/781-v5.17-4-net-dsa-support-use-of-phylink_generic_validate.patch new file mode 100644 index 0000000..6720e55 --- /dev/null +++ b/pkgs/patches-linux-5.15/781-v5.17-4-net-dsa-support-use-of-phylink_generic_validate.patch @@ -0,0 +1,46 @@ +From 3943c373f8848128218a8cceba63511ffcc09a9d Mon Sep 17 00:00:00 2001 +From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk> +Date: Tue, 30 Nov 2021 13:10:06 +0000 +Subject: [PATCH 4/6] net: dsa: support use of phylink_generic_validate() +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Support the use of phylink_generic_validate() when there is no +phylink_validate method given in the DSA switch operations and +mac_capabilities have been set in the phylink_config structure by the +DSA switch driver. + +This gives DSA switch drivers the option to use this if they provide +the supported_interfaces and mac_capabilities, while still giving them +an option to override the default implementation if necessary. + +Reviewed-by: Vladimir Oltean <vladimir.oltean@nxp.com> +Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk> +Reviewed-by: Marek Behún <kabel@kernel.org> +Reviewed-by: Andrew Lunn <andrew@lunn.ch> +Signed-off-by: Jakub Kicinski <kuba@kernel.org> +--- + net/dsa/port.c | 5 ++++- + 1 file changed, 4 insertions(+), 1 deletion(-) + +diff --git a/net/dsa/port.c b/net/dsa/port.c +index eed1b5397dc7..371d25606546 100644 +--- a/net/dsa/port.c ++++ b/net/dsa/port.c +@@ -985,8 +985,11 @@ static void dsa_port_phylink_validate(struct phylink_config *config, + struct dsa_port *dp = container_of(config, struct dsa_port, pl_config); + struct dsa_switch *ds = dp->ds; + +- if (!ds->ops->phylink_validate) ++ if (!ds->ops->phylink_validate) { ++ if (config->mac_capabilities) ++ phylink_generic_validate(config, supported, state); + return; ++ } + + ds->ops->phylink_validate(ds, dp->index, supported, state); + } +-- +2.35.1 + diff --git a/pkgs/patches-linux-5.15/781-v5.17-5-net-dsa-hellcreek-convert-to-phylink_generic_validat.patch b/pkgs/patches-linux-5.15/781-v5.17-5-net-dsa-hellcreek-convert-to-phylink_generic_validat.patch new file mode 100644 index 0000000..9c72c3e --- /dev/null +++ b/pkgs/patches-linux-5.15/781-v5.17-5-net-dsa-hellcreek-convert-to-phylink_generic_validat.patch @@ -0,0 +1,78 @@ +From 7e0b9a29934d228bec9a133f504f283903333029 Mon Sep 17 00:00:00 2001 +From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk> +Date: Tue, 30 Nov 2021 13:10:11 +0000 +Subject: [PATCH 5/6] net: dsa: hellcreek: convert to + phylink_generic_validate() + +Populate the supported interfaces and MAC capabilities for the +hellcreek DSA switch and remove the old validate implementation to +allow DSA to use phylink_generic_validate() for this switch driver. + +The switch actually only supports MII and RGMII, but as phylib defaults +to GMII, we need to include this interface mode to keep existing DT +working. + +Reviewed-by: Kurt Kanzenbach <kurt@linutronix.de> +Tested-by: Kurt Kanzenbach <kurt@linutronix.de> +Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk> +Signed-off-by: Jakub Kicinski <kuba@kernel.org> +--- + drivers/net/dsa/hirschmann/hellcreek.c | 24 +++++++++++++----------- + 1 file changed, 13 insertions(+), 11 deletions(-) + +diff --git a/drivers/net/dsa/hirschmann/hellcreek.c b/drivers/net/dsa/hirschmann/hellcreek.c +index b2bab460d2e9..a6ae2246844c 100644 +--- a/drivers/net/dsa/hirschmann/hellcreek.c ++++ b/drivers/net/dsa/hirschmann/hellcreek.c +@@ -1457,14 +1457,19 @@ static void hellcreek_teardown(struct dsa_switch *ds) + dsa_devlink_resources_unregister(ds); + } + +-static void hellcreek_phylink_validate(struct dsa_switch *ds, int port, +- unsigned long *supported, +- struct phylink_link_state *state) ++static void hellcreek_phylink_get_caps(struct dsa_switch *ds, int port, ++ struct phylink_config *config) + { +- __ETHTOOL_DECLARE_LINK_MODE_MASK(mask) = { 0, }; + struct hellcreek *hellcreek = ds->priv; + +- dev_dbg(hellcreek->dev, "Phylink validate for port %d\n", port); ++ __set_bit(PHY_INTERFACE_MODE_MII, config->supported_interfaces); ++ __set_bit(PHY_INTERFACE_MODE_RGMII, config->supported_interfaces); ++ ++ /* Include GMII - the hardware does not support this interface ++ * mode, but it's the default interface mode for phylib, so we ++ * need it for compatibility with existing DT. ++ */ ++ __set_bit(PHY_INTERFACE_MODE_GMII, config->supported_interfaces); + + /* The MAC settings are a hardware configuration option and cannot be + * changed at run time or by strapping. Therefore the attached PHYs +@@ -1472,12 +1477,9 @@ static void hellcreek_phylink_validate(struct dsa_switch *ds, int port, + * by the hardware. + */ + if (hellcreek->pdata->is_100_mbits) +- phylink_set(mask, 100baseT_Full); ++ config->mac_capabilities = MAC_100FD; + else +- phylink_set(mask, 1000baseT_Full); +- +- linkmode_and(supported, supported, mask); +- linkmode_and(state->advertising, state->advertising, mask); ++ config->mac_capabilities = MAC_1000FD; + } + + static int +@@ -1828,7 +1830,7 @@ static const struct dsa_switch_ops hellcreek_ds_ops = { + .get_strings = hellcreek_get_strings, + .get_tag_protocol = hellcreek_get_tag_protocol, + .get_ts_info = hellcreek_get_ts_info, +- .phylink_validate = hellcreek_phylink_validate, ++ .phylink_get_caps = hellcreek_phylink_get_caps, + .port_bridge_flags = hellcreek_bridge_flags, + .port_bridge_join = hellcreek_port_bridge_join, + .port_bridge_leave = hellcreek_port_bridge_leave, +-- +2.35.1 + diff --git a/pkgs/patches-linux-5.15/781-v5.17-6-net-dsa-lantiq-convert-to-phylink_generic_validate.patch b/pkgs/patches-linux-5.15/781-v5.17-6-net-dsa-lantiq-convert-to-phylink_generic_validate.patch new file mode 100644 index 0000000..9473bdf --- /dev/null +++ b/pkgs/patches-linux-5.15/781-v5.17-6-net-dsa-lantiq-convert-to-phylink_generic_validate.patch @@ -0,0 +1,196 @@ +From 7a9dfdfe2fdd746541377f395bf0c0d54ea56ad5 Mon Sep 17 00:00:00 2001 +From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk> +Date: Tue, 30 Nov 2021 13:10:16 +0000 +Subject: [PATCH 6/6] net: dsa: lantiq: convert to phylink_generic_validate() + +Populate the supported interfaces and MAC capabilities for the Lantiq +DSA switches and remove the old validate implementation to allow DSA to +use phylink_generic_validate() for this switch driver. + +The exclusion of Gigabit linkmodes for MII, Reverse MII and Reduced MII +links is handled within phylink_generic_validate() in phylink, so there +is no need to make them conditional on the interface mode in the driver. + +Reviewed-by: Hauke Mehrtens <hauke@hauke-m.de> +Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk> +Signed-off-by: Jakub Kicinski <kuba@kernel.org> +--- + drivers/net/dsa/lantiq_gswip.c | 120 +++++++++++---------------------- + 1 file changed, 38 insertions(+), 82 deletions(-) + +diff --git a/drivers/net/dsa/lantiq_gswip.c b/drivers/net/dsa/lantiq_gswip.c +index 4599e958fa05..de9489063824 100644 +--- a/drivers/net/dsa/lantiq_gswip.c ++++ b/drivers/net/dsa/lantiq_gswip.c +@@ -1426,114 +1426,70 @@ static int gswip_port_fdb_dump(struct dsa_switch *ds, int port, + return 0; + } + +-static void gswip_phylink_set_capab(unsigned long *supported, +- struct phylink_link_state *state) +-{ +- __ETHTOOL_DECLARE_LINK_MODE_MASK(mask) = { 0, }; +- +- /* Allow all the expected bits */ +- phylink_set(mask, Autoneg); +- phylink_set_port_modes(mask); +- phylink_set(mask, Pause); +- phylink_set(mask, Asym_Pause); +- +- /* With the exclusion of MII, Reverse MII and Reduced MII, we +- * support Gigabit, including Half duplex +- */ +- if (state->interface != PHY_INTERFACE_MODE_MII && +- state->interface != PHY_INTERFACE_MODE_REVMII && +- state->interface != PHY_INTERFACE_MODE_RMII) { +- phylink_set(mask, 1000baseT_Full); +- phylink_set(mask, 1000baseT_Half); +- } +- +- phylink_set(mask, 10baseT_Half); +- phylink_set(mask, 10baseT_Full); +- phylink_set(mask, 100baseT_Half); +- phylink_set(mask, 100baseT_Full); +- +- linkmode_and(supported, supported, mask); +- linkmode_and(state->advertising, state->advertising, mask); +-} +- +-static void gswip_xrx200_phylink_validate(struct dsa_switch *ds, int port, +- unsigned long *supported, +- struct phylink_link_state *state) ++static void gswip_xrx200_phylink_get_caps(struct dsa_switch *ds, int port, ++ struct phylink_config *config) + { + switch (port) { + case 0: + case 1: +- if (!phy_interface_mode_is_rgmii(state->interface) && +- state->interface != PHY_INTERFACE_MODE_MII && +- state->interface != PHY_INTERFACE_MODE_REVMII && +- state->interface != PHY_INTERFACE_MODE_RMII) +- goto unsupported; ++ phy_interface_set_rgmii(config->supported_interfaces); ++ __set_bit(PHY_INTERFACE_MODE_MII, ++ config->supported_interfaces); ++ __set_bit(PHY_INTERFACE_MODE_REVMII, ++ config->supported_interfaces); ++ __set_bit(PHY_INTERFACE_MODE_RMII, ++ config->supported_interfaces); + break; ++ + case 2: + case 3: + case 4: +- if (state->interface != PHY_INTERFACE_MODE_INTERNAL) +- goto unsupported; ++ __set_bit(PHY_INTERFACE_MODE_INTERNAL, ++ config->supported_interfaces); + break; ++ + case 5: +- if (!phy_interface_mode_is_rgmii(state->interface) && +- state->interface != PHY_INTERFACE_MODE_INTERNAL) +- goto unsupported; ++ phy_interface_set_rgmii(config->supported_interfaces); ++ __set_bit(PHY_INTERFACE_MODE_INTERNAL, ++ config->supported_interfaces); + break; +- default: +- linkmode_zero(supported); +- dev_err(ds->dev, "Unsupported port: %i\n", port); +- return; + } + +- gswip_phylink_set_capab(supported, state); +- +- return; +- +-unsupported: +- linkmode_zero(supported); +- dev_err(ds->dev, "Unsupported interface '%s' for port %d\n", +- phy_modes(state->interface), port); ++ config->mac_capabilities = MAC_ASYM_PAUSE | MAC_SYM_PAUSE | ++ MAC_10 | MAC_100 | MAC_1000; + } + +-static void gswip_xrx300_phylink_validate(struct dsa_switch *ds, int port, +- unsigned long *supported, +- struct phylink_link_state *state) ++static void gswip_xrx300_phylink_get_caps(struct dsa_switch *ds, int port, ++ struct phylink_config *config) + { + switch (port) { + case 0: +- if (!phy_interface_mode_is_rgmii(state->interface) && +- state->interface != PHY_INTERFACE_MODE_GMII && +- state->interface != PHY_INTERFACE_MODE_RMII) +- goto unsupported; ++ phy_interface_set_rgmii(config->supported_interfaces); ++ __set_bit(PHY_INTERFACE_MODE_GMII, ++ config->supported_interfaces); ++ __set_bit(PHY_INTERFACE_MODE_RMII, ++ config->supported_interfaces); + break; ++ + case 1: + case 2: + case 3: + case 4: +- if (state->interface != PHY_INTERFACE_MODE_INTERNAL) +- goto unsupported; ++ __set_bit(PHY_INTERFACE_MODE_INTERNAL, ++ config->supported_interfaces); + break; ++ + case 5: +- if (!phy_interface_mode_is_rgmii(state->interface) && +- state->interface != PHY_INTERFACE_MODE_INTERNAL && +- state->interface != PHY_INTERFACE_MODE_RMII) +- goto unsupported; ++ phy_interface_set_rgmii(config->supported_interfaces); ++ __set_bit(PHY_INTERFACE_MODE_INTERNAL, ++ config->supported_interfaces); ++ __set_bit(PHY_INTERFACE_MODE_RMII, ++ config->supported_interfaces); + break; +- default: +- linkmode_zero(supported); +- dev_err(ds->dev, "Unsupported port: %i\n", port); +- return; + } + +- gswip_phylink_set_capab(supported, state); +- +- return; +- +-unsupported: +- linkmode_zero(supported); +- dev_err(ds->dev, "Unsupported interface '%s' for port %d\n", +- phy_modes(state->interface), port); ++ config->mac_capabilities = MAC_ASYM_PAUSE | MAC_SYM_PAUSE | ++ MAC_10 | MAC_100 | MAC_1000; + } + + static void gswip_port_set_link(struct gswip_priv *priv, int port, bool link) +@@ -1812,7 +1768,7 @@ static const struct dsa_switch_ops gswip_xrx200_switch_ops = { + .port_fdb_add = gswip_port_fdb_add, + .port_fdb_del = gswip_port_fdb_del, + .port_fdb_dump = gswip_port_fdb_dump, +- .phylink_validate = gswip_xrx200_phylink_validate, ++ .phylink_get_caps = gswip_xrx200_phylink_get_caps, + .phylink_mac_config = gswip_phylink_mac_config, + .phylink_mac_link_down = gswip_phylink_mac_link_down, + .phylink_mac_link_up = gswip_phylink_mac_link_up, +@@ -1836,7 +1792,7 @@ static const struct dsa_switch_ops gswip_xrx300_switch_ops = { + .port_fdb_add = gswip_port_fdb_add, + .port_fdb_del = gswip_port_fdb_del, + .port_fdb_dump = gswip_port_fdb_dump, +- .phylink_validate = gswip_xrx300_phylink_validate, ++ .phylink_get_caps = gswip_xrx300_phylink_get_caps, + .phylink_mac_config = gswip_phylink_mac_config, + .phylink_mac_link_down = gswip_phylink_mac_link_down, + .phylink_mac_link_up = gswip_phylink_mac_link_up, +-- +2.35.1 + diff --git a/pkgs/patches-linux-5.15/782-v5.17-1-net-phylink-tidy-up-disable-bit-clearing.patch b/pkgs/patches-linux-5.15/782-v5.17-1-net-phylink-tidy-up-disable-bit-clearing.patch new file mode 100644 index 0000000..07fbe0f --- /dev/null +++ b/pkgs/patches-linux-5.15/782-v5.17-1-net-phylink-tidy-up-disable-bit-clearing.patch @@ -0,0 +1,70 @@ +From 219b507f5c0a08507c17ecf4df80eaa1ccb0f3b7 Mon Sep 17 00:00:00 2001 +From: Russell King <rmk+kernel@armlinux.org.uk> +Date: Tue, 30 Nov 2021 14:49:41 +0000 +Subject: [PATCH 1/2] net: phylink: tidy up disable bit clearing +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Tidy up the disable bit clearing where we clear a bit +and then run the link resolver. + +Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk> +Reviewed-by: Marek Behún <kabel@kernel.org> +Reviewed-by: Andrew Lunn <andrew@lunn.ch> +Link: https://lore.kernel.org/r/E1ms4Rx-00EKEc-En@rmk-PC.armlinux.org.uk +Signed-off-by: Jakub Kicinski <kuba@kernel.org> +--- + drivers/net/phy/phylink.c | 15 +++++++++------ + 1 file changed, 9 insertions(+), 6 deletions(-) + +diff --git a/drivers/net/phy/phylink.c b/drivers/net/phy/phylink.c +index 3e1d7dea616d..e95c6086d811 100644 +--- a/drivers/net/phy/phylink.c ++++ b/drivers/net/phy/phylink.c +@@ -1149,6 +1149,12 @@ static void phylink_run_resolve_and_disable(struct phylink *pl, int bit) + } + } + ++static void phylink_enable_and_run_resolve(struct phylink *pl, int bit) ++{ ++ clear_bit(bit, &pl->phylink_disable_state); ++ phylink_run_resolve(pl); ++} ++ + static void phylink_fixed_poll(struct timer_list *t) + { + struct phylink *pl = container_of(t, struct phylink, link_poll); +@@ -1636,8 +1642,7 @@ void phylink_start(struct phylink *pl) + */ + phylink_mac_initial_config(pl, true); + +- clear_bit(PHYLINK_DISABLE_STOPPED, &pl->phylink_disable_state); +- phylink_run_resolve(pl); ++ phylink_enable_and_run_resolve(pl, PHYLINK_DISABLE_STOPPED); + + if (pl->cfg_link_an_mode == MLO_AN_FIXED && pl->link_gpio) { + int irq = gpiod_to_irq(pl->link_gpio); +@@ -1777,8 +1782,7 @@ void phylink_resume(struct phylink *pl) + phylink_mac_initial_config(pl, true); + + /* Re-enable and re-resolve the link parameters */ +- clear_bit(PHYLINK_DISABLE_MAC_WOL, &pl->phylink_disable_state); +- phylink_run_resolve(pl); ++ phylink_enable_and_run_resolve(pl, PHYLINK_DISABLE_MAC_WOL); + } else { + phylink_start(pl); + } +@@ -2817,8 +2821,7 @@ static void phylink_sfp_link_up(void *upstream) + + ASSERT_RTNL(); + +- clear_bit(PHYLINK_DISABLE_LINK, &pl->phylink_disable_state); +- phylink_run_resolve(pl); ++ phylink_enable_and_run_resolve(pl, PHYLINK_DISABLE_LINK); + } + + /* The Broadcom BCM84881 in the Methode DM7052 is unable to provide a SGMII +-- +2.35.1 + diff --git a/pkgs/patches-linux-5.15/782-v5.17-2-net-mvneta-program-1ms-autonegotiation-clock-divisor.patch b/pkgs/patches-linux-5.15/782-v5.17-2-net-mvneta-program-1ms-autonegotiation-clock-divisor.patch new file mode 100644 index 0000000..d9883ca --- /dev/null +++ b/pkgs/patches-linux-5.15/782-v5.17-2-net-mvneta-program-1ms-autonegotiation-clock-divisor.patch @@ -0,0 +1,56 @@ +From 93bb797861804c03e561e72ce419f79933ce8aea Mon Sep 17 00:00:00 2001 +From: Russell King <rmk+kernel@armlinux.org.uk> +Date: Tue, 30 Nov 2021 14:54:05 +0000 +Subject: [PATCH 2/2] net: mvneta: program 1ms autonegotiation clock divisor +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Program the 1ms autonegotiation clock divisor according to the clocking +rate of neta - without this, the 1ms clock ticks at about 660us on +Armada 38x configured for 250MHz. Bring this into correct specification. + +Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk> +Reviewed-by: Marek Behún <kabel@kernel.org> +Link: https://lore.kernel.org/r/E1ms4WD-00EKLK-Ld@rmk-PC.armlinux.org.uk +Signed-off-by: Jakub Kicinski <kuba@kernel.org> +--- + drivers/net/ethernet/marvell/mvneta.c | 8 ++++++-- + 1 file changed, 6 insertions(+), 2 deletions(-) + +diff --git a/drivers/net/ethernet/marvell/mvneta.c b/drivers/net/ethernet/marvell/mvneta.c +index 191eef5c26a7..380e8791b805 100644 +--- a/drivers/net/ethernet/marvell/mvneta.c ++++ b/drivers/net/ethernet/marvell/mvneta.c +@@ -3949,7 +3949,7 @@ static void mvneta_mac_config(struct phylink_config *config, unsigned int mode, + } else if (state->interface == PHY_INTERFACE_MODE_SGMII) { + /* SGMII mode receives the state from the PHY */ + new_ctrl2 |= MVNETA_GMAC2_INBAND_AN_ENABLE; +- new_clk |= MVNETA_GMAC_1MS_CLOCK_ENABLE; ++ new_clk = MVNETA_GMAC_1MS_CLOCK_ENABLE; + new_an = (new_an & ~(MVNETA_GMAC_FORCE_LINK_DOWN | + MVNETA_GMAC_FORCE_LINK_PASS | + MVNETA_GMAC_CONFIG_MII_SPEED | +@@ -3961,7 +3961,7 @@ static void mvneta_mac_config(struct phylink_config *config, unsigned int mode, + } else { + /* 802.3z negotiation - only 1000base-X */ + new_ctrl0 |= MVNETA_GMAC0_PORT_1000BASE_X; +- new_clk |= MVNETA_GMAC_1MS_CLOCK_ENABLE; ++ new_clk = MVNETA_GMAC_1MS_CLOCK_ENABLE; + new_an = (new_an & ~(MVNETA_GMAC_FORCE_LINK_DOWN | + MVNETA_GMAC_FORCE_LINK_PASS | + MVNETA_GMAC_CONFIG_MII_SPEED)) | +@@ -3974,6 +3974,10 @@ static void mvneta_mac_config(struct phylink_config *config, unsigned int mode, + new_an |= MVNETA_GMAC_AN_FLOW_CTRL_EN; + } + ++ /* Set the 1ms clock divisor */ ++ if (new_clk == MVNETA_GMAC_1MS_CLOCK_ENABLE) ++ new_clk |= clk_get_rate(pp->clk) / 1000; ++ + /* Armada 370 documentation says we can only change the port mode + * and in-band enable when the link is down, so force it down + * while making these changes. We also do this for GMAC_CTRL2 +-- +2.35.1 + diff --git a/pkgs/patches-linux-5.15/784-v5.19-1-net-dsa-mv88e6xxx-add-mv88e6352_g2_scratch_port_has_.patch b/pkgs/patches-linux-5.15/784-v5.19-1-net-dsa-mv88e6xxx-add-mv88e6352_g2_scratch_port_has_.patch new file mode 100644 index 0000000..b18c019 --- /dev/null +++ b/pkgs/patches-linux-5.15/784-v5.19-1-net-dsa-mv88e6xxx-add-mv88e6352_g2_scratch_port_has_.patch @@ -0,0 +1,77 @@ +From 41a26921e2cb825bb1dea2620dd5d9250eb25258 Mon Sep 17 00:00:00 2001 +From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk> +Date: Thu, 3 Feb 2022 13:30:36 +0000 +Subject: [PATCH 1/6] net: dsa: mv88e6xxx: add + mv88e6352_g2_scratch_port_has_serdes() + +Read the hardware configuration to determine which port is attached +to the serdes. + +Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk> +Reviewed-by: Andrew Lunn <andrew@lunn.ch> +Signed-off-by: David S. Miller <davem@davemloft.net> +--- + drivers/net/dsa/mv88e6xxx/global2.h | 3 +++ + drivers/net/dsa/mv88e6xxx/global2_scratch.c | 28 +++++++++++++++++++++ + 2 files changed, 31 insertions(+) + +diff --git a/drivers/net/dsa/mv88e6xxx/global2.h b/drivers/net/dsa/mv88e6xxx/global2.h +index f3e27573a386..807aeaad9830 100644 +--- a/drivers/net/dsa/mv88e6xxx/global2.h ++++ b/drivers/net/dsa/mv88e6xxx/global2.h +@@ -299,6 +299,8 @@ + #define MV88E6352_G2_SCRATCH_CONFIG_DATA1_NO_CPU BIT(2) + #define MV88E6352_G2_SCRATCH_CONFIG_DATA2 0x72 + #define MV88E6352_G2_SCRATCH_CONFIG_DATA2_P0_MODE_MASK 0x3 ++#define MV88E6352_G2_SCRATCH_CONFIG_DATA3 0x73 ++#define MV88E6352_G2_SCRATCH_CONFIG_DATA3_S_SEL BIT(1) + + #define MV88E6352_G2_SCRATCH_GPIO_PCTL_GPIO 0 + #define MV88E6352_G2_SCRATCH_GPIO_PCTL_TRIG 1 +@@ -370,6 +372,7 @@ extern const struct mv88e6xxx_gpio_ops mv88e6352_gpio_ops; + + int mv88e6xxx_g2_scratch_gpio_set_smi(struct mv88e6xxx_chip *chip, + bool external); ++int mv88e6352_g2_scratch_port_has_serdes(struct mv88e6xxx_chip *chip, int port); + int mv88e6xxx_g2_atu_stats_set(struct mv88e6xxx_chip *chip, u16 kind, u16 bin); + int mv88e6xxx_g2_atu_stats_get(struct mv88e6xxx_chip *chip, u16 *stats); + +diff --git a/drivers/net/dsa/mv88e6xxx/global2_scratch.c b/drivers/net/dsa/mv88e6xxx/global2_scratch.c +index eda710062933..a9d6e40321a2 100644 +--- a/drivers/net/dsa/mv88e6xxx/global2_scratch.c ++++ b/drivers/net/dsa/mv88e6xxx/global2_scratch.c +@@ -289,3 +289,31 @@ int mv88e6xxx_g2_scratch_gpio_set_smi(struct mv88e6xxx_chip *chip, + + return mv88e6xxx_g2_scratch_write(chip, misc_cfg, val); + } ++ ++/** ++ * mv88e6352_g2_scratch_port_has_serdes - indicate if a port can have a serdes ++ * @chip: chip private data ++ * @port: port number to check for serdes ++ * ++ * Indicates whether the port may have a serdes attached according to the ++ * pin strapping. Returns negative error number, 0 if the port is not ++ * configured to have a serdes, and 1 if the port is configured to have a ++ * serdes attached. ++ */ ++int mv88e6352_g2_scratch_port_has_serdes(struct mv88e6xxx_chip *chip, int port) ++{ ++ u8 config3, p; ++ int err; ++ ++ err = mv88e6xxx_g2_scratch_read(chip, MV88E6352_G2_SCRATCH_CONFIG_DATA3, ++ &config3); ++ if (err) ++ return err; ++ ++ if (config3 & MV88E6352_G2_SCRATCH_CONFIG_DATA3_S_SEL) ++ p = 5; ++ else ++ p = 4; ++ ++ return port == p; ++} +-- +2.35.1 + diff --git a/pkgs/patches-linux-5.15/784-v5.19-2-net-dsa-mv88e6xxx-populate-supported_interfaces-and-.patch b/pkgs/patches-linux-5.15/784-v5.19-2-net-dsa-mv88e6xxx-populate-supported_interfaces-and-.patch new file mode 100644 index 0000000..7793bea --- /dev/null +++ b/pkgs/patches-linux-5.15/784-v5.19-2-net-dsa-mv88e6xxx-populate-supported_interfaces-and-.patch @@ -0,0 +1,548 @@ +From 25845d4c0a9932a9b9308139f2e5f9de580e2243 Mon Sep 17 00:00:00 2001 +From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk> +Date: Thu, 3 Feb 2022 13:30:42 +0000 +Subject: [PATCH 2/6] net: dsa: mv88e6xxx: populate supported_interfaces and + mac_capabilities +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Populate the supported interfaces and MAC capabilities for the +Marvell MV88E6xxx DSA switches in preparation to using these for the +validation functionality. + +Patch co-authored by Marek. + +Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk> +Signed-off-by: Marek Behún <kabel@kernel.org> [ fixed 6341 and 6393x ] +Reviewed-by: Andrew Lunn <andrew@lunn.ch> +Signed-off-by: David S. Miller <davem@davemloft.net> +--- + drivers/net/dsa/mv88e6xxx/chip.c | 275 ++++++++++++++++++++++++++++++- + drivers/net/dsa/mv88e6xxx/chip.h | 2 + + drivers/net/dsa/mv88e6xxx/port.h | 5 + + 3 files changed, 279 insertions(+), 3 deletions(-) + +diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/chip.c +index d2751f73246b..c86a08274ca3 100644 +--- a/drivers/net/dsa/mv88e6xxx/chip.c ++++ b/drivers/net/dsa/mv88e6xxx/chip.c +@@ -685,11 +685,251 @@ static void mv88e6xxx_validate(struct dsa_switch *ds, int port, + + linkmode_and(supported, supported, mask); + linkmode_and(state->advertising, state->advertising, mask); ++} ++ ++static const u8 mv88e6185_phy_interface_modes[] = { ++ [MV88E6185_PORT_STS_CMODE_GMII_FD] = PHY_INTERFACE_MODE_GMII, ++ [MV88E6185_PORT_STS_CMODE_MII_100_FD_PS] = PHY_INTERFACE_MODE_MII, ++ [MV88E6185_PORT_STS_CMODE_MII_100] = PHY_INTERFACE_MODE_MII, ++ [MV88E6185_PORT_STS_CMODE_MII_10] = PHY_INTERFACE_MODE_MII, ++ [MV88E6185_PORT_STS_CMODE_SERDES] = PHY_INTERFACE_MODE_1000BASEX, ++ [MV88E6185_PORT_STS_CMODE_1000BASE_X] = PHY_INTERFACE_MODE_1000BASEX, ++ [MV88E6185_PORT_STS_CMODE_PHY] = PHY_INTERFACE_MODE_SGMII, ++}; ++ ++static void mv88e6185_phylink_get_caps(struct mv88e6xxx_chip *chip, int port, ++ struct phylink_config *config) ++{ ++ u8 cmode = chip->ports[port].cmode; ++ ++ if (cmode <= ARRAY_SIZE(mv88e6185_phy_interface_modes) && ++ mv88e6185_phy_interface_modes[cmode]) ++ __set_bit(mv88e6185_phy_interface_modes[cmode], ++ config->supported_interfaces); + +- /* We can only operate at 2500BaseX or 1000BaseX. If requested +- * to advertise both, only report advertising at 2500BaseX. ++ config->mac_capabilities = MAC_SYM_PAUSE | MAC_10 | MAC_100 | ++ MAC_1000FD; ++} ++ ++static const u8 mv88e6xxx_phy_interface_modes[] = { ++ [MV88E6XXX_PORT_STS_CMODE_MII_PHY] = PHY_INTERFACE_MODE_MII, ++ [MV88E6XXX_PORT_STS_CMODE_MII] = PHY_INTERFACE_MODE_MII, ++ [MV88E6XXX_PORT_STS_CMODE_GMII] = PHY_INTERFACE_MODE_GMII, ++ [MV88E6XXX_PORT_STS_CMODE_RMII_PHY] = PHY_INTERFACE_MODE_RMII, ++ [MV88E6XXX_PORT_STS_CMODE_RMII] = PHY_INTERFACE_MODE_RMII, ++ [MV88E6XXX_PORT_STS_CMODE_100BASEX] = PHY_INTERFACE_MODE_100BASEX, ++ [MV88E6XXX_PORT_STS_CMODE_1000BASEX] = PHY_INTERFACE_MODE_1000BASEX, ++ [MV88E6XXX_PORT_STS_CMODE_SGMII] = PHY_INTERFACE_MODE_SGMII, ++ /* higher interface modes are not needed here, since ports supporting ++ * them are writable, and so the supported interfaces are filled in the ++ * corresponding .phylink_set_interfaces() implementation below + */ +- phylink_helper_basex_speed(state); ++}; ++ ++static void mv88e6xxx_translate_cmode(u8 cmode, unsigned long *supported) ++{ ++ if (cmode < ARRAY_SIZE(mv88e6xxx_phy_interface_modes) && ++ mv88e6xxx_phy_interface_modes[cmode]) ++ __set_bit(mv88e6xxx_phy_interface_modes[cmode], supported); ++ else if (cmode == MV88E6XXX_PORT_STS_CMODE_RGMII) ++ phy_interface_set_rgmii(supported); ++} ++ ++static void mv88e6250_phylink_get_caps(struct mv88e6xxx_chip *chip, int port, ++ struct phylink_config *config) ++{ ++ unsigned long *supported = config->supported_interfaces; ++ ++ /* Translate the default cmode */ ++ mv88e6xxx_translate_cmode(chip->ports[port].cmode, supported); ++ ++ config->mac_capabilities = MAC_SYM_PAUSE | MAC_10 | MAC_100; ++} ++ ++static int mv88e6352_get_port4_serdes_cmode(struct mv88e6xxx_chip *chip) ++{ ++ u16 reg, val; ++ int err; ++ ++ err = mv88e6xxx_port_read(chip, 4, MV88E6XXX_PORT_STS, ®); ++ if (err) ++ return err; ++ ++ /* If PHY_DETECT is zero, then we are not in auto-media mode */ ++ if (!(reg & MV88E6XXX_PORT_STS_PHY_DETECT)) ++ return 0xf; ++ ++ val = reg & ~MV88E6XXX_PORT_STS_PHY_DETECT; ++ err = mv88e6xxx_port_write(chip, 4, MV88E6XXX_PORT_STS, val); ++ if (err) ++ return err; ++ ++ err = mv88e6xxx_port_read(chip, 4, MV88E6XXX_PORT_STS, &val); ++ if (err) ++ return err; ++ ++ /* Restore PHY_DETECT value */ ++ err = mv88e6xxx_port_write(chip, 4, MV88E6XXX_PORT_STS, reg); ++ if (err) ++ return err; ++ ++ return val & MV88E6XXX_PORT_STS_CMODE_MASK; ++} ++ ++static void mv88e6352_phylink_get_caps(struct mv88e6xxx_chip *chip, int port, ++ struct phylink_config *config) ++{ ++ unsigned long *supported = config->supported_interfaces; ++ int err, cmode; ++ ++ /* Translate the default cmode */ ++ mv88e6xxx_translate_cmode(chip->ports[port].cmode, supported); ++ ++ config->mac_capabilities = MAC_SYM_PAUSE | MAC_10 | MAC_100 | ++ MAC_1000FD; ++ ++ /* Port 4 supports automedia if the serdes is associated with it. */ ++ if (port == 4) { ++ mv88e6xxx_reg_lock(chip); ++ err = mv88e6352_g2_scratch_port_has_serdes(chip, port); ++ if (err < 0) ++ dev_err(chip->dev, "p%d: failed to read scratch\n", ++ port); ++ if (err <= 0) ++ goto unlock; ++ ++ cmode = mv88e6352_get_port4_serdes_cmode(chip); ++ if (cmode < 0) ++ dev_err(chip->dev, "p%d: failed to read serdes cmode\n", ++ port); ++ else ++ mv88e6xxx_translate_cmode(cmode, supported); ++unlock: ++ mv88e6xxx_reg_unlock(chip); ++ } ++} ++ ++static void mv88e6341_phylink_get_caps(struct mv88e6xxx_chip *chip, int port, ++ struct phylink_config *config) ++{ ++ unsigned long *supported = config->supported_interfaces; ++ ++ /* Translate the default cmode */ ++ mv88e6xxx_translate_cmode(chip->ports[port].cmode, supported); ++ ++ /* No ethtool bits for 200Mbps */ ++ config->mac_capabilities = MAC_SYM_PAUSE | MAC_10 | MAC_100 | ++ MAC_1000FD; ++ ++ /* The C_Mode field is programmable on port 5 */ ++ if (port == 5) { ++ __set_bit(PHY_INTERFACE_MODE_SGMII, supported); ++ __set_bit(PHY_INTERFACE_MODE_1000BASEX, supported); ++ __set_bit(PHY_INTERFACE_MODE_2500BASEX, supported); ++ ++ config->mac_capabilities |= MAC_2500FD; ++ } ++} ++ ++static void mv88e6390_phylink_get_caps(struct mv88e6xxx_chip *chip, int port, ++ struct phylink_config *config) ++{ ++ unsigned long *supported = config->supported_interfaces; ++ ++ /* Translate the default cmode */ ++ mv88e6xxx_translate_cmode(chip->ports[port].cmode, supported); ++ ++ /* No ethtool bits for 200Mbps */ ++ config->mac_capabilities = MAC_SYM_PAUSE | MAC_10 | MAC_100 | ++ MAC_1000FD; ++ ++ /* The C_Mode field is programmable on ports 9 and 10 */ ++ if (port == 9 || port == 10) { ++ __set_bit(PHY_INTERFACE_MODE_SGMII, supported); ++ __set_bit(PHY_INTERFACE_MODE_1000BASEX, supported); ++ __set_bit(PHY_INTERFACE_MODE_2500BASEX, supported); ++ ++ config->mac_capabilities |= MAC_2500FD; ++ } ++} ++ ++static void mv88e6390x_phylink_get_caps(struct mv88e6xxx_chip *chip, int port, ++ struct phylink_config *config) ++{ ++ unsigned long *supported = config->supported_interfaces; ++ ++ mv88e6390_phylink_get_caps(chip, port, config); ++ ++ /* For the 6x90X, ports 2-7 can be in automedia mode. ++ * (Note that 6x90 doesn't support RXAUI nor XAUI). ++ * ++ * Port 2 can also support 1000BASE-X in automedia mode if port 9 is ++ * configured for 1000BASE-X, SGMII or 2500BASE-X. ++ * Port 3-4 can also support 1000BASE-X in automedia mode if port 9 is ++ * configured for RXAUI, 1000BASE-X, SGMII or 2500BASE-X. ++ * ++ * Port 5 can also support 1000BASE-X in automedia mode if port 10 is ++ * configured for 1000BASE-X, SGMII or 2500BASE-X. ++ * Port 6-7 can also support 1000BASE-X in automedia mode if port 10 is ++ * configured for RXAUI, 1000BASE-X, SGMII or 2500BASE-X. ++ * ++ * For now, be permissive (as the old code was) and allow 1000BASE-X ++ * on ports 2..7. ++ */ ++ if (port >= 2 && port <= 7) ++ __set_bit(PHY_INTERFACE_MODE_1000BASEX, supported); ++ ++ /* The C_Mode field can also be programmed for 10G speeds */ ++ if (port == 9 || port == 10) { ++ __set_bit(PHY_INTERFACE_MODE_XAUI, supported); ++ __set_bit(PHY_INTERFACE_MODE_RXAUI, supported); ++ ++ config->mac_capabilities |= MAC_10000FD; ++ } ++} ++ ++static void mv88e6393x_phylink_get_caps(struct mv88e6xxx_chip *chip, int port, ++ struct phylink_config *config) ++{ ++ unsigned long *supported = config->supported_interfaces; ++ bool is_6191x = ++ chip->info->prod_num == MV88E6XXX_PORT_SWITCH_ID_PROD_6191X; ++ ++ mv88e6xxx_translate_cmode(chip->ports[port].cmode, supported); ++ ++ config->mac_capabilities = MAC_SYM_PAUSE | MAC_10 | MAC_100 | ++ MAC_1000FD; ++ ++ /* The C_Mode field can be programmed for ports 0, 9 and 10 */ ++ if (port == 0 || port == 9 || port == 10) { ++ __set_bit(PHY_INTERFACE_MODE_SGMII, supported); ++ __set_bit(PHY_INTERFACE_MODE_1000BASEX, supported); ++ ++ /* 6191X supports >1G modes only on port 10 */ ++ if (!is_6191x || port == 10) { ++ __set_bit(PHY_INTERFACE_MODE_2500BASEX, supported); ++ __set_bit(PHY_INTERFACE_MODE_5GBASER, supported); ++ __set_bit(PHY_INTERFACE_MODE_10GBASER, supported); ++ /* FIXME: USXGMII is not supported yet */ ++ /* __set_bit(PHY_INTERFACE_MODE_USXGMII, supported); */ ++ ++ config->mac_capabilities |= MAC_2500FD | MAC_5000FD | ++ MAC_10000FD; ++ } ++ } ++} ++ ++static void mv88e6xxx_get_caps(struct dsa_switch *ds, int port, ++ struct phylink_config *config) ++{ ++ struct mv88e6xxx_chip *chip = ds->priv; ++ ++ chip->info->ops->phylink_get_caps(chip, port, config); ++ ++ /* Internal ports need GMII for PHYLIB */ ++ if (mv88e6xxx_phy_is_internal(ds, port)) ++ __set_bit(PHY_INTERFACE_MODE_GMII, ++ config->supported_interfaces); + } + + static void mv88e6xxx_mac_config(struct dsa_switch *ds, int port, +@@ -3599,6 +3839,7 @@ static const struct mv88e6xxx_ops mv88e6085_ops = { + .rmu_disable = mv88e6085_g1_rmu_disable, + .vtu_getnext = mv88e6352_g1_vtu_getnext, + .vtu_loadpurge = mv88e6352_g1_vtu_loadpurge, ++ .phylink_get_caps = mv88e6185_phylink_get_caps, + .phylink_validate = mv88e6185_phylink_validate, + .set_max_frame_size = mv88e6185_g1_set_max_frame_size, + }; +@@ -3633,6 +3874,7 @@ static const struct mv88e6xxx_ops mv88e6095_ops = { + .reset = mv88e6185_g1_reset, + .vtu_getnext = mv88e6185_g1_vtu_getnext, + .vtu_loadpurge = mv88e6185_g1_vtu_loadpurge, ++ .phylink_get_caps = mv88e6185_phylink_get_caps, + .phylink_validate = mv88e6185_phylink_validate, + .set_max_frame_size = mv88e6185_g1_set_max_frame_size, + }; +@@ -3680,6 +3922,7 @@ static const struct mv88e6xxx_ops mv88e6097_ops = { + .rmu_disable = mv88e6085_g1_rmu_disable, + .vtu_getnext = mv88e6352_g1_vtu_getnext, + .vtu_loadpurge = mv88e6352_g1_vtu_loadpurge, ++ .phylink_get_caps = mv88e6185_phylink_get_caps, + .phylink_validate = mv88e6185_phylink_validate, + .set_max_frame_size = mv88e6185_g1_set_max_frame_size, + }; +@@ -3717,6 +3960,7 @@ static const struct mv88e6xxx_ops mv88e6123_ops = { + .atu_set_hash = mv88e6165_g1_atu_set_hash, + .vtu_getnext = mv88e6352_g1_vtu_getnext, + .vtu_loadpurge = mv88e6352_g1_vtu_loadpurge, ++ .phylink_get_caps = mv88e6185_phylink_get_caps, + .phylink_validate = mv88e6185_phylink_validate, + .set_max_frame_size = mv88e6185_g1_set_max_frame_size, + }; +@@ -3758,6 +4002,7 @@ static const struct mv88e6xxx_ops mv88e6131_ops = { + .reset = mv88e6185_g1_reset, + .vtu_getnext = mv88e6185_g1_vtu_getnext, + .vtu_loadpurge = mv88e6185_g1_vtu_loadpurge, ++ .phylink_get_caps = mv88e6185_phylink_get_caps, + .phylink_validate = mv88e6185_phylink_validate, + }; + +@@ -3822,6 +4067,7 @@ static const struct mv88e6xxx_ops mv88e6141_ops = { + .serdes_get_stats = mv88e6390_serdes_get_stats, + .serdes_get_regs_len = mv88e6390_serdes_get_regs_len, + .serdes_get_regs = mv88e6390_serdes_get_regs, ++ .phylink_get_caps = mv88e6341_phylink_get_caps, + .phylink_validate = mv88e6341_phylink_validate, + }; + +@@ -3864,6 +4110,7 @@ static const struct mv88e6xxx_ops mv88e6161_ops = { + .vtu_loadpurge = mv88e6352_g1_vtu_loadpurge, + .avb_ops = &mv88e6165_avb_ops, + .ptp_ops = &mv88e6165_ptp_ops, ++ .phylink_get_caps = mv88e6185_phylink_get_caps, + .phylink_validate = mv88e6185_phylink_validate, + .set_max_frame_size = mv88e6185_g1_set_max_frame_size, + }; +@@ -3900,6 +4147,7 @@ static const struct mv88e6xxx_ops mv88e6165_ops = { + .vtu_loadpurge = mv88e6352_g1_vtu_loadpurge, + .avb_ops = &mv88e6165_avb_ops, + .ptp_ops = &mv88e6165_ptp_ops, ++ .phylink_get_caps = mv88e6185_phylink_get_caps, + .phylink_validate = mv88e6185_phylink_validate, + }; + +@@ -3942,6 +4190,7 @@ static const struct mv88e6xxx_ops mv88e6171_ops = { + .atu_set_hash = mv88e6165_g1_atu_set_hash, + .vtu_getnext = mv88e6352_g1_vtu_getnext, + .vtu_loadpurge = mv88e6352_g1_vtu_loadpurge, ++ .phylink_get_caps = mv88e6185_phylink_get_caps, + .phylink_validate = mv88e6185_phylink_validate, + }; + +@@ -3997,6 +4246,7 @@ static const struct mv88e6xxx_ops mv88e6172_ops = { + .serdes_get_regs_len = mv88e6352_serdes_get_regs_len, + .serdes_get_regs = mv88e6352_serdes_get_regs, + .gpio_ops = &mv88e6352_gpio_ops, ++ .phylink_get_caps = mv88e6352_phylink_get_caps, + .phylink_validate = mv88e6352_phylink_validate, + }; + +@@ -4039,6 +4289,7 @@ static const struct mv88e6xxx_ops mv88e6175_ops = { + .atu_set_hash = mv88e6165_g1_atu_set_hash, + .vtu_getnext = mv88e6352_g1_vtu_getnext, + .vtu_loadpurge = mv88e6352_g1_vtu_loadpurge, ++ .phylink_get_caps = mv88e6185_phylink_get_caps, + .phylink_validate = mv88e6185_phylink_validate, + }; + +@@ -4097,6 +4348,7 @@ static const struct mv88e6xxx_ops mv88e6176_ops = { + .serdes_get_regs_len = mv88e6352_serdes_get_regs_len, + .serdes_get_regs = mv88e6352_serdes_get_regs, + .gpio_ops = &mv88e6352_gpio_ops, ++ .phylink_get_caps = mv88e6352_phylink_get_caps, + .phylink_validate = mv88e6352_phylink_validate, + }; + +@@ -4136,6 +4388,7 @@ static const struct mv88e6xxx_ops mv88e6185_ops = { + .reset = mv88e6185_g1_reset, + .vtu_getnext = mv88e6185_g1_vtu_getnext, + .vtu_loadpurge = mv88e6185_g1_vtu_loadpurge, ++ .phylink_get_caps = mv88e6185_phylink_get_caps, + .phylink_validate = mv88e6185_phylink_validate, + .set_max_frame_size = mv88e6185_g1_set_max_frame_size, + }; +@@ -4198,6 +4451,7 @@ static const struct mv88e6xxx_ops mv88e6190_ops = { + .serdes_get_regs_len = mv88e6390_serdes_get_regs_len, + .serdes_get_regs = mv88e6390_serdes_get_regs, + .gpio_ops = &mv88e6352_gpio_ops, ++ .phylink_get_caps = mv88e6390_phylink_get_caps, + .phylink_validate = mv88e6390_phylink_validate, + }; + +@@ -4259,6 +4513,7 @@ static const struct mv88e6xxx_ops mv88e6190x_ops = { + .serdes_get_regs_len = mv88e6390_serdes_get_regs_len, + .serdes_get_regs = mv88e6390_serdes_get_regs, + .gpio_ops = &mv88e6352_gpio_ops, ++ .phylink_get_caps = mv88e6390x_phylink_get_caps, + .phylink_validate = mv88e6390x_phylink_validate, + }; + +@@ -4319,6 +4574,7 @@ static const struct mv88e6xxx_ops mv88e6191_ops = { + .serdes_get_regs = mv88e6390_serdes_get_regs, + .avb_ops = &mv88e6390_avb_ops, + .ptp_ops = &mv88e6352_ptp_ops, ++ .phylink_get_caps = mv88e6390_phylink_get_caps, + .phylink_validate = mv88e6390_phylink_validate, + }; + +@@ -4379,6 +4635,7 @@ static const struct mv88e6xxx_ops mv88e6240_ops = { + .gpio_ops = &mv88e6352_gpio_ops, + .avb_ops = &mv88e6352_avb_ops, + .ptp_ops = &mv88e6352_ptp_ops, ++ .phylink_get_caps = mv88e6352_phylink_get_caps, + .phylink_validate = mv88e6352_phylink_validate, + }; + +@@ -4419,6 +4676,7 @@ static const struct mv88e6xxx_ops mv88e6250_ops = { + .vtu_loadpurge = mv88e6185_g1_vtu_loadpurge, + .avb_ops = &mv88e6352_avb_ops, + .ptp_ops = &mv88e6250_ptp_ops, ++ .phylink_get_caps = mv88e6250_phylink_get_caps, + .phylink_validate = mv88e6065_phylink_validate, + }; + +@@ -4481,6 +4739,7 @@ static const struct mv88e6xxx_ops mv88e6290_ops = { + .gpio_ops = &mv88e6352_gpio_ops, + .avb_ops = &mv88e6390_avb_ops, + .ptp_ops = &mv88e6352_ptp_ops, ++ .phylink_get_caps = mv88e6390_phylink_get_caps, + .phylink_validate = mv88e6390_phylink_validate, + }; + +@@ -4525,6 +4784,7 @@ static const struct mv88e6xxx_ops mv88e6320_ops = { + .gpio_ops = &mv88e6352_gpio_ops, + .avb_ops = &mv88e6352_avb_ops, + .ptp_ops = &mv88e6352_ptp_ops, ++ .phylink_get_caps = mv88e6185_phylink_get_caps, + .phylink_validate = mv88e6185_phylink_validate, + }; + +@@ -4567,6 +4827,7 @@ static const struct mv88e6xxx_ops mv88e6321_ops = { + .gpio_ops = &mv88e6352_gpio_ops, + .avb_ops = &mv88e6352_avb_ops, + .ptp_ops = &mv88e6352_ptp_ops, ++ .phylink_get_caps = mv88e6185_phylink_get_caps, + .phylink_validate = mv88e6185_phylink_validate, + }; + +@@ -4633,6 +4894,7 @@ static const struct mv88e6xxx_ops mv88e6341_ops = { + .serdes_get_stats = mv88e6390_serdes_get_stats, + .serdes_get_regs_len = mv88e6390_serdes_get_regs_len, + .serdes_get_regs = mv88e6390_serdes_get_regs, ++ .phylink_get_caps = mv88e6341_phylink_get_caps, + .phylink_validate = mv88e6341_phylink_validate, + }; + +@@ -4675,6 +4937,7 @@ static const struct mv88e6xxx_ops mv88e6350_ops = { + .atu_set_hash = mv88e6165_g1_atu_set_hash, + .vtu_getnext = mv88e6352_g1_vtu_getnext, + .vtu_loadpurge = mv88e6352_g1_vtu_loadpurge, ++ .phylink_get_caps = mv88e6185_phylink_get_caps, + .phylink_validate = mv88e6185_phylink_validate, + }; + +@@ -4719,6 +4982,7 @@ static const struct mv88e6xxx_ops mv88e6351_ops = { + .vtu_loadpurge = mv88e6352_g1_vtu_loadpurge, + .avb_ops = &mv88e6352_avb_ops, + .ptp_ops = &mv88e6352_ptp_ops, ++ .phylink_get_caps = mv88e6185_phylink_get_caps, + .phylink_validate = mv88e6185_phylink_validate, + }; + +@@ -4782,6 +5046,7 @@ static const struct mv88e6xxx_ops mv88e6352_ops = { + .serdes_get_stats = mv88e6352_serdes_get_stats, + .serdes_get_regs_len = mv88e6352_serdes_get_regs_len, + .serdes_get_regs = mv88e6352_serdes_get_regs, ++ .phylink_get_caps = mv88e6352_phylink_get_caps, + .phylink_validate = mv88e6352_phylink_validate, + }; + +@@ -4847,6 +5112,7 @@ static const struct mv88e6xxx_ops mv88e6390_ops = { + .serdes_get_stats = mv88e6390_serdes_get_stats, + .serdes_get_regs_len = mv88e6390_serdes_get_regs_len, + .serdes_get_regs = mv88e6390_serdes_get_regs, ++ .phylink_get_caps = mv88e6390_phylink_get_caps, + .phylink_validate = mv88e6390_phylink_validate, + }; + +@@ -4911,6 +5177,7 @@ static const struct mv88e6xxx_ops mv88e6390x_ops = { + .gpio_ops = &mv88e6352_gpio_ops, + .avb_ops = &mv88e6390_avb_ops, + .ptp_ops = &mv88e6352_ptp_ops, ++ .phylink_get_caps = mv88e6390x_phylink_get_caps, + .phylink_validate = mv88e6390x_phylink_validate, + }; + +@@ -4975,6 +5242,7 @@ static const struct mv88e6xxx_ops mv88e6393x_ops = { + .gpio_ops = &mv88e6352_gpio_ops, + .avb_ops = &mv88e6390_avb_ops, + .ptp_ops = &mv88e6352_ptp_ops, ++ .phylink_get_caps = mv88e6393x_phylink_get_caps, + .phylink_validate = mv88e6393x_phylink_validate, + }; + +@@ -6244,6 +6512,7 @@ static const struct dsa_switch_ops mv88e6xxx_switch_ops = { + .teardown = mv88e6xxx_teardown, + .port_setup = mv88e6xxx_port_setup, + .port_teardown = mv88e6xxx_port_teardown, ++ .phylink_get_caps = mv88e6xxx_get_caps, + .phylink_validate = mv88e6xxx_validate, + .phylink_mac_link_state = mv88e6xxx_serdes_pcs_get_state, + .phylink_mac_config = mv88e6xxx_mac_config, +diff --git a/drivers/net/dsa/mv88e6xxx/chip.h b/drivers/net/dsa/mv88e6xxx/chip.h +index 8271b8aa7b71..5b0ee59b3c94 100644 +--- a/drivers/net/dsa/mv88e6xxx/chip.h ++++ b/drivers/net/dsa/mv88e6xxx/chip.h +@@ -609,6 +609,8 @@ struct mv88e6xxx_ops { + const struct mv88e6xxx_ptp_ops *ptp_ops; + + /* Phylink */ ++ void (*phylink_get_caps)(struct mv88e6xxx_chip *chip, int port, ++ struct phylink_config *config); + void (*phylink_validate)(struct mv88e6xxx_chip *chip, int port, + unsigned long *mask, + struct phylink_link_state *state); +diff --git a/drivers/net/dsa/mv88e6xxx/port.h b/drivers/net/dsa/mv88e6xxx/port.h +index 03382b66f800..ea6adfcfb42c 100644 +--- a/drivers/net/dsa/mv88e6xxx/port.h ++++ b/drivers/net/dsa/mv88e6xxx/port.h +@@ -42,6 +42,11 @@ + #define MV88E6XXX_PORT_STS_TX_PAUSED 0x0020 + #define MV88E6XXX_PORT_STS_FLOW_CTL 0x0010 + #define MV88E6XXX_PORT_STS_CMODE_MASK 0x000f ++#define MV88E6XXX_PORT_STS_CMODE_MII_PHY 0x0001 ++#define MV88E6XXX_PORT_STS_CMODE_MII 0x0002 ++#define MV88E6XXX_PORT_STS_CMODE_GMII 0x0003 ++#define MV88E6XXX_PORT_STS_CMODE_RMII_PHY 0x0004 ++#define MV88E6XXX_PORT_STS_CMODE_RMII 0x0005 + #define MV88E6XXX_PORT_STS_CMODE_RGMII 0x0007 + #define MV88E6XXX_PORT_STS_CMODE_100BASEX 0x0008 + #define MV88E6XXX_PORT_STS_CMODE_1000BASEX 0x0009 +-- +2.35.1 + diff --git a/pkgs/patches-linux-5.15/784-v5.19-3-net-dsa-mv88e6xxx-convert-to-phylink_generic_validat.patch b/pkgs/patches-linux-5.15/784-v5.19-3-net-dsa-mv88e6xxx-convert-to-phylink_generic_validat.patch new file mode 100644 index 0000000..ca67edc --- /dev/null +++ b/pkgs/patches-linux-5.15/784-v5.19-3-net-dsa-mv88e6xxx-convert-to-phylink_generic_validat.patch @@ -0,0 +1,407 @@ +From 632d16a663154c8a02810ea266bd6cff31316f9f Mon Sep 17 00:00:00 2001 +From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk> +Date: Thu, 3 Feb 2022 13:30:47 +0000 +Subject: [PATCH 3/6] net: dsa: mv88e6xxx: convert to + phylink_generic_validate() +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Now that the mv88e6xxx chip drivers are supplying the supported +interfaces and MAC capabilities, switch the driver to use the generic +phylink validation implementation by removing our own validation +implementations. This causes DSA to call phylink_generic_validate() +on our behalf. + +Reviewed-by: Marek Behún <kabel@kernel.org> +Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk> +Signed-off-by: David S. Miller <davem@davemloft.net> +--- + drivers/net/dsa/mv88e6xxx/chip.c | 153 ------------------------------- + drivers/net/dsa/mv88e6xxx/chip.h | 3 - + 2 files changed, 156 deletions(-) + +diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/chip.c +index c86a08274ca3..7172b3e2e323 100644 +--- a/drivers/net/dsa/mv88e6xxx/chip.c ++++ b/drivers/net/dsa/mv88e6xxx/chip.c +@@ -563,130 +563,6 @@ static int mv88e6xxx_serdes_pcs_link_up(struct mv88e6xxx_chip *chip, int port, + return 0; + } + +-static void mv88e6065_phylink_validate(struct mv88e6xxx_chip *chip, int port, +- unsigned long *mask, +- struct phylink_link_state *state) +-{ +- if (!phy_interface_mode_is_8023z(state->interface)) { +- /* 10M and 100M are only supported in non-802.3z mode */ +- phylink_set(mask, 10baseT_Half); +- phylink_set(mask, 10baseT_Full); +- phylink_set(mask, 100baseT_Half); +- phylink_set(mask, 100baseT_Full); +- } +-} +- +-static void mv88e6185_phylink_validate(struct mv88e6xxx_chip *chip, int port, +- unsigned long *mask, +- struct phylink_link_state *state) +-{ +- /* FIXME: if the port is in 1000Base-X mode, then it only supports +- * 1000M FD speeds. In this case, CMODE will indicate 5. +- */ +- phylink_set(mask, 1000baseT_Full); +- phylink_set(mask, 1000baseX_Full); +- +- mv88e6065_phylink_validate(chip, port, mask, state); +-} +- +-static void mv88e6341_phylink_validate(struct mv88e6xxx_chip *chip, int port, +- unsigned long *mask, +- struct phylink_link_state *state) +-{ +- if (port >= 5) +- phylink_set(mask, 2500baseX_Full); +- +- /* No ethtool bits for 200Mbps */ +- phylink_set(mask, 1000baseT_Full); +- phylink_set(mask, 1000baseX_Full); +- +- mv88e6065_phylink_validate(chip, port, mask, state); +-} +- +-static void mv88e6352_phylink_validate(struct mv88e6xxx_chip *chip, int port, +- unsigned long *mask, +- struct phylink_link_state *state) +-{ +- /* No ethtool bits for 200Mbps */ +- phylink_set(mask, 1000baseT_Full); +- phylink_set(mask, 1000baseX_Full); +- +- mv88e6065_phylink_validate(chip, port, mask, state); +-} +- +-static void mv88e6390_phylink_validate(struct mv88e6xxx_chip *chip, int port, +- unsigned long *mask, +- struct phylink_link_state *state) +-{ +- if (port >= 9) { +- phylink_set(mask, 2500baseX_Full); +- phylink_set(mask, 2500baseT_Full); +- } +- +- /* No ethtool bits for 200Mbps */ +- phylink_set(mask, 1000baseT_Full); +- phylink_set(mask, 1000baseX_Full); +- +- mv88e6065_phylink_validate(chip, port, mask, state); +-} +- +-static void mv88e6390x_phylink_validate(struct mv88e6xxx_chip *chip, int port, +- unsigned long *mask, +- struct phylink_link_state *state) +-{ +- if (port >= 9) { +- phylink_set(mask, 10000baseT_Full); +- phylink_set(mask, 10000baseKR_Full); +- } +- +- mv88e6390_phylink_validate(chip, port, mask, state); +-} +- +-static void mv88e6393x_phylink_validate(struct mv88e6xxx_chip *chip, int port, +- unsigned long *mask, +- struct phylink_link_state *state) +-{ +- bool is_6191x = +- chip->info->prod_num == MV88E6XXX_PORT_SWITCH_ID_PROD_6191X; +- +- if (((port == 0 || port == 9) && !is_6191x) || port == 10) { +- phylink_set(mask, 10000baseT_Full); +- phylink_set(mask, 10000baseKR_Full); +- phylink_set(mask, 10000baseCR_Full); +- phylink_set(mask, 10000baseSR_Full); +- phylink_set(mask, 10000baseLR_Full); +- phylink_set(mask, 10000baseLRM_Full); +- phylink_set(mask, 10000baseER_Full); +- phylink_set(mask, 5000baseT_Full); +- phylink_set(mask, 2500baseX_Full); +- phylink_set(mask, 2500baseT_Full); +- } +- +- phylink_set(mask, 1000baseT_Full); +- phylink_set(mask, 1000baseX_Full); +- +- mv88e6065_phylink_validate(chip, port, mask, state); +-} +- +-static void mv88e6xxx_validate(struct dsa_switch *ds, int port, +- unsigned long *supported, +- struct phylink_link_state *state) +-{ +- __ETHTOOL_DECLARE_LINK_MODE_MASK(mask) = { 0, }; +- struct mv88e6xxx_chip *chip = ds->priv; +- +- /* Allow all the expected bits */ +- phylink_set(mask, Autoneg); +- phylink_set(mask, Pause); +- phylink_set_port_modes(mask); +- +- if (chip->info->ops->phylink_validate) +- chip->info->ops->phylink_validate(chip, port, mask, state); +- +- linkmode_and(supported, supported, mask); +- linkmode_and(state->advertising, state->advertising, mask); +-} +- + static const u8 mv88e6185_phy_interface_modes[] = { + [MV88E6185_PORT_STS_CMODE_GMII_FD] = PHY_INTERFACE_MODE_GMII, + [MV88E6185_PORT_STS_CMODE_MII_100_FD_PS] = PHY_INTERFACE_MODE_MII, +@@ -3840,7 +3716,6 @@ static const struct mv88e6xxx_ops mv88e6085_ops = { + .vtu_getnext = mv88e6352_g1_vtu_getnext, + .vtu_loadpurge = mv88e6352_g1_vtu_loadpurge, + .phylink_get_caps = mv88e6185_phylink_get_caps, +- .phylink_validate = mv88e6185_phylink_validate, + .set_max_frame_size = mv88e6185_g1_set_max_frame_size, + }; + +@@ -3875,7 +3750,6 @@ static const struct mv88e6xxx_ops mv88e6095_ops = { + .vtu_getnext = mv88e6185_g1_vtu_getnext, + .vtu_loadpurge = mv88e6185_g1_vtu_loadpurge, + .phylink_get_caps = mv88e6185_phylink_get_caps, +- .phylink_validate = mv88e6185_phylink_validate, + .set_max_frame_size = mv88e6185_g1_set_max_frame_size, + }; + +@@ -3923,7 +3797,6 @@ static const struct mv88e6xxx_ops mv88e6097_ops = { + .vtu_getnext = mv88e6352_g1_vtu_getnext, + .vtu_loadpurge = mv88e6352_g1_vtu_loadpurge, + .phylink_get_caps = mv88e6185_phylink_get_caps, +- .phylink_validate = mv88e6185_phylink_validate, + .set_max_frame_size = mv88e6185_g1_set_max_frame_size, + }; + +@@ -3961,7 +3834,6 @@ static const struct mv88e6xxx_ops mv88e6123_ops = { + .vtu_getnext = mv88e6352_g1_vtu_getnext, + .vtu_loadpurge = mv88e6352_g1_vtu_loadpurge, + .phylink_get_caps = mv88e6185_phylink_get_caps, +- .phylink_validate = mv88e6185_phylink_validate, + .set_max_frame_size = mv88e6185_g1_set_max_frame_size, + }; + +@@ -4003,7 +3875,6 @@ static const struct mv88e6xxx_ops mv88e6131_ops = { + .vtu_getnext = mv88e6185_g1_vtu_getnext, + .vtu_loadpurge = mv88e6185_g1_vtu_loadpurge, + .phylink_get_caps = mv88e6185_phylink_get_caps, +- .phylink_validate = mv88e6185_phylink_validate, + }; + + static const struct mv88e6xxx_ops mv88e6141_ops = { +@@ -4068,7 +3939,6 @@ static const struct mv88e6xxx_ops mv88e6141_ops = { + .serdes_get_regs_len = mv88e6390_serdes_get_regs_len, + .serdes_get_regs = mv88e6390_serdes_get_regs, + .phylink_get_caps = mv88e6341_phylink_get_caps, +- .phylink_validate = mv88e6341_phylink_validate, + }; + + static const struct mv88e6xxx_ops mv88e6161_ops = { +@@ -4111,7 +3981,6 @@ static const struct mv88e6xxx_ops mv88e6161_ops = { + .avb_ops = &mv88e6165_avb_ops, + .ptp_ops = &mv88e6165_ptp_ops, + .phylink_get_caps = mv88e6185_phylink_get_caps, +- .phylink_validate = mv88e6185_phylink_validate, + .set_max_frame_size = mv88e6185_g1_set_max_frame_size, + }; + +@@ -4148,7 +4017,6 @@ static const struct mv88e6xxx_ops mv88e6165_ops = { + .avb_ops = &mv88e6165_avb_ops, + .ptp_ops = &mv88e6165_ptp_ops, + .phylink_get_caps = mv88e6185_phylink_get_caps, +- .phylink_validate = mv88e6185_phylink_validate, + }; + + static const struct mv88e6xxx_ops mv88e6171_ops = { +@@ -4191,7 +4059,6 @@ static const struct mv88e6xxx_ops mv88e6171_ops = { + .vtu_getnext = mv88e6352_g1_vtu_getnext, + .vtu_loadpurge = mv88e6352_g1_vtu_loadpurge, + .phylink_get_caps = mv88e6185_phylink_get_caps, +- .phylink_validate = mv88e6185_phylink_validate, + }; + + static const struct mv88e6xxx_ops mv88e6172_ops = { +@@ -4247,7 +4114,6 @@ static const struct mv88e6xxx_ops mv88e6172_ops = { + .serdes_get_regs = mv88e6352_serdes_get_regs, + .gpio_ops = &mv88e6352_gpio_ops, + .phylink_get_caps = mv88e6352_phylink_get_caps, +- .phylink_validate = mv88e6352_phylink_validate, + }; + + static const struct mv88e6xxx_ops mv88e6175_ops = { +@@ -4290,7 +4156,6 @@ static const struct mv88e6xxx_ops mv88e6175_ops = { + .vtu_getnext = mv88e6352_g1_vtu_getnext, + .vtu_loadpurge = mv88e6352_g1_vtu_loadpurge, + .phylink_get_caps = mv88e6185_phylink_get_caps, +- .phylink_validate = mv88e6185_phylink_validate, + }; + + static const struct mv88e6xxx_ops mv88e6176_ops = { +@@ -4349,7 +4214,6 @@ static const struct mv88e6xxx_ops mv88e6176_ops = { + .serdes_get_regs = mv88e6352_serdes_get_regs, + .gpio_ops = &mv88e6352_gpio_ops, + .phylink_get_caps = mv88e6352_phylink_get_caps, +- .phylink_validate = mv88e6352_phylink_validate, + }; + + static const struct mv88e6xxx_ops mv88e6185_ops = { +@@ -4389,7 +4253,6 @@ static const struct mv88e6xxx_ops mv88e6185_ops = { + .vtu_getnext = mv88e6185_g1_vtu_getnext, + .vtu_loadpurge = mv88e6185_g1_vtu_loadpurge, + .phylink_get_caps = mv88e6185_phylink_get_caps, +- .phylink_validate = mv88e6185_phylink_validate, + .set_max_frame_size = mv88e6185_g1_set_max_frame_size, + }; + +@@ -4452,7 +4315,6 @@ static const struct mv88e6xxx_ops mv88e6190_ops = { + .serdes_get_regs = mv88e6390_serdes_get_regs, + .gpio_ops = &mv88e6352_gpio_ops, + .phylink_get_caps = mv88e6390_phylink_get_caps, +- .phylink_validate = mv88e6390_phylink_validate, + }; + + static const struct mv88e6xxx_ops mv88e6190x_ops = { +@@ -4514,7 +4376,6 @@ static const struct mv88e6xxx_ops mv88e6190x_ops = { + .serdes_get_regs = mv88e6390_serdes_get_regs, + .gpio_ops = &mv88e6352_gpio_ops, + .phylink_get_caps = mv88e6390x_phylink_get_caps, +- .phylink_validate = mv88e6390x_phylink_validate, + }; + + static const struct mv88e6xxx_ops mv88e6191_ops = { +@@ -4575,7 +4436,6 @@ static const struct mv88e6xxx_ops mv88e6191_ops = { + .avb_ops = &mv88e6390_avb_ops, + .ptp_ops = &mv88e6352_ptp_ops, + .phylink_get_caps = mv88e6390_phylink_get_caps, +- .phylink_validate = mv88e6390_phylink_validate, + }; + + static const struct mv88e6xxx_ops mv88e6240_ops = { +@@ -4636,7 +4496,6 @@ static const struct mv88e6xxx_ops mv88e6240_ops = { + .avb_ops = &mv88e6352_avb_ops, + .ptp_ops = &mv88e6352_ptp_ops, + .phylink_get_caps = mv88e6352_phylink_get_caps, +- .phylink_validate = mv88e6352_phylink_validate, + }; + + static const struct mv88e6xxx_ops mv88e6250_ops = { +@@ -4677,7 +4536,6 @@ static const struct mv88e6xxx_ops mv88e6250_ops = { + .avb_ops = &mv88e6352_avb_ops, + .ptp_ops = &mv88e6250_ptp_ops, + .phylink_get_caps = mv88e6250_phylink_get_caps, +- .phylink_validate = mv88e6065_phylink_validate, + }; + + static const struct mv88e6xxx_ops mv88e6290_ops = { +@@ -4740,7 +4598,6 @@ static const struct mv88e6xxx_ops mv88e6290_ops = { + .avb_ops = &mv88e6390_avb_ops, + .ptp_ops = &mv88e6352_ptp_ops, + .phylink_get_caps = mv88e6390_phylink_get_caps, +- .phylink_validate = mv88e6390_phylink_validate, + }; + + static const struct mv88e6xxx_ops mv88e6320_ops = { +@@ -4785,7 +4642,6 @@ static const struct mv88e6xxx_ops mv88e6320_ops = { + .avb_ops = &mv88e6352_avb_ops, + .ptp_ops = &mv88e6352_ptp_ops, + .phylink_get_caps = mv88e6185_phylink_get_caps, +- .phylink_validate = mv88e6185_phylink_validate, + }; + + static const struct mv88e6xxx_ops mv88e6321_ops = { +@@ -4828,7 +4684,6 @@ static const struct mv88e6xxx_ops mv88e6321_ops = { + .avb_ops = &mv88e6352_avb_ops, + .ptp_ops = &mv88e6352_ptp_ops, + .phylink_get_caps = mv88e6185_phylink_get_caps, +- .phylink_validate = mv88e6185_phylink_validate, + }; + + static const struct mv88e6xxx_ops mv88e6341_ops = { +@@ -4895,7 +4750,6 @@ static const struct mv88e6xxx_ops mv88e6341_ops = { + .serdes_get_regs_len = mv88e6390_serdes_get_regs_len, + .serdes_get_regs = mv88e6390_serdes_get_regs, + .phylink_get_caps = mv88e6341_phylink_get_caps, +- .phylink_validate = mv88e6341_phylink_validate, + }; + + static const struct mv88e6xxx_ops mv88e6350_ops = { +@@ -4938,7 +4792,6 @@ static const struct mv88e6xxx_ops mv88e6350_ops = { + .vtu_getnext = mv88e6352_g1_vtu_getnext, + .vtu_loadpurge = mv88e6352_g1_vtu_loadpurge, + .phylink_get_caps = mv88e6185_phylink_get_caps, +- .phylink_validate = mv88e6185_phylink_validate, + }; + + static const struct mv88e6xxx_ops mv88e6351_ops = { +@@ -4983,7 +4836,6 @@ static const struct mv88e6xxx_ops mv88e6351_ops = { + .avb_ops = &mv88e6352_avb_ops, + .ptp_ops = &mv88e6352_ptp_ops, + .phylink_get_caps = mv88e6185_phylink_get_caps, +- .phylink_validate = mv88e6185_phylink_validate, + }; + + static const struct mv88e6xxx_ops mv88e6352_ops = { +@@ -5047,7 +4899,6 @@ static const struct mv88e6xxx_ops mv88e6352_ops = { + .serdes_get_regs_len = mv88e6352_serdes_get_regs_len, + .serdes_get_regs = mv88e6352_serdes_get_regs, + .phylink_get_caps = mv88e6352_phylink_get_caps, +- .phylink_validate = mv88e6352_phylink_validate, + }; + + static const struct mv88e6xxx_ops mv88e6390_ops = { +@@ -5113,7 +4964,6 @@ static const struct mv88e6xxx_ops mv88e6390_ops = { + .serdes_get_regs_len = mv88e6390_serdes_get_regs_len, + .serdes_get_regs = mv88e6390_serdes_get_regs, + .phylink_get_caps = mv88e6390_phylink_get_caps, +- .phylink_validate = mv88e6390_phylink_validate, + }; + + static const struct mv88e6xxx_ops mv88e6390x_ops = { +@@ -5178,7 +5028,6 @@ static const struct mv88e6xxx_ops mv88e6390x_ops = { + .avb_ops = &mv88e6390_avb_ops, + .ptp_ops = &mv88e6352_ptp_ops, + .phylink_get_caps = mv88e6390x_phylink_get_caps, +- .phylink_validate = mv88e6390x_phylink_validate, + }; + + static const struct mv88e6xxx_ops mv88e6393x_ops = { +@@ -5243,7 +5092,6 @@ static const struct mv88e6xxx_ops mv88e6393x_ops = { + .avb_ops = &mv88e6390_avb_ops, + .ptp_ops = &mv88e6352_ptp_ops, + .phylink_get_caps = mv88e6393x_phylink_get_caps, +- .phylink_validate = mv88e6393x_phylink_validate, + }; + + static const struct mv88e6xxx_info mv88e6xxx_table[] = { +@@ -6513,7 +6361,6 @@ static const struct dsa_switch_ops mv88e6xxx_switch_ops = { + .port_setup = mv88e6xxx_port_setup, + .port_teardown = mv88e6xxx_port_teardown, + .phylink_get_caps = mv88e6xxx_get_caps, +- .phylink_validate = mv88e6xxx_validate, + .phylink_mac_link_state = mv88e6xxx_serdes_pcs_get_state, + .phylink_mac_config = mv88e6xxx_mac_config, + .phylink_mac_an_restart = mv88e6xxx_serdes_pcs_an_restart, +diff --git a/drivers/net/dsa/mv88e6xxx/chip.h b/drivers/net/dsa/mv88e6xxx/chip.h +index 5b0ee59b3c94..3a685d0cf07c 100644 +--- a/drivers/net/dsa/mv88e6xxx/chip.h ++++ b/drivers/net/dsa/mv88e6xxx/chip.h +@@ -611,9 +611,6 @@ struct mv88e6xxx_ops { + /* Phylink */ + void (*phylink_get_caps)(struct mv88e6xxx_chip *chip, int port, + struct phylink_config *config); +- void (*phylink_validate)(struct mv88e6xxx_chip *chip, int port, +- unsigned long *mask, +- struct phylink_link_state *state); + + /* Max Frame Size */ + int (*set_max_frame_size)(struct mv88e6xxx_chip *chip, int mtu); +-- +2.35.1 + diff --git a/pkgs/patches-linux-5.15/784-v5.19-4-net-dsa-mv88e6xxx-improve-88e6352-serdes-statistics-.patch b/pkgs/patches-linux-5.15/784-v5.19-4-net-dsa-mv88e6xxx-improve-88e6352-serdes-statistics-.patch new file mode 100644 index 0000000..ce63919 --- /dev/null +++ b/pkgs/patches-linux-5.15/784-v5.19-4-net-dsa-mv88e6xxx-improve-88e6352-serdes-statistics-.patch @@ -0,0 +1,125 @@ +From d84031286e44a08a3512a8e98a6f3a4e37e6c11d Mon Sep 17 00:00:00 2001 +From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk> +Date: Thu, 3 Feb 2022 13:30:52 +0000 +Subject: [PATCH 4/6] net: dsa: mv88e6xxx: improve 88e6352 serdes statistics + detection +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +The decision whether to report serdes statistics currently depends on +the cached C_Mode value for the port, read at probe time or updated by +configuration. However, port 4 can be in "automedia" mode when it is +used as a serdes port, meaning it switches between the internal PHY and +the serdes, changing the read-only C_Mode value depending on which +first gains link. Consequently, the C_Mode value read at probe does not +accurately reflect whether the port has the serdes associated with it. + +In "net: dsa: mv88e6xxx: add mv88e6352_g2_scratch_port_has_serdes()", +we added a way to read the hardware configuration to determine which +port has the serdes associated with it. Use this to determine which +port reports the serdes statistics. + +Reviewed-by: Marek Behún <kabel@kernel.org> +Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk> +Reviewed-by: Andrew Lunn <andrew@lunn.ch> +Signed-off-by: David S. Miller <davem@davemloft.net> +--- + drivers/net/dsa/mv88e6xxx/serdes.c | 43 ++++++++++++++++-------------- + 1 file changed, 23 insertions(+), 20 deletions(-) + +diff --git a/drivers/net/dsa/mv88e6xxx/serdes.c b/drivers/net/dsa/mv88e6xxx/serdes.c +index 6ae7a0ed9e0b..bde2228b674d 100644 +--- a/drivers/net/dsa/mv88e6xxx/serdes.c ++++ b/drivers/net/dsa/mv88e6xxx/serdes.c +@@ -267,14 +267,6 @@ int mv88e6352_serdes_get_lane(struct mv88e6xxx_chip *chip, int port) + return lane; + } + +-static bool mv88e6352_port_has_serdes(struct mv88e6xxx_chip *chip, int port) +-{ +- if (mv88e6xxx_serdes_get_lane(chip, port) >= 0) +- return true; +- +- return false; +-} +- + struct mv88e6352_serdes_hw_stat { + char string[ETH_GSTRING_LEN]; + int sizeof_stat; +@@ -288,20 +280,24 @@ static struct mv88e6352_serdes_hw_stat mv88e6352_serdes_hw_stats[] = { + + int mv88e6352_serdes_get_sset_count(struct mv88e6xxx_chip *chip, int port) + { +- if (mv88e6352_port_has_serdes(chip, port)) +- return ARRAY_SIZE(mv88e6352_serdes_hw_stats); ++ int err; + +- return 0; ++ err = mv88e6352_g2_scratch_port_has_serdes(chip, port); ++ if (err <= 0) ++ return err; ++ ++ return ARRAY_SIZE(mv88e6352_serdes_hw_stats); + } + + int mv88e6352_serdes_get_strings(struct mv88e6xxx_chip *chip, + int port, uint8_t *data) + { + struct mv88e6352_serdes_hw_stat *stat; +- int i; ++ int err, i; + +- if (!mv88e6352_port_has_serdes(chip, port)) +- return 0; ++ err = mv88e6352_g2_scratch_port_has_serdes(chip, port); ++ if (err <= 0) ++ return err; + + for (i = 0; i < ARRAY_SIZE(mv88e6352_serdes_hw_stats); i++) { + stat = &mv88e6352_serdes_hw_stats[i]; +@@ -343,11 +339,12 @@ int mv88e6352_serdes_get_stats(struct mv88e6xxx_chip *chip, int port, + { + struct mv88e6xxx_port *mv88e6xxx_port = &chip->ports[port]; + struct mv88e6352_serdes_hw_stat *stat; ++ int i, err; + u64 value; +- int i; + +- if (!mv88e6352_port_has_serdes(chip, port)) +- return 0; ++ err = mv88e6352_g2_scratch_port_has_serdes(chip, port); ++ if (err <= 0) ++ return err; + + BUILD_BUG_ON(ARRAY_SIZE(mv88e6352_serdes_hw_stats) > + ARRAY_SIZE(mv88e6xxx_port->serdes_stats)); +@@ -414,8 +411,13 @@ unsigned int mv88e6352_serdes_irq_mapping(struct mv88e6xxx_chip *chip, int port) + + int mv88e6352_serdes_get_regs_len(struct mv88e6xxx_chip *chip, int port) + { +- if (!mv88e6352_port_has_serdes(chip, port)) +- return 0; ++ int err; ++ ++ mv88e6xxx_reg_lock(chip); ++ err = mv88e6352_g2_scratch_port_has_serdes(chip, port); ++ mv88e6xxx_reg_unlock(chip); ++ if (err <= 0) ++ return err; + + return 32 * sizeof(u16); + } +@@ -427,7 +429,8 @@ void mv88e6352_serdes_get_regs(struct mv88e6xxx_chip *chip, int port, void *_p) + int err; + int i; + +- if (!mv88e6352_port_has_serdes(chip, port)) ++ err = mv88e6352_g2_scratch_port_has_serdes(chip, port); ++ if (err <= 0) + return; + + for (i = 0 ; i < 32; i++) { +-- +2.35.1 + diff --git a/pkgs/patches-linux-5.15/784-v5.19-5-net-dsa-mv88e6xxx-Fix-off-by-in-one-in-mv88e6185_phy.patch b/pkgs/patches-linux-5.15/784-v5.19-5-net-dsa-mv88e6xxx-Fix-off-by-in-one-in-mv88e6185_phy.patch new file mode 100644 index 0000000..7679555 --- /dev/null +++ b/pkgs/patches-linux-5.15/784-v5.19-5-net-dsa-mv88e6xxx-Fix-off-by-in-one-in-mv88e6185_phy.patch @@ -0,0 +1,33 @@ +From f0fd1787a0db7b23ec859e50af3b66d9e8cc8c56 Mon Sep 17 00:00:00 2001 +From: Dan Carpenter <dan.carpenter@oracle.com> +Date: Mon, 7 Feb 2022 11:22:53 +0300 +Subject: [PATCH 5/6] net: dsa: mv88e6xxx: Fix off by in one in + mv88e6185_phylink_get_caps() + +The <= ARRAY_SIZE() needs to be < ARRAY_SIZE() to prevent an out of +bounds error. + +Fixes: d4ebf12bcec4 ("net: dsa: mv88e6xxx: populate supported_interfaces and mac_capabilities") +Signed-off-by: Dan Carpenter <dan.carpenter@oracle.com> +Reviewed-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk> +Signed-off-by: David S. Miller <davem@davemloft.net> +--- + drivers/net/dsa/mv88e6xxx/chip.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/chip.c +index 7172b3e2e323..5bac1d54325e 100644 +--- a/drivers/net/dsa/mv88e6xxx/chip.c ++++ b/drivers/net/dsa/mv88e6xxx/chip.c +@@ -578,7 +578,7 @@ static void mv88e6185_phylink_get_caps(struct mv88e6xxx_chip *chip, int port, + { + u8 cmode = chip->ports[port].cmode; + +- if (cmode <= ARRAY_SIZE(mv88e6185_phy_interface_modes) && ++ if (cmode < ARRAY_SIZE(mv88e6185_phy_interface_modes) && + mv88e6185_phy_interface_modes[cmode]) + __set_bit(mv88e6185_phy_interface_modes[cmode], + config->supported_interfaces); +-- +2.35.1 + diff --git a/pkgs/patches-linux-5.15/784-v5.19-6-net-dsa-mv88e6xxx-Fix-validation-of-built-in-PHYs-on.patch b/pkgs/patches-linux-5.15/784-v5.19-6-net-dsa-mv88e6xxx-Fix-validation-of-built-in-PHYs-on.patch new file mode 100644 index 0000000..f3e5410 --- /dev/null +++ b/pkgs/patches-linux-5.15/784-v5.19-6-net-dsa-mv88e6xxx-Fix-validation-of-built-in-PHYs-on.patch @@ -0,0 +1,73 @@ +From d2056d3cd927b7c0e0f1a466f77f21b0f6658e7b Mon Sep 17 00:00:00 2001 +From: Tobias Waldekranz <tobias@waldekranz.com> +Date: Sun, 13 Feb 2022 19:51:54 +0100 +Subject: [PATCH 6/6] net: dsa: mv88e6xxx: Fix validation of built-in PHYs on + 6095/6097 + +These chips have 8 built-in FE PHYs and 3 SERDES interfaces that can +run at 1G. With the blamed commit, the built-in PHYs could no longer +be connected to, using an MII PHY interface mode. + +Create a separate .phylink_get_caps callback for these chips, which +takes the FE/GE split into consideration. + +Fixes: 2ee84cfefb1e ("net: dsa: mv88e6xxx: convert to phylink_generic_validate()") +Signed-off-by: Tobias Waldekranz <tobias@waldekranz.com> +Reviewed-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk> +Link: https://lore.kernel.org/r/20220213185154.3262207-1-tobias@waldekranz.com +Signed-off-by: Jakub Kicinski <kuba@kernel.org> +--- + drivers/net/dsa/mv88e6xxx/chip.c | 23 +++++++++++++++++++++-- + 1 file changed, 21 insertions(+), 2 deletions(-) + +diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/chip.c +index 5bac1d54325e..5c3a490a206e 100644 +--- a/drivers/net/dsa/mv88e6xxx/chip.c ++++ b/drivers/net/dsa/mv88e6xxx/chip.c +@@ -573,6 +573,25 @@ static const u8 mv88e6185_phy_interface_modes[] = { + [MV88E6185_PORT_STS_CMODE_PHY] = PHY_INTERFACE_MODE_SGMII, + }; + ++static void mv88e6095_phylink_get_caps(struct mv88e6xxx_chip *chip, int port, ++ struct phylink_config *config) ++{ ++ u8 cmode = chip->ports[port].cmode; ++ ++ config->mac_capabilities = MAC_SYM_PAUSE | MAC_10 | MAC_100; ++ ++ if (mv88e6xxx_phy_is_internal(chip->ds, port)) { ++ __set_bit(PHY_INTERFACE_MODE_MII, config->supported_interfaces); ++ } else { ++ if (cmode < ARRAY_SIZE(mv88e6185_phy_interface_modes) && ++ mv88e6185_phy_interface_modes[cmode]) ++ __set_bit(mv88e6185_phy_interface_modes[cmode], ++ config->supported_interfaces); ++ ++ config->mac_capabilities |= MAC_1000FD; ++ } ++} ++ + static void mv88e6185_phylink_get_caps(struct mv88e6xxx_chip *chip, int port, + struct phylink_config *config) + { +@@ -3749,7 +3768,7 @@ static const struct mv88e6xxx_ops mv88e6095_ops = { + .reset = mv88e6185_g1_reset, + .vtu_getnext = mv88e6185_g1_vtu_getnext, + .vtu_loadpurge = mv88e6185_g1_vtu_loadpurge, +- .phylink_get_caps = mv88e6185_phylink_get_caps, ++ .phylink_get_caps = mv88e6095_phylink_get_caps, + .set_max_frame_size = mv88e6185_g1_set_max_frame_size, + }; + +@@ -3796,7 +3815,7 @@ static const struct mv88e6xxx_ops mv88e6097_ops = { + .rmu_disable = mv88e6085_g1_rmu_disable, + .vtu_getnext = mv88e6352_g1_vtu_getnext, + .vtu_loadpurge = mv88e6352_g1_vtu_loadpurge, +- .phylink_get_caps = mv88e6185_phylink_get_caps, ++ .phylink_get_caps = mv88e6095_phylink_get_caps, + .set_max_frame_size = mv88e6185_g1_set_max_frame_size, + }; + +-- +2.35.1 + diff --git a/pkgs/patches-linux-5.15/800-GPIO-add-named-gpio-exports.patch b/pkgs/patches-linux-5.15/800-GPIO-add-named-gpio-exports.patch new file mode 100644 index 0000000..9fde1ea --- /dev/null +++ b/pkgs/patches-linux-5.15/800-GPIO-add-named-gpio-exports.patch @@ -0,0 +1,162 @@ +From cc809a441d8f2924f785eb863dfa6aef47a25b0b Mon Sep 17 00:00:00 2001 +From: John Crispin <blogic@openwrt.org> +Date: Tue, 12 Aug 2014 20:49:27 +0200 +Subject: [PATCH 30/36] GPIO: add named gpio exports + +Signed-off-by: John Crispin <blogic@openwrt.org> +--- a/drivers/gpio/gpiolib-of.c ++++ b/drivers/gpio/gpiolib-of.c +@@ -19,6 +19,8 @@ + #include <linux/pinctrl/pinctrl.h> + #include <linux/slab.h> + #include <linux/gpio/machine.h> ++#include <linux/init.h> ++#include <linux/platform_device.h> + + #include "gpiolib.h" + #include "gpiolib-of.h" +@@ -1059,3 +1061,72 @@ void of_gpio_dev_init(struct gpio_chip * + else + gc->of_node = gdev->dev.of_node; + } ++ ++#ifdef CONFIG_GPIO_SYSFS ++ ++static struct of_device_id gpio_export_ids[] = { ++ { .compatible = "gpio-export" }, ++ { /* sentinel */ } ++}; ++ ++static int of_gpio_export_probe(struct platform_device *pdev) ++{ ++ struct device_node *np = pdev->dev.of_node; ++ struct device_node *cnp; ++ u32 val; ++ int nb = 0; ++ ++ for_each_child_of_node(np, cnp) { ++ const char *name = NULL; ++ int gpio; ++ bool dmc; ++ int max_gpio = 1; ++ int i; ++ ++ of_property_read_string(cnp, "gpio-export,name", &name); ++ ++ if (!name) ++ max_gpio = of_gpio_count(cnp); ++ ++ for (i = 0; i < max_gpio; i++) { ++ unsigned flags = 0; ++ enum of_gpio_flags of_flags; ++ ++ gpio = of_get_gpio_flags(cnp, i, &of_flags); ++ if (!gpio_is_valid(gpio)) ++ return gpio; ++ ++ if (of_flags == OF_GPIO_ACTIVE_LOW) ++ flags |= GPIOF_ACTIVE_LOW; ++ ++ if (!of_property_read_u32(cnp, "gpio-export,output", &val)) ++ flags |= val ? GPIOF_OUT_INIT_HIGH : GPIOF_OUT_INIT_LOW; ++ else ++ flags |= GPIOF_IN; ++ ++ if (devm_gpio_request_one(&pdev->dev, gpio, flags, name ? name : of_node_full_name(np))) ++ continue; ++ ++ dmc = of_property_read_bool(cnp, "gpio-export,direction_may_change"); ++ gpio_export_with_name(gpio, dmc, name); ++ nb++; ++ } ++ } ++ ++ dev_info(&pdev->dev, "%d gpio(s) exported\n", nb); ++ ++ return 0; ++} ++ ++static struct platform_driver gpio_export_driver = { ++ .driver = { ++ .name = "gpio-export", ++ .owner = THIS_MODULE, ++ .of_match_table = of_match_ptr(gpio_export_ids), ++ }, ++ .probe = of_gpio_export_probe, ++}; ++ ++module_platform_driver(gpio_export_driver); ++ ++#endif +--- a/include/asm-generic/gpio.h ++++ b/include/asm-generic/gpio.h +@@ -125,6 +125,12 @@ static inline int gpio_export(unsigned g + return gpiod_export(gpio_to_desc(gpio), direction_may_change); + } + ++int __gpiod_export(struct gpio_desc *desc, bool direction_may_change, const char *name); ++static inline int gpio_export_with_name(unsigned gpio, bool direction_may_change, const char *name) ++{ ++ return __gpiod_export(gpio_to_desc(gpio), direction_may_change, name); ++} ++ + static inline int gpio_export_link(struct device *dev, const char *name, + unsigned gpio) + { +--- a/include/linux/gpio/consumer.h ++++ b/include/linux/gpio/consumer.h +@@ -715,6 +715,7 @@ static inline void devm_acpi_dev_remove_ + + #if IS_ENABLED(CONFIG_GPIOLIB) && IS_ENABLED(CONFIG_GPIO_SYSFS) + ++int _gpiod_export(struct gpio_desc *desc, bool direction_may_change, const char *name); + int gpiod_export(struct gpio_desc *desc, bool direction_may_change); + int gpiod_export_link(struct device *dev, const char *name, + struct gpio_desc *desc); +@@ -722,6 +723,13 @@ void gpiod_unexport(struct gpio_desc *de + + #else /* CONFIG_GPIOLIB && CONFIG_GPIO_SYSFS */ + ++static inline int _gpiod_export(struct gpio_desc *desc, ++ bool direction_may_change, ++ const char *name) ++{ ++ return -ENOSYS; ++} ++ + static inline int gpiod_export(struct gpio_desc *desc, + bool direction_may_change) + { +--- a/drivers/gpio/gpiolib-sysfs.c ++++ b/drivers/gpio/gpiolib-sysfs.c +@@ -561,7 +561,7 @@ static struct class gpio_class = { + * + * Returns zero on success, else an error. + */ +-int gpiod_export(struct gpio_desc *desc, bool direction_may_change) ++int __gpiod_export(struct gpio_desc *desc, bool direction_may_change, const char *name) + { + struct gpio_chip *chip; + struct gpio_device *gdev; +@@ -623,6 +623,8 @@ int gpiod_export(struct gpio_desc *desc, + offset = gpio_chip_hwgpio(desc); + if (chip->names && chip->names[offset]) + ioname = chip->names[offset]; ++ if (name) ++ ioname = name; + + dev = device_create_with_groups(&gpio_class, &gdev->dev, + MKDEV(0, 0), data, gpio_groups, +@@ -644,6 +646,12 @@ err_unlock: + gpiod_dbg(desc, "%s: status %d\n", __func__, status); + return status; + } ++EXPORT_SYMBOL_GPL(__gpiod_export); ++ ++int gpiod_export(struct gpio_desc *desc, bool direction_may_change) ++{ ++ return __gpiod_export(desc, direction_may_change, NULL); ++} + EXPORT_SYMBOL_GPL(gpiod_export); + + static int match_export(struct device *dev, const void *desc) diff --git a/pkgs/patches-linux-5.15/800-bcma-get-SoC-device-struct-copy-its-DMA-params-to-th.patch b/pkgs/patches-linux-5.15/800-bcma-get-SoC-device-struct-copy-its-DMA-params-to-th.patch new file mode 100644 index 0000000..478a2cb --- /dev/null +++ b/pkgs/patches-linux-5.15/800-bcma-get-SoC-device-struct-copy-its-DMA-params-to-th.patch @@ -0,0 +1,73 @@ +From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <rafal@milecki.pl> +Subject: [PATCH] bcma: get SoC device struct & copy its DMA params to the + subdevices +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +For bus devices to be fully usable it's required to set their DMA +parameters. + +For years it has been missing and remained unnoticed because of +mips_dma_alloc_coherent() silently handling the empty coherent_dma_mask. +Kernel 4.19 came with a lot of DMA changes and caused a regression on +the bcm47xx. Starting with the commit f8c55dc6e828 ("MIPS: use generic +dma noncoherent ops for simple noncoherent platforms") DMA coherent +allocations just fail. Example: +[ 1.114914] bgmac_bcma bcma0:2: Allocation of TX ring 0x200 failed +[ 1.121215] bgmac_bcma bcma0:2: Unable to alloc memory for DMA +[ 1.127626] bgmac_bcma: probe of bcma0:2 failed with error -12 +[ 1.133838] bgmac_bcma: Broadcom 47xx GBit MAC driver loaded + +This change fixes above regression in addition to the MIPS bcm47xx +commit 321c46b91550 ("MIPS: BCM47XX: Setup struct device for the SoC"). + +It also fixes another *old* GPIO regression caused by a parent pointing +to the NULL: +[ 0.157054] missing gpiochip .dev parent pointer +[ 0.157287] bcma: bus0: Error registering GPIO driver: -22 +introduced by the commit 74f4e0cc6108 ("bcma: switch GPIO portions to +use GPIOLIB_IRQCHIP"). + +Fixes: f8c55dc6e828 ("MIPS: use generic dma noncoherent ops for simple noncoherent platforms") +Fixes: 74f4e0cc6108 ("bcma: switch GPIO portions to use GPIOLIB_IRQCHIP") +Cc: linux-mips@linux-mips.org +Cc: Christoph Hellwig <hch@lst.de> +Cc: Linus Walleij <linus.walleij@linaro.org> +Signed-off-by: Rafał Miłecki <rafal@milecki.pl> +--- + +--- a/drivers/bcma/host_soc.c ++++ b/drivers/bcma/host_soc.c +@@ -191,6 +191,8 @@ int __init bcma_host_soc_init(struct bcm + struct bcma_bus *bus = &soc->bus; + int err; + ++ bus->dev = soc->dev; ++ + /* Scan bus and initialize it */ + err = bcma_bus_early_register(bus); + if (err) +--- a/drivers/bcma/main.c ++++ b/drivers/bcma/main.c +@@ -236,13 +236,17 @@ EXPORT_SYMBOL(bcma_core_irq); + + void bcma_prepare_core(struct bcma_bus *bus, struct bcma_device *core) + { +- device_initialize(&core->dev); ++ struct device *dev = &core->dev; ++ ++ device_initialize(dev); + core->dev.release = bcma_release_core_dev; + core->dev.bus = &bcma_bus_type; +- dev_set_name(&core->dev, "bcma%d:%d", bus->num, core->core_index); ++ dev_set_name(dev, "bcma%d:%d", bus->num, core->core_index); + core->dev.parent = bus->dev; +- if (bus->dev) ++ if (bus->dev) { + bcma_of_fill_device(bus->dev, core); ++ dma_coerce_mask_and_coherent(dev, bus->dev->coherent_dma_mask); ++ } + + switch (bus->hosttype) { + case BCMA_HOSTTYPE_PCI: diff --git a/pkgs/patches-linux-5.15/800-cpuidle-mvebu-indicate-failure-to-enter-deeper-sleep.patch b/pkgs/patches-linux-5.15/800-cpuidle-mvebu-indicate-failure-to-enter-deeper-sleep.patch new file mode 100644 index 0000000..29f36be --- /dev/null +++ b/pkgs/patches-linux-5.15/800-cpuidle-mvebu-indicate-failure-to-enter-deeper-sleep.patch @@ -0,0 +1,40 @@ +From c28b2d367da8a471482e6a4aa8337ab6369a80c2 Mon Sep 17 00:00:00 2001 +From: Russell King <rmk+kernel@arm.linux.org.uk> +Date: Sat, 3 Oct 2015 09:13:05 +0100 +Subject: cpuidle: mvebu: indicate failure to enter deeper sleep states + +The cpuidle ->enter method expects the return value to be the sleep +state we entered. Returning negative numbers or other codes is not +permissible since coupled CPU idle was merged. + +At least some of the mvebu_v7_cpu_suspend() implementations return the +value from cpu_suspend(), which returns zero if the CPU vectors back +into the kernel via cpu_resume() (the success case), or the non-zero +return value of the suspend actor, or one (failure cases). + +We do not want to be returning the failure case value back to CPU idle +as that indicates that we successfully entered one of the deeper idle +states. Always return zero instead, indicating that we slept for the +shortest amount of time. + +Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk> +--- + drivers/cpuidle/cpuidle-mvebu-v7.c | 6 +++++- + 1 file changed, 5 insertions(+), 1 deletion(-) + +--- a/drivers/cpuidle/cpuidle-mvebu-v7.c ++++ b/drivers/cpuidle/cpuidle-mvebu-v7.c +@@ -39,8 +39,12 @@ static int mvebu_v7_enter_idle(struct cp + ret = mvebu_v7_cpu_suspend(deepidle); + cpu_pm_exit(); + ++ /* ++ * If we failed to enter the desired state, indicate that we ++ * slept lightly. ++ */ + if (ret) +- return ret; ++ return 0; + + return index; + } diff --git a/pkgs/patches-linux-5.15/800-v5.20-0001-dt-bindings-leds-add-Broadcom-s-BCM63138-controller.patch b/pkgs/patches-linux-5.15/800-v5.20-0001-dt-bindings-leds-add-Broadcom-s-BCM63138-controller.patch new file mode 100644 index 0000000..b1072ce --- /dev/null +++ b/pkgs/patches-linux-5.15/800-v5.20-0001-dt-bindings-leds-add-Broadcom-s-BCM63138-controller.patch @@ -0,0 +1,125 @@ +From 13344f8ce8a0d98aa7f5d69ce3b47393c73a343b Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <rafal@milecki.pl> +Date: Mon, 27 Dec 2021 15:59:04 +0100 +Subject: [PATCH] dt-bindings: leds: add Broadcom's BCM63138 controller +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Broadcom used 2 LEDs hardware blocks for their BCM63xx SoCs: +1. Older one (BCM6318, BCM6328, BCM6362, BCM63268, BCM6838) +2. Newer one (BCM6848, BCM6858, BCM63138, BCM63148, BCM63381, BCM68360) + +The newer one was also later also used on BCM4908 SoC. + +Old block is already documented in the leds-bcm6328.yaml. This binding +documents the new one which uses different registers & programming. It's +first used in BCM63138 thus the binding name. + +Signed-off-by: Rafał Miłecki <rafal@milecki.pl> +Reviewed-by: Rob Herring <robh@kernel.org> +Reviewed-by: Florian Fainelli <f.fainelli@gmail.com> +Signed-off-by: Pavel Machek <pavel@ucw.cz> +--- + .../bindings/leds/leds-bcm63138.yaml | 95 +++++++++++++++++++ + 1 file changed, 95 insertions(+) + create mode 100644 Documentation/devicetree/bindings/leds/leds-bcm63138.yaml + +--- /dev/null ++++ b/Documentation/devicetree/bindings/leds/leds-bcm63138.yaml +@@ -0,0 +1,95 @@ ++# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause ++%YAML 1.2 ++--- ++$id: http://devicetree.org/schemas/leds/leds-bcm63138.yaml# ++$schema: http://devicetree.org/meta-schemas/core.yaml# ++ ++title: Broadcom's BCM63138 LEDs controller ++ ++maintainers: ++ - Rafał Miłecki <rafal@milecki.pl> ++ ++description: | ++ This LEDs controller was first used on BCM63138 and later reused on BCM4908, ++ BCM6848, BCM6858, BCM63138, BCM63148, BCM63381 and BCM68360 SoCs. ++ ++ It supports up to 32 LEDs that can be connected parallelly or serially. It ++ also includes limited support for hardware blinking. ++ ++ Binding serially connected LEDs isn't documented yet. ++ ++properties: ++ compatible: ++ oneOf: ++ - items: ++ - enum: ++ - brcm,bcm4908-leds ++ - brcm,bcm6848-leds ++ - brcm,bcm6858-leds ++ - brcm,bcm63148-leds ++ - brcm,bcm63381-leds ++ - brcm,bcm68360-leds ++ - const: brcm,bcm63138-leds ++ - const: brcm,bcm63138-leds ++ ++ reg: ++ maxItems: 1 ++ ++ "#address-cells": ++ const: 1 ++ ++ "#size-cells": ++ const: 0 ++ ++patternProperties: ++ "^led@[a-f0-9]+$": ++ type: object ++ ++ $ref: common.yaml# ++ ++ properties: ++ reg: ++ maxItems: 1 ++ description: LED pin number ++ ++ active-low: ++ type: boolean ++ description: Makes LED active low. ++ ++ required: ++ - reg ++ ++ unevaluatedProperties: false ++ ++required: ++ - reg ++ - "#address-cells" ++ - "#size-cells" ++ ++additionalProperties: false ++ ++examples: ++ - | ++ #include <dt-bindings/leds/common.h> ++ ++ leds@ff800800 { ++ compatible = "brcm,bcm4908-leds", "brcm,bcm63138-leds"; ++ reg = <0xff800800 0xdc>; ++ ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ led@0 { ++ reg = <0x0>; ++ function = LED_FUNCTION_POWER; ++ color = <LED_COLOR_ID_GREEN>; ++ default-state = "on"; ++ }; ++ ++ led@3 { ++ reg = <0x3>; ++ function = LED_FUNCTION_STATUS; ++ color = <LED_COLOR_ID_GREEN>; ++ active-low; ++ }; ++ }; diff --git a/pkgs/patches-linux-5.15/800-v5.20-0002-leds-bcm63138-add-support-for-BCM63138-controller.patch b/pkgs/patches-linux-5.15/800-v5.20-0002-leds-bcm63138-add-support-for-BCM63138-controller.patch new file mode 100644 index 0000000..3765845 --- /dev/null +++ b/pkgs/patches-linux-5.15/800-v5.20-0002-leds-bcm63138-add-support-for-BCM63138-controller.patch @@ -0,0 +1,356 @@ +From a0ba692072d89075d0a75c7ad9df31f2c1ee9a1c Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <rafal@milecki.pl> +Date: Mon, 27 Dec 2021 15:59:05 +0100 +Subject: [PATCH] leds: bcm63138: add support for BCM63138 controller +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +It's a new controller first introduced in BCM63138 SoC. Later it was +also used in BCM4908, some BCM68xx and some BCM63xxx SoCs. + +Signed-off-by: Rafał Miłecki <rafal@milecki.pl> +Reviewed-by: Florian Fainelli <f.fainelli@gmail.com> +Signed-off-by: Pavel Machek <pavel@ucw.cz> +--- + drivers/leds/blink/Kconfig | 12 ++ + drivers/leds/blink/Makefile | 1 + + drivers/leds/blink/leds-bcm63138.c | 308 +++++++++++++++++++++++++++++ + 3 files changed, 321 insertions(+) + create mode 100644 drivers/leds/blink/leds-bcm63138.c + +--- a/drivers/leds/blink/Kconfig ++++ b/drivers/leds/blink/Kconfig +@@ -1,3 +1,15 @@ ++config LEDS_BCM63138 ++ tristate "LED Support for Broadcom BCM63138 SoC" ++ depends on LEDS_CLASS ++ depends on ARCH_BCM4908 || ARCH_BCM_5301X || BCM63XX || COMPILE_TEST ++ depends on HAS_IOMEM ++ depends on OF ++ default ARCH_BCM4908 ++ help ++ This option enables support for LED controller that is part of ++ BCM63138 SoC. The same hardware block is known to be also used ++ in BCM4908, BCM6848, BCM6858, BCM63148, BCM63381 and BCM68360. ++ + config LEDS_LGM + tristate "LED support for LGM SoC series" + depends on X86 || COMPILE_TEST +--- a/drivers/leds/blink/Makefile ++++ b/drivers/leds/blink/Makefile +@@ -1,2 +1,3 @@ + # SPDX-License-Identifier: GPL-2.0 ++obj-$(CONFIG_LEDS_BCM63138) += leds-bcm63138.o + obj-$(CONFIG_LEDS_LGM) += leds-lgm-sso.o +--- /dev/null ++++ b/drivers/leds/blink/leds-bcm63138.c +@@ -0,0 +1,308 @@ ++// SPDX-License-Identifier: GPL-2.0-only ++/* ++ * Copyright (C) 2021 Rafał Miłecki <rafal@milecki.pl> ++ */ ++#include <linux/delay.h> ++#include <linux/io.h> ++#include <linux/leds.h> ++#include <linux/module.h> ++#include <linux/of.h> ++#include <linux/pinctrl/consumer.h> ++#include <linux/platform_device.h> ++#include <linux/spinlock.h> ++ ++#define BCM63138_MAX_LEDS 32 ++#define BCM63138_MAX_BRIGHTNESS 9 ++ ++#define BCM63138_LED_BITS 4 /* how many bits control a single LED */ ++#define BCM63138_LED_MASK ((1 << BCM63138_LED_BITS) - 1) /* 0xf */ ++#define BCM63138_LEDS_PER_REG (32 / BCM63138_LED_BITS) /* 8 */ ++ ++#define BCM63138_GLB_CTRL 0x00 ++#define BCM63138_GLB_CTRL_SERIAL_LED_DATA_PPOL 0x00000002 ++#define BCM63138_GLB_CTRL_SERIAL_LED_EN_POL 0x00000008 ++#define BCM63138_MASK 0x04 ++#define BCM63138_HW_LED_EN 0x08 ++#define BCM63138_SERIAL_LED_SHIFT_SEL 0x0c ++#define BCM63138_FLASH_RATE_CTRL1 0x10 ++#define BCM63138_FLASH_RATE_CTRL2 0x14 ++#define BCM63138_FLASH_RATE_CTRL3 0x18 ++#define BCM63138_FLASH_RATE_CTRL4 0x1c ++#define BCM63138_BRIGHT_CTRL1 0x20 ++#define BCM63138_BRIGHT_CTRL2 0x24 ++#define BCM63138_BRIGHT_CTRL3 0x28 ++#define BCM63138_BRIGHT_CTRL4 0x2c ++#define BCM63138_POWER_LED_CFG 0x30 ++#define BCM63138_HW_POLARITY 0xb4 ++#define BCM63138_SW_DATA 0xb8 ++#define BCM63138_SW_POLARITY 0xbc ++#define BCM63138_PARALLEL_LED_POLARITY 0xc0 ++#define BCM63138_SERIAL_LED_POLARITY 0xc4 ++#define BCM63138_HW_LED_STATUS 0xc8 ++#define BCM63138_FLASH_CTRL_STATUS 0xcc ++#define BCM63138_FLASH_BRT_CTRL 0xd0 ++#define BCM63138_FLASH_P_LED_OUT_STATUS 0xd4 ++#define BCM63138_FLASH_S_LED_OUT_STATUS 0xd8 ++ ++struct bcm63138_leds { ++ struct device *dev; ++ void __iomem *base; ++ spinlock_t lock; ++}; ++ ++struct bcm63138_led { ++ struct bcm63138_leds *leds; ++ struct led_classdev cdev; ++ u32 pin; ++ bool active_low; ++}; ++ ++/* ++ * I/O access ++ */ ++ ++static void bcm63138_leds_write(struct bcm63138_leds *leds, unsigned int reg, ++ u32 data) ++{ ++ writel(data, leds->base + reg); ++} ++ ++static unsigned long bcm63138_leds_read(struct bcm63138_leds *leds, ++ unsigned int reg) ++{ ++ return readl(leds->base + reg); ++} ++ ++static void bcm63138_leds_update_bits(struct bcm63138_leds *leds, ++ unsigned int reg, u32 mask, u32 val) ++{ ++ WARN_ON(val & ~mask); ++ ++ bcm63138_leds_write(leds, reg, (bcm63138_leds_read(leds, reg) & ~mask) | (val & mask)); ++} ++ ++/* ++ * Helpers ++ */ ++ ++static void bcm63138_leds_set_flash_rate(struct bcm63138_leds *leds, ++ struct bcm63138_led *led, ++ u8 value) ++{ ++ int reg_offset = (led->pin >> fls((BCM63138_LEDS_PER_REG - 1))) * 4; ++ int shift = (led->pin & (BCM63138_LEDS_PER_REG - 1)) * BCM63138_LED_BITS; ++ ++ bcm63138_leds_update_bits(leds, BCM63138_FLASH_RATE_CTRL1 + reg_offset, ++ BCM63138_LED_MASK << shift, value << shift); ++} ++ ++static void bcm63138_leds_set_bright(struct bcm63138_leds *leds, ++ struct bcm63138_led *led, ++ u8 value) ++{ ++ int reg_offset = (led->pin >> fls((BCM63138_LEDS_PER_REG - 1))) * 4; ++ int shift = (led->pin & (BCM63138_LEDS_PER_REG - 1)) * BCM63138_LED_BITS; ++ ++ bcm63138_leds_update_bits(leds, BCM63138_BRIGHT_CTRL1 + reg_offset, ++ BCM63138_LED_MASK << shift, value << shift); ++} ++ ++static void bcm63138_leds_enable_led(struct bcm63138_leds *leds, ++ struct bcm63138_led *led, ++ enum led_brightness value) ++{ ++ u32 bit = BIT(led->pin); ++ ++ bcm63138_leds_update_bits(leds, BCM63138_SW_DATA, bit, ++ value == LED_OFF ? 0 : bit); ++} ++ ++/* ++ * API callbacks ++ */ ++ ++static void bcm63138_leds_brightness_set(struct led_classdev *led_cdev, ++ enum led_brightness value) ++{ ++ struct bcm63138_led *led = container_of(led_cdev, struct bcm63138_led, cdev); ++ struct bcm63138_leds *leds = led->leds; ++ unsigned long flags; ++ ++ spin_lock_irqsave(&leds->lock, flags); ++ ++ bcm63138_leds_enable_led(leds, led, value); ++ if (!value) ++ bcm63138_leds_set_flash_rate(leds, led, 0); ++ else ++ bcm63138_leds_set_bright(leds, led, value); ++ ++ spin_unlock_irqrestore(&leds->lock, flags); ++} ++ ++static int bcm63138_leds_blink_set(struct led_classdev *led_cdev, ++ unsigned long *delay_on, ++ unsigned long *delay_off) ++{ ++ struct bcm63138_led *led = container_of(led_cdev, struct bcm63138_led, cdev); ++ struct bcm63138_leds *leds = led->leds; ++ unsigned long flags; ++ u8 value; ++ ++ if (!*delay_on && !*delay_off) { ++ *delay_on = 640; ++ *delay_off = 640; ++ } ++ ++ if (*delay_on != *delay_off) { ++ dev_dbg(led_cdev->dev, "Blinking at unequal delays is not supported\n"); ++ return -EINVAL; ++ } ++ ++ switch (*delay_on) { ++ case 1152 ... 1408: /* 1280 ms ± 10% */ ++ value = 0x7; ++ break; ++ case 576 ... 704: /* 640 ms ± 10% */ ++ value = 0x6; ++ break; ++ case 288 ... 352: /* 320 ms ± 10% */ ++ value = 0x5; ++ break; ++ case 126 ... 154: /* 140 ms ± 10% */ ++ value = 0x4; ++ break; ++ case 59 ... 72: /* 65 ms ± 10% */ ++ value = 0x3; ++ break; ++ default: ++ dev_dbg(led_cdev->dev, "Blinking delay value %lu is unsupported\n", ++ *delay_on); ++ return -EINVAL; ++ } ++ ++ spin_lock_irqsave(&leds->lock, flags); ++ ++ bcm63138_leds_enable_led(leds, led, BCM63138_MAX_BRIGHTNESS); ++ bcm63138_leds_set_flash_rate(leds, led, value); ++ ++ spin_unlock_irqrestore(&leds->lock, flags); ++ ++ return 0; ++} ++ ++/* ++ * LED driver ++ */ ++ ++static void bcm63138_leds_create_led(struct bcm63138_leds *leds, ++ struct device_node *np) ++{ ++ struct led_init_data init_data = { ++ .fwnode = of_fwnode_handle(np), ++ }; ++ struct device *dev = leds->dev; ++ struct bcm63138_led *led; ++ struct pinctrl *pinctrl; ++ u32 bit; ++ int err; ++ ++ led = devm_kzalloc(dev, sizeof(*led), GFP_KERNEL); ++ if (!led) { ++ dev_err(dev, "Failed to alloc LED\n"); ++ return; ++ } ++ ++ led->leds = leds; ++ ++ if (of_property_read_u32(np, "reg", &led->pin)) { ++ dev_err(dev, "Missing \"reg\" property in %pOF\n", np); ++ goto err_free; ++ } ++ ++ if (led->pin >= BCM63138_MAX_LEDS) { ++ dev_err(dev, "Invalid \"reg\" value %d\n", led->pin); ++ goto err_free; ++ } ++ ++ led->active_low = of_property_read_bool(np, "active-low"); ++ ++ led->cdev.max_brightness = BCM63138_MAX_BRIGHTNESS; ++ led->cdev.brightness_set = bcm63138_leds_brightness_set; ++ led->cdev.blink_set = bcm63138_leds_blink_set; ++ ++ err = devm_led_classdev_register_ext(dev, &led->cdev, &init_data); ++ if (err) { ++ dev_err(dev, "Failed to register LED %pOF: %d\n", np, err); ++ goto err_free; ++ } ++ ++ pinctrl = devm_pinctrl_get_select_default(led->cdev.dev); ++ if (IS_ERR(pinctrl) && PTR_ERR(pinctrl) != -ENODEV) { ++ dev_warn(led->cdev.dev, "Failed to select %pOF pinctrl: %ld\n", ++ np, PTR_ERR(pinctrl)); ++ } ++ ++ bit = BIT(led->pin); ++ bcm63138_leds_update_bits(leds, BCM63138_PARALLEL_LED_POLARITY, bit, ++ led->active_low ? 0 : bit); ++ bcm63138_leds_update_bits(leds, BCM63138_HW_LED_EN, bit, 0); ++ bcm63138_leds_set_flash_rate(leds, led, 0); ++ bcm63138_leds_enable_led(leds, led, led->cdev.brightness); ++ ++ return; ++ ++err_free: ++ devm_kfree(dev, led); ++} ++ ++static int bcm63138_leds_probe(struct platform_device *pdev) ++{ ++ struct device_node *np = dev_of_node(&pdev->dev); ++ struct device *dev = &pdev->dev; ++ struct bcm63138_leds *leds; ++ struct device_node *child; ++ ++ leds = devm_kzalloc(dev, sizeof(*leds), GFP_KERNEL); ++ if (!leds) ++ return -ENOMEM; ++ ++ leds->dev = dev; ++ ++ leds->base = devm_platform_ioremap_resource(pdev, 0); ++ if (IS_ERR(leds->base)) ++ return PTR_ERR(leds->base); ++ ++ spin_lock_init(&leds->lock); ++ ++ bcm63138_leds_write(leds, BCM63138_GLB_CTRL, ++ BCM63138_GLB_CTRL_SERIAL_LED_DATA_PPOL | ++ BCM63138_GLB_CTRL_SERIAL_LED_EN_POL); ++ bcm63138_leds_write(leds, BCM63138_HW_LED_EN, 0); ++ bcm63138_leds_write(leds, BCM63138_SERIAL_LED_POLARITY, 0); ++ bcm63138_leds_write(leds, BCM63138_PARALLEL_LED_POLARITY, 0); ++ ++ for_each_available_child_of_node(np, child) { ++ bcm63138_leds_create_led(leds, child); ++ } ++ ++ return 0; ++} ++ ++static const struct of_device_id bcm63138_leds_of_match_table[] = { ++ { .compatible = "brcm,bcm63138-leds", }, ++ { }, ++}; ++ ++static struct platform_driver bcm63138_leds_driver = { ++ .probe = bcm63138_leds_probe, ++ .driver = { ++ .name = "leds-bcm63xxx", ++ .of_match_table = bcm63138_leds_of_match_table, ++ }, ++}; ++ ++module_platform_driver(bcm63138_leds_driver); ++ ++MODULE_AUTHOR("Rafał Miłecki"); ++MODULE_LICENSE("GPL"); ++MODULE_DEVICE_TABLE(of, bcm63138_leds_of_match_table); diff --git a/pkgs/patches-linux-5.15/801-gpio-gpio-cascade-add-generic-GPIO-cascade.patch b/pkgs/patches-linux-5.15/801-gpio-gpio-cascade-add-generic-GPIO-cascade.patch new file mode 100644 index 0000000..c1e14b9 --- /dev/null +++ b/pkgs/patches-linux-5.15/801-gpio-gpio-cascade-add-generic-GPIO-cascade.patch @@ -0,0 +1,222 @@ +From fc23ea48ba52c24f201fe5ca0132ee1a3de5a70a Mon Sep 17 00:00:00 2001 +From: Mauri Sandberg <maukka@ext.kapsi.fi> +Date: Thu, 25 Mar 2021 11:48:05 +0200 +Subject: [PATCH 2/2] gpio: gpio-cascade: add generic GPIO cascade + +Adds support for building cascades of GPIO lines. That is, it allows +setups when there is one upstream line and multiple cascaded lines, out +of which one can be chosen at a time. The status of the upstream line +can be conveyed to the selected cascaded line or, vice versa, the status +of the cascaded line can be conveyed to the upstream line. + +A multiplexer is being used to select, which cascaded GPIO line is being +used at any given time. + +At the moment only input direction is supported. In future it should be +possible to add support for output direction, too. + +Signed-off-by: Mauri Sandberg <maukka@ext.kapsi.fi> +Reviewed-by: Linus Walleij <linus.walleij@linaro.org> +Reviewed-by: Andy Shevchenko <andy.shevchenko@gmail.com> +--- +v7 -> v8: + - rearrange members in struct gpio_cascade + - cosmetic changes in file header and in one function declaration + - added Reviewed-by tags by Linus and Andy +v6 -> v7: + - In Kconfig add info about module name + - adhere to new convention that allows lines longer than 80 chars + - use dev_probe_err with upstream gpio line too + - refactor for cleaner exit of probe function. +v5 -> v6: + - In Kconfig, remove dependency to OF_GPIO and select only MULTIPLEXER + - refactor code preferring one-liners + - clean up prints, removing them from success-path. + - don't explicitly set gpio_chip.of_node as it's done in the GPIO library + - use devm_gpiochip_add_data instead of gpiochip_add +v4 -> v5: + - renamed gpio-mux-input -> gpio-cascade. refactored code accordingly + here and there and changed to use new bindings and compatible string + - ambigious and vague 'pin' was rename to 'upstream_line' + - dropped Tested-by and Reviewed-by due to changes in bindings + - dropped Reported-by suggested by an automatic bot as it was not really + appropriate to begin with + - functionally it's the same as v4 +v3 -> v4: + - Changed author email + - Included Tested-by and Reviewed-by from Drew +v2 -> v3: + - use managed device resources + - update Kconfig description +v1 -> v2: + - removed .owner from platform_driver as per test bot's instruction + - added MODULE_AUTHOR, MODULE_DESCRIPTION, MODULE_LICENSE + - added gpio_mux_input_get_direction as it's recommended for all chips + - removed because this is input only chip: gpio_mux_input_set_value + - removed because they are not needed for input/output only chips: + gpio_mux_input_direction_input + gpio_mux_input_direction_output + - fixed typo in an error message + - added info message about successful registration + - removed can_sleep flag as this does not sleep while getting GPIO value + like I2C or SPI do + - Updated description in Kconfig +--- + drivers/gpio/Kconfig | 15 +++++ + drivers/gpio/Makefile | 1 + + drivers/gpio/gpio-cascade.c | 117 ++++++++++++++++++++++++++++++++++++ + 3 files changed, 133 insertions(+) + create mode 100644 drivers/gpio/gpio-cascade.c + +--- a/drivers/gpio/Kconfig ++++ b/drivers/gpio/Kconfig +@@ -1683,4 +1683,19 @@ config GPIO_VIRTIO + + endmenu + ++comment "Other GPIO expanders" ++ ++config GPIO_CASCADE ++ tristate "General GPIO cascade" ++ select MULTIPLEXER ++ help ++ Say yes here to enable support for generic GPIO cascade. ++ ++ This allows building one-to-many cascades of GPIO lines using ++ different types of multiplexers readily available. At the ++ moment only input lines are supported. ++ ++ To build the driver as a module choose 'm' and the resulting module ++ will be called 'gpio-cascade'. ++ + endif +--- a/drivers/gpio/Makefile ++++ b/drivers/gpio/Makefile +@@ -45,6 +45,7 @@ obj-$(CONFIG_GPIO_BD9571MWV) += gpio-bd + obj-$(CONFIG_GPIO_BRCMSTB) += gpio-brcmstb.o + obj-$(CONFIG_GPIO_BT8XX) += gpio-bt8xx.o + obj-$(CONFIG_GPIO_CADENCE) += gpio-cadence.o ++obj-$(CONFIG_GPIO_CASCADE) += gpio-cascade.o + obj-$(CONFIG_GPIO_CLPS711X) += gpio-clps711x.o + obj-$(CONFIG_GPIO_SNPS_CREG) += gpio-creg-snps.o + obj-$(CONFIG_GPIO_CRYSTAL_COVE) += gpio-crystalcove.o +--- /dev/null ++++ b/drivers/gpio/gpio-cascade.c +@@ -0,0 +1,117 @@ ++// SPDX-License-Identifier: GPL-2.0-only ++/* ++ * A generic GPIO cascade driver ++ * ++ * Copyright (C) 2021 Mauri Sandberg <maukka@ext.kapsi.fi> ++ * ++ * This allows building cascades of GPIO lines in a manner illustrated ++ * below: ++ * ++ * /|---- Cascaded GPIO line 0 ++ * Upstream | |---- Cascaded GPIO line 1 ++ * GPIO line ----+ | . ++ * | | . ++ * \|---- Cascaded GPIO line n ++ * ++ * A multiplexer is being used to select, which cascaded line is being ++ * addressed at any given time. ++ * ++ * At the moment only input mode is supported due to lack of means for ++ * testing output functionality. At least theoretically output should be ++ * possible with open drain constructions. ++ */ ++ ++#include <linux/module.h> ++#include <linux/slab.h> ++#include <linux/platform_device.h> ++#include <linux/mux/consumer.h> ++ ++#include <linux/gpio/consumer.h> ++#include <linux/gpio/driver.h> ++ ++struct gpio_cascade { ++ struct gpio_chip gpio_chip; ++ struct device *parent; ++ struct mux_control *mux_control; ++ struct gpio_desc *upstream_line; ++}; ++ ++static struct gpio_cascade *chip_to_cascade(struct gpio_chip *gc) ++{ ++ return container_of(gc, struct gpio_cascade, gpio_chip); ++} ++ ++static int gpio_cascade_get_direction(struct gpio_chip *gc, unsigned int offset) ++{ ++ return GPIO_LINE_DIRECTION_IN; ++} ++ ++static int gpio_cascade_get_value(struct gpio_chip *gc, unsigned int offset) ++{ ++ struct gpio_cascade *cas = chip_to_cascade(gc); ++ int ret; ++ ++ ret = mux_control_select(cas->mux_control, offset); ++ if (ret) ++ return ret; ++ ++ ret = gpiod_get_value(cas->upstream_line); ++ mux_control_deselect(cas->mux_control); ++ return ret; ++} ++ ++static int gpio_cascade_probe(struct platform_device *pdev) ++{ ++ struct device *dev = &pdev->dev; ++ struct gpio_cascade *cas; ++ struct mux_control *mc; ++ struct gpio_desc *upstream; ++ struct gpio_chip *gc; ++ ++ cas = devm_kzalloc(dev, sizeof(*cas), GFP_KERNEL); ++ if (!cas) ++ return -ENOMEM; ++ ++ mc = devm_mux_control_get(dev, NULL); ++ if (IS_ERR(mc)) ++ return dev_err_probe(dev, PTR_ERR(mc), "unable to get mux-control\n"); ++ ++ cas->mux_control = mc; ++ upstream = devm_gpiod_get(dev, "upstream", GPIOD_IN); ++ if (IS_ERR(upstream)) ++ return dev_err_probe(dev, PTR_ERR(upstream), "unable to claim upstream GPIO line\n"); ++ ++ cas->upstream_line = upstream; ++ cas->parent = dev; ++ ++ gc = &cas->gpio_chip; ++ gc->get = gpio_cascade_get_value; ++ gc->get_direction = gpio_cascade_get_direction; ++ gc->base = -1; ++ gc->ngpio = mux_control_states(mc); ++ gc->label = dev_name(cas->parent); ++ gc->parent = cas->parent; ++ gc->owner = THIS_MODULE; ++ ++ platform_set_drvdata(pdev, cas); ++ return devm_gpiochip_add_data(dev, &cas->gpio_chip, NULL); ++} ++ ++static const struct of_device_id gpio_cascade_id[] = { ++ { .compatible = "gpio-cascade" }, ++ { /* sentinel */ } ++}; ++MODULE_DEVICE_TABLE(of, gpio_cascade_id); ++ ++static struct platform_driver gpio_cascade_driver = { ++ .driver = { ++ .name = "gpio-cascade", ++ .of_match_table = gpio_cascade_id, ++ }, ++ .probe = gpio_cascade_probe, ++}; ++module_platform_driver(gpio_cascade_driver); ++ ++MODULE_AUTHOR("Mauri Sandberg <maukka@ext.kapsi.fi>"); ++MODULE_DESCRIPTION("Generic GPIO cascade"); ++MODULE_LICENSE("GPL"); diff --git a/pkgs/patches-linux-5.15/801-v5.20-0001-dt-bindings-leds-leds-bcm63138-unify-full-stops-in-d.patch b/pkgs/patches-linux-5.15/801-v5.20-0001-dt-bindings-leds-leds-bcm63138-unify-full-stops-in-d.patch new file mode 100644 index 0000000..483826a --- /dev/null +++ b/pkgs/patches-linux-5.15/801-v5.20-0001-dt-bindings-leds-leds-bcm63138-unify-full-stops-in-d.patch @@ -0,0 +1,30 @@ +From 13b64a0c19059b38150c79d65d350ae44034c5df Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <rafal@milecki.pl> +Date: Sun, 17 Jul 2022 14:42:46 +0200 +Subject: [PATCH] dt-bindings: leds: leds-bcm63138: unify full stops in + descriptions +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Description of "reg" doesn't have full stop at the end. It makes sense +as it's a one-sentence only. Use the same style for "active-low". + +Reported-by: Pavel Machek <pavel@ucw.cz> +Signed-off-by: Rafał Miłecki <rafal@milecki.pl> +Signed-off-by: Pavel Machek <pavel@ucw.cz> +--- + Documentation/devicetree/bindings/leds/leds-bcm63138.yaml | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/Documentation/devicetree/bindings/leds/leds-bcm63138.yaml ++++ b/Documentation/devicetree/bindings/leds/leds-bcm63138.yaml +@@ -54,7 +54,7 @@ patternProperties: + + active-low: + type: boolean +- description: Makes LED active low. ++ description: Makes LED active low + + required: + - reg diff --git a/pkgs/patches-linux-5.15/801-v5.20-0002-leds-add-help-info-about-BCM63138-module-name.patch b/pkgs/patches-linux-5.15/801-v5.20-0002-leds-add-help-info-about-BCM63138-module-name.patch new file mode 100644 index 0000000..5430b1f --- /dev/null +++ b/pkgs/patches-linux-5.15/801-v5.20-0002-leds-add-help-info-about-BCM63138-module-name.patch @@ -0,0 +1,28 @@ +From bcc607cdbb1f931111196699426f0cb83bfb296a Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <rafal@milecki.pl> +Date: Sun, 17 Jul 2022 14:42:47 +0200 +Subject: [PATCH] leds: add help info about BCM63138 module name +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +It's what we do for all other LEDs drivers. + +Reported-by: Pavel Machek <pavel@ucw.cz> +Signed-off-by: Rafał Miłecki <rafal@milecki.pl> +Signed-off-by: Pavel Machek <pavel@ucw.cz> +--- + drivers/leds/blink/Kconfig | 2 ++ + 1 file changed, 2 insertions(+) + +--- a/drivers/leds/blink/Kconfig ++++ b/drivers/leds/blink/Kconfig +@@ -10,6 +10,8 @@ config LEDS_BCM63138 + BCM63138 SoC. The same hardware block is known to be also used + in BCM4908, BCM6848, BCM6858, BCM63148, BCM63381 and BCM68360. + ++ If compiled as module it will be called leds-bcm63138. ++ + config LEDS_LGM + tristate "LED support for LGM SoC series" + depends on X86 || COMPILE_TEST diff --git a/pkgs/patches-linux-5.15/801-v5.20-0003-leds-leds-bcm63138-get-rid-of-LED_OFF.patch b/pkgs/patches-linux-5.15/801-v5.20-0003-leds-leds-bcm63138-get-rid-of-LED_OFF.patch new file mode 100644 index 0000000..e125a54 --- /dev/null +++ b/pkgs/patches-linux-5.15/801-v5.20-0003-leds-leds-bcm63138-get-rid-of-LED_OFF.patch @@ -0,0 +1,30 @@ +From 92cfc71ee2ddfb499ed53e21b28bdf8739bc70bc Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <rafal@milecki.pl> +Date: Sun, 17 Jul 2022 14:42:48 +0200 +Subject: [PATCH] leds: leds-bcm63138: get rid of LED_OFF +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +The whole "enum led_brightness" is marked as obsolete. Replace it with a +(non-)zero check. + +Reported-by: Pavel Machek <pavel@ucw.cz> +Signed-off-by: Rafał Miłecki <rafal@milecki.pl> +Signed-off-by: Pavel Machek <pavel@ucw.cz> +--- + drivers/leds/blink/leds-bcm63138.c | 3 +-- + 1 file changed, 1 insertion(+), 2 deletions(-) + +--- a/drivers/leds/blink/leds-bcm63138.c ++++ b/drivers/leds/blink/leds-bcm63138.c +@@ -113,8 +113,7 @@ static void bcm63138_leds_enable_led(str + { + u32 bit = BIT(led->pin); + +- bcm63138_leds_update_bits(leds, BCM63138_SW_DATA, bit, +- value == LED_OFF ? 0 : bit); ++ bcm63138_leds_update_bits(leds, BCM63138_SW_DATA, bit, value ? bit : 0); + } + + /* diff --git a/pkgs/patches-linux-5.15/810-pci_disable_common_quirks.patch b/pkgs/patches-linux-5.15/810-pci_disable_common_quirks.patch new file mode 100644 index 0000000..7edbd94 --- /dev/null +++ b/pkgs/patches-linux-5.15/810-pci_disable_common_quirks.patch @@ -0,0 +1,62 @@ +From: Gabor Juhos <juhosg@openwrt.org> +Subject: debloat: add kernel config option to disabling common PCI quirks + +Signed-off-by: Gabor Juhos <juhosg@openwrt.org> +--- + drivers/pci/Kconfig | 6 ++++++ + drivers/pci/quirks.c | 6 ++++++ + 2 files changed, 12 insertions(+) + +--- a/drivers/pci/Kconfig ++++ b/drivers/pci/Kconfig +@@ -118,6 +118,13 @@ config XEN_PCIDEV_FRONTEND + The PCI device frontend driver allows the kernel to import arbitrary + PCI devices from a PCI backend to support PCI driver domains. + ++config PCI_DISABLE_COMMON_QUIRKS ++ bool "PCI disable common quirks" ++ depends on PCI ++ help ++ If you don't know what to do here, say N. ++ ++ + config PCI_ATS + bool + +--- a/drivers/pci/quirks.c ++++ b/drivers/pci/quirks.c +@@ -206,6 +206,7 @@ static void quirk_mmio_always_on(struct + DECLARE_PCI_FIXUP_CLASS_EARLY(PCI_ANY_ID, PCI_ANY_ID, + PCI_CLASS_BRIDGE_HOST, 8, quirk_mmio_always_on); + ++#ifndef CONFIG_PCI_DISABLE_COMMON_QUIRKS + /* + * The Mellanox Tavor device gives false positive parity errors. Disable + * parity error reporting. +@@ -3363,6 +3364,8 @@ DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_I + DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x65f9, quirk_intel_mc_errata); + DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x65fa, quirk_intel_mc_errata); + ++#endif /* !CONFIG_PCI_DISABLE_COMMON_QUIRKS */ ++ + /* + * Ivytown NTB BAR sizes are misreported by the hardware due to an erratum. + * To work around this, query the size it should be configured to by the +@@ -3388,6 +3391,8 @@ static void quirk_intel_ntb(struct pci_d + DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x0e08, quirk_intel_ntb); + DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x0e0d, quirk_intel_ntb); + ++#ifndef CONFIG_PCI_DISABLE_COMMON_QUIRKS ++ + /* + * Some BIOS implementations leave the Intel GPU interrupts enabled, even + * though no one is handling them (e.g., if the i915 driver is never +@@ -3426,6 +3431,8 @@ DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_IN + DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x010a, disable_igfx_irq); + DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x0152, disable_igfx_irq); + ++#endif /* !CONFIG_PCI_DISABLE_COMMON_QUIRKS */ ++ + /* + * PCI devices which are on Intel chips can skip the 10ms delay + * before entering D3 mode. diff --git a/pkgs/patches-linux-5.15/811-pci_disable_usb_common_quirks.patch b/pkgs/patches-linux-5.15/811-pci_disable_usb_common_quirks.patch new file mode 100644 index 0000000..67406ba --- /dev/null +++ b/pkgs/patches-linux-5.15/811-pci_disable_usb_common_quirks.patch @@ -0,0 +1,115 @@ +From: Felix Fietkau <nbd@nbd.name> +Subject: debloat: disable common USB quirks + +Signed-off-by: Felix Fietkau <nbd@nbd.name> +--- + drivers/usb/host/pci-quirks.c | 16 ++++++++++++++++ + drivers/usb/host/pci-quirks.h | 18 +++++++++++++++++- + include/linux/usb/hcd.h | 7 +++++++ + 3 files changed, 40 insertions(+), 1 deletion(-) + +--- a/drivers/usb/host/pci-quirks.c ++++ b/drivers/usb/host/pci-quirks.c +@@ -128,6 +128,8 @@ struct amd_chipset_type { + u8 rev; + }; + ++#ifndef CONFIG_PCI_DISABLE_COMMON_QUIRKS ++ + static struct amd_chipset_info { + struct pci_dev *nb_dev; + struct pci_dev *smbus_dev; +@@ -633,6 +635,10 @@ bool usb_amd_pt_check_port(struct device + } + EXPORT_SYMBOL_GPL(usb_amd_pt_check_port); + ++#endif /* CONFIG_PCI_DISABLE_COMMON_QUIRKS */ ++ ++#if IS_ENABLED(CONFIG_USB_UHCI_HCD) ++ + /* + * Make sure the controller is completely inactive, unable to + * generate interrupts or do DMA. +@@ -712,8 +718,17 @@ reset_needed: + uhci_reset_hc(pdev, base); + return 1; + } ++#else ++int uhci_check_and_reset_hc(struct pci_dev *pdev, unsigned long base) ++{ ++ return 0; ++} ++ ++#endif + EXPORT_SYMBOL_GPL(uhci_check_and_reset_hc); + ++#ifndef CONFIG_PCI_DISABLE_COMMON_QUIRKS ++ + static inline int io_type_enabled(struct pci_dev *pdev, unsigned int mask) + { + u16 cmd; +@@ -1285,3 +1300,4 @@ static void quirk_usb_early_handoff(stru + } + DECLARE_PCI_FIXUP_CLASS_FINAL(PCI_ANY_ID, PCI_ANY_ID, + PCI_CLASS_SERIAL_USB, 8, quirk_usb_early_handoff); ++#endif +--- a/drivers/usb/host/pci-quirks.h ++++ b/drivers/usb/host/pci-quirks.h +@@ -5,6 +5,9 @@ + #ifdef CONFIG_USB_PCI + void uhci_reset_hc(struct pci_dev *pdev, unsigned long base); + int uhci_check_and_reset_hc(struct pci_dev *pdev, unsigned long base); ++#endif /* CONFIG_USB_PCI */ ++ ++#if defined(CONFIG_USB_PCI) && !defined(CONFIG_PCI_DISABLE_COMMON_QUIRKS) + int usb_hcd_amd_remote_wakeup_quirk(struct pci_dev *pdev); + bool usb_amd_hang_symptom_quirk(void); + bool usb_amd_prefetch_quirk(void); +@@ -19,6 +22,18 @@ void sb800_prefetch(struct device *dev, + bool usb_amd_pt_check_port(struct device *device, int port); + #else + struct pci_dev; ++static inline int usb_amd_quirk_pll_check(void) ++{ ++ return 0; ++} ++static inline bool usb_amd_hang_symptom_quirk(void) ++{ ++ return false; ++} ++static inline bool usb_amd_prefetch_quirk(void) ++{ ++ return false; ++} + static inline void usb_amd_quirk_pll_disable(void) {} + static inline void usb_amd_quirk_pll_enable(void) {} + static inline void usb_asmedia_modifyflowcontrol(struct pci_dev *pdev) {} +@@ -29,6 +44,11 @@ static inline bool usb_amd_pt_check_port + { + return false; + } ++static inline void usb_enable_intel_xhci_ports(struct pci_dev *xhci_pdev) {} ++static inline bool usb_xhci_needs_pci_reset(struct pci_dev *pdev) ++{ ++ return false; ++} + #endif /* CONFIG_USB_PCI */ + + #endif /* __LINUX_USB_PCI_QUIRKS_H */ +--- a/include/linux/usb/hcd.h ++++ b/include/linux/usb/hcd.h +@@ -497,7 +497,14 @@ extern int usb_hcd_pci_probe(struct pci_ + extern void usb_hcd_pci_remove(struct pci_dev *dev); + extern void usb_hcd_pci_shutdown(struct pci_dev *dev); + ++#ifndef CONFIG_PCI_DISABLE_COMMON_QUIRKS + extern int usb_hcd_amd_remote_wakeup_quirk(struct pci_dev *dev); ++#else ++static inline int usb_hcd_amd_remote_wakeup_quirk(struct pci_dev *dev) ++{ ++ return 0; ++} ++#endif + + #ifdef CONFIG_PM + extern const struct dev_pm_ops usb_hcd_pci_pm_ops; diff --git a/pkgs/patches-linux-5.15/820-w1-gpio-fix-problem-with-platfom-data-in-w1-gpio.patch b/pkgs/patches-linux-5.15/820-w1-gpio-fix-problem-with-platfom-data-in-w1-gpio.patch new file mode 100644 index 0000000..33eb34c --- /dev/null +++ b/pkgs/patches-linux-5.15/820-w1-gpio-fix-problem-with-platfom-data-in-w1-gpio.patch @@ -0,0 +1,26 @@ +From d9c8bc8c1408f3e8529db6e4e04017b4c579c342 Mon Sep 17 00:00:00 2001 +From: Pawel Dembicki <paweldembicki@gmail.com> +Date: Sun, 18 Feb 2018 17:08:04 +0100 +Subject: [PATCH] w1: gpio: fix problem with platfom data in w1-gpio + +In devices, where fdt is used, is impossible to apply platform data +without proper fdt node. + +This patch allow to use platform data in devices with fdt. + +Signed-off-by: Pawel Dembicki <paweldembicki@gmail.com> +--- + drivers/w1/masters/w1-gpio.c | 7 +++---- + 1 file changed, 3 insertions(+), 4 deletions(-) + +--- a/drivers/w1/masters/w1-gpio.c ++++ b/drivers/w1/masters/w1-gpio.c +@@ -76,7 +76,7 @@ static int w1_gpio_probe(struct platform + enum gpiod_flags gflags = GPIOD_OUT_LOW_OPEN_DRAIN; + int err; + +- if (of_have_populated_dt()) { ++ if (of_have_populated_dt() && !dev_get_platdata(&pdev->dev)) { + pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL); + if (!pdata) + return -ENOMEM; diff --git a/pkgs/patches-linux-5.15/834-ledtrig-libata.patch b/pkgs/patches-linux-5.15/834-ledtrig-libata.patch new file mode 100644 index 0000000..3977b57 --- /dev/null +++ b/pkgs/patches-linux-5.15/834-ledtrig-libata.patch @@ -0,0 +1,149 @@ +From: Daniel Golle <daniel@makrotopia.org> +Subject: libata: add ledtrig support + +This adds a LED trigger for each ATA port indicating disk activity. + +As this is needed only on specific platforms (NAS SoCs and such), +these platforms should define ARCH_WANTS_LIBATA_LEDS if there +are boards with LED(s) intended to indicate ATA disk activity and +need the OS to take care of that. +In that way, if not selected, LED trigger support not will be +included in libata-core and both, codepaths and structures remain +untouched. + +Signed-off-by: Daniel Golle <daniel@makrotopia.org> +--- + drivers/ata/Kconfig | 16 ++++++++++++++++ + drivers/ata/libata-core.c | 41 +++++++++++++++++++++++++++++++++++++++++ + include/linux/libata.h | 9 +++++++++ + 3 files changed, 66 insertions(+) + +--- a/drivers/ata/Kconfig ++++ b/drivers/ata/Kconfig +@@ -67,6 +67,22 @@ config ATA_FORCE + + If unsure, say Y. + ++config ARCH_WANT_LIBATA_LEDS ++ bool ++ ++config ATA_LEDS ++ bool "support ATA port LED triggers" ++ depends on ARCH_WANT_LIBATA_LEDS ++ select NEW_LEDS ++ select LEDS_CLASS ++ select LEDS_TRIGGERS ++ default y ++ help ++ This option adds a LED trigger for each registered ATA port. ++ It is used to drive disk activity leds connected via GPIO. ++ ++ If unsure, say N. ++ + config ATA_ACPI + bool "ATA ACPI Support" + depends on ACPI +--- a/drivers/ata/libata-core.c ++++ b/drivers/ata/libata-core.c +@@ -656,6 +656,19 @@ u64 ata_tf_read_block(const struct ata_t + return block; + } + ++#ifdef CONFIG_ATA_LEDS ++#define LIBATA_BLINK_DELAY 20 /* ms */ ++static inline void ata_led_act(struct ata_port *ap) ++{ ++ unsigned long led_delay = LIBATA_BLINK_DELAY; ++ ++ if (unlikely(!ap->ledtrig)) ++ return; ++ ++ led_trigger_blink_oneshot(ap->ledtrig, &led_delay, &led_delay, 0); ++} ++#endif ++ + /** + * ata_build_rw_tf - Build ATA taskfile for given read/write request + * @tf: Target ATA taskfile +@@ -4576,6 +4589,9 @@ struct ata_queued_cmd *ata_qc_new_init(s + if (tag < 0) + return NULL; + } ++#ifdef CONFIG_ATA_LEDS ++ ata_led_act(ap); ++#endif + + qc = __ata_qc_from_tag(ap, tag); + qc->tag = qc->hw_tag = tag; +@@ -5354,6 +5370,9 @@ struct ata_port *ata_port_alloc(struct a + ap->stats.unhandled_irq = 1; + ap->stats.idle_irq = 1; + #endif ++#ifdef CONFIG_ATA_LEDS ++ ap->ledtrig = kzalloc(sizeof(struct led_trigger), GFP_KERNEL); ++#endif + ata_sff_port_init(ap); + + return ap; +@@ -5389,6 +5408,12 @@ static void ata_host_release(struct kref + + kfree(ap->pmp_link); + kfree(ap->slave_link); ++#ifdef CONFIG_ATA_LEDS ++ if (ap->ledtrig) { ++ led_trigger_unregister(ap->ledtrig); ++ kfree(ap->ledtrig); ++ }; ++#endif + kfree(ap); + host->ports[i] = NULL; + } +@@ -5795,7 +5820,23 @@ int ata_host_register(struct ata_host *h + host->ports[i]->print_id = atomic_inc_return(&ata_print_id); + host->ports[i]->local_port_no = i + 1; + } ++#ifdef CONFIG_ATA_LEDS ++ for (i = 0; i < host->n_ports; i++) { ++ if (unlikely(!host->ports[i]->ledtrig)) ++ continue; + ++ snprintf(host->ports[i]->ledtrig_name, ++ sizeof(host->ports[i]->ledtrig_name), "ata%u", ++ host->ports[i]->print_id); ++ ++ host->ports[i]->ledtrig->name = host->ports[i]->ledtrig_name; ++ ++ if (led_trigger_register(host->ports[i]->ledtrig)) { ++ kfree(host->ports[i]->ledtrig); ++ host->ports[i]->ledtrig = NULL; ++ } ++ } ++#endif + /* Create associated sysfs transport objects */ + for (i = 0; i < host->n_ports; i++) { + rc = ata_tport_add(host->dev,host->ports[i]); +--- a/include/linux/libata.h ++++ b/include/linux/libata.h +@@ -23,6 +23,9 @@ + #include <linux/cdrom.h> + #include <linux/sched.h> + #include <linux/async.h> ++#ifdef CONFIG_ATA_LEDS ++#include <linux/leds.h> ++#endif + + /* + * Define if arch has non-standard setup. This is a _PCI_ standard +@@ -888,6 +891,12 @@ struct ata_port { + #ifdef CONFIG_ATA_ACPI + struct ata_acpi_gtm __acpi_init_gtm; /* use ata_acpi_init_gtm() */ + #endif ++ ++#ifdef CONFIG_ATA_LEDS ++ struct led_trigger *ledtrig; ++ char ledtrig_name[8]; ++#endif ++ + /* owned by EH */ + u8 sector_buf[ATA_SECT_SIZE] ____cacheline_aligned; + }; diff --git a/pkgs/patches-linux-5.15/840-hwrng-bcm2835-set-quality-to-1000.patch b/pkgs/patches-linux-5.15/840-hwrng-bcm2835-set-quality-to-1000.patch new file mode 100644 index 0000000..5ca8933 --- /dev/null +++ b/pkgs/patches-linux-5.15/840-hwrng-bcm2835-set-quality-to-1000.patch @@ -0,0 +1,26 @@ +From d6988cf1d16faac56899918bb2b1be8d85155e3f Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?=C3=81lvaro=20Fern=C3=A1ndez=20Rojas?= <noltari@gmail.com> +Date: Sat, 20 Feb 2021 18:36:38 +0100 +Subject: [PATCH] hwrng: bcm2835: set quality to 1000 +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +This allows devices without a high precission timer to reduce boot from >100s +to <30s. + +Signed-off-by: Álvaro Fernández Rojas <noltari@gmail.com> +--- + drivers/char/hw_random/bcm2835-rng.c | 1 + + 1 file changed, 1 insertion(+) + +--- a/drivers/char/hw_random/bcm2835-rng.c ++++ b/drivers/char/hw_random/bcm2835-rng.c +@@ -170,6 +170,7 @@ static int bcm2835_rng_probe(struct plat + priv->rng.init = bcm2835_rng_init; + priv->rng.read = bcm2835_rng_read; + priv->rng.cleanup = bcm2835_rng_cleanup; ++ priv->rng.quality = 1000; + + if (dev_of_node(dev)) { + rng_id = of_match_node(bcm2835_rng_of_match, dev->of_node); diff --git a/pkgs/patches-linux-5.15/842-net-qmi_wwan-add-ZTE-MF286D-modem-19d2-1485.patch b/pkgs/patches-linux-5.15/842-net-qmi_wwan-add-ZTE-MF286D-modem-19d2-1485.patch new file mode 100644 index 0000000..530ab9b --- /dev/null +++ b/pkgs/patches-linux-5.15/842-net-qmi_wwan-add-ZTE-MF286D-modem-19d2-1485.patch @@ -0,0 +1,59 @@ +From 078c6a1cbd4cd7496048786beec2e312577bebbf Mon Sep 17 00:00:00 2001 +From: Pawel Dembicki <paweldembicki@gmail.com> +Date: Tue, 11 Jan 2022 23:11:32 +0100 +Subject: [PATCH] net: qmi_wwan: add ZTE MF286D modem 19d2:1485 +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Modem from ZTE MF286D is an Qualcomm MDM9250 based 3G/4G modem. + +T: Bus=02 Lev=01 Prnt=01 Port=00 Cnt=01 Dev#= 3 Spd=5000 MxCh= 0 +D: Ver= 3.00 Cls=00(>ifc ) Sub=00 Prot=00 MxPS= 9 #Cfgs= 1 +P: Vendor=19d2 ProdID=1485 Rev=52.87 +S: Manufacturer=ZTE,Incorporated +S: Product=ZTE Technologies MSM +S: SerialNumber=MF286DZTED000000 +C:* #Ifs= 7 Cfg#= 1 Atr=80 MxPwr=896mA +A: FirstIf#= 0 IfCount= 2 Cls=02(comm.) Sub=06 Prot=00 +I:* If#= 0 Alt= 0 #EPs= 1 Cls=02(comm.) Sub=02 Prot=ff Driver=rndis_host +E: Ad=82(I) Atr=03(Int.) MxPS= 8 Ivl=32ms +I:* If#= 1 Alt= 0 #EPs= 2 Cls=0a(data ) Sub=00 Prot=00 Driver=rndis_host +E: Ad=81(I) Atr=02(Bulk) MxPS=1024 Ivl=0ms +E: Ad=01(O) Atr=02(Bulk) MxPS=1024 Ivl=0ms +I:* If#= 2 Alt= 0 #EPs= 2 Cls=ff(vend.) Sub=ff Prot=ff Driver=option +E: Ad=83(I) Atr=02(Bulk) MxPS=1024 Ivl=0ms +E: Ad=02(O) Atr=02(Bulk) MxPS=1024 Ivl=0ms +I:* If#= 3 Alt= 0 #EPs= 3 Cls=ff(vend.) Sub=ff Prot=ff Driver=option +E: Ad=85(I) Atr=03(Int.) MxPS= 10 Ivl=32ms +E: Ad=84(I) Atr=02(Bulk) MxPS=1024 Ivl=0ms +E: Ad=03(O) Atr=02(Bulk) MxPS=1024 Ivl=0ms +I:* If#= 4 Alt= 0 #EPs= 3 Cls=ff(vend.) Sub=ff Prot=ff Driver=option +E: Ad=87(I) Atr=03(Int.) MxPS= 10 Ivl=32ms +E: Ad=86(I) Atr=02(Bulk) MxPS=1024 Ivl=0ms +E: Ad=04(O) Atr=02(Bulk) MxPS=1024 Ivl=0ms +I:* If#= 5 Alt= 0 #EPs= 3 Cls=ff(vend.) Sub=ff Prot=ff Driver=qmi_wwan +E: Ad=88(I) Atr=03(Int.) MxPS= 8 Ivl=32ms +E: Ad=8e(I) Atr=02(Bulk) MxPS=1024 Ivl=0ms +E: Ad=0f(O) Atr=02(Bulk) MxPS=1024 Ivl=0ms +I:* If#= 6 Alt= 0 #EPs= 2 Cls=ff(vend.) Sub=42 Prot=01 Driver=usbfs +E: Ad=05(O) Atr=02(Bulk) MxPS=1024 Ivl=0ms +E: Ad=89(I) Atr=02(Bulk) MxPS=1024 Ivl=0ms + +Signed-off-by: Pawel Dembicki <paweldembicki@gmail.com> +Acked-by: Bjørn Mork <bjorn@mork.no> +Signed-off-by: David S. Miller <davem@davemloft.net> +--- + drivers/net/usb/qmi_wwan.c | 1 + + 1 file changed, 1 insertion(+) + +--- a/drivers/net/usb/qmi_wwan.c ++++ b/drivers/net/usb/qmi_wwan.c +@@ -1313,6 +1313,7 @@ static const struct usb_device_id produc + {QMI_FIXED_INTF(0x19d2, 0x1426, 2)}, /* ZTE MF91 */ + {QMI_FIXED_INTF(0x19d2, 0x1428, 2)}, /* Telewell TW-LTE 4G v2 */ + {QMI_FIXED_INTF(0x19d2, 0x1432, 3)}, /* ZTE ME3620 */ ++ {QMI_FIXED_INTF(0x19d2, 0x1485, 5)}, /* ZTE MF286D */ + {QMI_FIXED_INTF(0x19d2, 0x2002, 4)}, /* ZTE (Vodafone) K3765-Z */ + {QMI_FIXED_INTF(0x2001, 0x7e16, 3)}, /* D-Link DWM-221 */ + {QMI_FIXED_INTF(0x2001, 0x7e19, 4)}, /* D-Link DWM-221 B1 */ diff --git a/pkgs/patches-linux-5.15/850-0023-PCI-aardvark-Make-main-irq_chip-structure-a-static-d.patch b/pkgs/patches-linux-5.15/850-0023-PCI-aardvark-Make-main-irq_chip-structure-a-static-d.patch new file mode 100644 index 0000000..de28ebe --- /dev/null +++ b/pkgs/patches-linux-5.15/850-0023-PCI-aardvark-Make-main-irq_chip-structure-a-static-d.patch @@ -0,0 +1,102 @@ +From 663b9f99bb35dbc0c7b685f71ee3668a60d31320 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Marek=20Beh=C3=BAn?= <kabel@kernel.org> +Date: Mon, 10 Jan 2022 02:02:00 +0100 +Subject: [PATCH] PCI: aardvark: Make main irq_chip structure a static driver + structure +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Marc Zyngier says [1] that we should use struct irq_chip as a global +static struct in the driver. Even though the structure currently +contains a dynamic member (parent_device), Marc says [2] that he plans +to kill it and make the structure completely static. + +We have already converted others irq_chip structures in this driver in +this way, but we omitted this one because the .name member is +dynamically created from device's name, and the name is displayed in +sysfs, so changing it would break sysfs ABI. + +The rationale for changing the name (to "advk-INT") in spite of sysfs +ABI, and thus allowing to convert to a static structure, is that after +the other changes we made in this series, the IRQ chip is basically +something different: it no logner generates ERR and PME interrupts (they +are generated by emulated bridge's rp_irq_chip). + +[1] https://lore.kernel.org/linux-pci/877dbcvngf.wl-maz@kernel.org/ +[2] https://lore.kernel.org/linux-pci/874k6gvkhz.wl-maz@kernel.org/ + +Signed-off-by: Marek Behún <kabel@kernel.org> +--- + drivers/pci/controller/pci-aardvark.c | 25 +++++++------------------ + 1 file changed, 7 insertions(+), 18 deletions(-) + +--- a/drivers/pci/controller/pci-aardvark.c ++++ b/drivers/pci/controller/pci-aardvark.c +@@ -274,7 +274,6 @@ struct advk_pcie { + u8 wins_count; + struct irq_domain *rp_irq_domain; + struct irq_domain *irq_domain; +- struct irq_chip irq_chip; + raw_spinlock_t irq_lock; + struct irq_domain *msi_domain; + struct irq_domain *msi_inner_domain; +@@ -1330,14 +1329,19 @@ static void advk_pcie_irq_unmask(struct + raw_spin_unlock_irqrestore(&pcie->irq_lock, flags); + } + ++static struct irq_chip advk_irq_chip = { ++ .name = "advk-INT", ++ .irq_mask = advk_pcie_irq_mask, ++ .irq_unmask = advk_pcie_irq_unmask, ++}; ++ + static int advk_pcie_irq_map(struct irq_domain *h, + unsigned int virq, irq_hw_number_t hwirq) + { + struct advk_pcie *pcie = h->host_data; + + irq_set_status_flags(virq, IRQ_LEVEL); +- irq_set_chip_and_handler(virq, &pcie->irq_chip, +- handle_level_irq); ++ irq_set_chip_and_handler(virq, &advk_irq_chip, handle_level_irq); + irq_set_chip_data(virq, pcie); + + return 0; +@@ -1396,7 +1400,6 @@ static int advk_pcie_init_irq_domain(str + struct device *dev = &pcie->pdev->dev; + struct device_node *node = dev->of_node; + struct device_node *pcie_intc_node; +- struct irq_chip *irq_chip; + int ret = 0; + + raw_spin_lock_init(&pcie->irq_lock); +@@ -1407,28 +1410,14 @@ static int advk_pcie_init_irq_domain(str + return -ENODEV; + } + +- irq_chip = &pcie->irq_chip; +- +- irq_chip->name = devm_kasprintf(dev, GFP_KERNEL, "%s-irq", +- dev_name(dev)); +- if (!irq_chip->name) { +- ret = -ENOMEM; +- goto out_put_node; +- } +- +- irq_chip->irq_mask = advk_pcie_irq_mask; +- irq_chip->irq_unmask = advk_pcie_irq_unmask; +- + pcie->irq_domain = + irq_domain_add_linear(pcie_intc_node, PCI_NUM_INTX, + &advk_pcie_irq_domain_ops, pcie); + if (!pcie->irq_domain) { + dev_err(dev, "Failed to get a INTx IRQ domain\n"); + ret = -ENOMEM; +- goto out_put_node; + } + +-out_put_node: + of_node_put(pcie_intc_node); + return ret; + } diff --git a/pkgs/patches-linux-5.15/851-0001-phy-marvell-phy-mvebu-a3700-comphy-Remove-port-from-.patch b/pkgs/patches-linux-5.15/851-0001-phy-marvell-phy-mvebu-a3700-comphy-Remove-port-from-.patch new file mode 100644 index 0000000..4a963be --- /dev/null +++ b/pkgs/patches-linux-5.15/851-0001-phy-marvell-phy-mvebu-a3700-comphy-Remove-port-from-.patch @@ -0,0 +1,217 @@ +From a719f7ba7fcba05d85801c6f0267f389a21627c1 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org> +Date: Fri, 24 Sep 2021 13:03:02 +0200 +Subject: [PATCH] phy: marvell: phy-mvebu-a3700-comphy: Remove port from driver + configuration +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Port number is encoded into argument for SMC call. It is zero for SATA, +PCIe and also both USB 3.0 PHYs. It is non-zero only for Ethernet PHY +(incorrectly called SGMII) on lane 0. Ethernet PHY on lane 1 also uses zero +port number. + +So construct "port" bits for SMC call argument can be constructed directly +from PHY type and lane number. + +Change driver code to always pass zero port number for non-ethernet PHYs +and for ethernet PHYs determinate port number from lane number. This +simplifies the driver. + +As port number from DT PHY configuration is not used anymore, remove whole +driver code which parses it. This also simplifies the driver. + +Signed-off-by: Pali Rohár <pali@kernel.org> +Signed-off-by: Marek Behún <kabel@kernel.org> +Reviewed-by: Miquel Raynal <miquel.raynal@bootlin.com> +--- + drivers/phy/marvell/phy-mvebu-a3700-comphy.c | 62 +++++++++----------- + 1 file changed, 29 insertions(+), 33 deletions(-) + +--- a/drivers/phy/marvell/phy-mvebu-a3700-comphy.c ++++ b/drivers/phy/marvell/phy-mvebu-a3700-comphy.c +@@ -20,7 +20,6 @@ + #include <linux/platform_device.h> + + #define MVEBU_A3700_COMPHY_LANES 3 +-#define MVEBU_A3700_COMPHY_PORTS 2 + + /* COMPHY Fast SMC function identifiers */ + #define COMPHY_SIP_POWER_ON 0x82000001 +@@ -45,51 +44,47 @@ + #define COMPHY_FW_NET(mode, idx, speed) (COMPHY_FW_MODE(mode) | \ + ((idx) << 8) | \ + ((speed) << 2)) +-#define COMPHY_FW_PCIE(mode, idx, speed, width) (COMPHY_FW_NET(mode, idx, speed) | \ ++#define COMPHY_FW_PCIE(mode, speed, width) (COMPHY_FW_NET(mode, 0, speed) | \ + ((width) << 18)) + + struct mvebu_a3700_comphy_conf { + unsigned int lane; + enum phy_mode mode; + int submode; +- unsigned int port; + u32 fw_mode; + }; + +-#define MVEBU_A3700_COMPHY_CONF(_lane, _mode, _smode, _port, _fw) \ ++#define MVEBU_A3700_COMPHY_CONF(_lane, _mode, _smode, _fw) \ + { \ + .lane = _lane, \ + .mode = _mode, \ + .submode = _smode, \ +- .port = _port, \ + .fw_mode = _fw, \ + } + +-#define MVEBU_A3700_COMPHY_CONF_GEN(_lane, _mode, _port, _fw) \ +- MVEBU_A3700_COMPHY_CONF(_lane, _mode, PHY_INTERFACE_MODE_NA, _port, _fw) ++#define MVEBU_A3700_COMPHY_CONF_GEN(_lane, _mode, _fw) \ ++ MVEBU_A3700_COMPHY_CONF(_lane, _mode, PHY_INTERFACE_MODE_NA, _fw) + +-#define MVEBU_A3700_COMPHY_CONF_ETH(_lane, _smode, _port, _fw) \ +- MVEBU_A3700_COMPHY_CONF(_lane, PHY_MODE_ETHERNET, _smode, _port, _fw) ++#define MVEBU_A3700_COMPHY_CONF_ETH(_lane, _smode, _fw) \ ++ MVEBU_A3700_COMPHY_CONF(_lane, PHY_MODE_ETHERNET, _smode, _fw) + + static const struct mvebu_a3700_comphy_conf mvebu_a3700_comphy_modes[] = { + /* lane 0 */ +- MVEBU_A3700_COMPHY_CONF_GEN(0, PHY_MODE_USB_HOST_SS, 0, ++ MVEBU_A3700_COMPHY_CONF_GEN(0, PHY_MODE_USB_HOST_SS, + COMPHY_FW_MODE_USB3H), +- MVEBU_A3700_COMPHY_CONF_ETH(0, PHY_INTERFACE_MODE_SGMII, 1, ++ MVEBU_A3700_COMPHY_CONF_ETH(0, PHY_INTERFACE_MODE_SGMII, + COMPHY_FW_MODE_SGMII), +- MVEBU_A3700_COMPHY_CONF_ETH(0, PHY_INTERFACE_MODE_2500BASEX, 1, ++ MVEBU_A3700_COMPHY_CONF_ETH(0, PHY_INTERFACE_MODE_2500BASEX, + COMPHY_FW_MODE_2500BASEX), + /* lane 1 */ +- MVEBU_A3700_COMPHY_CONF_GEN(1, PHY_MODE_PCIE, 0, +- COMPHY_FW_MODE_PCIE), +- MVEBU_A3700_COMPHY_CONF_ETH(1, PHY_INTERFACE_MODE_SGMII, 0, ++ MVEBU_A3700_COMPHY_CONF_GEN(1, PHY_MODE_PCIE, COMPHY_FW_MODE_PCIE), ++ MVEBU_A3700_COMPHY_CONF_ETH(1, PHY_INTERFACE_MODE_SGMII, + COMPHY_FW_MODE_SGMII), +- MVEBU_A3700_COMPHY_CONF_ETH(1, PHY_INTERFACE_MODE_2500BASEX, 0, ++ MVEBU_A3700_COMPHY_CONF_ETH(1, PHY_INTERFACE_MODE_2500BASEX, + COMPHY_FW_MODE_2500BASEX), + /* lane 2 */ +- MVEBU_A3700_COMPHY_CONF_GEN(2, PHY_MODE_SATA, 0, +- COMPHY_FW_MODE_SATA), +- MVEBU_A3700_COMPHY_CONF_GEN(2, PHY_MODE_USB_HOST_SS, 0, ++ MVEBU_A3700_COMPHY_CONF_GEN(2, PHY_MODE_SATA, COMPHY_FW_MODE_SATA), ++ MVEBU_A3700_COMPHY_CONF_GEN(2, PHY_MODE_USB_HOST_SS, + COMPHY_FW_MODE_USB3H), + }; + +@@ -98,7 +93,6 @@ struct mvebu_a3700_comphy_lane { + unsigned int id; + enum phy_mode mode; + int submode; +- int port; + }; + + static int mvebu_a3700_comphy_smc(unsigned long function, unsigned long lane, +@@ -120,7 +114,7 @@ static int mvebu_a3700_comphy_smc(unsign + } + } + +-static int mvebu_a3700_comphy_get_fw_mode(int lane, int port, ++static int mvebu_a3700_comphy_get_fw_mode(int lane, + enum phy_mode mode, + int submode) + { +@@ -132,7 +126,6 @@ static int mvebu_a3700_comphy_get_fw_mod + + for (i = 0; i < n; i++) { + if (mvebu_a3700_comphy_modes[i].lane == lane && +- mvebu_a3700_comphy_modes[i].port == port && + mvebu_a3700_comphy_modes[i].mode == mode && + mvebu_a3700_comphy_modes[i].submode == submode) + break; +@@ -153,7 +146,7 @@ static int mvebu_a3700_comphy_set_mode(s + if (submode == PHY_INTERFACE_MODE_1000BASEX) + submode = PHY_INTERFACE_MODE_SGMII; + +- fw_mode = mvebu_a3700_comphy_get_fw_mode(lane->id, lane->port, mode, ++ fw_mode = mvebu_a3700_comphy_get_fw_mode(lane->id, mode, + submode); + if (fw_mode < 0) { + dev_err(lane->dev, "invalid COMPHY mode\n"); +@@ -172,9 +165,10 @@ static int mvebu_a3700_comphy_power_on(s + struct mvebu_a3700_comphy_lane *lane = phy_get_drvdata(phy); + u32 fw_param; + int fw_mode; ++ int fw_port; + int ret; + +- fw_mode = mvebu_a3700_comphy_get_fw_mode(lane->id, lane->port, ++ fw_mode = mvebu_a3700_comphy_get_fw_mode(lane->id, + lane->mode, lane->submode); + if (fw_mode < 0) { + dev_err(lane->dev, "invalid COMPHY mode\n"); +@@ -191,17 +185,18 @@ static int mvebu_a3700_comphy_power_on(s + fw_param = COMPHY_FW_MODE(fw_mode); + break; + case PHY_MODE_ETHERNET: ++ fw_port = (lane->id == 0) ? 1 : 0; + switch (lane->submode) { + case PHY_INTERFACE_MODE_SGMII: + dev_dbg(lane->dev, "set lane %d to SGMII mode\n", + lane->id); +- fw_param = COMPHY_FW_NET(fw_mode, lane->port, ++ fw_param = COMPHY_FW_NET(fw_mode, fw_port, + COMPHY_FW_SPEED_1_25G); + break; + case PHY_INTERFACE_MODE_2500BASEX: + dev_dbg(lane->dev, "set lane %d to 2500BASEX mode\n", + lane->id); +- fw_param = COMPHY_FW_NET(fw_mode, lane->port, ++ fw_param = COMPHY_FW_NET(fw_mode, fw_port, + COMPHY_FW_SPEED_3_125G); + break; + default: +@@ -212,8 +207,7 @@ static int mvebu_a3700_comphy_power_on(s + break; + case PHY_MODE_PCIE: + dev_dbg(lane->dev, "set lane %d to PCIe mode\n", lane->id); +- fw_param = COMPHY_FW_PCIE(fw_mode, lane->port, +- COMPHY_FW_SPEED_5G, ++ fw_param = COMPHY_FW_PCIE(fw_mode, COMPHY_FW_SPEED_5G, + phy->attrs.bus_width); + break; + default: +@@ -247,17 +241,20 @@ static struct phy *mvebu_a3700_comphy_xl + struct of_phandle_args *args) + { + struct mvebu_a3700_comphy_lane *lane; ++ unsigned int port; + struct phy *phy; + +- if (WARN_ON(args->args[0] >= MVEBU_A3700_COMPHY_PORTS)) +- return ERR_PTR(-EINVAL); +- + phy = of_phy_simple_xlate(dev, args); + if (IS_ERR(phy)) + return phy; + + lane = phy_get_drvdata(phy); +- lane->port = args->args[0]; ++ ++ port = args->args[0]; ++ if (port != 0 && (port != 1 || lane->id != 0)) { ++ dev_err(lane->dev, "invalid port number %u\n", port); ++ return ERR_PTR(-EINVAL); ++ } + + return phy; + } +@@ -302,7 +299,6 @@ static int mvebu_a3700_comphy_probe(stru + lane->mode = PHY_MODE_INVALID; + lane->submode = PHY_INTERFACE_MODE_NA; + lane->id = lane_id; +- lane->port = -1; + phy_set_drvdata(phy, lane); + } + diff --git a/pkgs/patches-linux-5.15/851-0002-phy-marvell-phy-mvebu-a3700-comphy-Add-native-kernel.patch b/pkgs/patches-linux-5.15/851-0002-phy-marvell-phy-mvebu-a3700-comphy-Add-native-kernel.patch new file mode 100644 index 0000000..73ead1e --- /dev/null +++ b/pkgs/patches-linux-5.15/851-0002-phy-marvell-phy-mvebu-a3700-comphy-Add-native-kernel.patch @@ -0,0 +1,1564 @@ +From 9d276da259cce20b2ed7a868b6e6a6a205f7bb04 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org> +Date: Thu, 23 Sep 2021 19:20:13 +0200 +Subject: [PATCH] phy: marvell: phy-mvebu-a3700-comphy: Add native kernel + implementation +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Remove old RPC implementation and add a new native kernel implementation. + +The old implementation uses ARM SMC API to issue RPC calls to ARM Trusted +Firmware which provides real implementation of PHY configuration. + +But older versions of ARM Trusted Firmware do not provide this PHY +configuration functionality, simply returning: operation not supported; or +worse, some versions provide the configuration functionality incorrectly. + +For example the firmware shipped in ESPRESSObin board has this older +version of ARM Trusted Firmware and therefore SATA, USB 3.0 and PCIe +functionality do not work with newer versions of Linux kernel. + +Due to the above reasons, the following commits were introduced into Linux, +to workaround these issues by ignoring -EOPNOTSUPP error code from +phy-mvebu-a3700-comphy driver function phy_power_on(): + +commit 45aefe3d2251 ("ata: ahci: mvebu: Make SATA PHY optional for Armada +3720") +commit 3241929b67d2 ("usb: host: xhci: mvebu: make USB 3.0 PHY optional for +Armada 3720") +commit b0c6ae0f8948 ("PCI: aardvark: Fix initialization with old Marvell's +Arm Trusted Firmware") + +Replace this RPC implementation with proper native kernel implementation, +which is independent on the firmware. Never return -EOPNOTSUPP for proper +arguments. + +This should solve multiple issues with real-world boards, where it is not +possible or really inconvenient to change the firmware. Let's eliminate +these issues. + +This implementation is ported directly from Armada 3720 comphy driver found +in newest version of ARM Trusted Firmware source code, but with various +fixes of register names, some added comments, some refactoring due to the +original code not conforming to kernel standards. Also PCIe mode poweroff +support was added here, and PHY reset support. These changes are also going +to be sent to ARM Trusted Firmware. + +Signed-off-by: Pali Rohár <pali@kernel.org> +Acked-by: Miquel Raynal <miquel.raynal@bootlin.com> +[ Pali did the porting from ATF. + I (Marek) then fixed some register names, some various other things, + added some comments and refactored the code to kernel standards. Also + fixed PHY poweroff and added PHY reset. ] +Signed-off-by: Marek Behún <kabel@kernel.org> +--- + drivers/phy/marvell/phy-mvebu-a3700-comphy.c | 1351 ++++++++++++++++-- + 1 file changed, 1234 insertions(+), 117 deletions(-) + +--- a/drivers/phy/marvell/phy-mvebu-a3700-comphy.c ++++ b/drivers/phy/marvell/phy-mvebu-a3700-comphy.c +@@ -5,12 +5,16 @@ + * Authors: + * Evan Wang <xswang@marvell.com> + * Miquèl Raynal <miquel.raynal@bootlin.com> ++ * Pali Rohár <pali@kernel.org> ++ * Marek Behún <kabel@kernel.org> + * + * Structure inspired from phy-mvebu-cp110-comphy.c written by Antoine Tenart. +- * SMC call initial support done by Grzegorz Jaszczyk. ++ * Comphy code from ARM Trusted Firmware ported by Pali Rohár <pali@kernel.org> ++ * and Marek Behún <kabel@kernel.org>. + */ + +-#include <linux/arm-smccc.h> ++#include <linux/bitfield.h> ++#include <linux/clk.h> + #include <linux/io.h> + #include <linux/iopoll.h> + #include <linux/mfd/syscon.h> +@@ -18,103 +22,1147 @@ + #include <linux/phy.h> + #include <linux/phy/phy.h> + #include <linux/platform_device.h> ++#include <linux/spinlock.h> + +-#define MVEBU_A3700_COMPHY_LANES 3 ++#define PLL_SET_DELAY_US 600 ++#define COMPHY_PLL_SLEEP 1000 ++#define COMPHY_PLL_TIMEOUT 150000 ++ ++/* Comphy lane2 indirect access register offset */ ++#define COMPHY_LANE2_INDIR_ADDR 0x0 ++#define COMPHY_LANE2_INDIR_DATA 0x4 ++ ++/* SATA and USB3 PHY offset compared to SATA PHY */ ++#define COMPHY_LANE2_REGS_BASE 0x200 ++ ++/* ++ * When accessing common PHY lane registers directly, we need to shift by 1, ++ * since the registers are 16-bit. ++ */ ++#define COMPHY_LANE_REG_DIRECT(reg) (((reg) & 0x7FF) << 1) ++ ++/* COMPHY registers */ ++#define COMPHY_POWER_PLL_CTRL 0x01 ++#define PU_IVREF_BIT BIT(15) ++#define PU_PLL_BIT BIT(14) ++#define PU_RX_BIT BIT(13) ++#define PU_TX_BIT BIT(12) ++#define PU_TX_INTP_BIT BIT(11) ++#define PU_DFE_BIT BIT(10) ++#define RESET_DTL_RX_BIT BIT(9) ++#define PLL_LOCK_BIT BIT(8) ++#define REF_FREF_SEL_MASK GENMASK(4, 0) ++#define REF_FREF_SEL_SERDES_25MHZ FIELD_PREP(REF_FREF_SEL_MASK, 0x1) ++#define REF_FREF_SEL_SERDES_40MHZ FIELD_PREP(REF_FREF_SEL_MASK, 0x3) ++#define REF_FREF_SEL_SERDES_50MHZ FIELD_PREP(REF_FREF_SEL_MASK, 0x4) ++#define REF_FREF_SEL_PCIE_USB3_25MHZ FIELD_PREP(REF_FREF_SEL_MASK, 0x2) ++#define REF_FREF_SEL_PCIE_USB3_40MHZ FIELD_PREP(REF_FREF_SEL_MASK, 0x3) ++#define COMPHY_MODE_MASK GENMASK(7, 5) ++#define COMPHY_MODE_SATA FIELD_PREP(COMPHY_MODE_MASK, 0x0) ++#define COMPHY_MODE_PCIE FIELD_PREP(COMPHY_MODE_MASK, 0x3) ++#define COMPHY_MODE_SERDES FIELD_PREP(COMPHY_MODE_MASK, 0x4) ++#define COMPHY_MODE_USB3 FIELD_PREP(COMPHY_MODE_MASK, 0x5) ++ ++#define COMPHY_KVCO_CAL_CTRL 0x02 ++#define USE_MAX_PLL_RATE_BIT BIT(12) ++#define SPEED_PLL_MASK GENMASK(7, 2) ++#define SPEED_PLL_VALUE_16 FIELD_PREP(SPEED_PLL_MASK, 0x10) ++ ++#define COMPHY_DIG_LOOPBACK_EN 0x23 ++#define SEL_DATA_WIDTH_MASK GENMASK(11, 10) ++#define DATA_WIDTH_10BIT FIELD_PREP(SEL_DATA_WIDTH_MASK, 0x0) ++#define DATA_WIDTH_20BIT FIELD_PREP(SEL_DATA_WIDTH_MASK, 0x1) ++#define DATA_WIDTH_40BIT FIELD_PREP(SEL_DATA_WIDTH_MASK, 0x2) ++#define PLL_READY_TX_BIT BIT(4) ++ ++#define COMPHY_SYNC_PATTERN 0x24 ++#define TXD_INVERT_BIT BIT(10) ++#define RXD_INVERT_BIT BIT(11) ++ ++#define COMPHY_SYNC_MASK_GEN 0x25 ++#define PHY_GEN_MAX_MASK GENMASK(11, 10) ++#define PHY_GEN_MAX_USB3_5G FIELD_PREP(PHY_GEN_MAX_MASK, 0x1) ++ ++#define COMPHY_ISOLATION_CTRL 0x26 ++#define PHY_ISOLATE_MODE BIT(15) ++ ++#define COMPHY_GEN2_SET2 0x3e ++#define GS2_TX_SSC_AMP_MASK GENMASK(15, 9) ++#define GS2_TX_SSC_AMP_4128 FIELD_PREP(GS2_TX_SSC_AMP_MASK, 0x20) ++#define GS2_VREG_RXTX_MAS_ISET_MASK GENMASK(8, 7) ++#define GS2_VREG_RXTX_MAS_ISET_60U FIELD_PREP(GS2_VREG_RXTX_MAS_ISET_MASK,\ ++ 0x0) ++#define GS2_VREG_RXTX_MAS_ISET_80U FIELD_PREP(GS2_VREG_RXTX_MAS_ISET_MASK,\ ++ 0x1) ++#define GS2_VREG_RXTX_MAS_ISET_100U FIELD_PREP(GS2_VREG_RXTX_MAS_ISET_MASK,\ ++ 0x2) ++#define GS2_VREG_RXTX_MAS_ISET_120U FIELD_PREP(GS2_VREG_RXTX_MAS_ISET_MASK,\ ++ 0x3) ++#define GS2_RSVD_6_0_MASK GENMASK(6, 0) ++ ++#define COMPHY_GEN3_SET2 0x3f ++ ++#define COMPHY_IDLE_SYNC_EN 0x48 ++#define IDLE_SYNC_EN BIT(12) ++ ++#define COMPHY_MISC_CTRL0 0x4F ++#define CLK100M_125M_EN BIT(4) ++#define TXDCLK_2X_SEL BIT(6) ++#define CLK500M_EN BIT(7) ++#define PHY_REF_CLK_SEL BIT(10) ++ ++#define COMPHY_SFT_RESET 0x52 ++#define SFT_RST BIT(9) ++#define SFT_RST_NO_REG BIT(10) ++ ++#define COMPHY_MISC_CTRL1 0x73 ++#define SEL_BITS_PCIE_FORCE BIT(15) ++ ++#define COMPHY_GEN2_SET3 0x112 ++#define GS3_FFE_CAP_SEL_MASK GENMASK(3, 0) ++#define GS3_FFE_CAP_SEL_VALUE FIELD_PREP(GS3_FFE_CAP_SEL_MASK, 0xF) ++ ++/* PIPE registers */ ++#define COMPHY_PIPE_LANE_CFG0 0x180 ++#define PRD_TXDEEMPH0_MASK BIT(0) ++#define PRD_TXMARGIN_MASK GENMASK(3, 1) ++#define PRD_TXSWING_MASK BIT(4) ++#define CFG_TX_ALIGN_POS_MASK GENMASK(8, 5) ++ ++#define COMPHY_PIPE_LANE_CFG1 0x181 ++#define PRD_TXDEEMPH1_MASK BIT(15) ++#define USE_MAX_PLL_RATE_EN BIT(9) ++#define TX_DET_RX_MODE BIT(6) ++#define GEN2_TX_DATA_DLY_MASK GENMASK(4, 3) ++#define GEN2_TX_DATA_DLY_DEFT FIELD_PREP(GEN2_TX_DATA_DLY_MASK, 2) ++#define TX_ELEC_IDLE_MODE_EN BIT(0) ++ ++#define COMPHY_PIPE_LANE_STAT1 0x183 ++#define TXDCLK_PCLK_EN BIT(0) ++ ++#define COMPHY_PIPE_LANE_CFG4 0x188 ++#define SPREAD_SPECTRUM_CLK_EN BIT(7) ++ ++#define COMPHY_PIPE_RST_CLK_CTRL 0x1C1 ++#define PIPE_SOFT_RESET BIT(0) ++#define PIPE_REG_RESET BIT(1) ++#define MODE_CORE_CLK_FREQ_SEL BIT(9) ++#define MODE_PIPE_WIDTH_32 BIT(3) ++#define MODE_REFDIV_MASK GENMASK(5, 4) ++#define MODE_REFDIV_BY_4 FIELD_PREP(MODE_REFDIV_MASK, 0x2) ++ ++#define COMPHY_PIPE_TEST_MODE_CTRL 0x1C2 ++#define MODE_MARGIN_OVERRIDE BIT(2) ++ ++#define COMPHY_PIPE_CLK_SRC_LO 0x1C3 ++#define MODE_CLK_SRC BIT(0) ++#define BUNDLE_PERIOD_SEL BIT(1) ++#define BUNDLE_PERIOD_SCALE_MASK GENMASK(3, 2) ++#define BUNDLE_SAMPLE_CTRL BIT(4) ++#define PLL_READY_DLY_MASK GENMASK(7, 5) ++#define CFG_SEL_20B BIT(15) ++ ++#define COMPHY_PIPE_PWR_MGM_TIM1 0x1D0 ++#define CFG_PM_OSCCLK_WAIT_MASK GENMASK(15, 12) ++#define CFG_PM_RXDEN_WAIT_MASK GENMASK(11, 8) ++#define CFG_PM_RXDEN_WAIT_1_UNIT FIELD_PREP(CFG_PM_RXDEN_WAIT_MASK, 0x1) ++#define CFG_PM_RXDLOZ_WAIT_MASK GENMASK(7, 0) ++#define CFG_PM_RXDLOZ_WAIT_7_UNIT FIELD_PREP(CFG_PM_RXDLOZ_WAIT_MASK, 0x7) ++#define CFG_PM_RXDLOZ_WAIT_12_UNIT FIELD_PREP(CFG_PM_RXDLOZ_WAIT_MASK, 0xC) ++ ++/* ++ * This register is not from PHY lane register space. It only exists in the ++ * indirect register space, before the actual PHY lane 2 registers. So the ++ * offset is absolute, not relative to COMPHY_LANE2_REGS_BASE. ++ * It is used only for SATA PHY initialization. ++ */ ++#define COMPHY_RESERVED_REG 0x0E ++#define PHYCTRL_FRM_PIN_BIT BIT(13) + +-/* COMPHY Fast SMC function identifiers */ +-#define COMPHY_SIP_POWER_ON 0x82000001 +-#define COMPHY_SIP_POWER_OFF 0x82000002 +-#define COMPHY_SIP_PLL_LOCK 0x82000003 +- +-#define COMPHY_FW_MODE_SATA 0x1 +-#define COMPHY_FW_MODE_SGMII 0x2 +-#define COMPHY_FW_MODE_2500BASEX 0x3 +-#define COMPHY_FW_MODE_USB3H 0x4 +-#define COMPHY_FW_MODE_USB3D 0x5 +-#define COMPHY_FW_MODE_PCIE 0x6 +-#define COMPHY_FW_MODE_USB3 0xa +- +-#define COMPHY_FW_SPEED_1_25G 0 /* SGMII 1G */ +-#define COMPHY_FW_SPEED_2_5G 1 +-#define COMPHY_FW_SPEED_3_125G 2 /* 2500BASE-X */ +-#define COMPHY_FW_SPEED_5G 3 +-#define COMPHY_FW_SPEED_MAX 0x3F +- +-#define COMPHY_FW_MODE(mode) ((mode) << 12) +-#define COMPHY_FW_NET(mode, idx, speed) (COMPHY_FW_MODE(mode) | \ +- ((idx) << 8) | \ +- ((speed) << 2)) +-#define COMPHY_FW_PCIE(mode, speed, width) (COMPHY_FW_NET(mode, 0, speed) | \ +- ((width) << 18)) ++/* South Bridge PHY Configuration Registers */ ++#define COMPHY_PHY_REG(lane, reg) (((1 - (lane)) * 0x28) + ((reg) & 0x3f)) ++ ++/* ++ * lane0: USB3/GbE1 PHY Configuration 1 ++ * lane1: PCIe/GbE0 PHY Configuration 1 ++ * (used only by SGMII code) ++ */ ++#define COMPHY_PHY_CFG1 0x0 ++#define PIN_PU_IVREF_BIT BIT(1) ++#define PIN_RESET_CORE_BIT BIT(11) ++#define PIN_RESET_COMPHY_BIT BIT(12) ++#define PIN_PU_PLL_BIT BIT(16) ++#define PIN_PU_RX_BIT BIT(17) ++#define PIN_PU_TX_BIT BIT(18) ++#define PIN_TX_IDLE_BIT BIT(19) ++#define GEN_RX_SEL_MASK GENMASK(25, 22) ++#define GEN_RX_SEL_VALUE(val) FIELD_PREP(GEN_RX_SEL_MASK, (val)) ++#define GEN_TX_SEL_MASK GENMASK(29, 26) ++#define GEN_TX_SEL_VALUE(val) FIELD_PREP(GEN_TX_SEL_MASK, (val)) ++#define SERDES_SPEED_1_25_G 0x6 ++#define SERDES_SPEED_3_125_G 0x8 ++#define PHY_RX_INIT_BIT BIT(30) ++ ++/* ++ * lane0: USB3/GbE1 PHY Status 1 ++ * lane1: PCIe/GbE0 PHY Status 1 ++ * (used only by SGMII code) ++ */ ++#define COMPHY_PHY_STAT1 0x18 ++#define PHY_RX_INIT_DONE_BIT BIT(0) ++#define PHY_PLL_READY_RX_BIT BIT(2) ++#define PHY_PLL_READY_TX_BIT BIT(3) ++ ++/* PHY Selector */ ++#define COMPHY_SELECTOR_PHY_REG 0xFC ++/* bit0: 0: Lane1 is GbE0; 1: Lane1 is PCIe */ ++#define COMPHY_SELECTOR_PCIE_GBE0_SEL_BIT BIT(0) ++/* bit4: 0: Lane0 is GbE1; 1: Lane0 is USB3 */ ++#define COMPHY_SELECTOR_USB3_GBE1_SEL_BIT BIT(4) ++/* bit8: 0: Lane0 is USB3 instead of GbE1, Lane2 is SATA; 1: Lane2 is USB3 */ ++#define COMPHY_SELECTOR_USB3_PHY_SEL_BIT BIT(8) + + struct mvebu_a3700_comphy_conf { + unsigned int lane; + enum phy_mode mode; + int submode; +- u32 fw_mode; + }; + +-#define MVEBU_A3700_COMPHY_CONF(_lane, _mode, _smode, _fw) \ ++#define MVEBU_A3700_COMPHY_CONF(_lane, _mode, _smode) \ + { \ + .lane = _lane, \ + .mode = _mode, \ + .submode = _smode, \ +- .fw_mode = _fw, \ + } + +-#define MVEBU_A3700_COMPHY_CONF_GEN(_lane, _mode, _fw) \ +- MVEBU_A3700_COMPHY_CONF(_lane, _mode, PHY_INTERFACE_MODE_NA, _fw) ++#define MVEBU_A3700_COMPHY_CONF_GEN(_lane, _mode) \ ++ MVEBU_A3700_COMPHY_CONF(_lane, _mode, PHY_INTERFACE_MODE_NA) + +-#define MVEBU_A3700_COMPHY_CONF_ETH(_lane, _smode, _fw) \ +- MVEBU_A3700_COMPHY_CONF(_lane, PHY_MODE_ETHERNET, _smode, _fw) ++#define MVEBU_A3700_COMPHY_CONF_ETH(_lane, _smode) \ ++ MVEBU_A3700_COMPHY_CONF(_lane, PHY_MODE_ETHERNET, _smode) + + static const struct mvebu_a3700_comphy_conf mvebu_a3700_comphy_modes[] = { + /* lane 0 */ +- MVEBU_A3700_COMPHY_CONF_GEN(0, PHY_MODE_USB_HOST_SS, +- COMPHY_FW_MODE_USB3H), +- MVEBU_A3700_COMPHY_CONF_ETH(0, PHY_INTERFACE_MODE_SGMII, +- COMPHY_FW_MODE_SGMII), +- MVEBU_A3700_COMPHY_CONF_ETH(0, PHY_INTERFACE_MODE_2500BASEX, +- COMPHY_FW_MODE_2500BASEX), ++ MVEBU_A3700_COMPHY_CONF_GEN(0, PHY_MODE_USB_HOST_SS), ++ MVEBU_A3700_COMPHY_CONF_ETH(0, PHY_INTERFACE_MODE_SGMII), ++ MVEBU_A3700_COMPHY_CONF_ETH(0, PHY_INTERFACE_MODE_1000BASEX), ++ MVEBU_A3700_COMPHY_CONF_ETH(0, PHY_INTERFACE_MODE_2500BASEX), + /* lane 1 */ +- MVEBU_A3700_COMPHY_CONF_GEN(1, PHY_MODE_PCIE, COMPHY_FW_MODE_PCIE), +- MVEBU_A3700_COMPHY_CONF_ETH(1, PHY_INTERFACE_MODE_SGMII, +- COMPHY_FW_MODE_SGMII), +- MVEBU_A3700_COMPHY_CONF_ETH(1, PHY_INTERFACE_MODE_2500BASEX, +- COMPHY_FW_MODE_2500BASEX), ++ MVEBU_A3700_COMPHY_CONF_GEN(1, PHY_MODE_PCIE), ++ MVEBU_A3700_COMPHY_CONF_ETH(1, PHY_INTERFACE_MODE_SGMII), ++ MVEBU_A3700_COMPHY_CONF_ETH(1, PHY_INTERFACE_MODE_1000BASEX), ++ MVEBU_A3700_COMPHY_CONF_ETH(1, PHY_INTERFACE_MODE_2500BASEX), + /* lane 2 */ +- MVEBU_A3700_COMPHY_CONF_GEN(2, PHY_MODE_SATA, COMPHY_FW_MODE_SATA), +- MVEBU_A3700_COMPHY_CONF_GEN(2, PHY_MODE_USB_HOST_SS, +- COMPHY_FW_MODE_USB3H), ++ MVEBU_A3700_COMPHY_CONF_GEN(2, PHY_MODE_SATA), ++ MVEBU_A3700_COMPHY_CONF_GEN(2, PHY_MODE_USB_HOST_SS), ++}; ++ ++struct mvebu_a3700_comphy_priv { ++ void __iomem *comphy_regs; ++ void __iomem *lane0_phy_regs; /* USB3 and GbE1 */ ++ void __iomem *lane1_phy_regs; /* PCIe and GbE0 */ ++ void __iomem *lane2_phy_indirect; /* SATA and USB3 */ ++ spinlock_t lock; /* for PHY selector access */ ++ bool xtal_is_40m; + }; + + struct mvebu_a3700_comphy_lane { ++ struct mvebu_a3700_comphy_priv *priv; + struct device *dev; + unsigned int id; + enum phy_mode mode; + int submode; ++ bool invert_tx; ++ bool invert_rx; ++ bool needs_reset; ++}; ++ ++struct gbe_phy_init_data_fix { ++ u16 addr; ++ u16 value; ++}; ++ ++/* Changes to 40M1G25 mode data required for running 40M3G125 init mode */ ++static struct gbe_phy_init_data_fix gbe_phy_init_fix[] = { ++ { 0x005, 0x07CC }, { 0x015, 0x0000 }, { 0x01B, 0x0000 }, ++ { 0x01D, 0x0000 }, { 0x01E, 0x0000 }, { 0x01F, 0x0000 }, ++ { 0x020, 0x0000 }, { 0x021, 0x0030 }, { 0x026, 0x0888 }, ++ { 0x04D, 0x0152 }, { 0x04F, 0xA020 }, { 0x050, 0x07CC }, ++ { 0x053, 0xE9CA }, { 0x055, 0xBD97 }, { 0x071, 0x3015 }, ++ { 0x076, 0x03AA }, { 0x07C, 0x0FDF }, { 0x0C2, 0x3030 }, ++ { 0x0C3, 0x8000 }, { 0x0E2, 0x5550 }, { 0x0E3, 0x12A4 }, ++ { 0x0E4, 0x7D00 }, { 0x0E6, 0x0C83 }, { 0x101, 0xFCC0 }, ++ { 0x104, 0x0C10 } + }; + +-static int mvebu_a3700_comphy_smc(unsigned long function, unsigned long lane, +- unsigned long mode) ++/* 40M1G25 mode init data */ ++static u16 gbe_phy_init[512] = { ++ /* 0 1 2 3 4 5 6 7 */ ++ /*-----------------------------------------------------------*/ ++ /* 8 9 A B C D E F */ ++ 0x3110, 0xFD83, 0x6430, 0x412F, 0x82C0, 0x06FA, 0x4500, 0x6D26, /* 00 */ ++ 0xAFC0, 0x8000, 0xC000, 0x0000, 0x2000, 0x49CC, 0x0BC9, 0x2A52, /* 08 */ ++ 0x0BD2, 0x0CDE, 0x13D2, 0x0CE8, 0x1149, 0x10E0, 0x0000, 0x0000, /* 10 */ ++ 0x0000, 0x0000, 0x0000, 0x0001, 0x0000, 0x4134, 0x0D2D, 0xFFFF, /* 18 */ ++ 0xFFE0, 0x4030, 0x1016, 0x0030, 0x0000, 0x0800, 0x0866, 0x0000, /* 20 */ ++ 0x0000, 0x0000, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, /* 28 */ ++ 0xFFFF, 0xFFFF, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /* 30 */ ++ 0x0000, 0x0000, 0x000F, 0x6A62, 0x1988, 0x3100, 0x3100, 0x3100, /* 38 */ ++ 0x3100, 0xA708, 0x2430, 0x0830, 0x1030, 0x4610, 0xFF00, 0xFF00, /* 40 */ ++ 0x0060, 0x1000, 0x0400, 0x0040, 0x00F0, 0x0155, 0x1100, 0xA02A, /* 48 */ ++ 0x06FA, 0x0080, 0xB008, 0xE3ED, 0x5002, 0xB592, 0x7A80, 0x0001, /* 50 */ ++ 0x020A, 0x8820, 0x6014, 0x8054, 0xACAA, 0xFC88, 0x2A02, 0x45CF, /* 58 */ ++ 0x000F, 0x1817, 0x2860, 0x064F, 0x0000, 0x0204, 0x1800, 0x6000, /* 60 */ ++ 0x810F, 0x4F23, 0x4000, 0x4498, 0x0850, 0x0000, 0x000E, 0x1002, /* 68 */ ++ 0x9D3A, 0x3009, 0xD066, 0x0491, 0x0001, 0x6AB0, 0x0399, 0x3780, /* 70 */ ++ 0x0040, 0x5AC0, 0x4A80, 0x0000, 0x01DF, 0x0000, 0x0007, 0x0000, /* 78 */ ++ 0x2D54, 0x00A1, 0x4000, 0x0100, 0xA20A, 0x0000, 0x0000, 0x0000, /* 80 */ ++ 0x0000, 0x0000, 0x0000, 0x7400, 0x0E81, 0x1000, 0x1242, 0x0210, /* 88 */ ++ 0x80DF, 0x0F1F, 0x2F3F, 0x4F5F, 0x6F7F, 0x0F1F, 0x2F3F, 0x4F5F, /* 90 */ ++ 0x6F7F, 0x4BAD, 0x0000, 0x0000, 0x0800, 0x0000, 0x2400, 0xB651, /* 98 */ ++ 0xC9E0, 0x4247, 0x0A24, 0x0000, 0xAF19, 0x1004, 0x0000, 0x0000, /* A0 */ ++ 0x0000, 0x0013, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /* A8 */ ++ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /* B0 */ ++ 0x0000, 0x0000, 0x0000, 0x0060, 0x0000, 0x0000, 0x0000, 0x0000, /* B8 */ ++ 0x0000, 0x0000, 0x3010, 0xFA00, 0x0000, 0x0000, 0x0000, 0x0003, /* C0 */ ++ 0x1618, 0x8200, 0x8000, 0x0400, 0x050F, 0x0000, 0x0000, 0x0000, /* C8 */ ++ 0x4C93, 0x0000, 0x1000, 0x1120, 0x0010, 0x1242, 0x1242, 0x1E00, /* D0 */ ++ 0x0000, 0x0000, 0x0000, 0x00F8, 0x0000, 0x0041, 0x0800, 0x0000, /* D8 */ ++ 0x82A0, 0x572E, 0x2490, 0x14A9, 0x4E00, 0x0000, 0x0803, 0x0541, /* E0 */ ++ 0x0C15, 0x0000, 0x0000, 0x0400, 0x2626, 0x0000, 0x0000, 0x4200, /* E8 */ ++ 0x0000, 0xAA55, 0x1020, 0x0000, 0x0000, 0x5010, 0x0000, 0x0000, /* F0 */ ++ 0x0000, 0x0000, 0x5000, 0x0000, 0x0000, 0x0000, 0x02F2, 0x0000, /* F8 */ ++ 0x101F, 0xFDC0, 0x4000, 0x8010, 0x0110, 0x0006, 0x0000, 0x0000, /*100 */ ++ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /*108 */ ++ 0x04CF, 0x0000, 0x04CF, 0x0000, 0x04CF, 0x0000, 0x04C6, 0x0000, /*110 */ ++ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /*118 */ ++ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /*120 */ ++ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /*128 */ ++ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /*130 */ ++ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /*138 */ ++ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /*140 */ ++ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /*148 */ ++ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /*150 */ ++ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /*158 */ ++ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /*160 */ ++ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /*168 */ ++ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /*170 */ ++ 0x0000, 0x0000, 0x0000, 0x00F0, 0x08A2, 0x3112, 0x0A14, 0x0000, /*178 */ ++ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /*180 */ ++ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /*188 */ ++ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /*190 */ ++ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /*198 */ ++ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /*1A0 */ ++ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /*1A8 */ ++ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /*1B0 */ ++ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /*1B8 */ ++ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /*1C0 */ ++ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /*1C8 */ ++ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /*1D0 */ ++ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /*1D8 */ ++ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /*1E0 */ ++ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /*1E8 */ ++ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /*1F0 */ ++ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000 /*1F8 */ ++}; ++ ++static inline void comphy_reg_set(void __iomem *addr, u32 data, u32 mask) + { +- struct arm_smccc_res res; +- s32 ret; ++ u32 val; + +- arm_smccc_smc(function, lane, mode, 0, 0, 0, 0, 0, &res); +- ret = res.a0; ++ val = readl(addr); ++ val = (val & ~mask) | (data & mask); ++ writel(val, addr); ++} + +- switch (ret) { +- case SMCCC_RET_SUCCESS: +- return 0; +- case SMCCC_RET_NOT_SUPPORTED: +- return -EOPNOTSUPP; ++static inline void comphy_reg_set16(void __iomem *addr, u16 data, u16 mask) ++{ ++ u16 val; ++ ++ val = readw(addr); ++ val = (val & ~mask) | (data & mask); ++ writew(val, addr); ++} ++ ++/* Used for accessing lane 2 registers (SATA/USB3 PHY) */ ++static void comphy_set_indirect(struct mvebu_a3700_comphy_priv *priv, ++ u32 offset, u16 data, u16 mask) ++{ ++ writel(offset, ++ priv->lane2_phy_indirect + COMPHY_LANE2_INDIR_ADDR); ++ comphy_reg_set(priv->lane2_phy_indirect + COMPHY_LANE2_INDIR_DATA, ++ data, mask); ++} ++ ++static void comphy_lane_reg_set(struct mvebu_a3700_comphy_lane *lane, ++ u16 reg, u16 data, u16 mask) ++{ ++ if (lane->id == 2) { ++ /* lane 2 PHY registers are accessed indirectly */ ++ comphy_set_indirect(lane->priv, ++ reg + COMPHY_LANE2_REGS_BASE, ++ data, mask); ++ } else { ++ void __iomem *base = lane->id == 1 ? ++ lane->priv->lane1_phy_regs : ++ lane->priv->lane0_phy_regs; ++ ++ comphy_reg_set16(base + COMPHY_LANE_REG_DIRECT(reg), ++ data, mask); ++ } ++} ++ ++static int comphy_lane_reg_poll(struct mvebu_a3700_comphy_lane *lane, ++ u16 reg, u16 bits, ++ ulong sleep_us, ulong timeout_us) ++{ ++ int ret; ++ ++ if (lane->id == 2) { ++ u32 data; ++ ++ /* lane 2 PHY registers are accessed indirectly */ ++ writel(reg + COMPHY_LANE2_REGS_BASE, ++ lane->priv->lane2_phy_indirect + ++ COMPHY_LANE2_INDIR_ADDR); ++ ++ ret = readl_poll_timeout(lane->priv->lane2_phy_indirect + ++ COMPHY_LANE2_INDIR_DATA, ++ data, (data & bits) == bits, ++ sleep_us, timeout_us); ++ } else { ++ void __iomem *base = lane->id == 1 ? ++ lane->priv->lane1_phy_regs : ++ lane->priv->lane0_phy_regs; ++ u16 data; ++ ++ ret = readw_poll_timeout(base + COMPHY_LANE_REG_DIRECT(reg), ++ data, (data & bits) == bits, ++ sleep_us, timeout_us); ++ } ++ ++ return ret; ++} ++ ++static void comphy_periph_reg_set(struct mvebu_a3700_comphy_lane *lane, ++ u8 reg, u32 data, u32 mask) ++{ ++ comphy_reg_set(lane->priv->comphy_regs + COMPHY_PHY_REG(lane->id, reg), ++ data, mask); ++} ++ ++static int comphy_periph_reg_poll(struct mvebu_a3700_comphy_lane *lane, ++ u8 reg, u32 bits, ++ ulong sleep_us, ulong timeout_us) ++{ ++ u32 data; ++ ++ return readl_poll_timeout(lane->priv->comphy_regs + ++ COMPHY_PHY_REG(lane->id, reg), ++ data, (data & bits) == bits, ++ sleep_us, timeout_us); ++} ++ ++/* PHY selector configures with corresponding modes */ ++static int ++mvebu_a3700_comphy_set_phy_selector(struct mvebu_a3700_comphy_lane *lane) ++{ ++ u32 old, new, clr = 0, set = 0; ++ unsigned long flags; ++ ++ switch (lane->mode) { ++ case PHY_MODE_SATA: ++ /* SATA must be in Lane2 */ ++ if (lane->id == 2) ++ clr = COMPHY_SELECTOR_USB3_PHY_SEL_BIT; ++ else ++ goto error; ++ break; ++ ++ case PHY_MODE_ETHERNET: ++ if (lane->id == 0) ++ clr = COMPHY_SELECTOR_USB3_GBE1_SEL_BIT; ++ else if (lane->id == 1) ++ clr = COMPHY_SELECTOR_PCIE_GBE0_SEL_BIT; ++ else ++ goto error; ++ break; ++ ++ case PHY_MODE_USB_HOST_SS: ++ if (lane->id == 2) ++ set = COMPHY_SELECTOR_USB3_PHY_SEL_BIT; ++ else if (lane->id == 0) ++ set = COMPHY_SELECTOR_USB3_GBE1_SEL_BIT; ++ else ++ goto error; ++ break; ++ ++ case PHY_MODE_PCIE: ++ /* PCIE must be in Lane1 */ ++ if (lane->id == 1) ++ set = COMPHY_SELECTOR_PCIE_GBE0_SEL_BIT; ++ else ++ goto error; ++ break; ++ ++ default: ++ goto error; ++ } ++ ++ spin_lock_irqsave(&lane->priv->lock, flags); ++ ++ old = readl(lane->priv->comphy_regs + COMPHY_SELECTOR_PHY_REG); ++ new = (old & ~clr) | set; ++ writel(new, lane->priv->comphy_regs + COMPHY_SELECTOR_PHY_REG); ++ ++ spin_unlock_irqrestore(&lane->priv->lock, flags); ++ ++ dev_dbg(lane->dev, ++ "COMPHY[%d] mode[%d] changed PHY selector 0x%08x -> 0x%08x\n", ++ lane->id, lane->mode, old, new); ++ ++ return 0; ++error: ++ dev_err(lane->dev, "COMPHY[%d] mode[%d] is invalid\n", lane->id, ++ lane->mode); ++ return -EINVAL; ++} ++ ++static int ++mvebu_a3700_comphy_sata_power_on(struct mvebu_a3700_comphy_lane *lane) ++{ ++ u32 mask, data, ref_clk; ++ int ret; ++ ++ /* Configure phy selector for SATA */ ++ ret = mvebu_a3700_comphy_set_phy_selector(lane); ++ if (ret) ++ return ret; ++ ++ /* Clear phy isolation mode to make it work in normal mode */ ++ comphy_lane_reg_set(lane, COMPHY_ISOLATION_CTRL, ++ 0x0, PHY_ISOLATE_MODE); ++ ++ /* 0. Check the Polarity invert bits */ ++ data = 0x0; ++ if (lane->invert_tx) ++ data |= TXD_INVERT_BIT; ++ if (lane->invert_rx) ++ data |= RXD_INVERT_BIT; ++ mask = TXD_INVERT_BIT | RXD_INVERT_BIT; ++ comphy_lane_reg_set(lane, COMPHY_SYNC_PATTERN, data, mask); ++ ++ /* 1. Select 40-bit data width */ ++ comphy_lane_reg_set(lane, COMPHY_DIG_LOOPBACK_EN, ++ DATA_WIDTH_40BIT, SEL_DATA_WIDTH_MASK); ++ ++ /* 2. Select reference clock(25M) and PHY mode (SATA) */ ++ if (lane->priv->xtal_is_40m) ++ ref_clk = REF_FREF_SEL_SERDES_40MHZ; ++ else ++ ref_clk = REF_FREF_SEL_SERDES_25MHZ; ++ ++ data = ref_clk | COMPHY_MODE_SATA; ++ mask = REF_FREF_SEL_MASK | COMPHY_MODE_MASK; ++ comphy_lane_reg_set(lane, COMPHY_POWER_PLL_CTRL, data, mask); ++ ++ /* 3. Use maximum PLL rate (no power save) */ ++ comphy_lane_reg_set(lane, COMPHY_KVCO_CAL_CTRL, ++ USE_MAX_PLL_RATE_BIT, USE_MAX_PLL_RATE_BIT); ++ ++ /* 4. Reset reserved bit */ ++ comphy_set_indirect(lane->priv, COMPHY_RESERVED_REG, ++ 0x0, PHYCTRL_FRM_PIN_BIT); ++ ++ /* 5. Set vendor-specific configuration (It is done in sata driver) */ ++ /* XXX: in U-Boot below sequence was executed in this place, in Linux ++ * not. Now it is done only in U-Boot before this comphy ++ * initialization - tests shows that it works ok, but in case of any ++ * future problem it is left for reference. ++ * reg_set(MVEBU_REGS_BASE + 0xe00a0, 0, 0xffffffff); ++ * reg_set(MVEBU_REGS_BASE + 0xe00a4, BIT(6), BIT(6)); ++ */ ++ ++ /* Wait for > 55 us to allow PLL be enabled */ ++ udelay(PLL_SET_DELAY_US); ++ ++ /* Polling status */ ++ ret = comphy_lane_reg_poll(lane, COMPHY_DIG_LOOPBACK_EN, ++ PLL_READY_TX_BIT, COMPHY_PLL_SLEEP, ++ COMPHY_PLL_TIMEOUT); ++ if (ret) { ++ dev_err(lane->dev, "Failed to lock SATA PLL\n"); ++ return ret; ++ } ++ ++ return 0; ++} ++ ++static void comphy_gbe_phy_init(struct mvebu_a3700_comphy_lane *lane, ++ bool is_1gbps) ++{ ++ int addr, fix_idx; ++ u16 val; ++ ++ fix_idx = 0; ++ for (addr = 0; addr < 512; addr++) { ++ /* ++ * All PHY register values are defined in full for 3.125Gbps ++ * SERDES speed. The values required for 1.25 Gbps are almost ++ * the same and only few registers should be "fixed" in ++ * comparison to 3.125 Gbps values. These register values are ++ * stored in "gbe_phy_init_fix" array. ++ */ ++ if (!is_1gbps && gbe_phy_init_fix[fix_idx].addr == addr) { ++ /* Use new value */ ++ val = gbe_phy_init_fix[fix_idx].value; ++ if (fix_idx < ARRAY_SIZE(gbe_phy_init_fix)) ++ fix_idx++; ++ } else { ++ val = gbe_phy_init[addr]; ++ } ++ ++ comphy_lane_reg_set(lane, addr, val, 0xFFFF); ++ } ++} ++ ++static int ++mvebu_a3700_comphy_ethernet_power_on(struct mvebu_a3700_comphy_lane *lane) ++{ ++ u32 mask, data, speed_sel; ++ int ret; ++ ++ /* Set selector */ ++ ret = mvebu_a3700_comphy_set_phy_selector(lane); ++ if (ret) ++ return ret; ++ ++ /* ++ * 1. Reset PHY by setting PHY input port PIN_RESET=1. ++ * 2. Set PHY input port PIN_TX_IDLE=1, PIN_PU_IVREF=1 to keep ++ * PHY TXP/TXN output to idle state during PHY initialization ++ * 3. Set PHY input port PIN_PU_PLL=0, PIN_PU_RX=0, PIN_PU_TX=0. ++ */ ++ data = PIN_PU_IVREF_BIT | PIN_TX_IDLE_BIT | PIN_RESET_COMPHY_BIT; ++ mask = data | PIN_RESET_CORE_BIT | PIN_PU_PLL_BIT | PIN_PU_RX_BIT | ++ PIN_PU_TX_BIT | PHY_RX_INIT_BIT; ++ comphy_periph_reg_set(lane, COMPHY_PHY_CFG1, data, mask); ++ ++ /* 4. Release reset to the PHY by setting PIN_RESET=0. */ ++ data = 0x0; ++ mask = PIN_RESET_COMPHY_BIT; ++ comphy_periph_reg_set(lane, COMPHY_PHY_CFG1, data, mask); ++ ++ /* ++ * 5. Set PIN_PHY_GEN_TX[3:0] and PIN_PHY_GEN_RX[3:0] to decide COMPHY ++ * bit rate ++ */ ++ switch (lane->submode) { ++ case PHY_INTERFACE_MODE_SGMII: ++ case PHY_INTERFACE_MODE_1000BASEX: ++ /* SGMII 1G, SerDes speed 1.25G */ ++ speed_sel = SERDES_SPEED_1_25_G; ++ break; ++ case PHY_INTERFACE_MODE_2500BASEX: ++ /* 2500Base-X, SerDes speed 3.125G */ ++ speed_sel = SERDES_SPEED_3_125_G; ++ break; + default: ++ /* Other rates are not supported */ ++ dev_err(lane->dev, ++ "unsupported phy speed %d on comphy lane%d\n", ++ lane->submode, lane->id); + return -EINVAL; + } ++ data = GEN_RX_SEL_VALUE(speed_sel) | GEN_TX_SEL_VALUE(speed_sel); ++ mask = GEN_RX_SEL_MASK | GEN_TX_SEL_MASK; ++ comphy_periph_reg_set(lane, COMPHY_PHY_CFG1, data, mask); ++ ++ /* ++ * 6. Wait 10mS for bandgap and reference clocks to stabilize; then ++ * start SW programming. ++ */ ++ mdelay(10); ++ ++ /* 7. Program COMPHY register PHY_MODE */ ++ data = COMPHY_MODE_SERDES; ++ mask = COMPHY_MODE_MASK; ++ comphy_lane_reg_set(lane, COMPHY_POWER_PLL_CTRL, data, mask); ++ ++ /* ++ * 8. Set COMPHY register REFCLK_SEL to select the correct REFCLK ++ * source ++ */ ++ data = 0x0; ++ mask = PHY_REF_CLK_SEL; ++ comphy_lane_reg_set(lane, COMPHY_MISC_CTRL0, data, mask); ++ ++ /* ++ * 9. Set correct reference clock frequency in COMPHY register ++ * REF_FREF_SEL. ++ */ ++ if (lane->priv->xtal_is_40m) ++ data = REF_FREF_SEL_SERDES_50MHZ; ++ else ++ data = REF_FREF_SEL_SERDES_25MHZ; ++ ++ mask = REF_FREF_SEL_MASK; ++ comphy_lane_reg_set(lane, COMPHY_POWER_PLL_CTRL, data, mask); ++ ++ /* 10. Program COMPHY register PHY_GEN_MAX[1:0] ++ * This step is mentioned in the flow received from verification team. ++ * However the PHY_GEN_MAX value is only meaningful for other interfaces ++ * (not SERDES). For instance, it selects SATA speed 1.5/3/6 Gbps or ++ * PCIe speed 2.5/5 Gbps ++ */ ++ ++ /* ++ * 11. Program COMPHY register SEL_BITS to set correct parallel data ++ * bus width ++ */ ++ data = DATA_WIDTH_10BIT; ++ mask = SEL_DATA_WIDTH_MASK; ++ comphy_lane_reg_set(lane, COMPHY_DIG_LOOPBACK_EN, data, mask); ++ ++ /* ++ * 12. As long as DFE function needs to be enabled in any mode, ++ * COMPHY register DFE_UPDATE_EN[5:0] shall be programmed to 0x3F ++ * for real chip during COMPHY power on. ++ * The step 14 exists (and empty) in the original initialization flow ++ * obtained from the verification team. According to the functional ++ * specification DFE_UPDATE_EN already has the default value 0x3F ++ */ ++ ++ /* ++ * 13. Program COMPHY GEN registers. ++ * These registers should be programmed based on the lab testing result ++ * to achieve optimal performance. Please contact the CEA group to get ++ * the related GEN table during real chip bring-up. We only required to ++ * run though the entire registers programming flow defined by ++ * "comphy_gbe_phy_init" when the REF clock is 40 MHz. For REF clock ++ * 25 MHz the default values stored in PHY registers are OK. ++ */ ++ dev_dbg(lane->dev, "Running C-DPI phy init %s mode\n", ++ lane->submode == PHY_INTERFACE_MODE_2500BASEX ? "2G5" : "1G"); ++ if (lane->priv->xtal_is_40m) ++ comphy_gbe_phy_init(lane, ++ lane->submode != PHY_INTERFACE_MODE_2500BASEX); ++ ++ /* ++ * 14. [Simulation Only] should not be used for real chip. ++ * By pass power up calibration by programming EXT_FORCE_CAL_DONE ++ * (R02h[9]) to 1 to shorten COMPHY simulation time. ++ */ ++ ++ /* ++ * 15. [Simulation Only: should not be used for real chip] ++ * Program COMPHY register FAST_DFE_TIMER_EN=1 to shorten RX training ++ * simulation time. ++ */ ++ ++ /* ++ * 16. Check the PHY Polarity invert bit ++ */ ++ data = 0x0; ++ if (lane->invert_tx) ++ data |= TXD_INVERT_BIT; ++ if (lane->invert_rx) ++ data |= RXD_INVERT_BIT; ++ mask = TXD_INVERT_BIT | RXD_INVERT_BIT; ++ comphy_lane_reg_set(lane, COMPHY_SYNC_PATTERN, data, mask); ++ ++ /* ++ * 17. Set PHY input ports PIN_PU_PLL, PIN_PU_TX and PIN_PU_RX to 1 to ++ * start PHY power up sequence. All the PHY register programming should ++ * be done before PIN_PU_PLL=1. There should be no register programming ++ * for normal PHY operation from this point. ++ */ ++ data = PIN_PU_PLL_BIT | PIN_PU_RX_BIT | PIN_PU_TX_BIT; ++ mask = data; ++ comphy_periph_reg_set(lane, COMPHY_PHY_CFG1, data, mask); ++ ++ /* ++ * 18. Wait for PHY power up sequence to finish by checking output ports ++ * PIN_PLL_READY_TX=1 and PIN_PLL_READY_RX=1. ++ */ ++ ret = comphy_periph_reg_poll(lane, COMPHY_PHY_STAT1, ++ PHY_PLL_READY_TX_BIT | ++ PHY_PLL_READY_RX_BIT, ++ COMPHY_PLL_SLEEP, COMPHY_PLL_TIMEOUT); ++ if (ret) { ++ dev_err(lane->dev, "Failed to lock PLL for SERDES PHY %d\n", ++ lane->id); ++ return ret; ++ } ++ ++ /* ++ * 19. Set COMPHY input port PIN_TX_IDLE=0 ++ */ ++ comphy_periph_reg_set(lane, COMPHY_PHY_CFG1, 0x0, PIN_TX_IDLE_BIT); ++ ++ /* ++ * 20. After valid data appear on PIN_RXDATA bus, set PIN_RX_INIT=1. To ++ * start RX initialization. PIN_RX_INIT_DONE will be cleared to 0 by the ++ * PHY After RX initialization is done, PIN_RX_INIT_DONE will be set to ++ * 1 by COMPHY Set PIN_RX_INIT=0 after PIN_RX_INIT_DONE= 1. Please ++ * refer to RX initialization part for details. ++ */ ++ comphy_periph_reg_set(lane, COMPHY_PHY_CFG1, ++ PHY_RX_INIT_BIT, PHY_RX_INIT_BIT); ++ ++ ret = comphy_periph_reg_poll(lane, COMPHY_PHY_STAT1, ++ PHY_PLL_READY_TX_BIT | ++ PHY_PLL_READY_RX_BIT, ++ COMPHY_PLL_SLEEP, COMPHY_PLL_TIMEOUT); ++ if (ret) { ++ dev_err(lane->dev, "Failed to lock PLL for SERDES PHY %d\n", ++ lane->id); ++ return ret; ++ } ++ ++ ret = comphy_periph_reg_poll(lane, COMPHY_PHY_STAT1, ++ PHY_RX_INIT_DONE_BIT, ++ COMPHY_PLL_SLEEP, COMPHY_PLL_TIMEOUT); ++ if (ret) { ++ dev_err(lane->dev, "Failed to init RX of SERDES PHY %d\n", ++ lane->id); ++ return ret; ++ } ++ ++ return 0; + } + +-static int mvebu_a3700_comphy_get_fw_mode(int lane, ++static int ++mvebu_a3700_comphy_usb3_power_on(struct mvebu_a3700_comphy_lane *lane) ++{ ++ u32 mask, data, cfg, ref_clk; ++ int ret; ++ ++ /* Set phy seclector */ ++ ret = mvebu_a3700_comphy_set_phy_selector(lane); ++ if (ret) ++ return ret; ++ ++ /* ++ * 0. Set PHY OTG Control(0x5d034), bit 4, Power up OTG module The ++ * register belong to UTMI module, so it is set in UTMI phy driver. ++ */ ++ ++ /* ++ * 1. Set PRD_TXDEEMPH (3.5db de-emph) ++ */ ++ data = PRD_TXDEEMPH0_MASK; ++ mask = PRD_TXDEEMPH0_MASK | PRD_TXMARGIN_MASK | PRD_TXSWING_MASK | ++ CFG_TX_ALIGN_POS_MASK; ++ comphy_lane_reg_set(lane, COMPHY_PIPE_LANE_CFG0, data, mask); ++ ++ /* ++ * 2. Set BIT0: enable transmitter in high impedance mode ++ * Set BIT[3:4]: delay 2 clock cycles for HiZ off latency ++ * Set BIT6: Tx detect Rx at HiZ mode ++ * Unset BIT15: set to 0 to set USB3 De-emphasize level to -3.5db ++ * together with bit 0 of COMPHY_PIPE_LANE_CFG0 register ++ */ ++ data = TX_DET_RX_MODE | GEN2_TX_DATA_DLY_DEFT | TX_ELEC_IDLE_MODE_EN; ++ mask = PRD_TXDEEMPH1_MASK | TX_DET_RX_MODE | GEN2_TX_DATA_DLY_MASK | ++ TX_ELEC_IDLE_MODE_EN; ++ comphy_lane_reg_set(lane, COMPHY_PIPE_LANE_CFG1, data, mask); ++ ++ /* ++ * 3. Set Spread Spectrum Clock Enabled ++ */ ++ comphy_lane_reg_set(lane, COMPHY_PIPE_LANE_CFG4, ++ SPREAD_SPECTRUM_CLK_EN, SPREAD_SPECTRUM_CLK_EN); ++ ++ /* ++ * 4. Set Override Margining Controls From the MAC: ++ * Use margining signals from lane configuration ++ */ ++ comphy_lane_reg_set(lane, COMPHY_PIPE_TEST_MODE_CTRL, ++ MODE_MARGIN_OVERRIDE, 0xFFFF); ++ ++ /* ++ * 5. Set Lane-to-Lane Bundle Clock Sampling Period = per PCLK cycles ++ * set Mode Clock Source = PCLK is generated from REFCLK ++ */ ++ data = 0x0; ++ mask = MODE_CLK_SRC | BUNDLE_PERIOD_SEL | BUNDLE_PERIOD_SCALE_MASK | ++ BUNDLE_SAMPLE_CTRL | PLL_READY_DLY_MASK; ++ comphy_lane_reg_set(lane, COMPHY_PIPE_CLK_SRC_LO, data, mask); ++ ++ /* ++ * 6. Set G2 Spread Spectrum Clock Amplitude at 4K ++ */ ++ comphy_lane_reg_set(lane, COMPHY_GEN2_SET2, ++ GS2_TX_SSC_AMP_4128, GS2_TX_SSC_AMP_MASK); ++ ++ /* ++ * 7. Unset G3 Spread Spectrum Clock Amplitude ++ * set G3 TX and RX Register Master Current Select ++ */ ++ data = GS2_VREG_RXTX_MAS_ISET_60U; ++ mask = GS2_TX_SSC_AMP_MASK | GS2_VREG_RXTX_MAS_ISET_MASK | ++ GS2_RSVD_6_0_MASK; ++ comphy_lane_reg_set(lane, COMPHY_GEN3_SET2, data, mask); ++ ++ /* ++ * 8. Check crystal jumper setting and program the Power and PLL Control ++ * accordingly Change RX wait ++ */ ++ if (lane->priv->xtal_is_40m) { ++ ref_clk = REF_FREF_SEL_PCIE_USB3_40MHZ; ++ cfg = CFG_PM_RXDLOZ_WAIT_12_UNIT; ++ } else { ++ ref_clk = REF_FREF_SEL_PCIE_USB3_25MHZ; ++ cfg = CFG_PM_RXDLOZ_WAIT_7_UNIT; ++ } ++ ++ data = PU_IVREF_BIT | PU_PLL_BIT | PU_RX_BIT | PU_TX_BIT | ++ PU_TX_INTP_BIT | PU_DFE_BIT | COMPHY_MODE_USB3 | ref_clk; ++ mask = PU_IVREF_BIT | PU_PLL_BIT | PU_RX_BIT | PU_TX_BIT | ++ PU_TX_INTP_BIT | PU_DFE_BIT | PLL_LOCK_BIT | COMPHY_MODE_MASK | ++ REF_FREF_SEL_MASK; ++ comphy_lane_reg_set(lane, COMPHY_POWER_PLL_CTRL, data, mask); ++ ++ data = CFG_PM_RXDEN_WAIT_1_UNIT | cfg; ++ mask = CFG_PM_OSCCLK_WAIT_MASK | CFG_PM_RXDEN_WAIT_MASK | ++ CFG_PM_RXDLOZ_WAIT_MASK; ++ comphy_lane_reg_set(lane, COMPHY_PIPE_PWR_MGM_TIM1, data, mask); ++ ++ /* ++ * 9. Enable idle sync ++ */ ++ comphy_lane_reg_set(lane, COMPHY_IDLE_SYNC_EN, ++ IDLE_SYNC_EN, IDLE_SYNC_EN); ++ ++ /* ++ * 10. Enable the output of 500M clock ++ */ ++ comphy_lane_reg_set(lane, COMPHY_MISC_CTRL0, CLK500M_EN, CLK500M_EN); ++ ++ /* ++ * 11. Set 20-bit data width ++ */ ++ comphy_lane_reg_set(lane, COMPHY_DIG_LOOPBACK_EN, ++ DATA_WIDTH_20BIT, 0xFFFF); ++ ++ /* ++ * 12. Override Speed_PLL value and use MAC PLL ++ */ ++ data = SPEED_PLL_VALUE_16 | USE_MAX_PLL_RATE_BIT; ++ mask = 0xFFFF; ++ comphy_lane_reg_set(lane, COMPHY_KVCO_CAL_CTRL, data, mask); ++ ++ /* ++ * 13. Check the Polarity invert bit ++ */ ++ data = 0x0; ++ if (lane->invert_tx) ++ data |= TXD_INVERT_BIT; ++ if (lane->invert_rx) ++ data |= RXD_INVERT_BIT; ++ mask = TXD_INVERT_BIT | RXD_INVERT_BIT; ++ comphy_lane_reg_set(lane, COMPHY_SYNC_PATTERN, data, mask); ++ ++ /* ++ * 14. Set max speed generation to USB3.0 5Gbps ++ */ ++ comphy_lane_reg_set(lane, COMPHY_SYNC_MASK_GEN, ++ PHY_GEN_MAX_USB3_5G, PHY_GEN_MAX_MASK); ++ ++ /* ++ * 15. Set capacitor value for FFE gain peaking to 0xF ++ */ ++ comphy_lane_reg_set(lane, COMPHY_GEN2_SET3, ++ GS3_FFE_CAP_SEL_VALUE, GS3_FFE_CAP_SEL_MASK); ++ ++ /* ++ * 16. Release SW reset ++ */ ++ data = MODE_CORE_CLK_FREQ_SEL | MODE_PIPE_WIDTH_32 | MODE_REFDIV_BY_4; ++ mask = 0xFFFF; ++ comphy_lane_reg_set(lane, COMPHY_PIPE_RST_CLK_CTRL, data, mask); ++ ++ /* Wait for > 55 us to allow PCLK be enabled */ ++ udelay(PLL_SET_DELAY_US); ++ ++ ret = comphy_lane_reg_poll(lane, COMPHY_PIPE_LANE_STAT1, TXDCLK_PCLK_EN, ++ COMPHY_PLL_SLEEP, COMPHY_PLL_TIMEOUT); ++ if (ret) { ++ dev_err(lane->dev, "Failed to lock USB3 PLL\n"); ++ return ret; ++ } ++ ++ return 0; ++} ++ ++static int ++mvebu_a3700_comphy_pcie_power_on(struct mvebu_a3700_comphy_lane *lane) ++{ ++ u32 mask, data, ref_clk; ++ int ret; ++ ++ /* Configure phy selector for PCIe */ ++ ret = mvebu_a3700_comphy_set_phy_selector(lane); ++ if (ret) ++ return ret; ++ ++ /* 1. Enable max PLL. */ ++ comphy_lane_reg_set(lane, COMPHY_PIPE_LANE_CFG1, ++ USE_MAX_PLL_RATE_EN, USE_MAX_PLL_RATE_EN); ++ ++ /* 2. Select 20 bit SERDES interface. */ ++ comphy_lane_reg_set(lane, COMPHY_PIPE_CLK_SRC_LO, ++ CFG_SEL_20B, CFG_SEL_20B); ++ ++ /* 3. Force to use reg setting for PCIe mode */ ++ comphy_lane_reg_set(lane, COMPHY_MISC_CTRL1, ++ SEL_BITS_PCIE_FORCE, SEL_BITS_PCIE_FORCE); ++ ++ /* 4. Change RX wait */ ++ data = CFG_PM_RXDEN_WAIT_1_UNIT | CFG_PM_RXDLOZ_WAIT_12_UNIT; ++ mask = CFG_PM_OSCCLK_WAIT_MASK | CFG_PM_RXDEN_WAIT_MASK | ++ CFG_PM_RXDLOZ_WAIT_MASK; ++ comphy_lane_reg_set(lane, COMPHY_PIPE_PWR_MGM_TIM1, data, mask); ++ ++ /* 5. Enable idle sync */ ++ comphy_lane_reg_set(lane, COMPHY_IDLE_SYNC_EN, ++ IDLE_SYNC_EN, IDLE_SYNC_EN); ++ ++ /* 6. Enable the output of 100M/125M/500M clock */ ++ data = CLK500M_EN | TXDCLK_2X_SEL | CLK100M_125M_EN; ++ mask = data; ++ comphy_lane_reg_set(lane, COMPHY_MISC_CTRL0, data, mask); ++ ++ /* ++ * 7. Enable TX, PCIE global register, 0xd0074814, it is done in ++ * PCI-E driver ++ */ ++ ++ /* ++ * 8. Check crystal jumper setting and program the Power and PLL ++ * Control accordingly ++ */ ++ ++ if (lane->priv->xtal_is_40m) ++ ref_clk = REF_FREF_SEL_PCIE_USB3_40MHZ; ++ else ++ ref_clk = REF_FREF_SEL_PCIE_USB3_25MHZ; ++ ++ data = PU_IVREF_BIT | PU_PLL_BIT | PU_RX_BIT | PU_TX_BIT | ++ PU_TX_INTP_BIT | PU_DFE_BIT | COMPHY_MODE_PCIE | ref_clk; ++ mask = 0xFFFF; ++ comphy_lane_reg_set(lane, COMPHY_POWER_PLL_CTRL, data, mask); ++ ++ /* 9. Override Speed_PLL value and use MAC PLL */ ++ comphy_lane_reg_set(lane, COMPHY_KVCO_CAL_CTRL, ++ SPEED_PLL_VALUE_16 | USE_MAX_PLL_RATE_BIT, ++ 0xFFFF); ++ ++ /* 10. Check the Polarity invert bit */ ++ data = 0x0; ++ if (lane->invert_tx) ++ data |= TXD_INVERT_BIT; ++ if (lane->invert_rx) ++ data |= RXD_INVERT_BIT; ++ mask = TXD_INVERT_BIT | RXD_INVERT_BIT; ++ comphy_lane_reg_set(lane, COMPHY_SYNC_PATTERN, data, mask); ++ ++ /* 11. Release SW reset */ ++ data = MODE_CORE_CLK_FREQ_SEL | MODE_PIPE_WIDTH_32; ++ mask = data | PIPE_SOFT_RESET | MODE_REFDIV_MASK; ++ comphy_lane_reg_set(lane, COMPHY_PIPE_RST_CLK_CTRL, data, mask); ++ ++ /* Wait for > 55 us to allow PCLK be enabled */ ++ udelay(PLL_SET_DELAY_US); ++ ++ ret = comphy_lane_reg_poll(lane, COMPHY_PIPE_LANE_STAT1, TXDCLK_PCLK_EN, ++ COMPHY_PLL_SLEEP, COMPHY_PLL_TIMEOUT); ++ if (ret) { ++ dev_err(lane->dev, "Failed to lock PCIE PLL\n"); ++ return ret; ++ } ++ ++ return 0; ++} ++ ++static void ++mvebu_a3700_comphy_usb3_power_off(struct mvebu_a3700_comphy_lane *lane) ++{ ++ /* ++ * Currently the USB3 MAC sets the USB3 PHY to low state, so we do not ++ * need to power off USB3 PHY again. ++ */ ++} ++ ++static void ++mvebu_a3700_comphy_sata_power_off(struct mvebu_a3700_comphy_lane *lane) ++{ ++ /* Set phy isolation mode */ ++ comphy_lane_reg_set(lane, COMPHY_ISOLATION_CTRL, ++ PHY_ISOLATE_MODE, PHY_ISOLATE_MODE); ++ ++ /* Power off PLL, Tx, Rx */ ++ comphy_lane_reg_set(lane, COMPHY_POWER_PLL_CTRL, ++ 0x0, PU_PLL_BIT | PU_RX_BIT | PU_TX_BIT); ++} ++ ++static void ++mvebu_a3700_comphy_ethernet_power_off(struct mvebu_a3700_comphy_lane *lane) ++{ ++ u32 mask, data; ++ ++ data = PIN_RESET_CORE_BIT | PIN_RESET_COMPHY_BIT | PIN_PU_IVREF_BIT | ++ PHY_RX_INIT_BIT; ++ mask = data; ++ comphy_periph_reg_set(lane, COMPHY_PHY_CFG1, data, mask); ++} ++ ++static void ++mvebu_a3700_comphy_pcie_power_off(struct mvebu_a3700_comphy_lane *lane) ++{ ++ /* Power off PLL, Tx, Rx */ ++ comphy_lane_reg_set(lane, COMPHY_POWER_PLL_CTRL, ++ 0x0, PU_PLL_BIT | PU_RX_BIT | PU_TX_BIT); ++} ++ ++static int mvebu_a3700_comphy_reset(struct phy *phy) ++{ ++ struct mvebu_a3700_comphy_lane *lane = phy_get_drvdata(phy); ++ u16 mask, data; ++ ++ dev_dbg(lane->dev, "resetting lane %d\n", lane->id); ++ ++ /* COMPHY reset for internal logic */ ++ comphy_lane_reg_set(lane, COMPHY_SFT_RESET, ++ SFT_RST_NO_REG, SFT_RST_NO_REG); ++ ++ /* COMPHY register reset (cleared automatically) */ ++ comphy_lane_reg_set(lane, COMPHY_SFT_RESET, SFT_RST, SFT_RST); ++ ++ /* PIPE soft and register reset */ ++ data = PIPE_SOFT_RESET | PIPE_REG_RESET; ++ mask = data; ++ comphy_lane_reg_set(lane, COMPHY_PIPE_RST_CLK_CTRL, data, mask); ++ ++ /* Release PIPE register reset */ ++ comphy_lane_reg_set(lane, COMPHY_PIPE_RST_CLK_CTRL, ++ 0x0, PIPE_REG_RESET); ++ ++ /* Reset SB configuration register (only for lanes 0 and 1) */ ++ if (lane->id == 0 || lane->id == 1) { ++ u32 mask, data; ++ ++ data = PIN_RESET_CORE_BIT | PIN_RESET_COMPHY_BIT | ++ PIN_PU_PLL_BIT | PIN_PU_RX_BIT | PIN_PU_TX_BIT; ++ mask = data | PIN_PU_IVREF_BIT | PIN_TX_IDLE_BIT; ++ comphy_periph_reg_set(lane, COMPHY_PHY_CFG1, data, mask); ++ } ++ ++ return 0; ++} ++ ++static bool mvebu_a3700_comphy_check_mode(int lane, + enum phy_mode mode, + int submode) + { +@@ -122,7 +1170,7 @@ static int mvebu_a3700_comphy_get_fw_mod + + /* Unused PHY mux value is 0x0 */ + if (mode == PHY_MODE_INVALID) +- return -EINVAL; ++ return false; + + for (i = 0; i < n; i++) { + if (mvebu_a3700_comphy_modes[i].lane == lane && +@@ -132,27 +1180,30 @@ static int mvebu_a3700_comphy_get_fw_mod + } + + if (i == n) +- return -EINVAL; ++ return false; + +- return mvebu_a3700_comphy_modes[i].fw_mode; ++ return true; + } + + static int mvebu_a3700_comphy_set_mode(struct phy *phy, enum phy_mode mode, + int submode) + { + struct mvebu_a3700_comphy_lane *lane = phy_get_drvdata(phy); +- int fw_mode; +- +- if (submode == PHY_INTERFACE_MODE_1000BASEX) +- submode = PHY_INTERFACE_MODE_SGMII; + +- fw_mode = mvebu_a3700_comphy_get_fw_mode(lane->id, mode, +- submode); +- if (fw_mode < 0) { ++ if (!mvebu_a3700_comphy_check_mode(lane->id, mode, submode)) { + dev_err(lane->dev, "invalid COMPHY mode\n"); +- return fw_mode; ++ return -EINVAL; + } + ++ /* Mode cannot be changed while the PHY is powered on */ ++ if (phy->power_count && ++ (lane->mode != mode || lane->submode != submode)) ++ return -EBUSY; ++ ++ /* If changing mode, ensure reset is called */ ++ if (lane->mode != PHY_MODE_INVALID && lane->mode != mode) ++ lane->needs_reset = true; ++ + /* Just remember the mode, ->power_on() will do the real setup */ + lane->mode = mode; + lane->submode = submode; +@@ -163,76 +1214,68 @@ static int mvebu_a3700_comphy_set_mode(s + static int mvebu_a3700_comphy_power_on(struct phy *phy) + { + struct mvebu_a3700_comphy_lane *lane = phy_get_drvdata(phy); +- u32 fw_param; +- int fw_mode; +- int fw_port; + int ret; + +- fw_mode = mvebu_a3700_comphy_get_fw_mode(lane->id, +- lane->mode, lane->submode); +- if (fw_mode < 0) { ++ if (!mvebu_a3700_comphy_check_mode(lane->id, lane->mode, ++ lane->submode)) { + dev_err(lane->dev, "invalid COMPHY mode\n"); +- return fw_mode; ++ return -EINVAL; ++ } ++ ++ if (lane->needs_reset) { ++ ret = mvebu_a3700_comphy_reset(phy); ++ if (ret) ++ return ret; ++ ++ lane->needs_reset = false; + } + + switch (lane->mode) { + case PHY_MODE_USB_HOST_SS: + dev_dbg(lane->dev, "set lane %d to USB3 host mode\n", lane->id); +- fw_param = COMPHY_FW_MODE(fw_mode); +- break; ++ return mvebu_a3700_comphy_usb3_power_on(lane); + case PHY_MODE_SATA: + dev_dbg(lane->dev, "set lane %d to SATA mode\n", lane->id); +- fw_param = COMPHY_FW_MODE(fw_mode); +- break; ++ return mvebu_a3700_comphy_sata_power_on(lane); + case PHY_MODE_ETHERNET: +- fw_port = (lane->id == 0) ? 1 : 0; +- switch (lane->submode) { +- case PHY_INTERFACE_MODE_SGMII: +- dev_dbg(lane->dev, "set lane %d to SGMII mode\n", +- lane->id); +- fw_param = COMPHY_FW_NET(fw_mode, fw_port, +- COMPHY_FW_SPEED_1_25G); +- break; +- case PHY_INTERFACE_MODE_2500BASEX: +- dev_dbg(lane->dev, "set lane %d to 2500BASEX mode\n", +- lane->id); +- fw_param = COMPHY_FW_NET(fw_mode, fw_port, +- COMPHY_FW_SPEED_3_125G); +- break; +- default: +- dev_err(lane->dev, "unsupported PHY submode (%d)\n", +- lane->submode); +- return -ENOTSUPP; +- } +- break; ++ dev_dbg(lane->dev, "set lane %d to Ethernet mode\n", lane->id); ++ return mvebu_a3700_comphy_ethernet_power_on(lane); + case PHY_MODE_PCIE: + dev_dbg(lane->dev, "set lane %d to PCIe mode\n", lane->id); +- fw_param = COMPHY_FW_PCIE(fw_mode, COMPHY_FW_SPEED_5G, +- phy->attrs.bus_width); +- break; ++ return mvebu_a3700_comphy_pcie_power_on(lane); + default: + dev_err(lane->dev, "unsupported PHY mode (%d)\n", lane->mode); +- return -ENOTSUPP; ++ return -EOPNOTSUPP; + } +- +- ret = mvebu_a3700_comphy_smc(COMPHY_SIP_POWER_ON, lane->id, fw_param); +- if (ret == -EOPNOTSUPP) +- dev_err(lane->dev, +- "unsupported SMC call, try updating your firmware\n"); +- +- return ret; + } + + static int mvebu_a3700_comphy_power_off(struct phy *phy) + { + struct mvebu_a3700_comphy_lane *lane = phy_get_drvdata(phy); + +- return mvebu_a3700_comphy_smc(COMPHY_SIP_POWER_OFF, lane->id, 0); ++ switch (lane->mode) { ++ case PHY_MODE_USB_HOST_SS: ++ mvebu_a3700_comphy_usb3_power_off(lane); ++ return 0; ++ case PHY_MODE_SATA: ++ mvebu_a3700_comphy_sata_power_off(lane); ++ return 0; ++ case PHY_MODE_ETHERNET: ++ mvebu_a3700_comphy_ethernet_power_off(lane); ++ return 0; ++ case PHY_MODE_PCIE: ++ mvebu_a3700_comphy_pcie_power_off(lane); ++ return 0; ++ default: ++ dev_err(lane->dev, "invalid COMPHY mode\n"); ++ return -EINVAL; ++ } + } + + static const struct phy_ops mvebu_a3700_comphy_ops = { + .power_on = mvebu_a3700_comphy_power_on, + .power_off = mvebu_a3700_comphy_power_off, ++ .reset = mvebu_a3700_comphy_reset, + .set_mode = mvebu_a3700_comphy_set_mode, + .owner = THIS_MODULE, + }; +@@ -256,13 +1299,75 @@ static struct phy *mvebu_a3700_comphy_xl + return ERR_PTR(-EINVAL); + } + ++ lane->invert_tx = args->args[1] & BIT(0); ++ lane->invert_rx = args->args[1] & BIT(1); ++ + return phy; + } + + static int mvebu_a3700_comphy_probe(struct platform_device *pdev) + { ++ struct mvebu_a3700_comphy_priv *priv; + struct phy_provider *provider; + struct device_node *child; ++ struct resource *res; ++ struct clk *clk; ++ int ret; ++ ++ priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); ++ if (!priv) ++ return -ENOMEM; ++ ++ spin_lock_init(&priv->lock); ++ ++ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "comphy"); ++ priv->comphy_regs = devm_ioremap_resource(&pdev->dev, res); ++ if (IS_ERR(priv->comphy_regs)) ++ return PTR_ERR(priv->comphy_regs); ++ ++ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, ++ "lane1_pcie_gbe"); ++ priv->lane1_phy_regs = devm_ioremap_resource(&pdev->dev, res); ++ if (IS_ERR(priv->lane1_phy_regs)) ++ return PTR_ERR(priv->lane1_phy_regs); ++ ++ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, ++ "lane0_usb3_gbe"); ++ priv->lane0_phy_regs = devm_ioremap_resource(&pdev->dev, res); ++ if (IS_ERR(priv->lane0_phy_regs)) ++ return PTR_ERR(priv->lane0_phy_regs); ++ ++ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, ++ "lane2_sata_usb3"); ++ priv->lane2_phy_indirect = devm_ioremap_resource(&pdev->dev, res); ++ if (IS_ERR(priv->lane2_phy_indirect)) ++ return PTR_ERR(priv->lane2_phy_indirect); ++ ++ /* ++ * Driver needs to know if reference xtal clock is 40MHz or 25MHz. ++ * Old DT bindings do not have xtal clk present. So do not fail here ++ * and expects that default 25MHz reference clock is used. ++ */ ++ clk = clk_get(&pdev->dev, "xtal"); ++ if (IS_ERR(clk)) { ++ if (PTR_ERR(clk) == -EPROBE_DEFER) ++ return -EPROBE_DEFER; ++ dev_warn(&pdev->dev, "missing 'xtal' clk (%ld)\n", ++ PTR_ERR(clk)); ++ } else { ++ ret = clk_prepare_enable(clk); ++ if (ret) { ++ dev_warn(&pdev->dev, "enabling xtal clk failed (%d)\n", ++ ret); ++ } else { ++ if (clk_get_rate(clk) == 40000000) ++ priv->xtal_is_40m = true; ++ clk_disable_unprepare(clk); ++ } ++ clk_put(clk); ++ } ++ ++ dev_set_drvdata(&pdev->dev, priv); + + for_each_available_child_of_node(pdev->dev.of_node, child) { + struct mvebu_a3700_comphy_lane *lane; +@@ -277,7 +1382,7 @@ static int mvebu_a3700_comphy_probe(stru + continue; + } + +- if (lane_id >= MVEBU_A3700_COMPHY_LANES) { ++ if (lane_id >= 3) { + dev_err(&pdev->dev, "invalid 'reg' property\n"); + continue; + } +@@ -295,11 +1400,21 @@ static int mvebu_a3700_comphy_probe(stru + return PTR_ERR(phy); + } + ++ lane->priv = priv; + lane->dev = &pdev->dev; + lane->mode = PHY_MODE_INVALID; + lane->submode = PHY_INTERFACE_MODE_NA; + lane->id = lane_id; ++ lane->invert_tx = false; ++ lane->invert_rx = false; + phy_set_drvdata(phy, lane); ++ ++ /* ++ * To avoid relying on the bootloader/firmware configuration, ++ * power off all comphys. ++ */ ++ mvebu_a3700_comphy_reset(phy); ++ lane->needs_reset = false; + } + + provider = devm_of_phy_provider_register(&pdev->dev, +@@ -323,5 +1438,7 @@ static struct platform_driver mvebu_a370 + module_platform_driver(mvebu_a3700_comphy_driver); + + MODULE_AUTHOR("Miquèl Raynal <miquel.raynal@bootlin.com>"); ++MODULE_AUTHOR("Pali Rohár <pali@kernel.org>"); ++MODULE_AUTHOR("Marek Behún <kabel@kernel.org>"); + MODULE_DESCRIPTION("Common PHY driver for A3700"); + MODULE_LICENSE("GPL v2"); diff --git a/pkgs/patches-linux-5.15/851-0003-arm64-dts-marvell-armada-37xx-Add-xtal-clock-to-comp.patch b/pkgs/patches-linux-5.15/851-0003-arm64-dts-marvell-armada-37xx-Add-xtal-clock-to-comp.patch new file mode 100644 index 0000000..33203a1 --- /dev/null +++ b/pkgs/patches-linux-5.15/851-0003-arm64-dts-marvell-armada-37xx-Add-xtal-clock-to-comp.patch @@ -0,0 +1,31 @@ +From 66c51c39fd4bf05e99debf0e71de5704231c57dc Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org> +Date: Thu, 23 Sep 2021 19:26:26 +0200 +Subject: [PATCH] arm64: dts: marvell: armada-37xx: Add xtal clock to comphy + node +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Kernel driver phy-mvebu-a3700-comphy.c needs to know the rate of the +reference xtal clock. So add missing xtal clock source into comphy device +tree node. If the property is not present, the driver defaults to 25 MHz +xtal rate (which, as far as we know, is used by all the existing boards). + +Signed-off-by: Pali Rohár <pali@kernel.org> +Signed-off-by: Marek Behún <kabel@kernel.org> +--- + arch/arm64/boot/dts/marvell/armada-37xx.dtsi | 2 ++ + 1 file changed, 2 insertions(+) + +--- a/arch/arm64/boot/dts/marvell/armada-37xx.dtsi ++++ b/arch/arm64/boot/dts/marvell/armada-37xx.dtsi +@@ -265,6 +265,8 @@ + "lane2_sata_usb3"; + #address-cells = <1>; + #size-cells = <0>; ++ clocks = <&xtalclk>; ++ clock-names = "xtal"; + + comphy0: phy@0 { + reg = <0>; diff --git a/pkgs/patches-linux-5.15/851-0004-Revert-ata-ahci-mvebu-Make-SATA-PHY-optional-for-Arm.patch b/pkgs/patches-linux-5.15/851-0004-Revert-ata-ahci-mvebu-Make-SATA-PHY-optional-for-Arm.patch new file mode 100644 index 0000000..3c994d2 --- /dev/null +++ b/pkgs/patches-linux-5.15/851-0004-Revert-ata-ahci-mvebu-Make-SATA-PHY-optional-for-Arm.patch @@ -0,0 +1,61 @@ +From 750bb44dbbe9dfb4ba3e1f8a746b831b39ba3cd9 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org> +Date: Thu, 23 Sep 2021 19:35:57 +0200 +Subject: [PATCH] Revert "ata: ahci: mvebu: Make SATA PHY optional for Armada + 3720" +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +This reverts commit 45aefe3d2251e4e229d7662052739f96ad1d08d9. + +Armada 3720 PHY driver (phy-mvebu-a3700-comphy.c) does not return +-EOPNOTSUPP from phy_power_on() callback anymore. + +So remove AHCI_HFLAG_IGN_NOTSUPP_POWER_ON flag from Armada 3720 plat data. + +AHCI_HFLAG_IGN_NOTSUPP_POWER_ON is not used by any other ahci driver, so +remove this flag completely. + +Signed-off-by: Pali Rohár <pali@kernel.org> +Signed-off-by: Marek Behún <kabel@kernel.org> +Acked-by: Miquel Raynal <miquel.raynal@bootlin.com> +--- + drivers/ata/ahci.h | 2 -- + drivers/ata/ahci_mvebu.c | 2 +- + drivers/ata/libahci_platform.c | 2 +- + 3 files changed, 2 insertions(+), 4 deletions(-) + +--- a/drivers/ata/ahci.h ++++ b/drivers/ata/ahci.h +@@ -240,8 +240,6 @@ enum { + as default lpm_policy */ + AHCI_HFLAG_SUSPEND_PHYS = (1 << 26), /* handle PHYs during + suspend/resume */ +- AHCI_HFLAG_IGN_NOTSUPP_POWER_ON = (1 << 27), /* ignore -EOPNOTSUPP +- from phy_power_on() */ + AHCI_HFLAG_NO_SXS = (1 << 28), /* SXS not supported */ + + /* ap->flags bits */ +--- a/drivers/ata/ahci_mvebu.c ++++ b/drivers/ata/ahci_mvebu.c +@@ -227,7 +227,7 @@ static const struct ahci_mvebu_plat_data + + static const struct ahci_mvebu_plat_data ahci_mvebu_armada_3700_plat_data = { + .plat_config = ahci_mvebu_armada_3700_config, +- .flags = AHCI_HFLAG_SUSPEND_PHYS | AHCI_HFLAG_IGN_NOTSUPP_POWER_ON, ++ .flags = AHCI_HFLAG_SUSPEND_PHYS, + }; + + static const struct of_device_id ahci_mvebu_of_match[] = { +--- a/drivers/ata/libahci_platform.c ++++ b/drivers/ata/libahci_platform.c +@@ -59,7 +59,7 @@ int ahci_platform_enable_phys(struct ahc + } + + rc = phy_power_on(hpriv->phys[i]); +- if (rc && !(rc == -EOPNOTSUPP && (hpriv->flags & AHCI_HFLAG_IGN_NOTSUPP_POWER_ON))) { ++ if (rc) { + phy_exit(hpriv->phys[i]); + goto disable_phys; + } diff --git a/pkgs/patches-linux-5.15/851-0005-Revert-usb-host-xhci-mvebu-make-USB-3.0-PHY-optional.patch b/pkgs/patches-linux-5.15/851-0005-Revert-usb-host-xhci-mvebu-make-USB-3.0-PHY-optional.patch new file mode 100644 index 0000000..b8a3e88 --- /dev/null +++ b/pkgs/patches-linux-5.15/851-0005-Revert-usb-host-xhci-mvebu-make-USB-3.0-PHY-optional.patch @@ -0,0 +1,163 @@ +From 9f0dfb279b1dd505d5e10b10e4a78a62030978d8 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org> +Date: Thu, 23 Sep 2021 19:40:06 +0200 +Subject: [PATCH] Revert "usb: host: xhci: mvebu: make USB 3.0 PHY optional for + Armada 3720" +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +This reverts commit 3241929b67d28c83945d3191c6816a3271fd6b85. + +Armada 3720 phy driver (phy-mvebu-a3700-comphy.c) does not return +-EOPNOTSUPP from phy_power_on() callback anymore. + +So remove XHCI_SKIP_PHY_INIT flag from xhci_mvebu_a3700_plat_setup() and +then also whole xhci_mvebu_a3700_plat_setup() function which is there just +to handle -EOPNOTSUPP for XHCI_SKIP_PHY_INIT. + +xhci plat_setup callback is not used by any other xhci plat driver, so +remove this callback completely. + +Signed-off-by: Pali Rohár <pali@kernel.org> +Signed-off-by: Marek Behún <kabel@kernel.org> +Acked-by: Miquel Raynal <miquel.raynal@bootlin.com> +--- + drivers/usb/host/xhci-mvebu.c | 42 ----------------------------------- + drivers/usb/host/xhci-mvebu.h | 6 ----- + drivers/usb/host/xhci-plat.c | 20 +---------------- + drivers/usb/host/xhci-plat.h | 1 - + 4 files changed, 1 insertion(+), 68 deletions(-) + +--- a/drivers/usb/host/xhci-mvebu.c ++++ b/drivers/usb/host/xhci-mvebu.c +@@ -8,7 +8,6 @@ + #include <linux/mbus.h> + #include <linux/of.h> + #include <linux/platform_device.h> +-#include <linux/phy/phy.h> + + #include <linux/usb.h> + #include <linux/usb/hcd.h> +@@ -74,47 +73,6 @@ int xhci_mvebu_mbus_init_quirk(struct us + + return 0; + } +- +-int xhci_mvebu_a3700_plat_setup(struct usb_hcd *hcd) +-{ +- struct xhci_hcd *xhci = hcd_to_xhci(hcd); +- struct device *dev = hcd->self.controller; +- struct phy *phy; +- int ret; +- +- /* Old bindings miss the PHY handle */ +- phy = of_phy_get(dev->of_node, "usb3-phy"); +- if (IS_ERR(phy) && PTR_ERR(phy) == -EPROBE_DEFER) +- return -EPROBE_DEFER; +- else if (IS_ERR(phy)) +- goto phy_out; +- +- ret = phy_init(phy); +- if (ret) +- goto phy_put; +- +- ret = phy_set_mode(phy, PHY_MODE_USB_HOST_SS); +- if (ret) +- goto phy_exit; +- +- ret = phy_power_on(phy); +- if (ret == -EOPNOTSUPP) { +- /* Skip initializatin of XHCI PHY when it is unsupported by firmware */ +- dev_warn(dev, "PHY unsupported by firmware\n"); +- xhci->quirks |= XHCI_SKIP_PHY_INIT; +- } +- if (ret) +- goto phy_exit; +- +- phy_power_off(phy); +-phy_exit: +- phy_exit(phy); +-phy_put: +- of_phy_put(phy); +-phy_out: +- +- return 0; +-} + + int xhci_mvebu_a3700_init_quirk(struct usb_hcd *hcd) + { +--- a/drivers/usb/host/xhci-mvebu.h ++++ b/drivers/usb/host/xhci-mvebu.h +@@ -12,18 +12,12 @@ struct usb_hcd; + + #if IS_ENABLED(CONFIG_USB_XHCI_MVEBU) + int xhci_mvebu_mbus_init_quirk(struct usb_hcd *hcd); +-int xhci_mvebu_a3700_plat_setup(struct usb_hcd *hcd); + int xhci_mvebu_a3700_init_quirk(struct usb_hcd *hcd); + #else + static inline int xhci_mvebu_mbus_init_quirk(struct usb_hcd *hcd) + { + return 0; + } +- +-static inline int xhci_mvebu_a3700_plat_setup(struct usb_hcd *hcd) +-{ +- return 0; +-} + + static inline int xhci_mvebu_a3700_init_quirk(struct usb_hcd *hcd) + { +--- a/drivers/usb/host/xhci-plat.c ++++ b/drivers/usb/host/xhci-plat.c +@@ -44,16 +44,6 @@ static void xhci_priv_plat_start(struct + priv->plat_start(hcd); + } + +-static int xhci_priv_plat_setup(struct usb_hcd *hcd) +-{ +- struct xhci_plat_priv *priv = hcd_to_xhci_priv(hcd); +- +- if (!priv->plat_setup) +- return 0; +- +- return priv->plat_setup(hcd); +-} +- + static int xhci_priv_init_quirk(struct usb_hcd *hcd) + { + struct xhci_plat_priv *priv = hcd_to_xhci_priv(hcd); +@@ -121,7 +111,6 @@ static const struct xhci_plat_priv xhci_ + }; + + static const struct xhci_plat_priv xhci_plat_marvell_armada3700 = { +- .plat_setup = xhci_mvebu_a3700_plat_setup, + .init_quirk = xhci_mvebu_a3700_init_quirk, + }; + +@@ -341,14 +330,7 @@ static int xhci_plat_probe(struct platfo + + hcd->tpl_support = of_usb_host_tpl_support(sysdev->of_node); + xhci->shared_hcd->tpl_support = hcd->tpl_support; +- +- if (priv) { +- ret = xhci_priv_plat_setup(hcd); +- if (ret) +- goto disable_usb_phy; +- } +- +- if ((xhci->quirks & XHCI_SKIP_PHY_INIT) || (priv && (priv->quirks & XHCI_SKIP_PHY_INIT))) ++ if (priv && (priv->quirks & XHCI_SKIP_PHY_INIT)) + hcd->skip_phy_initialization = 1; + + if (priv && (priv->quirks & XHCI_SG_TRB_CACHE_SIZE_QUIRK)) +--- a/drivers/usb/host/xhci-plat.h ++++ b/drivers/usb/host/xhci-plat.h +@@ -13,7 +13,6 @@ + struct xhci_plat_priv { + const char *firmware_name; + unsigned long long quirks; +- int (*plat_setup)(struct usb_hcd *); + void (*plat_start)(struct usb_hcd *); + int (*init_quirk)(struct usb_hcd *); + int (*suspend_quirk)(struct usb_hcd *); diff --git a/pkgs/patches-linux-5.15/851-0006-Revert-PCI-aardvark-Fix-initialization-with-old-Marv.patch b/pkgs/patches-linux-5.15/851-0006-Revert-PCI-aardvark-Fix-initialization-with-old-Marv.patch new file mode 100644 index 0000000..4425e3f --- /dev/null +++ b/pkgs/patches-linux-5.15/851-0006-Revert-PCI-aardvark-Fix-initialization-with-old-Marv.patch @@ -0,0 +1,36 @@ +From 9a352062b7e3857742389dff6f64393481dc755e Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org> +Date: Thu, 23 Sep 2021 19:37:05 +0200 +Subject: [PATCH] Revert "PCI: aardvark: Fix initialization with old Marvell's + Arm Trusted Firmware" +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +This reverts commit b0c6ae0f8948a2be6bf4e8b4bbab9ca1343289b6. + +Armada 3720 phy driver (phy-mvebu-a3700-comphy.c) does not return +-EOPNOTSUPP from phy_power_on() callback anymore. + +So remove dead code which handles -EOPNOTSUPP return value. + +Signed-off-by: Pali Rohár <pali@kernel.org> +Signed-off-by: Marek Behún <kabel@kernel.org> +Acked-by: Miquel Raynal <miquel.raynal@bootlin.com> +--- + drivers/pci/controller/pci-aardvark.c | 4 +--- + 1 file changed, 1 insertion(+), 3 deletions(-) + +--- a/drivers/pci/controller/pci-aardvark.c ++++ b/drivers/pci/controller/pci-aardvark.c +@@ -1616,9 +1616,7 @@ static int advk_pcie_enable_phy(struct a + } + + ret = phy_power_on(pcie->phy); +- if (ret == -EOPNOTSUPP) { +- dev_warn(&pcie->pdev->dev, "PHY unsupported by firmware\n"); +- } else if (ret) { ++ if (ret) { + phy_exit(pcie->phy); + return ret; + } diff --git a/pkgs/patches-linux-5.15/860-v5.17-MIPS-ath79-drop-_machine_restart-again.patch b/pkgs/patches-linux-5.15/860-v5.17-MIPS-ath79-drop-_machine_restart-again.patch new file mode 100644 index 0000000..e9d692b --- /dev/null +++ b/pkgs/patches-linux-5.15/860-v5.17-MIPS-ath79-drop-_machine_restart-again.patch @@ -0,0 +1,49 @@ +From d3115128bdafb62628ab41861a4f06f6d02ac320 Mon Sep 17 00:00:00 2001 +From: Lech Perczak <lech.perczak@gmail.com> +Date: Mon, 10 Jan 2022 23:48:44 +0100 +Subject: MIPS: ath79: drop _machine_restart again + +Commit 81424d0ad0d4 ("MIPS: ath79: Use the reset controller to restart +OF machines") removed setup of _machine_restart on OF machines to use +reset handler in reset controller driver. +While removing remnants of non-OF machines in commit 3a77e0d75eed +("MIPS: ath79: drop machfiles"), this was introduced again, making it +impossible to use additional restart handlers registered through device +tree. Drop setting _machine_restart altogether, and ath79_restart +function, which is no longer used after this. + +Fixes: 3a77e0d75eed ("MIPS: ath79: drop machfiles") +Cc: John Crispin <john@phrozen.org> +Cc: Florian Fainelli <f.fainelli@gmail.com> +Signed-off-by: Lech Perczak <lech.perczak@gmail.com> +Signed-off-by: Thomas Bogendoerfer <tsbogend@alpha.franken.de> +--- + arch/mips/ath79/setup.c | 10 ---------- + 1 file changed, 10 deletions(-) + +--- a/arch/mips/ath79/setup.c ++++ b/arch/mips/ath79/setup.c +@@ -34,15 +34,6 @@ + + static char ath79_sys_type[ATH79_SYS_TYPE_LEN]; + +-static void ath79_restart(char *command) +-{ +- local_irq_disable(); +- ath79_device_reset_set(AR71XX_RESET_FULL_CHIP); +- for (;;) +- if (cpu_wait) +- cpu_wait(); +-} +- + static void ath79_halt(void) + { + while (1) +@@ -234,7 +225,6 @@ void __init plat_mem_setup(void) + + detect_memory_region(0, ATH79_MEM_SIZE_MIN, ATH79_MEM_SIZE_MAX); + +- _machine_restart = ath79_restart; + _machine_halt = ath79_halt; + pm_power_off = ath79_halt; + } diff --git a/pkgs/patches-linux-5.15/870-hwmon-next-hwmon-lm70-Add-ti-tmp125-support.patch b/pkgs/patches-linux-5.15/870-hwmon-next-hwmon-lm70-Add-ti-tmp125-support.patch new file mode 100644 index 0000000..fabf177 --- /dev/null +++ b/pkgs/patches-linux-5.15/870-hwmon-next-hwmon-lm70-Add-ti-tmp125-support.patch @@ -0,0 +1,71 @@ +From 31d8f414e1596ba54a4315418e4c0086fda9e428 Mon Sep 17 00:00:00 2001 +From: Christian Lamparter <chunkeey@gmail.com> +Date: Fri, 18 Feb 2022 10:06:43 +0100 +Subject: hwmon: (lm70) Add ti,tmp125 support + +The TMP125 is a 2 degree Celsius accurate Digital +Temperature Sensor with a SPI interface. + +The temperature register is a 16-bit, read-only register. +The MSB (Bit 15) is a leading zero and never set. Bits 14 +to 5 are the 1+9 temperature data bits in a two's +complement format. Bits 4 to 0 are useless copies of +Bit 5 value and therefore ignored. + +Signed-off-by: Christian Lamparter <chunkeey@gmail.com> +Link: https://lore.kernel.org/r/43b19cbd4e7f51e9509e561b02b5d8d0e7079fac.1645175187.git.chunkeey@gmail.com +Signed-off-by: Guenter Roeck <linux@roeck-us.net> +--- +--- a/drivers/hwmon/lm70.c ++++ b/drivers/hwmon/lm70.c +@@ -34,6 +34,7 @@ + #define LM70_CHIP_LM71 2 /* NS LM71 */ + #define LM70_CHIP_LM74 3 /* NS LM74 */ + #define LM70_CHIP_TMP122 4 /* TI TMP122/TMP124 */ ++#define LM70_CHIP_TMP125 5 /* TI TMP125 */ + + struct lm70 { + struct spi_device *spi; +@@ -87,6 +88,12 @@ static ssize_t temp1_input_show(struct d + * LM71: + * 14 bits of 2's complement data, discard LSB 2 bits, + * resolution 0.0312 degrees celsius. ++ * ++ * TMP125: ++ * MSB/D15 is a leading zero. D14 is the sign-bit. This is ++ * followed by 9 temperature bits (D13..D5) in 2's complement ++ * data format with a resolution of 0.25 degrees celsius per unit. ++ * LSB 5 bits (D4..D0) share the same value as D5 and get discarded. + */ + switch (p_lm70->chip) { + case LM70_CHIP_LM70: +@@ -102,6 +109,10 @@ static ssize_t temp1_input_show(struct d + case LM70_CHIP_LM71: + val = ((int)raw / 4) * 3125 / 100; + break; ++ ++ case LM70_CHIP_TMP125: ++ val = (sign_extend32(raw, 14) / 32) * 250; ++ break; + } + + status = sprintf(buf, "%d\n", val); /* millidegrees Celsius */ +@@ -136,6 +147,10 @@ static const struct of_device_id lm70_of + .data = (void *) LM70_CHIP_TMP122, + }, + { ++ .compatible = "ti,tmp125", ++ .data = (void *) LM70_CHIP_TMP125, ++ }, ++ { + .compatible = "ti,lm71", + .data = (void *) LM70_CHIP_LM71, + }, +@@ -184,6 +199,7 @@ static const struct spi_device_id lm70_i + { "lm70", LM70_CHIP_LM70 }, + { "tmp121", LM70_CHIP_TMP121 }, + { "tmp122", LM70_CHIP_TMP122 }, ++ { "tmp125", LM70_CHIP_TMP125 }, + { "lm71", LM70_CHIP_LM71 }, + { "lm74", LM70_CHIP_LM74 }, + { }, diff --git a/pkgs/patches-linux-5.15/880-v5.19-cdc_ether-export-usbnet_cdc_zte_rx_fixup.patch b/pkgs/patches-linux-5.15/880-v5.19-cdc_ether-export-usbnet_cdc_zte_rx_fixup.patch new file mode 100644 index 0000000..39fdb32 --- /dev/null +++ b/pkgs/patches-linux-5.15/880-v5.19-cdc_ether-export-usbnet_cdc_zte_rx_fixup.patch @@ -0,0 +1,58 @@ +From a79a5613e1907e1bf09bb6ba6fd5ff43b66c1afe Mon Sep 17 00:00:00 2001 +From: Lech Perczak <lech.perczak@gmail.com> +Date: Fri, 1 Apr 2022 22:03:55 +0200 +Subject: [PATCH 1/3] cdc_ether: export usbnet_cdc_zte_rx_fixup +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Commit bfe9b9d2df66 ("cdc_ether: Improve ZTE MF823/831/910 handling") +introduces a workaround for certain ZTE modems reporting invalid MAC +addresses over CDC-ECM. +The same issue was present on their RNDIS interface,which was fixed in +commit a5a18bdf7453 ("rndis_host: Set valid random MAC on buggy devices"). + +However, internal modem of ZTE MF286R router, on its RNDIS interface, also +exhibits a second issue fixed already in CDC-ECM, of the device not +respecting configured random MAC address. In order to share the fixup for +this with rndis_host driver, export the workaround function, which will +be re-used in the following commit in rndis_host. + +Cc: Kristian Evensen <kristian.evensen@gmail.com> +Cc: Bjørn Mork <bjorn@mork.no> +Cc: Oliver Neukum <oliver@neukum.org> +Signed-off-by: Lech Perczak <lech.perczak@gmail.com> +--- + drivers/net/usb/cdc_ether.c | 3 ++- + include/linux/usb/usbnet.h | 1 + + 2 files changed, 3 insertions(+), 1 deletion(-) + +--- a/drivers/net/usb/cdc_ether.c ++++ b/drivers/net/usb/cdc_ether.c +@@ -479,7 +479,7 @@ static int usbnet_cdc_zte_bind(struct us + * device MAC address has been updated). Always set MAC address to that of the + * device. + */ +-static int usbnet_cdc_zte_rx_fixup(struct usbnet *dev, struct sk_buff *skb) ++int usbnet_cdc_zte_rx_fixup(struct usbnet *dev, struct sk_buff *skb) + { + if (skb->len < ETH_HLEN || !(skb->data[0] & 0x02)) + return 1; +@@ -489,6 +489,7 @@ static int usbnet_cdc_zte_rx_fixup(struc + + return 1; + } ++EXPORT_SYMBOL_GPL(usbnet_cdc_zte_rx_fixup); + + /* Ensure correct link state + * +--- a/include/linux/usb/usbnet.h ++++ b/include/linux/usb/usbnet.h +@@ -214,6 +214,7 @@ extern int usbnet_ether_cdc_bind(struct + extern int usbnet_cdc_bind(struct usbnet *, struct usb_interface *); + extern void usbnet_cdc_unbind(struct usbnet *, struct usb_interface *); + extern void usbnet_cdc_status(struct usbnet *, struct urb *); ++extern int usbnet_cdc_zte_rx_fixup(struct usbnet *dev, struct sk_buff *skb); + + /* CDC and RNDIS support the same host-chosen packet filters for IN transfers */ + #define DEFAULT_FILTER (USB_CDC_PACKET_TYPE_BROADCAST \ diff --git a/pkgs/patches-linux-5.15/8800-ARM-dts-mvebu-armada-385-turris-omnia-separate-dts-f.patch b/pkgs/patches-linux-5.15/8800-ARM-dts-mvebu-armada-385-turris-omnia-separate-dts-f.patch new file mode 100644 index 0000000..3c427cd --- /dev/null +++ b/pkgs/patches-linux-5.15/8800-ARM-dts-mvebu-armada-385-turris-omnia-separate-dts-f.patch @@ -0,0 +1,213 @@ +From 848dfda2610dba5c051c9114ab05449e64d5de08 Mon Sep 17 00:00:00 2001 +From: Tomas Hlavacek <tmshlvck@gmail.com> +Date: Tue, 5 May 2020 20:40:24 +0200 +Subject: [PATCH] ARM: dts: mvebu: armada-385-turris-omnia: separate dts for + SFP and PHY +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +The Turris Omnia board contains dual-personality ethernet NIC eth2 with +two operation modes: 1) SFP cage and 2) metalic 1000BASE-X PHY. +The differential pair carrying SGMII/1000BASE-X of eth2 is wired through +a multiplexor driven by the module-detect signal from the SFP cage. +The pin status can be read through I2C GPIO expander chip in userspace +when the sfp driver module is unloaded and / or in U-Boot prior to the +start of the kernel and the proper DTS file can be selected for the +(floolowing) boot. + +Split DTS for Turris Omnia (that does not have any support for SFP cage) +into three files: + armada-385-turris-omnia.dtsi - common base + armada-385-turris-omnia-sfp.dts - DT with the SFP configuration and +PHY disabled + armada-385-turris-omnia-phy.dts - DT with the PHY configuration and +SFP disabled + +Current DSA driver does not allow multiple CPU ports and Turris Omnia +has two RGMII iterfaces wired between CPU and DSA switch. +Disable the second CPU port until there is a suitable driver to use it. + +Signed-off-by: Tomas Hlavacek <tmshlvck@gmail.com> +Signed-off-by: Marek Behún <marek.behun@nic.cz> +--- + arch/arm/boot/dts/Makefile | 3 +- + .../boot/dts/armada-385-turris-omnia-phy.dts | 22 ++++++++++++ + .../boot/dts/armada-385-turris-omnia-sfp.dts | 23 ++++++++++++ + ...omnia.dts => armada-385-turris-omnia.dtsi} | 35 +++++++++++-------- + 4 files changed, 68 insertions(+), 15 deletions(-) + create mode 100644 arch/arm/boot/dts/armada-385-turris-omnia-phy.dts + create mode 100644 arch/arm/boot/dts/armada-385-turris-omnia-sfp.dts + rename arch/arm/boot/dts/{armada-385-turris-omnia.dts => armada-385-turris-omnia.dtsi} (94%) + +diff --git a/arch/arm/boot/dts/Makefile b/arch/arm/boot/dts/Makefile +index ce66ffd5a1bb..e702896345e9 100644 +--- a/arch/arm/boot/dts/Makefile ++++ b/arch/arm/boot/dts/Makefile +@@ -1328,7 +1328,8 @@ dtb-$(CONFIG_MACH_ARMADA_38X) += \ + armada-385-linksys-rango.dtb \ + armada-385-linksys-shelby.dtb \ + armada-385-synology-ds116.dtb \ +- armada-385-turris-omnia.dtb \ ++ armada-385-turris-omnia-phy.dtb \ ++ armada-385-turris-omnia-sfp.dtb \ + armada-388-clearfog.dtb \ + armada-388-clearfog-base.dtb \ + armada-388-clearfog-pro.dtb \ +diff --git a/arch/arm/boot/dts/armada-385-turris-omnia-phy.dts b/arch/arm/boot/dts/armada-385-turris-omnia-phy.dts +new file mode 100644 +index 000000000000..706f6a2f8065 +--- /dev/null ++++ b/arch/arm/boot/dts/armada-385-turris-omnia-phy.dts +@@ -0,0 +1,22 @@ ++// SPDX-License-Identifier: (GPL-2.0 OR MIT) ++/* ++ * Device Tree file for the Turris Omnia ++ * ++ * Copyright (C) 2016 Uwe Kleine-König <uwe@kleine-koenig.org> ++ * Copyright (C) 2016-2019 Tomas Hlavacek <tmshlvkc@gmail.com> ++ * ++ * Schematic available at https://www.turris.cz/doc/_media/rtrom01-schema.pdf ++ */ ++ ++/dts-v1/; ++ ++#include "armada-385-turris-omnia.dtsi" ++ ++&phy1 { ++ status = "okay"; ++}; ++ ++ð2 { ++ phy-mode = "sgmii"; ++ phy = <&phy1>; ++}; +diff --git a/arch/arm/boot/dts/armada-385-turris-omnia-sfp.dts b/arch/arm/boot/dts/armada-385-turris-omnia-sfp.dts +new file mode 100644 +index 000000000000..b9f2b88834be +--- /dev/null ++++ b/arch/arm/boot/dts/armada-385-turris-omnia-sfp.dts +@@ -0,0 +1,23 @@ ++// SPDX-License-Identifier: (GPL-2.0 OR MIT) ++/* ++ * Device Tree file for the Turris Omnia ++ * ++ * Copyright (C) 2016 Uwe Kleine-König <uwe@kleine-koenig.org> ++ * Copyright (C) 2016-2019 Tomas Hlavacek <tmshlvkc@gmail.com> ++ * ++ * Schematic available at https://www.turris.cz/doc/_media/rtrom01-schema.pdf ++ */ ++ ++/dts-v1/; ++ ++#include "armada-385-turris-omnia.dtsi" ++ ++&sfp { ++ status = "okay"; ++}; ++ ++ð2 { ++ phy-mode = "sgmii"; ++ managed = "in-band-status"; ++ sfp = <&sfp>; ++}; +diff --git a/arch/arm/boot/dts/armada-385-turris-omnia.dts b/arch/arm/boot/dts/armada-385-turris-omnia.dtsi +similarity index 94% +rename from arch/arm/boot/dts/armada-385-turris-omnia.dts +rename to arch/arm/boot/dts/armada-385-turris-omnia.dtsi +index 5bd6a66d2c2b..6bf7d3e184ab 100644 +--- a/arch/arm/boot/dts/armada-385-turris-omnia.dts ++++ b/arch/arm/boot/dts/armada-385-turris-omnia.dtsi +@@ -8,8 +8,6 @@ + * Schematic available at https://www.turris.cz/doc/_media/rtrom01-schema.pdf + */ + +-/dts-v1/; +- + #include <dt-bindings/gpio/gpio.h> + #include <dt-bindings/input/input.h> + #include <dt-bindings/leds/common.h> +@@ -88,11 +86,11 @@ pcie@3,0 { + sfp: sfp { + compatible = "sff,sfp"; + i2c-bus = <&sfp_i2c>; +- tx-fault-gpios = <&pcawan 0 GPIO_ACTIVE_HIGH>; +- tx-disable-gpios = <&pcawan 1 GPIO_ACTIVE_HIGH>; +- rate-select0-gpios = <&pcawan 2 GPIO_ACTIVE_HIGH>; +- los-gpios = <&pcawan 3 GPIO_ACTIVE_HIGH>; +- mod-def0-gpios = <&pcawan 4 GPIO_ACTIVE_LOW>; ++ tx-fault-gpios = <&sfpgpio 0 GPIO_ACTIVE_HIGH>; ++ tx-disable-gpios = <&sfpgpio 1 GPIO_ACTIVE_HIGH>; ++ rate-select0-gpios = <&sfpgpio 2 GPIO_ACTIVE_HIGH>; ++ los-gpios = <&sfpgpio 3 GPIO_ACTIVE_HIGH>; ++ mod-def0-gpios = <&sfpgpio 4 GPIO_ACTIVE_LOW>; + maximum-power-milliwatt = <3000>; + + /* +@@ -144,7 +142,7 @@ fixed-link { + }; + }; + +-/* WAN port */ ++/* WAN dual-personality port */ + ð2 { + /* + * eth2 is connected via a multiplexor to both the SFP cage and to +@@ -155,9 +153,8 @@ ð2 { + * is present, U-Boot has to enable the sfp node above, remove phy + * handle and add managed = "in-band-status" property. + */ ++ phys = <&comphy5 2>; + status = "okay"; +- phy-mode = "sgmii"; +- phy-handle = <&phy1>; + phys = <&comphy5 2>; + sfp = <&sfp>; + buffer-manager = <&bm>; +@@ -361,7 +358,7 @@ i2c@7 { + #size-cells = <0>; + reg = <7>; + +- pcawan: gpio@71 { ++ sfpgpio: gpio@71 { + /* + * GPIO expander for SFP+ signals and + * and phy irq +@@ -370,7 +367,7 @@ pcawan: gpio@71 { + reg = <0x71>; + + pinctrl-names = "default"; +- pinctrl-0 = <&pcawan_pins>; ++ pinctrl-0 = <&wanint_pins>; + + interrupt-parent = <&gpio1>; + interrupts = <14 IRQ_TYPE_LEVEL_LOW>; +@@ -450,13 +447,23 @@ fixed-link { + }; + }; + +- /* port 6 is connected to eth0 */ ++ ports@6 { ++ reg = <6>; ++ label = "cpu"; ++ ethernet = <ð0>; ++ phy-mode = "rgmii-id"; ++ ++ fixed-link { ++ speed = <1000>; ++ full-duplex; ++ }; ++ }; + }; + }; + }; + + &pinctrl { +- pcawan_pins: pcawan-pins { ++ wanint_pins: wanint-pins { + marvell,pins = "mpp46"; + marvell,function = "gpio"; + }; +-- +2.30.2 + diff --git a/pkgs/patches-linux-5.15/881-v5.19-rndis_host-enable-the-bogus-MAC-fixup-for-ZTE-device.patch b/pkgs/patches-linux-5.15/881-v5.19-rndis_host-enable-the-bogus-MAC-fixup-for-ZTE-device.patch new file mode 100644 index 0000000..7da2280 --- /dev/null +++ b/pkgs/patches-linux-5.15/881-v5.19-rndis_host-enable-the-bogus-MAC-fixup-for-ZTE-device.patch @@ -0,0 +1,118 @@ +From aa8aff10e969aca0cb64f5e54ff7489355582667 Mon Sep 17 00:00:00 2001 +From: Lech Perczak <lech.perczak@gmail.com> +Date: Fri, 1 Apr 2022 22:04:01 +0200 +Subject: [PATCH 2/3] rndis_host: enable the bogus MAC fixup for ZTE devices + from cdc_ether +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Certain ZTE modems, namely: MF823. MF831, MF910, built-in modem from +MF286R, expose both CDC-ECM and RNDIS network interfaces. +They have a trait of ignoring the locally-administered MAC address +configured on the interface both in CDC-ECM and RNDIS part, +and this leads to dropping of incoming traffic by the host. +However, the workaround was only present in CDC-ECM, and MF286R +explicitly requires it in RNDIS mode. + +Re-use the workaround in rndis_host as well, to fix operation of MF286R +module, some versions of which expose only the RNDIS interface. Do so by +introducing new flag, RNDIS_DRIVER_DATA_DST_MAC_FIXUP, and testing for it +in rndis_rx_fixup. This is required, as RNDIS uses frame batching, and all +of the packets inside the batch need the fixup. This might introduce a +performance penalty, because test is done for every returned Ethernet +frame. + +Apply the workaround to both "flavors" of RNDIS interfaces, as older ZTE +modems, like MF823 found in the wild, report the USB_CLASS_COMM class +interfaces, while MF286R reports USB_CLASS_WIRELESS_CONTROLLER. + +Suggested-by: Bjørn Mork <bjorn@mork.no> +Cc: Kristian Evensen <kristian.evensen@gmail.com> +Cc: Oliver Neukum <oliver@neukum.org> +Signed-off-by: Lech Perczak <lech.perczak@gmail.com> +--- + drivers/net/usb/rndis_host.c | 32 ++++++++++++++++++++++++++++++++ + include/linux/usb/rndis_host.h | 1 + + 2 files changed, 33 insertions(+) + +--- a/drivers/net/usb/rndis_host.c ++++ b/drivers/net/usb/rndis_host.c +@@ -485,10 +485,14 @@ EXPORT_SYMBOL_GPL(rndis_unbind); + */ + int rndis_rx_fixup(struct usbnet *dev, struct sk_buff *skb) + { ++ bool dst_mac_fixup; ++ + /* This check is no longer done by usbnet */ + if (skb->len < dev->net->hard_header_len) + return 0; + ++ dst_mac_fixup = !!(dev->driver_info->data & RNDIS_DRIVER_DATA_DST_MAC_FIXUP); ++ + /* peripheral may have batched packets to us... */ + while (likely(skb->len)) { + struct rndis_data_hdr *hdr = (void *)skb->data; +@@ -523,10 +527,17 @@ int rndis_rx_fixup(struct usbnet *dev, s + break; + skb_pull(skb, msg_len - sizeof *hdr); + skb_trim(skb2, data_len); ++ ++ if (unlikely(dst_mac_fixup)) ++ usbnet_cdc_zte_rx_fixup(dev, skb2); ++ + usbnet_skb_return(dev, skb2); + } + + /* caller will usbnet_skb_return the remaining packet */ ++ if (unlikely(dst_mac_fixup)) ++ usbnet_cdc_zte_rx_fixup(dev, skb); ++ + return 1; + } + EXPORT_SYMBOL_GPL(rndis_rx_fixup); +@@ -600,6 +611,17 @@ static const struct driver_info rndis_po + .tx_fixup = rndis_tx_fixup, + }; + ++static const struct driver_info zte_rndis_info = { ++ .description = "ZTE RNDIS device", ++ .flags = FLAG_ETHER | FLAG_POINTTOPOINT | FLAG_FRAMING_RN | FLAG_NO_SETINT, ++ .data = RNDIS_DRIVER_DATA_DST_MAC_FIXUP, ++ .bind = rndis_bind, ++ .unbind = rndis_unbind, ++ .status = rndis_status, ++ .rx_fixup = rndis_rx_fixup, ++ .tx_fixup = rndis_tx_fixup, ++}; ++ + /*-------------------------------------------------------------------------*/ + + static const struct usb_device_id products [] = { +@@ -614,6 +636,16 @@ static const struct usb_device_id produc + USB_CLASS_COMM, 2 /* ACM */, 0x0ff), + .driver_info = (unsigned long)&rndis_info, + }, { ++ /* ZTE WWAN modules */ ++ USB_VENDOR_AND_INTERFACE_INFO(0x19d2, ++ USB_CLASS_WIRELESS_CONTROLLER, 1, 3), ++ .driver_info = (unsigned long)&zte_rndis_info, ++}, { ++ /* ZTE WWAN modules, ACM flavour */ ++ USB_VENDOR_AND_INTERFACE_INFO(0x19d2, ++ USB_CLASS_COMM, 2 /* ACM */, 0x0ff), ++ .driver_info = (unsigned long)&zte_rndis_info, ++}, { + /* RNDIS is MSFT's un-official variant of CDC ACM */ + USB_INTERFACE_INFO(USB_CLASS_COMM, 2 /* ACM */, 0x0ff), + .driver_info = (unsigned long) &rndis_info, +--- a/include/linux/usb/rndis_host.h ++++ b/include/linux/usb/rndis_host.h +@@ -197,6 +197,7 @@ struct rndis_keepalive_c { /* IN (option + + /* Flags for driver_info::data */ + #define RNDIS_DRIVER_DATA_POLL_STATUS 1 /* poll status before control */ ++#define RNDIS_DRIVER_DATA_DST_MAC_FIXUP 2 /* device ignores configured MAC address */ + + extern void rndis_status(struct usbnet *dev, struct urb *urb); + extern int diff --git a/pkgs/patches-linux-5.15/882-v5.19-rndis_host-limit-scope-of-bogus-MAC-address-detectio.patch b/pkgs/patches-linux-5.15/882-v5.19-rndis_host-limit-scope-of-bogus-MAC-address-detectio.patch new file mode 100644 index 0000000..ebe7a43 --- /dev/null +++ b/pkgs/patches-linux-5.15/882-v5.19-rndis_host-limit-scope-of-bogus-MAC-address-detectio.patch @@ -0,0 +1,63 @@ +From 9bfb4bcda7ba32d73ea322ea56a8ebe32e9247f6 Mon Sep 17 00:00:00 2001 +From: Lech Perczak <lech.perczak@gmail.com> +Date: Sat, 2 Apr 2022 02:19:57 +0200 +Subject: [PATCH 3/3] rndis_host: limit scope of bogus MAC address detection to + ZTE devices +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Reporting of bogus MAC addresses and ignoring configuration of new +destination address wasn't observed outside of a range of ZTE devices, +among which this seems to be the common bug. Align rndis_host driver +with implementation found in cdc_ether, which also limits this workaround +to ZTE devices. + +Suggested-by: Bjørn Mork <bjorn@mork.no> +Cc: Kristian Evensen <kristian.evensen@gmail.com> +Cc: Oliver Neukum <oliver@neukum.org> +Signed-off-by: Lech Perczak <lech.perczak@gmail.com> +--- + drivers/net/usb/rndis_host.c | 17 ++++++++++++----- + 1 file changed, 12 insertions(+), 5 deletions(-) + +--- a/drivers/net/usb/rndis_host.c ++++ b/drivers/net/usb/rndis_host.c +@@ -418,10 +418,7 @@ generic_rndis_bind(struct usbnet *dev, s + goto halt_fail_and_release; + } + +- if (bp[0] & 0x02) +- eth_hw_addr_random(net); +- else +- ether_addr_copy(net->dev_addr, bp); ++ ether_addr_copy(net->dev_addr, bp); + + /* set a nonzero filter to enable data transfers */ + memset(u.set, 0, sizeof *u.set); +@@ -463,6 +460,16 @@ static int rndis_bind(struct usbnet *dev + return generic_rndis_bind(dev, intf, FLAG_RNDIS_PHYM_NOT_WIRELESS); + } + ++static int zte_rndis_bind(struct usbnet *dev, struct usb_interface *intf) ++{ ++ int status = rndis_bind(dev, intf); ++ ++ if (!status && (dev->net->dev_addr[0] & 0x02)) ++ eth_hw_addr_random(dev->net); ++ ++ return status; ++} ++ + void rndis_unbind(struct usbnet *dev, struct usb_interface *intf) + { + struct rndis_halt *halt; +@@ -615,7 +622,7 @@ static const struct driver_info zte_rndi + .description = "ZTE RNDIS device", + .flags = FLAG_ETHER | FLAG_POINTTOPOINT | FLAG_FRAMING_RN | FLAG_NO_SETINT, + .data = RNDIS_DRIVER_DATA_DST_MAC_FIXUP, +- .bind = rndis_bind, ++ .bind = zte_rndis_bind, + .unbind = rndis_unbind, + .status = rndis_status, + .rx_fixup = rndis_rx_fixup, diff --git a/pkgs/patches-linux-5.15/900-v5.20-powerpc-add-Turris-1x-dts.patch b/pkgs/patches-linux-5.15/900-v5.20-powerpc-add-Turris-1x-dts.patch new file mode 100644 index 0000000..b87db86 --- /dev/null +++ b/pkgs/patches-linux-5.15/900-v5.20-powerpc-add-Turris-1x-dts.patch @@ -0,0 +1,508 @@ +From 54c15ec3b738c6086f2be001dae962ec412640e5 Mon Sep 17 00:00:00 2001 +From: Pali Rohár <pali@kernel.org> +Date: Fri, 24 Jun 2022 10:55:50 +0200 +Subject: powerpc: dts: Add DTS file for CZ.NIC Turris 1.x routers +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +CZ.NIC Turris 1.0 and 1.1 are open source routers, they have dual-core +PowerPC Freescale P2020 CPU and are based on Freescale P2020RDB-PC-A board. +Hardware design is fully open source, all firmware and hardware design +files are available at Turris project website: + +https://docs.turris.cz/hw/turris-1x/turris-1x/ +https://project.turris.cz/en/hardware.html + +Signed-off-by: Pali Rohár <pali@kernel.org> +Signed-off-by: Michael Ellerman <mpe@ellerman.id.au> +Link: https://lore.kernel.org/r/20220624085550.20570-1-pali@kernel.org +--- + arch/powerpc/boot/dts/turris1x.dts | 475 +++++++++++++++++++++++++++++++++++++ + 1 file changed, 475 insertions(+) + create mode 100644 arch/powerpc/boot/dts/turris1x.dts + +diff --git a/arch/powerpc/boot/dts/turris1x.dts b/arch/powerpc/boot/dts/turris1x.dts +new file mode 100644 +index 0000000000000..c76b628cf0262 +--- /dev/null ++++ b/arch/powerpc/boot/dts/turris1x.dts +@@ -0,0 +1,475 @@ ++// SPDX-License-Identifier: GPL-2.0+ ++/* ++ * Turris 1.x Device Tree Source ++ * ++ * Copyright 2013 - 2022 CZ.NIC z.s.p.o. (http://www.nic.cz/) ++ * ++ * Pinout, Schematics and Altium hardware design files are open source ++ * and available at: https://docs.turris.cz/hw/turris-1x/turris-1x/ ++ */ ++ ++#include <dt-bindings/gpio/gpio.h> ++#include <dt-bindings/interrupt-controller/irq.h> ++#include <dt-bindings/leds/common.h> ++/include/ "fsl/p2020si-pre.dtsi" ++ ++/ { ++ model = "Turris 1.x"; ++ compatible = "cznic,turris1x", "fsl,P2020RDB-PC"; /* fsl,P2020RDB-PC is required for booting Linux */ ++ ++ aliases { ++ ethernet0 = &enet0; ++ ethernet1 = &enet1; ++ ethernet2 = &enet2; ++ serial0 = &serial0; ++ serial1 = &serial1; ++ pci0 = &pci0; ++ pci1 = &pci1; ++ pci2 = &pci2; ++ spi0 = &spi0; ++ }; ++ ++ memory { ++ device_type = "memory"; ++ }; ++ ++ soc: soc@ffe00000 { ++ ranges = <0x0 0x0 0xffe00000 0x00100000>; ++ ++ i2c@3000 { ++ /* PCA9557PW GPIO controller for boot config */ ++ gpio-controller@18 { ++ compatible = "nxp,pca9557"; ++ label = "bootcfg"; ++ reg = <0x18>; ++ #gpio-cells = <2>; ++ gpio-controller; ++ polarity = <0x00>; ++ }; ++ ++ /* STM32F030R8T6 MCU for power control */ ++ power-control@2a { ++ /* ++ * Turris Power Control firmware runs on STM32F0 MCU. ++ * This firmware is open source and available at: ++ * https://gitlab.nic.cz/turris/hw/turris_power_control ++ */ ++ reg = <0x2a>; ++ }; ++ ++ /* DDR3 SPD/EEPROM PSWP instruction */ ++ eeprom@32 { ++ reg = <0x32>; ++ }; ++ ++ /* SA56004ED temperature control */ ++ temperature-sensor@4c { ++ compatible = "nxp,sa56004"; ++ reg = <0x4c>; ++ interrupt-parent = <&gpio>; ++ interrupts = <12 IRQ_TYPE_LEVEL_LOW>, /* GPIO12 - ALERT pin */ ++ <13 IRQ_TYPE_LEVEL_LOW>; /* GPIO13 - CRIT pin */ ++ }; ++ ++ /* DDR3 SPD/EEPROM */ ++ eeprom@52 { ++ compatible = "atmel,spd"; ++ reg = <0x52>; ++ }; ++ ++ /* MCP79402-I/ST Protected EEPROM */ ++ eeprom@57 { ++ reg = <0x57>; ++ }; ++ ++ /* ATSHA204-TH-DA-T crypto module */ ++ crypto@64 { ++ compatible = "atmel,atsha204"; ++ reg = <0x64>; ++ }; ++ ++ /* IDT6V49205BNLGI clock generator */ ++ clock-generator@69 { ++ compatible = "idt,6v49205b"; ++ reg = <0x69>; ++ }; ++ ++ /* MCP79402-I/ST RTC */ ++ rtc@6f { ++ compatible = "microchip,mcp7940x"; ++ reg = <0x6f>; ++ interrupt-parent = <&gpio>; ++ interrupts = <14 0>; /* GPIO14 - MFP pin */ ++ }; ++ }; ++ ++ /* SPI on connector P1 */ ++ spi0: spi@7000 { ++ }; ++ ++ gpio: gpio-controller@fc00 { ++ #interrupt-cells = <2>; ++ interrupt-controller; ++ }; ++ ++ /* Connected to SMSC USB2412-DZK 2-Port USB 2.0 Hub Controller */ ++ usb@22000 { ++ phy_type = "ulpi"; ++ dr_mode = "host"; ++ }; ++ ++ enet0: ethernet@24000 { ++ /* Connected to port 6 of QCA8337N-AL3C switch */ ++ phy-connection-type = "rgmii-id"; ++ ++ fixed-link { ++ speed = <1000>; ++ full-duplex; ++ }; ++ }; ++ ++ mdio@24520 { ++ /* KSZ9031RNXCA ethernet phy for WAN port */ ++ phy: ethernet-phy@7 { ++ interrupts = <3 1 0 0>; ++ reg = <0x7>; ++ }; ++ ++ /* QCA8337N-AL3C switch with integrated ethernet PHYs for LAN ports */ ++ switch@10 { ++ compatible = "qca,qca8337"; ++ interrupts = <2 1 0 0>; ++ reg = <0x10>; ++ ++ ports { ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ port@0 { ++ reg = <0>; ++ label = "cpu1"; ++ ethernet = <&enet1>; ++ phy-mode = "rgmii-id"; ++ ++ fixed-link { ++ speed = <1000>; ++ full-duplex; ++ }; ++ }; ++ ++ port@1 { ++ reg = <1>; ++ label = "lan5"; ++ }; ++ ++ port@2 { ++ reg = <2>; ++ label = "lan4"; ++ }; ++ ++ port@3 { ++ reg = <3>; ++ label = "lan3"; ++ }; ++ ++ port@4 { ++ reg = <4>; ++ label = "lan2"; ++ }; ++ ++ port@5 { ++ reg = <5>; ++ label = "lan1"; ++ }; ++ ++ port@6 { ++ reg = <6>; ++ label = "cpu0"; ++ ethernet = <&enet0>; ++ phy-mode = "rgmii-id"; ++ ++ fixed-link { ++ speed = <1000>; ++ full-duplex; ++ }; ++ }; ++ }; ++ }; ++ }; ++ ++ ptp_clock@24e00 { ++ fsl,tclk-period = <5>; ++ fsl,tmr-prsc = <200>; ++ fsl,tmr-add = <0xcccccccd>; ++ fsl,tmr-fiper1 = <0x3b9ac9fb>; ++ fsl,tmr-fiper2 = <0x0001869b>; ++ fsl,max-adj = <249999999>; ++ }; ++ ++ enet1: ethernet@25000 { ++ /* Connected to port 0 of QCA8337N-AL3C switch */ ++ phy-connection-type = "rgmii-id"; ++ ++ fixed-link { ++ speed = <1000>; ++ full-duplex; ++ }; ++ }; ++ ++ mdio@25520 { ++ status = "disabled"; ++ }; ++ ++ enet2: ethernet@26000 { ++ /* Connected to KSZ9031RNXCA ethernet phy (WAN port) */ ++ label = "wan"; ++ phy-handle = <&phy>; ++ phy-connection-type = "rgmii-id"; ++ }; ++ ++ mdio@26520 { ++ status = "disabled"; ++ }; ++ ++ sdhc@2e000 { ++ bus-width = <4>; ++ cd-gpios = <&gpio 8 GPIO_ACTIVE_LOW>; ++ }; ++ }; ++ ++ lbc: localbus@ffe05000 { ++ reg = <0 0xffe05000 0 0x1000>; ++ ++ ranges = <0x0 0x0 0x0 0xef000000 0x01000000>, /* NOR */ ++ <0x1 0x0 0x0 0xff800000 0x00040000>, /* NAND */ ++ <0x3 0x0 0x0 0xffa00000 0x00020000>; /* CPLD */ ++ ++ /* S29GL128P90TFIR10 NOR */ ++ nor@0,0 { ++ compatible = "cfi-flash"; ++ reg = <0x0 0x0 0x01000000>; ++ bank-width = <2>; ++ device-width = <1>; ++ ++ partitions { ++ compatible = "fixed-partitions"; ++ #address-cells = <1>; ++ #size-cells = <1>; ++ ++ partition@0 { ++ /* 128 kB for Device Tree Blob */ ++ reg = <0x00000000 0x00020000>; ++ label = "dtb"; ++ }; ++ ++ partition@20000 { ++ /* 1.7 MB for Rescue Linux Kernel Image */ ++ reg = <0x00020000 0x001a0000>; ++ label = "rescue-kernel"; ++ }; ++ ++ partition@1c0000 { ++ /* 1.5 MB for Rescue JFFS2 Root File System */ ++ reg = <0x001c0000 0x00180000>; ++ label = "rescue-rootfs"; ++ }; ++ ++ partition@340000 { ++ /* 11 MB for TAR.XZ Backup with content of NAND Root File System */ ++ reg = <0x00340000 0x00b00000>; ++ label = "backup-rootfs"; ++ }; ++ ++ partition@e40000 { ++ /* 768 kB for Certificates JFFS2 File System */ ++ reg = <0x00e40000 0x000c0000>; ++ label = "certificates"; ++ }; ++ ++ /* free unused space 0x00f00000-0x00f20000 */ ++ ++ partition@f20000 { ++ /* 128 kB for U-Boot Environment Variables */ ++ reg = <0x00f20000 0x00020000>; ++ label = "u-boot-env"; ++ }; ++ ++ partition@f40000 { ++ /* 768 kB for U-Boot Bootloader Image */ ++ reg = <0x00f40000 0x000c0000>; ++ label = "u-boot"; ++ }; ++ }; ++ }; ++ ++ /* MT29F2G08ABAEAWP:E NAND */ ++ nand@1,0 { ++ compatible = "fsl,p2020-fcm-nand", "fsl,elbc-fcm-nand"; ++ reg = <0x1 0x0 0x00040000>; ++ nand-ecc-mode = "soft"; ++ nand-ecc-algo = "bch"; ++ ++ partitions { ++ compatible = "fixed-partitions"; ++ #address-cells = <1>; ++ #size-cells = <1>; ++ ++ partition@0 { ++ /* 256 MB for UBI with one volume: UBIFS Root File System */ ++ reg = <0x00000000 0x10000000>; ++ label = "rootfs"; ++ }; ++ }; ++ }; ++ ++ /* LCMXO1200C-3FTN256C FPGA */ ++ cpld@3,0 { ++ /* ++ * Turris CPLD firmware which runs on this Lattice FPGA, ++ * is extended version of P1021RDB-PC CPLD v4.1 firmware. ++ * It is backward compatible with its original version ++ * and the only extension is support for Turris LEDs. ++ * Turris CPLD firmware is open source and available at: ++ * https://gitlab.nic.cz/turris/hw/turris_cpld/-/blob/master/CZ_NIC_Router_CPLD.v ++ */ ++ compatible = "cznic,turris1x-cpld", "fsl,p1021rdb-pc-cpld", "simple-bus"; ++ reg = <0x3 0x0 0x30>; ++ #address-cells = <1>; ++ #size-cells = <1>; ++ ranges = <0x0 0x3 0x0 0x00020000>; ++ ++ /* MAX6370KA+T watchdog */ ++ watchdog@2 { ++ /* ++ * CPLD firmware maps SET0, SET1 and SET2 ++ * input logic of MAX6370KA+T chip to CPLD ++ * memory space at byte offset 0x2. WDI ++ * input logic is outside of the CPLD and ++ * connected via external GPIO. ++ */ ++ compatible = "maxim,max6370"; ++ reg = <0x02 0x01>; ++ gpios = <&gpio 11 GPIO_ACTIVE_LOW>; ++ }; ++ ++ led-controller@13 { ++ /* ++ * LEDs are controlled by CPLD firmware. ++ * All five LAN LEDs share common RGB settings ++ * and so it is not possible to set different ++ * colors on different LAN ports. ++ */ ++ compatible = "cznic,turris1x-leds"; ++ reg = <0x13 0x1d>; ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ multi-led@0 { ++ reg = <0x0>; ++ color = <LED_COLOR_ID_RGB>; ++ function = LED_FUNCTION_WAN; ++ }; ++ ++ multi-led@1 { ++ reg = <0x1>; ++ color = <LED_COLOR_ID_RGB>; ++ function = LED_FUNCTION_LAN; ++ function-enumerator = <5>; ++ }; ++ ++ multi-led@2 { ++ reg = <0x2>; ++ color = <LED_COLOR_ID_RGB>; ++ function = LED_FUNCTION_LAN; ++ function-enumerator = <4>; ++ }; ++ ++ multi-led@3 { ++ reg = <0x3>; ++ color = <LED_COLOR_ID_RGB>; ++ function = LED_FUNCTION_LAN; ++ function-enumerator = <3>; ++ }; ++ ++ multi-led@4 { ++ reg = <0x4>; ++ color = <LED_COLOR_ID_RGB>; ++ function = LED_FUNCTION_LAN; ++ function-enumerator = <2>; ++ }; ++ ++ multi-led@5 { ++ reg = <0x5>; ++ color = <LED_COLOR_ID_RGB>; ++ function = LED_FUNCTION_LAN; ++ function-enumerator = <1>; ++ }; ++ ++ multi-led@6 { ++ reg = <0x6>; ++ color = <LED_COLOR_ID_RGB>; ++ function = LED_FUNCTION_WLAN; ++ }; ++ ++ multi-led@7 { ++ reg = <0x7>; ++ color = <LED_COLOR_ID_RGB>; ++ function = LED_FUNCTION_POWER; ++ }; ++ }; ++ }; ++ }; ++ ++ pci2: pcie@ffe08000 { ++ /* ++ * PCIe bus for on-board TUSB7340RKM USB 3.0 xHCI controller. ++ * This xHCI controller is available only on Turris 1.1 boards. ++ * Turris 1.0 boards have nothing connected to this PCIe bus, ++ * so system would see only PCIe Root Port of this PCIe Root ++ * Complex. TUSB7340RKM xHCI controller has four SuperSpeed ++ * channels. Channel 0 is connected to the front USB 3.0 port, ++ * channel 1 (but only USB 2.0 subset) to USB 2.0 pins on mPCIe ++ * slot 1 (CN5), channels 2 and 3 to connector P600. ++ * ++ * P2020 PCIe Root Port uses 1MB of PCIe MEM and xHCI controller ++ * uses 64kB + 8kB of PCIe MEM. No PCIe IO is used or required. ++ * So allocate 2MB of PCIe MEM for this PCIe bus. ++ */ ++ reg = <0 0xffe08000 0 0x1000>; ++ ranges = <0x02000000 0x0 0xc0000000 0 0xc0000000 0x0 0x00200000>, /* MEM */ ++ <0x01000000 0x0 0x00000000 0 0xffc20000 0x0 0x00010000>; /* IO */ ++ ++ pcie@0 { ++ ranges; ++ }; ++ }; ++ ++ pci1: pcie@ffe09000 { ++ /* PCIe bus on mPCIe slot 2 (CN6) for expansion mPCIe card */ ++ reg = <0 0xffe09000 0 0x1000>; ++ ranges = <0x02000000 0x0 0xa0000000 0 0xa0000000 0x0 0x20000000>, /* MEM */ ++ <0x01000000 0x0 0x00000000 0 0xffc10000 0x0 0x00010000>; /* IO */ ++ ++ pcie@0 { ++ ranges; ++ }; ++ }; ++ ++ pci0: pcie@ffe0a000 { ++ /* ++ * PCIe bus on mPCIe slot 1 (CN5) for expansion mPCIe card. ++ * Turris 1.1 boards have in this mPCIe slot additional USB 2.0 ++ * pins via channel 1 of TUSB7340RKM xHCI controller and also ++ * additional SIM card slot, both for USB-based WWAN cards. ++ */ ++ reg = <0 0xffe0a000 0 0x1000>; ++ ranges = <0x02000000 0x0 0x80000000 0 0x80000000 0x0 0x20000000>, /* MEM */ ++ <0x01000000 0x0 0x00000000 0 0xffc00000 0x0 0x00010000>; /* IO */ ++ ++ pcie@0 { ++ ranges; ++ }; ++ }; ++}; ++ ++/include/ "fsl/p2020si-post.dtsi" +-- +cgit + diff --git a/pkgs/patches-linux-5.15/901-debloat_sock_diag.patch b/pkgs/patches-linux-5.15/901-debloat_sock_diag.patch new file mode 100644 index 0000000..ab629d5 --- /dev/null +++ b/pkgs/patches-linux-5.15/901-debloat_sock_diag.patch @@ -0,0 +1,162 @@ +From 3b6115d6b57a263bdc8c9b1df273bd4a7955eead Mon Sep 17 00:00:00 2001 +From: Felix Fietkau <nbd@nbd.name> +Date: Sat, 8 Jul 2017 08:16:31 +0200 +Subject: debloat: add some debloat patches, strip down procfs and make O_DIRECT support optional, saves ~15K after lzma on MIPS + +Signed-off-by: Felix Fietkau <nbd@nbd.name> +--- + net/Kconfig | 3 +++ + net/core/Makefile | 3 ++- + net/core/sock.c | 2 ++ + net/ipv4/Kconfig | 1 + + net/netlink/Kconfig | 1 + + net/packet/Kconfig | 1 + + net/unix/Kconfig | 1 + + 7 files changed, 11 insertions(+), 1 deletion(-) + +--- a/net/Kconfig ++++ b/net/Kconfig +@@ -104,6 +104,9 @@ source "net/mptcp/Kconfig" + + endif # if INET + ++config SOCK_DIAG ++ bool ++ + config NETWORK_SECMARK + bool "Security Marking" + help +--- a/net/core/Makefile ++++ b/net/core/Makefile +@@ -10,9 +10,10 @@ obj-$(CONFIG_SYSCTL) += sysctl_net_core. + + obj-y += dev.o dev_addr_lists.o dst.o netevent.o \ + neighbour.o rtnetlink.o utils.o link_watch.o filter.o \ +- sock_diag.o dev_ioctl.o tso.o sock_reuseport.o \ ++ dev_ioctl.o tso.o sock_reuseport.o \ + fib_notifier.o xdp.o flow_offload.o + ++obj-$(CONFIG_SOCK_DIAG) += sock_diag.o + obj-y += net-sysfs.o + obj-$(CONFIG_PAGE_POOL) += page_pool.o + obj-$(CONFIG_PROC_FS) += net-procfs.o +--- a/net/core/sock.c ++++ b/net/core/sock.c +@@ -114,6 +114,7 @@ + #include <linux/memcontrol.h> + #include <linux/prefetch.h> + #include <linux/compat.h> ++#include <linux/cookie.h> + + #include <linux/uaccess.h> + +@@ -143,6 +144,7 @@ + + static DEFINE_MUTEX(proto_list_mutex); + static LIST_HEAD(proto_list); ++DEFINE_COOKIE(sock_cookie); + + static void sock_inuse_add(struct net *net, int val); + +@@ -545,6 +547,18 @@ discard_and_relse: + } + EXPORT_SYMBOL(__sk_receive_skb); + ++u64 __sock_gen_cookie(struct sock *sk) ++{ ++ while (1) { ++ u64 res = atomic64_read(&sk->sk_cookie); ++ ++ if (res) ++ return res; ++ res = gen_cookie_next(&sock_cookie); ++ atomic64_cmpxchg(&sk->sk_cookie, 0, res); ++ } ++} ++ + INDIRECT_CALLABLE_DECLARE(struct dst_entry *ip6_dst_check(struct dst_entry *, + u32)); + INDIRECT_CALLABLE_DECLARE(struct dst_entry *ipv4_dst_check(struct dst_entry *, +@@ -1983,9 +1997,11 @@ static void __sk_free(struct sock *sk) + if (likely(sk->sk_net_refcnt)) + sock_inuse_add(sock_net(sk), -1); + ++#ifdef CONFIG_SOCK_DIAG + if (unlikely(sk->sk_net_refcnt && sock_diag_has_destroy_listeners(sk))) + sock_diag_broadcast_destroy(sk); + else ++#endif + sk_destruct(sk); + } + +--- a/net/core/sock_diag.c ++++ b/net/core/sock_diag.c +@@ -11,7 +11,6 @@ + #include <linux/tcp.h> + #include <linux/workqueue.h> + #include <linux/nospec.h> +-#include <linux/cookie.h> + #include <linux/inet_diag.h> + #include <linux/sock_diag.h> + +@@ -20,20 +19,6 @@ static int (*inet_rcv_compat)(struct sk_ + static DEFINE_MUTEX(sock_diag_table_mutex); + static struct workqueue_struct *broadcast_wq; + +-DEFINE_COOKIE(sock_cookie); +- +-u64 __sock_gen_cookie(struct sock *sk) +-{ +- while (1) { +- u64 res = atomic64_read(&sk->sk_cookie); +- +- if (res) +- return res; +- res = gen_cookie_next(&sock_cookie); +- atomic64_cmpxchg(&sk->sk_cookie, 0, res); +- } +-} +- + int sock_diag_check_cookie(struct sock *sk, const __u32 *cookie) + { + u64 res; +--- a/net/ipv4/Kconfig ++++ b/net/ipv4/Kconfig +@@ -414,6 +414,7 @@ config INET_TUNNEL + + config INET_DIAG + tristate "INET: socket monitoring interface" ++ select SOCK_DIAG + default y + help + Support for INET (TCP, DCCP, etc) socket monitoring interface used by +--- a/net/netlink/Kconfig ++++ b/net/netlink/Kconfig +@@ -5,6 +5,7 @@ + + config NETLINK_DIAG + tristate "NETLINK: socket monitoring interface" ++ select SOCK_DIAG + default n + help + Support for NETLINK socket monitoring interface used by the ss tool. +--- a/net/packet/Kconfig ++++ b/net/packet/Kconfig +@@ -19,6 +19,7 @@ config PACKET + config PACKET_DIAG + tristate "Packet: sockets monitoring interface" + depends on PACKET ++ select SOCK_DIAG + default n + help + Support for PF_PACKET sockets monitoring interface used by the ss tool. +--- a/net/unix/Kconfig ++++ b/net/unix/Kconfig +@@ -33,6 +33,7 @@ config AF_UNIX_OOB + config UNIX_DIAG + tristate "UNIX: socket monitoring interface" + depends on UNIX ++ select SOCK_DIAG + default n + help + Support for UNIX socket monitoring interface used by the ss tool. diff --git a/pkgs/patches-linux-5.15/901-dt-bindings-Add-IEI-vendor-prefix-and-IEI-WT61P803-P.patch b/pkgs/patches-linux-5.15/901-dt-bindings-Add-IEI-vendor-prefix-and-IEI-WT61P803-P.patch new file mode 100644 index 0000000..3446086 --- /dev/null +++ b/pkgs/patches-linux-5.15/901-dt-bindings-Add-IEI-vendor-prefix-and-IEI-WT61P803-P.patch @@ -0,0 +1,218 @@ +From aa4a0ccc41997f2da172165c92803abace43bd1c Mon Sep 17 00:00:00 2001 +From: Luka Kovacic <luka.kovacic () sartura ! hr> +Date: Tue, 24 Aug 2021 12:44:32 +0000 +Subject: [PATCH 1/7] dt-bindings: Add IEI vendor prefix and IEI WT61P803 + PUZZLE driver bindings + +Add the IEI WT61P803 PUZZLE Device Tree bindings for MFD, HWMON and LED +drivers. A new vendor prefix is also added accordingly for +IEI Integration Corp. + +Signed-off-by: Luka Kovacic <luka.kovacic@sartura.hr> +Signed-off-by: Pavo Banicevic <pavo.banicevic@sartura.hr> +Cc: Luka Perkov <luka.perkov@sartura.hr> +Cc: Robert Marko <robert.marko@sartura.hr> +--- + .../hwmon/iei,wt61p803-puzzle-hwmon.yaml | 53 ++++++++++++ + .../leds/iei,wt61p803-puzzle-leds.yaml | 39 +++++++++ + .../bindings/mfd/iei,wt61p803-puzzle.yaml | 82 +++++++++++++++++++ + .../devicetree/bindings/vendor-prefixes.yaml | 2 + + 4 files changed, 176 insertions(+) + create mode 100644 Documentation/devicetree/bindings/hwmon/iei,wt61p803-puzzle-hwmon.yaml + create mode 100644 Documentation/devicetree/bindings/leds/iei,wt61p803-puzzle-leds.yaml + create mode 100644 Documentation/devicetree/bindings/mfd/iei,wt61p803-puzzle.yaml + +--- /dev/null ++++ b/Documentation/devicetree/bindings/hwmon/iei,wt61p803-puzzle-hwmon.yaml +@@ -0,0 +1,53 @@ ++# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause ++%YAML 1.2 ++--- ++$id: http://devicetree.org/schemas/hwmon/iei,wt61p803-puzzle-hwmon.yaml# ++$schema: http://devicetree.org/meta-schemas/core.yaml# ++ ++title: IEI WT61P803 PUZZLE MCU HWMON module from IEI Integration Corp. ++ ++maintainers: ++ - Luka Kovacic <luka.kovacic@sartura.hr> ++ ++description: | ++ This module is a part of the IEI WT61P803 PUZZLE MFD device. For more details ++ see Documentation/devicetree/bindings/mfd/iei,wt61p803-puzzle.yaml. ++ ++ The HWMON module is a sub-node of the MCU node in the Device Tree. ++ ++properties: ++ compatible: ++ const: iei,wt61p803-puzzle-hwmon ++ ++ "#address-cells": ++ const: 1 ++ ++ "#size-cells": ++ const: 0 ++ ++patternProperties: ++ "^fan-group@[0-1]$": ++ type: object ++ properties: ++ reg: ++ minimum: 0 ++ maximum: 1 ++ description: ++ Fan group ID ++ ++ cooling-levels: ++ minItems: 1 ++ maxItems: 255 ++ description: ++ Cooling levels for the fans (PWM value mapping) ++ description: | ++ Properties for each fan group. ++ required: ++ - reg ++ ++required: ++ - compatible ++ - "#address-cells" ++ - "#size-cells" ++ ++additionalProperties: false +--- /dev/null ++++ b/Documentation/devicetree/bindings/leds/iei,wt61p803-puzzle-leds.yaml +@@ -0,0 +1,39 @@ ++# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause ++%YAML 1.2 ++--- ++$id: http://devicetree.org/schemas/leds/iei,wt61p803-puzzle-leds.yaml# ++$schema: http://devicetree.org/meta-schemas/core.yaml# ++ ++title: IEI WT61P803 PUZZLE MCU LED module from IEI Integration Corp. ++ ++maintainers: ++ - Luka Kovacic <luka.kovacic@sartura.hr> ++ ++description: | ++ This module is a part of the IEI WT61P803 PUZZLE MFD device. For more details ++ see Documentation/devicetree/bindings/mfd/iei,wt61p803-puzzle.yaml. ++ ++ The LED module is a sub-node of the MCU node in the Device Tree. ++ ++properties: ++ compatible: ++ const: iei,wt61p803-puzzle-leds ++ ++ "#address-cells": ++ const: 1 ++ ++ "#size-cells": ++ const: 0 ++ ++ led@0: ++ type: object ++ $ref: common.yaml ++ description: | ++ Properties for a single LED. ++ ++required: ++ - compatible ++ - "#address-cells" ++ - "#size-cells" ++ ++additionalProperties: false +--- /dev/null ++++ b/Documentation/devicetree/bindings/mfd/iei,wt61p803-puzzle.yaml +@@ -0,0 +1,82 @@ ++# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause ++%YAML 1.2 ++--- ++$id: http://devicetree.org/schemas/mfd/iei,wt61p803-puzzle.yaml# ++$schema: http://devicetree.org/meta-schemas/core.yaml# ++ ++title: IEI WT61P803 PUZZLE MCU from IEI Integration Corp. ++ ++maintainers: ++ - Luka Kovacic <luka.kovacic@sartura.hr> ++ ++description: | ++ IEI WT61P803 PUZZLE MCU is embedded in some IEI Puzzle series boards. ++ It's used for controlling system power states, fans, LEDs and temperature ++ sensors. ++ ++ For Device Tree bindings of other sub-modules (HWMON, LEDs) refer to the ++ binding documents under the respective subsystem directories. ++ ++properties: ++ compatible: ++ const: iei,wt61p803-puzzle ++ ++ current-speed: ++ description: ++ Serial bus speed in bps ++ maxItems: 1 ++ ++ enable-beep: true ++ ++ hwmon: ++ $ref: /schemas/hwmon/iei,wt61p803-puzzle-hwmon.yaml ++ ++ leds: ++ $ref: /schemas/leds/iei,wt61p803-puzzle-leds.yaml ++ ++required: ++ - compatible ++ - current-speed ++ ++additionalProperties: false ++ ++examples: ++ - | ++ #include <dt-bindings/leds/common.h> ++ serial { ++ mcu { ++ compatible = "iei,wt61p803-puzzle"; ++ current-speed = <115200>; ++ enable-beep; ++ ++ leds { ++ compatible = "iei,wt61p803-puzzle-leds"; ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ led@0 { ++ reg = <0>; ++ function = LED_FUNCTION_POWER; ++ color = <LED_COLOR_ID_BLUE>; ++ }; ++ }; ++ ++ hwmon { ++ compatible = "iei,wt61p803-puzzle-hwmon"; ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ fan-group@0 { ++ #cooling-cells = <2>; ++ reg = <0x00>; ++ cooling-levels = <64 102 170 230 250>; ++ }; ++ ++ fan-group@1 { ++ #cooling-cells = <2>; ++ reg = <0x01>; ++ cooling-levels = <64 102 170 230 250>; ++ }; ++ }; ++ }; ++ }; +--- a/Documentation/devicetree/bindings/vendor-prefixes.yaml ++++ b/Documentation/devicetree/bindings/vendor-prefixes.yaml +@@ -519,6 +519,8 @@ patternProperties: + description: IC Plus Corp. + "^idt,.*": + description: Integrated Device Technologies, Inc. ++ "^iei,.*": ++ description: IEI Integration Corp. + "^ifi,.*": + description: Ingenieurburo Fur Ic-Technologie (I/F/I) + "^ilitek,.*": diff --git a/pkgs/patches-linux-5.15/902-debloat_proc.patch b/pkgs/patches-linux-5.15/902-debloat_proc.patch new file mode 100644 index 0000000..c58370e --- /dev/null +++ b/pkgs/patches-linux-5.15/902-debloat_proc.patch @@ -0,0 +1,408 @@ +From 9e3f1d0805b2d919904dd9a4ff0d956314cc3cba Mon Sep 17 00:00:00 2001 +From: Felix Fietkau <nbd@nbd.name> +Date: Sat, 8 Jul 2017 08:20:09 +0200 +Subject: debloat: procfs + +Signed-off-by: Felix Fietkau <nbd@nbd.name> +--- + fs/locks.c | 2 ++ + fs/proc/Kconfig | 5 +++++ + fs/proc/consoles.c | 3 +++ + fs/proc/proc_tty.c | 11 ++++++++++- + include/net/snmp.h | 18 +++++++++++++++++- + ipc/msg.c | 3 +++ + ipc/sem.c | 2 ++ + ipc/shm.c | 2 ++ + ipc/util.c | 3 +++ + kernel/exec_domain.c | 2 ++ + kernel/irq/proc.c | 9 +++++++++ + kernel/time/timer_list.c | 2 ++ + mm/vmalloc.c | 2 ++ + mm/vmstat.c | 8 +++++--- + net/8021q/vlanproc.c | 6 ++++++ + net/core/net-procfs.c | 18 ++++++++++++------ + net/core/sock.c | 2 ++ + net/ipv4/fib_trie.c | 18 ++++++++++++------ + net/ipv4/proc.c | 3 +++ + net/ipv4/route.c | 3 +++ + 20 files changed, 105 insertions(+), 17 deletions(-) + +--- a/fs/locks.c ++++ b/fs/locks.c +@@ -2929,6 +2929,8 @@ static const struct seq_operations locks + + static int __init proc_locks_init(void) + { ++ if (IS_ENABLED(CONFIG_PROC_STRIPPED)) ++ return 0; + proc_create_seq_private("locks", 0, NULL, &locks_seq_operations, + sizeof(struct locks_iterator), NULL); + return 0; +--- a/fs/proc/Kconfig ++++ b/fs/proc/Kconfig +@@ -100,6 +100,11 @@ config PROC_CHILDREN + Say Y if you are running any user-space software which takes benefit from + this interface. For example, rkt is such a piece of software. + ++config PROC_STRIPPED ++ default n ++ depends on EXPERT ++ bool "Strip non-essential /proc functionality to reduce code size" ++ + config PROC_PID_ARCH_STATUS + def_bool n + depends on PROC_FS +--- a/fs/proc/consoles.c ++++ b/fs/proc/consoles.c +@@ -92,6 +92,9 @@ static const struct seq_operations conso + + static int __init proc_consoles_init(void) + { ++ if (IS_ENABLED(CONFIG_PROC_STRIPPED)) ++ return 0; ++ + proc_create_seq("consoles", 0, NULL, &consoles_op); + return 0; + } +--- a/fs/proc/proc_tty.c ++++ b/fs/proc/proc_tty.c +@@ -133,7 +133,10 @@ static const struct seq_operations tty_d + void proc_tty_register_driver(struct tty_driver *driver) + { + struct proc_dir_entry *ent; +- ++ ++ if (IS_ENABLED(CONFIG_PROC_STRIPPED)) ++ return; ++ + if (!driver->driver_name || driver->proc_entry || + !driver->ops->proc_show) + return; +@@ -150,6 +153,9 @@ void proc_tty_unregister_driver(struct t + { + struct proc_dir_entry *ent; + ++ if (IS_ENABLED(CONFIG_PROC_STRIPPED)) ++ return; ++ + ent = driver->proc_entry; + if (!ent) + return; +@@ -164,6 +170,9 @@ void proc_tty_unregister_driver(struct t + */ + void __init proc_tty_init(void) + { ++ if (IS_ENABLED(CONFIG_PROC_STRIPPED)) ++ return; ++ + if (!proc_mkdir("tty", NULL)) + return; + proc_mkdir("tty/ldisc", NULL); /* Preserved: it's userspace visible */ +--- a/include/net/snmp.h ++++ b/include/net/snmp.h +@@ -124,6 +124,21 @@ struct linux_tls_mib { + #define DECLARE_SNMP_STAT(type, name) \ + extern __typeof__(type) __percpu *name + ++#ifdef CONFIG_PROC_STRIPPED ++#define __SNMP_STATS_DUMMY(mib) \ ++ do { (void) mib->mibs[0]; } while(0) ++ ++#define __SNMP_INC_STATS(mib, field) __SNMP_STATS_DUMMY(mib) ++#define SNMP_INC_STATS_ATOMIC_LONG(mib, field) __SNMP_STATS_DUMMY(mib) ++#define SNMP_INC_STATS(mib, field) __SNMP_STATS_DUMMY(mib) ++#define SNMP_DEC_STATS(mib, field) __SNMP_STATS_DUMMY(mib) ++#define __SNMP_ADD_STATS(mib, field, addend) __SNMP_STATS_DUMMY(mib) ++#define SNMP_ADD_STATS(mib, field, addend) __SNMP_STATS_DUMMY(mib) ++#define SNMP_UPD_PO_STATS(mib, basefield, addend) __SNMP_STATS_DUMMY(mib) ++#define __SNMP_UPD_PO_STATS(mib, basefield, addend) __SNMP_STATS_DUMMY(mib) ++ ++#else ++ + #define __SNMP_INC_STATS(mib, field) \ + __this_cpu_inc(mib->mibs[field]) + +@@ -154,8 +169,9 @@ struct linux_tls_mib { + __this_cpu_add(ptr[basefield##OCTETS], addend); \ + } while (0) + ++#endif + +-#if BITS_PER_LONG==32 ++#if (BITS_PER_LONG==32) && !defined(CONFIG_PROC_STRIPPED) + + #define __SNMP_ADD_STATS64(mib, field, addend) \ + do { \ +--- a/ipc/msg.c ++++ b/ipc/msg.c +@@ -1350,6 +1350,9 @@ void __init msg_init(void) + { + msg_init_ns(&init_ipc_ns); + ++ if (IS_ENABLED(CONFIG_PROC_STRIPPED)) ++ return; ++ + ipc_init_proc_interface("sysvipc/msg", + " key msqid perms cbytes qnum lspid lrpid uid gid cuid cgid stime rtime ctime\n", + IPC_MSG_IDS, sysvipc_msg_proc_show); +--- a/ipc/sem.c ++++ b/ipc/sem.c +@@ -268,6 +268,8 @@ void sem_exit_ns(struct ipc_namespace *n + void __init sem_init(void) + { + sem_init_ns(&init_ipc_ns); ++ if (IS_ENABLED(CONFIG_PROC_STRIPPED)) ++ return; + ipc_init_proc_interface("sysvipc/sem", + " key semid perms nsems uid gid cuid cgid otime ctime\n", + IPC_SEM_IDS, sysvipc_sem_proc_show); +--- a/ipc/shm.c ++++ b/ipc/shm.c +@@ -154,6 +154,8 @@ pure_initcall(ipc_ns_init); + + void __init shm_init(void) + { ++ if (IS_ENABLED(CONFIG_PROC_STRIPPED)) ++ return; + ipc_init_proc_interface("sysvipc/shm", + #if BITS_PER_LONG <= 32 + " key shmid perms size cpid lpid nattch uid gid cuid cgid atime dtime ctime rss swap\n", +--- a/ipc/util.c ++++ b/ipc/util.c +@@ -141,6 +141,9 @@ void __init ipc_init_proc_interface(cons + struct proc_dir_entry *pde; + struct ipc_proc_iface *iface; + ++ if (IS_ENABLED(CONFIG_PROC_STRIPPED)) ++ return; ++ + iface = kmalloc(sizeof(*iface), GFP_KERNEL); + if (!iface) + return; +--- a/kernel/exec_domain.c ++++ b/kernel/exec_domain.c +@@ -29,6 +29,8 @@ static int execdomains_proc_show(struct + + static int __init proc_execdomains_init(void) + { ++ if (IS_ENABLED(CONFIG_PROC_STRIPPED)) ++ return 0; + proc_create_single("execdomains", 0, NULL, execdomains_proc_show); + return 0; + } +--- a/kernel/irq/proc.c ++++ b/kernel/irq/proc.c +@@ -341,6 +341,9 @@ void register_irq_proc(unsigned int irq, + void __maybe_unused *irqp = (void *)(unsigned long) irq; + char name [MAX_NAMELEN]; + ++ if (IS_ENABLED(CONFIG_PROC_STRIPPED) && !IS_ENABLED(CONFIG_SMP)) ++ return; ++ + if (!root_irq_dir || (desc->irq_data.chip == &no_irq_chip)) + return; + +@@ -394,6 +397,9 @@ void unregister_irq_proc(unsigned int ir + { + char name [MAX_NAMELEN]; + ++ if (IS_ENABLED(CONFIG_PROC_STRIPPED) && !IS_ENABLED(CONFIG_SMP)) ++ return; ++ + if (!root_irq_dir || !desc->dir) + return; + #ifdef CONFIG_SMP +@@ -432,6 +438,9 @@ void init_irq_proc(void) + unsigned int irq; + struct irq_desc *desc; + ++ if (IS_ENABLED(CONFIG_PROC_STRIPPED) && !IS_ENABLED(CONFIG_SMP)) ++ return; ++ + /* create /proc/irq */ + root_irq_dir = proc_mkdir("irq", NULL); + if (!root_irq_dir) +--- a/kernel/time/timer_list.c ++++ b/kernel/time/timer_list.c +@@ -350,6 +350,8 @@ static int __init init_timer_list_procfs + { + struct proc_dir_entry *pe; + ++ if (IS_ENABLED(CONFIG_PROC_STRIPPED)) ++ return 0; + pe = proc_create_seq_private("timer_list", 0400, NULL, &timer_list_sops, + sizeof(struct timer_list_iter), NULL); + if (!pe) +--- a/mm/vmalloc.c ++++ b/mm/vmalloc.c +@@ -3962,6 +3962,8 @@ static const struct seq_operations vmall + + static int __init proc_vmalloc_init(void) + { ++ if (IS_ENABLED(CONFIG_PROC_STRIPPED)) ++ return 0; + if (IS_ENABLED(CONFIG_NUMA)) + proc_create_seq_private("vmallocinfo", 0400, NULL, + &vmalloc_op, +--- a/mm/vmstat.c ++++ b/mm/vmstat.c +@@ -2083,10 +2083,12 @@ void __init init_mm_internals(void) + start_shepherd_timer(); + #endif + #ifdef CONFIG_PROC_FS +- proc_create_seq("buddyinfo", 0444, NULL, &fragmentation_op); +- proc_create_seq("pagetypeinfo", 0400, NULL, &pagetypeinfo_op); ++ if (!IS_ENABLED(CONFIG_PROC_STRIPPED)) { ++ proc_create_seq("buddyinfo", 0444, NULL, &fragmentation_op); ++ proc_create_seq("pagetypeinfo", 0400, NULL, &pagetypeinfo_op); ++ proc_create_seq("zoneinfo", 0444, NULL, &zoneinfo_op); ++ } + proc_create_seq("vmstat", 0444, NULL, &vmstat_op); +- proc_create_seq("zoneinfo", 0444, NULL, &zoneinfo_op); + #endif + } + +--- a/net/8021q/vlanproc.c ++++ b/net/8021q/vlanproc.c +@@ -93,6 +93,9 @@ void vlan_proc_cleanup(struct net *net) + { + struct vlan_net *vn = net_generic(net, vlan_net_id); + ++ if (IS_ENABLED(CONFIG_PROC_STRIPPED)) ++ return; ++ + if (vn->proc_vlan_conf) + remove_proc_entry(name_conf, vn->proc_vlan_dir); + +@@ -112,6 +115,9 @@ int __net_init vlan_proc_init(struct net + { + struct vlan_net *vn = net_generic(net, vlan_net_id); + ++ if (IS_ENABLED(CONFIG_PROC_STRIPPED)) ++ return 0; ++ + vn->proc_vlan_dir = proc_net_mkdir(net, name_root, net->proc_net); + if (!vn->proc_vlan_dir) + goto err; +--- a/net/core/net-procfs.c ++++ b/net/core/net-procfs.c +@@ -317,10 +317,12 @@ static int __net_init dev_proc_net_init( + if (!proc_create_net("dev", 0444, net->proc_net, &dev_seq_ops, + sizeof(struct seq_net_private))) + goto out; +- if (!proc_create_seq("softnet_stat", 0444, net->proc_net, ++ if (!IS_ENABLED(CONFIG_PROC_STRIPPED) && ++ !proc_create_seq("softnet_stat", 0444, net->proc_net, + &softnet_seq_ops)) + goto out_dev; +- if (!proc_create_net("ptype", 0444, net->proc_net, &ptype_seq_ops, ++ if (!IS_ENABLED(CONFIG_PROC_STRIPPED) && ++ !proc_create_net("ptype", 0444, net->proc_net, &ptype_seq_ops, + sizeof(struct seq_net_private))) + goto out_softnet; + +@@ -330,9 +332,11 @@ static int __net_init dev_proc_net_init( + out: + return rc; + out_ptype: +- remove_proc_entry("ptype", net->proc_net); ++ if (!IS_ENABLED(CONFIG_PROC_STRIPPED)) ++ remove_proc_entry("ptype", net->proc_net); + out_softnet: +- remove_proc_entry("softnet_stat", net->proc_net); ++ if (!IS_ENABLED(CONFIG_PROC_STRIPPED)) ++ remove_proc_entry("softnet_stat", net->proc_net); + out_dev: + remove_proc_entry("dev", net->proc_net); + goto out; +@@ -342,8 +346,10 @@ static void __net_exit dev_proc_net_exit + { + wext_proc_exit(net); + +- remove_proc_entry("ptype", net->proc_net); +- remove_proc_entry("softnet_stat", net->proc_net); ++ if (!IS_ENABLED(CONFIG_PROC_STRIPPED)) { ++ remove_proc_entry("ptype", net->proc_net); ++ remove_proc_entry("softnet_stat", net->proc_net); ++ } + remove_proc_entry("dev", net->proc_net); + } + +--- a/net/core/sock.c ++++ b/net/core/sock.c +@@ -3857,6 +3857,8 @@ static __net_initdata struct pernet_oper + + static int __init proto_init(void) + { ++ if (IS_ENABLED(CONFIG_PROC_STRIPPED)) ++ return 0; + return register_pernet_subsys(&proto_net_ops); + } + +--- a/net/ipv4/fib_trie.c ++++ b/net/ipv4/fib_trie.c +@@ -3022,11 +3022,13 @@ static const struct seq_operations fib_r + + int __net_init fib_proc_init(struct net *net) + { +- if (!proc_create_net("fib_trie", 0444, net->proc_net, &fib_trie_seq_ops, ++ if (!IS_ENABLED(CONFIG_PROC_STRIPPED) && ++ !proc_create_net("fib_trie", 0444, net->proc_net, &fib_trie_seq_ops, + sizeof(struct fib_trie_iter))) + goto out1; + +- if (!proc_create_net_single("fib_triestat", 0444, net->proc_net, ++ if (!IS_ENABLED(CONFIG_PROC_STRIPPED) && ++ !proc_create_net_single("fib_triestat", 0444, net->proc_net, + fib_triestat_seq_show, NULL)) + goto out2; + +@@ -3037,17 +3039,21 @@ int __net_init fib_proc_init(struct net + return 0; + + out3: +- remove_proc_entry("fib_triestat", net->proc_net); ++ if (!IS_ENABLED(CONFIG_PROC_STRIPPED)) ++ remove_proc_entry("fib_triestat", net->proc_net); + out2: +- remove_proc_entry("fib_trie", net->proc_net); ++ if (!IS_ENABLED(CONFIG_PROC_STRIPPED)) ++ remove_proc_entry("fib_trie", net->proc_net); + out1: + return -ENOMEM; + } + + void __net_exit fib_proc_exit(struct net *net) + { +- remove_proc_entry("fib_trie", net->proc_net); +- remove_proc_entry("fib_triestat", net->proc_net); ++ if (!IS_ENABLED(CONFIG_PROC_STRIPPED)) { ++ remove_proc_entry("fib_trie", net->proc_net); ++ remove_proc_entry("fib_triestat", net->proc_net); ++ } + remove_proc_entry("route", net->proc_net); + } + +--- a/net/ipv4/proc.c ++++ b/net/ipv4/proc.c +@@ -553,5 +553,8 @@ static __net_initdata struct pernet_oper + + int __init ip_misc_proc_init(void) + { ++ if (IS_ENABLED(CONFIG_PROC_STRIPPED)) ++ return 0; ++ + return register_pernet_subsys(&ip_proc_ops); + } +--- a/net/ipv4/route.c ++++ b/net/ipv4/route.c +@@ -387,6 +387,9 @@ static struct pernet_operations ip_rt_pr + + static int __init ip_rt_proc_init(void) + { ++ if (IS_ENABLED(CONFIG_PROC_STRIPPED)) ++ return 0; ++ + return register_pernet_subsys(&ip_rt_proc_ops); + } + diff --git a/pkgs/patches-linux-5.15/902-drivers-mfd-Add-a-driver-for-IEI-WT61P803-PUZZLE-MCU.patch b/pkgs/patches-linux-5.15/902-drivers-mfd-Add-a-driver-for-IEI-WT61P803-PUZZLE-MCU.patch new file mode 100644 index 0000000..84d995b --- /dev/null +++ b/pkgs/patches-linux-5.15/902-drivers-mfd-Add-a-driver-for-IEI-WT61P803-PUZZLE-MCU.patch @@ -0,0 +1,1034 @@ +From 692cfa85272dd12995b427c0a7a585ced5d54f32 Mon Sep 17 00:00:00 2001 +From: Luka Kovacic <luka.kovacic () sartura ! hr> +Date: Tue, 24 Aug 2021 12:44:33 +0000 +Subject: [PATCH 2/7] drivers: mfd: Add a driver for IEI WT61P803 PUZZLE MCU + +Add a driver for the IEI WT61P803 PUZZLE microcontroller, used in some +IEI Puzzle series devices. The microcontroller controls system power, +temperature sensors, fans and LEDs. + +This driver implements the core functionality for device communication +over the system serial (serdev bus). It handles MCU messages and the +internal MCU properties. Some properties can be managed over sysfs. + +Signed-off-by: Luka Kovacic <luka.kovacic@sartura.hr> +Signed-off-by: Pavo Banicevic <pavo.banicevic@sartura.hr> +Cc: Luka Perkov <luka.perkov@sartura.hr> +Cc: Robert Marko <robert.marko@sartura.hr> +--- + drivers/mfd/Kconfig | 8 + + drivers/mfd/Makefile | 1 + + drivers/mfd/iei-wt61p803-puzzle.c | 908 ++++++++++++++++++++++++ + include/linux/mfd/iei-wt61p803-puzzle.h | 66 ++ + 4 files changed, 983 insertions(+) + create mode 100644 drivers/mfd/iei-wt61p803-puzzle.c + create mode 100644 include/linux/mfd/iei-wt61p803-puzzle.h + +--- a/drivers/mfd/Kconfig ++++ b/drivers/mfd/Kconfig +@@ -2187,6 +2187,15 @@ config SGI_MFD_IOC3 + If you have an SGI Origin, Octane, or a PCI IOC3 card, + then say Y. Otherwise say N. + ++config MFD_IEI_WT61P803_PUZZLE ++ tristate "IEI WT61P803 PUZZLE MCU driver" ++ depends on SERIAL_DEV_BUS ++ select MFD_CORE ++ help ++ IEI WT61P803 PUZZLE is a system power management microcontroller ++ used for fan control, temperature sensor reading, LED control ++ and system identification. ++ + config MFD_INTEL_M10_BMC + tristate "Intel MAX 10 Board Management Controller" + depends on SPI_MASTER +--- a/drivers/mfd/Makefile ++++ b/drivers/mfd/Makefile +@@ -237,6 +237,7 @@ obj-$(CONFIG_MFD_DLN2) += dln2.o + obj-$(CONFIG_MFD_RT4831) += rt4831.o + obj-$(CONFIG_MFD_RT5033) += rt5033.o + obj-$(CONFIG_MFD_SKY81452) += sky81452.o ++obj-$(CONFIG_MFD_IEI_WT61P803_PUZZLE) += iei-wt61p803-puzzle.o + + intel-soc-pmic-objs := intel_soc_pmic_core.o intel_soc_pmic_crc.o + obj-$(CONFIG_INTEL_SOC_PMIC) += intel-soc-pmic.o +--- /dev/null ++++ b/drivers/mfd/iei-wt61p803-puzzle.c +@@ -0,0 +1,908 @@ ++// SPDX-License-Identifier: GPL-2.0-only ++/* IEI WT61P803 PUZZLE MCU Driver ++ * System management microcontroller for fan control, temperature sensor reading, ++ * LED control and system identification on IEI Puzzle series ARM-based appliances. ++ * ++ * Copyright (C) 2020 Sartura Ltd. ++ * Author: Luka Kovacic <luka.kovacic@sartura.hr> ++ */ ++ ++#include <linux/atomic.h> ++#include <linux/delay.h> ++#include <linux/export.h> ++#include <linux/init.h> ++#include <linux/kernel.h> ++#include <linux/mfd/core.h> ++#include <linux/mfd/iei-wt61p803-puzzle.h> ++#include <linux/mod_devicetable.h> ++#include <linux/module.h> ++#include <linux/of_platform.h> ++#include <linux/property.h> ++#include <linux/sched.h> ++#include <linux/serdev.h> ++#include <linux/slab.h> ++#include <linux/sysfs.h> ++#include <asm/unaligned.h> ++ ++/* start, payload and XOR checksum at end */ ++#define IEI_WT61P803_PUZZLE_MAX_COMMAND_LENGTH (1 + 20 + 1) ++#define IEI_WT61P803_PUZZLE_RESP_BUF_SIZE 512 ++ ++#define IEI_WT61P803_PUZZLE_MAC_LENGTH 17 ++#define IEI_WT61P803_PUZZLE_SN_LENGTH 36 ++#define IEI_WT61P803_PUZZLE_VERSION_LENGTH 6 ++#define IEI_WT61P803_PUZZLE_BUILD_INFO_LENGTH 16 ++#define IEI_WT61P803_PUZZLE_PROTOCOL_VERSION_LENGTH 8 ++#define IEI_WT61P803_PUZZLE_NB_MAC 8 ++ ++/* Use HZ as a timeout value throughout the driver */ ++#define IEI_WT61P803_PUZZLE_GENERAL_TIMEOUT HZ ++ ++enum iei_wt61p803_puzzle_attribute_type { ++ IEI_WT61P803_PUZZLE_VERSION, ++ IEI_WT61P803_PUZZLE_BUILD_INFO, ++ IEI_WT61P803_PUZZLE_BOOTLOADER_MODE, ++ IEI_WT61P803_PUZZLE_PROTOCOL_VERSION, ++ IEI_WT61P803_PUZZLE_SERIAL_NUMBER, ++ IEI_WT61P803_PUZZLE_MAC_ADDRESS, ++ IEI_WT61P803_PUZZLE_AC_RECOVERY_STATUS, ++ IEI_WT61P803_PUZZLE_POWER_LOSS_RECOVERY, ++ IEI_WT61P803_PUZZLE_POWER_STATUS, ++}; ++ ++struct iei_wt61p803_puzzle_device_attribute { ++ struct device_attribute dev_attr; ++ enum iei_wt61p803_puzzle_attribute_type type; ++ u8 index; ++}; ++ ++/** ++ * struct iei_wt61p803_puzzle_mcu_status - MCU flags state ++ * @ac_recovery_status_flag: AC Recovery Status Flag ++ * @power_loss_recovery: System recovery after power loss ++ * @power_status: System Power-on Method ++ */ ++struct iei_wt61p803_puzzle_mcu_status { ++ u8 ac_recovery_status_flag; ++ u8 power_loss_recovery; ++ u8 power_status; ++}; ++ ++/** ++ * struct iei_wt61p803_puzzle_reply - MCU reply ++ * @size: Size of the MCU reply ++ * @data: Full MCU reply buffer ++ * @state: Current state of the packet ++ * @received: Was the response fullfilled ++ */ ++struct iei_wt61p803_puzzle_reply { ++ size_t size; ++ unsigned char data[IEI_WT61P803_PUZZLE_RESP_BUF_SIZE]; ++ struct completion received; ++}; ++ ++/** ++ * struct iei_wt61p803_puzzle_mcu_version - MCU version status ++ * @version: Primary firmware version ++ * @build_info: Build date and time ++ * @bootloader_mode: Status of the MCU operation ++ * @protocol_version: MCU communication protocol version ++ * @serial_number: Device factory serial number ++ * @mac_address: Device factory MAC addresses ++ * ++ * Last element of arrays is reserved for '\0'. ++ */ ++struct iei_wt61p803_puzzle_mcu_version { ++ char version[IEI_WT61P803_PUZZLE_VERSION_LENGTH + 1]; ++ char build_info[IEI_WT61P803_PUZZLE_BUILD_INFO_LENGTH + 1]; ++ bool bootloader_mode; ++ char protocol_version[IEI_WT61P803_PUZZLE_PROTOCOL_VERSION_LENGTH + 1]; ++ char serial_number[IEI_WT61P803_PUZZLE_SN_LENGTH + 1]; ++ char mac_address[IEI_WT61P803_PUZZLE_NB_MAC][IEI_WT61P803_PUZZLE_MAC_LENGTH + 1]; ++}; ++ ++/** ++ * struct iei_wt61p803_puzzle - IEI WT61P803 PUZZLE MCU Driver ++ * @serdev: Pointer to underlying serdev device ++ * @dev: Pointer to underlying dev device ++ * @reply_lock: Reply mutex lock ++ * @reply: Pointer to the iei_wt61p803_puzzle_reply struct ++ * @version: MCU version related data ++ * @status: MCU status related data ++ * @response_buffer Command response buffer allocation ++ * @lock General member mutex lock ++ */ ++struct iei_wt61p803_puzzle { ++ struct serdev_device *serdev; ++ struct device *dev; ++ struct mutex reply_lock; /* lock to prevent multiple firmware calls */ ++ struct iei_wt61p803_puzzle_reply *reply; ++ struct iei_wt61p803_puzzle_mcu_version version; ++ struct iei_wt61p803_puzzle_mcu_status status; ++ unsigned char response_buffer[IEI_WT61P803_PUZZLE_BUF_SIZE]; ++ struct mutex lock; /* lock to protect response buffer */ ++}; ++ ++static unsigned char iei_wt61p803_puzzle_checksum(unsigned char *buf, size_t len) ++{ ++ unsigned char checksum = 0; ++ size_t i; ++ ++ for (i = 0; i < len; i++) ++ checksum ^= buf[i]; ++ return checksum; ++} ++ ++static int iei_wt61p803_puzzle_process_resp(struct iei_wt61p803_puzzle *mcu, ++ const unsigned char *raw_resp_data, size_t size) ++{ ++ unsigned char checksum; ++ ++ /* Check the incoming frame header */ ++ if (!(raw_resp_data[0] == IEI_WT61P803_PUZZLE_CMD_HEADER_START || ++ raw_resp_data[0] == IEI_WT61P803_PUZZLE_CMD_HEADER_START_OTHER || ++ (raw_resp_data[0] == IEI_WT61P803_PUZZLE_CMD_HEADER_EEPROM && ++ raw_resp_data[1] == IEI_WT61P803_PUZZLE_CMD_EEPROM_READ))) { ++ if (mcu->reply->size + size >= sizeof(mcu->reply->data)) ++ return -EIO; ++ ++ /* Append the frame to existing data */ ++ memcpy(mcu->reply->data + mcu->reply->size, raw_resp_data, size); ++ mcu->reply->size += size; ++ } else { ++ if (size >= sizeof(mcu->reply->data)) ++ return -EIO; ++ ++ /* Start processing a new frame */ ++ memcpy(mcu->reply->data, raw_resp_data, size); ++ mcu->reply->size = size; ++ } ++ ++ checksum = iei_wt61p803_puzzle_checksum(mcu->reply->data, mcu->reply->size - 1); ++ if (checksum != mcu->reply->data[mcu->reply->size - 1]) { ++ /* The checksum isn't matched yet, wait for new frames */ ++ return size; ++ } ++ ++ /* Received all the data */ ++ complete(&mcu->reply->received); ++ ++ return size; ++} ++ ++static int iei_wt61p803_puzzle_recv_buf(struct serdev_device *serdev, ++ const unsigned char *data, size_t size) ++{ ++ struct iei_wt61p803_puzzle *mcu = serdev_device_get_drvdata(serdev); ++ int ret; ++ ++ ret = iei_wt61p803_puzzle_process_resp(mcu, data, size); ++ /* Return the number of processed bytes if function returns error, ++ * discard the remaining incoming data, since the frame this data ++ * belongs to is broken anyway ++ */ ++ if (ret < 0) ++ return size; ++ ++ return ret; ++} ++ ++static const struct serdev_device_ops iei_wt61p803_puzzle_serdev_device_ops = { ++ .receive_buf = iei_wt61p803_puzzle_recv_buf, ++ .write_wakeup = serdev_device_write_wakeup, ++}; ++ ++/** ++ * iei_wt61p803_puzzle_write_command_watchdog() - Watchdog of the normal cmd ++ * @mcu: Pointer to the iei_wt61p803_puzzle core MFD struct ++ * @cmd: Pointer to the char array to send (size should be content + 1 (xor)) ++ * @size: Size of the cmd char array ++ * @reply_data: Pointer to the reply/response data array (should be allocated) ++ * @reply_size: Pointer to size_t (size of reply_data) ++ * @retry_count: Number of times to retry sending the command to the MCU ++ */ ++int iei_wt61p803_puzzle_write_command_watchdog(struct iei_wt61p803_puzzle *mcu, ++ unsigned char *cmd, size_t size, ++ unsigned char *reply_data, ++ size_t *reply_size, int retry_count) ++{ ++ struct device *dev = &mcu->serdev->dev; ++ int ret, i; ++ ++ for (i = 0; i < retry_count; i++) { ++ ret = iei_wt61p803_puzzle_write_command(mcu, cmd, size, ++ reply_data, reply_size); ++ if (ret != -ETIMEDOUT) ++ return ret; ++ } ++ ++ dev_err(dev, "Command response timed out. Retries: %d\n", retry_count); ++ ++ return -ETIMEDOUT; ++} ++EXPORT_SYMBOL_GPL(iei_wt61p803_puzzle_write_command_watchdog); ++ ++/** ++ * iei_wt61p803_puzzle_write_command() - Send a structured command to the MCU ++ * @mcu: Pointer to the iei_wt61p803_puzzle core MFD struct ++ * @cmd: Pointer to the char array to send (size should be content + 1 (xor)) ++ * @size: Size of the cmd char array ++ * @reply_data: Pointer to the reply/response data array (should be allocated) ++ * ++ * Sends a structured command to the MCU. ++ */ ++int iei_wt61p803_puzzle_write_command(struct iei_wt61p803_puzzle *mcu, ++ unsigned char *cmd, size_t size, ++ unsigned char *reply_data, ++ size_t *reply_size) ++{ ++ struct device *dev = &mcu->serdev->dev; ++ int ret; ++ ++ if (size <= 1 || size > IEI_WT61P803_PUZZLE_MAX_COMMAND_LENGTH) ++ return -EINVAL; ++ ++ mutex_lock(&mcu->reply_lock); ++ ++ cmd[size - 1] = iei_wt61p803_puzzle_checksum(cmd, size - 1); ++ ++ /* Initialize reply struct */ ++ reinit_completion(&mcu->reply->received); ++ mcu->reply->size = 0; ++ usleep_range(2000, 10000); ++ serdev_device_write_flush(mcu->serdev); ++ ret = serdev_device_write_buf(mcu->serdev, cmd, size); ++ if (ret < 0) ++ goto exit; ++ ++ serdev_device_wait_until_sent(mcu->serdev, IEI_WT61P803_PUZZLE_GENERAL_TIMEOUT); ++ ret = wait_for_completion_timeout(&mcu->reply->received, ++ IEI_WT61P803_PUZZLE_GENERAL_TIMEOUT); ++ if (ret == 0) { ++ dev_err(dev, "Command reply receive timeout\n"); ++ ret = -ETIMEDOUT; ++ goto exit; ++ } ++ ++ *reply_size = mcu->reply->size; ++ /* Copy the received data, as it will not be available after a new frame is received */ ++ memcpy(reply_data, mcu->reply->data, mcu->reply->size); ++ ret = 0; ++exit: ++ mutex_unlock(&mcu->reply_lock); ++ return ret; ++} ++EXPORT_SYMBOL_GPL(iei_wt61p803_puzzle_write_command); ++ ++static int iei_wt61p803_puzzle_buzzer(struct iei_wt61p803_puzzle *mcu, bool long_beep) ++{ ++ unsigned char *resp_buf = mcu->response_buffer; ++ unsigned char buzzer_cmd[4] = {}; ++ size_t reply_size; ++ int ret; ++ ++ buzzer_cmd[0] = IEI_WT61P803_PUZZLE_CMD_HEADER_START; ++ buzzer_cmd[1] = IEI_WT61P803_PUZZLE_CMD_FUNCTION_SINGLE; ++ buzzer_cmd[2] = long_beep ? '3' : '2'; /* Buzzer 1.5 / 0.5 second beep */ ++ ++ mutex_lock(&mcu->lock); ++ ret = iei_wt61p803_puzzle_write_command(mcu, buzzer_cmd, sizeof(buzzer_cmd), ++ resp_buf, &reply_size); ++ if (ret) ++ goto exit; ++ ++ if (reply_size != 3) { ++ ret = -EIO; ++ goto exit; ++ } ++ ++ if (!(resp_buf[0] == IEI_WT61P803_PUZZLE_CMD_HEADER_START && ++ resp_buf[1] == IEI_WT61P803_PUZZLE_CMD_RESPONSE_OK && ++ resp_buf[2] == IEI_WT61P803_PUZZLE_CHECKSUM_RESPONSE_OK)) { ++ ret = -EPROTO; ++ goto exit; ++ } ++exit: ++ mutex_unlock(&mcu->lock); ++ return ret; ++} ++ ++static int iei_wt61p803_puzzle_get_version(struct iei_wt61p803_puzzle *mcu) ++{ ++ unsigned char version_cmd[3] = { ++ IEI_WT61P803_PUZZLE_CMD_HEADER_START_OTHER, ++ IEI_WT61P803_PUZZLE_CMD_OTHER_VERSION, ++ }; ++ unsigned char build_info_cmd[3] = { ++ IEI_WT61P803_PUZZLE_CMD_HEADER_START_OTHER, ++ IEI_WT61P803_PUZZLE_CMD_OTHER_BUILD, ++ }; ++ unsigned char bootloader_mode_cmd[3] = { ++ IEI_WT61P803_PUZZLE_CMD_HEADER_START_OTHER, ++ IEI_WT61P803_PUZZLE_CMD_OTHER_BOOTLOADER_MODE, ++ }; ++ unsigned char protocol_version_cmd[3] = { ++ IEI_WT61P803_PUZZLE_CMD_HEADER_START_OTHER, ++ IEI_WT61P803_PUZZLE_CMD_OTHER_PROTOCOL_VERSION, ++ }; ++ unsigned char *rb = mcu->response_buffer; ++ size_t reply_size; ++ int ret; ++ ++ mutex_lock(&mcu->lock); ++ ++ ret = iei_wt61p803_puzzle_write_command(mcu, version_cmd, sizeof(version_cmd), ++ rb, &reply_size); ++ if (ret) ++ goto err; ++ if (reply_size < 7) { ++ ret = -EIO; ++ goto err; ++ } ++ sprintf(mcu->version.version, "v%c.%.3s", rb[2], &rb[3]); ++ ++ ret = iei_wt61p803_puzzle_write_command(mcu, build_info_cmd, ++ sizeof(build_info_cmd), rb, ++ &reply_size); ++ if (ret) ++ goto err; ++ if (reply_size < 15) { ++ ret = -EIO; ++ goto err; ++ } ++ sprintf(mcu->version.build_info, "%c%c/%c%c/%.4s %c%c:%c%c", ++ rb[8], rb[9], rb[6], rb[7], &rb[2], rb[10], rb[11], ++ rb[12], rb[13]); ++ ++ ret = iei_wt61p803_puzzle_write_command(mcu, bootloader_mode_cmd, ++ sizeof(bootloader_mode_cmd), rb, ++ &reply_size); ++ if (ret) ++ goto err; ++ if (reply_size < 4) { ++ ret = -EIO; ++ goto err; ++ } ++ if (rb[2] == IEI_WT61P803_PUZZLE_CMD_OTHER_MODE_APPS) ++ mcu->version.bootloader_mode = false; ++ else if (rb[2] == IEI_WT61P803_PUZZLE_CMD_OTHER_MODE_BOOTLOADER) ++ mcu->version.bootloader_mode = true; ++ ++ ret = iei_wt61p803_puzzle_write_command(mcu, protocol_version_cmd, ++ sizeof(protocol_version_cmd), rb, ++ &reply_size); ++ if (ret) ++ goto err; ++ if (reply_size < 9) { ++ ret = -EIO; ++ goto err; ++ } ++ sprintf(mcu->version.protocol_version, "v%c.%c%c%c%c%c", ++ rb[7], rb[6], rb[5], rb[4], rb[3], rb[2]); ++err: ++ mutex_unlock(&mcu->lock); ++ return ret; ++} ++ ++static int iei_wt61p803_puzzle_get_mcu_status(struct iei_wt61p803_puzzle *mcu) ++{ ++ unsigned char mcu_status_cmd[5] = { ++ IEI_WT61P803_PUZZLE_CMD_HEADER_START, ++ IEI_WT61P803_PUZZLE_CMD_FUNCTION_OTHER, ++ IEI_WT61P803_PUZZLE_CMD_FUNCTION_OTHER_STATUS, ++ IEI_WT61P803_PUZZLE_CMD_FUNCTION_OTHER_STATUS, ++ }; ++ unsigned char *resp_buf = mcu->response_buffer; ++ size_t reply_size; ++ int ret; ++ ++ mutex_lock(&mcu->lock); ++ ret = iei_wt61p803_puzzle_write_command(mcu, mcu_status_cmd, sizeof(mcu_status_cmd), ++ resp_buf, &reply_size); ++ if (ret) ++ goto exit; ++ if (reply_size < 20) { ++ ret = -EIO; ++ goto exit; ++ } ++ ++ /* Response format: ++ * (IDX RESPONSE) ++ * 0 @ ++ * 1 O ++ * 2 S ++ * 3 S ++ * ... ++ * 5 AC Recovery Status Flag ++ * ... ++ * 10 Power Loss Recovery ++ * ... ++ * 19 Power Status (system power on method) ++ * 20 XOR checksum ++ */ ++ if (resp_buf[0] == IEI_WT61P803_PUZZLE_CMD_HEADER_START && ++ resp_buf[1] == IEI_WT61P803_PUZZLE_CMD_FUNCTION_OTHER && ++ resp_buf[2] == IEI_WT61P803_PUZZLE_CMD_FUNCTION_OTHER_STATUS && ++ resp_buf[3] == IEI_WT61P803_PUZZLE_CMD_FUNCTION_OTHER_STATUS) { ++ mcu->status.ac_recovery_status_flag = resp_buf[5]; ++ mcu->status.power_loss_recovery = resp_buf[10]; ++ mcu->status.power_status = resp_buf[19]; ++ } ++exit: ++ mutex_unlock(&mcu->lock); ++ return ret; ++} ++ ++static int iei_wt61p803_puzzle_get_serial_number(struct iei_wt61p803_puzzle *mcu) ++{ ++ unsigned char *resp_buf = mcu->response_buffer; ++ unsigned char serial_number_cmd[5] = { ++ IEI_WT61P803_PUZZLE_CMD_HEADER_EEPROM, ++ IEI_WT61P803_PUZZLE_CMD_EEPROM_READ, ++ 0x00, /* EEPROM read address */ ++ 0x24, /* Data length */ ++ }; ++ size_t reply_size; ++ int ret; ++ ++ mutex_lock(&mcu->lock); ++ ret = iei_wt61p803_puzzle_write_command(mcu, serial_number_cmd, ++ sizeof(serial_number_cmd), ++ resp_buf, &reply_size); ++ if (ret) ++ goto err; ++ ++ if (reply_size < IEI_WT61P803_PUZZLE_SN_LENGTH + 4) { ++ ret = -EIO; ++ goto err; ++ } ++ ++ sprintf(mcu->version.serial_number, "%.*s", ++ IEI_WT61P803_PUZZLE_SN_LENGTH, resp_buf + 4); ++err: ++ mutex_unlock(&mcu->lock); ++ return ret; ++} ++ ++static int iei_wt61p803_puzzle_write_serial_number(struct iei_wt61p803_puzzle *mcu, ++ unsigned char serial_number[36]) ++{ ++ unsigned char *resp_buf = mcu->response_buffer; ++ unsigned char serial_number_header[4] = { ++ IEI_WT61P803_PUZZLE_CMD_HEADER_EEPROM, ++ IEI_WT61P803_PUZZLE_CMD_EEPROM_WRITE, ++ 0x00, /* EEPROM write address */ ++ 0xC, /* Data length */ ++ }; ++ unsigned char serial_number_cmd[4 + 12 + 1]; /* header, serial number, XOR checksum */ ++ int ret, sn_counter; ++ size_t reply_size; ++ ++ /* The MCU can only handle 22 byte messages, send the S/N in 12 byte chunks */ ++ mutex_lock(&mcu->lock); ++ for (sn_counter = 0; sn_counter < 3; sn_counter++) { ++ serial_number_header[2] = 0x0 + 0xC * sn_counter; ++ ++ memcpy(serial_number_cmd, serial_number_header, sizeof(serial_number_header)); ++ memcpy(serial_number_cmd + sizeof(serial_number_header), ++ serial_number + 0xC * sn_counter, 0xC); ++ ++ ret = iei_wt61p803_puzzle_write_command(mcu, serial_number_cmd, ++ sizeof(serial_number_cmd), ++ resp_buf, &reply_size); ++ if (ret) ++ goto err; ++ if (reply_size != 3) { ++ ret = -EIO; ++ goto err; ++ } ++ if (!(resp_buf[0] == IEI_WT61P803_PUZZLE_CMD_HEADER_START && ++ resp_buf[1] == IEI_WT61P803_PUZZLE_CMD_RESPONSE_OK && ++ resp_buf[2] == IEI_WT61P803_PUZZLE_CHECKSUM_RESPONSE_OK)) { ++ ret = -EPROTO; ++ goto err; ++ } ++ } ++ ++ sprintf(mcu->version.serial_number, "%.*s", ++ IEI_WT61P803_PUZZLE_SN_LENGTH, serial_number); ++err: ++ mutex_unlock(&mcu->lock); ++ return ret; ++} ++ ++static int iei_wt61p803_puzzle_get_mac_address(struct iei_wt61p803_puzzle *mcu, int index) ++{ ++ unsigned char *resp_buf = mcu->response_buffer; ++ unsigned char mac_address_cmd[5] = { ++ IEI_WT61P803_PUZZLE_CMD_HEADER_EEPROM, ++ IEI_WT61P803_PUZZLE_CMD_EEPROM_READ, ++ 0x00, /* EEPROM read address */ ++ 0x11, /* Data length */ ++ }; ++ size_t reply_size; ++ int ret; ++ ++ mutex_lock(&mcu->lock); ++ mac_address_cmd[2] = 0x24 + 0x11 * index; ++ ++ ret = iei_wt61p803_puzzle_write_command(mcu, mac_address_cmd, ++ sizeof(mac_address_cmd), ++ resp_buf, &reply_size); ++ if (ret) ++ goto err; ++ ++ if (reply_size < 22) { ++ ret = -EIO; ++ goto err; ++ } ++ ++ sprintf(mcu->version.mac_address[index], "%.*s", ++ IEI_WT61P803_PUZZLE_MAC_LENGTH, resp_buf + 4); ++err: ++ mutex_unlock(&mcu->lock); ++ return ret; ++} ++ ++static int ++iei_wt61p803_puzzle_write_mac_address(struct iei_wt61p803_puzzle *mcu, ++ unsigned char mac_address[IEI_WT61P803_PUZZLE_MAC_LENGTH], ++ int mac_address_idx) ++{ ++ unsigned char mac_address_cmd[4 + IEI_WT61P803_PUZZLE_MAC_LENGTH + 1]; ++ unsigned char *resp_buf = mcu->response_buffer; ++ unsigned char mac_address_header[4] = { ++ IEI_WT61P803_PUZZLE_CMD_HEADER_EEPROM, ++ IEI_WT61P803_PUZZLE_CMD_EEPROM_WRITE, ++ 0x00, /* EEPROM write address */ ++ 0x11, /* Data length */ ++ }; ++ size_t reply_size; ++ int ret; ++ ++ if (mac_address_idx < 0 || mac_address_idx >= IEI_WT61P803_PUZZLE_NB_MAC) ++ return -EINVAL; ++ ++ mac_address_header[2] = 0x24 + 0x11 * mac_address_idx; ++ ++ /* Concat mac_address_header, mac_address to mac_address_cmd */ ++ memcpy(mac_address_cmd, mac_address_header, sizeof(mac_address_header)); ++ memcpy(mac_address_cmd + sizeof(mac_address_header), mac_address, ++ IEI_WT61P803_PUZZLE_MAC_LENGTH); ++ ++ mutex_lock(&mcu->lock); ++ ret = iei_wt61p803_puzzle_write_command(mcu, mac_address_cmd, ++ sizeof(mac_address_cmd), ++ resp_buf, &reply_size); ++ if (ret) ++ goto err; ++ if (reply_size != 3) { ++ ret = -EIO; ++ goto err; ++ } ++ if (!(resp_buf[0] == IEI_WT61P803_PUZZLE_CMD_HEADER_START && ++ resp_buf[1] == IEI_WT61P803_PUZZLE_CMD_RESPONSE_OK && ++ resp_buf[2] == IEI_WT61P803_PUZZLE_CHECKSUM_RESPONSE_OK)) { ++ ret = -EPROTO; ++ goto err; ++ } ++ ++ sprintf(mcu->version.mac_address[mac_address_idx], "%.*s", ++ IEI_WT61P803_PUZZLE_MAC_LENGTH, mac_address); ++err: ++ mutex_unlock(&mcu->lock); ++ return ret; ++} ++ ++static int iei_wt61p803_puzzle_write_power_loss_recovery(struct iei_wt61p803_puzzle *mcu, ++ int power_loss_recovery_action) ++{ ++ unsigned char *resp_buf = mcu->response_buffer; ++ unsigned char power_loss_recovery_cmd[5] = {}; ++ size_t reply_size; ++ int ret; ++ ++ if (power_loss_recovery_action < 0 || power_loss_recovery_action > 4) ++ return -EINVAL; ++ ++ power_loss_recovery_cmd[0] = IEI_WT61P803_PUZZLE_CMD_HEADER_START; ++ power_loss_recovery_cmd[1] = IEI_WT61P803_PUZZLE_CMD_FUNCTION_OTHER; ++ power_loss_recovery_cmd[2] = IEI_WT61P803_PUZZLE_CMD_FUNCTION_OTHER_POWER_LOSS; ++ power_loss_recovery_cmd[3] = hex_asc[power_loss_recovery_action]; ++ ++ mutex_lock(&mcu->lock); ++ ret = iei_wt61p803_puzzle_write_command(mcu, power_loss_recovery_cmd, ++ sizeof(power_loss_recovery_cmd), ++ resp_buf, &reply_size); ++ if (ret) ++ goto exit; ++ mcu->status.power_loss_recovery = power_loss_recovery_action; ++exit: ++ mutex_unlock(&mcu->lock); ++ return ret; ++} ++ ++#define to_puzzle_dev_attr(_attr) \ ++ container_of(_attr, struct iei_wt61p803_puzzle_device_attribute, dev_attr) ++ ++static ssize_t show_output(struct device *dev, ++ struct device_attribute *attr, char *buf) ++{ ++ struct iei_wt61p803_puzzle *mcu = dev_get_drvdata(dev); ++ struct iei_wt61p803_puzzle_device_attribute *pattr = to_puzzle_dev_attr(attr); ++ int ret; ++ ++ switch (pattr->type) { ++ case IEI_WT61P803_PUZZLE_VERSION: ++ return scnprintf(buf, PAGE_SIZE, "%s\n", mcu->version.version); ++ case IEI_WT61P803_PUZZLE_BUILD_INFO: ++ return scnprintf(buf, PAGE_SIZE, "%s\n", mcu->version.build_info); ++ case IEI_WT61P803_PUZZLE_BOOTLOADER_MODE: ++ return scnprintf(buf, PAGE_SIZE, "%d\n", mcu->version.bootloader_mode); ++ case IEI_WT61P803_PUZZLE_PROTOCOL_VERSION: ++ return scnprintf(buf, PAGE_SIZE, "%s\n", mcu->version.protocol_version); ++ case IEI_WT61P803_PUZZLE_SERIAL_NUMBER: ++ ret = iei_wt61p803_puzzle_get_serial_number(mcu); ++ if (!ret) ++ ret = scnprintf(buf, PAGE_SIZE, "%s\n", mcu->version.serial_number); ++ else ++ ret = 0; ++ return ret; ++ case IEI_WT61P803_PUZZLE_MAC_ADDRESS: ++ ret = iei_wt61p803_puzzle_get_mac_address(mcu, pattr->index); ++ if (!ret) ++ ret = scnprintf(buf, PAGE_SIZE, "%s\n", ++ mcu->version.mac_address[pattr->index]); ++ else ++ ret = 0; ++ return ret; ++ case IEI_WT61P803_PUZZLE_AC_RECOVERY_STATUS: ++ case IEI_WT61P803_PUZZLE_POWER_LOSS_RECOVERY: ++ case IEI_WT61P803_PUZZLE_POWER_STATUS: ++ ret = iei_wt61p803_puzzle_get_mcu_status(mcu); ++ if (ret) ++ return ret; ++ ++ mutex_lock(&mcu->lock); ++ switch (pattr->type) { ++ case IEI_WT61P803_PUZZLE_AC_RECOVERY_STATUS: ++ ret = scnprintf(buf, PAGE_SIZE, "%x\n", ++ mcu->status.ac_recovery_status_flag); ++ break; ++ case IEI_WT61P803_PUZZLE_POWER_LOSS_RECOVERY: ++ ret = scnprintf(buf, PAGE_SIZE, "%x\n", mcu->status.power_loss_recovery); ++ break; ++ case IEI_WT61P803_PUZZLE_POWER_STATUS: ++ ret = scnprintf(buf, PAGE_SIZE, "%x\n", mcu->status.power_status); ++ break; ++ default: ++ ret = 0; ++ break; ++ } ++ mutex_unlock(&mcu->lock); ++ return ret; ++ default: ++ return 0; ++ } ++ ++ return 0; ++} ++ ++static ssize_t store_output(struct device *dev, ++ struct device_attribute *attr, ++ const char *buf, size_t len) ++{ ++ unsigned char serial_number[IEI_WT61P803_PUZZLE_SN_LENGTH]; ++ unsigned char mac_address[IEI_WT61P803_PUZZLE_MAC_LENGTH]; ++ struct iei_wt61p803_puzzle *mcu = dev_get_drvdata(dev); ++ struct iei_wt61p803_puzzle_device_attribute *pattr = to_puzzle_dev_attr(attr); ++ int power_loss_recovery_action = 0; ++ int ret; ++ ++ switch (pattr->type) { ++ case IEI_WT61P803_PUZZLE_SERIAL_NUMBER: ++ if (len != (size_t)(IEI_WT61P803_PUZZLE_SN_LENGTH + 1)) ++ return -EINVAL; ++ memcpy(serial_number, buf, sizeof(serial_number)); ++ ret = iei_wt61p803_puzzle_write_serial_number(mcu, serial_number); ++ if (ret) ++ return ret; ++ return len; ++ case IEI_WT61P803_PUZZLE_MAC_ADDRESS: ++ if (len != (size_t)(IEI_WT61P803_PUZZLE_MAC_LENGTH + 1)) ++ return -EINVAL; ++ ++ memcpy(mac_address, buf, sizeof(mac_address)); ++ ++ if (strlen(attr->attr.name) != 13) ++ return -EIO; ++ ++ ret = iei_wt61p803_puzzle_write_mac_address(mcu, mac_address, pattr->index); ++ if (ret) ++ return ret; ++ return len; ++ case IEI_WT61P803_PUZZLE_POWER_LOSS_RECOVERY: ++ ret = kstrtoint(buf, 10, &power_loss_recovery_action); ++ if (ret) ++ return ret; ++ ret = iei_wt61p803_puzzle_write_power_loss_recovery(mcu, ++ power_loss_recovery_action); ++ if (ret) ++ return ret; ++ return len; ++ default: ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++ ++#define IEI_WT61P803_PUZZLE_ATTR(_name, _mode, _show, _store, _type, _index) \ ++ struct iei_wt61p803_puzzle_device_attribute dev_attr_##_name = \ ++ { .dev_attr = __ATTR(_name, _mode, _show, _store), \ ++ .type = _type, \ ++ .index = _index } ++ ++#define IEI_WT61P803_PUZZLE_ATTR_RO(_name, _type, _id) \ ++ IEI_WT61P803_PUZZLE_ATTR(_name, 0444, show_output, NULL, _type, _id) ++ ++#define IEI_WT61P803_PUZZLE_ATTR_RW(_name, _type, _id) \ ++ IEI_WT61P803_PUZZLE_ATTR(_name, 0644, show_output, store_output, _type, _id) ++ ++static IEI_WT61P803_PUZZLE_ATTR_RO(version, IEI_WT61P803_PUZZLE_VERSION, 0); ++static IEI_WT61P803_PUZZLE_ATTR_RO(build_info, IEI_WT61P803_PUZZLE_BUILD_INFO, 0); ++static IEI_WT61P803_PUZZLE_ATTR_RO(bootloader_mode, IEI_WT61P803_PUZZLE_BOOTLOADER_MODE, 0); ++static IEI_WT61P803_PUZZLE_ATTR_RO(protocol_version, IEI_WT61P803_PUZZLE_PROTOCOL_VERSION, 0); ++static IEI_WT61P803_PUZZLE_ATTR_RW(serial_number, IEI_WT61P803_PUZZLE_SERIAL_NUMBER, 0); ++static IEI_WT61P803_PUZZLE_ATTR_RW(mac_address_0, IEI_WT61P803_PUZZLE_MAC_ADDRESS, 0); ++static IEI_WT61P803_PUZZLE_ATTR_RW(mac_address_1, IEI_WT61P803_PUZZLE_MAC_ADDRESS, 1); ++static IEI_WT61P803_PUZZLE_ATTR_RW(mac_address_2, IEI_WT61P803_PUZZLE_MAC_ADDRESS, 2); ++static IEI_WT61P803_PUZZLE_ATTR_RW(mac_address_3, IEI_WT61P803_PUZZLE_MAC_ADDRESS, 3); ++static IEI_WT61P803_PUZZLE_ATTR_RW(mac_address_4, IEI_WT61P803_PUZZLE_MAC_ADDRESS, 4); ++static IEI_WT61P803_PUZZLE_ATTR_RW(mac_address_5, IEI_WT61P803_PUZZLE_MAC_ADDRESS, 5); ++static IEI_WT61P803_PUZZLE_ATTR_RW(mac_address_6, IEI_WT61P803_PUZZLE_MAC_ADDRESS, 6); ++static IEI_WT61P803_PUZZLE_ATTR_RW(mac_address_7, IEI_WT61P803_PUZZLE_MAC_ADDRESS, 7); ++static IEI_WT61P803_PUZZLE_ATTR_RO(ac_recovery_status, IEI_WT61P803_PUZZLE_AC_RECOVERY_STATUS, 0); ++static IEI_WT61P803_PUZZLE_ATTR_RW(power_loss_recovery, IEI_WT61P803_PUZZLE_POWER_LOSS_RECOVERY, 0); ++static IEI_WT61P803_PUZZLE_ATTR_RO(power_status, IEI_WT61P803_PUZZLE_POWER_STATUS, 0); ++ ++static struct attribute *iei_wt61p803_puzzle_attrs[] = { ++ &dev_attr_version.dev_attr.attr, ++ &dev_attr_build_info.dev_attr.attr, ++ &dev_attr_bootloader_mode.dev_attr.attr, ++ &dev_attr_protocol_version.dev_attr.attr, ++ &dev_attr_serial_number.dev_attr.attr, ++ &dev_attr_mac_address_0.dev_attr.attr, ++ &dev_attr_mac_address_1.dev_attr.attr, ++ &dev_attr_mac_address_2.dev_attr.attr, ++ &dev_attr_mac_address_3.dev_attr.attr, ++ &dev_attr_mac_address_4.dev_attr.attr, ++ &dev_attr_mac_address_5.dev_attr.attr, ++ &dev_attr_mac_address_6.dev_attr.attr, ++ &dev_attr_mac_address_7.dev_attr.attr, ++ &dev_attr_ac_recovery_status.dev_attr.attr, ++ &dev_attr_power_loss_recovery.dev_attr.attr, ++ &dev_attr_power_status.dev_attr.attr, ++ NULL ++}; ++ATTRIBUTE_GROUPS(iei_wt61p803_puzzle); ++ ++static int iei_wt61p803_puzzle_sysfs_create(struct device *dev, ++ struct iei_wt61p803_puzzle *mcu) ++{ ++ int ret; ++ ++ ret = sysfs_create_groups(&mcu->dev->kobj, iei_wt61p803_puzzle_groups); ++ if (ret) ++ mfd_remove_devices(mcu->dev); ++ ++ return ret; ++} ++ ++static int iei_wt61p803_puzzle_sysfs_remove(struct device *dev, ++ struct iei_wt61p803_puzzle *mcu) ++{ ++ /* Remove sysfs groups */ ++ sysfs_remove_groups(&mcu->dev->kobj, iei_wt61p803_puzzle_groups); ++ mfd_remove_devices(mcu->dev); ++ ++ return 0; ++} ++ ++static int iei_wt61p803_puzzle_probe(struct serdev_device *serdev) ++{ ++ struct device *dev = &serdev->dev; ++ struct iei_wt61p803_puzzle *mcu; ++ u32 baud; ++ int ret; ++ ++ /* Read the baud rate from 'current-speed', because the MCU supports different rates */ ++ if (device_property_read_u32(dev, "current-speed", &baud)) { ++ dev_err(dev, ++ "'current-speed' is not specified in device node\n"); ++ return -EINVAL; ++ } ++ dev_dbg(dev, "Driver baud rate: %d\n", baud); ++ ++ /* Allocate the memory */ ++ mcu = devm_kzalloc(dev, sizeof(*mcu), GFP_KERNEL); ++ if (!mcu) ++ return -ENOMEM; ++ ++ mcu->reply = devm_kzalloc(dev, sizeof(*mcu->reply), GFP_KERNEL); ++ if (!mcu->reply) ++ return -ENOMEM; ++ ++ /* Initialize device struct data */ ++ mcu->serdev = serdev; ++ mcu->dev = dev; ++ init_completion(&mcu->reply->received); ++ mutex_init(&mcu->reply_lock); ++ mutex_init(&mcu->lock); ++ ++ /* Setup UART interface */ ++ serdev_device_set_drvdata(serdev, mcu); ++ serdev_device_set_client_ops(serdev, &iei_wt61p803_puzzle_serdev_device_ops); ++ ret = devm_serdev_device_open(dev, serdev); ++ if (ret) ++ return ret; ++ serdev_device_set_baudrate(serdev, baud); ++ serdev_device_set_flow_control(serdev, false); ++ ret = serdev_device_set_parity(serdev, SERDEV_PARITY_NONE); ++ if (ret) { ++ dev_err(dev, "Failed to set parity\n"); ++ return ret; ++ } ++ ++ ret = iei_wt61p803_puzzle_get_version(mcu); ++ if (ret) ++ return ret; ++ ++ dev_dbg(dev, "MCU version: %s\n", mcu->version.version); ++ dev_dbg(dev, "MCU firmware build info: %s\n", mcu->version.build_info); ++ dev_dbg(dev, "MCU in bootloader mode: %s\n", ++ mcu->version.bootloader_mode ? "true" : "false"); ++ dev_dbg(dev, "MCU protocol version: %s\n", mcu->version.protocol_version); ++ ++ if (device_property_read_bool(dev, "enable-beep")) { ++ ret = iei_wt61p803_puzzle_buzzer(mcu, false); ++ if (ret) ++ return ret; ++ } ++ ++ ret = iei_wt61p803_puzzle_sysfs_create(dev, mcu); ++ if (ret) ++ return ret; ++ ++ return devm_of_platform_populate(dev); ++} ++ ++static void iei_wt61p803_puzzle_remove(struct serdev_device *serdev) ++{ ++ struct device *dev = &serdev->dev; ++ struct iei_wt61p803_puzzle *mcu = dev_get_drvdata(dev); ++ ++ iei_wt61p803_puzzle_sysfs_remove(dev, mcu); ++} ++ ++static const struct of_device_id iei_wt61p803_puzzle_dt_ids[] = { ++ { .compatible = "iei,wt61p803-puzzle" }, ++ { } ++}; ++ ++MODULE_DEVICE_TABLE(of, iei_wt61p803_puzzle_dt_ids); ++ ++static struct serdev_device_driver iei_wt61p803_puzzle_drv = { ++ .probe = iei_wt61p803_puzzle_probe, ++ .remove = iei_wt61p803_puzzle_remove, ++ .driver = { ++ .name = "iei-wt61p803-puzzle", ++ .of_match_table = iei_wt61p803_puzzle_dt_ids, ++ }, ++}; ++ ++module_serdev_device_driver(iei_wt61p803_puzzle_drv); ++ ++MODULE_LICENSE("GPL v2"); ++MODULE_AUTHOR("Luka Kovacic <luka.kovacic@sartura.hr>"); ++MODULE_DESCRIPTION("IEI WT61P803 PUZZLE MCU Driver"); +--- /dev/null ++++ b/include/linux/mfd/iei-wt61p803-puzzle.h +@@ -0,0 +1,66 @@ ++/* SPDX-License-Identifier: GPL-2.0-only */ ++/* IEI WT61P803 PUZZLE MCU Driver ++ * System management microcontroller for fan control, temperature sensor reading, ++ * LED control and system identification on IEI Puzzle series ARM-based appliances. ++ * ++ * Copyright (C) 2020 Sartura Ltd. ++ * Author: Luka Kovacic <luka.kovacic@sartura.hr> ++ */ ++ ++#ifndef _MFD_IEI_WT61P803_PUZZLE_H_ ++#define _MFD_IEI_WT61P803_PUZZLE_H_ ++ ++#define IEI_WT61P803_PUZZLE_BUF_SIZE 512 ++ ++/* Command magic numbers */ ++#define IEI_WT61P803_PUZZLE_CMD_HEADER_START 0x40 /* @ */ ++#define IEI_WT61P803_PUZZLE_CMD_HEADER_START_OTHER 0x25 /* % */ ++#define IEI_WT61P803_PUZZLE_CMD_HEADER_EEPROM 0xF7 ++ ++#define IEI_WT61P803_PUZZLE_CMD_RESPONSE_OK 0x30 /* 0 */ ++#define IEI_WT61P803_PUZZLE_CHECKSUM_RESPONSE_OK 0x70 ++ ++#define IEI_WT61P803_PUZZLE_CMD_EEPROM_READ 0xA1 ++#define IEI_WT61P803_PUZZLE_CMD_EEPROM_WRITE 0xA0 ++ ++#define IEI_WT61P803_PUZZLE_CMD_OTHER_VERSION 0x56 /* V */ ++#define IEI_WT61P803_PUZZLE_CMD_OTHER_BUILD 0x42 /* B */ ++#define IEI_WT61P803_PUZZLE_CMD_OTHER_BOOTLOADER_MODE 0x4D /* M */ ++#define IEI_WT61P803_PUZZLE_CMD_OTHER_MODE_BOOTLOADER 0x30 ++#define IEI_WT61P803_PUZZLE_CMD_OTHER_MODE_APPS 0x31 ++#define IEI_WT61P803_PUZZLE_CMD_OTHER_PROTOCOL_VERSION 0x50 /* P */ ++ ++#define IEI_WT61P803_PUZZLE_CMD_FUNCTION_SINGLE 0x43 /* C */ ++#define IEI_WT61P803_PUZZLE_CMD_FUNCTION_OTHER 0x4F /* O */ ++#define IEI_WT61P803_PUZZLE_CMD_FUNCTION_OTHER_STATUS 0x53 /* S */ ++#define IEI_WT61P803_PUZZLE_CMD_FUNCTION_OTHER_POWER_LOSS 0x41 /* A */ ++ ++#define IEI_WT61P803_PUZZLE_CMD_LED 0x52 /* R */ ++#define IEI_WT61P803_PUZZLE_CMD_LED_POWER 0x31 /* 1 */ ++ ++#define IEI_WT61P803_PUZZLE_CMD_TEMP 0x54 /* T */ ++#define IEI_WT61P803_PUZZLE_CMD_TEMP_ALL 0x41 /* A */ ++ ++#define IEI_WT61P803_PUZZLE_CMD_FAN 0x46 /* F */ ++#define IEI_WT61P803_PUZZLE_CMD_FAN_PWM_READ 0x5A /* Z */ ++#define IEI_WT61P803_PUZZLE_CMD_FAN_PWM_WRITE 0x57 /* W */ ++#define IEI_WT61P803_PUZZLE_CMD_FAN_PWM_BASE 0x30 ++#define IEI_WT61P803_PUZZLE_CMD_FAN_RPM_BASE 0x41 /* A */ ++ ++#define IEI_WT61P803_PUZZLE_CMD_FAN_PWM(x) (IEI_WT61P803_PUZZLE_CMD_FAN_PWM_BASE + (x)) /* 0 - 1 */ ++#define IEI_WT61P803_PUZZLE_CMD_FAN_RPM(x) (IEI_WT61P803_PUZZLE_CMD_FAN_RPM_BASE + (x)) /* 0 - 5 */ ++ ++struct iei_wt61p803_puzzle_mcu_version; ++struct iei_wt61p803_puzzle_reply; ++struct iei_wt61p803_puzzle; ++ ++int iei_wt61p803_puzzle_write_command_watchdog(struct iei_wt61p803_puzzle *mcu, ++ unsigned char *cmd, size_t size, ++ unsigned char *reply_data, size_t *reply_size, ++ int retry_count); ++ ++int iei_wt61p803_puzzle_write_command(struct iei_wt61p803_puzzle *mcu, ++ unsigned char *cmd, size_t size, ++ unsigned char *reply_data, size_t *reply_size); ++ ++#endif /* _MFD_IEI_WT61P803_PUZZLE_H_ */ diff --git a/pkgs/patches-linux-5.15/903-drivers-hwmon-Add-the-IEI-WT61P803-PUZZLE-HWMON-driv.patch b/pkgs/patches-linux-5.15/903-drivers-hwmon-Add-the-IEI-WT61P803-PUZZLE-HWMON-driv.patch new file mode 100644 index 0000000..c22314e --- /dev/null +++ b/pkgs/patches-linux-5.15/903-drivers-hwmon-Add-the-IEI-WT61P803-PUZZLE-HWMON-driv.patch @@ -0,0 +1,469 @@ +From e3310a638cd310bfd93dbbc6d2732ab6aea18dd2 Mon Sep 17 00:00:00 2001 +From: Luka Kovacic <luka.kovacic () sartura ! hr> +Date: Tue, 24 Aug 2021 12:44:34 +0000 +Subject: [PATCH 3/7] drivers: hwmon: Add the IEI WT61P803 PUZZLE HWMON driver + +Add the IEI WT61P803 PUZZLE HWMON driver, that handles the fan speed +control via PWM, reading fan speed and reading on-board temperature +sensors. + +The driver registers a HWMON device and a simple thermal cooling device to +enable in-kernel fan management. + +This driver depends on the IEI WT61P803 PUZZLE MFD driver. + +Signed-off-by: Luka Kovacic <luka.kovacic@sartura.hr> +Signed-off-by: Pavo Banicevic <pavo.banicevic@sartura.hr> +Acked-by: Guenter Roeck <linux@roeck-us.net> +Cc: Luka Perkov <luka.perkov@sartura.hr> +Cc: Robert Marko <robert.marko@sartura.hr> +--- + drivers/hwmon/Kconfig | 8 + + drivers/hwmon/Makefile | 1 + + drivers/hwmon/iei-wt61p803-puzzle-hwmon.c | 413 ++++++++++++++++++++++ + 3 files changed, 422 insertions(+) + create mode 100644 drivers/hwmon/iei-wt61p803-puzzle-hwmon.c + +--- a/drivers/hwmon/Kconfig ++++ b/drivers/hwmon/Kconfig +@@ -732,6 +732,14 @@ config SENSORS_IBMPOWERNV + This driver can also be built as a module. If so, the module + will be called ibmpowernv. + ++config SENSORS_IEI_WT61P803_PUZZLE_HWMON ++ tristate "IEI WT61P803 PUZZLE MFD HWMON Driver" ++ depends on MFD_IEI_WT61P803_PUZZLE ++ help ++ The IEI WT61P803 PUZZLE MFD HWMON Driver handles reading fan speed ++ and writing fan PWM values. It also supports reading on-board ++ temperature sensors. ++ + config SENSORS_IIO_HWMON + tristate "Hwmon driver that uses channels specified via iio maps" + depends on IIO +--- a/drivers/hwmon/Makefile ++++ b/drivers/hwmon/Makefile +@@ -84,6 +84,7 @@ obj-$(CONFIG_SENSORS_HIH6130) += hih6130 + obj-$(CONFIG_SENSORS_ULTRA45) += ultra45_env.o + obj-$(CONFIG_SENSORS_I5500) += i5500_temp.o + obj-$(CONFIG_SENSORS_I5K_AMB) += i5k_amb.o ++obj-$(CONFIG_SENSORS_IEI_WT61P803_PUZZLE_HWMON) += iei-wt61p803-puzzle-hwmon.o + obj-$(CONFIG_SENSORS_IBMAEM) += ibmaem.o + obj-$(CONFIG_SENSORS_IBMPEX) += ibmpex.o + obj-$(CONFIG_SENSORS_IBMPOWERNV)+= ibmpowernv.o +--- /dev/null ++++ b/drivers/hwmon/iei-wt61p803-puzzle-hwmon.c +@@ -0,0 +1,413 @@ ++// SPDX-License-Identifier: GPL-2.0-only ++/* IEI WT61P803 PUZZLE MCU HWMON Driver ++ * ++ * Copyright (C) 2020 Sartura Ltd. ++ * Author: Luka Kovacic <luka.kovacic@sartura.hr> ++ */ ++ ++#include <linux/err.h> ++#include <linux/hwmon.h> ++#include <linux/interrupt.h> ++#include <linux/irq.h> ++#include <linux/math64.h> ++#include <linux/mfd/iei-wt61p803-puzzle.h> ++#include <linux/mod_devicetable.h> ++#include <linux/module.h> ++#include <linux/platform_device.h> ++#include <linux/property.h> ++#include <linux/slab.h> ++#include <linux/thermal.h> ++ ++#define IEI_WT61P803_PUZZLE_HWMON_MAX_PWM 2 ++#define IEI_WT61P803_PUZZLE_HWMON_MAX_PWM_VAL 255 ++ ++/** ++ * struct iei_wt61p803_puzzle_thermal_cooling_device - Thermal cooling device instance ++ * @mcu_hwmon: Parent driver struct pointer ++ * @tcdev: Thermal cooling device pointer ++ * @name: Thermal cooling device name ++ * @pwm_channel: Controlled PWM channel (0 or 1) ++ * @cooling_levels: Thermal cooling device cooling levels (DT) ++ */ ++struct iei_wt61p803_puzzle_thermal_cooling_device { ++ struct iei_wt61p803_puzzle_hwmon *mcu_hwmon; ++ struct thermal_cooling_device *tcdev; ++ char name[THERMAL_NAME_LENGTH]; ++ int pwm_channel; ++ u8 *cooling_levels; ++}; ++ ++/** ++ * struct iei_wt61p803_puzzle_hwmon - MCU HWMON Driver ++ * @mcu: MCU struct pointer ++ * @response_buffer Global MCU response buffer ++ * @thermal_cooling_dev_present: Per-channel thermal cooling device control indicator ++ * @cdev: Per-channel thermal cooling device private structure ++ */ ++struct iei_wt61p803_puzzle_hwmon { ++ struct iei_wt61p803_puzzle *mcu; ++ unsigned char response_buffer[IEI_WT61P803_PUZZLE_BUF_SIZE]; ++ bool thermal_cooling_dev_present[IEI_WT61P803_PUZZLE_HWMON_MAX_PWM]; ++ struct iei_wt61p803_puzzle_thermal_cooling_device ++ *cdev[IEI_WT61P803_PUZZLE_HWMON_MAX_PWM]; ++ struct mutex lock; /* mutex to protect response_buffer array */ ++}; ++ ++#define raw_temp_to_milidegree_celsius(x) (((x) - 0x80) * 1000) ++static int iei_wt61p803_puzzle_read_temp_sensor(struct iei_wt61p803_puzzle_hwmon *mcu_hwmon, ++ int channel, long *value) ++{ ++ unsigned char *resp_buf = mcu_hwmon->response_buffer; ++ unsigned char temp_sensor_ntc_cmd[4] = { ++ IEI_WT61P803_PUZZLE_CMD_HEADER_START, ++ IEI_WT61P803_PUZZLE_CMD_TEMP, ++ IEI_WT61P803_PUZZLE_CMD_TEMP_ALL, ++ }; ++ size_t reply_size; ++ int ret; ++ ++ mutex_lock(&mcu_hwmon->lock); ++ ret = iei_wt61p803_puzzle_write_command(mcu_hwmon->mcu, temp_sensor_ntc_cmd, ++ sizeof(temp_sensor_ntc_cmd), resp_buf, ++ &reply_size); ++ if (ret) ++ goto exit; ++ ++ if (reply_size != 7) { ++ ret = -EIO; ++ goto exit; ++ } ++ ++ /* Check the number of NTC values */ ++ if (resp_buf[3] != '2') { ++ ret = -EIO; ++ goto exit; ++ } ++ ++ *value = raw_temp_to_milidegree_celsius(resp_buf[4 + channel]); ++exit: ++ mutex_unlock(&mcu_hwmon->lock); ++ return ret; ++} ++ ++#define raw_fan_val_to_rpm(x, y) ((((x) << 8 | (y)) / 2) * 60) ++static int iei_wt61p803_puzzle_read_fan_speed(struct iei_wt61p803_puzzle_hwmon *mcu_hwmon, ++ int channel, long *value) ++{ ++ unsigned char *resp_buf = mcu_hwmon->response_buffer; ++ unsigned char fan_speed_cmd[4] = {}; ++ size_t reply_size; ++ int ret; ++ ++ fan_speed_cmd[0] = IEI_WT61P803_PUZZLE_CMD_HEADER_START; ++ fan_speed_cmd[1] = IEI_WT61P803_PUZZLE_CMD_FAN; ++ fan_speed_cmd[2] = IEI_WT61P803_PUZZLE_CMD_FAN_RPM(channel); ++ ++ mutex_lock(&mcu_hwmon->lock); ++ ret = iei_wt61p803_puzzle_write_command(mcu_hwmon->mcu, fan_speed_cmd, ++ sizeof(fan_speed_cmd), resp_buf, ++ &reply_size); ++ if (ret) ++ goto exit; ++ ++ if (reply_size != 7) { ++ ret = -EIO; ++ goto exit; ++ } ++ ++ *value = raw_fan_val_to_rpm(resp_buf[3], resp_buf[4]); ++exit: ++ mutex_unlock(&mcu_hwmon->lock); ++ return ret; ++} ++ ++static int iei_wt61p803_puzzle_write_pwm_channel(struct iei_wt61p803_puzzle_hwmon *mcu_hwmon, ++ int channel, long pwm_set_val) ++{ ++ unsigned char *resp_buf = mcu_hwmon->response_buffer; ++ unsigned char pwm_set_cmd[6] = {}; ++ size_t reply_size; ++ int ret; ++ ++ pwm_set_cmd[0] = IEI_WT61P803_PUZZLE_CMD_HEADER_START; ++ pwm_set_cmd[1] = IEI_WT61P803_PUZZLE_CMD_FAN; ++ pwm_set_cmd[2] = IEI_WT61P803_PUZZLE_CMD_FAN_PWM_WRITE; ++ pwm_set_cmd[3] = IEI_WT61P803_PUZZLE_CMD_FAN_PWM(channel); ++ pwm_set_cmd[4] = pwm_set_val; ++ ++ mutex_lock(&mcu_hwmon->lock); ++ ret = iei_wt61p803_puzzle_write_command(mcu_hwmon->mcu, pwm_set_cmd, ++ sizeof(pwm_set_cmd), resp_buf, ++ &reply_size); ++ if (ret) ++ goto exit; ++ ++ if (reply_size != 3) { ++ ret = -EIO; ++ goto exit; ++ } ++ ++ if (!(resp_buf[0] == IEI_WT61P803_PUZZLE_CMD_HEADER_START && ++ resp_buf[1] == IEI_WT61P803_PUZZLE_CMD_RESPONSE_OK && ++ resp_buf[2] == IEI_WT61P803_PUZZLE_CHECKSUM_RESPONSE_OK)) { ++ ret = -EIO; ++ goto exit; ++ } ++exit: ++ mutex_unlock(&mcu_hwmon->lock); ++ return ret; ++} ++ ++static int iei_wt61p803_puzzle_read_pwm_channel(struct iei_wt61p803_puzzle_hwmon *mcu_hwmon, ++ int channel, long *value) ++{ ++ unsigned char *resp_buf = mcu_hwmon->response_buffer; ++ unsigned char pwm_get_cmd[5] = {}; ++ size_t reply_size; ++ int ret; ++ ++ pwm_get_cmd[0] = IEI_WT61P803_PUZZLE_CMD_HEADER_START; ++ pwm_get_cmd[1] = IEI_WT61P803_PUZZLE_CMD_FAN; ++ pwm_get_cmd[2] = IEI_WT61P803_PUZZLE_CMD_FAN_PWM_READ; ++ pwm_get_cmd[3] = IEI_WT61P803_PUZZLE_CMD_FAN_PWM(channel); ++ ++ ret = iei_wt61p803_puzzle_write_command(mcu_hwmon->mcu, pwm_get_cmd, ++ sizeof(pwm_get_cmd), resp_buf, ++ &reply_size); ++ if (ret) ++ return ret; ++ ++ if (reply_size != 5) ++ return -EIO; ++ ++ if (resp_buf[2] != IEI_WT61P803_PUZZLE_CMD_FAN_PWM_READ) ++ return -EIO; ++ ++ *value = resp_buf[3]; ++ ++ return 0; ++} ++ ++static int iei_wt61p803_puzzle_read(struct device *dev, enum hwmon_sensor_types type, ++ u32 attr, int channel, long *val) ++{ ++ struct iei_wt61p803_puzzle_hwmon *mcu_hwmon = dev_get_drvdata(dev->parent); ++ ++ switch (type) { ++ case hwmon_pwm: ++ return iei_wt61p803_puzzle_read_pwm_channel(mcu_hwmon, channel, val); ++ case hwmon_fan: ++ return iei_wt61p803_puzzle_read_fan_speed(mcu_hwmon, channel, val); ++ case hwmon_temp: ++ return iei_wt61p803_puzzle_read_temp_sensor(mcu_hwmon, channel, val); ++ default: ++ return -EINVAL; ++ } ++} ++ ++static int iei_wt61p803_puzzle_write(struct device *dev, enum hwmon_sensor_types type, ++ u32 attr, int channel, long val) ++{ ++ struct iei_wt61p803_puzzle_hwmon *mcu_hwmon = dev_get_drvdata(dev->parent); ++ ++ return iei_wt61p803_puzzle_write_pwm_channel(mcu_hwmon, channel, val); ++} ++ ++static umode_t iei_wt61p803_puzzle_is_visible(const void *data, enum hwmon_sensor_types type, ++ u32 attr, int channel) ++{ ++ const struct iei_wt61p803_puzzle_hwmon *mcu_hwmon = data; ++ ++ switch (type) { ++ case hwmon_pwm: ++ if (mcu_hwmon->thermal_cooling_dev_present[channel]) ++ return 0444; ++ if (attr == hwmon_pwm_input) ++ return 0644; ++ break; ++ case hwmon_fan: ++ if (attr == hwmon_fan_input) ++ return 0444; ++ break; ++ case hwmon_temp: ++ if (attr == hwmon_temp_input) ++ return 0444; ++ break; ++ default: ++ return 0; ++ } ++ ++ return 0; ++} ++ ++static const struct hwmon_ops iei_wt61p803_puzzle_hwmon_ops = { ++ .is_visible = iei_wt61p803_puzzle_is_visible, ++ .read = iei_wt61p803_puzzle_read, ++ .write = iei_wt61p803_puzzle_write, ++}; ++ ++static const struct hwmon_channel_info *iei_wt61p803_puzzle_info[] = { ++ HWMON_CHANNEL_INFO(pwm, ++ HWMON_PWM_INPUT, ++ HWMON_PWM_INPUT), ++ HWMON_CHANNEL_INFO(fan, ++ HWMON_F_INPUT, ++ HWMON_F_INPUT, ++ HWMON_F_INPUT, ++ HWMON_F_INPUT, ++ HWMON_F_INPUT), ++ HWMON_CHANNEL_INFO(temp, ++ HWMON_T_INPUT, ++ HWMON_T_INPUT), ++ NULL ++}; ++ ++static const struct hwmon_chip_info iei_wt61p803_puzzle_chip_info = { ++ .ops = &iei_wt61p803_puzzle_hwmon_ops, ++ .info = iei_wt61p803_puzzle_info, ++}; ++ ++static int iei_wt61p803_puzzle_get_max_state(struct thermal_cooling_device *tcdev, ++ unsigned long *state) ++{ ++ *state = IEI_WT61P803_PUZZLE_HWMON_MAX_PWM_VAL; ++ ++ return 0; ++} ++ ++static int iei_wt61p803_puzzle_get_cur_state(struct thermal_cooling_device *tcdev, ++ unsigned long *state) ++{ ++ struct iei_wt61p803_puzzle_thermal_cooling_device *cdev = tcdev->devdata; ++ struct iei_wt61p803_puzzle_hwmon *mcu_hwmon = cdev->mcu_hwmon; ++ long value; ++ int ret; ++ ++ ret = iei_wt61p803_puzzle_read_pwm_channel(mcu_hwmon, cdev->pwm_channel, &value); ++ if (ret) ++ return ret; ++ *state = value; ++ return 0; ++} ++ ++static int iei_wt61p803_puzzle_set_cur_state(struct thermal_cooling_device *tcdev, ++ unsigned long state) ++{ ++ struct iei_wt61p803_puzzle_thermal_cooling_device *cdev = tcdev->devdata; ++ struct iei_wt61p803_puzzle_hwmon *mcu_hwmon = cdev->mcu_hwmon; ++ ++ return iei_wt61p803_puzzle_write_pwm_channel(mcu_hwmon, cdev->pwm_channel, state); ++} ++ ++static const struct thermal_cooling_device_ops iei_wt61p803_puzzle_cooling_ops = { ++ .get_max_state = iei_wt61p803_puzzle_get_max_state, ++ .get_cur_state = iei_wt61p803_puzzle_get_cur_state, ++ .set_cur_state = iei_wt61p803_puzzle_set_cur_state, ++}; ++ ++static int ++iei_wt61p803_puzzle_enable_thermal_cooling_dev(struct device *dev, ++ struct fwnode_handle *child, ++ struct iei_wt61p803_puzzle_hwmon *mcu_hwmon) ++{ ++ struct iei_wt61p803_puzzle_thermal_cooling_device *cdev; ++ u32 pwm_channel; ++ u8 num_levels; ++ int ret; ++ ++ ret = fwnode_property_read_u32(child, "reg", &pwm_channel); ++ if (ret) ++ return ret; ++ ++ mcu_hwmon->thermal_cooling_dev_present[pwm_channel] = true; ++ ++ num_levels = fwnode_property_count_u8(child, "cooling-levels"); ++ if (!num_levels) ++ return -EINVAL; ++ ++ cdev = devm_kzalloc(dev, sizeof(*cdev), GFP_KERNEL); ++ if (!cdev) ++ return -ENOMEM; ++ ++ cdev->cooling_levels = devm_kmalloc_array(dev, num_levels, sizeof(u8), GFP_KERNEL); ++ if (!cdev->cooling_levels) ++ return -ENOMEM; ++ ++ ret = fwnode_property_read_u8_array(child, "cooling-levels", ++ cdev->cooling_levels, ++ num_levels); ++ if (ret) { ++ dev_err(dev, "Couldn't read property 'cooling-levels'\n"); ++ return ret; ++ } ++ ++ snprintf(cdev->name, THERMAL_NAME_LENGTH, "wt61p803_puzzle_%d", pwm_channel); ++ cdev->tcdev = devm_thermal_of_cooling_device_register(dev, NULL, cdev->name, cdev, ++ &iei_wt61p803_puzzle_cooling_ops); ++ if (IS_ERR(cdev->tcdev)) ++ return PTR_ERR(cdev->tcdev); ++ ++ cdev->mcu_hwmon = mcu_hwmon; ++ cdev->pwm_channel = pwm_channel; ++ mcu_hwmon->cdev[pwm_channel] = cdev; ++ ++ return 0; ++} ++ ++static int iei_wt61p803_puzzle_hwmon_probe(struct platform_device *pdev) ++{ ++ struct device *dev = &pdev->dev; ++ struct iei_wt61p803_puzzle *mcu = dev_get_drvdata(dev->parent); ++ struct iei_wt61p803_puzzle_hwmon *mcu_hwmon; ++ struct fwnode_handle *child; ++ struct device *hwmon_dev; ++ int ret; ++ ++ mcu_hwmon = devm_kzalloc(dev, sizeof(*mcu_hwmon), GFP_KERNEL); ++ if (!mcu_hwmon) ++ return -ENOMEM; ++ ++ mcu_hwmon->mcu = mcu; ++ platform_set_drvdata(pdev, mcu_hwmon); ++ mutex_init(&mcu_hwmon->lock); ++ ++ hwmon_dev = devm_hwmon_device_register_with_info(dev, "iei_wt61p803_puzzle", ++ mcu_hwmon, ++ &iei_wt61p803_puzzle_chip_info, ++ NULL); ++ if (IS_ERR(hwmon_dev)) ++ return PTR_ERR(hwmon_dev); ++ ++ /* Control fans via PWM lines via Linux Kernel */ ++ if (IS_ENABLED(CONFIG_THERMAL)) { ++ device_for_each_child_node(dev, child) { ++ ret = iei_wt61p803_puzzle_enable_thermal_cooling_dev(dev, child, mcu_hwmon); ++ if (ret) { ++ dev_err(dev, "Enabling the PWM fan failed\n"); ++ fwnode_handle_put(child); ++ return ret; ++ } ++ } ++ } ++ return 0; ++} ++ ++static const struct of_device_id iei_wt61p803_puzzle_hwmon_id_table[] = { ++ { .compatible = "iei,wt61p803-puzzle-hwmon" }, ++ {} ++}; ++MODULE_DEVICE_TABLE(of, iei_wt61p803_puzzle_hwmon_id_table); ++ ++static struct platform_driver iei_wt61p803_puzzle_hwmon_driver = { ++ .driver = { ++ .name = "iei-wt61p803-puzzle-hwmon", ++ .of_match_table = iei_wt61p803_puzzle_hwmon_id_table, ++ }, ++ .probe = iei_wt61p803_puzzle_hwmon_probe, ++}; ++ ++module_platform_driver(iei_wt61p803_puzzle_hwmon_driver); ++ ++MODULE_DESCRIPTION("IEI WT61P803 PUZZLE MCU HWMON Driver"); ++MODULE_AUTHOR("Luka Kovacic <luka.kovacic@sartura.hr>"); ++MODULE_LICENSE("GPL v2"); diff --git a/pkgs/patches-linux-5.15/904-debloat_dma_buf.patch b/pkgs/patches-linux-5.15/904-debloat_dma_buf.patch new file mode 100644 index 0000000..302c000 --- /dev/null +++ b/pkgs/patches-linux-5.15/904-debloat_dma_buf.patch @@ -0,0 +1,92 @@ +From e3692cb2fcd5ba1244512a0f43b8118f65f1c375 Mon Sep 17 00:00:00 2001 +From: Felix Fietkau <nbd@nbd.name> +Date: Sat, 8 Jul 2017 08:20:43 +0200 +Subject: debloat: dmabuf + +Signed-off-by: Felix Fietkau <nbd@nbd.name> +--- + drivers/base/Kconfig | 2 +- + drivers/dma-buf/Makefile | 10 +++++++--- + drivers/dma-buf/dma-buf.c | 4 +++- + kernel/sched/core.c | 1 + + 4 files changed, 12 insertions(+), 5 deletions(-) + +--- a/drivers/base/Kconfig ++++ b/drivers/base/Kconfig +@@ -187,7 +187,7 @@ config SOC_BUS + source "drivers/base/regmap/Kconfig" + + config DMA_SHARED_BUFFER +- bool ++ tristate + default n + select IRQ_WORK + help +--- a/drivers/dma-buf/heaps/Makefile ++++ b/drivers/dma-buf/heaps/Makefile +@@ -1,3 +1,3 @@ + # SPDX-License-Identifier: GPL-2.0 +-obj-$(CONFIG_DMABUF_HEAPS_SYSTEM) += system_heap.o +-obj-$(CONFIG_DMABUF_HEAPS_CMA) += cma_heap.o ++dma-buf-objs-$(CONFIG_DMABUF_HEAPS_SYSTEM) += system_heap.o ++dma-buf-objs-$(CONFIG_DMABUF_HEAPS_CMA) += cma_heap.o +--- a/drivers/dma-buf/Makefile ++++ b/drivers/dma-buf/Makefile +@@ -1,16 +1,20 @@ + # SPDX-License-Identifier: GPL-2.0-only +-obj-y := dma-buf.o dma-fence.o dma-fence-array.o dma-fence-chain.o \ ++obj-$(CONFIG_DMA_SHARED_BUFFER) := dma-shared-buffer.o ++ ++dma-buf-objs-y := dma-buf.o dma-fence.o dma-fence-array.o dma-fence-chain.o \ + dma-resv.o seqno-fence.o +-obj-$(CONFIG_DMABUF_HEAPS) += dma-heap.o +-obj-$(CONFIG_DMABUF_HEAPS) += heaps/ +-obj-$(CONFIG_SYNC_FILE) += sync_file.o +-obj-$(CONFIG_SW_SYNC) += sw_sync.o sync_debug.o +-obj-$(CONFIG_UDMABUF) += udmabuf.o +-obj-$(CONFIG_DMABUF_SYSFS_STATS) += dma-buf-sysfs-stats.o ++dma-buf-objs-$(CONFIG_DMABUF_HEAPS) += dma-heap.o ++obj-$(CONFIG_DMABUF_HEAPS) += heaps/ ++dma-buf-objs-$(CONFIG_SYNC_FILE) += sync_file.o ++dma-buf-objs-$(CONFIG_SW_SYNC) += sw_sync.o sync_debug.o ++dma-buf-objs-$(CONFIG_UDMABUF) += udmabuf.o ++dma-buf-objs-$(CONFIG_DMABUF_SYSFS_STATS) += udmabuf.o + + dmabuf_selftests-y := \ + selftest.o \ + st-dma-fence.o \ + st-dma-fence-chain.o + +-obj-$(CONFIG_DMABUF_SELFTESTS) += dmabuf_selftests.o ++dma-buf-objs-$(CONFIG_DMABUF_SELFTESTS) += dmabuf_selftests.o ++ ++dma-shared-buffer-objs := $(dma-buf-objs-y) +--- a/drivers/dma-buf/dma-buf.c ++++ b/drivers/dma-buf/dma-buf.c +@@ -1513,4 +1513,5 @@ static void __exit dma_buf_deinit(void) + kern_unmount(dma_buf_mnt); + dma_buf_uninit_sysfs_statistics(); + } +-__exitcall(dma_buf_deinit); ++module_exit(dma_buf_deinit); ++MODULE_LICENSE("GPL"); +--- a/kernel/sched/core.c ++++ b/kernel/sched/core.c +@@ -4184,6 +4184,7 @@ int wake_up_state(struct task_struct *p, + { + return try_to_wake_up(p, state, 0); + } ++EXPORT_SYMBOL_GPL(wake_up_state); + + /* + * Perform scheduler related setup for a newly forked process p. +--- a/fs/d_path.c ++++ b/fs/d_path.c +@@ -316,6 +316,7 @@ char *dynamic_dname(struct dentry *dentr + buffer += buflen - sz; + return memcpy(buffer, temp, sz); + } ++EXPORT_SYMBOL_GPL(dynamic_dname); + + char *simple_dname(struct dentry *dentry, char *buffer, int buflen) + { diff --git a/pkgs/patches-linux-5.15/904-drivers-leds-Add-the-IEI-WT61P803-PUZZLE-LED-driver.patch b/pkgs/patches-linux-5.15/904-drivers-leds-Add-the-IEI-WT61P803-PUZZLE-LED-driver.patch new file mode 100644 index 0000000..0452bd6 --- /dev/null +++ b/pkgs/patches-linux-5.15/904-drivers-leds-Add-the-IEI-WT61P803-PUZZLE-LED-driver.patch @@ -0,0 +1,207 @@ +From f3b44eb69cc561cf05d00506dcec0dd9be003ed8 Mon Sep 17 00:00:00 2001 +From: Luka Kovacic <luka.kovacic () sartura ! hr> +Date: Tue, 24 Aug 2021 12:44:35 +0000 +Subject: [PATCH 4/7] drivers: leds: Add the IEI WT61P803 PUZZLE LED driver + +Add support for the IEI WT61P803 PUZZLE LED driver. +Currently only the front panel power LED is supported, +since it is the only LED on this board wired through the +MCU. + +The LED is wired directly to the on-board MCU controller +and is toggled using an MCU command. + +Support for more LEDs is going to be added in case more +boards implement this microcontroller, as LEDs use many +different GPIOs. + +This driver depends on the IEI WT61P803 PUZZLE MFD driver. + +Signed-off-by: Luka Kovacic <luka.kovacic@sartura.hr> +Signed-off-by: Pavo Banicevic <pavo.banicevic@sartura.hr> +Cc: Luka Perkov <luka.perkov@sartura.hr> +Cc: Robert Marko <robert.marko@sartura.hr> +--- + drivers/leds/Kconfig | 8 ++ + drivers/leds/Makefile | 1 + + drivers/leds/leds-iei-wt61p803-puzzle.c | 147 ++++++++++++++++++++++++ + 3 files changed, 156 insertions(+) + create mode 100644 drivers/leds/leds-iei-wt61p803-puzzle.c + +--- a/drivers/leds/Kconfig ++++ b/drivers/leds/Kconfig +@@ -305,6 +305,14 @@ config LEDS_IPAQ_MICRO + Choose this option if you want to use the notification LED on + Compaq/HP iPAQ h3100 and h3600. + ++config LEDS_IEI_WT61P803_PUZZLE ++ tristate "LED Support for the IEI WT61P803 PUZZLE MCU" ++ depends on LEDS_CLASS ++ depends on MFD_IEI_WT61P803_PUZZLE ++ help ++ This option enables support for LEDs controlled by the IEI WT61P803 ++ M801 MCU. ++ + config LEDS_HP6XX + tristate "LED Support for the HP Jornada 6xx" + depends on LEDS_CLASS +--- a/drivers/leds/Makefile ++++ b/drivers/leds/Makefile +@@ -33,6 +33,7 @@ obj-$(CONFIG_LEDS_HP6XX) += leds-hp6xx. + obj-$(CONFIG_LEDS_INTEL_SS4200) += leds-ss4200.o + obj-$(CONFIG_LEDS_IP30) += leds-ip30.o + obj-$(CONFIG_LEDS_IPAQ_MICRO) += leds-ipaq-micro.o ++obj-$(CONFIG_LEDS_IEI_WT61P803_PUZZLE) += leds-iei-wt61p803-puzzle.o + obj-$(CONFIG_LEDS_IS31FL319X) += leds-is31fl319x.o + obj-$(CONFIG_LEDS_IS31FL32XX) += leds-is31fl32xx.o + obj-$(CONFIG_LEDS_LM3530) += leds-lm3530.o +--- /dev/null ++++ b/drivers/leds/leds-iei-wt61p803-puzzle.c +@@ -0,0 +1,147 @@ ++// SPDX-License-Identifier: GPL-2.0-only ++/* IEI WT61P803 PUZZLE MCU LED Driver ++ * ++ * Copyright (C) 2020 Sartura Ltd. ++ * Author: Luka Kovacic <luka.kovacic@sartura.hr> ++ */ ++ ++#include <linux/leds.h> ++#include <linux/mfd/iei-wt61p803-puzzle.h> ++#include <linux/mod_devicetable.h> ++#include <linux/module.h> ++#include <linux/platform_device.h> ++#include <linux/property.h> ++#include <linux/slab.h> ++ ++enum iei_wt61p803_puzzle_led_state { ++ IEI_LED_OFF = 0x30, ++ IEI_LED_ON = 0x31, ++ IEI_LED_BLINK_5HZ = 0x32, ++ IEI_LED_BLINK_1HZ = 0x33, ++}; ++ ++/** ++ * struct iei_wt61p803_puzzle_led - MCU LED Driver ++ * @cdev: LED classdev ++ * @mcu: MCU struct pointer ++ * @response_buffer Global MCU response buffer ++ * @lock: General mutex lock to protect simultaneous R/W access to led_power_state ++ * @led_power_state: State of the front panel power LED ++ */ ++struct iei_wt61p803_puzzle_led { ++ struct led_classdev cdev; ++ struct iei_wt61p803_puzzle *mcu; ++ unsigned char response_buffer[IEI_WT61P803_PUZZLE_BUF_SIZE]; ++ struct mutex lock; /* mutex to protect led_power_state */ ++ int led_power_state; ++}; ++ ++static inline struct iei_wt61p803_puzzle_led *cdev_to_iei_wt61p803_puzzle_led ++ (struct led_classdev *led_cdev) ++{ ++ return container_of(led_cdev, struct iei_wt61p803_puzzle_led, cdev); ++} ++ ++static int iei_wt61p803_puzzle_led_brightness_set_blocking(struct led_classdev *cdev, ++ enum led_brightness brightness) ++{ ++ struct iei_wt61p803_puzzle_led *priv = cdev_to_iei_wt61p803_puzzle_led(cdev); ++ unsigned char *resp_buf = priv->response_buffer; ++ unsigned char led_power_cmd[5] = {}; ++ size_t reply_size; ++ int ret; ++ ++ led_power_cmd[0] = IEI_WT61P803_PUZZLE_CMD_HEADER_START; ++ led_power_cmd[1] = IEI_WT61P803_PUZZLE_CMD_LED; ++ led_power_cmd[2] = IEI_WT61P803_PUZZLE_CMD_LED_POWER; ++ led_power_cmd[3] = brightness == LED_OFF ? IEI_LED_OFF : IEI_LED_ON; ++ ++ ret = iei_wt61p803_puzzle_write_command(priv->mcu, led_power_cmd, ++ sizeof(led_power_cmd), ++ resp_buf, ++ &reply_size); ++ if (ret) ++ return ret; ++ ++ if (reply_size != 3) ++ return -EIO; ++ ++ if (!(resp_buf[0] == IEI_WT61P803_PUZZLE_CMD_HEADER_START && ++ resp_buf[1] == IEI_WT61P803_PUZZLE_CMD_RESPONSE_OK && ++ resp_buf[2] == IEI_WT61P803_PUZZLE_CHECKSUM_RESPONSE_OK)) ++ return -EIO; ++ ++ mutex_lock(&priv->lock); ++ priv->led_power_state = brightness; ++ mutex_unlock(&priv->lock); ++ ++ return 0; ++} ++ ++static enum led_brightness iei_wt61p803_puzzle_led_brightness_get(struct led_classdev *cdev) ++{ ++ struct iei_wt61p803_puzzle_led *priv = cdev_to_iei_wt61p803_puzzle_led(cdev); ++ int led_state; ++ ++ mutex_lock(&priv->lock); ++ led_state = priv->led_power_state; ++ mutex_unlock(&priv->lock); ++ ++ return led_state; ++} ++ ++static int iei_wt61p803_puzzle_led_probe(struct platform_device *pdev) ++{ ++ struct device *dev = &pdev->dev; ++ struct iei_wt61p803_puzzle *mcu = dev_get_drvdata(dev->parent); ++ struct iei_wt61p803_puzzle_led *priv; ++ struct led_init_data init_data = {}; ++ struct fwnode_handle *child; ++ int ret; ++ ++ if (device_get_child_node_count(dev) != 1) ++ return -EINVAL; ++ ++ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); ++ if (!priv) ++ return -ENOMEM; ++ ++ priv->mcu = mcu; ++ priv->led_power_state = 1; ++ mutex_init(&priv->lock); ++ dev_set_drvdata(dev, priv); ++ ++ child = device_get_next_child_node(dev, NULL); ++ init_data.fwnode = child; ++ ++ priv->cdev.brightness_set_blocking = iei_wt61p803_puzzle_led_brightness_set_blocking; ++ priv->cdev.brightness_get = iei_wt61p803_puzzle_led_brightness_get; ++ priv->cdev.max_brightness = 1; ++ ++ ret = devm_led_classdev_register_ext(dev, &priv->cdev, &init_data); ++ if (ret) ++ dev_err(dev, "Could not register LED\n"); ++ ++ fwnode_handle_put(child); ++ return ret; ++} ++ ++static const struct of_device_id iei_wt61p803_puzzle_led_of_match[] = { ++ { .compatible = "iei,wt61p803-puzzle-leds" }, ++ { } ++}; ++MODULE_DEVICE_TABLE(of, iei_wt61p803_puzzle_led_of_match); ++ ++static struct platform_driver iei_wt61p803_puzzle_led_driver = { ++ .driver = { ++ .name = "iei-wt61p803-puzzle-led", ++ .of_match_table = iei_wt61p803_puzzle_led_of_match, ++ }, ++ .probe = iei_wt61p803_puzzle_led_probe, ++}; ++module_platform_driver(iei_wt61p803_puzzle_led_driver); ++ ++MODULE_DESCRIPTION("IEI WT61P803 PUZZLE front panel LED driver"); ++MODULE_AUTHOR("Luka Kovacic <luka.kovacic@sartura.hr>"); ++MODULE_LICENSE("GPL v2"); ++MODULE_ALIAS("platform:leds-iei-wt61p803-puzzle"); diff --git a/pkgs/patches-linux-5.15/905-Documentation-ABI-Add-iei-wt61p803-puzzle-driver-sys.patch b/pkgs/patches-linux-5.15/905-Documentation-ABI-Add-iei-wt61p803-puzzle-driver-sys.patch new file mode 100644 index 0000000..b1d420e --- /dev/null +++ b/pkgs/patches-linux-5.15/905-Documentation-ABI-Add-iei-wt61p803-puzzle-driver-sys.patch @@ -0,0 +1,82 @@ +From 2fab3b4956c5b2f83c1e1abffc1df39de2933d83 Mon Sep 17 00:00:00 2001 +From: Luka Kovacic <luka.kovacic () sartura ! hr> +Date: Tue, 24 Aug 2021 12:44:36 +0000 +Subject: [PATCH 5/7] Documentation/ABI: Add iei-wt61p803-puzzle driver sysfs + interface documentation + +Add the iei-wt61p803-puzzle driver sysfs interface documentation to allow +monitoring and control of the microcontroller from user space. + +Signed-off-by: Luka Kovacic <luka.kovacic@sartura.hr> +Signed-off-by: Pavo Banicevic <pavo.banicevic@sartura.hr> +Cc: Luka Perkov <luka.perkov@sartura.hr> +Cc: Robert Marko <robert.marko@sartura.hr> +--- + .../testing/sysfs-driver-iei-wt61p803-puzzle | 61 +++++++++++++++++++ + 1 file changed, 61 insertions(+) + create mode 100644 Documentation/ABI/testing/sysfs-driver-iei-wt61p803-puzzle + +--- /dev/null ++++ b/Documentation/ABI/testing/sysfs-driver-iei-wt61p803-puzzle +@@ -0,0 +1,61 @@ ++What: /sys/bus/serial/devices/.../mac_address_* ++Date: September 2020 ++Contact: Luka Kovacic <luka.kovacic@sartura.hr> ++Description: (RW) Internal factory assigned MAC address values ++ ++What: /sys/bus/serial/devices/.../serial_number ++Date: September 2020 ++Contact: Luka Kovacic <luka.kovacic@sartura.hr> ++Description: (RW) Internal factory assigned serial number ++ ++What: /sys/bus/serial/devices/.../version ++Date: September 2020 ++Contact: Luka Kovacic <luka.kovacic@sartura.hr> ++Description: (RO) Internal MCU firmware version ++ ++What: /sys/bus/serial/devices/.../protocol_version ++Date: September 2020 ++Contact: Luka Kovacic <luka.kovacic@sartura.hr> ++Description: (RO) Internal MCU communication protocol version ++ ++What: /sys/bus/serial/devices/.../power_loss_recovery ++Date: September 2020 ++Contact: Luka Kovacic <luka.kovacic@sartura.hr> ++Description: (RW) Host platform power loss recovery settings ++ Value mapping: 0 - Always-On, 1 - Always-Off, 2 - Always-AC, 3 - Always-WA ++ ++What: /sys/bus/serial/devices/.../bootloader_mode ++Date: September 2020 ++Contact: Luka Kovacic <luka.kovacic@sartura.hr> ++Description: (RO) Internal MCU bootloader mode status ++ Value mapping: ++ 0 - normal mode ++ 1 - bootloader mode ++ ++What: /sys/bus/serial/devices/.../power_status ++Date: September 2020 ++Contact: Luka Kovacic <luka.kovacic@sartura.hr> ++Description: (RO) Power status indicates the host platform power on method. ++ Value mapping (bitwise list): ++ 0x80 - Null ++ 0x40 - Firmware flag ++ 0x20 - Power loss detection flag (powered off) ++ 0x10 - Power loss detection flag (AC mode) ++ 0x08 - Button power on ++ 0x04 - Wake-on-LAN power on ++ 0x02 - RTC alarm power on ++ 0x01 - AC recover power on ++ ++What: /sys/bus/serial/devices/.../build_info ++Date: September 2020 ++Contact: Luka Kovacic <luka.kovacic@sartura.hr> ++Description: (RO) Internal MCU firmware build date ++ Format: yyyy/mm/dd hh:mm ++ ++What: /sys/bus/serial/devices/.../ac_recovery_status ++Date: September 2020 ++Contact: Luka Kovacic <luka.kovacic@sartura.hr> ++Description: (RO) Host platform AC recovery status value ++ Value mapping: ++ 0 - board has not been recovered from power down ++ 1 - board has been recovered from power down diff --git a/pkgs/patches-linux-5.15/906-Documentation-hwmon-Add-iei-wt61p803-puzzle-hwmon-dr.patch b/pkgs/patches-linux-5.15/906-Documentation-hwmon-Add-iei-wt61p803-puzzle-hwmon-dr.patch new file mode 100644 index 0000000..9015436 --- /dev/null +++ b/pkgs/patches-linux-5.15/906-Documentation-hwmon-Add-iei-wt61p803-puzzle-hwmon-dr.patch @@ -0,0 +1,74 @@ +From 0aff3e5923fecc6842473ad07a688d6e2f2c2d55 Mon Sep 17 00:00:00 2001 +From: Luka Kovacic <luka.kovacic () sartura ! hr> +Date: Tue, 24 Aug 2021 12:44:37 +0000 +Subject: [PATCH 6/7] Documentation/hwmon: Add iei-wt61p803-puzzle hwmon driver + documentation + +Add the iei-wt61p803-puzzle driver hwmon driver interface documentation. + +Signed-off-by: Luka Kovacic <luka.kovacic@sartura.hr> +Signed-off-by: Pavo Banicevic <pavo.banicevic@sartura.hr> +Cc: Luka Perkov <luka.perkov@sartura.hr> +Cc: Robert Marko <robert.marko@sartura.hr> +--- + .../hwmon/iei-wt61p803-puzzle-hwmon.rst | 43 +++++++++++++++++++ + Documentation/hwmon/index.rst | 1 + + 2 files changed, 44 insertions(+) + create mode 100644 Documentation/hwmon/iei-wt61p803-puzzle-hwmon.rst + +--- /dev/null ++++ b/Documentation/hwmon/iei-wt61p803-puzzle-hwmon.rst +@@ -0,0 +1,43 @@ ++.. SPDX-License-Identifier: GPL-2.0-only ++ ++Kernel driver iei-wt61p803-puzzle-hwmon ++======================================= ++ ++Supported chips: ++ * IEI WT61P803 PUZZLE for IEI Puzzle M801 ++ ++ Prefix: 'iei-wt61p803-puzzle-hwmon' ++ ++Author: Luka Kovacic <luka.kovacic@sartura.hr> ++ ++ ++Description ++----------- ++ ++This driver adds fan and temperature sensor reading for some IEI Puzzle ++series boards. ++ ++Sysfs attributes ++---------------- ++ ++The following attributes are supported: ++ ++- IEI WT61P803 PUZZLE for IEI Puzzle M801 ++ ++/sys files in hwmon subsystem ++----------------------------- ++ ++================= == ===================================================== ++fan[1-5]_input RO files for fan speed (in RPM) ++pwm[1-2] RW files for fan[1-2] target duty cycle (0..255) ++temp[1-2]_input RO files for temperature sensors, in millidegree Celsius ++================= == ===================================================== ++ ++/sys files in thermal subsystem ++------------------------------- ++ ++================= == ===================================================== ++cur_state RW file for current cooling state of the cooling device ++ (0..max_state) ++max_state RO file for maximum cooling state of the cooling device ++================= == ===================================================== +--- a/Documentation/hwmon/index.rst ++++ b/Documentation/hwmon/index.rst +@@ -74,6 +74,7 @@ Hardware Monitoring Kernel Drivers + ibmaem + ibm-cffps + ibmpowernv ++ iei-wt61p803-puzzle-hwmon + ina209 + ina2xx + ina3221 diff --git a/pkgs/patches-linux-5.15/907-MAINTAINERS-Add-an-entry-for-the-IEI-WT61P803-PUZZLE.patch b/pkgs/patches-linux-5.15/907-MAINTAINERS-Add-an-entry-for-the-IEI-WT61P803-PUZZLE.patch new file mode 100644 index 0000000..14b928d --- /dev/null +++ b/pkgs/patches-linux-5.15/907-MAINTAINERS-Add-an-entry-for-the-IEI-WT61P803-PUZZLE.patch @@ -0,0 +1,41 @@ +From 12479baad28d2a08c6cb9e83471057635fa1635c Mon Sep 17 00:00:00 2001 +From: Luka Kovacic <luka.kovacic () sartura ! hr> +Date: Tue, 24 Aug 2021 12:44:38 +0000 +Subject: [PATCH 7/7] MAINTAINERS: Add an entry for the IEI WT61P803 PUZZLE + driver + +Add an entry for the IEI WT61P803 PUZZLE driver (MFD, HWMON, LED drivers). + +Signed-off-by: Luka Kovacic <luka.kovacic@sartura.hr> +Signed-off-by: Pavo Banicevic <pavo.banicevic@sartura.hr> +Cc: Luka Perkov <luka.perkov@sartura.hr> +Cc: Robert Marko <robert.marko@sartura.hr> +--- + MAINTAINERS | 16 ++++++++++++++++ + 1 file changed, 16 insertions(+) + +--- a/MAINTAINERS ++++ b/MAINTAINERS +@@ -9060,6 +9060,22 @@ F: include/net/nl802154.h + F: net/ieee802154/ + F: net/mac802154/ + ++IEI WT61P803 M801 MFD DRIVER ++M: Luka Kovacic <luka.kovacic@sartura.hr> ++M: Luka Perkov <luka.perkov@sartura.hr> ++M: Goran Medic <goran.medic@sartura.hr> ++L: linux-kernel@vger.kernel.org ++S: Maintained ++F: Documentation/ABI/stable/sysfs-driver-iei-wt61p803-puzzle ++F: Documentation/devicetree/bindings/hwmon/iei,wt61p803-puzzle-hwmon.yaml ++F: Documentation/devicetree/bindings/leds/iei,wt61p803-puzzle-leds.yaml ++F: Documentation/devicetree/bindings/mfd/iei,wt61p803-puzzle.yaml ++F: Documentation/hwmon/iei-wt61p803-puzzle-hwmon.rst ++F: drivers/hwmon/iei-wt61p803-puzzle-hwmon.c ++F: drivers/leds/leds-iei-wt61p803-puzzle.c ++F: drivers/mfd/iei-wt61p803-puzzle.c ++F: include/linux/mfd/iei-wt61p803-puzzle.h ++ + IFE PROTOCOL + M: Yotam Gigi <yotam.gi@gmail.com> + M: Jamal Hadi Salim <jhs@mojatatu.com> diff --git a/pkgs/patches-linux-5.15/910-drivers-leds-wt61p803-puzzle-improvements.patch b/pkgs/patches-linux-5.15/910-drivers-leds-wt61p803-puzzle-improvements.patch new file mode 100644 index 0000000..150a654 --- /dev/null +++ b/pkgs/patches-linux-5.15/910-drivers-leds-wt61p803-puzzle-improvements.patch @@ -0,0 +1,271 @@ +--- a/drivers/leds/leds-iei-wt61p803-puzzle.c ++++ b/drivers/leds/leds-iei-wt61p803-puzzle.c +@@ -9,9 +9,13 @@ + #include <linux/mfd/iei-wt61p803-puzzle.h> + #include <linux/mod_devicetable.h> + #include <linux/module.h> ++#include <linux/of.h> + #include <linux/platform_device.h> + #include <linux/property.h> + #include <linux/slab.h> ++#include <linux/workqueue.h> ++ ++#define IEI_LEDS_MAX 4 + + enum iei_wt61p803_puzzle_led_state { + IEI_LED_OFF = 0x30, +@@ -33,7 +37,11 @@ struct iei_wt61p803_puzzle_led { + struct iei_wt61p803_puzzle *mcu; + unsigned char response_buffer[IEI_WT61P803_PUZZLE_BUF_SIZE]; + struct mutex lock; /* mutex to protect led_power_state */ ++ struct work_struct work; + int led_power_state; ++ int id; ++ u8 blinking; ++ bool active_low; + }; + + static inline struct iei_wt61p803_puzzle_led *cdev_to_iei_wt61p803_puzzle_led +@@ -51,10 +59,18 @@ static int iei_wt61p803_puzzle_led_brigh + size_t reply_size; + int ret; + ++ if (priv->blinking) { ++ if (brightness == LED_OFF) ++ priv->blinking = 0; ++ else ++ return 0; ++ } ++ + led_power_cmd[0] = IEI_WT61P803_PUZZLE_CMD_HEADER_START; + led_power_cmd[1] = IEI_WT61P803_PUZZLE_CMD_LED; +- led_power_cmd[2] = IEI_WT61P803_PUZZLE_CMD_LED_POWER; +- led_power_cmd[3] = brightness == LED_OFF ? IEI_LED_OFF : IEI_LED_ON; ++ led_power_cmd[2] = IEI_WT61P803_PUZZLE_CMD_LED_SET(priv->id); ++ led_power_cmd[3] = ((brightness == LED_OFF) ^ priv->active_low) ? ++ IEI_LED_OFF : priv->blinking?priv->blinking:IEI_LED_ON; + + ret = iei_wt61p803_puzzle_write_command(priv->mcu, led_power_cmd, + sizeof(led_power_cmd), +@@ -90,39 +106,166 @@ static enum led_brightness iei_wt61p803_ + return led_state; + } + ++static void iei_wt61p803_puzzle_led_apply_blink(struct work_struct *work) ++{ ++ struct iei_wt61p803_puzzle_led *priv = container_of(work, struct iei_wt61p803_puzzle_led, work); ++ unsigned char led_blink_cmd[5] = {}; ++ unsigned char resp_buf[IEI_WT61P803_PUZZLE_BUF_SIZE]; ++ size_t reply_size; ++ ++ led_blink_cmd[0] = IEI_WT61P803_PUZZLE_CMD_HEADER_START; ++ led_blink_cmd[1] = IEI_WT61P803_PUZZLE_CMD_LED; ++ led_blink_cmd[2] = IEI_WT61P803_PUZZLE_CMD_LED_SET(priv->id); ++ led_blink_cmd[3] = priv->blinking; ++ ++ iei_wt61p803_puzzle_write_command(priv->mcu, led_blink_cmd, ++ sizeof(led_blink_cmd), ++ resp_buf, ++ &reply_size); ++ ++ return; ++} ++ ++static int iei_wt61p803_puzzle_led_set_blink(struct led_classdev *cdev, ++ unsigned long *delay_on, ++ unsigned long *delay_off) ++{ ++ struct iei_wt61p803_puzzle_led *priv = cdev_to_iei_wt61p803_puzzle_led(cdev); ++ u8 blink_mode = 0; ++ int ret = 0; ++ ++ /* set defaults */ ++ if (!*delay_on && !*delay_off) { ++ *delay_on = 500; ++ *delay_off = 500; ++ } ++ ++ /* minimum delay for soft-driven blinking is 100ms to keep load low */ ++ if (*delay_on < 100) ++ *delay_on = 100; ++ ++ if (*delay_off < 100) ++ *delay_off = 100; ++ ++ /* offload blinking to hardware, if possible */ ++ if (*delay_on != *delay_off) { ++ ret = -EINVAL; ++ } else if (*delay_on == 100) { ++ blink_mode = IEI_LED_BLINK_5HZ; ++ *delay_on = 100; ++ *delay_off = 100; ++ } else if (*delay_on <= 500) { ++ blink_mode = IEI_LED_BLINK_1HZ; ++ *delay_on = 500; ++ *delay_off = 500; ++ } else { ++ ret = -EINVAL; ++ } ++ ++ mutex_lock(&priv->lock); ++ priv->blinking = blink_mode; ++ mutex_unlock(&priv->lock); ++ ++ if (blink_mode) ++ schedule_work(&priv->work); ++ ++ return ret; ++} ++ ++ ++static int iei_wt61p803_puzzle_led_set_dt_default(struct led_classdev *cdev, ++ struct device_node *np) ++{ ++ const char *state; ++ int ret = 0; ++ ++ state = of_get_property(np, "default-state", NULL); ++ if (state) { ++ if (!strcmp(state, "on")) { ++ ret = ++ iei_wt61p803_puzzle_led_brightness_set_blocking( ++ cdev, cdev->max_brightness); ++ } else { ++ ret = iei_wt61p803_puzzle_led_brightness_set_blocking( ++ cdev, LED_OFF); ++ } ++ } ++ ++ return ret; ++} ++ + static int iei_wt61p803_puzzle_led_probe(struct platform_device *pdev) + { + struct device *dev = &pdev->dev; ++ struct device_node *np = dev_of_node(dev); ++ struct device_node *child; + struct iei_wt61p803_puzzle *mcu = dev_get_drvdata(dev->parent); + struct iei_wt61p803_puzzle_led *priv; +- struct led_init_data init_data = {}; +- struct fwnode_handle *child; + int ret; ++ u32 reg; + +- if (device_get_child_node_count(dev) != 1) ++ if (device_get_child_node_count(dev) > IEI_LEDS_MAX) + return -EINVAL; + +- priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); +- if (!priv) +- return -ENOMEM; +- +- priv->mcu = mcu; +- priv->led_power_state = 1; +- mutex_init(&priv->lock); +- dev_set_drvdata(dev, priv); +- +- child = device_get_next_child_node(dev, NULL); +- init_data.fwnode = child; +- +- priv->cdev.brightness_set_blocking = iei_wt61p803_puzzle_led_brightness_set_blocking; +- priv->cdev.brightness_get = iei_wt61p803_puzzle_led_brightness_get; +- priv->cdev.max_brightness = 1; ++ for_each_available_child_of_node(np, child) { ++ struct led_init_data init_data = {}; + +- ret = devm_led_classdev_register_ext(dev, &priv->cdev, &init_data); +- if (ret) +- dev_err(dev, "Could not register LED\n"); ++ ret = of_property_read_u32(child, "reg", ®); ++ if (ret) { ++ dev_err(dev, "Failed to read led 'reg' property\n"); ++ goto put_child_node; ++ } ++ ++ if (reg > IEI_LEDS_MAX) { ++ dev_err(dev, "Invalid led reg %u\n", reg); ++ ret = -EINVAL; ++ goto put_child_node; ++ } ++ ++ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); ++ if (!priv) { ++ ret = -ENOMEM; ++ goto put_child_node; ++ } ++ ++ mutex_init(&priv->lock); ++ ++ dev_set_drvdata(dev, priv); ++ ++ if (of_property_read_bool(child, "active-low")) ++ priv->active_low = true; ++ ++ priv->mcu = mcu; ++ priv->id = reg; ++ priv->led_power_state = 1; ++ priv->blinking = 0; ++ init_data.fwnode = of_fwnode_handle(child); ++ ++ priv->cdev.brightness_set_blocking = iei_wt61p803_puzzle_led_brightness_set_blocking; ++ priv->cdev.brightness_get = iei_wt61p803_puzzle_led_brightness_get; ++ priv->cdev.blink_set = iei_wt61p803_puzzle_led_set_blink; ++ ++ priv->cdev.max_brightness = 1; ++ ++ INIT_WORK(&priv->work, iei_wt61p803_puzzle_led_apply_blink); ++ ++ ret = iei_wt61p803_puzzle_led_set_dt_default(&priv->cdev, child); ++ if (ret) { ++ dev_err(dev, "Could apply default from DT\n"); ++ goto put_child_node; ++ } ++ ++ ret = devm_led_classdev_register_ext(dev, &priv->cdev, &init_data); ++ if (ret) { ++ dev_err(dev, "Could not register LED\n"); ++ goto put_child_node; ++ } ++ } ++ ++ return ret; + +- fwnode_handle_put(child); ++put_child_node: ++ of_node_put(child); + return ret; + } + +--- a/include/linux/mfd/iei-wt61p803-puzzle.h ++++ b/include/linux/mfd/iei-wt61p803-puzzle.h +@@ -36,7 +36,7 @@ + #define IEI_WT61P803_PUZZLE_CMD_FUNCTION_OTHER_POWER_LOSS 0x41 /* A */ + + #define IEI_WT61P803_PUZZLE_CMD_LED 0x52 /* R */ +-#define IEI_WT61P803_PUZZLE_CMD_LED_POWER 0x31 /* 1 */ ++#define IEI_WT61P803_PUZZLE_CMD_LED_SET(n) (0x30 | (n)) + + #define IEI_WT61P803_PUZZLE_CMD_TEMP 0x54 /* T */ + #define IEI_WT61P803_PUZZLE_CMD_TEMP_ALL 0x41 /* A */ +--- a/drivers/mfd/iei-wt61p803-puzzle.c ++++ b/drivers/mfd/iei-wt61p803-puzzle.c +@@ -176,6 +176,9 @@ static int iei_wt61p803_puzzle_recv_buf( + struct iei_wt61p803_puzzle *mcu = serdev_device_get_drvdata(serdev); + int ret; + ++ print_hex_dump_debug("puzzle-mcu rx: ", DUMP_PREFIX_NONE, ++ 16, 1, data, size, false); ++ + ret = iei_wt61p803_puzzle_process_resp(mcu, data, size); + /* Return the number of processed bytes if function returns error, + * discard the remaining incoming data, since the frame this data +@@ -246,6 +249,9 @@ int iei_wt61p803_puzzle_write_command(st + + cmd[size - 1] = iei_wt61p803_puzzle_checksum(cmd, size - 1); + ++ print_hex_dump_debug("puzzle-mcu tx: ", DUMP_PREFIX_NONE, ++ 16, 1, cmd, size, false); ++ + /* Initialize reply struct */ + reinit_completion(&mcu->reply->received); + mcu->reply->size = 0; diff --git a/pkgs/patches-linux-5.15/910-kobject_uevent.patch b/pkgs/patches-linux-5.15/910-kobject_uevent.patch new file mode 100644 index 0000000..c4c41ca --- /dev/null +++ b/pkgs/patches-linux-5.15/910-kobject_uevent.patch @@ -0,0 +1,32 @@ +From 0d37e6edc09c99e683dd91ca0e83bbc0df8477b3 Mon Sep 17 00:00:00 2001 +From: Felix Fietkau <nbd@nbd.name> +Date: Sun, 16 Jul 2017 16:56:10 +0200 +Subject: lib: add uevent_next_seqnum() + +Signed-off-by: Felix Fietkau <nbd@nbd.name> +--- + include/linux/kobject.h | 5 +++++ + lib/kobject_uevent.c | 37 +++++++++++++++++++++++++++++++++++++ + 2 files changed, 42 insertions(+) + +--- a/lib/kobject_uevent.c ++++ b/lib/kobject_uevent.c +@@ -179,6 +179,18 @@ out: + return r; + } + ++u64 uevent_next_seqnum(void) ++{ ++ u64 seq; ++ ++ mutex_lock(&uevent_sock_mutex); ++ seq = ++uevent_seqnum; ++ mutex_unlock(&uevent_sock_mutex); ++ ++ return seq; ++} ++EXPORT_SYMBOL_GPL(uevent_next_seqnum); ++ + /** + * kobject_synth_uevent - send synthetic uevent with arguments + * diff --git a/pkgs/patches-linux-5.15/911-kobject_add_broadcast_uevent.patch b/pkgs/patches-linux-5.15/911-kobject_add_broadcast_uevent.patch new file mode 100644 index 0000000..a487d55 --- /dev/null +++ b/pkgs/patches-linux-5.15/911-kobject_add_broadcast_uevent.patch @@ -0,0 +1,76 @@ +From 0d37e6edc09c99e683dd91ca0e83bbc0df8477b3 Mon Sep 17 00:00:00 2001 +From: Felix Fietkau <nbd@nbd.name> +Date: Sun, 16 Jul 2017 16:56:10 +0200 +Subject: lib: add uevent_next_seqnum() + +Signed-off-by: Felix Fietkau <nbd@nbd.name> +--- + include/linux/kobject.h | 5 +++++ + lib/kobject_uevent.c | 37 +++++++++++++++++++++++++++++++++++++ + 2 files changed, 42 insertions(+) + +--- a/include/linux/kobject.h ++++ b/include/linux/kobject.h +@@ -32,6 +32,8 @@ + #define UEVENT_NUM_ENVP 64 /* number of env pointers */ + #define UEVENT_BUFFER_SIZE 2048 /* buffer for the variables */ + ++struct sk_buff; ++ + #ifdef CONFIG_UEVENT_HELPER + /* path to the userspace helper executed on an event */ + extern char uevent_helper[]; +@@ -244,4 +246,7 @@ int kobject_synth_uevent(struct kobject + __printf(2, 3) + int add_uevent_var(struct kobj_uevent_env *env, const char *format, ...); + ++int broadcast_uevent(struct sk_buff *skb, __u32 pid, __u32 group, ++ gfp_t allocation); ++ + #endif /* _KOBJECT_H_ */ +--- a/lib/kobject_uevent.c ++++ b/lib/kobject_uevent.c +@@ -691,6 +691,43 @@ int add_uevent_var(struct kobj_uevent_en + EXPORT_SYMBOL_GPL(add_uevent_var); + + #if defined(CONFIG_NET) ++int broadcast_uevent(struct sk_buff *skb, __u32 pid, __u32 group, ++ gfp_t allocation) ++{ ++ struct uevent_sock *ue_sk; ++ int err = 0; ++ ++ /* send netlink message */ ++ mutex_lock(&uevent_sock_mutex); ++ list_for_each_entry(ue_sk, &uevent_sock_list, list) { ++ struct sock *uevent_sock = ue_sk->sk; ++ struct sk_buff *skb2; ++ ++ skb2 = skb_clone(skb, allocation); ++ if (!skb2) ++ break; ++ ++ err = netlink_broadcast(uevent_sock, skb2, pid, group, ++ allocation); ++ if (err) ++ break; ++ } ++ mutex_unlock(&uevent_sock_mutex); ++ ++ kfree_skb(skb); ++ return err; ++} ++#else ++int broadcast_uevent(struct sk_buff *skb, __u32 pid, __u32 group, ++ gfp_t allocation) ++{ ++ kfree_skb(skb); ++ return 0; ++} ++#endif ++EXPORT_SYMBOL_GPL(broadcast_uevent); ++ ++#if defined(CONFIG_NET) + static int uevent_net_broadcast(struct sock *usk, struct sk_buff *skb, + struct netlink_ext_ack *extack) + { diff --git a/pkgs/patches-linux-5.15/920-device_tree_cmdline.patch b/pkgs/patches-linux-5.15/920-device_tree_cmdline.patch new file mode 100644 index 0000000..d1f36e7 --- /dev/null +++ b/pkgs/patches-linux-5.15/920-device_tree_cmdline.patch @@ -0,0 +1,21 @@ +From e08bcbbaa52fcc41f02743fd2e62a33255ce52da Mon Sep 17 00:00:00 2001 +From: OpenWrt community <openwrt-devel@lists.openwrt.org> +Date: Wed, 13 Jul 2022 13:52:28 +0200 +Subject: [PATCH] of/ftd: add device tree cmdline + +--- + drivers/of/fdt.c | 3 +++ + 1 file changed, 3 insertions(+) + +--- a/drivers/of/fdt.c ++++ b/drivers/of/fdt.c +@@ -1158,6 +1158,9 @@ int __init early_init_dt_scan_chosen(uns + p = of_get_flat_dt_prop(node, "bootargs", &l); + if (p != NULL && l > 0) + strlcpy(data, p, min(l, COMMAND_LINE_SIZE)); ++ p = of_get_flat_dt_prop(node, "bootargs-append", &l); ++ if (p != NULL && l > 0) ++ strlcat(data, p, min_t(int, strlen(data) + (int)l, COMMAND_LINE_SIZE)); + + /* + * CONFIG_CMDLINE is meant to be a default in case nothing else diff --git a/pkgs/patches-linux-5.15/920-mangle_bootargs.patch b/pkgs/patches-linux-5.15/920-mangle_bootargs.patch new file mode 100644 index 0000000..dbcd6a5 --- /dev/null +++ b/pkgs/patches-linux-5.15/920-mangle_bootargs.patch @@ -0,0 +1,71 @@ +From: Imre Kaloz <kaloz@openwrt.org> +Subject: init: add CONFIG_MANGLE_BOOTARGS and disable it by default + +Enabling this option renames the bootloader supplied root= +and rootfstype= variables, which might have to be know but +would break the automatisms OpenWrt uses. + +Signed-off-by: Imre Kaloz <kaloz@openwrt.org> +--- + init/Kconfig | 9 +++++++++ + init/main.c | 24 ++++++++++++++++++++++++ + 2 files changed, 33 insertions(+) + +--- a/init/Kconfig ++++ b/init/Kconfig +@@ -1810,6 +1810,15 @@ config EMBEDDED + an embedded system so certain expert options are available + for configuration. + ++config MANGLE_BOOTARGS ++ bool "Rename offending bootargs" ++ depends on EXPERT ++ help ++ Sometimes the bootloader passed bogus root= and rootfstype= ++ parameters to the kernel, and while you want to ignore them, ++ you need to know the values f.e. to support dual firmware ++ layouts on the flash. ++ + config HAVE_PERF_EVENTS + bool + help +--- a/init/main.c ++++ b/init/main.c +@@ -615,6 +615,29 @@ static inline void setup_nr_cpu_ids(void + static inline void smp_prepare_cpus(unsigned int maxcpus) { } + #endif + ++#ifdef CONFIG_MANGLE_BOOTARGS ++static void __init mangle_bootargs(char *command_line) ++{ ++ char *rootdev; ++ char *rootfs; ++ ++ rootdev = strstr(command_line, "root=/dev/mtdblock"); ++ ++ if (rootdev) ++ strncpy(rootdev, "mangled_rootblock=", 18); ++ ++ rootfs = strstr(command_line, "rootfstype"); ++ ++ if (rootfs) ++ strncpy(rootfs, "mangled_fs", 10); ++ ++} ++#else ++static void __init mangle_bootargs(char *command_line) ++{ ++} ++#endif ++ + /* + * We need to store the untouched command line for future reference. + * We also need to store the touched command line since the parameter +@@ -955,6 +978,7 @@ asmlinkage __visible void __init __no_sa + pr_notice("%s", linux_banner); + early_security_init(); + setup_arch(&command_line); ++ mangle_bootargs(command_line); + setup_boot_config(); + setup_command_line(command_line); + setup_nr_cpu_ids(); diff --git a/pkgs/patches-linux-5.15/930-Revert-Revert-Revert-driver-core-Set-fw_devlink-on-b.patch b/pkgs/patches-linux-5.15/930-Revert-Revert-Revert-driver-core-Set-fw_devlink-on-b.patch new file mode 100644 index 0000000..4f4d6c7 --- /dev/null +++ b/pkgs/patches-linux-5.15/930-Revert-Revert-Revert-driver-core-Set-fw_devlink-on-b.patch @@ -0,0 +1,30 @@ +From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <rafal@milecki.pl> +Date: Tue, 19 Jul 2022 06:17:48 +0200 +Subject: [PATCH] Revert "Revert "Revert "driver core: Set fw_devlink=on by + default""" +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +This reverts commit ea718c699055c8566eb64432388a04974c43b2ea. + +With of_platform_populate() called for MTD partitions that commit breaks +probing devices which reference MTD in device tree. + +Link: https://lore.kernel.org/all/696cb2da-20b9-b3dd-46d9-de4bf91a1506@gmail.com/T/#u +Signed-off-by: Rafał Miłecki <rafal@milecki.pl> +--- + drivers/base/core.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/drivers/base/core.c ++++ b/drivers/base/core.c +@@ -1562,7 +1562,7 @@ static void device_links_purge(struct de + #define FW_DEVLINK_FLAGS_RPM (FW_DEVLINK_FLAGS_ON | \ + DL_FLAG_PM_RUNTIME) + +-static u32 fw_devlink_flags = FW_DEVLINK_FLAGS_ON; ++static u32 fw_devlink_flags = FW_DEVLINK_FLAGS_PERMISSIVE; + static int __init fw_devlink_setup(char *arg) + { + if (!arg) |