From f71bba55d02d294c9e87a331a03c50e5ac51f3cf Mon Sep 17 00:00:00 2001 From: Christian Marangi Date: Tue, 18 Oct 2022 22:26:34 +0200 Subject: [PATCH] generic: copy backport, hack, pending patch and config from 5.15 to 6.1 Copy backport, hack, pending patch and config from 5.15 to 6.1. Signed-off-by: Christian Marangi --- ...ild-use-Wdeclaration-after-statement.patch | 73 + ...05-v5.17-02-Kbuild-move-to-std-gnu11.patch | 60 + ...-use-std-gnu11-for-KBUILD_USERCFLAGS.patch | 43 + ...-x86-arm64-add-arch_has_hw_pte_young.patch | 169 + ...dd-CONFIG_ARCH_HAS_NONLEAF_PMD_YOUNG.patch | 111 + ...-03-mm-vmscan.c-refactor-shrink_node.patch | 224 + ...-mm-multigenerational-lru-groundwork.patch | 996 +++ ...multigenerational-lru-mm_struct-list.patch | 760 ++ ....1-06-mm-multigenerational-lru-aging.patch | 1176 +++ ...07-mm-multigenerational-lru-eviction.patch | 1002 +++ ...multigenerational-lru-user-interface.patch | 496 ++ ...-09-mm-multigenerational-lru-Kconfig.patch | 80 + ...-multigenerational-lru-documentation.patch | 161 + ...don-t-sync-disk-for-each-aging-cycle.patch | 32 + ...-uasm-Enable-muhu-opcode-for-MIPS-R6.patch | 65 + ...rkaround-for-Loongson-2F-nop-CPU-err.patch | 31 + ...ips-bpf-Add-eBPF-JIT-for-32-bit-MIPS.patch | 3078 +++++++ ...bpf-Add-new-eBPF-JIT-for-64-bit-MIPS.patch | 1005 +++ ...f-Add-JIT-workarounds-for-CPU-errata.patch | 120 + ...0-v5.16-05-mips-bpf-Enable-eBPF-JITs.patch | 61 + ...f-Remove-old-BPF-JIT-implementations.patch | 387 + ...to-define-reg_update_bits-for-no-bus.patch | 52 + ...63xx-use-more-precise-Kconfig-symbol.patch | 37 + ...resolve_btfids-Build-with-host-flags.patch | 49 + ...te-to-upstream-version-v1.6.1-19-g0a.patch | 997 +++ ...or-setting-affinity-if-no-IRQ-parent.patch | 48 + ...-Support-reserved-memory-description.patch | 166 + ...kernel-proc-add-CPU-option-reporting.patch | 162 + ...mp_processor_id-in-preemptible-in-sh.patch | 62 + ...spinand-Add-support-for-XTX-XT26G0xA.patch | 186 + ...mvebu-a3700-comphy-Remove-port-from-.patch | 219 + ...mvebu-a3700-comphy-Add-native-kernel.patch | 1552 ++++ ...l-armada-37xx-Add-xtal-clock-to-comp.patch | 32 + ...mvebu-Make-SATA-PHY-optional-for-Arm.patch | 64 + ...xhci-mvebu-make-USB-3.0-PHY-optional.patch | 166 + ...ark-Fix-initialization-with-old-Marv.patch | 39 + ...mvebu-a3700-comphy-Remove-broken-res.patch | 194 + ...platform_populate-for-MTD-partitions.patch | 72 + ...s-add-support-for-Sercomm-partitions.patch | 302 + ...ce-of-support-for-dynamic-partitions.patch | 106 + ...g-MTD-device-associated-with-a-speci.patch | 72 + ...e-check-partition-before-dereference.patch | 30 + ...sing-of_node_get-in-dynamic-partitio.patch | 101 + ...y-a-bit-code-find-partition-matching.patch | 65 + ...find-OF-node-for-every-MTD-partition.patch | 84 + ...-Don-t-print-error-message-on-EPROBE.patch | 32 + ...x-allow-to-use-on-MediaTek-MIPS-SoCs.patch | 33 + ...device-add-support-for-GD5FxGQ4xExxG.patch | 58 + ...device-add-support-for-GD5F1GQ5RExxG.patch | 33 + ...device-add-support-for-GD5F-2-4-GQ5x.patch | 84 + ...device-add-support-for-GD5FxGM7xExxG.patch | 91 + ...TP-Link-SafeLoader-partitions-table-.patch | 229 + ...v5.18-page_pool-Add-allocation-stats.patch | 165 + ...01-v5.18-page_pool-Add-recycle-stats.patch | 140 + ...d-function-to-batch-and-return-stats.patch | 77 + ...cycle-stats-to-page_pool_put_page_bu.patch | 55 + ...et-page_pool-introduce-ethtool-stats.patch | 147 + ...ce-flags-field-in-xdp_buff-xdp_frame.patch | 99 + ...gs-support-to-xdp_return_-buff-frame.patch | 137 + ...ize-metadata-to-skb_shared_info-for-.patch | 31 + ...-total-xdp_frame-len-running-ndo_xdp.patch | 65 + ...-veth-Allow-jumbo-frames-in-xdp-mode.patch | 40 + ...uce-tagger-owned-storage-for-private.patch | 279 + ...ocols-connect-to-individual-switches.patch | 274 + ..._eth_soc-add-support-for-coherent-DM.patch | 327 + ...ek-mt7622-add-support-for-coherent-D.patch | 30 + ..._eth_soc-add-support-for-Wireless-Et.patch | 1679 ++++ ..._eth_soc-implement-flow-offloading-t.patch | 269 + ...ek-mt7622-introduce-nodes-for-Wirele.patch | 62 + ..._eth_soc-add-ipv6-flow-offload-suppo.patch | 79 + ..._eth_soc-support-TC_SETUP_BLOCK-for-.patch | 29 + ..._eth_soc-allocate-struct-mtk_ppe-sep.patch | 159 + ..._eth_soc-rework-hardware-flow-table-.patch | 424 + ..._eth_soc-remove-bridge-flow-offload-.patch | 44 + ..._eth_soc-support-creating-mac-addres.patch | 553 ++ ..._eth_soc-wed-fix-sparse-endian-warni.patch | 56 + ..._eth_soc-fix-return-value-check-in-m.patch | 25 + ..._eth_soc-use-standard-property-for-c.patch | 35 + ..._eth_soc-use-after-free-in-__mtk_ppe.patch | 33 + ..._eth_soc-add-check-for-allocation-fa.patch | 22 + ...silence-the-GCC-12-array-bounds-warn.patch | 26 + ..._eth_soc-rely-on-GFP_KERNEL-for-dma_.patch | 52 + ..._eth_soc-move-tx-dma-desc-configurat.patch | 206 + ..._eth_soc-add-txd_size-to-mtk_soc_dat.patch | 167 + ..._eth_soc-rely-on-txd_size-in-mtk_tx_.patch | 78 + ..._eth_soc-rely-on-txd_size-in-mtk_des.patch | 109 + ..._eth_soc-rely-on-txd_size-in-txd_to_.patch | 39 + ..._eth_soc-add-rxd_size-to-mtk_soc_dat.patch | 102 + ..._eth_soc-rely-on-txd_size-field-in-m.patch | 46 + ..._eth_soc-rely-on-rxd_size-field-in-m.patch | 68 + ..._eth_soc-introduce-device-register-m.patch | 814 ++ ..._eth_soc-introduce-MTK_NETSYS_V2-sup.patch | 917 ++ ..._eth_soc-convert-ring-dma-pointer-to.patch | 135 + ..._eth_soc-convert-scratch_ring-pointe.patch | 33 + ..._eth_soc-introduce-support-for-mt798.patch | 138 + ..._eth_soc-fix-error-code-in-mtk_flow_.patch | 25 + ..._eth_soc-enable-rx-cksum-offload-for.patch | 47 + ...19-34-eth-mtk_ppe-fix-up-after-merge.patch | 28 + ...-users-of-bitmap_foo-to-linkmode_foo.patch | 948 +++ ...ylink-add-MAC-phy_interface_t-bitmap.patch | 24 + ...supported_interfaces-for-phylink-val.patch | 98 + ...populate-supported_interfaces-member.patch | 63 + ...net-dsa-consolidate-phylink-creation.patch | 149 + ...phylink_get_interfaces-with-phylink_.patch | 51 + ...d-support-for-phylink-mac_select_pcs.patch | 59 + ...y-add-phy_interface_t-bitmap-support.patch | 61 + ...mac_select_pcs-method-to-phylink_mac.patch | 197 + ...-add-generic-validate-implementation.patch | 341 + ...e-helpers-for-iterating-through-port.patch | 68 + ...-net-phylink-add-pcs_validate-method.patch | 106 + ...k-add-legacy_pre_march2020-indicator.patch | 43 + ...-DSA-phylink-as-legacy_pre_march2020.patch | 36 + ...net-phylink-use-legacy_pre_march2020.patch | 115 + ...-fix-DSA-mac_select_pcs-introduction.patch | 88 + ...populate-supported_interfaces-member.patch | 48 + ...e-interface-checks-in-mvneta_validat.patch | 35 + ...op-use-of-phylink_helper_basex_speed.patch | 55 + ...-mvneta-use-phylink_generic_validate.patch | 72 + ...ark-as-a-legacy_pre_march2020-driver.patch | 29 + ...populate-supported_interfaces-member.patch | 43 + ...remove-interface-checks-in-mtk_valid.patch | 75 + ...drop-use-of-phylink_helper_basex_spe.patch | 42 + ...eth_soc-use-phylink_generic_validate.patch | 84 + ...mark-as-a-legacy_pre_march2020-drive.patch | 29 + ...remove-a-copy-of-the-NAPI_POLL_WEIGH.patch | 40 + ...9-mtk_eth_soc-remove-unused-mac-mode.patch | 35 + ...tk_eth_soc-remove-unused-sgmii-flags.patch | 40 + ...add-mask-and-update-PCS-speed-defini.patch | 40 + ...eth_soc-correct-802.3z-speed-setting.patch | 60 + ...th_soc-correct-802.3z-duplex-setting.patch | 101 + ...stop-passing-phylink-state-to-sgmii-.patch | 60 + ...mtk_eth_soc-provide-mtk_sgmii_config.patch | 89 + ...add-fixme-comment-for-state-speed-us.patch | 38 + ...c-move-MAC_MCR-setting-to-mac_finish.patch | 79 + ...move-restoration-of-SYSCFG0-to-mac_f.patch | 57 + ...convert-code-structure-to-suit-split.patch | 254 + ...soc-partially-convert-to-phylink_pcs.patch | 262 + ...terate-using-dsa_switch_for_each_use.patch | 106 + ...opulate-supported_interfaces-and-mac.patch | 166 + ...t-dsa-mt7530-remove-interface-checks.patch | 172 + ...rop-use-of-phylink_helper_basex_spee.patch | 34 + ...nly-indicate-linkmodes-that-can-be-s.patch | 86 + ...-switch-to-use-phylink_get_linkmodes.patch | 131 + ...530-partially-convert-to-phylink_pcs.patch | 385 + ...ove-autoneg-handling-to-PCS-validati.patch | 80 + ...19-net-dsa-mt7530-mark-as-non-legacy.patch | 34 + ...mt753x-fix-pcs-conversion-regression.patch | 116 + ...t7530-rework-mt7530_hw_vlan_-add-del.patch | 87 + ...et-dsa-mt7530-rework-mt753-01-_setup.patch | 75 + ...et-cpu-port-via-dp-cpu_dp-instead-of.patch | 117 + ..._eth_soc-rely-on-page_pool-for-singl.patch | 330 + ...et-mtk_eth_soc-add-basic-XDP-support.patch | 291 + ..._eth_soc-introduce-xdp-ethtool-count.patch | 110 + ...net-mtk_eth_soc-add-xmit-XDP-support.patch | 340 + ..._eth_soc-add-support-for-page_pool_g.patch | 95 + ..._eth_soc-introduce-mtk_xdp_frame_map.patch | 113 + ..._eth_soc-introduce-xdp-multi-frag-su.patch | 218 + ..._eth_soc-fix-hw-hash-reporting-for-M.patch | 74 + ..._eth_soc-fix-off-by-one-check-of-ARR.patch | 31 + ..._ppe-fix-possible-NULL-pointer-deref.patch | 27 + ...-ppe-fix-traffic-offload-with-bridge.patch | 64 + ..._eth_soc-remove-mtk_foe_entry_timest.patch | 33 + ..._eth_soc-enable-XDP-support-just-for.patch | 35 + ..._eth_soc-move-gdma_to_ppe-and-ppe_ba.patch | 127 + ..._eth_soc-move-ppe-table-hash-offset-.patch | 199 + ..._eth_soc-add-the-capability-to-run-m.patch | 317 + ..._eth_soc-move-wdma_base-definitions-.patch | 80 + ..._eth_soc-add-foe_entry_size-to-mtk_e.patch | 251 + ..._eth_soc-fix-typo-in-__mtk_foe_entry.patch | 27 + ..._eth_soc-check-max-allowed-value-in-.patch | 28 + ..._eth_wed-add-mtk_wed_configure_irq-a.patch | 189 + ..._eth_wed-add-wed-support-for-mt7986-.patch | 942 ++ ...rnet-mtk_eth_wed-add-axi-bus-support.patch | 237 + ..._eth_soc-introduce-flow-offloading-s.patch | 882 ++ ..._eth_soc-enable-flow-offloading-supp.patch | 26 + ..._eth_soc-fix-wrong-use-of-new-helper.patch | 46 + ..._eth_soc-fix-usage-of-foe_entry_size.patch | 49 + ...-0001-net-bgmac-improve-handling-PHY.patch | 84 + ...t-bgmac-support-MDIO-described-in-DT.patch | 54 + ...dd-support-for-qca-8327-internal-phy.patch | 48 + ...3-Include-all-ports-in-enabled_ports.patch | 131 + ...-BCM5301x-workaround-for-a-wrong-CPU.patch | 42 + ...prove-flow-control-setup-on-BCM5301x.patch | 32 + ...t-dsa-b53-Drop-unused-cpu_port-field.patch | 205 + ...x-add-support-for-qca-8327-A-variant.patch | 65 + ...dd-resume-suspend-function-to-qca83x.patch | 45 + ...ix-spacing-and-improve-name-for-83xx.patch | 95 + ...hy-at803x-fix-resume-for-QCA8327-phy.patch | 131 + ...x-add-DAC-amplitude-fix-for-8327-phy.patch | 91 + ...nable-prefer-master-for-83xx-interna.patch | 27 + ...hy-at803x-better-describe-debug-regs.patch | 127 + ...-dsa-qca8k-add-mac-power-sel-support.patch | 80 + ...dsa-qca8k-Add-SGMII-clock-phase-prop.patch | 30 + ...k-add-support-for-sgmii-falling-edge.patch | 127 + ...dsa-qca8k-Document-support-for-CPU-p.patch | 29 + ...dsa-qca8k-add-support-for-cpu-port-6.patch | 153 + ...work-rgmii-delay-logic-and-scan-for-.patch | 295 + ...dsa-qca8k-Document-qca-sgmii-enable-.patch | 33 + ...-qca8k-add-explicit-SGMII-PLL-enable.patch | 65 + ...dsa-qca8k-Document-qca-led-open-drai.patch | 37 + ...qca8k-add-support-for-pws-config-reg.patch | 92 + ...dsa-qca8k-document-support-for-qca83.patch | 32 + ...et-dsa-qca8k-add-support-for-QCA8328.patch | 78 + ...8k-set-internal-delay-also-for-sgmii.patch | 159 + ...move-port-config-to-dedicated-struct.patch | 124 + ...ipq8064-mdio-fix-warning-with-new-qc.patch | 26 + ...net-dsa-qca8k-convert-to-YAML-schema.patch | 631 ++ ...elay-applied-to-wrong-cpu-in-parse-p.patch | 28 + ...for-loop-in-setup-and-add-cpu-port-c.patch | 151 + ...sure-pad0-mac06-exchange-is-disabled.patch | 47 + ...ernal-delay-applied-to-the-wrong-PAD.patch | 48 + ...16-net-dsa-qca8k-fix-MTU-calculation.patch | 46 + ...redundant-check-in-parse_port_config.patch | 29 + ...vert-to-GENMASK_FIELD_PREP_FIELD_GET.patch | 507 ++ ...move-extra-mutex_init-in-qca8k_setup.patch | 25 + ...move-regmap-init-in-probe-and-set-it.patch | 46 + ...k-initial-conversion-to-regmap-heper.patch | 249 + ...ca8k-add-additional-MIB-counter-and-.patch | 120 + ...ca8k-add-support-for-port-fast-aging.patch | 53 + ...sa-qca8k-add-set_ageing_time-support.patch | 78 + ...sa-qca8k-add-support-for-mdb_add-del.patch | 142 + ...sa-qca8k-add-support-for-mirror-mode.patch | 155 + ...t-next-net-dsa-qca8k-add-LAG-support.patch | 288 + ...dsa-qca8k-fix-warning-in-LAG-feature.patch | 40 + ...PHY-initialization-with-MTU-setup-in.patch | 52 + ...nl_lock-sections-in-dsa_slave_create.patch | 34 + ...op-updating-master-MTU-from-master.c.patch | 91 + ...l_mutex-when-calling-dsa_master_-set.patch | 78 + ...t-up-shared-ports-then-non-shared-po.patch | 118 + ...xt-net-dsa-setup-master-before-ports.patch | 115 + ...switch-operations-for-tracking-the-m.patch | 254 + ...aster-state-events-in-dsa_tree_-setu.patch | 89 + ...t-dsa-tag_qca-convert-to-FIELD-macro.patch | 86 + ...qca-move-define-to-include-linux-dsa.patch | 71 + ...ag_qca-enable-promisc_on_master-flag.patch | 27 + ...add-define-for-handling-mgmt-Etherne.patch | 110 + ...a-add-define-for-handling-MIB-packet.patch | 45 + ...add-support-for-handling-mgmt-and-MI.patch | 116 + ...8k-add-tracking-state-of-master-port.patch | 67 + ...d-support-for-mgmt-read-write-in-Eth.patch | 363 + ...d-support-for-mib-autocast-in-Ethern.patch | 226 + ...d-support-for-phy-read-write-with-mg.patch | 287 + ...qca8k-move-page-cache-to-driver-priv.patch | 208 + ...qca8k-cache-lo-and-hi-for-mdio-write.patch | 164 + ...d-support-for-larger-read-write-size.patch | 206 + ...troduce-qca8k_bulk_read-write-functi.patch | 104 + ...eck-correct-variable-in-qca8k_phy_et.patch | 28 + ...dsa-qca8k-fix-noderef.cocci-warnings.patch | 34 + ...8k-drop-MTU-tracking-from-qca8k_priv.patch | 79 + ...-qca8k-drop-port_sts-from-qca8k_priv.patch | 116 + ...8k-rework-and-simplify-mdiobus-logic.patch | 173 + ...-drop-dsa_switch_ops-from-qca8k_priv.patch | 39 + ...a8k-correctly-handle-mdio-read-error.patch | 33 + ...ify-bus-id-naming-with-legacy-and-OF.patch | 44 + ...net-dsa-qca8k-move-driver-to-qca-dir.patch | 7389 ++++++++++++++++ ...-cache-match-data-to-speed-up-access.patch | 157 + ...k-make-mib-autocast-feature-optional.patch | 77 + ...qca8k-move-mib-struct-to-common-code.patch | 6532 ++++++++++++++ ...ve-qca8k-read-write-rmw-and-reg-tabl.patch | 135 + ...ve-qca8k-bulk-read-write-helper-to-c.patch | 145 + ...ove-mib-init-function-to-common-code.patch | 137 + ...ve-port-set-status-eee-ethtool-stats.patch | 281 + ...move-bridge-functions-to-common-code.patch | 237 + ...ve-set-age-MTU-port-enable-disable-f.patch | 227 + ...ve-port-FDB-MDB-function-to-common-c.patch | 704 ++ ...ve-port-mirror-functions-to-common-c.patch | 232 + ...ve-port-VLAN-functions-to-common-cod.patch | 448 + ...ve-port-LAG-functions-to-common-code.patch | 384 + ...ve-read_switch_id-function-to-common.patch | 102 + ...x-NULL-pointer-dereference-for-of_de.patch | 29 + ...N-filtering-syncing-out-of-dsa_switc.patch | 77 + ...cross-chip-syncing-of-VLAN-filtering.patch | 52 + ...-rtl8366rb-Support-bridge-offloading.patch | 141 + ...-dsa-rtl8366-Drop-custom-VLAN-set-up.patch | 118 + ...b-Rewrite-weird-VLAN-filering-enable.patch | 270 + ...-Drop-and-depromote-pointless-prints.patch | 51 + ...tl8366rb-Use-core-filtering-tracking.patch | 61 + ...rtl8366rb-Support-disabling-learning.patch | 115 + ...net-dsa-rtl8366rb-Support-fast-aging.patch | 57 + ...-rtl8366rb-Support-setting-STP-state.patch | 107 + ...icro-stmmac-move-queue-reset-to-dedi.patch | 142 + ...icro-stmmac-first-disable-all-queues.patch | 37 + ...icro-stmmac-move-dma-conf-to-dedicat.patch | 1289 +++ ...icro-stmmac-generate-stmmac-dma-conf.patch | 1161 +++ ...icro-stmmac-permit-MTU-change-with-i.patch | 73 + ...x-inband-mgmt-for-big-endian-systems.patch | 156 + ...x-ethtool-autocast-mib-for-big-endia.patch | 81 + ...eric-Introduce-Sierra-EM919X-support.patch | 80 + ...ost-always-print-detected-modem-name.patch | 37 + ...-add-support-for-in-band-link-status.patch | 130 + ...e-implement-soft-state-polling-setup.patch | 98 + ...t-sfp-move-quirk-handling-into-sfp.c.patch | 291 + ...move-Alcatel-Lucent-3FE46541AA-fixup.patch | 69 + ....1-net-sfp-move-Huawei-MA5671A-fixup.patch | 48 + ...t-sfp-add-support-for-HALNy-GPON-SFP.patch | 79 + ..._eth_soc-fix-mask-of-RX_DMA_GET_SPOR.patch | 32 + ..._eth_soc-fix-state-in-__mtk_foe_entr.patch | 37 + ...s-add-Broadcom-s-BCM63138-controller.patch | 125 + ...-add-support-for-BCM63138-controller.patch | 356 + ...-leds-bcm63138-unify-full-stops-in-d.patch | 30 + ...help-info-about-BCM63138-module-name.patch | 28 + ...eds-leds-bcm63138-get-rid-of-LED_OFF.patch | 30 + ...-handling-U-Boot-environment-variabl.patch | 278 + ...-find-Device-Tree-nodes-for-NVMEM-ce.patch | 29 + ...em-u-boot-env-fix-crc32-casting-type.patch | 30 + ...support-passing-DT-node-in-cell-info.patch | 41 + ..._wwan-add-ZTE-MF286D-modem-19d2-1485.patch | 59 + ...Support-public-address-configuration.patch | 51 + ...Fix-application-of-sizeof-to-pointer.patch | 29 + ...Add-a-new-PID-VID-13d3-3567-for-MT79.patch | 70 + ...Add-a-new-PID-VID-0489-e0c8-for-MT79.patch | 68 + ...Add-a-new-VID-PID-0e8d-0608-for-MT79.patch | 66 + ...PS-ath79-drop-_machine_restart-again.patch | 49 + ....18-hwmon-lm70-Add-ti-tmp125-support.patch | 71 + ...ether-export-usbnet_cdc_zte_rx_fixup.patch | 58 + ...e-the-bogus-MAC-fixup-for-ZTE-device.patch | 118 + ...-scope-of-bogus-MAC-address-detectio.patch | 63 + target/linux/generic/config-6.1 | 7559 +++++++++++++++++ .../generic/hack-6.1/204-module_strip.patch | 212 + .../generic/hack-6.1/205-kconfig-exit.patch | 20 + .../hack-6.1/210-darwin_scripts_include.patch | 3053 +++++++ .../211-darwin-uuid-typedef-clash.patch | 22 + .../hack-6.1/212-tools_portability.patch | 110 + .../hack-6.1/214-spidev_h_portability.patch | 24 + .../hack-6.1/220-arm-gc_sections.patch | 123 + .../generic/hack-6.1/221-module_exports.patch | 126 + .../hack-6.1/230-openwrt_lzma_options.patch | 34 + .../hack-6.1/250-netfilter_depends.patch | 27 + .../linux/generic/hack-6.1/251-kconfig.patch | 199 + .../generic/hack-6.1/253-ksmbd-config.patch | 32 + .../generic/hack-6.1/259-regmap_dynamic.patch | 144 + .../260-crypto_test_dependencies.patch | 52 + .../hack-6.1/261-lib-arc4-unhide.patch | 24 + .../generic/hack-6.1/280-rfkill-stubs.patch | 84 + ...cache-use-more-efficient-cache-blast.patch | 64 + .../321-powerpc_crtsavres_prereq.patch | 38 + ...rans-call-add-disks-after-mtd-device.patch | 98 + .../410-block-fit-partition-parser.patch | 214 + .../420-mtd-set-rootfs-to-be-root-dev.patch | 39 + ...ers-add-nvmem-support-to-cmdlinepart.patch | 120 + .../hack-6.1/430-mtk-bmt-support.patch | 33 + .../generic/hack-6.1/600-bridge_offload.patch | 846 ++ ...of_net-add-mac-address-ascii-support.patch | 112 + ...lter-connmark-introduce-set-dscpmark.patch | 212 + ...-netfilter-add-xt_FLOWOFFLOAD-target.patch | 860 ++ .../hack-6.1/651-wireless_mesh_header.patch | 24 + .../hack-6.1/660-fq_codel_defaults.patch | 27 + ...t-size-the-hashtable-more-adequately.patch | 25 + .../700-swconfig_switch_drivers.patch | 131 + ...-dsa-mv88e6xxx-disable-ATU-violation.patch | 21 + .../hack-6.1/720-net-phy-add-aqr-phys.patch | 142 + .../721-net-add-packet-mangeling.patch | 178 + ...hy-aquantia-enable-AQR112-and-AQR412.patch | 154 + ...aquantia-fix-system-side-protocol-mi.patch | 34 + ...y-aquantia-Add-AQR113-driver-support.patch | 43 + ...ntia-add-PHY_IDs-for-AQR112-variants.patch | 63 + ...-r8152-add-LED-configuration-from-OF.patch | 74 + ...et-add-RTL8152-binding-documentation.patch | 54 + .../hack-6.1/773-bgmac-add-srab-switch.patch | 98 + .../780-usb-net-MeigLink_modem_support.patch | 43 + .../790-SFP-GE-T-ignore-TX_FAULT.patch | 65 + .../800-GPIO-add-named-gpio-exports.patch | 162 + .../hack-6.1/901-debloat_sock_diag.patch | 162 + .../generic/hack-6.1/902-debloat_proc.patch | 408 + .../hack-6.1/904-debloat_dma_buf.patch | 92 + .../generic/hack-6.1/910-kobject_uevent.patch | 32 + .../911-kobject_add_broadcast_uevent.patch | 76 + .../hack-6.1/920-device_tree_cmdline.patch | 21 + ...vert-driver-core-Set-fw_devlink-on-b.patch | 30 + ...include-asm-rwonce.h-for-kernel-code.patch | 29 + ...-Use-stddefs.h-instead-of-compiler.h.patch | 22 + ...s-negative-stack-offsets-on-stack-tr.patch | 57 + .../103-kbuild-export-SUBARCH.patch | 21 + ...e_mem_map-with-ARCH_PFN_OFFSET-calcu.patch | 82 + ...0-add-linux-spidev-compatible-si3210.patch | 18 + ...ame2-and-add-RENAME_WHITEOUT-support.patch | 81 + ...41-jffs2-add-RENAME_EXCHANGE-support.patch | 73 + .../142-jffs2-add-splice-ops.patch | 20 + ...ge_allow_receiption_on_disabled_port.patch | 45 + ...-rs5c372-support_alarms_up_to_1_week.patch | 94 + ...he_alarm_to_be_used_as_wakeup_source.patch | 70 + .../203-kallsyms_uncompressed.patch | 119 + .../205-backtrace_module_info.patch | 41 + ...e-filenames-from-deps_initramfs-list.patch | 30 + ...able_wilink_platform_without_drivers.patch | 20 + .../270-platform-mikrotik-build-bits.patch | 31 + .../300-mips_expose_boot_raw.patch | 40 + .../302-mips_no_branch_likely.patch | 22 + .../pending-6.1/305-mips_module_reloc.patch | 370 + .../pending-6.1/307-mips_highmem_offset.patch | 19 + .../pending-6.1/308-mips32r2_tune.patch | 22 + .../310-arm_module_unresolved_weak_sym.patch | 22 + ...t-command-line-parameters-from-users.patch | 283 + .../332-arc-add-OWRTDTB-section.patch | 84 + ...able-unaligned-access-in-kernel-mode.patch | 24 + ...ernel-XZ-compression-option-on-PPC_8.patch | 25 + .../400-mtd-mtdsplit-support.patch | 327 + ...support-for-minor-aligned-partitions.patch | 245 + .../pending-6.1/420-mtd-redboot_space.patch | 41 + ...30-mtd-add-myloader-partition-parser.patch | 229 + ...check-for-bad-blocks-when-calculatin.patch | 68 + ...bcm47xxpart-detect-T_Meter-partition.patch | 37 + ...mtd-add-routerbootpart-parser-config.patch | 38 + ...mtd-cfi_cmdset_0002-no-erase_suspend.patch | 25 + ...et_0002-add-buffer-write-cmd-timeout.patch | 17 + ...25p80-mx-disable-software-protection.patch | 18 + .../476-mtd-spi-nor-add-eon-en25q128.patch | 18 + .../479-mtd-spi-nor-add-xtx-xt25f128b.patch | 79 + ...r-add-support-for-Gigadevice-GD25D05.patch | 22 + .../482-mtd-spi-nor-add-gd25q512.patch | 21 + .../484-mtd-spi-nor-add-esmt-f25l16pa.patch | 23 + .../485-mtd-spi-nor-add-xmc-xm25qh128c.patch | 24 + ...nand-add-support-for-ESMT-F50x1G41LB.patch | 143 + ...nd-Add-support-for-Etron-EM73D044VCx.patch | 168 + ...mtd-device-named-ubi-or-data-on-boot.patch | 97 + ...to-create-ubiblock-device-for-rootfs.patch | 69 + ...ting-ubi0-rootfs-in-init-do_mounts.c.patch | 53 + ...ROOT_DEV-to-ubiblock-rootfs-if-unset.patch | 34 + .../494-mtd-ubi-add-EOF-marker-support.patch | 60 + ...-add-bindings-for-mtd-concat-devices.patch | 52 + ...cat-add-dt-driver-for-concat-devices.patch | 216 + ...i-nor-locking-support-for-MX25L6405D.patch | 34 + ...i-nor-disable-16-bit-sr-for-macronix.patch | 30 + .../500-fs_cdrom_dependencies.patch | 52 + .../530-jffs2_make_lzma_available.patch | 4581 ++++++++++ .../pending-6.1/532-jffs2_eofdetect.patch | 65 + .../600-netfilter_conntrack_flush.patch | 88 + ...etfilter_match_bypass_default_checks.patch | 110 + ...netfilter_match_bypass_default_table.patch | 106 + ...netfilter_match_reduce_memory_access.patch | 22 + ...-netfilter_optional_tcp_window_check.patch | 83 + ...del-do-not-defer-queue-length-update.patch | 86 + .../pending-6.1/630-packet_socket_type.patch | 138 + .../pending-6.1/655-increase_skb_pad.patch | 20 + ...Add-support-for-MAP-E-FMRs-mesh-mode.patch | 511 ++ ...ng-with-source-address-failed-policy.patch | 263 + ...nes-for-_POLICY_FAILED-until-all-cod.patch | 50 + ...T-skip-GRO-for-foreign-MAC-addresses.patch | 149 + ...et-add-mac-address-increment-support.patch | 89 + ...83-of_net-add-mac-address-to-of-tree.patch | 55 + ...t-do-mac-address-increment-only-once.patch | 31 + ...ow_offload-handle-netdevice-events-f.patch | 106 + ...net-mtk_eth_soc-enable-threaded-NAPI.patch | 41 + ...detach-callback-to-struct-phy_driver.patch | 38 + ...a-tag_mtk-add-padding-for-tx-packets.patch | 29 + ...d-knob-for-filtering-rx-tx-BPDU-pack.patch | 174 + ...rtl8221-allow-to-configure-SERDES-mo.patch | 101 + ...e-all-MACs-are-powered-down-before-r.patch | 28 + ...-net-mtk_sgmii-implement-mtk_pcs_ops.patch | 46 + ..._sgmii-fix-powering-up-the-SGMII-phy.patch | 39 + ...sure-the-SGMII-PHY-is-powered-down-o.patch | 65 + ...k_pcs_setup_mode_an-don-t-rely-on-re.patch | 31 + ...t-the-speed-according-to-the-phy-int.patch | 47 + .../729-net-mtk_eth_soc-improve-comment.patch | 22 + ...enable-PCS-polling-to-allow-SFP-work.patch | 23 + ...equest-assisted-learning-on-CPU-port.patch | 27 + ...-missing-linux-if_ether.h-for-ETH_AL.patch | 61 + ...ice-struct-copy-its-DMA-params-to-th.patch | 73 + ...pio-cascade-add-generic-GPIO-cascade.patch | 222 + .../810-pci_disable_common_quirks.patch | 62 + .../811-pci_disable_usb_common_quirks.patch | 115 + ...problem-with-platfom-data-in-w1-gpio.patch | 26 + .../pending-6.1/834-ledtrig-libata.patch | 149 + ...40-hwrng-bcm2835-set-quality-to-1000.patch | 26 + ...e-main-irq_chip-structure-a-static-d.patch | 102 + .../pending-6.1/920-mangle_bootargs.patch | 71 + 466 files changed, 96022 insertions(+) create mode 100644 target/linux/generic/backport-6.1/005-v5.17-01-Kbuild-use-Wdeclaration-after-statement.patch create mode 100644 target/linux/generic/backport-6.1/005-v5.17-02-Kbuild-move-to-std-gnu11.patch create mode 100644 target/linux/generic/backport-6.1/005-v5.17-03-Kbuild-use-std-gnu11-for-KBUILD_USERCFLAGS.patch create mode 100644 target/linux/generic/backport-6.1/020-v6.1-01-mm-x86-arm64-add-arch_has_hw_pte_young.patch create mode 100644 target/linux/generic/backport-6.1/020-v6.1-02-mm-x86-add-CONFIG_ARCH_HAS_NONLEAF_PMD_YOUNG.patch create mode 100644 target/linux/generic/backport-6.1/020-v6.1-03-mm-vmscan.c-refactor-shrink_node.patch create mode 100644 target/linux/generic/backport-6.1/020-v6.1-04-mm-multigenerational-lru-groundwork.patch create mode 100644 target/linux/generic/backport-6.1/020-v6.1-05-mm-multigenerational-lru-mm_struct-list.patch create mode 100644 target/linux/generic/backport-6.1/020-v6.1-06-mm-multigenerational-lru-aging.patch create mode 100644 target/linux/generic/backport-6.1/020-v6.1-07-mm-multigenerational-lru-eviction.patch create mode 100644 target/linux/generic/backport-6.1/020-v6.1-08-mm-multigenerational-lru-user-interface.patch create mode 100644 target/linux/generic/backport-6.1/020-v6.1-09-mm-multigenerational-lru-Kconfig.patch create mode 100644 target/linux/generic/backport-6.1/020-v6.1-10-mm-multigenerational-lru-documentation.patch create mode 100644 target/linux/generic/backport-6.1/021-v6.1-mm-mglru-don-t-sync-disk-for-each-aging-cycle.patch create mode 100644 target/linux/generic/backport-6.1/050-v5.16-00-MIPS-uasm-Enable-muhu-opcode-for-MIPS-R6.patch create mode 100644 target/linux/generic/backport-6.1/050-v5.16-01-mips-uasm-Add-workaround-for-Loongson-2F-nop-CPU-err.patch create mode 100644 target/linux/generic/backport-6.1/050-v5.16-02-mips-bpf-Add-eBPF-JIT-for-32-bit-MIPS.patch create mode 100644 target/linux/generic/backport-6.1/050-v5.16-03-mips-bpf-Add-new-eBPF-JIT-for-64-bit-MIPS.patch create mode 100644 target/linux/generic/backport-6.1/050-v5.16-04-mips-bpf-Add-JIT-workarounds-for-CPU-errata.patch create mode 100644 target/linux/generic/backport-6.1/050-v5.16-05-mips-bpf-Enable-eBPF-JITs.patch create mode 100644 target/linux/generic/backport-6.1/050-v5.16-06-mips-bpf-Remove-old-BPF-JIT-implementations.patch create mode 100644 target/linux/generic/backport-6.1/081-v5.17-regmap-allow-to-define-reg_update_bits-for-no-bus.patch create mode 100644 target/linux/generic/backport-6.1/100-v5.18-tty-serial-bcm63xx-use-more-precise-Kconfig-symbol.patch create mode 100644 target/linux/generic/backport-6.1/200-v5.18-tools-resolve_btfids-Build-with-host-flags.patch create mode 100644 target/linux/generic/backport-6.1/201-v5.16-scripts-dtc-Update-to-upstream-version-v1.6.1-19-g0a.patch create mode 100644 target/linux/generic/backport-6.1/300-v5.18-pinctrl-qcom-Return--EINVAL-for-setting-affinity-if-no-IRQ-parent.patch create mode 100644 target/linux/generic/backport-6.1/301-v5.16-soc-qcom-smem-Support-reserved-memory-description.patch create mode 100644 target/linux/generic/backport-6.1/330-v5.16-01-MIPS-kernel-proc-add-CPU-option-reporting.patch create mode 100644 target/linux/generic/backport-6.1/330-v5.16-02-MIPS-Fix-using-smp_processor_id-in-preemptible-in-sh.patch create mode 100644 target/linux/generic/backport-6.1/331-v5.19-mtd-spinand-Add-support-for-XTX-XT26G0xA.patch create mode 100644 target/linux/generic/backport-6.1/344-v5.18-01-phy-marvell-phy-mvebu-a3700-comphy-Remove-port-from-.patch create mode 100644 target/linux/generic/backport-6.1/344-v5.18-02-phy-marvell-phy-mvebu-a3700-comphy-Add-native-kernel.patch create mode 100644 target/linux/generic/backport-6.1/345-v5.17-arm64-dts-marvell-armada-37xx-Add-xtal-clock-to-comp.patch create mode 100644 target/linux/generic/backport-6.1/346-v5.18-01-Revert-ata-ahci-mvebu-Make-SATA-PHY-optional-for-Arm.patch create mode 100644 target/linux/generic/backport-6.1/346-v5.18-02-Revert-usb-host-xhci-mvebu-make-USB-3.0-PHY-optional.patch create mode 100644 target/linux/generic/backport-6.1/346-v5.18-03-Revert-PCI-aardvark-Fix-initialization-with-old-Marv.patch create mode 100644 target/linux/generic/backport-6.1/347-v6.0-phy-marvell-phy-mvebu-a3700-comphy-Remove-broken-res.patch create mode 100644 target/linux/generic/backport-6.1/400-v5.19-mtd-call-of_platform_populate-for-MTD-partitions.patch create mode 100644 target/linux/generic/backport-6.1/401-v6.0-mtd-parsers-add-support-for-Sercomm-partitions.patch create mode 100644 target/linux/generic/backport-6.1/402-v6.0-mtd-next-mtd-core-introduce-of-support-for-dynamic-partitions.patch create mode 100644 target/linux/generic/backport-6.1/403-v6.1-mtd-allow-getting-MTD-device-associated-with-a-speci.patch create mode 100644 target/linux/generic/backport-6.1/404-v6.0-mtd-core-check-partition-before-dereference.patch create mode 100644 target/linux/generic/backport-6.1/405-v6.1-mtd-core-add-missing-of_node_get-in-dynamic-partitio.patch create mode 100644 target/linux/generic/backport-6.1/406-v6.2-0001-mtd-core-simplify-a-bit-code-find-partition-matching.patch create mode 100644 target/linux/generic/backport-6.1/406-v6.2-0002-mtd-core-try-to-find-OF-node-for-every-MTD-partition.patch create mode 100644 target/linux/generic/backport-6.1/407-v5.17-mtd-parsers-qcom-Don-t-print-error-message-on-EPROBE.patch create mode 100644 target/linux/generic/backport-6.1/410-v5.18-mtd-parsers-trx-allow-to-use-on-MediaTek-MIPS-SoCs.patch create mode 100644 target/linux/generic/backport-6.1/420-v5.19-02-mtd-spinand-gigadevice-add-support-for-GD5FxGQ4xExxG.patch create mode 100644 target/linux/generic/backport-6.1/420-v5.19-03-mtd-spinand-gigadevice-add-support-for-GD5F1GQ5RExxG.patch create mode 100644 target/linux/generic/backport-6.1/420-v5.19-04-mtd-spinand-gigadevice-add-support-for-GD5F-2-4-GQ5x.patch create mode 100644 target/linux/generic/backport-6.1/420-v5.19-05-mtd-spinand-gigadevice-add-support-for-GD5FxGM7xExxG.patch create mode 100644 target/linux/generic/backport-6.1/421-v6.2-mtd-parsers-add-TP-Link-SafeLoader-partitions-table-.patch create mode 100644 target/linux/generic/backport-6.1/600-v5.18-page_pool-Add-allocation-stats.patch create mode 100644 target/linux/generic/backport-6.1/601-v5.18-page_pool-Add-recycle-stats.patch create mode 100644 target/linux/generic/backport-6.1/602-v5.18-page_pool-Add-function-to-batch-and-return-stats.patch create mode 100644 target/linux/generic/backport-6.1/603-v5.19-page_pool-Add-recycle-stats-to-page_pool_put_page_bu.patch create mode 100644 target/linux/generic/backport-6.1/604-v5.19-net-page_pool-introduce-ethtool-stats.patch create mode 100644 target/linux/generic/backport-6.1/605-v5.18-xdp-introduce-flags-field-in-xdp_buff-xdp_frame.patch create mode 100644 target/linux/generic/backport-6.1/606-v5.18-xdp-add-frags-support-to-xdp_return_-buff-frame.patch create mode 100644 target/linux/generic/backport-6.1/607-v5.18-net-skbuff-add-size-metadata-to-skb_shared_info-for-.patch create mode 100644 target/linux/generic/backport-6.1/608-v5.18-net-veth-Account-total-xdp_frame-len-running-ndo_xdp.patch create mode 100644 target/linux/generic/backport-6.1/609-v5.18-veth-Allow-jumbo-frames-in-xdp-mode.patch create mode 100644 target/linux/generic/backport-6.1/700-v5.17-net-dsa-introduce-tagger-owned-storage-for-private.patch create mode 100644 target/linux/generic/backport-6.1/701-v5.17-dsa-make-tagging-protocols-connect-to-individual-switches.patch create mode 100644 target/linux/generic/backport-6.1/702-v5.19-00-net-ethernet-mtk_eth_soc-add-support-for-coherent-DM.patch create mode 100644 target/linux/generic/backport-6.1/702-v5.19-01-arm64-dts-mediatek-mt7622-add-support-for-coherent-D.patch create mode 100644 target/linux/generic/backport-6.1/702-v5.19-02-net-ethernet-mtk_eth_soc-add-support-for-Wireless-Et.patch create mode 100644 target/linux/generic/backport-6.1/702-v5.19-03-net-ethernet-mtk_eth_soc-implement-flow-offloading-t.patch create mode 100644 target/linux/generic/backport-6.1/702-v5.19-04-arm64-dts-mediatek-mt7622-introduce-nodes-for-Wirele.patch create mode 100644 target/linux/generic/backport-6.1/702-v5.19-05-net-ethernet-mtk_eth_soc-add-ipv6-flow-offload-suppo.patch create mode 100644 target/linux/generic/backport-6.1/702-v5.19-06-net-ethernet-mtk_eth_soc-support-TC_SETUP_BLOCK-for-.patch create mode 100644 target/linux/generic/backport-6.1/702-v5.19-07-net-ethernet-mtk_eth_soc-allocate-struct-mtk_ppe-sep.patch create mode 100644 target/linux/generic/backport-6.1/702-v5.19-08-net-ethernet-mtk_eth_soc-rework-hardware-flow-table-.patch create mode 100644 target/linux/generic/backport-6.1/702-v5.19-09-net-ethernet-mtk_eth_soc-remove-bridge-flow-offload-.patch create mode 100644 target/linux/generic/backport-6.1/702-v5.19-10-net-ethernet-mtk_eth_soc-support-creating-mac-addres.patch create mode 100644 target/linux/generic/backport-6.1/702-v5.19-11-net-ethernet-mtk_eth_soc-wed-fix-sparse-endian-warni.patch create mode 100644 target/linux/generic/backport-6.1/702-v5.19-12-net-ethernet-mtk_eth_soc-fix-return-value-check-in-m.patch create mode 100644 target/linux/generic/backport-6.1/702-v5.19-13-net-ethernet-mtk_eth_soc-use-standard-property-for-c.patch create mode 100644 target/linux/generic/backport-6.1/702-v5.19-14-net-ethernet-mtk_eth_soc-use-after-free-in-__mtk_ppe.patch create mode 100644 target/linux/generic/backport-6.1/702-v5.19-15-net-ethernet-mtk_eth_soc-add-check-for-allocation-fa.patch create mode 100644 target/linux/generic/backport-6.1/702-v5.19-16-eth-mtk_eth_soc-silence-the-GCC-12-array-bounds-warn.patch create mode 100644 target/linux/generic/backport-6.1/702-v5.19-17-net-ethernet-mtk_eth_soc-rely-on-GFP_KERNEL-for-dma_.patch create mode 100644 target/linux/generic/backport-6.1/702-v5.19-18-net-ethernet-mtk_eth_soc-move-tx-dma-desc-configurat.patch create mode 100644 target/linux/generic/backport-6.1/702-v5.19-19-net-ethernet-mtk_eth_soc-add-txd_size-to-mtk_soc_dat.patch create mode 100644 target/linux/generic/backport-6.1/702-v5.19-20-net-ethernet-mtk_eth_soc-rely-on-txd_size-in-mtk_tx_.patch create mode 100644 target/linux/generic/backport-6.1/702-v5.19-21-net-ethernet-mtk_eth_soc-rely-on-txd_size-in-mtk_des.patch create mode 100644 target/linux/generic/backport-6.1/702-v5.19-22-net-ethernet-mtk_eth_soc-rely-on-txd_size-in-txd_to_.patch create mode 100644 target/linux/generic/backport-6.1/702-v5.19-23-net-ethernet-mtk_eth_soc-add-rxd_size-to-mtk_soc_dat.patch create mode 100644 target/linux/generic/backport-6.1/702-v5.19-24-net-ethernet-mtk_eth_soc-rely-on-txd_size-field-in-m.patch create mode 100644 target/linux/generic/backport-6.1/702-v5.19-25-net-ethernet-mtk_eth_soc-rely-on-rxd_size-field-in-m.patch create mode 100644 target/linux/generic/backport-6.1/702-v5.19-26-net-ethernet-mtk_eth_soc-introduce-device-register-m.patch create mode 100644 target/linux/generic/backport-6.1/702-v5.19-27-net-ethernet-mtk_eth_soc-introduce-MTK_NETSYS_V2-sup.patch create mode 100644 target/linux/generic/backport-6.1/702-v5.19-28-net-ethernet-mtk_eth_soc-convert-ring-dma-pointer-to.patch create mode 100644 target/linux/generic/backport-6.1/702-v5.19-29-net-ethernet-mtk_eth_soc-convert-scratch_ring-pointe.patch create mode 100644 target/linux/generic/backport-6.1/702-v5.19-30-net-ethernet-mtk_eth_soc-introduce-support-for-mt798.patch create mode 100644 target/linux/generic/backport-6.1/702-v5.19-31-net-ethernet-mtk_eth_soc-fix-error-code-in-mtk_flow_.patch create mode 100644 target/linux/generic/backport-6.1/702-v5.19-33-net-ethernet-mtk_eth_soc-enable-rx-cksum-offload-for.patch create mode 100644 target/linux/generic/backport-6.1/702-v5.19-34-eth-mtk_ppe-fix-up-after-merge.patch create mode 100644 target/linux/generic/backport-6.1/703-00-v5.16-net-convert-users-of-bitmap_foo-to-linkmode_foo.patch create mode 100644 target/linux/generic/backport-6.1/703-01-v5.16-net-phylink-add-MAC-phy_interface_t-bitmap.patch create mode 100644 target/linux/generic/backport-6.1/703-02-v5.16-net-phylink-use-supported_interfaces-for-phylink-val.patch create mode 100644 target/linux/generic/backport-6.1/703-03-v5.16-net-dsa-populate-supported_interfaces-member.patch create mode 100644 target/linux/generic/backport-6.1/703-04-v5.17-net-dsa-consolidate-phylink-creation.patch create mode 100644 target/linux/generic/backport-6.1/703-05-v5.17-net-dsa-replace-phylink_get_interfaces-with-phylink_.patch create mode 100644 target/linux/generic/backport-6.1/703-06-v5.18-net-dsa-add-support-for-phylink-mac_select_pcs.patch create mode 100644 target/linux/generic/backport-6.1/703-07-v5.16-net-phy-add-phy_interface_t-bitmap-support.patch create mode 100644 target/linux/generic/backport-6.1/703-08-v5.17-net-phylink-add-mac_select_pcs-method-to-phylink_mac.patch create mode 100644 target/linux/generic/backport-6.1/703-09-v5.17-net-phylink-add-generic-validate-implementation.patch create mode 100644 target/linux/generic/backport-6.1/703-10-v5.16-net-dsa-introduce-helpers-for-iterating-through-port.patch create mode 100644 target/linux/generic/backport-6.1/703-11-v5.17-net-phylink-add-pcs_validate-method.patch create mode 100644 target/linux/generic/backport-6.1/703-12-v5.17-net-phylink-add-legacy_pre_march2020-indicator.patch create mode 100644 target/linux/generic/backport-6.1/703-13-v5.17-net-dsa-mark-DSA-phylink-as-legacy_pre_march2020.patch create mode 100644 target/linux/generic/backport-6.1/703-14-v5.17-net-phylink-use-legacy_pre_march2020.patch create mode 100644 target/linux/generic/backport-6.1/703-15-v5.18-net-phy-phylink-fix-DSA-mac_select_pcs-introduction.patch create mode 100644 target/linux/generic/backport-6.1/703-16-v5.16-net-mvneta-populate-supported_interfaces-member.patch create mode 100644 target/linux/generic/backport-6.1/703-17-v5.16-net-mvneta-remove-interface-checks-in-mvneta_validat.patch create mode 100644 target/linux/generic/backport-6.1/703-18-v5.16-net-mvneta-drop-use-of-phylink_helper_basex_speed.patch create mode 100644 target/linux/generic/backport-6.1/703-19-v5.17-net-mvneta-use-phylink_generic_validate.patch create mode 100644 target/linux/generic/backport-6.1/703-20-v5.17-net-mvneta-mark-as-a-legacy_pre_march2020-driver.patch create mode 100644 target/linux/generic/backport-6.1/704-01-v5.17-net-mtk_eth_soc-populate-supported_interfaces-member.patch create mode 100644 target/linux/generic/backport-6.1/704-02-v5.17-net-mtk_eth_soc-remove-interface-checks-in-mtk_valid.patch create mode 100644 target/linux/generic/backport-6.1/704-03-v5.17-net-mtk_eth_soc-drop-use-of-phylink_helper_basex_spe.patch create mode 100644 target/linux/generic/backport-6.1/704-04-v5.17-net-mtk_eth_soc-use-phylink_generic_validate.patch create mode 100644 target/linux/generic/backport-6.1/704-05-v5.17-net-mtk_eth_soc-mark-as-a-legacy_pre_march2020-drive.patch create mode 100644 target/linux/generic/backport-6.1/704-06-v5.19-eth-mtk_eth_soc-remove-a-copy-of-the-NAPI_POLL_WEIGH.patch create mode 100644 target/linux/generic/backport-6.1/704-07-v5.19-mtk_eth_soc-remove-unused-mac-mode.patch create mode 100644 target/linux/generic/backport-6.1/704-08-v5.19-net-mtk_eth_soc-remove-unused-sgmii-flags.patch create mode 100644 target/linux/generic/backport-6.1/704-09-v5.19-net-mtk_eth_soc-add-mask-and-update-PCS-speed-defini.patch create mode 100644 target/linux/generic/backport-6.1/704-10-v5.19-net-mtk_eth_soc-correct-802.3z-speed-setting.patch create mode 100644 target/linux/generic/backport-6.1/704-11-v5.19-net-mtk_eth_soc-correct-802.3z-duplex-setting.patch create mode 100644 target/linux/generic/backport-6.1/704-12-v5.19-net-mtk_eth_soc-stop-passing-phylink-state-to-sgmii-.patch create mode 100644 target/linux/generic/backport-6.1/704-13-v5.19-net-mtk_eth_soc-provide-mtk_sgmii_config.patch create mode 100644 target/linux/generic/backport-6.1/704-14-v5.19-net-mtk_eth_soc-add-fixme-comment-for-state-speed-us.patch create mode 100644 target/linux/generic/backport-6.1/704-15-v5.19-net-mtk_eth_soc-move-MAC_MCR-setting-to-mac_finish.patch create mode 100644 target/linux/generic/backport-6.1/704-16-v5.19-net-mtk_eth_soc-move-restoration-of-SYSCFG0-to-mac_f.patch create mode 100644 target/linux/generic/backport-6.1/704-17-v5.19-net-mtk_eth_soc-convert-code-structure-to-suit-split.patch create mode 100644 target/linux/generic/backport-6.1/704-18-v5.19-net-mtk_eth_soc-partially-convert-to-phylink_pcs.patch create mode 100644 target/linux/generic/backport-6.1/705-01-v5.17-net-dsa-mt7530-iterate-using-dsa_switch_for_each_use.patch create mode 100644 target/linux/generic/backport-6.1/705-02-v5.19-net-dsa-mt7530-populate-supported_interfaces-and-mac.patch create mode 100644 target/linux/generic/backport-6.1/705-03-v5.19-net-dsa-mt7530-remove-interface-checks.patch create mode 100644 target/linux/generic/backport-6.1/705-04-v5.19-net-dsa-mt7530-drop-use-of-phylink_helper_basex_spee.patch create mode 100644 target/linux/generic/backport-6.1/705-05-v5.19-net-dsa-mt7530-only-indicate-linkmodes-that-can-be-s.patch create mode 100644 target/linux/generic/backport-6.1/705-06-v5.19-net-dsa-mt7530-switch-to-use-phylink_get_linkmodes.patch create mode 100644 target/linux/generic/backport-6.1/705-07-v5.19-net-dsa-mt7530-partially-convert-to-phylink_pcs.patch create mode 100644 target/linux/generic/backport-6.1/705-08-v5.19-net-dsa-mt7530-move-autoneg-handling-to-PCS-validati.patch create mode 100644 target/linux/generic/backport-6.1/705-09-v5.19-net-dsa-mt7530-mark-as-non-legacy.patch create mode 100644 target/linux/generic/backport-6.1/705-10-v5.19-net-dsa-mt753x-fix-pcs-conversion-regression.patch create mode 100644 target/linux/generic/backport-6.1/705-11-v6.0-net-dsa-mt7530-rework-mt7530_hw_vlan_-add-del.patch create mode 100644 target/linux/generic/backport-6.1/705-12-v6.0-net-dsa-mt7530-rework-mt753-01-_setup.patch create mode 100644 target/linux/generic/backport-6.1/705-13-v6.0-net-dsa-mt7530-get-cpu-port-via-dp-cpu_dp-instead-of.patch create mode 100644 target/linux/generic/backport-6.1/706-00-v6.0-net-ethernet-mtk_eth_soc-rely-on-page_pool-for-singl.patch create mode 100644 target/linux/generic/backport-6.1/706-01-v6.0-net-ethernet-mtk_eth_soc-add-basic-XDP-support.patch create mode 100644 target/linux/generic/backport-6.1/706-02-v6.0-net-ethernet-mtk_eth_soc-introduce-xdp-ethtool-count.patch create mode 100644 target/linux/generic/backport-6.1/706-03-v6.0-net-ethernet-mtk_eth_soc-add-xmit-XDP-support.patch create mode 100644 target/linux/generic/backport-6.1/706-04-v6.0-net-ethernet-mtk_eth_soc-add-support-for-page_pool_g.patch create mode 100644 target/linux/generic/backport-6.1/706-05-v6.0-net-ethernet-mtk_eth_soc-introduce-mtk_xdp_frame_map.patch create mode 100644 target/linux/generic/backport-6.1/706-06-v6.0-net-ethernet-mtk_eth_soc-introduce-xdp-multi-frag-su.patch create mode 100644 target/linux/generic/backport-6.1/710-v6.0-net-ethernet-mtk_eth_soc-fix-hw-hash-reporting-for-M.patch create mode 100644 target/linux/generic/backport-6.1/711-v6.0-01-net-ethernet-mtk_eth_soc-fix-off-by-one-check-of-ARR.patch create mode 100644 target/linux/generic/backport-6.1/711-v6.0-02-net-ethernet-mtk_ppe-fix-possible-NULL-pointer-deref.patch create mode 100644 target/linux/generic/backport-6.1/711-v6.0-03-net-ethernet-mtk-ppe-fix-traffic-offload-with-bridge.patch create mode 100644 target/linux/generic/backport-6.1/711-v6.0-04-net-ethernet-mtk_eth_soc-remove-mtk_foe_entry_timest.patch create mode 100644 target/linux/generic/backport-6.1/712-v6.0-net-ethernet-mtk_eth_soc-enable-XDP-support-just-for.patch create mode 100644 target/linux/generic/backport-6.1/713-v6.0-net-ethernet-mtk_eth_soc-move-gdma_to_ppe-and-ppe_ba.patch create mode 100644 target/linux/generic/backport-6.1/714-v6.0-net-ethernet-mtk_eth_soc-move-ppe-table-hash-offset-.patch create mode 100644 target/linux/generic/backport-6.1/715-v6.0-net-ethernet-mtk_eth_soc-add-the-capability-to-run-m.patch create mode 100644 target/linux/generic/backport-6.1/716-v6.0-net-ethernet-mtk_eth_soc-move-wdma_base-definitions-.patch create mode 100644 target/linux/generic/backport-6.1/717-v6.0-net-ethernet-mtk_eth_soc-add-foe_entry_size-to-mtk_e.patch create mode 100644 target/linux/generic/backport-6.1/718-v6.0-net-ethernet-mtk_eth_soc-fix-typo-in-__mtk_foe_entry.patch create mode 100644 target/linux/generic/backport-6.1/719-v6.0-net-ethernet-mtk_eth_soc-check-max-allowed-value-in-.patch create mode 100644 target/linux/generic/backport-6.1/720-v6.0-net-ethernet-mtk_eth_wed-add-mtk_wed_configure_irq-a.patch create mode 100644 target/linux/generic/backport-6.1/721-v6.0-net-ethernet-mtk_eth_wed-add-wed-support-for-mt7986-.patch create mode 100644 target/linux/generic/backport-6.1/722-v6.0-net-ethernet-mtk_eth_wed-add-axi-bus-support.patch create mode 100644 target/linux/generic/backport-6.1/723-v6.0-net-ethernet-mtk_eth_soc-introduce-flow-offloading-s.patch create mode 100644 target/linux/generic/backport-6.1/724-v6.0-net-ethernet-mtk_eth_soc-enable-flow-offloading-supp.patch create mode 100644 target/linux/generic/backport-6.1/724-v6.0-net-ethernet-mtk_eth_soc-fix-wrong-use-of-new-helper.patch create mode 100644 target/linux/generic/backport-6.1/725-v6.0-net-ethernet-mtk_eth_soc-fix-usage-of-foe_entry_size.patch create mode 100644 target/linux/generic/backport-6.1/734-v5.16-0001-net-bgmac-improve-handling-PHY.patch create mode 100644 target/linux/generic/backport-6.1/734-v5.16-0002-net-bgmac-support-MDIO-described-in-DT.patch create mode 100644 target/linux/generic/backport-6.1/742-v5.16-net-phy-at803x-add-support-for-qca-8327-internal-phy.patch create mode 100644 target/linux/generic/backport-6.1/743-v5.16-0001-net-dsa-b53-Include-all-ports-in-enabled_ports.patch create mode 100644 target/linux/generic/backport-6.1/743-v5.16-0002-net-dsa-b53-Drop-BCM5301x-workaround-for-a-wrong-CPU.patch create mode 100644 target/linux/generic/backport-6.1/743-v5.16-0003-net-dsa-b53-Improve-flow-control-setup-on-BCM5301x.patch create mode 100644 target/linux/generic/backport-6.1/743-v5.16-0004-net-dsa-b53-Drop-unused-cpu_port-field.patch create mode 100644 target/linux/generic/backport-6.1/745-v5.16-01-net-phy-at803x-add-support-for-qca-8327-A-variant.patch create mode 100644 target/linux/generic/backport-6.1/745-v5.16-02-net-phy-at803x-add-resume-suspend-function-to-qca83x.patch create mode 100644 target/linux/generic/backport-6.1/745-v5.16-03-net-phy-at803x-fix-spacing-and-improve-name-for-83xx.patch create mode 100644 target/linux/generic/backport-6.1/746-v5.16-01-net-phy-at803x-fix-resume-for-QCA8327-phy.patch create mode 100644 target/linux/generic/backport-6.1/746-v5.16-02-net-phy-at803x-add-DAC-amplitude-fix-for-8327-phy.patch create mode 100644 target/linux/generic/backport-6.1/746-v5.16-03-net-phy-at803x-enable-prefer-master-for-83xx-interna.patch create mode 100644 target/linux/generic/backport-6.1/746-v5.16-04-net-phy-at803x-better-describe-debug-regs.patch create mode 100644 target/linux/generic/backport-6.1/747-v5.16-01-dsa-qca8k-add-mac-power-sel-support.patch create mode 100644 target/linux/generic/backport-6.1/747-v5.16-02-dt-bindings-net-dsa-qca8k-Add-SGMII-clock-phase-prop.patch create mode 100644 target/linux/generic/backport-6.1/747-v5.16-03-net-dsa-qca8k-add-support-for-sgmii-falling-edge.patch create mode 100644 target/linux/generic/backport-6.1/747-v5.16-04-dt-bindings-net-dsa-qca8k-Document-support-for-CPU-p.patch create mode 100644 target/linux/generic/backport-6.1/747-v5.16-05-net-dsa-qca8k-add-support-for-cpu-port-6.patch create mode 100644 target/linux/generic/backport-6.1/747-v5.16-06-net-dsa-qca8k-rework-rgmii-delay-logic-and-scan-for-.patch create mode 100644 target/linux/generic/backport-6.1/747-v5.16-07-dt-bindings-net-dsa-qca8k-Document-qca-sgmii-enable-.patch create mode 100644 target/linux/generic/backport-6.1/747-v5.16-08-net-dsa-qca8k-add-explicit-SGMII-PLL-enable.patch create mode 100644 target/linux/generic/backport-6.1/747-v5.16-09-dt-bindings-net-dsa-qca8k-Document-qca-led-open-drai.patch create mode 100644 target/linux/generic/backport-6.1/747-v5.16-10-net-dsa-qca8k-add-support-for-pws-config-reg.patch create mode 100644 target/linux/generic/backport-6.1/747-v5.16-11-dt-bindings-net-dsa-qca8k-document-support-for-qca83.patch create mode 100644 target/linux/generic/backport-6.1/747-v5.16-12-net-dsa-qca8k-add-support-for-QCA8328.patch create mode 100644 target/linux/generic/backport-6.1/747-v5.16-13-net-dsa-qca8k-set-internal-delay-also-for-sgmii.patch create mode 100644 target/linux/generic/backport-6.1/747-v5.16-14-net-dsa-qca8k-move-port-config-to-dedicated-struct.patch create mode 100644 target/linux/generic/backport-6.1/747-v5.16-15-dt-bindings-net-ipq8064-mdio-fix-warning-with-new-qc.patch create mode 100644 target/linux/generic/backport-6.1/747-v5.16-16-dt-bindings-net-dsa-qca8k-convert-to-YAML-schema.patch create mode 100644 target/linux/generic/backport-6.1/748-v5.16-net-dsa-qca8k-fix-delay-applied-to-wrong-cpu-in-parse-p.patch create mode 100644 target/linux/generic/backport-6.1/749-v5.16-net-dsa-qca8k-tidy-for-loop-in-setup-and-add-cpu-port-c.patch create mode 100644 target/linux/generic/backport-6.1/750-v5.16-net-dsa-qca8k-make-sure-pad0-mac06-exchange-is-disabled.patch create mode 100644 target/linux/generic/backport-6.1/751-v5.16-net-dsa-qca8k-fix-internal-delay-applied-to-the-wrong-PAD.patch create mode 100644 target/linux/generic/backport-6.1/752-v5.16-net-dsa-qca8k-fix-MTU-calculation.patch create mode 100644 target/linux/generic/backport-6.1/753-v5.17-net-next-net-dsa-qca8k-remove-redundant-check-in-parse_port_config.patch create mode 100644 target/linux/generic/backport-6.1/754-v5.17-net-next-net-dsa-qca8k-convert-to-GENMASK_FIELD_PREP_FIELD_GET.patch create mode 100644 target/linux/generic/backport-6.1/755-v5.17-net-next-net-dsa-qca8k-remove-extra-mutex_init-in-qca8k_setup.patch create mode 100644 target/linux/generic/backport-6.1/756-v5.17-net-next-net-dsa-qca8k-move-regmap-init-in-probe-and-set-it.patch create mode 100644 target/linux/generic/backport-6.1/757-v5.17-net-next-net-dsa-qca8k-initial-conversion-to-regmap-heper.patch create mode 100644 target/linux/generic/backport-6.1/758-v5.17-net-next-net-dsa-qca8k-add-additional-MIB-counter-and-.patch create mode 100644 target/linux/generic/backport-6.1/759-v5.17-net-next-net-dsa-qca8k-add-support-for-port-fast-aging.patch create mode 100644 target/linux/generic/backport-6.1/760-v5.17-net-next-net-dsa-qca8k-add-set_ageing_time-support.patch create mode 100644 target/linux/generic/backport-6.1/761-v5.17-net-next-net-dsa-qca8k-add-support-for-mdb_add-del.patch create mode 100644 target/linux/generic/backport-6.1/762-v5.17-net-next-net-dsa-qca8k-add-support-for-mirror-mode.patch create mode 100644 target/linux/generic/backport-6.1/763-v5.17-net-next-net-dsa-qca8k-add-LAG-support.patch create mode 100644 target/linux/generic/backport-6.1/764-v5.17-net-next-net-dsa-qca8k-fix-warning-in-LAG-feature.patch create mode 100644 target/linux/generic/backport-6.1/765-v5.17-01-net-next-net-dsa-reorder-PHY-initialization-with-MTU-setup-in.patch create mode 100644 target/linux/generic/backport-6.1/765-v5.17-02-net-next-net-dsa-merge-rtnl_lock-sections-in-dsa_slave_create.patch create mode 100644 target/linux/generic/backport-6.1/765-v5.17-03-net-next-net-dsa-stop-updating-master-MTU-from-master.c.patch create mode 100644 target/linux/generic/backport-6.1/765-v5.17-04-net-next-net-dsa-hold-rtnl_mutex-when-calling-dsa_master_-set.patch create mode 100644 target/linux/generic/backport-6.1/765-v5.17-05-net-next-net-dsa-first-set-up-shared-ports-then-non-shared-po.patch create mode 100644 target/linux/generic/backport-6.1/765-v5.17-06-net-next-net-dsa-setup-master-before-ports.patch create mode 100644 target/linux/generic/backport-6.1/766-v5.18-01-net-dsa-provide-switch-operations-for-tracking-the-m.patch create mode 100644 target/linux/generic/backport-6.1/766-v5.18-02-net-dsa-replay-master-state-events-in-dsa_tree_-setu.patch create mode 100644 target/linux/generic/backport-6.1/766-v5.18-03-net-dsa-tag_qca-convert-to-FIELD-macro.patch create mode 100644 target/linux/generic/backport-6.1/766-v5.18-04-net-dsa-tag_qca-move-define-to-include-linux-dsa.patch create mode 100644 target/linux/generic/backport-6.1/766-v5.18-05-net-dsa-tag_qca-enable-promisc_on_master-flag.patch create mode 100644 target/linux/generic/backport-6.1/766-v5.18-06-net-dsa-tag_qca-add-define-for-handling-mgmt-Etherne.patch create mode 100644 target/linux/generic/backport-6.1/766-v5.18-07-net-dsa-tag_qca-add-define-for-handling-MIB-packet.patch create mode 100644 target/linux/generic/backport-6.1/766-v5.18-08-net-dsa-tag_qca-add-support-for-handling-mgmt-and-MI.patch create mode 100644 target/linux/generic/backport-6.1/766-v5.18-09-net-dsa-qca8k-add-tracking-state-of-master-port.patch create mode 100644 target/linux/generic/backport-6.1/766-v5.18-10-net-dsa-qca8k-add-support-for-mgmt-read-write-in-Eth.patch create mode 100644 target/linux/generic/backport-6.1/766-v5.18-11-net-dsa-qca8k-add-support-for-mib-autocast-in-Ethern.patch create mode 100644 target/linux/generic/backport-6.1/766-v5.18-12-net-dsa-qca8k-add-support-for-phy-read-write-with-mg.patch create mode 100644 target/linux/generic/backport-6.1/766-v5.18-13-net-dsa-qca8k-move-page-cache-to-driver-priv.patch create mode 100644 target/linux/generic/backport-6.1/766-v5.18-14-net-dsa-qca8k-cache-lo-and-hi-for-mdio-write.patch create mode 100644 target/linux/generic/backport-6.1/766-v5.18-15-net-dsa-qca8k-add-support-for-larger-read-write-size.patch create mode 100644 target/linux/generic/backport-6.1/766-v5.18-16-net-dsa-qca8k-introduce-qca8k_bulk_read-write-functi.patch create mode 100644 target/linux/generic/backport-6.1/767-v5.18-net-dsa-qca8k-check-correct-variable-in-qca8k_phy_et.patch create mode 100644 target/linux/generic/backport-6.1/768-v5.18-net-dsa-qca8k-fix-noderef.cocci-warnings.patch create mode 100644 target/linux/generic/backport-6.1/769-v5.19-01-net-dsa-qca8k-drop-MTU-tracking-from-qca8k_priv.patch create mode 100644 target/linux/generic/backport-6.1/769-v5.19-02-net-dsa-qca8k-drop-port_sts-from-qca8k_priv.patch create mode 100644 target/linux/generic/backport-6.1/769-v5.19-03-net-dsa-qca8k-rework-and-simplify-mdiobus-logic.patch create mode 100644 target/linux/generic/backport-6.1/769-v5.19-04-net-dsa-qca8k-drop-dsa_switch_ops-from-qca8k_priv.patch create mode 100644 target/linux/generic/backport-6.1/769-v5.19-05-net-dsa-qca8k-correctly-handle-mdio-read-error.patch create mode 100644 target/linux/generic/backport-6.1/769-v5.19-06-net-dsa-qca8k-unify-bus-id-naming-with-legacy-and-OF.patch create mode 100644 target/linux/generic/backport-6.1/770-v6.0-net-dsa-qca8k-move-driver-to-qca-dir.patch create mode 100644 target/linux/generic/backport-6.1/771-v6.0-01-net-dsa-qca8k-cache-match-data-to-speed-up-access.patch create mode 100644 target/linux/generic/backport-6.1/771-v6.0-02-net-dsa-qca8k-make-mib-autocast-feature-optional.patch create mode 100644 target/linux/generic/backport-6.1/771-v6.0-03-net-dsa-qca8k-move-mib-struct-to-common-code.patch create mode 100644 target/linux/generic/backport-6.1/771-v6.0-04-net-dsa-qca8k-move-qca8k-read-write-rmw-and-reg-tabl.patch create mode 100644 target/linux/generic/backport-6.1/771-v6.0-05-net-dsa-qca8k-move-qca8k-bulk-read-write-helper-to-c.patch create mode 100644 target/linux/generic/backport-6.1/771-v6.0-06-net-dsa-qca8k-move-mib-init-function-to-common-code.patch create mode 100644 target/linux/generic/backport-6.1/771-v6.0-07-net-dsa-qca8k-move-port-set-status-eee-ethtool-stats.patch create mode 100644 target/linux/generic/backport-6.1/771-v6.0-08-net-dsa-qca8k-move-bridge-functions-to-common-code.patch create mode 100644 target/linux/generic/backport-6.1/771-v6.0-09-net-dsa-qca8k-move-set-age-MTU-port-enable-disable-f.patch create mode 100644 target/linux/generic/backport-6.1/771-v6.0-10-net-dsa-qca8k-move-port-FDB-MDB-function-to-common-c.patch create mode 100644 target/linux/generic/backport-6.1/771-v6.0-11-net-dsa-qca8k-move-port-mirror-functions-to-common-c.patch create mode 100644 target/linux/generic/backport-6.1/771-v6.0-12-net-dsa-qca8k-move-port-VLAN-functions-to-common-cod.patch create mode 100644 target/linux/generic/backport-6.1/771-v6.0-13-net-dsa-qca8k-move-port-LAG-functions-to-common-code.patch create mode 100644 target/linux/generic/backport-6.1/771-v6.0-14-net-dsa-qca8k-move-read_switch_id-function-to-common.patch create mode 100644 target/linux/generic/backport-6.1/772-v6.0-net-dsa-qca8k-fix-NULL-pointer-dereference-for-of_de.patch create mode 100644 target/linux/generic/backport-6.1/773-v5.18-1-net-dsa-Move-VLAN-filtering-syncing-out-of-dsa_switc.patch create mode 100644 target/linux/generic/backport-6.1/773-v5.18-2-net-dsa-Avoid-cross-chip-syncing-of-VLAN-filtering.patch create mode 100644 target/linux/generic/backport-6.1/774-v5.16-01-net-dsa-rtl8366rb-Support-bridge-offloading.patch create mode 100644 target/linux/generic/backport-6.1/774-v5.16-02-net-dsa-rtl8366-Drop-custom-VLAN-set-up.patch create mode 100644 target/linux/generic/backport-6.1/774-v5.16-03-net-dsa-rtl8366rb-Rewrite-weird-VLAN-filering-enable.patch create mode 100644 target/linux/generic/backport-6.1/774-v5.16-06-net-dsa-rtl8366-Drop-and-depromote-pointless-prints.patch create mode 100644 target/linux/generic/backport-6.1/774-v5.16-07-net-dsa-rtl8366rb-Use-core-filtering-tracking.patch create mode 100644 target/linux/generic/backport-6.1/774-v5.16-08-net-dsa-rtl8366rb-Support-disabling-learning.patch create mode 100644 target/linux/generic/backport-6.1/774-v5.16-09-net-dsa-rtl8366rb-Support-fast-aging.patch create mode 100644 target/linux/generic/backport-6.1/774-v5.16-10-net-dsa-rtl8366rb-Support-setting-STP-state.patch create mode 100644 target/linux/generic/backport-6.1/775-v6.0-01-net-ethernet-stmicro-stmmac-move-queue-reset-to-dedi.patch create mode 100644 target/linux/generic/backport-6.1/775-v6.0-02-net-ethernet-stmicro-stmmac-first-disable-all-queues.patch create mode 100644 target/linux/generic/backport-6.1/775-v6.0-03-net-ethernet-stmicro-stmmac-move-dma-conf-to-dedicat.patch create mode 100644 target/linux/generic/backport-6.1/775-v6.0-04-net-ethernet-stmicro-stmmac-generate-stmmac-dma-conf.patch create mode 100644 target/linux/generic/backport-6.1/775-v6.0-05-net-ethernet-stmicro-stmmac-permit-MTU-change-with-i.patch create mode 100644 target/linux/generic/backport-6.1/776-v6.1-01-net-dsa-qca8k-fix-inband-mgmt-for-big-endian-systems.patch create mode 100644 target/linux/generic/backport-6.1/776-v6.1-02-net-dsa-qca8k-fix-ethtool-autocast-mib-for-big-endia.patch create mode 100644 target/linux/generic/backport-6.1/780-v5.16-bus-mhi-pci_generic-Introduce-Sierra-EM919X-support.patch create mode 100644 target/linux/generic/backport-6.1/781-v6.1-bus-mhi-host-always-print-detected-modem-name.patch create mode 100644 target/linux/generic/backport-6.1/782-v6.1-net-dsa-mt7530-add-support-for-in-band-link-status.patch create mode 100644 target/linux/generic/backport-6.1/783-v6.1-net-sfp-re-implement-soft-state-polling-setup.patch create mode 100644 target/linux/generic/backport-6.1/784-v6.1-net-sfp-move-quirk-handling-into-sfp.c.patch create mode 100644 target/linux/generic/backport-6.1/785-v6.1-net-sfp-move-Alcatel-Lucent-3FE46541AA-fixup.patch create mode 100644 target/linux/generic/backport-6.1/786-v6.1-net-sfp-move-Huawei-MA5671A-fixup.patch create mode 100644 target/linux/generic/backport-6.1/787-v6.1-net-sfp-add-support-for-HALNy-GPON-SFP.patch create mode 100644 target/linux/generic/backport-6.1/788-v6.0-net-ethernet-mtk_eth_soc-fix-mask-of-RX_DMA_GET_SPOR.patch create mode 100644 target/linux/generic/backport-6.1/789-v6.1-net-ethernet-mtk_eth_soc-fix-state-in-__mtk_foe_entr.patch create mode 100644 target/linux/generic/backport-6.1/800-v6.0-0001-dt-bindings-leds-add-Broadcom-s-BCM63138-controller.patch create mode 100644 target/linux/generic/backport-6.1/800-v6.0-0002-leds-bcm63138-add-support-for-BCM63138-controller.patch create mode 100644 target/linux/generic/backport-6.1/801-v6.0-0001-dt-bindings-leds-leds-bcm63138-unify-full-stops-in-d.patch create mode 100644 target/linux/generic/backport-6.1/801-v6.0-0002-leds-add-help-info-about-BCM63138-module-name.patch create mode 100644 target/linux/generic/backport-6.1/801-v6.0-0003-leds-leds-bcm63138-get-rid-of-LED_OFF.patch create mode 100644 target/linux/generic/backport-6.1/802-v6.1-0001-nvmem-add-driver-handling-U-Boot-environment-variabl.patch create mode 100644 target/linux/generic/backport-6.1/802-v6.1-0002-nvmem-u-boot-env-find-Device-Tree-nodes-for-NVMEM-ce.patch create mode 100644 target/linux/generic/backport-6.1/802-v6.1-0003-nvmem-u-boot-env-fix-crc32-casting-type.patch create mode 100644 target/linux/generic/backport-6.1/803-v5.19-nvmem-core-support-passing-DT-node-in-cell-info.patch create mode 100644 target/linux/generic/backport-6.1/810-v5.17-net-qmi_wwan-add-ZTE-MF286D-modem-19d2-1485.patch create mode 100644 target/linux/generic/backport-6.1/821-v5.16-Bluetooth-btusb-Support-public-address-configuration.patch create mode 100644 target/linux/generic/backport-6.1/822-v5.17-Bluetooth-btusb-Fix-application-of-sizeof-to-pointer.patch create mode 100644 target/linux/generic/backport-6.1/823-v5.18-Bluetooth-btusb-Add-a-new-PID-VID-13d3-3567-for-MT79.patch create mode 100644 target/linux/generic/backport-6.1/824-v5.19-Bluetooth-btusb-Add-a-new-PID-VID-0489-e0c8-for-MT79.patch create mode 100644 target/linux/generic/backport-6.1/825-v6.1-Bluetooth-btusb-Add-a-new-VID-PID-0e8d-0608-for-MT79.patch create mode 100644 target/linux/generic/backport-6.1/860-v5.17-MIPS-ath79-drop-_machine_restart-again.patch create mode 100644 target/linux/generic/backport-6.1/870-v5.18-hwmon-lm70-Add-ti-tmp125-support.patch create mode 100644 target/linux/generic/backport-6.1/880-v5.19-cdc_ether-export-usbnet_cdc_zte_rx_fixup.patch create mode 100644 target/linux/generic/backport-6.1/881-v5.19-rndis_host-enable-the-bogus-MAC-fixup-for-ZTE-device.patch create mode 100644 target/linux/generic/backport-6.1/882-v5.19-rndis_host-limit-scope-of-bogus-MAC-address-detectio.patch create mode 100644 target/linux/generic/config-6.1 create mode 100644 target/linux/generic/hack-6.1/204-module_strip.patch create mode 100644 target/linux/generic/hack-6.1/205-kconfig-exit.patch create mode 100644 target/linux/generic/hack-6.1/210-darwin_scripts_include.patch create mode 100644 target/linux/generic/hack-6.1/211-darwin-uuid-typedef-clash.patch create mode 100644 target/linux/generic/hack-6.1/212-tools_portability.patch create mode 100644 target/linux/generic/hack-6.1/214-spidev_h_portability.patch create mode 100644 target/linux/generic/hack-6.1/220-arm-gc_sections.patch create mode 100644 target/linux/generic/hack-6.1/221-module_exports.patch create mode 100644 target/linux/generic/hack-6.1/230-openwrt_lzma_options.patch create mode 100644 target/linux/generic/hack-6.1/250-netfilter_depends.patch create mode 100644 target/linux/generic/hack-6.1/251-kconfig.patch create mode 100644 target/linux/generic/hack-6.1/253-ksmbd-config.patch create mode 100644 target/linux/generic/hack-6.1/259-regmap_dynamic.patch create mode 100644 target/linux/generic/hack-6.1/260-crypto_test_dependencies.patch create mode 100644 target/linux/generic/hack-6.1/261-lib-arc4-unhide.patch create mode 100644 target/linux/generic/hack-6.1/280-rfkill-stubs.patch create mode 100644 target/linux/generic/hack-6.1/300-MIPS-r4k_cache-use-more-efficient-cache-blast.patch create mode 100644 target/linux/generic/hack-6.1/321-powerpc_crtsavres_prereq.patch create mode 100644 target/linux/generic/hack-6.1/402-mtd-blktrans-call-add-disks-after-mtd-device.patch create mode 100644 target/linux/generic/hack-6.1/410-block-fit-partition-parser.patch create mode 100644 target/linux/generic/hack-6.1/420-mtd-set-rootfs-to-be-root-dev.patch create mode 100644 target/linux/generic/hack-6.1/421-drivers-mtd-parsers-add-nvmem-support-to-cmdlinepart.patch create mode 100644 target/linux/generic/hack-6.1/430-mtk-bmt-support.patch create mode 100644 target/linux/generic/hack-6.1/600-bridge_offload.patch create mode 100644 target/linux/generic/hack-6.1/601-of_net-add-mac-address-ascii-support.patch create mode 100644 target/linux/generic/hack-6.1/645-netfilter-connmark-introduce-set-dscpmark.patch create mode 100644 target/linux/generic/hack-6.1/650-netfilter-add-xt_FLOWOFFLOAD-target.patch create mode 100644 target/linux/generic/hack-6.1/651-wireless_mesh_header.patch create mode 100644 target/linux/generic/hack-6.1/660-fq_codel_defaults.patch create mode 100644 target/linux/generic/hack-6.1/661-kernel-ct-size-the-hashtable-more-adequately.patch create mode 100644 target/linux/generic/hack-6.1/700-swconfig_switch_drivers.patch create mode 100644 target/linux/generic/hack-6.1/711-net-dsa-mv88e6xxx-disable-ATU-violation.patch create mode 100644 target/linux/generic/hack-6.1/720-net-phy-add-aqr-phys.patch create mode 100644 target/linux/generic/hack-6.1/721-net-add-packet-mangeling.patch create mode 100644 target/linux/generic/hack-6.1/722-net-phy-aquantia-enable-AQR112-and-AQR412.patch create mode 100644 target/linux/generic/hack-6.1/723-net-phy-aquantia-fix-system-side-protocol-mi.patch create mode 100644 target/linux/generic/hack-6.1/724-net-phy-aquantia-Add-AQR113-driver-support.patch create mode 100644 target/linux/generic/hack-6.1/725-net-phy-aquantia-add-PHY_IDs-for-AQR112-variants.patch create mode 100644 target/linux/generic/hack-6.1/760-net-usb-r8152-add-LED-configuration-from-OF.patch create mode 100644 target/linux/generic/hack-6.1/761-dt-bindings-net-add-RTL8152-binding-documentation.patch create mode 100644 target/linux/generic/hack-6.1/773-bgmac-add-srab-switch.patch create mode 100644 target/linux/generic/hack-6.1/780-usb-net-MeigLink_modem_support.patch create mode 100644 target/linux/generic/hack-6.1/790-SFP-GE-T-ignore-TX_FAULT.patch create mode 100644 target/linux/generic/hack-6.1/800-GPIO-add-named-gpio-exports.patch create mode 100644 target/linux/generic/hack-6.1/901-debloat_sock_diag.patch create mode 100644 target/linux/generic/hack-6.1/902-debloat_proc.patch create mode 100644 target/linux/generic/hack-6.1/904-debloat_dma_buf.patch create mode 100644 target/linux/generic/hack-6.1/910-kobject_uevent.patch create mode 100644 target/linux/generic/hack-6.1/911-kobject_add_broadcast_uevent.patch create mode 100644 target/linux/generic/hack-6.1/920-device_tree_cmdline.patch create mode 100644 target/linux/generic/hack-6.1/930-Revert-Revert-Revert-driver-core-Set-fw_devlink-on-b.patch create mode 100644 target/linux/generic/pending-6.1/100-compiler.h-only-include-asm-rwonce.h-for-kernel-code.patch create mode 100644 target/linux/generic/pending-6.1/101-Use-stddefs.h-instead-of-compiler.h.patch create mode 100644 target/linux/generic/pending-6.1/102-MIPS-only-process-negative-stack-offsets-on-stack-tr.patch create mode 100644 target/linux/generic/pending-6.1/103-kbuild-export-SUBARCH.patch create mode 100644 target/linux/generic/pending-6.1/120-Fix-alloc_node_mem_map-with-ARCH_PFN_OFFSET-calcu.patch create mode 100644 target/linux/generic/pending-6.1/130-add-linux-spidev-compatible-si3210.patch create mode 100644 target/linux/generic/pending-6.1/140-jffs2-use-.rename2-and-add-RENAME_WHITEOUT-support.patch create mode 100644 target/linux/generic/pending-6.1/141-jffs2-add-RENAME_EXCHANGE-support.patch create mode 100644 target/linux/generic/pending-6.1/142-jffs2-add-splice-ops.patch create mode 100644 target/linux/generic/pending-6.1/150-bridge_allow_receiption_on_disabled_port.patch create mode 100644 target/linux/generic/pending-6.1/190-rtc-rs5c372-support_alarms_up_to_1_week.patch create mode 100644 target/linux/generic/pending-6.1/191-rtc-rs5c372-let_the_alarm_to_be_used_as_wakeup_source.patch create mode 100644 target/linux/generic/pending-6.1/203-kallsyms_uncompressed.patch create mode 100644 target/linux/generic/pending-6.1/205-backtrace_module_info.patch create mode 100644 target/linux/generic/pending-6.1/240-remove-unsane-filenames-from-deps_initramfs-list.patch create mode 100644 target/linux/generic/pending-6.1/261-enable_wilink_platform_without_drivers.patch create mode 100644 target/linux/generic/pending-6.1/270-platform-mikrotik-build-bits.patch create mode 100644 target/linux/generic/pending-6.1/300-mips_expose_boot_raw.patch create mode 100644 target/linux/generic/pending-6.1/302-mips_no_branch_likely.patch create mode 100644 target/linux/generic/pending-6.1/305-mips_module_reloc.patch create mode 100644 target/linux/generic/pending-6.1/307-mips_highmem_offset.patch create mode 100644 target/linux/generic/pending-6.1/308-mips32r2_tune.patch create mode 100644 target/linux/generic/pending-6.1/310-arm_module_unresolved_weak_sym.patch create mode 100644 target/linux/generic/pending-6.1/330-MIPS-kexec-Accept-command-line-parameters-from-users.patch create mode 100644 target/linux/generic/pending-6.1/332-arc-add-OWRTDTB-section.patch create mode 100644 target/linux/generic/pending-6.1/333-arc-enable-unaligned-access-in-kernel-mode.patch create mode 100644 target/linux/generic/pending-6.1/342-powerpc-Enable-kernel-XZ-compression-option-on-PPC_8.patch create mode 100644 target/linux/generic/pending-6.1/400-mtd-mtdsplit-support.patch create mode 100644 target/linux/generic/pending-6.1/402-mtd-spi-nor-write-support-for-minor-aligned-partitions.patch create mode 100644 target/linux/generic/pending-6.1/420-mtd-redboot_space.patch create mode 100644 target/linux/generic/pending-6.1/430-mtd-add-myloader-partition-parser.patch create mode 100644 target/linux/generic/pending-6.1/431-mtd-bcm47xxpart-check-for-bad-blocks-when-calculatin.patch create mode 100644 target/linux/generic/pending-6.1/432-mtd-bcm47xxpart-detect-T_Meter-partition.patch create mode 100644 target/linux/generic/pending-6.1/435-mtd-add-routerbootpart-parser-config.patch create mode 100644 target/linux/generic/pending-6.1/460-mtd-cfi_cmdset_0002-no-erase_suspend.patch create mode 100644 target/linux/generic/pending-6.1/461-mtd-cfi_cmdset_0002-add-buffer-write-cmd-timeout.patch create mode 100644 target/linux/generic/pending-6.1/465-m25p80-mx-disable-software-protection.patch create mode 100644 target/linux/generic/pending-6.1/476-mtd-spi-nor-add-eon-en25q128.patch create mode 100644 target/linux/generic/pending-6.1/479-mtd-spi-nor-add-xtx-xt25f128b.patch create mode 100644 target/linux/generic/pending-6.1/481-mtd-spi-nor-add-support-for-Gigadevice-GD25D05.patch create mode 100644 target/linux/generic/pending-6.1/482-mtd-spi-nor-add-gd25q512.patch create mode 100644 target/linux/generic/pending-6.1/484-mtd-spi-nor-add-esmt-f25l16pa.patch create mode 100644 target/linux/generic/pending-6.1/485-mtd-spi-nor-add-xmc-xm25qh128c.patch create mode 100644 target/linux/generic/pending-6.1/486-01-mtd-spinand-add-support-for-ESMT-F50x1G41LB.patch create mode 100644 target/linux/generic/pending-6.1/487-mtd-spinand-Add-support-for-Etron-EM73D044VCx.patch create mode 100644 target/linux/generic/pending-6.1/490-ubi-auto-attach-mtd-device-named-ubi-or-data-on-boot.patch create mode 100644 target/linux/generic/pending-6.1/491-ubi-auto-create-ubiblock-device-for-rootfs.patch create mode 100644 target/linux/generic/pending-6.1/492-try-auto-mounting-ubi0-rootfs-in-init-do_mounts.c.patch create mode 100644 target/linux/generic/pending-6.1/493-ubi-set-ROOT_DEV-to-ubiblock-rootfs-if-unset.patch create mode 100644 target/linux/generic/pending-6.1/494-mtd-ubi-add-EOF-marker-support.patch create mode 100644 target/linux/generic/pending-6.1/496-dt-bindings-add-bindings-for-mtd-concat-devices.patch create mode 100644 target/linux/generic/pending-6.1/497-mtd-mtdconcat-add-dt-driver-for-concat-devices.patch create mode 100644 target/linux/generic/pending-6.1/498-mtd-spi-nor-locking-support-for-MX25L6405D.patch create mode 100644 target/linux/generic/pending-6.1/499-mtd-spi-nor-disable-16-bit-sr-for-macronix.patch create mode 100644 target/linux/generic/pending-6.1/500-fs_cdrom_dependencies.patch create mode 100644 target/linux/generic/pending-6.1/530-jffs2_make_lzma_available.patch create mode 100644 target/linux/generic/pending-6.1/532-jffs2_eofdetect.patch create mode 100644 target/linux/generic/pending-6.1/600-netfilter_conntrack_flush.patch create mode 100644 target/linux/generic/pending-6.1/610-netfilter_match_bypass_default_checks.patch create mode 100644 target/linux/generic/pending-6.1/611-netfilter_match_bypass_default_table.patch create mode 100644 target/linux/generic/pending-6.1/612-netfilter_match_reduce_memory_access.patch create mode 100644 target/linux/generic/pending-6.1/613-netfilter_optional_tcp_window_check.patch create mode 100644 target/linux/generic/pending-6.1/620-net_sched-codel-do-not-defer-queue-length-update.patch create mode 100644 target/linux/generic/pending-6.1/630-packet_socket_type.patch create mode 100644 target/linux/generic/pending-6.1/655-increase_skb_pad.patch create mode 100644 target/linux/generic/pending-6.1/666-Add-support-for-MAP-E-FMRs-mesh-mode.patch create mode 100644 target/linux/generic/pending-6.1/670-ipv6-allow-rejecting-with-source-address-failed-policy.patch create mode 100644 target/linux/generic/pending-6.1/671-net-provide-defines-for-_POLICY_FAILED-until-all-cod.patch create mode 100644 target/linux/generic/pending-6.1/680-NET-skip-GRO-for-foreign-MAC-addresses.patch create mode 100644 target/linux/generic/pending-6.1/682-of_net-add-mac-address-increment-support.patch create mode 100644 target/linux/generic/pending-6.1/683-of_net-add-mac-address-to-of-tree.patch create mode 100644 target/linux/generic/pending-6.1/684-of_net-do-mac-address-increment-only-once.patch create mode 100644 target/linux/generic/pending-6.1/700-netfilter-nft_flow_offload-handle-netdevice-events-f.patch create mode 100644 target/linux/generic/pending-6.1/702-net-ethernet-mtk_eth_soc-enable-threaded-NAPI.patch create mode 100644 target/linux/generic/pending-6.1/703-phy-add-detach-callback-to-struct-phy_driver.patch create mode 100644 target/linux/generic/pending-6.1/705-net-dsa-tag_mtk-add-padding-for-tx-packets.patch create mode 100644 target/linux/generic/pending-6.1/710-bridge-add-knob-for-filtering-rx-tx-BPDU-pack.patch create mode 100644 target/linux/generic/pending-6.1/721-net-phy-realtek-rtl8221-allow-to-configure-SERDES-mo.patch create mode 100644 target/linux/generic/pending-6.1/723-net-mt7531-ensure-all-MACs-are-powered-down-before-r.patch create mode 100644 target/linux/generic/pending-6.1/724-net-mtk_sgmii-implement-mtk_pcs_ops.patch create mode 100644 target/linux/generic/pending-6.1/725-net-mtk_sgmii-fix-powering-up-the-SGMII-phy.patch create mode 100644 target/linux/generic/pending-6.1/726-net-mtk_sgmii-ensure-the-SGMII-PHY-is-powered-down-o.patch create mode 100644 target/linux/generic/pending-6.1/727-net-mtk_sgmii-mtk_pcs_setup_mode_an-don-t-rely-on-re.patch create mode 100644 target/linux/generic/pending-6.1/728-net-mtk_sgmii-set-the-speed-according-to-the-phy-int.patch create mode 100644 target/linux/generic/pending-6.1/729-net-mtk_eth_soc-improve-comment.patch create mode 100644 target/linux/generic/pending-6.1/730-mtk_sgmii-enable-PCS-polling-to-allow-SFP-work.patch create mode 100644 target/linux/generic/pending-6.1/768-net-dsa-mv88e6xxx-Request-assisted-learning-on-CPU-port.patch create mode 100644 target/linux/generic/pending-6.1/780-ARM-kirkwood-add-missing-linux-if_ether.h-for-ETH_AL.patch create mode 100644 target/linux/generic/pending-6.1/800-bcma-get-SoC-device-struct-copy-its-DMA-params-to-th.patch create mode 100644 target/linux/generic/pending-6.1/801-gpio-gpio-cascade-add-generic-GPIO-cascade.patch create mode 100644 target/linux/generic/pending-6.1/810-pci_disable_common_quirks.patch create mode 100644 target/linux/generic/pending-6.1/811-pci_disable_usb_common_quirks.patch create mode 100644 target/linux/generic/pending-6.1/820-w1-gpio-fix-problem-with-platfom-data-in-w1-gpio.patch create mode 100644 target/linux/generic/pending-6.1/834-ledtrig-libata.patch create mode 100644 target/linux/generic/pending-6.1/840-hwrng-bcm2835-set-quality-to-1000.patch create mode 100644 target/linux/generic/pending-6.1/850-0023-PCI-aardvark-Make-main-irq_chip-structure-a-static-d.patch create mode 100644 target/linux/generic/pending-6.1/920-mangle_bootargs.patch diff --git a/target/linux/generic/backport-6.1/005-v5.17-01-Kbuild-use-Wdeclaration-after-statement.patch b/target/linux/generic/backport-6.1/005-v5.17-01-Kbuild-use-Wdeclaration-after-statement.patch new file mode 100644 index 0000000000..ff4c6d7041 --- /dev/null +++ b/target/linux/generic/backport-6.1/005-v5.17-01-Kbuild-use-Wdeclaration-after-statement.patch @@ -0,0 +1,73 @@ +From 2fd7e7f9317d3048a14026816d081b08ba98ea8e Mon Sep 17 00:00:00 2001 +From: Mark Rutland +Date: Tue, 8 Mar 2022 22:56:13 +0100 +Subject: [PATCH 1/3] Kbuild: use -Wdeclaration-after-statement + +The kernel is moving from using `-std=gnu89` to `-std=gnu11`, permitting +the use of additional C11 features such as for-loop initial declarations. + +One contentious aspect of C99 is that it permits mixed declarations and +code, and for now at least, it seems preferable to enforce that +declarations must come first. + +These warnings were already enabled in the kernel itself, but not +for KBUILD_USERCFLAGS or the compat VDSO on arch/arm64, which uses +a separate set of CFLAGS. + +This patch fixes an existing violation in modpost.c, which is not +reported because of the missing flag in KBUILD_USERCFLAGS: + +| scripts/mod/modpost.c: In function ‘match’: +| scripts/mod/modpost.c:837:3: warning: ISO C90 forbids mixed declarations and code [-Wdeclaration-after-statement] +| 837 | const char *endp = p + strlen(p) - 1; +| | ^~~~~ + +Signed-off-by: Mark Rutland +[arnd: don't add a duplicate flag to the default set, update changelog] +Signed-off-by: Arnd Bergmann +Reviewed-by: Nathan Chancellor +Reviewed-by: Nick Desaulniers +Tested-by: Sedat Dilek # LLVM/Clang v13.0.0 (x86-64) +Signed-off-by: Masahiro Yamada +--- + Makefile | 3 ++- + arch/arm64/kernel/vdso32/Makefile | 1 + + scripts/mod/modpost.c | 4 +++- + 3 files changed, 6 insertions(+), 2 deletions(-) + +--- a/Makefile ++++ b/Makefile +@@ -432,7 +432,8 @@ HOSTCXX = g++ + endif + + export KBUILD_USERCFLAGS := -Wall -Wmissing-prototypes -Wstrict-prototypes \ +- -O2 -fomit-frame-pointer -std=gnu89 ++ -O2 -fomit-frame-pointer -std=gnu89 \ ++ -Wdeclaration-after-statement + export KBUILD_USERLDFLAGS := + + KBUILD_HOSTCFLAGS := $(KBUILD_USERCFLAGS) $(HOST_LFS_CFLAGS) $(HOSTCFLAGS) +--- a/arch/arm64/kernel/vdso32/Makefile ++++ b/arch/arm64/kernel/vdso32/Makefile +@@ -76,6 +76,7 @@ VDSO_CFLAGS += -Wall -Wundef -Wstrict-pr + -fno-strict-aliasing -fno-common \ + -Werror-implicit-function-declaration \ + -Wno-format-security \ ++ -Wdeclaration-after-statement \ + -std=gnu89 + VDSO_CFLAGS += -O2 + # Some useful compiler-dependent flags from top-level Makefile +--- a/scripts/mod/modpost.c ++++ b/scripts/mod/modpost.c +@@ -833,8 +833,10 @@ static int match(const char *sym, const + { + const char *p; + while (*pat) { ++ const char *endp; ++ + p = *pat++; +- const char *endp = p + strlen(p) - 1; ++ endp = p + strlen(p) - 1; + + /* "*foo*" */ + if (*p == '*' && *endp == '*') { diff --git a/target/linux/generic/backport-6.1/005-v5.17-02-Kbuild-move-to-std-gnu11.patch b/target/linux/generic/backport-6.1/005-v5.17-02-Kbuild-move-to-std-gnu11.patch new file mode 100644 index 0000000000..d22eefb773 --- /dev/null +++ b/target/linux/generic/backport-6.1/005-v5.17-02-Kbuild-move-to-std-gnu11.patch @@ -0,0 +1,60 @@ +From b810c8e719ea082e47c7a8f7cf878bc84fa2455d Mon Sep 17 00:00:00 2001 +From: Arnd Bergmann +Date: Tue, 8 Mar 2022 22:56:14 +0100 +Subject: [PATCH 2/3] Kbuild: move to -std=gnu11 + +During a patch discussion, Linus brought up the option of changing +the C standard version from gnu89 to gnu99, which allows using variable +declaration inside of a for() loop. While the C99, C11 and later standards +introduce many other features, most of these are already available in +gnu89 as GNU extensions as well. + +An earlier attempt to do this when gcc-5 started defaulting to +-std=gnu11 failed because at the time that caused warnings about +designated initializers with older compilers. Now that gcc-5.1 is +the minimum compiler version used for building kernels, that is no +longer a concern. Similarly, the behavior of 'inline' functions changes +between gnu89 using gnu_inline behavior and gnu11 using standard c99+ +behavior, but this was taken care of by defining 'inline' to include +__attribute__((gnu_inline)) in order to allow building with clang a +while ago. + +Nathan Chancellor reported a new -Wdeclaration-after-statement +warning that appears in a system header on arm, this still needs a +workaround. + +The differences between gnu99, gnu11, gnu1x and gnu17 are fairly +minimal and mainly impact warnings at the -Wpedantic level that the +kernel never enables. Between these, gnu11 is the newest version +that is supported by all supported compiler versions, though it is +only the default on gcc-5, while all other supported versions of +gcc or clang default to gnu1x/gnu17. + +Link: https://lore.kernel.org/lkml/CAHk-=wiyCH7xeHcmiFJ-YgXUy2Jaj7pnkdKpcovt8fYbVFW3TA@mail.gmail.com/ +Link: https://github.com/ClangBuiltLinux/linux/issues/1603 +Suggested-by: Linus Torvalds +Acked-by: Marco Elver +Acked-by: Jani Nikula +Acked-by: David Sterba +Tested-by: Sedat Dilek +Reviewed-by: Alex Shi +Reviewed-by: Nick Desaulniers +Reviewed-by: Miguel Ojeda +Signed-off-by: Arnd Bergmann +Reviewed-by: Nathan Chancellor +Signed-off-by: Masahiro Yamada +--- + Makefile | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/Makefile ++++ b/Makefile +@@ -516,7 +516,7 @@ KBUILD_CFLAGS := -Wall -Wundef -Werror + -fno-strict-aliasing -fno-common -fshort-wchar -fno-PIE \ + -Werror=implicit-function-declaration -Werror=implicit-int \ + -Werror=return-type -Wno-format-security \ +- -std=gnu89 ++ -std=gnu11 + KBUILD_CPPFLAGS := -D__KERNEL__ + KBUILD_AFLAGS_KERNEL := + KBUILD_CFLAGS_KERNEL := diff --git a/target/linux/generic/backport-6.1/005-v5.17-03-Kbuild-use-std-gnu11-for-KBUILD_USERCFLAGS.patch b/target/linux/generic/backport-6.1/005-v5.17-03-Kbuild-use-std-gnu11-for-KBUILD_USERCFLAGS.patch new file mode 100644 index 0000000000..198c9e671a --- /dev/null +++ b/target/linux/generic/backport-6.1/005-v5.17-03-Kbuild-use-std-gnu11-for-KBUILD_USERCFLAGS.patch @@ -0,0 +1,43 @@ +From 40337d6f3d677aee7ad3052ae662d3f53dd4d5cb Mon Sep 17 00:00:00 2001 +From: Arnd Bergmann +Date: Tue, 8 Mar 2022 22:56:15 +0100 +Subject: [PATCH 3/3] Kbuild: use -std=gnu11 for KBUILD_USERCFLAGS + +As we change the C language standard for the kernel from gnu89 to +gnu11, it makes sense to also update the version for user space +compilation. + +Some users have older native compilers than what they use for +kernel builds, so I considered using gnu99 as the default version +for wider compatibility with gcc-4.6 and earlier. + +However, testing with older compilers showed that we already require +HOSTCC version 5.1 as well because a lot of host tools include +linux/compiler.h that uses __has_attribute(): + + CC tools/objtool/exec-cmd.o +In file included from tools/include/linux/compiler_types.h:36:0, + from tools/include/linux/compiler.h:5, + from exec-cmd.c:2: +tools/include/linux/compiler-gcc.h:19:5: error: "__has_attribute" is not defined [-Werror=undef] + +Signed-off-by: Arnd Bergmann +Reviewed-by: Nathan Chancellor +Reviewed-by: Nick Desaulniers +Tested-by: Sedat Dilek +Signed-off-by: Masahiro Yamada +--- + Makefile | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/Makefile ++++ b/Makefile +@@ -432,7 +432,7 @@ HOSTCXX = g++ + endif + + export KBUILD_USERCFLAGS := -Wall -Wmissing-prototypes -Wstrict-prototypes \ +- -O2 -fomit-frame-pointer -std=gnu89 \ ++ -O2 -fomit-frame-pointer -std=gnu11 \ + -Wdeclaration-after-statement + export KBUILD_USERLDFLAGS := + diff --git a/target/linux/generic/backport-6.1/020-v6.1-01-mm-x86-arm64-add-arch_has_hw_pte_young.patch b/target/linux/generic/backport-6.1/020-v6.1-01-mm-x86-arm64-add-arch_has_hw_pte_young.patch new file mode 100644 index 0000000000..48bcaf3e3e --- /dev/null +++ b/target/linux/generic/backport-6.1/020-v6.1-01-mm-x86-arm64-add-arch_has_hw_pte_young.patch @@ -0,0 +1,169 @@ +From a8e6015d9534f39abc08e6804566af059e498a60 Mon Sep 17 00:00:00 2001 +From: Yu Zhao +Date: Wed, 4 Aug 2021 01:31:34 -0600 +Subject: [PATCH 01/10] mm: x86, arm64: add arch_has_hw_pte_young() + +Some architectures automatically set the accessed bit in PTEs, e.g., +x86 and arm64 v8.2. On architectures that do not have this capability, +clearing the accessed bit in a PTE triggers a page fault following the +TLB miss of this PTE. + +Being aware of this capability can help make better decisions, i.e., +whether to limit the size of each batch of PTEs and the burst of +batches when clearing the accessed bit. + +Signed-off-by: Yu Zhao +Change-Id: Ib49b44fb56df3333a2ff1fcc496fb1980b976e7a +--- + arch/arm64/include/asm/cpufeature.h | 5 +++++ + arch/arm64/include/asm/pgtable.h | 13 ++++++++----- + arch/arm64/kernel/cpufeature.c | 10 ++++++++++ + arch/arm64/tools/cpucaps | 1 + + arch/x86/include/asm/pgtable.h | 6 +++--- + include/linux/pgtable.h | 13 +++++++++++++ + mm/memory.c | 14 +------------- + 7 files changed, 41 insertions(+), 21 deletions(-) + +--- a/arch/arm64/include/asm/cpufeature.h ++++ b/arch/arm64/include/asm/cpufeature.h +@@ -808,6 +808,11 @@ static inline bool system_supports_tlb_r + cpus_have_const_cap(ARM64_HAS_TLB_RANGE); + } + ++static inline bool system_has_hw_af(void) ++{ ++ return IS_ENABLED(CONFIG_ARM64_HW_AFDBM) && cpus_have_const_cap(ARM64_HW_AF); ++} ++ + extern int do_emulate_mrs(struct pt_regs *regs, u32 sys_reg, u32 rt); + + static inline u32 id_aa64mmfr0_parange_to_phys_shift(int parange) +--- a/arch/arm64/include/asm/pgtable.h ++++ b/arch/arm64/include/asm/pgtable.h +@@ -999,13 +999,16 @@ static inline void update_mmu_cache(stru + * page after fork() + CoW for pfn mappings. We don't always have a + * hardware-managed access flag on arm64. + */ +-static inline bool arch_faults_on_old_pte(void) ++static inline bool arch_has_hw_pte_young(bool local) + { +- WARN_ON(preemptible()); ++ if (local) { ++ WARN_ON(preemptible()); ++ return cpu_has_hw_af(); ++ } + +- return !cpu_has_hw_af(); ++ return system_has_hw_af(); + } +-#define arch_faults_on_old_pte arch_faults_on_old_pte ++#define arch_has_hw_pte_young arch_has_hw_pte_young + + /* + * Experimentally, it's cheap to set the access flag in hardware and we +@@ -1013,7 +1016,7 @@ static inline bool arch_faults_on_old_pt + */ + static inline bool arch_wants_old_prefaulted_pte(void) + { +- return !arch_faults_on_old_pte(); ++ return arch_has_hw_pte_young(true); + } + #define arch_wants_old_prefaulted_pte arch_wants_old_prefaulted_pte + +--- a/arch/arm64/kernel/cpufeature.c ++++ b/arch/arm64/kernel/cpufeature.c +@@ -2187,6 +2187,16 @@ static const struct arm64_cpu_capabiliti + .matches = has_hw_dbm, + .cpu_enable = cpu_enable_hw_dbm, + }, ++ { ++ .desc = "Hardware update of the Access flag", ++ .type = ARM64_CPUCAP_SYSTEM_FEATURE, ++ .capability = ARM64_HW_AF, ++ .sys_reg = SYS_ID_AA64MMFR1_EL1, ++ .sign = FTR_UNSIGNED, ++ .field_pos = ID_AA64MMFR1_HADBS_SHIFT, ++ .min_field_value = 1, ++ .matches = has_cpuid_feature, ++ }, + #endif + { + .desc = "CRC32 instructions", +--- a/arch/arm64/tools/cpucaps ++++ b/arch/arm64/tools/cpucaps +@@ -35,6 +35,7 @@ HAS_STAGE2_FWB + HAS_SYSREG_GIC_CPUIF + HAS_TLB_RANGE + HAS_VIRT_HOST_EXTN ++HW_AF + HW_DBM + KVM_PROTECTED_MODE + MISMATCHED_CACHE_TYPE +--- a/arch/x86/include/asm/pgtable.h ++++ b/arch/x86/include/asm/pgtable.h +@@ -1397,10 +1397,10 @@ static inline bool arch_has_pfn_modify_c + return boot_cpu_has_bug(X86_BUG_L1TF); + } + +-#define arch_faults_on_old_pte arch_faults_on_old_pte +-static inline bool arch_faults_on_old_pte(void) ++#define arch_has_hw_pte_young arch_has_hw_pte_young ++static inline bool arch_has_hw_pte_young(bool local) + { +- return false; ++ return true; + } + + #endif /* __ASSEMBLY__ */ +--- a/include/linux/pgtable.h ++++ b/include/linux/pgtable.h +@@ -259,6 +259,19 @@ static inline int pmdp_clear_flush_young + #endif /* CONFIG_TRANSPARENT_HUGEPAGE */ + #endif + ++#ifndef arch_has_hw_pte_young ++/* ++ * Return whether the accessed bit is supported by the local CPU or all CPUs. ++ * ++ * Those arches which have hw access flag feature need to implement their own ++ * helper. By default, "false" means pagefault will be hit on old pte. ++ */ ++static inline bool arch_has_hw_pte_young(bool local) ++{ ++ return false; ++} ++#endif ++ + #ifndef __HAVE_ARCH_PTEP_GET_AND_CLEAR + static inline pte_t ptep_get_and_clear(struct mm_struct *mm, + unsigned long address, +--- a/mm/memory.c ++++ b/mm/memory.c +@@ -121,18 +121,6 @@ int randomize_va_space __read_mostly = + 2; + #endif + +-#ifndef arch_faults_on_old_pte +-static inline bool arch_faults_on_old_pte(void) +-{ +- /* +- * Those arches which don't have hw access flag feature need to +- * implement their own helper. By default, "true" means pagefault +- * will be hit on old pte. +- */ +- return true; +-} +-#endif +- + #ifndef arch_wants_old_prefaulted_pte + static inline bool arch_wants_old_prefaulted_pte(void) + { +@@ -2782,7 +2770,7 @@ static inline bool cow_user_page(struct + * On architectures with software "accessed" bits, we would + * take a double page fault, so mark it accessed here. + */ +- if (arch_faults_on_old_pte() && !pte_young(vmf->orig_pte)) { ++ if (!arch_has_hw_pte_young(true) && !pte_young(vmf->orig_pte)) { + pte_t entry; + + vmf->pte = pte_offset_map_lock(mm, vmf->pmd, addr, &vmf->ptl); diff --git a/target/linux/generic/backport-6.1/020-v6.1-02-mm-x86-add-CONFIG_ARCH_HAS_NONLEAF_PMD_YOUNG.patch b/target/linux/generic/backport-6.1/020-v6.1-02-mm-x86-add-CONFIG_ARCH_HAS_NONLEAF_PMD_YOUNG.patch new file mode 100644 index 0000000000..785af275f5 --- /dev/null +++ b/target/linux/generic/backport-6.1/020-v6.1-02-mm-x86-add-CONFIG_ARCH_HAS_NONLEAF_PMD_YOUNG.patch @@ -0,0 +1,111 @@ +From f8b663bbfa30af5515e222fd74df20ea4e8393a2 Mon Sep 17 00:00:00 2001 +From: Yu Zhao +Date: Sat, 26 Sep 2020 21:17:18 -0600 +Subject: [PATCH 02/10] mm: x86: add CONFIG_ARCH_HAS_NONLEAF_PMD_YOUNG + +Some architectures support the accessed bit on non-leaf PMD entries, +e.g., x86_64 sets the accessed bit on a non-leaf PMD entry when using +it as part of linear address translation [1]. As an optimization, page +table walkers who are interested in the accessed bit can skip the PTEs +under a non-leaf PMD entry if the accessed bit is cleared on this PMD +entry. + +Although an inline function may be preferable, this capability is +added as a configuration option to look consistent when used with the +existing macros. + +[1]: Intel 64 and IA-32 Architectures Software Developer's Manual + Volume 3 (June 2021), section 4.8 + +Signed-off-by: Yu Zhao +Tested-by: Konstantin Kharlamov +Change-Id: I1a17be3ae926f721f7b17ea1539e5c39e8c4f9a8 +--- + arch/Kconfig | 9 +++++++++ + arch/x86/Kconfig | 1 + + arch/x86/include/asm/pgtable.h | 3 ++- + arch/x86/mm/pgtable.c | 5 ++++- + include/linux/pgtable.h | 4 ++-- + 5 files changed, 18 insertions(+), 4 deletions(-) + +--- a/arch/Kconfig ++++ b/arch/Kconfig +@@ -1295,6 +1295,15 @@ config ARCH_HAS_ELFCORE_COMPAT + config ARCH_HAS_PARANOID_L1D_FLUSH + bool + ++config ARCH_HAS_NONLEAF_PMD_YOUNG ++ bool ++ depends on PGTABLE_LEVELS > 2 ++ help ++ Architectures that select this are able to set the accessed bit on ++ non-leaf PMD entries in addition to leaf PTE entries where pages are ++ mapped. For them, page table walkers that clear the accessed bit may ++ stop at non-leaf PMD entries if they do not see the accessed bit. ++ + source "kernel/gcov/Kconfig" + + source "scripts/gcc-plugins/Kconfig" +--- a/arch/x86/Kconfig ++++ b/arch/x86/Kconfig +@@ -84,6 +84,7 @@ config X86 + select ARCH_HAS_PMEM_API if X86_64 + select ARCH_HAS_PTE_DEVMAP if X86_64 + select ARCH_HAS_PTE_SPECIAL ++ select ARCH_HAS_NONLEAF_PMD_YOUNG if X86_64 + select ARCH_HAS_UACCESS_FLUSHCACHE if X86_64 + select ARCH_HAS_COPY_MC if X86_64 + select ARCH_HAS_SET_MEMORY +--- a/arch/x86/include/asm/pgtable.h ++++ b/arch/x86/include/asm/pgtable.h +@@ -817,7 +817,8 @@ static inline unsigned long pmd_page_vad + + static inline int pmd_bad(pmd_t pmd) + { +- return (pmd_flags(pmd) & ~_PAGE_USER) != _KERNPG_TABLE; ++ return (pmd_flags(pmd) & ~(_PAGE_USER | _PAGE_ACCESSED)) != ++ (_KERNPG_TABLE & ~_PAGE_ACCESSED); + } + + static inline unsigned long pages_to_mb(unsigned long npg) +--- a/arch/x86/mm/pgtable.c ++++ b/arch/x86/mm/pgtable.c +@@ -550,7 +550,7 @@ int ptep_test_and_clear_young(struct vm_ + return ret; + } + +-#ifdef CONFIG_TRANSPARENT_HUGEPAGE ++#if defined(CONFIG_TRANSPARENT_HUGEPAGE) || defined(CONFIG_ARCH_HAS_NONLEAF_PMD_YOUNG) + int pmdp_test_and_clear_young(struct vm_area_struct *vma, + unsigned long addr, pmd_t *pmdp) + { +@@ -562,6 +562,9 @@ int pmdp_test_and_clear_young(struct vm_ + + return ret; + } ++#endif ++ ++#ifdef CONFIG_TRANSPARENT_HUGEPAGE + int pudp_test_and_clear_young(struct vm_area_struct *vma, + unsigned long addr, pud_t *pudp) + { +--- a/include/linux/pgtable.h ++++ b/include/linux/pgtable.h +@@ -212,7 +212,7 @@ static inline int ptep_test_and_clear_yo + #endif + + #ifndef __HAVE_ARCH_PMDP_TEST_AND_CLEAR_YOUNG +-#ifdef CONFIG_TRANSPARENT_HUGEPAGE ++#if defined(CONFIG_TRANSPARENT_HUGEPAGE) || defined(CONFIG_ARCH_HAS_NONLEAF_PMD_YOUNG) + static inline int pmdp_test_and_clear_young(struct vm_area_struct *vma, + unsigned long address, + pmd_t *pmdp) +@@ -233,7 +233,7 @@ static inline int pmdp_test_and_clear_yo + BUILD_BUG(); + return 0; + } +-#endif /* CONFIG_TRANSPARENT_HUGEPAGE */ ++#endif /* CONFIG_TRANSPARENT_HUGEPAGE || CONFIG_ARCH_HAS_NONLEAF_PMD_YOUNG */ + #endif + + #ifndef __HAVE_ARCH_PTEP_CLEAR_YOUNG_FLUSH diff --git a/target/linux/generic/backport-6.1/020-v6.1-03-mm-vmscan.c-refactor-shrink_node.patch b/target/linux/generic/backport-6.1/020-v6.1-03-mm-vmscan.c-refactor-shrink_node.patch new file mode 100644 index 0000000000..f466f1105c --- /dev/null +++ b/target/linux/generic/backport-6.1/020-v6.1-03-mm-vmscan.c-refactor-shrink_node.patch @@ -0,0 +1,224 @@ +From a810f8e2f1bdd0707eaf05c8b4ba84a3ff2801bd Mon Sep 17 00:00:00 2001 +From: Yu Zhao +Date: Sun, 27 Sep 2020 20:49:08 -0600 +Subject: [PATCH 03/10] mm/vmscan.c: refactor shrink_node() + +This patch refactors shrink_node(). This will make the upcoming +changes to mm/vmscan.c more readable. + +Signed-off-by: Yu Zhao +Tested-by: Konstantin Kharlamov +Change-Id: Iae734b5b4030205b7db6e8c841f747b6f6ae1a04 +--- + mm/vmscan.c | 186 +++++++++++++++++++++++++++------------------------- + 1 file changed, 98 insertions(+), 88 deletions(-) + +--- a/mm/vmscan.c ++++ b/mm/vmscan.c +@@ -2562,6 +2562,103 @@ enum scan_balance { + SCAN_FILE, + }; + ++static void prepare_scan_count(pg_data_t *pgdat, struct scan_control *sc) ++{ ++ unsigned long file; ++ struct lruvec *target_lruvec; ++ ++ target_lruvec = mem_cgroup_lruvec(sc->target_mem_cgroup, pgdat); ++ ++ /* ++ * Determine the scan balance between anon and file LRUs. ++ */ ++ spin_lock_irq(&target_lruvec->lru_lock); ++ sc->anon_cost = target_lruvec->anon_cost; ++ sc->file_cost = target_lruvec->file_cost; ++ spin_unlock_irq(&target_lruvec->lru_lock); ++ ++ /* ++ * Target desirable inactive:active list ratios for the anon ++ * and file LRU lists. ++ */ ++ if (!sc->force_deactivate) { ++ unsigned long refaults; ++ ++ refaults = lruvec_page_state(target_lruvec, ++ WORKINGSET_ACTIVATE_ANON); ++ if (refaults != target_lruvec->refaults[0] || ++ inactive_is_low(target_lruvec, LRU_INACTIVE_ANON)) ++ sc->may_deactivate |= DEACTIVATE_ANON; ++ else ++ sc->may_deactivate &= ~DEACTIVATE_ANON; ++ ++ /* ++ * When refaults are being observed, it means a new ++ * workingset is being established. Deactivate to get ++ * rid of any stale active pages quickly. ++ */ ++ refaults = lruvec_page_state(target_lruvec, ++ WORKINGSET_ACTIVATE_FILE); ++ if (refaults != target_lruvec->refaults[1] || ++ inactive_is_low(target_lruvec, LRU_INACTIVE_FILE)) ++ sc->may_deactivate |= DEACTIVATE_FILE; ++ else ++ sc->may_deactivate &= ~DEACTIVATE_FILE; ++ } else ++ sc->may_deactivate = DEACTIVATE_ANON | DEACTIVATE_FILE; ++ ++ /* ++ * If we have plenty of inactive file pages that aren't ++ * thrashing, try to reclaim those first before touching ++ * anonymous pages. ++ */ ++ file = lruvec_page_state(target_lruvec, NR_INACTIVE_FILE); ++ if (file >> sc->priority && !(sc->may_deactivate & DEACTIVATE_FILE)) ++ sc->cache_trim_mode = 1; ++ else ++ sc->cache_trim_mode = 0; ++ ++ /* ++ * Prevent the reclaimer from falling into the cache trap: as ++ * cache pages start out inactive, every cache fault will tip ++ * the scan balance towards the file LRU. And as the file LRU ++ * shrinks, so does the window for rotation from references. ++ * This means we have a runaway feedback loop where a tiny ++ * thrashing file LRU becomes infinitely more attractive than ++ * anon pages. Try to detect this based on file LRU size. ++ */ ++ if (!cgroup_reclaim(sc)) { ++ unsigned long total_high_wmark = 0; ++ unsigned long free, anon; ++ int z; ++ ++ free = sum_zone_node_page_state(pgdat->node_id, NR_FREE_PAGES); ++ file = node_page_state(pgdat, NR_ACTIVE_FILE) + ++ node_page_state(pgdat, NR_INACTIVE_FILE); ++ ++ for (z = 0; z < MAX_NR_ZONES; z++) { ++ struct zone *zone = &pgdat->node_zones[z]; ++ ++ if (!managed_zone(zone)) ++ continue; ++ ++ total_high_wmark += high_wmark_pages(zone); ++ } ++ ++ /* ++ * Consider anon: if that's low too, this isn't a ++ * runaway file reclaim problem, but rather just ++ * extreme pressure. Reclaim as per usual then. ++ */ ++ anon = node_page_state(pgdat, NR_INACTIVE_ANON); ++ ++ sc->file_is_tiny = ++ file + free <= total_high_wmark && ++ !(sc->may_deactivate & DEACTIVATE_ANON) && ++ anon >> sc->priority; ++ } ++} ++ + /* + * Determine how aggressively the anon and file LRU lists should be + * scanned. The relative value of each set of LRU lists is determined +@@ -3032,7 +3129,6 @@ static void shrink_node(pg_data_t *pgdat + unsigned long nr_reclaimed, nr_scanned; + struct lruvec *target_lruvec; + bool reclaimable = false; +- unsigned long file; + + target_lruvec = mem_cgroup_lruvec(sc->target_mem_cgroup, pgdat); + +@@ -3048,93 +3144,7 @@ again: + nr_reclaimed = sc->nr_reclaimed; + nr_scanned = sc->nr_scanned; + +- /* +- * Determine the scan balance between anon and file LRUs. +- */ +- spin_lock_irq(&target_lruvec->lru_lock); +- sc->anon_cost = target_lruvec->anon_cost; +- sc->file_cost = target_lruvec->file_cost; +- spin_unlock_irq(&target_lruvec->lru_lock); +- +- /* +- * Target desirable inactive:active list ratios for the anon +- * and file LRU lists. +- */ +- if (!sc->force_deactivate) { +- unsigned long refaults; +- +- refaults = lruvec_page_state(target_lruvec, +- WORKINGSET_ACTIVATE_ANON); +- if (refaults != target_lruvec->refaults[0] || +- inactive_is_low(target_lruvec, LRU_INACTIVE_ANON)) +- sc->may_deactivate |= DEACTIVATE_ANON; +- else +- sc->may_deactivate &= ~DEACTIVATE_ANON; +- +- /* +- * When refaults are being observed, it means a new +- * workingset is being established. Deactivate to get +- * rid of any stale active pages quickly. +- */ +- refaults = lruvec_page_state(target_lruvec, +- WORKINGSET_ACTIVATE_FILE); +- if (refaults != target_lruvec->refaults[1] || +- inactive_is_low(target_lruvec, LRU_INACTIVE_FILE)) +- sc->may_deactivate |= DEACTIVATE_FILE; +- else +- sc->may_deactivate &= ~DEACTIVATE_FILE; +- } else +- sc->may_deactivate = DEACTIVATE_ANON | DEACTIVATE_FILE; +- +- /* +- * If we have plenty of inactive file pages that aren't +- * thrashing, try to reclaim those first before touching +- * anonymous pages. +- */ +- file = lruvec_page_state(target_lruvec, NR_INACTIVE_FILE); +- if (file >> sc->priority && !(sc->may_deactivate & DEACTIVATE_FILE)) +- sc->cache_trim_mode = 1; +- else +- sc->cache_trim_mode = 0; +- +- /* +- * Prevent the reclaimer from falling into the cache trap: as +- * cache pages start out inactive, every cache fault will tip +- * the scan balance towards the file LRU. And as the file LRU +- * shrinks, so does the window for rotation from references. +- * This means we have a runaway feedback loop where a tiny +- * thrashing file LRU becomes infinitely more attractive than +- * anon pages. Try to detect this based on file LRU size. +- */ +- if (!cgroup_reclaim(sc)) { +- unsigned long total_high_wmark = 0; +- unsigned long free, anon; +- int z; +- +- free = sum_zone_node_page_state(pgdat->node_id, NR_FREE_PAGES); +- file = node_page_state(pgdat, NR_ACTIVE_FILE) + +- node_page_state(pgdat, NR_INACTIVE_FILE); +- +- for (z = 0; z < MAX_NR_ZONES; z++) { +- struct zone *zone = &pgdat->node_zones[z]; +- if (!managed_zone(zone)) +- continue; +- +- total_high_wmark += high_wmark_pages(zone); +- } +- +- /* +- * Consider anon: if that's low too, this isn't a +- * runaway file reclaim problem, but rather just +- * extreme pressure. Reclaim as per usual then. +- */ +- anon = node_page_state(pgdat, NR_INACTIVE_ANON); +- +- sc->file_is_tiny = +- file + free <= total_high_wmark && +- !(sc->may_deactivate & DEACTIVATE_ANON) && +- anon >> sc->priority; +- } ++ prepare_scan_count(pgdat, sc); + + shrink_node_memcgs(pgdat, sc); + diff --git a/target/linux/generic/backport-6.1/020-v6.1-04-mm-multigenerational-lru-groundwork.patch b/target/linux/generic/backport-6.1/020-v6.1-04-mm-multigenerational-lru-groundwork.patch new file mode 100644 index 0000000000..fbc15d8c91 --- /dev/null +++ b/target/linux/generic/backport-6.1/020-v6.1-04-mm-multigenerational-lru-groundwork.patch @@ -0,0 +1,996 @@ +From 05f366c941ae2bb8ba21c79fafcb747a5a6b967b Mon Sep 17 00:00:00 2001 +From: Yu Zhao +Date: Mon, 25 Jan 2021 21:12:33 -0700 +Subject: [PATCH 04/10] mm: multigenerational lru: groundwork + +For each lruvec, evictable pages are divided into multiple +generations. The youngest generation number is stored in +lrugen->max_seq for both anon and file types as they are aged on an +equal footing. The oldest generation numbers are stored in +lrugen->min_seq[] separately for anon and file types as clean file +pages can be evicted regardless of swap constraints. These three +variables are monotonically increasing. Generation numbers are +truncated into order_base_2(MAX_NR_GENS+1) bits in order to fit into +page->flags. The sliding window technique is used to prevent truncated +generation numbers from overlapping. Each truncated generation number +is an index to +lrugen->lists[MAX_NR_GENS][ANON_AND_FILE][MAX_NR_ZONES]. + +The framework comprises two conceptually independent components: the +aging, which produces young generations, and the eviction, which +consumes old generations. Both can be invoked independently from user +space for the purpose of working set estimation and proactive reclaim. + +The protection of hot pages and the selection of cold pages are based +on page access types and patterns. There are two access types: one via +page tables and the other via file descriptors. The protection of the +former type is by design stronger because: + 1) The uncertainty in determining the access patterns of the former + type is higher due to the coalesced nature of the accessed bit. + 2) The cost of evicting the former type is higher due to the TLB + flushes required and the likelihood of involving I/O. + 3) The penalty of under-protecting the former type is higher because + applications usually do not prepare themselves for major faults like + they do for blocked I/O. For example, client applications commonly + dedicate blocked I/O to separate threads to avoid UI janks that + negatively affect user experience. + +There are also two access patterns: one with temporal locality and the +other without. The latter pattern, e.g., random and sequential, needs +to be explicitly excluded to avoid weakening the protection of the +former pattern. Generally the former type follows the former pattern +unless MADV_SEQUENTIAL is specified and the latter type follows the +latter pattern unless outlying refaults have been observed. + +Upon faulting, a page is added to the youngest generation, which +provides the strongest protection as the eviction will not consider +this page before the aging has scanned it at least twice. The first +scan clears the accessed bit set during the initial fault. And the +second scan makes sure this page has not been used since the first +scan. A page from any other generations is brought back to the +youngest generation whenever the aging finds the accessed bit set on +any of the PTEs mapping this page. + +Unmapped pages are initially added to the oldest generation and then +conditionally protected by tiers. This is done later [PATCH 07/10]. + +Signed-off-by: Yu Zhao +Tested-by: Konstantin Kharlamov +Change-Id: I71de7cd15b8dfa6f9fdd838023474693c4fee0a7 +--- + fs/fuse/dev.c | 3 +- + include/linux/cgroup.h | 15 +- + include/linux/mm.h | 36 ++++ + include/linux/mm_inline.h | 182 ++++++++++++++++++++ + include/linux/mmzone.h | 70 ++++++++ + include/linux/page-flags-layout.h | 19 ++- + include/linux/page-flags.h | 4 +- + include/linux/sched.h | 3 + + kernel/bounds.c | 3 + + kernel/cgroup/cgroup-internal.h | 1 - + mm/huge_memory.c | 3 +- + mm/memcontrol.c | 1 + + mm/memory.c | 7 + + mm/mm_init.c | 6 +- + mm/page_alloc.c | 1 + + mm/swap.c | 9 +- + mm/swapfile.c | 2 + + mm/vmscan.c | 268 ++++++++++++++++++++++++++++++ + 18 files changed, 618 insertions(+), 15 deletions(-) + +--- a/fs/fuse/dev.c ++++ b/fs/fuse/dev.c +@@ -785,7 +785,8 @@ static int fuse_check_page(struct page * + 1 << PG_active | + 1 << PG_workingset | + 1 << PG_reclaim | +- 1 << PG_waiters))) { ++ 1 << PG_waiters | ++ LRU_GEN_MASK | LRU_REFS_MASK))) { + dump_page(page, "fuse: trying to steal weird page"); + return 1; + } +--- a/include/linux/cgroup.h ++++ b/include/linux/cgroup.h +@@ -432,6 +432,18 @@ static inline void cgroup_put(struct cgr + css_put(&cgrp->self); + } + ++extern struct mutex cgroup_mutex; ++ ++static inline void cgroup_lock(void) ++{ ++ mutex_lock(&cgroup_mutex); ++} ++ ++static inline void cgroup_unlock(void) ++{ ++ mutex_unlock(&cgroup_mutex); ++} ++ + /** + * task_css_set_check - obtain a task's css_set with extra access conditions + * @task: the task to obtain css_set for +@@ -446,7 +458,6 @@ static inline void cgroup_put(struct cgr + * as locks used during the cgroup_subsys::attach() methods. + */ + #ifdef CONFIG_PROVE_RCU +-extern struct mutex cgroup_mutex; + extern spinlock_t css_set_lock; + #define task_css_set_check(task, __c) \ + rcu_dereference_check((task)->cgroups, \ +@@ -707,6 +718,8 @@ struct cgroup; + static inline u64 cgroup_id(const struct cgroup *cgrp) { return 1; } + static inline void css_get(struct cgroup_subsys_state *css) {} + static inline void css_put(struct cgroup_subsys_state *css) {} ++static inline void cgroup_lock(void) {} ++static inline void cgroup_unlock(void) {} + static inline int cgroup_attach_task_all(struct task_struct *from, + struct task_struct *t) { return 0; } + static inline int cgroupstats_build(struct cgroupstats *stats, +--- a/include/linux/mm.h ++++ b/include/linux/mm.h +@@ -1093,6 +1093,8 @@ vm_fault_t finish_mkwrite_fault(struct v + #define ZONES_PGOFF (NODES_PGOFF - ZONES_WIDTH) + #define LAST_CPUPID_PGOFF (ZONES_PGOFF - LAST_CPUPID_WIDTH) + #define KASAN_TAG_PGOFF (LAST_CPUPID_PGOFF - KASAN_TAG_WIDTH) ++#define LRU_GEN_PGOFF (KASAN_TAG_PGOFF - LRU_GEN_WIDTH) ++#define LRU_REFS_PGOFF (LRU_GEN_PGOFF - LRU_REFS_WIDTH) + + /* + * Define the bit shifts to access each section. For non-existent +@@ -1807,6 +1809,40 @@ static inline void unmap_mapping_range(s + loff_t const holebegin, loff_t const holelen, int even_cows) { } + #endif + ++#ifdef CONFIG_LRU_GEN ++static inline void task_enter_nonseq_fault(void) ++{ ++ WARN_ON(current->in_nonseq_fault); ++ ++ current->in_nonseq_fault = 1; ++} ++ ++static inline void task_exit_nonseq_fault(void) ++{ ++ WARN_ON(!current->in_nonseq_fault); ++ ++ current->in_nonseq_fault = 0; ++} ++ ++static inline bool task_in_nonseq_fault(void) ++{ ++ return current->in_nonseq_fault; ++} ++#else ++static inline void task_enter_nonseq_fault(void) ++{ ++} ++ ++static inline void task_exit_nonseq_fault(void) ++{ ++} ++ ++static inline bool task_in_nonseq_fault(void) ++{ ++ return false; ++} ++#endif /* CONFIG_LRU_GEN */ ++ + static inline void unmap_shared_mapping_range(struct address_space *mapping, + loff_t const holebegin, loff_t const holelen) + { +--- a/include/linux/mm_inline.h ++++ b/include/linux/mm_inline.h +@@ -79,11 +79,187 @@ static __always_inline enum lru_list pag + return lru; + } + ++#ifdef CONFIG_LRU_GEN ++ ++static inline bool lru_gen_enabled(void) ++{ ++#ifdef CONFIG_LRU_GEN_ENABLED ++ DECLARE_STATIC_KEY_TRUE(lru_gen_static_key); ++ ++ return static_branch_likely(&lru_gen_static_key); ++#else ++ DECLARE_STATIC_KEY_FALSE(lru_gen_static_key); ++ ++ return static_branch_unlikely(&lru_gen_static_key); ++#endif ++} ++ ++/* Return an index within the sliding window that tracks MAX_NR_GENS generations. */ ++static inline int lru_gen_from_seq(unsigned long seq) ++{ ++ return seq % MAX_NR_GENS; ++} ++ ++/* The youngest and the second youngest generations are counted as active. */ ++static inline bool lru_gen_is_active(struct lruvec *lruvec, int gen) ++{ ++ unsigned long max_seq = lruvec->evictable.max_seq; ++ ++ VM_BUG_ON(gen >= MAX_NR_GENS); ++ ++ return gen == lru_gen_from_seq(max_seq) || gen == lru_gen_from_seq(max_seq - 1); ++} ++ ++/* Update the sizes of the multigenerational lru lists. */ ++static inline void lru_gen_update_size(struct page *page, struct lruvec *lruvec, ++ int old_gen, int new_gen) ++{ ++ int type = page_is_file_lru(page); ++ int zone = page_zonenum(page); ++ int delta = thp_nr_pages(page); ++ enum lru_list lru = type * LRU_FILE; ++ struct lrugen *lrugen = &lruvec->evictable; ++ ++ lockdep_assert_held(&lruvec->lru_lock); ++ VM_BUG_ON(old_gen != -1 && old_gen >= MAX_NR_GENS); ++ VM_BUG_ON(new_gen != -1 && new_gen >= MAX_NR_GENS); ++ VM_BUG_ON(old_gen == -1 && new_gen == -1); ++ ++ if (old_gen >= 0) ++ WRITE_ONCE(lrugen->sizes[old_gen][type][zone], ++ lrugen->sizes[old_gen][type][zone] - delta); ++ if (new_gen >= 0) ++ WRITE_ONCE(lrugen->sizes[new_gen][type][zone], ++ lrugen->sizes[new_gen][type][zone] + delta); ++ ++ if (old_gen < 0) { ++ if (lru_gen_is_active(lruvec, new_gen)) ++ lru += LRU_ACTIVE; ++ update_lru_size(lruvec, lru, zone, delta); ++ return; ++ } ++ ++ if (new_gen < 0) { ++ if (lru_gen_is_active(lruvec, old_gen)) ++ lru += LRU_ACTIVE; ++ update_lru_size(lruvec, lru, zone, -delta); ++ return; ++ } ++ ++ if (!lru_gen_is_active(lruvec, old_gen) && lru_gen_is_active(lruvec, new_gen)) { ++ update_lru_size(lruvec, lru, zone, -delta); ++ update_lru_size(lruvec, lru + LRU_ACTIVE, zone, delta); ++ } ++ ++ VM_BUG_ON(lru_gen_is_active(lruvec, old_gen) && !lru_gen_is_active(lruvec, new_gen)); ++} ++ ++/* Add a page to one of the multigenerational lru lists. Return true on success. */ ++static inline bool lru_gen_add_page(struct page *page, struct lruvec *lruvec, bool reclaiming) ++{ ++ int gen; ++ unsigned long old_flags, new_flags; ++ int type = page_is_file_lru(page); ++ int zone = page_zonenum(page); ++ struct lrugen *lrugen = &lruvec->evictable; ++ ++ if (PageUnevictable(page) || !lrugen->enabled[type]) ++ return false; ++ /* ++ * If a page shouldn't be considered for eviction, i.e., a page mapped ++ * upon fault during which the accessed bit is set, add it to the ++ * youngest generation. ++ * ++ * If a page can't be evicted immediately, i.e., an anon page not in ++ * swap cache or a dirty page pending writeback, add it to the second ++ * oldest generation. ++ * ++ * If a page could be evicted immediately, e.g., a clean page, add it to ++ * the oldest generation. ++ */ ++ if (PageActive(page)) ++ gen = lru_gen_from_seq(lrugen->max_seq); ++ else if ((!type && !PageSwapCache(page)) || ++ (PageReclaim(page) && (PageDirty(page) || PageWriteback(page)))) ++ gen = lru_gen_from_seq(lrugen->min_seq[type] + 1); ++ else ++ gen = lru_gen_from_seq(lrugen->min_seq[type]); ++ ++ do { ++ new_flags = old_flags = READ_ONCE(page->flags); ++ VM_BUG_ON_PAGE(new_flags & LRU_GEN_MASK, page); ++ ++ new_flags &= ~(LRU_GEN_MASK | BIT(PG_active)); ++ new_flags |= (gen + 1UL) << LRU_GEN_PGOFF; ++ } while (cmpxchg(&page->flags, old_flags, new_flags) != old_flags); ++ ++ lru_gen_update_size(page, lruvec, -1, gen); ++ /* for rotate_reclaimable_page() */ ++ if (reclaiming) ++ list_add_tail(&page->lru, &lrugen->lists[gen][type][zone]); ++ else ++ list_add(&page->lru, &lrugen->lists[gen][type][zone]); ++ ++ return true; ++} ++ ++/* Delete a page from one of the multigenerational lru lists. Return true on success. */ ++static inline bool lru_gen_del_page(struct page *page, struct lruvec *lruvec, bool reclaiming) ++{ ++ int gen; ++ unsigned long old_flags, new_flags; ++ ++ do { ++ new_flags = old_flags = READ_ONCE(page->flags); ++ if (!(new_flags & LRU_GEN_MASK)) ++ return false; ++ ++ VM_BUG_ON_PAGE(PageActive(page), page); ++ VM_BUG_ON_PAGE(PageUnevictable(page), page); ++ ++ gen = ((new_flags & LRU_GEN_MASK) >> LRU_GEN_PGOFF) - 1; ++ ++ new_flags &= ~LRU_GEN_MASK; ++ /* for shrink_page_list() */ ++ if (reclaiming) ++ new_flags &= ~(BIT(PG_referenced) | BIT(PG_reclaim)); ++ else if (lru_gen_is_active(lruvec, gen)) ++ new_flags |= BIT(PG_active); ++ } while (cmpxchg(&page->flags, old_flags, new_flags) != old_flags); ++ ++ lru_gen_update_size(page, lruvec, gen, -1); ++ list_del(&page->lru); ++ ++ return true; ++} ++ ++#else ++ ++static inline bool lru_gen_enabled(void) ++{ ++ return false; ++} ++ ++static inline bool lru_gen_add_page(struct page *page, struct lruvec *lruvec, bool reclaiming) ++{ ++ return false; ++} ++ ++static inline bool lru_gen_del_page(struct page *page, struct lruvec *lruvec, bool reclaiming) ++{ ++ return false; ++} ++ ++#endif /* CONFIG_LRU_GEN */ ++ + static __always_inline void add_page_to_lru_list(struct page *page, + struct lruvec *lruvec) + { + enum lru_list lru = page_lru(page); + ++ if (lru_gen_add_page(page, lruvec, false)) ++ return; ++ + update_lru_size(lruvec, lru, page_zonenum(page), thp_nr_pages(page)); + list_add(&page->lru, &lruvec->lists[lru]); + } +@@ -93,6 +269,9 @@ static __always_inline void add_page_to_ + { + enum lru_list lru = page_lru(page); + ++ if (lru_gen_add_page(page, lruvec, true)) ++ return; ++ + update_lru_size(lruvec, lru, page_zonenum(page), thp_nr_pages(page)); + list_add_tail(&page->lru, &lruvec->lists[lru]); + } +@@ -100,6 +279,9 @@ static __always_inline void add_page_to_ + static __always_inline void del_page_from_lru_list(struct page *page, + struct lruvec *lruvec) + { ++ if (lru_gen_del_page(page, lruvec, false)) ++ return; ++ + list_del(&page->lru); + update_lru_size(lruvec, page_lru(page), page_zonenum(page), + -thp_nr_pages(page)); +--- a/include/linux/mmzone.h ++++ b/include/linux/mmzone.h +@@ -294,6 +294,72 @@ enum lruvec_flags { + */ + }; + ++struct lruvec; ++ ++#define LRU_GEN_MASK ((BIT(LRU_GEN_WIDTH) - 1) << LRU_GEN_PGOFF) ++#define LRU_REFS_MASK ((BIT(LRU_REFS_WIDTH) - 1) << LRU_REFS_PGOFF) ++ ++#ifdef CONFIG_LRU_GEN ++ ++/* ++ * For each lruvec, evictable pages are divided into multiple generations. The ++ * youngest and the oldest generation numbers, AKA max_seq and min_seq, are ++ * monotonically increasing. The sliding window technique is used to track at ++ * least MIN_NR_GENS and at most MAX_NR_GENS generations. An offset within the ++ * window, AKA gen, indexes an array of per-type and per-zone lists for the ++ * corresponding generation. The counter in page->flags stores gen+1 while a ++ * page is on one of the multigenerational lru lists. Otherwise, it stores 0. ++ * ++ * After a page is faulted in, the aging must check the accessed bit at least ++ * twice before the eviction would consider it. The first check clears the ++ * accessed bit set during the initial fault. The second check makes sure this ++ * page hasn't been used since then. ++ */ ++#define MIN_NR_GENS 2 ++#define MAX_NR_GENS ((unsigned int)CONFIG_NR_LRU_GENS) ++ ++struct lrugen { ++ /* the aging increments the max generation number */ ++ unsigned long max_seq; ++ /* the eviction increments the min generation numbers */ ++ unsigned long min_seq[ANON_AND_FILE]; ++ /* the birth time of each generation in jiffies */ ++ unsigned long timestamps[MAX_NR_GENS]; ++ /* the multigenerational lru lists */ ++ struct list_head lists[MAX_NR_GENS][ANON_AND_FILE][MAX_NR_ZONES]; ++ /* the sizes of the multigenerational lru lists in pages */ ++ unsigned long sizes[MAX_NR_GENS][ANON_AND_FILE][MAX_NR_ZONES]; ++ /* whether the multigenerational lru is enabled */ ++ bool enabled[ANON_AND_FILE]; ++}; ++ ++#define MAX_BATCH_SIZE 8192 ++ ++void lru_gen_init_state(struct mem_cgroup *memcg, struct lruvec *lruvec); ++void lru_gen_change_state(bool enable, bool main, bool swap); ++ ++#ifdef CONFIG_MEMCG ++void lru_gen_init_memcg(struct mem_cgroup *memcg); ++#endif ++ ++#else /* !CONFIG_LRU_GEN */ ++ ++static inline void lru_gen_init_state(struct mem_cgroup *memcg, struct lruvec *lruvec) ++{ ++} ++ ++static inline void lru_gen_change_state(bool enable, bool main, bool swap) ++{ ++} ++ ++#ifdef CONFIG_MEMCG ++static inline void lru_gen_init_memcg(struct mem_cgroup *memcg) ++{ ++} ++#endif ++ ++#endif /* CONFIG_LRU_GEN */ ++ + struct lruvec { + struct list_head lists[NR_LRU_LISTS]; + /* per lruvec lru_lock for memcg */ +@@ -311,6 +377,10 @@ struct lruvec { + unsigned long refaults[ANON_AND_FILE]; + /* Various lruvec state flags (enum lruvec_flags) */ + unsigned long flags; ++#ifdef CONFIG_LRU_GEN ++ /* unevictable pages are on LRU_UNEVICTABLE */ ++ struct lrugen evictable; ++#endif + #ifdef CONFIG_MEMCG + struct pglist_data *pgdat; + #endif +--- a/include/linux/page-flags-layout.h ++++ b/include/linux/page-flags-layout.h +@@ -26,6 +26,14 @@ + + #define ZONES_WIDTH ZONES_SHIFT + ++#ifdef CONFIG_LRU_GEN ++/* LRU_GEN_WIDTH is generated from order_base_2(CONFIG_NR_LRU_GENS + 1). */ ++#define LRU_REFS_WIDTH (CONFIG_TIERS_PER_GEN - 2) ++#else ++#define LRU_GEN_WIDTH 0 ++#define LRU_REFS_WIDTH 0 ++#endif /* CONFIG_LRU_GEN */ ++ + #ifdef CONFIG_SPARSEMEM + #include + #define SECTIONS_SHIFT (MAX_PHYSMEM_BITS - SECTION_SIZE_BITS) +@@ -55,7 +63,8 @@ + #define SECTIONS_WIDTH 0 + #endif + +-#if ZONES_WIDTH + SECTIONS_WIDTH + NODES_SHIFT <= BITS_PER_LONG - NR_PAGEFLAGS ++#if ZONES_WIDTH + LRU_GEN_WIDTH + LRU_REFS_WIDTH + SECTIONS_WIDTH + NODES_SHIFT \ ++ <= BITS_PER_LONG - NR_PAGEFLAGS + #define NODES_WIDTH NODES_SHIFT + #elif defined(CONFIG_SPARSEMEM_VMEMMAP) + #error "Vmemmap: No space for nodes field in page flags" +@@ -89,8 +98,8 @@ + #define LAST_CPUPID_SHIFT 0 + #endif + +-#if ZONES_WIDTH + SECTIONS_WIDTH + NODES_WIDTH + KASAN_TAG_WIDTH + LAST_CPUPID_SHIFT \ +- <= BITS_PER_LONG - NR_PAGEFLAGS ++#if ZONES_WIDTH + LRU_GEN_WIDTH + LRU_REFS_WIDTH + SECTIONS_WIDTH + NODES_WIDTH + \ ++ KASAN_TAG_WIDTH + LAST_CPUPID_SHIFT <= BITS_PER_LONG - NR_PAGEFLAGS + #define LAST_CPUPID_WIDTH LAST_CPUPID_SHIFT + #else + #define LAST_CPUPID_WIDTH 0 +@@ -100,8 +109,8 @@ + #define LAST_CPUPID_NOT_IN_PAGE_FLAGS + #endif + +-#if ZONES_WIDTH + SECTIONS_WIDTH + NODES_WIDTH + KASAN_TAG_WIDTH + LAST_CPUPID_WIDTH \ +- > BITS_PER_LONG - NR_PAGEFLAGS ++#if ZONES_WIDTH + LRU_GEN_WIDTH + LRU_REFS_WIDTH + SECTIONS_WIDTH + NODES_WIDTH + \ ++ KASAN_TAG_WIDTH + LAST_CPUPID_WIDTH > BITS_PER_LONG - NR_PAGEFLAGS + #error "Not enough bits in page flags" + #endif + +--- a/include/linux/page-flags.h ++++ b/include/linux/page-flags.h +@@ -845,7 +845,7 @@ static inline void ClearPageSlabPfmemall + 1UL << PG_private | 1UL << PG_private_2 | \ + 1UL << PG_writeback | 1UL << PG_reserved | \ + 1UL << PG_slab | 1UL << PG_active | \ +- 1UL << PG_unevictable | __PG_MLOCKED) ++ 1UL << PG_unevictable | __PG_MLOCKED | LRU_GEN_MASK) + + /* + * Flags checked when a page is prepped for return by the page allocator. +@@ -856,7 +856,7 @@ static inline void ClearPageSlabPfmemall + * alloc-free cycle to prevent from reusing the page. + */ + #define PAGE_FLAGS_CHECK_AT_PREP \ +- (PAGEFLAGS_MASK & ~__PG_HWPOISON) ++ ((PAGEFLAGS_MASK & ~__PG_HWPOISON) | LRU_GEN_MASK | LRU_REFS_MASK) + + #define PAGE_FLAGS_PRIVATE \ + (1UL << PG_private | 1UL << PG_private_2) +--- a/include/linux/sched.h ++++ b/include/linux/sched.h +@@ -911,6 +911,9 @@ struct task_struct { + #ifdef CONFIG_MEMCG + unsigned in_user_fault:1; + #endif ++#ifdef CONFIG_LRU_GEN ++ unsigned in_nonseq_fault:1; ++#endif + #ifdef CONFIG_COMPAT_BRK + unsigned brk_randomized:1; + #endif +--- a/kernel/bounds.c ++++ b/kernel/bounds.c +@@ -22,6 +22,9 @@ int main(void) + DEFINE(NR_CPUS_BITS, ilog2(CONFIG_NR_CPUS)); + #endif + DEFINE(SPINLOCK_SIZE, sizeof(spinlock_t)); ++#ifdef CONFIG_LRU_GEN ++ DEFINE(LRU_GEN_WIDTH, order_base_2(CONFIG_NR_LRU_GENS + 1)); ++#endif + /* End of constants */ + + return 0; +--- a/kernel/cgroup/cgroup-internal.h ++++ b/kernel/cgroup/cgroup-internal.h +@@ -165,7 +165,6 @@ struct cgroup_mgctx { + #define DEFINE_CGROUP_MGCTX(name) \ + struct cgroup_mgctx name = CGROUP_MGCTX_INIT(name) + +-extern struct mutex cgroup_mutex; + extern spinlock_t css_set_lock; + extern struct cgroup_subsys *cgroup_subsys[]; + extern struct list_head cgroup_roots; +--- a/mm/huge_memory.c ++++ b/mm/huge_memory.c +@@ -2364,7 +2364,8 @@ static void __split_huge_page_tail(struc + #ifdef CONFIG_64BIT + (1L << PG_arch_2) | + #endif +- (1L << PG_dirty))); ++ (1L << PG_dirty) | ++ LRU_GEN_MASK | LRU_REFS_MASK)); + + /* ->mapping in first tail page is compound_mapcount */ + VM_BUG_ON_PAGE(tail > 2 && page_tail->mapping != TAIL_MAPPING, +--- a/mm/memcontrol.c ++++ b/mm/memcontrol.c +@@ -5226,6 +5226,7 @@ static struct mem_cgroup *mem_cgroup_all + memcg->deferred_split_queue.split_queue_len = 0; + #endif + idr_replace(&mem_cgroup_idr, memcg, memcg->id.id); ++ lru_gen_init_memcg(memcg); + return memcg; + fail: + mem_cgroup_id_remove(memcg); +--- a/mm/memory.c ++++ b/mm/memory.c +@@ -4788,6 +4788,7 @@ vm_fault_t handle_mm_fault(struct vm_are + unsigned int flags, struct pt_regs *regs) + { + vm_fault_t ret; ++ bool nonseq_fault = !(vma->vm_flags & VM_SEQ_READ); + + __set_current_state(TASK_RUNNING); + +@@ -4809,11 +4810,17 @@ vm_fault_t handle_mm_fault(struct vm_are + if (flags & FAULT_FLAG_USER) + mem_cgroup_enter_user_fault(); + ++ if (nonseq_fault) ++ task_enter_nonseq_fault(); ++ + if (unlikely(is_vm_hugetlb_page(vma))) + ret = hugetlb_fault(vma->vm_mm, vma, address, flags); + else + ret = __handle_mm_fault(vma, address, flags); + ++ if (nonseq_fault) ++ task_exit_nonseq_fault(); ++ + if (flags & FAULT_FLAG_USER) { + mem_cgroup_exit_user_fault(); + /* +--- a/mm/mm_init.c ++++ b/mm/mm_init.c +@@ -65,14 +65,16 @@ void __init mminit_verify_pageflags_layo + + shift = 8 * sizeof(unsigned long); + width = shift - SECTIONS_WIDTH - NODES_WIDTH - ZONES_WIDTH +- - LAST_CPUPID_SHIFT - KASAN_TAG_WIDTH; ++ - LAST_CPUPID_SHIFT - KASAN_TAG_WIDTH - LRU_GEN_WIDTH - LRU_REFS_WIDTH; + mminit_dprintk(MMINIT_TRACE, "pageflags_layout_widths", +- "Section %d Node %d Zone %d Lastcpupid %d Kasantag %d Flags %d\n", ++ "Section %d Node %d Zone %d Lastcpupid %d Kasantag %d Gen %d Tier %d Flags %d\n", + SECTIONS_WIDTH, + NODES_WIDTH, + ZONES_WIDTH, + LAST_CPUPID_WIDTH, + KASAN_TAG_WIDTH, ++ LRU_GEN_WIDTH, ++ LRU_REFS_WIDTH, + NR_PAGEFLAGS); + mminit_dprintk(MMINIT_TRACE, "pageflags_layout_shifts", + "Section %d Node %d Zone %d Lastcpupid %d Kasantag %d\n", +--- a/mm/page_alloc.c ++++ b/mm/page_alloc.c +@@ -7456,6 +7456,7 @@ static void __meminit pgdat_init_interna + + pgdat_page_ext_init(pgdat); + lruvec_init(&pgdat->__lruvec); ++ lru_gen_init_state(NULL, &pgdat->__lruvec); + } + + static void __meminit zone_init_internals(struct zone *zone, enum zone_type idx, int nid, +--- a/mm/swap.c ++++ b/mm/swap.c +@@ -446,6 +446,11 @@ void lru_cache_add(struct page *page) + VM_BUG_ON_PAGE(PageActive(page) && PageUnevictable(page), page); + VM_BUG_ON_PAGE(PageLRU(page), page); + ++ /* see the comment in lru_gen_add_page() */ ++ if (lru_gen_enabled() && !PageUnevictable(page) && ++ task_in_nonseq_fault() && !(current->flags & PF_MEMALLOC)) ++ SetPageActive(page); ++ + get_page(page); + local_lock(&lru_pvecs.lock); + pvec = this_cpu_ptr(&lru_pvecs.lru_add); +@@ -547,7 +552,7 @@ static void lru_deactivate_file_fn(struc + + static void lru_deactivate_fn(struct page *page, struct lruvec *lruvec) + { +- if (PageActive(page) && !PageUnevictable(page)) { ++ if (!PageUnevictable(page) && (PageActive(page) || lru_gen_enabled())) { + int nr_pages = thp_nr_pages(page); + + del_page_from_lru_list(page, lruvec); +@@ -661,7 +666,7 @@ void deactivate_file_page(struct page *p + */ + void deactivate_page(struct page *page) + { +- if (PageLRU(page) && PageActive(page) && !PageUnevictable(page)) { ++ if (PageLRU(page) && !PageUnevictable(page) && (PageActive(page) || lru_gen_enabled())) { + struct pagevec *pvec; + + local_lock(&lru_pvecs.lock); +--- a/mm/swapfile.c ++++ b/mm/swapfile.c +@@ -2688,6 +2688,7 @@ SYSCALL_DEFINE1(swapoff, const char __us + err = 0; + atomic_inc(&proc_poll_event); + wake_up_interruptible(&proc_poll_wait); ++ lru_gen_change_state(false, false, true); + + out_dput: + filp_close(victim, NULL); +@@ -3349,6 +3350,7 @@ SYSCALL_DEFINE2(swapon, const char __use + mutex_unlock(&swapon_mutex); + atomic_inc(&proc_poll_event); + wake_up_interruptible(&proc_poll_wait); ++ lru_gen_change_state(true, false, true); + + error = 0; + goto out; +--- a/mm/vmscan.c ++++ b/mm/vmscan.c +@@ -50,6 +50,7 @@ + #include + #include + #include ++#include + + #include + #include +@@ -2880,6 +2881,273 @@ static bool can_age_anon_pages(struct pg + return can_demote(pgdat->node_id, sc); + } + ++#ifdef CONFIG_LRU_GEN ++ ++/****************************************************************************** ++ * shorthand helpers ++ ******************************************************************************/ ++ ++#define for_each_gen_type_zone(gen, type, zone) \ ++ for ((gen) = 0; (gen) < MAX_NR_GENS; (gen)++) \ ++ for ((type) = 0; (type) < ANON_AND_FILE; (type)++) \ ++ for ((zone) = 0; (zone) < MAX_NR_ZONES; (zone)++) ++ ++static int page_lru_gen(struct page *page) ++{ ++ unsigned long flags = READ_ONCE(page->flags); ++ ++ return ((flags & LRU_GEN_MASK) >> LRU_GEN_PGOFF) - 1; ++} ++ ++static struct lruvec *get_lruvec(int nid, struct mem_cgroup *memcg) ++{ ++ struct pglist_data *pgdat = NODE_DATA(nid); ++ ++#ifdef CONFIG_MEMCG ++ if (memcg) { ++ struct lruvec *lruvec = &memcg->nodeinfo[nid]->lruvec; ++ ++ if (lruvec->pgdat != pgdat) ++ lruvec->pgdat = pgdat; ++ ++ return lruvec; ++ } ++#endif ++ return pgdat ? &pgdat->__lruvec : NULL; ++} ++ ++static int get_nr_gens(struct lruvec *lruvec, int type) ++{ ++ return lruvec->evictable.max_seq - lruvec->evictable.min_seq[type] + 1; ++} ++ ++static bool __maybe_unused seq_is_valid(struct lruvec *lruvec) ++{ ++ return get_nr_gens(lruvec, 1) >= MIN_NR_GENS && ++ get_nr_gens(lruvec, 1) <= get_nr_gens(lruvec, 0) && ++ get_nr_gens(lruvec, 0) <= MAX_NR_GENS; ++} ++ ++/****************************************************************************** ++ * state change ++ ******************************************************************************/ ++ ++#ifdef CONFIG_LRU_GEN_ENABLED ++DEFINE_STATIC_KEY_TRUE(lru_gen_static_key); ++#else ++DEFINE_STATIC_KEY_FALSE(lru_gen_static_key); ++#endif ++ ++static int lru_gen_nr_swapfiles; ++ ++static bool __maybe_unused state_is_valid(struct lruvec *lruvec) ++{ ++ int gen, type, zone; ++ enum lru_list lru; ++ struct lrugen *lrugen = &lruvec->evictable; ++ ++ for_each_evictable_lru(lru) { ++ type = is_file_lru(lru); ++ ++ if (lrugen->enabled[type] && !list_empty(&lruvec->lists[lru])) ++ return false; ++ } ++ ++ for_each_gen_type_zone(gen, type, zone) { ++ if (!lrugen->enabled[type] && !list_empty(&lrugen->lists[gen][type][zone])) ++ return false; ++ ++ /* unlikely but not a bug when reset_batch_size() is pending */ ++ VM_WARN_ON(!lrugen->enabled[type] && lrugen->sizes[gen][type][zone]); ++ } ++ ++ return true; ++} ++ ++static bool fill_lists(struct lruvec *lruvec) ++{ ++ enum lru_list lru; ++ int remaining = MAX_BATCH_SIZE; ++ ++ for_each_evictable_lru(lru) { ++ int type = is_file_lru(lru); ++ bool active = is_active_lru(lru); ++ struct list_head *head = &lruvec->lists[lru]; ++ ++ if (!lruvec->evictable.enabled[type]) ++ continue; ++ ++ while (!list_empty(head)) { ++ bool success; ++ struct page *page = lru_to_page(head); ++ ++ VM_BUG_ON_PAGE(PageTail(page), page); ++ VM_BUG_ON_PAGE(PageUnevictable(page), page); ++ VM_BUG_ON_PAGE(PageActive(page) != active, page); ++ VM_BUG_ON_PAGE(page_is_file_lru(page) != type, page); ++ VM_BUG_ON_PAGE(page_lru_gen(page) < MAX_NR_GENS, page); ++ ++ prefetchw_prev_lru_page(page, head, flags); ++ ++ del_page_from_lru_list(page, lruvec); ++ success = lru_gen_add_page(page, lruvec, false); ++ VM_BUG_ON(!success); ++ ++ if (!--remaining) ++ return false; ++ } ++ } ++ ++ return true; ++} ++ ++static bool drain_lists(struct lruvec *lruvec) ++{ ++ int gen, type, zone; ++ int remaining = MAX_BATCH_SIZE; ++ ++ for_each_gen_type_zone(gen, type, zone) { ++ struct list_head *head = &lruvec->evictable.lists[gen][type][zone]; ++ ++ if (lruvec->evictable.enabled[type]) ++ continue; ++ ++ while (!list_empty(head)) { ++ bool success; ++ struct page *page = lru_to_page(head); ++ ++ VM_BUG_ON_PAGE(PageTail(page), page); ++ VM_BUG_ON_PAGE(PageUnevictable(page), page); ++ VM_BUG_ON_PAGE(PageActive(page), page); ++ VM_BUG_ON_PAGE(page_is_file_lru(page) != type, page); ++ VM_BUG_ON_PAGE(page_zonenum(page) != zone, page); ++ ++ prefetchw_prev_lru_page(page, head, flags); ++ ++ success = lru_gen_del_page(page, lruvec, false); ++ VM_BUG_ON(!success); ++ add_page_to_lru_list(page, lruvec); ++ ++ if (!--remaining) ++ return false; ++ } ++ } ++ ++ return true; ++} ++ ++/* ++ * For file page tracking, we enable/disable it according to the main switch. ++ * For anon page tracking, we only enabled it when the main switch is on and ++ * there is at least one swapfile; we disable it when there are no swapfiles ++ * regardless of the value of the main switch. Otherwise, we will eventually ++ * reach the max size of the sliding window and have to call inc_min_seq(). ++ */ ++void lru_gen_change_state(bool enable, bool main, bool swap) ++{ ++ static DEFINE_MUTEX(state_mutex); ++ ++ struct mem_cgroup *memcg; ++ ++ mem_hotplug_begin(); ++ cgroup_lock(); ++ mutex_lock(&state_mutex); ++ ++ if (swap) { ++ if (enable) ++ swap = !lru_gen_nr_swapfiles++; ++ else ++ swap = !--lru_gen_nr_swapfiles; ++ } ++ ++ if (main && enable != lru_gen_enabled()) { ++ if (enable) ++ static_branch_enable(&lru_gen_static_key); ++ else ++ static_branch_disable(&lru_gen_static_key); ++ } else if (!swap || !lru_gen_enabled()) ++ goto unlock; ++ ++ memcg = mem_cgroup_iter(NULL, NULL, NULL); ++ do { ++ int nid; ++ ++ for_each_node(nid) { ++ struct lruvec *lruvec = get_lruvec(nid, memcg); ++ ++ if (!lruvec) ++ continue; ++ ++ spin_lock_irq(&lruvec->lru_lock); ++ ++ VM_BUG_ON(!seq_is_valid(lruvec)); ++ VM_BUG_ON(!state_is_valid(lruvec)); ++ ++ lruvec->evictable.enabled[0] = lru_gen_enabled() && lru_gen_nr_swapfiles; ++ lruvec->evictable.enabled[1] = lru_gen_enabled(); ++ ++ while (!(enable ? fill_lists(lruvec) : drain_lists(lruvec))) { ++ spin_unlock_irq(&lruvec->lru_lock); ++ cond_resched(); ++ spin_lock_irq(&lruvec->lru_lock); ++ } ++ ++ spin_unlock_irq(&lruvec->lru_lock); ++ } ++ ++ cond_resched(); ++ } while ((memcg = mem_cgroup_iter(NULL, memcg, NULL))); ++unlock: ++ mutex_unlock(&state_mutex); ++ cgroup_unlock(); ++ mem_hotplug_done(); ++} ++ ++/****************************************************************************** ++ * initialization ++ ******************************************************************************/ ++ ++void lru_gen_init_state(struct mem_cgroup *memcg, struct lruvec *lruvec) ++{ ++ int i; ++ int gen, type, zone; ++ struct lrugen *lrugen = &lruvec->evictable; ++ ++ lrugen->max_seq = MIN_NR_GENS + 1; ++ lrugen->enabled[0] = lru_gen_enabled() && lru_gen_nr_swapfiles; ++ lrugen->enabled[1] = lru_gen_enabled(); ++ ++ for (i = 0; i <= MIN_NR_GENS + 1; i++) ++ lrugen->timestamps[i] = jiffies; ++ ++ for_each_gen_type_zone(gen, type, zone) ++ INIT_LIST_HEAD(&lrugen->lists[gen][type][zone]); ++} ++ ++#ifdef CONFIG_MEMCG ++void lru_gen_init_memcg(struct mem_cgroup *memcg) ++{ ++ int nid; ++ ++ for_each_node(nid) { ++ struct lruvec *lruvec = get_lruvec(nid, memcg); ++ ++ lru_gen_init_state(memcg, lruvec); ++ } ++} ++#endif ++ ++static int __init init_lru_gen(void) ++{ ++ BUILD_BUG_ON(MIN_NR_GENS + 1 >= MAX_NR_GENS); ++ BUILD_BUG_ON(BIT(LRU_GEN_WIDTH) <= MAX_NR_GENS); ++ ++ return 0; ++}; ++late_initcall(init_lru_gen); ++ ++#endif /* CONFIG_LRU_GEN */ ++ + static void shrink_lruvec(struct lruvec *lruvec, struct scan_control *sc) + { + unsigned long nr[NR_LRU_LISTS]; diff --git a/target/linux/generic/backport-6.1/020-v6.1-05-mm-multigenerational-lru-mm_struct-list.patch b/target/linux/generic/backport-6.1/020-v6.1-05-mm-multigenerational-lru-mm_struct-list.patch new file mode 100644 index 0000000000..2592f18e06 --- /dev/null +++ b/target/linux/generic/backport-6.1/020-v6.1-05-mm-multigenerational-lru-mm_struct-list.patch @@ -0,0 +1,760 @@ +From 534bcc4a0bb5b24600891ce793f0295a142e9dae Mon Sep 17 00:00:00 2001 +From: Yu Zhao +Date: Mon, 5 Apr 2021 04:17:41 -0600 +Subject: [PATCH 05/10] mm: multigenerational lru: mm_struct list + +To scan PTEs for accessed pages, a mm_struct list is maintained for +each memcg. When multiple threads traverse the same memcg->mm_list, +each of them gets a unique mm_struct and therefore they can run +walk_page_range() concurrently to reach page tables of all processes +of this memcg. + +This infrastructure also provides the following optimizations: + 1) it allows walkers to skip processes that have been sleeping since + the last walk by tracking the usage of mm_struct between context + switches. + 2) it allows walkers to add interesting items they find during a + walk to a Bloom filter so that they can skip uninteresting items + during the next walk by testing whether an item is in this Bloom + filter. + +Signed-off-by: Yu Zhao +Tested-by: Konstantin Kharlamov +Change-Id: I25d9eda8c6bdc7c3653b9f210a159d6c247c81e8 +--- + fs/exec.c | 2 + + include/linux/memcontrol.h | 4 + + include/linux/mm_inline.h | 6 + + include/linux/mm_types.h | 75 +++++++++ + include/linux/mmzone.h | 63 +++++++ + kernel/exit.c | 1 + + kernel/fork.c | 9 + + kernel/sched/core.c | 1 + + mm/memcontrol.c | 25 +++ + mm/vmscan.c | 331 +++++++++++++++++++++++++++++++++++++ + 10 files changed, 517 insertions(+) + +--- a/fs/exec.c ++++ b/fs/exec.c +@@ -1013,6 +1013,7 @@ static int exec_mmap(struct mm_struct *m + active_mm = tsk->active_mm; + tsk->active_mm = mm; + tsk->mm = mm; ++ lru_gen_add_mm(mm); + /* + * This prevents preemption while active_mm is being loaded and + * it and mm are being updated, which could cause problems for +@@ -1023,6 +1024,7 @@ static int exec_mmap(struct mm_struct *m + if (!IS_ENABLED(CONFIG_ARCH_WANT_IRQS_OFF_ACTIVATE_MM)) + local_irq_enable(); + activate_mm(active_mm, mm); ++ lru_gen_activate_mm(mm); + if (IS_ENABLED(CONFIG_ARCH_WANT_IRQS_OFF_ACTIVATE_MM)) + local_irq_enable(); + tsk->mm->vmacache_seqnum = 0; +--- a/include/linux/memcontrol.h ++++ b/include/linux/memcontrol.h +@@ -348,6 +348,10 @@ struct mem_cgroup { + struct deferred_split deferred_split_queue; + #endif + ++#ifdef CONFIG_LRU_GEN ++ struct lru_gen_mm_list mm_list; ++#endif ++ + struct mem_cgroup_per_node *nodeinfo[]; + }; + +--- a/include/linux/mm_inline.h ++++ b/include/linux/mm_inline.h +@@ -100,6 +100,12 @@ static inline int lru_gen_from_seq(unsig + return seq % MAX_NR_GENS; + } + ++/* Return a proper index regardless whether we keep stats for historical generations. */ ++static inline int lru_hist_from_seq(unsigned long seq) ++{ ++ return seq % NR_HIST_GENS; ++} ++ + /* The youngest and the second youngest generations are counted as active. */ + static inline bool lru_gen_is_active(struct lruvec *lruvec, int gen) + { +--- a/include/linux/mm_types.h ++++ b/include/linux/mm_types.h +@@ -3,6 +3,7 @@ + #define _LINUX_MM_TYPES_H + + #include ++#include + + #include + #include +@@ -15,6 +16,8 @@ + #include + #include + #include ++#include ++#include + + #include + +@@ -580,6 +583,18 @@ struct mm_struct { + #ifdef CONFIG_IOMMU_SUPPORT + u32 pasid; + #endif ++#ifdef CONFIG_LRU_GEN ++ struct { ++ /* the node of a global or per-memcg mm_struct list */ ++ struct list_head list; ++#ifdef CONFIG_MEMCG ++ /* points to the memcg of the owner task above */ ++ struct mem_cgroup *memcg; ++#endif ++ /* whether this mm_struct has been used since the last walk */ ++ nodemask_t nodes; ++ } lrugen; ++#endif /* CONFIG_LRU_GEN */ + } __randomize_layout; + + /* +@@ -606,6 +621,66 @@ static inline cpumask_t *mm_cpumask(stru + return (struct cpumask *)&mm->cpu_bitmap; + } + ++#ifdef CONFIG_LRU_GEN ++ ++struct lru_gen_mm_list { ++ /* a global or per-memcg mm_struct list */ ++ struct list_head fifo; ++ /* protects the list above */ ++ spinlock_t lock; ++}; ++ ++void lru_gen_add_mm(struct mm_struct *mm); ++void lru_gen_del_mm(struct mm_struct *mm); ++#ifdef CONFIG_MEMCG ++void lru_gen_migrate_mm(struct mm_struct *mm); ++#endif ++ ++static inline void lru_gen_init_mm(struct mm_struct *mm) ++{ ++ INIT_LIST_HEAD(&mm->lrugen.list); ++#ifdef CONFIG_MEMCG ++ mm->lrugen.memcg = NULL; ++#endif ++ nodes_clear(mm->lrugen.nodes); ++} ++ ++/* Track the usage of each mm_struct so that we can skip inactive ones. */ ++static inline void lru_gen_activate_mm(struct mm_struct *mm) ++{ ++ /* unlikely but not a bug when racing with lru_gen_migrate_mm() */ ++ VM_WARN_ON(list_empty(&mm->lrugen.list)); ++ ++ if (!(current->flags & PF_KTHREAD) && !nodes_full(mm->lrugen.nodes)) ++ nodes_setall(mm->lrugen.nodes); ++} ++ ++#else /* !CONFIG_LRU_GEN */ ++ ++static inline void lru_gen_add_mm(struct mm_struct *mm) ++{ ++} ++ ++static inline void lru_gen_del_mm(struct mm_struct *mm) ++{ ++} ++ ++#ifdef CONFIG_MEMCG ++static inline void lru_gen_migrate_mm(struct mm_struct *mm) ++{ ++} ++#endif ++ ++static inline void lru_gen_init_mm(struct mm_struct *mm) ++{ ++} ++ ++static inline void lru_gen_activate_mm(struct mm_struct *mm) ++{ ++} ++ ++#endif /* CONFIG_LRU_GEN */ ++ + struct mmu_gather; + extern void tlb_gather_mmu(struct mmu_gather *tlb, struct mm_struct *mm); + extern void tlb_gather_mmu_fullmm(struct mmu_gather *tlb, struct mm_struct *mm); +--- a/include/linux/mmzone.h ++++ b/include/linux/mmzone.h +@@ -318,6 +318,13 @@ struct lruvec; + #define MIN_NR_GENS 2 + #define MAX_NR_GENS ((unsigned int)CONFIG_NR_LRU_GENS) + ++/* Whether to keep stats for historical generations. */ ++#ifdef CONFIG_LRU_GEN_STATS ++#define NR_HIST_GENS ((unsigned int)CONFIG_NR_LRU_GENS) ++#else ++#define NR_HIST_GENS 1U ++#endif ++ + struct lrugen { + /* the aging increments the max generation number */ + unsigned long max_seq; +@@ -333,13 +340,63 @@ struct lrugen { + bool enabled[ANON_AND_FILE]; + }; + ++enum { ++ MM_LEAF_TOTAL, /* total leaf entries */ ++ MM_LEAF_OLD, /* old leaf entries */ ++ MM_LEAF_YOUNG, /* young leaf entries */ ++ MM_NONLEAF_TOTAL, /* total non-leaf entries */ ++ MM_NONLEAF_PREV, /* previously worthy non-leaf entries */ ++ MM_NONLEAF_CUR, /* currently worthy non-leaf entries */ ++ NR_MM_STATS ++}; ++ ++/* mnemonic codes for the stats above */ ++#define MM_STAT_CODES "toydpc" ++ ++/* double buffering bloom filters */ ++#define NR_BLOOM_FILTERS 2 ++ ++struct lru_gen_mm_walk { ++ /* set to max_seq after each round of walk */ ++ unsigned long seq; ++ /* the next mm_struct on the list to walk */ ++ struct list_head *head; ++ /* the first mm_struct never walked before */ ++ struct list_head *tail; ++ /* to wait for the last walker to finish */ ++ struct wait_queue_head wait; ++ /* bloom filters flip after each round of walk */ ++ unsigned long *filters[NR_BLOOM_FILTERS]; ++ /* page table stats for debugging */ ++ unsigned long stats[NR_HIST_GENS][NR_MM_STATS]; ++ /* the number of concurrent walkers */ ++ int nr_walkers; ++}; ++ ++#define MIN_BATCH_SIZE 64 + #define MAX_BATCH_SIZE 8192 + ++struct mm_walk_args { ++ struct mem_cgroup *memcg; ++ unsigned long max_seq; ++ unsigned long start_pfn; ++ unsigned long end_pfn; ++ unsigned long next_addr; ++ unsigned long bitmap[BITS_TO_LONGS(MIN_BATCH_SIZE)]; ++ int node_id; ++ int swappiness; ++ int batch_size; ++ int nr_pages[MAX_NR_GENS][ANON_AND_FILE][MAX_NR_ZONES]; ++ int mm_stats[NR_MM_STATS]; ++ bool use_filter; ++}; ++ + void lru_gen_init_state(struct mem_cgroup *memcg, struct lruvec *lruvec); + void lru_gen_change_state(bool enable, bool main, bool swap); + + #ifdef CONFIG_MEMCG + void lru_gen_init_memcg(struct mem_cgroup *memcg); ++void lru_gen_free_memcg(struct mem_cgroup *memcg); + #endif + + #else /* !CONFIG_LRU_GEN */ +@@ -356,6 +413,10 @@ static inline void lru_gen_change_state( + static inline void lru_gen_init_memcg(struct mem_cgroup *memcg) + { + } ++ ++static inline void lru_gen_free_memcg(struct mem_cgroup *memcg) ++{ ++} + #endif + + #endif /* CONFIG_LRU_GEN */ +@@ -380,6 +441,8 @@ struct lruvec { + #ifdef CONFIG_LRU_GEN + /* unevictable pages are on LRU_UNEVICTABLE */ + struct lrugen evictable; ++ /* state for mm list and page table walks */ ++ struct lru_gen_mm_walk mm_walk; + #endif + #ifdef CONFIG_MEMCG + struct pglist_data *pgdat; +--- a/kernel/exit.c ++++ b/kernel/exit.c +@@ -422,6 +422,7 @@ assign_new_owner: + goto retry; + } + WRITE_ONCE(mm->owner, c); ++ lru_gen_migrate_mm(mm); + task_unlock(c); + put_task_struct(c); + } +--- a/kernel/fork.c ++++ b/kernel/fork.c +@@ -1080,6 +1080,7 @@ static struct mm_struct *mm_init(struct + goto fail_nocontext; + + mm->user_ns = get_user_ns(user_ns); ++ lru_gen_init_mm(mm); + return mm; + + fail_nocontext: +@@ -1122,6 +1123,7 @@ static inline void __mmput(struct mm_str + } + if (mm->binfmt) + module_put(mm->binfmt->module); ++ lru_gen_del_mm(mm); + mmdrop(mm); + } + +@@ -2617,6 +2619,13 @@ pid_t kernel_clone(struct kernel_clone_a + get_task_struct(p); + } + ++ if (IS_ENABLED(CONFIG_LRU_GEN) && !(clone_flags & CLONE_VM)) { ++ /* lock the task to synchronize with memcg migration */ ++ task_lock(p); ++ lru_gen_add_mm(p->mm); ++ task_unlock(p); ++ } ++ + wake_up_new_task(p); + + /* forking complete and child started to run, tell ptracer */ +--- a/kernel/sched/core.c ++++ b/kernel/sched/core.c +@@ -4978,6 +4978,7 @@ context_switch(struct rq *rq, struct tas + * finish_task_switch()'s mmdrop(). + */ + switch_mm_irqs_off(prev->active_mm, next->mm, next); ++ lru_gen_activate_mm(next->mm); + + if (!prev->mm) { // from kernel + /* will mmdrop() in finish_task_switch(). */ +--- a/mm/memcontrol.c ++++ b/mm/memcontrol.c +@@ -5163,6 +5163,7 @@ static void __mem_cgroup_free(struct mem + + static void mem_cgroup_free(struct mem_cgroup *memcg) + { ++ lru_gen_free_memcg(memcg); + memcg_wb_domain_exit(memcg); + __mem_cgroup_free(memcg); + } +@@ -6195,6 +6196,29 @@ static void mem_cgroup_move_task(void) + } + #endif + ++#ifdef CONFIG_LRU_GEN ++static void mem_cgroup_attach(struct cgroup_taskset *tset) ++{ ++ struct cgroup_subsys_state *css; ++ struct task_struct *task = NULL; ++ ++ cgroup_taskset_for_each_leader(task, css, tset) ++ break; ++ ++ if (!task) ++ return; ++ ++ task_lock(task); ++ if (task->mm && task->mm->owner == task) ++ lru_gen_migrate_mm(task->mm); ++ task_unlock(task); ++} ++#else ++static void mem_cgroup_attach(struct cgroup_taskset *tset) ++{ ++} ++#endif /* CONFIG_LRU_GEN */ ++ + static int seq_puts_memcg_tunable(struct seq_file *m, unsigned long value) + { + if (value == PAGE_COUNTER_MAX) +@@ -6538,6 +6562,7 @@ struct cgroup_subsys memory_cgrp_subsys + .css_reset = mem_cgroup_css_reset, + .css_rstat_flush = mem_cgroup_css_rstat_flush, + .can_attach = mem_cgroup_can_attach, ++ .attach = mem_cgroup_attach, + .cancel_attach = mem_cgroup_cancel_attach, + .post_attach = mem_cgroup_move_task, + .dfl_cftypes = memory_files, +--- a/mm/vmscan.c ++++ b/mm/vmscan.c +@@ -2929,6 +2929,306 @@ static bool __maybe_unused seq_is_valid( + } + + /****************************************************************************** ++ * mm_struct list ++ ******************************************************************************/ ++ ++static struct lru_gen_mm_list *get_mm_list(struct mem_cgroup *memcg) ++{ ++ static struct lru_gen_mm_list mm_list = { ++ .fifo = LIST_HEAD_INIT(mm_list.fifo), ++ .lock = __SPIN_LOCK_UNLOCKED(mm_list.lock), ++ }; ++ ++#ifdef CONFIG_MEMCG ++ if (memcg) ++ return &memcg->mm_list; ++#endif ++ return &mm_list; ++} ++ ++void lru_gen_add_mm(struct mm_struct *mm) ++{ ++ int nid; ++ struct mem_cgroup *memcg = get_mem_cgroup_from_mm(mm); ++ struct lru_gen_mm_list *mm_list = get_mm_list(memcg); ++ ++ VM_BUG_ON_MM(!list_empty(&mm->lrugen.list), mm); ++#ifdef CONFIG_MEMCG ++ VM_BUG_ON_MM(mm->lrugen.memcg, mm); ++ mm->lrugen.memcg = memcg; ++#endif ++ spin_lock(&mm_list->lock); ++ ++ list_add_tail(&mm->lrugen.list, &mm_list->fifo); ++ ++ for_each_node(nid) { ++ struct lruvec *lruvec = get_lruvec(nid, memcg); ++ ++ if (!lruvec) ++ continue; ++ ++ if (lruvec->mm_walk.tail == &mm_list->fifo) ++ lruvec->mm_walk.tail = lruvec->mm_walk.tail->prev; ++ } ++ ++ spin_unlock(&mm_list->lock); ++} ++ ++void lru_gen_del_mm(struct mm_struct *mm) ++{ ++ int nid; ++ struct lru_gen_mm_list *mm_list; ++ struct mem_cgroup *memcg = NULL; ++ ++ if (list_empty(&mm->lrugen.list)) ++ return; ++ ++#ifdef CONFIG_MEMCG ++ memcg = mm->lrugen.memcg; ++#endif ++ mm_list = get_mm_list(memcg); ++ ++ spin_lock(&mm_list->lock); ++ ++ for_each_node(nid) { ++ struct lruvec *lruvec = get_lruvec(nid, memcg); ++ ++ if (!lruvec) ++ continue; ++ ++ if (lruvec->mm_walk.tail == &mm->lrugen.list) ++ lruvec->mm_walk.tail = lruvec->mm_walk.tail->next; ++ ++ if (lruvec->mm_walk.head != &mm->lrugen.list) ++ continue; ++ ++ lruvec->mm_walk.head = lruvec->mm_walk.head->next; ++ if (lruvec->mm_walk.head == &mm_list->fifo) ++ WRITE_ONCE(lruvec->mm_walk.seq, lruvec->mm_walk.seq + 1); ++ } ++ ++ list_del_init(&mm->lrugen.list); ++ ++ spin_unlock(&mm_list->lock); ++ ++#ifdef CONFIG_MEMCG ++ mem_cgroup_put(mm->lrugen.memcg); ++ mm->lrugen.memcg = NULL; ++#endif ++} ++ ++#ifdef CONFIG_MEMCG ++void lru_gen_migrate_mm(struct mm_struct *mm) ++{ ++ struct mem_cgroup *memcg; ++ ++ lockdep_assert_held(&mm->owner->alloc_lock); ++ ++ if (mem_cgroup_disabled()) ++ return; ++ ++ rcu_read_lock(); ++ memcg = mem_cgroup_from_task(mm->owner); ++ rcu_read_unlock(); ++ if (memcg == mm->lrugen.memcg) ++ return; ++ ++ VM_BUG_ON_MM(!mm->lrugen.memcg, mm); ++ VM_BUG_ON_MM(list_empty(&mm->lrugen.list), mm); ++ ++ lru_gen_del_mm(mm); ++ lru_gen_add_mm(mm); ++} ++#endif ++ ++#define BLOOM_FILTER_SHIFT 15 ++ ++static inline int filter_gen_from_seq(unsigned long seq) ++{ ++ return seq % NR_BLOOM_FILTERS; ++} ++ ++static void get_item_key(void *item, int *key) ++{ ++ u32 hash = hash_ptr(item, BLOOM_FILTER_SHIFT * 2); ++ ++ BUILD_BUG_ON(BLOOM_FILTER_SHIFT * 2 > BITS_PER_TYPE(u32)); ++ ++ key[0] = hash & (BIT(BLOOM_FILTER_SHIFT) - 1); ++ key[1] = hash >> BLOOM_FILTER_SHIFT; ++} ++ ++static void clear_bloom_filter(struct lruvec *lruvec, unsigned long seq) ++{ ++ unsigned long *filter; ++ int gen = filter_gen_from_seq(seq); ++ ++ lockdep_assert_held(&get_mm_list(lruvec_memcg(lruvec))->lock); ++ ++ filter = lruvec->mm_walk.filters[gen]; ++ if (filter) { ++ bitmap_clear(filter, 0, BIT(BLOOM_FILTER_SHIFT)); ++ return; ++ } ++ ++ filter = bitmap_zalloc(BIT(BLOOM_FILTER_SHIFT), GFP_ATOMIC); ++ WRITE_ONCE(lruvec->mm_walk.filters[gen], filter); ++} ++ ++static void set_bloom_filter(struct lruvec *lruvec, unsigned long seq, void *item) ++{ ++ int key[2]; ++ unsigned long *filter; ++ int gen = filter_gen_from_seq(seq); ++ ++ filter = READ_ONCE(lruvec->mm_walk.filters[gen]); ++ if (!filter) ++ return; ++ ++ get_item_key(item, key); ++ ++ if (!test_bit(key[0], filter)) ++ set_bit(key[0], filter); ++ if (!test_bit(key[1], filter)) ++ set_bit(key[1], filter); ++} ++ ++static bool test_bloom_filter(struct lruvec *lruvec, unsigned long seq, void *item) ++{ ++ int key[2]; ++ unsigned long *filter; ++ int gen = filter_gen_from_seq(seq); ++ ++ filter = READ_ONCE(lruvec->mm_walk.filters[gen]); ++ if (!filter) ++ return false; ++ ++ get_item_key(item, key); ++ ++ return test_bit(key[0], filter) && test_bit(key[1], filter); ++} ++ ++static void reset_mm_stats(struct lruvec *lruvec, bool last, struct mm_walk_args *args) ++{ ++ int i; ++ int hist = lru_hist_from_seq(args->max_seq); ++ ++ lockdep_assert_held(&get_mm_list(lruvec_memcg(lruvec))->lock); ++ ++ for (i = 0; i < NR_MM_STATS; i++) { ++ WRITE_ONCE(lruvec->mm_walk.stats[hist][i], ++ lruvec->mm_walk.stats[hist][i] + args->mm_stats[i]); ++ args->mm_stats[i] = 0; ++ } ++ ++ if (!last || NR_HIST_GENS == 1) ++ return; ++ ++ hist = lru_hist_from_seq(args->max_seq + 1); ++ for (i = 0; i < NR_MM_STATS; i++) ++ WRITE_ONCE(lruvec->mm_walk.stats[hist][i], 0); ++} ++ ++static bool should_skip_mm(struct mm_struct *mm, struct mm_walk_args *args) ++{ ++ int type; ++ unsigned long size = 0; ++ ++ if (cpumask_empty(mm_cpumask(mm)) && !node_isset(args->node_id, mm->lrugen.nodes)) ++ return true; ++ ++ if (mm_is_oom_victim(mm)) ++ return true; ++ ++ for (type = !args->swappiness; type < ANON_AND_FILE; type++) { ++ size += type ? get_mm_counter(mm, MM_FILEPAGES) : ++ get_mm_counter(mm, MM_ANONPAGES) + ++ get_mm_counter(mm, MM_SHMEMPAGES); ++ } ++ ++ if (size < MIN_BATCH_SIZE) ++ return true; ++ ++ if (!mmget_not_zero(mm)) ++ return true; ++ ++ node_clear(args->node_id, mm->lrugen.nodes); ++ ++ return false; ++} ++ ++/* To support multiple walkers that concurrently walk an mm_struct list. */ ++static bool get_next_mm(struct lruvec *lruvec, struct mm_walk_args *args, ++ struct mm_struct **iter) ++{ ++ bool first = false; ++ bool last = true; ++ struct mm_struct *mm = NULL; ++ struct lru_gen_mm_walk *mm_walk = &lruvec->mm_walk; ++ struct lru_gen_mm_list *mm_list = get_mm_list(args->memcg); ++ ++ if (*iter) ++ mmput_async(*iter); ++ else if (args->max_seq <= READ_ONCE(mm_walk->seq)) ++ return false; ++ ++ spin_lock(&mm_list->lock); ++ ++ VM_BUG_ON(args->max_seq > mm_walk->seq + 1); ++ VM_BUG_ON(*iter && args->max_seq < mm_walk->seq); ++ VM_BUG_ON(*iter && !mm_walk->nr_walkers); ++ ++ if (args->max_seq <= mm_walk->seq) { ++ if (!*iter) ++ last = false; ++ goto done; ++ } ++ ++ if (mm_walk->head == &mm_list->fifo) { ++ VM_BUG_ON(mm_walk->nr_walkers); ++ mm_walk->head = mm_walk->head->next; ++ first = true; ++ } ++ ++ while (!mm && mm_walk->head != &mm_list->fifo) { ++ mm = list_entry(mm_walk->head, struct mm_struct, lrugen.list); ++ ++ mm_walk->head = mm_walk->head->next; ++ ++ if (mm_walk->tail == &mm->lrugen.list) { ++ mm_walk->tail = mm_walk->tail->next; ++ args->use_filter = false; ++ } ++ ++ if (should_skip_mm(mm, args)) ++ mm = NULL; ++ } ++ ++ if (mm_walk->head == &mm_list->fifo) ++ WRITE_ONCE(mm_walk->seq, mm_walk->seq + 1); ++done: ++ if (*iter && !mm) ++ mm_walk->nr_walkers--; ++ if (!*iter && mm) ++ mm_walk->nr_walkers++; ++ ++ if (mm_walk->nr_walkers) ++ last = false; ++ ++ if (mm && first) ++ clear_bloom_filter(lruvec, args->max_seq + 1); ++ ++ if (*iter || last) ++ reset_mm_stats(lruvec, last, args); ++ ++ spin_unlock(&mm_list->lock); ++ ++ *iter = mm; ++ ++ return last; ++} ++ ++/****************************************************************************** + * state change + ******************************************************************************/ + +@@ -3112,6 +3412,7 @@ void lru_gen_init_state(struct mem_cgrou + int i; + int gen, type, zone; + struct lrugen *lrugen = &lruvec->evictable; ++ struct lru_gen_mm_list *mm_list = get_mm_list(memcg); + + lrugen->max_seq = MIN_NR_GENS + 1; + lrugen->enabled[0] = lru_gen_enabled() && lru_gen_nr_swapfiles; +@@ -3122,6 +3423,17 @@ void lru_gen_init_state(struct mem_cgrou + + for_each_gen_type_zone(gen, type, zone) + INIT_LIST_HEAD(&lrugen->lists[gen][type][zone]); ++ ++ if (IS_ENABLED(CONFIG_MEMORY_HOTPLUG) && !memcg) ++ spin_lock(&mm_list->lock); ++ ++ lruvec->mm_walk.seq = MIN_NR_GENS; ++ lruvec->mm_walk.head = &mm_list->fifo; ++ lruvec->mm_walk.tail = &mm_list->fifo; ++ init_waitqueue_head(&lruvec->mm_walk.wait); ++ ++ if (IS_ENABLED(CONFIG_MEMORY_HOTPLUG) && !memcg) ++ spin_unlock(&mm_list->lock); + } + + #ifdef CONFIG_MEMCG +@@ -3129,18 +3441,37 @@ void lru_gen_init_memcg(struct mem_cgrou + { + int nid; + ++ INIT_LIST_HEAD(&memcg->mm_list.fifo); ++ spin_lock_init(&memcg->mm_list.lock); ++ + for_each_node(nid) { + struct lruvec *lruvec = get_lruvec(nid, memcg); + + lru_gen_init_state(memcg, lruvec); + } + } ++ ++void lru_gen_free_memcg(struct mem_cgroup *memcg) ++{ ++ int nid; ++ ++ for_each_node(nid) { ++ int i; ++ struct lruvec *lruvec = get_lruvec(nid, memcg); ++ ++ for (i = 0; i < NR_BLOOM_FILTERS; i++) { ++ bitmap_free(lruvec->mm_walk.filters[i]); ++ lruvec->mm_walk.filters[i] = NULL; ++ } ++ } ++} + #endif + + static int __init init_lru_gen(void) + { + BUILD_BUG_ON(MIN_NR_GENS + 1 >= MAX_NR_GENS); + BUILD_BUG_ON(BIT(LRU_GEN_WIDTH) <= MAX_NR_GENS); ++ BUILD_BUG_ON(sizeof(MM_STAT_CODES) != NR_MM_STATS + 1); + + return 0; + }; diff --git a/target/linux/generic/backport-6.1/020-v6.1-06-mm-multigenerational-lru-aging.patch b/target/linux/generic/backport-6.1/020-v6.1-06-mm-multigenerational-lru-aging.patch new file mode 100644 index 0000000000..6fc93d9422 --- /dev/null +++ b/target/linux/generic/backport-6.1/020-v6.1-06-mm-multigenerational-lru-aging.patch @@ -0,0 +1,1176 @@ +From 8217cd2238c40cf77208aa27a7cc09879e685890 Mon Sep 17 00:00:00 2001 +From: Yu Zhao +Date: Mon, 5 Apr 2021 04:35:07 -0600 +Subject: [PATCH 06/10] mm: multigenerational lru: aging + +The aging produces young generations. Given an lruvec, the aging +traverses lruvec_memcg()->mm_list and calls walk_page_range() to scan +PTEs for accessed pages. Upon finding one, the aging updates its +generation number to max_seq (modulo MAX_NR_GENS). After each round of +traversal, the aging increments max_seq. The aging is due when +min_seq[] reaches max_seq-1. + +The aging uses the following optimizations when walking page tables: + 1) It skips non-leaf PMD entries that have the accessed bit cleared + when CONFIG_ARCH_HAS_NONLEAF_PMD_YOUNG=y. + 2) It does not zigzag between a PGD table and the same PMD or PTE + table spanning multiple VMAs. In other words, it finishes all the + VMAs within the range of the same PMD or PTE table before it returns + to this PGD table. This optimizes workloads that have large numbers + of tiny VMAs, especially when CONFIG_PGTABLE_LEVELS=5. + +Signed-off-by: Yu Zhao +Tested-by: Konstantin Kharlamov +Change-Id: I3ae8abc3100d023cecb3a699d86020ae6fc10a45 +--- + include/linux/memcontrol.h | 3 + + include/linux/mmzone.h | 9 + + include/linux/oom.h | 16 + + include/linux/swap.h | 3 + + mm/memcontrol.c | 5 + + mm/oom_kill.c | 4 +- + mm/rmap.c | 8 + + mm/vmscan.c | 948 +++++++++++++++++++++++++++++++++++++ + 8 files changed, 994 insertions(+), 2 deletions(-) + +--- a/include/linux/memcontrol.h ++++ b/include/linux/memcontrol.h +@@ -1367,10 +1367,13 @@ mem_cgroup_print_oom_meminfo(struct mem_ + + static inline void lock_page_memcg(struct page *page) + { ++ /* to match page_memcg_rcu() */ ++ rcu_read_lock(); + } + + static inline void unlock_page_memcg(struct page *page) + { ++ rcu_read_unlock(); + } + + static inline void mem_cgroup_handle_over_high(void) +--- a/include/linux/mmzone.h ++++ b/include/linux/mmzone.h +@@ -295,6 +295,7 @@ enum lruvec_flags { + }; + + struct lruvec; ++struct page_vma_mapped_walk; + + #define LRU_GEN_MASK ((BIT(LRU_GEN_WIDTH) - 1) << LRU_GEN_PGOFF) + #define LRU_REFS_MASK ((BIT(LRU_REFS_WIDTH) - 1) << LRU_REFS_PGOFF) +@@ -393,6 +394,7 @@ struct mm_walk_args { + + void lru_gen_init_state(struct mem_cgroup *memcg, struct lruvec *lruvec); + void lru_gen_change_state(bool enable, bool main, bool swap); ++void lru_gen_look_around(struct page_vma_mapped_walk *pvmw); + + #ifdef CONFIG_MEMCG + void lru_gen_init_memcg(struct mem_cgroup *memcg); +@@ -409,6 +411,10 @@ static inline void lru_gen_change_state( + { + } + ++static inline void lru_gen_look_around(struct page_vma_mapped_walk *pvmw) ++{ ++} ++ + #ifdef CONFIG_MEMCG + static inline void lru_gen_init_memcg(struct mem_cgroup *memcg) + { +@@ -1028,6 +1034,9 @@ typedef struct pglist_data { + + unsigned long flags; + ++#ifdef CONFIG_LRU_GEN ++ struct mm_walk_args mm_walk_args; ++#endif + ZONE_PADDING(_pad2_) + + /* Per-node vmstats */ +--- a/include/linux/oom.h ++++ b/include/linux/oom.h +@@ -57,6 +57,22 @@ struct oom_control { + extern struct mutex oom_lock; + extern struct mutex oom_adj_mutex; + ++#ifdef CONFIG_MMU ++extern struct task_struct *oom_reaper_list; ++extern struct wait_queue_head oom_reaper_wait; ++ ++static inline bool oom_reaping_in_progress(void) ++{ ++ /* racy check to see if oom reaping could be in progress */ ++ return READ_ONCE(oom_reaper_list) || !waitqueue_active(&oom_reaper_wait); ++} ++#else ++static inline bool oom_reaping_in_progress(void) ++{ ++ return false; ++} ++#endif ++ + static inline void set_current_oom_origin(void) + { + current->signal->oom_flag_origin = true; +--- a/include/linux/swap.h ++++ b/include/linux/swap.h +@@ -137,6 +137,9 @@ union swap_header { + */ + struct reclaim_state { + unsigned long reclaimed_slab; ++#ifdef CONFIG_LRU_GEN ++ struct mm_walk_args *mm_walk_args; ++#endif + }; + + #ifdef __KERNEL__ +--- a/mm/memcontrol.c ++++ b/mm/memcontrol.c +@@ -1304,12 +1304,17 @@ void mem_cgroup_update_lru_size(struct l + *lru_size += nr_pages; + + size = *lru_size; ++#ifdef CONFIG_LRU_GEN ++ /* unlikely but not a bug when reset_batch_size() is pending */ ++ VM_WARN_ON(size + MAX_BATCH_SIZE < 0); ++#else + if (WARN_ONCE(size < 0, + "%s(%p, %d, %d): lru_size %ld\n", + __func__, lruvec, lru, nr_pages, size)) { + VM_BUG_ON(1); + *lru_size = 0; + } ++#endif + + if (nr_pages > 0) + *lru_size += nr_pages; +--- a/mm/oom_kill.c ++++ b/mm/oom_kill.c +@@ -508,8 +508,8 @@ bool process_shares_mm(struct task_struc + * victim (if that is possible) to help the OOM killer to move on. + */ + static struct task_struct *oom_reaper_th; +-static DECLARE_WAIT_QUEUE_HEAD(oom_reaper_wait); +-static struct task_struct *oom_reaper_list; ++DECLARE_WAIT_QUEUE_HEAD(oom_reaper_wait); ++struct task_struct *oom_reaper_list; + static DEFINE_SPINLOCK(oom_reaper_lock); + + bool __oom_reap_task_mm(struct mm_struct *mm) +--- a/mm/rmap.c ++++ b/mm/rmap.c +@@ -73,6 +73,7 @@ + #include + #include + #include ++#include + + #include + +@@ -793,6 +794,13 @@ static bool page_referenced_one(struct p + } + + if (pvmw.pte) { ++ /* the multigenerational lru exploits the spatial locality */ ++ if (lru_gen_enabled() && pte_young(*pvmw.pte) && ++ !(vma->vm_flags & VM_SEQ_READ)) { ++ lru_gen_look_around(&pvmw); ++ referenced++; ++ } ++ + if (ptep_clear_flush_young_notify(vma, address, + pvmw.pte)) { + /* +--- a/mm/vmscan.c ++++ b/mm/vmscan.c +@@ -51,6 +51,8 @@ + #include + #include + #include ++#include ++#include + + #include + #include +@@ -2887,6 +2889,15 @@ static bool can_age_anon_pages(struct pg + * shorthand helpers + ******************************************************************************/ + ++#define DEFINE_MAX_SEQ(lruvec) \ ++ unsigned long max_seq = READ_ONCE((lruvec)->evictable.max_seq) ++ ++#define DEFINE_MIN_SEQ(lruvec) \ ++ unsigned long min_seq[ANON_AND_FILE] = { \ ++ READ_ONCE((lruvec)->evictable.min_seq[0]), \ ++ READ_ONCE((lruvec)->evictable.min_seq[1]), \ ++ } ++ + #define for_each_gen_type_zone(gen, type, zone) \ + for ((gen) = 0; (gen) < MAX_NR_GENS; (gen)++) \ + for ((type) = 0; (type) < ANON_AND_FILE; (type)++) \ +@@ -2899,6 +2910,12 @@ static int page_lru_gen(struct page *pag + return ((flags & LRU_GEN_MASK) >> LRU_GEN_PGOFF) - 1; + } + ++static int get_swappiness(struct mem_cgroup *memcg) ++{ ++ return mem_cgroup_get_nr_swap_pages(memcg) >= MIN_BATCH_SIZE ? ++ mem_cgroup_swappiness(memcg) : 0; ++} ++ + static struct lruvec *get_lruvec(int nid, struct mem_cgroup *memcg) + { + struct pglist_data *pgdat = NODE_DATA(nid); +@@ -3229,6 +3246,926 @@ done: + } + + /****************************************************************************** ++ * the aging ++ ******************************************************************************/ ++ ++static int page_update_gen(struct page *page, int gen) ++{ ++ unsigned long old_flags, new_flags; ++ ++ VM_BUG_ON(gen >= MAX_NR_GENS); ++ ++ do { ++ new_flags = old_flags = READ_ONCE(page->flags); ++ ++ if (!(new_flags & LRU_GEN_MASK)) { ++ new_flags |= BIT(PG_referenced); ++ continue; ++ } ++ ++ new_flags &= ~LRU_GEN_MASK; ++ new_flags |= (gen + 1UL) << LRU_GEN_PGOFF; ++ } while (new_flags != old_flags && ++ cmpxchg(&page->flags, old_flags, new_flags) != old_flags); ++ ++ return ((old_flags & LRU_GEN_MASK) >> LRU_GEN_PGOFF) - 1; ++} ++ ++static void page_inc_gen(struct page *page, struct lruvec *lruvec, bool reclaiming) ++{ ++ int old_gen, new_gen; ++ unsigned long old_flags, new_flags; ++ int type = page_is_file_lru(page); ++ int zone = page_zonenum(page); ++ struct lrugen *lrugen = &lruvec->evictable; ++ ++ old_gen = lru_gen_from_seq(lrugen->min_seq[type]); ++ ++ do { ++ new_flags = old_flags = READ_ONCE(page->flags); ++ VM_BUG_ON_PAGE(!(new_flags & LRU_GEN_MASK), page); ++ ++ new_gen = ((new_flags & LRU_GEN_MASK) >> LRU_GEN_PGOFF) - 1; ++ /* page_update_gen() has updated this page? */ ++ if (new_gen >= 0 && new_gen != old_gen) { ++ list_move(&page->lru, &lrugen->lists[new_gen][type][zone]); ++ return; ++ } ++ ++ new_gen = (old_gen + 1) % MAX_NR_GENS; ++ ++ new_flags &= ~LRU_GEN_MASK; ++ new_flags |= (new_gen + 1UL) << LRU_GEN_PGOFF; ++ /* for end_page_writeback() */ ++ if (reclaiming) ++ new_flags |= BIT(PG_reclaim); ++ } while (cmpxchg(&page->flags, old_flags, new_flags) != old_flags); ++ ++ lru_gen_update_size(page, lruvec, old_gen, new_gen); ++ if (reclaiming) ++ list_move(&page->lru, &lrugen->lists[new_gen][type][zone]); ++ else ++ list_move_tail(&page->lru, &lrugen->lists[new_gen][type][zone]); ++} ++ ++static void update_batch_size(struct page *page, int old_gen, int new_gen, ++ struct mm_walk_args *args) ++{ ++ int type = page_is_file_lru(page); ++ int zone = page_zonenum(page); ++ int delta = thp_nr_pages(page); ++ ++ VM_BUG_ON(old_gen >= MAX_NR_GENS); ++ VM_BUG_ON(new_gen >= MAX_NR_GENS); ++ ++ args->batch_size++; ++ ++ args->nr_pages[old_gen][type][zone] -= delta; ++ args->nr_pages[new_gen][type][zone] += delta; ++} ++ ++static void reset_batch_size(struct lruvec *lruvec, struct mm_walk_args *args) ++{ ++ int gen, type, zone; ++ struct lrugen *lrugen = &lruvec->evictable; ++ ++ args->batch_size = 0; ++ ++ for_each_gen_type_zone(gen, type, zone) { ++ enum lru_list lru = type * LRU_FILE; ++ int delta = args->nr_pages[gen][type][zone]; ++ ++ if (!delta) ++ continue; ++ ++ args->nr_pages[gen][type][zone] = 0; ++ WRITE_ONCE(lrugen->sizes[gen][type][zone], ++ lrugen->sizes[gen][type][zone] + delta); ++ ++ if (lru_gen_is_active(lruvec, gen)) ++ lru += LRU_ACTIVE; ++ update_lru_size(lruvec, lru, zone, delta); ++ } ++} ++ ++static int should_skip_vma(unsigned long start, unsigned long end, struct mm_walk *walk) ++{ ++ struct address_space *mapping; ++ struct vm_area_struct *vma = walk->vma; ++ struct mm_walk_args *args = walk->private; ++ ++ if (!vma_is_accessible(vma) || is_vm_hugetlb_page(vma) || ++ (vma->vm_flags & (VM_LOCKED | VM_SPECIAL | VM_SEQ_READ))) ++ return true; ++ ++ if (vma_is_anonymous(vma)) ++ return !args->swappiness; ++ ++ if (WARN_ON_ONCE(!vma->vm_file || !vma->vm_file->f_mapping)) ++ return true; ++ ++ mapping = vma->vm_file->f_mapping; ++ if (!mapping->a_ops->writepage) ++ return true; ++ ++ return (shmem_mapping(mapping) && !args->swappiness) || mapping_unevictable(mapping); ++} ++ ++/* ++ * Some userspace memory allocators create many single-page VMAs. So instead of ++ * returning back to the PGD table for each of such VMAs, we finish at least an ++ * entire PMD table and therefore avoid many zigzags. ++ */ ++static bool get_next_vma(struct mm_walk *walk, unsigned long mask, unsigned long size, ++ unsigned long *start, unsigned long *end) ++{ ++ unsigned long next = round_up(*end, size); ++ ++ VM_BUG_ON(mask & size); ++ VM_BUG_ON(*start >= *end); ++ VM_BUG_ON((next & mask) != (*start & mask)); ++ ++ while (walk->vma) { ++ if (next >= walk->vma->vm_end) { ++ walk->vma = walk->vma->vm_next; ++ continue; ++ } ++ ++ if ((next & mask) != (walk->vma->vm_start & mask)) ++ return false; ++ ++ if (should_skip_vma(walk->vma->vm_start, walk->vma->vm_end, walk)) { ++ walk->vma = walk->vma->vm_next; ++ continue; ++ } ++ ++ *start = max(next, walk->vma->vm_start); ++ next = (next | ~mask) + 1; ++ /* rounded-up boundaries can wrap to 0 */ ++ *end = next && next < walk->vma->vm_end ? next : walk->vma->vm_end; ++ ++ return true; ++ } ++ ++ return false; ++} ++ ++static bool walk_pte_range(pmd_t *pmd, unsigned long start, unsigned long end, ++ struct mm_walk *walk) ++{ ++ int i; ++ pte_t *pte; ++ spinlock_t *ptl; ++ unsigned long addr; ++ int worth = 0; ++ struct mm_walk_args *args = walk->private; ++ int old_gen, new_gen = lru_gen_from_seq(args->max_seq); ++ ++ VM_BUG_ON(pmd_leaf(*pmd)); ++ ++ pte = pte_offset_map_lock(walk->mm, pmd, start & PMD_MASK, &ptl); ++ arch_enter_lazy_mmu_mode(); ++restart: ++ for (i = pte_index(start), addr = start; addr != end; i++, addr += PAGE_SIZE) { ++ struct page *page; ++ unsigned long pfn = pte_pfn(pte[i]); ++ ++ args->mm_stats[MM_LEAF_TOTAL]++; ++ ++ if (!pte_present(pte[i]) || is_zero_pfn(pfn)) ++ continue; ++ ++ if (WARN_ON_ONCE(pte_devmap(pte[i]) || pte_special(pte[i]))) ++ continue; ++ ++ if (!pte_young(pte[i])) { ++ args->mm_stats[MM_LEAF_OLD]++; ++ continue; ++ } ++ ++ VM_BUG_ON(!pfn_valid(pfn)); ++ if (pfn < args->start_pfn || pfn >= args->end_pfn) ++ continue; ++ ++ page = compound_head(pfn_to_page(pfn)); ++ if (page_to_nid(page) != args->node_id) ++ continue; ++ ++ if (page_memcg_rcu(page) != args->memcg) ++ continue; ++ ++ VM_BUG_ON(addr < walk->vma->vm_start || addr >= walk->vma->vm_end); ++ if (!ptep_test_and_clear_young(walk->vma, addr, pte + i)) ++ continue; ++ ++ args->mm_stats[MM_LEAF_YOUNG]++; ++ ++ if (pte_dirty(pte[i]) && !PageDirty(page) && ++ !(PageAnon(page) && PageSwapBacked(page) && !PageSwapCache(page))) ++ set_page_dirty(page); ++ ++ old_gen = page_update_gen(page, new_gen); ++ if (old_gen >= 0 && old_gen != new_gen) ++ update_batch_size(page, old_gen, new_gen, args); ++ ++ worth++; ++ } ++ ++ if (i < PTRS_PER_PTE && get_next_vma(walk, PMD_MASK, PAGE_SIZE, &start, &end)) ++ goto restart; ++ ++ arch_leave_lazy_mmu_mode(); ++ pte_unmap_unlock(pte, ptl); ++ ++ return worth >= MIN_BATCH_SIZE / 2; ++} ++ ++/* ++ * We scan PMD entries in two passes. The first pass reaches to PTE tables and ++ * doesn't take the PMD lock. The second pass clears the accessed bit on PMD ++ * entries and needs to take the PMD lock. ++ */ ++#if defined(CONFIG_TRANSPARENT_HUGEPAGE) || defined(CONFIG_ARCH_HAS_NONLEAF_PMD_YOUNG) ++static void walk_pmd_range_locked(pud_t *pud, unsigned long start, int offset, ++ struct vm_area_struct *vma, struct mm_walk *walk) ++{ ++ int i; ++ pmd_t *pmd; ++ spinlock_t *ptl; ++ struct mm_walk_args *args = walk->private; ++ int old_gen, new_gen = lru_gen_from_seq(args->max_seq); ++ ++ VM_BUG_ON(pud_leaf(*pud)); ++ ++ start = (start & PUD_MASK) + offset * PMD_SIZE; ++ pmd = pmd_offset(pud, start); ++ ptl = pmd_lock(walk->mm, pmd); ++ arch_enter_lazy_mmu_mode(); ++ ++ for_each_set_bit(i, args->bitmap, MIN_BATCH_SIZE) { ++ struct page *page; ++ unsigned long pfn = pmd_pfn(pmd[i]); ++ unsigned long addr = start + i * PMD_SIZE; ++ ++ if (!pmd_present(pmd[i]) || is_huge_zero_pmd(pmd[i])) ++ continue; ++ ++ if (WARN_ON_ONCE(pmd_devmap(pmd[i]))) ++ continue; ++ ++ if (!pmd_trans_huge(pmd[i])) { ++ if (IS_ENABLED(CONFIG_ARCH_HAS_NONLEAF_PMD_YOUNG)) ++ pmdp_test_and_clear_young(vma, addr, pmd + i); ++ continue; ++ } ++ ++ VM_BUG_ON(!pfn_valid(pfn)); ++ if (pfn < args->start_pfn || pfn >= args->end_pfn) ++ continue; ++ ++ page = pfn_to_page(pfn); ++ VM_BUG_ON_PAGE(PageTail(page), page); ++ if (page_to_nid(page) != args->node_id) ++ continue; ++ ++ if (page_memcg_rcu(page) != args->memcg) ++ continue; ++ ++ VM_BUG_ON(addr < vma->vm_start || addr >= vma->vm_end); ++ if (!pmdp_test_and_clear_young(vma, addr, pmd + i)) ++ continue; ++ ++ args->mm_stats[MM_LEAF_YOUNG]++; ++ ++ if (pmd_dirty(pmd[i]) && !PageDirty(page) && ++ !(PageAnon(page) && PageSwapBacked(page) && !PageSwapCache(page))) ++ set_page_dirty(page); ++ ++ old_gen = page_update_gen(page, new_gen); ++ if (old_gen >= 0 && old_gen != new_gen) ++ update_batch_size(page, old_gen, new_gen, args); ++ } ++ ++ arch_leave_lazy_mmu_mode(); ++ spin_unlock(ptl); ++ ++ bitmap_zero(args->bitmap, MIN_BATCH_SIZE); ++} ++#else ++static void walk_pmd_range_locked(pud_t *pud, unsigned long start, int offset, ++ struct vm_area_struct *vma, struct mm_walk *walk) ++{ ++} ++#endif ++ ++static void walk_pmd_range(pud_t *pud, unsigned long start, unsigned long end, ++ struct mm_walk *walk) ++{ ++ int i; ++ pmd_t *pmd; ++ unsigned long next; ++ unsigned long addr; ++ struct vm_area_struct *vma; ++ int offset = -1; ++ bool reset = false; ++ struct mm_walk_args *args = walk->private; ++ struct lruvec *lruvec = get_lruvec(args->node_id, args->memcg); ++ ++ VM_BUG_ON(pud_leaf(*pud)); ++ ++ pmd = pmd_offset(pud, start & PUD_MASK); ++restart: ++ vma = walk->vma; ++ for (i = pmd_index(start), addr = start; addr != end; i++, addr = next) { ++ pmd_t val = pmd_read_atomic(pmd + i); ++ ++ /* for pmd_read_atomic() */ ++ barrier(); ++ ++ next = pmd_addr_end(addr, end); ++ ++ if (!pmd_present(val)) { ++ args->mm_stats[MM_LEAF_TOTAL]++; ++ continue; ++ } ++ ++#ifdef CONFIG_TRANSPARENT_HUGEPAGE ++ if (pmd_trans_huge(val)) { ++ unsigned long pfn = pmd_pfn(val); ++ ++ args->mm_stats[MM_LEAF_TOTAL]++; ++ ++ if (is_huge_zero_pmd(val)) ++ continue; ++ ++ if (!pmd_young(val)) { ++ args->mm_stats[MM_LEAF_OLD]++; ++ continue; ++ } ++ ++ if (pfn < args->start_pfn || pfn >= args->end_pfn) ++ continue; ++ ++ if (offset < 0) ++ offset = i; ++ else if (i - offset >= MIN_BATCH_SIZE) { ++ walk_pmd_range_locked(pud, start, offset, vma, walk); ++ offset = i; ++ } ++ __set_bit(i - offset, args->bitmap); ++ reset = true; ++ continue; ++ } ++#endif ++ args->mm_stats[MM_NONLEAF_TOTAL]++; ++ ++#ifdef CONFIG_ARCH_HAS_NONLEAF_PMD_YOUNG ++ if (!pmd_young(val)) ++ continue; ++ ++ if (offset < 0) ++ offset = i; ++ else if (i - offset >= MIN_BATCH_SIZE) { ++ walk_pmd_range_locked(pud, start, offset, vma, walk); ++ offset = i; ++ reset = false; ++ } ++ __set_bit(i - offset, args->bitmap); ++#endif ++ if (args->use_filter && !test_bloom_filter(lruvec, args->max_seq, pmd + i)) ++ continue; ++ ++ args->mm_stats[MM_NONLEAF_PREV]++; ++ ++ if (!walk_pte_range(&val, addr, next, walk)) ++ continue; ++ ++ args->mm_stats[MM_NONLEAF_CUR]++; ++ ++ set_bloom_filter(lruvec, args->max_seq + 1, pmd + i); ++ } ++ ++ if (reset) { ++ walk_pmd_range_locked(pud, start, offset, vma, walk); ++ offset = -1; ++ reset = false; ++ } ++ ++ if (i < PTRS_PER_PMD && get_next_vma(walk, PUD_MASK, PMD_SIZE, &start, &end)) ++ goto restart; ++ ++ if (offset >= 0) ++ walk_pmd_range_locked(pud, start, offset, vma, walk); ++} ++ ++static int walk_pud_range(p4d_t *p4d, unsigned long start, unsigned long end, ++ struct mm_walk *walk) ++{ ++ int i; ++ pud_t *pud; ++ unsigned long addr; ++ unsigned long next; ++ struct mm_walk_args *args = walk->private; ++ ++ VM_BUG_ON(p4d_leaf(*p4d)); ++ ++ pud = pud_offset(p4d, start & P4D_MASK); ++restart: ++ for (i = pud_index(start), addr = start; addr != end; i++, addr = next) { ++ pud_t val = READ_ONCE(pud[i]); ++ ++ next = pud_addr_end(addr, end); ++ ++ if (!pud_present(val) || WARN_ON_ONCE(pud_leaf(val))) ++ continue; ++ ++ walk_pmd_range(&val, addr, next, walk); ++ ++ if (args->batch_size >= MAX_BATCH_SIZE) { ++ end = (addr | ~PUD_MASK) + 1; ++ goto done; ++ } ++ } ++ ++ if (i < PTRS_PER_PUD && get_next_vma(walk, P4D_MASK, PUD_SIZE, &start, &end)) ++ goto restart; ++ ++ end = round_up(end, P4D_SIZE); ++done: ++ /* rounded-up boundaries can wrap to 0 */ ++ args->next_addr = end && walk->vma ? max(end, walk->vma->vm_start) : 0; ++ ++ return -EAGAIN; ++} ++ ++static void walk_mm(struct lruvec *lruvec, struct mm_struct *mm, struct mm_walk_args *args) ++{ ++ static const struct mm_walk_ops mm_walk_ops = { ++ .test_walk = should_skip_vma, ++ .p4d_entry = walk_pud_range, ++ }; ++ ++ int err; ++ ++ args->next_addr = FIRST_USER_ADDRESS; ++ ++ do { ++ unsigned long start = args->next_addr; ++ unsigned long end = mm->highest_vm_end; ++ ++ err = -EBUSY; ++ ++ rcu_read_lock(); ++#ifdef CONFIG_MEMCG ++ if (args->memcg && atomic_read(&args->memcg->moving_account)) ++ goto contended; ++#endif ++ if (!mmap_read_trylock(mm)) ++ goto contended; ++ ++ err = walk_page_range(mm, start, end, &mm_walk_ops, args); ++ ++ mmap_read_unlock(mm); ++ ++ if (args->batch_size) { ++ spin_lock_irq(&lruvec->lru_lock); ++ reset_batch_size(lruvec, args); ++ spin_unlock_irq(&lruvec->lru_lock); ++ } ++contended: ++ rcu_read_unlock(); ++ ++ cond_resched(); ++ } while (err == -EAGAIN && args->next_addr && !mm_is_oom_victim(mm)); ++} ++ ++static struct mm_walk_args *alloc_mm_walk_args(void) ++{ ++ if (!current->reclaim_state || !current->reclaim_state->mm_walk_args) ++ return kvzalloc(sizeof(struct mm_walk_args), GFP_KERNEL); ++ ++ return current->reclaim_state->mm_walk_args; ++} ++ ++static void free_mm_walk_args(struct mm_walk_args *args) ++{ ++ if (!current->reclaim_state || !current->reclaim_state->mm_walk_args) ++ kvfree(args); ++} ++ ++static bool inc_min_seq(struct lruvec *lruvec, int type) ++{ ++ int gen, zone; ++ int remaining = MAX_BATCH_SIZE; ++ struct lrugen *lrugen = &lruvec->evictable; ++ ++ VM_BUG_ON(!seq_is_valid(lruvec)); ++ ++ if (get_nr_gens(lruvec, type) != MAX_NR_GENS) ++ return true; ++ ++ gen = lru_gen_from_seq(lrugen->min_seq[type]); ++ ++ for (zone = 0; zone < MAX_NR_ZONES; zone++) { ++ struct list_head *head = &lrugen->lists[gen][type][zone]; ++ ++ while (!list_empty(head)) { ++ struct page *page = lru_to_page(head); ++ ++ VM_BUG_ON_PAGE(PageTail(page), page); ++ VM_BUG_ON_PAGE(PageUnevictable(page), page); ++ VM_BUG_ON_PAGE(PageActive(page), page); ++ VM_BUG_ON_PAGE(page_is_file_lru(page) != type, page); ++ VM_BUG_ON_PAGE(page_zonenum(page) != zone, page); ++ ++ prefetchw_prev_lru_page(page, head, flags); ++ ++ page_inc_gen(page, lruvec, false); ++ ++ if (!--remaining) ++ return false; ++ } ++ } ++ ++ WRITE_ONCE(lrugen->min_seq[type], lrugen->min_seq[type] + 1); ++ ++ return true; ++} ++ ++static bool try_to_inc_min_seq(struct lruvec *lruvec, int swappiness) ++{ ++ int gen, type, zone; ++ bool success = false; ++ struct lrugen *lrugen = &lruvec->evictable; ++ DEFINE_MIN_SEQ(lruvec); ++ ++ VM_BUG_ON(!seq_is_valid(lruvec)); ++ ++ for (type = 0; type < ANON_AND_FILE; type++) { ++ while (lrugen->max_seq - min_seq[type] >= MIN_NR_GENS) { ++ gen = lru_gen_from_seq(min_seq[type]); ++ ++ for (zone = 0; zone < MAX_NR_ZONES; zone++) { ++ if (!list_empty(&lrugen->lists[gen][type][zone])) ++ goto next; ++ } ++ ++ min_seq[type]++; ++ } ++next: ++ ; ++ } ++ ++ min_seq[0] = min(min_seq[0], min_seq[1]); ++ if (swappiness) ++ min_seq[1] = max(min_seq[0], lrugen->min_seq[1]); ++ ++ for (type = 0; type < ANON_AND_FILE; type++) { ++ if (min_seq[type] == lrugen->min_seq[type]) ++ continue; ++ ++ WRITE_ONCE(lrugen->min_seq[type], min_seq[type]); ++ success = true; ++ } ++ ++ return success; ++} ++ ++static void inc_max_seq(struct lruvec *lruvec, unsigned long max_seq) ++{ ++ int gen, type, zone; ++ struct lrugen *lrugen = &lruvec->evictable; ++ ++ spin_lock_irq(&lruvec->lru_lock); ++ ++ VM_BUG_ON(!seq_is_valid(lruvec)); ++ ++ if (max_seq != lrugen->max_seq) ++ goto unlock; ++ ++ if (!try_to_inc_min_seq(lruvec, true)) { ++ for (type = ANON_AND_FILE - 1; type >= 0; type--) { ++ while (!inc_min_seq(lruvec, type)) { ++ spin_unlock_irq(&lruvec->lru_lock); ++ cond_resched(); ++ spin_lock_irq(&lruvec->lru_lock); ++ } ++ } ++ } ++ ++ gen = lru_gen_from_seq(lrugen->max_seq - 1); ++ for (type = 0; type < ANON_AND_FILE; type++) { ++ for (zone = 0; zone < MAX_NR_ZONES; zone++) { ++ enum lru_list lru = type * LRU_FILE; ++ long delta = lrugen->sizes[gen][type][zone]; ++ ++ if (!delta) ++ continue; ++ ++ WARN_ON_ONCE(delta != (int)delta); ++ ++ update_lru_size(lruvec, lru, zone, delta); ++ update_lru_size(lruvec, lru + LRU_ACTIVE, zone, -delta); ++ } ++ } ++ ++ gen = lru_gen_from_seq(lrugen->max_seq + 1); ++ for (type = 0; type < ANON_AND_FILE; type++) { ++ for (zone = 0; zone < MAX_NR_ZONES; zone++) { ++ enum lru_list lru = type * LRU_FILE; ++ long delta = lrugen->sizes[gen][type][zone]; ++ ++ if (!delta) ++ continue; ++ ++ WARN_ON_ONCE(delta != (int)delta); ++ ++ update_lru_size(lruvec, lru, zone, -delta); ++ update_lru_size(lruvec, lru + LRU_ACTIVE, zone, delta); ++ } ++ } ++ ++ WRITE_ONCE(lrugen->timestamps[gen], jiffies); ++ /* make sure all preceding modifications appear first */ ++ smp_store_release(&lrugen->max_seq, lrugen->max_seq + 1); ++unlock: ++ spin_unlock_irq(&lruvec->lru_lock); ++} ++ ++/* Main function used by the foreground, the background and the user-triggered aging. */ ++static bool try_to_inc_max_seq(struct lruvec *lruvec, struct scan_control *sc, int swappiness, ++ unsigned long max_seq, bool use_filter) ++{ ++ bool last; ++ struct mm_walk_args *args; ++ struct mm_struct *mm = NULL; ++ struct lrugen *lrugen = &lruvec->evictable; ++ struct mem_cgroup *memcg = lruvec_memcg(lruvec); ++ struct pglist_data *pgdat = lruvec_pgdat(lruvec); ++ int nid = pgdat->node_id; ++ ++ VM_BUG_ON(max_seq > READ_ONCE(lrugen->max_seq)); ++ ++ /* ++ * If we are not from run_aging() and clearing the accessed bit may ++ * trigger page faults, then don't proceed to clearing all accessed ++ * PTEs. Instead, fallback to lru_gen_look_around(), which only clears a ++ * handful of accessed PTEs. This is less efficient but causes fewer ++ * page faults on CPUs that don't have the capability. ++ */ ++ if ((current->flags & PF_MEMALLOC) && !arch_has_hw_pte_young(false)) { ++ inc_max_seq(lruvec, max_seq); ++ return true; ++ } ++ ++ args = alloc_mm_walk_args(); ++ if (!args) ++ return false; ++ ++ args->memcg = memcg; ++ args->max_seq = max_seq; ++ args->start_pfn = pgdat->node_start_pfn; ++ args->end_pfn = pgdat_end_pfn(pgdat); ++ args->node_id = nid; ++ args->swappiness = swappiness; ++ args->use_filter = use_filter; ++ ++ do { ++ last = get_next_mm(lruvec, args, &mm); ++ if (mm) ++ walk_mm(lruvec, mm, args); ++ ++ cond_resched(); ++ } while (mm); ++ ++ free_mm_walk_args(args); ++ ++ if (!last) { ++ /* don't wait unless we may have trouble reclaiming */ ++ if (!current_is_kswapd() && sc->priority < DEF_PRIORITY - 2) ++ wait_event_killable(lruvec->mm_walk.wait, ++ max_seq < READ_ONCE(lrugen->max_seq)); ++ ++ return max_seq < READ_ONCE(lrugen->max_seq); ++ } ++ ++ VM_BUG_ON(max_seq != READ_ONCE(lrugen->max_seq)); ++ ++ inc_max_seq(lruvec, max_seq); ++ /* either we see any waiters or they will see updated max_seq */ ++ if (wq_has_sleeper(&lruvec->mm_walk.wait)) ++ wake_up_all(&lruvec->mm_walk.wait); ++ ++ wakeup_flusher_threads(WB_REASON_VMSCAN); ++ ++ return true; ++} ++ ++static long get_nr_evictable(struct lruvec *lruvec, struct scan_control *sc, int swappiness, ++ unsigned long max_seq, unsigned long *min_seq, bool *low) ++{ ++ int gen, type, zone; ++ long max = 0; ++ long min = 0; ++ struct lrugen *lrugen = &lruvec->evictable; ++ ++ for (type = !swappiness; type < ANON_AND_FILE; type++) { ++ unsigned long seq; ++ ++ for (seq = min_seq[type]; seq <= max_seq; seq++) { ++ long size = 0; ++ ++ gen = lru_gen_from_seq(seq); ++ ++ for (zone = 0; zone <= sc->reclaim_idx; zone++) ++ size += READ_ONCE(lrugen->sizes[gen][type][zone]); ++ ++ max += size; ++ if (type && max_seq - seq >= MIN_NR_GENS) ++ min += size; ++ } ++ } ++ ++ *low = max_seq - min_seq[1] <= MIN_NR_GENS && min < MIN_BATCH_SIZE; ++ ++ return max > 0 ? max : 0; ++} ++ ++static bool age_lruvec(struct lruvec *lruvec, struct scan_control *sc, ++ unsigned long min_ttl) ++{ ++ bool low; ++ long nr_to_scan; ++ struct mem_cgroup *memcg = lruvec_memcg(lruvec); ++ int swappiness = get_swappiness(memcg); ++ DEFINE_MAX_SEQ(lruvec); ++ DEFINE_MIN_SEQ(lruvec); ++ ++ if (mem_cgroup_below_min(memcg)) ++ return false; ++ ++ if (min_ttl) { ++ int gen = lru_gen_from_seq(min_seq[1]); ++ unsigned long birth = READ_ONCE(lruvec->evictable.timestamps[gen]); ++ ++ if (time_is_after_jiffies(birth + min_ttl)) ++ return false; ++ } ++ ++ nr_to_scan = get_nr_evictable(lruvec, sc, swappiness, max_seq, min_seq, &low); ++ if (!nr_to_scan) ++ return false; ++ ++ nr_to_scan >>= sc->priority; ++ ++ if (!mem_cgroup_online(memcg)) ++ nr_to_scan++; ++ ++ if (nr_to_scan && low && (!mem_cgroup_below_low(memcg) || sc->memcg_low_reclaim)) ++ try_to_inc_max_seq(lruvec, sc, swappiness, max_seq, true); ++ ++ return true; ++} ++ ++/* Protect the working set accessed within the last N milliseconds. */ ++static unsigned long lru_gen_min_ttl __read_mostly; ++ ++static void lru_gen_age_node(struct pglist_data *pgdat, struct scan_control *sc) ++{ ++ struct mem_cgroup *memcg; ++ bool success = false; ++ unsigned long min_ttl = READ_ONCE(lru_gen_min_ttl); ++ ++ VM_BUG_ON(!current_is_kswapd()); ++ ++ if (!sc->force_deactivate) { ++ sc->force_deactivate = 1; ++ return; ++ } ++ ++ current->reclaim_state->mm_walk_args = &pgdat->mm_walk_args; ++ ++ memcg = mem_cgroup_iter(NULL, NULL, NULL); ++ do { ++ struct lruvec *lruvec = mem_cgroup_lruvec(memcg, pgdat); ++ ++ if (age_lruvec(lruvec, sc, min_ttl)) ++ success = true; ++ ++ cond_resched(); ++ } while ((memcg = mem_cgroup_iter(NULL, memcg, NULL))); ++ ++ if (!success && mutex_trylock(&oom_lock)) { ++ struct oom_control oc = { ++ .gfp_mask = sc->gfp_mask, ++ .order = sc->order, ++ }; ++ ++ /* to avoid overkilling */ ++ if (!oom_reaping_in_progress()) ++ out_of_memory(&oc); ++ ++ mutex_unlock(&oom_lock); ++ } ++ ++ current->reclaim_state->mm_walk_args = NULL; ++} ++ ++/* Scan the vicinity of an accessed PTE when shrink_page_list() uses the rmap. */ ++void lru_gen_look_around(struct page_vma_mapped_walk *pvmw) ++{ ++ int i; ++ pte_t *pte; ++ struct page *page; ++ int old_gen, new_gen; ++ unsigned long start; ++ unsigned long end; ++ unsigned long addr; ++ struct mm_walk_args *args; ++ int worth = 0; ++ struct mem_cgroup *memcg = page_memcg(pvmw->page); ++ struct pglist_data *pgdat = page_pgdat(pvmw->page); ++ struct lruvec *lruvec = mem_cgroup_lruvec(memcg, pgdat); ++ DEFINE_MAX_SEQ(lruvec); ++ ++ lockdep_assert_held(pvmw->ptl); ++ VM_BUG_ON_PAGE(PageLRU(pvmw->page), pvmw->page); ++ ++ args = current->reclaim_state ? current->reclaim_state->mm_walk_args : NULL; ++ if (!args) ++ return; ++ ++ start = max(pvmw->address & PMD_MASK, pvmw->vma->vm_start); ++ end = min(pvmw->address | ~PMD_MASK, pvmw->vma->vm_end - 1) + 1; ++ ++ if (end - start > MIN_BATCH_SIZE * PAGE_SIZE) { ++ if (pvmw->address - start < MIN_BATCH_SIZE * PAGE_SIZE / 2) ++ end = start + MIN_BATCH_SIZE * PAGE_SIZE; ++ else if (end - pvmw->address < MIN_BATCH_SIZE * PAGE_SIZE / 2) ++ start = end - MIN_BATCH_SIZE * PAGE_SIZE; ++ else { ++ start = pvmw->address - MIN_BATCH_SIZE * PAGE_SIZE / 2; ++ end = pvmw->address + MIN_BATCH_SIZE * PAGE_SIZE / 2; ++ } ++ } ++ ++ pte = pvmw->pte - (pvmw->address - start) / PAGE_SIZE; ++ new_gen = lru_gen_from_seq(max_seq); ++ ++ lock_page_memcg(pvmw->page); ++ arch_enter_lazy_mmu_mode(); ++ ++ for (i = 0, addr = start; addr != end; i++, addr += PAGE_SIZE) { ++ unsigned long pfn = pte_pfn(pte[i]); ++ ++ if (!pte_present(pte[i]) || is_zero_pfn(pfn)) ++ continue; ++ ++ if (WARN_ON_ONCE(pte_devmap(pte[i]) || pte_special(pte[i]))) ++ continue; ++ ++ VM_BUG_ON(!pfn_valid(pfn)); ++ if (pfn < pgdat->node_start_pfn || pfn >= pgdat_end_pfn(pgdat)) ++ continue; ++ ++ worth++; ++ ++ if (!pte_young(pte[i])) ++ continue; ++ ++ page = compound_head(pfn_to_page(pfn)); ++ if (page_to_nid(page) != pgdat->node_id) ++ continue; ++ ++ if (page_memcg_rcu(page) != memcg) ++ continue; ++ ++ VM_BUG_ON(addr < pvmw->vma->vm_start || addr >= pvmw->vma->vm_end); ++ if (!ptep_test_and_clear_young(pvmw->vma, addr, pte + i)) ++ continue; ++ ++ if (pte_dirty(pte[i]) && !PageDirty(page) && ++ !(PageAnon(page) && PageSwapBacked(page) && !PageSwapCache(page))) ++ __set_bit(i, args->bitmap); ++ ++ old_gen = page_update_gen(page, new_gen); ++ if (old_gen >= 0 && old_gen != new_gen) ++ update_batch_size(page, old_gen, new_gen, args); ++ } ++ ++ arch_leave_lazy_mmu_mode(); ++ unlock_page_memcg(pvmw->page); ++ ++ if (worth >= MIN_BATCH_SIZE / 2) ++ set_bloom_filter(lruvec, max_seq, pvmw->pmd); ++ ++ for_each_set_bit(i, args->bitmap, MIN_BATCH_SIZE) ++ set_page_dirty(pte_page(pte[i])); ++ ++ bitmap_zero(args->bitmap, MIN_BATCH_SIZE); ++} ++ ++/****************************************************************************** + * state change + ******************************************************************************/ + +@@ -3477,6 +4414,12 @@ static int __init init_lru_gen(void) + }; + late_initcall(init_lru_gen); + ++#else ++ ++static void lru_gen_age_node(struct pglist_data *pgdat, struct scan_control *sc) ++{ ++} ++ + #endif /* CONFIG_LRU_GEN */ + + static void shrink_lruvec(struct lruvec *lruvec, struct scan_control *sc) +@@ -4333,6 +5276,11 @@ static void age_active_anon(struct pglis + struct mem_cgroup *memcg; + struct lruvec *lruvec; + ++ if (lru_gen_enabled()) { ++ lru_gen_age_node(pgdat, sc); ++ return; ++ } ++ + if (!can_age_anon_pages(pgdat, sc)) + return; + diff --git a/target/linux/generic/backport-6.1/020-v6.1-07-mm-multigenerational-lru-eviction.patch b/target/linux/generic/backport-6.1/020-v6.1-07-mm-multigenerational-lru-eviction.patch new file mode 100644 index 0000000000..09e4315dcc --- /dev/null +++ b/target/linux/generic/backport-6.1/020-v6.1-07-mm-multigenerational-lru-eviction.patch @@ -0,0 +1,1002 @@ +From f4b881ce07ccb2a519f664afaa2a68225b612ca3 Mon Sep 17 00:00:00 2001 +From: Yu Zhao +Date: Tue, 29 Jun 2021 20:46:47 -0600 +Subject: [PATCH 07/10] mm: multigenerational lru: eviction + +The eviction consumes old generations. Given an lruvec, the eviction +scans pages on lrugen->lists indexed by anon and file min_seq[] +(modulo MAX_NR_GENS). It first tries to select a type based on the +values of min_seq[]. If they are equal, it selects the type that has +a lower refaulted %. The eviction sorts a page according to its +updated generation number if the aging has found this page accessed. +It also moves a page to the next generation if this page is from an +upper tier that has a higher refaulted % than the base tier. The +eviction increments min_seq[] of a selected type when it finds +lrugen->lists indexed by min_seq[] of this selected type are empty. + +Each generation is divided into multiple tiers. Tiers represent +different ranges of numbers of accesses from file descriptors only. +Pages accessed N times via file descriptors belong to tier +order_base_2(N). Each generation contains at most MAX_NR_TIERS tiers, +and they require additional MAX_NR_TIERS-2 bits in page->flags. In +contrast to moving between generations which requires list operations, +moving between tiers only involves operations on page->flags and +therefore has a negligible cost. A feedback loop modeled after the PID +controller monitors refaulted % across all tiers and decides when to +protect pages from which tiers. + +Unmapped pages are initially added to the oldest generation and then +conditionally protected by tiers. Each tier keeps track of how many +pages from it have refaulted. Tier 0 is the base tier and pages from +it are evicted unconditionally because there are no better candidates. +Pages from an upper tier are either evicted or moved to the next +generation, depending on whether this upper tier has a higher +refaulted % than the base tier. This model has the following +advantages: + 1) It removes the cost in the buffered access path and reduces the + overall cost of protection because pages are conditionally protected + in the reclaim path. + 2) It takes mapped pages into account and avoids overprotecting + pages accessed multiple times via file descriptors. + 3 Additional tiers improve the protection of pages accessed more + than twice. + +Signed-off-by: Yu Zhao +Tested-by: Konstantin Kharlamov +Change-Id: I64c06d8f2cdb83ac7d56c7e1d07f043483956cac +--- + include/linux/mm_inline.h | 10 + + include/linux/mmzone.h | 33 +++ + mm/swap.c | 42 +++ + mm/vmscan.c | 555 +++++++++++++++++++++++++++++++++++++- + mm/workingset.c | 120 ++++++++- + 5 files changed, 757 insertions(+), 3 deletions(-) + +--- a/include/linux/mm_inline.h ++++ b/include/linux/mm_inline.h +@@ -106,6 +106,14 @@ static inline int lru_hist_from_seq(unsi + return seq % NR_HIST_GENS; + } + ++/* Convert the number of accesses to a tier. See the comment on MAX_NR_TIERS. */ ++static inline int lru_tier_from_refs(int refs) ++{ ++ VM_BUG_ON(refs > BIT(LRU_REFS_WIDTH)); ++ ++ return order_base_2(refs + 1); ++} ++ + /* The youngest and the second youngest generations are counted as active. */ + static inline bool lru_gen_is_active(struct lruvec *lruvec, int gen) + { +@@ -226,6 +234,8 @@ static inline bool lru_gen_del_page(stru + gen = ((new_flags & LRU_GEN_MASK) >> LRU_GEN_PGOFF) - 1; + + new_flags &= ~LRU_GEN_MASK; ++ if ((new_flags & LRU_REFS_FLAGS) != LRU_REFS_FLAGS) ++ new_flags &= ~(LRU_REFS_MASK | LRU_REFS_FLAGS); + /* for shrink_page_list() */ + if (reclaiming) + new_flags &= ~(BIT(PG_referenced) | BIT(PG_reclaim)); +--- a/include/linux/mmzone.h ++++ b/include/linux/mmzone.h +@@ -319,6 +319,30 @@ struct page_vma_mapped_walk; + #define MIN_NR_GENS 2 + #define MAX_NR_GENS ((unsigned int)CONFIG_NR_LRU_GENS) + ++/* ++ * Each generation is divided into multiple tiers. Tiers represent different ++ * ranges of numbers of accesses from file descriptors, i.e., ++ * mark_page_accessed(). In contrast to moving between generations which ++ * requires the lru lock, moving between tiers only involves an atomic ++ * operation on page->flags and therefore has a negligible cost. ++ * ++ * The purposes of tiers are to: ++ * 1) estimate whether pages accessed multiple times via file descriptors are ++ * more active than pages accessed only via page tables by separating the two ++ * access types into upper tiers and the base tier, and comparing refaulted % ++ * across all tiers. ++ * 2) improve buffered io performance by deferring the protection of pages ++ * accessed multiple times until the eviction. That is the protection happens ++ * in the reclaim path, not the access path. ++ * ++ * Pages accessed N times via file descriptors belong to tier order_base_2(N). ++ * The base tier may be marked by PageReferenced(). All upper tiers are marked ++ * by PageReferenced() && PageWorkingset(). Additional bits from page->flags are ++ * used to support more than one upper tier. ++ */ ++#define MAX_NR_TIERS ((unsigned int)CONFIG_TIERS_PER_GEN) ++#define LRU_REFS_FLAGS (BIT(PG_referenced) | BIT(PG_workingset)) ++ + /* Whether to keep stats for historical generations. */ + #ifdef CONFIG_LRU_GEN_STATS + #define NR_HIST_GENS ((unsigned int)CONFIG_NR_LRU_GENS) +@@ -337,6 +361,15 @@ struct lrugen { + struct list_head lists[MAX_NR_GENS][ANON_AND_FILE][MAX_NR_ZONES]; + /* the sizes of the multigenerational lru lists in pages */ + unsigned long sizes[MAX_NR_GENS][ANON_AND_FILE][MAX_NR_ZONES]; ++ /* the exponential moving average of refaulted */ ++ unsigned long avg_refaulted[ANON_AND_FILE][MAX_NR_TIERS]; ++ /* the exponential moving average of protected+evicted */ ++ unsigned long avg_total[ANON_AND_FILE][MAX_NR_TIERS]; ++ /* the base tier isn't protected, hence the minus one */ ++ unsigned long protected[NR_HIST_GENS][ANON_AND_FILE][MAX_NR_TIERS - 1]; ++ /* incremented without holding the lru lock */ ++ atomic_long_t evicted[NR_HIST_GENS][ANON_AND_FILE][MAX_NR_TIERS]; ++ atomic_long_t refaulted[NR_HIST_GENS][ANON_AND_FILE][MAX_NR_TIERS]; + /* whether the multigenerational lru is enabled */ + bool enabled[ANON_AND_FILE]; + }; +--- a/mm/swap.c ++++ b/mm/swap.c +@@ -389,6 +389,43 @@ static void __lru_cache_activate_page(st + local_unlock(&lru_pvecs.lock); + } + ++#ifdef CONFIG_LRU_GEN ++static void page_inc_refs(struct page *page) ++{ ++ unsigned long refs; ++ unsigned long old_flags, new_flags; ++ ++ if (PageUnevictable(page)) ++ return; ++ ++ /* see the comment on MAX_NR_TIERS */ ++ do { ++ new_flags = old_flags = READ_ONCE(page->flags); ++ ++ if (!(new_flags & BIT(PG_referenced))) { ++ new_flags |= BIT(PG_referenced); ++ continue; ++ } ++ ++ if (!(new_flags & BIT(PG_workingset))) { ++ new_flags |= BIT(PG_workingset); ++ continue; ++ } ++ ++ refs = new_flags & LRU_REFS_MASK; ++ refs = min(refs + BIT(LRU_REFS_PGOFF), LRU_REFS_MASK); ++ ++ new_flags &= ~LRU_REFS_MASK; ++ new_flags |= refs; ++ } while (new_flags != old_flags && ++ cmpxchg(&page->flags, old_flags, new_flags) != old_flags); ++} ++#else ++static void page_inc_refs(struct page *page) ++{ ++} ++#endif /* CONFIG_LRU_GEN */ ++ + /* + * Mark a page as having seen activity. + * +@@ -403,6 +440,11 @@ void mark_page_accessed(struct page *pag + { + page = compound_head(page); + ++ if (lru_gen_enabled()) { ++ page_inc_refs(page); ++ return; ++ } ++ + if (!PageReferenced(page)) { + SetPageReferenced(page); + } else if (PageUnevictable(page)) { +--- a/mm/vmscan.c ++++ b/mm/vmscan.c +@@ -1145,9 +1145,11 @@ static int __remove_mapping(struct addre + + if (PageSwapCache(page)) { + swp_entry_t swap = { .val = page_private(page) }; +- mem_cgroup_swapout(page, swap); ++ ++ /* get a shadow entry before page_memcg() is cleared */ + if (reclaimed && !mapping_exiting(mapping)) + shadow = workingset_eviction(page, target_memcg); ++ mem_cgroup_swapout(page, swap); + __delete_from_swap_cache(page, swap, shadow); + xa_unlock_irq(&mapping->i_pages); + put_swap_page(page, swap); +@@ -1410,6 +1412,11 @@ retry: + if (!sc->may_unmap && page_mapped(page)) + goto keep_locked; + ++ /* lru_gen_look_around() has updated this page? */ ++ if (lru_gen_enabled() && !ignore_references && ++ page_mapped(page) && PageReferenced(page)) ++ goto keep_locked; ++ + may_enter_fs = (sc->gfp_mask & __GFP_FS) || + (PageSwapCache(page) && (sc->gfp_mask & __GFP_IO)); + +@@ -2570,6 +2577,9 @@ static void prepare_scan_count(pg_data_t + unsigned long file; + struct lruvec *target_lruvec; + ++ if (lru_gen_enabled()) ++ return; ++ + target_lruvec = mem_cgroup_lruvec(sc->target_mem_cgroup, pgdat); + + /* +@@ -2910,6 +2920,17 @@ static int page_lru_gen(struct page *pag + return ((flags & LRU_GEN_MASK) >> LRU_GEN_PGOFF) - 1; + } + ++static int page_lru_tier(struct page *page) ++{ ++ int refs; ++ unsigned long flags = READ_ONCE(page->flags); ++ ++ refs = (flags & LRU_REFS_FLAGS) == LRU_REFS_FLAGS ? ++ ((flags & LRU_REFS_MASK) >> LRU_REFS_PGOFF) + 1 : 0; ++ ++ return lru_tier_from_refs(refs); ++} ++ + static int get_swappiness(struct mem_cgroup *memcg) + { + return mem_cgroup_get_nr_swap_pages(memcg) >= MIN_BATCH_SIZE ? +@@ -3246,6 +3267,91 @@ done: + } + + /****************************************************************************** ++ * refault feedback loop ++ ******************************************************************************/ ++ ++/* ++ * A feedback loop modeled after the PID controller. Currently supports the ++ * proportional (P) and the integral (I) terms; the derivative (D) term can be ++ * added if necessary. The setpoint (SP) is the desired position; the process ++ * variable (PV) is the measured position. The error is the difference between ++ * the SP and the PV. A positive error results in a positive control output ++ * correction, which, in our case, is to allow eviction. ++ * ++ * The P term is refaulted % of the current generation being evicted. The I ++ * term is the exponential moving average of refaulted % of previously evicted ++ * generations, using the smoothing factor 1/2. ++ * ++ * Our goal is to maintain proportional refaulted % across all tiers. ++ */ ++struct ctrl_pos { ++ unsigned long refaulted; ++ unsigned long total; ++ int gain; ++}; ++ ++static void read_ctrl_pos(struct lruvec *lruvec, int type, int tier, int gain, ++ struct ctrl_pos *pos) ++{ ++ struct lrugen *lrugen = &lruvec->evictable; ++ int hist = lru_hist_from_seq(lrugen->min_seq[type]); ++ ++ pos->refaulted = lrugen->avg_refaulted[type][tier] + ++ atomic_long_read(&lrugen->refaulted[hist][type][tier]); ++ pos->total = lrugen->avg_total[type][tier] + ++ atomic_long_read(&lrugen->evicted[hist][type][tier]); ++ if (tier) ++ pos->total += lrugen->protected[hist][type][tier - 1]; ++ pos->gain = gain; ++} ++ ++static void reset_ctrl_pos(struct lruvec *lruvec, int gen, int type) ++{ ++ int tier; ++ int hist = lru_hist_from_seq(gen); ++ struct lrugen *lrugen = &lruvec->evictable; ++ bool carryover = gen == lru_gen_from_seq(lrugen->min_seq[type]); ++ bool clear = carryover ? NR_HIST_GENS == 1 : NR_HIST_GENS > 1; ++ ++ if (!carryover && !clear) ++ return; ++ ++ for (tier = 0; tier < MAX_NR_TIERS; tier++) { ++ if (carryover) { ++ unsigned long sum; ++ ++ sum = lrugen->avg_refaulted[type][tier] + ++ atomic_long_read(&lrugen->refaulted[hist][type][tier]); ++ WRITE_ONCE(lrugen->avg_refaulted[type][tier], sum / 2); ++ ++ sum = lrugen->avg_total[type][tier] + ++ atomic_long_read(&lrugen->evicted[hist][type][tier]); ++ if (tier) ++ sum += lrugen->protected[hist][type][tier - 1]; ++ WRITE_ONCE(lrugen->avg_total[type][tier], sum / 2); ++ } ++ ++ if (clear) { ++ atomic_long_set(&lrugen->refaulted[hist][type][tier], 0); ++ atomic_long_set(&lrugen->evicted[hist][type][tier], 0); ++ if (tier) ++ WRITE_ONCE(lrugen->protected[hist][type][tier - 1], 0); ++ } ++ } ++} ++ ++static bool positive_ctrl_err(struct ctrl_pos *sp, struct ctrl_pos *pv) ++{ ++ /* ++ * Allow eviction if the PV has a limited number of refaulted pages or a ++ * lower refaulted % than the SP. ++ */ ++ return pv->refaulted < MIN_BATCH_SIZE || ++ pv->refaulted * max(sp->total, 1UL) * sp->gain <= ++ sp->refaulted * max(pv->total, 1UL) * pv->gain; ++} ++ ++/****************************************************************************** + * the aging + ******************************************************************************/ + +@@ -3265,6 +3371,7 @@ static int page_update_gen(struct page * + + new_flags &= ~LRU_GEN_MASK; + new_flags |= (gen + 1UL) << LRU_GEN_PGOFF; ++ new_flags &= ~(LRU_REFS_MASK | LRU_REFS_FLAGS); + } while (new_flags != old_flags && + cmpxchg(&page->flags, old_flags, new_flags) != old_flags); + +@@ -3296,6 +3403,7 @@ static void page_inc_gen(struct page *pa + + new_flags &= ~LRU_GEN_MASK; + new_flags |= (new_gen + 1UL) << LRU_GEN_PGOFF; ++ new_flags &= ~(LRU_REFS_MASK | LRU_REFS_FLAGS); + /* for end_page_writeback() */ + if (reclaiming) + new_flags |= BIT(PG_reclaim); +@@ -3787,6 +3895,7 @@ static bool inc_min_seq(struct lruvec *l + } + } + ++ reset_ctrl_pos(lruvec, gen, type); + WRITE_ONCE(lrugen->min_seq[type], lrugen->min_seq[type] + 1); + + return true; +@@ -3824,6 +3933,8 @@ next: + if (min_seq[type] == lrugen->min_seq[type]) + continue; + ++ gen = lru_gen_from_seq(lrugen->min_seq[type]); ++ reset_ctrl_pos(lruvec, gen, type); + WRITE_ONCE(lrugen->min_seq[type], min_seq[type]); + success = true; + } +@@ -3885,6 +3996,9 @@ static void inc_max_seq(struct lruvec *l + } + } + ++ for (type = 0; type < ANON_AND_FILE; type++) ++ reset_ctrl_pos(lruvec, gen, type); ++ + WRITE_ONCE(lrugen->timestamps[gen], jiffies); + /* make sure all preceding modifications appear first */ + smp_store_release(&lrugen->max_seq, lrugen->max_seq + 1); +@@ -4166,6 +4280,433 @@ void lru_gen_look_around(struct page_vma + } + + /****************************************************************************** ++ * the eviction ++ ******************************************************************************/ ++ ++static bool sort_page(struct page *page, struct lruvec *lruvec, int tier_idx) ++{ ++ bool success; ++ int gen = page_lru_gen(page); ++ int type = page_is_file_lru(page); ++ int zone = page_zonenum(page); ++ int tier = page_lru_tier(page); ++ int delta = thp_nr_pages(page); ++ struct lrugen *lrugen = &lruvec->evictable; ++ ++ VM_BUG_ON_PAGE(gen >= MAX_NR_GENS, page); ++ ++ /* an mlocked page? */ ++ if (!page_evictable(page)) { ++ success = lru_gen_del_page(page, lruvec, true); ++ VM_BUG_ON_PAGE(!success, page); ++ SetPageUnevictable(page); ++ add_page_to_lru_list(page, lruvec); ++ __count_vm_events(UNEVICTABLE_PGCULLED, delta); ++ return true; ++ } ++ ++ /* a lazy-free page that has been written into? */ ++ if (type && PageDirty(page) && PageAnon(page)) { ++ success = lru_gen_del_page(page, lruvec, true); ++ VM_BUG_ON_PAGE(!success, page); ++ SetPageSwapBacked(page); ++ add_page_to_lru_list_tail(page, lruvec); ++ return true; ++ } ++ ++ /* page_update_gen() has updated this page? */ ++ if (gen != lru_gen_from_seq(lrugen->min_seq[type])) { ++ list_move(&page->lru, &lrugen->lists[gen][type][zone]); ++ return true; ++ } ++ ++ /* protect this page if its tier has a higher refaulted % */ ++ if (tier > tier_idx) { ++ int hist = lru_hist_from_seq(gen); ++ ++ page_inc_gen(page, lruvec, false); ++ WRITE_ONCE(lrugen->protected[hist][type][tier - 1], ++ lrugen->protected[hist][type][tier - 1] + delta); ++ __mod_lruvec_state(lruvec, WORKINGSET_ACTIVATE_BASE + type, delta); ++ return true; ++ } ++ ++ /* mark this page for reclaim if it's pending writeback */ ++ if (PageWriteback(page) || (type && PageDirty(page))) { ++ page_inc_gen(page, lruvec, true); ++ return true; ++ } ++ ++ return false; ++} ++ ++static bool isolate_page(struct page *page, struct lruvec *lruvec, struct scan_control *sc) ++{ ++ bool success; ++ ++ if (!sc->may_unmap && page_mapped(page)) ++ return false; ++ ++ if (!(sc->may_writepage && (sc->gfp_mask & __GFP_IO)) && ++ (PageDirty(page) || (PageAnon(page) && !PageSwapCache(page)))) ++ return false; ++ ++ if (!get_page_unless_zero(page)) ++ return false; ++ ++ if (!TestClearPageLRU(page)) { ++ put_page(page); ++ return false; ++ } ++ ++ success = lru_gen_del_page(page, lruvec, true); ++ VM_BUG_ON_PAGE(!success, page); ++ ++ return true; ++} ++ ++static int scan_pages(struct lruvec *lruvec, struct scan_control *sc, ++ int type, int tier, struct list_head *list) ++{ ++ int gen, zone; ++ enum vm_event_item item; ++ int sorted = 0; ++ int scanned = 0; ++ int isolated = 0; ++ int remaining = MAX_BATCH_SIZE; ++ struct lrugen *lrugen = &lruvec->evictable; ++ struct mem_cgroup *memcg = lruvec_memcg(lruvec); ++ ++ VM_BUG_ON(!list_empty(list)); ++ ++ if (get_nr_gens(lruvec, type) == MIN_NR_GENS) ++ return 0; ++ ++ gen = lru_gen_from_seq(lrugen->min_seq[type]); ++ ++ for (zone = sc->reclaim_idx; zone >= 0; zone--) { ++ LIST_HEAD(moved); ++ int skipped = 0; ++ struct list_head *head = &lrugen->lists[gen][type][zone]; ++ ++ while (!list_empty(head)) { ++ struct page *page = lru_to_page(head); ++ int delta = thp_nr_pages(page); ++ ++ VM_BUG_ON_PAGE(PageTail(page), page); ++ VM_BUG_ON_PAGE(PageUnevictable(page), page); ++ VM_BUG_ON_PAGE(PageActive(page), page); ++ VM_BUG_ON_PAGE(page_is_file_lru(page) != type, page); ++ VM_BUG_ON_PAGE(page_zonenum(page) != zone, page); ++ ++ prefetchw_prev_lru_page(page, head, flags); ++ ++ scanned += delta; ++ ++ if (sort_page(page, lruvec, tier)) ++ sorted += delta; ++ else if (isolate_page(page, lruvec, sc)) { ++ list_add(&page->lru, list); ++ isolated += delta; ++ } else { ++ list_move(&page->lru, &moved); ++ skipped += delta; ++ } ++ ++ if (!--remaining || max(isolated, skipped) >= MIN_BATCH_SIZE) ++ break; ++ } ++ ++ if (skipped) { ++ list_splice(&moved, head); ++ __count_zid_vm_events(PGSCAN_SKIP, zone, skipped); ++ } ++ ++ if (!remaining || isolated >= MIN_BATCH_SIZE) ++ break; ++ } ++ ++ item = current_is_kswapd() ? PGSCAN_KSWAPD : PGSCAN_DIRECT; ++ if (!cgroup_reclaim(sc)) { ++ __count_vm_events(item, isolated); ++ __count_vm_events(PGREFILL, sorted); ++ } ++ __count_memcg_events(memcg, item, isolated); ++ __count_memcg_events(memcg, PGREFILL, sorted); ++ __count_vm_events(PGSCAN_ANON + type, isolated); ++ ++ /* ++ * We may have trouble finding eligible pages due to reclaim_idx, ++ * may_unmap and may_writepage. Check `remaining` to make sure we won't ++ * be stuck if we aren't making enough progress. ++ */ ++ return isolated || !remaining ? scanned : 0; ++} ++ ++static int get_tier_idx(struct lruvec *lruvec, int type) ++{ ++ int tier; ++ struct ctrl_pos sp, pv; ++ ++ /* ++ * Ideally we don't want to evict upper tiers that have higher refaulted ++ * %. However, we need to leave a margin for the fluctuation in ++ * refaulted %. So we use a larger gain factor to make sure upper tiers ++ * are indeed more active. We choose 2 because the lowest upper tier ++ * would have twice of refaulted % of the base tier, according to their ++ * numbers of accesses. ++ */ ++ read_ctrl_pos(lruvec, type, 0, 1, &sp); ++ for (tier = 1; tier < MAX_NR_TIERS; tier++) { ++ read_ctrl_pos(lruvec, type, tier, 2, &pv); ++ if (!positive_ctrl_err(&sp, &pv)) ++ break; ++ } ++ ++ return tier - 1; ++} ++ ++static int get_type_to_scan(struct lruvec *lruvec, int swappiness, int *tier_idx) ++{ ++ int type, tier; ++ struct ctrl_pos sp, pv; ++ int gain[ANON_AND_FILE] = { swappiness, 200 - swappiness }; ++ ++ /* ++ * Compare refaulted % between the base tiers of anon and file to ++ * determine which type to evict. Also need to compare refaulted % of ++ * the upper tiers of the selected type with that of the base tier of ++ * the other type to determine which tier of the selected type to evict. ++ */ ++ read_ctrl_pos(lruvec, 0, 0, gain[0], &sp); ++ read_ctrl_pos(lruvec, 1, 0, gain[1], &pv); ++ type = positive_ctrl_err(&sp, &pv); ++ ++ read_ctrl_pos(lruvec, !type, 0, gain[!type], &sp); ++ for (tier = 1; tier < MAX_NR_TIERS; tier++) { ++ read_ctrl_pos(lruvec, type, tier, gain[type], &pv); ++ if (!positive_ctrl_err(&sp, &pv)) ++ break; ++ } ++ ++ *tier_idx = tier - 1; ++ ++ return type; ++} ++ ++static int isolate_pages(struct lruvec *lruvec, struct scan_control *sc, int swappiness, ++ int *type_scanned, struct list_head *list) ++{ ++ int i; ++ int type; ++ int scanned; ++ int tier = -1; ++ DEFINE_MIN_SEQ(lruvec); ++ ++ VM_BUG_ON(!seq_is_valid(lruvec)); ++ ++ /* ++ * Try to select a type based on generations and swappiness, and if that ++ * fails, fall back to get_type_to_scan(). When anon and file are both ++ * available from the same generation, swappiness 200 is interpreted as ++ * anon first and swappiness 1 is interpreted as file first. ++ */ ++ if (!swappiness) ++ type = 1; ++ else if (min_seq[0] < min_seq[1]) ++ type = 0; ++ else if (swappiness == 1) ++ type = 1; ++ else if (swappiness == 200) ++ type = 0; ++ else ++ type = get_type_to_scan(lruvec, swappiness, &tier); ++ ++ for (i = !swappiness; i < ANON_AND_FILE; i++) { ++ if (tier < 0) ++ tier = get_tier_idx(lruvec, type); ++ ++ scanned = scan_pages(lruvec, sc, type, tier, list); ++ if (scanned) ++ break; ++ ++ type = !type; ++ tier = -1; ++ } ++ ++ *type_scanned = type; ++ ++ return scanned; ++} ++ ++/* Main function used by the foreground, the background and the user-triggered eviction. */ ++static int evict_pages(struct lruvec *lruvec, struct scan_control *sc, int swappiness) ++{ ++ int type; ++ int scanned; ++ int reclaimed; ++ LIST_HEAD(list); ++ struct page *page; ++ enum vm_event_item item; ++ struct reclaim_stat stat; ++ struct mm_walk_args *args; ++ struct mem_cgroup *memcg = lruvec_memcg(lruvec); ++ struct pglist_data *pgdat = lruvec_pgdat(lruvec); ++ ++ spin_lock_irq(&lruvec->lru_lock); ++ ++ scanned = isolate_pages(lruvec, sc, swappiness, &type, &list); ++ ++ if (try_to_inc_min_seq(lruvec, swappiness)) ++ scanned++; ++ ++ if (get_nr_gens(lruvec, 1) == MIN_NR_GENS) ++ scanned = 0; ++ ++ spin_unlock_irq(&lruvec->lru_lock); ++ ++ if (list_empty(&list)) ++ return scanned; ++ ++ reclaimed = shrink_page_list(&list, pgdat, sc, &stat, false); ++ /* ++ * We need to prevent rejected pages from being added back to the same ++ * lists they were isolated from. Otherwise we may risk looping on them ++ * forever. ++ */ ++ list_for_each_entry(page, &list, lru) { ++ if (!PageReclaim(page) || !(PageDirty(page) || PageWriteback(page))) ++ SetPageActive(page); ++ ++ ClearPageReferenced(page); ++ ClearPageWorkingset(page); ++ } ++ ++ spin_lock_irq(&lruvec->lru_lock); ++ ++ move_pages_to_lru(lruvec, &list); ++ ++ args = current->reclaim_state ? current->reclaim_state->mm_walk_args : NULL; ++ if (args && args->batch_size) ++ reset_batch_size(lruvec, args); ++ ++ item = current_is_kswapd() ? PGSTEAL_KSWAPD : PGSTEAL_DIRECT; ++ if (!cgroup_reclaim(sc)) ++ __count_vm_events(item, reclaimed); ++ __count_memcg_events(memcg, item, reclaimed); ++ __count_vm_events(PGSTEAL_ANON + type, reclaimed); ++ ++ spin_unlock_irq(&lruvec->lru_lock); ++ ++ mem_cgroup_uncharge_list(&list); ++ free_unref_page_list(&list); ++ ++ sc->nr_reclaimed += reclaimed; ++ ++ return scanned; ++} ++ ++static long get_nr_to_scan(struct lruvec *lruvec, struct scan_control *sc, int swappiness) ++{ ++ bool low; ++ long nr_to_scan; ++ struct mem_cgroup *memcg = lruvec_memcg(lruvec); ++ int priority = sc->priority; ++ DEFINE_MAX_SEQ(lruvec); ++ DEFINE_MIN_SEQ(lruvec); ++ ++ if (mem_cgroup_below_min(memcg) || ++ (mem_cgroup_below_low(memcg) && !sc->memcg_low_reclaim)) ++ return 0; ++ ++ if (sc->nr_reclaimed >= sc->nr_to_reclaim) { ++ priority = DEF_PRIORITY; ++ sc->force_deactivate = 0; ++ } ++ ++ nr_to_scan = get_nr_evictable(lruvec, sc, swappiness, max_seq, min_seq, &low); ++ if (!nr_to_scan) ++ return 0; ++ ++ nr_to_scan >>= priority; ++ ++ if (!mem_cgroup_online(memcg)) ++ nr_to_scan++; ++ ++ if (!nr_to_scan) ++ return 0; ++ ++ if (current_is_kswapd()) { ++ /* leave the work to lru_gen_age_node() */ ++ if (max_seq - min_seq[1] < MIN_NR_GENS) ++ return 0; ++ ++ if (!low) ++ sc->force_deactivate = 0; ++ ++ return nr_to_scan; ++ } ++ ++ if (max_seq - min_seq[1] >= MIN_NR_GENS) ++ return nr_to_scan; ++ ++ /* move onto slab and other memcgs if we haven't tried them all */ ++ if (!sc->force_deactivate) { ++ sc->skipped_deactivate = 1; ++ return 0; ++ } ++ ++ return try_to_inc_max_seq(lruvec, sc, swappiness, max_seq, true) ? nr_to_scan : 0; ++} ++ ++static void lru_gen_shrink_lruvec(struct lruvec *lruvec, struct scan_control *sc) ++{ ++ struct blk_plug plug; ++ long scanned = 0; ++ struct mem_cgroup *memcg = lruvec_memcg(lruvec); ++ struct pglist_data *pgdat = lruvec_pgdat(lruvec); ++ ++ lru_add_drain(); ++ ++ if (current_is_kswapd()) ++ current->reclaim_state->mm_walk_args = &pgdat->mm_walk_args; ++ ++ blk_start_plug(&plug); ++ ++ while (true) { ++ int delta; ++ int swappiness; ++ long nr_to_scan; ++ ++ if (sc->may_swap) ++ swappiness = get_swappiness(memcg); ++ else if (!cgroup_reclaim(sc) && get_swappiness(memcg)) ++ swappiness = 1; ++ else ++ swappiness = 0; ++ ++ nr_to_scan = get_nr_to_scan(lruvec, sc, swappiness); ++ if (!nr_to_scan) ++ break; ++ ++ delta = evict_pages(lruvec, sc, swappiness); ++ if (!delta) ++ break; ++ ++ scanned += delta; ++ if (scanned >= nr_to_scan) ++ break; ++ ++ cond_resched(); ++ } ++ ++ blk_finish_plug(&plug); ++ ++ if (current_is_kswapd()) ++ current->reclaim_state->mm_walk_args = NULL; ++} ++ ++/****************************************************************************** + * state change + ******************************************************************************/ + +@@ -4420,6 +4961,10 @@ static void lru_gen_age_node(struct pgli + { + } + ++static void lru_gen_shrink_lruvec(struct lruvec *lruvec, struct scan_control *sc) ++{ ++} ++ + #endif /* CONFIG_LRU_GEN */ + + static void shrink_lruvec(struct lruvec *lruvec, struct scan_control *sc) +@@ -4433,6 +4978,11 @@ static void shrink_lruvec(struct lruvec + struct blk_plug plug; + bool scan_adjusted; + ++ if (lru_gen_enabled()) { ++ lru_gen_shrink_lruvec(lruvec, sc); ++ return; ++ } ++ + get_scan_count(lruvec, sc, nr); + + /* Record the original scan target for proportional adjustments later */ +@@ -4906,6 +5456,9 @@ static void snapshot_refaults(struct mem + struct lruvec *target_lruvec; + unsigned long refaults; + ++ if (lru_gen_enabled()) ++ return; ++ + target_lruvec = mem_cgroup_lruvec(target_memcg, pgdat); + refaults = lruvec_page_state(target_lruvec, WORKINGSET_ACTIVATE_ANON); + target_lruvec->refaults[0] = refaults; +--- a/mm/workingset.c ++++ b/mm/workingset.c +@@ -187,7 +187,6 @@ static unsigned int bucket_order __read_ + static void *pack_shadow(int memcgid, pg_data_t *pgdat, unsigned long eviction, + bool workingset) + { +- eviction >>= bucket_order; + eviction &= EVICTION_MASK; + eviction = (eviction << MEM_CGROUP_ID_SHIFT) | memcgid; + eviction = (eviction << NODES_SHIFT) | pgdat->node_id; +@@ -212,10 +211,117 @@ static void unpack_shadow(void *shadow, + + *memcgidp = memcgid; + *pgdat = NODE_DATA(nid); +- *evictionp = entry << bucket_order; ++ *evictionp = entry; + *workingsetp = workingset; + } + ++#ifdef CONFIG_LRU_GEN ++ ++static int page_lru_refs(struct page *page) ++{ ++ unsigned long flags = READ_ONCE(page->flags); ++ ++ BUILD_BUG_ON(LRU_GEN_WIDTH + LRU_REFS_WIDTH > BITS_PER_LONG - EVICTION_SHIFT); ++ ++ /* see the comment on MAX_NR_TIERS */ ++ return flags & BIT(PG_workingset) ? (flags & LRU_REFS_MASK) >> LRU_REFS_PGOFF : 0; ++} ++ ++/* Return a token to be stored in the shadow entry of a page being evicted. */ ++static void *lru_gen_eviction(struct page *page) ++{ ++ int hist, tier; ++ unsigned long token; ++ unsigned long min_seq; ++ struct lruvec *lruvec; ++ struct lrugen *lrugen; ++ int type = page_is_file_lru(page); ++ int refs = page_lru_refs(page); ++ int delta = thp_nr_pages(page); ++ bool workingset = PageWorkingset(page); ++ struct mem_cgroup *memcg = page_memcg(page); ++ struct pglist_data *pgdat = page_pgdat(page); ++ ++ lruvec = mem_cgroup_lruvec(memcg, pgdat); ++ lrugen = &lruvec->evictable; ++ min_seq = READ_ONCE(lrugen->min_seq[type]); ++ token = (min_seq << LRU_REFS_WIDTH) | refs; ++ ++ hist = lru_hist_from_seq(min_seq); ++ tier = lru_tier_from_refs(refs + workingset); ++ atomic_long_add(delta, &lrugen->evicted[hist][type][tier]); ++ ++ return pack_shadow(mem_cgroup_id(memcg), pgdat, token, workingset); ++} ++ ++/* Count a refaulted page based on the token stored in its shadow entry. */ ++static void lru_gen_refault(struct page *page, void *shadow) ++{ ++ int hist, tier, refs; ++ int memcg_id; ++ bool workingset; ++ unsigned long token; ++ unsigned long min_seq; ++ struct lruvec *lruvec; ++ struct lrugen *lrugen; ++ struct mem_cgroup *memcg; ++ struct pglist_data *pgdat; ++ int type = page_is_file_lru(page); ++ int delta = thp_nr_pages(page); ++ ++ unpack_shadow(shadow, &memcg_id, &pgdat, &token, &workingset); ++ if (page_pgdat(page) != pgdat) ++ return; ++ ++ rcu_read_lock(); ++ memcg = page_memcg_rcu(page); ++ if (mem_cgroup_id(memcg) != memcg_id) ++ goto unlock; ++ ++ refs = token & (BIT(LRU_REFS_WIDTH) - 1); ++ if (refs && !workingset) ++ goto unlock; ++ ++ token >>= LRU_REFS_WIDTH; ++ lruvec = mem_cgroup_lruvec(memcg, pgdat); ++ lrugen = &lruvec->evictable; ++ min_seq = READ_ONCE(lrugen->min_seq[type]); ++ if (token != (min_seq & (EVICTION_MASK >> LRU_REFS_WIDTH))) ++ goto unlock; ++ ++ hist = lru_hist_from_seq(min_seq); ++ tier = lru_tier_from_refs(refs + workingset); ++ atomic_long_add(delta, &lrugen->refaulted[hist][type][tier]); ++ mod_lruvec_state(lruvec, WORKINGSET_REFAULT_BASE + type, delta); ++ ++ /* ++ * Tiers don't offer any protection to pages accessed via page tables. ++ * That's what generations do. Tiers can't fully protect pages after ++ * their numbers of accesses has exceeded the max value. Conservatively ++ * count these two conditions as stalls even though they might not ++ * indicate any real memory pressure. ++ */ ++ if (task_in_nonseq_fault() || refs + workingset == BIT(LRU_REFS_WIDTH)) { ++ SetPageWorkingset(page); ++ mod_lruvec_state(lruvec, WORKINGSET_RESTORE_BASE + type, delta); ++ } ++unlock: ++ rcu_read_unlock(); ++} ++ ++#else ++ ++static void *lru_gen_eviction(struct page *page) ++{ ++ return NULL; ++} ++ ++static void lru_gen_refault(struct page *page, void *shadow) ++{ ++} ++ ++#endif /* CONFIG_LRU_GEN */ ++ + /** + * workingset_age_nonresident - age non-resident entries as LRU ages + * @lruvec: the lruvec that was aged +@@ -264,10 +370,14 @@ void *workingset_eviction(struct page *p + VM_BUG_ON_PAGE(page_count(page), page); + VM_BUG_ON_PAGE(!PageLocked(page), page); + ++ if (lru_gen_enabled()) ++ return lru_gen_eviction(page); ++ + lruvec = mem_cgroup_lruvec(target_memcg, pgdat); + /* XXX: target_memcg can be NULL, go through lruvec */ + memcgid = mem_cgroup_id(lruvec_memcg(lruvec)); + eviction = atomic_long_read(&lruvec->nonresident_age); ++ eviction >>= bucket_order; + workingset_age_nonresident(lruvec, thp_nr_pages(page)); + return pack_shadow(memcgid, pgdat, eviction, PageWorkingset(page)); + } +@@ -296,7 +406,13 @@ void workingset_refault(struct page *pag + bool workingset; + int memcgid; + ++ if (lru_gen_enabled()) { ++ lru_gen_refault(page, shadow); ++ return; ++ } ++ + unpack_shadow(shadow, &memcgid, &pgdat, &eviction, &workingset); ++ eviction <<= bucket_order; + + rcu_read_lock(); + /* diff --git a/target/linux/generic/backport-6.1/020-v6.1-08-mm-multigenerational-lru-user-interface.patch b/target/linux/generic/backport-6.1/020-v6.1-08-mm-multigenerational-lru-user-interface.patch new file mode 100644 index 0000000000..a1a749fc38 --- /dev/null +++ b/target/linux/generic/backport-6.1/020-v6.1-08-mm-multigenerational-lru-user-interface.patch @@ -0,0 +1,496 @@ +From 5cc7fdec54e87e32b4fb0f07d84b21769d5f8d92 Mon Sep 17 00:00:00 2001 +From: Yu Zhao +Date: Mon, 25 Jan 2021 21:38:02 -0700 +Subject: [PATCH 08/10] mm: multigenerational lru: user interface + +Add /sys/kernel/mm/lru_gen/enabled to enable and disable the +multigenerational lru at runtime. + +Add /sys/kernel/mm/lru_gen/min_ttl_ms to protect the working set of a +given number of milliseconds. The OOM killer is invoked if this +working set cannot be kept in memory. + +Add /sys/kernel/debug/lru_gen to monitor the multigenerational lru and +invoke the aging and the eviction. This file has the following output: + memcg memcg_id memcg_path + node node_id + min_gen birth_time anon_size file_size + ... + max_gen birth_time anon_size file_size + +min_gen is the oldest generation number and max_gen is the youngest +generation number. birth_time is in milliseconds. anon_size and +file_size are in pages. + +This file takes the following input: + + memcg_id node_id max_gen [swappiness] [use_bloom_filter] + - memcg_id node_id min_gen [swappiness] [nr_to_reclaim] + +The first command line invokes the aging, which scans PTEs for +accessed pages and then creates the next generation max_gen+1. A swap +file and a non-zero swappiness, which overrides vm.swappiness, are +required to scan PTEs mapping anon pages. The second command line +invokes the eviction, which evicts generations less than or equal to +min_gen. min_gen should be less than max_gen-1 as max_gen and +max_gen-1 are not fully aged and therefore cannot be evicted. +Setting nr_to_reclaim to N limits the number of pages to evict. +Setting use_bloom_filter to 0 overrides the default behavior which +only scans PTE tables found populated. Multiple command lines are +supported, as is concatenation with delimiters "," and ";". + +Signed-off-by: Yu Zhao +Tested-by: Konstantin Kharlamov +Change-Id: I4448e60029badbe347aa3b624f429b280cc3a3d3 +--- + include/linux/nodemask.h | 1 + + mm/vmscan.c | 415 +++++++++++++++++++++++++++++++++++++++ + 2 files changed, 416 insertions(+) + +--- a/include/linux/nodemask.h ++++ b/include/linux/nodemask.h +@@ -485,6 +485,7 @@ static inline int num_node_state(enum no + #define first_online_node 0 + #define first_memory_node 0 + #define next_online_node(nid) (MAX_NUMNODES) ++#define next_memory_node(nid) (MAX_NUMNODES) + #define nr_node_ids 1U + #define nr_online_nodes 1U + +--- a/mm/vmscan.c ++++ b/mm/vmscan.c +@@ -53,6 +53,8 @@ + #include + #include + #include ++#include ++#include + + #include + #include +@@ -4882,6 +4884,413 @@ unlock: + } + + /****************************************************************************** ++ * sysfs interface ++ ******************************************************************************/ ++ ++static ssize_t show_min_ttl(struct kobject *kobj, struct kobj_attribute *attr, char *buf) ++{ ++ return sprintf(buf, "%u\n", jiffies_to_msecs(READ_ONCE(lru_gen_min_ttl))); ++} ++ ++static ssize_t store_min_ttl(struct kobject *kobj, struct kobj_attribute *attr, ++ const char *buf, size_t len) ++{ ++ unsigned int msecs; ++ ++ if (kstrtouint(buf, 10, &msecs)) ++ return -EINVAL; ++ ++ WRITE_ONCE(lru_gen_min_ttl, msecs_to_jiffies(msecs)); ++ ++ return len; ++} ++ ++static struct kobj_attribute lru_gen_min_ttl_attr = __ATTR( ++ min_ttl_ms, 0644, show_min_ttl, store_min_ttl ++); ++ ++static ssize_t show_enable(struct kobject *kobj, struct kobj_attribute *attr, char *buf) ++{ ++ return snprintf(buf, PAGE_SIZE, "%d\n", lru_gen_enabled()); ++} ++ ++static ssize_t store_enable(struct kobject *kobj, struct kobj_attribute *attr, ++ const char *buf, size_t len) ++{ ++ bool enable; ++ ++ if (kstrtobool(buf, &enable)) ++ return -EINVAL; ++ ++ lru_gen_change_state(enable, true, false); ++ ++ return len; ++} ++ ++static struct kobj_attribute lru_gen_enabled_attr = __ATTR( ++ enabled, 0644, show_enable, store_enable ++); ++ ++static struct attribute *lru_gen_attrs[] = { ++ &lru_gen_min_ttl_attr.attr, ++ &lru_gen_enabled_attr.attr, ++ NULL ++}; ++ ++static struct attribute_group lru_gen_attr_group = { ++ .name = "lru_gen", ++ .attrs = lru_gen_attrs, ++}; ++ ++/****************************************************************************** ++ * debugfs interface ++ ******************************************************************************/ ++ ++static void *lru_gen_seq_start(struct seq_file *m, loff_t *pos) ++{ ++ struct mem_cgroup *memcg; ++ loff_t nr_to_skip = *pos; ++ ++ m->private = kvmalloc(PATH_MAX, GFP_KERNEL); ++ if (!m->private) ++ return ERR_PTR(-ENOMEM); ++ ++ memcg = mem_cgroup_iter(NULL, NULL, NULL); ++ do { ++ int nid; ++ ++ for_each_node_state(nid, N_MEMORY) { ++ if (!nr_to_skip--) ++ return get_lruvec(nid, memcg); ++ } ++ } while ((memcg = mem_cgroup_iter(NULL, memcg, NULL))); ++ ++ return NULL; ++} ++ ++static void lru_gen_seq_stop(struct seq_file *m, void *v) ++{ ++ if (!IS_ERR_OR_NULL(v)) ++ mem_cgroup_iter_break(NULL, lruvec_memcg(v)); ++ ++ kvfree(m->private); ++ m->private = NULL; ++} ++ ++static void *lru_gen_seq_next(struct seq_file *m, void *v, loff_t *pos) ++{ ++ int nid = lruvec_pgdat(v)->node_id; ++ struct mem_cgroup *memcg = lruvec_memcg(v); ++ ++ ++*pos; ++ ++ nid = next_memory_node(nid); ++ if (nid == MAX_NUMNODES) { ++ memcg = mem_cgroup_iter(NULL, memcg, NULL); ++ if (!memcg) ++ return NULL; ++ ++ nid = first_memory_node; ++ } ++ ++ return get_lruvec(nid, memcg); ++} ++ ++static void lru_gen_seq_show_full(struct seq_file *m, struct lruvec *lruvec, ++ unsigned long max_seq, unsigned long *min_seq, ++ unsigned long seq) ++{ ++ int i; ++ int type, tier; ++ int hist = lru_hist_from_seq(seq); ++ struct lrugen *lrugen = &lruvec->evictable; ++ ++ for (tier = 0; tier < MAX_NR_TIERS; tier++) { ++ seq_printf(m, " %10d", tier); ++ for (type = 0; type < ANON_AND_FILE; type++) { ++ unsigned long n[3] = {}; ++ ++ if (seq == max_seq) { ++ n[0] = READ_ONCE(lrugen->avg_refaulted[type][tier]); ++ n[1] = READ_ONCE(lrugen->avg_total[type][tier]); ++ ++ seq_printf(m, " %10luR %10luT %10lu ", n[0], n[1], n[2]); ++ } else if (seq == min_seq[type] || NR_HIST_GENS > 1) { ++ n[0] = atomic_long_read(&lrugen->refaulted[hist][type][tier]); ++ n[1] = atomic_long_read(&lrugen->evicted[hist][type][tier]); ++ if (tier) ++ n[2] = READ_ONCE(lrugen->protected[hist][type][tier - 1]); ++ ++ seq_printf(m, " %10lur %10lue %10lup", n[0], n[1], n[2]); ++ } else ++ seq_puts(m, " 0 0 0 "); ++ } ++ seq_putc(m, '\n'); ++ } ++ ++ seq_puts(m, " "); ++ for (i = 0; i < NR_MM_STATS; i++) { ++ if (seq == max_seq && NR_HIST_GENS == 1) ++ seq_printf(m, " %10lu%c", READ_ONCE(lruvec->mm_walk.stats[hist][i]), ++ toupper(MM_STAT_CODES[i])); ++ else if (seq != max_seq && NR_HIST_GENS > 1) ++ seq_printf(m, " %10lu%c", READ_ONCE(lruvec->mm_walk.stats[hist][i]), ++ MM_STAT_CODES[i]); ++ else ++ seq_puts(m, " 0 "); ++ } ++ seq_putc(m, '\n'); ++} ++ ++static int lru_gen_seq_show(struct seq_file *m, void *v) ++{ ++ unsigned long seq; ++ bool full = !debugfs_real_fops(m->file)->write; ++ struct lruvec *lruvec = v; ++ struct lrugen *lrugen = &lruvec->evictable; ++ int nid = lruvec_pgdat(lruvec)->node_id; ++ struct mem_cgroup *memcg = lruvec_memcg(lruvec); ++ DEFINE_MAX_SEQ(lruvec); ++ DEFINE_MIN_SEQ(lruvec); ++ ++ if (nid == first_memory_node) { ++ const char *path = memcg ? m->private : ""; ++ ++#ifdef CONFIG_MEMCG ++ if (memcg) ++ cgroup_path(memcg->css.cgroup, m->private, PATH_MAX); ++#endif ++ seq_printf(m, "memcg %5hu %s\n", mem_cgroup_id(memcg), path); ++ } ++ ++ seq_printf(m, " node %5d\n", nid); ++ ++ if (!full) ++ seq = min_seq[0]; ++ else if (max_seq >= MAX_NR_GENS) ++ seq = max_seq - MAX_NR_GENS + 1; ++ else ++ seq = 0; ++ ++ for (; seq <= max_seq; seq++) { ++ int gen, type, zone; ++ unsigned int msecs; ++ ++ gen = lru_gen_from_seq(seq); ++ msecs = jiffies_to_msecs(jiffies - READ_ONCE(lrugen->timestamps[gen])); ++ ++ seq_printf(m, " %10lu %10u", seq, msecs); ++ ++ for (type = 0; type < ANON_AND_FILE; type++) { ++ long size = 0; ++ ++ if (seq < min_seq[type]) { ++ seq_puts(m, " -0 "); ++ continue; ++ } ++ ++ for (zone = 0; zone < MAX_NR_ZONES; zone++) ++ size += READ_ONCE(lrugen->sizes[gen][type][zone]); ++ ++ seq_printf(m, " %10lu ", max(size, 0L)); ++ } ++ ++ seq_putc(m, '\n'); ++ ++ if (full) ++ lru_gen_seq_show_full(m, lruvec, max_seq, min_seq, seq); ++ } ++ ++ return 0; ++} ++ ++static const struct seq_operations lru_gen_seq_ops = { ++ .start = lru_gen_seq_start, ++ .stop = lru_gen_seq_stop, ++ .next = lru_gen_seq_next, ++ .show = lru_gen_seq_show, ++}; ++ ++static int run_aging(struct lruvec *lruvec, struct scan_control *sc, int swappiness, ++ unsigned long seq, bool use_filter) ++{ ++ DEFINE_MAX_SEQ(lruvec); ++ ++ if (seq == max_seq) ++ try_to_inc_max_seq(lruvec, sc, swappiness, max_seq, use_filter); ++ ++ return seq > max_seq ? -EINVAL : 0; ++} ++ ++static int run_eviction(struct lruvec *lruvec, struct scan_control *sc, int swappiness, ++ unsigned long seq, unsigned long nr_to_reclaim) ++{ ++ struct blk_plug plug; ++ int err = -EINTR; ++ DEFINE_MAX_SEQ(lruvec); ++ ++ if (seq >= max_seq - 1) ++ return -EINVAL; ++ ++ sc->nr_reclaimed = 0; ++ ++ blk_start_plug(&plug); ++ ++ while (!signal_pending(current)) { ++ DEFINE_MIN_SEQ(lruvec); ++ ++ if (seq < min_seq[!swappiness] || sc->nr_reclaimed >= nr_to_reclaim || ++ !evict_pages(lruvec, sc, swappiness)) { ++ err = 0; ++ break; ++ } ++ ++ cond_resched(); ++ } ++ ++ blk_finish_plug(&plug); ++ ++ return err; ++} ++ ++static int run_cmd(char cmd, int memcg_id, int nid, struct scan_control *sc, ++ int swappiness, unsigned long seq, unsigned long opt) ++{ ++ struct lruvec *lruvec; ++ int err = -EINVAL; ++ struct mem_cgroup *memcg = NULL; ++ ++ if (!mem_cgroup_disabled()) { ++ rcu_read_lock(); ++ memcg = mem_cgroup_from_id(memcg_id); ++#ifdef CONFIG_MEMCG ++ if (memcg && !css_tryget(&memcg->css)) ++ memcg = NULL; ++#endif ++ rcu_read_unlock(); ++ ++ if (!memcg) ++ goto done; ++ } ++ if (memcg_id != mem_cgroup_id(memcg)) ++ goto done; ++ ++ if (nid < 0 || nid >= MAX_NUMNODES || !node_state(nid, N_MEMORY)) ++ goto done; ++ ++ lruvec = get_lruvec(nid, memcg); ++ ++ if (swappiness < 0) ++ swappiness = get_swappiness(memcg); ++ else if (swappiness > 200) ++ goto done; ++ ++ switch (cmd) { ++ case '+': ++ err = run_aging(lruvec, sc, swappiness, seq, opt); ++ break; ++ case '-': ++ err = run_eviction(lruvec, sc, swappiness, seq, opt); ++ break; ++ } ++done: ++ mem_cgroup_put(memcg); ++ ++ return err; ++} ++ ++static ssize_t lru_gen_seq_write(struct file *file, const char __user *src, ++ size_t len, loff_t *pos) ++{ ++ void *buf; ++ char *cur, *next; ++ unsigned int flags; ++ int err = 0; ++ struct scan_control sc = { ++ .may_writepage = 1, ++ .may_unmap = 1, ++ .may_swap = 1, ++ .reclaim_idx = MAX_NR_ZONES - 1, ++ .gfp_mask = GFP_KERNEL, ++ }; ++ ++ buf = kvmalloc(len + 1, GFP_KERNEL); ++ if (!buf) ++ return -ENOMEM; ++ ++ if (copy_from_user(buf, src, len)) { ++ kvfree(buf); ++ return -EFAULT; ++ } ++ ++ next = buf; ++ next[len] = '\0'; ++ ++ sc.reclaim_state.mm_walk_args = alloc_mm_walk_args(); ++ if (!sc.reclaim_state.mm_walk_args) { ++ kvfree(buf); ++ return -ENOMEM; ++ } ++ ++ flags = memalloc_noreclaim_save(); ++ set_task_reclaim_state(current, &sc.reclaim_state); ++ ++ while ((cur = strsep(&next, ",;\n"))) { ++ int n; ++ int end; ++ char cmd; ++ unsigned int memcg_id; ++ unsigned int nid; ++ unsigned long seq; ++ unsigned int swappiness = -1; ++ unsigned long opt = -1; ++ ++ cur = skip_spaces(cur); ++ if (!*cur) ++ continue; ++ ++ n = sscanf(cur, "%c %u %u %lu %n %u %n %lu %n", &cmd, &memcg_id, &nid, ++ &seq, &end, &swappiness, &end, &opt, &end); ++ if (n < 4 || cur[end]) { ++ err = -EINVAL; ++ break; ++ } ++ ++ err = run_cmd(cmd, memcg_id, nid, &sc, swappiness, seq, opt); ++ if (err) ++ break; ++ } ++ ++ set_task_reclaim_state(current, NULL); ++ memalloc_noreclaim_restore(flags); ++ ++ free_mm_walk_args(sc.reclaim_state.mm_walk_args); ++ kvfree(buf); ++ ++ return err ? : len; ++} ++ ++static int lru_gen_seq_open(struct inode *inode, struct file *file) ++{ ++ return seq_open(file, &lru_gen_seq_ops); ++} ++ ++static const struct file_operations lru_gen_rw_fops = { ++ .open = lru_gen_seq_open, ++ .read = seq_read, ++ .write = lru_gen_seq_write, ++ .llseek = seq_lseek, ++ .release = seq_release, ++}; ++ ++static const struct file_operations lru_gen_ro_fops = { ++ .open = lru_gen_seq_open, ++ .read = seq_read, ++ .llseek = seq_lseek, ++ .release = seq_release, ++}; ++ ++/****************************************************************************** + * initialization + ******************************************************************************/ + +@@ -4951,6 +5360,12 @@ static int __init init_lru_gen(void) + BUILD_BUG_ON(BIT(LRU_GEN_WIDTH) <= MAX_NR_GENS); + BUILD_BUG_ON(sizeof(MM_STAT_CODES) != NR_MM_STATS + 1); + ++ if (sysfs_create_group(mm_kobj, &lru_gen_attr_group)) ++ pr_err("lru_gen: failed to create sysfs group\n"); ++ ++ debugfs_create_file("lru_gen", 0644, NULL, NULL, &lru_gen_rw_fops); ++ debugfs_create_file("lru_gen_full", 0444, NULL, NULL, &lru_gen_ro_fops); ++ + return 0; + }; + late_initcall(init_lru_gen); diff --git a/target/linux/generic/backport-6.1/020-v6.1-09-mm-multigenerational-lru-Kconfig.patch b/target/linux/generic/backport-6.1/020-v6.1-09-mm-multigenerational-lru-Kconfig.patch new file mode 100644 index 0000000000..4462549f99 --- /dev/null +++ b/target/linux/generic/backport-6.1/020-v6.1-09-mm-multigenerational-lru-Kconfig.patch @@ -0,0 +1,80 @@ +From 3008095eb835d207dd7e5b60899aad17f32aa9f7 Mon Sep 17 00:00:00 2001 +From: Yu Zhao +Date: Mon, 25 Jan 2021 21:47:24 -0700 +Subject: [PATCH 09/10] mm: multigenerational lru: Kconfig + +Add configuration options for the multigenerational lru. + +Signed-off-by: Yu Zhao +Tested-by: Konstantin Kharlamov +Change-Id: Ic74ea07f8fb5f56e6904a1b80c3c286bc2911635 +--- + mm/Kconfig | 59 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ + 1 file changed, 59 insertions(+) + +--- a/mm/Kconfig ++++ b/mm/Kconfig +@@ -899,4 +899,63 @@ config SECRETMEM + + source "mm/damon/Kconfig" + ++# the multigenerational lru { ++config LRU_GEN ++ bool "Multigenerational LRU" ++ depends on MMU ++ # the following options may leave not enough spare bits in page->flags ++ depends on !MAXSMP && (64BIT || !SPARSEMEM || SPARSEMEM_VMEMMAP) ++ help ++ A high performance LRU implementation to heavily overcommit workloads ++ that are not IO bound. See Documentation/vm/multigen_lru.rst for ++ details. ++ ++ Warning: do not enable this option unless you plan to use it because ++ it introduces a small per-process and per-memcg and per-node memory ++ overhead. ++ ++config LRU_GEN_ENABLED ++ bool "Turn on by default" ++ depends on LRU_GEN ++ help ++ The default value of /sys/kernel/mm/lru_gen/enabled is 0. This option ++ changes it to 1. ++ ++ Warning: the default value is the fast path. See ++ Documentation/static-keys.txt for details. ++ ++config LRU_GEN_STATS ++ bool "Full stats for debugging" ++ depends on LRU_GEN ++ help ++ This option keeps full stats for each generation, which can be read ++ from /sys/kernel/debug/lru_gen_full. ++ ++ Warning: do not enable this option unless you plan to use it because ++ it introduces an additional small per-process and per-memcg and ++ per-node memory overhead. ++ ++config NR_LRU_GENS ++ int "Max number of generations" ++ depends on LRU_GEN ++ range 4 31 ++ default 7 ++ help ++ This will use order_base_2(N+1) spare bits from page flags. ++ ++ Warning: do not use numbers larger than necessary because each ++ generation introduces a small per-node and per-memcg memory overhead. ++ ++config TIERS_PER_GEN ++ int "Number of tiers per generation" ++ depends on LRU_GEN ++ range 2 5 ++ default 4 ++ help ++ This will use N-2 spare bits from page flags. ++ ++ Larger values generally offer better protection to active pages under ++ heavy buffered I/O workloads. ++# } ++ + endmenu diff --git a/target/linux/generic/backport-6.1/020-v6.1-10-mm-multigenerational-lru-documentation.patch b/target/linux/generic/backport-6.1/020-v6.1-10-mm-multigenerational-lru-documentation.patch new file mode 100644 index 0000000000..f4716fb68d --- /dev/null +++ b/target/linux/generic/backport-6.1/020-v6.1-10-mm-multigenerational-lru-documentation.patch @@ -0,0 +1,161 @@ +From f59c618ed70a1e48accc4cad91a200966f2569c9 Mon Sep 17 00:00:00 2001 +From: Yu Zhao +Date: Tue, 2 Feb 2021 01:27:45 -0700 +Subject: [PATCH 10/10] mm: multigenerational lru: documentation + +Add Documentation/vm/multigen_lru.rst. + +Signed-off-by: Yu Zhao +Tested-by: Konstantin Kharlamov +Change-Id: I1902178bcbb5adfa0a748c4d284a6456059bdd7e +--- + Documentation/vm/index.rst | 1 + + Documentation/vm/multigen_lru.rst | 132 ++++++++++++++++++++++++++++++ + 2 files changed, 133 insertions(+) + create mode 100644 Documentation/vm/multigen_lru.rst + +--- a/Documentation/vm/index.rst ++++ b/Documentation/vm/index.rst +@@ -17,6 +17,7 @@ various features of the Linux memory man + + swap_numa + zswap ++ multigen_lru + + Kernel developers MM documentation + ================================== +--- /dev/null ++++ b/Documentation/vm/multigen_lru.rst +@@ -0,0 +1,132 @@ ++.. SPDX-License-Identifier: GPL-2.0 ++ ++===================== ++Multigenerational LRU ++===================== ++ ++Quick Start ++=========== ++Build Configurations ++-------------------- ++:Required: Set ``CONFIG_LRU_GEN=y``. ++ ++:Optional: Set ``CONFIG_LRU_GEN_ENABLED=y`` to turn the feature on by ++ default. ++ ++Runtime Configurations ++---------------------- ++:Required: Write ``1`` to ``/sys/kernel/mm/lru_gen/enable`` if the ++ feature was not turned on by default. ++ ++:Optional: Write ``N`` to ``/sys/kernel/mm/lru_gen/min_ttl_ms`` to ++ protect the working set of ``N`` milliseconds. The OOM killer is ++ invoked if this working set cannot be kept in memory. ++ ++:Optional: Read ``/sys/kernel/debug/lru_gen`` to confirm the feature ++ is turned on. This file has the following output: ++ ++:: ++ ++ memcg memcg_id memcg_path ++ node node_id ++ min_gen birth_time anon_size file_size ++ ... ++ max_gen birth_time anon_size file_size ++ ++``min_gen`` is the oldest generation number and ``max_gen`` is the ++youngest generation number. ``birth_time`` is in milliseconds. ++``anon_size`` and ``file_size`` are in pages. ++ ++Phones/Laptops/Workstations ++--------------------------- ++No additional configurations required. ++ ++Servers/Data Centers ++-------------------- ++:To support more generations: Change ``CONFIG_NR_LRU_GENS`` to a ++ larger number. ++ ++:To support more tiers: Change ``CONFIG_TIERS_PER_GEN`` to a larger ++ number. ++ ++:To support full stats: Set ``CONFIG_LRU_GEN_STATS=y``. ++ ++:Working set estimation: Write ``+ memcg_id node_id max_gen ++ [swappiness] [use_bloom_filter]`` to ``/sys/kernel/debug/lru_gen`` to ++ invoke the aging, which scans PTEs for accessed pages and then ++ creates the next generation ``max_gen+1``. A swap file and a non-zero ++ ``swappiness``, which overrides ``vm.swappiness``, are required to ++ scan PTEs mapping anon pages. Set ``use_bloom_filter`` to 0 to ++ override the default behavior which only scans PTE tables found ++ populated. ++ ++:Proactive reclaim: Write ``- memcg_id node_id min_gen [swappiness] ++ [nr_to_reclaim]`` to ``/sys/kernel/debug/lru_gen`` to invoke the ++ eviction, which evicts generations less than or equal to ``min_gen``. ++ ``min_gen`` should be less than ``max_gen-1`` as ``max_gen`` and ++ ``max_gen-1`` are not fully aged and therefore cannot be evicted. ++ Use ``nr_to_reclaim`` to limit the number of pages to evict. Multiple ++ command lines are supported, so does concatenation with delimiters ++ ``,`` and ``;``. ++ ++Framework ++========= ++For each ``lruvec``, evictable pages are divided into multiple ++generations. The youngest generation number is stored in ++``lrugen->max_seq`` for both anon and file types as they are aged on ++an equal footing. The oldest generation numbers are stored in ++``lrugen->min_seq[]`` separately for anon and file types as clean ++file pages can be evicted regardless of swap and writeback ++constraints. These three variables are monotonically increasing. ++Generation numbers are truncated into ++``order_base_2(CONFIG_NR_LRU_GENS+1)`` bits in order to fit into ++``page->flags``. The sliding window technique is used to prevent ++truncated generation numbers from overlapping. Each truncated ++generation number is an index to an array of per-type and per-zone ++lists ``lrugen->lists``. ++ ++Each generation is divided into multiple tiers. Tiers represent ++different ranges of numbers of accesses from file descriptors only. ++Pages accessed ``N`` times via file descriptors belong to tier ++``order_base_2(N)``. Each generation contains at most ++``CONFIG_TIERS_PER_GEN`` tiers, and they require additional ++``CONFIG_TIERS_PER_GEN-2`` bits in ``page->flags``. In contrast to ++moving between generations which requires list operations, moving ++between tiers only involves operations on ``page->flags`` and ++therefore has a negligible cost. A feedback loop modeled after the PID ++controller monitors refaulted % across all tiers and decides when to ++protect pages from which tiers. ++ ++The framework comprises two conceptually independent components: the ++aging and the eviction, which can be invoked separately from user ++space for the purpose of working set estimation and proactive reclaim. ++ ++Aging ++----- ++The aging produces young generations. Given an ``lruvec``, the aging ++traverses ``lruvec_memcg()->mm_list`` and calls ``walk_page_range()`` ++to scan PTEs for accessed pages (a ``mm_struct`` list is maintained ++for each ``memcg``). Upon finding one, the aging updates its ++generation number to ``max_seq`` (modulo ``CONFIG_NR_LRU_GENS``). ++After each round of traversal, the aging increments ``max_seq``. The ++aging is due when ``min_seq[]`` reaches ``max_seq-1``. ++ ++Eviction ++-------- ++The eviction consumes old generations. Given an ``lruvec``, the ++eviction scans pages on the per-zone lists indexed by anon and file ++``min_seq[]`` (modulo ``CONFIG_NR_LRU_GENS``). It first tries to ++select a type based on the values of ``min_seq[]``. If they are ++equal, it selects the type that has a lower refaulted %. The eviction ++sorts a page according to its updated generation number if the aging ++has found this page accessed. It also moves a page to the next ++generation if this page is from an upper tier that has a higher ++refaulted % than the base tier. The eviction increments ``min_seq[]`` ++of a selected type when it finds all the per-zone lists indexed by ++``min_seq[]`` of this selected type are empty. ++ ++To-do List ++========== ++KVM Optimization ++---------------- ++Support shadow page table walk. diff --git a/target/linux/generic/backport-6.1/021-v6.1-mm-mglru-don-t-sync-disk-for-each-aging-cycle.patch b/target/linux/generic/backport-6.1/021-v6.1-mm-mglru-don-t-sync-disk-for-each-aging-cycle.patch new file mode 100644 index 0000000000..269e5eb204 --- /dev/null +++ b/target/linux/generic/backport-6.1/021-v6.1-mm-mglru-don-t-sync-disk-for-each-aging-cycle.patch @@ -0,0 +1,32 @@ +From 14aa8b2d5c2ebead01b542f62d68029023054774 Mon Sep 17 00:00:00 2001 +From: Yu Zhao +Date: Wed, 28 Sep 2022 13:36:58 -0600 +Subject: [PATCH 1/1] mm/mglru: don't sync disk for each aging cycle + +wakeup_flusher_threads() was added under the assumption that if a system +runs out of clean cold pages, it might want to write back dirty pages more +aggressively so that they can become clean and be dropped. + +However, doing so can breach the rate limit a system wants to impose on +writeback, resulting in early SSD wearout. + +Link: https://lkml.kernel.org/r/YzSiWq9UEER5LKup@google.com +Fixes: bd74fdaea146 ("mm: multi-gen LRU: support page table walks") +Signed-off-by: Yu Zhao +Reported-by: Axel Rasmussen +Signed-off-by: Andrew Morton +--- + mm/vmscan.c | 2 -- + 1 file changed, 2 deletions(-) + +--- a/mm/vmscan.c ++++ b/mm/vmscan.c +@@ -4072,8 +4072,6 @@ static bool try_to_inc_max_seq(struct lr + if (wq_has_sleeper(&lruvec->mm_walk.wait)) + wake_up_all(&lruvec->mm_walk.wait); + +- wakeup_flusher_threads(WB_REASON_VMSCAN); +- + return true; + } + diff --git a/target/linux/generic/backport-6.1/050-v5.16-00-MIPS-uasm-Enable-muhu-opcode-for-MIPS-R6.patch b/target/linux/generic/backport-6.1/050-v5.16-00-MIPS-uasm-Enable-muhu-opcode-for-MIPS-R6.patch new file mode 100644 index 0000000000..82feb7421d --- /dev/null +++ b/target/linux/generic/backport-6.1/050-v5.16-00-MIPS-uasm-Enable-muhu-opcode-for-MIPS-R6.patch @@ -0,0 +1,65 @@ +From: Johan Almbladh +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 + +This patch is a dependency for my 32-bit MIPS eBPF JIT. + +Signed-off-by: Johan Almbladh +--- + +--- 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/target/linux/generic/backport-6.1/050-v5.16-01-mips-uasm-Add-workaround-for-Loongson-2F-nop-CPU-err.patch b/target/linux/generic/backport-6.1/050-v5.16-01-mips-uasm-Add-workaround-for-Loongson-2F-nop-CPU-err.patch new file mode 100644 index 0000000000..3a4d573f80 --- /dev/null +++ b/target/linux/generic/backport-6.1/050-v5.16-01-mips-uasm-Add-workaround-for-Loongson-2F-nop-CPU-err.patch @@ -0,0 +1,31 @@ +From: Johan Almbladh +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 +Reviewed-by: Jiaxun Yang +--- + +--- 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/target/linux/generic/backport-6.1/050-v5.16-02-mips-bpf-Add-eBPF-JIT-for-32-bit-MIPS.patch b/target/linux/generic/backport-6.1/050-v5.16-02-mips-bpf-Add-eBPF-JIT-for-32-bit-MIPS.patch new file mode 100644 index 0000000000..7980659961 --- /dev/null +++ b/target/linux/generic/backport-6.1/050-v5.16-02-mips-bpf-Add-eBPF-JIT-for-32-bit-MIPS.patch @@ -0,0 +1,3078 @@ +From: Johan Almbladh +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 +--- + 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 ++ * ++ * Based on code and ideas from ++ * Copyright (c) 2017 Cavium, Inc. ++ * Copyright (c) 2017 Shubham Bansal ++ * Copyright (c) 2011 Mircea Gherzan ++ */ ++ ++/* ++ * 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. ++ * ++ * ! +2 Inverted PC-relative branch ++ * nop Delay slot ++ * j 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 ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#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 ++ * ++ * Based on code and ideas from ++ * Copyright (c) 2017 Cavium, Inc. ++ * Copyright (c) 2017 Shubham Bansal ++ * Copyright (c) 2011 Mircea Gherzan ++ */ ++ ++#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 ++ * ++ * Based on code and ideas from ++ * Copyright (c) 2017 Cavium, Inc. ++ * Copyright (c) 2017 Shubham Bansal ++ * Copyright (c) 2011 Mircea Gherzan ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#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 > 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 > 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 = -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/target/linux/generic/backport-6.1/050-v5.16-03-mips-bpf-Add-new-eBPF-JIT-for-64-bit-MIPS.patch b/target/linux/generic/backport-6.1/050-v5.16-03-mips-bpf-Add-new-eBPF-JIT-for-64-bit-MIPS.patch new file mode 100644 index 0000000000..38b46c0b76 --- /dev/null +++ b/target/linux/generic/backport-6.1/050-v5.16-03-mips-bpf-Add-new-eBPF-JIT-for-64-bit-MIPS.patch @@ -0,0 +1,1005 @@ +From: Johan Almbladh +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 +--- + 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 ++ * ++ * Based on code and ideas from ++ * Copyright (c) 2017 Cavium, Inc. ++ * Copyright (c) 2017 Shubham Bansal ++ * Copyright (c) 2011 Mircea Gherzan ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#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/target/linux/generic/backport-6.1/050-v5.16-04-mips-bpf-Add-JIT-workarounds-for-CPU-errata.patch b/target/linux/generic/backport-6.1/050-v5.16-04-mips-bpf-Add-JIT-workarounds-for-CPU-errata.patch new file mode 100644 index 0000000000..63553ebe58 --- /dev/null +++ b/target/linux/generic/backport-6.1/050-v5.16-04-mips-bpf-Add-JIT-workarounds-for-CPU-errata.patch @@ -0,0 +1,120 @@ +From: Johan Almbladh +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 +Reviewed-by: Jiaxun Yang +--- + +--- 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/target/linux/generic/backport-6.1/050-v5.16-05-mips-bpf-Enable-eBPF-JITs.patch b/target/linux/generic/backport-6.1/050-v5.16-05-mips-bpf-Enable-eBPF-JITs.patch new file mode 100644 index 0000000000..41b3d8139f --- /dev/null +++ b/target/linux/generic/backport-6.1/050-v5.16-05-mips-bpf-Enable-eBPF-JITs.patch @@ -0,0 +1,61 @@ +From: Johan Almbladh +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 +--- + +--- 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 + M: Paul Burton + 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/target/linux/generic/backport-6.1/050-v5.16-06-mips-bpf-Remove-old-BPF-JIT-implementations.patch b/target/linux/generic/backport-6.1/050-v5.16-06-mips-bpf-Remove-old-BPF-JIT-implementations.patch new file mode 100644 index 0000000000..e25c336831 --- /dev/null +++ b/target/linux/generic/backport-6.1/050-v5.16-06-mips-bpf-Remove-old-BPF-JIT-implementations.patch @@ -0,0 +1,387 @@ +From: Johan Almbladh +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 +--- + 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 +- */ +- +-#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 +- * +- * 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 +-#include +-#include +-#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/target/linux/generic/backport-6.1/081-v5.17-regmap-allow-to-define-reg_update_bits-for-no-bus.patch b/target/linux/generic/backport-6.1/081-v5.17-regmap-allow-to-define-reg_update_bits-for-no-bus.patch new file mode 100644 index 0000000000..e4c0833ae7 --- /dev/null +++ b/target/linux/generic/backport-6.1/081-v5.17-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 +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 +Link: https://lore.kernel.org/r/20211104150040.1260-1-ansuelsmth@gmail.com +Signed-off-by: Mark Brown +--- + 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/target/linux/generic/backport-6.1/100-v5.18-tty-serial-bcm63xx-use-more-precise-Kconfig-symbol.patch b/target/linux/generic/backport-6.1/100-v5.18-tty-serial-bcm63xx-use-more-precise-Kconfig-symbol.patch new file mode 100644 index 0000000000..7de3cbbda0 --- /dev/null +++ b/target/linux/generic/backport-6.1/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?= +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 +Reviewed-by: Geert Uytterhoeven +Acked-by: Florian Fainelli +Signed-off-by: Rafał Miłecki +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 +--- + 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/target/linux/generic/backport-6.1/200-v5.18-tools-resolve_btfids-Build-with-host-flags.patch b/target/linux/generic/backport-6.1/200-v5.18-tools-resolve_btfids-Build-with-host-flags.patch new file mode 100644 index 0000000000..caec8db5d6 --- /dev/null +++ b/target/linux/generic/backport-6.1/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 +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 +Signed-off-by: Andrii Nakryiko +Acked-by: Song Liu +Link: https://lore.kernel.org/bpf/20220112002503.115968-1-connoro@google.com +(cherry picked from commit 0e3a1c902ffb56e9fe4416f0cd382c97b09ecbf6) +Signed-off-by: Stijn Tintel +--- + 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/target/linux/generic/backport-6.1/201-v5.16-scripts-dtc-Update-to-upstream-version-v1.6.1-19-g0a.patch b/target/linux/generic/backport-6.1/201-v5.16-scripts-dtc-Update-to-upstream-version-v1.6.1-19-g0a.patch new file mode 100644 index 0000000000..d1bef74f32 --- /dev/null +++ b/target/linux/generic/backport-6.1/201-v5.16-scripts-dtc-Update-to-upstream-version-v1.6.1-19-g0a.patch @@ -0,0 +1,997 @@ +From a77725a9a3c5924e2fd4cd5b3557dd92a8e46f87 Mon Sep 17 00:00:00 2001 +From: Rob Herring +Date: Mon, 25 Oct 2021 11:05:45 -0500 +Subject: [PATCH 1/1] scripts/dtc: Update to upstream version + v1.6.1-19-g0a3a9d3449c8 + +This adds the following commits from upstream: + +0a3a9d3449c8 checks: Add an interrupt-map check +8fd24744e361 checks: Ensure '#interrupt-cells' only exists in interrupt providers +d8d1a9a77863 checks: Drop interrupt provider '#address-cells' check +52a16fd72824 checks: Make interrupt_provider check dependent on interrupts_extended_is_cell +37fd700685da treesource: Maintain phandle label/path on output +e33ce1d6a8c7 flattree: Use '\n', not ';' to separate asm pseudo-ops +d24cc189dca6 asm: Use assembler macros instead of cpp macros +ff3a30c115ad asm: Use .asciz and .ascii instead of .string +5eb5927d81ee fdtdump: fix -Werror=int-to-pointer-cast +0869f8269161 libfdt: Add ALIGNMENT error string +69595a167f06 checks: Fix bus-range check +72d09e2682a4 Makefile: add -Wsign-compare to warning options +b587787ef388 checks: Fix signedness comparisons warnings +69bed6c2418f dtc: Wrap phandle validity check +910221185560 fdtget: Fix signedness comparisons warnings +d966f08fcd21 tests: Fix signedness comparisons warnings +ecfb438c07fa dtc: Fix signedness comparisons warnings: pointer diff +5bec74a6d135 dtc: Fix signedness comparisons warnings: reservednum +24e7f511fd4a fdtdump: Fix signedness comparisons warnings +b6910bec1161 Bump version to v1.6.1 +21d61d18f968 Fix CID 1461557 +4c2ef8f4d14c checks: Introduce is_multiple_of() +e59ca36fb70e Make handling of cpp line information more tolerant +0c3fd9b6aceb checks: Drop interrupt_cells_is_cell check +6b3081abc4ac checks: Add check_is_cell() for all phandle+arg properties +2dffc192a77f yamltree: Remove marker ordering dependency +61e513439e40 pylibfdt: Rework "avoid unused variable warning" lines +c8bddd106095 tests: add a positive gpio test case +ad4abfadb687 checks: replace strstr and strrchr with strends +09c6a6e88718 dtc.h: add strends for suffix matching +9bb9b8d0b4a0 checks: tigthen up nr-gpios prop exception +b07b62ee3342 libfdt: Add FDT alignment check to fdt_check_header() +a2def5479950 libfdt: Check that the root-node name is empty +4ca61f84dc21 libfdt: Check that there is only one root node +34d708249a91 dtc: Remove -O dtbo support +8e7ff260f755 libfdt: Fix a possible "unchecked return value" warning +88875268c05c checks: Warn on node-name and property name being the same +9d2279e7e6ee checks: Change node-name check to match devicetree spec +f527c867a8c6 util: limit gnu_printf format attribute to gcc >= 4.4.0 + +Reviewed-by: Frank Rowand +Tested-by: Frank Rowand +Signed-off-by: Rob Herring +--- + scripts/dtc/checks.c | 222 ++++++++++++++++++++++-------- + scripts/dtc/dtc-lexer.l | 2 +- + scripts/dtc/dtc.c | 6 +- + scripts/dtc/dtc.h | 40 +++++- + scripts/dtc/flattree.c | 11 +- + scripts/dtc/libfdt/fdt.c | 4 + + scripts/dtc/libfdt/fdt_rw.c | 18 ++- + scripts/dtc/libfdt/fdt_strerror.c | 1 + + scripts/dtc/libfdt/libfdt.h | 7 + + scripts/dtc/livetree.c | 6 +- + scripts/dtc/treesource.c | 48 +++---- + scripts/dtc/util.h | 6 +- + scripts/dtc/version_gen.h | 2 +- + scripts/dtc/yamltree.c | 16 ++- + 14 files changed, 275 insertions(+), 114 deletions(-) + +--- a/scripts/dtc/checks.c ++++ b/scripts/dtc/checks.c +@@ -143,6 +143,14 @@ static void check_nodes_props(struct che + check_nodes_props(c, dti, child); + } + ++static bool is_multiple_of(int multiple, int divisor) ++{ ++ if (divisor == 0) ++ return multiple == 0; ++ else ++ return (multiple % divisor) == 0; ++} ++ + static bool run_check(struct check *c, struct dt_info *dti) + { + struct node *dt = dti->dt; +@@ -297,19 +305,20 @@ ERROR(duplicate_property_names, check_du + #define LOWERCASE "abcdefghijklmnopqrstuvwxyz" + #define UPPERCASE "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + #define DIGITS "0123456789" +-#define PROPNODECHARS LOWERCASE UPPERCASE DIGITS ",._+*#?-" ++#define NODECHARS LOWERCASE UPPERCASE DIGITS ",._+-@" ++#define PROPCHARS LOWERCASE UPPERCASE DIGITS ",._+*#?-" + #define PROPNODECHARSSTRICT LOWERCASE UPPERCASE DIGITS ",-" + + static void check_node_name_chars(struct check *c, struct dt_info *dti, + struct node *node) + { +- int n = strspn(node->name, c->data); ++ size_t n = strspn(node->name, c->data); + + if (n < strlen(node->name)) + FAIL(c, dti, node, "Bad character '%c' in node name", + node->name[n]); + } +-ERROR(node_name_chars, check_node_name_chars, PROPNODECHARS "@"); ++ERROR(node_name_chars, check_node_name_chars, NODECHARS); + + static void check_node_name_chars_strict(struct check *c, struct dt_info *dti, + struct node *node) +@@ -330,6 +339,20 @@ static void check_node_name_format(struc + } + ERROR(node_name_format, check_node_name_format, NULL, &node_name_chars); + ++static void check_node_name_vs_property_name(struct check *c, ++ struct dt_info *dti, ++ struct node *node) ++{ ++ if (!node->parent) ++ return; ++ ++ if (get_property(node->parent, node->name)) { ++ FAIL(c, dti, node, "node name and property name conflict"); ++ } ++} ++WARNING(node_name_vs_property_name, check_node_name_vs_property_name, ++ NULL, &node_name_chars); ++ + static void check_unit_address_vs_reg(struct check *c, struct dt_info *dti, + struct node *node) + { +@@ -363,14 +386,14 @@ static void check_property_name_chars(st + struct property *prop; + + for_each_property(node, prop) { +- int n = strspn(prop->name, c->data); ++ size_t n = strspn(prop->name, c->data); + + if (n < strlen(prop->name)) + FAIL_PROP(c, dti, node, prop, "Bad character '%c' in property name", + prop->name[n]); + } + } +-ERROR(property_name_chars, check_property_name_chars, PROPNODECHARS); ++ERROR(property_name_chars, check_property_name_chars, PROPCHARS); + + static void check_property_name_chars_strict(struct check *c, + struct dt_info *dti, +@@ -380,7 +403,7 @@ static void check_property_name_chars_st + + for_each_property(node, prop) { + const char *name = prop->name; +- int n = strspn(name, c->data); ++ size_t n = strspn(name, c->data); + + if (n == strlen(prop->name)) + continue; +@@ -497,7 +520,7 @@ static cell_t check_phandle_prop(struct + + phandle = propval_cell(prop); + +- if ((phandle == 0) || (phandle == -1)) { ++ if (!phandle_is_valid(phandle)) { + FAIL_PROP(c, dti, node, prop, "bad value (0x%x) in %s property", + phandle, prop->name); + return 0; +@@ -556,7 +579,7 @@ static void check_name_properties(struct + if (!prop) + return; /* No name property, that's fine */ + +- if ((prop->val.len != node->basenamelen+1) ++ if ((prop->val.len != node->basenamelen + 1U) + || (memcmp(prop->val.val, node->name, node->basenamelen) != 0)) { + FAIL(c, dti, node, "\"name\" property is incorrect (\"%s\" instead" + " of base node name)", prop->val.val); +@@ -657,7 +680,6 @@ ERROR(omit_unused_nodes, fixup_omit_unus + */ + WARNING_IF_NOT_CELL(address_cells_is_cell, "#address-cells"); + WARNING_IF_NOT_CELL(size_cells_is_cell, "#size-cells"); +-WARNING_IF_NOT_CELL(interrupt_cells_is_cell, "#interrupt-cells"); + + WARNING_IF_NOT_STRING(device_type_is_string, "device_type"); + WARNING_IF_NOT_STRING(model_is_string, "model"); +@@ -672,8 +694,7 @@ static void check_names_is_string_list(s + struct property *prop; + + for_each_property(node, prop) { +- const char *s = strrchr(prop->name, '-'); +- if (!s || !streq(s, "-names")) ++ if (!strends(prop->name, "-names")) + continue; + + c->data = prop->name; +@@ -753,7 +774,7 @@ static void check_reg_format(struct chec + size_cells = node_size_cells(node->parent); + entrylen = (addr_cells + size_cells) * sizeof(cell_t); + +- if (!entrylen || (prop->val.len % entrylen) != 0) ++ if (!is_multiple_of(prop->val.len, entrylen)) + FAIL_PROP(c, dti, node, prop, "property has invalid length (%d bytes) " + "(#address-cells == %d, #size-cells == %d)", + prop->val.len, addr_cells, size_cells); +@@ -794,7 +815,7 @@ static void check_ranges_format(struct c + "#size-cells (%d) differs from %s (%d)", + ranges, c_size_cells, node->parent->fullpath, + p_size_cells); +- } else if ((prop->val.len % entrylen) != 0) { ++ } else if (!is_multiple_of(prop->val.len, entrylen)) { + FAIL_PROP(c, dti, node, prop, "\"%s\" property has invalid length (%d bytes) " + "(parent #address-cells == %d, child #address-cells == %d, " + "#size-cells == %d)", ranges, prop->val.len, +@@ -871,7 +892,7 @@ static void check_pci_device_bus_num(str + } else { + cells = (cell_t *)prop->val.val; + min_bus = fdt32_to_cpu(cells[0]); +- max_bus = fdt32_to_cpu(cells[0]); ++ max_bus = fdt32_to_cpu(cells[1]); + } + if ((bus_num < min_bus) || (bus_num > max_bus)) + FAIL_PROP(c, dti, node, prop, "PCI bus number %d out of range, expected (%d - %d)", +@@ -1367,9 +1388,9 @@ static void check_property_phandle_args( + const struct provider *provider) + { + struct node *root = dti->dt; +- int cell, cellsize = 0; ++ unsigned int cell, cellsize = 0; + +- if (prop->val.len % sizeof(cell_t)) { ++ if (!is_multiple_of(prop->val.len, sizeof(cell_t))) { + FAIL_PROP(c, dti, node, prop, + "property size (%d) is invalid, expected multiple of %zu", + prop->val.len, sizeof(cell_t)); +@@ -1379,14 +1400,14 @@ static void check_property_phandle_args( + for (cell = 0; cell < prop->val.len / sizeof(cell_t); cell += cellsize + 1) { + struct node *provider_node; + struct property *cellprop; +- int phandle; ++ cell_t phandle; + + phandle = propval_cell_n(prop, cell); + /* + * Some bindings use a cell value 0 or -1 to skip over optional + * entries when each index position has a specific definition. + */ +- if (phandle == 0 || phandle == -1) { ++ if (!phandle_is_valid(phandle)) { + /* Give up if this is an overlay with external references */ + if (dti->dtsflags & DTSF_PLUGIN) + break; +@@ -1452,7 +1473,8 @@ static void check_provider_cells_propert + } + #define WARNING_PROPERTY_PHANDLE_CELLS(nm, propname, cells_name, ...) \ + static struct provider nm##_provider = { (propname), (cells_name), __VA_ARGS__ }; \ +- WARNING(nm##_property, check_provider_cells_property, &nm##_provider, &phandle_references); ++ WARNING_IF_NOT_CELL(nm##_is_cell, cells_name); \ ++ WARNING(nm##_property, check_provider_cells_property, &nm##_provider, &nm##_is_cell, &phandle_references); + + WARNING_PROPERTY_PHANDLE_CELLS(clocks, "clocks", "#clock-cells"); + WARNING_PROPERTY_PHANDLE_CELLS(cooling_device, "cooling-device", "#cooling-cells"); +@@ -1473,24 +1495,17 @@ WARNING_PROPERTY_PHANDLE_CELLS(thermal_s + + static bool prop_is_gpio(struct property *prop) + { +- char *str; +- + /* + * *-gpios and *-gpio can appear in property names, + * so skip over any false matches (only one known ATM) + */ +- if (strstr(prop->name, "nr-gpio")) ++ if (strends(prop->name, ",nr-gpios")) + return false; + +- str = strrchr(prop->name, '-'); +- if (str) +- str++; +- else +- str = prop->name; +- if (!(streq(str, "gpios") || streq(str, "gpio"))) +- return false; +- +- return true; ++ return strends(prop->name, "-gpios") || ++ streq(prop->name, "gpios") || ++ strends(prop->name, "-gpio") || ++ streq(prop->name, "gpio"); + } + + static void check_gpios_property(struct check *c, +@@ -1525,13 +1540,10 @@ static void check_deprecated_gpio_proper + struct property *prop; + + for_each_property(node, prop) { +- char *str; +- + if (!prop_is_gpio(prop)) + continue; + +- str = strstr(prop->name, "gpio"); +- if (!streq(str, "gpio")) ++ if (!strends(prop->name, "gpio")) + continue; + + FAIL_PROP(c, dti, node, prop, +@@ -1561,21 +1573,106 @@ static void check_interrupt_provider(str + struct node *node) + { + struct property *prop; ++ bool irq_provider = node_is_interrupt_provider(node); + +- if (!node_is_interrupt_provider(node)) ++ prop = get_property(node, "#interrupt-cells"); ++ if (irq_provider && !prop) { ++ FAIL(c, dti, node, ++ "Missing '#interrupt-cells' in interrupt provider"); + return; ++ } + +- prop = get_property(node, "#interrupt-cells"); +- if (!prop) ++ if (!irq_provider && prop) { + FAIL(c, dti, node, +- "Missing #interrupt-cells in interrupt provider"); ++ "'#interrupt-cells' found, but node is not an interrupt provider"); ++ return; ++ } ++} ++WARNING(interrupt_provider, check_interrupt_provider, NULL, &interrupts_extended_is_cell); + +- prop = get_property(node, "#address-cells"); +- if (!prop) ++static void check_interrupt_map(struct check *c, ++ struct dt_info *dti, ++ struct node *node) ++{ ++ struct node *root = dti->dt; ++ struct property *prop, *irq_map_prop; ++ size_t cellsize, cell, map_cells; ++ ++ irq_map_prop = get_property(node, "interrupt-map"); ++ if (!irq_map_prop) ++ return; ++ ++ if (node->addr_cells < 0) { + FAIL(c, dti, node, +- "Missing #address-cells in interrupt provider"); ++ "Missing '#address-cells' in interrupt-map provider"); ++ return; ++ } ++ cellsize = node_addr_cells(node); ++ cellsize += propval_cell(get_property(node, "#interrupt-cells")); ++ ++ prop = get_property(node, "interrupt-map-mask"); ++ if (prop && (prop->val.len != (cellsize * sizeof(cell_t)))) ++ FAIL_PROP(c, dti, node, prop, ++ "property size (%d) is invalid, expected %zu", ++ prop->val.len, cellsize * sizeof(cell_t)); ++ ++ if (!is_multiple_of(irq_map_prop->val.len, sizeof(cell_t))) { ++ FAIL_PROP(c, dti, node, irq_map_prop, ++ "property size (%d) is invalid, expected multiple of %zu", ++ irq_map_prop->val.len, sizeof(cell_t)); ++ return; ++ } ++ ++ map_cells = irq_map_prop->val.len / sizeof(cell_t); ++ for (cell = 0; cell < map_cells; ) { ++ struct node *provider_node; ++ struct property *cellprop; ++ int phandle; ++ size_t parent_cellsize; ++ ++ if ((cell + cellsize) >= map_cells) { ++ FAIL_PROP(c, dti, node, irq_map_prop, ++ "property size (%d) too small, expected > %zu", ++ irq_map_prop->val.len, (cell + cellsize) * sizeof(cell_t)); ++ break; ++ } ++ cell += cellsize; ++ ++ phandle = propval_cell_n(irq_map_prop, cell); ++ if (!phandle_is_valid(phandle)) { ++ /* Give up if this is an overlay with external references */ ++ if (!(dti->dtsflags & DTSF_PLUGIN)) ++ FAIL_PROP(c, dti, node, irq_map_prop, ++ "Cell %zu is not a phandle(%d)", ++ cell, phandle); ++ break; ++ } ++ ++ provider_node = get_node_by_phandle(root, phandle); ++ if (!provider_node) { ++ FAIL_PROP(c, dti, node, irq_map_prop, ++ "Could not get phandle(%d) node for (cell %zu)", ++ phandle, cell); ++ break; ++ } ++ ++ cellprop = get_property(provider_node, "#interrupt-cells"); ++ if (cellprop) { ++ parent_cellsize = propval_cell(cellprop); ++ } else { ++ FAIL(c, dti, node, "Missing property '#interrupt-cells' in node %s or bad phandle (referred from interrupt-map[%zu])", ++ provider_node->fullpath, cell); ++ break; ++ } ++ ++ cellprop = get_property(provider_node, "#address-cells"); ++ if (cellprop) ++ parent_cellsize += propval_cell(cellprop); ++ ++ cell += 1 + parent_cellsize; ++ } + } +-WARNING(interrupt_provider, check_interrupt_provider, NULL); ++WARNING(interrupt_map, check_interrupt_map, NULL, &phandle_references, &addr_size_cells, &interrupt_provider); + + static void check_interrupts_property(struct check *c, + struct dt_info *dti, +@@ -1584,13 +1681,13 @@ static void check_interrupts_property(st + struct node *root = dti->dt; + struct node *irq_node = NULL, *parent = node; + struct property *irq_prop, *prop = NULL; +- int irq_cells, phandle; ++ cell_t irq_cells, phandle; + + irq_prop = get_property(node, "interrupts"); + if (!irq_prop) + return; + +- if (irq_prop->val.len % sizeof(cell_t)) ++ if (!is_multiple_of(irq_prop->val.len, sizeof(cell_t))) + FAIL_PROP(c, dti, node, irq_prop, "size (%d) is invalid, expected multiple of %zu", + irq_prop->val.len, sizeof(cell_t)); + +@@ -1603,7 +1700,7 @@ static void check_interrupts_property(st + prop = get_property(parent, "interrupt-parent"); + if (prop) { + phandle = propval_cell(prop); +- if ((phandle == 0) || (phandle == -1)) { ++ if (!phandle_is_valid(phandle)) { + /* Give up if this is an overlay with + * external references */ + if (dti->dtsflags & DTSF_PLUGIN) +@@ -1639,7 +1736,7 @@ static void check_interrupts_property(st + } + + irq_cells = propval_cell(prop); +- if (irq_prop->val.len % (irq_cells * sizeof(cell_t))) { ++ if (!is_multiple_of(irq_prop->val.len, irq_cells * sizeof(cell_t))) { + FAIL_PROP(c, dti, node, prop, + "size is (%d), expected multiple of %d", + irq_prop->val.len, (int)(irq_cells * sizeof(cell_t))); +@@ -1750,7 +1847,7 @@ WARNING(graph_port, check_graph_port, NU + static struct node *get_remote_endpoint(struct check *c, struct dt_info *dti, + struct node *endpoint) + { +- int phandle; ++ cell_t phandle; + struct node *node; + struct property *prop; + +@@ -1760,7 +1857,7 @@ static struct node *get_remote_endpoint( + + phandle = propval_cell(prop); + /* Give up if this is an overlay with external references */ +- if (phandle == 0 || phandle == -1) ++ if (!phandle_is_valid(phandle)) + return NULL; + + node = get_node_by_phandle(dti->dt, phandle); +@@ -1796,7 +1893,7 @@ WARNING(graph_endpoint, check_graph_endp + static struct check *check_table[] = { + &duplicate_node_names, &duplicate_property_names, + &node_name_chars, &node_name_format, &property_name_chars, +- &name_is_string, &name_properties, ++ &name_is_string, &name_properties, &node_name_vs_property_name, + + &duplicate_label, + +@@ -1804,7 +1901,7 @@ static struct check *check_table[] = { + &phandle_references, &path_references, + &omit_unused_nodes, + +- &address_cells_is_cell, &size_cells_is_cell, &interrupt_cells_is_cell, ++ &address_cells_is_cell, &size_cells_is_cell, + &device_type_is_string, &model_is_string, &status_is_string, + &label_is_string, + +@@ -1839,26 +1936,43 @@ static struct check *check_table[] = { + &chosen_node_is_root, &chosen_node_bootargs, &chosen_node_stdout_path, + + &clocks_property, ++ &clocks_is_cell, + &cooling_device_property, ++ &cooling_device_is_cell, + &dmas_property, ++ &dmas_is_cell, + &hwlocks_property, ++ &hwlocks_is_cell, + &interrupts_extended_property, ++ &interrupts_extended_is_cell, + &io_channels_property, ++ &io_channels_is_cell, + &iommus_property, ++ &iommus_is_cell, + &mboxes_property, ++ &mboxes_is_cell, + &msi_parent_property, ++ &msi_parent_is_cell, + &mux_controls_property, ++ &mux_controls_is_cell, + &phys_property, ++ &phys_is_cell, + &power_domains_property, ++ &power_domains_is_cell, + &pwms_property, ++ &pwms_is_cell, + &resets_property, ++ &resets_is_cell, + &sound_dai_property, ++ &sound_dai_is_cell, + &thermal_sensors_property, ++ &thermal_sensors_is_cell, + + &deprecated_gpio_property, + &gpios_property, + &interrupts_property, + &interrupt_provider, ++ &interrupt_map, + + &alias_paths, + +@@ -1882,7 +1996,7 @@ static void enable_warning_error(struct + + static void disable_warning_error(struct check *c, bool warn, bool error) + { +- int i; ++ unsigned int i; + + /* Lowering level, also lower it for things this is the prereq + * for */ +@@ -1903,7 +2017,7 @@ static void disable_warning_error(struct + + void parse_checks_option(bool warn, bool error, const char *arg) + { +- int i; ++ unsigned int i; + const char *name = arg; + bool enable = true; + +@@ -1930,7 +2044,7 @@ void parse_checks_option(bool warn, bool + + void process_checks(bool force, struct dt_info *dti) + { +- int i; ++ unsigned int i; + int error = 0; + + for (i = 0; i < ARRAY_SIZE(check_table); i++) { +--- a/scripts/dtc/dtc-lexer.l ++++ b/scripts/dtc/dtc-lexer.l +@@ -57,7 +57,7 @@ static void PRINTF(1, 2) lexical_error(c + push_input_file(name); + } + +-<*>^"#"(line)?[ \t]+[0-9]+[ \t]+{STRING}([ \t]+[0-9]+)? { ++<*>^"#"(line)?[ \t]+[0-9]+[ \t]+{STRING}([ \t]+[0-9]+)* { + char *line, *fnstart, *fnend; + struct data fn; + /* skip text before line # */ +--- a/scripts/dtc/dtc.c ++++ b/scripts/dtc/dtc.c +@@ -12,7 +12,7 @@ + * Command line options + */ + int quiet; /* Level of quietness */ +-int reservenum; /* Number of memory reservation slots */ ++unsigned int reservenum;/* Number of memory reservation slots */ + int minsize; /* Minimum blob size */ + int padsize; /* Additional padding to blob */ + int alignsize; /* Additional padding to blob accroding to the alignsize */ +@@ -197,7 +197,7 @@ int main(int argc, char *argv[]) + depname = optarg; + break; + case 'R': +- reservenum = strtol(optarg, NULL, 0); ++ reservenum = strtoul(optarg, NULL, 0); + break; + case 'S': + minsize = strtol(optarg, NULL, 0); +@@ -359,8 +359,6 @@ int main(int argc, char *argv[]) + #endif + } else if (streq(outform, "dtb")) { + dt_to_blob(outf, dti, outversion); +- } else if (streq(outform, "dtbo")) { +- dt_to_blob(outf, dti, outversion); + } else if (streq(outform, "asm")) { + dt_to_asm(outf, dti, outversion); + } else if (streq(outform, "null")) { +--- a/scripts/dtc/dtc.h ++++ b/scripts/dtc/dtc.h +@@ -35,7 +35,7 @@ + * Command line options + */ + extern int quiet; /* Level of quietness */ +-extern int reservenum; /* Number of memory reservation slots */ ++extern unsigned int reservenum; /* Number of memory reservation slots */ + extern int minsize; /* Minimum blob size */ + extern int padsize; /* Additional padding to blob */ + extern int alignsize; /* Additional padding to blob accroding to the alignsize */ +@@ -51,6 +51,11 @@ extern int annotate; /* annotate .dts w + + typedef uint32_t cell_t; + ++static inline bool phandle_is_valid(cell_t phandle) ++{ ++ return phandle != 0 && phandle != ~0U; ++} ++ + static inline uint16_t dtb_ld16(const void *p) + { + const uint8_t *bp = (const uint8_t *)p; +@@ -86,6 +91,16 @@ static inline uint64_t dtb_ld64(const vo + #define streq(a, b) (strcmp((a), (b)) == 0) + #define strstarts(s, prefix) (strncmp((s), (prefix), strlen(prefix)) == 0) + #define strprefixeq(a, n, b) (strlen(b) == (n) && (memcmp(a, b, n) == 0)) ++static inline bool strends(const char *str, const char *suffix) ++{ ++ unsigned int len, suffix_len; ++ ++ len = strlen(str); ++ suffix_len = strlen(suffix); ++ if (len < suffix_len) ++ return false; ++ return streq(str + len - suffix_len, suffix); ++} + + #define ALIGN(x, a) (((x) + (a) - 1) & ~((a) - 1)) + +@@ -101,6 +116,12 @@ enum markertype { + TYPE_UINT64, + TYPE_STRING, + }; ++ ++static inline bool is_type_marker(enum markertype type) ++{ ++ return type >= TYPE_UINT8; ++} ++ + extern const char *markername(enum markertype markertype); + + struct marker { +@@ -125,7 +146,22 @@ struct data { + for_each_marker(m) \ + if ((m)->type == (t)) + +-size_t type_marker_length(struct marker *m); ++static inline struct marker *next_type_marker(struct marker *m) ++{ ++ for_each_marker(m) ++ if (is_type_marker(m->type)) ++ break; ++ return m; ++} ++ ++static inline size_t type_marker_length(struct marker *m) ++{ ++ struct marker *next = next_type_marker(m->next); ++ ++ if (next) ++ return next->offset - m->offset; ++ return 0; ++} + + void data_free(struct data d); + +--- a/scripts/dtc/flattree.c ++++ b/scripts/dtc/flattree.c +@@ -124,7 +124,8 @@ static void asm_emit_cell(void *e, cell_ + { + FILE *f = e; + +- fprintf(f, "\t.byte 0x%02x; .byte 0x%02x; .byte 0x%02x; .byte 0x%02x\n", ++ fprintf(f, "\t.byte\t0x%02x\n" "\t.byte\t0x%02x\n" ++ "\t.byte\t0x%02x\n" "\t.byte\t0x%02x\n", + (val >> 24) & 0xff, (val >> 16) & 0xff, + (val >> 8) & 0xff, val & 0xff); + } +@@ -134,9 +135,9 @@ static void asm_emit_string(void *e, con + FILE *f = e; + + if (len != 0) +- fprintf(f, "\t.string\t\"%.*s\"\n", len, str); ++ fprintf(f, "\t.asciz\t\"%.*s\"\n", len, str); + else +- fprintf(f, "\t.string\t\"%s\"\n", str); ++ fprintf(f, "\t.asciz\t\"%s\"\n", str); + } + + static void asm_emit_align(void *e, int a) +@@ -295,7 +296,7 @@ static struct data flatten_reserve_list( + { + struct reserve_info *re; + struct data d = empty_data; +- int j; ++ unsigned int j; + + for (re = reservelist; re; re = re->next) { + d = data_append_re(d, re->address, re->size); +@@ -438,7 +439,7 @@ static void dump_stringtable_asm(FILE *f + + while (p < (strbuf.val + strbuf.len)) { + len = strlen(p); +- fprintf(f, "\t.string \"%s\"\n", p); ++ fprintf(f, "\t.asciz \"%s\"\n", p); + p += len+1; + } + } +--- a/scripts/dtc/libfdt/fdt.c ++++ b/scripts/dtc/libfdt/fdt.c +@@ -90,6 +90,10 @@ int fdt_check_header(const void *fdt) + { + size_t hdrsize; + ++ /* The device tree must be at an 8-byte aligned address */ ++ if ((uintptr_t)fdt & 7) ++ return -FDT_ERR_ALIGNMENT; ++ + if (fdt_magic(fdt) != FDT_MAGIC) + return -FDT_ERR_BADMAGIC; + if (!can_assume(LATEST)) { +--- a/scripts/dtc/libfdt/fdt_rw.c ++++ b/scripts/dtc/libfdt/fdt_rw.c +@@ -349,7 +349,10 @@ int fdt_add_subnode_namelen(void *fdt, i + return offset; + + /* Try to place the new node after the parent's properties */ +- fdt_next_tag(fdt, parentoffset, &nextoffset); /* skip the BEGIN_NODE */ ++ tag = fdt_next_tag(fdt, parentoffset, &nextoffset); ++ /* the fdt_subnode_offset_namelen() should ensure this never hits */ ++ if (!can_assume(LIBFDT_FLAWLESS) && (tag != FDT_BEGIN_NODE)) ++ return -FDT_ERR_INTERNAL; + do { + offset = nextoffset; + tag = fdt_next_tag(fdt, offset, &nextoffset); +@@ -391,7 +394,9 @@ int fdt_del_node(void *fdt, int nodeoffs + } + + static void fdt_packblocks_(const char *old, char *new, +- int mem_rsv_size, int struct_size) ++ int mem_rsv_size, ++ int struct_size, ++ int strings_size) + { + int mem_rsv_off, struct_off, strings_off; + +@@ -406,8 +411,7 @@ static void fdt_packblocks_(const char * + fdt_set_off_dt_struct(new, struct_off); + fdt_set_size_dt_struct(new, struct_size); + +- memmove(new + strings_off, old + fdt_off_dt_strings(old), +- fdt_size_dt_strings(old)); ++ memmove(new + strings_off, old + fdt_off_dt_strings(old), strings_size); + fdt_set_off_dt_strings(new, strings_off); + fdt_set_size_dt_strings(new, fdt_size_dt_strings(old)); + } +@@ -467,7 +471,8 @@ int fdt_open_into(const void *fdt, void + return -FDT_ERR_NOSPACE; + } + +- fdt_packblocks_(fdt, tmp, mem_rsv_size, struct_size); ++ fdt_packblocks_(fdt, tmp, mem_rsv_size, struct_size, ++ fdt_size_dt_strings(fdt)); + memmove(buf, tmp, newsize); + + fdt_set_magic(buf, FDT_MAGIC); +@@ -487,7 +492,8 @@ int fdt_pack(void *fdt) + + mem_rsv_size = (fdt_num_mem_rsv(fdt)+1) + * sizeof(struct fdt_reserve_entry); +- fdt_packblocks_(fdt, fdt, mem_rsv_size, fdt_size_dt_struct(fdt)); ++ fdt_packblocks_(fdt, fdt, mem_rsv_size, fdt_size_dt_struct(fdt), ++ fdt_size_dt_strings(fdt)); + fdt_set_totalsize(fdt, fdt_data_size_(fdt)); + + return 0; +--- a/scripts/dtc/libfdt/fdt_strerror.c ++++ b/scripts/dtc/libfdt/fdt_strerror.c +@@ -39,6 +39,7 @@ static struct fdt_errtabent fdt_errtable + FDT_ERRTABENT(FDT_ERR_BADOVERLAY), + FDT_ERRTABENT(FDT_ERR_NOPHANDLES), + FDT_ERRTABENT(FDT_ERR_BADFLAGS), ++ FDT_ERRTABENT(FDT_ERR_ALIGNMENT), + }; + #define FDT_ERRTABSIZE ((int)(sizeof(fdt_errtable) / sizeof(fdt_errtable[0]))) + +--- a/scripts/dtc/libfdt/libfdt.h ++++ b/scripts/dtc/libfdt/libfdt.h +@@ -131,6 +131,13 @@ uint32_t fdt_next_tag(const void *fdt, i + * to work even with unaligned pointers on platforms (such as ARMv5) that don't + * like unaligned loads and stores. + */ ++static inline uint16_t fdt16_ld(const fdt16_t *p) ++{ ++ const uint8_t *bp = (const uint8_t *)p; ++ ++ return ((uint16_t)bp[0] << 8) | bp[1]; ++} ++ + static inline uint32_t fdt32_ld(const fdt32_t *p) + { + const uint8_t *bp = (const uint8_t *)p; +--- a/scripts/dtc/livetree.c ++++ b/scripts/dtc/livetree.c +@@ -526,7 +526,7 @@ struct node *get_node_by_path(struct nod + p = strchr(path, '/'); + + for_each_child(tree, child) { +- if (p && strprefixeq(path, p - path, child->name)) ++ if (p && strprefixeq(path, (size_t)(p - path), child->name)) + return get_node_by_path(child, p+1); + else if (!p && streq(path, child->name)) + return child; +@@ -559,7 +559,7 @@ struct node *get_node_by_phandle(struct + { + struct node *child, *node; + +- if ((phandle == 0) || (phandle == -1)) { ++ if (!phandle_is_valid(phandle)) { + assert(generate_fixups); + return NULL; + } +@@ -594,7 +594,7 @@ cell_t get_node_phandle(struct node *roo + static cell_t phandle = 1; /* FIXME: ick, static local */ + struct data d = empty_data; + +- if ((node->phandle != 0) && (node->phandle != -1)) ++ if (phandle_is_valid(node->phandle)) + return node->phandle; + + while (get_node_by_phandle(root, phandle)) +--- a/scripts/dtc/treesource.c ++++ b/scripts/dtc/treesource.c +@@ -124,27 +124,6 @@ static void write_propval_int(FILE *f, c + } + } + +-static bool has_data_type_information(struct marker *m) +-{ +- return m->type >= TYPE_UINT8; +-} +- +-static struct marker *next_type_marker(struct marker *m) +-{ +- while (m && !has_data_type_information(m)) +- m = m->next; +- return m; +-} +- +-size_t type_marker_length(struct marker *m) +-{ +- struct marker *next = next_type_marker(m->next); +- +- if (next) +- return next->offset - m->offset; +- return 0; +-} +- + static const char *delim_start[] = { + [TYPE_UINT8] = "[", + [TYPE_UINT16] = "/bits/ 16 <", +@@ -229,26 +208,39 @@ static void write_propval(FILE *f, struc + size_t chunk_len = (m->next ? m->next->offset : len) - m->offset; + size_t data_len = type_marker_length(m) ? : len - m->offset; + const char *p = &prop->val.val[m->offset]; ++ struct marker *m_phandle; + +- if (has_data_type_information(m)) { ++ if (is_type_marker(m->type)) { + emit_type = m->type; + fprintf(f, " %s", delim_start[emit_type]); + } else if (m->type == LABEL) + fprintf(f, " %s:", m->ref); +- else if (m->offset) +- fputc(' ', f); + +- if (emit_type == TYPE_NONE) { +- assert(chunk_len == 0); ++ if (emit_type == TYPE_NONE || chunk_len == 0) + continue; +- } + + switch(emit_type) { + case TYPE_UINT16: + write_propval_int(f, p, chunk_len, 2); + break; + case TYPE_UINT32: +- write_propval_int(f, p, chunk_len, 4); ++ m_phandle = prop->val.markers; ++ for_each_marker_of_type(m_phandle, REF_PHANDLE) ++ if (m->offset == m_phandle->offset) ++ break; ++ ++ if (m_phandle) { ++ if (m_phandle->ref[0] == '/') ++ fprintf(f, "&{%s}", m_phandle->ref); ++ else ++ fprintf(f, "&%s", m_phandle->ref); ++ if (chunk_len > 4) { ++ fputc(' ', f); ++ write_propval_int(f, p + 4, chunk_len - 4, 4); ++ } ++ } else { ++ write_propval_int(f, p, chunk_len, 4); ++ } + break; + case TYPE_UINT64: + write_propval_int(f, p, chunk_len, 8); +--- a/scripts/dtc/util.h ++++ b/scripts/dtc/util.h +@@ -13,10 +13,10 @@ + */ + + #ifdef __GNUC__ +-#ifdef __clang__ +-#define PRINTF(i, j) __attribute__((format (printf, i, j))) +-#else ++#if __GNUC__ >= 5 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 4) + #define PRINTF(i, j) __attribute__((format (gnu_printf, i, j))) ++#else ++#define PRINTF(i, j) __attribute__((format (printf, i, j))) + #endif + #define NORETURN __attribute__((noreturn)) + #else +--- a/scripts/dtc/version_gen.h ++++ b/scripts/dtc/version_gen.h +@@ -1 +1 @@ +-#define DTC_VERSION "DTC 1.6.0-g183df9e9" ++#define DTC_VERSION "DTC 1.6.1-g0a3a9d34" +--- a/scripts/dtc/yamltree.c ++++ b/scripts/dtc/yamltree.c +@@ -29,11 +29,12 @@ char *yaml_error_name[] = { + (emitter)->problem, __func__, __LINE__); \ + }) + +-static void yaml_propval_int(yaml_emitter_t *emitter, struct marker *markers, char *data, unsigned int len, int width) ++static void yaml_propval_int(yaml_emitter_t *emitter, struct marker *markers, ++ char *data, unsigned int seq_offset, unsigned int len, int width) + { + yaml_event_t event; + void *tag; +- unsigned int off, start_offset = markers->offset; ++ unsigned int off; + + switch(width) { + case 1: tag = "!u8"; break; +@@ -66,7 +67,7 @@ static void yaml_propval_int(yaml_emitte + m = markers; + is_phandle = false; + for_each_marker_of_type(m, REF_PHANDLE) { +- if (m->offset == (start_offset + off)) { ++ if (m->offset == (seq_offset + off)) { + is_phandle = true; + break; + } +@@ -114,6 +115,7 @@ static void yaml_propval(yaml_emitter_t + yaml_event_t event; + unsigned int len = prop->val.len; + struct marker *m = prop->val.markers; ++ struct marker *markers = prop->val.markers; + + /* Emit the property name */ + yaml_scalar_event_initialize(&event, NULL, +@@ -151,19 +153,19 @@ static void yaml_propval(yaml_emitter_t + + switch(m->type) { + case TYPE_UINT16: +- yaml_propval_int(emitter, m, data, chunk_len, 2); ++ yaml_propval_int(emitter, markers, data, m->offset, chunk_len, 2); + break; + case TYPE_UINT32: +- yaml_propval_int(emitter, m, data, chunk_len, 4); ++ yaml_propval_int(emitter, markers, data, m->offset, chunk_len, 4); + break; + case TYPE_UINT64: +- yaml_propval_int(emitter, m, data, chunk_len, 8); ++ yaml_propval_int(emitter, markers, data, m->offset, chunk_len, 8); + break; + case TYPE_STRING: + yaml_propval_string(emitter, data, chunk_len); + break; + default: +- yaml_propval_int(emitter, m, data, chunk_len, 1); ++ yaml_propval_int(emitter, markers, data, m->offset, chunk_len, 1); + break; + } + } diff --git a/target/linux/generic/backport-6.1/300-v5.18-pinctrl-qcom-Return--EINVAL-for-setting-affinity-if-no-IRQ-parent.patch b/target/linux/generic/backport-6.1/300-v5.18-pinctrl-qcom-Return--EINVAL-for-setting-affinity-if-no-IRQ-parent.patch new file mode 100644 index 0000000000..18a8752a18 --- /dev/null +++ b/target/linux/generic/backport-6.1/300-v5.18-pinctrl-qcom-Return--EINVAL-for-setting-affinity-if-no-IRQ-parent.patch @@ -0,0 +1,48 @@ +From: Manivannan Sadhasivam +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 +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 +--- + 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/target/linux/generic/backport-6.1/301-v5.16-soc-qcom-smem-Support-reserved-memory-description.patch b/target/linux/generic/backport-6.1/301-v5.16-soc-qcom-smem-Support-reserved-memory-description.patch new file mode 100644 index 0000000000..ee0bf9309f --- /dev/null +++ b/target/linux/generic/backport-6.1/301-v5.16-soc-qcom-smem-Support-reserved-memory-description.patch @@ -0,0 +1,166 @@ +From b5af64fceb04dc298c5e69c517b4d83893ff060b Mon Sep 17 00:00:00 2001 +From: Bjorn Andersson +Date: Thu, 30 Sep 2021 11:21:10 -0700 +Subject: [PATCH 1/1] soc: qcom: smem: Support reserved-memory description + +Practically all modern Qualcomm platforms has a single reserved-memory +region for SMEM. So rather than having to describe SMEM in the form of a +node with a reference to a reserved-memory node, allow the SMEM device +to be instantiated directly from the reserved-memory node. + +The current means of falling back to dereferencing the "memory-region" +is kept as a fallback, if it's determined that the SMEM node is a +reserved-memory node. + +The "qcom,smem" compatible is added to the reserved_mem_matches list, to +allow the reserved-memory device to be probed. + +In order to retain the readability of the code, the resolution of +resources is split from the actual ioremapping. + +Signed-off-by: Bjorn Andersson +Acked-by: Rob Herring +Reviewed-by: Vladimir Zapolskiy +Link: https://lore.kernel.org/r/20210930182111.57353-4-bjorn.andersson@linaro.org +--- + drivers/of/platform.c | 1 + + drivers/soc/qcom/smem.c | 57 ++++++++++++++++++++++++++++------------- + 2 files changed, 40 insertions(+), 18 deletions(-) + +--- a/drivers/of/platform.c ++++ b/drivers/of/platform.c +@@ -509,6 +509,7 @@ EXPORT_SYMBOL_GPL(of_platform_default_po + static const struct of_device_id reserved_mem_matches[] = { + { .compatible = "qcom,rmtfs-mem" }, + { .compatible = "qcom,cmd-db" }, ++ { .compatible = "qcom,smem" }, + { .compatible = "ramoops" }, + { .compatible = "nvmem-rmem" }, + {} +--- a/drivers/soc/qcom/smem.c ++++ b/drivers/soc/qcom/smem.c +@@ -9,6 +9,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -240,7 +241,7 @@ static const u8 SMEM_INFO_MAGIC[] = { 0x + * @size: size of the memory region + */ + struct smem_region { +- u32 aux_base; ++ phys_addr_t aux_base; + void __iomem *virt_base; + size_t size; + }; +@@ -499,7 +500,7 @@ static void *qcom_smem_get_global(struct + for (i = 0; i < smem->num_regions; i++) { + region = &smem->regions[i]; + +- if (region->aux_base == aux_base || !aux_base) { ++ if ((u32)region->aux_base == aux_base || !aux_base) { + if (size != NULL) + *size = le32_to_cpu(entry->size); + return region->virt_base + le32_to_cpu(entry->offset); +@@ -664,7 +665,7 @@ phys_addr_t qcom_smem_virt_to_phys(void + if (p < region->virt_base + region->size) { + u64 offset = p - region->virt_base; + +- return (phys_addr_t)region->aux_base + offset; ++ return region->aux_base + offset; + } + } + +@@ -863,12 +864,12 @@ qcom_smem_enumerate_partitions(struct qc + return 0; + } + +-static int qcom_smem_map_memory(struct qcom_smem *smem, struct device *dev, +- const char *name, int i) ++static int qcom_smem_resolve_mem(struct qcom_smem *smem, const char *name, ++ struct smem_region *region) + { ++ struct device *dev = smem->dev; + struct device_node *np; + struct resource r; +- resource_size_t size; + int ret; + + np = of_parse_phandle(dev->of_node, name, 0); +@@ -881,13 +882,9 @@ static int qcom_smem_map_memory(struct q + of_node_put(np); + if (ret) + return ret; +- size = resource_size(&r); + +- smem->regions[i].virt_base = devm_ioremap_wc(dev, r.start, size); +- if (!smem->regions[i].virt_base) +- return -ENOMEM; +- smem->regions[i].aux_base = (u32)r.start; +- smem->regions[i].size = size; ++ region->aux_base = r.start; ++ region->size = resource_size(&r); + + return 0; + } +@@ -895,12 +892,14 @@ static int qcom_smem_map_memory(struct q + static int qcom_smem_probe(struct platform_device *pdev) + { + struct smem_header *header; ++ struct reserved_mem *rmem; + struct qcom_smem *smem; + size_t array_size; + int num_regions; + int hwlock_id; + u32 version; + int ret; ++ int i; + + num_regions = 1; + if (of_find_property(pdev->dev.of_node, "qcom,rpm-msg-ram", NULL)) +@@ -914,13 +913,35 @@ static int qcom_smem_probe(struct platfo + smem->dev = &pdev->dev; + smem->num_regions = num_regions; + +- ret = qcom_smem_map_memory(smem, &pdev->dev, "memory-region", 0); +- if (ret) +- return ret; +- +- if (num_regions > 1 && (ret = qcom_smem_map_memory(smem, &pdev->dev, +- "qcom,rpm-msg-ram", 1))) +- return ret; ++ rmem = of_reserved_mem_lookup(pdev->dev.of_node); ++ if (rmem) { ++ smem->regions[0].aux_base = rmem->base; ++ smem->regions[0].size = rmem->size; ++ } else { ++ /* ++ * Fall back to the memory-region reference, if we're not a ++ * reserved-memory node. ++ */ ++ ret = qcom_smem_resolve_mem(smem, "memory-region", &smem->regions[0]); ++ if (ret) ++ return ret; ++ } ++ ++ if (num_regions > 1) { ++ ret = qcom_smem_resolve_mem(smem, "qcom,rpm-msg-ram", &smem->regions[1]); ++ if (ret) ++ return ret; ++ } ++ ++ for (i = 0; i < num_regions; i++) { ++ smem->regions[i].virt_base = devm_ioremap_wc(&pdev->dev, ++ smem->regions[i].aux_base, ++ smem->regions[i].size); ++ if (!smem->regions[i].virt_base) { ++ dev_err(&pdev->dev, "failed to remap %pa\n", &smem->regions[i].aux_base); ++ return -ENOMEM; ++ } ++ } + + header = smem->regions[0].virt_base; + if (le32_to_cpu(header->initialized) != 1 || diff --git a/target/linux/generic/backport-6.1/330-v5.16-01-MIPS-kernel-proc-add-CPU-option-reporting.patch b/target/linux/generic/backport-6.1/330-v5.16-01-MIPS-kernel-proc-add-CPU-option-reporting.patch new file mode 100644 index 0000000000..c66a3f11b4 --- /dev/null +++ b/target/linux/generic/backport-6.1/330-v5.16-01-MIPS-kernel-proc-add-CPU-option-reporting.patch @@ -0,0 +1,162 @@ +From 626bfa03729959ea9917181fb3d8ffaa1594d02a Mon Sep 17 00:00:00 2001 +From: Hauke Mehrtens +Date: Wed, 13 Oct 2021 22:40:18 -0700 +Subject: [PATCH 1/1] MIPS: kernel: proc: add CPU option reporting + +Many MIPS CPUs have optional CPU features which are not activated for +all CPU cores. Print the CPU options, which are implemented in the core, +in /proc/cpuinfo. This makes it possible to see which 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 + +Changes from original patch[0]: +- Remove cpu_has_6k_cache and cpu_has_8k_cache due to commit 6ce91ba8589a + ("MIPS: Remove cpu_has_6k_cache and cpu_has_8k_cache in cpu_cache_init()") +- Add new options: mac2008_only, ftlbparex, gsexcex, mmid, mm_sysad, + mm_full +- Use seq_puts instead of seq_printf as suggested by checkpatch +- Minor commit message reword + +[0]: https://lore.kernel.org/linux-mips/20181223225224.23042-1-hauke@hauke-m.de/ + +Signed-off-by: Ilya Lipnitskiy +Acked-by: Hauke Mehrtens +Signed-off-by: Thomas Bogendoerfer +--- + arch/mips/kernel/proc.c | 122 ++++++++++++++++++++++++++++++++++++++++ + 1 file changed, 122 insertions(+) + +--- a/arch/mips/kernel/proc.c ++++ b/arch/mips/kernel/proc.c +@@ -138,6 +138,128 @@ 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_puts(m, "Options implemented\t:"); ++ if (cpu_has_tlb) ++ seq_puts(m, " tlb"); ++ if (cpu_has_ftlb) ++ seq_puts(m, " ftlb"); ++ if (cpu_has_tlbinv) ++ seq_puts(m, " tlbinv"); ++ if (cpu_has_segments) ++ seq_puts(m, " segments"); ++ if (cpu_has_rixiex) ++ seq_puts(m, " rixiex"); ++ if (cpu_has_ldpte) ++ seq_puts(m, " ldpte"); ++ if (cpu_has_maar) ++ seq_puts(m, " maar"); ++ if (cpu_has_rw_llb) ++ seq_puts(m, " rw_llb"); ++ if (cpu_has_4kex) ++ seq_puts(m, " 4kex"); ++ if (cpu_has_3k_cache) ++ seq_puts(m, " 3k_cache"); ++ if (cpu_has_4k_cache) ++ seq_puts(m, " 4k_cache"); ++ if (cpu_has_tx39_cache) ++ seq_puts(m, " tx39_cache"); ++ if (cpu_has_octeon_cache) ++ seq_puts(m, " octeon_cache"); ++ if (cpu_has_fpu) ++ seq_puts(m, " fpu"); ++ if (cpu_has_32fpr) ++ seq_puts(m, " 32fpr"); ++ if (cpu_has_cache_cdex_p) ++ seq_puts(m, " cache_cdex_p"); ++ if (cpu_has_cache_cdex_s) ++ seq_puts(m, " cache_cdex_s"); ++ if (cpu_has_prefetch) ++ seq_puts(m, " prefetch"); ++ if (cpu_has_mcheck) ++ seq_puts(m, " mcheck"); ++ if (cpu_has_ejtag) ++ seq_puts(m, " ejtag"); ++ if (cpu_has_llsc) ++ seq_puts(m, " llsc"); ++ if (cpu_has_guestctl0ext) ++ seq_puts(m, " guestctl0ext"); ++ if (cpu_has_guestctl1) ++ seq_puts(m, " guestctl1"); ++ if (cpu_has_guestctl2) ++ seq_puts(m, " guestctl2"); ++ if (cpu_has_guestid) ++ seq_puts(m, " guestid"); ++ if (cpu_has_drg) ++ seq_puts(m, " drg"); ++ if (cpu_has_rixi) ++ seq_puts(m, " rixi"); ++ if (cpu_has_lpa) ++ seq_puts(m, " lpa"); ++ if (cpu_has_mvh) ++ seq_puts(m, " mvh"); ++ if (cpu_has_vtag_icache) ++ seq_puts(m, " vtag_icache"); ++ if (cpu_has_dc_aliases) ++ seq_puts(m, " dc_aliases"); ++ if (cpu_has_ic_fills_f_dc) ++ seq_puts(m, " ic_fills_f_dc"); ++ if (cpu_has_pindexed_dcache) ++ seq_puts(m, " pindexed_dcache"); ++ if (cpu_has_userlocal) ++ seq_puts(m, " userlocal"); ++ if (cpu_has_nofpuex) ++ seq_puts(m, " nofpuex"); ++ if (cpu_has_vint) ++ seq_puts(m, " vint"); ++ if (cpu_has_veic) ++ seq_puts(m, " veic"); ++ if (cpu_has_inclusive_pcaches) ++ seq_puts(m, " inclusive_pcaches"); ++ if (cpu_has_perf_cntr_intr_bit) ++ seq_puts(m, " perf_cntr_intr_bit"); ++ if (cpu_has_ufr) ++ seq_puts(m, " ufr"); ++ if (cpu_has_fre) ++ seq_puts(m, " fre"); ++ if (cpu_has_cdmm) ++ seq_puts(m, " cdmm"); ++ if (cpu_has_small_pages) ++ seq_puts(m, " small_pages"); ++ if (cpu_has_nan_legacy) ++ seq_puts(m, " nan_legacy"); ++ if (cpu_has_nan_2008) ++ seq_puts(m, " nan_2008"); ++ if (cpu_has_ebase_wg) ++ seq_puts(m, " ebase_wg"); ++ if (cpu_has_badinstr) ++ seq_puts(m, " badinstr"); ++ if (cpu_has_badinstrp) ++ seq_puts(m, " badinstrp"); ++ if (cpu_has_contextconfig) ++ seq_puts(m, " contextconfig"); ++ if (cpu_has_perf) ++ seq_puts(m, " perf"); ++ if (cpu_has_mac2008_only) ++ seq_puts(m, " mac2008_only"); ++ if (cpu_has_ftlbparex) ++ seq_puts(m, " ftlbparex"); ++ if (cpu_has_gsexcex) ++ seq_puts(m, " gsexcex"); ++ if (cpu_has_shared_ftlb_ram) ++ seq_puts(m, " shared_ftlb_ram"); ++ if (cpu_has_shared_ftlb_entries) ++ seq_puts(m, " shared_ftlb_entries"); ++ if (cpu_has_mipsmt_pertccounters) ++ seq_puts(m, " mipsmt_pertccounters"); ++ if (cpu_has_mmid) ++ seq_puts(m, " mmid"); ++ if (cpu_has_mm_sysad) ++ seq_puts(m, " mm_sysad"); ++ if (cpu_has_mm_full) ++ seq_puts(m, " mm_full"); ++ seq_puts(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/target/linux/generic/backport-6.1/330-v5.16-02-MIPS-Fix-using-smp_processor_id-in-preemptible-in-sh.patch b/target/linux/generic/backport-6.1/330-v5.16-02-MIPS-Fix-using-smp_processor_id-in-preemptible-in-sh.patch new file mode 100644 index 0000000000..6caf7d06d4 --- /dev/null +++ b/target/linux/generic/backport-6.1/330-v5.16-02-MIPS-Fix-using-smp_processor_id-in-preemptible-in-sh.patch @@ -0,0 +1,62 @@ +From 1cab5bd69eb1f995ced2d7576cb15f8a8941fd85 Mon Sep 17 00:00:00 2001 +From: Tiezhu Yang +Date: Thu, 25 Nov 2021 19:39:32 +0800 +Subject: [PATCH 1/1] MIPS: Fix using smp_processor_id() in preemptible in + show_cpuinfo() + +There exists the following issue under DEBUG_PREEMPT: + + BUG: using smp_processor_id() in preemptible [00000000] code: systemd/1 + caller is show_cpuinfo+0x460/0xea0 + ... + Call Trace: + [] show_stack+0x94/0x128 + [] dump_stack_lvl+0x94/0xd8 + [] check_preemption_disabled+0x104/0x110 + [] show_cpuinfo+0x460/0xea0 + [] seq_read_iter+0xfc/0x4f8 + [] new_sync_read+0x110/0x1b8 + [] vfs_read+0x1b4/0x1d0 + [] ksys_read+0xd0/0x110 + [] syscall_common+0x34/0x58 + +We can see the following call trace: + show_cpuinfo() + cpu_has_fpu + current_cpu_data + smp_processor_id() + + $ addr2line -f -e vmlinux 0xffffffff802209c8 + show_cpuinfo + arch/mips/kernel/proc.c:188 + + $ head -188 arch/mips/kernel/proc.c | tail -1 + if (cpu_has_fpu) + + arch/mips/include/asm/cpu-features.h + # define cpu_has_fpu (current_cpu_data.options & MIPS_CPU_FPU) + + arch/mips/include/asm/cpu-info.h + #define current_cpu_data cpu_data[smp_processor_id()] + +Based on the above analysis, fix the issue by using raw_cpu_has_fpu +which calls raw_smp_processor_id() in show_cpuinfo(). + +Fixes: 626bfa037299 ("MIPS: kernel: proc: add CPU option reporting") +Signed-off-by: Tiezhu Yang +Signed-off-by: Thomas Bogendoerfer +--- + arch/mips/kernel/proc.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/arch/mips/kernel/proc.c ++++ b/arch/mips/kernel/proc.c +@@ -166,7 +166,7 @@ static int show_cpuinfo(struct seq_file + seq_puts(m, " tx39_cache"); + if (cpu_has_octeon_cache) + seq_puts(m, " octeon_cache"); +- if (cpu_has_fpu) ++ if (raw_cpu_has_fpu) + seq_puts(m, " fpu"); + if (cpu_has_32fpr) + seq_puts(m, " 32fpr"); diff --git a/target/linux/generic/backport-6.1/331-v5.19-mtd-spinand-Add-support-for-XTX-XT26G0xA.patch b/target/linux/generic/backport-6.1/331-v5.19-mtd-spinand-Add-support-for-XTX-XT26G0xA.patch new file mode 100644 index 0000000000..541d247542 --- /dev/null +++ b/target/linux/generic/backport-6.1/331-v5.19-mtd-spinand-Add-support-for-XTX-XT26G0xA.patch @@ -0,0 +1,186 @@ +From f4c5c7f9d2e5ab005d57826b740b694b042a737c Mon Sep 17 00:00:00 2001 +From: Felix Matouschek +Date: Mon, 18 Apr 2022 15:28:03 +0200 +Subject: [PATCH 1/1] 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. + +Links: + - http://www.xtxtech.com/download/?AId=225 + - https://datasheet.lcsc.com/szlcsc/2005251034_XTX-XT26G01AWSEGA_C558841.pdf +Signed-off-by: Felix Matouschek +Signed-off-by: Miquel Raynal +Link: https://lore.kernel.org/linux-mtd/20220418132803.664103-1-felix@matouschek.org +--- + drivers/mtd/nand/spi/Makefile | 2 +- + drivers/mtd/nand/spi/core.c | 1 + + drivers/mtd/nand/spi/xtx.c | 129 ++++++++++++++++++++++++++++++++++ + include/linux/mtd/spinand.h | 1 + + 4 files changed, 132 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,129 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Author: ++ * Felix Matouschek ++ */ ++ ++#include ++#include ++#include ++ ++#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) ++{ ++ status = status & XT26G0XA_STATUS_ECC_MASK; ++ ++ switch (status) { ++ 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: ++ break; ++ } ++ ++ /* At this point values greater than (2 << 4) are invalid */ ++ if (status > XT26G0XA_STATUS_ECC_UNCOR_ERROR) ++ return -EINVAL; ++ ++ /* (1 << 2) through (7 << 2) are 1-7 corrected errors */ ++ return status >> 2; ++} ++ ++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/target/linux/generic/backport-6.1/344-v5.18-01-phy-marvell-phy-mvebu-a3700-comphy-Remove-port-from-.patch b/target/linux/generic/backport-6.1/344-v5.18-01-phy-marvell-phy-mvebu-a3700-comphy-Remove-port-from-.patch new file mode 100644 index 0000000000..d6c4b4fd1e --- /dev/null +++ b/target/linux/generic/backport-6.1/344-v5.18-01-phy-marvell-phy-mvebu-a3700-comphy-Remove-port-from-.patch @@ -0,0 +1,219 @@ +From 4bf18d5a2dd02db8c5b16a2cfae513510506df5b Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Pali=20Roh=C3=A1r?= +Date: Thu, 3 Feb 2022 22:44:40 +0100 +Subject: [PATCH 1/2] 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 +Signed-off-by: Marek Behún +Reviewed-by: Miquel Raynal +Link: https://lore.kernel.org/r/20220203214444.1508-2-kabel@kernel.org +Signed-off-by: Vinod Koul +--- + 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 + + #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/target/linux/generic/backport-6.1/344-v5.18-02-phy-marvell-phy-mvebu-a3700-comphy-Add-native-kernel.patch b/target/linux/generic/backport-6.1/344-v5.18-02-phy-marvell-phy-mvebu-a3700-comphy-Add-native-kernel.patch new file mode 100644 index 0000000000..4593d14bfe --- /dev/null +++ b/target/linux/generic/backport-6.1/344-v5.18-02-phy-marvell-phy-mvebu-a3700-comphy-Add-native-kernel.patch @@ -0,0 +1,1552 @@ +From 934337080c6c59b75db76b180b509f218640ad48 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Pali=20Roh=C3=A1r?= +Date: Thu, 3 Feb 2022 22:44:41 +0100 +Subject: [PATCH 2/2] 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. + +[ 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: Pali Rohár +Acked-by: Miquel Raynal +Signed-off-by: Marek Behún +Link: https://lore.kernel.org/r/20220203214444.1508-3-kabel@kernel.org +Signed-off-by: Vinod Koul +--- + drivers/phy/marvell/phy-mvebu-a3700-comphy.c | 1332 ++++++++++++++++-- + 1 file changed, 1215 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 + * Miquèl Raynal ++ * Pali Rohár ++ * Marek Behún + * + * 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 ++ * and Marek Behún . + */ + +-#include ++#include ++#include + #include + #include + #include +@@ -18,103 +22,1118 @@ + #include + #include + #include ++#include + +-#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; ++ ++ val = readl(addr); ++ val = (val & ~mask) | (data & mask); ++ writel(val, addr); ++} + +- arm_smccc_smc(function, lane, mode, 0, 0, 0, 0, 0, &res); +- ret = res.a0; ++static inline void comphy_reg_set16(void __iomem *addr, u16 data, u16 mask) ++{ ++ u16 val; + +- switch (ret) { +- case SMCCC_RET_SUCCESS: +- return 0; +- case SMCCC_RET_NOT_SUPPORTED: +- return -EOPNOTSUPP; ++ 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; ++} ++ ++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 value of the DFE_UPDATE_EN already is 0x3F, because it is the ++ * default value after reset of the PHY. ++ */ ++ ++ /* ++ * 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. 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); ++ ++ /* ++ * 15. 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); ++ ++ /* ++ * 16. 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; ++ } ++ ++ /* ++ * 17. Set COMPHY input port PIN_TX_IDLE=0 ++ */ ++ comphy_periph_reg_set(lane, COMPHY_PHY_CFG1, 0x0, PIN_TX_IDLE_BIT); ++ ++ /* ++ * 18. 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; + } + +-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; ++} ++ ++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; ++} ++ ++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 +1141,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 +1151,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 +1185,77 @@ 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: ++ /* ++ * The USB3 MAC sets the USB3 PHY to low state, so we do not ++ * need to power off USB3 PHY again. ++ */ ++ break; ++ ++ case PHY_MODE_SATA: ++ mvebu_a3700_comphy_sata_power_off(lane); ++ break; ++ ++ case PHY_MODE_ETHERNET: ++ mvebu_a3700_comphy_ethernet_power_off(lane); ++ break; ++ ++ case PHY_MODE_PCIE: ++ mvebu_a3700_comphy_pcie_power_off(lane); ++ break; ++ ++ default: ++ dev_err(lane->dev, "invalid COMPHY mode\n"); ++ return -EINVAL; ++ } ++ ++ return 0; + } + + 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 +1279,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 +1362,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,15 +1380,26 @@ 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, + mvebu_a3700_comphy_xlate); ++ + return PTR_ERR_OR_ZERO(provider); + } + +@@ -323,5 +1419,7 @@ static struct platform_driver mvebu_a370 + module_platform_driver(mvebu_a3700_comphy_driver); + + MODULE_AUTHOR("Miquèl Raynal "); ++MODULE_AUTHOR("Pali Rohár "); ++MODULE_AUTHOR("Marek Behún "); + MODULE_DESCRIPTION("Common PHY driver for A3700"); + MODULE_LICENSE("GPL v2"); diff --git a/target/linux/generic/backport-6.1/345-v5.17-arm64-dts-marvell-armada-37xx-Add-xtal-clock-to-comp.patch b/target/linux/generic/backport-6.1/345-v5.17-arm64-dts-marvell-armada-37xx-Add-xtal-clock-to-comp.patch new file mode 100644 index 0000000000..03b6a5754d --- /dev/null +++ b/target/linux/generic/backport-6.1/345-v5.17-arm64-dts-marvell-armada-37xx-Add-xtal-clock-to-comp.patch @@ -0,0 +1,32 @@ +From 73a78b6130d9e13daca22b86ad52f063b9403e03 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Pali=20Roh=C3=A1r?= +Date: Wed, 8 Dec 2021 03:40:35 +0100 +Subject: [PATCH 1/1] 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 +Signed-off-by: Marek Behún +Signed-off-by: Gregory CLEMENT +--- + 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/target/linux/generic/backport-6.1/346-v5.18-01-Revert-ata-ahci-mvebu-Make-SATA-PHY-optional-for-Arm.patch b/target/linux/generic/backport-6.1/346-v5.18-01-Revert-ata-ahci-mvebu-Make-SATA-PHY-optional-for-Arm.patch new file mode 100644 index 0000000000..b254e7c0b3 --- /dev/null +++ b/target/linux/generic/backport-6.1/346-v5.18-01-Revert-ata-ahci-mvebu-Make-SATA-PHY-optional-for-Arm.patch @@ -0,0 +1,64 @@ +From ee995101fde67f85a3cd4c74f4f92fc4592e726b Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Pali=20Roh=C3=A1r?= +Date: Thu, 3 Feb 2022 22:44:42 +0100 +Subject: [PATCH 1/3] 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 +Signed-off-by: Marek Behún +Acked-by: Miquel Raynal +Acked-by: Damien Le Moal +Link: https://lore.kernel.org/r/20220203214444.1508-4-kabel@kernel.org +Signed-off-by: Vinod Koul +--- + 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/target/linux/generic/backport-6.1/346-v5.18-02-Revert-usb-host-xhci-mvebu-make-USB-3.0-PHY-optional.patch b/target/linux/generic/backport-6.1/346-v5.18-02-Revert-usb-host-xhci-mvebu-make-USB-3.0-PHY-optional.patch new file mode 100644 index 0000000000..1e8afb7bbf --- /dev/null +++ b/target/linux/generic/backport-6.1/346-v5.18-02-Revert-usb-host-xhci-mvebu-make-USB-3.0-PHY-optional.patch @@ -0,0 +1,166 @@ +From 8e10548f7f4814e530857d2049d6af6bc78add53 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Pali=20Roh=C3=A1r?= +Date: Thu, 3 Feb 2022 22:44:43 +0100 +Subject: [PATCH 2/3] 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 +Signed-off-by: Marek Behún +Acked-by: Miquel Raynal +Acked-by: Greg Kroah-Hartman +Link: https://lore.kernel.org/r/20220203214444.1508-5-kabel@kernel.org +Signed-off-by: Vinod Koul +--- + 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 + #include + #include +-#include + + #include + #include +@@ -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/target/linux/generic/backport-6.1/346-v5.18-03-Revert-PCI-aardvark-Fix-initialization-with-old-Marv.patch b/target/linux/generic/backport-6.1/346-v5.18-03-Revert-PCI-aardvark-Fix-initialization-with-old-Marv.patch new file mode 100644 index 0000000000..fcfb02d35a --- /dev/null +++ b/target/linux/generic/backport-6.1/346-v5.18-03-Revert-PCI-aardvark-Fix-initialization-with-old-Marv.patch @@ -0,0 +1,39 @@ +From 9a4556dad7bd0a6b8339cb72e169f5c76f2af6f1 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Pali=20Roh=C3=A1r?= +Date: Thu, 3 Feb 2022 22:44:44 +0100 +Subject: [PATCH 3/3] 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 +Signed-off-by: Marek Behún +Acked-by: Miquel Raynal +Acked-by: Lorenzo Pieralisi +Link: https://lore.kernel.org/r/20220203214444.1508-6-kabel@kernel.org +Signed-off-by: Vinod Koul +--- + 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 +@@ -1642,9 +1642,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/target/linux/generic/backport-6.1/347-v6.0-phy-marvell-phy-mvebu-a3700-comphy-Remove-broken-res.patch b/target/linux/generic/backport-6.1/347-v6.0-phy-marvell-phy-mvebu-a3700-comphy-Remove-broken-res.patch new file mode 100644 index 0000000000..a2c897b7a9 --- /dev/null +++ b/target/linux/generic/backport-6.1/347-v6.0-phy-marvell-phy-mvebu-a3700-comphy-Remove-broken-res.patch @@ -0,0 +1,194 @@ +From 0a6fc70d76bddf98278af2ac000379c82aec8f11 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Pali=20Roh=C3=A1r?= +Date: Mon, 29 Aug 2022 10:30:46 +0200 +Subject: [PATCH] phy: marvell: phy-mvebu-a3700-comphy: Remove broken reset + support +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Reset support for SATA PHY is somehow broken and after calling it, kernel +is not able to detect and initialize SATA disk Samsung SSD 850 EMT0 [1]. + +Reset support was introduced in commit 934337080c6c ("phy: marvell: +phy-mvebu-a3700-comphy: Add native kernel implementation") as part of +complete rewrite of this driver. v1 patch series of that commit [2] did +not contain reset support and was tested that is working fine with +Ethernet, SATA and USB PHYs without issues too. + +So for now remove broken reset support and change implementation of +power_off callback to power off all functions on specified lane (and not +only selected function) because during startup kernel does not know which +function was selected and configured by bootloader. Same logic was used +also in v1 patch series of that commit. + +This change fixes issues with initialization of SATA disk Samsung SSD 850 +and disk is working again, like before mentioned commit. + +Once problem with PHY reset callback is solved its functionality could be +re-introduced. But for now it is unknown why it does not work. + +[1] - https://lore.kernel.org/r/20220531124159.3e4lgn2v462irbtz@shindev/ +[2] - https://lore.kernel.org/r/20211028184242.22105-1-kabel@kernel.org/ + +Reported-by: Shinichiro Kawasaki +Fixes: 934337080c6c ("phy: marvell: phy-mvebu-a3700-comphy: Add native kernel implementation") +Cc: stable@vger.kernel.org # v5.18+ +Signed-off-by: Pali Rohár +Tested-by: Shinichiro Kawasaki +Link: https://lore.kernel.org/r/20220829083046.15082-1-pali@kernel.org +Signed-off-by: Vinod Koul +--- + drivers/phy/marvell/phy-mvebu-a3700-comphy.c | 87 ++++---------------- + 1 file changed, 17 insertions(+), 70 deletions(-) + +--- a/drivers/phy/marvell/phy-mvebu-a3700-comphy.c ++++ b/drivers/phy/marvell/phy-mvebu-a3700-comphy.c +@@ -274,7 +274,6 @@ struct mvebu_a3700_comphy_lane { + int submode; + bool invert_tx; + bool invert_rx; +- bool needs_reset; + }; + + struct gbe_phy_init_data_fix { +@@ -1097,40 +1096,12 @@ mvebu_a3700_comphy_pcie_power_off(struct + 0x0, PU_PLL_BIT | PU_RX_BIT | PU_TX_BIT); + } + +-static int mvebu_a3700_comphy_reset(struct phy *phy) ++static void mvebu_a3700_comphy_usb3_power_off(struct mvebu_a3700_comphy_lane *lane) + { +- 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; ++ /* ++ * The USB3 MAC sets the USB3 PHY to low state, so we do not ++ * need to power off USB3 PHY again. ++ */ + } + + static bool mvebu_a3700_comphy_check_mode(int lane, +@@ -1171,10 +1142,6 @@ static int mvebu_a3700_comphy_set_mode(s + (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; +@@ -1185,7 +1152,6 @@ 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); +- int ret; + + if (!mvebu_a3700_comphy_check_mode(lane->id, lane->mode, + lane->submode)) { +@@ -1193,14 +1159,6 @@ static int mvebu_a3700_comphy_power_on(s + 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); +@@ -1224,38 +1182,28 @@ static int mvebu_a3700_comphy_power_off( + { + struct mvebu_a3700_comphy_lane *lane = phy_get_drvdata(phy); + +- switch (lane->mode) { +- case PHY_MODE_USB_HOST_SS: +- /* +- * The USB3 MAC sets the USB3 PHY to low state, so we do not +- * need to power off USB3 PHY again. +- */ +- break; +- +- case PHY_MODE_SATA: +- mvebu_a3700_comphy_sata_power_off(lane); +- break; +- +- case PHY_MODE_ETHERNET: ++ switch (lane->id) { ++ case 0: ++ mvebu_a3700_comphy_usb3_power_off(lane); + mvebu_a3700_comphy_ethernet_power_off(lane); +- break; +- +- case PHY_MODE_PCIE: ++ return 0; ++ case 1: + mvebu_a3700_comphy_pcie_power_off(lane); +- break; +- ++ mvebu_a3700_comphy_ethernet_power_off(lane); ++ return 0; ++ case 2: ++ mvebu_a3700_comphy_usb3_power_off(lane); ++ mvebu_a3700_comphy_sata_power_off(lane); ++ return 0; + default: + dev_err(lane->dev, "invalid COMPHY mode\n"); + return -EINVAL; + } +- +- return 0; + } + + 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, + }; +@@ -1393,8 +1341,7 @@ static int mvebu_a3700_comphy_probe(stru + * To avoid relying on the bootloader/firmware configuration, + * power off all comphys. + */ +- mvebu_a3700_comphy_reset(phy); +- lane->needs_reset = false; ++ mvebu_a3700_comphy_power_off(phy); + } + + provider = devm_of_phy_provider_register(&pdev->dev, diff --git a/target/linux/generic/backport-6.1/400-v5.19-mtd-call-of_platform_populate-for-MTD-partitions.patch b/target/linux/generic/backport-6.1/400-v5.19-mtd-call-of_platform_populate-for-MTD-partitions.patch new file mode 100644 index 0000000000..1f3aae13b4 --- /dev/null +++ b/target/linux/generic/backport-6.1/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?= +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 +Signed-off-by: Miquel Raynal +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 + #include + #include ++#include + + #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/target/linux/generic/backport-6.1/401-v6.0-mtd-parsers-add-support-for-Sercomm-partitions.patch b/target/linux/generic/backport-6.1/401-v6.0-mtd-parsers-add-support-for-Sercomm-partitions.patch new file mode 100644 index 0000000000..113a96ad42 --- /dev/null +++ b/target/linux/generic/backport-6.1/401-v6.0-mtd-parsers-add-support-for-Sercomm-partitions.patch @@ -0,0 +1,302 @@ +From 9b78ef0c7997052e9eaa0f7a4513d546fa17358c Mon Sep 17 00:00:00 2001 +From: Mikhail Zhilkin +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 +Signed-off-by: Mikhail Zhilkin +Signed-off-by: Miquel Raynal +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 ++#include ++#include ++#include ++#include ++ ++#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 "); ++MODULE_AUTHOR("Mikhail Zhilkin "); ++MODULE_DESCRIPTION("Sercomm partition parser"); diff --git a/target/linux/generic/backport-6.1/402-v6.0-mtd-next-mtd-core-introduce-of-support-for-dynamic-partitions.patch b/target/linux/generic/backport-6.1/402-v6.0-mtd-next-mtd-core-introduce-of-support-for-dynamic-partitions.patch new file mode 100644 index 0000000000..8b8e478b00 --- /dev/null +++ b/target/linux/generic/backport-6.1/402-v6.0-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 +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 +Signed-off-by: Miquel Raynal +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/target/linux/generic/backport-6.1/403-v6.1-mtd-allow-getting-MTD-device-associated-with-a-speci.patch b/target/linux/generic/backport-6.1/403-v6.1-mtd-allow-getting-MTD-device-associated-with-a-speci.patch new file mode 100644 index 0000000000..be1aa9cfb8 --- /dev/null +++ b/target/linux/generic/backport-6.1/403-v6.1-mtd-allow-getting-MTD-device-associated-with-a-speci.patch @@ -0,0 +1,72 @@ +From b0321721be50b80c03a51866a94fde4f94690e18 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= +Date: Wed, 15 Jun 2022 21:42:59 +0200 +Subject: [PATCH] mtd: allow getting MTD device associated with a specific DT + node +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +MTD subsystem API allows interacting with MTD devices (e.g. reading, +writing, handling bad blocks). So far a random driver could get MTD +device only by its name (get_mtd_device_nm()). This change allows +getting them also by a DT node. + +This API is required for drivers handling DT defined MTD partitions in a +specific way (e.g. U-Boot (sub)partition with environment variables). + +Signed-off-by: Rafał Miłecki +Acked-by: Miquel Raynal +Signed-off-by: Srinivas Kandagatla +--- + drivers/mtd/mtdcore.c | 28 ++++++++++++++++++++++++++++ + include/linux/mtd/mtd.h | 1 + + 2 files changed, 29 insertions(+) + +--- a/drivers/mtd/mtdcore.c ++++ b/drivers/mtd/mtdcore.c +@@ -1227,6 +1227,34 @@ int __get_mtd_device(struct mtd_info *mt + EXPORT_SYMBOL_GPL(__get_mtd_device); + + /** ++ * of_get_mtd_device_by_node - obtain an MTD device associated with a given node ++ * ++ * @np: device tree node ++ */ ++struct mtd_info *of_get_mtd_device_by_node(struct device_node *np) ++{ ++ struct mtd_info *mtd = NULL; ++ struct mtd_info *tmp; ++ int err; ++ ++ mutex_lock(&mtd_table_mutex); ++ ++ err = -EPROBE_DEFER; ++ mtd_for_each_device(tmp) { ++ if (mtd_get_of_node(tmp) == np) { ++ mtd = tmp; ++ err = __get_mtd_device(mtd); ++ break; ++ } ++ } ++ ++ mutex_unlock(&mtd_table_mutex); ++ ++ return err ? ERR_PTR(err) : mtd; ++} ++EXPORT_SYMBOL_GPL(of_get_mtd_device_by_node); ++ ++/** + * get_mtd_device_nm - obtain a validated handle for an MTD device by + * device name + * @name: MTD device name to open +--- a/include/linux/mtd/mtd.h ++++ b/include/linux/mtd/mtd.h +@@ -682,6 +682,7 @@ extern int mtd_device_unregister(struct + extern struct mtd_info *get_mtd_device(struct mtd_info *mtd, int num); + extern int __get_mtd_device(struct mtd_info *mtd); + extern void __put_mtd_device(struct mtd_info *mtd); ++extern struct mtd_info *of_get_mtd_device_by_node(struct device_node *np); + extern struct mtd_info *get_mtd_device_nm(const char *name); + extern void put_mtd_device(struct mtd_info *mtd); + diff --git a/target/linux/generic/backport-6.1/404-v6.0-mtd-core-check-partition-before-dereference.patch b/target/linux/generic/backport-6.1/404-v6.0-mtd-core-check-partition-before-dereference.patch new file mode 100644 index 0000000000..e45e2ab48e --- /dev/null +++ b/target/linux/generic/backport-6.1/404-v6.0-mtd-core-check-partition-before-dereference.patch @@ -0,0 +1,30 @@ +From 7ec4cdb321738d44ae5d405e7b6ac73dfbf99caa Mon Sep 17 00:00:00 2001 +From: Tetsuo Handa +Date: Mon, 25 Jul 2022 22:49:25 +0900 +Subject: [PATCH] mtd: core: check partition before dereference + +syzbot is reporting NULL pointer dereference at mtd_check_of_node() [1], +for mtdram test device (CONFIG_MTD_MTDRAM) is not partition. + +Link: https://syzkaller.appspot.com/bug?extid=fe013f55a2814a9e8cfd [1] +Reported-by: syzbot +Reported-by: kernel test robot +Fixes: ad9b10d1eaada169 ("mtd: core: introduce of support for dynamic partitions") +Signed-off-by: Tetsuo Handa +CC: stable@vger.kernel.org +Signed-off-by: Richard Weinberger +--- + drivers/mtd/mtdcore.c | 2 ++ + 1 file changed, 2 insertions(+) + +--- a/drivers/mtd/mtdcore.c ++++ b/drivers/mtd/mtdcore.c +@@ -577,6 +577,8 @@ static void mtd_check_of_node(struct mtd + return; + + /* Check if a partitions node exist */ ++ if (!mtd_is_partition(mtd)) ++ return; + parent = mtd->parent; + parent_dn = dev_of_node(&parent->dev); + if (!parent_dn) diff --git a/target/linux/generic/backport-6.1/405-v6.1-mtd-core-add-missing-of_node_get-in-dynamic-partitio.patch b/target/linux/generic/backport-6.1/405-v6.1-mtd-core-add-missing-of_node_get-in-dynamic-partitio.patch new file mode 100644 index 0000000000..9399a00aa1 --- /dev/null +++ b/target/linux/generic/backport-6.1/405-v6.1-mtd-core-add-missing-of_node_get-in-dynamic-partitio.patch @@ -0,0 +1,101 @@ +From 12b58961de0bd88b3c7dfa5d21f6d67f4678b780 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= +Date: Tue, 18 Oct 2022 07:18:22 +0200 +Subject: [PATCH] mtd: core: add missing of_node_get() in dynamic partitions + code +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +This fixes unbalanced of_node_put(): +[ 1.078910] 6 cmdlinepart partitions found on MTD device gpmi-nand +[ 1.085116] Creating 6 MTD partitions on "gpmi-nand": +[ 1.090181] 0x000000000000-0x000008000000 : "nandboot" +[ 1.096952] 0x000008000000-0x000009000000 : "nandfit" +[ 1.103547] 0x000009000000-0x00000b000000 : "nandkernel" +[ 1.110317] 0x00000b000000-0x00000c000000 : "nanddtb" +[ 1.115525] ------------[ cut here ]------------ +[ 1.120141] refcount_t: addition on 0; use-after-free. +[ 1.125328] WARNING: CPU: 0 PID: 1 at lib/refcount.c:25 refcount_warn_saturate+0xdc/0x148 +[ 1.133528] Modules linked in: +[ 1.136589] CPU: 0 PID: 1 Comm: swapper/0 Not tainted 6.0.0-rc7-next-20220930-04543-g8cf3f7 +[ 1.146342] Hardware name: Freescale i.MX8DXL DDR3L EVK (DT) +[ 1.151999] pstate: 600000c5 (nZCv daIF -PAN -UAO -TCO -DIT -SSBS BTYPE=--) +[ 1.158965] pc : refcount_warn_saturate+0xdc/0x148 +[ 1.163760] lr : refcount_warn_saturate+0xdc/0x148 +[ 1.168556] sp : ffff800009ddb080 +[ 1.171866] x29: ffff800009ddb080 x28: ffff800009ddb35a x27: 0000000000000002 +[ 1.179015] x26: ffff8000098b06ad x25: ffffffffffffffff x24: ffff0a00ffffff05 +[ 1.186165] x23: ffff00001fdf6470 x22: ffff800009ddb367 x21: 0000000000000000 +[ 1.193314] x20: ffff00001fdfebe8 x19: ffff00001fdfec50 x18: ffffffffffffffff +[ 1.200464] x17: 0000000000000000 x16: 0000000000000118 x15: 0000000000000004 +[ 1.207614] x14: 0000000000000fff x13: ffff800009bca248 x12: 0000000000000003 +[ 1.214764] x11: 00000000ffffefff x10: c0000000ffffefff x9 : 4762cb2ccb52de00 +[ 1.221914] x8 : 4762cb2ccb52de00 x7 : 205d313431303231 x6 : 312e31202020205b +[ 1.229063] x5 : ffff800009d55c1f x4 : 0000000000000001 x3 : 0000000000000000 +[ 1.236213] x2 : 0000000000000000 x1 : ffff800009954be6 x0 : 000000000000002a +[ 1.243365] Call trace: +[ 1.245806] refcount_warn_saturate+0xdc/0x148 +[ 1.250253] kobject_get+0x98/0x9c +[ 1.253658] of_node_get+0x20/0x34 +[ 1.257072] of_fwnode_get+0x3c/0x54 +[ 1.260652] fwnode_get_nth_parent+0xd8/0xf4 +[ 1.264926] fwnode_full_name_string+0x3c/0xb4 +[ 1.269373] device_node_string+0x498/0x5b4 +[ 1.273561] pointer+0x41c/0x5d0 +[ 1.276793] vsnprintf+0x4d8/0x694 +[ 1.280198] vprintk_store+0x164/0x528 +[ 1.283951] vprintk_emit+0x98/0x164 +[ 1.287530] vprintk_default+0x44/0x6c +[ 1.291284] vprintk+0xf0/0x134 +[ 1.294428] _printk+0x54/0x7c +[ 1.297486] of_node_release+0xe8/0x128 +[ 1.301326] kobject_put+0x98/0xfc +[ 1.304732] of_node_put+0x1c/0x28 +[ 1.308137] add_mtd_device+0x484/0x6d4 +[ 1.311977] add_mtd_partitions+0xf0/0x1d0 +[ 1.316078] parse_mtd_partitions+0x45c/0x518 +[ 1.320439] mtd_device_parse_register+0xb0/0x274 +[ 1.325147] gpmi_nand_probe+0x51c/0x650 +[ 1.329074] platform_probe+0xa8/0xd0 +[ 1.332740] really_probe+0x130/0x334 +[ 1.336406] __driver_probe_device+0xb4/0xe0 +[ 1.340681] driver_probe_device+0x3c/0x1f8 +[ 1.344869] __driver_attach+0xdc/0x1a4 +[ 1.348708] bus_for_each_dev+0x80/0xcc +[ 1.352548] driver_attach+0x24/0x30 +[ 1.356127] bus_add_driver+0x108/0x1f4 +[ 1.359967] driver_register+0x78/0x114 +[ 1.363807] __platform_driver_register+0x24/0x30 +[ 1.368515] gpmi_nand_driver_init+0x1c/0x28 +[ 1.372798] do_one_initcall+0xbc/0x238 +[ 1.376638] do_initcall_level+0x94/0xb4 +[ 1.380565] do_initcalls+0x54/0x94 +[ 1.384058] do_basic_setup+0x1c/0x28 +[ 1.387724] kernel_init_freeable+0x110/0x188 +[ 1.392084] kernel_init+0x20/0x1a0 +[ 1.395578] ret_from_fork+0x10/0x20 +[ 1.399157] ---[ end trace 0000000000000000 ]--- +[ 1.403782] ------------[ cut here ]------------ + +Reported-by: Han Xu +Fixes: ad9b10d1eaada169 ("mtd: core: introduce of support for dynamic partitions") +Signed-off-by: Rafał Miłecki +Tested-by: Han Xu +Signed-off-by: Miquel Raynal +Link: https://lore.kernel.org/linux-mtd/20221018051822.28685-1-zajec5@gmail.com +--- + drivers/mtd/mtdcore.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/drivers/mtd/mtdcore.c ++++ b/drivers/mtd/mtdcore.c +@@ -580,7 +580,7 @@ static void mtd_check_of_node(struct mtd + if (!mtd_is_partition(mtd)) + return; + parent = mtd->parent; +- parent_dn = dev_of_node(&parent->dev); ++ parent_dn = of_node_get(dev_of_node(&parent->dev)); + if (!parent_dn) + return; + diff --git a/target/linux/generic/backport-6.1/406-v6.2-0001-mtd-core-simplify-a-bit-code-find-partition-matching.patch b/target/linux/generic/backport-6.1/406-v6.2-0001-mtd-core-simplify-a-bit-code-find-partition-matching.patch new file mode 100644 index 0000000000..7e9645ea98 --- /dev/null +++ b/target/linux/generic/backport-6.1/406-v6.2-0001-mtd-core-simplify-a-bit-code-find-partition-matching.patch @@ -0,0 +1,65 @@ +From 63db0cb35e1cb3b3c134906d1062f65513fdda2d Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= +Date: Tue, 4 Oct 2022 10:37:09 +0200 +Subject: [PATCH] mtd: core: simplify (a bit) code find partition-matching + dynamic OF node +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +1. Don't hardcode "partition-" string twice +2. Use simpler logic & use ->name to avoid of_property_read_string() +3. Use mtd_get_of_node() helper + +Cc: Christian Marangi +Signed-off-by: Rafał Miłecki +Signed-off-by: Miquel Raynal +Link: https://lore.kernel.org/linux-mtd/20221004083710.27704-1-zajec5@gmail.com +--- + drivers/mtd/mtdcore.c | 16 +++++++--------- + 1 file changed, 7 insertions(+), 9 deletions(-) + +--- a/drivers/mtd/mtdcore.c ++++ b/drivers/mtd/mtdcore.c +@@ -569,18 +569,16 @@ static void mtd_check_of_node(struct 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)) ++ if (mtd_get_of_node(mtd)) + return; + + /* Check if a partitions node exist */ + if (!mtd_is_partition(mtd)) + return; +- parent = mtd->parent; +- parent_dn = of_node_get(dev_of_node(&parent->dev)); ++ parent_dn = of_node_get(mtd_get_of_node(mtd->parent)); + if (!parent_dn) + return; + +@@ -593,15 +591,15 @@ static void mtd_check_of_node(struct mtd + + /* 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-")) ++ if (!of_node_name_prefix(mtd_dn, prefix)) + continue; + + /* Label have priority. Check that first */ +- if (of_property_read_string(mtd_dn, "label", &pname)) { +- of_property_read_string(mtd_dn, "name", &pname); ++ if (!of_property_read_string(mtd_dn, "label", &pname)) { ++ offset = 0; ++ } else { ++ pname = mtd_dn->name; + offset = prefix_len; + } + diff --git a/target/linux/generic/backport-6.1/406-v6.2-0002-mtd-core-try-to-find-OF-node-for-every-MTD-partition.patch b/target/linux/generic/backport-6.1/406-v6.2-0002-mtd-core-try-to-find-OF-node-for-every-MTD-partition.patch new file mode 100644 index 0000000000..48a7c13cd0 --- /dev/null +++ b/target/linux/generic/backport-6.1/406-v6.2-0002-mtd-core-try-to-find-OF-node-for-every-MTD-partition.patch @@ -0,0 +1,84 @@ +From ddb8cefb7af288950447ca6eeeafb09977dab56f Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= +Date: Tue, 4 Oct 2022 10:37:10 +0200 +Subject: [PATCH] mtd: core: try to find OF node for every MTD partition +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +So far this feature was limited to the top-level "nvmem-cells" node. +There are multiple parsers creating partitions and subpartitions +dynamically. Extend that code to handle them too. + +This allows finding partition-* node for every MTD (sub)partition. + +Random example: + +partitions { + compatible = "brcm,bcm947xx-cfe-partitions"; + + partition-firmware { + compatible = "brcm,trx"; + + partition-loader { + }; + }; +}; + +Cc: Christian Marangi +Signed-off-by: Rafał Miłecki +Signed-off-by: Miquel Raynal +Link: https://lore.kernel.org/linux-mtd/20221004083710.27704-2-zajec5@gmail.com +--- + drivers/mtd/mtdcore.c | 18 ++++++------------ + 1 file changed, 6 insertions(+), 12 deletions(-) + +--- a/drivers/mtd/mtdcore.c ++++ b/drivers/mtd/mtdcore.c +@@ -569,20 +569,22 @@ static void mtd_check_of_node(struct mtd + struct device_node *partitions, *parent_dn, *mtd_dn = NULL; + const char *pname, *prefix = "partition-"; + int plen, mtd_name_len, offset, prefix_len; +- bool found = false; + + /* Check if MTD already has a device node */ + if (mtd_get_of_node(mtd)) + return; + +- /* Check if a partitions node exist */ + if (!mtd_is_partition(mtd)) + return; ++ + parent_dn = of_node_get(mtd_get_of_node(mtd->parent)); + if (!parent_dn) + return; + +- partitions = of_get_child_by_name(parent_dn, "partitions"); ++ if (mtd_is_partition(mtd->parent)) ++ partitions = of_node_get(parent_dn); ++ else ++ partitions = of_get_child_by_name(parent_dn, "partitions"); + if (!partitions) + goto exit_parent; + +@@ -606,19 +608,11 @@ static void mtd_check_of_node(struct mtd + plen = strlen(pname) - offset; + if (plen == mtd_name_len && + !strncmp(mtd->name, pname + offset, plen)) { +- found = true; ++ mtd_set_of_node(mtd, mtd_dn); + 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); diff --git a/target/linux/generic/backport-6.1/407-v5.17-mtd-parsers-qcom-Don-t-print-error-message-on-EPROBE.patch b/target/linux/generic/backport-6.1/407-v5.17-mtd-parsers-qcom-Don-t-print-error-message-on-EPROBE.patch new file mode 100644 index 0000000000..0efad99157 --- /dev/null +++ b/target/linux/generic/backport-6.1/407-v5.17-mtd-parsers-qcom-Don-t-print-error-message-on-EPROBE.patch @@ -0,0 +1,32 @@ +From 26bccc9671ba5e01f7153addbe94e7dc3f677375 Mon Sep 17 00:00:00 2001 +From: Bryan O'Donoghue +Date: Mon, 3 Jan 2022 03:03:16 +0000 +Subject: [PATCH 13/14] mtd: parsers: qcom: Don't print error message on + -EPROBE_DEFER + +Its possible for the main smem driver to not be loaded by the time we come +along to parse the smem partition description but, this is a perfectly +normal thing. + +No need to print out an error message in this case. + +Signed-off-by: Bryan O'Donoghue +Reviewed-by: Manivannan Sadhasivam +Signed-off-by: Miquel Raynal +Link: https://lore.kernel.org/linux-mtd/20220103030316.58301-3-bryan.odonoghue@linaro.org +--- + drivers/mtd/parsers/qcomsmempart.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +--- a/drivers/mtd/parsers/qcomsmempart.c ++++ b/drivers/mtd/parsers/qcomsmempart.c +@@ -75,7 +75,8 @@ static int parse_qcomsmem_part(struct mt + pr_debug("Parsing partition table info from SMEM\n"); + ptable = qcom_smem_get(SMEM_APPS, SMEM_AARM_PARTITION_TABLE, &len); + if (IS_ERR(ptable)) { +- pr_err("Error reading partition table header\n"); ++ if (PTR_ERR(ptable) != -EPROBE_DEFER) ++ pr_err("Error reading partition table header\n"); + return PTR_ERR(ptable); + } + diff --git a/target/linux/generic/backport-6.1/410-v5.18-mtd-parsers-trx-allow-to-use-on-MediaTek-MIPS-SoCs.patch b/target/linux/generic/backport-6.1/410-v5.18-mtd-parsers-trx-allow-to-use-on-MediaTek-MIPS-SoCs.patch new file mode 100644 index 0000000000..5c49841760 --- /dev/null +++ b/target/linux/generic/backport-6.1/410-v5.18-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 +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 +Signed-off-by: Miquel Raynal +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/target/linux/generic/backport-6.1/420-v5.19-02-mtd-spinand-gigadevice-add-support-for-GD5FxGQ4xExxG.patch b/target/linux/generic/backport-6.1/420-v5.19-02-mtd-spinand-gigadevice-add-support-for-GD5FxGQ4xExxG.patch new file mode 100644 index 0000000000..181c912fbf --- /dev/null +++ b/target/linux/generic/backport-6.1/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 +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 +Signed-off-by: Miquel Raynal +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/target/linux/generic/backport-6.1/420-v5.19-03-mtd-spinand-gigadevice-add-support-for-GD5F1GQ5RExxG.patch b/target/linux/generic/backport-6.1/420-v5.19-03-mtd-spinand-gigadevice-add-support-for-GD5F1GQ5RExxG.patch new file mode 100644 index 0000000000..3a1cc9efcf --- /dev/null +++ b/target/linux/generic/backport-6.1/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 +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 +Signed-off-by: Miquel Raynal +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/target/linux/generic/backport-6.1/420-v5.19-04-mtd-spinand-gigadevice-add-support-for-GD5F-2-4-GQ5x.patch b/target/linux/generic/backport-6.1/420-v5.19-04-mtd-spinand-gigadevice-add-support-for-GD5F-2-4-GQ5x.patch new file mode 100644 index 0000000000..cee9d9db3e --- /dev/null +++ b/target/linux/generic/backport-6.1/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 +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 +Signed-off-by: Miquel Raynal +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/target/linux/generic/backport-6.1/420-v5.19-05-mtd-spinand-gigadevice-add-support-for-GD5FxGM7xExxG.patch b/target/linux/generic/backport-6.1/420-v5.19-05-mtd-spinand-gigadevice-add-support-for-GD5FxGM7xExxG.patch new file mode 100644 index 0000000000..d63113e1a6 --- /dev/null +++ b/target/linux/generic/backport-6.1/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 +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 +Signed-off-by: Miquel Raynal +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/target/linux/generic/backport-6.1/421-v6.2-mtd-parsers-add-TP-Link-SafeLoader-partitions-table-.patch b/target/linux/generic/backport-6.1/421-v6.2-mtd-parsers-add-TP-Link-SafeLoader-partitions-table-.patch new file mode 100644 index 0000000000..9f543365a5 --- /dev/null +++ b/target/linux/generic/backport-6.1/421-v6.2-mtd-parsers-add-TP-Link-SafeLoader-partitions-table-.patch @@ -0,0 +1,229 @@ +From aec4d5f5ffd0f0092bd9dc21ea90e0bc237d4b74 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= +Date: Sat, 15 Oct 2022 11:29:50 +0200 +Subject: [PATCH] mtd: parsers: add TP-Link SafeLoader partitions table parser +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +This parser deals with most TP-Link home routers. It reads info about +partitions and registers them in the MTD subsystem. + +Example from TP-Link Archer C5 V2: + +spi-nor spi0.0: s25fl128s1 (16384 Kbytes) +15 tplink-safeloader partitions found on MTD device spi0.0 +Creating 15 MTD partitions on "spi0.0": +0x000000000000-0x000000040000 : "fs-uboot" +0x000000040000-0x000000440000 : "os-image" +0x000000440000-0x000000e40000 : "rootfs" +0x000000e40000-0x000000e40200 : "default-mac" +0x000000e40200-0x000000e40400 : "pin" +0x000000e40400-0x000000e40600 : "product-info" +0x000000e50000-0x000000e60000 : "partition-table" +0x000000e60000-0x000000e60200 : "soft-version" +0x000000e61000-0x000000e70000 : "support-list" +0x000000e70000-0x000000e80000 : "profile" +0x000000e80000-0x000000e90000 : "default-config" +0x000000e90000-0x000000ee0000 : "user-config" +0x000000ee0000-0x000000fe0000 : "log" +0x000000fe0000-0x000000ff0000 : "radio_bk" +0x000000ff0000-0x000001000000 : "radio" + +Signed-off-by: Rafał Miłecki +Signed-off-by: Miquel Raynal +Link: https://lore.kernel.org/linux-mtd/20221015092950.27467-2-zajec5@gmail.com +--- + drivers/mtd/parsers/Kconfig | 15 +++ + drivers/mtd/parsers/Makefile | 1 + + drivers/mtd/parsers/tplink_safeloader.c | 150 ++++++++++++++++++++++++ + 3 files changed, 166 insertions(+) + create mode 100644 drivers/mtd/parsers/tplink_safeloader.c + +--- a/drivers/mtd/parsers/Kconfig ++++ b/drivers/mtd/parsers/Kconfig +@@ -113,6 +113,21 @@ config MTD_AFS_PARTS + for your particular device. It won't happen automatically. The + 'physmap' map driver (CONFIG_MTD_PHYSMAP) does this, for example. + ++config MTD_PARSER_TPLINK_SAFELOADER ++ tristate "TP-Link Safeloader partitions parser" ++ depends on MTD && (ARCH_BCM_5301X || ATH79 || SOC_MT7620 || SOC_MT7621 || COMPILE_TEST) ++ help ++ TP-Link home routers use flash partitions to store various data. Info ++ about flash space layout is stored in a partitions table using a ++ custom ASCII-based format. ++ ++ That format was first found in devices with SafeLoader bootloader and ++ was named after it. Later it was adapted to CFE and U-Boot ++ bootloaders. ++ ++ This driver reads partitions table, parses it and creates MTD ++ partitions. ++ + config MTD_PARSER_TRX + tristate "Parser for TRX format partitions" + depends on MTD && (BCM47XX || ARCH_BCM_5301X || ARCH_MEDIATEK || RALINK || COMPILE_TEST) +--- a/drivers/mtd/parsers/Makefile ++++ b/drivers/mtd/parsers/Makefile +@@ -9,6 +9,7 @@ ofpart-$(CONFIG_MTD_OF_PARTS_BCM4908) += + ofpart-$(CONFIG_MTD_OF_PARTS_LINKSYS_NS)+= ofpart_linksys_ns.o + obj-$(CONFIG_MTD_PARSER_IMAGETAG) += parser_imagetag.o + obj-$(CONFIG_MTD_AFS_PARTS) += afs.o ++obj-$(CONFIG_MTD_PARSER_TPLINK_SAFELOADER) += tplink_safeloader.o + obj-$(CONFIG_MTD_PARSER_TRX) += parser_trx.o + obj-$(CONFIG_MTD_SERCOMM_PARTS) += scpart.o + obj-$(CONFIG_MTD_SHARPSL_PARTS) += sharpslpart.o +--- /dev/null ++++ b/drivers/mtd/parsers/tplink_safeloader.c +@@ -0,0 +1,150 @@ ++// SPDX-License-Identifier: GPL-2.0-only ++/* ++ * Copyright © 2022 Rafał Miłecki ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#define TPLINK_SAFELOADER_DATA_OFFSET 4 ++#define TPLINK_SAFELOADER_MAX_PARTS 32 ++ ++struct safeloader_cmn_header { ++ __be32 size; ++ uint32_t unused; ++} __packed; ++ ++static void *mtd_parser_tplink_safeloader_read_table(struct mtd_info *mtd) ++{ ++ struct safeloader_cmn_header hdr; ++ struct device_node *np; ++ size_t bytes_read; ++ size_t offset; ++ size_t size; ++ char *buf; ++ int err; ++ ++ np = mtd_get_of_node(mtd); ++ if (mtd_is_partition(mtd)) ++ of_node_get(np); ++ else ++ np = of_get_child_by_name(np, "partitions"); ++ ++ if (of_property_read_u32(np, "partitions-table-offset", (u32 *)&offset)) { ++ pr_err("Failed to get partitions table offset\n"); ++ goto err_put; ++ } ++ ++ err = mtd_read(mtd, offset, sizeof(hdr), &bytes_read, (uint8_t *)&hdr); ++ if (err && !mtd_is_bitflip(err)) { ++ pr_err("Failed to read from %s at 0x%zx\n", mtd->name, offset); ++ goto err_put; ++ } ++ ++ size = be32_to_cpu(hdr.size); ++ ++ buf = kmalloc(size + 1, GFP_KERNEL); ++ if (!buf) ++ goto err_put; ++ ++ err = mtd_read(mtd, offset + sizeof(hdr), size, &bytes_read, buf); ++ if (err && !mtd_is_bitflip(err)) { ++ pr_err("Failed to read from %s at 0x%zx\n", mtd->name, offset + sizeof(hdr)); ++ goto err_kfree; ++ } ++ ++ buf[size] = '\0'; ++ ++ of_node_put(np); ++ ++ return buf; ++ ++err_kfree: ++ kfree(buf); ++err_put: ++ of_node_put(np); ++ return NULL; ++} ++ ++static int mtd_parser_tplink_safeloader_parse(struct mtd_info *mtd, ++ const struct mtd_partition **pparts, ++ struct mtd_part_parser_data *data) ++{ ++ struct mtd_partition *parts; ++ char name[65]; ++ size_t offset; ++ size_t bytes; ++ char *buf; ++ int idx; ++ int err; ++ ++ parts = kcalloc(TPLINK_SAFELOADER_MAX_PARTS, sizeof(*parts), GFP_KERNEL); ++ if (!parts) { ++ err = -ENOMEM; ++ goto err_out; ++ } ++ ++ buf = mtd_parser_tplink_safeloader_read_table(mtd); ++ if (!buf) { ++ err = -ENOENT; ++ goto err_out; ++ } ++ ++ for (idx = 0, offset = TPLINK_SAFELOADER_DATA_OFFSET; ++ idx < TPLINK_SAFELOADER_MAX_PARTS && ++ sscanf(buf + offset, "partition %64s base 0x%llx size 0x%llx%zn\n", ++ name, &parts[idx].offset, &parts[idx].size, &bytes) == 3; ++ idx++, offset += bytes + 1) { ++ parts[idx].name = kstrdup(name, GFP_KERNEL); ++ if (!parts[idx].name) { ++ err = -ENOMEM; ++ goto err_free; ++ } ++ } ++ ++ if (idx == TPLINK_SAFELOADER_MAX_PARTS) ++ pr_warn("Reached maximum number of partitions!\n"); ++ ++ kfree(buf); ++ ++ *pparts = parts; ++ ++ return idx; ++ ++err_free: ++ for (idx -= 1; idx >= 0; idx--) ++ kfree(parts[idx].name); ++err_out: ++ return err; ++}; ++ ++static void mtd_parser_tplink_safeloader_cleanup(const struct mtd_partition *pparts, ++ int nr_parts) ++{ ++ int i; ++ ++ for (i = 0; i < nr_parts; i++) ++ kfree(pparts[i].name); ++ ++ kfree(pparts); ++} ++ ++static const struct of_device_id mtd_parser_tplink_safeloader_of_match_table[] = { ++ { .compatible = "tplink,safeloader-partitions" }, ++ {}, ++}; ++MODULE_DEVICE_TABLE(of, mtd_parser_tplink_safeloader_of_match_table); ++ ++static struct mtd_part_parser mtd_parser_tplink_safeloader = { ++ .parse_fn = mtd_parser_tplink_safeloader_parse, ++ .cleanup = mtd_parser_tplink_safeloader_cleanup, ++ .name = "tplink-safeloader", ++ .of_match_table = mtd_parser_tplink_safeloader_of_match_table, ++}; ++module_mtd_part_parser(mtd_parser_tplink_safeloader); ++ ++MODULE_LICENSE("GPL"); diff --git a/target/linux/generic/backport-6.1/600-v5.18-page_pool-Add-allocation-stats.patch b/target/linux/generic/backport-6.1/600-v5.18-page_pool-Add-allocation-stats.patch new file mode 100644 index 0000000000..9e383de92c --- /dev/null +++ b/target/linux/generic/backport-6.1/600-v5.18-page_pool-Add-allocation-stats.patch @@ -0,0 +1,165 @@ +From 8610037e8106b48c79cfe0afb92b2b2466e51c3d Mon Sep 17 00:00:00 2001 +From: Joe Damato +Date: Tue, 1 Mar 2022 23:55:47 -0800 +Subject: [PATCH] page_pool: Add allocation stats + +Add per-pool statistics counters for the allocation path of a page pool. +These stats are incremented in softirq context, so no locking or per-cpu +variables are needed. + +This code is disabled by default and a kernel config option is provided for +users who wish to enable them. + +The statistics added are: + - fast: successful fast path allocations + - slow: slow path order-0 allocations + - slow_high_order: slow path high order allocations + - empty: ptr ring is empty, so a slow path allocation was forced. + - refill: an allocation which triggered a refill of the cache + - waive: pages obtained from the ptr ring that cannot be added to + the cache due to a NUMA mismatch. + +Signed-off-by: Joe Damato +Acked-by: Jesper Dangaard Brouer +Reviewed-by: Ilias Apalodimas +Signed-off-by: David S. Miller +--- + include/net/page_pool.h | 18 ++++++++++++++++++ + net/Kconfig | 13 +++++++++++++ + net/core/page_pool.c | 24 ++++++++++++++++++++---- + 3 files changed, 51 insertions(+), 4 deletions(-) + +--- a/include/net/page_pool.h ++++ b/include/net/page_pool.h +@@ -82,6 +82,19 @@ struct page_pool_params { + unsigned int offset; /* DMA addr offset */ + }; + ++#ifdef CONFIG_PAGE_POOL_STATS ++struct page_pool_alloc_stats { ++ u64 fast; /* fast path allocations */ ++ u64 slow; /* slow-path order 0 allocations */ ++ u64 slow_high_order; /* slow-path high order allocations */ ++ u64 empty; /* failed refills due to empty ptr ring, forcing ++ * slow path allocation ++ */ ++ u64 refill; /* allocations via successful refill */ ++ u64 waive; /* failed refills due to numa zone mismatch */ ++}; ++#endif ++ + struct page_pool { + struct page_pool_params p; + +@@ -132,6 +145,11 @@ struct page_pool { + refcount_t user_cnt; + + u64 destroy_cnt; ++ ++#ifdef CONFIG_PAGE_POOL_STATS ++ /* these stats are incremented while in softirq context */ ++ struct page_pool_alloc_stats alloc_stats; ++#endif + }; + + struct page *page_pool_alloc_pages(struct page_pool *pool, gfp_t gfp); +--- a/net/Kconfig ++++ b/net/Kconfig +@@ -434,6 +434,19 @@ config NET_DEVLINK + config PAGE_POOL + bool + ++config PAGE_POOL_STATS ++ default n ++ bool "Page pool stats" ++ depends on PAGE_POOL ++ help ++ Enable page pool statistics to track page allocation and recycling ++ in page pools. This option incurs additional CPU cost in allocation ++ and recycle paths and additional memory cost to store the statistics. ++ These statistics are only available if this option is enabled and if ++ the driver using the page pool supports exporting this data. ++ ++ If unsure, say N. ++ + config FAILOVER + tristate "Generic failover module" + help +--- a/net/core/page_pool.c ++++ b/net/core/page_pool.c +@@ -26,6 +26,13 @@ + + #define BIAS_MAX LONG_MAX + ++#ifdef CONFIG_PAGE_POOL_STATS ++/* alloc_stat_inc is intended to be used in softirq context */ ++#define alloc_stat_inc(pool, __stat) (pool->alloc_stats.__stat++) ++#else ++#define alloc_stat_inc(pool, __stat) ++#endif ++ + static int page_pool_init(struct page_pool *pool, + const struct page_pool_params *params) + { +@@ -117,8 +124,10 @@ static struct page *page_pool_refill_all + int pref_nid; /* preferred NUMA node */ + + /* Quicker fallback, avoid locks when ring is empty */ +- if (__ptr_ring_empty(r)) ++ if (__ptr_ring_empty(r)) { ++ alloc_stat_inc(pool, empty); + return NULL; ++ } + + /* Softirq guarantee CPU and thus NUMA node is stable. This, + * assumes CPU refilling driver RX-ring will also run RX-NAPI. +@@ -148,14 +157,17 @@ static struct page *page_pool_refill_all + * This limit stress on page buddy alloactor. + */ + page_pool_return_page(pool, page); ++ alloc_stat_inc(pool, waive); + page = NULL; + break; + } + } while (pool->alloc.count < PP_ALLOC_CACHE_REFILL); + + /* Return last page */ +- if (likely(pool->alloc.count > 0)) ++ if (likely(pool->alloc.count > 0)) { + page = pool->alloc.cache[--pool->alloc.count]; ++ alloc_stat_inc(pool, refill); ++ } + + spin_unlock(&r->consumer_lock); + return page; +@@ -170,6 +182,7 @@ static struct page *__page_pool_get_cach + if (likely(pool->alloc.count)) { + /* Fast-path */ + page = pool->alloc.cache[--pool->alloc.count]; ++ alloc_stat_inc(pool, fast); + } else { + page = page_pool_refill_alloc_cache(pool); + } +@@ -241,6 +254,7 @@ static struct page *__page_pool_alloc_pa + return NULL; + } + ++ alloc_stat_inc(pool, slow_high_order); + page_pool_set_pp_info(pool, page); + + /* Track how many pages are held 'in-flight' */ +@@ -295,10 +309,12 @@ static struct page *__page_pool_alloc_pa + } + + /* Return last page */ +- if (likely(pool->alloc.count > 0)) ++ if (likely(pool->alloc.count > 0)) { + page = pool->alloc.cache[--pool->alloc.count]; +- else ++ alloc_stat_inc(pool, slow); ++ } else { + page = NULL; ++ } + + /* When page just alloc'ed is should/must have refcnt 1. */ + return page; diff --git a/target/linux/generic/backport-6.1/601-v5.18-page_pool-Add-recycle-stats.patch b/target/linux/generic/backport-6.1/601-v5.18-page_pool-Add-recycle-stats.patch new file mode 100644 index 0000000000..fb11f0035f --- /dev/null +++ b/target/linux/generic/backport-6.1/601-v5.18-page_pool-Add-recycle-stats.patch @@ -0,0 +1,140 @@ +From ad6fa1e1ab1b8164f1ba296b1b4dc556a483bcad Mon Sep 17 00:00:00 2001 +From: Joe Damato +Date: Tue, 1 Mar 2022 23:55:48 -0800 +Subject: [PATCH 2/3] page_pool: Add recycle stats + +Add per-cpu stats tracking page pool recycling events: + - cached: recycling placed page in the page pool cache + - cache_full: page pool cache was full + - ring: page placed into the ptr ring + - ring_full: page released from page pool because the ptr ring was full + - released_refcnt: page released (and not recycled) because refcnt > 1 + +Signed-off-by: Joe Damato +Acked-by: Jesper Dangaard Brouer +Reviewed-by: Ilias Apalodimas +Signed-off-by: David S. Miller +--- + include/net/page_pool.h | 16 ++++++++++++++++ + net/core/page_pool.c | 30 ++++++++++++++++++++++++++++-- + 2 files changed, 44 insertions(+), 2 deletions(-) + +--- a/include/net/page_pool.h ++++ b/include/net/page_pool.h +@@ -93,6 +93,18 @@ struct page_pool_alloc_stats { + u64 refill; /* allocations via successful refill */ + u64 waive; /* failed refills due to numa zone mismatch */ + }; ++ ++struct page_pool_recycle_stats { ++ u64 cached; /* recycling placed page in the cache. */ ++ u64 cache_full; /* cache was full */ ++ u64 ring; /* recycling placed page back into ptr ring */ ++ u64 ring_full; /* page was released from page-pool because ++ * PTR ring was full. ++ */ ++ u64 released_refcnt; /* page released because of elevated ++ * refcnt ++ */ ++}; + #endif + + struct page_pool { +@@ -136,6 +148,10 @@ struct page_pool { + */ + struct ptr_ring ring; + ++#ifdef CONFIG_PAGE_POOL_STATS ++ /* recycle stats are per-cpu to avoid locking */ ++ struct page_pool_recycle_stats __percpu *recycle_stats; ++#endif + atomic_t pages_state_release_cnt; + + /* A page_pool is strictly tied to a single RX-queue being +--- a/net/core/page_pool.c ++++ b/net/core/page_pool.c +@@ -29,8 +29,15 @@ + #ifdef CONFIG_PAGE_POOL_STATS + /* alloc_stat_inc is intended to be used in softirq context */ + #define alloc_stat_inc(pool, __stat) (pool->alloc_stats.__stat++) ++/* recycle_stat_inc is safe to use when preemption is possible. */ ++#define recycle_stat_inc(pool, __stat) \ ++ do { \ ++ struct page_pool_recycle_stats __percpu *s = pool->recycle_stats; \ ++ this_cpu_inc(s->__stat); \ ++ } while (0) + #else + #define alloc_stat_inc(pool, __stat) ++#define recycle_stat_inc(pool, __stat) + #endif + + static int page_pool_init(struct page_pool *pool, +@@ -80,6 +87,12 @@ static int page_pool_init(struct page_po + pool->p.flags & PP_FLAG_PAGE_FRAG) + return -EINVAL; + ++#ifdef CONFIG_PAGE_POOL_STATS ++ pool->recycle_stats = alloc_percpu(struct page_pool_recycle_stats); ++ if (!pool->recycle_stats) ++ return -ENOMEM; ++#endif ++ + if (ptr_ring_init(&pool->ring, ring_qsize, GFP_KERNEL) < 0) + return -ENOMEM; + +@@ -412,7 +425,12 @@ static bool page_pool_recycle_in_ring(st + else + ret = ptr_ring_produce_bh(&pool->ring, page); + +- return (ret == 0) ? true : false; ++ if (!ret) { ++ recycle_stat_inc(pool, ring); ++ return true; ++ } ++ ++ return false; + } + + /* Only allow direct recycling in special circumstances, into the +@@ -423,11 +441,14 @@ static bool page_pool_recycle_in_ring(st + static bool page_pool_recycle_in_cache(struct page *page, + struct page_pool *pool) + { +- if (unlikely(pool->alloc.count == PP_ALLOC_CACHE_SIZE)) ++ if (unlikely(pool->alloc.count == PP_ALLOC_CACHE_SIZE)) { ++ recycle_stat_inc(pool, cache_full); + return false; ++ } + + /* Caller MUST have verified/know (page_ref_count(page) == 1) */ + pool->alloc.cache[pool->alloc.count++] = page; ++ recycle_stat_inc(pool, cached); + return true; + } + +@@ -482,6 +503,7 @@ __page_pool_put_page(struct page_pool *p + * doing refcnt based recycle tricks, meaning another process + * will be invoking put_page. + */ ++ recycle_stat_inc(pool, released_refcnt); + /* Do not replace this with page_pool_return_page() */ + page_pool_release_page(pool, page); + put_page(page); +@@ -495,6 +517,7 @@ void page_pool_put_page(struct page_pool + page = __page_pool_put_page(pool, page, dma_sync_size, allow_direct); + if (page && !page_pool_recycle_in_ring(pool, page)) { + /* Cache full, fallback to free pages */ ++ recycle_stat_inc(pool, ring_full); + page_pool_return_page(pool, page); + } + } +@@ -641,6 +664,9 @@ static void page_pool_free(struct page_p + if (pool->p.flags & PP_FLAG_DMA_MAP) + put_device(pool->p.dev); + ++#ifdef CONFIG_PAGE_POOL_STATS ++ free_percpu(pool->recycle_stats); ++#endif + kfree(pool); + } + diff --git a/target/linux/generic/backport-6.1/602-v5.18-page_pool-Add-function-to-batch-and-return-stats.patch b/target/linux/generic/backport-6.1/602-v5.18-page_pool-Add-function-to-batch-and-return-stats.patch new file mode 100644 index 0000000000..41188fb7f2 --- /dev/null +++ b/target/linux/generic/backport-6.1/602-v5.18-page_pool-Add-function-to-batch-and-return-stats.patch @@ -0,0 +1,77 @@ +From 6b95e3388b1ea0ca63500c5a6e39162dbf828433 Mon Sep 17 00:00:00 2001 +From: Joe Damato +Date: Tue, 1 Mar 2022 23:55:49 -0800 +Subject: [PATCH 3/3] page_pool: Add function to batch and return stats + +Adds a function page_pool_get_stats which can be used by drivers to obtain +stats for a specified page_pool. + +Signed-off-by: Joe Damato +Acked-by: Jesper Dangaard Brouer +Reviewed-by: Ilias Apalodimas +Signed-off-by: David S. Miller +--- + include/net/page_pool.h | 17 +++++++++++++++++ + net/core/page_pool.c | 25 +++++++++++++++++++++++++ + 2 files changed, 42 insertions(+) + +--- a/include/net/page_pool.h ++++ b/include/net/page_pool.h +@@ -105,6 +105,23 @@ struct page_pool_recycle_stats { + * refcnt + */ + }; ++ ++/* This struct wraps the above stats structs so users of the ++ * page_pool_get_stats API can pass a single argument when requesting the ++ * stats for the page pool. ++ */ ++struct page_pool_stats { ++ struct page_pool_alloc_stats alloc_stats; ++ struct page_pool_recycle_stats recycle_stats; ++}; ++ ++/* ++ * Drivers that wish to harvest page pool stats and report them to users ++ * (perhaps via ethtool, debugfs, or another mechanism) can allocate a ++ * struct page_pool_stats call page_pool_get_stats to get stats for the specified pool. ++ */ ++bool page_pool_get_stats(struct page_pool *pool, ++ struct page_pool_stats *stats); + #endif + + struct page_pool { +--- a/net/core/page_pool.c ++++ b/net/core/page_pool.c +@@ -35,6 +35,31 @@ + struct page_pool_recycle_stats __percpu *s = pool->recycle_stats; \ + this_cpu_inc(s->__stat); \ + } while (0) ++ ++bool page_pool_get_stats(struct page_pool *pool, ++ struct page_pool_stats *stats) ++{ ++ int cpu = 0; ++ ++ if (!stats) ++ return false; ++ ++ memcpy(&stats->alloc_stats, &pool->alloc_stats, sizeof(pool->alloc_stats)); ++ ++ for_each_possible_cpu(cpu) { ++ const struct page_pool_recycle_stats *pcpu = ++ per_cpu_ptr(pool->recycle_stats, cpu); ++ ++ stats->recycle_stats.cached += pcpu->cached; ++ stats->recycle_stats.cache_full += pcpu->cache_full; ++ stats->recycle_stats.ring += pcpu->ring; ++ stats->recycle_stats.ring_full += pcpu->ring_full; ++ stats->recycle_stats.released_refcnt += pcpu->released_refcnt; ++ } ++ ++ return true; ++} ++EXPORT_SYMBOL(page_pool_get_stats); + #else + #define alloc_stat_inc(pool, __stat) + #define recycle_stat_inc(pool, __stat) diff --git a/target/linux/generic/backport-6.1/603-v5.19-page_pool-Add-recycle-stats-to-page_pool_put_page_bu.patch b/target/linux/generic/backport-6.1/603-v5.19-page_pool-Add-recycle-stats-to-page_pool_put_page_bu.patch new file mode 100644 index 0000000000..6ae3fb7133 --- /dev/null +++ b/target/linux/generic/backport-6.1/603-v5.19-page_pool-Add-recycle-stats-to-page_pool_put_page_bu.patch @@ -0,0 +1,55 @@ +From 590032a4d2133ecc10d3078a8db1d85a4842f12c Mon Sep 17 00:00:00 2001 +From: Lorenzo Bianconi +Date: Mon, 11 Apr 2022 16:05:26 +0200 +Subject: [PATCH] page_pool: Add recycle stats to page_pool_put_page_bulk + +Add missing recycle stats to page_pool_put_page_bulk routine. + +Reviewed-by: Joe Damato +Signed-off-by: Lorenzo Bianconi +Reviewed-by: Ilias Apalodimas +Link: https://lore.kernel.org/r/3712178b51c007cfaed910ea80e68f00c916b1fa.1649685634.git.lorenzo@kernel.org +Signed-off-by: Paolo Abeni +--- + net/core/page_pool.c | 15 +++++++++++++-- + 1 file changed, 13 insertions(+), 2 deletions(-) + +--- a/net/core/page_pool.c ++++ b/net/core/page_pool.c +@@ -36,6 +36,12 @@ + this_cpu_inc(s->__stat); \ + } while (0) + ++#define recycle_stat_add(pool, __stat, val) \ ++ do { \ ++ struct page_pool_recycle_stats __percpu *s = pool->recycle_stats; \ ++ this_cpu_add(s->__stat, val); \ ++ } while (0) ++ + bool page_pool_get_stats(struct page_pool *pool, + struct page_pool_stats *stats) + { +@@ -63,6 +69,7 @@ EXPORT_SYMBOL(page_pool_get_stats); + #else + #define alloc_stat_inc(pool, __stat) + #define recycle_stat_inc(pool, __stat) ++#define recycle_stat_add(pool, __stat, val) + #endif + + static int page_pool_init(struct page_pool *pool, +@@ -569,9 +576,13 @@ void page_pool_put_page_bulk(struct page + /* Bulk producer into ptr_ring page_pool cache */ + page_pool_ring_lock(pool); + for (i = 0; i < bulk_len; i++) { +- if (__ptr_ring_produce(&pool->ring, data[i])) +- break; /* ring full */ ++ if (__ptr_ring_produce(&pool->ring, data[i])) { ++ /* ring full */ ++ recycle_stat_inc(pool, ring_full); ++ break; ++ } + } ++ recycle_stat_add(pool, ring, i); + page_pool_ring_unlock(pool); + + /* Hopefully all pages was return into ptr_ring */ diff --git a/target/linux/generic/backport-6.1/604-v5.19-net-page_pool-introduce-ethtool-stats.patch b/target/linux/generic/backport-6.1/604-v5.19-net-page_pool-introduce-ethtool-stats.patch new file mode 100644 index 0000000000..e5cf91ceee --- /dev/null +++ b/target/linux/generic/backport-6.1/604-v5.19-net-page_pool-introduce-ethtool-stats.patch @@ -0,0 +1,147 @@ +From f3c5264f452a5b0ac1de1f2f657efbabdea3c76a Mon Sep 17 00:00:00 2001 +From: Lorenzo Bianconi +Date: Tue, 12 Apr 2022 18:31:58 +0200 +Subject: [PATCH] net: page_pool: introduce ethtool stats + +Introduce page_pool APIs to report stats through ethtool and reduce +duplicated code in each driver. + +Signed-off-by: Lorenzo Bianconi +Reviewed-by: Jakub Kicinski +Reviewed-by: Ilias Apalodimas +Signed-off-by: David S. Miller +--- + include/net/page_pool.h | 21 ++++++++++++++ + net/core/page_pool.c | 63 ++++++++++++++++++++++++++++++++++++++++- + 2 files changed, 83 insertions(+), 1 deletion(-) + +--- a/include/net/page_pool.h ++++ b/include/net/page_pool.h +@@ -115,6 +115,10 @@ struct page_pool_stats { + struct page_pool_recycle_stats recycle_stats; + }; + ++int page_pool_ethtool_stats_get_count(void); ++u8 *page_pool_ethtool_stats_get_strings(u8 *data); ++u64 *page_pool_ethtool_stats_get(u64 *data, void *stats); ++ + /* + * Drivers that wish to harvest page pool stats and report them to users + * (perhaps via ethtool, debugfs, or another mechanism) can allocate a +@@ -122,6 +126,23 @@ struct page_pool_stats { + */ + bool page_pool_get_stats(struct page_pool *pool, + struct page_pool_stats *stats); ++#else ++ ++static inline int page_pool_ethtool_stats_get_count(void) ++{ ++ return 0; ++} ++ ++static inline u8 *page_pool_ethtool_stats_get_strings(u8 *data) ++{ ++ return data; ++} ++ ++static inline u64 *page_pool_ethtool_stats_get(u64 *data, void *stats) ++{ ++ return data; ++} ++ + #endif + + struct page_pool { +--- a/net/core/page_pool.c ++++ b/net/core/page_pool.c +@@ -18,6 +18,7 @@ + #include + #include /* for __put_page() */ + #include ++#include + + #include + +@@ -42,6 +43,20 @@ + this_cpu_add(s->__stat, val); \ + } while (0) + ++static const char pp_stats[][ETH_GSTRING_LEN] = { ++ "rx_pp_alloc_fast", ++ "rx_pp_alloc_slow", ++ "rx_pp_alloc_slow_ho", ++ "rx_pp_alloc_empty", ++ "rx_pp_alloc_refill", ++ "rx_pp_alloc_waive", ++ "rx_pp_recycle_cached", ++ "rx_pp_recycle_cache_full", ++ "rx_pp_recycle_ring", ++ "rx_pp_recycle_ring_full", ++ "rx_pp_recycle_released_ref", ++}; ++ + bool page_pool_get_stats(struct page_pool *pool, + struct page_pool_stats *stats) + { +@@ -50,7 +65,13 @@ bool page_pool_get_stats(struct page_poo + if (!stats) + return false; + +- memcpy(&stats->alloc_stats, &pool->alloc_stats, sizeof(pool->alloc_stats)); ++ /* The caller is responsible to initialize stats. */ ++ stats->alloc_stats.fast += pool->alloc_stats.fast; ++ stats->alloc_stats.slow += pool->alloc_stats.slow; ++ stats->alloc_stats.slow_high_order += pool->alloc_stats.slow_high_order; ++ stats->alloc_stats.empty += pool->alloc_stats.empty; ++ stats->alloc_stats.refill += pool->alloc_stats.refill; ++ stats->alloc_stats.waive += pool->alloc_stats.waive; + + for_each_possible_cpu(cpu) { + const struct page_pool_recycle_stats *pcpu = +@@ -66,6 +87,46 @@ bool page_pool_get_stats(struct page_poo + return true; + } + EXPORT_SYMBOL(page_pool_get_stats); ++ ++u8 *page_pool_ethtool_stats_get_strings(u8 *data) ++{ ++ int i; ++ ++ for (i = 0; i < ARRAY_SIZE(pp_stats); i++) { ++ memcpy(data, pp_stats[i], ETH_GSTRING_LEN); ++ data += ETH_GSTRING_LEN; ++ } ++ ++ return data; ++} ++EXPORT_SYMBOL(page_pool_ethtool_stats_get_strings); ++ ++int page_pool_ethtool_stats_get_count(void) ++{ ++ return ARRAY_SIZE(pp_stats); ++} ++EXPORT_SYMBOL(page_pool_ethtool_stats_get_count); ++ ++u64 *page_pool_ethtool_stats_get(u64 *data, void *stats) ++{ ++ struct page_pool_stats *pool_stats = stats; ++ ++ *data++ = pool_stats->alloc_stats.fast; ++ *data++ = pool_stats->alloc_stats.slow; ++ *data++ = pool_stats->alloc_stats.slow_high_order; ++ *data++ = pool_stats->alloc_stats.empty; ++ *data++ = pool_stats->alloc_stats.refill; ++ *data++ = pool_stats->alloc_stats.waive; ++ *data++ = pool_stats->recycle_stats.cached; ++ *data++ = pool_stats->recycle_stats.cache_full; ++ *data++ = pool_stats->recycle_stats.ring; ++ *data++ = pool_stats->recycle_stats.ring_full; ++ *data++ = pool_stats->recycle_stats.released_refcnt; ++ ++ return data; ++} ++EXPORT_SYMBOL(page_pool_ethtool_stats_get); ++ + #else + #define alloc_stat_inc(pool, __stat) + #define recycle_stat_inc(pool, __stat) diff --git a/target/linux/generic/backport-6.1/605-v5.18-xdp-introduce-flags-field-in-xdp_buff-xdp_frame.patch b/target/linux/generic/backport-6.1/605-v5.18-xdp-introduce-flags-field-in-xdp_buff-xdp_frame.patch new file mode 100644 index 0000000000..4a914404a2 --- /dev/null +++ b/target/linux/generic/backport-6.1/605-v5.18-xdp-introduce-flags-field-in-xdp_buff-xdp_frame.patch @@ -0,0 +1,99 @@ +From 2e88d4ff03013937028f5397268b21e10cf68713 Mon Sep 17 00:00:00 2001 +From: Lorenzo Bianconi +Date: Fri, 21 Jan 2022 11:09:45 +0100 +Subject: [PATCH] xdp: introduce flags field in xdp_buff/xdp_frame + +Introduce flags field in xdp_frame and xdp_buffer data structures +to define additional buffer features. At the moment the only +supported buffer feature is frags bit (XDP_FLAGS_HAS_FRAGS). +frags bit is used to specify if this is a linear buffer +(XDP_FLAGS_HAS_FRAGS not set) or a frags frame (XDP_FLAGS_HAS_FRAGS +set). In the latter case the driver is expected to initialize the +skb_shared_info structure at the end of the first buffer to link together +subsequent buffers belonging to the same frame. + +Acked-by: Toke Hoiland-Jorgensen +Acked-by: John Fastabend +Acked-by: Jesper Dangaard Brouer +Signed-off-by: Lorenzo Bianconi +Link: https://lore.kernel.org/r/e389f14f3a162c0a5bc6a2e1aa8dd01a90be117d.1642758637.git.lorenzo@kernel.org +Signed-off-by: Alexei Starovoitov +--- + include/net/xdp.h | 29 +++++++++++++++++++++++++++++ + 1 file changed, 29 insertions(+) + +--- a/include/net/xdp.h ++++ b/include/net/xdp.h +@@ -66,6 +66,10 @@ struct xdp_txq_info { + struct net_device *dev; + }; + ++enum xdp_buff_flags { ++ XDP_FLAGS_HAS_FRAGS = BIT(0), /* non-linear xdp buff */ ++}; ++ + struct xdp_buff { + void *data; + void *data_end; +@@ -74,13 +78,30 @@ struct xdp_buff { + struct xdp_rxq_info *rxq; + struct xdp_txq_info *txq; + u32 frame_sz; /* frame size to deduce data_hard_end/reserved tailroom*/ ++ u32 flags; /* supported values defined in xdp_buff_flags */ + }; + ++static __always_inline bool xdp_buff_has_frags(struct xdp_buff *xdp) ++{ ++ return !!(xdp->flags & XDP_FLAGS_HAS_FRAGS); ++} ++ ++static __always_inline void xdp_buff_set_frags_flag(struct xdp_buff *xdp) ++{ ++ xdp->flags |= XDP_FLAGS_HAS_FRAGS; ++} ++ ++static __always_inline void xdp_buff_clear_frags_flag(struct xdp_buff *xdp) ++{ ++ xdp->flags &= ~XDP_FLAGS_HAS_FRAGS; ++} ++ + static __always_inline void + xdp_init_buff(struct xdp_buff *xdp, u32 frame_sz, struct xdp_rxq_info *rxq) + { + xdp->frame_sz = frame_sz; + xdp->rxq = rxq; ++ xdp->flags = 0; + } + + static __always_inline void +@@ -122,8 +143,14 @@ struct xdp_frame { + */ + struct xdp_mem_info mem; + struct net_device *dev_rx; /* used by cpumap */ ++ u32 flags; /* supported values defined in xdp_buff_flags */ + }; + ++static __always_inline bool xdp_frame_has_frags(struct xdp_frame *frame) ++{ ++ return !!(frame->flags & XDP_FLAGS_HAS_FRAGS); ++} ++ + #define XDP_BULK_QUEUE_SIZE 16 + struct xdp_frame_bulk { + int count; +@@ -180,6 +207,7 @@ void xdp_convert_frame_to_buff(struct xd + xdp->data_end = frame->data + frame->len; + xdp->data_meta = frame->data - frame->metasize; + xdp->frame_sz = frame->frame_sz; ++ xdp->flags = frame->flags; + } + + static inline +@@ -206,6 +234,7 @@ int xdp_update_frame_from_buff(struct xd + xdp_frame->headroom = headroom - sizeof(*xdp_frame); + xdp_frame->metasize = metasize; + xdp_frame->frame_sz = xdp->frame_sz; ++ xdp_frame->flags = xdp->flags; + + return 0; + } diff --git a/target/linux/generic/backport-6.1/606-v5.18-xdp-add-frags-support-to-xdp_return_-buff-frame.patch b/target/linux/generic/backport-6.1/606-v5.18-xdp-add-frags-support-to-xdp_return_-buff-frame.patch new file mode 100644 index 0000000000..86d24367dd --- /dev/null +++ b/target/linux/generic/backport-6.1/606-v5.18-xdp-add-frags-support-to-xdp_return_-buff-frame.patch @@ -0,0 +1,137 @@ +From 7c48cb0176c6d6d3b55029f7ff4ffa05faee6446 Mon Sep 17 00:00:00 2001 +From: Lorenzo Bianconi +Date: Fri, 21 Jan 2022 11:09:50 +0100 +Subject: [PATCH] xdp: add frags support to xdp_return_{buff/frame} + +Take into account if the received xdp_buff/xdp_frame is non-linear +recycling/returning the frame memory to the allocator or into +xdp_frame_bulk. + +Acked-by: Toke Hoiland-Jorgensen +Acked-by: John Fastabend +Signed-off-by: Lorenzo Bianconi +Link: https://lore.kernel.org/r/a961069febc868508ce1bdf5e53a343eb4e57cb2.1642758637.git.lorenzo@kernel.org +Signed-off-by: Alexei Starovoitov +--- + include/net/xdp.h | 18 ++++++++++++++-- + net/core/xdp.c | 54 ++++++++++++++++++++++++++++++++++++++++++++++- + 2 files changed, 69 insertions(+), 3 deletions(-) + +--- a/include/net/xdp.h ++++ b/include/net/xdp.h +@@ -275,10 +275,24 @@ void __xdp_release_frame(void *data, str + static inline void xdp_release_frame(struct xdp_frame *xdpf) + { + struct xdp_mem_info *mem = &xdpf->mem; ++ struct skb_shared_info *sinfo; ++ int i; + + /* Curr only page_pool needs this */ +- if (mem->type == MEM_TYPE_PAGE_POOL) +- __xdp_release_frame(xdpf->data, mem); ++ if (mem->type != MEM_TYPE_PAGE_POOL) ++ return; ++ ++ if (likely(!xdp_frame_has_frags(xdpf))) ++ goto out; ++ ++ sinfo = xdp_get_shared_info_from_frame(xdpf); ++ for (i = 0; i < sinfo->nr_frags; i++) { ++ struct page *page = skb_frag_page(&sinfo->frags[i]); ++ ++ __xdp_release_frame(page_address(page), mem); ++ } ++out: ++ __xdp_release_frame(xdpf->data, mem); + } + + int xdp_rxq_info_reg(struct xdp_rxq_info *xdp_rxq, +--- a/net/core/xdp.c ++++ b/net/core/xdp.c +@@ -376,12 +376,38 @@ static void __xdp_return(void *data, str + + void xdp_return_frame(struct xdp_frame *xdpf) + { ++ struct skb_shared_info *sinfo; ++ int i; ++ ++ if (likely(!xdp_frame_has_frags(xdpf))) ++ goto out; ++ ++ sinfo = xdp_get_shared_info_from_frame(xdpf); ++ for (i = 0; i < sinfo->nr_frags; i++) { ++ struct page *page = skb_frag_page(&sinfo->frags[i]); ++ ++ __xdp_return(page_address(page), &xdpf->mem, false, NULL); ++ } ++out: + __xdp_return(xdpf->data, &xdpf->mem, false, NULL); + } + EXPORT_SYMBOL_GPL(xdp_return_frame); + + void xdp_return_frame_rx_napi(struct xdp_frame *xdpf) + { ++ struct skb_shared_info *sinfo; ++ int i; ++ ++ if (likely(!xdp_frame_has_frags(xdpf))) ++ goto out; ++ ++ sinfo = xdp_get_shared_info_from_frame(xdpf); ++ for (i = 0; i < sinfo->nr_frags; i++) { ++ struct page *page = skb_frag_page(&sinfo->frags[i]); ++ ++ __xdp_return(page_address(page), &xdpf->mem, true, NULL); ++ } ++out: + __xdp_return(xdpf->data, &xdpf->mem, true, NULL); + } + EXPORT_SYMBOL_GPL(xdp_return_frame_rx_napi); +@@ -417,7 +443,7 @@ void xdp_return_frame_bulk(struct xdp_fr + struct xdp_mem_allocator *xa; + + if (mem->type != MEM_TYPE_PAGE_POOL) { +- __xdp_return(xdpf->data, &xdpf->mem, false, NULL); ++ xdp_return_frame(xdpf); + return; + } + +@@ -436,12 +462,38 @@ void xdp_return_frame_bulk(struct xdp_fr + bq->xa = rhashtable_lookup(mem_id_ht, &mem->id, mem_id_rht_params); + } + ++ if (unlikely(xdp_frame_has_frags(xdpf))) { ++ struct skb_shared_info *sinfo; ++ int i; ++ ++ sinfo = xdp_get_shared_info_from_frame(xdpf); ++ for (i = 0; i < sinfo->nr_frags; i++) { ++ skb_frag_t *frag = &sinfo->frags[i]; ++ ++ bq->q[bq->count++] = skb_frag_address(frag); ++ if (bq->count == XDP_BULK_QUEUE_SIZE) ++ xdp_flush_frame_bulk(bq); ++ } ++ } + bq->q[bq->count++] = xdpf->data; + } + EXPORT_SYMBOL_GPL(xdp_return_frame_bulk); + + void xdp_return_buff(struct xdp_buff *xdp) + { ++ struct skb_shared_info *sinfo; ++ int i; ++ ++ if (likely(!xdp_buff_has_frags(xdp))) ++ goto out; ++ ++ sinfo = xdp_get_shared_info_from_buff(xdp); ++ for (i = 0; i < sinfo->nr_frags; i++) { ++ struct page *page = skb_frag_page(&sinfo->frags[i]); ++ ++ __xdp_return(page_address(page), &xdp->rxq->mem, true, xdp); ++ } ++out: + __xdp_return(xdp->data, &xdp->rxq->mem, true, xdp); + } + diff --git a/target/linux/generic/backport-6.1/607-v5.18-net-skbuff-add-size-metadata-to-skb_shared_info-for-.patch b/target/linux/generic/backport-6.1/607-v5.18-net-skbuff-add-size-metadata-to-skb_shared_info-for-.patch new file mode 100644 index 0000000000..a0344a332a --- /dev/null +++ b/target/linux/generic/backport-6.1/607-v5.18-net-skbuff-add-size-metadata-to-skb_shared_info-for-.patch @@ -0,0 +1,31 @@ +From d16697cb6261d4cc23422e6b1cb2759df8aa76d0 Mon Sep 17 00:00:00 2001 +From: Lorenzo Bianconi +Date: Fri, 21 Jan 2022 11:09:44 +0100 +Subject: [PATCH] net: skbuff: add size metadata to skb_shared_info for xdp + +Introduce xdp_frags_size field in skb_shared_info data structure +to store xdp_buff/xdp_frame frame paged size (xdp_frags_size will +be used in xdp frags support). In order to not increase +skb_shared_info size we will use a hole due to skb_shared_info +alignment. + +Acked-by: Toke Hoiland-Jorgensen +Acked-by: John Fastabend +Acked-by: Jesper Dangaard Brouer +Signed-off-by: Lorenzo Bianconi +Link: https://lore.kernel.org/r/8a849819a3e0a143d540f78a3a5add76e17e980d.1642758637.git.lorenzo@kernel.org +Signed-off-by: Alexei Starovoitov +--- + include/linux/skbuff.h | 1 + + 1 file changed, 1 insertion(+) + +--- a/include/linux/skbuff.h ++++ b/include/linux/skbuff.h +@@ -567,6 +567,7 @@ struct skb_shared_info { + * Warning : all fields before dataref are cleared in __alloc_skb() + */ + atomic_t dataref; ++ unsigned int xdp_frags_size; + + /* Intermediate layers must ensure that destructor_arg + * remains valid until skb destructor */ diff --git a/target/linux/generic/backport-6.1/608-v5.18-net-veth-Account-total-xdp_frame-len-running-ndo_xdp.patch b/target/linux/generic/backport-6.1/608-v5.18-net-veth-Account-total-xdp_frame-len-running-ndo_xdp.patch new file mode 100644 index 0000000000..3bdba8728c --- /dev/null +++ b/target/linux/generic/backport-6.1/608-v5.18-net-veth-Account-total-xdp_frame-len-running-ndo_xdp.patch @@ -0,0 +1,65 @@ +From 5142239a22219921a7863cf00c9ab853c00689d8 Mon Sep 17 00:00:00 2001 +From: Lorenzo Bianconi +Date: Fri, 11 Mar 2022 10:14:18 +0100 +Subject: [PATCH] net: veth: Account total xdp_frame len running ndo_xdp_xmit + +Even if this is a theoretical issue since it is not possible to perform +XDP_REDIRECT on a non-linear xdp_frame, veth driver does not account +paged area in ndo_xdp_xmit function pointer. +Introduce xdp_get_frame_len utility routine to get the xdp_frame full +length and account total frame size running XDP_REDIRECT of a +non-linear xdp frame into a veth device. + +Signed-off-by: Lorenzo Bianconi +Signed-off-by: Daniel Borkmann +Acked-by: Toke Hoiland-Jorgensen +Acked-by: John Fastabend +Link: https://lore.kernel.org/bpf/54f9fd3bb65d190daf2c0bbae2f852ff16cfbaa0.1646989407.git.lorenzo@kernel.org +--- + drivers/net/veth.c | 4 ++-- + include/net/xdp.h | 14 ++++++++++++++ + 2 files changed, 16 insertions(+), 2 deletions(-) + +--- a/drivers/net/veth.c ++++ b/drivers/net/veth.c +@@ -501,7 +501,7 @@ static int veth_xdp_xmit(struct net_devi + struct xdp_frame *frame = frames[i]; + void *ptr = veth_xdp_to_ptr(frame); + +- if (unlikely(frame->len > max_len || ++ if (unlikely(xdp_get_frame_len(frame) > max_len || + __ptr_ring_produce(&rq->xdp_ring, ptr))) + break; + nxmit++; +@@ -862,7 +862,7 @@ static int veth_xdp_rcv(struct veth_rq * + /* ndo_xdp_xmit */ + struct xdp_frame *frame = veth_ptr_to_xdp(ptr); + +- stats->xdp_bytes += frame->len; ++ stats->xdp_bytes += xdp_get_frame_len(frame); + frame = veth_xdp_rcv_one(rq, frame, bq, stats); + if (frame) { + /* XDP_PASS */ +--- a/include/net/xdp.h ++++ b/include/net/xdp.h +@@ -295,6 +295,20 @@ out: + __xdp_release_frame(xdpf->data, mem); + } + ++static __always_inline unsigned int xdp_get_frame_len(struct xdp_frame *xdpf) ++{ ++ struct skb_shared_info *sinfo; ++ unsigned int len = xdpf->len; ++ ++ if (likely(!xdp_frame_has_frags(xdpf))) ++ goto out; ++ ++ sinfo = xdp_get_shared_info_from_frame(xdpf); ++ len += sinfo->xdp_frags_size; ++out: ++ return len; ++} ++ + int xdp_rxq_info_reg(struct xdp_rxq_info *xdp_rxq, + struct net_device *dev, u32 queue_index, unsigned int napi_id); + void xdp_rxq_info_unreg(struct xdp_rxq_info *xdp_rxq); diff --git a/target/linux/generic/backport-6.1/609-v5.18-veth-Allow-jumbo-frames-in-xdp-mode.patch b/target/linux/generic/backport-6.1/609-v5.18-veth-Allow-jumbo-frames-in-xdp-mode.patch new file mode 100644 index 0000000000..08744be524 --- /dev/null +++ b/target/linux/generic/backport-6.1/609-v5.18-veth-Allow-jumbo-frames-in-xdp-mode.patch @@ -0,0 +1,40 @@ +From 7cda76d858a4e71ac4a04066c093679a12e1312c Mon Sep 17 00:00:00 2001 +From: Lorenzo Bianconi +Date: Fri, 11 Mar 2022 10:14:20 +0100 +Subject: [PATCH] veth: Allow jumbo frames in xdp mode +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Allow increasing the MTU over page boundaries on veth devices +if the attached xdp program declares to support xdp fragments. + +Signed-off-by: Lorenzo Bianconi +Signed-off-by: Daniel Borkmann +Acked-by: Toke Høiland-Jørgensen +Acked-by: John Fastabend +Link: https://lore.kernel.org/bpf/d5dc039c3d4123426e7023a488c449181a7bc57f.1646989407.git.lorenzo@kernel.org +--- + drivers/net/veth.c | 11 ++++++++--- + 1 file changed, 8 insertions(+), 3 deletions(-) + +--- a/drivers/net/veth.c ++++ b/drivers/net/veth.c +@@ -1470,9 +1470,14 @@ static int veth_xdp_set(struct net_devic + goto err; + } + +- max_mtu = PAGE_SIZE - VETH_XDP_HEADROOM - +- peer->hard_header_len - +- SKB_DATA_ALIGN(sizeof(struct skb_shared_info)); ++ max_mtu = SKB_WITH_OVERHEAD(PAGE_SIZE - VETH_XDP_HEADROOM) - ++ peer->hard_header_len; ++ /* Allow increasing the max_mtu if the program supports ++ * XDP fragments. ++ */ ++ //if (prog->aux->xdp_has_frags) ++ max_mtu += PAGE_SIZE * MAX_SKB_FRAGS; ++ + if (peer->mtu > max_mtu) { + NL_SET_ERR_MSG_MOD(extack, "Peer MTU is too large to set XDP"); + err = -ERANGE; diff --git a/target/linux/generic/backport-6.1/700-v5.17-net-dsa-introduce-tagger-owned-storage-for-private.patch b/target/linux/generic/backport-6.1/700-v5.17-net-dsa-introduce-tagger-owned-storage-for-private.patch new file mode 100644 index 0000000000..fe47c175a4 --- /dev/null +++ b/target/linux/generic/backport-6.1/700-v5.17-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 +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 +Signed-off-by: Ansuel Smith +Signed-off-by: Vladimir Oltean +Signed-off-by: David S. Miller +--- + 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/target/linux/generic/backport-6.1/701-v5.17-dsa-make-tagging-protocols-connect-to-individual-switches.patch b/target/linux/generic/backport-6.1/701-v5.17-dsa-make-tagging-protocols-connect-to-individual-switches.patch new file mode 100644 index 0000000000..f682260699 --- /dev/null +++ b/target/linux/generic/backport-6.1/701-v5.17-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 +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 +Signed-off-by: David S. Miller +--- + 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/target/linux/generic/backport-6.1/702-v5.19-00-net-ethernet-mtk_eth_soc-add-support-for-coherent-DM.patch b/target/linux/generic/backport-6.1/702-v5.19-00-net-ethernet-mtk_eth_soc-add-support-for-coherent-DM.patch new file mode 100644 index 0000000000..afd78fdda2 --- /dev/null +++ b/target/linux/generic/backport-6.1/702-v5.19-00-net-ethernet-mtk_eth_soc-add-support-for-coherent-DM.patch @@ -0,0 +1,327 @@ +From: Felix Fietkau +Date: Sat, 5 Feb 2022 17:59:07 +0100 +Subject: [PATCH] net: ethernet: mtk_eth_soc: add support for coherent + DMA + +It improves performance by eliminating the need for a cache flush on rx and tx +In preparation for supporting WED (Wireless Ethernet Dispatch), also add a +function for disabling coherent DMA at runtime. + +Signed-off-by: Felix Fietkau +--- + +--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c ++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c +@@ -9,6 +9,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -839,7 +840,7 @@ static int mtk_init_fq_dma(struct mtk_et + dma_addr_t dma_addr; + int i; + +- eth->scratch_ring = dma_alloc_coherent(eth->dev, ++ eth->scratch_ring = dma_alloc_coherent(eth->dma_dev, + cnt * sizeof(struct mtk_tx_dma), + ð->phy_scratch_ring, + GFP_ATOMIC); +@@ -851,10 +852,10 @@ static int mtk_init_fq_dma(struct mtk_et + if (unlikely(!eth->scratch_head)) + return -ENOMEM; + +- dma_addr = dma_map_single(eth->dev, ++ dma_addr = dma_map_single(eth->dma_dev, + eth->scratch_head, cnt * MTK_QDMA_PAGE_SIZE, + DMA_FROM_DEVICE); +- if (unlikely(dma_mapping_error(eth->dev, dma_addr))) ++ if (unlikely(dma_mapping_error(eth->dma_dev, dma_addr))) + return -ENOMEM; + + phy_ring_tail = eth->phy_scratch_ring + +@@ -908,26 +909,26 @@ static void mtk_tx_unmap(struct mtk_eth + { + if (MTK_HAS_CAPS(eth->soc->caps, MTK_QDMA)) { + if (tx_buf->flags & MTK_TX_FLAGS_SINGLE0) { +- dma_unmap_single(eth->dev, ++ dma_unmap_single(eth->dma_dev, + dma_unmap_addr(tx_buf, dma_addr0), + dma_unmap_len(tx_buf, dma_len0), + DMA_TO_DEVICE); + } else if (tx_buf->flags & MTK_TX_FLAGS_PAGE0) { +- dma_unmap_page(eth->dev, ++ dma_unmap_page(eth->dma_dev, + dma_unmap_addr(tx_buf, dma_addr0), + dma_unmap_len(tx_buf, dma_len0), + DMA_TO_DEVICE); + } + } else { + if (dma_unmap_len(tx_buf, dma_len0)) { +- dma_unmap_page(eth->dev, ++ dma_unmap_page(eth->dma_dev, + dma_unmap_addr(tx_buf, dma_addr0), + dma_unmap_len(tx_buf, dma_len0), + DMA_TO_DEVICE); + } + + if (dma_unmap_len(tx_buf, dma_len1)) { +- dma_unmap_page(eth->dev, ++ dma_unmap_page(eth->dma_dev, + dma_unmap_addr(tx_buf, dma_addr1), + dma_unmap_len(tx_buf, dma_len1), + DMA_TO_DEVICE); +@@ -1005,9 +1006,9 @@ static int mtk_tx_map(struct sk_buff *sk + if (skb_vlan_tag_present(skb)) + txd4 |= TX_DMA_INS_VLAN | skb_vlan_tag_get(skb); + +- mapped_addr = dma_map_single(eth->dev, skb->data, ++ mapped_addr = dma_map_single(eth->dma_dev, skb->data, + skb_headlen(skb), DMA_TO_DEVICE); +- if (unlikely(dma_mapping_error(eth->dev, mapped_addr))) ++ if (unlikely(dma_mapping_error(eth->dma_dev, mapped_addr))) + return -ENOMEM; + + WRITE_ONCE(itxd->txd1, mapped_addr); +@@ -1046,10 +1047,10 @@ static int mtk_tx_map(struct sk_buff *sk + + + frag_map_size = min(frag_size, MTK_TX_DMA_BUF_LEN); +- mapped_addr = skb_frag_dma_map(eth->dev, frag, offset, ++ mapped_addr = skb_frag_dma_map(eth->dma_dev, frag, offset, + frag_map_size, + DMA_TO_DEVICE); +- if (unlikely(dma_mapping_error(eth->dev, mapped_addr))) ++ if (unlikely(dma_mapping_error(eth->dma_dev, mapped_addr))) + goto err_dma; + + if (i == nr_frags - 1 && +@@ -1330,18 +1331,18 @@ static int mtk_poll_rx(struct napi_struc + netdev->stats.rx_dropped++; + goto release_desc; + } +- dma_addr = dma_map_single(eth->dev, ++ dma_addr = dma_map_single(eth->dma_dev, + new_data + NET_SKB_PAD + + eth->ip_align, + ring->buf_size, + DMA_FROM_DEVICE); +- if (unlikely(dma_mapping_error(eth->dev, dma_addr))) { ++ if (unlikely(dma_mapping_error(eth->dma_dev, dma_addr))) { + skb_free_frag(new_data); + netdev->stats.rx_dropped++; + goto release_desc; + } + +- dma_unmap_single(eth->dev, trxd.rxd1, ++ dma_unmap_single(eth->dma_dev, trxd.rxd1, + ring->buf_size, DMA_FROM_DEVICE); + + /* receive data */ +@@ -1614,7 +1615,7 @@ static int mtk_tx_alloc(struct mtk_eth * + if (!ring->buf) + goto no_tx_mem; + +- ring->dma = dma_alloc_coherent(eth->dev, MTK_DMA_SIZE * sz, ++ ring->dma = dma_alloc_coherent(eth->dma_dev, MTK_DMA_SIZE * sz, + &ring->phys, GFP_ATOMIC); + if (!ring->dma) + goto no_tx_mem; +@@ -1632,7 +1633,7 @@ static int mtk_tx_alloc(struct mtk_eth * + * descriptors in ring->dma_pdma. + */ + if (!MTK_HAS_CAPS(eth->soc->caps, MTK_QDMA)) { +- ring->dma_pdma = dma_alloc_coherent(eth->dev, MTK_DMA_SIZE * sz, ++ ring->dma_pdma = dma_alloc_coherent(eth->dma_dev, MTK_DMA_SIZE * sz, + &ring->phys_pdma, + GFP_ATOMIC); + if (!ring->dma_pdma) +@@ -1691,7 +1692,7 @@ static void mtk_tx_clean(struct mtk_eth + } + + if (ring->dma) { +- dma_free_coherent(eth->dev, ++ dma_free_coherent(eth->dma_dev, + MTK_DMA_SIZE * sizeof(*ring->dma), + ring->dma, + ring->phys); +@@ -1699,7 +1700,7 @@ static void mtk_tx_clean(struct mtk_eth + } + + if (ring->dma_pdma) { +- dma_free_coherent(eth->dev, ++ dma_free_coherent(eth->dma_dev, + MTK_DMA_SIZE * sizeof(*ring->dma_pdma), + ring->dma_pdma, + ring->phys_pdma); +@@ -1747,18 +1748,18 @@ static int mtk_rx_alloc(struct mtk_eth * + return -ENOMEM; + } + +- ring->dma = dma_alloc_coherent(eth->dev, ++ ring->dma = dma_alloc_coherent(eth->dma_dev, + rx_dma_size * sizeof(*ring->dma), + &ring->phys, GFP_ATOMIC); + if (!ring->dma) + return -ENOMEM; + + for (i = 0; i < rx_dma_size; i++) { +- dma_addr_t dma_addr = dma_map_single(eth->dev, ++ dma_addr_t dma_addr = dma_map_single(eth->dma_dev, + ring->data[i] + NET_SKB_PAD + eth->ip_align, + ring->buf_size, + DMA_FROM_DEVICE); +- if (unlikely(dma_mapping_error(eth->dev, dma_addr))) ++ if (unlikely(dma_mapping_error(eth->dma_dev, dma_addr))) + return -ENOMEM; + ring->dma[i].rxd1 = (unsigned int)dma_addr; + +@@ -1794,7 +1795,7 @@ static void mtk_rx_clean(struct mtk_eth + continue; + if (!ring->dma[i].rxd1) + continue; +- dma_unmap_single(eth->dev, ++ dma_unmap_single(eth->dma_dev, + ring->dma[i].rxd1, + ring->buf_size, + DMA_FROM_DEVICE); +@@ -1805,7 +1806,7 @@ static void mtk_rx_clean(struct mtk_eth + } + + if (ring->dma) { +- dma_free_coherent(eth->dev, ++ dma_free_coherent(eth->dma_dev, + ring->dma_size * sizeof(*ring->dma), + ring->dma, + ring->phys); +@@ -2161,7 +2162,7 @@ static void mtk_dma_free(struct mtk_eth + if (eth->netdev[i]) + netdev_reset_queue(eth->netdev[i]); + if (eth->scratch_ring) { +- dma_free_coherent(eth->dev, ++ dma_free_coherent(eth->dma_dev, + MTK_DMA_SIZE * sizeof(struct mtk_tx_dma), + eth->scratch_ring, + eth->phy_scratch_ring); +@@ -2511,6 +2512,8 @@ static void mtk_dim_tx(struct work_struc + + static int mtk_hw_init(struct mtk_eth *eth) + { ++ u32 dma_mask = ETHSYS_DMA_AG_MAP_PDMA | ETHSYS_DMA_AG_MAP_QDMA | ++ ETHSYS_DMA_AG_MAP_PPE; + int i, val, ret; + + if (test_and_set_bit(MTK_HW_INIT, ð->state)) +@@ -2523,6 +2526,10 @@ static int mtk_hw_init(struct mtk_eth *e + if (ret) + goto err_disable_pm; + ++ if (eth->ethsys) ++ regmap_update_bits(eth->ethsys, ETHSYS_DMA_AG_MAP, dma_mask, ++ of_dma_is_coherent(eth->dma_dev->of_node) * dma_mask); ++ + if (MTK_HAS_CAPS(eth->soc->caps, MTK_SOC_MT7628)) { + ret = device_reset(eth->dev); + if (ret) { +@@ -3076,6 +3083,35 @@ free_netdev: + return err; + } + ++void mtk_eth_set_dma_device(struct mtk_eth *eth, struct device *dma_dev) ++{ ++ struct net_device *dev, *tmp; ++ LIST_HEAD(dev_list); ++ int i; ++ ++ rtnl_lock(); ++ ++ for (i = 0; i < MTK_MAC_COUNT; i++) { ++ dev = eth->netdev[i]; ++ ++ if (!dev || !(dev->flags & IFF_UP)) ++ continue; ++ ++ list_add_tail(&dev->close_list, &dev_list); ++ } ++ ++ dev_close_many(&dev_list, false); ++ ++ eth->dma_dev = dma_dev; ++ ++ list_for_each_entry_safe(dev, tmp, &dev_list, close_list) { ++ list_del_init(&dev->close_list); ++ dev_open(dev, NULL); ++ } ++ ++ rtnl_unlock(); ++} ++ + static int mtk_probe(struct platform_device *pdev) + { + struct device_node *mac_np; +@@ -3089,6 +3125,7 @@ static int mtk_probe(struct platform_dev + eth->soc = of_device_get_match_data(&pdev->dev); + + eth->dev = &pdev->dev; ++ eth->dma_dev = &pdev->dev; + eth->base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(eth->base)) + return PTR_ERR(eth->base); +@@ -3137,6 +3174,16 @@ static int mtk_probe(struct platform_dev + } + } + ++ if (of_dma_is_coherent(pdev->dev.of_node)) { ++ struct regmap *cci; ++ ++ cci = syscon_regmap_lookup_by_phandle(pdev->dev.of_node, ++ "mediatek,cci-control"); ++ /* enable CPU/bus coherency */ ++ if (!IS_ERR(cci)) ++ regmap_write(cci, 0, 3); ++ } ++ + if (MTK_HAS_CAPS(eth->soc->caps, MTK_SGMII)) { + eth->sgmii = devm_kzalloc(eth->dev, sizeof(*eth->sgmii), + GFP_KERNEL); +--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h ++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h +@@ -462,6 +462,12 @@ + #define RSTCTRL_FE BIT(6) + #define RSTCTRL_PPE BIT(31) + ++/* ethernet dma channel agent map */ ++#define ETHSYS_DMA_AG_MAP 0x408 ++#define ETHSYS_DMA_AG_MAP_PDMA BIT(0) ++#define ETHSYS_DMA_AG_MAP_QDMA BIT(1) ++#define ETHSYS_DMA_AG_MAP_PPE BIT(2) ++ + /* SGMII subsystem config registers */ + /* Register to auto-negotiation restart */ + #define SGMSYS_PCS_CONTROL_1 0x0 +@@ -879,6 +885,7 @@ struct mtk_sgmii { + /* struct mtk_eth - This is the main datasructure for holding the state + * of the driver + * @dev: The device pointer ++ * @dev: The device pointer used for dma mapping/alloc + * @base: The mapped register i/o base + * @page_lock: Make sure that register operations are atomic + * @tx_irq__lock: Make sure that IRQ register operations are atomic +@@ -922,6 +929,7 @@ struct mtk_sgmii { + + struct mtk_eth { + struct device *dev; ++ struct device *dma_dev; + void __iomem *base; + spinlock_t page_lock; + spinlock_t tx_irq_lock; +@@ -1020,6 +1028,7 @@ int mtk_gmac_rgmii_path_setup(struct mtk + int mtk_eth_offload_init(struct mtk_eth *eth); + int mtk_eth_setup_tc(struct net_device *dev, enum tc_setup_type type, + void *type_data); ++void mtk_eth_set_dma_device(struct mtk_eth *eth, struct device *dma_dev); + + + #endif /* MTK_ETH_H */ diff --git a/target/linux/generic/backport-6.1/702-v5.19-01-arm64-dts-mediatek-mt7622-add-support-for-coherent-D.patch b/target/linux/generic/backport-6.1/702-v5.19-01-arm64-dts-mediatek-mt7622-add-support-for-coherent-D.patch new file mode 100644 index 0000000000..d9015d4805 --- /dev/null +++ b/target/linux/generic/backport-6.1/702-v5.19-01-arm64-dts-mediatek-mt7622-add-support-for-coherent-D.patch @@ -0,0 +1,30 @@ +From: Felix Fietkau +Date: Mon, 7 Feb 2022 10:27:22 +0100 +Subject: [PATCH] arm64: dts: mediatek: mt7622: add support for coherent + DMA + +It improves performance by eliminating the need for a cache flush on rx and tx + +Signed-off-by: Felix Fietkau +--- + +--- a/arch/arm64/boot/dts/mediatek/mt7622.dtsi ++++ b/arch/arm64/boot/dts/mediatek/mt7622.dtsi +@@ -357,7 +357,7 @@ + }; + + cci_control2: slave-if@5000 { +- compatible = "arm,cci-400-ctrl-if"; ++ compatible = "arm,cci-400-ctrl-if", "syscon"; + interface-type = "ace"; + reg = <0x5000 0x1000>; + }; +@@ -937,6 +937,8 @@ + power-domains = <&scpsys MT7622_POWER_DOMAIN_ETHSYS>; + mediatek,ethsys = <ðsys>; + mediatek,sgmiisys = <&sgmiisys>; ++ mediatek,cci-control = <&cci_control2>; ++ dma-coherent; + #address-cells = <1>; + #size-cells = <0>; + status = "disabled"; diff --git a/target/linux/generic/backport-6.1/702-v5.19-02-net-ethernet-mtk_eth_soc-add-support-for-Wireless-Et.patch b/target/linux/generic/backport-6.1/702-v5.19-02-net-ethernet-mtk_eth_soc-add-support-for-Wireless-Et.patch new file mode 100644 index 0000000000..502d1d0e9b --- /dev/null +++ b/target/linux/generic/backport-6.1/702-v5.19-02-net-ethernet-mtk_eth_soc-add-support-for-Wireless-Et.patch @@ -0,0 +1,1679 @@ +From: Felix Fietkau +Date: Sat, 5 Feb 2022 17:56:08 +0100 +Subject: [PATCH] net: ethernet: mtk_eth_soc: add support for Wireless + Ethernet Dispatch (WED) + +The Wireless Ethernet Dispatch subsystem on the MT7622 SoC can be +configured to intercept and handle access to the DMA queues and +PCIe interrupts for a MT7615/MT7915 wireless card. +It can manage the internal WDMA (Wireless DMA) controller, which allows +ethernet packets to be passed from the packet switch engine (PSE) to the +wireless card, bypassing the CPU entirely. +This can be used to implement hardware flow offloading from ethernet to +WLAN. + +Signed-off-by: Felix Fietkau +--- + create mode 100644 drivers/net/ethernet/mediatek/mtk_wed.c + create mode 100644 drivers/net/ethernet/mediatek/mtk_wed.h + create mode 100644 drivers/net/ethernet/mediatek/mtk_wed_debugfs.c + create mode 100644 drivers/net/ethernet/mediatek/mtk_wed_ops.c + create mode 100644 drivers/net/ethernet/mediatek/mtk_wed_regs.h + create mode 100644 include/linux/soc/mediatek/mtk_wed.h + +--- a/drivers/net/ethernet/mediatek/Kconfig ++++ b/drivers/net/ethernet/mediatek/Kconfig +@@ -7,6 +7,10 @@ config NET_VENDOR_MEDIATEK + + if NET_VENDOR_MEDIATEK + ++config NET_MEDIATEK_SOC_WED ++ depends on ARCH_MEDIATEK || COMPILE_TEST ++ def_bool NET_MEDIATEK_SOC != n ++ + config NET_MEDIATEK_SOC + tristate "MediaTek SoC Gigabit Ethernet support" + depends on NET_DSA || !NET_DSA +--- a/drivers/net/ethernet/mediatek/Makefile ++++ b/drivers/net/ethernet/mediatek/Makefile +@@ -5,4 +5,9 @@ + + obj-$(CONFIG_NET_MEDIATEK_SOC) += mtk_eth.o + mtk_eth-y := mtk_eth_soc.o mtk_sgmii.o mtk_eth_path.o mtk_ppe.o mtk_ppe_debugfs.o mtk_ppe_offload.o ++mtk_eth-$(CONFIG_NET_MEDIATEK_SOC_WED) += mtk_wed.o ++ifdef CONFIG_DEBUG_FS ++mtk_eth-$(CONFIG_NET_MEDIATEK_SOC_WED) += mtk_wed_debugfs.o ++endif ++obj-$(CONFIG_NET_MEDIATEK_SOC_WED) += mtk_wed_ops.o + obj-$(CONFIG_NET_MEDIATEK_STAR_EMAC) += mtk_star_emac.o +--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c ++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c +@@ -24,6 +24,7 @@ + #include + + #include "mtk_eth_soc.h" ++#include "mtk_wed.h" + + static int mtk_msg_level = -1; + module_param_named(msg_level, mtk_msg_level, int, 0); +@@ -3206,6 +3207,22 @@ static int mtk_probe(struct platform_dev + } + } + ++ for (i = 0;; i++) { ++ struct device_node *np = of_parse_phandle(pdev->dev.of_node, ++ "mediatek,wed", i); ++ static const u32 wdma_regs[] = { ++ MTK_WDMA0_BASE, ++ MTK_WDMA1_BASE ++ }; ++ void __iomem *wdma; ++ ++ if (!np || i >= ARRAY_SIZE(wdma_regs)) ++ break; ++ ++ wdma = eth->base + wdma_regs[i]; ++ mtk_wed_add_hw(np, eth, wdma, i); ++ } ++ + for (i = 0; i < 3; i++) { + if (MTK_HAS_CAPS(eth->soc->caps, MTK_SHARED_INT) && i > 0) + eth->irq[i] = eth->irq[0]; +--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h ++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h +@@ -295,6 +295,9 @@ + #define MTK_GDM1_TX_GPCNT 0x2438 + #define MTK_STAT_OFFSET 0x40 + ++#define MTK_WDMA0_BASE 0x2800 ++#define MTK_WDMA1_BASE 0x2c00 ++ + /* QDMA descriptor txd4 */ + #define TX_DMA_CHKSUM (0x7 << 29) + #define TX_DMA_TSO BIT(28) +--- /dev/null ++++ b/drivers/net/ethernet/mediatek/mtk_wed.c +@@ -0,0 +1,875 @@ ++// SPDX-License-Identifier: GPL-2.0-only ++/* Copyright (C) 2021 Felix Fietkau */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include "mtk_eth_soc.h" ++#include "mtk_wed_regs.h" ++#include "mtk_wed.h" ++#include "mtk_ppe.h" ++ ++#define MTK_PCIE_BASE(n) (0x1a143000 + (n) * 0x2000) ++ ++#define MTK_WED_PKT_SIZE 1900 ++#define MTK_WED_BUF_SIZE 2048 ++#define MTK_WED_BUF_PER_PAGE (PAGE_SIZE / 2048) ++ ++#define MTK_WED_TX_RING_SIZE 2048 ++#define MTK_WED_WDMA_RING_SIZE 1024 ++ ++static struct mtk_wed_hw *hw_list[2]; ++static DEFINE_MUTEX(hw_lock); ++ ++static void ++wed_m32(struct mtk_wed_device *dev, u32 reg, u32 mask, u32 val) ++{ ++ regmap_update_bits(dev->hw->regs, reg, mask | val, val); ++} ++ ++static void ++wed_set(struct mtk_wed_device *dev, u32 reg, u32 mask) ++{ ++ return wed_m32(dev, reg, 0, mask); ++} ++ ++static void ++wed_clr(struct mtk_wed_device *dev, u32 reg, u32 mask) ++{ ++ return wed_m32(dev, reg, mask, 0); ++} ++ ++static void ++wdma_m32(struct mtk_wed_device *dev, u32 reg, u32 mask, u32 val) ++{ ++ wdma_w32(dev, reg, (wdma_r32(dev, reg) & ~mask) | val); ++} ++ ++static void ++wdma_set(struct mtk_wed_device *dev, u32 reg, u32 mask) ++{ ++ wdma_m32(dev, reg, 0, mask); ++} ++ ++static u32 ++mtk_wed_read_reset(struct mtk_wed_device *dev) ++{ ++ return wed_r32(dev, MTK_WED_RESET); ++} ++ ++static void ++mtk_wed_reset(struct mtk_wed_device *dev, u32 mask) ++{ ++ u32 status; ++ ++ wed_w32(dev, MTK_WED_RESET, mask); ++ if (readx_poll_timeout(mtk_wed_read_reset, dev, status, ++ !(status & mask), 0, 1000)) ++ WARN_ON_ONCE(1); ++} ++ ++static struct mtk_wed_hw * ++mtk_wed_assign(struct mtk_wed_device *dev) ++{ ++ struct mtk_wed_hw *hw; ++ ++ hw = hw_list[pci_domain_nr(dev->wlan.pci_dev->bus)]; ++ if (!hw || hw->wed_dev) ++ return NULL; ++ ++ hw->wed_dev = dev; ++ return hw; ++} ++ ++static int ++mtk_wed_buffer_alloc(struct mtk_wed_device *dev) ++{ ++ struct mtk_wdma_desc *desc; ++ dma_addr_t desc_phys; ++ void **page_list; ++ int token = dev->wlan.token_start; ++ int ring_size; ++ int n_pages; ++ int i, page_idx; ++ ++ ring_size = dev->wlan.nbuf & ~(MTK_WED_BUF_PER_PAGE - 1); ++ n_pages = ring_size / MTK_WED_BUF_PER_PAGE; ++ ++ page_list = kcalloc(n_pages, sizeof(*page_list), GFP_KERNEL); ++ if (!page_list) ++ return -ENOMEM; ++ ++ dev->buf_ring.size = ring_size; ++ dev->buf_ring.pages = page_list; ++ ++ desc = dma_alloc_coherent(dev->hw->dev, ring_size * sizeof(*desc), ++ &desc_phys, GFP_KERNEL); ++ if (!desc) ++ return -ENOMEM; ++ ++ dev->buf_ring.desc = desc; ++ dev->buf_ring.desc_phys = desc_phys; ++ ++ for (i = 0, page_idx = 0; i < ring_size; i += MTK_WED_BUF_PER_PAGE) { ++ dma_addr_t page_phys, buf_phys; ++ struct page *page; ++ void *buf; ++ int s; ++ ++ page = __dev_alloc_pages(GFP_KERNEL, 0); ++ if (!page) ++ return -ENOMEM; ++ ++ page_phys = dma_map_page(dev->hw->dev, page, 0, PAGE_SIZE, ++ DMA_BIDIRECTIONAL); ++ if (dma_mapping_error(dev->hw->dev, page_phys)) { ++ __free_page(page); ++ return -ENOMEM; ++ } ++ ++ page_list[page_idx++] = page; ++ dma_sync_single_for_cpu(dev->hw->dev, page_phys, PAGE_SIZE, ++ DMA_BIDIRECTIONAL); ++ ++ buf = page_to_virt(page); ++ buf_phys = page_phys; ++ ++ for (s = 0; s < MTK_WED_BUF_PER_PAGE; s++) { ++ u32 txd_size; ++ ++ txd_size = dev->wlan.init_buf(buf, buf_phys, token++); ++ ++ desc->buf0 = buf_phys; ++ desc->buf1 = buf_phys + txd_size; ++ desc->ctrl = FIELD_PREP(MTK_WDMA_DESC_CTRL_LEN0, ++ txd_size) | ++ FIELD_PREP(MTK_WDMA_DESC_CTRL_LEN1, ++ MTK_WED_BUF_SIZE - txd_size) | ++ MTK_WDMA_DESC_CTRL_LAST_SEG1; ++ desc->info = 0; ++ desc++; ++ ++ buf += MTK_WED_BUF_SIZE; ++ buf_phys += MTK_WED_BUF_SIZE; ++ } ++ ++ dma_sync_single_for_device(dev->hw->dev, page_phys, PAGE_SIZE, ++ DMA_BIDIRECTIONAL); ++ } ++ ++ return 0; ++} ++ ++static void ++mtk_wed_free_buffer(struct mtk_wed_device *dev) ++{ ++ struct mtk_wdma_desc *desc = dev->buf_ring.desc; ++ void **page_list = dev->buf_ring.pages; ++ int page_idx; ++ int i; ++ ++ if (!page_list) ++ return; ++ ++ if (!desc) ++ goto free_pagelist; ++ ++ for (i = 0, page_idx = 0; i < dev->buf_ring.size; i += MTK_WED_BUF_PER_PAGE) { ++ void *page = page_list[page_idx++]; ++ ++ if (!page) ++ break; ++ ++ dma_unmap_page(dev->hw->dev, desc[i].buf0, ++ PAGE_SIZE, DMA_BIDIRECTIONAL); ++ __free_page(page); ++ } ++ ++ dma_free_coherent(dev->hw->dev, dev->buf_ring.size * sizeof(*desc), ++ desc, dev->buf_ring.desc_phys); ++ ++free_pagelist: ++ kfree(page_list); ++} ++ ++static void ++mtk_wed_free_ring(struct mtk_wed_device *dev, struct mtk_wed_ring *ring) ++{ ++ if (!ring->desc) ++ return; ++ ++ dma_free_coherent(dev->hw->dev, ring->size * sizeof(*ring->desc), ++ ring->desc, ring->desc_phys); ++} ++ ++static void ++mtk_wed_free_tx_rings(struct mtk_wed_device *dev) ++{ ++ int i; ++ ++ for (i = 0; i < ARRAY_SIZE(dev->tx_ring); i++) ++ mtk_wed_free_ring(dev, &dev->tx_ring[i]); ++ for (i = 0; i < ARRAY_SIZE(dev->tx_wdma); i++) ++ mtk_wed_free_ring(dev, &dev->tx_wdma[i]); ++} ++ ++static void ++mtk_wed_set_ext_int(struct mtk_wed_device *dev, bool en) ++{ ++ u32 mask = MTK_WED_EXT_INT_STATUS_ERROR_MASK; ++ ++ if (!dev->hw->num_flows) ++ mask &= ~MTK_WED_EXT_INT_STATUS_TKID_WO_PYLD; ++ ++ wed_w32(dev, MTK_WED_EXT_INT_MASK, en ? mask : 0); ++ wed_r32(dev, MTK_WED_EXT_INT_MASK); ++} ++ ++static void ++mtk_wed_stop(struct mtk_wed_device *dev) ++{ ++ regmap_write(dev->hw->mirror, dev->hw->index * 4, 0); ++ mtk_wed_set_ext_int(dev, false); ++ ++ wed_clr(dev, MTK_WED_CTRL, ++ MTK_WED_CTRL_WDMA_INT_AGENT_EN | ++ MTK_WED_CTRL_WPDMA_INT_AGENT_EN | ++ MTK_WED_CTRL_WED_TX_BM_EN | ++ MTK_WED_CTRL_WED_TX_FREE_AGENT_EN); ++ wed_w32(dev, MTK_WED_WPDMA_INT_TRIGGER, 0); ++ wed_w32(dev, MTK_WED_WDMA_INT_TRIGGER, 0); ++ wdma_w32(dev, MTK_WDMA_INT_MASK, 0); ++ wdma_w32(dev, MTK_WDMA_INT_GRP2, 0); ++ wed_w32(dev, MTK_WED_WPDMA_INT_MASK, 0); ++ ++ wed_clr(dev, MTK_WED_GLO_CFG, ++ MTK_WED_GLO_CFG_TX_DMA_EN | ++ MTK_WED_GLO_CFG_RX_DMA_EN); ++ wed_clr(dev, MTK_WED_WPDMA_GLO_CFG, ++ MTK_WED_WPDMA_GLO_CFG_TX_DRV_EN | ++ MTK_WED_WPDMA_GLO_CFG_RX_DRV_EN); ++ wed_clr(dev, MTK_WED_WDMA_GLO_CFG, ++ MTK_WED_WDMA_GLO_CFG_RX_DRV_EN); ++} ++ ++static void ++mtk_wed_detach(struct mtk_wed_device *dev) ++{ ++ struct device_node *wlan_node = dev->wlan.pci_dev->dev.of_node; ++ struct mtk_wed_hw *hw = dev->hw; ++ ++ mutex_lock(&hw_lock); ++ ++ mtk_wed_stop(dev); ++ ++ wdma_w32(dev, MTK_WDMA_RESET_IDX, MTK_WDMA_RESET_IDX_RX); ++ wdma_w32(dev, MTK_WDMA_RESET_IDX, 0); ++ ++ mtk_wed_reset(dev, MTK_WED_RESET_WED); ++ ++ mtk_wed_free_buffer(dev); ++ mtk_wed_free_tx_rings(dev); ++ ++ if (of_dma_is_coherent(wlan_node)) ++ regmap_update_bits(hw->hifsys, HIFSYS_DMA_AG_MAP, ++ BIT(hw->index), BIT(hw->index)); ++ ++ if (!hw_list[!hw->index]->wed_dev && ++ hw->eth->dma_dev != hw->eth->dev) ++ mtk_eth_set_dma_device(hw->eth, hw->eth->dev); ++ ++ memset(dev, 0, sizeof(*dev)); ++ module_put(THIS_MODULE); ++ ++ hw->wed_dev = NULL; ++ mutex_unlock(&hw_lock); ++} ++ ++static void ++mtk_wed_hw_init_early(struct mtk_wed_device *dev) ++{ ++ u32 mask, set; ++ u32 offset; ++ ++ mtk_wed_stop(dev); ++ mtk_wed_reset(dev, MTK_WED_RESET_WED); ++ ++ mask = MTK_WED_WDMA_GLO_CFG_BT_SIZE | ++ MTK_WED_WDMA_GLO_CFG_DYNAMIC_DMAD_RECYCLE | ++ MTK_WED_WDMA_GLO_CFG_RX_DIS_FSM_AUTO_IDLE; ++ set = FIELD_PREP(MTK_WED_WDMA_GLO_CFG_BT_SIZE, 2) | ++ MTK_WED_WDMA_GLO_CFG_DYNAMIC_SKIP_DMAD_PREP | ++ MTK_WED_WDMA_GLO_CFG_IDLE_DMAD_SUPPLY; ++ wed_m32(dev, MTK_WED_WDMA_GLO_CFG, mask, set); ++ ++ wdma_set(dev, MTK_WDMA_GLO_CFG, MTK_WDMA_GLO_CFG_RX_INFO_PRERES); ++ ++ offset = dev->hw->index ? 0x04000400 : 0; ++ wed_w32(dev, MTK_WED_WDMA_OFFSET0, 0x2a042a20 + offset); ++ wed_w32(dev, MTK_WED_WDMA_OFFSET1, 0x29002800 + offset); ++ ++ wed_w32(dev, MTK_WED_PCIE_CFG_BASE, MTK_PCIE_BASE(dev->hw->index)); ++ wed_w32(dev, MTK_WED_WPDMA_CFG_BASE, dev->wlan.wpdma_phys); ++} ++ ++static void ++mtk_wed_hw_init(struct mtk_wed_device *dev) ++{ ++ if (dev->init_done) ++ return; ++ ++ dev->init_done = true; ++ mtk_wed_set_ext_int(dev, false); ++ wed_w32(dev, MTK_WED_TX_BM_CTRL, ++ MTK_WED_TX_BM_CTRL_PAUSE | ++ FIELD_PREP(MTK_WED_TX_BM_CTRL_VLD_GRP_NUM, ++ dev->buf_ring.size / 128) | ++ FIELD_PREP(MTK_WED_TX_BM_CTRL_RSV_GRP_NUM, ++ MTK_WED_TX_RING_SIZE / 256)); ++ ++ wed_w32(dev, MTK_WED_TX_BM_BASE, dev->buf_ring.desc_phys); ++ ++ wed_w32(dev, MTK_WED_TX_BM_TKID, ++ FIELD_PREP(MTK_WED_TX_BM_TKID_START, ++ dev->wlan.token_start) | ++ FIELD_PREP(MTK_WED_TX_BM_TKID_END, ++ dev->wlan.token_start + dev->wlan.nbuf - 1)); ++ ++ wed_w32(dev, MTK_WED_TX_BM_BUF_LEN, MTK_WED_PKT_SIZE); ++ ++ wed_w32(dev, MTK_WED_TX_BM_DYN_THR, ++ FIELD_PREP(MTK_WED_TX_BM_DYN_THR_LO, 1) | ++ MTK_WED_TX_BM_DYN_THR_HI); ++ ++ mtk_wed_reset(dev, MTK_WED_RESET_TX_BM); ++ ++ wed_set(dev, MTK_WED_CTRL, ++ MTK_WED_CTRL_WED_TX_BM_EN | ++ MTK_WED_CTRL_WED_TX_FREE_AGENT_EN); ++ ++ wed_clr(dev, MTK_WED_TX_BM_CTRL, MTK_WED_TX_BM_CTRL_PAUSE); ++} ++ ++static void ++mtk_wed_ring_reset(struct mtk_wdma_desc *desc, int size) ++{ ++ int i; ++ ++ for (i = 0; i < size; i++) { ++ desc[i].buf0 = 0; ++ desc[i].ctrl = cpu_to_le32(MTK_WDMA_DESC_CTRL_DMA_DONE); ++ desc[i].buf1 = 0; ++ desc[i].info = 0; ++ } ++} ++ ++static u32 ++mtk_wed_check_busy(struct mtk_wed_device *dev) ++{ ++ if (wed_r32(dev, MTK_WED_GLO_CFG) & MTK_WED_GLO_CFG_TX_DMA_BUSY) ++ return true; ++ ++ if (wed_r32(dev, MTK_WED_WPDMA_GLO_CFG) & ++ MTK_WED_WPDMA_GLO_CFG_TX_DRV_BUSY) ++ return true; ++ ++ if (wed_r32(dev, MTK_WED_CTRL) & MTK_WED_CTRL_WDMA_INT_AGENT_BUSY) ++ return true; ++ ++ if (wed_r32(dev, MTK_WED_WDMA_GLO_CFG) & ++ MTK_WED_WDMA_GLO_CFG_RX_DRV_BUSY) ++ return true; ++ ++ if (wdma_r32(dev, MTK_WDMA_GLO_CFG) & ++ MTK_WED_WDMA_GLO_CFG_RX_DRV_BUSY) ++ return true; ++ ++ if (wed_r32(dev, MTK_WED_CTRL) & ++ (MTK_WED_CTRL_WED_TX_BM_BUSY | MTK_WED_CTRL_WED_TX_FREE_AGENT_BUSY)) ++ return true; ++ ++ return false; ++} ++ ++static int ++mtk_wed_poll_busy(struct mtk_wed_device *dev) ++{ ++ int sleep = 15000; ++ int timeout = 100 * sleep; ++ u32 val; ++ ++ return read_poll_timeout(mtk_wed_check_busy, val, !val, sleep, ++ timeout, false, dev); ++} ++ ++static void ++mtk_wed_reset_dma(struct mtk_wed_device *dev) ++{ ++ bool busy = false; ++ u32 val; ++ int i; ++ ++ for (i = 0; i < ARRAY_SIZE(dev->tx_ring); i++) { ++ struct mtk_wdma_desc *desc = dev->tx_ring[i].desc; ++ ++ if (!desc) ++ continue; ++ ++ mtk_wed_ring_reset(desc, MTK_WED_TX_RING_SIZE); ++ } ++ ++ if (mtk_wed_poll_busy(dev)) ++ busy = mtk_wed_check_busy(dev); ++ ++ if (busy) { ++ mtk_wed_reset(dev, MTK_WED_RESET_WED_TX_DMA); ++ } else { ++ wed_w32(dev, MTK_WED_RESET_IDX, ++ MTK_WED_RESET_IDX_TX | ++ MTK_WED_RESET_IDX_RX); ++ wed_w32(dev, MTK_WED_RESET_IDX, 0); ++ } ++ ++ wdma_w32(dev, MTK_WDMA_RESET_IDX, MTK_WDMA_RESET_IDX_RX); ++ wdma_w32(dev, MTK_WDMA_RESET_IDX, 0); ++ ++ if (busy) { ++ mtk_wed_reset(dev, MTK_WED_RESET_WDMA_INT_AGENT); ++ mtk_wed_reset(dev, MTK_WED_RESET_WDMA_RX_DRV); ++ } else { ++ wed_w32(dev, MTK_WED_WDMA_RESET_IDX, ++ MTK_WED_WDMA_RESET_IDX_RX | MTK_WED_WDMA_RESET_IDX_DRV); ++ wed_w32(dev, MTK_WED_WDMA_RESET_IDX, 0); ++ ++ wed_set(dev, MTK_WED_WDMA_GLO_CFG, ++ MTK_WED_WDMA_GLO_CFG_RST_INIT_COMPLETE); ++ ++ wed_clr(dev, MTK_WED_WDMA_GLO_CFG, ++ MTK_WED_WDMA_GLO_CFG_RST_INIT_COMPLETE); ++ } ++ ++ for (i = 0; i < 100; i++) { ++ val = wed_r32(dev, MTK_WED_TX_BM_INTF); ++ if (FIELD_GET(MTK_WED_TX_BM_INTF_TKFIFO_FDEP, val) == 0x40) ++ break; ++ } ++ ++ mtk_wed_reset(dev, MTK_WED_RESET_TX_FREE_AGENT); ++ mtk_wed_reset(dev, MTK_WED_RESET_TX_BM); ++ ++ if (busy) { ++ mtk_wed_reset(dev, MTK_WED_RESET_WPDMA_INT_AGENT); ++ mtk_wed_reset(dev, MTK_WED_RESET_WPDMA_TX_DRV); ++ mtk_wed_reset(dev, MTK_WED_RESET_WPDMA_RX_DRV); ++ } else { ++ wed_w32(dev, MTK_WED_WPDMA_RESET_IDX, ++ MTK_WED_WPDMA_RESET_IDX_TX | ++ MTK_WED_WPDMA_RESET_IDX_RX); ++ wed_w32(dev, MTK_WED_WPDMA_RESET_IDX, 0); ++ } ++ ++} ++ ++static int ++mtk_wed_ring_alloc(struct mtk_wed_device *dev, struct mtk_wed_ring *ring, ++ int size) ++{ ++ ring->desc = dma_alloc_coherent(dev->hw->dev, ++ size * sizeof(*ring->desc), ++ &ring->desc_phys, GFP_KERNEL); ++ if (!ring->desc) ++ return -ENOMEM; ++ ++ ring->size = size; ++ mtk_wed_ring_reset(ring->desc, size); ++ ++ return 0; ++} ++ ++static int ++mtk_wed_wdma_ring_setup(struct mtk_wed_device *dev, int idx, int size) ++{ ++ struct mtk_wed_ring *wdma = &dev->tx_wdma[idx]; ++ ++ if (mtk_wed_ring_alloc(dev, wdma, MTK_WED_WDMA_RING_SIZE)) ++ return -ENOMEM; ++ ++ wdma_w32(dev, MTK_WDMA_RING_RX(idx) + MTK_WED_RING_OFS_BASE, ++ wdma->desc_phys); ++ wdma_w32(dev, MTK_WDMA_RING_RX(idx) + MTK_WED_RING_OFS_COUNT, ++ size); ++ wdma_w32(dev, MTK_WDMA_RING_RX(idx) + MTK_WED_RING_OFS_CPU_IDX, 0); ++ ++ wed_w32(dev, MTK_WED_WDMA_RING_RX(idx) + MTK_WED_RING_OFS_BASE, ++ wdma->desc_phys); ++ wed_w32(dev, MTK_WED_WDMA_RING_RX(idx) + MTK_WED_RING_OFS_COUNT, ++ size); ++ ++ return 0; ++} ++ ++static void ++mtk_wed_start(struct mtk_wed_device *dev, u32 irq_mask) ++{ ++ u32 wdma_mask; ++ u32 val; ++ int i; ++ ++ for (i = 0; i < ARRAY_SIZE(dev->tx_wdma); i++) ++ if (!dev->tx_wdma[i].desc) ++ mtk_wed_wdma_ring_setup(dev, i, 16); ++ ++ wdma_mask = FIELD_PREP(MTK_WDMA_INT_MASK_RX_DONE, GENMASK(1, 0)); ++ ++ mtk_wed_hw_init(dev); ++ ++ wed_set(dev, MTK_WED_CTRL, ++ MTK_WED_CTRL_WDMA_INT_AGENT_EN | ++ MTK_WED_CTRL_WPDMA_INT_AGENT_EN | ++ MTK_WED_CTRL_WED_TX_BM_EN | ++ MTK_WED_CTRL_WED_TX_FREE_AGENT_EN); ++ ++ wed_w32(dev, MTK_WED_PCIE_INT_TRIGGER, MTK_WED_PCIE_INT_TRIGGER_STATUS); ++ ++ wed_w32(dev, MTK_WED_WPDMA_INT_TRIGGER, ++ MTK_WED_WPDMA_INT_TRIGGER_RX_DONE | ++ MTK_WED_WPDMA_INT_TRIGGER_TX_DONE); ++ ++ wed_set(dev, MTK_WED_WPDMA_INT_CTRL, ++ MTK_WED_WPDMA_INT_CTRL_SUBRT_ADV); ++ ++ wed_w32(dev, MTK_WED_WDMA_INT_TRIGGER, wdma_mask); ++ wed_clr(dev, MTK_WED_WDMA_INT_CTRL, wdma_mask); ++ ++ wdma_w32(dev, MTK_WDMA_INT_MASK, wdma_mask); ++ wdma_w32(dev, MTK_WDMA_INT_GRP2, wdma_mask); ++ ++ wed_w32(dev, MTK_WED_WPDMA_INT_MASK, irq_mask); ++ wed_w32(dev, MTK_WED_INT_MASK, irq_mask); ++ ++ wed_set(dev, MTK_WED_GLO_CFG, ++ MTK_WED_GLO_CFG_TX_DMA_EN | ++ MTK_WED_GLO_CFG_RX_DMA_EN); ++ wed_set(dev, MTK_WED_WPDMA_GLO_CFG, ++ MTK_WED_WPDMA_GLO_CFG_TX_DRV_EN | ++ MTK_WED_WPDMA_GLO_CFG_RX_DRV_EN); ++ wed_set(dev, MTK_WED_WDMA_GLO_CFG, ++ MTK_WED_WDMA_GLO_CFG_RX_DRV_EN); ++ ++ mtk_wed_set_ext_int(dev, true); ++ val = dev->wlan.wpdma_phys | ++ MTK_PCIE_MIRROR_MAP_EN | ++ FIELD_PREP(MTK_PCIE_MIRROR_MAP_WED_ID, dev->hw->index); ++ ++ if (dev->hw->index) ++ val |= BIT(1); ++ val |= BIT(0); ++ regmap_write(dev->hw->mirror, dev->hw->index * 4, val); ++ ++ dev->running = true; ++} ++ ++static int ++mtk_wed_attach(struct mtk_wed_device *dev) ++ __releases(RCU) ++{ ++ struct mtk_wed_hw *hw; ++ int ret = 0; ++ ++ RCU_LOCKDEP_WARN(!rcu_read_lock_held(), ++ "mtk_wed_attach without holding the RCU read lock"); ++ ++ if (pci_domain_nr(dev->wlan.pci_dev->bus) > 1 || ++ !try_module_get(THIS_MODULE)) ++ ret = -ENODEV; ++ ++ rcu_read_unlock(); ++ ++ if (ret) ++ return ret; ++ ++ mutex_lock(&hw_lock); ++ ++ hw = mtk_wed_assign(dev); ++ if (!hw) { ++ module_put(THIS_MODULE); ++ ret = -ENODEV; ++ goto out; ++ } ++ ++ dev_info(&dev->wlan.pci_dev->dev, "attaching wed device %d\n", hw->index); ++ ++ dev->hw = hw; ++ dev->dev = hw->dev; ++ dev->irq = hw->irq; ++ dev->wdma_idx = hw->index; ++ ++ if (hw->eth->dma_dev == hw->eth->dev && ++ of_dma_is_coherent(hw->eth->dev->of_node)) ++ mtk_eth_set_dma_device(hw->eth, hw->dev); ++ ++ ret = mtk_wed_buffer_alloc(dev); ++ if (ret) { ++ mtk_wed_detach(dev); ++ goto out; ++ } ++ ++ mtk_wed_hw_init_early(dev); ++ regmap_update_bits(hw->hifsys, HIFSYS_DMA_AG_MAP, BIT(hw->index), 0); ++ ++out: ++ mutex_unlock(&hw_lock); ++ ++ return ret; ++} ++ ++static int ++mtk_wed_tx_ring_setup(struct mtk_wed_device *dev, int idx, void __iomem *regs) ++{ ++ struct mtk_wed_ring *ring = &dev->tx_ring[idx]; ++ ++ /* ++ * Tx ring redirection: ++ * Instead of configuring the WLAN PDMA TX ring directly, the WLAN ++ * driver allocated DMA ring gets configured into WED MTK_WED_RING_TX(n) ++ * registers. ++ * ++ * WED driver posts its own DMA ring as WLAN PDMA TX and configures it ++ * into MTK_WED_WPDMA_RING_TX(n) registers. ++ * It gets filled with packets picked up from WED TX ring and from ++ * WDMA RX. ++ */ ++ ++ BUG_ON(idx > ARRAY_SIZE(dev->tx_ring)); ++ ++ if (mtk_wed_ring_alloc(dev, ring, MTK_WED_TX_RING_SIZE)) ++ return -ENOMEM; ++ ++ if (mtk_wed_wdma_ring_setup(dev, idx, MTK_WED_WDMA_RING_SIZE)) ++ return -ENOMEM; ++ ++ ring->reg_base = MTK_WED_RING_TX(idx); ++ ring->wpdma = regs; ++ ++ /* WED -> WPDMA */ ++ wpdma_tx_w32(dev, idx, MTK_WED_RING_OFS_BASE, ring->desc_phys); ++ wpdma_tx_w32(dev, idx, MTK_WED_RING_OFS_COUNT, MTK_WED_TX_RING_SIZE); ++ wpdma_tx_w32(dev, idx, MTK_WED_RING_OFS_CPU_IDX, 0); ++ ++ wed_w32(dev, MTK_WED_WPDMA_RING_TX(idx) + MTK_WED_RING_OFS_BASE, ++ ring->desc_phys); ++ wed_w32(dev, MTK_WED_WPDMA_RING_TX(idx) + MTK_WED_RING_OFS_COUNT, ++ MTK_WED_TX_RING_SIZE); ++ wed_w32(dev, MTK_WED_WPDMA_RING_TX(idx) + MTK_WED_RING_OFS_CPU_IDX, 0); ++ ++ return 0; ++} ++ ++static int ++mtk_wed_txfree_ring_setup(struct mtk_wed_device *dev, void __iomem *regs) ++{ ++ struct mtk_wed_ring *ring = &dev->txfree_ring; ++ int i; ++ ++ /* ++ * For txfree event handling, the same DMA ring is shared between WED ++ * and WLAN. The WLAN driver accesses the ring index registers through ++ * WED ++ */ ++ ring->reg_base = MTK_WED_RING_RX(1); ++ ring->wpdma = regs; ++ ++ for (i = 0; i < 12; i += 4) { ++ u32 val = readl(regs + i); ++ ++ wed_w32(dev, MTK_WED_RING_RX(1) + i, val); ++ wed_w32(dev, MTK_WED_WPDMA_RING_RX(1) + i, val); ++ } ++ ++ return 0; ++} ++ ++static u32 ++mtk_wed_irq_get(struct mtk_wed_device *dev, u32 mask) ++{ ++ u32 val; ++ ++ val = wed_r32(dev, MTK_WED_EXT_INT_STATUS); ++ wed_w32(dev, MTK_WED_EXT_INT_STATUS, val); ++ val &= MTK_WED_EXT_INT_STATUS_ERROR_MASK; ++ if (!dev->hw->num_flows) ++ val &= ~MTK_WED_EXT_INT_STATUS_TKID_WO_PYLD; ++ if (val && net_ratelimit()) ++ pr_err("mtk_wed%d: error status=%08x\n", dev->hw->index, val); ++ ++ val = wed_r32(dev, MTK_WED_INT_STATUS); ++ val &= mask; ++ wed_w32(dev, MTK_WED_INT_STATUS, val); /* ACK */ ++ ++ return val; ++} ++ ++static void ++mtk_wed_irq_set_mask(struct mtk_wed_device *dev, u32 mask) ++{ ++ if (!dev->running) ++ return; ++ ++ mtk_wed_set_ext_int(dev, !!mask); ++ wed_w32(dev, MTK_WED_INT_MASK, mask); ++} ++ ++int mtk_wed_flow_add(int index) ++{ ++ struct mtk_wed_hw *hw = hw_list[index]; ++ int ret; ++ ++ if (!hw || !hw->wed_dev) ++ return -ENODEV; ++ ++ if (hw->num_flows) { ++ hw->num_flows++; ++ return 0; ++ } ++ ++ mutex_lock(&hw_lock); ++ if (!hw->wed_dev) { ++ ret = -ENODEV; ++ goto out; ++ } ++ ++ ret = hw->wed_dev->wlan.offload_enable(hw->wed_dev); ++ if (!ret) ++ hw->num_flows++; ++ mtk_wed_set_ext_int(hw->wed_dev, true); ++ ++out: ++ mutex_unlock(&hw_lock); ++ ++ return ret; ++} ++ ++void mtk_wed_flow_remove(int index) ++{ ++ struct mtk_wed_hw *hw = hw_list[index]; ++ ++ if (!hw) ++ return; ++ ++ if (--hw->num_flows) ++ return; ++ ++ mutex_lock(&hw_lock); ++ if (!hw->wed_dev) ++ goto out; ++ ++ hw->wed_dev->wlan.offload_disable(hw->wed_dev); ++ mtk_wed_set_ext_int(hw->wed_dev, true); ++ ++out: ++ mutex_unlock(&hw_lock); ++} ++ ++void mtk_wed_add_hw(struct device_node *np, struct mtk_eth *eth, ++ void __iomem *wdma, int index) ++{ ++ static const struct mtk_wed_ops wed_ops = { ++ .attach = mtk_wed_attach, ++ .tx_ring_setup = mtk_wed_tx_ring_setup, ++ .txfree_ring_setup = mtk_wed_txfree_ring_setup, ++ .start = mtk_wed_start, ++ .stop = mtk_wed_stop, ++ .reset_dma = mtk_wed_reset_dma, ++ .reg_read = wed_r32, ++ .reg_write = wed_w32, ++ .irq_get = mtk_wed_irq_get, ++ .irq_set_mask = mtk_wed_irq_set_mask, ++ .detach = mtk_wed_detach, ++ }; ++ struct device_node *eth_np = eth->dev->of_node; ++ struct platform_device *pdev; ++ struct mtk_wed_hw *hw; ++ struct regmap *regs; ++ int irq; ++ ++ if (!np) ++ return; ++ ++ pdev = of_find_device_by_node(np); ++ if (!pdev) ++ return; ++ ++ get_device(&pdev->dev); ++ irq = platform_get_irq(pdev, 0); ++ if (irq < 0) ++ return; ++ ++ regs = syscon_regmap_lookup_by_phandle(np, NULL); ++ if (!regs) ++ return; ++ ++ rcu_assign_pointer(mtk_soc_wed_ops, &wed_ops); ++ ++ mutex_lock(&hw_lock); ++ ++ if (WARN_ON(hw_list[index])) ++ goto unlock; ++ ++ hw = kzalloc(sizeof(*hw), GFP_KERNEL); ++ hw->node = np; ++ hw->regs = regs; ++ hw->eth = eth; ++ hw->dev = &pdev->dev; ++ hw->wdma = wdma; ++ hw->index = index; ++ hw->irq = irq; ++ hw->mirror = syscon_regmap_lookup_by_phandle(eth_np, ++ "mediatek,pcie-mirror"); ++ hw->hifsys = syscon_regmap_lookup_by_phandle(eth_np, ++ "mediatek,hifsys"); ++ if (IS_ERR(hw->mirror) || IS_ERR(hw->hifsys)) { ++ kfree(hw); ++ goto unlock; ++ } ++ ++ if (!index) { ++ regmap_write(hw->mirror, 0, 0); ++ regmap_write(hw->mirror, 4, 0); ++ } ++ mtk_wed_hw_add_debugfs(hw); ++ ++ hw_list[index] = hw; ++ ++unlock: ++ mutex_unlock(&hw_lock); ++} ++ ++void mtk_wed_exit(void) ++{ ++ int i; ++ ++ rcu_assign_pointer(mtk_soc_wed_ops, NULL); ++ ++ synchronize_rcu(); ++ ++ for (i = 0; i < ARRAY_SIZE(hw_list); i++) { ++ struct mtk_wed_hw *hw; ++ ++ hw = hw_list[i]; ++ if (!hw) ++ continue; ++ ++ hw_list[i] = NULL; ++ debugfs_remove(hw->debugfs_dir); ++ put_device(hw->dev); ++ kfree(hw); ++ } ++} +--- /dev/null ++++ b/drivers/net/ethernet/mediatek/mtk_wed.h +@@ -0,0 +1,128 @@ ++// SPDX-License-Identifier: GPL-2.0-only ++/* Copyright (C) 2021 Felix Fietkau */ ++ ++#ifndef __MTK_WED_PRIV_H ++#define __MTK_WED_PRIV_H ++ ++#include ++#include ++#include ++ ++struct mtk_eth; ++ ++struct mtk_wed_hw { ++ struct device_node *node; ++ struct mtk_eth *eth; ++ struct regmap *regs; ++ struct regmap *hifsys; ++ struct device *dev; ++ void __iomem *wdma; ++ struct regmap *mirror; ++ struct dentry *debugfs_dir; ++ struct mtk_wed_device *wed_dev; ++ u32 debugfs_reg; ++ u32 num_flows; ++ char dirname[5]; ++ int irq; ++ int index; ++}; ++ ++ ++#ifdef CONFIG_NET_MEDIATEK_SOC_WED ++static inline void ++wed_w32(struct mtk_wed_device *dev, u32 reg, u32 val) ++{ ++ regmap_write(dev->hw->regs, reg, val); ++} ++ ++static inline u32 ++wed_r32(struct mtk_wed_device *dev, u32 reg) ++{ ++ unsigned int val; ++ ++ regmap_read(dev->hw->regs, reg, &val); ++ ++ return val; ++} ++ ++static inline void ++wdma_w32(struct mtk_wed_device *dev, u32 reg, u32 val) ++{ ++ writel(val, dev->hw->wdma + reg); ++} ++ ++static inline u32 ++wdma_r32(struct mtk_wed_device *dev, u32 reg) ++{ ++ return readl(dev->hw->wdma + reg); ++} ++ ++static inline u32 ++wpdma_tx_r32(struct mtk_wed_device *dev, int ring, u32 reg) ++{ ++ if (!dev->tx_ring[ring].wpdma) ++ return 0; ++ ++ return readl(dev->tx_ring[ring].wpdma + reg); ++} ++ ++static inline void ++wpdma_tx_w32(struct mtk_wed_device *dev, int ring, u32 reg, u32 val) ++{ ++ if (!dev->tx_ring[ring].wpdma) ++ return; ++ ++ writel(val, dev->tx_ring[ring].wpdma + reg); ++} ++ ++static inline u32 ++wpdma_txfree_r32(struct mtk_wed_device *dev, u32 reg) ++{ ++ if (!dev->txfree_ring.wpdma) ++ return 0; ++ ++ return readl(dev->txfree_ring.wpdma + reg); ++} ++ ++static inline void ++wpdma_txfree_w32(struct mtk_wed_device *dev, u32 reg, u32 val) ++{ ++ if (!dev->txfree_ring.wpdma) ++ return; ++ ++ writel(val, dev->txfree_ring.wpdma + reg); ++} ++ ++void mtk_wed_add_hw(struct device_node *np, struct mtk_eth *eth, ++ void __iomem *wdma, int index); ++void mtk_wed_exit(void); ++int mtk_wed_flow_add(int index); ++void mtk_wed_flow_remove(int index); ++#else ++static inline void ++mtk_wed_add_hw(struct device_node *np, struct mtk_eth *eth, ++ void __iomem *wdma, int index) ++{ ++} ++static inline void ++mtk_wed_exit(void) ++{ ++} ++static inline int mtk_wed_flow_add(int index) ++{ ++ return -EINVAL; ++} ++static inline void mtk_wed_flow_remove(int index) ++{ ++} ++#endif ++ ++#ifdef CONFIG_DEBUG_FS ++void mtk_wed_hw_add_debugfs(struct mtk_wed_hw *hw); ++#else ++static inline void mtk_wed_hw_add_debugfs(struct mtk_wed_hw *hw) ++{ ++} ++#endif ++ ++#endif +--- /dev/null ++++ b/drivers/net/ethernet/mediatek/mtk_wed_debugfs.c +@@ -0,0 +1,175 @@ ++// SPDX-License-Identifier: GPL-2.0-only ++/* Copyright (C) 2021 Felix Fietkau */ ++ ++#include ++#include "mtk_wed.h" ++#include "mtk_wed_regs.h" ++ ++struct reg_dump { ++ const char *name; ++ u16 offset; ++ u8 type; ++ u8 base; ++}; ++ ++enum { ++ DUMP_TYPE_STRING, ++ DUMP_TYPE_WED, ++ DUMP_TYPE_WDMA, ++ DUMP_TYPE_WPDMA_TX, ++ DUMP_TYPE_WPDMA_TXFREE, ++}; ++ ++#define DUMP_STR(_str) { _str, 0, DUMP_TYPE_STRING } ++#define DUMP_REG(_reg, ...) { #_reg, MTK_##_reg, __VA_ARGS__ } ++#define DUMP_RING(_prefix, _base, ...) \ ++ { _prefix " BASE", _base, __VA_ARGS__ }, \ ++ { _prefix " CNT", _base + 0x4, __VA_ARGS__ }, \ ++ { _prefix " CIDX", _base + 0x8, __VA_ARGS__ }, \ ++ { _prefix " DIDX", _base + 0xc, __VA_ARGS__ } ++ ++#define DUMP_WED(_reg) DUMP_REG(_reg, DUMP_TYPE_WED) ++#define DUMP_WED_RING(_base) DUMP_RING(#_base, MTK_##_base, DUMP_TYPE_WED) ++ ++#define DUMP_WDMA(_reg) DUMP_REG(_reg, DUMP_TYPE_WDMA) ++#define DUMP_WDMA_RING(_base) DUMP_RING(#_base, MTK_##_base, DUMP_TYPE_WDMA) ++ ++#define DUMP_WPDMA_TX_RING(_n) DUMP_RING("WPDMA_TX" #_n, 0, DUMP_TYPE_WPDMA_TX, _n) ++#define DUMP_WPDMA_TXFREE_RING DUMP_RING("WPDMA_RX1", 0, DUMP_TYPE_WPDMA_TXFREE) ++ ++static void ++print_reg_val(struct seq_file *s, const char *name, u32 val) ++{ ++ seq_printf(s, "%-32s %08x\n", name, val); ++} ++ ++static void ++dump_wed_regs(struct seq_file *s, struct mtk_wed_device *dev, ++ const struct reg_dump *regs, int n_regs) ++{ ++ const struct reg_dump *cur; ++ u32 val; ++ ++ for (cur = regs; cur < ®s[n_regs]; cur++) { ++ switch (cur->type) { ++ case DUMP_TYPE_STRING: ++ seq_printf(s, "%s======== %s:\n", ++ cur > regs ? "\n" : "", ++ cur->name); ++ continue; ++ case DUMP_TYPE_WED: ++ val = wed_r32(dev, cur->offset); ++ break; ++ case DUMP_TYPE_WDMA: ++ val = wdma_r32(dev, cur->offset); ++ break; ++ case DUMP_TYPE_WPDMA_TX: ++ val = wpdma_tx_r32(dev, cur->base, cur->offset); ++ break; ++ case DUMP_TYPE_WPDMA_TXFREE: ++ val = wpdma_txfree_r32(dev, cur->offset); ++ break; ++ } ++ print_reg_val(s, cur->name, val); ++ } ++} ++ ++ ++static int ++wed_txinfo_show(struct seq_file *s, void *data) ++{ ++ static const struct reg_dump regs[] = { ++ DUMP_STR("WED TX"), ++ DUMP_WED(WED_TX_MIB(0)), ++ DUMP_WED_RING(WED_RING_TX(0)), ++ ++ DUMP_WED(WED_TX_MIB(1)), ++ DUMP_WED_RING(WED_RING_TX(1)), ++ ++ DUMP_STR("WPDMA TX"), ++ DUMP_WED(WED_WPDMA_TX_MIB(0)), ++ DUMP_WED_RING(WED_WPDMA_RING_TX(0)), ++ DUMP_WED(WED_WPDMA_TX_COHERENT_MIB(0)), ++ ++ DUMP_WED(WED_WPDMA_TX_MIB(1)), ++ DUMP_WED_RING(WED_WPDMA_RING_TX(1)), ++ DUMP_WED(WED_WPDMA_TX_COHERENT_MIB(1)), ++ ++ DUMP_STR("WPDMA TX"), ++ DUMP_WPDMA_TX_RING(0), ++ DUMP_WPDMA_TX_RING(1), ++ ++ DUMP_STR("WED WDMA RX"), ++ DUMP_WED(WED_WDMA_RX_MIB(0)), ++ DUMP_WED_RING(WED_WDMA_RING_RX(0)), ++ DUMP_WED(WED_WDMA_RX_THRES(0)), ++ DUMP_WED(WED_WDMA_RX_RECYCLE_MIB(0)), ++ DUMP_WED(WED_WDMA_RX_PROCESSED_MIB(0)), ++ ++ DUMP_WED(WED_WDMA_RX_MIB(1)), ++ DUMP_WED_RING(WED_WDMA_RING_RX(1)), ++ DUMP_WED(WED_WDMA_RX_THRES(1)), ++ DUMP_WED(WED_WDMA_RX_RECYCLE_MIB(1)), ++ DUMP_WED(WED_WDMA_RX_PROCESSED_MIB(1)), ++ ++ DUMP_STR("WDMA RX"), ++ DUMP_WDMA(WDMA_GLO_CFG), ++ DUMP_WDMA_RING(WDMA_RING_RX(0)), ++ DUMP_WDMA_RING(WDMA_RING_RX(1)), ++ }; ++ struct mtk_wed_hw *hw = s->private; ++ struct mtk_wed_device *dev = hw->wed_dev; ++ ++ if (!dev) ++ return 0; ++ ++ dump_wed_regs(s, dev, regs, ARRAY_SIZE(regs)); ++ ++ return 0; ++} ++DEFINE_SHOW_ATTRIBUTE(wed_txinfo); ++ ++ ++static int ++mtk_wed_reg_set(void *data, u64 val) ++{ ++ struct mtk_wed_hw *hw = data; ++ ++ regmap_write(hw->regs, hw->debugfs_reg, val); ++ ++ return 0; ++} ++ ++static int ++mtk_wed_reg_get(void *data, u64 *val) ++{ ++ struct mtk_wed_hw *hw = data; ++ unsigned int regval; ++ int ret; ++ ++ ret = regmap_read(hw->regs, hw->debugfs_reg, ®val); ++ if (ret) ++ return ret; ++ ++ *val = regval; ++ ++ return 0; ++} ++ ++DEFINE_DEBUGFS_ATTRIBUTE(fops_regval, mtk_wed_reg_get, mtk_wed_reg_set, ++ "0x%08llx\n"); ++ ++void mtk_wed_hw_add_debugfs(struct mtk_wed_hw *hw) ++{ ++ struct dentry *dir; ++ ++ snprintf(hw->dirname, sizeof(hw->dirname), "wed%d", hw->index); ++ dir = debugfs_create_dir(hw->dirname, NULL); ++ if (!dir) ++ return; ++ ++ hw->debugfs_dir = dir; ++ debugfs_create_u32("regidx", 0600, dir, &hw->debugfs_reg); ++ debugfs_create_file_unsafe("regval", 0600, dir, hw, &fops_regval); ++ debugfs_create_file_unsafe("txinfo", 0400, dir, hw, &wed_txinfo_fops); ++} +--- /dev/null ++++ b/drivers/net/ethernet/mediatek/mtk_wed_ops.c +@@ -0,0 +1,8 @@ ++// SPDX-License-Identifier: GPL-2.0-only ++/* Copyright (C) 2020 Felix Fietkau */ ++ ++#include ++#include ++ ++const struct mtk_wed_ops __rcu *mtk_soc_wed_ops; ++EXPORT_SYMBOL_GPL(mtk_soc_wed_ops); +--- /dev/null ++++ b/drivers/net/ethernet/mediatek/mtk_wed_regs.h +@@ -0,0 +1,251 @@ ++// SPDX-License-Identifier: GPL-2.0-only ++/* Copyright (C) 2020 Felix Fietkau */ ++ ++#ifndef __MTK_WED_REGS_H ++#define __MTK_WED_REGS_H ++ ++#define MTK_WDMA_DESC_CTRL_LEN1 GENMASK(14, 0) ++#define MTK_WDMA_DESC_CTRL_LAST_SEG1 BIT(15) ++#define MTK_WDMA_DESC_CTRL_BURST BIT(16) ++#define MTK_WDMA_DESC_CTRL_LEN0 GENMASK(29, 16) ++#define MTK_WDMA_DESC_CTRL_LAST_SEG0 BIT(30) ++#define MTK_WDMA_DESC_CTRL_DMA_DONE BIT(31) ++ ++struct mtk_wdma_desc { ++ __le32 buf0; ++ __le32 ctrl; ++ __le32 buf1; ++ __le32 info; ++} __packed __aligned(4); ++ ++#define MTK_WED_RESET 0x008 ++#define MTK_WED_RESET_TX_BM BIT(0) ++#define MTK_WED_RESET_TX_FREE_AGENT BIT(4) ++#define MTK_WED_RESET_WPDMA_TX_DRV BIT(8) ++#define MTK_WED_RESET_WPDMA_RX_DRV BIT(9) ++#define MTK_WED_RESET_WPDMA_INT_AGENT BIT(11) ++#define MTK_WED_RESET_WED_TX_DMA BIT(12) ++#define MTK_WED_RESET_WDMA_RX_DRV BIT(17) ++#define MTK_WED_RESET_WDMA_INT_AGENT BIT(19) ++#define MTK_WED_RESET_WED BIT(31) ++ ++#define MTK_WED_CTRL 0x00c ++#define MTK_WED_CTRL_WPDMA_INT_AGENT_EN BIT(0) ++#define MTK_WED_CTRL_WPDMA_INT_AGENT_BUSY BIT(1) ++#define MTK_WED_CTRL_WDMA_INT_AGENT_EN BIT(2) ++#define MTK_WED_CTRL_WDMA_INT_AGENT_BUSY BIT(3) ++#define MTK_WED_CTRL_WED_TX_BM_EN BIT(8) ++#define MTK_WED_CTRL_WED_TX_BM_BUSY BIT(9) ++#define MTK_WED_CTRL_WED_TX_FREE_AGENT_EN BIT(10) ++#define MTK_WED_CTRL_WED_TX_FREE_AGENT_BUSY BIT(11) ++#define MTK_WED_CTRL_RESERVE_EN BIT(12) ++#define MTK_WED_CTRL_RESERVE_BUSY BIT(13) ++#define MTK_WED_CTRL_FINAL_DIDX_READ BIT(24) ++#define MTK_WED_CTRL_MIB_READ_CLEAR BIT(28) ++ ++#define MTK_WED_EXT_INT_STATUS 0x020 ++#define MTK_WED_EXT_INT_STATUS_TF_LEN_ERR BIT(0) ++#define MTK_WED_EXT_INT_STATUS_TKID_WO_PYLD BIT(1) ++#define MTK_WED_EXT_INT_STATUS_TKID_TITO_INVALID BIT(4) ++#define MTK_WED_EXT_INT_STATUS_TX_FBUF_LO_TH BIT(8) ++#define MTK_WED_EXT_INT_STATUS_TX_FBUF_HI_TH BIT(9) ++#define MTK_WED_EXT_INT_STATUS_RX_FBUF_LO_TH BIT(12) ++#define MTK_WED_EXT_INT_STATUS_RX_FBUF_HI_TH BIT(13) ++#define MTK_WED_EXT_INT_STATUS_RX_DRV_R_RESP_ERR BIT(16) ++#define MTK_WED_EXT_INT_STATUS_RX_DRV_W_RESP_ERR BIT(17) ++#define MTK_WED_EXT_INT_STATUS_RX_DRV_COHERENT BIT(18) ++#define MTK_WED_EXT_INT_STATUS_RX_DRV_INIT_WDMA_EN BIT(19) ++#define MTK_WED_EXT_INT_STATUS_RX_DRV_BM_DMAD_COHERENT BIT(20) ++#define MTK_WED_EXT_INT_STATUS_TX_DRV_R_RESP_ERR BIT(21) ++#define MTK_WED_EXT_INT_STATUS_TX_DRV_W_RESP_ERR BIT(22) ++#define MTK_WED_EXT_INT_STATUS_RX_DRV_DMA_RECYCLE BIT(24) ++#define MTK_WED_EXT_INT_STATUS_ERROR_MASK (MTK_WED_EXT_INT_STATUS_TF_LEN_ERR | \ ++ MTK_WED_EXT_INT_STATUS_TKID_WO_PYLD | \ ++ MTK_WED_EXT_INT_STATUS_TKID_TITO_INVALID | \ ++ MTK_WED_EXT_INT_STATUS_RX_DRV_R_RESP_ERR | \ ++ MTK_WED_EXT_INT_STATUS_RX_DRV_W_RESP_ERR | \ ++ MTK_WED_EXT_INT_STATUS_RX_DRV_INIT_WDMA_EN | \ ++ MTK_WED_EXT_INT_STATUS_TX_DRV_R_RESP_ERR | \ ++ MTK_WED_EXT_INT_STATUS_TX_DRV_W_RESP_ERR) ++ ++#define MTK_WED_EXT_INT_MASK 0x028 ++ ++#define MTK_WED_STATUS 0x060 ++#define MTK_WED_STATUS_TX GENMASK(15, 8) ++ ++#define MTK_WED_TX_BM_CTRL 0x080 ++#define MTK_WED_TX_BM_CTRL_VLD_GRP_NUM GENMASK(6, 0) ++#define MTK_WED_TX_BM_CTRL_RSV_GRP_NUM GENMASK(22, 16) ++#define MTK_WED_TX_BM_CTRL_PAUSE BIT(28) ++ ++#define MTK_WED_TX_BM_BASE 0x084 ++ ++#define MTK_WED_TX_BM_TKID 0x088 ++#define MTK_WED_TX_BM_TKID_START GENMASK(15, 0) ++#define MTK_WED_TX_BM_TKID_END GENMASK(31, 16) ++ ++#define MTK_WED_TX_BM_BUF_LEN 0x08c ++ ++#define MTK_WED_TX_BM_INTF 0x09c ++#define MTK_WED_TX_BM_INTF_TKID GENMASK(15, 0) ++#define MTK_WED_TX_BM_INTF_TKFIFO_FDEP GENMASK(23, 16) ++#define MTK_WED_TX_BM_INTF_TKID_VALID BIT(28) ++#define MTK_WED_TX_BM_INTF_TKID_READ BIT(29) ++ ++#define MTK_WED_TX_BM_DYN_THR 0x0a0 ++#define MTK_WED_TX_BM_DYN_THR_LO GENMASK(6, 0) ++#define MTK_WED_TX_BM_DYN_THR_HI GENMASK(22, 16) ++ ++#define MTK_WED_INT_STATUS 0x200 ++#define MTK_WED_INT_MASK 0x204 ++ ++#define MTK_WED_GLO_CFG 0x208 ++#define MTK_WED_GLO_CFG_TX_DMA_EN BIT(0) ++#define MTK_WED_GLO_CFG_TX_DMA_BUSY BIT(1) ++#define MTK_WED_GLO_CFG_RX_DMA_EN BIT(2) ++#define MTK_WED_GLO_CFG_RX_DMA_BUSY BIT(3) ++#define MTK_WED_GLO_CFG_RX_BT_SIZE GENMASK(5, 4) ++#define MTK_WED_GLO_CFG_TX_WB_DDONE BIT(6) ++#define MTK_WED_GLO_CFG_BIG_ENDIAN BIT(7) ++#define MTK_WED_GLO_CFG_DIS_BT_SIZE_ALIGN BIT(8) ++#define MTK_WED_GLO_CFG_TX_BT_SIZE_LO BIT(9) ++#define MTK_WED_GLO_CFG_MULTI_DMA_EN GENMASK(11, 10) ++#define MTK_WED_GLO_CFG_FIFO_LITTLE_ENDIAN BIT(12) ++#define MTK_WED_GLO_CFG_MI_DEPTH_RD GENMASK(21, 13) ++#define MTK_WED_GLO_CFG_TX_BT_SIZE_HI GENMASK(23, 22) ++#define MTK_WED_GLO_CFG_SW_RESET BIT(24) ++#define MTK_WED_GLO_CFG_FIRST_TOKEN_ONLY BIT(26) ++#define MTK_WED_GLO_CFG_OMIT_RX_INFO BIT(27) ++#define MTK_WED_GLO_CFG_OMIT_TX_INFO BIT(28) ++#define MTK_WED_GLO_CFG_BYTE_SWAP BIT(29) ++#define MTK_WED_GLO_CFG_RX_2B_OFFSET BIT(31) ++ ++#define MTK_WED_RESET_IDX 0x20c ++#define MTK_WED_RESET_IDX_TX GENMASK(3, 0) ++#define MTK_WED_RESET_IDX_RX GENMASK(17, 16) ++ ++#define MTK_WED_TX_MIB(_n) (0x2a0 + (_n) * 4) ++ ++#define MTK_WED_RING_TX(_n) (0x300 + (_n) * 0x10) ++ ++#define MTK_WED_RING_RX(_n) (0x400 + (_n) * 0x10) ++ ++#define MTK_WED_WPDMA_INT_TRIGGER 0x504 ++#define MTK_WED_WPDMA_INT_TRIGGER_RX_DONE BIT(1) ++#define MTK_WED_WPDMA_INT_TRIGGER_TX_DONE GENMASK(5, 4) ++ ++#define MTK_WED_WPDMA_GLO_CFG 0x508 ++#define MTK_WED_WPDMA_GLO_CFG_TX_DRV_EN BIT(0) ++#define MTK_WED_WPDMA_GLO_CFG_TX_DRV_BUSY BIT(1) ++#define MTK_WED_WPDMA_GLO_CFG_RX_DRV_EN BIT(2) ++#define MTK_WED_WPDMA_GLO_CFG_RX_DRV_BUSY BIT(3) ++#define MTK_WED_WPDMA_GLO_CFG_RX_BT_SIZE GENMASK(5, 4) ++#define MTK_WED_WPDMA_GLO_CFG_TX_WB_DDONE BIT(6) ++#define MTK_WED_WPDMA_GLO_CFG_BIG_ENDIAN BIT(7) ++#define MTK_WED_WPDMA_GLO_CFG_DIS_BT_SIZE_ALIGN BIT(8) ++#define MTK_WED_WPDMA_GLO_CFG_TX_BT_SIZE_LO BIT(9) ++#define MTK_WED_WPDMA_GLO_CFG_MULTI_DMA_EN GENMASK(11, 10) ++#define MTK_WED_WPDMA_GLO_CFG_FIFO_LITTLE_ENDIAN BIT(12) ++#define MTK_WED_WPDMA_GLO_CFG_MI_DEPTH_RD GENMASK(21, 13) ++#define MTK_WED_WPDMA_GLO_CFG_TX_BT_SIZE_HI GENMASK(23, 22) ++#define MTK_WED_WPDMA_GLO_CFG_SW_RESET BIT(24) ++#define MTK_WED_WPDMA_GLO_CFG_FIRST_TOKEN_ONLY BIT(26) ++#define MTK_WED_WPDMA_GLO_CFG_OMIT_RX_INFO BIT(27) ++#define MTK_WED_WPDMA_GLO_CFG_OMIT_TX_INFO BIT(28) ++#define MTK_WED_WPDMA_GLO_CFG_BYTE_SWAP BIT(29) ++#define MTK_WED_WPDMA_GLO_CFG_RX_2B_OFFSET BIT(31) ++ ++#define MTK_WED_WPDMA_RESET_IDX 0x50c ++#define MTK_WED_WPDMA_RESET_IDX_TX GENMASK(3, 0) ++#define MTK_WED_WPDMA_RESET_IDX_RX GENMASK(17, 16) ++ ++#define MTK_WED_WPDMA_INT_CTRL 0x520 ++#define MTK_WED_WPDMA_INT_CTRL_SUBRT_ADV BIT(21) ++ ++#define MTK_WED_WPDMA_INT_MASK 0x524 ++ ++#define MTK_WED_PCIE_CFG_BASE 0x560 ++ ++#define MTK_WED_PCIE_INT_TRIGGER 0x570 ++#define MTK_WED_PCIE_INT_TRIGGER_STATUS BIT(16) ++ ++#define MTK_WED_WPDMA_CFG_BASE 0x580 ++ ++#define MTK_WED_WPDMA_TX_MIB(_n) (0x5a0 + (_n) * 4) ++#define MTK_WED_WPDMA_TX_COHERENT_MIB(_n) (0x5d0 + (_n) * 4) ++ ++#define MTK_WED_WPDMA_RING_TX(_n) (0x600 + (_n) * 0x10) ++#define MTK_WED_WPDMA_RING_RX(_n) (0x700 + (_n) * 0x10) ++#define MTK_WED_WDMA_RING_RX(_n) (0x900 + (_n) * 0x10) ++#define MTK_WED_WDMA_RX_THRES(_n) (0x940 + (_n) * 0x4) ++ ++#define MTK_WED_WDMA_GLO_CFG 0xa04 ++#define MTK_WED_WDMA_GLO_CFG_TX_DRV_EN BIT(0) ++#define MTK_WED_WDMA_GLO_CFG_RX_DRV_EN BIT(2) ++#define MTK_WED_WDMA_GLO_CFG_RX_DRV_BUSY BIT(3) ++#define MTK_WED_WDMA_GLO_CFG_BT_SIZE GENMASK(5, 4) ++#define MTK_WED_WDMA_GLO_CFG_TX_WB_DDONE BIT(6) ++#define MTK_WED_WDMA_GLO_CFG_RX_DIS_FSM_AUTO_IDLE BIT(13) ++#define MTK_WED_WDMA_GLO_CFG_WCOMPLETE_SEL BIT(16) ++#define MTK_WED_WDMA_GLO_CFG_INIT_PHASE_RXDMA_BYPASS BIT(17) ++#define MTK_WED_WDMA_GLO_CFG_INIT_PHASE_BYPASS BIT(18) ++#define MTK_WED_WDMA_GLO_CFG_FSM_RETURN_IDLE BIT(19) ++#define MTK_WED_WDMA_GLO_CFG_WAIT_COHERENT BIT(20) ++#define MTK_WED_WDMA_GLO_CFG_AXI_W_AFTER_AW BIT(21) ++#define MTK_WED_WDMA_GLO_CFG_IDLE_DMAD_SUPPLY_SINGLE_W BIT(22) ++#define MTK_WED_WDMA_GLO_CFG_IDLE_DMAD_SUPPLY BIT(23) ++#define MTK_WED_WDMA_GLO_CFG_DYNAMIC_SKIP_DMAD_PREP BIT(24) ++#define MTK_WED_WDMA_GLO_CFG_DYNAMIC_DMAD_RECYCLE BIT(25) ++#define MTK_WED_WDMA_GLO_CFG_RST_INIT_COMPLETE BIT(26) ++#define MTK_WED_WDMA_GLO_CFG_RXDRV_CLKGATE_BYPASS BIT(30) ++ ++#define MTK_WED_WDMA_RESET_IDX 0xa08 ++#define MTK_WED_WDMA_RESET_IDX_RX GENMASK(17, 16) ++#define MTK_WED_WDMA_RESET_IDX_DRV GENMASK(25, 24) ++ ++#define MTK_WED_WDMA_INT_TRIGGER 0xa28 ++#define MTK_WED_WDMA_INT_TRIGGER_RX_DONE GENMASK(17, 16) ++ ++#define MTK_WED_WDMA_INT_CTRL 0xa2c ++#define MTK_WED_WDMA_INT_CTRL_POLL_SRC_SEL GENMASK(17, 16) ++ ++#define MTK_WED_WDMA_OFFSET0 0xaa4 ++#define MTK_WED_WDMA_OFFSET1 0xaa8 ++ ++#define MTK_WED_WDMA_RX_MIB(_n) (0xae0 + (_n) * 4) ++#define MTK_WED_WDMA_RX_RECYCLE_MIB(_n) (0xae8 + (_n) * 4) ++#define MTK_WED_WDMA_RX_PROCESSED_MIB(_n) (0xaf0 + (_n) * 4) ++ ++#define MTK_WED_RING_OFS_BASE 0x00 ++#define MTK_WED_RING_OFS_COUNT 0x04 ++#define MTK_WED_RING_OFS_CPU_IDX 0x08 ++#define MTK_WED_RING_OFS_DMA_IDX 0x0c ++ ++#define MTK_WDMA_RING_RX(_n) (0x100 + (_n) * 0x10) ++ ++#define MTK_WDMA_GLO_CFG 0x204 ++#define MTK_WDMA_GLO_CFG_RX_INFO_PRERES GENMASK(28, 26) ++ ++#define MTK_WDMA_RESET_IDX 0x208 ++#define MTK_WDMA_RESET_IDX_TX GENMASK(3, 0) ++#define MTK_WDMA_RESET_IDX_RX GENMASK(17, 16) ++ ++#define MTK_WDMA_INT_MASK 0x228 ++#define MTK_WDMA_INT_MASK_TX_DONE GENMASK(3, 0) ++#define MTK_WDMA_INT_MASK_RX_DONE GENMASK(17, 16) ++#define MTK_WDMA_INT_MASK_TX_DELAY BIT(28) ++#define MTK_WDMA_INT_MASK_TX_COHERENT BIT(29) ++#define MTK_WDMA_INT_MASK_RX_DELAY BIT(30) ++#define MTK_WDMA_INT_MASK_RX_COHERENT BIT(31) ++ ++#define MTK_WDMA_INT_GRP1 0x250 ++#define MTK_WDMA_INT_GRP2 0x254 ++ ++#define MTK_PCIE_MIRROR_MAP(n) ((n) ? 0x4 : 0x0) ++#define MTK_PCIE_MIRROR_MAP_EN BIT(0) ++#define MTK_PCIE_MIRROR_MAP_WED_ID BIT(1) ++ ++/* DMA channel mapping */ ++#define HIFSYS_DMA_AG_MAP 0x008 ++ ++#endif +--- /dev/null ++++ b/include/linux/soc/mediatek/mtk_wed.h +@@ -0,0 +1,131 @@ ++#ifndef __MTK_WED_H ++#define __MTK_WED_H ++ ++#include ++#include ++#include ++#include ++ ++#define MTK_WED_TX_QUEUES 2 ++ ++struct mtk_wed_hw; ++struct mtk_wdma_desc; ++ ++struct mtk_wed_ring { ++ struct mtk_wdma_desc *desc; ++ dma_addr_t desc_phys; ++ int size; ++ ++ u32 reg_base; ++ void __iomem *wpdma; ++}; ++ ++struct mtk_wed_device { ++#ifdef CONFIG_NET_MEDIATEK_SOC_WED ++ const struct mtk_wed_ops *ops; ++ struct device *dev; ++ struct mtk_wed_hw *hw; ++ bool init_done, running; ++ int wdma_idx; ++ int irq; ++ ++ struct mtk_wed_ring tx_ring[MTK_WED_TX_QUEUES]; ++ struct mtk_wed_ring txfree_ring; ++ struct mtk_wed_ring tx_wdma[MTK_WED_TX_QUEUES]; ++ ++ struct { ++ int size; ++ void **pages; ++ struct mtk_wdma_desc *desc; ++ dma_addr_t desc_phys; ++ } buf_ring; ++ ++ /* filled by driver: */ ++ struct { ++ struct pci_dev *pci_dev; ++ ++ u32 wpdma_phys; ++ ++ u16 token_start; ++ unsigned int nbuf; ++ ++ u32 (*init_buf)(void *ptr, dma_addr_t phys, int token_id); ++ int (*offload_enable)(struct mtk_wed_device *wed); ++ void (*offload_disable)(struct mtk_wed_device *wed); ++ } wlan; ++#endif ++}; ++ ++struct mtk_wed_ops { ++ int (*attach)(struct mtk_wed_device *dev); ++ int (*tx_ring_setup)(struct mtk_wed_device *dev, int ring, ++ void __iomem *regs); ++ int (*txfree_ring_setup)(struct mtk_wed_device *dev, ++ void __iomem *regs); ++ void (*detach)(struct mtk_wed_device *dev); ++ ++ void (*stop)(struct mtk_wed_device *dev); ++ void (*start)(struct mtk_wed_device *dev, u32 irq_mask); ++ void (*reset_dma)(struct mtk_wed_device *dev); ++ ++ u32 (*reg_read)(struct mtk_wed_device *dev, u32 reg); ++ void (*reg_write)(struct mtk_wed_device *dev, u32 reg, u32 val); ++ ++ u32 (*irq_get)(struct mtk_wed_device *dev, u32 mask); ++ void (*irq_set_mask)(struct mtk_wed_device *dev, u32 mask); ++}; ++ ++extern const struct mtk_wed_ops __rcu *mtk_soc_wed_ops; ++ ++static inline int ++mtk_wed_device_attach(struct mtk_wed_device *dev) ++{ ++ int ret = -ENODEV; ++ ++#ifdef CONFIG_NET_MEDIATEK_SOC_WED ++ rcu_read_lock(); ++ dev->ops = rcu_dereference(mtk_soc_wed_ops); ++ if (dev->ops) ++ ret = dev->ops->attach(dev); ++ else ++ rcu_read_unlock(); ++ ++ if (ret) ++ dev->ops = NULL; ++#endif ++ ++ return ret; ++} ++ ++#ifdef CONFIG_NET_MEDIATEK_SOC_WED ++#define mtk_wed_device_active(_dev) !!(_dev)->ops ++#define mtk_wed_device_detach(_dev) (_dev)->ops->detach(_dev) ++#define mtk_wed_device_start(_dev, _mask) (_dev)->ops->start(_dev, _mask) ++#define mtk_wed_device_tx_ring_setup(_dev, _ring, _regs) \ ++ (_dev)->ops->tx_ring_setup(_dev, _ring, _regs) ++#define mtk_wed_device_txfree_ring_setup(_dev, _regs) \ ++ (_dev)->ops->txfree_ring_setup(_dev, _regs) ++#define mtk_wed_device_reg_read(_dev, _reg) \ ++ (_dev)->ops->reg_read(_dev, _reg) ++#define mtk_wed_device_reg_write(_dev, _reg, _val) \ ++ (_dev)->ops->reg_write(_dev, _reg, _val) ++#define mtk_wed_device_irq_get(_dev, _mask) \ ++ (_dev)->ops->irq_get(_dev, _mask) ++#define mtk_wed_device_irq_set_mask(_dev, _mask) \ ++ (_dev)->ops->irq_set_mask(_dev, _mask) ++#else ++static inline bool mtk_wed_device_active(struct mtk_wed_device *dev) ++{ ++ return false; ++} ++#define mtk_wed_device_detach(_dev) do {} while (0) ++#define mtk_wed_device_start(_dev, _mask) do {} while (0) ++#define mtk_wed_device_tx_ring_setup(_dev, _ring, _regs) -ENODEV ++#define mtk_wed_device_txfree_ring_setup(_dev, _ring, _regs) -ENODEV ++#define mtk_wed_device_reg_read(_dev, _reg) 0 ++#define mtk_wed_device_reg_write(_dev, _reg, _val) do {} while (0) ++#define mtk_wed_device_irq_get(_dev, _mask) 0 ++#define mtk_wed_device_irq_set_mask(_dev, _mask) do {} while (0) ++#endif ++ ++#endif diff --git a/target/linux/generic/backport-6.1/702-v5.19-03-net-ethernet-mtk_eth_soc-implement-flow-offloading-t.patch b/target/linux/generic/backport-6.1/702-v5.19-03-net-ethernet-mtk_eth_soc-implement-flow-offloading-t.patch new file mode 100644 index 0000000000..2f93518e15 --- /dev/null +++ b/target/linux/generic/backport-6.1/702-v5.19-03-net-ethernet-mtk_eth_soc-implement-flow-offloading-t.patch @@ -0,0 +1,269 @@ +From: Felix Fietkau +Date: Sat, 5 Feb 2022 18:29:22 +0100 +Subject: [PATCH] net: ethernet: mtk_eth_soc: implement flow offloading + to WED devices + +This allows hardware flow offloading from Ethernet to WLAN on MT7622 SoC + +Co-developed-by: Lorenzo Bianconi +Signed-off-by: Lorenzo Bianconi +Signed-off-by: Felix Fietkau +--- + +--- a/drivers/net/ethernet/mediatek/mtk_ppe.c ++++ b/drivers/net/ethernet/mediatek/mtk_ppe.c +@@ -329,6 +329,24 @@ int mtk_foe_entry_set_pppoe(struct mtk_f + return 0; + } + ++int mtk_foe_entry_set_wdma(struct mtk_foe_entry *entry, int wdma_idx, int txq, ++ int bss, int wcid) ++{ ++ struct mtk_foe_mac_info *l2 = mtk_foe_entry_l2(entry); ++ u32 *ib2 = mtk_foe_entry_ib2(entry); ++ ++ *ib2 &= ~MTK_FOE_IB2_PORT_MG; ++ *ib2 |= MTK_FOE_IB2_WDMA_WINFO; ++ if (wdma_idx) ++ *ib2 |= MTK_FOE_IB2_WDMA_DEVIDX; ++ ++ l2->vlan2 = FIELD_PREP(MTK_FOE_VLAN2_WINFO_BSS, bss) | ++ FIELD_PREP(MTK_FOE_VLAN2_WINFO_WCID, wcid) | ++ FIELD_PREP(MTK_FOE_VLAN2_WINFO_RING, txq); ++ ++ return 0; ++} ++ + static inline bool mtk_foe_entry_usable(struct mtk_foe_entry *entry) + { + return !(entry->ib1 & MTK_FOE_IB1_STATIC) && +--- a/drivers/net/ethernet/mediatek/mtk_ppe.h ++++ b/drivers/net/ethernet/mediatek/mtk_ppe.h +@@ -48,9 +48,9 @@ enum { + #define MTK_FOE_IB2_DEST_PORT GENMASK(7, 5) + #define MTK_FOE_IB2_MULTICAST BIT(8) + +-#define MTK_FOE_IB2_WHNAT_QID2 GENMASK(13, 12) +-#define MTK_FOE_IB2_WHNAT_DEVIDX BIT(16) +-#define MTK_FOE_IB2_WHNAT_NAT BIT(17) ++#define MTK_FOE_IB2_WDMA_QID2 GENMASK(13, 12) ++#define MTK_FOE_IB2_WDMA_DEVIDX BIT(16) ++#define MTK_FOE_IB2_WDMA_WINFO BIT(17) + + #define MTK_FOE_IB2_PORT_MG GENMASK(17, 12) + +@@ -58,9 +58,9 @@ enum { + + #define MTK_FOE_IB2_DSCP GENMASK(31, 24) + +-#define MTK_FOE_VLAN2_WHNAT_BSS GEMMASK(5, 0) +-#define MTK_FOE_VLAN2_WHNAT_WCID GENMASK(13, 6) +-#define MTK_FOE_VLAN2_WHNAT_RING GENMASK(15, 14) ++#define MTK_FOE_VLAN2_WINFO_BSS GENMASK(5, 0) ++#define MTK_FOE_VLAN2_WINFO_WCID GENMASK(13, 6) ++#define MTK_FOE_VLAN2_WINFO_RING GENMASK(15, 14) + + enum { + MTK_FOE_STATE_INVALID, +@@ -281,6 +281,8 @@ int mtk_foe_entry_set_ipv6_tuple(struct + int mtk_foe_entry_set_dsa(struct mtk_foe_entry *entry, int port); + int mtk_foe_entry_set_vlan(struct mtk_foe_entry *entry, int vid); + int mtk_foe_entry_set_pppoe(struct mtk_foe_entry *entry, int sid); ++int mtk_foe_entry_set_wdma(struct mtk_foe_entry *entry, int wdma_idx, int txq, ++ int bss, int wcid); + int mtk_foe_entry_commit(struct mtk_ppe *ppe, struct mtk_foe_entry *entry, + u16 timestamp); + int mtk_ppe_debugfs_init(struct mtk_ppe *ppe); +--- a/drivers/net/ethernet/mediatek/mtk_ppe_offload.c ++++ b/drivers/net/ethernet/mediatek/mtk_ppe_offload.c +@@ -10,6 +10,7 @@ + #include + #include + #include "mtk_eth_soc.h" ++#include "mtk_wed.h" + + struct mtk_flow_data { + struct ethhdr eth; +@@ -39,6 +40,7 @@ struct mtk_flow_entry { + struct rhash_head node; + unsigned long cookie; + u16 hash; ++ s8 wed_index; + }; + + static const struct rhashtable_params mtk_flow_ht_params = { +@@ -80,6 +82,35 @@ mtk_flow_offload_mangle_eth(const struct + memcpy(dest, src, act->mangle.mask ? 2 : 4); + } + ++static int ++mtk_flow_get_wdma_info(struct net_device *dev, const u8 *addr, struct mtk_wdma_info *info) ++{ ++ struct net_device_path_ctx ctx = { ++ .dev = dev, ++ .daddr = addr, ++ }; ++ struct net_device_path path = {}; ++ ++ if (!IS_ENABLED(CONFIG_NET_MEDIATEK_SOC_WED)) ++ return -1; ++ ++ if (!dev->netdev_ops->ndo_fill_forward_path) ++ return -1; ++ ++ if (dev->netdev_ops->ndo_fill_forward_path(&ctx, &path)) ++ return -1; ++ ++ if (path.type != DEV_PATH_MTK_WDMA) ++ return -1; ++ ++ info->wdma_idx = path.mtk_wdma.wdma_idx; ++ info->queue = path.mtk_wdma.queue; ++ info->bss = path.mtk_wdma.bss; ++ info->wcid = path.mtk_wdma.wcid; ++ ++ return 0; ++} ++ + + static int + mtk_flow_mangle_ports(const struct flow_action_entry *act, +@@ -149,10 +180,20 @@ mtk_flow_get_dsa_port(struct net_device + + static int + mtk_flow_set_output_device(struct mtk_eth *eth, struct mtk_foe_entry *foe, +- struct net_device *dev) ++ struct net_device *dev, const u8 *dest_mac, ++ int *wed_index) + { ++ struct mtk_wdma_info info = {}; + int pse_port, dsa_port; + ++ if (mtk_flow_get_wdma_info(dev, dest_mac, &info) == 0) { ++ mtk_foe_entry_set_wdma(foe, info.wdma_idx, info.queue, info.bss, ++ info.wcid); ++ pse_port = 3; ++ *wed_index = info.wdma_idx; ++ goto out; ++ } ++ + dsa_port = mtk_flow_get_dsa_port(&dev); + if (dsa_port >= 0) + mtk_foe_entry_set_dsa(foe, dsa_port); +@@ -164,6 +205,7 @@ mtk_flow_set_output_device(struct mtk_et + else + return -EOPNOTSUPP; + ++out: + mtk_foe_entry_set_pse_port(foe, pse_port); + + return 0; +@@ -179,6 +221,7 @@ mtk_flow_offload_replace(struct mtk_eth + struct net_device *odev = NULL; + struct mtk_flow_entry *entry; + int offload_type = 0; ++ int wed_index = -1; + u16 addr_type = 0; + u32 timestamp; + u8 l4proto = 0; +@@ -326,10 +369,14 @@ mtk_flow_offload_replace(struct mtk_eth + if (data.pppoe.num == 1) + mtk_foe_entry_set_pppoe(&foe, data.pppoe.sid); + +- err = mtk_flow_set_output_device(eth, &foe, odev); ++ err = mtk_flow_set_output_device(eth, &foe, odev, data.eth.h_dest, ++ &wed_index); + if (err) + return err; + ++ if (wed_index >= 0 && (err = mtk_wed_flow_add(wed_index)) < 0) ++ return err; ++ + entry = kzalloc(sizeof(*entry), GFP_KERNEL); + if (!entry) + return -ENOMEM; +@@ -343,6 +390,7 @@ mtk_flow_offload_replace(struct mtk_eth + } + + entry->hash = hash; ++ entry->wed_index = wed_index; + err = rhashtable_insert_fast(ð->flow_table, &entry->node, + mtk_flow_ht_params); + if (err < 0) +@@ -353,6 +401,8 @@ clear_flow: + mtk_foe_entry_clear(ð->ppe, hash); + free: + kfree(entry); ++ if (wed_index >= 0) ++ mtk_wed_flow_remove(wed_index); + return err; + } + +@@ -369,6 +419,8 @@ mtk_flow_offload_destroy(struct mtk_eth + mtk_foe_entry_clear(ð->ppe, entry->hash); + rhashtable_remove_fast(ð->flow_table, &entry->node, + mtk_flow_ht_params); ++ if (entry->wed_index >= 0) ++ mtk_wed_flow_remove(entry->wed_index); + kfree(entry); + + return 0; +--- a/drivers/net/ethernet/mediatek/mtk_wed.h ++++ b/drivers/net/ethernet/mediatek/mtk_wed.h +@@ -7,6 +7,7 @@ + #include + #include + #include ++#include + + struct mtk_eth; + +@@ -27,6 +28,12 @@ struct mtk_wed_hw { + int index; + }; + ++struct mtk_wdma_info { ++ u8 wdma_idx; ++ u8 queue; ++ u16 wcid; ++ u8 bss; ++}; + + #ifdef CONFIG_NET_MEDIATEK_SOC_WED + static inline void +--- a/include/linux/netdevice.h ++++ b/include/linux/netdevice.h +@@ -863,6 +863,7 @@ enum net_device_path_type { + DEV_PATH_BRIDGE, + DEV_PATH_PPPOE, + DEV_PATH_DSA, ++ DEV_PATH_MTK_WDMA, + }; + + struct net_device_path { +@@ -888,6 +889,12 @@ struct net_device_path { + int port; + u16 proto; + } dsa; ++ struct { ++ u8 wdma_idx; ++ u8 queue; ++ u16 wcid; ++ u8 bss; ++ } mtk_wdma; + }; + }; + +--- a/net/core/dev.c ++++ b/net/core/dev.c +@@ -763,6 +763,10 @@ int dev_fill_forward_path(const struct n + if (WARN_ON_ONCE(last_dev == ctx.dev)) + return -1; + } ++ ++ if (!ctx.dev) ++ return ret; ++ + path = dev_fwd_path(stack); + if (!path) + return -1; diff --git a/target/linux/generic/backport-6.1/702-v5.19-04-arm64-dts-mediatek-mt7622-introduce-nodes-for-Wirele.patch b/target/linux/generic/backport-6.1/702-v5.19-04-arm64-dts-mediatek-mt7622-introduce-nodes-for-Wirele.patch new file mode 100644 index 0000000000..f59a364a73 --- /dev/null +++ b/target/linux/generic/backport-6.1/702-v5.19-04-arm64-dts-mediatek-mt7622-introduce-nodes-for-Wirele.patch @@ -0,0 +1,62 @@ +From: Felix Fietkau +Date: Sat, 5 Feb 2022 18:36:36 +0100 +Subject: [PATCH] arm64: dts: mediatek: mt7622: introduce nodes for + Wireless Ethernet Dispatch + +Introduce wed0 and wed1 nodes in order to enable offloading forwarding +between ethernet and wireless devices on the mt7622 chipset. + +Signed-off-by: Felix Fietkau +--- + +--- a/arch/arm64/boot/dts/mediatek/mt7622.dtsi ++++ b/arch/arm64/boot/dts/mediatek/mt7622.dtsi +@@ -893,6 +893,11 @@ + }; + }; + ++ hifsys: syscon@1af00000 { ++ compatible = "mediatek,mt7622-hifsys", "syscon"; ++ reg = <0 0x1af00000 0 0x70>; ++ }; ++ + ethsys: syscon@1b000000 { + compatible = "mediatek,mt7622-ethsys", + "syscon"; +@@ -911,6 +916,26 @@ + #dma-cells = <1>; + }; + ++ pcie_mirror: pcie-mirror@10000400 { ++ compatible = "mediatek,mt7622-pcie-mirror", ++ "syscon"; ++ reg = <0 0x10000400 0 0x10>; ++ }; ++ ++ wed0: wed@1020a000 { ++ compatible = "mediatek,mt7622-wed", ++ "syscon"; ++ reg = <0 0x1020a000 0 0x1000>; ++ interrupts = ; ++ }; ++ ++ wed1: wed@1020b000 { ++ compatible = "mediatek,mt7622-wed", ++ "syscon"; ++ reg = <0 0x1020b000 0 0x1000>; ++ interrupts = ; ++ }; ++ + eth: ethernet@1b100000 { + compatible = "mediatek,mt7622-eth", + "mediatek,mt2701-eth", +@@ -938,6 +963,9 @@ + mediatek,ethsys = <ðsys>; + mediatek,sgmiisys = <&sgmiisys>; + mediatek,cci-control = <&cci_control2>; ++ mediatek,wed = <&wed0>, <&wed1>; ++ mediatek,pcie-mirror = <&pcie_mirror>; ++ mediatek,hifsys = <&hifsys>; + dma-coherent; + #address-cells = <1>; + #size-cells = <0>; diff --git a/target/linux/generic/backport-6.1/702-v5.19-05-net-ethernet-mtk_eth_soc-add-ipv6-flow-offload-suppo.patch b/target/linux/generic/backport-6.1/702-v5.19-05-net-ethernet-mtk_eth_soc-add-ipv6-flow-offload-suppo.patch new file mode 100644 index 0000000000..9adb067015 --- /dev/null +++ b/target/linux/generic/backport-6.1/702-v5.19-05-net-ethernet-mtk_eth_soc-add-ipv6-flow-offload-suppo.patch @@ -0,0 +1,79 @@ +From: David Bentham +Date: Mon, 21 Feb 2022 15:36:16 +0100 +Subject: [PATCH] net: ethernet: mtk_eth_soc: add ipv6 flow offload + support + +Add the missing IPv6 flow offloading support for routing only. +Hardware flow offloading is done by the packet processing engine (PPE) +of the Ethernet MAC and as it doesn't support mangling of IPv6 packets, +IPv6 NAT cannot be supported. + +Signed-off-by: David Bentham +Signed-off-by: Felix Fietkau +--- + +--- a/drivers/net/ethernet/mediatek/mtk_ppe_offload.c ++++ b/drivers/net/ethernet/mediatek/mtk_ppe_offload.c +@@ -6,6 +6,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -20,6 +21,11 @@ struct mtk_flow_data { + __be32 src_addr; + __be32 dst_addr; + } v4; ++ ++ struct { ++ struct in6_addr src_addr; ++ struct in6_addr dst_addr; ++ } v6; + }; + + __be16 src_port; +@@ -65,6 +71,14 @@ mtk_flow_set_ipv4_addr(struct mtk_foe_en + data->v4.dst_addr, data->dst_port); + } + ++static int ++mtk_flow_set_ipv6_addr(struct mtk_foe_entry *foe, struct mtk_flow_data *data) ++{ ++ return mtk_foe_entry_set_ipv6_tuple(foe, ++ data->v6.src_addr.s6_addr32, data->src_port, ++ data->v6.dst_addr.s6_addr32, data->dst_port); ++} ++ + static void + mtk_flow_offload_mangle_eth(const struct flow_action_entry *act, void *eth) + { +@@ -296,6 +310,9 @@ mtk_flow_offload_replace(struct mtk_eth + case FLOW_DISSECTOR_KEY_IPV4_ADDRS: + offload_type = MTK_PPE_PKT_TYPE_IPV4_HNAPT; + break; ++ case FLOW_DISSECTOR_KEY_IPV6_ADDRS: ++ offload_type = MTK_PPE_PKT_TYPE_IPV6_ROUTE_5T; ++ break; + default: + return -EOPNOTSUPP; + } +@@ -331,6 +348,17 @@ mtk_flow_offload_replace(struct mtk_eth + mtk_flow_set_ipv4_addr(&foe, &data, false); + } + ++ if (addr_type == FLOW_DISSECTOR_KEY_IPV6_ADDRS) { ++ struct flow_match_ipv6_addrs addrs; ++ ++ flow_rule_match_ipv6_addrs(rule, &addrs); ++ ++ data.v6.src_addr = addrs.key->src; ++ data.v6.dst_addr = addrs.key->dst; ++ ++ mtk_flow_set_ipv6_addr(&foe, &data); ++ } ++ + flow_action_for_each(i, act, &rule->action) { + if (act->id != FLOW_ACTION_MANGLE) + continue; diff --git a/target/linux/generic/backport-6.1/702-v5.19-06-net-ethernet-mtk_eth_soc-support-TC_SETUP_BLOCK-for-.patch b/target/linux/generic/backport-6.1/702-v5.19-06-net-ethernet-mtk_eth_soc-support-TC_SETUP_BLOCK-for-.patch new file mode 100644 index 0000000000..72c6d28172 --- /dev/null +++ b/target/linux/generic/backport-6.1/702-v5.19-06-net-ethernet-mtk_eth_soc-support-TC_SETUP_BLOCK-for-.patch @@ -0,0 +1,29 @@ +From: Felix Fietkau +Date: Mon, 21 Feb 2022 15:37:21 +0100 +Subject: [PATCH] net: ethernet: mtk_eth_soc: support TC_SETUP_BLOCK for + PPE offload + +This allows offload entries to be created from user space + +Signed-off-by: Felix Fietkau +--- + +--- a/drivers/net/ethernet/mediatek/mtk_ppe_offload.c ++++ b/drivers/net/ethernet/mediatek/mtk_ppe_offload.c +@@ -563,10 +563,13 @@ mtk_eth_setup_tc_block(struct net_device + int mtk_eth_setup_tc(struct net_device *dev, enum tc_setup_type type, + void *type_data) + { +- if (type == TC_SETUP_FT) ++ switch (type) { ++ case TC_SETUP_BLOCK: ++ case TC_SETUP_FT: + return mtk_eth_setup_tc_block(dev, type_data); +- +- return -EOPNOTSUPP; ++ default: ++ return -EOPNOTSUPP; ++ } + } + + int mtk_eth_offload_init(struct mtk_eth *eth) diff --git a/target/linux/generic/backport-6.1/702-v5.19-07-net-ethernet-mtk_eth_soc-allocate-struct-mtk_ppe-sep.patch b/target/linux/generic/backport-6.1/702-v5.19-07-net-ethernet-mtk_eth_soc-allocate-struct-mtk_ppe-sep.patch new file mode 100644 index 0000000000..a17d49cac1 --- /dev/null +++ b/target/linux/generic/backport-6.1/702-v5.19-07-net-ethernet-mtk_eth_soc-allocate-struct-mtk_ppe-sep.patch @@ -0,0 +1,159 @@ +From: Felix Fietkau +Date: Mon, 21 Feb 2022 15:38:20 +0100 +Subject: [PATCH] net: ethernet: mtk_eth_soc: allocate struct mtk_ppe + separately + +Preparation for adding more data to it, which will increase its size. + +Signed-off-by: Felix Fietkau +--- + +--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c ++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c +@@ -2332,7 +2332,7 @@ static int mtk_open(struct net_device *d + if (err) + return err; + +- if (eth->soc->offload_version && mtk_ppe_start(ð->ppe) == 0) ++ if (eth->soc->offload_version && mtk_ppe_start(eth->ppe) == 0) + gdm_config = MTK_GDMA_TO_PPE; + + mtk_gdm_config(eth, gdm_config); +@@ -2406,7 +2406,7 @@ static int mtk_stop(struct net_device *d + mtk_dma_free(eth); + + if (eth->soc->offload_version) +- mtk_ppe_stop(ð->ppe); ++ mtk_ppe_stop(eth->ppe); + + return 0; + } +@@ -3298,10 +3298,11 @@ static int mtk_probe(struct platform_dev + } + + if (eth->soc->offload_version) { +- err = mtk_ppe_init(ð->ppe, eth->dev, +- eth->base + MTK_ETH_PPE_BASE, 2); +- if (err) ++ eth->ppe = mtk_ppe_init(eth->dev, eth->base + MTK_ETH_PPE_BASE, 2); ++ if (!eth->ppe) { ++ err = -ENOMEM; + goto err_free_dev; ++ } + + err = mtk_eth_offload_init(eth); + if (err) +--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h ++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h +@@ -982,7 +982,7 @@ struct mtk_eth { + u32 rx_dma_l4_valid; + int ip_align; + +- struct mtk_ppe ppe; ++ struct mtk_ppe *ppe; + struct rhashtable flow_table; + }; + +--- a/drivers/net/ethernet/mediatek/mtk_ppe.c ++++ b/drivers/net/ethernet/mediatek/mtk_ppe.c +@@ -384,10 +384,15 @@ int mtk_foe_entry_commit(struct mtk_ppe + return hash; + } + +-int mtk_ppe_init(struct mtk_ppe *ppe, struct device *dev, void __iomem *base, ++struct mtk_ppe *mtk_ppe_init(struct device *dev, void __iomem *base, + int version) + { + struct mtk_foe_entry *foe; ++ struct mtk_ppe *ppe; ++ ++ ppe = devm_kzalloc(dev, sizeof(*ppe), GFP_KERNEL); ++ if (!ppe) ++ return NULL; + + /* need to allocate a separate device, since it PPE DMA access is + * not coherent. +@@ -399,13 +404,13 @@ int mtk_ppe_init(struct mtk_ppe *ppe, st + foe = dmam_alloc_coherent(ppe->dev, MTK_PPE_ENTRIES * sizeof(*foe), + &ppe->foe_phys, GFP_KERNEL); + if (!foe) +- return -ENOMEM; ++ return NULL; + + ppe->foe_table = foe; + + mtk_ppe_debugfs_init(ppe); + +- return 0; ++ return ppe; + } + + static void mtk_ppe_init_foe_table(struct mtk_ppe *ppe) +--- a/drivers/net/ethernet/mediatek/mtk_ppe.h ++++ b/drivers/net/ethernet/mediatek/mtk_ppe.h +@@ -246,8 +246,7 @@ struct mtk_ppe { + void *acct_table; + }; + +-int mtk_ppe_init(struct mtk_ppe *ppe, struct device *dev, void __iomem *base, +- int version); ++struct mtk_ppe *mtk_ppe_init(struct device *dev, void __iomem *base, int version); + int mtk_ppe_start(struct mtk_ppe *ppe); + int mtk_ppe_stop(struct mtk_ppe *ppe); + +--- a/drivers/net/ethernet/mediatek/mtk_ppe_offload.c ++++ b/drivers/net/ethernet/mediatek/mtk_ppe_offload.c +@@ -411,7 +411,7 @@ mtk_flow_offload_replace(struct mtk_eth + + entry->cookie = f->cookie; + timestamp = mtk_eth_timestamp(eth); +- hash = mtk_foe_entry_commit(ð->ppe, &foe, timestamp); ++ hash = mtk_foe_entry_commit(eth->ppe, &foe, timestamp); + if (hash < 0) { + err = hash; + goto free; +@@ -426,7 +426,7 @@ mtk_flow_offload_replace(struct mtk_eth + + return 0; + clear_flow: +- mtk_foe_entry_clear(ð->ppe, hash); ++ mtk_foe_entry_clear(eth->ppe, hash); + free: + kfree(entry); + if (wed_index >= 0) +@@ -444,7 +444,7 @@ mtk_flow_offload_destroy(struct mtk_eth + if (!entry) + return -ENOENT; + +- mtk_foe_entry_clear(ð->ppe, entry->hash); ++ mtk_foe_entry_clear(eth->ppe, entry->hash); + rhashtable_remove_fast(ð->flow_table, &entry->node, + mtk_flow_ht_params); + if (entry->wed_index >= 0) +@@ -466,7 +466,7 @@ mtk_flow_offload_stats(struct mtk_eth *e + if (!entry) + return -ENOENT; + +- timestamp = mtk_foe_entry_timestamp(ð->ppe, entry->hash); ++ timestamp = mtk_foe_entry_timestamp(eth->ppe, entry->hash); + if (timestamp < 0) + return -ETIMEDOUT; + +@@ -522,7 +522,7 @@ mtk_eth_setup_tc_block(struct net_device + struct flow_block_cb *block_cb; + flow_setup_cb_t *cb; + +- if (!eth->ppe.foe_table) ++ if (!eth->ppe || !eth->ppe->foe_table) + return -EOPNOTSUPP; + + if (f->binder_type != FLOW_BLOCK_BINDER_TYPE_CLSACT_INGRESS) +@@ -574,7 +574,7 @@ int mtk_eth_setup_tc(struct net_device * + + int mtk_eth_offload_init(struct mtk_eth *eth) + { +- if (!eth->ppe.foe_table) ++ if (!eth->ppe || !eth->ppe->foe_table) + return 0; + + return rhashtable_init(ð->flow_table, &mtk_flow_ht_params); diff --git a/target/linux/generic/backport-6.1/702-v5.19-08-net-ethernet-mtk_eth_soc-rework-hardware-flow-table-.patch b/target/linux/generic/backport-6.1/702-v5.19-08-net-ethernet-mtk_eth_soc-rework-hardware-flow-table-.patch new file mode 100644 index 0000000000..76e8eb11fd --- /dev/null +++ b/target/linux/generic/backport-6.1/702-v5.19-08-net-ethernet-mtk_eth_soc-rework-hardware-flow-table-.patch @@ -0,0 +1,424 @@ +From: Felix Fietkau +Date: Mon, 21 Feb 2022 15:39:18 +0100 +Subject: [PATCH] net: ethernet: mtk_eth_soc: rework hardware flow table + management + +The hardware was designed to handle flow detection and creation of flow entries +by itself, relying on the software primarily for filling in egress routing +information. +When there is a hash collision between multiple flows, this allows the hardware +to maintain the entry for the most active flow. +Additionally, the hardware only keeps offloading active for entries with at +least 30 packets per second. + +With this rework, the code no longer creates a hardware entries directly. +Instead, the hardware entry is only created when the PPE reports a matching +unbound flow with the minimum target rate. +In order to reduce CPU overhead, looking for flows belonging to a hash entry +is rate limited to once every 100ms. + +This rework is also used as preparation for emulating bridge offload by +managing L4 offload entries on demand. + +Signed-off-by: Felix Fietkau +--- + +--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c ++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c +@@ -21,6 +21,7 @@ + #include + #include + #include ++#include + #include + + #include "mtk_eth_soc.h" +@@ -1292,7 +1293,7 @@ static int mtk_poll_rx(struct napi_struc + struct net_device *netdev; + unsigned int pktlen; + dma_addr_t dma_addr; +- u32 hash; ++ u32 hash, reason; + int mac; + + ring = mtk_get_rx_ring(eth); +@@ -1371,6 +1372,11 @@ static int mtk_poll_rx(struct napi_struc + skb_set_hash(skb, hash, PKT_HASH_TYPE_L4); + } + ++ reason = FIELD_GET(MTK_RXD4_PPE_CPU_REASON, trxd.rxd4); ++ if (reason == MTK_PPE_CPU_REASON_HIT_UNBIND_RATE_REACHED) ++ mtk_ppe_check_skb(eth->ppe, skb, ++ trxd.rxd4 & MTK_RXD4_FOE_ENTRY); ++ + if (netdev->features & NETIF_F_HW_VLAN_CTAG_RX && + (trxd.rxd2 & RX_DMA_VTAG)) + __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), +@@ -3298,7 +3304,7 @@ static int mtk_probe(struct platform_dev + } + + if (eth->soc->offload_version) { +- eth->ppe = mtk_ppe_init(eth->dev, eth->base + MTK_ETH_PPE_BASE, 2); ++ eth->ppe = mtk_ppe_init(eth, eth->base + MTK_ETH_PPE_BASE, 2); + if (!eth->ppe) { + err = -ENOMEM; + goto err_free_dev; +--- a/drivers/net/ethernet/mediatek/mtk_ppe.c ++++ b/drivers/net/ethernet/mediatek/mtk_ppe.c +@@ -6,9 +6,12 @@ + #include + #include + #include ++#include "mtk_eth_soc.h" + #include "mtk_ppe.h" + #include "mtk_ppe_regs.h" + ++static DEFINE_SPINLOCK(ppe_lock); ++ + static void ppe_w32(struct mtk_ppe *ppe, u32 reg, u32 val) + { + writel(val, ppe->base + reg); +@@ -41,6 +44,11 @@ static u32 ppe_clear(struct mtk_ppe *ppe + return ppe_m32(ppe, reg, val, 0); + } + ++static u32 mtk_eth_timestamp(struct mtk_eth *eth) ++{ ++ return mtk_r32(eth, 0x0010) & MTK_FOE_IB1_BIND_TIMESTAMP; ++} ++ + static int mtk_ppe_wait_busy(struct mtk_ppe *ppe) + { + int ret; +@@ -353,26 +361,59 @@ static inline bool mtk_foe_entry_usable( + FIELD_GET(MTK_FOE_IB1_STATE, entry->ib1) != MTK_FOE_STATE_BIND; + } + +-int mtk_foe_entry_commit(struct mtk_ppe *ppe, struct mtk_foe_entry *entry, +- u16 timestamp) ++static bool ++mtk_flow_entry_match(struct mtk_flow_entry *entry, struct mtk_foe_entry *data) ++{ ++ int type, len; ++ ++ if ((data->ib1 ^ entry->data.ib1) & MTK_FOE_IB1_UDP) ++ return false; ++ ++ type = FIELD_GET(MTK_FOE_IB1_PACKET_TYPE, entry->data.ib1); ++ if (type > MTK_PPE_PKT_TYPE_IPV4_DSLITE) ++ len = offsetof(struct mtk_foe_entry, ipv6._rsv); ++ else ++ len = offsetof(struct mtk_foe_entry, ipv4.ib2); ++ ++ return !memcmp(&entry->data.data, &data->data, len - 4); ++} ++ ++static void ++mtk_flow_entry_update(struct mtk_ppe *ppe, struct mtk_flow_entry *entry) + { + struct mtk_foe_entry *hwe; +- u32 hash; ++ struct mtk_foe_entry foe; + ++ spin_lock_bh(&ppe_lock); ++ if (entry->hash == 0xffff) ++ goto out; ++ ++ hwe = &ppe->foe_table[entry->hash]; ++ memcpy(&foe, hwe, sizeof(foe)); ++ if (!mtk_flow_entry_match(entry, &foe)) { ++ entry->hash = 0xffff; ++ goto out; ++ } ++ ++ entry->data.ib1 = foe.ib1; ++ ++out: ++ spin_unlock_bh(&ppe_lock); ++} ++ ++static void ++__mtk_foe_entry_commit(struct mtk_ppe *ppe, struct mtk_foe_entry *entry, ++ u16 hash) ++{ ++ struct mtk_foe_entry *hwe; ++ u16 timestamp; ++ ++ timestamp = mtk_eth_timestamp(ppe->eth); + timestamp &= MTK_FOE_IB1_BIND_TIMESTAMP; + entry->ib1 &= ~MTK_FOE_IB1_BIND_TIMESTAMP; + entry->ib1 |= FIELD_PREP(MTK_FOE_IB1_BIND_TIMESTAMP, timestamp); + +- hash = mtk_ppe_hash_entry(entry); + hwe = &ppe->foe_table[hash]; +- if (!mtk_foe_entry_usable(hwe)) { +- hwe++; +- hash++; +- +- if (!mtk_foe_entry_usable(hwe)) +- return -ENOSPC; +- } +- + memcpy(&hwe->data, &entry->data, sizeof(hwe->data)); + wmb(); + hwe->ib1 = entry->ib1; +@@ -380,13 +421,77 @@ int mtk_foe_entry_commit(struct mtk_ppe + dma_wmb(); + + mtk_ppe_cache_clear(ppe); ++} + +- return hash; ++void mtk_foe_entry_clear(struct mtk_ppe *ppe, struct mtk_flow_entry *entry) ++{ ++ spin_lock_bh(&ppe_lock); ++ hlist_del_init(&entry->list); ++ if (entry->hash != 0xffff) { ++ ppe->foe_table[entry->hash].ib1 &= ~MTK_FOE_IB1_STATE; ++ ppe->foe_table[entry->hash].ib1 |= FIELD_PREP(MTK_FOE_IB1_STATE, ++ MTK_FOE_STATE_BIND); ++ dma_wmb(); ++ } ++ entry->hash = 0xffff; ++ spin_unlock_bh(&ppe_lock); ++} ++ ++int mtk_foe_entry_commit(struct mtk_ppe *ppe, struct mtk_flow_entry *entry) ++{ ++ u32 hash = mtk_ppe_hash_entry(&entry->data); ++ ++ entry->hash = 0xffff; ++ spin_lock_bh(&ppe_lock); ++ hlist_add_head(&entry->list, &ppe->foe_flow[hash / 2]); ++ spin_unlock_bh(&ppe_lock); ++ ++ return 0; ++} ++ ++void __mtk_ppe_check_skb(struct mtk_ppe *ppe, struct sk_buff *skb, u16 hash) ++{ ++ struct hlist_head *head = &ppe->foe_flow[hash / 2]; ++ struct mtk_flow_entry *entry; ++ struct mtk_foe_entry *hwe = &ppe->foe_table[hash]; ++ bool found = false; ++ ++ if (hlist_empty(head)) ++ return; ++ ++ spin_lock_bh(&ppe_lock); ++ hlist_for_each_entry(entry, head, list) { ++ if (found || !mtk_flow_entry_match(entry, hwe)) { ++ if (entry->hash != 0xffff) ++ entry->hash = 0xffff; ++ continue; ++ } ++ ++ entry->hash = hash; ++ __mtk_foe_entry_commit(ppe, &entry->data, hash); ++ found = true; ++ } ++ spin_unlock_bh(&ppe_lock); ++} ++ ++int mtk_foe_entry_idle_time(struct mtk_ppe *ppe, struct mtk_flow_entry *entry) ++{ ++ u16 now = mtk_eth_timestamp(ppe->eth) & MTK_FOE_IB1_BIND_TIMESTAMP; ++ u16 timestamp; ++ ++ mtk_flow_entry_update(ppe, entry); ++ timestamp = entry->data.ib1 & MTK_FOE_IB1_BIND_TIMESTAMP; ++ ++ if (timestamp > now) ++ return MTK_FOE_IB1_BIND_TIMESTAMP + 1 - timestamp + now; ++ else ++ return now - timestamp; + } + +-struct mtk_ppe *mtk_ppe_init(struct device *dev, void __iomem *base, ++struct mtk_ppe *mtk_ppe_init(struct mtk_eth *eth, void __iomem *base, + int version) + { ++ struct device *dev = eth->dev; + struct mtk_foe_entry *foe; + struct mtk_ppe *ppe; + +@@ -398,6 +503,7 @@ struct mtk_ppe *mtk_ppe_init(struct devi + * not coherent. + */ + ppe->base = base; ++ ppe->eth = eth; + ppe->dev = dev; + ppe->version = version; + +--- a/drivers/net/ethernet/mediatek/mtk_ppe.h ++++ b/drivers/net/ethernet/mediatek/mtk_ppe.h +@@ -235,7 +235,17 @@ enum { + MTK_PPE_CPU_REASON_INVALID = 0x1f, + }; + ++struct mtk_flow_entry { ++ struct rhash_head node; ++ struct hlist_node list; ++ unsigned long cookie; ++ struct mtk_foe_entry data; ++ u16 hash; ++ s8 wed_index; ++}; ++ + struct mtk_ppe { ++ struct mtk_eth *eth; + struct device *dev; + void __iomem *base; + int version; +@@ -243,18 +253,33 @@ struct mtk_ppe { + struct mtk_foe_entry *foe_table; + dma_addr_t foe_phys; + ++ u16 foe_check_time[MTK_PPE_ENTRIES]; ++ struct hlist_head foe_flow[MTK_PPE_ENTRIES / 2]; ++ + void *acct_table; + }; + +-struct mtk_ppe *mtk_ppe_init(struct device *dev, void __iomem *base, int version); ++struct mtk_ppe *mtk_ppe_init(struct mtk_eth *eth, void __iomem *base, int version); + int mtk_ppe_start(struct mtk_ppe *ppe); + int mtk_ppe_stop(struct mtk_ppe *ppe); + ++void __mtk_ppe_check_skb(struct mtk_ppe *ppe, struct sk_buff *skb, u16 hash); ++ + static inline void +-mtk_foe_entry_clear(struct mtk_ppe *ppe, u16 hash) ++mtk_ppe_check_skb(struct mtk_ppe *ppe, struct sk_buff *skb, u16 hash) + { +- ppe->foe_table[hash].ib1 = 0; +- dma_wmb(); ++ u16 now, diff; ++ ++ if (!ppe) ++ return; ++ ++ now = (u16)jiffies; ++ diff = now - ppe->foe_check_time[hash]; ++ if (diff < HZ / 10) ++ return; ++ ++ ppe->foe_check_time[hash] = now; ++ __mtk_ppe_check_skb(ppe, skb, hash); + } + + static inline int +@@ -282,8 +307,9 @@ int mtk_foe_entry_set_vlan(struct mtk_fo + int mtk_foe_entry_set_pppoe(struct mtk_foe_entry *entry, int sid); + int mtk_foe_entry_set_wdma(struct mtk_foe_entry *entry, int wdma_idx, int txq, + int bss, int wcid); +-int mtk_foe_entry_commit(struct mtk_ppe *ppe, struct mtk_foe_entry *entry, +- u16 timestamp); ++int mtk_foe_entry_commit(struct mtk_ppe *ppe, struct mtk_flow_entry *entry); ++void mtk_foe_entry_clear(struct mtk_ppe *ppe, struct mtk_flow_entry *entry); ++int mtk_foe_entry_idle_time(struct mtk_ppe *ppe, struct mtk_flow_entry *entry); + int mtk_ppe_debugfs_init(struct mtk_ppe *ppe); + + #endif +--- a/drivers/net/ethernet/mediatek/mtk_ppe_offload.c ++++ b/drivers/net/ethernet/mediatek/mtk_ppe_offload.c +@@ -42,13 +42,6 @@ struct mtk_flow_data { + } pppoe; + }; + +-struct mtk_flow_entry { +- struct rhash_head node; +- unsigned long cookie; +- u16 hash; +- s8 wed_index; +-}; +- + static const struct rhashtable_params mtk_flow_ht_params = { + .head_offset = offsetof(struct mtk_flow_entry, node), + .key_offset = offsetof(struct mtk_flow_entry, cookie), +@@ -56,12 +49,6 @@ static const struct rhashtable_params mt + .automatic_shrinking = true, + }; + +-static u32 +-mtk_eth_timestamp(struct mtk_eth *eth) +-{ +- return mtk_r32(eth, 0x0010) & MTK_FOE_IB1_BIND_TIMESTAMP; +-} +- + static int + mtk_flow_set_ipv4_addr(struct mtk_foe_entry *foe, struct mtk_flow_data *data, + bool egress) +@@ -237,10 +224,8 @@ mtk_flow_offload_replace(struct mtk_eth + int offload_type = 0; + int wed_index = -1; + u16 addr_type = 0; +- u32 timestamp; + u8 l4proto = 0; + int err = 0; +- int hash; + int i; + + if (rhashtable_lookup(ð->flow_table, &f->cookie, mtk_flow_ht_params)) +@@ -410,23 +395,21 @@ mtk_flow_offload_replace(struct mtk_eth + return -ENOMEM; + + entry->cookie = f->cookie; +- timestamp = mtk_eth_timestamp(eth); +- hash = mtk_foe_entry_commit(eth->ppe, &foe, timestamp); +- if (hash < 0) { +- err = hash; ++ memcpy(&entry->data, &foe, sizeof(entry->data)); ++ entry->wed_index = wed_index; ++ ++ if (mtk_foe_entry_commit(eth->ppe, entry) < 0) + goto free; +- } + +- entry->hash = hash; +- entry->wed_index = wed_index; + err = rhashtable_insert_fast(ð->flow_table, &entry->node, + mtk_flow_ht_params); + if (err < 0) +- goto clear_flow; ++ goto clear; + + return 0; +-clear_flow: +- mtk_foe_entry_clear(eth->ppe, hash); ++ ++clear: ++ mtk_foe_entry_clear(eth->ppe, entry); + free: + kfree(entry); + if (wed_index >= 0) +@@ -444,7 +427,7 @@ mtk_flow_offload_destroy(struct mtk_eth + if (!entry) + return -ENOENT; + +- mtk_foe_entry_clear(eth->ppe, entry->hash); ++ mtk_foe_entry_clear(eth->ppe, entry); + rhashtable_remove_fast(ð->flow_table, &entry->node, + mtk_flow_ht_params); + if (entry->wed_index >= 0) +@@ -458,7 +441,6 @@ static int + mtk_flow_offload_stats(struct mtk_eth *eth, struct flow_cls_offload *f) + { + struct mtk_flow_entry *entry; +- int timestamp; + u32 idle; + + entry = rhashtable_lookup(ð->flow_table, &f->cookie, +@@ -466,11 +448,7 @@ mtk_flow_offload_stats(struct mtk_eth *e + if (!entry) + return -ENOENT; + +- timestamp = mtk_foe_entry_timestamp(eth->ppe, entry->hash); +- if (timestamp < 0) +- return -ETIMEDOUT; +- +- idle = mtk_eth_timestamp(eth) - timestamp; ++ idle = mtk_foe_entry_idle_time(eth->ppe, entry); + f->stats.lastused = jiffies - idle * HZ; + + return 0; diff --git a/target/linux/generic/backport-6.1/702-v5.19-09-net-ethernet-mtk_eth_soc-remove-bridge-flow-offload-.patch b/target/linux/generic/backport-6.1/702-v5.19-09-net-ethernet-mtk_eth_soc-remove-bridge-flow-offload-.patch new file mode 100644 index 0000000000..2ff0b341f9 --- /dev/null +++ b/target/linux/generic/backport-6.1/702-v5.19-09-net-ethernet-mtk_eth_soc-remove-bridge-flow-offload-.patch @@ -0,0 +1,44 @@ +From: Felix Fietkau +Date: Mon, 21 Feb 2022 15:55:19 +0100 +Subject: [PATCH] net: ethernet: mtk_eth_soc: remove bridge flow offload + type entry support + +According to MediaTek, this feature is not supported in current hardware + +Signed-off-by: Felix Fietkau +--- + +--- a/drivers/net/ethernet/mediatek/mtk_ppe.c ++++ b/drivers/net/ethernet/mediatek/mtk_ppe.c +@@ -84,13 +84,6 @@ static u32 mtk_ppe_hash_entry(struct mtk + u32 hash; + + switch (FIELD_GET(MTK_FOE_IB1_PACKET_TYPE, e->ib1)) { +- case MTK_PPE_PKT_TYPE_BRIDGE: +- hv1 = e->bridge.src_mac_lo; +- hv1 ^= ((e->bridge.src_mac_hi & 0xffff) << 16); +- hv2 = e->bridge.src_mac_hi >> 16; +- hv2 ^= e->bridge.dest_mac_lo; +- hv3 = e->bridge.dest_mac_hi; +- break; + case MTK_PPE_PKT_TYPE_IPV4_ROUTE: + case MTK_PPE_PKT_TYPE_IPV4_HNAPT: + hv1 = e->ipv4.orig.ports; +@@ -572,7 +565,6 @@ int mtk_ppe_start(struct mtk_ppe *ppe) + MTK_PPE_FLOW_CFG_IP4_NAT | + MTK_PPE_FLOW_CFG_IP4_NAPT | + MTK_PPE_FLOW_CFG_IP4_DSLITE | +- MTK_PPE_FLOW_CFG_L2_BRIDGE | + MTK_PPE_FLOW_CFG_IP4_NAT_FRAG; + ppe_w32(ppe, MTK_PPE_FLOW_CFG, val); + +--- a/drivers/net/ethernet/mediatek/mtk_ppe_debugfs.c ++++ b/drivers/net/ethernet/mediatek/mtk_ppe_debugfs.c +@@ -32,7 +32,6 @@ static const char *mtk_foe_pkt_type_str( + static const char * const type_str[] = { + [MTK_PPE_PKT_TYPE_IPV4_HNAPT] = "IPv4 5T", + [MTK_PPE_PKT_TYPE_IPV4_ROUTE] = "IPv4 3T", +- [MTK_PPE_PKT_TYPE_BRIDGE] = "L2", + [MTK_PPE_PKT_TYPE_IPV4_DSLITE] = "DS-LITE", + [MTK_PPE_PKT_TYPE_IPV6_ROUTE_3T] = "IPv6 3T", + [MTK_PPE_PKT_TYPE_IPV6_ROUTE_5T] = "IPv6 5T", diff --git a/target/linux/generic/backport-6.1/702-v5.19-10-net-ethernet-mtk_eth_soc-support-creating-mac-addres.patch b/target/linux/generic/backport-6.1/702-v5.19-10-net-ethernet-mtk_eth_soc-support-creating-mac-addres.patch new file mode 100644 index 0000000000..209c65e66a --- /dev/null +++ b/target/linux/generic/backport-6.1/702-v5.19-10-net-ethernet-mtk_eth_soc-support-creating-mac-addres.patch @@ -0,0 +1,553 @@ +From: Felix Fietkau +Date: Wed, 23 Feb 2022 10:56:34 +0100 +Subject: [PATCH] net: ethernet: mtk_eth_soc: support creating mac + address based offload entries + +This will be used to implement a limited form of bridge offloading. +Since the hardware does not support flow table entries with just source +and destination MAC address, the driver has to emulate it. + +The hardware automatically creates entries entries for incoming flows, even +when they are bridged instead of routed, and reports when packets for these +flows have reached the minimum PPS rate for offloading. + +After this happens, we look up the L2 flow offload entry based on the MAC +header and fill in the output routing information in the flow table. +The dynamically created per-flow entries are automatically removed when +either the hardware flowtable entry expires, is replaced, or if the offload +rule they belong to is removed + +Signed-off-by: Felix Fietkau +--- + +--- a/drivers/net/ethernet/mediatek/mtk_ppe.c ++++ b/drivers/net/ethernet/mediatek/mtk_ppe.c +@@ -6,12 +6,22 @@ + #include + #include + #include ++#include ++#include ++#include + #include "mtk_eth_soc.h" + #include "mtk_ppe.h" + #include "mtk_ppe_regs.h" + + static DEFINE_SPINLOCK(ppe_lock); + ++static const struct rhashtable_params mtk_flow_l2_ht_params = { ++ .head_offset = offsetof(struct mtk_flow_entry, l2_node), ++ .key_offset = offsetof(struct mtk_flow_entry, data.bridge), ++ .key_len = offsetof(struct mtk_foe_bridge, key_end), ++ .automatic_shrinking = true, ++}; ++ + static void ppe_w32(struct mtk_ppe *ppe, u32 reg, u32 val) + { + writel(val, ppe->base + reg); +@@ -123,6 +133,9 @@ mtk_foe_entry_l2(struct mtk_foe_entry *e + { + int type = FIELD_GET(MTK_FOE_IB1_PACKET_TYPE, entry->ib1); + ++ if (type == MTK_PPE_PKT_TYPE_BRIDGE) ++ return &entry->bridge.l2; ++ + if (type >= MTK_PPE_PKT_TYPE_IPV4_DSLITE) + return &entry->ipv6.l2; + +@@ -134,6 +147,9 @@ mtk_foe_entry_ib2(struct mtk_foe_entry * + { + int type = FIELD_GET(MTK_FOE_IB1_PACKET_TYPE, entry->ib1); + ++ if (type == MTK_PPE_PKT_TYPE_BRIDGE) ++ return &entry->bridge.ib2; ++ + if (type >= MTK_PPE_PKT_TYPE_IPV4_DSLITE) + return &entry->ipv6.ib2; + +@@ -168,7 +184,12 @@ int mtk_foe_entry_prepare(struct mtk_foe + if (type == MTK_PPE_PKT_TYPE_IPV6_ROUTE_3T) + entry->ipv6.ports = ports_pad; + +- if (type >= MTK_PPE_PKT_TYPE_IPV4_DSLITE) { ++ if (type == MTK_PPE_PKT_TYPE_BRIDGE) { ++ ether_addr_copy(entry->bridge.src_mac, src_mac); ++ ether_addr_copy(entry->bridge.dest_mac, dest_mac); ++ entry->bridge.ib2 = val; ++ l2 = &entry->bridge.l2; ++ } else if (type >= MTK_PPE_PKT_TYPE_IPV4_DSLITE) { + entry->ipv6.ib2 = val; + l2 = &entry->ipv6.l2; + } else { +@@ -372,12 +393,96 @@ mtk_flow_entry_match(struct mtk_flow_ent + } + + static void ++__mtk_foe_entry_clear(struct mtk_ppe *ppe, struct mtk_flow_entry *entry) ++{ ++ struct hlist_head *head; ++ struct hlist_node *tmp; ++ ++ if (entry->type == MTK_FLOW_TYPE_L2) { ++ rhashtable_remove_fast(&ppe->l2_flows, &entry->l2_node, ++ mtk_flow_l2_ht_params); ++ ++ head = &entry->l2_flows; ++ hlist_for_each_entry_safe(entry, tmp, head, l2_data.list) ++ __mtk_foe_entry_clear(ppe, entry); ++ return; ++ } ++ ++ hlist_del_init(&entry->list); ++ if (entry->hash != 0xffff) { ++ ppe->foe_table[entry->hash].ib1 &= ~MTK_FOE_IB1_STATE; ++ ppe->foe_table[entry->hash].ib1 |= FIELD_PREP(MTK_FOE_IB1_STATE, ++ MTK_FOE_STATE_BIND); ++ dma_wmb(); ++ } ++ entry->hash = 0xffff; ++ ++ if (entry->type != MTK_FLOW_TYPE_L2_SUBFLOW) ++ return; ++ ++ hlist_del_init(&entry->l2_data.list); ++ kfree(entry); ++} ++ ++static int __mtk_foe_entry_idle_time(struct mtk_ppe *ppe, u32 ib1) ++{ ++ u16 timestamp; ++ u16 now; ++ ++ now = mtk_eth_timestamp(ppe->eth) & MTK_FOE_IB1_BIND_TIMESTAMP; ++ timestamp = ib1 & MTK_FOE_IB1_BIND_TIMESTAMP; ++ ++ if (timestamp > now) ++ return MTK_FOE_IB1_BIND_TIMESTAMP + 1 - timestamp + now; ++ else ++ return now - timestamp; ++} ++ ++static void ++mtk_flow_entry_update_l2(struct mtk_ppe *ppe, struct mtk_flow_entry *entry) ++{ ++ struct mtk_flow_entry *cur; ++ struct mtk_foe_entry *hwe; ++ struct hlist_node *tmp; ++ int idle; ++ ++ idle = __mtk_foe_entry_idle_time(ppe, entry->data.ib1); ++ hlist_for_each_entry_safe(cur, tmp, &entry->l2_flows, l2_data.list) { ++ int cur_idle; ++ u32 ib1; ++ ++ hwe = &ppe->foe_table[cur->hash]; ++ ib1 = READ_ONCE(hwe->ib1); ++ ++ if (FIELD_GET(MTK_FOE_IB1_STATE, ib1) != MTK_FOE_STATE_BIND) { ++ cur->hash = 0xffff; ++ __mtk_foe_entry_clear(ppe, cur); ++ continue; ++ } ++ ++ cur_idle = __mtk_foe_entry_idle_time(ppe, ib1); ++ if (cur_idle >= idle) ++ continue; ++ ++ idle = cur_idle; ++ entry->data.ib1 &= ~MTK_FOE_IB1_BIND_TIMESTAMP; ++ entry->data.ib1 |= hwe->ib1 & MTK_FOE_IB1_BIND_TIMESTAMP; ++ } ++} ++ ++static void + mtk_flow_entry_update(struct mtk_ppe *ppe, struct mtk_flow_entry *entry) + { + struct mtk_foe_entry *hwe; + struct mtk_foe_entry foe; + + spin_lock_bh(&ppe_lock); ++ ++ if (entry->type == MTK_FLOW_TYPE_L2) { ++ mtk_flow_entry_update_l2(ppe, entry); ++ goto out; ++ } ++ + if (entry->hash == 0xffff) + goto out; + +@@ -419,21 +524,28 @@ __mtk_foe_entry_commit(struct mtk_ppe *p + void mtk_foe_entry_clear(struct mtk_ppe *ppe, struct mtk_flow_entry *entry) + { + spin_lock_bh(&ppe_lock); +- hlist_del_init(&entry->list); +- if (entry->hash != 0xffff) { +- ppe->foe_table[entry->hash].ib1 &= ~MTK_FOE_IB1_STATE; +- ppe->foe_table[entry->hash].ib1 |= FIELD_PREP(MTK_FOE_IB1_STATE, +- MTK_FOE_STATE_BIND); +- dma_wmb(); +- } +- entry->hash = 0xffff; ++ __mtk_foe_entry_clear(ppe, entry); + spin_unlock_bh(&ppe_lock); + } + ++static int ++mtk_foe_entry_commit_l2(struct mtk_ppe *ppe, struct mtk_flow_entry *entry) ++{ ++ entry->type = MTK_FLOW_TYPE_L2; ++ ++ return rhashtable_insert_fast(&ppe->l2_flows, &entry->l2_node, ++ mtk_flow_l2_ht_params); ++} ++ + int mtk_foe_entry_commit(struct mtk_ppe *ppe, struct mtk_flow_entry *entry) + { +- u32 hash = mtk_ppe_hash_entry(&entry->data); ++ int type = FIELD_GET(MTK_FOE_IB1_PACKET_TYPE, entry->data.ib1); ++ u32 hash; ++ ++ if (type == MTK_PPE_PKT_TYPE_BRIDGE) ++ return mtk_foe_entry_commit_l2(ppe, entry); + ++ hash = mtk_ppe_hash_entry(&entry->data); + entry->hash = 0xffff; + spin_lock_bh(&ppe_lock); + hlist_add_head(&entry->list, &ppe->foe_flow[hash / 2]); +@@ -442,18 +554,72 @@ int mtk_foe_entry_commit(struct mtk_ppe + return 0; + } + ++static void ++mtk_foe_entry_commit_subflow(struct mtk_ppe *ppe, struct mtk_flow_entry *entry, ++ u16 hash) ++{ ++ struct mtk_flow_entry *flow_info; ++ struct mtk_foe_entry foe, *hwe; ++ struct mtk_foe_mac_info *l2; ++ u32 ib1_mask = MTK_FOE_IB1_PACKET_TYPE | MTK_FOE_IB1_UDP; ++ int type; ++ ++ flow_info = kzalloc(offsetof(struct mtk_flow_entry, l2_data.end), ++ GFP_ATOMIC); ++ if (!flow_info) ++ return; ++ ++ flow_info->l2_data.base_flow = entry; ++ flow_info->type = MTK_FLOW_TYPE_L2_SUBFLOW; ++ flow_info->hash = hash; ++ hlist_add_head(&flow_info->list, &ppe->foe_flow[hash / 2]); ++ hlist_add_head(&flow_info->l2_data.list, &entry->l2_flows); ++ ++ hwe = &ppe->foe_table[hash]; ++ memcpy(&foe, hwe, sizeof(foe)); ++ foe.ib1 &= ib1_mask; ++ foe.ib1 |= entry->data.ib1 & ~ib1_mask; ++ ++ l2 = mtk_foe_entry_l2(&foe); ++ memcpy(l2, &entry->data.bridge.l2, sizeof(*l2)); ++ ++ type = FIELD_GET(MTK_FOE_IB1_PACKET_TYPE, foe.ib1); ++ if (type == MTK_PPE_PKT_TYPE_IPV4_HNAPT) ++ memcpy(&foe.ipv4.new, &foe.ipv4.orig, sizeof(foe.ipv4.new)); ++ else if (type >= MTK_PPE_PKT_TYPE_IPV6_ROUTE_3T && l2->etype == ETH_P_IP) ++ l2->etype = ETH_P_IPV6; ++ ++ *mtk_foe_entry_ib2(&foe) = entry->data.bridge.ib2; ++ ++ __mtk_foe_entry_commit(ppe, &foe, hash); ++} ++ + void __mtk_ppe_check_skb(struct mtk_ppe *ppe, struct sk_buff *skb, u16 hash) + { + struct hlist_head *head = &ppe->foe_flow[hash / 2]; +- struct mtk_flow_entry *entry; + struct mtk_foe_entry *hwe = &ppe->foe_table[hash]; ++ struct mtk_flow_entry *entry; ++ struct mtk_foe_bridge key = {}; ++ struct ethhdr *eh; + bool found = false; +- +- if (hlist_empty(head)) +- return; ++ u8 *tag; + + spin_lock_bh(&ppe_lock); ++ ++ if (FIELD_GET(MTK_FOE_IB1_STATE, hwe->ib1) == MTK_FOE_STATE_BIND) ++ goto out; ++ + hlist_for_each_entry(entry, head, list) { ++ if (entry->type == MTK_FLOW_TYPE_L2_SUBFLOW) { ++ if (unlikely(FIELD_GET(MTK_FOE_IB1_STATE, hwe->ib1) == ++ MTK_FOE_STATE_BIND)) ++ continue; ++ ++ entry->hash = 0xffff; ++ __mtk_foe_entry_clear(ppe, entry); ++ continue; ++ } ++ + if (found || !mtk_flow_entry_match(entry, hwe)) { + if (entry->hash != 0xffff) + entry->hash = 0xffff; +@@ -464,21 +630,50 @@ void __mtk_ppe_check_skb(struct mtk_ppe + __mtk_foe_entry_commit(ppe, &entry->data, hash); + found = true; + } ++ ++ if (found) ++ goto out; ++ ++ eh = eth_hdr(skb); ++ ether_addr_copy(key.dest_mac, eh->h_dest); ++ ether_addr_copy(key.src_mac, eh->h_source); ++ tag = skb->data - 2; ++ key.vlan = 0; ++ switch (skb->protocol) { ++#if IS_ENABLED(CONFIG_NET_DSA) ++ case htons(ETH_P_XDSA): ++ if (!netdev_uses_dsa(skb->dev) || ++ skb->dev->dsa_ptr->tag_ops->proto != DSA_TAG_PROTO_MTK) ++ goto out; ++ ++ tag += 4; ++ if (get_unaligned_be16(tag) != ETH_P_8021Q) ++ break; ++ ++ fallthrough; ++#endif ++ case htons(ETH_P_8021Q): ++ key.vlan = get_unaligned_be16(tag + 2) & VLAN_VID_MASK; ++ break; ++ default: ++ break; ++ } ++ ++ entry = rhashtable_lookup_fast(&ppe->l2_flows, &key, mtk_flow_l2_ht_params); ++ if (!entry) ++ goto out; ++ ++ mtk_foe_entry_commit_subflow(ppe, entry, hash); ++ ++out: + spin_unlock_bh(&ppe_lock); + } + + int mtk_foe_entry_idle_time(struct mtk_ppe *ppe, struct mtk_flow_entry *entry) + { +- u16 now = mtk_eth_timestamp(ppe->eth) & MTK_FOE_IB1_BIND_TIMESTAMP; +- u16 timestamp; +- + mtk_flow_entry_update(ppe, entry); +- timestamp = entry->data.ib1 & MTK_FOE_IB1_BIND_TIMESTAMP; + +- if (timestamp > now) +- return MTK_FOE_IB1_BIND_TIMESTAMP + 1 - timestamp + now; +- else +- return now - timestamp; ++ return __mtk_foe_entry_idle_time(ppe, entry->data.ib1); + } + + struct mtk_ppe *mtk_ppe_init(struct mtk_eth *eth, void __iomem *base, +@@ -492,6 +687,8 @@ struct mtk_ppe *mtk_ppe_init(struct mtk_ + if (!ppe) + return NULL; + ++ rhashtable_init(&ppe->l2_flows, &mtk_flow_l2_ht_params); ++ + /* need to allocate a separate device, since it PPE DMA access is + * not coherent. + */ +--- a/drivers/net/ethernet/mediatek/mtk_ppe.h ++++ b/drivers/net/ethernet/mediatek/mtk_ppe.h +@@ -6,6 +6,7 @@ + + #include + #include ++#include + + #define MTK_ETH_PPE_BASE 0xc00 + +@@ -84,19 +85,16 @@ struct mtk_foe_mac_info { + u16 src_mac_lo; + }; + ++/* software-only entry type */ + struct mtk_foe_bridge { +- u32 dest_mac_hi; +- +- u16 src_mac_lo; +- u16 dest_mac_lo; ++ u8 dest_mac[ETH_ALEN]; ++ u8 src_mac[ETH_ALEN]; ++ u16 vlan; + +- u32 src_mac_hi; ++ struct {} key_end; + + u32 ib2; + +- u32 _rsv[5]; +- +- u32 udf_tsid; + struct mtk_foe_mac_info l2; + }; + +@@ -235,13 +233,33 @@ enum { + MTK_PPE_CPU_REASON_INVALID = 0x1f, + }; + ++enum { ++ MTK_FLOW_TYPE_L4, ++ MTK_FLOW_TYPE_L2, ++ MTK_FLOW_TYPE_L2_SUBFLOW, ++}; ++ + struct mtk_flow_entry { ++ union { ++ struct hlist_node list; ++ struct { ++ struct rhash_head l2_node; ++ struct hlist_head l2_flows; ++ }; ++ }; ++ u8 type; ++ s8 wed_index; ++ u16 hash; ++ union { ++ struct mtk_foe_entry data; ++ struct { ++ struct mtk_flow_entry *base_flow; ++ struct hlist_node list; ++ struct {} end; ++ } l2_data; ++ }; + struct rhash_head node; +- struct hlist_node list; + unsigned long cookie; +- struct mtk_foe_entry data; +- u16 hash; +- s8 wed_index; + }; + + struct mtk_ppe { +@@ -256,6 +274,8 @@ struct mtk_ppe { + u16 foe_check_time[MTK_PPE_ENTRIES]; + struct hlist_head foe_flow[MTK_PPE_ENTRIES / 2]; + ++ struct rhashtable l2_flows; ++ + void *acct_table; + }; + +--- a/drivers/net/ethernet/mediatek/mtk_ppe_offload.c ++++ b/drivers/net/ethernet/mediatek/mtk_ppe_offload.c +@@ -31,6 +31,8 @@ struct mtk_flow_data { + __be16 src_port; + __be16 dst_port; + ++ u16 vlan_in; ++ + struct { + u16 id; + __be16 proto; +@@ -257,9 +259,45 @@ mtk_flow_offload_replace(struct mtk_eth + return -EOPNOTSUPP; + } + ++ switch (addr_type) { ++ case 0: ++ offload_type = MTK_PPE_PKT_TYPE_BRIDGE; ++ if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_ETH_ADDRS)) { ++ struct flow_match_eth_addrs match; ++ ++ flow_rule_match_eth_addrs(rule, &match); ++ memcpy(data.eth.h_dest, match.key->dst, ETH_ALEN); ++ memcpy(data.eth.h_source, match.key->src, ETH_ALEN); ++ } else { ++ return -EOPNOTSUPP; ++ } ++ ++ if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_VLAN)) { ++ struct flow_match_vlan match; ++ ++ flow_rule_match_vlan(rule, &match); ++ ++ if (match.key->vlan_tpid != cpu_to_be16(ETH_P_8021Q)) ++ return -EOPNOTSUPP; ++ ++ data.vlan_in = match.key->vlan_id; ++ } ++ break; ++ case FLOW_DISSECTOR_KEY_IPV4_ADDRS: ++ offload_type = MTK_PPE_PKT_TYPE_IPV4_HNAPT; ++ break; ++ case FLOW_DISSECTOR_KEY_IPV6_ADDRS: ++ offload_type = MTK_PPE_PKT_TYPE_IPV6_ROUTE_5T; ++ break; ++ default: ++ return -EOPNOTSUPP; ++ } ++ + flow_action_for_each(i, act, &rule->action) { + switch (act->id) { + case FLOW_ACTION_MANGLE: ++ if (offload_type == MTK_PPE_PKT_TYPE_BRIDGE) ++ return -EOPNOTSUPP; + if (act->mangle.htype == FLOW_ACT_MANGLE_HDR_TYPE_ETH) + mtk_flow_offload_mangle_eth(act, &data.eth); + break; +@@ -291,17 +329,6 @@ mtk_flow_offload_replace(struct mtk_eth + } + } + +- switch (addr_type) { +- case FLOW_DISSECTOR_KEY_IPV4_ADDRS: +- offload_type = MTK_PPE_PKT_TYPE_IPV4_HNAPT; +- break; +- case FLOW_DISSECTOR_KEY_IPV6_ADDRS: +- offload_type = MTK_PPE_PKT_TYPE_IPV6_ROUTE_5T; +- break; +- default: +- return -EOPNOTSUPP; +- } +- + if (!is_valid_ether_addr(data.eth.h_source) || + !is_valid_ether_addr(data.eth.h_dest)) + return -EINVAL; +@@ -315,10 +342,13 @@ mtk_flow_offload_replace(struct mtk_eth + if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_PORTS)) { + struct flow_match_ports ports; + ++ if (offload_type == MTK_PPE_PKT_TYPE_BRIDGE) ++ return -EOPNOTSUPP; ++ + flow_rule_match_ports(rule, &ports); + data.src_port = ports.key->src; + data.dst_port = ports.key->dst; +- } else { ++ } else if (offload_type != MTK_PPE_PKT_TYPE_BRIDGE) { + return -EOPNOTSUPP; + } + +@@ -348,6 +378,9 @@ mtk_flow_offload_replace(struct mtk_eth + if (act->id != FLOW_ACTION_MANGLE) + continue; + ++ if (offload_type == MTK_PPE_PKT_TYPE_BRIDGE) ++ return -EOPNOTSUPP; ++ + switch (act->mangle.htype) { + case FLOW_ACT_MANGLE_HDR_TYPE_TCP: + case FLOW_ACT_MANGLE_HDR_TYPE_UDP: +@@ -373,6 +406,9 @@ mtk_flow_offload_replace(struct mtk_eth + return err; + } + ++ if (offload_type == MTK_PPE_PKT_TYPE_BRIDGE) ++ foe.bridge.vlan = data.vlan_in; ++ + if (data.vlan.num == 1) { + if (data.vlan.proto != htons(ETH_P_8021Q)) + return -EOPNOTSUPP; diff --git a/target/linux/generic/backport-6.1/702-v5.19-11-net-ethernet-mtk_eth_soc-wed-fix-sparse-endian-warni.patch b/target/linux/generic/backport-6.1/702-v5.19-11-net-ethernet-mtk_eth_soc-wed-fix-sparse-endian-warni.patch new file mode 100644 index 0000000000..8f3dfe8239 --- /dev/null +++ b/target/linux/generic/backport-6.1/702-v5.19-11-net-ethernet-mtk_eth_soc-wed-fix-sparse-endian-warni.patch @@ -0,0 +1,56 @@ +From: Felix Fietkau +Date: Fri, 8 Apr 2022 10:59:45 +0200 +Subject: [PATCH] net: ethernet: mtk_eth_soc/wed: fix sparse endian warnings + +Descriptor fields are little-endian + +Fixes: 804775dfc288 ("net: ethernet: mtk_eth_soc: add support for Wireless Ethernet Dispatch (WED)") +Reported-by: kernel test robot +Signed-off-by: Felix Fietkau +Signed-off-by: David S. Miller +--- + +--- a/drivers/net/ethernet/mediatek/mtk_wed.c ++++ b/drivers/net/ethernet/mediatek/mtk_wed.c +@@ -144,16 +144,17 @@ mtk_wed_buffer_alloc(struct mtk_wed_devi + + for (s = 0; s < MTK_WED_BUF_PER_PAGE; s++) { + u32 txd_size; ++ u32 ctrl; + + txd_size = dev->wlan.init_buf(buf, buf_phys, token++); + +- desc->buf0 = buf_phys; +- desc->buf1 = buf_phys + txd_size; +- desc->ctrl = FIELD_PREP(MTK_WDMA_DESC_CTRL_LEN0, +- txd_size) | +- FIELD_PREP(MTK_WDMA_DESC_CTRL_LEN1, +- MTK_WED_BUF_SIZE - txd_size) | +- MTK_WDMA_DESC_CTRL_LAST_SEG1; ++ desc->buf0 = cpu_to_le32(buf_phys); ++ desc->buf1 = cpu_to_le32(buf_phys + txd_size); ++ ctrl = FIELD_PREP(MTK_WDMA_DESC_CTRL_LEN0, txd_size) | ++ FIELD_PREP(MTK_WDMA_DESC_CTRL_LEN1, ++ MTK_WED_BUF_SIZE - txd_size) | ++ MTK_WDMA_DESC_CTRL_LAST_SEG1; ++ desc->ctrl = cpu_to_le32(ctrl); + desc->info = 0; + desc++; + +@@ -184,12 +185,14 @@ mtk_wed_free_buffer(struct mtk_wed_devic + + for (i = 0, page_idx = 0; i < dev->buf_ring.size; i += MTK_WED_BUF_PER_PAGE) { + void *page = page_list[page_idx++]; ++ dma_addr_t buf_addr; + + if (!page) + break; + +- dma_unmap_page(dev->hw->dev, desc[i].buf0, +- PAGE_SIZE, DMA_BIDIRECTIONAL); ++ buf_addr = le32_to_cpu(desc[i].buf0); ++ dma_unmap_page(dev->hw->dev, buf_addr, PAGE_SIZE, ++ DMA_BIDIRECTIONAL); + __free_page(page); + } + diff --git a/target/linux/generic/backport-6.1/702-v5.19-12-net-ethernet-mtk_eth_soc-fix-return-value-check-in-m.patch b/target/linux/generic/backport-6.1/702-v5.19-12-net-ethernet-mtk_eth_soc-fix-return-value-check-in-m.patch new file mode 100644 index 0000000000..4ec8fe74bc --- /dev/null +++ b/target/linux/generic/backport-6.1/702-v5.19-12-net-ethernet-mtk_eth_soc-fix-return-value-check-in-m.patch @@ -0,0 +1,25 @@ +From: Yang Yingliang +Date: Fri, 8 Apr 2022 11:22:46 +0800 +Subject: [PATCH] net: ethernet: mtk_eth_soc: fix return value check in + mtk_wed_add_hw() + +If syscon_regmap_lookup_by_phandle() fails, it never return NULL pointer, +change the check to IS_ERR(). + +Fixes: 804775dfc288 ("net: ethernet: mtk_eth_soc: add support for Wireless Ethernet Dispatch (WED)") +Reported-by: Hulk Robot +Signed-off-by: Yang Yingliang +Signed-off-by: David S. Miller +--- + +--- a/drivers/net/ethernet/mediatek/mtk_wed.c ++++ b/drivers/net/ethernet/mediatek/mtk_wed.c +@@ -816,7 +816,7 @@ void mtk_wed_add_hw(struct device_node * + return; + + regs = syscon_regmap_lookup_by_phandle(np, NULL); +- if (!regs) ++ if (IS_ERR(regs)) + return; + + rcu_assign_pointer(mtk_soc_wed_ops, &wed_ops); diff --git a/target/linux/generic/backport-6.1/702-v5.19-13-net-ethernet-mtk_eth_soc-use-standard-property-for-c.patch b/target/linux/generic/backport-6.1/702-v5.19-13-net-ethernet-mtk_eth_soc-use-standard-property-for-c.patch new file mode 100644 index 0000000000..d87022fce4 --- /dev/null +++ b/target/linux/generic/backport-6.1/702-v5.19-13-net-ethernet-mtk_eth_soc-use-standard-property-for-c.patch @@ -0,0 +1,35 @@ +From: Lorenzo Bianconi +Date: Mon, 11 Apr 2022 12:13:25 +0200 +Subject: [PATCH] net: ethernet: mtk_eth_soc: use standard property for + cci-control-port + +Rely on standard cci-control-port property to identify CCI port +reference. +Update mt7622 dts binding. + +Signed-off-by: Lorenzo Bianconi +Signed-off-by: David S. Miller +--- + +--- a/arch/arm64/boot/dts/mediatek/mt7622.dtsi ++++ b/arch/arm64/boot/dts/mediatek/mt7622.dtsi +@@ -962,7 +962,7 @@ + power-domains = <&scpsys MT7622_POWER_DOMAIN_ETHSYS>; + mediatek,ethsys = <ðsys>; + mediatek,sgmiisys = <&sgmiisys>; +- mediatek,cci-control = <&cci_control2>; ++ cci-control-port = <&cci_control2>; + mediatek,wed = <&wed0>, <&wed1>; + mediatek,pcie-mirror = <&pcie_mirror>; + mediatek,hifsys = <&hifsys>; +--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c ++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c +@@ -3185,7 +3185,7 @@ static int mtk_probe(struct platform_dev + struct regmap *cci; + + cci = syscon_regmap_lookup_by_phandle(pdev->dev.of_node, +- "mediatek,cci-control"); ++ "cci-control-port"); + /* enable CPU/bus coherency */ + if (!IS_ERR(cci)) + regmap_write(cci, 0, 3); diff --git a/target/linux/generic/backport-6.1/702-v5.19-14-net-ethernet-mtk_eth_soc-use-after-free-in-__mtk_ppe.patch b/target/linux/generic/backport-6.1/702-v5.19-14-net-ethernet-mtk_eth_soc-use-after-free-in-__mtk_ppe.patch new file mode 100644 index 0000000000..656b3a159e --- /dev/null +++ b/target/linux/generic/backport-6.1/702-v5.19-14-net-ethernet-mtk_eth_soc-use-after-free-in-__mtk_ppe.patch @@ -0,0 +1,33 @@ +From: Dan Carpenter +Date: Tue, 12 Apr 2022 12:24:19 +0300 +Subject: [PATCH] net: ethernet: mtk_eth_soc: use after free in + __mtk_ppe_check_skb() + +The __mtk_foe_entry_clear() function frees "entry" so we have to use +the _safe() version of hlist_for_each_entry() to prevent a use after +free. + +Fixes: 33fc42de3327 ("net: ethernet: mtk_eth_soc: support creating mac address based offload entries") +Signed-off-by: Dan Carpenter +Signed-off-by: David S. Miller +--- + +--- a/drivers/net/ethernet/mediatek/mtk_ppe.c ++++ b/drivers/net/ethernet/mediatek/mtk_ppe.c +@@ -600,6 +600,7 @@ void __mtk_ppe_check_skb(struct mtk_ppe + struct mtk_foe_entry *hwe = &ppe->foe_table[hash]; + struct mtk_flow_entry *entry; + struct mtk_foe_bridge key = {}; ++ struct hlist_node *n; + struct ethhdr *eh; + bool found = false; + u8 *tag; +@@ -609,7 +610,7 @@ void __mtk_ppe_check_skb(struct mtk_ppe + if (FIELD_GET(MTK_FOE_IB1_STATE, hwe->ib1) == MTK_FOE_STATE_BIND) + goto out; + +- hlist_for_each_entry(entry, head, list) { ++ hlist_for_each_entry_safe(entry, n, head, list) { + if (entry->type == MTK_FLOW_TYPE_L2_SUBFLOW) { + if (unlikely(FIELD_GET(MTK_FOE_IB1_STATE, hwe->ib1) == + MTK_FOE_STATE_BIND)) diff --git a/target/linux/generic/backport-6.1/702-v5.19-15-net-ethernet-mtk_eth_soc-add-check-for-allocation-fa.patch b/target/linux/generic/backport-6.1/702-v5.19-15-net-ethernet-mtk_eth_soc-add-check-for-allocation-fa.patch new file mode 100644 index 0000000000..714163c86b --- /dev/null +++ b/target/linux/generic/backport-6.1/702-v5.19-15-net-ethernet-mtk_eth_soc-add-check-for-allocation-fa.patch @@ -0,0 +1,22 @@ +From: Dan Carpenter +Date: Thu, 21 Apr 2022 18:49:02 +0300 +Subject: [PATCH] net: ethernet: mtk_eth_soc: add check for allocation failure + +Check if the kzalloc() failed. + +Fixes: 804775dfc288 ("net: ethernet: mtk_eth_soc: add support for Wireless Ethernet Dispatch (WED)") +Signed-off-by: Dan Carpenter +Signed-off-by: David S. Miller +--- + +--- a/drivers/net/ethernet/mediatek/mtk_wed.c ++++ b/drivers/net/ethernet/mediatek/mtk_wed.c +@@ -827,6 +827,8 @@ void mtk_wed_add_hw(struct device_node * + goto unlock; + + hw = kzalloc(sizeof(*hw), GFP_KERNEL); ++ if (!hw) ++ goto unlock; + hw->node = np; + hw->regs = regs; + hw->eth = eth; diff --git a/target/linux/generic/backport-6.1/702-v5.19-16-eth-mtk_eth_soc-silence-the-GCC-12-array-bounds-warn.patch b/target/linux/generic/backport-6.1/702-v5.19-16-eth-mtk_eth_soc-silence-the-GCC-12-array-bounds-warn.patch new file mode 100644 index 0000000000..aa98745ac6 --- /dev/null +++ b/target/linux/generic/backport-6.1/702-v5.19-16-eth-mtk_eth_soc-silence-the-GCC-12-array-bounds-warn.patch @@ -0,0 +1,26 @@ +From: Jakub Kicinski +Date: Fri, 20 May 2022 12:56:03 -0700 +Subject: [PATCH] eth: mtk_eth_soc: silence the GCC 12 array-bounds warning + +GCC 12 gets upset because in mtk_foe_entry_commit_subflow() +this driver allocates a partial structure. The writes are +within bounds. + +Silence these warnings for now, our build bot runs GCC 12 +so we won't allow any new instances. + +Signed-off-by: Jakub Kicinski +Signed-off-by: David S. Miller +--- + +--- a/drivers/net/ethernet/mediatek/Makefile ++++ b/drivers/net/ethernet/mediatek/Makefile +@@ -11,3 +11,8 @@ mtk_eth-$(CONFIG_NET_MEDIATEK_SOC_WED) + + endif + obj-$(CONFIG_NET_MEDIATEK_SOC_WED) += mtk_wed_ops.o + obj-$(CONFIG_NET_MEDIATEK_STAR_EMAC) += mtk_star_emac.o ++ ++# FIXME: temporarily silence -Warray-bounds on non W=1+ builds ++ifndef KBUILD_EXTRA_WARN ++CFLAGS_mtk_ppe.o += -Wno-array-bounds ++endif diff --git a/target/linux/generic/backport-6.1/702-v5.19-17-net-ethernet-mtk_eth_soc-rely-on-GFP_KERNEL-for-dma_.patch b/target/linux/generic/backport-6.1/702-v5.19-17-net-ethernet-mtk_eth_soc-rely-on-GFP_KERNEL-for-dma_.patch new file mode 100644 index 0000000000..97677670cc --- /dev/null +++ b/target/linux/generic/backport-6.1/702-v5.19-17-net-ethernet-mtk_eth_soc-rely-on-GFP_KERNEL-for-dma_.patch @@ -0,0 +1,52 @@ +From: Lorenzo Bianconi +Date: Fri, 20 May 2022 20:11:26 +0200 +Subject: [PATCH] net: ethernet: mtk_eth_soc: rely on GFP_KERNEL for + dma_alloc_coherent whenever possible + +Rely on GFP_KERNEL for dma descriptors mappings in mtk_tx_alloc(), +mtk_rx_alloc() and mtk_init_fq_dma() since they are run in non-irq +context. + +Signed-off-by: Lorenzo Bianconi +Signed-off-by: David S. Miller +--- + +--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c ++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c +@@ -845,7 +845,7 @@ static int mtk_init_fq_dma(struct mtk_et + eth->scratch_ring = dma_alloc_coherent(eth->dma_dev, + cnt * sizeof(struct mtk_tx_dma), + ð->phy_scratch_ring, +- GFP_ATOMIC); ++ GFP_KERNEL); + if (unlikely(!eth->scratch_ring)) + return -ENOMEM; + +@@ -1623,7 +1623,7 @@ static int mtk_tx_alloc(struct mtk_eth * + goto no_tx_mem; + + ring->dma = dma_alloc_coherent(eth->dma_dev, MTK_DMA_SIZE * sz, +- &ring->phys, GFP_ATOMIC); ++ &ring->phys, GFP_KERNEL); + if (!ring->dma) + goto no_tx_mem; + +@@ -1641,8 +1641,7 @@ static int mtk_tx_alloc(struct mtk_eth * + */ + if (!MTK_HAS_CAPS(eth->soc->caps, MTK_QDMA)) { + ring->dma_pdma = dma_alloc_coherent(eth->dma_dev, MTK_DMA_SIZE * sz, +- &ring->phys_pdma, +- GFP_ATOMIC); ++ &ring->phys_pdma, GFP_KERNEL); + if (!ring->dma_pdma) + goto no_tx_mem; + +@@ -1757,7 +1756,7 @@ static int mtk_rx_alloc(struct mtk_eth * + + ring->dma = dma_alloc_coherent(eth->dma_dev, + rx_dma_size * sizeof(*ring->dma), +- &ring->phys, GFP_ATOMIC); ++ &ring->phys, GFP_KERNEL); + if (!ring->dma) + return -ENOMEM; + diff --git a/target/linux/generic/backport-6.1/702-v5.19-18-net-ethernet-mtk_eth_soc-move-tx-dma-desc-configurat.patch b/target/linux/generic/backport-6.1/702-v5.19-18-net-ethernet-mtk_eth_soc-move-tx-dma-desc-configurat.patch new file mode 100644 index 0000000000..95f122f730 --- /dev/null +++ b/target/linux/generic/backport-6.1/702-v5.19-18-net-ethernet-mtk_eth_soc-move-tx-dma-desc-configurat.patch @@ -0,0 +1,206 @@ +From: Lorenzo Bianconi +Date: Fri, 20 May 2022 20:11:27 +0200 +Subject: [PATCH] net: ethernet: mtk_eth_soc: move tx dma desc configuration in + mtk_tx_set_dma_desc + +Move tx dma descriptor configuration in mtk_tx_set_dma_desc routine. +This is a preliminary patch to introduce mt7986 ethernet support since +it relies on a different tx dma descriptor layout. + +Tested-by: Sam Shih +Signed-off-by: Lorenzo Bianconi +Signed-off-by: David S. Miller +--- + +--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c ++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c +@@ -971,18 +971,51 @@ static void setup_tx_buf(struct mtk_eth + } + } + ++static void mtk_tx_set_dma_desc(struct net_device *dev, struct mtk_tx_dma *desc, ++ struct mtk_tx_dma_desc_info *info) ++{ ++ struct mtk_mac *mac = netdev_priv(dev); ++ u32 data; ++ ++ WRITE_ONCE(desc->txd1, info->addr); ++ ++ data = TX_DMA_SWC | TX_DMA_PLEN0(info->size); ++ if (info->last) ++ data |= TX_DMA_LS0; ++ WRITE_ONCE(desc->txd3, data); ++ ++ data = (mac->id + 1) << TX_DMA_FPORT_SHIFT; /* forward port */ ++ if (info->first) { ++ if (info->gso) ++ data |= TX_DMA_TSO; ++ /* tx checksum offload */ ++ if (info->csum) ++ data |= TX_DMA_CHKSUM; ++ /* vlan header offload */ ++ if (info->vlan) ++ data |= TX_DMA_INS_VLAN | info->vlan_tci; ++ } ++ WRITE_ONCE(desc->txd4, data); ++} ++ + static int mtk_tx_map(struct sk_buff *skb, struct net_device *dev, + int tx_num, struct mtk_tx_ring *ring, bool gso) + { ++ struct mtk_tx_dma_desc_info txd_info = { ++ .size = skb_headlen(skb), ++ .gso = gso, ++ .csum = skb->ip_summed == CHECKSUM_PARTIAL, ++ .vlan = skb_vlan_tag_present(skb), ++ .vlan_tci = skb_vlan_tag_get(skb), ++ .first = true, ++ .last = !skb_is_nonlinear(skb), ++ }; + struct mtk_mac *mac = netdev_priv(dev); + struct mtk_eth *eth = mac->hw; + struct mtk_tx_dma *itxd, *txd; + struct mtk_tx_dma *itxd_pdma, *txd_pdma; + struct mtk_tx_buf *itx_buf, *tx_buf; +- dma_addr_t mapped_addr; +- unsigned int nr_frags; + int i, n_desc = 1; +- u32 txd4 = 0, fport; + int k = 0; + + itxd = ring->next_free; +@@ -990,49 +1023,32 @@ static int mtk_tx_map(struct sk_buff *sk + if (itxd == ring->last_free) + return -ENOMEM; + +- /* set the forward port */ +- fport = (mac->id + 1) << TX_DMA_FPORT_SHIFT; +- txd4 |= fport; +- + itx_buf = mtk_desc_to_tx_buf(ring, itxd); + memset(itx_buf, 0, sizeof(*itx_buf)); + +- if (gso) +- txd4 |= TX_DMA_TSO; +- +- /* TX Checksum offload */ +- if (skb->ip_summed == CHECKSUM_PARTIAL) +- txd4 |= TX_DMA_CHKSUM; +- +- /* VLAN header offload */ +- if (skb_vlan_tag_present(skb)) +- txd4 |= TX_DMA_INS_VLAN | skb_vlan_tag_get(skb); +- +- mapped_addr = dma_map_single(eth->dma_dev, skb->data, +- skb_headlen(skb), DMA_TO_DEVICE); +- if (unlikely(dma_mapping_error(eth->dma_dev, mapped_addr))) ++ txd_info.addr = dma_map_single(eth->dma_dev, skb->data, txd_info.size, ++ DMA_TO_DEVICE); ++ if (unlikely(dma_mapping_error(eth->dma_dev, txd_info.addr))) + return -ENOMEM; + +- WRITE_ONCE(itxd->txd1, mapped_addr); ++ mtk_tx_set_dma_desc(dev, itxd, &txd_info); ++ + itx_buf->flags |= MTK_TX_FLAGS_SINGLE0; + itx_buf->flags |= (!mac->id) ? MTK_TX_FLAGS_FPORT0 : + MTK_TX_FLAGS_FPORT1; +- setup_tx_buf(eth, itx_buf, itxd_pdma, mapped_addr, skb_headlen(skb), ++ setup_tx_buf(eth, itx_buf, itxd_pdma, txd_info.addr, txd_info.size, + k++); + + /* TX SG offload */ + txd = itxd; + txd_pdma = qdma_to_pdma(ring, txd); +- nr_frags = skb_shinfo(skb)->nr_frags; + +- for (i = 0; i < nr_frags; i++) { ++ for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) { + skb_frag_t *frag = &skb_shinfo(skb)->frags[i]; + unsigned int offset = 0; + int frag_size = skb_frag_size(frag); + + while (frag_size) { +- bool last_frag = false; +- unsigned int frag_map_size; + bool new_desc = true; + + if (MTK_HAS_CAPS(eth->soc->caps, MTK_QDMA) || +@@ -1047,23 +1063,17 @@ static int mtk_tx_map(struct sk_buff *sk + new_desc = false; + } + +- +- frag_map_size = min(frag_size, MTK_TX_DMA_BUF_LEN); +- mapped_addr = skb_frag_dma_map(eth->dma_dev, frag, offset, +- frag_map_size, +- DMA_TO_DEVICE); +- if (unlikely(dma_mapping_error(eth->dma_dev, mapped_addr))) ++ memset(&txd_info, 0, sizeof(struct mtk_tx_dma_desc_info)); ++ txd_info.size = min(frag_size, MTK_TX_DMA_BUF_LEN); ++ txd_info.last = i == skb_shinfo(skb)->nr_frags - 1 && ++ !(frag_size - txd_info.size); ++ txd_info.addr = skb_frag_dma_map(eth->dma_dev, frag, ++ offset, txd_info.size, ++ DMA_TO_DEVICE); ++ if (unlikely(dma_mapping_error(eth->dma_dev, txd_info.addr))) + goto err_dma; + +- if (i == nr_frags - 1 && +- (frag_size - frag_map_size) == 0) +- last_frag = true; +- +- WRITE_ONCE(txd->txd1, mapped_addr); +- WRITE_ONCE(txd->txd3, (TX_DMA_SWC | +- TX_DMA_PLEN0(frag_map_size) | +- last_frag * TX_DMA_LS0)); +- WRITE_ONCE(txd->txd4, fport); ++ mtk_tx_set_dma_desc(dev, txd, &txd_info); + + tx_buf = mtk_desc_to_tx_buf(ring, txd); + if (new_desc) +@@ -1073,20 +1083,17 @@ static int mtk_tx_map(struct sk_buff *sk + tx_buf->flags |= (!mac->id) ? MTK_TX_FLAGS_FPORT0 : + MTK_TX_FLAGS_FPORT1; + +- setup_tx_buf(eth, tx_buf, txd_pdma, mapped_addr, +- frag_map_size, k++); ++ setup_tx_buf(eth, tx_buf, txd_pdma, txd_info.addr, ++ txd_info.size, k++); + +- frag_size -= frag_map_size; +- offset += frag_map_size; ++ frag_size -= txd_info.size; ++ offset += txd_info.size; + } + } + + /* store skb to cleanup */ + itx_buf->skb = skb; + +- WRITE_ONCE(itxd->txd4, txd4); +- WRITE_ONCE(itxd->txd3, (TX_DMA_SWC | TX_DMA_PLEN0(skb_headlen(skb)) | +- (!nr_frags * TX_DMA_LS0))); + if (!MTK_HAS_CAPS(eth->soc->caps, MTK_QDMA)) { + if (k & 0x1) + txd_pdma->txd2 |= TX_DMA_LS0; +--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h ++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h +@@ -842,6 +842,17 @@ enum mkt_eth_capabilities { + MTK_MUX_U3_GMAC2_TO_QPHY | \ + MTK_MUX_GMAC12_TO_GEPHY_SGMII | MTK_QDMA) + ++struct mtk_tx_dma_desc_info { ++ dma_addr_t addr; ++ u32 size; ++ u16 vlan_tci; ++ u8 gso:1; ++ u8 csum:1; ++ u8 vlan:1; ++ u8 first:1; ++ u8 last:1; ++}; ++ + /* struct mtk_eth_data - This is the structure holding all differences + * among various plaforms + * @ana_rgc3: The offset for register ANA_RGC3 related to diff --git a/target/linux/generic/backport-6.1/702-v5.19-19-net-ethernet-mtk_eth_soc-add-txd_size-to-mtk_soc_dat.patch b/target/linux/generic/backport-6.1/702-v5.19-19-net-ethernet-mtk_eth_soc-add-txd_size-to-mtk_soc_dat.patch new file mode 100644 index 0000000000..7aeeda0ba0 --- /dev/null +++ b/target/linux/generic/backport-6.1/702-v5.19-19-net-ethernet-mtk_eth_soc-add-txd_size-to-mtk_soc_dat.patch @@ -0,0 +1,167 @@ +From: Lorenzo Bianconi +Date: Fri, 20 May 2022 20:11:28 +0200 +Subject: [PATCH] net: ethernet: mtk_eth_soc: add txd_size to mtk_soc_data + +In order to remove mtk_tx_dma size dependency, introduce txd_size in +mtk_soc_data data structure. Rely on txd_size in mtk_init_fq_dma() and +mtk_dma_free() routines. +This is a preliminary patch to add mt7986 ethernet support. + +Tested-by: Sam Shih +Signed-off-by: Lorenzo Bianconi +Signed-off-by: David S. Miller +--- + +--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c ++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c +@@ -837,20 +837,20 @@ static void *mtk_max_lro_buf_alloc(gfp_t + /* the qdma core needs scratch memory to be setup */ + static int mtk_init_fq_dma(struct mtk_eth *eth) + { ++ const struct mtk_soc_data *soc = eth->soc; + dma_addr_t phy_ring_tail; + int cnt = MTK_DMA_SIZE; + dma_addr_t dma_addr; + int i; + + eth->scratch_ring = dma_alloc_coherent(eth->dma_dev, +- cnt * sizeof(struct mtk_tx_dma), ++ cnt * soc->txrx.txd_size, + ð->phy_scratch_ring, + GFP_KERNEL); + if (unlikely(!eth->scratch_ring)) + return -ENOMEM; + +- eth->scratch_head = kcalloc(cnt, MTK_QDMA_PAGE_SIZE, +- GFP_KERNEL); ++ eth->scratch_head = kcalloc(cnt, MTK_QDMA_PAGE_SIZE, GFP_KERNEL); + if (unlikely(!eth->scratch_head)) + return -ENOMEM; + +@@ -860,16 +860,19 @@ static int mtk_init_fq_dma(struct mtk_et + if (unlikely(dma_mapping_error(eth->dma_dev, dma_addr))) + return -ENOMEM; + +- phy_ring_tail = eth->phy_scratch_ring + +- (sizeof(struct mtk_tx_dma) * (cnt - 1)); ++ phy_ring_tail = eth->phy_scratch_ring + soc->txrx.txd_size * (cnt - 1); + + for (i = 0; i < cnt; i++) { +- eth->scratch_ring[i].txd1 = +- (dma_addr + (i * MTK_QDMA_PAGE_SIZE)); ++ struct mtk_tx_dma *txd; ++ ++ txd = (void *)eth->scratch_ring + i * soc->txrx.txd_size; ++ txd->txd1 = dma_addr + i * MTK_QDMA_PAGE_SIZE; + if (i < cnt - 1) +- eth->scratch_ring[i].txd2 = (eth->phy_scratch_ring + +- ((i + 1) * sizeof(struct mtk_tx_dma))); +- eth->scratch_ring[i].txd3 = TX_DMA_SDL(MTK_QDMA_PAGE_SIZE); ++ txd->txd2 = eth->phy_scratch_ring + ++ (i + 1) * soc->txrx.txd_size; ++ ++ txd->txd3 = TX_DMA_PLEN0(MTK_QDMA_PAGE_SIZE); ++ txd->txd4 = 0; + } + + mtk_w32(eth, eth->phy_scratch_ring, MTK_QDMA_FQ_HEAD); +@@ -2169,6 +2172,7 @@ static int mtk_dma_init(struct mtk_eth * + + static void mtk_dma_free(struct mtk_eth *eth) + { ++ const struct mtk_soc_data *soc = eth->soc; + int i; + + for (i = 0; i < MTK_MAC_COUNT; i++) +@@ -2176,9 +2180,8 @@ static void mtk_dma_free(struct mtk_eth + netdev_reset_queue(eth->netdev[i]); + if (eth->scratch_ring) { + dma_free_coherent(eth->dma_dev, +- MTK_DMA_SIZE * sizeof(struct mtk_tx_dma), +- eth->scratch_ring, +- eth->phy_scratch_ring); ++ MTK_DMA_SIZE * soc->txrx.txd_size, ++ eth->scratch_ring, eth->phy_scratch_ring); + eth->scratch_ring = NULL; + eth->phy_scratch_ring = 0; + } +@@ -3388,6 +3391,9 @@ static const struct mtk_soc_data mt2701_ + .hw_features = MTK_HW_FEATURES, + .required_clks = MT7623_CLKS_BITMAP, + .required_pctl = true, ++ .txrx = { ++ .txd_size = sizeof(struct mtk_tx_dma), ++ }, + }; + + static const struct mtk_soc_data mt7621_data = { +@@ -3396,6 +3402,9 @@ static const struct mtk_soc_data mt7621_ + .required_clks = MT7621_CLKS_BITMAP, + .required_pctl = false, + .offload_version = 2, ++ .txrx = { ++ .txd_size = sizeof(struct mtk_tx_dma), ++ }, + }; + + static const struct mtk_soc_data mt7622_data = { +@@ -3405,6 +3414,9 @@ static const struct mtk_soc_data mt7622_ + .required_clks = MT7622_CLKS_BITMAP, + .required_pctl = false, + .offload_version = 2, ++ .txrx = { ++ .txd_size = sizeof(struct mtk_tx_dma), ++ }, + }; + + static const struct mtk_soc_data mt7623_data = { +@@ -3413,6 +3425,9 @@ static const struct mtk_soc_data mt7623_ + .required_clks = MT7623_CLKS_BITMAP, + .required_pctl = true, + .offload_version = 2, ++ .txrx = { ++ .txd_size = sizeof(struct mtk_tx_dma), ++ }, + }; + + static const struct mtk_soc_data mt7629_data = { +@@ -3421,6 +3436,9 @@ static const struct mtk_soc_data mt7629_ + .hw_features = MTK_HW_FEATURES, + .required_clks = MT7629_CLKS_BITMAP, + .required_pctl = false, ++ .txrx = { ++ .txd_size = sizeof(struct mtk_tx_dma), ++ }, + }; + + static const struct mtk_soc_data rt5350_data = { +@@ -3428,6 +3446,9 @@ static const struct mtk_soc_data rt5350_ + .hw_features = MTK_HW_FEATURES_MT7628, + .required_clks = MT7628_CLKS_BITMAP, + .required_pctl = false, ++ .txrx = { ++ .txd_size = sizeof(struct mtk_tx_dma), ++ }, + }; + + const struct of_device_id of_mtk_match[] = { +--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h ++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h +@@ -863,6 +863,7 @@ struct mtk_tx_dma_desc_info { + * the target SoC + * @required_pctl A bool value to show whether the SoC requires + * the extra setup for those pins used by GMAC. ++ * @txd_size Tx DMA descriptor size. + */ + struct mtk_soc_data { + u32 ana_rgc3; +@@ -871,6 +872,9 @@ struct mtk_soc_data { + bool required_pctl; + u8 offload_version; + netdev_features_t hw_features; ++ struct { ++ u32 txd_size; ++ } txrx; + }; + + /* currently no SoC has more than 2 macs */ diff --git a/target/linux/generic/backport-6.1/702-v5.19-20-net-ethernet-mtk_eth_soc-rely-on-txd_size-in-mtk_tx_.patch b/target/linux/generic/backport-6.1/702-v5.19-20-net-ethernet-mtk_eth_soc-rely-on-txd_size-in-mtk_tx_.patch new file mode 100644 index 0000000000..01dbca0753 --- /dev/null +++ b/target/linux/generic/backport-6.1/702-v5.19-20-net-ethernet-mtk_eth_soc-rely-on-txd_size-in-mtk_tx_.patch @@ -0,0 +1,78 @@ +From: Lorenzo Bianconi +Date: Fri, 20 May 2022 20:11:29 +0200 +Subject: [PATCH] net: ethernet: mtk_eth_soc: rely on txd_size in + mtk_tx_alloc/mtk_tx_clean + +This is a preliminary patch to add mt7986 ethernet support. + +Tested-by: Sam Shih +Signed-off-by: Lorenzo Bianconi +Signed-off-by: David S. Miller +--- + +--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c ++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c +@@ -1624,8 +1624,10 @@ static int mtk_napi_rx(struct napi_struc + + static int mtk_tx_alloc(struct mtk_eth *eth) + { ++ const struct mtk_soc_data *soc = eth->soc; + struct mtk_tx_ring *ring = ð->tx_ring; +- int i, sz = sizeof(*ring->dma); ++ int i, sz = soc->txrx.txd_size; ++ struct mtk_tx_dma *txd; + + ring->buf = kcalloc(MTK_DMA_SIZE, sizeof(*ring->buf), + GFP_KERNEL); +@@ -1641,8 +1643,10 @@ static int mtk_tx_alloc(struct mtk_eth * + int next = (i + 1) % MTK_DMA_SIZE; + u32 next_ptr = ring->phys + next * sz; + +- ring->dma[i].txd2 = next_ptr; +- ring->dma[i].txd3 = TX_DMA_LS0 | TX_DMA_OWNER_CPU; ++ txd = (void *)ring->dma + i * sz; ++ txd->txd2 = next_ptr; ++ txd->txd3 = TX_DMA_LS0 | TX_DMA_OWNER_CPU; ++ txd->txd4 = 0; + } + + /* On MT7688 (PDMA only) this driver uses the ring->dma structs +@@ -1664,7 +1668,7 @@ static int mtk_tx_alloc(struct mtk_eth * + ring->dma_size = MTK_DMA_SIZE; + atomic_set(&ring->free_count, MTK_DMA_SIZE - 2); + ring->next_free = &ring->dma[0]; +- ring->last_free = &ring->dma[MTK_DMA_SIZE - 1]; ++ ring->last_free = (void *)txd; + ring->last_free_ptr = (u32)(ring->phys + ((MTK_DMA_SIZE - 1) * sz)); + ring->thresh = MAX_SKB_FRAGS; + +@@ -1697,6 +1701,7 @@ no_tx_mem: + + static void mtk_tx_clean(struct mtk_eth *eth) + { ++ const struct mtk_soc_data *soc = eth->soc; + struct mtk_tx_ring *ring = ð->tx_ring; + int i; + +@@ -1709,17 +1714,15 @@ static void mtk_tx_clean(struct mtk_eth + + if (ring->dma) { + dma_free_coherent(eth->dma_dev, +- MTK_DMA_SIZE * sizeof(*ring->dma), +- ring->dma, +- ring->phys); ++ MTK_DMA_SIZE * soc->txrx.txd_size, ++ ring->dma, ring->phys); + ring->dma = NULL; + } + + if (ring->dma_pdma) { + dma_free_coherent(eth->dma_dev, +- MTK_DMA_SIZE * sizeof(*ring->dma_pdma), +- ring->dma_pdma, +- ring->phys_pdma); ++ MTK_DMA_SIZE * soc->txrx.txd_size, ++ ring->dma_pdma, ring->phys_pdma); + ring->dma_pdma = NULL; + } + } diff --git a/target/linux/generic/backport-6.1/702-v5.19-21-net-ethernet-mtk_eth_soc-rely-on-txd_size-in-mtk_des.patch b/target/linux/generic/backport-6.1/702-v5.19-21-net-ethernet-mtk_eth_soc-rely-on-txd_size-in-mtk_des.patch new file mode 100644 index 0000000000..1d23a178b4 --- /dev/null +++ b/target/linux/generic/backport-6.1/702-v5.19-21-net-ethernet-mtk_eth_soc-rely-on-txd_size-in-mtk_des.patch @@ -0,0 +1,109 @@ +From: Lorenzo Bianconi +Date: Fri, 20 May 2022 20:11:30 +0200 +Subject: [PATCH] net: ethernet: mtk_eth_soc: rely on txd_size in + mtk_desc_to_tx_buf + +This is a preliminary patch to add mt7986 ethernet support. + +Tested-by: Sam Shih +Signed-off-by: Lorenzo Bianconi +Signed-off-by: David S. Miller +--- + +--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c ++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c +@@ -890,10 +890,11 @@ static inline void *mtk_qdma_phys_to_vir + return ret + (desc - ring->phys); + } + +-static inline struct mtk_tx_buf *mtk_desc_to_tx_buf(struct mtk_tx_ring *ring, +- struct mtk_tx_dma *txd) ++static struct mtk_tx_buf *mtk_desc_to_tx_buf(struct mtk_tx_ring *ring, ++ struct mtk_tx_dma *txd, ++ u32 txd_size) + { +- int idx = txd - ring->dma; ++ int idx = ((void *)txd - (void *)ring->dma) / txd_size; + + return &ring->buf[idx]; + } +@@ -1015,6 +1016,7 @@ static int mtk_tx_map(struct sk_buff *sk + }; + struct mtk_mac *mac = netdev_priv(dev); + struct mtk_eth *eth = mac->hw; ++ const struct mtk_soc_data *soc = eth->soc; + struct mtk_tx_dma *itxd, *txd; + struct mtk_tx_dma *itxd_pdma, *txd_pdma; + struct mtk_tx_buf *itx_buf, *tx_buf; +@@ -1026,7 +1028,7 @@ static int mtk_tx_map(struct sk_buff *sk + if (itxd == ring->last_free) + return -ENOMEM; + +- itx_buf = mtk_desc_to_tx_buf(ring, itxd); ++ itx_buf = mtk_desc_to_tx_buf(ring, itxd, soc->txrx.txd_size); + memset(itx_buf, 0, sizeof(*itx_buf)); + + txd_info.addr = dma_map_single(eth->dma_dev, skb->data, txd_info.size, +@@ -1054,7 +1056,7 @@ static int mtk_tx_map(struct sk_buff *sk + while (frag_size) { + bool new_desc = true; + +- if (MTK_HAS_CAPS(eth->soc->caps, MTK_QDMA) || ++ if (MTK_HAS_CAPS(soc->caps, MTK_QDMA) || + (i & 0x1)) { + txd = mtk_qdma_phys_to_virt(ring, txd->txd2); + txd_pdma = qdma_to_pdma(ring, txd); +@@ -1078,7 +1080,8 @@ static int mtk_tx_map(struct sk_buff *sk + + mtk_tx_set_dma_desc(dev, txd, &txd_info); + +- tx_buf = mtk_desc_to_tx_buf(ring, txd); ++ tx_buf = mtk_desc_to_tx_buf(ring, txd, ++ soc->txrx.txd_size); + if (new_desc) + memset(tx_buf, 0, sizeof(*tx_buf)); + tx_buf->skb = (struct sk_buff *)MTK_DMA_DUMMY_DESC; +@@ -1097,7 +1100,7 @@ static int mtk_tx_map(struct sk_buff *sk + /* store skb to cleanup */ + itx_buf->skb = skb; + +- if (!MTK_HAS_CAPS(eth->soc->caps, MTK_QDMA)) { ++ if (!MTK_HAS_CAPS(soc->caps, MTK_QDMA)) { + if (k & 0x1) + txd_pdma->txd2 |= TX_DMA_LS0; + else +@@ -1115,7 +1118,7 @@ static int mtk_tx_map(struct sk_buff *sk + */ + wmb(); + +- if (MTK_HAS_CAPS(eth->soc->caps, MTK_QDMA)) { ++ if (MTK_HAS_CAPS(soc->caps, MTK_QDMA)) { + if (netif_xmit_stopped(netdev_get_tx_queue(dev, 0)) || + !netdev_xmit_more()) + mtk_w32(eth, txd->txd2, MTK_QTX_CTX_PTR); +@@ -1129,13 +1132,13 @@ static int mtk_tx_map(struct sk_buff *sk + + err_dma: + do { +- tx_buf = mtk_desc_to_tx_buf(ring, itxd); ++ tx_buf = mtk_desc_to_tx_buf(ring, itxd, soc->txrx.txd_size); + + /* unmap dma */ + mtk_tx_unmap(eth, tx_buf, false); + + itxd->txd3 = TX_DMA_LS0 | TX_DMA_OWNER_CPU; +- if (!MTK_HAS_CAPS(eth->soc->caps, MTK_QDMA)) ++ if (!MTK_HAS_CAPS(soc->caps, MTK_QDMA)) + itxd_pdma->txd2 = TX_DMA_DESP2_DEF; + + itxd = mtk_qdma_phys_to_virt(ring, itxd->txd2); +@@ -1449,7 +1452,8 @@ static int mtk_poll_tx_qdma(struct mtk_e + if ((desc->txd3 & TX_DMA_OWNER_CPU) == 0) + break; + +- tx_buf = mtk_desc_to_tx_buf(ring, desc); ++ tx_buf = mtk_desc_to_tx_buf(ring, desc, ++ eth->soc->txrx.txd_size); + if (tx_buf->flags & MTK_TX_FLAGS_FPORT1) + mac = 1; + diff --git a/target/linux/generic/backport-6.1/702-v5.19-22-net-ethernet-mtk_eth_soc-rely-on-txd_size-in-txd_to_.patch b/target/linux/generic/backport-6.1/702-v5.19-22-net-ethernet-mtk_eth_soc-rely-on-txd_size-in-txd_to_.patch new file mode 100644 index 0000000000..2989d190d8 --- /dev/null +++ b/target/linux/generic/backport-6.1/702-v5.19-22-net-ethernet-mtk_eth_soc-rely-on-txd_size-in-txd_to_.patch @@ -0,0 +1,39 @@ +From: Lorenzo Bianconi +Date: Fri, 20 May 2022 20:11:31 +0200 +Subject: [PATCH] net: ethernet: mtk_eth_soc: rely on txd_size in txd_to_idx + +This is a preliminary patch to add mt7986 ethernet support. + +Tested-by: Sam Shih +Signed-off-by: Lorenzo Bianconi +Signed-off-by: David S. Miller +--- + +--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c ++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c +@@ -905,9 +905,10 @@ static struct mtk_tx_dma *qdma_to_pdma(s + return ring->dma_pdma - ring->dma + dma; + } + +-static int txd_to_idx(struct mtk_tx_ring *ring, struct mtk_tx_dma *dma) ++static int txd_to_idx(struct mtk_tx_ring *ring, struct mtk_tx_dma *dma, ++ u32 txd_size) + { +- return ((void *)dma - (void *)ring->dma) / sizeof(*dma); ++ return ((void *)dma - (void *)ring->dma) / txd_size; + } + + static void mtk_tx_unmap(struct mtk_eth *eth, struct mtk_tx_buf *tx_buf, +@@ -1123,8 +1124,10 @@ static int mtk_tx_map(struct sk_buff *sk + !netdev_xmit_more()) + mtk_w32(eth, txd->txd2, MTK_QTX_CTX_PTR); + } else { +- int next_idx = NEXT_DESP_IDX(txd_to_idx(ring, txd), +- ring->dma_size); ++ int next_idx; ++ ++ next_idx = NEXT_DESP_IDX(txd_to_idx(ring, txd, soc->txrx.txd_size), ++ ring->dma_size); + mtk_w32(eth, next_idx, MT7628_TX_CTX_IDX0); + } + diff --git a/target/linux/generic/backport-6.1/702-v5.19-23-net-ethernet-mtk_eth_soc-add-rxd_size-to-mtk_soc_dat.patch b/target/linux/generic/backport-6.1/702-v5.19-23-net-ethernet-mtk_eth_soc-add-rxd_size-to-mtk_soc_dat.patch new file mode 100644 index 0000000000..be258da75a --- /dev/null +++ b/target/linux/generic/backport-6.1/702-v5.19-23-net-ethernet-mtk_eth_soc-add-rxd_size-to-mtk_soc_dat.patch @@ -0,0 +1,102 @@ +From: Lorenzo Bianconi +Date: Fri, 20 May 2022 20:11:32 +0200 +Subject: [PATCH] net: ethernet: mtk_eth_soc: add rxd_size to mtk_soc_data + +Similar to tx counterpart, introduce rxd_size in mtk_soc_data data +structure. +This is a preliminary patch to add mt7986 ethernet support. + +Tested-by: Sam Shih +Signed-off-by: Lorenzo Bianconi +Signed-off-by: David S. Miller +--- + +--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c ++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c +@@ -1775,7 +1775,7 @@ static int mtk_rx_alloc(struct mtk_eth * + } + + ring->dma = dma_alloc_coherent(eth->dma_dev, +- rx_dma_size * sizeof(*ring->dma), ++ rx_dma_size * eth->soc->txrx.rxd_size, + &ring->phys, GFP_KERNEL); + if (!ring->dma) + return -ENOMEM; +@@ -1833,9 +1833,8 @@ static void mtk_rx_clean(struct mtk_eth + + if (ring->dma) { + dma_free_coherent(eth->dma_dev, +- ring->dma_size * sizeof(*ring->dma), +- ring->dma, +- ring->phys); ++ ring->dma_size * eth->soc->txrx.rxd_size, ++ ring->dma, ring->phys); + ring->dma = NULL; + } + } +@@ -3403,6 +3402,7 @@ static const struct mtk_soc_data mt2701_ + .required_pctl = true, + .txrx = { + .txd_size = sizeof(struct mtk_tx_dma), ++ .rxd_size = sizeof(struct mtk_rx_dma), + }, + }; + +@@ -3414,6 +3414,7 @@ static const struct mtk_soc_data mt7621_ + .offload_version = 2, + .txrx = { + .txd_size = sizeof(struct mtk_tx_dma), ++ .rxd_size = sizeof(struct mtk_rx_dma), + }, + }; + +@@ -3426,6 +3427,7 @@ static const struct mtk_soc_data mt7622_ + .offload_version = 2, + .txrx = { + .txd_size = sizeof(struct mtk_tx_dma), ++ .rxd_size = sizeof(struct mtk_rx_dma), + }, + }; + +@@ -3437,6 +3439,7 @@ static const struct mtk_soc_data mt7623_ + .offload_version = 2, + .txrx = { + .txd_size = sizeof(struct mtk_tx_dma), ++ .rxd_size = sizeof(struct mtk_rx_dma), + }, + }; + +@@ -3448,6 +3451,7 @@ static const struct mtk_soc_data mt7629_ + .required_pctl = false, + .txrx = { + .txd_size = sizeof(struct mtk_tx_dma), ++ .rxd_size = sizeof(struct mtk_rx_dma), + }, + }; + +@@ -3458,6 +3462,7 @@ static const struct mtk_soc_data rt5350_ + .required_pctl = false, + .txrx = { + .txd_size = sizeof(struct mtk_tx_dma), ++ .rxd_size = sizeof(struct mtk_rx_dma), + }, + }; + +--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h ++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h +@@ -864,6 +864,7 @@ struct mtk_tx_dma_desc_info { + * @required_pctl A bool value to show whether the SoC requires + * the extra setup for those pins used by GMAC. + * @txd_size Tx DMA descriptor size. ++ * @rxd_size Rx DMA descriptor size. + */ + struct mtk_soc_data { + u32 ana_rgc3; +@@ -874,6 +875,7 @@ struct mtk_soc_data { + netdev_features_t hw_features; + struct { + u32 txd_size; ++ u32 rxd_size; + } txrx; + }; + diff --git a/target/linux/generic/backport-6.1/702-v5.19-24-net-ethernet-mtk_eth_soc-rely-on-txd_size-field-in-m.patch b/target/linux/generic/backport-6.1/702-v5.19-24-net-ethernet-mtk_eth_soc-rely-on-txd_size-field-in-m.patch new file mode 100644 index 0000000000..53af586b6c --- /dev/null +++ b/target/linux/generic/backport-6.1/702-v5.19-24-net-ethernet-mtk_eth_soc-rely-on-txd_size-field-in-m.patch @@ -0,0 +1,46 @@ +From: Lorenzo Bianconi +Date: Fri, 20 May 2022 20:11:33 +0200 +Subject: [PATCH] net: ethernet: mtk_eth_soc: rely on txd_size field in + mtk_poll_tx/mtk_poll_rx + +This is a preliminary to ad mt7986 ethernet support. + +Tested-by: Sam Shih +Signed-off-by: Lorenzo Bianconi +Signed-off-by: David S. Miller +--- + +--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c ++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c +@@ -1264,9 +1264,12 @@ static struct mtk_rx_ring *mtk_get_rx_ri + return ð->rx_ring[0]; + + for (i = 0; i < MTK_MAX_RX_RING_NUM; i++) { ++ struct mtk_rx_dma *rxd; ++ + ring = ð->rx_ring[i]; + idx = NEXT_DESP_IDX(ring->calc_idx, ring->dma_size); +- if (ring->dma[idx].rxd2 & RX_DMA_DONE) { ++ rxd = (void *)ring->dma + idx * eth->soc->txrx.rxd_size; ++ if (rxd->rxd2 & RX_DMA_DONE) { + ring->calc_idx_update = true; + return ring; + } +@@ -1317,7 +1320,7 @@ static int mtk_poll_rx(struct napi_struc + goto rx_done; + + idx = NEXT_DESP_IDX(ring->calc_idx, ring->dma_size); +- rxd = &ring->dma[idx]; ++ rxd = (void *)ring->dma + idx * eth->soc->txrx.rxd_size; + data = ring->data[idx]; + + if (!mtk_rx_get_desc(&trxd, rxd)) +@@ -1509,7 +1512,7 @@ static int mtk_poll_tx_pdma(struct mtk_e + + mtk_tx_unmap(eth, tx_buf, true); + +- desc = &ring->dma[cpu]; ++ desc = (void *)ring->dma + cpu * eth->soc->txrx.txd_size; + ring->last_free = desc; + atomic_inc(&ring->free_count); + diff --git a/target/linux/generic/backport-6.1/702-v5.19-25-net-ethernet-mtk_eth_soc-rely-on-rxd_size-field-in-m.patch b/target/linux/generic/backport-6.1/702-v5.19-25-net-ethernet-mtk_eth_soc-rely-on-rxd_size-field-in-m.patch new file mode 100644 index 0000000000..1f4fa1dfb5 --- /dev/null +++ b/target/linux/generic/backport-6.1/702-v5.19-25-net-ethernet-mtk_eth_soc-rely-on-rxd_size-field-in-m.patch @@ -0,0 +1,68 @@ +From: Lorenzo Bianconi +Date: Fri, 20 May 2022 20:11:34 +0200 +Subject: [PATCH] net: ethernet: mtk_eth_soc: rely on rxd_size field in + mtk_rx_alloc/mtk_rx_clean + +Remove mtk_rx_dma structure layout dependency in mtk_rx_alloc/mtk_rx_clean. +Initialize to 0 rxd3 and rxd4 in mtk_rx_alloc. +This is a preliminary patch to add mt7986 ethernet support. + +Tested-by: Sam Shih +Signed-off-by: Lorenzo Bianconi +Signed-off-by: David S. Miller +--- + +--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c ++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c +@@ -1784,18 +1784,25 @@ static int mtk_rx_alloc(struct mtk_eth * + return -ENOMEM; + + for (i = 0; i < rx_dma_size; i++) { ++ struct mtk_rx_dma *rxd; ++ + dma_addr_t dma_addr = dma_map_single(eth->dma_dev, + ring->data[i] + NET_SKB_PAD + eth->ip_align, + ring->buf_size, + DMA_FROM_DEVICE); + if (unlikely(dma_mapping_error(eth->dma_dev, dma_addr))) + return -ENOMEM; +- ring->dma[i].rxd1 = (unsigned int)dma_addr; ++ ++ rxd = (void *)ring->dma + i * eth->soc->txrx.rxd_size; ++ rxd->rxd1 = (unsigned int)dma_addr; + + if (MTK_HAS_CAPS(eth->soc->caps, MTK_SOC_MT7628)) +- ring->dma[i].rxd2 = RX_DMA_LSO; ++ rxd->rxd2 = RX_DMA_LSO; + else +- ring->dma[i].rxd2 = RX_DMA_PLEN0(ring->buf_size); ++ rxd->rxd2 = RX_DMA_PLEN0(ring->buf_size); ++ ++ rxd->rxd3 = 0; ++ rxd->rxd4 = 0; + } + ring->dma_size = rx_dma_size; + ring->calc_idx_update = false; +@@ -1820,14 +1827,17 @@ static void mtk_rx_clean(struct mtk_eth + + if (ring->data && ring->dma) { + for (i = 0; i < ring->dma_size; i++) { ++ struct mtk_rx_dma *rxd; ++ + if (!ring->data[i]) + continue; +- if (!ring->dma[i].rxd1) ++ ++ rxd = (void *)ring->dma + i * eth->soc->txrx.rxd_size; ++ if (!rxd->rxd1) + continue; +- dma_unmap_single(eth->dma_dev, +- ring->dma[i].rxd1, +- ring->buf_size, +- DMA_FROM_DEVICE); ++ ++ dma_unmap_single(eth->dma_dev, rxd->rxd1, ++ ring->buf_size, DMA_FROM_DEVICE); + skb_free_frag(ring->data[i]); + } + kfree(ring->data); diff --git a/target/linux/generic/backport-6.1/702-v5.19-26-net-ethernet-mtk_eth_soc-introduce-device-register-m.patch b/target/linux/generic/backport-6.1/702-v5.19-26-net-ethernet-mtk_eth_soc-introduce-device-register-m.patch new file mode 100644 index 0000000000..99f482c418 --- /dev/null +++ b/target/linux/generic/backport-6.1/702-v5.19-26-net-ethernet-mtk_eth_soc-introduce-device-register-m.patch @@ -0,0 +1,814 @@ +From: Lorenzo Bianconi +Date: Fri, 20 May 2022 20:11:35 +0200 +Subject: [PATCH] net: ethernet: mtk_eth_soc: introduce device register map + +Introduce reg_map structure to add the capability to support different +register definitions. Move register definitions in mtk_regmap structure. +This is a preliminary patch to introduce mt7986 ethernet support. + +Tested-by: Sam Shih +Signed-off-by: Lorenzo Bianconi +Signed-off-by: David S. Miller +--- + +--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c ++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c +@@ -34,6 +34,59 @@ MODULE_PARM_DESC(msg_level, "Message lev + #define MTK_ETHTOOL_STAT(x) { #x, \ + offsetof(struct mtk_hw_stats, x) / sizeof(u64) } + ++static const struct mtk_reg_map mtk_reg_map = { ++ .tx_irq_mask = 0x1a1c, ++ .tx_irq_status = 0x1a18, ++ .pdma = { ++ .rx_ptr = 0x0900, ++ .rx_cnt_cfg = 0x0904, ++ .pcrx_ptr = 0x0908, ++ .glo_cfg = 0x0a04, ++ .rst_idx = 0x0a08, ++ .delay_irq = 0x0a0c, ++ .irq_status = 0x0a20, ++ .irq_mask = 0x0a28, ++ .int_grp = 0x0a50, ++ }, ++ .qdma = { ++ .qtx_cfg = 0x1800, ++ .rx_ptr = 0x1900, ++ .rx_cnt_cfg = 0x1904, ++ .qcrx_ptr = 0x1908, ++ .glo_cfg = 0x1a04, ++ .rst_idx = 0x1a08, ++ .delay_irq = 0x1a0c, ++ .fc_th = 0x1a10, ++ .int_grp = 0x1a20, ++ .hred = 0x1a44, ++ .ctx_ptr = 0x1b00, ++ .dtx_ptr = 0x1b04, ++ .crx_ptr = 0x1b10, ++ .drx_ptr = 0x1b14, ++ .fq_head = 0x1b20, ++ .fq_tail = 0x1b24, ++ .fq_count = 0x1b28, ++ .fq_blen = 0x1b2c, ++ }, ++ .gdm1_cnt = 0x2400, ++}; ++ ++static const struct mtk_reg_map mt7628_reg_map = { ++ .tx_irq_mask = 0x0a28, ++ .tx_irq_status = 0x0a20, ++ .pdma = { ++ .rx_ptr = 0x0900, ++ .rx_cnt_cfg = 0x0904, ++ .pcrx_ptr = 0x0908, ++ .glo_cfg = 0x0a04, ++ .rst_idx = 0x0a08, ++ .delay_irq = 0x0a0c, ++ .irq_status = 0x0a20, ++ .irq_mask = 0x0a28, ++ .int_grp = 0x0a50, ++ }, ++}; ++ + /* strings used by ethtool */ + static const struct mtk_ethtool_stats { + char str[ETH_GSTRING_LEN]; +@@ -618,8 +671,8 @@ static inline void mtk_tx_irq_disable(st + u32 val; + + spin_lock_irqsave(ð->tx_irq_lock, flags); +- val = mtk_r32(eth, eth->tx_int_mask_reg); +- mtk_w32(eth, val & ~mask, eth->tx_int_mask_reg); ++ val = mtk_r32(eth, eth->soc->reg_map->tx_irq_mask); ++ mtk_w32(eth, val & ~mask, eth->soc->reg_map->tx_irq_mask); + spin_unlock_irqrestore(ð->tx_irq_lock, flags); + } + +@@ -629,8 +682,8 @@ static inline void mtk_tx_irq_enable(str + u32 val; + + spin_lock_irqsave(ð->tx_irq_lock, flags); +- val = mtk_r32(eth, eth->tx_int_mask_reg); +- mtk_w32(eth, val | mask, eth->tx_int_mask_reg); ++ val = mtk_r32(eth, eth->soc->reg_map->tx_irq_mask); ++ mtk_w32(eth, val | mask, eth->soc->reg_map->tx_irq_mask); + spin_unlock_irqrestore(ð->tx_irq_lock, flags); + } + +@@ -640,8 +693,8 @@ static inline void mtk_rx_irq_disable(st + u32 val; + + spin_lock_irqsave(ð->rx_irq_lock, flags); +- val = mtk_r32(eth, MTK_PDMA_INT_MASK); +- mtk_w32(eth, val & ~mask, MTK_PDMA_INT_MASK); ++ val = mtk_r32(eth, eth->soc->reg_map->pdma.irq_mask); ++ mtk_w32(eth, val & ~mask, eth->soc->reg_map->pdma.irq_mask); + spin_unlock_irqrestore(ð->rx_irq_lock, flags); + } + +@@ -651,8 +704,8 @@ static inline void mtk_rx_irq_enable(str + u32 val; + + spin_lock_irqsave(ð->rx_irq_lock, flags); +- val = mtk_r32(eth, MTK_PDMA_INT_MASK); +- mtk_w32(eth, val | mask, MTK_PDMA_INT_MASK); ++ val = mtk_r32(eth, eth->soc->reg_map->pdma.irq_mask); ++ mtk_w32(eth, val | mask, eth->soc->reg_map->pdma.irq_mask); + spin_unlock_irqrestore(ð->rx_irq_lock, flags); + } + +@@ -703,39 +756,39 @@ void mtk_stats_update_mac(struct mtk_mac + hw_stats->rx_checksum_errors += + mtk_r32(mac->hw, MT7628_SDM_CS_ERR); + } else { ++ const struct mtk_reg_map *reg_map = eth->soc->reg_map; + unsigned int offs = hw_stats->reg_offset; + u64 stats; + +- hw_stats->rx_bytes += mtk_r32(mac->hw, +- MTK_GDM1_RX_GBCNT_L + offs); +- stats = mtk_r32(mac->hw, MTK_GDM1_RX_GBCNT_H + offs); ++ hw_stats->rx_bytes += mtk_r32(mac->hw, reg_map->gdm1_cnt + offs); ++ stats = mtk_r32(mac->hw, reg_map->gdm1_cnt + 0x4 + offs); + if (stats) + hw_stats->rx_bytes += (stats << 32); + hw_stats->rx_packets += +- mtk_r32(mac->hw, MTK_GDM1_RX_GPCNT + offs); ++ mtk_r32(mac->hw, reg_map->gdm1_cnt + 0x8 + offs); + hw_stats->rx_overflow += +- mtk_r32(mac->hw, MTK_GDM1_RX_OERCNT + offs); ++ mtk_r32(mac->hw, reg_map->gdm1_cnt + 0x10 + offs); + hw_stats->rx_fcs_errors += +- mtk_r32(mac->hw, MTK_GDM1_RX_FERCNT + offs); ++ mtk_r32(mac->hw, reg_map->gdm1_cnt + 0x14 + offs); + hw_stats->rx_short_errors += +- mtk_r32(mac->hw, MTK_GDM1_RX_SERCNT + offs); ++ mtk_r32(mac->hw, reg_map->gdm1_cnt + 0x18 + offs); + hw_stats->rx_long_errors += +- mtk_r32(mac->hw, MTK_GDM1_RX_LENCNT + offs); ++ mtk_r32(mac->hw, reg_map->gdm1_cnt + 0x1c + offs); + hw_stats->rx_checksum_errors += +- mtk_r32(mac->hw, MTK_GDM1_RX_CERCNT + offs); ++ mtk_r32(mac->hw, reg_map->gdm1_cnt + 0x20 + offs); + hw_stats->rx_flow_control_packets += +- mtk_r32(mac->hw, MTK_GDM1_RX_FCCNT + offs); ++ mtk_r32(mac->hw, reg_map->gdm1_cnt + 0x24 + offs); + hw_stats->tx_skip += +- mtk_r32(mac->hw, MTK_GDM1_TX_SKIPCNT + offs); ++ mtk_r32(mac->hw, reg_map->gdm1_cnt + 0x28 + offs); + hw_stats->tx_collisions += +- mtk_r32(mac->hw, MTK_GDM1_TX_COLCNT + offs); ++ mtk_r32(mac->hw, reg_map->gdm1_cnt + 0x2c + offs); + hw_stats->tx_bytes += +- mtk_r32(mac->hw, MTK_GDM1_TX_GBCNT_L + offs); +- stats = mtk_r32(mac->hw, MTK_GDM1_TX_GBCNT_H + offs); ++ mtk_r32(mac->hw, reg_map->gdm1_cnt + 0x30 + offs); ++ stats = mtk_r32(mac->hw, reg_map->gdm1_cnt + 0x34 + offs); + if (stats) + hw_stats->tx_bytes += (stats << 32); + hw_stats->tx_packets += +- mtk_r32(mac->hw, MTK_GDM1_TX_GPCNT + offs); ++ mtk_r32(mac->hw, reg_map->gdm1_cnt + 0x38 + offs); + } + + u64_stats_update_end(&hw_stats->syncp); +@@ -875,10 +928,10 @@ static int mtk_init_fq_dma(struct mtk_et + txd->txd4 = 0; + } + +- mtk_w32(eth, eth->phy_scratch_ring, MTK_QDMA_FQ_HEAD); +- mtk_w32(eth, phy_ring_tail, MTK_QDMA_FQ_TAIL); +- mtk_w32(eth, (cnt << 16) | cnt, MTK_QDMA_FQ_CNT); +- mtk_w32(eth, MTK_QDMA_PAGE_SIZE << 16, MTK_QDMA_FQ_BLEN); ++ mtk_w32(eth, eth->phy_scratch_ring, soc->reg_map->qdma.fq_head); ++ mtk_w32(eth, phy_ring_tail, soc->reg_map->qdma.fq_tail); ++ mtk_w32(eth, (cnt << 16) | cnt, soc->reg_map->qdma.fq_count); ++ mtk_w32(eth, MTK_QDMA_PAGE_SIZE << 16, soc->reg_map->qdma.fq_blen); + + return 0; + } +@@ -1122,7 +1175,7 @@ static int mtk_tx_map(struct sk_buff *sk + if (MTK_HAS_CAPS(soc->caps, MTK_QDMA)) { + if (netif_xmit_stopped(netdev_get_tx_queue(dev, 0)) || + !netdev_xmit_more()) +- mtk_w32(eth, txd->txd2, MTK_QTX_CTX_PTR); ++ mtk_w32(eth, txd->txd2, soc->reg_map->qdma.ctx_ptr); + } else { + int next_idx; + +@@ -1439,6 +1492,7 @@ rx_done: + static int mtk_poll_tx_qdma(struct mtk_eth *eth, int budget, + unsigned int *done, unsigned int *bytes) + { ++ const struct mtk_reg_map *reg_map = eth->soc->reg_map; + struct mtk_tx_ring *ring = ð->tx_ring; + struct mtk_tx_dma *desc; + struct sk_buff *skb; +@@ -1446,7 +1500,7 @@ static int mtk_poll_tx_qdma(struct mtk_e + u32 cpu, dma; + + cpu = ring->last_free_ptr; +- dma = mtk_r32(eth, MTK_QTX_DRX_PTR); ++ dma = mtk_r32(eth, reg_map->qdma.drx_ptr); + + desc = mtk_qdma_phys_to_virt(ring, cpu); + +@@ -1481,7 +1535,7 @@ static int mtk_poll_tx_qdma(struct mtk_e + } + + ring->last_free_ptr = cpu; +- mtk_w32(eth, cpu, MTK_QTX_CRX_PTR); ++ mtk_w32(eth, cpu, reg_map->qdma.crx_ptr); + + return budget; + } +@@ -1574,24 +1628,25 @@ static void mtk_handle_status_irq(struct + static int mtk_napi_tx(struct napi_struct *napi, int budget) + { + struct mtk_eth *eth = container_of(napi, struct mtk_eth, tx_napi); ++ const struct mtk_reg_map *reg_map = eth->soc->reg_map; + int tx_done = 0; + + if (MTK_HAS_CAPS(eth->soc->caps, MTK_QDMA)) + mtk_handle_status_irq(eth); +- mtk_w32(eth, MTK_TX_DONE_INT, eth->tx_int_status_reg); ++ mtk_w32(eth, MTK_TX_DONE_INT, reg_map->tx_irq_status); + tx_done = mtk_poll_tx(eth, budget); + + if (unlikely(netif_msg_intr(eth))) { + dev_info(eth->dev, + "done tx %d, intr 0x%08x/0x%x\n", tx_done, +- mtk_r32(eth, eth->tx_int_status_reg), +- mtk_r32(eth, eth->tx_int_mask_reg)); ++ mtk_r32(eth, reg_map->tx_irq_status), ++ mtk_r32(eth, reg_map->tx_irq_mask)); + } + + if (tx_done == budget) + return budget; + +- if (mtk_r32(eth, eth->tx_int_status_reg) & MTK_TX_DONE_INT) ++ if (mtk_r32(eth, reg_map->tx_irq_status) & MTK_TX_DONE_INT) + return budget; + + if (napi_complete_done(napi, tx_done)) +@@ -1603,6 +1658,7 @@ static int mtk_napi_tx(struct napi_struc + static int mtk_napi_rx(struct napi_struct *napi, int budget) + { + struct mtk_eth *eth = container_of(napi, struct mtk_eth, rx_napi); ++ const struct mtk_reg_map *reg_map = eth->soc->reg_map; + int rx_done_total = 0; + + mtk_handle_status_irq(eth); +@@ -1610,21 +1666,21 @@ static int mtk_napi_rx(struct napi_struc + do { + int rx_done; + +- mtk_w32(eth, MTK_RX_DONE_INT, MTK_PDMA_INT_STATUS); ++ mtk_w32(eth, MTK_RX_DONE_INT, reg_map->pdma.irq_status); + rx_done = mtk_poll_rx(napi, budget - rx_done_total, eth); + rx_done_total += rx_done; + + if (unlikely(netif_msg_intr(eth))) { + dev_info(eth->dev, + "done rx %d, intr 0x%08x/0x%x\n", rx_done, +- mtk_r32(eth, MTK_PDMA_INT_STATUS), +- mtk_r32(eth, MTK_PDMA_INT_MASK)); ++ mtk_r32(eth, reg_map->pdma.irq_status), ++ mtk_r32(eth, reg_map->pdma.irq_mask)); + } + + if (rx_done_total == budget) + return budget; + +- } while (mtk_r32(eth, MTK_PDMA_INT_STATUS) & MTK_RX_DONE_INT); ++ } while (mtk_r32(eth, reg_map->pdma.irq_status) & MTK_RX_DONE_INT); + + if (napi_complete_done(napi, rx_done_total)) + mtk_rx_irq_enable(eth, MTK_RX_DONE_INT); +@@ -1687,20 +1743,20 @@ static int mtk_tx_alloc(struct mtk_eth * + */ + wmb(); + +- if (MTK_HAS_CAPS(eth->soc->caps, MTK_QDMA)) { +- mtk_w32(eth, ring->phys, MTK_QTX_CTX_PTR); +- mtk_w32(eth, ring->phys, MTK_QTX_DTX_PTR); ++ if (MTK_HAS_CAPS(soc->caps, MTK_QDMA)) { ++ mtk_w32(eth, ring->phys, soc->reg_map->qdma.ctx_ptr); ++ mtk_w32(eth, ring->phys, soc->reg_map->qdma.dtx_ptr); + mtk_w32(eth, + ring->phys + ((MTK_DMA_SIZE - 1) * sz), +- MTK_QTX_CRX_PTR); +- mtk_w32(eth, ring->last_free_ptr, MTK_QTX_DRX_PTR); ++ soc->reg_map->qdma.crx_ptr); ++ mtk_w32(eth, ring->last_free_ptr, soc->reg_map->qdma.drx_ptr); + mtk_w32(eth, (QDMA_RES_THRES << 8) | QDMA_RES_THRES, +- MTK_QTX_CFG(0)); ++ soc->reg_map->qdma.qtx_cfg); + } else { + mtk_w32(eth, ring->phys_pdma, MT7628_TX_BASE_PTR0); + mtk_w32(eth, MTK_DMA_SIZE, MT7628_TX_MAX_CNT0); + mtk_w32(eth, 0, MT7628_TX_CTX_IDX0); +- mtk_w32(eth, MT7628_PST_DTX_IDX0, MTK_PDMA_RST_IDX); ++ mtk_w32(eth, MT7628_PST_DTX_IDX0, soc->reg_map->pdma.rst_idx); + } + + return 0; +@@ -1739,6 +1795,7 @@ static void mtk_tx_clean(struct mtk_eth + + static int mtk_rx_alloc(struct mtk_eth *eth, int ring_no, int rx_flag) + { ++ const struct mtk_reg_map *reg_map = eth->soc->reg_map; + struct mtk_rx_ring *ring; + int rx_data_len, rx_dma_size; + int i; +@@ -1807,16 +1864,18 @@ static int mtk_rx_alloc(struct mtk_eth * + ring->dma_size = rx_dma_size; + ring->calc_idx_update = false; + ring->calc_idx = rx_dma_size - 1; +- ring->crx_idx_reg = MTK_PRX_CRX_IDX_CFG(ring_no); ++ ring->crx_idx_reg = reg_map->pdma.pcrx_ptr + ring_no * MTK_QRX_OFFSET; + /* make sure that all changes to the dma ring are flushed before we + * continue + */ + wmb(); + +- mtk_w32(eth, ring->phys, MTK_PRX_BASE_PTR_CFG(ring_no) + offset); +- mtk_w32(eth, rx_dma_size, MTK_PRX_MAX_CNT_CFG(ring_no) + offset); ++ mtk_w32(eth, ring->phys, ++ reg_map->pdma.rx_ptr + ring_no * MTK_QRX_OFFSET + offset); ++ mtk_w32(eth, rx_dma_size, ++ reg_map->pdma.rx_cnt_cfg + ring_no * MTK_QRX_OFFSET + offset); + mtk_w32(eth, ring->calc_idx, ring->crx_idx_reg + offset); +- mtk_w32(eth, MTK_PST_DRX_IDX_CFG(ring_no), MTK_PDMA_RST_IDX + offset); ++ mtk_w32(eth, MTK_PST_DRX_IDX_CFG(ring_no), reg_map->pdma.rst_idx + offset); + + return 0; + } +@@ -2125,9 +2184,9 @@ static int mtk_dma_busy_wait(struct mtk_ + u32 val; + + if (MTK_HAS_CAPS(eth->soc->caps, MTK_QDMA)) +- reg = MTK_QDMA_GLO_CFG; ++ reg = eth->soc->reg_map->qdma.glo_cfg; + else +- reg = MTK_PDMA_GLO_CFG; ++ reg = eth->soc->reg_map->pdma.glo_cfg; + + ret = readx_poll_timeout_atomic(__raw_readl, eth->base + reg, val, + !(val & (MTK_RX_DMA_BUSY | MTK_TX_DMA_BUSY)), +@@ -2185,8 +2244,8 @@ static int mtk_dma_init(struct mtk_eth * + * automatically + */ + mtk_w32(eth, FC_THRES_DROP_MODE | FC_THRES_DROP_EN | +- FC_THRES_MIN, MTK_QDMA_FC_THRES); +- mtk_w32(eth, 0x0, MTK_QDMA_HRED2); ++ FC_THRES_MIN, eth->soc->reg_map->qdma.fc_th); ++ mtk_w32(eth, 0x0, eth->soc->reg_map->qdma.hred); + } + + return 0; +@@ -2260,13 +2319,14 @@ static irqreturn_t mtk_handle_irq_tx(int + static irqreturn_t mtk_handle_irq(int irq, void *_eth) + { + struct mtk_eth *eth = _eth; ++ const struct mtk_reg_map *reg_map = eth->soc->reg_map; + +- if (mtk_r32(eth, MTK_PDMA_INT_MASK) & MTK_RX_DONE_INT) { +- if (mtk_r32(eth, MTK_PDMA_INT_STATUS) & MTK_RX_DONE_INT) ++ if (mtk_r32(eth, reg_map->pdma.irq_mask) & MTK_RX_DONE_INT) { ++ if (mtk_r32(eth, reg_map->pdma.irq_status) & MTK_RX_DONE_INT) + mtk_handle_irq_rx(irq, _eth); + } +- if (mtk_r32(eth, eth->tx_int_mask_reg) & MTK_TX_DONE_INT) { +- if (mtk_r32(eth, eth->tx_int_status_reg) & MTK_TX_DONE_INT) ++ if (mtk_r32(eth, reg_map->tx_irq_mask) & MTK_TX_DONE_INT) { ++ if (mtk_r32(eth, reg_map->tx_irq_status) & MTK_TX_DONE_INT) + mtk_handle_irq_tx(irq, _eth); + } + +@@ -2290,6 +2350,7 @@ static void mtk_poll_controller(struct n + static int mtk_start_dma(struct mtk_eth *eth) + { + u32 rx_2b_offset = (NET_IP_ALIGN == 2) ? MTK_RX_2B_OFFSET : 0; ++ const struct mtk_reg_map *reg_map = eth->soc->reg_map; + int err; + + err = mtk_dma_init(eth); +@@ -2304,16 +2365,15 @@ static int mtk_start_dma(struct mtk_eth + MTK_TX_BT_32DWORDS | MTK_NDP_CO_PRO | + MTK_RX_DMA_EN | MTK_RX_2B_OFFSET | + MTK_RX_BT_32DWORDS, +- MTK_QDMA_GLO_CFG); +- ++ reg_map->qdma.glo_cfg); + mtk_w32(eth, + MTK_RX_DMA_EN | rx_2b_offset | + MTK_RX_BT_32DWORDS | MTK_MULTI_EN, +- MTK_PDMA_GLO_CFG); ++ reg_map->pdma.glo_cfg); + } else { + mtk_w32(eth, MTK_TX_WB_DDONE | MTK_TX_DMA_EN | MTK_RX_DMA_EN | + MTK_MULTI_EN | MTK_PDMA_SIZE_8DWORDS, +- MTK_PDMA_GLO_CFG); ++ reg_map->pdma.glo_cfg); + } + + return 0; +@@ -2437,8 +2497,8 @@ static int mtk_stop(struct net_device *d + cancel_work_sync(ð->tx_dim.work); + + if (MTK_HAS_CAPS(eth->soc->caps, MTK_QDMA)) +- mtk_stop_dma(eth, MTK_QDMA_GLO_CFG); +- mtk_stop_dma(eth, MTK_PDMA_GLO_CFG); ++ mtk_stop_dma(eth, eth->soc->reg_map->qdma.glo_cfg); ++ mtk_stop_dma(eth, eth->soc->reg_map->pdma.glo_cfg); + + mtk_dma_free(eth); + +@@ -2492,6 +2552,7 @@ static void mtk_dim_rx(struct work_struc + { + struct dim *dim = container_of(work, struct dim, work); + struct mtk_eth *eth = container_of(dim, struct mtk_eth, rx_dim); ++ const struct mtk_reg_map *reg_map = eth->soc->reg_map; + struct dim_cq_moder cur_profile; + u32 val, cur; + +@@ -2499,7 +2560,7 @@ static void mtk_dim_rx(struct work_struc + dim->profile_ix); + spin_lock_bh(ð->dim_lock); + +- val = mtk_r32(eth, MTK_PDMA_DELAY_INT); ++ val = mtk_r32(eth, reg_map->pdma.delay_irq); + val &= MTK_PDMA_DELAY_TX_MASK; + val |= MTK_PDMA_DELAY_RX_EN; + +@@ -2509,9 +2570,9 @@ static void mtk_dim_rx(struct work_struc + cur = min_t(u32, cur_profile.pkts, MTK_PDMA_DELAY_PINT_MASK); + val |= cur << MTK_PDMA_DELAY_RX_PINT_SHIFT; + +- mtk_w32(eth, val, MTK_PDMA_DELAY_INT); ++ mtk_w32(eth, val, reg_map->pdma.delay_irq); + if (MTK_HAS_CAPS(eth->soc->caps, MTK_QDMA)) +- mtk_w32(eth, val, MTK_QDMA_DELAY_INT); ++ mtk_w32(eth, val, reg_map->qdma.delay_irq); + + spin_unlock_bh(ð->dim_lock); + +@@ -2522,6 +2583,7 @@ static void mtk_dim_tx(struct work_struc + { + struct dim *dim = container_of(work, struct dim, work); + struct mtk_eth *eth = container_of(dim, struct mtk_eth, tx_dim); ++ const struct mtk_reg_map *reg_map = eth->soc->reg_map; + struct dim_cq_moder cur_profile; + u32 val, cur; + +@@ -2529,7 +2591,7 @@ static void mtk_dim_tx(struct work_struc + dim->profile_ix); + spin_lock_bh(ð->dim_lock); + +- val = mtk_r32(eth, MTK_PDMA_DELAY_INT); ++ val = mtk_r32(eth, reg_map->pdma.delay_irq); + val &= MTK_PDMA_DELAY_RX_MASK; + val |= MTK_PDMA_DELAY_TX_EN; + +@@ -2539,9 +2601,9 @@ static void mtk_dim_tx(struct work_struc + cur = min_t(u32, cur_profile.pkts, MTK_PDMA_DELAY_PINT_MASK); + val |= cur << MTK_PDMA_DELAY_TX_PINT_SHIFT; + +- mtk_w32(eth, val, MTK_PDMA_DELAY_INT); ++ mtk_w32(eth, val, reg_map->pdma.delay_irq); + if (MTK_HAS_CAPS(eth->soc->caps, MTK_QDMA)) +- mtk_w32(eth, val, MTK_QDMA_DELAY_INT); ++ mtk_w32(eth, val, reg_map->qdma.delay_irq); + + spin_unlock_bh(ð->dim_lock); + +@@ -2552,6 +2614,7 @@ static int mtk_hw_init(struct mtk_eth *e + { + u32 dma_mask = ETHSYS_DMA_AG_MAP_PDMA | ETHSYS_DMA_AG_MAP_QDMA | + ETHSYS_DMA_AG_MAP_PPE; ++ const struct mtk_reg_map *reg_map = eth->soc->reg_map; + int i, val, ret; + + if (test_and_set_bit(MTK_HW_INIT, ð->state)) +@@ -2626,10 +2689,10 @@ static int mtk_hw_init(struct mtk_eth *e + mtk_rx_irq_disable(eth, ~0); + + /* FE int grouping */ +- mtk_w32(eth, MTK_TX_DONE_INT, MTK_PDMA_INT_GRP1); +- mtk_w32(eth, MTK_RX_DONE_INT, MTK_PDMA_INT_GRP2); +- mtk_w32(eth, MTK_TX_DONE_INT, MTK_QDMA_INT_GRP1); +- mtk_w32(eth, MTK_RX_DONE_INT, MTK_QDMA_INT_GRP2); ++ mtk_w32(eth, MTK_TX_DONE_INT, reg_map->pdma.int_grp); ++ mtk_w32(eth, MTK_RX_DONE_INT, reg_map->pdma.int_grp + 4); ++ mtk_w32(eth, MTK_TX_DONE_INT, reg_map->qdma.int_grp); ++ mtk_w32(eth, MTK_RX_DONE_INT, reg_map->qdma.int_grp + 4); + mtk_w32(eth, 0x21021000, MTK_FE_INT_GRP); + + return 0; +@@ -3168,14 +3231,6 @@ static int mtk_probe(struct platform_dev + if (IS_ERR(eth->base)) + return PTR_ERR(eth->base); + +- if (MTK_HAS_CAPS(eth->soc->caps, MTK_QDMA)) { +- eth->tx_int_mask_reg = MTK_QDMA_INT_MASK; +- eth->tx_int_status_reg = MTK_QDMA_INT_STATUS; +- } else { +- eth->tx_int_mask_reg = MTK_PDMA_INT_MASK; +- eth->tx_int_status_reg = MTK_PDMA_INT_STATUS; +- } +- + if (MTK_HAS_CAPS(eth->soc->caps, MTK_SOC_MT7628)) { + eth->rx_dma_l4_valid = RX_DMA_L4_VALID_PDMA; + eth->ip_align = NET_IP_ALIGN; +@@ -3409,6 +3464,7 @@ static int mtk_remove(struct platform_de + } + + static const struct mtk_soc_data mt2701_data = { ++ .reg_map = &mtk_reg_map, + .caps = MT7623_CAPS | MTK_HWLRO, + .hw_features = MTK_HW_FEATURES, + .required_clks = MT7623_CLKS_BITMAP, +@@ -3420,6 +3476,7 @@ static const struct mtk_soc_data mt2701_ + }; + + static const struct mtk_soc_data mt7621_data = { ++ .reg_map = &mtk_reg_map, + .caps = MT7621_CAPS, + .hw_features = MTK_HW_FEATURES, + .required_clks = MT7621_CLKS_BITMAP, +@@ -3432,6 +3489,7 @@ static const struct mtk_soc_data mt7621_ + }; + + static const struct mtk_soc_data mt7622_data = { ++ .reg_map = &mtk_reg_map, + .ana_rgc3 = 0x2028, + .caps = MT7622_CAPS | MTK_HWLRO, + .hw_features = MTK_HW_FEATURES, +@@ -3445,6 +3503,7 @@ static const struct mtk_soc_data mt7622_ + }; + + static const struct mtk_soc_data mt7623_data = { ++ .reg_map = &mtk_reg_map, + .caps = MT7623_CAPS | MTK_HWLRO, + .hw_features = MTK_HW_FEATURES, + .required_clks = MT7623_CLKS_BITMAP, +@@ -3457,6 +3516,7 @@ static const struct mtk_soc_data mt7623_ + }; + + static const struct mtk_soc_data mt7629_data = { ++ .reg_map = &mtk_reg_map, + .ana_rgc3 = 0x128, + .caps = MT7629_CAPS | MTK_HWLRO, + .hw_features = MTK_HW_FEATURES, +@@ -3469,6 +3529,7 @@ static const struct mtk_soc_data mt7629_ + }; + + static const struct mtk_soc_data rt5350_data = { ++ .reg_map = &mt7628_reg_map, + .caps = MT7628_CAPS, + .hw_features = MTK_HW_FEATURES_MT7628, + .required_clks = MT7628_CLKS_BITMAP, +--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h ++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h +@@ -48,6 +48,8 @@ + #define MTK_HW_FEATURES_MT7628 (NETIF_F_SG | NETIF_F_RXCSUM) + #define NEXT_DESP_IDX(X, Y) (((X) + 1) & ((Y) - 1)) + ++#define MTK_QRX_OFFSET 0x10 ++ + #define MTK_MAX_RX_RING_NUM 4 + #define MTK_HW_LRO_DMA_SIZE 8 + +@@ -100,18 +102,6 @@ + /* Unicast Filter MAC Address Register - High */ + #define MTK_GDMA_MAC_ADRH(x) (0x50C + (x * 0x1000)) + +-/* PDMA RX Base Pointer Register */ +-#define MTK_PRX_BASE_PTR0 0x900 +-#define MTK_PRX_BASE_PTR_CFG(x) (MTK_PRX_BASE_PTR0 + (x * 0x10)) +- +-/* PDMA RX Maximum Count Register */ +-#define MTK_PRX_MAX_CNT0 0x904 +-#define MTK_PRX_MAX_CNT_CFG(x) (MTK_PRX_MAX_CNT0 + (x * 0x10)) +- +-/* PDMA RX CPU Pointer Register */ +-#define MTK_PRX_CRX_IDX0 0x908 +-#define MTK_PRX_CRX_IDX_CFG(x) (MTK_PRX_CRX_IDX0 + (x * 0x10)) +- + /* PDMA HW LRO Control Registers */ + #define MTK_PDMA_LRO_CTRL_DW0 0x980 + #define MTK_LRO_EN BIT(0) +@@ -126,18 +116,19 @@ + #define MTK_ADMA_MODE BIT(15) + #define MTK_LRO_MIN_RXD_SDL (MTK_HW_LRO_SDL_REMAIN_ROOM << 16) + +-/* PDMA Global Configuration Register */ +-#define MTK_PDMA_GLO_CFG 0xa04 ++#define MTK_RX_DMA_LRO_EN BIT(8) + #define MTK_MULTI_EN BIT(10) + #define MTK_PDMA_SIZE_8DWORDS (1 << 4) + ++/* PDMA Global Configuration Register */ ++#define MTK_PDMA_LRO_SDL 0x3000 ++#define MTK_RX_CFG_SDL_OFFSET 16 ++ + /* PDMA Reset Index Register */ +-#define MTK_PDMA_RST_IDX 0xa08 + #define MTK_PST_DRX_IDX0 BIT(16) + #define MTK_PST_DRX_IDX_CFG(x) (MTK_PST_DRX_IDX0 << (x)) + + /* PDMA Delay Interrupt Register */ +-#define MTK_PDMA_DELAY_INT 0xa0c + #define MTK_PDMA_DELAY_RX_MASK GENMASK(15, 0) + #define MTK_PDMA_DELAY_RX_EN BIT(15) + #define MTK_PDMA_DELAY_RX_PINT_SHIFT 8 +@@ -151,19 +142,9 @@ + #define MTK_PDMA_DELAY_PINT_MASK 0x7f + #define MTK_PDMA_DELAY_PTIME_MASK 0xff + +-/* PDMA Interrupt Status Register */ +-#define MTK_PDMA_INT_STATUS 0xa20 +- +-/* PDMA Interrupt Mask Register */ +-#define MTK_PDMA_INT_MASK 0xa28 +- + /* PDMA HW LRO Alter Flow Delta Register */ + #define MTK_PDMA_LRO_ALT_SCORE_DELTA 0xa4c + +-/* PDMA Interrupt grouping registers */ +-#define MTK_PDMA_INT_GRP1 0xa50 +-#define MTK_PDMA_INT_GRP2 0xa54 +- + /* PDMA HW LRO IP Setting Registers */ + #define MTK_LRO_RX_RING0_DIP_DW0 0xb04 + #define MTK_LRO_DIP_DW0_CFG(x) (MTK_LRO_RX_RING0_DIP_DW0 + (x * 0x40)) +@@ -185,26 +166,9 @@ + #define MTK_RING_MAX_AGG_CNT_H ((MTK_HW_LRO_MAX_AGG_CNT >> 6) & 0x3) + + /* QDMA TX Queue Configuration Registers */ +-#define MTK_QTX_CFG(x) (0x1800 + (x * 0x10)) + #define QDMA_RES_THRES 4 + +-/* QDMA TX Queue Scheduler Registers */ +-#define MTK_QTX_SCH(x) (0x1804 + (x * 0x10)) +- +-/* QDMA RX Base Pointer Register */ +-#define MTK_QRX_BASE_PTR0 0x1900 +- +-/* QDMA RX Maximum Count Register */ +-#define MTK_QRX_MAX_CNT0 0x1904 +- +-/* QDMA RX CPU Pointer Register */ +-#define MTK_QRX_CRX_IDX0 0x1908 +- +-/* QDMA RX DMA Pointer Register */ +-#define MTK_QRX_DRX_IDX0 0x190C +- + /* QDMA Global Configuration Register */ +-#define MTK_QDMA_GLO_CFG 0x1A04 + #define MTK_RX_2B_OFFSET BIT(31) + #define MTK_RX_BT_32DWORDS (3 << 11) + #define MTK_NDP_CO_PRO BIT(10) +@@ -216,20 +180,12 @@ + #define MTK_TX_DMA_EN BIT(0) + #define MTK_DMA_BUSY_TIMEOUT_US 1000000 + +-/* QDMA Reset Index Register */ +-#define MTK_QDMA_RST_IDX 0x1A08 +- +-/* QDMA Delay Interrupt Register */ +-#define MTK_QDMA_DELAY_INT 0x1A0C +- + /* QDMA Flow Control Register */ +-#define MTK_QDMA_FC_THRES 0x1A10 + #define FC_THRES_DROP_MODE BIT(20) + #define FC_THRES_DROP_EN (7 << 16) + #define FC_THRES_MIN 0x4444 + + /* QDMA Interrupt Status Register */ +-#define MTK_QDMA_INT_STATUS 0x1A18 + #define MTK_RX_DONE_DLY BIT(30) + #define MTK_TX_DONE_DLY BIT(28) + #define MTK_RX_DONE_INT3 BIT(19) +@@ -244,55 +200,8 @@ + #define MTK_TX_DONE_INT MTK_TX_DONE_DLY + + /* QDMA Interrupt grouping registers */ +-#define MTK_QDMA_INT_GRP1 0x1a20 +-#define MTK_QDMA_INT_GRP2 0x1a24 + #define MTK_RLS_DONE_INT BIT(0) + +-/* QDMA Interrupt Status Register */ +-#define MTK_QDMA_INT_MASK 0x1A1C +- +-/* QDMA Interrupt Mask Register */ +-#define MTK_QDMA_HRED2 0x1A44 +- +-/* QDMA TX Forward CPU Pointer Register */ +-#define MTK_QTX_CTX_PTR 0x1B00 +- +-/* QDMA TX Forward DMA Pointer Register */ +-#define MTK_QTX_DTX_PTR 0x1B04 +- +-/* QDMA TX Release CPU Pointer Register */ +-#define MTK_QTX_CRX_PTR 0x1B10 +- +-/* QDMA TX Release DMA Pointer Register */ +-#define MTK_QTX_DRX_PTR 0x1B14 +- +-/* QDMA FQ Head Pointer Register */ +-#define MTK_QDMA_FQ_HEAD 0x1B20 +- +-/* QDMA FQ Head Pointer Register */ +-#define MTK_QDMA_FQ_TAIL 0x1B24 +- +-/* QDMA FQ Free Page Counter Register */ +-#define MTK_QDMA_FQ_CNT 0x1B28 +- +-/* QDMA FQ Free Page Buffer Length Register */ +-#define MTK_QDMA_FQ_BLEN 0x1B2C +- +-/* GMA1 counter / statics register */ +-#define MTK_GDM1_RX_GBCNT_L 0x2400 +-#define MTK_GDM1_RX_GBCNT_H 0x2404 +-#define MTK_GDM1_RX_GPCNT 0x2408 +-#define MTK_GDM1_RX_OERCNT 0x2410 +-#define MTK_GDM1_RX_FERCNT 0x2414 +-#define MTK_GDM1_RX_SERCNT 0x2418 +-#define MTK_GDM1_RX_LENCNT 0x241c +-#define MTK_GDM1_RX_CERCNT 0x2420 +-#define MTK_GDM1_RX_FCCNT 0x2424 +-#define MTK_GDM1_TX_SKIPCNT 0x2428 +-#define MTK_GDM1_TX_COLCNT 0x242c +-#define MTK_GDM1_TX_GBCNT_L 0x2430 +-#define MTK_GDM1_TX_GBCNT_H 0x2434 +-#define MTK_GDM1_TX_GPCNT 0x2438 + #define MTK_STAT_OFFSET 0x40 + + #define MTK_WDMA0_BASE 0x2800 +@@ -853,8 +762,46 @@ struct mtk_tx_dma_desc_info { + u8 last:1; + }; + ++struct mtk_reg_map { ++ u32 tx_irq_mask; ++ u32 tx_irq_status; ++ struct { ++ u32 rx_ptr; /* rx base pointer */ ++ u32 rx_cnt_cfg; /* rx max count configuration */ ++ u32 pcrx_ptr; /* rx cpu pointer */ ++ u32 glo_cfg; /* global configuration */ ++ u32 rst_idx; /* reset index */ ++ u32 delay_irq; /* delay interrupt */ ++ u32 irq_status; /* interrupt status */ ++ u32 irq_mask; /* interrupt mask */ ++ u32 int_grp; ++ } pdma; ++ struct { ++ u32 qtx_cfg; /* tx queue configuration */ ++ u32 rx_ptr; /* rx base pointer */ ++ u32 rx_cnt_cfg; /* rx max count configuration */ ++ u32 qcrx_ptr; /* rx cpu pointer */ ++ u32 glo_cfg; /* global configuration */ ++ u32 rst_idx; /* reset index */ ++ u32 delay_irq; /* delay interrupt */ ++ u32 fc_th; /* flow control */ ++ u32 int_grp; ++ u32 hred; /* interrupt mask */ ++ u32 ctx_ptr; /* tx acquire cpu pointer */ ++ u32 dtx_ptr; /* tx acquire dma pointer */ ++ u32 crx_ptr; /* tx release cpu pointer */ ++ u32 drx_ptr; /* tx release dma pointer */ ++ u32 fq_head; /* fq head pointer */ ++ u32 fq_tail; /* fq tail pointer */ ++ u32 fq_count; /* fq free page count */ ++ u32 fq_blen; /* fq free page buffer length */ ++ } qdma; ++ u32 gdm1_cnt; ++}; ++ + /* struct mtk_eth_data - This is the structure holding all differences + * among various plaforms ++ * @reg_map Soc register map. + * @ana_rgc3: The offset for register ANA_RGC3 related to + * sgmiisys syscon + * @caps Flags shown the extra capability for the SoC +@@ -867,6 +814,7 @@ struct mtk_tx_dma_desc_info { + * @rxd_size Rx DMA descriptor size. + */ + struct mtk_soc_data { ++ const struct mtk_reg_map *reg_map; + u32 ana_rgc3; + u32 caps; + u32 required_clks; +@@ -994,8 +942,6 @@ struct mtk_eth { + u32 tx_bytes; + struct dim tx_dim; + +- u32 tx_int_mask_reg; +- u32 tx_int_status_reg; + u32 rx_dma_l4_valid; + int ip_align; + diff --git a/target/linux/generic/backport-6.1/702-v5.19-27-net-ethernet-mtk_eth_soc-introduce-MTK_NETSYS_V2-sup.patch b/target/linux/generic/backport-6.1/702-v5.19-27-net-ethernet-mtk_eth_soc-introduce-MTK_NETSYS_V2-sup.patch new file mode 100644 index 0000000000..e15854ecad --- /dev/null +++ b/target/linux/generic/backport-6.1/702-v5.19-27-net-ethernet-mtk_eth_soc-introduce-MTK_NETSYS_V2-sup.patch @@ -0,0 +1,917 @@ +From: Lorenzo Bianconi +Date: Fri, 20 May 2022 20:11:36 +0200 +Subject: [PATCH] net: ethernet: mtk_eth_soc: introduce MTK_NETSYS_V2 support + +Introduce MTK_NETSYS_V2 support. MTK_NETSYS_V2 defines 32B TX/RX DMA +descriptors. +This is a preliminary patch to add mt7986 ethernet support. + +Tested-by: Sam Shih +Signed-off-by: Lorenzo Bianconi +Signed-off-by: David S. Miller +--- + +--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c ++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c +@@ -862,8 +862,8 @@ static inline int mtk_max_buf_size(int f + return buf_size; + } + +-static inline bool mtk_rx_get_desc(struct mtk_rx_dma *rxd, +- struct mtk_rx_dma *dma_rxd) ++static bool mtk_rx_get_desc(struct mtk_eth *eth, struct mtk_rx_dma_v2 *rxd, ++ struct mtk_rx_dma_v2 *dma_rxd) + { + rxd->rxd2 = READ_ONCE(dma_rxd->rxd2); + if (!(rxd->rxd2 & RX_DMA_DONE)) +@@ -872,6 +872,10 @@ static inline bool mtk_rx_get_desc(struc + rxd->rxd1 = READ_ONCE(dma_rxd->rxd1); + rxd->rxd3 = READ_ONCE(dma_rxd->rxd3); + rxd->rxd4 = READ_ONCE(dma_rxd->rxd4); ++ if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V2)) { ++ rxd->rxd5 = READ_ONCE(dma_rxd->rxd5); ++ rxd->rxd6 = READ_ONCE(dma_rxd->rxd6); ++ } + + return true; + } +@@ -916,7 +920,7 @@ static int mtk_init_fq_dma(struct mtk_et + phy_ring_tail = eth->phy_scratch_ring + soc->txrx.txd_size * (cnt - 1); + + for (i = 0; i < cnt; i++) { +- struct mtk_tx_dma *txd; ++ struct mtk_tx_dma_v2 *txd; + + txd = (void *)eth->scratch_ring + i * soc->txrx.txd_size; + txd->txd1 = dma_addr + i * MTK_QDMA_PAGE_SIZE; +@@ -926,6 +930,12 @@ static int mtk_init_fq_dma(struct mtk_et + + txd->txd3 = TX_DMA_PLEN0(MTK_QDMA_PAGE_SIZE); + txd->txd4 = 0; ++ if (MTK_HAS_CAPS(soc->caps, MTK_NETSYS_V2)) { ++ txd->txd5 = 0; ++ txd->txd6 = 0; ++ txd->txd7 = 0; ++ txd->txd8 = 0; ++ } + } + + mtk_w32(eth, eth->phy_scratch_ring, soc->reg_map->qdma.fq_head); +@@ -1029,10 +1039,12 @@ static void setup_tx_buf(struct mtk_eth + } + } + +-static void mtk_tx_set_dma_desc(struct net_device *dev, struct mtk_tx_dma *desc, +- struct mtk_tx_dma_desc_info *info) ++static void mtk_tx_set_dma_desc_v1(struct net_device *dev, void *txd, ++ struct mtk_tx_dma_desc_info *info) + { + struct mtk_mac *mac = netdev_priv(dev); ++ struct mtk_eth *eth = mac->hw; ++ struct mtk_tx_dma *desc = txd; + u32 data; + + WRITE_ONCE(desc->txd1, info->addr); +@@ -1056,6 +1068,59 @@ static void mtk_tx_set_dma_desc(struct n + WRITE_ONCE(desc->txd4, data); + } + ++static void mtk_tx_set_dma_desc_v2(struct net_device *dev, void *txd, ++ struct mtk_tx_dma_desc_info *info) ++{ ++ struct mtk_mac *mac = netdev_priv(dev); ++ struct mtk_tx_dma_v2 *desc = txd; ++ struct mtk_eth *eth = mac->hw; ++ u32 data; ++ ++ WRITE_ONCE(desc->txd1, info->addr); ++ ++ data = TX_DMA_PLEN0(info->size); ++ if (info->last) ++ data |= TX_DMA_LS0; ++ WRITE_ONCE(desc->txd3, data); ++ ++ if (!info->qid && mac->id) ++ info->qid = MTK_QDMA_GMAC2_QID; ++ ++ data = (mac->id + 1) << TX_DMA_FPORT_SHIFT_V2; /* forward port */ ++ data |= TX_DMA_SWC_V2 | QID_BITS_V2(info->qid); ++ WRITE_ONCE(desc->txd4, data); ++ ++ data = 0; ++ if (info->first) { ++ if (info->gso) ++ data |= TX_DMA_TSO_V2; ++ /* tx checksum offload */ ++ if (info->csum) ++ data |= TX_DMA_CHKSUM_V2; ++ } ++ WRITE_ONCE(desc->txd5, data); ++ ++ data = 0; ++ if (info->first && info->vlan) ++ data |= TX_DMA_INS_VLAN_V2 | info->vlan_tci; ++ WRITE_ONCE(desc->txd6, data); ++ ++ WRITE_ONCE(desc->txd7, 0); ++ WRITE_ONCE(desc->txd8, 0); ++} ++ ++static void mtk_tx_set_dma_desc(struct net_device *dev, void *txd, ++ struct mtk_tx_dma_desc_info *info) ++{ ++ struct mtk_mac *mac = netdev_priv(dev); ++ struct mtk_eth *eth = mac->hw; ++ ++ if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V2)) ++ mtk_tx_set_dma_desc_v2(dev, txd, info); ++ else ++ mtk_tx_set_dma_desc_v1(dev, txd, info); ++} ++ + static int mtk_tx_map(struct sk_buff *skb, struct net_device *dev, + int tx_num, struct mtk_tx_ring *ring, bool gso) + { +@@ -1064,6 +1129,7 @@ static int mtk_tx_map(struct sk_buff *sk + .gso = gso, + .csum = skb->ip_summed == CHECKSUM_PARTIAL, + .vlan = skb_vlan_tag_present(skb), ++ .qid = skb->mark & MTK_QDMA_TX_MASK, + .vlan_tci = skb_vlan_tag_get(skb), + .first = true, + .last = !skb_is_nonlinear(skb), +@@ -1123,7 +1189,9 @@ static int mtk_tx_map(struct sk_buff *sk + } + + memset(&txd_info, 0, sizeof(struct mtk_tx_dma_desc_info)); +- txd_info.size = min(frag_size, MTK_TX_DMA_BUF_LEN); ++ txd_info.size = min_t(unsigned int, frag_size, ++ soc->txrx.dma_max_len); ++ txd_info.qid = skb->mark & MTK_QDMA_TX_MASK; + txd_info.last = i == skb_shinfo(skb)->nr_frags - 1 && + !(frag_size - txd_info.size); + txd_info.addr = skb_frag_dma_map(eth->dma_dev, frag, +@@ -1204,17 +1272,16 @@ err_dma: + return -ENOMEM; + } + +-static inline int mtk_cal_txd_req(struct sk_buff *skb) ++static int mtk_cal_txd_req(struct mtk_eth *eth, struct sk_buff *skb) + { +- int i, nfrags; ++ int i, nfrags = 1; + skb_frag_t *frag; + +- nfrags = 1; + if (skb_is_gso(skb)) { + for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) { + frag = &skb_shinfo(skb)->frags[i]; + nfrags += DIV_ROUND_UP(skb_frag_size(frag), +- MTK_TX_DMA_BUF_LEN); ++ eth->soc->txrx.dma_max_len); + } + } else { + nfrags += skb_shinfo(skb)->nr_frags; +@@ -1266,7 +1333,7 @@ static netdev_tx_t mtk_start_xmit(struct + if (unlikely(test_bit(MTK_RESETTING, ð->state))) + goto drop; + +- tx_num = mtk_cal_txd_req(skb); ++ tx_num = mtk_cal_txd_req(eth, skb); + if (unlikely(atomic_read(&ring->free_count) <= tx_num)) { + netif_stop_queue(dev); + netif_err(eth, tx_queued, dev, +@@ -1358,7 +1425,7 @@ static int mtk_poll_rx(struct napi_struc + int idx; + struct sk_buff *skb; + u8 *data, *new_data; +- struct mtk_rx_dma *rxd, trxd; ++ struct mtk_rx_dma_v2 *rxd, trxd; + int done = 0, bytes = 0; + + while (done < budget) { +@@ -1366,7 +1433,7 @@ static int mtk_poll_rx(struct napi_struc + unsigned int pktlen; + dma_addr_t dma_addr; + u32 hash, reason; +- int mac; ++ int mac = 0; + + ring = mtk_get_rx_ring(eth); + if (unlikely(!ring)) +@@ -1376,16 +1443,15 @@ static int mtk_poll_rx(struct napi_struc + rxd = (void *)ring->dma + idx * eth->soc->txrx.rxd_size; + data = ring->data[idx]; + +- if (!mtk_rx_get_desc(&trxd, rxd)) ++ if (!mtk_rx_get_desc(eth, &trxd, rxd)) + break; + + /* find out which mac the packet come from. values start at 1 */ +- if (MTK_HAS_CAPS(eth->soc->caps, MTK_SOC_MT7628) || +- (trxd.rxd4 & RX_DMA_SPECIAL_TAG)) +- mac = 0; +- else +- mac = ((trxd.rxd4 >> RX_DMA_FPORT_SHIFT) & +- RX_DMA_FPORT_MASK) - 1; ++ if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V2)) ++ mac = RX_DMA_GET_SPORT_V2(trxd.rxd5) - 1; ++ else if (!MTK_HAS_CAPS(eth->soc->caps, MTK_SOC_MT7628) && ++ !(trxd.rxd4 & RX_DMA_SPECIAL_TAG)) ++ mac = RX_DMA_GET_SPORT(trxd.rxd4) - 1; + + if (unlikely(mac < 0 || mac >= MTK_MAC_COUNT || + !eth->netdev[mac])) +@@ -1431,7 +1497,7 @@ static int mtk_poll_rx(struct napi_struc + pktlen = RX_DMA_GET_PLEN0(trxd.rxd2); + skb->dev = netdev; + skb_put(skb, pktlen); +- if (trxd.rxd4 & eth->rx_dma_l4_valid) ++ if (trxd.rxd4 & eth->soc->txrx.rx_dma_l4_valid) + skb->ip_summed = CHECKSUM_UNNECESSARY; + else + skb_checksum_none_assert(skb); +@@ -1449,10 +1515,25 @@ static int mtk_poll_rx(struct napi_struc + mtk_ppe_check_skb(eth->ppe, skb, + trxd.rxd4 & MTK_RXD4_FOE_ENTRY); + +- if (netdev->features & NETIF_F_HW_VLAN_CTAG_RX && +- (trxd.rxd2 & RX_DMA_VTAG)) +- __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), +- RX_DMA_VID(trxd.rxd3)); ++ if (netdev->features & NETIF_F_HW_VLAN_CTAG_RX) { ++ if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V2)) { ++ if (trxd.rxd3 & RX_DMA_VTAG_V2) ++ __vlan_hwaccel_put_tag(skb, ++ htons(RX_DMA_VPID(trxd.rxd4)), ++ RX_DMA_VID(trxd.rxd4)); ++ } else if (trxd.rxd2 & RX_DMA_VTAG) { ++ __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), ++ RX_DMA_VID(trxd.rxd3)); ++ } ++ ++ /* If the device is attached to a dsa switch, the special ++ * tag inserted in VLAN field by hw switch can * be offloaded ++ * by RX HW VLAN offload. Clear vlan info. ++ */ ++ if (netdev_uses_dsa(netdev)) ++ __vlan_hwaccel_clear_tag(skb); ++ } ++ + skb_record_rx_queue(skb, 0); + napi_gro_receive(napi, skb); + +@@ -1464,7 +1545,7 @@ release_desc: + if (MTK_HAS_CAPS(eth->soc->caps, MTK_SOC_MT7628)) + rxd->rxd2 = RX_DMA_LSO; + else +- rxd->rxd2 = RX_DMA_PLEN0(ring->buf_size); ++ rxd->rxd2 = RX_DMA_PREP_PLEN0(ring->buf_size); + + ring->calc_idx = idx; + +@@ -1666,7 +1747,8 @@ static int mtk_napi_rx(struct napi_struc + do { + int rx_done; + +- mtk_w32(eth, MTK_RX_DONE_INT, reg_map->pdma.irq_status); ++ mtk_w32(eth, eth->soc->txrx.rx_irq_done_mask, ++ reg_map->pdma.irq_status); + rx_done = mtk_poll_rx(napi, budget - rx_done_total, eth); + rx_done_total += rx_done; + +@@ -1680,10 +1762,11 @@ static int mtk_napi_rx(struct napi_struc + if (rx_done_total == budget) + return budget; + +- } while (mtk_r32(eth, reg_map->pdma.irq_status) & MTK_RX_DONE_INT); ++ } while (mtk_r32(eth, reg_map->pdma.irq_status) & ++ eth->soc->txrx.rx_irq_done_mask); + + if (napi_complete_done(napi, rx_done_total)) +- mtk_rx_irq_enable(eth, MTK_RX_DONE_INT); ++ mtk_rx_irq_enable(eth, eth->soc->txrx.rx_irq_done_mask); + + return rx_done_total; + } +@@ -1693,7 +1776,7 @@ static int mtk_tx_alloc(struct mtk_eth * + const struct mtk_soc_data *soc = eth->soc; + struct mtk_tx_ring *ring = ð->tx_ring; + int i, sz = soc->txrx.txd_size; +- struct mtk_tx_dma *txd; ++ struct mtk_tx_dma_v2 *txd; + + ring->buf = kcalloc(MTK_DMA_SIZE, sizeof(*ring->buf), + GFP_KERNEL); +@@ -1713,13 +1796,19 @@ static int mtk_tx_alloc(struct mtk_eth * + txd->txd2 = next_ptr; + txd->txd3 = TX_DMA_LS0 | TX_DMA_OWNER_CPU; + txd->txd4 = 0; ++ if (MTK_HAS_CAPS(soc->caps, MTK_NETSYS_V2)) { ++ txd->txd5 = 0; ++ txd->txd6 = 0; ++ txd->txd7 = 0; ++ txd->txd8 = 0; ++ } + } + + /* On MT7688 (PDMA only) this driver uses the ring->dma structs + * only as the framework. The real HW descriptors are the PDMA + * descriptors in ring->dma_pdma. + */ +- if (!MTK_HAS_CAPS(eth->soc->caps, MTK_QDMA)) { ++ if (!MTK_HAS_CAPS(soc->caps, MTK_QDMA)) { + ring->dma_pdma = dma_alloc_coherent(eth->dma_dev, MTK_DMA_SIZE * sz, + &ring->phys_pdma, GFP_KERNEL); + if (!ring->dma_pdma) +@@ -1799,13 +1888,11 @@ static int mtk_rx_alloc(struct mtk_eth * + struct mtk_rx_ring *ring; + int rx_data_len, rx_dma_size; + int i; +- u32 offset = 0; + + if (rx_flag == MTK_RX_FLAGS_QDMA) { + if (ring_no) + return -EINVAL; + ring = ð->rx_ring_qdma; +- offset = 0x1000; + } else { + ring = ð->rx_ring[ring_no]; + } +@@ -1841,7 +1928,7 @@ static int mtk_rx_alloc(struct mtk_eth * + return -ENOMEM; + + for (i = 0; i < rx_dma_size; i++) { +- struct mtk_rx_dma *rxd; ++ struct mtk_rx_dma_v2 *rxd; + + dma_addr_t dma_addr = dma_map_single(eth->dma_dev, + ring->data[i] + NET_SKB_PAD + eth->ip_align, +@@ -1856,26 +1943,47 @@ static int mtk_rx_alloc(struct mtk_eth * + if (MTK_HAS_CAPS(eth->soc->caps, MTK_SOC_MT7628)) + rxd->rxd2 = RX_DMA_LSO; + else +- rxd->rxd2 = RX_DMA_PLEN0(ring->buf_size); ++ rxd->rxd2 = RX_DMA_PREP_PLEN0(ring->buf_size); + + rxd->rxd3 = 0; + rxd->rxd4 = 0; ++ if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V2)) { ++ rxd->rxd5 = 0; ++ rxd->rxd6 = 0; ++ rxd->rxd7 = 0; ++ rxd->rxd8 = 0; ++ } + } + ring->dma_size = rx_dma_size; + ring->calc_idx_update = false; + ring->calc_idx = rx_dma_size - 1; +- ring->crx_idx_reg = reg_map->pdma.pcrx_ptr + ring_no * MTK_QRX_OFFSET; ++ if (rx_flag == MTK_RX_FLAGS_QDMA) ++ ring->crx_idx_reg = reg_map->qdma.qcrx_ptr + ++ ring_no * MTK_QRX_OFFSET; ++ else ++ ring->crx_idx_reg = reg_map->pdma.pcrx_ptr + ++ ring_no * MTK_QRX_OFFSET; + /* make sure that all changes to the dma ring are flushed before we + * continue + */ + wmb(); + +- mtk_w32(eth, ring->phys, +- reg_map->pdma.rx_ptr + ring_no * MTK_QRX_OFFSET + offset); +- mtk_w32(eth, rx_dma_size, +- reg_map->pdma.rx_cnt_cfg + ring_no * MTK_QRX_OFFSET + offset); +- mtk_w32(eth, ring->calc_idx, ring->crx_idx_reg + offset); +- mtk_w32(eth, MTK_PST_DRX_IDX_CFG(ring_no), reg_map->pdma.rst_idx + offset); ++ if (rx_flag == MTK_RX_FLAGS_QDMA) { ++ mtk_w32(eth, ring->phys, ++ reg_map->qdma.rx_ptr + ring_no * MTK_QRX_OFFSET); ++ mtk_w32(eth, rx_dma_size, ++ reg_map->qdma.rx_cnt_cfg + ring_no * MTK_QRX_OFFSET); ++ mtk_w32(eth, MTK_PST_DRX_IDX_CFG(ring_no), ++ reg_map->qdma.rst_idx); ++ } else { ++ mtk_w32(eth, ring->phys, ++ reg_map->pdma.rx_ptr + ring_no * MTK_QRX_OFFSET); ++ mtk_w32(eth, rx_dma_size, ++ reg_map->pdma.rx_cnt_cfg + ring_no * MTK_QRX_OFFSET); ++ mtk_w32(eth, MTK_PST_DRX_IDX_CFG(ring_no), ++ reg_map->pdma.rst_idx); ++ } ++ mtk_w32(eth, ring->calc_idx, ring->crx_idx_reg); + + return 0; + } +@@ -2297,7 +2405,7 @@ static irqreturn_t mtk_handle_irq_rx(int + eth->rx_events++; + if (likely(napi_schedule_prep(ð->rx_napi))) { + __napi_schedule(ð->rx_napi); +- mtk_rx_irq_disable(eth, MTK_RX_DONE_INT); ++ mtk_rx_irq_disable(eth, eth->soc->txrx.rx_irq_done_mask); + } + + return IRQ_HANDLED; +@@ -2321,8 +2429,10 @@ static irqreturn_t mtk_handle_irq(int ir + struct mtk_eth *eth = _eth; + const struct mtk_reg_map *reg_map = eth->soc->reg_map; + +- if (mtk_r32(eth, reg_map->pdma.irq_mask) & MTK_RX_DONE_INT) { +- if (mtk_r32(eth, reg_map->pdma.irq_status) & MTK_RX_DONE_INT) ++ if (mtk_r32(eth, reg_map->pdma.irq_mask) & ++ eth->soc->txrx.rx_irq_done_mask) { ++ if (mtk_r32(eth, reg_map->pdma.irq_status) & ++ eth->soc->txrx.rx_irq_done_mask) + mtk_handle_irq_rx(irq, _eth); + } + if (mtk_r32(eth, reg_map->tx_irq_mask) & MTK_TX_DONE_INT) { +@@ -2340,16 +2450,16 @@ static void mtk_poll_controller(struct n + struct mtk_eth *eth = mac->hw; + + mtk_tx_irq_disable(eth, MTK_TX_DONE_INT); +- mtk_rx_irq_disable(eth, MTK_RX_DONE_INT); ++ mtk_rx_irq_disable(eth, eth->soc->txrx.rx_irq_done_mask); + mtk_handle_irq_rx(eth->irq[2], dev); + mtk_tx_irq_enable(eth, MTK_TX_DONE_INT); +- mtk_rx_irq_enable(eth, MTK_RX_DONE_INT); ++ mtk_rx_irq_enable(eth, eth->soc->txrx.rx_irq_done_mask); + } + #endif + + static int mtk_start_dma(struct mtk_eth *eth) + { +- u32 rx_2b_offset = (NET_IP_ALIGN == 2) ? MTK_RX_2B_OFFSET : 0; ++ u32 val, rx_2b_offset = (NET_IP_ALIGN == 2) ? MTK_RX_2B_OFFSET : 0; + const struct mtk_reg_map *reg_map = eth->soc->reg_map; + int err; + +@@ -2360,12 +2470,19 @@ static int mtk_start_dma(struct mtk_eth + } + + if (MTK_HAS_CAPS(eth->soc->caps, MTK_QDMA)) { +- mtk_w32(eth, +- MTK_TX_WB_DDONE | MTK_TX_DMA_EN | +- MTK_TX_BT_32DWORDS | MTK_NDP_CO_PRO | +- MTK_RX_DMA_EN | MTK_RX_2B_OFFSET | +- MTK_RX_BT_32DWORDS, +- reg_map->qdma.glo_cfg); ++ val = mtk_r32(eth, reg_map->qdma.glo_cfg); ++ val |= MTK_TX_DMA_EN | MTK_RX_DMA_EN | ++ MTK_TX_BT_32DWORDS | MTK_NDP_CO_PRO | ++ MTK_RX_2B_OFFSET | MTK_TX_WB_DDONE; ++ ++ if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V2)) ++ val |= MTK_MUTLI_CNT | MTK_RESV_BUF | ++ MTK_WCOMP_EN | MTK_DMAD_WR_WDONE | ++ MTK_CHK_DDONE_EN; ++ else ++ val |= MTK_RX_BT_32DWORDS; ++ mtk_w32(eth, val, reg_map->qdma.glo_cfg); ++ + mtk_w32(eth, + MTK_RX_DMA_EN | rx_2b_offset | + MTK_RX_BT_32DWORDS | MTK_MULTI_EN, +@@ -2437,7 +2554,7 @@ static int mtk_open(struct net_device *d + napi_enable(ð->tx_napi); + napi_enable(ð->rx_napi); + mtk_tx_irq_enable(eth, MTK_TX_DONE_INT); +- mtk_rx_irq_enable(eth, MTK_RX_DONE_INT); ++ mtk_rx_irq_enable(eth, eth->soc->txrx.rx_irq_done_mask); + refcount_set(ð->dma_refcnt, 1); + } + else +@@ -2489,7 +2606,7 @@ static int mtk_stop(struct net_device *d + mtk_gdm_config(eth, MTK_GDMA_DROP_ALL); + + mtk_tx_irq_disable(eth, MTK_TX_DONE_INT); +- mtk_rx_irq_disable(eth, MTK_RX_DONE_INT); ++ mtk_rx_irq_disable(eth, eth->soc->txrx.rx_irq_done_mask); + napi_disable(ð->tx_napi); + napi_disable(ð->rx_napi); + +@@ -2649,9 +2766,25 @@ static int mtk_hw_init(struct mtk_eth *e + return 0; + } + +- /* Non-MT7628 handling... */ +- ethsys_reset(eth, RSTCTRL_FE); +- ethsys_reset(eth, RSTCTRL_PPE); ++ val = RSTCTRL_FE | RSTCTRL_PPE; ++ if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V2)) { ++ regmap_write(eth->ethsys, ETHSYS_FE_RST_CHK_IDLE_EN, 0); ++ ++ val |= RSTCTRL_ETH; ++ if (MTK_HAS_CAPS(eth->soc->caps, MTK_RSTCTRL_PPE1)) ++ val |= RSTCTRL_PPE1; ++ } ++ ++ ethsys_reset(eth, val); ++ ++ if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V2)) { ++ regmap_write(eth->ethsys, ETHSYS_FE_RST_CHK_IDLE_EN, ++ 0x3ffffff); ++ ++ /* Set FE to PDMAv2 if necessary */ ++ val = mtk_r32(eth, MTK_FE_GLO_MISC); ++ mtk_w32(eth, val | BIT(4), MTK_FE_GLO_MISC); ++ } + + if (eth->pctl) { + /* Set GE2 driving and slew rate */ +@@ -2690,11 +2823,47 @@ static int mtk_hw_init(struct mtk_eth *e + + /* FE int grouping */ + mtk_w32(eth, MTK_TX_DONE_INT, reg_map->pdma.int_grp); +- mtk_w32(eth, MTK_RX_DONE_INT, reg_map->pdma.int_grp + 4); ++ mtk_w32(eth, eth->soc->txrx.rx_irq_done_mask, reg_map->pdma.int_grp + 4); + mtk_w32(eth, MTK_TX_DONE_INT, reg_map->qdma.int_grp); +- mtk_w32(eth, MTK_RX_DONE_INT, reg_map->qdma.int_grp + 4); ++ mtk_w32(eth, eth->soc->txrx.rx_irq_done_mask, reg_map->qdma.int_grp + 4); + mtk_w32(eth, 0x21021000, MTK_FE_INT_GRP); + ++ if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V2)) { ++ /* PSE should not drop port8 and port9 packets */ ++ mtk_w32(eth, 0x00000300, PSE_DROP_CFG); ++ ++ /* PSE Free Queue Flow Control */ ++ mtk_w32(eth, 0x01fa01f4, PSE_FQFC_CFG2); ++ ++ /* PSE config input queue threshold */ ++ mtk_w32(eth, 0x001a000e, PSE_IQ_REV(1)); ++ mtk_w32(eth, 0x01ff001a, PSE_IQ_REV(2)); ++ mtk_w32(eth, 0x000e01ff, PSE_IQ_REV(3)); ++ mtk_w32(eth, 0x000e000e, PSE_IQ_REV(4)); ++ mtk_w32(eth, 0x000e000e, PSE_IQ_REV(5)); ++ mtk_w32(eth, 0x000e000e, PSE_IQ_REV(6)); ++ mtk_w32(eth, 0x000e000e, PSE_IQ_REV(7)); ++ mtk_w32(eth, 0x000e000e, PSE_IQ_REV(8)); ++ ++ /* PSE config output queue threshold */ ++ mtk_w32(eth, 0x000f000a, PSE_OQ_TH(1)); ++ mtk_w32(eth, 0x001a000f, PSE_OQ_TH(2)); ++ mtk_w32(eth, 0x000f001a, PSE_OQ_TH(3)); ++ mtk_w32(eth, 0x01ff000f, PSE_OQ_TH(4)); ++ mtk_w32(eth, 0x000f000f, PSE_OQ_TH(5)); ++ mtk_w32(eth, 0x0006000f, PSE_OQ_TH(6)); ++ mtk_w32(eth, 0x00060006, PSE_OQ_TH(7)); ++ mtk_w32(eth, 0x00060006, PSE_OQ_TH(8)); ++ ++ /* GDM and CDM Threshold */ ++ mtk_w32(eth, 0x00000004, MTK_GDM2_THRES); ++ mtk_w32(eth, 0x00000004, MTK_CDMW0_THRES); ++ mtk_w32(eth, 0x00000004, MTK_CDMW1_THRES); ++ mtk_w32(eth, 0x00000004, MTK_CDME0_THRES); ++ mtk_w32(eth, 0x00000004, MTK_CDME1_THRES); ++ mtk_w32(eth, 0x00000004, MTK_CDMM_THRES); ++ } ++ + return 0; + + err_disable_pm: +@@ -3231,12 +3400,8 @@ static int mtk_probe(struct platform_dev + if (IS_ERR(eth->base)) + return PTR_ERR(eth->base); + +- if (MTK_HAS_CAPS(eth->soc->caps, MTK_SOC_MT7628)) { +- eth->rx_dma_l4_valid = RX_DMA_L4_VALID_PDMA; ++ if (MTK_HAS_CAPS(eth->soc->caps, MTK_SOC_MT7628)) + eth->ip_align = NET_IP_ALIGN; +- } else { +- eth->rx_dma_l4_valid = RX_DMA_L4_VALID; +- } + + spin_lock_init(ð->page_lock); + spin_lock_init(ð->tx_irq_lock); +@@ -3472,6 +3637,10 @@ static const struct mtk_soc_data mt2701_ + .txrx = { + .txd_size = sizeof(struct mtk_tx_dma), + .rxd_size = sizeof(struct mtk_rx_dma), ++ .rx_irq_done_mask = MTK_RX_DONE_INT, ++ .rx_dma_l4_valid = RX_DMA_L4_VALID, ++ .dma_max_len = MTK_TX_DMA_BUF_LEN, ++ .dma_len_offset = 16, + }, + }; + +@@ -3485,6 +3654,10 @@ static const struct mtk_soc_data mt7621_ + .txrx = { + .txd_size = sizeof(struct mtk_tx_dma), + .rxd_size = sizeof(struct mtk_rx_dma), ++ .rx_irq_done_mask = MTK_RX_DONE_INT, ++ .rx_dma_l4_valid = RX_DMA_L4_VALID, ++ .dma_max_len = MTK_TX_DMA_BUF_LEN, ++ .dma_len_offset = 16, + }, + }; + +@@ -3499,6 +3672,10 @@ static const struct mtk_soc_data mt7622_ + .txrx = { + .txd_size = sizeof(struct mtk_tx_dma), + .rxd_size = sizeof(struct mtk_rx_dma), ++ .rx_irq_done_mask = MTK_RX_DONE_INT, ++ .rx_dma_l4_valid = RX_DMA_L4_VALID, ++ .dma_max_len = MTK_TX_DMA_BUF_LEN, ++ .dma_len_offset = 16, + }, + }; + +@@ -3512,6 +3689,10 @@ static const struct mtk_soc_data mt7623_ + .txrx = { + .txd_size = sizeof(struct mtk_tx_dma), + .rxd_size = sizeof(struct mtk_rx_dma), ++ .rx_irq_done_mask = MTK_RX_DONE_INT, ++ .rx_dma_l4_valid = RX_DMA_L4_VALID, ++ .dma_max_len = MTK_TX_DMA_BUF_LEN, ++ .dma_len_offset = 16, + }, + }; + +@@ -3525,6 +3706,10 @@ static const struct mtk_soc_data mt7629_ + .txrx = { + .txd_size = sizeof(struct mtk_tx_dma), + .rxd_size = sizeof(struct mtk_rx_dma), ++ .rx_irq_done_mask = MTK_RX_DONE_INT, ++ .rx_dma_l4_valid = RX_DMA_L4_VALID, ++ .dma_max_len = MTK_TX_DMA_BUF_LEN, ++ .dma_len_offset = 16, + }, + }; + +@@ -3537,6 +3722,10 @@ static const struct mtk_soc_data rt5350_ + .txrx = { + .txd_size = sizeof(struct mtk_tx_dma), + .rxd_size = sizeof(struct mtk_rx_dma), ++ .rx_irq_done_mask = MTK_RX_DONE_INT, ++ .rx_dma_l4_valid = RX_DMA_L4_VALID_PDMA, ++ .dma_max_len = MTK_TX_DMA_BUF_LEN, ++ .dma_len_offset = 16, + }, + }; + +--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h ++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h +@@ -23,6 +23,7 @@ + #define MTK_MAX_RX_LENGTH 1536 + #define MTK_MAX_RX_LENGTH_2K 2048 + #define MTK_TX_DMA_BUF_LEN 0x3fff ++#define MTK_TX_DMA_BUF_LEN_V2 0xffff + #define MTK_DMA_SIZE 512 + #define MTK_NAPI_WEIGHT 64 + #define MTK_MAC_COUNT 2 +@@ -83,6 +84,10 @@ + #define MTK_CDMQ_IG_CTRL 0x1400 + #define MTK_CDMQ_STAG_EN BIT(0) + ++/* CDMP Ingress Control Register */ ++#define MTK_CDMP_IG_CTRL 0x400 ++#define MTK_CDMP_STAG_EN BIT(0) ++ + /* CDMP Exgress Control Register */ + #define MTK_CDMP_EG_CTRL 0x404 + +@@ -102,13 +107,38 @@ + /* Unicast Filter MAC Address Register - High */ + #define MTK_GDMA_MAC_ADRH(x) (0x50C + (x * 0x1000)) + ++/* FE global misc reg*/ ++#define MTK_FE_GLO_MISC 0x124 ++ ++/* PSE Free Queue Flow Control */ ++#define PSE_FQFC_CFG1 0x100 ++#define PSE_FQFC_CFG2 0x104 ++#define PSE_DROP_CFG 0x108 ++ ++/* PSE Input Queue Reservation Register*/ ++#define PSE_IQ_REV(x) (0x140 + (((x) - 1) << 2)) ++ ++/* PSE Output Queue Threshold Register*/ ++#define PSE_OQ_TH(x) (0x160 + (((x) - 1) << 2)) ++ ++/* GDM and CDM Threshold */ ++#define MTK_GDM2_THRES 0x1530 ++#define MTK_CDMW0_THRES 0x164c ++#define MTK_CDMW1_THRES 0x1650 ++#define MTK_CDME0_THRES 0x1654 ++#define MTK_CDME1_THRES 0x1658 ++#define MTK_CDMM_THRES 0x165c ++ + /* PDMA HW LRO Control Registers */ + #define MTK_PDMA_LRO_CTRL_DW0 0x980 + #define MTK_LRO_EN BIT(0) + #define MTK_L3_CKS_UPD_EN BIT(7) ++#define MTK_L3_CKS_UPD_EN_V2 BIT(19) + #define MTK_LRO_ALT_PKT_CNT_MODE BIT(21) + #define MTK_LRO_RING_RELINQUISH_REQ (0x7 << 26) ++#define MTK_LRO_RING_RELINQUISH_REQ_V2 (0xf << 24) + #define MTK_LRO_RING_RELINQUISH_DONE (0x7 << 29) ++#define MTK_LRO_RING_RELINQUISH_DONE_V2 (0xf << 28) + + #define MTK_PDMA_LRO_CTRL_DW1 0x984 + #define MTK_PDMA_LRO_CTRL_DW2 0x988 +@@ -180,6 +210,13 @@ + #define MTK_TX_DMA_EN BIT(0) + #define MTK_DMA_BUSY_TIMEOUT_US 1000000 + ++/* QDMA V2 Global Configuration Register */ ++#define MTK_CHK_DDONE_EN BIT(28) ++#define MTK_DMAD_WR_WDONE BIT(26) ++#define MTK_WCOMP_EN BIT(24) ++#define MTK_RESV_BUF (0x40 << 16) ++#define MTK_MUTLI_CNT (0x4 << 12) ++ + /* QDMA Flow Control Register */ + #define FC_THRES_DROP_MODE BIT(20) + #define FC_THRES_DROP_EN (7 << 16) +@@ -199,11 +236,32 @@ + #define MTK_RX_DONE_INT MTK_RX_DONE_DLY + #define MTK_TX_DONE_INT MTK_TX_DONE_DLY + ++#define MTK_RX_DONE_INT_V2 BIT(14) ++ + /* QDMA Interrupt grouping registers */ + #define MTK_RLS_DONE_INT BIT(0) + + #define MTK_STAT_OFFSET 0x40 + ++/* QDMA TX NUM */ ++#define MTK_QDMA_TX_NUM 16 ++#define MTK_QDMA_TX_MASK (MTK_QDMA_TX_NUM - 1) ++#define QID_BITS_V2(x) (((x) & 0x3f) << 16) ++#define MTK_QDMA_GMAC2_QID 8 ++ ++#define MTK_TX_DMA_BUF_SHIFT 8 ++ ++/* QDMA V2 descriptor txd6 */ ++#define TX_DMA_INS_VLAN_V2 BIT(16) ++/* QDMA V2 descriptor txd5 */ ++#define TX_DMA_CHKSUM_V2 (0x7 << 28) ++#define TX_DMA_TSO_V2 BIT(31) ++ ++/* QDMA V2 descriptor txd4 */ ++#define TX_DMA_FPORT_SHIFT_V2 8 ++#define TX_DMA_FPORT_MASK_V2 0xf ++#define TX_DMA_SWC_V2 BIT(30) ++ + #define MTK_WDMA0_BASE 0x2800 + #define MTK_WDMA1_BASE 0x2c00 + +@@ -217,10 +275,9 @@ + /* QDMA descriptor txd3 */ + #define TX_DMA_OWNER_CPU BIT(31) + #define TX_DMA_LS0 BIT(30) +-#define TX_DMA_PLEN0(_x) (((_x) & MTK_TX_DMA_BUF_LEN) << 16) +-#define TX_DMA_PLEN1(_x) ((_x) & MTK_TX_DMA_BUF_LEN) ++#define TX_DMA_PLEN0(x) (((x) & eth->soc->txrx.dma_max_len) << eth->soc->txrx.dma_len_offset) ++#define TX_DMA_PLEN1(x) ((x) & eth->soc->txrx.dma_max_len) + #define TX_DMA_SWC BIT(14) +-#define TX_DMA_SDL(_x) (((_x) & 0x3fff) << 16) + + /* PDMA on MT7628 */ + #define TX_DMA_DONE BIT(31) +@@ -230,12 +287,14 @@ + /* QDMA descriptor rxd2 */ + #define RX_DMA_DONE BIT(31) + #define RX_DMA_LSO BIT(30) +-#define RX_DMA_PLEN0(_x) (((_x) & 0x3fff) << 16) +-#define RX_DMA_GET_PLEN0(_x) (((_x) >> 16) & 0x3fff) ++#define RX_DMA_PREP_PLEN0(x) (((x) & eth->soc->txrx.dma_max_len) << eth->soc->txrx.dma_len_offset) ++#define RX_DMA_GET_PLEN0(x) (((x) >> eth->soc->txrx.dma_len_offset) & eth->soc->txrx.dma_max_len) + #define RX_DMA_VTAG BIT(15) + + /* QDMA descriptor rxd3 */ +-#define RX_DMA_VID(_x) ((_x) & 0xfff) ++#define RX_DMA_VID(x) ((x) & VLAN_VID_MASK) ++#define RX_DMA_TCI(x) ((x) & (VLAN_PRIO_MASK | VLAN_VID_MASK)) ++#define RX_DMA_VPID(x) (((x) >> 16) & 0xffff) + + /* QDMA descriptor rxd4 */ + #define MTK_RXD4_FOE_ENTRY GENMASK(13, 0) +@@ -246,10 +305,15 @@ + /* QDMA descriptor rxd4 */ + #define RX_DMA_L4_VALID BIT(24) + #define RX_DMA_L4_VALID_PDMA BIT(30) /* when PDMA is used */ +-#define RX_DMA_FPORT_SHIFT 19 +-#define RX_DMA_FPORT_MASK 0x7 + #define RX_DMA_SPECIAL_TAG BIT(22) + ++#define RX_DMA_GET_SPORT(x) (((x) >> 19) & 0xf) ++#define RX_DMA_GET_SPORT_V2(x) (((x) >> 26) & 0x7) ++ ++/* PDMA V2 descriptor rxd3 */ ++#define RX_DMA_VTAG_V2 BIT(0) ++#define RX_DMA_L4_VALID_V2 BIT(2) ++ + /* PHY Indirect Access Control registers */ + #define MTK_PHY_IAC 0x10004 + #define PHY_IAC_ACCESS BIT(31) +@@ -370,6 +434,16 @@ + #define ETHSYS_TRGMII_MT7621_DDR_PLL BIT(5) + + /* ethernet reset control register */ ++#define ETHSYS_RSTCTRL 0x34 ++#define RSTCTRL_FE BIT(6) ++#define RSTCTRL_PPE BIT(31) ++#define RSTCTRL_PPE1 BIT(30) ++#define RSTCTRL_ETH BIT(23) ++ ++/* ethernet reset check idle register */ ++#define ETHSYS_FE_RST_CHK_IDLE_EN 0x28 ++ ++/* ethernet reset control register */ + #define ETHSYS_RSTCTRL 0x34 + #define RSTCTRL_FE BIT(6) + #define RSTCTRL_PPE BIT(31) +@@ -453,6 +527,17 @@ struct mtk_rx_dma { + unsigned int rxd4; + } __packed __aligned(4); + ++struct mtk_rx_dma_v2 { ++ unsigned int rxd1; ++ unsigned int rxd2; ++ unsigned int rxd3; ++ unsigned int rxd4; ++ unsigned int rxd5; ++ unsigned int rxd6; ++ unsigned int rxd7; ++ unsigned int rxd8; ++} __packed __aligned(4); ++ + struct mtk_tx_dma { + unsigned int txd1; + unsigned int txd2; +@@ -460,6 +545,17 @@ struct mtk_tx_dma { + unsigned int txd4; + } __packed __aligned(4); + ++struct mtk_tx_dma_v2 { ++ unsigned int txd1; ++ unsigned int txd2; ++ unsigned int txd3; ++ unsigned int txd4; ++ unsigned int txd5; ++ unsigned int txd6; ++ unsigned int txd7; ++ unsigned int txd8; ++} __packed __aligned(4); ++ + struct mtk_eth; + struct mtk_mac; + +@@ -646,7 +742,9 @@ enum mkt_eth_capabilities { + MTK_SHARED_INT_BIT, + MTK_TRGMII_MT7621_CLK_BIT, + MTK_QDMA_BIT, ++ MTK_NETSYS_V2_BIT, + MTK_SOC_MT7628_BIT, ++ MTK_RSTCTRL_PPE1_BIT, + + /* MUX BITS*/ + MTK_ETH_MUX_GDM1_TO_GMAC1_ESW_BIT, +@@ -678,7 +776,9 @@ enum mkt_eth_capabilities { + #define MTK_SHARED_INT BIT(MTK_SHARED_INT_BIT) + #define MTK_TRGMII_MT7621_CLK BIT(MTK_TRGMII_MT7621_CLK_BIT) + #define MTK_QDMA BIT(MTK_QDMA_BIT) ++#define MTK_NETSYS_V2 BIT(MTK_NETSYS_V2_BIT) + #define MTK_SOC_MT7628 BIT(MTK_SOC_MT7628_BIT) ++#define MTK_RSTCTRL_PPE1 BIT(MTK_RSTCTRL_PPE1_BIT) + + #define MTK_ETH_MUX_GDM1_TO_GMAC1_ESW \ + BIT(MTK_ETH_MUX_GDM1_TO_GMAC1_ESW_BIT) +@@ -755,6 +855,7 @@ struct mtk_tx_dma_desc_info { + dma_addr_t addr; + u32 size; + u16 vlan_tci; ++ u16 qid; + u8 gso:1; + u8 csum:1; + u8 vlan:1; +@@ -812,6 +913,10 @@ struct mtk_reg_map { + * the extra setup for those pins used by GMAC. + * @txd_size Tx DMA descriptor size. + * @rxd_size Rx DMA descriptor size. ++ * @rx_irq_done_mask Rx irq done register mask. ++ * @rx_dma_l4_valid Rx DMA valid register mask. ++ * @dma_max_len Max DMA tx/rx buffer length. ++ * @dma_len_offset Tx/Rx DMA length field offset. + */ + struct mtk_soc_data { + const struct mtk_reg_map *reg_map; +@@ -824,6 +929,10 @@ struct mtk_soc_data { + struct { + u32 txd_size; + u32 rxd_size; ++ u32 rx_irq_done_mask; ++ u32 rx_dma_l4_valid; ++ u32 dma_max_len; ++ u32 dma_len_offset; + } txrx; + }; + +@@ -942,7 +1051,6 @@ struct mtk_eth { + u32 tx_bytes; + struct dim tx_dim; + +- u32 rx_dma_l4_valid; + int ip_align; + + struct mtk_ppe *ppe; diff --git a/target/linux/generic/backport-6.1/702-v5.19-28-net-ethernet-mtk_eth_soc-convert-ring-dma-pointer-to.patch b/target/linux/generic/backport-6.1/702-v5.19-28-net-ethernet-mtk_eth_soc-convert-ring-dma-pointer-to.patch new file mode 100644 index 0000000000..a956639239 --- /dev/null +++ b/target/linux/generic/backport-6.1/702-v5.19-28-net-ethernet-mtk_eth_soc-convert-ring-dma-pointer-to.patch @@ -0,0 +1,135 @@ +From: Lorenzo Bianconi +Date: Fri, 20 May 2022 20:11:37 +0200 +Subject: [PATCH] net: ethernet: mtk_eth_soc: convert ring dma pointer to void + +Simplify the code converting {tx,rx} ring dma pointer to void + +Signed-off-by: Lorenzo Bianconi +Signed-off-by: David S. Miller +--- + +--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c ++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c +@@ -946,18 +946,15 @@ static int mtk_init_fq_dma(struct mtk_et + return 0; + } + +-static inline void *mtk_qdma_phys_to_virt(struct mtk_tx_ring *ring, u32 desc) ++static void *mtk_qdma_phys_to_virt(struct mtk_tx_ring *ring, u32 desc) + { +- void *ret = ring->dma; +- +- return ret + (desc - ring->phys); ++ return ring->dma + (desc - ring->phys); + } + + static struct mtk_tx_buf *mtk_desc_to_tx_buf(struct mtk_tx_ring *ring, +- struct mtk_tx_dma *txd, +- u32 txd_size) ++ void *txd, u32 txd_size) + { +- int idx = ((void *)txd - (void *)ring->dma) / txd_size; ++ int idx = (txd - ring->dma) / txd_size; + + return &ring->buf[idx]; + } +@@ -965,13 +962,12 @@ static struct mtk_tx_buf *mtk_desc_to_tx + static struct mtk_tx_dma *qdma_to_pdma(struct mtk_tx_ring *ring, + struct mtk_tx_dma *dma) + { +- return ring->dma_pdma - ring->dma + dma; ++ return ring->dma_pdma - (struct mtk_tx_dma *)ring->dma + dma; + } + +-static int txd_to_idx(struct mtk_tx_ring *ring, struct mtk_tx_dma *dma, +- u32 txd_size) ++static int txd_to_idx(struct mtk_tx_ring *ring, void *dma, u32 txd_size) + { +- return ((void *)dma - (void *)ring->dma) / txd_size; ++ return (dma - ring->dma) / txd_size; + } + + static void mtk_tx_unmap(struct mtk_eth *eth, struct mtk_tx_buf *tx_buf, +@@ -1388,7 +1384,7 @@ static struct mtk_rx_ring *mtk_get_rx_ri + + ring = ð->rx_ring[i]; + idx = NEXT_DESP_IDX(ring->calc_idx, ring->dma_size); +- rxd = (void *)ring->dma + idx * eth->soc->txrx.rxd_size; ++ rxd = ring->dma + idx * eth->soc->txrx.rxd_size; + if (rxd->rxd2 & RX_DMA_DONE) { + ring->calc_idx_update = true; + return ring; +@@ -1440,7 +1436,7 @@ static int mtk_poll_rx(struct napi_struc + goto rx_done; + + idx = NEXT_DESP_IDX(ring->calc_idx, ring->dma_size); +- rxd = (void *)ring->dma + idx * eth->soc->txrx.rxd_size; ++ rxd = ring->dma + idx * eth->soc->txrx.rxd_size; + data = ring->data[idx]; + + if (!mtk_rx_get_desc(eth, &trxd, rxd)) +@@ -1647,7 +1643,7 @@ static int mtk_poll_tx_pdma(struct mtk_e + + mtk_tx_unmap(eth, tx_buf, true); + +- desc = (void *)ring->dma + cpu * eth->soc->txrx.txd_size; ++ desc = ring->dma + cpu * eth->soc->txrx.txd_size; + ring->last_free = desc; + atomic_inc(&ring->free_count); + +@@ -1792,7 +1788,7 @@ static int mtk_tx_alloc(struct mtk_eth * + int next = (i + 1) % MTK_DMA_SIZE; + u32 next_ptr = ring->phys + next * sz; + +- txd = (void *)ring->dma + i * sz; ++ txd = ring->dma + i * sz; + txd->txd2 = next_ptr; + txd->txd3 = TX_DMA_LS0 | TX_DMA_OWNER_CPU; + txd->txd4 = 0; +@@ -1822,7 +1818,7 @@ static int mtk_tx_alloc(struct mtk_eth * + + ring->dma_size = MTK_DMA_SIZE; + atomic_set(&ring->free_count, MTK_DMA_SIZE - 2); +- ring->next_free = &ring->dma[0]; ++ ring->next_free = ring->dma; + ring->last_free = (void *)txd; + ring->last_free_ptr = (u32)(ring->phys + ((MTK_DMA_SIZE - 1) * sz)); + ring->thresh = MAX_SKB_FRAGS; +@@ -1937,7 +1933,7 @@ static int mtk_rx_alloc(struct mtk_eth * + if (unlikely(dma_mapping_error(eth->dma_dev, dma_addr))) + return -ENOMEM; + +- rxd = (void *)ring->dma + i * eth->soc->txrx.rxd_size; ++ rxd = ring->dma + i * eth->soc->txrx.rxd_size; + rxd->rxd1 = (unsigned int)dma_addr; + + if (MTK_HAS_CAPS(eth->soc->caps, MTK_SOC_MT7628)) +@@ -1999,7 +1995,7 @@ static void mtk_rx_clean(struct mtk_eth + if (!ring->data[i]) + continue; + +- rxd = (void *)ring->dma + i * eth->soc->txrx.rxd_size; ++ rxd = ring->dma + i * eth->soc->txrx.rxd_size; + if (!rxd->rxd1) + continue; + +--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h ++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h +@@ -688,7 +688,7 @@ struct mtk_tx_buf { + * are present + */ + struct mtk_tx_ring { +- struct mtk_tx_dma *dma; ++ void *dma; + struct mtk_tx_buf *buf; + dma_addr_t phys; + struct mtk_tx_dma *next_free; +@@ -718,7 +718,7 @@ enum mtk_rx_flags { + * @calc_idx: The current head of ring + */ + struct mtk_rx_ring { +- struct mtk_rx_dma *dma; ++ void *dma; + u8 **data; + dma_addr_t phys; + u16 frag_size; diff --git a/target/linux/generic/backport-6.1/702-v5.19-29-net-ethernet-mtk_eth_soc-convert-scratch_ring-pointe.patch b/target/linux/generic/backport-6.1/702-v5.19-29-net-ethernet-mtk_eth_soc-convert-scratch_ring-pointe.patch new file mode 100644 index 0000000000..459ffd5406 --- /dev/null +++ b/target/linux/generic/backport-6.1/702-v5.19-29-net-ethernet-mtk_eth_soc-convert-scratch_ring-pointe.patch @@ -0,0 +1,33 @@ +From: Lorenzo Bianconi +Date: Fri, 20 May 2022 20:11:38 +0200 +Subject: [PATCH] net: ethernet: mtk_eth_soc: convert scratch_ring pointer to + void + +Simplify the code converting scratch_ring pointer to void + +Signed-off-by: Lorenzo Bianconi +Signed-off-by: David S. Miller +--- + +--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c ++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c +@@ -922,7 +922,7 @@ static int mtk_init_fq_dma(struct mtk_et + for (i = 0; i < cnt; i++) { + struct mtk_tx_dma_v2 *txd; + +- txd = (void *)eth->scratch_ring + i * soc->txrx.txd_size; ++ txd = eth->scratch_ring + i * soc->txrx.txd_size; + txd->txd1 = dma_addr + i * MTK_QDMA_PAGE_SIZE; + if (i < cnt - 1) + txd->txd2 = eth->phy_scratch_ring + +--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h ++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h +@@ -1028,7 +1028,7 @@ struct mtk_eth { + struct mtk_rx_ring rx_ring_qdma; + struct napi_struct tx_napi; + struct napi_struct rx_napi; +- struct mtk_tx_dma *scratch_ring; ++ void *scratch_ring; + dma_addr_t phy_scratch_ring; + void *scratch_head; + struct clk *clks[MTK_CLK_MAX]; diff --git a/target/linux/generic/backport-6.1/702-v5.19-30-net-ethernet-mtk_eth_soc-introduce-support-for-mt798.patch b/target/linux/generic/backport-6.1/702-v5.19-30-net-ethernet-mtk_eth_soc-introduce-support-for-mt798.patch new file mode 100644 index 0000000000..4baeb2244f --- /dev/null +++ b/target/linux/generic/backport-6.1/702-v5.19-30-net-ethernet-mtk_eth_soc-introduce-support-for-mt798.patch @@ -0,0 +1,138 @@ +From: Lorenzo Bianconi +Date: Fri, 20 May 2022 20:11:39 +0200 +Subject: [PATCH] net: ethernet: mtk_eth_soc: introduce support for mt7986 + chipset + +Add support for mt7986-eth driver available on mt7986 soc. + +Tested-by: Sam Shih +Signed-off-by: Lorenzo Bianconi +Signed-off-by: David S. Miller +--- + +--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c ++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c +@@ -87,6 +87,43 @@ static const struct mtk_reg_map mt7628_r + }, + }; + ++static const struct mtk_reg_map mt7986_reg_map = { ++ .tx_irq_mask = 0x461c, ++ .tx_irq_status = 0x4618, ++ .pdma = { ++ .rx_ptr = 0x6100, ++ .rx_cnt_cfg = 0x6104, ++ .pcrx_ptr = 0x6108, ++ .glo_cfg = 0x6204, ++ .rst_idx = 0x6208, ++ .delay_irq = 0x620c, ++ .irq_status = 0x6220, ++ .irq_mask = 0x6228, ++ .int_grp = 0x6250, ++ }, ++ .qdma = { ++ .qtx_cfg = 0x4400, ++ .rx_ptr = 0x4500, ++ .rx_cnt_cfg = 0x4504, ++ .qcrx_ptr = 0x4508, ++ .glo_cfg = 0x4604, ++ .rst_idx = 0x4608, ++ .delay_irq = 0x460c, ++ .fc_th = 0x4610, ++ .int_grp = 0x4620, ++ .hred = 0x4644, ++ .ctx_ptr = 0x4700, ++ .dtx_ptr = 0x4704, ++ .crx_ptr = 0x4710, ++ .drx_ptr = 0x4714, ++ .fq_head = 0x4720, ++ .fq_tail = 0x4724, ++ .fq_count = 0x4728, ++ .fq_blen = 0x472c, ++ }, ++ .gdm1_cnt = 0x1c00, ++}; ++ + /* strings used by ethtool */ + static const struct mtk_ethtool_stats { + char str[ETH_GSTRING_LEN]; +@@ -110,7 +147,7 @@ static const char * const mtk_clks_sourc + "ethif", "sgmiitop", "esw", "gp0", "gp1", "gp2", "fe", "trgpll", + "sgmii_tx250m", "sgmii_rx250m", "sgmii_cdr_ref", "sgmii_cdr_fb", + "sgmii2_tx250m", "sgmii2_rx250m", "sgmii2_cdr_ref", "sgmii2_cdr_fb", +- "sgmii_ck", "eth2pll", ++ "sgmii_ck", "eth2pll", "wocpu0", "wocpu1", "netsys0", "netsys1" + }; + + void mtk_w32(struct mtk_eth *eth, u32 val, unsigned reg) +@@ -3709,6 +3746,21 @@ static const struct mtk_soc_data mt7629_ + }, + }; + ++static const struct mtk_soc_data mt7986_data = { ++ .reg_map = &mt7986_reg_map, ++ .ana_rgc3 = 0x128, ++ .caps = MT7986_CAPS, ++ .required_clks = MT7986_CLKS_BITMAP, ++ .required_pctl = false, ++ .txrx = { ++ .txd_size = sizeof(struct mtk_tx_dma_v2), ++ .rxd_size = sizeof(struct mtk_rx_dma_v2), ++ .rx_irq_done_mask = MTK_RX_DONE_INT_V2, ++ .dma_max_len = MTK_TX_DMA_BUF_LEN_V2, ++ .dma_len_offset = 8, ++ }, ++}; ++ + static const struct mtk_soc_data rt5350_data = { + .reg_map = &mt7628_reg_map, + .caps = MT7628_CAPS, +@@ -3731,6 +3783,7 @@ const struct of_device_id of_mtk_match[] + { .compatible = "mediatek,mt7622-eth", .data = &mt7622_data}, + { .compatible = "mediatek,mt7623-eth", .data = &mt7623_data}, + { .compatible = "mediatek,mt7629-eth", .data = &mt7629_data}, ++ { .compatible = "mediatek,mt7986-eth", .data = &mt7986_data}, + { .compatible = "ralink,rt5350-eth", .data = &rt5350_data}, + {}, + }; +--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h ++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h +@@ -623,6 +623,10 @@ enum mtk_clks_map { + MTK_CLK_SGMII2_CDR_FB, + MTK_CLK_SGMII_CK, + MTK_CLK_ETH2PLL, ++ MTK_CLK_WOCPU0, ++ MTK_CLK_WOCPU1, ++ MTK_CLK_NETSYS0, ++ MTK_CLK_NETSYS1, + MTK_CLK_MAX + }; + +@@ -653,6 +657,16 @@ enum mtk_clks_map { + BIT(MTK_CLK_SGMII2_CDR_FB) | \ + BIT(MTK_CLK_SGMII_CK) | \ + BIT(MTK_CLK_ETH2PLL) | BIT(MTK_CLK_SGMIITOP)) ++#define MT7986_CLKS_BITMAP (BIT(MTK_CLK_FE) | BIT(MTK_CLK_GP2) | BIT(MTK_CLK_GP1) | \ ++ BIT(MTK_CLK_WOCPU1) | BIT(MTK_CLK_WOCPU0) | \ ++ BIT(MTK_CLK_SGMII_TX_250M) | \ ++ BIT(MTK_CLK_SGMII_RX_250M) | \ ++ BIT(MTK_CLK_SGMII_CDR_REF) | \ ++ BIT(MTK_CLK_SGMII_CDR_FB) | \ ++ BIT(MTK_CLK_SGMII2_TX_250M) | \ ++ BIT(MTK_CLK_SGMII2_RX_250M) | \ ++ BIT(MTK_CLK_SGMII2_CDR_REF) | \ ++ BIT(MTK_CLK_SGMII2_CDR_FB)) + + enum mtk_dev_state { + MTK_HW_INIT, +@@ -851,6 +865,10 @@ enum mkt_eth_capabilities { + MTK_MUX_U3_GMAC2_TO_QPHY | \ + MTK_MUX_GMAC12_TO_GEPHY_SGMII | MTK_QDMA) + ++#define MT7986_CAPS (MTK_GMAC1_SGMII | MTK_GMAC2_SGMII | \ ++ MTK_MUX_GMAC12_TO_GEPHY_SGMII | MTK_QDMA | \ ++ MTK_NETSYS_V2 | MTK_RSTCTRL_PPE1) ++ + struct mtk_tx_dma_desc_info { + dma_addr_t addr; + u32 size; diff --git a/target/linux/generic/backport-6.1/702-v5.19-31-net-ethernet-mtk_eth_soc-fix-error-code-in-mtk_flow_.patch b/target/linux/generic/backport-6.1/702-v5.19-31-net-ethernet-mtk_eth_soc-fix-error-code-in-mtk_flow_.patch new file mode 100644 index 0000000000..e490333a9b --- /dev/null +++ b/target/linux/generic/backport-6.1/702-v5.19-31-net-ethernet-mtk_eth_soc-fix-error-code-in-mtk_flow_.patch @@ -0,0 +1,25 @@ +From: Dan Carpenter +Date: Thu, 19 May 2022 17:08:00 +0300 +Subject: [PATCH] net: ethernet: mtk_eth_soc: fix error code in + mtk_flow_offload_replace() + +Preserve the error code from mtk_foe_entry_commit(). Do not return +success. + +Fixes: c4f033d9e03e ("net: ethernet: mtk_eth_soc: rework hardware flow table management") +Signed-off-by: Dan Carpenter +Signed-off-by: David S. Miller +--- + +--- a/drivers/net/ethernet/mediatek/mtk_ppe_offload.c ++++ b/drivers/net/ethernet/mediatek/mtk_ppe_offload.c +@@ -434,7 +434,8 @@ mtk_flow_offload_replace(struct mtk_eth + memcpy(&entry->data, &foe, sizeof(entry->data)); + entry->wed_index = wed_index; + +- if (mtk_foe_entry_commit(eth->ppe, entry) < 0) ++ err = mtk_foe_entry_commit(eth->ppe, entry); ++ if (err < 0) + goto free; + + err = rhashtable_insert_fast(ð->flow_table, &entry->node, diff --git a/target/linux/generic/backport-6.1/702-v5.19-33-net-ethernet-mtk_eth_soc-enable-rx-cksum-offload-for.patch b/target/linux/generic/backport-6.1/702-v5.19-33-net-ethernet-mtk_eth_soc-enable-rx-cksum-offload-for.patch new file mode 100644 index 0000000000..2e3ad698b5 --- /dev/null +++ b/target/linux/generic/backport-6.1/702-v5.19-33-net-ethernet-mtk_eth_soc-enable-rx-cksum-offload-for.patch @@ -0,0 +1,47 @@ +From: Lorenzo Bianconi +Date: Mon, 6 Jun 2022 21:49:00 +0200 +Subject: [PATCH] net: ethernet: mtk_eth_soc: enable rx cksum offload for + MTK_NETSYS_V2 + +Enable rx checksum offload for mt7986 chipset. + +Signed-off-by: Lorenzo Bianconi +Link: https://lore.kernel.org/r/c8699805c18f7fd38315fcb8da2787676d83a32c.1654544585.git.lorenzo@kernel.org +Signed-off-by: Jakub Kicinski +--- + +--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c ++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c +@@ -1462,8 +1462,8 @@ static int mtk_poll_rx(struct napi_struc + int done = 0, bytes = 0; + + while (done < budget) { ++ unsigned int pktlen, *rxdcsum; + struct net_device *netdev; +- unsigned int pktlen; + dma_addr_t dma_addr; + u32 hash, reason; + int mac = 0; +@@ -1530,7 +1530,13 @@ static int mtk_poll_rx(struct napi_struc + pktlen = RX_DMA_GET_PLEN0(trxd.rxd2); + skb->dev = netdev; + skb_put(skb, pktlen); +- if (trxd.rxd4 & eth->soc->txrx.rx_dma_l4_valid) ++ ++ if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V2)) ++ rxdcsum = &trxd.rxd3; ++ else ++ rxdcsum = &trxd.rxd4; ++ ++ if (*rxdcsum & eth->soc->txrx.rx_dma_l4_valid) + skb->ip_summed = CHECKSUM_UNNECESSARY; + else + skb_checksum_none_assert(skb); +@@ -3756,6 +3762,7 @@ static const struct mtk_soc_data mt7986_ + .txd_size = sizeof(struct mtk_tx_dma_v2), + .rxd_size = sizeof(struct mtk_rx_dma_v2), + .rx_irq_done_mask = MTK_RX_DONE_INT_V2, ++ .rx_dma_l4_valid = RX_DMA_L4_VALID_V2, + .dma_max_len = MTK_TX_DMA_BUF_LEN_V2, + .dma_len_offset = 8, + }, diff --git a/target/linux/generic/backport-6.1/702-v5.19-34-eth-mtk_ppe-fix-up-after-merge.patch b/target/linux/generic/backport-6.1/702-v5.19-34-eth-mtk_ppe-fix-up-after-merge.patch new file mode 100644 index 0000000000..5303ca48a7 --- /dev/null +++ b/target/linux/generic/backport-6.1/702-v5.19-34-eth-mtk_ppe-fix-up-after-merge.patch @@ -0,0 +1,28 @@ +From: Jakub Kicinski +Date: Thu, 19 May 2022 18:25:55 -0700 +Subject: [PATCH] eth: mtk_ppe: fix up after merge + +I missed this in the barrage of GCC 12 warnings. Commit cf2df74e202d +("net: fix dev_fill_forward_path with pppoe + bridge") changed +the pointer into an array. + +Fixes: d7e6f5836038 ("Merge git://git.kernel.org/pub/scm/linux/kernel/git/netdev/net") +Link: https://lore.kernel.org/r/20220520012555.2262461-1-kuba@kernel.org +Signed-off-by: Jakub Kicinski +--- + +--- a/drivers/net/ethernet/mediatek/mtk_ppe_offload.c ++++ b/drivers/net/ethernet/mediatek/mtk_ppe_offload.c +@@ -90,10 +90,11 @@ mtk_flow_get_wdma_info(struct net_device + { + struct net_device_path_ctx ctx = { + .dev = dev, +- .daddr = addr, + }; + struct net_device_path path = {}; + ++ memcpy(ctx.daddr, addr, sizeof(ctx.daddr)); ++ + if (!IS_ENABLED(CONFIG_NET_MEDIATEK_SOC_WED)) + return -1; + diff --git a/target/linux/generic/backport-6.1/703-00-v5.16-net-convert-users-of-bitmap_foo-to-linkmode_foo.patch b/target/linux/generic/backport-6.1/703-00-v5.16-net-convert-users-of-bitmap_foo-to-linkmode_foo.patch new file mode 100644 index 0000000000..555e7eaa95 --- /dev/null +++ b/target/linux/generic/backport-6.1/703-00-v5.16-net-convert-users-of-bitmap_foo-to-linkmode_foo.patch @@ -0,0 +1,948 @@ +From 4973056cceacc70966396039fae99867dfafd796 Mon Sep 17 00:00:00 2001 +From: Sean Anderson +Date: Fri, 22 Oct 2021 18:41:04 -0400 +Subject: [PATCH] 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 +| + #include +| + #include +| + #include +| + #include +| + #include +| + #include +| + #include +| + #include +| + #include +| + #include +| + #include +| + #include +| + #include +| + #include +| + #include +| + #include +| + #include +| + #include +| + #include +| + #include +| + #include +| + #include +| + #include +| + #include +| + #include +| + #include +| + #include +| + #include +| + #include +| + #include +| + #include +| + #include +| + #include +| + #include +| + #include +| + #include +| + #include +| + #include +| + #include +| + #include +| + #include +| + #include +| + #include +| + #include +| + #include +| + #include +| + #include +| + #include +| + #include +| + #include +| + #include +| + #include +| + #include +| + #include +| + #include +| + #include +| + #include +| + #include +| + #include +| + #include +| + #include +| + #include +| + #include +| + #include +| + #include +| + #include +| + #include +| + #include +| + #include +| + #include +| + #include +| + #include +| + #include +| + #include +| + #include +| + #include +| + #include +| + #include +| + #include +| + #include +| + #include +| + #include +| + #include +| + #include +| + #include +| + #include +| + #include +| + #include +| + #include +| + #include +| + #include +| + #include +| + #include +| + #include +| + #include +| + #include +| + #include +) + +@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 +Signed-off-by: David S. Miller +--- + 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(-) + +--- a/drivers/net/dsa/b53/b53_common.c ++++ b/drivers/net/dsa/b53/b53_common.c +@@ -1349,10 +1349,8 @@ void b53_phylink_validate(struct dsa_swi + 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); + } +--- 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 d + 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 d + 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, +--- a/drivers/net/dsa/hirschmann/hellcreek.c ++++ b/drivers/net/dsa/hirschmann/hellcreek.c +@@ -1476,10 +1476,8 @@ static void hellcreek_phylink_validate(s + 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 +--- a/drivers/net/dsa/lantiq_gswip.c ++++ b/drivers/net/dsa/lantiq_gswip.c +@@ -1452,10 +1452,8 @@ static void gswip_phylink_set_capab(unsi + 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_validat + 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_validat + 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_validat + 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_validat + 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); + } +--- a/drivers/net/dsa/microchip/ksz8795.c ++++ b/drivers/net/dsa/microchip/ksz8795.c +@@ -1542,15 +1542,13 @@ static void ksz8_validate(struct dsa_swi + 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); + } +--- a/drivers/net/dsa/mv88e6xxx/chip.c ++++ b/drivers/net/dsa/mv88e6xxx/chip.c +@@ -683,9 +683,8 @@ static void mv88e6xxx_validate(struct ds + 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. +--- 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(str + + 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(str + 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, +--- 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(str + + 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(str + 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, +--- a/drivers/net/dsa/qca/ar9331.c ++++ b/drivers/net/dsa/qca/ar9331.c +@@ -522,7 +522,7 @@ static void ar9331_sw_phylink_validate(s + 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(s + 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); + } +--- 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(str + */ + 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(str + 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 +--- a/drivers/net/dsa/xrs700x/xrs700x.c ++++ b/drivers/net/dsa/xrs700x/xrs700x.c +@@ -457,7 +457,7 @@ static void xrs700x_phylink_validate(str + 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(str + 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, +--- 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(struc + __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(struc + 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); +--- a/drivers/net/ethernet/atheros/ag71xx.c ++++ b/drivers/net/ethernet/atheros/ag71xx.c +@@ -1082,14 +1082,12 @@ static void ag71xx_mac_validate(struct p + 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, +--- 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 + 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 + 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, +--- a/drivers/net/ethernet/freescale/enetc/enetc_pf.c ++++ b/drivers/net/ethernet/freescale/enetc/enetc_pf.c +@@ -968,7 +968,7 @@ static void enetc_pl_mac_validate(struct + 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; + } + +@@ -991,10 +991,8 @@ static void enetc_pl_mac_validate(struct + 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, +--- 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(stru + } + } + +- 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; + } +--- 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(stru + * 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 */ +--- a/drivers/net/ethernet/marvell/mvneta.c ++++ b/drivers/net/ethernet/marvell/mvneta.c +@@ -3835,14 +3835,14 @@ static void mvneta_validate(struct phyli + */ + 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; + } + +@@ -3871,10 +3871,8 @@ static void mvneta_validate(struct phyli + 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. +--- 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(struc + 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, +--- 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(struc + 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); +--- 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_de + 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) +--- a/drivers/net/ethernet/mellanox/mlx4/en_ethtool.c ++++ b/drivers/net/ethernet/mellanox/mlx4/en_ethtool.c +@@ -39,6 +39,7 @@ + #include + #include + #include ++#include + + #include "mlx4_en.h" + #include "en_port.h" +@@ -643,10 +644,8 @@ static unsigned long *ptys2ethtool_link_ + 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_mod + 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 + 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; +--- 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(stru + } + 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, +--- 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(str + + 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(str + 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, +--- 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(stru + 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); +--- 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 phyl + 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 phyl + 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, +--- 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, + 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); + +--- a/drivers/net/phy/sfp-bus.c ++++ b/drivers/net/phy/sfp-bus.c +@@ -379,7 +379,7 @@ void sfp_parse_support(struct sfp_bus *b + if (bus->sfp_quirk) + 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); +--- a/net/ethtool/ioctl.c ++++ b/net/ethtool/ioctl.c +@@ -335,7 +335,7 @@ EXPORT_SYMBOL(ethtool_intersect_link_mas + 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 + 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; + } diff --git a/target/linux/generic/backport-6.1/703-01-v5.16-net-phylink-add-MAC-phy_interface_t-bitmap.patch b/target/linux/generic/backport-6.1/703-01-v5.16-net-phylink-add-MAC-phy_interface_t-bitmap.patch new file mode 100644 index 0000000000..b72faec8d9 --- /dev/null +++ b/target/linux/generic/backport-6.1/703-01-v5.16-net-phylink-add-MAC-phy_interface_t-bitmap.patch @@ -0,0 +1,24 @@ +From 38c310eb46f5f80213a92093af11af270c209a76 Mon Sep 17 00:00:00 2001 +From: Russell King +Date: Tue, 26 Oct 2021 11:06:06 +0100 +Subject: [PATCH] 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 +Signed-off-by: David S. Miller +--- + include/linux/phylink.h | 1 + + 1 file changed, 1 insertion(+) + +--- 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); + }; + + /** diff --git a/target/linux/generic/backport-6.1/703-02-v5.16-net-phylink-use-supported_interfaces-for-phylink-val.patch b/target/linux/generic/backport-6.1/703-02-v5.16-net-phylink-use-supported_interfaces-for-phylink-val.patch new file mode 100644 index 0000000000..8996fc8d45 --- /dev/null +++ b/target/linux/generic/backport-6.1/703-02-v5.16-net-phylink-use-supported_interfaces-for-phylink-val.patch @@ -0,0 +1,98 @@ +From d25f3a74f30aace819163dfa54f2a4b8ca1dc932 Mon Sep 17 00:00:00 2001 +From: "Russell King (Oracle)" +Date: Tue, 26 Oct 2021 11:06:11 +0100 +Subject: [PATCH] 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 +Signed-off-by: Russell King (Oracle) +Signed-off-by: David S. Miller +--- + drivers/net/phy/phylink.c | 36 ++++++++++++++++++++++++++++++++++++ + include/linux/phylink.h | 12 ++++++++++-- + 2 files changed, 46 insertions(+), 2 deletions(-) + +--- a/drivers/net/phy/phylink.c ++++ b/drivers/net/phy/phylink.c +@@ -155,9 +155,45 @@ static const char *phylink_an_mode_str(u + 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; +--- 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. diff --git a/target/linux/generic/backport-6.1/703-03-v5.16-net-dsa-populate-supported_interfaces-member.patch b/target/linux/generic/backport-6.1/703-03-v5.16-net-dsa-populate-supported_interfaces-member.patch new file mode 100644 index 0000000000..83d1f7ee7c --- /dev/null +++ b/target/linux/generic/backport-6.1/703-03-v5.16-net-dsa-populate-supported_interfaces-member.patch @@ -0,0 +1,63 @@ +From c07c6e8eb4b38bae921f9e2f108d1e7f8e14226e Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Marek=20Beh=C3=BAn?= +Date: Thu, 28 Oct 2021 18:00:14 +0100 +Subject: [PATCH] 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 +[tweaked patch and description to add more complete support -- rmk] +Signed-off-by: Russell King +Signed-off-by: Russell King (Oracle) +Signed-off-by: David S. Miller +--- + include/net/dsa.h | 2 ++ + net/dsa/port.c | 4 ++++ + net/dsa/slave.c | 4 ++++ + 3 files changed, 10 insertions(+) + +--- a/include/net/dsa.h ++++ b/include/net/dsa.h +@@ -626,6 +626,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); +--- a/net/dsa/port.c ++++ b/net/dsa/port.c +@@ -1172,6 +1172,10 @@ static int dsa_port_phylink_register(str + 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)) { +--- a/net/dsa/slave.c ++++ b/net/dsa/slave.c +@@ -1837,6 +1837,10 @@ static int dsa_slave_phy_setup(struct ne + 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)) { diff --git a/target/linux/generic/backport-6.1/703-04-v5.17-net-dsa-consolidate-phylink-creation.patch b/target/linux/generic/backport-6.1/703-04-v5.17-net-dsa-consolidate-phylink-creation.patch new file mode 100644 index 0000000000..8b58c83312 --- /dev/null +++ b/target/linux/generic/backport-6.1/703-04-v5.17-net-dsa-consolidate-phylink-creation.patch @@ -0,0 +1,149 @@ +From 21bd64bd717dedac96f53b668144cbe37d3c12d4 Mon Sep 17 00:00:00 2001 +From: "Russell King (Oracle)" +Date: Tue, 30 Nov 2021 13:09:55 +0000 +Subject: [PATCH] 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) +Reviewed-by: Marek Behún +Reviewed-by: Andrew Lunn +Signed-off-by: Jakub Kicinski +--- + net/dsa/dsa_priv.h | 2 +- + net/dsa/port.c | 44 ++++++++++++++++++++++++++++---------------- + net/dsa/slave.c | 19 +++---------------- + 3 files changed, 32 insertions(+), 33 deletions(-) + +--- a/net/dsa/dsa_priv.h ++++ b/net/dsa/dsa_priv.h +@@ -260,13 +260,13 @@ int dsa_port_mrp_add_ring_role(const str + 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) +--- a/net/dsa/port.c ++++ b/net/dsa/port.c +@@ -1076,7 +1076,7 @@ static void dsa_port_phylink_mac_link_up + 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_ph + .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(str + { + 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) { +--- a/net/dsa/slave.c ++++ b/net/dsa/slave.c +@@ -1817,14 +1817,9 @@ static int dsa_slave_phy_setup(struct ne + 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 ne + 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); diff --git a/target/linux/generic/backport-6.1/703-05-v5.17-net-dsa-replace-phylink_get_interfaces-with-phylink_.patch b/target/linux/generic/backport-6.1/703-05-v5.17-net-dsa-replace-phylink_get_interfaces-with-phylink_.patch new file mode 100644 index 0000000000..4cea5994da --- /dev/null +++ b/target/linux/generic/backport-6.1/703-05-v5.17-net-dsa-replace-phylink_get_interfaces-with-phylink_.patch @@ -0,0 +1,51 @@ +From 072eea6c22b2af680c3949e64f9adde278c71e68 Mon Sep 17 00:00:00 2001 +From: "Russell King (Oracle)" +Date: Tue, 30 Nov 2021 13:10:01 +0000 +Subject: [PATCH] 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) +Reviewed-by: Marek Behún +Reviewed-by: Andrew Lunn +Signed-off-by: Jakub Kicinski +--- + include/net/dsa.h | 4 ++-- + net/dsa/port.c | 5 ++--- + 2 files changed, 4 insertions(+), 5 deletions(-) + +--- a/include/net/dsa.h ++++ b/include/net/dsa.h +@@ -626,8 +626,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); +--- a/net/dsa/port.c ++++ b/net/dsa/port.c +@@ -1095,9 +1095,8 @@ int dsa_port_phylink_create(struct dsa_p + 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); diff --git a/target/linux/generic/backport-6.1/703-06-v5.18-net-dsa-add-support-for-phylink-mac_select_pcs.patch b/target/linux/generic/backport-6.1/703-06-v5.18-net-dsa-add-support-for-phylink-mac_select_pcs.patch new file mode 100644 index 0000000000..a28d14d27a --- /dev/null +++ b/target/linux/generic/backport-6.1/703-06-v5.18-net-dsa-add-support-for-phylink-mac_select_pcs.patch @@ -0,0 +1,59 @@ +From bde018222c6b084ac32933a9f933581dd83da18e Mon Sep 17 00:00:00 2001 +From: "Russell King (Oracle)" +Date: Thu, 17 Feb 2022 18:30:35 +0000 +Subject: [PATCH] net: dsa: add support for phylink mac_select_pcs() + +Add DSA support for the phylink mac_select_pcs() method so DSA drivers +can return provide phylink with the appropriate PCS for the PHY +interface mode. + +Signed-off-by: Russell King (Oracle) +Signed-off-by: David S. Miller +--- + include/net/dsa.h | 3 +++ + net/dsa/port.c | 15 +++++++++++++++ + 2 files changed, 18 insertions(+) + +--- a/include/net/dsa.h ++++ b/include/net/dsa.h +@@ -631,6 +631,9 @@ struct dsa_switch_ops { + void (*phylink_validate)(struct dsa_switch *ds, int port, + unsigned long *supported, + struct phylink_link_state *state); ++ struct phylink_pcs *(*phylink_mac_select_pcs)(struct dsa_switch *ds, ++ int port, ++ phy_interface_t iface); + int (*phylink_mac_link_state)(struct dsa_switch *ds, int port, + struct phylink_link_state *state); + void (*phylink_mac_config)(struct dsa_switch *ds, int port, +--- a/net/dsa/port.c ++++ b/net/dsa/port.c +@@ -1012,6 +1012,20 @@ static void dsa_port_phylink_mac_pcs_get + } + } + ++static struct phylink_pcs * ++dsa_port_phylink_mac_select_pcs(struct phylink_config *config, ++ phy_interface_t interface) ++{ ++ struct dsa_port *dp = container_of(config, struct dsa_port, pl_config); ++ struct dsa_switch *ds = dp->ds; ++ struct phylink_pcs *pcs = NULL; ++ ++ if (ds->ops->phylink_mac_select_pcs) ++ pcs = ds->ops->phylink_mac_select_pcs(ds, dp->index, interface); ++ ++ return pcs; ++} ++ + static void dsa_port_phylink_mac_config(struct phylink_config *config, + unsigned int mode, + const struct phylink_link_state *state) +@@ -1078,6 +1092,7 @@ static void dsa_port_phylink_mac_link_up + + static const struct phylink_mac_ops dsa_port_phylink_mac_ops = { + .validate = dsa_port_phylink_validate, ++ .mac_select_pcs = dsa_port_phylink_mac_select_pcs, + .mac_pcs_get_state = dsa_port_phylink_mac_pcs_get_state, + .mac_config = dsa_port_phylink_mac_config, + .mac_an_restart = dsa_port_phylink_mac_an_restart, diff --git a/target/linux/generic/backport-6.1/703-07-v5.16-net-phy-add-phy_interface_t-bitmap-support.patch b/target/linux/generic/backport-6.1/703-07-v5.16-net-phy-add-phy_interface_t-bitmap-support.patch new file mode 100644 index 0000000000..1a7817b0f9 --- /dev/null +++ b/target/linux/generic/backport-6.1/703-07-v5.16-net-phy-add-phy_interface_t-bitmap-support.patch @@ -0,0 +1,61 @@ +From 8e20f591f204f8db7f1182918f8e2285d3f589e0 Mon Sep 17 00:00:00 2001 +From: "Russell King (Oracle)" +Date: Tue, 26 Oct 2021 11:06:01 +0100 +Subject: [PATCH] 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) +Signed-off-by: David S. Miller +--- + include/linux/phy.h | 34 ++++++++++++++++++++++++++++++++++ + 1 file changed, 34 insertions(+) + +--- 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 + */ diff --git a/target/linux/generic/backport-6.1/703-08-v5.17-net-phylink-add-mac_select_pcs-method-to-phylink_mac.patch b/target/linux/generic/backport-6.1/703-08-v5.17-net-phylink-add-mac_select_pcs-method-to-phylink_mac.patch new file mode 100644 index 0000000000..e1cfc3f439 --- /dev/null +++ b/target/linux/generic/backport-6.1/703-08-v5.17-net-phylink-add-mac_select_pcs-method-to-phylink_mac.patch @@ -0,0 +1,197 @@ +From d1e86325af377129adb7fc6f34eb044ca6068b47 Mon Sep 17 00:00:00 2001 +From: "Russell King (Oracle)" +Date: Wed, 15 Dec 2021 15:34:15 +0000 +Subject: [PATCH] net: phylink: add mac_select_pcs() method to phylink_mac_ops + +mac_select_pcs() allows us to have an explicit point to query which +PCS the MAC wishes to use for a particular PHY interface mode, thereby +allowing us to add support to validate the link settings with the PCS. + +Phylink will also use this to select the PCS to be used during a major +configuration event without the MAC driver needing to call +phylink_set_pcs(). + +Note that if mac_select_pcs() is present, the supported_interfaces +bitmap must be filled in; this avoids mac_select_pcs() being called +with PHY_INTERFACE_MODE_NA when we want to get support for all +interface types. Phylink will return an error in phylink_create() +unless this condition is satisfied. + +Signed-off-by: Russell King (Oracle) +Signed-off-by: David S. Miller +--- + drivers/net/phy/phylink.c | 68 +++++++++++++++++++++++++++++++++------ + include/linux/phylink.h | 18 +++++++++++ + 2 files changed, 77 insertions(+), 9 deletions(-) + +--- a/drivers/net/phy/phylink.c ++++ b/drivers/net/phy/phylink.c +@@ -155,6 +155,23 @@ static const char *phylink_an_mode_str(u + return mode < ARRAY_SIZE(modestr) ? modestr[mode] : "unknown"; + } + ++static int phylink_validate_mac_and_pcs(struct phylink *pl, ++ unsigned long *supported, ++ struct phylink_link_state *state) ++{ ++ struct phylink_pcs *pcs; ++ ++ if (pl->mac_ops->mac_select_pcs) { ++ pcs = pl->mac_ops->mac_select_pcs(pl->config, state->interface); ++ if (IS_ERR(pcs)) ++ return PTR_ERR(pcs); ++ } ++ ++ pl->mac_ops->validate(pl->config, supported, state); ++ ++ return phylink_is_empty_linkmode(supported) ? -EINVAL : 0; ++} ++ + static int phylink_validate_any(struct phylink *pl, unsigned long *supported, + struct phylink_link_state *state) + { +@@ -170,9 +187,10 @@ static int phylink_validate_any(struct p + + 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); ++ if (!phylink_validate_mac_and_pcs(pl, s, &t)) { ++ linkmode_or(all_s, all_s, s); ++ linkmode_or(all_adv, all_adv, t.advertising); ++ } + } + } + +@@ -194,9 +212,7 @@ static int phylink_validate(struct phyli + return -EINVAL; + } + +- pl->mac_ops->validate(pl->config, supported, state); +- +- return phylink_is_empty_linkmode(supported) ? -EINVAL : 0; ++ return phylink_validate_mac_and_pcs(pl, supported, state); + } + + static int phylink_parse_fixedlink(struct phylink *pl, +@@ -486,10 +502,21 @@ static void phylink_mac_pcs_an_restart(s + static void phylink_major_config(struct phylink *pl, bool restart, + const struct phylink_link_state *state) + { ++ struct phylink_pcs *pcs = NULL; + int err; + + phylink_dbg(pl, "major config %s\n", phy_modes(state->interface)); + ++ if (pl->mac_ops->mac_select_pcs) { ++ pcs = pl->mac_ops->mac_select_pcs(pl->config, state->interface); ++ if (IS_ERR(pcs)) { ++ phylink_err(pl, ++ "mac_select_pcs unexpectedly failed: %pe\n", ++ pcs); ++ return; ++ } ++ } ++ + if (pl->mac_ops->mac_prepare) { + err = pl->mac_ops->mac_prepare(pl->config, pl->cur_link_an_mode, + state->interface); +@@ -500,6 +527,12 @@ static void phylink_major_config(struct + } + } + ++ /* If we have a new PCS, switch to the new PCS after preparing the MAC ++ * for the change. ++ */ ++ if (pcs) ++ phylink_set_pcs(pl, pcs); ++ + phylink_mac_config(pl, state); + + if (pl->pcs_ops) { +@@ -879,6 +912,14 @@ struct phylink *phylink_create(struct ph + struct phylink *pl; + int ret; + ++ /* Validate the supplied configuration */ ++ if (mac_ops->mac_select_pcs && ++ phy_interface_empty(config->supported_interfaces)) { ++ dev_err(config->dev, ++ "phylink: error: empty supported_interfaces but mac_select_pcs() method present\n"); ++ return ERR_PTR(-EINVAL); ++ } ++ + pl = kzalloc(sizeof(*pl), GFP_KERNEL); + if (!pl) + return ERR_PTR(-ENOMEM); +@@ -946,9 +987,10 @@ EXPORT_SYMBOL_GPL(phylink_create); + * @pl: a pointer to a &struct phylink returned from phylink_create() + * @pcs: a pointer to the &struct phylink_pcs + * +- * Bind the MAC PCS to phylink. This may be called after phylink_create(), +- * in mac_prepare() or mac_config() methods if it is desired to dynamically +- * change the PCS. ++ * Bind the MAC PCS to phylink. This may be called after phylink_create(). ++ * If it is desired to dynamically change the PCS, then the preferred method ++ * is to use mac_select_pcs(), but it may also be called in mac_prepare() ++ * or mac_config(). + * + * Please note that there are behavioural changes with the mac_config() + * callback if a PCS is present (denoting a newer setup) so removing a PCS +@@ -959,6 +1001,14 @@ void phylink_set_pcs(struct phylink *pl, + { + pl->pcs = pcs; + pl->pcs_ops = pcs->ops; ++ ++ if (!pl->phylink_disable_state && ++ pl->cfg_link_an_mode == MLO_AN_INBAND) { ++ if (pl->config->pcs_poll || pcs->poll) ++ mod_timer(&pl->link_poll, jiffies + HZ); ++ else ++ del_timer(&pl->link_poll); ++ } + } + EXPORT_SYMBOL_GPL(phylink_set_pcs); + +--- a/include/linux/phylink.h ++++ b/include/linux/phylink.h +@@ -84,6 +84,7 @@ struct phylink_config { + /** + * struct phylink_mac_ops - MAC operations structure. + * @validate: Validate and update the link configuration. ++ * @mac_select_pcs: Select a PCS for the interface mode. + * @mac_pcs_get_state: Read the current link state from the hardware. + * @mac_prepare: prepare for a major reconfiguration of the interface. + * @mac_config: configure the MAC for the selected mode and state. +@@ -98,6 +99,8 @@ struct phylink_mac_ops { + void (*validate)(struct phylink_config *config, + unsigned long *supported, + struct phylink_link_state *state); ++ struct phylink_pcs *(*mac_select_pcs)(struct phylink_config *config, ++ phy_interface_t interface); + void (*mac_pcs_get_state)(struct phylink_config *config, + struct phylink_link_state *state); + int (*mac_prepare)(struct phylink_config *config, unsigned int mode, +@@ -150,6 +153,21 @@ struct phylink_mac_ops { + */ + void validate(struct phylink_config *config, unsigned long *supported, + struct phylink_link_state *state); ++/** ++ * mac_select_pcs: Select a PCS for the interface mode. ++ * @config: a pointer to a &struct phylink_config. ++ * @interface: PHY interface mode for PCS ++ * ++ * Return the &struct phylink_pcs for the specified interface mode, or ++ * NULL if none is required, or an error pointer on error. ++ * ++ * This must not modify any state. It is used to query which PCS should ++ * be used. Phylink will use this during validation to ensure that the ++ * configuration is valid, and when setting a configuration to internally ++ * set the PCS that will be used. ++ */ ++struct phylink_pcs *mac_select_pcs(struct phylink_config *config, ++ phy_interface_t interface); + + /** + * mac_pcs_get_state() - Read the current inband link state from the hardware diff --git a/target/linux/generic/backport-6.1/703-09-v5.17-net-phylink-add-generic-validate-implementation.patch b/target/linux/generic/backport-6.1/703-09-v5.17-net-phylink-add-generic-validate-implementation.patch new file mode 100644 index 0000000000..73c8b414da --- /dev/null +++ b/target/linux/generic/backport-6.1/703-09-v5.17-net-phylink-add-generic-validate-implementation.patch @@ -0,0 +1,341 @@ +From 34ae2c09d46a2d0abd907e139b466f798e4095a8 Mon Sep 17 00:00:00 2001 +From: "Russell King (Oracle)" +Date: Mon, 15 Nov 2021 10:00:27 +0000 +Subject: [PATCH] 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) +Signed-off-by: David S. Miller +--- + drivers/net/phy/phylink.c | 252 ++++++++++++++++++++++++++++++++++++++ + include/linux/phylink.h | 31 +++++ + 2 files changed, 283 insertions(+) + +--- a/drivers/net/phy/phylink.c ++++ b/drivers/net/phy/phylink.c +@@ -172,6 +172,258 @@ static int phylink_validate_mac_and_pcs( + return phylink_is_empty_linkmode(supported) ? -EINVAL : 0; + } + ++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) + { +--- 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; + }; + + /** +@@ -460,6 +485,12 @@ void pcs_link_up(struct phylink_pcs *pcs + 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); diff --git a/target/linux/generic/backport-6.1/703-10-v5.16-net-dsa-introduce-helpers-for-iterating-through-port.patch b/target/linux/generic/backport-6.1/703-10-v5.16-net-dsa-introduce-helpers-for-iterating-through-port.patch new file mode 100644 index 0000000000..a55623519c --- /dev/null +++ b/target/linux/generic/backport-6.1/703-10-v5.16-net-dsa-introduce-helpers-for-iterating-through-port.patch @@ -0,0 +1,68 @@ +From 82b318983c515f29b8b3a0dad9f6a5fe8a68a7f4 Mon Sep 17 00:00:00 2001 +From: Vladimir Oltean +Date: Wed, 20 Oct 2021 20:49:49 +0300 +Subject: [PATCH] net: dsa: introduce helpers for iterating through ports using + dp + +Since the DSA conversion from the ds->ports array into the dst->ports +list, the DSA API has encouraged driver writers, as well as the core +itself, to write inefficient code. + +Currently, code that wants to filter by a specific type of port when +iterating, like {!unused, user, cpu, dsa}, uses the dsa_is_*_port helper. +Under the hood, this uses dsa_to_port which iterates again through +dst->ports. But the driver iterates through the port list already, so +the complexity is quadratic for the typical case of a single-switch +tree. + +This patch introduces some iteration helpers where the iterator is +already a struct dsa_port *dp, so that the other variant of the +filtering functions, dsa_port_is_{unused,user,cpu_dsa}, can be used +directly on the iterator. This eliminates the second lookup. + +These functions can be used both by the core and by drivers. + +Signed-off-by: Vladimir Oltean +Reviewed-by: Florian Fainelli +Signed-off-by: David S. Miller +--- + include/net/dsa.h | 28 ++++++++++++++++++++++++++++ + 1 file changed, 28 insertions(+) + +--- a/include/net/dsa.h ++++ b/include/net/dsa.h +@@ -476,6 +476,34 @@ static inline bool dsa_is_user_port(stru + return dsa_to_port(ds, p)->type == DSA_PORT_TYPE_USER; + } + ++#define dsa_tree_for_each_user_port(_dp, _dst) \ ++ list_for_each_entry((_dp), &(_dst)->ports, list) \ ++ if (dsa_port_is_user((_dp))) ++ ++#define dsa_switch_for_each_port(_dp, _ds) \ ++ list_for_each_entry((_dp), &(_ds)->dst->ports, list) \ ++ if ((_dp)->ds == (_ds)) ++ ++#define dsa_switch_for_each_port_safe(_dp, _next, _ds) \ ++ list_for_each_entry_safe((_dp), (_next), &(_ds)->dst->ports, list) \ ++ if ((_dp)->ds == (_ds)) ++ ++#define dsa_switch_for_each_port_continue_reverse(_dp, _ds) \ ++ list_for_each_entry_continue_reverse((_dp), &(_ds)->dst->ports, list) \ ++ if ((_dp)->ds == (_ds)) ++ ++#define dsa_switch_for_each_available_port(_dp, _ds) \ ++ dsa_switch_for_each_port((_dp), (_ds)) \ ++ if (!dsa_port_is_unused((_dp))) ++ ++#define dsa_switch_for_each_user_port(_dp, _ds) \ ++ dsa_switch_for_each_port((_dp), (_ds)) \ ++ if (dsa_port_is_user((_dp))) ++ ++#define dsa_switch_for_each_cpu_port(_dp, _ds) \ ++ dsa_switch_for_each_port((_dp), (_ds)) \ ++ if (dsa_port_is_cpu((_dp))) ++ + static inline u32 dsa_user_ports(struct dsa_switch *ds) + { + u32 mask = 0; diff --git a/target/linux/generic/backport-6.1/703-11-v5.17-net-phylink-add-pcs_validate-method.patch b/target/linux/generic/backport-6.1/703-11-v5.17-net-phylink-add-pcs_validate-method.patch new file mode 100644 index 0000000000..add2e6e352 --- /dev/null +++ b/target/linux/generic/backport-6.1/703-11-v5.17-net-phylink-add-pcs_validate-method.patch @@ -0,0 +1,106 @@ +From 0d22d4b626a4eaa3196019092eb6c1919e9f8caa Mon Sep 17 00:00:00 2001 +From: "Russell King (Oracle)" +Date: Wed, 15 Dec 2021 15:34:20 +0000 +Subject: [PATCH] net: phylink: add pcs_validate() method + +Add a hook for PCS to validate the link parameters. This avoids MAC +drivers having to have knowledge of their PCS in their validate() +method, thereby allowing several MAC drivers to be simplfied. + +Signed-off-by: Russell King (Oracle) +Signed-off-by: David S. Miller +--- + drivers/net/phy/phylink.c | 31 +++++++++++++++++++++++++++++++ + include/linux/phylink.h | 20 ++++++++++++++++++++ + 2 files changed, 51 insertions(+) + +--- a/drivers/net/phy/phylink.c ++++ b/drivers/net/phy/phylink.c +@@ -160,13 +160,44 @@ static int phylink_validate_mac_and_pcs( + struct phylink_link_state *state) + { + struct phylink_pcs *pcs; ++ int ret; + ++ /* Get the PCS for this interface mode */ + if (pl->mac_ops->mac_select_pcs) { + pcs = pl->mac_ops->mac_select_pcs(pl->config, state->interface); + if (IS_ERR(pcs)) + return PTR_ERR(pcs); ++ } else { ++ pcs = pl->pcs; + } + ++ if (pcs) { ++ /* The PCS, if present, must be setup before phylink_create() ++ * has been called. If the ops is not initialised, print an ++ * error and backtrace rather than oopsing the kernel. ++ */ ++ if (!pcs->ops) { ++ phylink_err(pl, "interface %s: uninitialised PCS\n", ++ phy_modes(state->interface)); ++ dump_stack(); ++ return -EINVAL; ++ } ++ ++ /* Validate the link parameters with the PCS */ ++ if (pcs->ops->pcs_validate) { ++ ret = pcs->ops->pcs_validate(pcs, supported, state); ++ if (ret < 0 || phylink_is_empty_linkmode(supported)) ++ return -EINVAL; ++ ++ /* Ensure the advertising mask is a subset of the ++ * supported mask. ++ */ ++ linkmode_and(state->advertising, state->advertising, ++ supported); ++ } ++ } ++ ++ /* Then validate the link parameters with the MAC */ + pl->mac_ops->validate(pl->config, supported, state); + + return phylink_is_empty_linkmode(supported) ? -EINVAL : 0; +--- a/include/linux/phylink.h ++++ b/include/linux/phylink.h +@@ -396,6 +396,7 @@ struct phylink_pcs { + + /** + * struct phylink_pcs_ops - MAC PCS operations structure. ++ * @pcs_validate: validate the link configuration. + * @pcs_get_state: read the current MAC PCS link state from the hardware. + * @pcs_config: configure the MAC PCS for the selected mode and state. + * @pcs_an_restart: restart 802.3z BaseX autonegotiation. +@@ -403,6 +404,8 @@ struct phylink_pcs { + * (where necessary). + */ + struct phylink_pcs_ops { ++ int (*pcs_validate)(struct phylink_pcs *pcs, unsigned long *supported, ++ const struct phylink_link_state *state); + void (*pcs_get_state)(struct phylink_pcs *pcs, + struct phylink_link_state *state); + int (*pcs_config)(struct phylink_pcs *pcs, unsigned int mode, +@@ -416,6 +419,23 @@ struct phylink_pcs_ops { + + #if 0 /* For kernel-doc purposes only. */ + /** ++ * pcs_validate() - validate the link configuration. ++ * @pcs: a pointer to a &struct phylink_pcs. ++ * @supported: ethtool bitmask for supported link modes. ++ * @state: a const pointer to a &struct phylink_link_state. ++ * ++ * Validate the interface mode, and advertising's autoneg bit, removing any ++ * media ethtool link modes that would not be supportable from the supported ++ * mask. Phylink will propagate the changes to the advertising mask. See the ++ * &struct phylink_mac_ops validate() method. ++ * ++ * Returns -EINVAL if the interface mode/autoneg mode is not supported. ++ * Returns non-zero positive if the link state can be supported. ++ */ ++int pcs_validate(struct phylink_pcs *pcs, unsigned long *supported, ++ const struct phylink_link_state *state); ++ ++/** + * pcs_get_state() - Read the current inband link state from the hardware + * @pcs: a pointer to a &struct phylink_pcs. + * @state: a pointer to a &struct phylink_link_state. diff --git a/target/linux/generic/backport-6.1/703-12-v5.17-net-phylink-add-legacy_pre_march2020-indicator.patch b/target/linux/generic/backport-6.1/703-12-v5.17-net-phylink-add-legacy_pre_march2020-indicator.patch new file mode 100644 index 0000000000..6fbde12507 --- /dev/null +++ b/target/linux/generic/backport-6.1/703-12-v5.17-net-phylink-add-legacy_pre_march2020-indicator.patch @@ -0,0 +1,43 @@ +From 3e5b1feccea7db576353ffc302f78d522e4116e6 Mon Sep 17 00:00:00 2001 +From: "Russell King (Oracle)" +Date: Thu, 9 Dec 2021 13:11:32 +0000 +Subject: [PATCH] net: phylink: add legacy_pre_march2020 indicator + +Add a boolean to phylink_config to indicate whether a driver has not +been updated for the changes in commit 7cceb599d15d ("net: phylink: +avoid mac_config calls"), and thus are reliant on the old behaviour. + +We were currently keying the phylink behaviour on the presence of a +PCS, but this is sub-optimal for modern drivers that may not have a +PCS. + +This commit merely introduces the new flag, but does not add any use, +since we need all legacy drivers to set this flag before it can be +used. Once these legacy drivers have been updated, we can remove this +flag. + +Signed-off-by: Russell King (Oracle) +Signed-off-by: Jakub Kicinski +--- + include/linux/phylink.h | 3 +++ + 1 file changed, 3 insertions(+) + +--- a/include/linux/phylink.h ++++ b/include/linux/phylink.h +@@ -84,6 +84,8 @@ enum phylink_op_type { + * struct phylink_config - PHYLINK configuration structure + * @dev: a pointer to a struct device associated with the MAC + * @type: operation type of PHYLINK instance ++ * @legacy_pre_march2020: driver has not been updated for March 2020 updates ++ * (See commit 7cceb599d15d ("net: phylink: avoid mac_config calls") + * @pcs_poll: MAC PCS cannot provide link change interrupt + * @poll_fixed_state: if true, starts link_poll, + * if MAC link is at %MLO_AN_FIXED mode. +@@ -97,6 +99,7 @@ enum phylink_op_type { + struct phylink_config { + struct device *dev; + enum phylink_op_type type; ++ bool legacy_pre_march2020; + bool pcs_poll; + bool poll_fixed_state; + bool ovr_an_inband; diff --git a/target/linux/generic/backport-6.1/703-13-v5.17-net-dsa-mark-DSA-phylink-as-legacy_pre_march2020.patch b/target/linux/generic/backport-6.1/703-13-v5.17-net-dsa-mark-DSA-phylink-as-legacy_pre_march2020.patch new file mode 100644 index 0000000000..dff0db5db6 --- /dev/null +++ b/target/linux/generic/backport-6.1/703-13-v5.17-net-dsa-mark-DSA-phylink-as-legacy_pre_march2020.patch @@ -0,0 +1,36 @@ +From 0a9f0794d9bd67e590a9488afe87fbb0419d9539 Mon Sep 17 00:00:00 2001 +From: "Russell King (Oracle)" +Date: Thu, 9 Dec 2021 13:11:38 +0000 +Subject: [PATCH] net: dsa: mark DSA phylink as legacy_pre_march2020 + +The majority of DSA drivers do not make use of the PCS support, and +thus operate in legacy mode. In order to preserve this behaviour in +future, we need to set the legacy_pre_march2020 flag so phylink knows +this may require the legacy calls. + +There are some DSA drivers that do make use of PCS support, and these +will continue operating as before - legacy_pre_march2020 will not +prevent split-PCS support enabling the newer phylink behaviour. + +Signed-off-by: Russell King (Oracle) +Signed-off-by: Jakub Kicinski +--- + net/dsa/port.c | 7 +++++++ + 1 file changed, 7 insertions(+) + +--- a/net/dsa/port.c ++++ b/net/dsa/port.c +@@ -1110,6 +1110,13 @@ int dsa_port_phylink_create(struct dsa_p + if (err) + mode = PHY_INTERFACE_MODE_NA; + ++ /* Presence of phylink_mac_link_state or phylink_mac_an_restart is ++ * an indicator of a legacy phylink driver. ++ */ ++ if (ds->ops->phylink_mac_link_state || ++ ds->ops->phylink_mac_an_restart) ++ dp->pl_config.legacy_pre_march2020 = true; ++ + if (ds->ops->phylink_get_caps) + ds->ops->phylink_get_caps(ds, dp->index, &dp->pl_config); + diff --git a/target/linux/generic/backport-6.1/703-14-v5.17-net-phylink-use-legacy_pre_march2020.patch b/target/linux/generic/backport-6.1/703-14-v5.17-net-phylink-use-legacy_pre_march2020.patch new file mode 100644 index 0000000000..361fa10d4d --- /dev/null +++ b/target/linux/generic/backport-6.1/703-14-v5.17-net-phylink-use-legacy_pre_march2020.patch @@ -0,0 +1,115 @@ +From 001f4261fe4d5ae710cf1f445b6cae6d9d3ae26e Mon Sep 17 00:00:00 2001 +From: "Russell King (Oracle)" +Date: Thu, 9 Dec 2021 13:11:48 +0000 +Subject: [PATCH] net: phylink: use legacy_pre_march2020 + +Use the legacy flag to indicate whether we should operate in legacy +mode. This allows us to stop using the presence of a PCS as an +indicator to the age of the phylink user, and make PCS presence +optional. + +Legacy mode involves: +1) calling mac_config() whenever the link comes up +2) calling mac_config() whenever the inband advertisement changes, + possibly followed by a call to mac_an_restart() +3) making use of mac_an_restart() +4) making use of mac_pcs_get_state() + +All the above functionality was moved to a seperate "PCS" block of +operations in March 2020. + +Update the documents to indicate that the differences that this flag +makes. + +Signed-off-by: Russell King (Oracle) +Signed-off-by: Jakub Kicinski +--- + drivers/net/phy/phylink.c | 12 ++++++------ + include/linux/phylink.h | 17 +++++++++++++++++ + 2 files changed, 23 insertions(+), 6 deletions(-) + +--- a/drivers/net/phy/phylink.c ++++ b/drivers/net/phy/phylink.c +@@ -777,7 +777,7 @@ static void phylink_mac_pcs_an_restart(s + phylink_autoneg_inband(pl->cur_link_an_mode)) { + if (pl->pcs_ops) + pl->pcs_ops->pcs_an_restart(pl->pcs); +- else ++ else if (pl->config->legacy_pre_march2020) + pl->mac_ops->mac_an_restart(pl->config); + } + } +@@ -855,7 +855,7 @@ static int phylink_change_inband_advert( + if (test_bit(PHYLINK_DISABLE_STOPPED, &pl->phylink_disable_state)) + return 0; + +- if (!pl->pcs_ops) { ++ if (!pl->pcs_ops && pl->config->legacy_pre_march2020) { + /* Legacy method */ + phylink_mac_config(pl, &pl->link_config); + phylink_mac_pcs_an_restart(pl); +@@ -900,7 +900,8 @@ static void phylink_mac_pcs_get_state(st + + if (pl->pcs_ops) + pl->pcs_ops->pcs_get_state(pl->pcs, state); +- else if (pl->mac_ops->mac_pcs_get_state) ++ else if (pl->mac_ops->mac_pcs_get_state && ++ pl->config->legacy_pre_march2020) + pl->mac_ops->mac_pcs_get_state(pl->config, state); + else + state->link = 0; +@@ -1094,12 +1095,11 @@ static void phylink_resolve(struct work_ + } + phylink_major_config(pl, false, &link_state); + pl->link_config.interface = link_state.interface; +- } else if (!pl->pcs_ops) { ++ } else if (!pl->pcs_ops && pl->config->legacy_pre_march2020) { + /* The interface remains unchanged, only the speed, + * duplex or pause settings have changed. Call the + * old mac_config() method to configure the MAC/PCS +- * only if we do not have a PCS installed (an +- * unconverted user.) ++ * only if we do not have a legacy MAC driver. + */ + phylink_mac_config(pl, &link_state); + } +--- a/include/linux/phylink.h ++++ b/include/linux/phylink.h +@@ -208,6 +208,10 @@ struct phylink_pcs *mac_select_pcs(struc + * negotiation completion state in @state->an_complete, and link up state + * in @state->link. If possible, @state->lp_advertising should also be + * populated. ++ * ++ * Note: This is a legacy method. This function will not be called unless ++ * legacy_pre_march2020 is set in &struct phylink_config and there is no ++ * PCS attached. + */ + void mac_pcs_get_state(struct phylink_config *config, + struct phylink_link_state *state); +@@ -248,6 +252,15 @@ int mac_prepare(struct phylink_config *c + * guaranteed to be correct, and so any mac_config() implementation must + * never reference these fields. + * ++ * Note: For legacy March 2020 drivers (drivers with legacy_pre_march2020 set ++ * in their &phylnk_config and which don't have a PCS), this function will be ++ * called on each link up event, and to also change the in-band advert. For ++ * non-legacy drivers, it will only be called to reconfigure the MAC for a ++ * "major" change in e.g. interface mode. It will not be called for changes ++ * in speed, duplex or pause modes or to change the in-band advertisement. ++ * In any case, it is strongly preferred that speed, duplex and pause settings ++ * are handled in the mac_link_up() method and not in this method. ++ * + * (this requires a rewrite - please refer to mac_link_up() for situations + * where the PCS and MAC are not tightly integrated.) + * +@@ -332,6 +345,10 @@ int mac_finish(struct phylink_config *co + /** + * mac_an_restart() - restart 802.3z BaseX autonegotiation + * @config: a pointer to a &struct phylink_config. ++ * ++ * Note: This is a legacy method. This function will not be called unless ++ * legacy_pre_march2020 is set in &struct phylink_config and there is no ++ * PCS attached. + */ + void mac_an_restart(struct phylink_config *config); + diff --git a/target/linux/generic/backport-6.1/703-15-v5.18-net-phy-phylink-fix-DSA-mac_select_pcs-introduction.patch b/target/linux/generic/backport-6.1/703-15-v5.18-net-phy-phylink-fix-DSA-mac_select_pcs-introduction.patch new file mode 100644 index 0000000000..4ab90fafe1 --- /dev/null +++ b/target/linux/generic/backport-6.1/703-15-v5.18-net-phy-phylink-fix-DSA-mac_select_pcs-introduction.patch @@ -0,0 +1,88 @@ +From 1054457006d4a14de4ae4132030e33d7eedaeba1 Mon Sep 17 00:00:00 2001 +From: "Russell King (Oracle)" +Date: Mon, 21 Feb 2022 17:10:52 +0000 +Subject: [PATCH] net: phy: phylink: fix DSA mac_select_pcs() introduction + +Vladimir Oltean reports that probing on DSA drivers that aren't yet +populating supported_interfaces now fails. Fix this by allowing +phylink to detect whether DSA actually provides an underlying +mac_select_pcs() implementation. + +Reported-by: Vladimir Oltean +Fixes: bde018222c6b ("net: dsa: add support for phylink mac_select_pcs()") +Signed-off-by: Russell King (Oracle) +Tested-by: Vladimir Oltean +Link: https://lore.kernel.org/r/E1nMCD6-00A0wC-FG@rmk-PC.armlinux.org.uk +Signed-off-by: Jakub Kicinski +--- + drivers/net/phy/phylink.c | 14 +++++++++++--- + net/dsa/port.c | 2 +- + 2 files changed, 12 insertions(+), 4 deletions(-) + +--- a/drivers/net/phy/phylink.c ++++ b/drivers/net/phy/phylink.c +@@ -74,6 +74,7 @@ struct phylink { + struct work_struct resolve; + + bool mac_link_dropped; ++ bool using_mac_select_pcs; + + struct sfp_bus *sfp_bus; + bool sfp_may_have_phy; +@@ -163,7 +164,7 @@ static int phylink_validate_mac_and_pcs( + int ret; + + /* Get the PCS for this interface mode */ +- if (pl->mac_ops->mac_select_pcs) { ++ if (pl->using_mac_select_pcs) { + pcs = pl->mac_ops->mac_select_pcs(pl->config, state->interface); + if (IS_ERR(pcs)) + return PTR_ERR(pcs); +@@ -790,7 +791,7 @@ static void phylink_major_config(struct + + phylink_dbg(pl, "major config %s\n", phy_modes(state->interface)); + +- if (pl->mac_ops->mac_select_pcs) { ++ if (pl->using_mac_select_pcs) { + pcs = pl->mac_ops->mac_select_pcs(pl->config, state->interface); + if (IS_ERR(pcs)) { + phylink_err(pl, +@@ -1192,11 +1193,17 @@ struct phylink *phylink_create(struct ph + phy_interface_t iface, + const struct phylink_mac_ops *mac_ops) + { ++ bool using_mac_select_pcs = false; + struct phylink *pl; + int ret; + +- /* Validate the supplied configuration */ + if (mac_ops->mac_select_pcs && ++ mac_ops->mac_select_pcs(config, PHY_INTERFACE_MODE_NA) != ++ ERR_PTR(-EOPNOTSUPP)) ++ using_mac_select_pcs = true; ++ ++ /* Validate the supplied configuration */ ++ if (using_mac_select_pcs && + phy_interface_empty(config->supported_interfaces)) { + dev_err(config->dev, + "phylink: error: empty supported_interfaces but mac_select_pcs() method present\n"); +@@ -1220,6 +1227,7 @@ struct phylink *phylink_create(struct ph + return ERR_PTR(-EINVAL); + } + ++ pl->using_mac_select_pcs = using_mac_select_pcs; + pl->phy_state.interface = iface; + pl->link_interface = iface; + if (iface == PHY_INTERFACE_MODE_MOCA) +--- a/net/dsa/port.c ++++ b/net/dsa/port.c +@@ -1017,8 +1017,8 @@ dsa_port_phylink_mac_select_pcs(struct p + phy_interface_t interface) + { + struct dsa_port *dp = container_of(config, struct dsa_port, pl_config); ++ struct phylink_pcs *pcs = ERR_PTR(-EOPNOTSUPP); + struct dsa_switch *ds = dp->ds; +- struct phylink_pcs *pcs = NULL; + + if (ds->ops->phylink_mac_select_pcs) + pcs = ds->ops->phylink_mac_select_pcs(ds, dp->index, interface); diff --git a/target/linux/generic/backport-6.1/703-16-v5.16-net-mvneta-populate-supported_interfaces-member.patch b/target/linux/generic/backport-6.1/703-16-v5.16-net-mvneta-populate-supported_interfaces-member.patch new file mode 100644 index 0000000000..f21fa4b277 --- /dev/null +++ b/target/linux/generic/backport-6.1/703-16-v5.16-net-mvneta-populate-supported_interfaces-member.patch @@ -0,0 +1,48 @@ +From fdedb695e6a8657302341cda81d519ef04f9acaa Mon Sep 17 00:00:00 2001 +From: Russell King +Date: Wed, 27 Oct 2021 10:03:43 +0100 +Subject: [PATCH] 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 +Signed-off-by: David S. Miller +--- + drivers/net/ethernet/marvell/mvneta.c | 25 +++++++++++++++++++++++++ + 1 file changed, 25 insertions(+) + +--- a/drivers/net/ethernet/marvell/mvneta.c ++++ b/drivers/net/ethernet/marvell/mvneta.c +@@ -5180,6 +5180,31 @@ static int mvneta_probe(struct platform_ + + 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); diff --git a/target/linux/generic/backport-6.1/703-17-v5.16-net-mvneta-remove-interface-checks-in-mvneta_validat.patch b/target/linux/generic/backport-6.1/703-17-v5.16-net-mvneta-remove-interface-checks-in-mvneta_validat.patch new file mode 100644 index 0000000000..e287e39d6a --- /dev/null +++ b/target/linux/generic/backport-6.1/703-17-v5.16-net-mvneta-remove-interface-checks-in-mvneta_validat.patch @@ -0,0 +1,35 @@ +From d9ca72807ecb236f679b960c70ef5b7d4a5f0222 Mon Sep 17 00:00:00 2001 +From: "Russell King (Oracle)" +Date: Wed, 27 Oct 2021 10:03:48 +0100 +Subject: [PATCH] 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) +Signed-off-by: David S. Miller +--- + drivers/net/ethernet/marvell/mvneta.c | 11 ++--------- + 1 file changed, 2 insertions(+), 9 deletions(-) + +--- a/drivers/net/ethernet/marvell/mvneta.c ++++ b/drivers/net/ethernet/marvell/mvneta.c +@@ -3833,15 +3833,8 @@ static void mvneta_validate(struct phyli + * "Bit 2 Field InBandAnEn In-band Auto-Negotiation enable. ... + * When = 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; + } diff --git a/target/linux/generic/backport-6.1/703-18-v5.16-net-mvneta-drop-use-of-phylink_helper_basex_speed.patch b/target/linux/generic/backport-6.1/703-18-v5.16-net-mvneta-drop-use-of-phylink_helper_basex_speed.patch new file mode 100644 index 0000000000..9121612bf8 --- /dev/null +++ b/target/linux/generic/backport-6.1/703-18-v5.16-net-mvneta-drop-use-of-phylink_helper_basex_speed.patch @@ -0,0 +1,55 @@ +From 099cbfa286ab937d8213c2dc5c0b401969b78042 Mon Sep 17 00:00:00 2001 +From: "Russell King (Oracle)" +Date: Wed, 27 Oct 2021 10:03:53 +0100 +Subject: [PATCH] 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) +Signed-off-by: David S. Miller +--- + drivers/net/ethernet/marvell/mvneta.c | 12 +++--------- + 1 file changed, 3 insertions(+), 9 deletions(-) + +--- a/drivers/net/ethernet/marvell/mvneta.c ++++ b/drivers/net/ethernet/marvell/mvneta.c +@@ -3824,8 +3824,6 @@ static void mvneta_validate(struct phyli + 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. +@@ -3847,11 +3845,12 @@ static void mvneta_validate(struct phyli + 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); + } +@@ -3866,11 +3865,6 @@ static void mvneta_validate(struct phyli + + 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, diff --git a/target/linux/generic/backport-6.1/703-19-v5.17-net-mvneta-use-phylink_generic_validate.patch b/target/linux/generic/backport-6.1/703-19-v5.17-net-mvneta-use-phylink_generic_validate.patch new file mode 100644 index 0000000000..209dfbc0de --- /dev/null +++ b/target/linux/generic/backport-6.1/703-19-v5.17-net-mvneta-use-phylink_generic_validate.patch @@ -0,0 +1,72 @@ +From 02a0988b98930491db95966fb8086072e47dabb6 Mon Sep 17 00:00:00 2001 +From: "Russell King (Oracle)" +Date: Mon, 15 Nov 2021 10:00:32 +0000 +Subject: [PATCH] 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) +Signed-off-by: David S. Miller +--- + drivers/net/ethernet/marvell/mvneta.c | 34 ++++----------------------- + 1 file changed, 4 insertions(+), 30 deletions(-) + +--- a/drivers/net/ethernet/marvell/mvneta.c ++++ b/drivers/net/ethernet/marvell/mvneta.c +@@ -3824,8 +3824,6 @@ static void mvneta_validate(struct phyli + 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. ... +@@ -3837,34 +3835,7 @@ static void mvneta_validate(struct phyli + 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, +@@ -5167,6 +5138,9 @@ static int mvneta_probe(struct platform_ + + 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); diff --git a/target/linux/generic/backport-6.1/703-20-v5.17-net-mvneta-mark-as-a-legacy_pre_march2020-driver.patch b/target/linux/generic/backport-6.1/703-20-v5.17-net-mvneta-mark-as-a-legacy_pre_march2020-driver.patch new file mode 100644 index 0000000000..31717565bf --- /dev/null +++ b/target/linux/generic/backport-6.1/703-20-v5.17-net-mvneta-mark-as-a-legacy_pre_march2020-driver.patch @@ -0,0 +1,29 @@ +From 2106be4fdf3223d9c5bd485e6ef094139e3197ba Mon Sep 17 00:00:00 2001 +From: "Russell King (Oracle)" +Date: Sun, 12 Dec 2021 13:01:21 +0000 +Subject: [PATCH] net: mvneta: mark as a legacy_pre_march2020 driver +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +mvneta provides mac_an_restart and mac_pcs_get_state methods, so needs +to be marked as a legacy driver. Marek spotted that mvneta had stopped +working in 2500base-X mode - thanks for reporting. + +Reported-by: Marek Behún +Signed-off-by: Russell King (Oracle) +Signed-off-by: David S. Miller +--- + drivers/net/ethernet/marvell/mvneta.c | 1 + + 1 file changed, 1 insertion(+) + +--- a/drivers/net/ethernet/marvell/mvneta.c ++++ b/drivers/net/ethernet/marvell/mvneta.c +@@ -5138,6 +5138,7 @@ static int mvneta_probe(struct platform_ + + pp->phylink_config.dev = &dev->dev; + pp->phylink_config.type = PHYLINK_NETDEV; ++ pp->phylink_config.legacy_pre_march2020 = true; + pp->phylink_config.mac_capabilities = MAC_SYM_PAUSE | MAC_10 | + MAC_100 | MAC_1000FD | MAC_2500FD; + diff --git a/target/linux/generic/backport-6.1/704-01-v5.17-net-mtk_eth_soc-populate-supported_interfaces-member.patch b/target/linux/generic/backport-6.1/704-01-v5.17-net-mtk_eth_soc-populate-supported_interfaces-member.patch new file mode 100644 index 0000000000..43e1f9cc31 --- /dev/null +++ b/target/linux/generic/backport-6.1/704-01-v5.17-net-mtk_eth_soc-populate-supported_interfaces-member.patch @@ -0,0 +1,43 @@ +From 83800d29f0c578e82554e7d4c6bfdbdf9b6cf428 Mon Sep 17 00:00:00 2001 +From: "Russell King (Oracle)" +Date: Tue, 16 Nov 2021 10:06:43 +0000 +Subject: [PATCH] net: mtk_eth_soc: populate supported_interfaces member + +Populate the phy interface mode bitmap for the Mediatek driver with +interfaces modes supported by the MAC. + +Signed-off-by: Russell King (Oracle) +Signed-off-by: David S. Miller +--- + drivers/net/ethernet/mediatek/mtk_eth_soc.c | 20 ++++++++++++++++++++ + 1 file changed, 20 insertions(+) + +--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c ++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c +@@ -3352,6 +3352,26 @@ static int mtk_add_mac(struct mtk_eth *e + + mac->phylink_config.dev = ð->netdev[id]->dev; + mac->phylink_config.type = PHYLINK_NETDEV; ++ __set_bit(PHY_INTERFACE_MODE_MII, ++ mac->phylink_config.supported_interfaces); ++ __set_bit(PHY_INTERFACE_MODE_GMII, ++ mac->phylink_config.supported_interfaces); ++ ++ if (MTK_HAS_CAPS(mac->hw->soc->caps, MTK_RGMII)) ++ phy_interface_set_rgmii(mac->phylink_config.supported_interfaces); ++ ++ if (MTK_HAS_CAPS(mac->hw->soc->caps, MTK_TRGMII) && !mac->id) ++ __set_bit(PHY_INTERFACE_MODE_TRGMII, ++ mac->phylink_config.supported_interfaces); ++ ++ if (MTK_HAS_CAPS(mac->hw->soc->caps, MTK_SGMII)) { ++ __set_bit(PHY_INTERFACE_MODE_SGMII, ++ mac->phylink_config.supported_interfaces); ++ __set_bit(PHY_INTERFACE_MODE_1000BASEX, ++ mac->phylink_config.supported_interfaces); ++ __set_bit(PHY_INTERFACE_MODE_2500BASEX, ++ mac->phylink_config.supported_interfaces); ++ } + + phylink = phylink_create(&mac->phylink_config, + of_fwnode_handle(mac->of_node), diff --git a/target/linux/generic/backport-6.1/704-02-v5.17-net-mtk_eth_soc-remove-interface-checks-in-mtk_valid.patch b/target/linux/generic/backport-6.1/704-02-v5.17-net-mtk_eth_soc-remove-interface-checks-in-mtk_valid.patch new file mode 100644 index 0000000000..05a84c4f67 --- /dev/null +++ b/target/linux/generic/backport-6.1/704-02-v5.17-net-mtk_eth_soc-remove-interface-checks-in-mtk_valid.patch @@ -0,0 +1,75 @@ +From db81ca153814475d7e07365d46a4d1134bd122e2 Mon Sep 17 00:00:00 2001 +From: "Russell King (Oracle)" +Date: Tue, 16 Nov 2021 10:06:48 +0000 +Subject: [PATCH] net: mtk_eth_soc: remove interface checks in mtk_validate() + +As phylink checks the interface mode against the supported_interfaces +bitmap, we no longer need to validate the interface mode, nor handle +PHY_INTERFACE_MODE_NA in the validation function. Remove these to +simplify the implementation. + +Signed-off-by: Russell King (Oracle) +Signed-off-by: David S. Miller +--- + drivers/net/ethernet/mediatek/mtk_eth_soc.c | 34 --------------------- + 1 file changed, 34 deletions(-) + +--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c ++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c +@@ -567,24 +567,8 @@ static void mtk_validate(struct phylink_ + unsigned long *supported, + struct phylink_link_state *state) + { +- struct mtk_mac *mac = container_of(config, struct mtk_mac, +- phylink_config); + __ETHTOOL_DECLARE_LINK_MODE_MASK(mask) = { 0, }; + +- if (state->interface != PHY_INTERFACE_MODE_NA && +- state->interface != PHY_INTERFACE_MODE_MII && +- state->interface != PHY_INTERFACE_MODE_GMII && +- !(MTK_HAS_CAPS(mac->hw->soc->caps, MTK_RGMII) && +- phy_interface_mode_is_rgmii(state->interface)) && +- !(MTK_HAS_CAPS(mac->hw->soc->caps, MTK_TRGMII) && +- !mac->id && state->interface == PHY_INTERFACE_MODE_TRGMII) && +- !(MTK_HAS_CAPS(mac->hw->soc->caps, MTK_SGMII) && +- (state->interface == PHY_INTERFACE_MODE_SGMII || +- phy_interface_mode_is_8023z(state->interface)))) { +- linkmode_zero(supported); +- return; +- } +- + phylink_set_port_modes(mask); + phylink_set(mask, Autoneg); + +@@ -611,7 +595,6 @@ static void mtk_validate(struct phylink_ + case PHY_INTERFACE_MODE_MII: + case PHY_INTERFACE_MODE_RMII: + case PHY_INTERFACE_MODE_REVMII: +- case PHY_INTERFACE_MODE_NA: + default: + phylink_set(mask, 10baseT_Half); + phylink_set(mask, 10baseT_Full); +@@ -620,23 +603,6 @@ static void mtk_validate(struct phylink_ + break; + } + +- if (state->interface == PHY_INTERFACE_MODE_NA) { +- if (MTK_HAS_CAPS(mac->hw->soc->caps, MTK_SGMII)) { +- phylink_set(mask, 1000baseT_Full); +- phylink_set(mask, 1000baseX_Full); +- phylink_set(mask, 2500baseX_Full); +- } +- if (MTK_HAS_CAPS(mac->hw->soc->caps, MTK_RGMII)) { +- phylink_set(mask, 1000baseT_Full); +- phylink_set(mask, 1000baseT_Half); +- phylink_set(mask, 1000baseX_Full); +- } +- if (MTK_HAS_CAPS(mac->hw->soc->caps, MTK_GEPHY)) { +- phylink_set(mask, 1000baseT_Full); +- phylink_set(mask, 1000baseT_Half); +- } +- } +- + phylink_set(mask, Pause); + phylink_set(mask, Asym_Pause); + diff --git a/target/linux/generic/backport-6.1/704-03-v5.17-net-mtk_eth_soc-drop-use-of-phylink_helper_basex_spe.patch b/target/linux/generic/backport-6.1/704-03-v5.17-net-mtk_eth_soc-drop-use-of-phylink_helper_basex_spe.patch new file mode 100644 index 0000000000..a3cfab7f88 --- /dev/null +++ b/target/linux/generic/backport-6.1/704-03-v5.17-net-mtk_eth_soc-drop-use-of-phylink_helper_basex_spe.patch @@ -0,0 +1,42 @@ +From 71d927494463c4f016d828e1134da26b7e961af5 Mon Sep 17 00:00:00 2001 +From: "Russell King (Oracle)" +Date: Tue, 16 Nov 2021 10:06:53 +0000 +Subject: [PATCH] net: mtk_eth_soc: 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) +Signed-off-by: David S. Miller +--- + drivers/net/ethernet/mediatek/mtk_eth_soc.c | 8 ++------ + 1 file changed, 2 insertions(+), 6 deletions(-) + +--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c ++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c +@@ -577,8 +577,9 @@ static void mtk_validate(struct phylink_ + phylink_set(mask, 1000baseT_Full); + break; + case PHY_INTERFACE_MODE_1000BASEX: +- case PHY_INTERFACE_MODE_2500BASEX: + phylink_set(mask, 1000baseX_Full); ++ break; ++ case PHY_INTERFACE_MODE_2500BASEX: + phylink_set(mask, 2500baseX_Full); + break; + case PHY_INTERFACE_MODE_GMII: +@@ -608,11 +609,6 @@ static void mtk_validate(struct phylink_ + + 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 const struct phylink_mac_ops mtk_phylink_ops = { diff --git a/target/linux/generic/backport-6.1/704-04-v5.17-net-mtk_eth_soc-use-phylink_generic_validate.patch b/target/linux/generic/backport-6.1/704-04-v5.17-net-mtk_eth_soc-use-phylink_generic_validate.patch new file mode 100644 index 0000000000..7f3734a765 --- /dev/null +++ b/target/linux/generic/backport-6.1/704-04-v5.17-net-mtk_eth_soc-use-phylink_generic_validate.patch @@ -0,0 +1,84 @@ +From a4238f6ce151afa331375d74a5033b76da637644 Mon Sep 17 00:00:00 2001 +From: "Russell King (Oracle)" +Date: Tue, 16 Nov 2021 10:06:58 +0000 +Subject: [PATCH] net: mtk_eth_soc: use phylink_generic_validate() + +mtk_eth_soc has no special behaviour in its validation implementation, +so can be switched to phylink_generic_validate(). + +Signed-off-by: Russell King (Oracle) +Signed-off-by: David S. Miller +--- + drivers/net/ethernet/mediatek/mtk_eth_soc.c | 53 ++------------------- + 1 file changed, 4 insertions(+), 49 deletions(-) + +--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c ++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c +@@ -563,56 +563,8 @@ static void mtk_mac_link_up(struct phyli + mtk_w32(mac->hw, mcr, MTK_MAC_MCR(mac->id)); + } + +-static void mtk_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); +- +- switch (state->interface) { +- case PHY_INTERFACE_MODE_TRGMII: +- phylink_set(mask, 1000baseT_Full); +- break; +- case PHY_INTERFACE_MODE_1000BASEX: +- phylink_set(mask, 1000baseX_Full); +- break; +- case PHY_INTERFACE_MODE_2500BASEX: +- phylink_set(mask, 2500baseX_Full); +- break; +- case PHY_INTERFACE_MODE_GMII: +- case PHY_INTERFACE_MODE_RGMII: +- case PHY_INTERFACE_MODE_RGMII_ID: +- case PHY_INTERFACE_MODE_RGMII_RXID: +- case PHY_INTERFACE_MODE_RGMII_TXID: +- phylink_set(mask, 1000baseT_Half); +- fallthrough; +- case PHY_INTERFACE_MODE_SGMII: +- phylink_set(mask, 1000baseT_Full); +- phylink_set(mask, 1000baseX_Full); +- fallthrough; +- case PHY_INTERFACE_MODE_MII: +- case PHY_INTERFACE_MODE_RMII: +- case PHY_INTERFACE_MODE_REVMII: +- default: +- phylink_set(mask, 10baseT_Half); +- phylink_set(mask, 10baseT_Full); +- phylink_set(mask, 100baseT_Half); +- phylink_set(mask, 100baseT_Full); +- break; +- } +- +- phylink_set(mask, Pause); +- phylink_set(mask, Asym_Pause); +- +- linkmode_and(supported, supported, mask); +- linkmode_and(state->advertising, state->advertising, mask); +-} +- + static const struct phylink_mac_ops mtk_phylink_ops = { +- .validate = mtk_validate, ++ .validate = phylink_generic_validate, + .mac_pcs_get_state = mtk_mac_pcs_get_state, + .mac_an_restart = mtk_mac_an_restart, + .mac_config = mtk_mac_config, +@@ -3314,6 +3266,9 @@ static int mtk_add_mac(struct mtk_eth *e + + mac->phylink_config.dev = ð->netdev[id]->dev; + mac->phylink_config.type = PHYLINK_NETDEV; ++ mac->phylink_config.mac_capabilities = MAC_ASYM_PAUSE | MAC_SYM_PAUSE | ++ MAC_10 | MAC_100 | MAC_1000 | MAC_2500FD; ++ + __set_bit(PHY_INTERFACE_MODE_MII, + mac->phylink_config.supported_interfaces); + __set_bit(PHY_INTERFACE_MODE_GMII, diff --git a/target/linux/generic/backport-6.1/704-05-v5.17-net-mtk_eth_soc-mark-as-a-legacy_pre_march2020-drive.patch b/target/linux/generic/backport-6.1/704-05-v5.17-net-mtk_eth_soc-mark-as-a-legacy_pre_march2020-drive.patch new file mode 100644 index 0000000000..6aa99acf77 --- /dev/null +++ b/target/linux/generic/backport-6.1/704-05-v5.17-net-mtk_eth_soc-mark-as-a-legacy_pre_march2020-drive.patch @@ -0,0 +1,29 @@ +From b06515367facfadcf5e70cf6f39db749cf4eb5e3 Mon Sep 17 00:00:00 2001 +From: "Russell King (Oracle)" +Date: Thu, 9 Dec 2021 13:11:43 +0000 +Subject: [PATCH] net: mtk_eth_soc: mark as a legacy_pre_march2020 driver + +mtk_eth_soc has not been updated for commit 7cceb599d15d ("net: phylink: +avoid mac_config calls"), and makes use of state->speed and +state->duplex in contravention of the phylink documentation. This makes +reliant on the legacy behaviours, so mark it as a legacy driver. + +Signed-off-by: Russell King (Oracle) +Signed-off-by: Jakub Kicinski +--- + drivers/net/ethernet/mediatek/mtk_eth_soc.c | 4 ++++ + 1 file changed, 4 insertions(+) + +--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c ++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c +@@ -3266,6 +3266,10 @@ static int mtk_add_mac(struct mtk_eth *e + + mac->phylink_config.dev = ð->netdev[id]->dev; + mac->phylink_config.type = PHYLINK_NETDEV; ++ /* This driver makes use of state->speed/state->duplex in ++ * mac_config ++ */ ++ mac->phylink_config.legacy_pre_march2020 = true; + mac->phylink_config.mac_capabilities = MAC_ASYM_PAUSE | MAC_SYM_PAUSE | + MAC_10 | MAC_100 | MAC_1000 | MAC_2500FD; + diff --git a/target/linux/generic/backport-6.1/704-06-v5.19-eth-mtk_eth_soc-remove-a-copy-of-the-NAPI_POLL_WEIGH.patch b/target/linux/generic/backport-6.1/704-06-v5.19-eth-mtk_eth_soc-remove-a-copy-of-the-NAPI_POLL_WEIGH.patch new file mode 100644 index 0000000000..e5f70e36f0 --- /dev/null +++ b/target/linux/generic/backport-6.1/704-06-v5.19-eth-mtk_eth_soc-remove-a-copy-of-the-NAPI_POLL_WEIGH.patch @@ -0,0 +1,40 @@ +From 889e3691b9d6573de133da1f5e78f590e52152cd Mon Sep 17 00:00:00 2001 +From: Jakub Kicinski +Date: Thu, 28 Apr 2022 14:23:13 -0700 +Subject: [PATCH] eth: mtk_eth_soc: remove a copy of the NAPI_POLL_WEIGHT + define + +Defining local versions of NAPI_POLL_WEIGHT with the same +values in the drivers just makes refactoring harder. + +Signed-off-by: Jakub Kicinski +Signed-off-by: David S. Miller +--- + drivers/net/ethernet/mediatek/mtk_eth_soc.c | 4 ++-- + drivers/net/ethernet/mediatek/mtk_eth_soc.h | 1 - + 2 files changed, 2 insertions(+), 3 deletions(-) + +--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c ++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c +@@ -3565,9 +3565,9 @@ static int mtk_probe(struct platform_dev + */ + init_dummy_netdev(ð->dummy_dev); + netif_napi_add(ð->dummy_dev, ð->tx_napi, mtk_napi_tx, +- MTK_NAPI_WEIGHT); ++ NAPI_POLL_WEIGHT); + netif_napi_add(ð->dummy_dev, ð->rx_napi, mtk_napi_rx, +- MTK_NAPI_WEIGHT); ++ NAPI_POLL_WEIGHT); + + platform_set_drvdata(pdev, eth); + +--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h ++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h +@@ -25,7 +25,6 @@ + #define MTK_TX_DMA_BUF_LEN 0x3fff + #define MTK_TX_DMA_BUF_LEN_V2 0xffff + #define MTK_DMA_SIZE 512 +-#define MTK_NAPI_WEIGHT 64 + #define MTK_MAC_COUNT 2 + #define MTK_RX_ETH_HLEN (ETH_HLEN + ETH_FCS_LEN) + #define MTK_RX_HLEN (NET_SKB_PAD + MTK_RX_ETH_HLEN + NET_IP_ALIGN) diff --git a/target/linux/generic/backport-6.1/704-07-v5.19-mtk_eth_soc-remove-unused-mac-mode.patch b/target/linux/generic/backport-6.1/704-07-v5.19-mtk_eth_soc-remove-unused-mac-mode.patch new file mode 100644 index 0000000000..4d896cdf39 --- /dev/null +++ b/target/linux/generic/backport-6.1/704-07-v5.19-mtk_eth_soc-remove-unused-mac-mode.patch @@ -0,0 +1,35 @@ +From 0600bdde1fae75fb9bad72033d28edddc72b44b2 Mon Sep 17 00:00:00 2001 +From: "Russell King (Oracle)" +Date: Wed, 18 May 2022 15:54:31 +0100 +Subject: [PATCH 01/12] net: mtk_eth_soc: remove unused mac->mode + +mac->mode is only ever written to in one location, and is thus +superflous. Remove it. + +Signed-off-by: Russell King (Oracle) +Signed-off-by: Jakub Kicinski +--- + drivers/net/ethernet/mediatek/mtk_eth_soc.c | 1 - + drivers/net/ethernet/mediatek/mtk_eth_soc.h | 1 - + 2 files changed, 2 deletions(-) + +--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c ++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c +@@ -3261,7 +3261,6 @@ static int mtk_add_mac(struct mtk_eth *e + + /* mac config is not set */ + mac->interface = PHY_INTERFACE_MODE_NA; +- mac->mode = MLO_AN_PHY; + mac->speed = SPEED_UNKNOWN; + + mac->phylink_config.dev = ð->netdev[id]->dev; +--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h ++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h +@@ -1085,7 +1085,6 @@ struct mtk_eth { + struct mtk_mac { + int id; + phy_interface_t interface; +- unsigned int mode; + int speed; + struct device_node *of_node; + struct phylink *phylink; diff --git a/target/linux/generic/backport-6.1/704-08-v5.19-net-mtk_eth_soc-remove-unused-sgmii-flags.patch b/target/linux/generic/backport-6.1/704-08-v5.19-net-mtk_eth_soc-remove-unused-sgmii-flags.patch new file mode 100644 index 0000000000..39aa24157e --- /dev/null +++ b/target/linux/generic/backport-6.1/704-08-v5.19-net-mtk_eth_soc-remove-unused-sgmii-flags.patch @@ -0,0 +1,40 @@ +From 5a7a2f4b29d7546244da7d8bbc1962fce5b230f2 Mon Sep 17 00:00:00 2001 +From: "Russell King (Oracle)" +Date: Wed, 18 May 2022 15:54:36 +0100 +Subject: [PATCH 02/12] net: mtk_eth_soc: remove unused sgmii flags + +The "flags" member of struct mtk_sgmii appears to be unused, as are +the MTK_SGMII_PHYSPEED_* and MTK_HAS_FLAGS() macros. Remove them. + +Signed-off-by: Russell King (Oracle) +Signed-off-by: Jakub Kicinski +--- + drivers/net/ethernet/mediatek/mtk_eth_soc.h | 8 -------- + 1 file changed, 8 deletions(-) + +--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h ++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h +@@ -956,23 +956,15 @@ struct mtk_soc_data { + /* currently no SoC has more than 2 macs */ + #define MTK_MAX_DEVS 2 + +-#define MTK_SGMII_PHYSPEED_AN BIT(31) +-#define MTK_SGMII_PHYSPEED_MASK GENMASK(2, 0) +-#define MTK_SGMII_PHYSPEED_1000 BIT(0) +-#define MTK_SGMII_PHYSPEED_2500 BIT(1) +-#define MTK_HAS_FLAGS(flags, _x) (((flags) & (_x)) == (_x)) +- + /* struct mtk_sgmii - This is the structure holding sgmii regmap and its + * characteristics + * @regmap: The register map pointing at the range used to setup + * SGMII modes +- * @flags: The enum refers to which mode the sgmii wants to run on + * @ana_rgc3: The offset refers to register ANA_RGC3 related to regmap + */ + + struct mtk_sgmii { + struct regmap *regmap[MTK_MAX_DEVS]; +- u32 flags[MTK_MAX_DEVS]; + u32 ana_rgc3; + }; + diff --git a/target/linux/generic/backport-6.1/704-09-v5.19-net-mtk_eth_soc-add-mask-and-update-PCS-speed-defini.patch b/target/linux/generic/backport-6.1/704-09-v5.19-net-mtk_eth_soc-add-mask-and-update-PCS-speed-defini.patch new file mode 100644 index 0000000000..f2e1f86bac --- /dev/null +++ b/target/linux/generic/backport-6.1/704-09-v5.19-net-mtk_eth_soc-add-mask-and-update-PCS-speed-defini.patch @@ -0,0 +1,40 @@ +From bc5e93e0cd22e360eda23859b939280205567580 Mon Sep 17 00:00:00 2001 +From: "Russell King (Oracle)" +Date: Wed, 18 May 2022 15:54:42 +0100 +Subject: [PATCH 03/12] net: mtk_eth_soc: add mask and update PCS speed + definitions + +The PCS speed setting is a two bit field, but it is defined as two +separate bits. Add a bitfield mask for the speed definitions, an + use the FIELD_PREP() macro to define each PCS speed. + +Signed-off-by: Russell King (Oracle) +Signed-off-by: Jakub Kicinski +--- + drivers/net/ethernet/mediatek/mtk_eth_soc.h | 8 +++++--- + 1 file changed, 5 insertions(+), 3 deletions(-) + +--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h ++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h +@@ -17,6 +17,7 @@ + #include + #include + #include ++#include + #include "mtk_ppe.h" + + #define MTK_QDMA_PAGE_SIZE 2048 +@@ -473,9 +474,10 @@ + #define SGMSYS_SGMII_MODE 0x20 + #define SGMII_IF_MODE_BIT0 BIT(0) + #define SGMII_SPEED_DUPLEX_AN BIT(1) +-#define SGMII_SPEED_10 0x0 +-#define SGMII_SPEED_100 BIT(2) +-#define SGMII_SPEED_1000 BIT(3) ++#define SGMII_SPEED_MASK GENMASK(3, 2) ++#define SGMII_SPEED_10 FIELD_PREP(SGMII_SPEED_MASK, 0) ++#define SGMII_SPEED_100 FIELD_PREP(SGMII_SPEED_MASK, 1) ++#define SGMII_SPEED_1000 FIELD_PREP(SGMII_SPEED_MASK, 2) + #define SGMII_DUPLEX_FULL BIT(4) + #define SGMII_IF_MODE_BIT5 BIT(5) + #define SGMII_REMOTE_FAULT_DIS BIT(8) diff --git a/target/linux/generic/backport-6.1/704-10-v5.19-net-mtk_eth_soc-correct-802.3z-speed-setting.patch b/target/linux/generic/backport-6.1/704-10-v5.19-net-mtk_eth_soc-correct-802.3z-speed-setting.patch new file mode 100644 index 0000000000..fb1ee4e310 --- /dev/null +++ b/target/linux/generic/backport-6.1/704-10-v5.19-net-mtk_eth_soc-correct-802.3z-speed-setting.patch @@ -0,0 +1,60 @@ +From 7da3f901f8ecb425105fad39a0f5de73306abe52 Mon Sep 17 00:00:00 2001 +From: "Russell King (Oracle)" +Date: Wed, 18 May 2022 15:54:47 +0100 +Subject: [PATCH 04/12] net: mtk_eth_soc: correct 802.3z speed setting + +Phylink does not guarantee that state->speed will be set correctly in +the mac_config() call, so it's a bug that the driver makes use of it. +Moreover, it is making use of it in a function that is only ever called +for 1000BASE-X and 2500BASE-X which operate at a fixed speed which +happens to be the same setting irrespective of the interface mode. We +can simply remove the switch statement and just set the SGMII interface +speed. + +Signed-off-by: Russell King (Oracle) +Signed-off-by: Jakub Kicinski +--- + drivers/net/ethernet/mediatek/mtk_sgmii.c | 18 +++++------------- + 1 file changed, 5 insertions(+), 13 deletions(-) + +--- a/drivers/net/ethernet/mediatek/mtk_sgmii.c ++++ b/drivers/net/ethernet/mediatek/mtk_sgmii.c +@@ -34,6 +34,7 @@ int mtk_sgmii_init(struct mtk_sgmii *ss, + return 0; + } + ++/* For SGMII interface mode */ + int mtk_sgmii_setup_mode_an(struct mtk_sgmii *ss, int id) + { + unsigned int val; +@@ -60,6 +61,9 @@ int mtk_sgmii_setup_mode_an(struct mtk_s + return 0; + } + ++/* For 1000BASE-X and 2500BASE-X interface modes, which operate at a ++ * fixed speed. ++ */ + int mtk_sgmii_setup_mode_force(struct mtk_sgmii *ss, int id, + const struct phylink_link_state *state) + { +@@ -82,19 +86,7 @@ int mtk_sgmii_setup_mode_force(struct mt + /* SGMII force mode setting */ + regmap_read(ss->regmap[id], SGMSYS_SGMII_MODE, &val); + val &= ~SGMII_IF_MODE_MASK; +- +- switch (state->speed) { +- case SPEED_10: +- val |= SGMII_SPEED_10; +- break; +- case SPEED_100: +- val |= SGMII_SPEED_100; +- break; +- case SPEED_2500: +- case SPEED_1000: +- val |= SGMII_SPEED_1000; +- break; +- } ++ val |= SGMII_SPEED_1000; + + if (state->duplex == DUPLEX_FULL) + val |= SGMII_DUPLEX_FULL; diff --git a/target/linux/generic/backport-6.1/704-11-v5.19-net-mtk_eth_soc-correct-802.3z-duplex-setting.patch b/target/linux/generic/backport-6.1/704-11-v5.19-net-mtk_eth_soc-correct-802.3z-duplex-setting.patch new file mode 100644 index 0000000000..140ff3cab5 --- /dev/null +++ b/target/linux/generic/backport-6.1/704-11-v5.19-net-mtk_eth_soc-correct-802.3z-duplex-setting.patch @@ -0,0 +1,101 @@ +From a459187390bb221827f9c07866c3a5ffbdf9622b Mon Sep 17 00:00:00 2001 +From: Russell King +Date: Wed, 18 May 2022 15:54:52 +0100 +Subject: [PATCH 05/12] net: mtk_eth_soc: correct 802.3z duplex setting + +Phylink does not guarantee that state->duplex will be set correctly in +the mac_config() call, so it's a bug that the driver makes use of it. + +Move the 802.3z PCS duplex configuration to mac_link_up(). + +Signed-off-by: Russell King +Signed-off-by: Jakub Kicinski +--- + drivers/net/ethernet/mediatek/mtk_eth_soc.c | 16 +++++++++++---- + drivers/net/ethernet/mediatek/mtk_eth_soc.h | 1 + + drivers/net/ethernet/mediatek/mtk_sgmii.c | 22 +++++++++++++++------ + 3 files changed, 29 insertions(+), 10 deletions(-) + +--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c ++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c +@@ -532,8 +532,18 @@ static void mtk_mac_link_up(struct phyli + { + struct mtk_mac *mac = container_of(config, struct mtk_mac, + phylink_config); +- u32 mcr = mtk_r32(mac->hw, MTK_MAC_MCR(mac->id)); ++ u32 mcr; + ++ if (phy_interface_mode_is_8023z(interface)) { ++ struct mtk_eth *eth = mac->hw; ++ ++ /* Decide how GMAC and SGMIISYS be mapped */ ++ int sid = (MTK_HAS_CAPS(eth->soc->caps, MTK_SHARED_SGMII)) ? ++ 0 : mac->id; ++ mtk_sgmii_link_up(eth->sgmii, sid, speed, duplex); ++ } ++ ++ mcr = mtk_r32(mac->hw, MTK_MAC_MCR(mac->id)); + mcr &= ~(MAC_MCR_SPEED_100 | MAC_MCR_SPEED_1000 | + MAC_MCR_FORCE_DPX | MAC_MCR_FORCE_TX_FC | + MAC_MCR_FORCE_RX_FC); +@@ -3265,9 +3275,7 @@ static int mtk_add_mac(struct mtk_eth *e + + mac->phylink_config.dev = ð->netdev[id]->dev; + mac->phylink_config.type = PHYLINK_NETDEV; +- /* This driver makes use of state->speed/state->duplex in +- * mac_config +- */ ++ /* This driver makes use of state->speed in mac_config */ + mac->phylink_config.legacy_pre_march2020 = true; + mac->phylink_config.mac_capabilities = MAC_ASYM_PAUSE | MAC_SYM_PAUSE | + MAC_10 | MAC_100 | MAC_1000 | MAC_2500FD; +--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h ++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h +@@ -1103,6 +1103,7 @@ int mtk_sgmii_init(struct mtk_sgmii *ss, + int mtk_sgmii_setup_mode_an(struct mtk_sgmii *ss, int id); + int mtk_sgmii_setup_mode_force(struct mtk_sgmii *ss, int id, + const struct phylink_link_state *state); ++void mtk_sgmii_link_up(struct mtk_sgmii *ss, int id, int speed, int duplex); + void mtk_sgmii_restart_an(struct mtk_eth *eth, int mac_id); + + int mtk_gmac_sgmii_path_setup(struct mtk_eth *eth, int mac_id); +--- a/drivers/net/ethernet/mediatek/mtk_sgmii.c ++++ b/drivers/net/ethernet/mediatek/mtk_sgmii.c +@@ -83,14 +83,10 @@ int mtk_sgmii_setup_mode_force(struct mt + val &= ~SGMII_AN_ENABLE; + regmap_write(ss->regmap[id], SGMSYS_PCS_CONTROL_1, val); + +- /* SGMII force mode setting */ ++ /* Set the speed etc but leave the duplex unchanged */ + regmap_read(ss->regmap[id], SGMSYS_SGMII_MODE, &val); +- val &= ~SGMII_IF_MODE_MASK; ++ val &= SGMII_DUPLEX_FULL | ~SGMII_IF_MODE_MASK; + val |= SGMII_SPEED_1000; +- +- if (state->duplex == DUPLEX_FULL) +- val |= SGMII_DUPLEX_FULL; +- + regmap_write(ss->regmap[id], SGMSYS_SGMII_MODE, val); + + /* Release PHYA power down state */ +@@ -101,6 +97,20 @@ int mtk_sgmii_setup_mode_force(struct mt + return 0; + } + ++/* For 1000BASE-X and 2500BASE-X interface modes */ ++void mtk_sgmii_link_up(struct mtk_sgmii *ss, int id, int speed, int duplex) ++{ ++ unsigned int val; ++ ++ /* SGMII force duplex setting */ ++ regmap_read(ss->regmap[id], SGMSYS_SGMII_MODE, &val); ++ val &= ~SGMII_DUPLEX_FULL; ++ if (duplex == DUPLEX_FULL) ++ val |= SGMII_DUPLEX_FULL; ++ ++ regmap_write(ss->regmap[id], SGMSYS_SGMII_MODE, val); ++} ++ + void mtk_sgmii_restart_an(struct mtk_eth *eth, int mac_id) + { + struct mtk_sgmii *ss = eth->sgmii; diff --git a/target/linux/generic/backport-6.1/704-12-v5.19-net-mtk_eth_soc-stop-passing-phylink-state-to-sgmii-.patch b/target/linux/generic/backport-6.1/704-12-v5.19-net-mtk_eth_soc-stop-passing-phylink-state-to-sgmii-.patch new file mode 100644 index 0000000000..56b5e43e53 --- /dev/null +++ b/target/linux/generic/backport-6.1/704-12-v5.19-net-mtk_eth_soc-stop-passing-phylink-state-to-sgmii-.patch @@ -0,0 +1,60 @@ +From 4ce5a0bd3958ed248f0325bfcb95339f7c74feb2 Mon Sep 17 00:00:00 2001 +From: "Russell King (Oracle)" +Date: Wed, 18 May 2022 15:54:57 +0100 +Subject: [PATCH 06/12] net: mtk_eth_soc: stop passing phylink state to sgmii + setup + +Now that mtk_sgmii_setup_mode_force() only uses the interface mode +from the phylink state, pass just the interface mode into this +function. + +Signed-off-by: Russell King (Oracle) +Signed-off-by: Jakub Kicinski +--- + drivers/net/ethernet/mediatek/mtk_eth_soc.c | 2 +- + drivers/net/ethernet/mediatek/mtk_eth_soc.h | 2 +- + drivers/net/ethernet/mediatek/mtk_sgmii.c | 4 ++-- + 3 files changed, 4 insertions(+), 4 deletions(-) + +--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c ++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c +@@ -437,7 +437,7 @@ static void mtk_mac_config(struct phylin + /* Setup SGMIISYS with the determined property */ + if (state->interface != PHY_INTERFACE_MODE_SGMII) + err = mtk_sgmii_setup_mode_force(eth->sgmii, sid, +- state); ++ state->interface); + else if (phylink_autoneg_inband(mode)) + err = mtk_sgmii_setup_mode_an(eth->sgmii, sid); + +--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h ++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h +@@ -1102,7 +1102,7 @@ int mtk_sgmii_init(struct mtk_sgmii *ss, + u32 ana_rgc3); + int mtk_sgmii_setup_mode_an(struct mtk_sgmii *ss, int id); + int mtk_sgmii_setup_mode_force(struct mtk_sgmii *ss, int id, +- const struct phylink_link_state *state); ++ phy_interface_t interface); + void mtk_sgmii_link_up(struct mtk_sgmii *ss, int id, int speed, int duplex); + void mtk_sgmii_restart_an(struct mtk_eth *eth, int mac_id); + +--- a/drivers/net/ethernet/mediatek/mtk_sgmii.c ++++ b/drivers/net/ethernet/mediatek/mtk_sgmii.c +@@ -65,7 +65,7 @@ int mtk_sgmii_setup_mode_an(struct mtk_s + * fixed speed. + */ + int mtk_sgmii_setup_mode_force(struct mtk_sgmii *ss, int id, +- const struct phylink_link_state *state) ++ phy_interface_t interface) + { + unsigned int val; + +@@ -74,7 +74,7 @@ int mtk_sgmii_setup_mode_force(struct mt + + regmap_read(ss->regmap[id], ss->ana_rgc3, &val); + val &= ~RG_PHY_SPEED_MASK; +- if (state->interface == PHY_INTERFACE_MODE_2500BASEX) ++ if (interface == PHY_INTERFACE_MODE_2500BASEX) + val |= RG_PHY_SPEED_3_125G; + regmap_write(ss->regmap[id], ss->ana_rgc3, val); + diff --git a/target/linux/generic/backport-6.1/704-13-v5.19-net-mtk_eth_soc-provide-mtk_sgmii_config.patch b/target/linux/generic/backport-6.1/704-13-v5.19-net-mtk_eth_soc-provide-mtk_sgmii_config.patch new file mode 100644 index 0000000000..4c91cf68f4 --- /dev/null +++ b/target/linux/generic/backport-6.1/704-13-v5.19-net-mtk_eth_soc-provide-mtk_sgmii_config.patch @@ -0,0 +1,89 @@ +From 1ec619ee4a052fb9ac48b57554ac2722a0bfe73c Mon Sep 17 00:00:00 2001 +From: "Russell King (Oracle)" +Date: Wed, 18 May 2022 15:55:02 +0100 +Subject: [PATCH 07/12] net: mtk_eth_soc: provide mtk_sgmii_config() + +Provide mtk_sgmii_config() to wrap up the decisions about which SGMII +configuration will be called. + +Signed-off-by: Russell King (Oracle) +Signed-off-by: Jakub Kicinski +--- + drivers/net/ethernet/mediatek/mtk_eth_soc.c | 7 +------ + drivers/net/ethernet/mediatek/mtk_eth_soc.h | 5 ++--- + drivers/net/ethernet/mediatek/mtk_sgmii.c | 20 +++++++++++++++++--- + 3 files changed, 20 insertions(+), 12 deletions(-) + +--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c ++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c +@@ -435,12 +435,7 @@ static void mtk_mac_config(struct phylin + 0 : mac->id; + + /* Setup SGMIISYS with the determined property */ +- if (state->interface != PHY_INTERFACE_MODE_SGMII) +- err = mtk_sgmii_setup_mode_force(eth->sgmii, sid, +- state->interface); +- else if (phylink_autoneg_inband(mode)) +- err = mtk_sgmii_setup_mode_an(eth->sgmii, sid); +- ++ err = mtk_sgmii_config(eth->sgmii, sid, mode, state->interface); + if (err) + goto init_err; + +--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h ++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h +@@ -1100,9 +1100,8 @@ u32 mtk_r32(struct mtk_eth *eth, unsigne + + int mtk_sgmii_init(struct mtk_sgmii *ss, struct device_node *np, + u32 ana_rgc3); +-int mtk_sgmii_setup_mode_an(struct mtk_sgmii *ss, int id); +-int mtk_sgmii_setup_mode_force(struct mtk_sgmii *ss, int id, +- phy_interface_t interface); ++int mtk_sgmii_config(struct mtk_sgmii *ss, int id, unsigned int mode, ++ phy_interface_t interface); + void mtk_sgmii_link_up(struct mtk_sgmii *ss, int id, int speed, int duplex); + void mtk_sgmii_restart_an(struct mtk_eth *eth, int mac_id); + +--- a/drivers/net/ethernet/mediatek/mtk_sgmii.c ++++ b/drivers/net/ethernet/mediatek/mtk_sgmii.c +@@ -35,7 +35,7 @@ int mtk_sgmii_init(struct mtk_sgmii *ss, + } + + /* For SGMII interface mode */ +-int mtk_sgmii_setup_mode_an(struct mtk_sgmii *ss, int id) ++static int mtk_sgmii_setup_mode_an(struct mtk_sgmii *ss, int id) + { + unsigned int val; + +@@ -64,8 +64,8 @@ int mtk_sgmii_setup_mode_an(struct mtk_s + /* For 1000BASE-X and 2500BASE-X interface modes, which operate at a + * fixed speed. + */ +-int mtk_sgmii_setup_mode_force(struct mtk_sgmii *ss, int id, +- phy_interface_t interface) ++static int mtk_sgmii_setup_mode_force(struct mtk_sgmii *ss, int id, ++ phy_interface_t interface) + { + unsigned int val; + +@@ -97,6 +97,20 @@ int mtk_sgmii_setup_mode_force(struct mt + return 0; + } + ++int mtk_sgmii_config(struct mtk_sgmii *ss, int id, unsigned int mode, ++ phy_interface_t interface) ++{ ++ int err = 0; ++ ++ /* Setup SGMIISYS with the determined property */ ++ if (interface != PHY_INTERFACE_MODE_SGMII) ++ err = mtk_sgmii_setup_mode_force(ss, id, interface); ++ else if (phylink_autoneg_inband(mode)) ++ err = mtk_sgmii_setup_mode_an(ss, id); ++ ++ return err; ++} ++ + /* For 1000BASE-X and 2500BASE-X interface modes */ + void mtk_sgmii_link_up(struct mtk_sgmii *ss, int id, int speed, int duplex) + { diff --git a/target/linux/generic/backport-6.1/704-14-v5.19-net-mtk_eth_soc-add-fixme-comment-for-state-speed-us.patch b/target/linux/generic/backport-6.1/704-14-v5.19-net-mtk_eth_soc-add-fixme-comment-for-state-speed-us.patch new file mode 100644 index 0000000000..8080a2ca44 --- /dev/null +++ b/target/linux/generic/backport-6.1/704-14-v5.19-net-mtk_eth_soc-add-fixme-comment-for-state-speed-us.patch @@ -0,0 +1,38 @@ +From 650a49bc65df6b0e0051a8f62d7c22d95a8f350d Mon Sep 17 00:00:00 2001 +From: "Russell King (Oracle)" +Date: Wed, 18 May 2022 15:55:07 +0100 +Subject: [PATCH 08/12] net: mtk_eth_soc: add fixme comment for state->speed + use + +Add a fixme comment for the last remaining incorrect usage of +state->speed in the mac_config() method, which is strangely in a code +path which is only run when the PHY interface mode changes. + +This means if we are in RGMII mode, changes in state->speed will not +cause the INTF_MODE, TRGMII_RCK_CTRL and TRGMII_TCK_CTRL registers to +be set according to the speed, nor will the TRGPLL clock be set to the +correct value. + +Signed-off-by: Russell King (Oracle) +Signed-off-by: Jakub Kicinski +--- + drivers/net/ethernet/mediatek/mtk_eth_soc.c | 8 ++++++++ + 1 file changed, 8 insertions(+) + +--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c ++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c +@@ -374,6 +374,14 @@ static void mtk_mac_config(struct phylin + state->interface)) + goto err_phy; + } else { ++ /* FIXME: this is incorrect. Not only does it ++ * use state->speed (which is not guaranteed ++ * to be correct) but it also makes use of it ++ * in a code path that will only be reachable ++ * when the PHY interface mode changes, not ++ * when the speed changes. Consequently, RGMII ++ * is probably broken. ++ */ + mtk_gmac0_rgmii_adjust(mac->hw, + state->interface, + state->speed); diff --git a/target/linux/generic/backport-6.1/704-15-v5.19-net-mtk_eth_soc-move-MAC_MCR-setting-to-mac_finish.patch b/target/linux/generic/backport-6.1/704-15-v5.19-net-mtk_eth_soc-move-MAC_MCR-setting-to-mac_finish.patch new file mode 100644 index 0000000000..368db4cca2 --- /dev/null +++ b/target/linux/generic/backport-6.1/704-15-v5.19-net-mtk_eth_soc-move-MAC_MCR-setting-to-mac_finish.patch @@ -0,0 +1,79 @@ +From 0e37ad71b2ff772009595002da2860999e98e14e Mon Sep 17 00:00:00 2001 +From: "Russell King (Oracle)" +Date: Wed, 18 May 2022 15:55:12 +0100 +Subject: [PATCH 09/12] net: mtk_eth_soc: move MAC_MCR setting to mac_finish() + +Move the setting of the MTK_MAC_MCR register from the end of mac_config +into the phylink mac_finish() method, to keep it as the very last write +that is done during configuration. + +Signed-off-by: Russell King (Oracle) +Signed-off-by: Jakub Kicinski +--- + drivers/net/ethernet/mediatek/mtk_eth_soc.c | 33 ++++++++++++++------- + 1 file changed, 22 insertions(+), 11 deletions(-) + +--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c ++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c +@@ -316,8 +316,8 @@ static void mtk_mac_config(struct phylin + struct mtk_mac *mac = container_of(config, struct mtk_mac, + phylink_config); + struct mtk_eth *eth = mac->hw; +- u32 mcr_cur, mcr_new, sid, i; + int val, ge_mode, err = 0; ++ u32 sid, i; + + /* MT76x8 has no hardware settings between for the MAC */ + if (!MTK_HAS_CAPS(eth->soc->caps, MTK_SOC_MT7628) && +@@ -455,16 +455,6 @@ static void mtk_mac_config(struct phylin + return; + } + +- /* Setup gmac */ +- mcr_cur = mtk_r32(mac->hw, MTK_MAC_MCR(mac->id)); +- mcr_new = mcr_cur; +- mcr_new |= MAC_MCR_IPG_CFG | MAC_MCR_FORCE_MODE | +- MAC_MCR_BACKOFF_EN | MAC_MCR_BACKPR_EN | MAC_MCR_FORCE_LINK; +- +- /* Only update control register when needed! */ +- if (mcr_new != mcr_cur) +- mtk_w32(mac->hw, mcr_new, MTK_MAC_MCR(mac->id)); +- + return; + + err_phy: +@@ -477,6 +467,26 @@ init_err: + mac->id, phy_modes(state->interface), err); + } + ++static int mtk_mac_finish(struct phylink_config *config, unsigned int mode, ++ phy_interface_t interface) ++{ ++ struct mtk_mac *mac = container_of(config, struct mtk_mac, ++ phylink_config); ++ u32 mcr_cur, mcr_new; ++ ++ /* Setup gmac */ ++ mcr_cur = mtk_r32(mac->hw, MTK_MAC_MCR(mac->id)); ++ mcr_new = mcr_cur; ++ mcr_new |= MAC_MCR_IPG_CFG | MAC_MCR_FORCE_MODE | ++ MAC_MCR_BACKOFF_EN | MAC_MCR_BACKPR_EN | MAC_MCR_FORCE_LINK; ++ ++ /* Only update control register when needed! */ ++ if (mcr_new != mcr_cur) ++ mtk_w32(mac->hw, mcr_new, MTK_MAC_MCR(mac->id)); ++ ++ return 0; ++} ++ + static void mtk_mac_pcs_get_state(struct phylink_config *config, + struct phylink_link_state *state) + { +@@ -581,6 +591,7 @@ static const struct phylink_mac_ops mtk_ + .mac_pcs_get_state = mtk_mac_pcs_get_state, + .mac_an_restart = mtk_mac_an_restart, + .mac_config = mtk_mac_config, ++ .mac_finish = mtk_mac_finish, + .mac_link_down = mtk_mac_link_down, + .mac_link_up = mtk_mac_link_up, + }; diff --git a/target/linux/generic/backport-6.1/704-16-v5.19-net-mtk_eth_soc-move-restoration-of-SYSCFG0-to-mac_f.patch b/target/linux/generic/backport-6.1/704-16-v5.19-net-mtk_eth_soc-move-restoration-of-SYSCFG0-to-mac_f.patch new file mode 100644 index 0000000000..ad6ec60288 --- /dev/null +++ b/target/linux/generic/backport-6.1/704-16-v5.19-net-mtk_eth_soc-move-restoration-of-SYSCFG0-to-mac_f.patch @@ -0,0 +1,57 @@ +From 21089867278deb2a110b685e3cd33f64f9ce41e2 Mon Sep 17 00:00:00 2001 +From: "Russell King (Oracle)" +Date: Wed, 18 May 2022 15:55:17 +0100 +Subject: [PATCH 10/12] net: mtk_eth_soc: move restoration of SYSCFG0 to + mac_finish() + +The SGMIISYS configuration is performed while ETHSYS_SYSCFG0 is in a +disabled state. In order to preserve this when we switch to phylink_pcs +we need to move the restoration of this register to the mac_finish() +callback. + +Signed-off-by: Russell King (Oracle) +Signed-off-by: Jakub Kicinski +--- + drivers/net/ethernet/mediatek/mtk_eth_soc.c | 11 +++++++++-- + drivers/net/ethernet/mediatek/mtk_eth_soc.h | 1 + + 2 files changed, 10 insertions(+), 2 deletions(-) + +--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c ++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c +@@ -447,8 +447,8 @@ static void mtk_mac_config(struct phylin + if (err) + goto init_err; + +- regmap_update_bits(eth->ethsys, ETHSYS_SYSCFG0, +- SYSCFG0_SGMII_MASK, val); ++ /* Save the syscfg0 value for mac_finish */ ++ mac->syscfg0 = val; + } else if (phylink_autoneg_inband(mode)) { + dev_err(eth->dev, + "In-band mode not supported in non SGMII mode!\n"); +@@ -472,8 +472,15 @@ static int mtk_mac_finish(struct phylink + { + struct mtk_mac *mac = container_of(config, struct mtk_mac, + phylink_config); ++ struct mtk_eth *eth = mac->hw; + u32 mcr_cur, mcr_new; + ++ /* Enable SGMII */ ++ if (interface == PHY_INTERFACE_MODE_SGMII || ++ phy_interface_mode_is_8023z(interface)) ++ regmap_update_bits(eth->ethsys, ETHSYS_SYSCFG0, ++ SYSCFG0_SGMII_MASK, mac->syscfg0); ++ + /* Setup gmac */ + mcr_cur = mtk_r32(mac->hw, MTK_MAC_MCR(mac->id)); + mcr_new = mcr_cur; +--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h ++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h +@@ -1087,6 +1087,7 @@ struct mtk_mac { + struct mtk_hw_stats *hw_stats; + __be32 hwlro_ip[MTK_MAX_LRO_IP_CNT]; + int hwlro_ip_cnt; ++ unsigned int syscfg0; + }; + + /* the struct describing the SoC. these are declared in the soc_xyz.c files */ diff --git a/target/linux/generic/backport-6.1/704-17-v5.19-net-mtk_eth_soc-convert-code-structure-to-suit-split.patch b/target/linux/generic/backport-6.1/704-17-v5.19-net-mtk_eth_soc-convert-code-structure-to-suit-split.patch new file mode 100644 index 0000000000..623658f459 --- /dev/null +++ b/target/linux/generic/backport-6.1/704-17-v5.19-net-mtk_eth_soc-convert-code-structure-to-suit-split.patch @@ -0,0 +1,254 @@ +From 901f3fbe13c3e56f0742e02717ccbfabbc95c463 Mon Sep 17 00:00:00 2001 +From: "Russell King (Oracle)" +Date: Wed, 18 May 2022 15:55:22 +0100 +Subject: [PATCH 11/12] net: mtk_eth_soc: convert code structure to suit split + PCS support + +Provide a mtk_pcs structure which encapsulates everything that the PCS +functions need (the regmap and ana_rgc3 offset), and use this in the +PCS functions. Provide shim functions to convert from the existing +"mtk_sgmii_*" interface to the converted PCS functions. + +Signed-off-by: Russell King (Oracle) +Signed-off-by: Jakub Kicinski +--- + drivers/net/ethernet/mediatek/mtk_eth_soc.h | 15 ++- + drivers/net/ethernet/mediatek/mtk_sgmii.c | 123 +++++++++++--------- + 2 files changed, 79 insertions(+), 59 deletions(-) + +--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h ++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h +@@ -958,16 +958,23 @@ struct mtk_soc_data { + /* currently no SoC has more than 2 macs */ + #define MTK_MAX_DEVS 2 + +-/* struct mtk_sgmii - This is the structure holding sgmii regmap and its +- * characteristics ++/* struct mtk_pcs - This structure holds each sgmii regmap and associated ++ * data + * @regmap: The register map pointing at the range used to setup + * SGMII modes + * @ana_rgc3: The offset refers to register ANA_RGC3 related to regmap + */ ++struct mtk_pcs { ++ struct regmap *regmap; ++ u32 ana_rgc3; ++}; + ++/* struct mtk_sgmii - This is the structure holding sgmii regmap and its ++ * characteristics ++ * @pcs Array of individual PCS structures ++ */ + struct mtk_sgmii { +- struct regmap *regmap[MTK_MAX_DEVS]; +- u32 ana_rgc3; ++ struct mtk_pcs pcs[MTK_MAX_DEVS]; + }; + + /* struct mtk_eth - This is the main datasructure for holding the state +--- a/drivers/net/ethernet/mediatek/mtk_sgmii.c ++++ b/drivers/net/ethernet/mediatek/mtk_sgmii.c +@@ -9,90 +9,71 @@ + + #include + #include ++#include + #include + + #include "mtk_eth_soc.h" + +-int mtk_sgmii_init(struct mtk_sgmii *ss, struct device_node *r, u32 ana_rgc3) +-{ +- struct device_node *np; +- int i; +- +- ss->ana_rgc3 = ana_rgc3; +- +- for (i = 0; i < MTK_MAX_DEVS; i++) { +- np = of_parse_phandle(r, "mediatek,sgmiisys", i); +- if (!np) +- break; +- +- ss->regmap[i] = syscon_node_to_regmap(np); +- of_node_put(np); +- if (IS_ERR(ss->regmap[i])) +- return PTR_ERR(ss->regmap[i]); +- } +- +- return 0; +-} +- + /* For SGMII interface mode */ +-static int mtk_sgmii_setup_mode_an(struct mtk_sgmii *ss, int id) ++static int mtk_pcs_setup_mode_an(struct mtk_pcs *mpcs) + { + unsigned int val; + +- if (!ss->regmap[id]) ++ if (!mpcs->regmap) + return -EINVAL; + + /* Setup the link timer and QPHY power up inside SGMIISYS */ +- regmap_write(ss->regmap[id], SGMSYS_PCS_LINK_TIMER, ++ regmap_write(mpcs->regmap, SGMSYS_PCS_LINK_TIMER, + SGMII_LINK_TIMER_DEFAULT); + +- regmap_read(ss->regmap[id], SGMSYS_SGMII_MODE, &val); ++ regmap_read(mpcs->regmap, SGMSYS_SGMII_MODE, &val); + val |= SGMII_REMOTE_FAULT_DIS; +- regmap_write(ss->regmap[id], SGMSYS_SGMII_MODE, val); ++ regmap_write(mpcs->regmap, SGMSYS_SGMII_MODE, val); + +- regmap_read(ss->regmap[id], SGMSYS_PCS_CONTROL_1, &val); ++ regmap_read(mpcs->regmap, SGMSYS_PCS_CONTROL_1, &val); + val |= SGMII_AN_RESTART; +- regmap_write(ss->regmap[id], SGMSYS_PCS_CONTROL_1, val); ++ regmap_write(mpcs->regmap, SGMSYS_PCS_CONTROL_1, val); + +- regmap_read(ss->regmap[id], SGMSYS_QPHY_PWR_STATE_CTRL, &val); ++ regmap_read(mpcs->regmap, SGMSYS_QPHY_PWR_STATE_CTRL, &val); + val &= ~SGMII_PHYA_PWD; +- regmap_write(ss->regmap[id], SGMSYS_QPHY_PWR_STATE_CTRL, val); ++ regmap_write(mpcs->regmap, SGMSYS_QPHY_PWR_STATE_CTRL, val); + + return 0; ++ + } + + /* For 1000BASE-X and 2500BASE-X interface modes, which operate at a + * fixed speed. + */ +-static int mtk_sgmii_setup_mode_force(struct mtk_sgmii *ss, int id, +- phy_interface_t interface) ++static int mtk_pcs_setup_mode_force(struct mtk_pcs *mpcs, ++ phy_interface_t interface) + { + unsigned int val; + +- if (!ss->regmap[id]) ++ if (!mpcs->regmap) + return -EINVAL; + +- regmap_read(ss->regmap[id], ss->ana_rgc3, &val); ++ regmap_read(mpcs->regmap, mpcs->ana_rgc3, &val); + val &= ~RG_PHY_SPEED_MASK; + if (interface == PHY_INTERFACE_MODE_2500BASEX) + val |= RG_PHY_SPEED_3_125G; +- regmap_write(ss->regmap[id], ss->ana_rgc3, val); ++ regmap_write(mpcs->regmap, mpcs->ana_rgc3, val); + + /* Disable SGMII AN */ +- regmap_read(ss->regmap[id], SGMSYS_PCS_CONTROL_1, &val); ++ regmap_read(mpcs->regmap, SGMSYS_PCS_CONTROL_1, &val); + val &= ~SGMII_AN_ENABLE; +- regmap_write(ss->regmap[id], SGMSYS_PCS_CONTROL_1, val); ++ regmap_write(mpcs->regmap, SGMSYS_PCS_CONTROL_1, val); + + /* Set the speed etc but leave the duplex unchanged */ +- regmap_read(ss->regmap[id], SGMSYS_SGMII_MODE, &val); ++ regmap_read(mpcs->regmap, SGMSYS_SGMII_MODE, &val); + val &= SGMII_DUPLEX_FULL | ~SGMII_IF_MODE_MASK; + val |= SGMII_SPEED_1000; +- regmap_write(ss->regmap[id], SGMSYS_SGMII_MODE, val); ++ regmap_write(mpcs->regmap, SGMSYS_SGMII_MODE, val); + + /* Release PHYA power down state */ +- regmap_read(ss->regmap[id], SGMSYS_QPHY_PWR_STATE_CTRL, &val); ++ regmap_read(mpcs->regmap, SGMSYS_QPHY_PWR_STATE_CTRL, &val); + val &= ~SGMII_PHYA_PWD; +- regmap_write(ss->regmap[id], SGMSYS_QPHY_PWR_STATE_CTRL, val); ++ regmap_write(mpcs->regmap, SGMSYS_QPHY_PWR_STATE_CTRL, val); + + return 0; + } +@@ -100,44 +81,76 @@ static int mtk_sgmii_setup_mode_force(st + int mtk_sgmii_config(struct mtk_sgmii *ss, int id, unsigned int mode, + phy_interface_t interface) + { ++ struct mtk_pcs *mpcs = &ss->pcs[id]; + int err = 0; + + /* Setup SGMIISYS with the determined property */ + if (interface != PHY_INTERFACE_MODE_SGMII) +- err = mtk_sgmii_setup_mode_force(ss, id, interface); ++ err = mtk_pcs_setup_mode_force(mpcs, interface); + else if (phylink_autoneg_inband(mode)) +- err = mtk_sgmii_setup_mode_an(ss, id); ++ err = mtk_pcs_setup_mode_an(mpcs); + + return err; + } + +-/* For 1000BASE-X and 2500BASE-X interface modes */ +-void mtk_sgmii_link_up(struct mtk_sgmii *ss, int id, int speed, int duplex) ++static void mtk_pcs_restart_an(struct mtk_pcs *mpcs) ++{ ++ unsigned int val; ++ ++ if (!mpcs->regmap) ++ return; ++ ++ regmap_read(mpcs->regmap, SGMSYS_PCS_CONTROL_1, &val); ++ val |= SGMII_AN_RESTART; ++ regmap_write(mpcs->regmap, SGMSYS_PCS_CONTROL_1, val); ++} ++ ++static void mtk_pcs_link_up(struct mtk_pcs *mpcs, int speed, int duplex) + { + unsigned int val; + + /* SGMII force duplex setting */ +- regmap_read(ss->regmap[id], SGMSYS_SGMII_MODE, &val); ++ regmap_read(mpcs->regmap, SGMSYS_SGMII_MODE, &val); + val &= ~SGMII_DUPLEX_FULL; + if (duplex == DUPLEX_FULL) + val |= SGMII_DUPLEX_FULL; + +- regmap_write(ss->regmap[id], SGMSYS_SGMII_MODE, val); ++ regmap_write(mpcs->regmap, SGMSYS_SGMII_MODE, val); ++} ++ ++/* For 1000BASE-X and 2500BASE-X interface modes */ ++void mtk_sgmii_link_up(struct mtk_sgmii *ss, int id, int speed, int duplex) ++{ ++ mtk_pcs_link_up(&ss->pcs[id], speed, duplex); ++} ++ ++int mtk_sgmii_init(struct mtk_sgmii *ss, struct device_node *r, u32 ana_rgc3) ++{ ++ struct device_node *np; ++ int i; ++ ++ for (i = 0; i < MTK_MAX_DEVS; i++) { ++ np = of_parse_phandle(r, "mediatek,sgmiisys", i); ++ if (!np) ++ break; ++ ++ ss->pcs[i].ana_rgc3 = ana_rgc3; ++ ss->pcs[i].regmap = syscon_node_to_regmap(np); ++ of_node_put(np); ++ if (IS_ERR(ss->pcs[i].regmap)) ++ return PTR_ERR(ss->pcs[i].regmap); ++ } ++ ++ return 0; + } + + void mtk_sgmii_restart_an(struct mtk_eth *eth, int mac_id) + { +- struct mtk_sgmii *ss = eth->sgmii; +- unsigned int val, sid; ++ unsigned int sid; + + /* Decide how GMAC and SGMIISYS be mapped */ + sid = (MTK_HAS_CAPS(eth->soc->caps, MTK_SHARED_SGMII)) ? + 0 : mac_id; + +- if (!ss->regmap[sid]) +- return; +- +- regmap_read(ss->regmap[sid], SGMSYS_PCS_CONTROL_1, &val); +- val |= SGMII_AN_RESTART; +- regmap_write(ss->regmap[sid], SGMSYS_PCS_CONTROL_1, val); ++ mtk_pcs_restart_an(ð->sgmii->pcs[sid]); + } diff --git a/target/linux/generic/backport-6.1/704-18-v5.19-net-mtk_eth_soc-partially-convert-to-phylink_pcs.patch b/target/linux/generic/backport-6.1/704-18-v5.19-net-mtk_eth_soc-partially-convert-to-phylink_pcs.patch new file mode 100644 index 0000000000..df675e2899 --- /dev/null +++ b/target/linux/generic/backport-6.1/704-18-v5.19-net-mtk_eth_soc-partially-convert-to-phylink_pcs.patch @@ -0,0 +1,262 @@ +From 14a44ab0330d290fade1403a920e299cc56d7300 Mon Sep 17 00:00:00 2001 +From: "Russell King (Oracle)" +Date: Wed, 18 May 2022 15:55:28 +0100 +Subject: [PATCH 12/12] net: mtk_eth_soc: partially convert to phylink_pcs + +Partially convert mtk_eth_soc to phylink_pcs, moving the configuration, +link up and AN restart over. However, it seems mac_pcs_get_state() +doesn't actually get the state from the PCS, so we can't convert that +over without a better understanding of the hardware. + +Signed-off-by: Russell King (Oracle) +Signed-off-by: Jakub Kicinski +--- + drivers/net/ethernet/mediatek/mtk_eth_soc.c | 49 ++++++++---------- + drivers/net/ethernet/mediatek/mtk_eth_soc.h | 7 ++- + drivers/net/ethernet/mediatek/mtk_sgmii.c | 55 +++++++++++---------- + 3 files changed, 53 insertions(+), 58 deletions(-) + +--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c ++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c +@@ -310,6 +310,25 @@ static void mtk_gmac0_rgmii_adjust(struc + mtk_w32(eth, val, TRGMII_TCK_CTRL); + } + ++static struct phylink_pcs *mtk_mac_select_pcs(struct phylink_config *config, ++ phy_interface_t interface) ++{ ++ struct mtk_mac *mac = container_of(config, struct mtk_mac, ++ phylink_config); ++ struct mtk_eth *eth = mac->hw; ++ unsigned int sid; ++ ++ if (interface == PHY_INTERFACE_MODE_SGMII || ++ phy_interface_mode_is_8023z(interface)) { ++ sid = (MTK_HAS_CAPS(eth->soc->caps, MTK_SHARED_SGMII)) ? ++ 0 : mac->id; ++ ++ return mtk_sgmii_select_pcs(eth->sgmii, sid); ++ } ++ ++ return NULL; ++} ++ + static void mtk_mac_config(struct phylink_config *config, unsigned int mode, + const struct phylink_link_state *state) + { +@@ -317,7 +336,7 @@ static void mtk_mac_config(struct phylin + phylink_config); + struct mtk_eth *eth = mac->hw; + int val, ge_mode, err = 0; +- u32 sid, i; ++ u32 i; + + /* MT76x8 has no hardware settings between for the MAC */ + if (!MTK_HAS_CAPS(eth->soc->caps, MTK_SOC_MT7628) && +@@ -438,15 +457,6 @@ static void mtk_mac_config(struct phylin + SYSCFG0_SGMII_MASK, + ~(u32)SYSCFG0_SGMII_MASK); + +- /* Decide how GMAC and SGMIISYS be mapped */ +- sid = (MTK_HAS_CAPS(eth->soc->caps, MTK_SHARED_SGMII)) ? +- 0 : mac->id; +- +- /* Setup SGMIISYS with the determined property */ +- err = mtk_sgmii_config(eth->sgmii, sid, mode, state->interface); +- if (err) +- goto init_err; +- + /* Save the syscfg0 value for mac_finish */ + mac->syscfg0 = val; + } else if (phylink_autoneg_inband(mode)) { +@@ -526,14 +536,6 @@ static void mtk_mac_pcs_get_state(struct + state->pause |= MLO_PAUSE_TX; + } + +-static void mtk_mac_an_restart(struct phylink_config *config) +-{ +- struct mtk_mac *mac = container_of(config, struct mtk_mac, +- phylink_config); +- +- mtk_sgmii_restart_an(mac->hw, mac->id); +-} +- + static void mtk_mac_link_down(struct phylink_config *config, unsigned int mode, + phy_interface_t interface) + { +@@ -554,15 +556,6 @@ static void mtk_mac_link_up(struct phyli + phylink_config); + u32 mcr; + +- if (phy_interface_mode_is_8023z(interface)) { +- struct mtk_eth *eth = mac->hw; +- +- /* Decide how GMAC and SGMIISYS be mapped */ +- int sid = (MTK_HAS_CAPS(eth->soc->caps, MTK_SHARED_SGMII)) ? +- 0 : mac->id; +- mtk_sgmii_link_up(eth->sgmii, sid, speed, duplex); +- } +- + mcr = mtk_r32(mac->hw, MTK_MAC_MCR(mac->id)); + mcr &= ~(MAC_MCR_SPEED_100 | MAC_MCR_SPEED_1000 | + MAC_MCR_FORCE_DPX | MAC_MCR_FORCE_TX_FC | +@@ -595,8 +588,8 @@ static void mtk_mac_link_up(struct phyli + + static const struct phylink_mac_ops mtk_phylink_ops = { + .validate = phylink_generic_validate, ++ .mac_select_pcs = mtk_mac_select_pcs, + .mac_pcs_get_state = mtk_mac_pcs_get_state, +- .mac_an_restart = mtk_mac_an_restart, + .mac_config = mtk_mac_config, + .mac_finish = mtk_mac_finish, + .mac_link_down = mtk_mac_link_down, +--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h ++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h +@@ -963,10 +963,12 @@ struct mtk_soc_data { + * @regmap: The register map pointing at the range used to setup + * SGMII modes + * @ana_rgc3: The offset refers to register ANA_RGC3 related to regmap ++ * @pcs: Phylink PCS structure + */ + struct mtk_pcs { + struct regmap *regmap; + u32 ana_rgc3; ++ struct phylink_pcs pcs; + }; + + /* struct mtk_sgmii - This is the structure holding sgmii regmap and its +@@ -1106,12 +1108,9 @@ void mtk_stats_update_mac(struct mtk_mac + void mtk_w32(struct mtk_eth *eth, u32 val, unsigned reg); + u32 mtk_r32(struct mtk_eth *eth, unsigned reg); + ++struct phylink_pcs *mtk_sgmii_select_pcs(struct mtk_sgmii *ss, int id); + int mtk_sgmii_init(struct mtk_sgmii *ss, struct device_node *np, + u32 ana_rgc3); +-int mtk_sgmii_config(struct mtk_sgmii *ss, int id, unsigned int mode, +- phy_interface_t interface); +-void mtk_sgmii_link_up(struct mtk_sgmii *ss, int id, int speed, int duplex); +-void mtk_sgmii_restart_an(struct mtk_eth *eth, int mac_id); + + int mtk_gmac_sgmii_path_setup(struct mtk_eth *eth, int mac_id); + int mtk_gmac_gephy_path_setup(struct mtk_eth *eth, int mac_id); +--- a/drivers/net/ethernet/mediatek/mtk_sgmii.c ++++ b/drivers/net/ethernet/mediatek/mtk_sgmii.c +@@ -14,14 +14,16 @@ + + #include "mtk_eth_soc.h" + ++static struct mtk_pcs *pcs_to_mtk_pcs(struct phylink_pcs *pcs) ++{ ++ return container_of(pcs, struct mtk_pcs, pcs); ++} ++ + /* For SGMII interface mode */ + static int mtk_pcs_setup_mode_an(struct mtk_pcs *mpcs) + { + unsigned int val; + +- if (!mpcs->regmap) +- return -EINVAL; +- + /* Setup the link timer and QPHY power up inside SGMIISYS */ + regmap_write(mpcs->regmap, SGMSYS_PCS_LINK_TIMER, + SGMII_LINK_TIMER_DEFAULT); +@@ -50,9 +52,6 @@ static int mtk_pcs_setup_mode_force(stru + { + unsigned int val; + +- if (!mpcs->regmap) +- return -EINVAL; +- + regmap_read(mpcs->regmap, mpcs->ana_rgc3, &val); + val &= ~RG_PHY_SPEED_MASK; + if (interface == PHY_INTERFACE_MODE_2500BASEX) +@@ -78,10 +77,12 @@ static int mtk_pcs_setup_mode_force(stru + return 0; + } + +-int mtk_sgmii_config(struct mtk_sgmii *ss, int id, unsigned int mode, +- phy_interface_t interface) ++static int mtk_pcs_config(struct phylink_pcs *pcs, unsigned int mode, ++ phy_interface_t interface, ++ const unsigned long *advertising, ++ bool permit_pause_to_mac) + { +- struct mtk_pcs *mpcs = &ss->pcs[id]; ++ struct mtk_pcs *mpcs = pcs_to_mtk_pcs(pcs); + int err = 0; + + /* Setup SGMIISYS with the determined property */ +@@ -93,22 +94,25 @@ int mtk_sgmii_config(struct mtk_sgmii *s + return err; + } + +-static void mtk_pcs_restart_an(struct mtk_pcs *mpcs) ++static void mtk_pcs_restart_an(struct phylink_pcs *pcs) + { ++ struct mtk_pcs *mpcs = pcs_to_mtk_pcs(pcs); + unsigned int val; + +- if (!mpcs->regmap) +- return; +- + regmap_read(mpcs->regmap, SGMSYS_PCS_CONTROL_1, &val); + val |= SGMII_AN_RESTART; + regmap_write(mpcs->regmap, SGMSYS_PCS_CONTROL_1, val); + } + +-static void mtk_pcs_link_up(struct mtk_pcs *mpcs, int speed, int duplex) ++static void mtk_pcs_link_up(struct phylink_pcs *pcs, unsigned int mode, ++ phy_interface_t interface, int speed, int duplex) + { ++ struct mtk_pcs *mpcs = pcs_to_mtk_pcs(pcs); + unsigned int val; + ++ if (!phy_interface_mode_is_8023z(interface)) ++ return; ++ + /* SGMII force duplex setting */ + regmap_read(mpcs->regmap, SGMSYS_SGMII_MODE, &val); + val &= ~SGMII_DUPLEX_FULL; +@@ -118,11 +122,11 @@ static void mtk_pcs_link_up(struct mtk_p + regmap_write(mpcs->regmap, SGMSYS_SGMII_MODE, val); + } + +-/* For 1000BASE-X and 2500BASE-X interface modes */ +-void mtk_sgmii_link_up(struct mtk_sgmii *ss, int id, int speed, int duplex) +-{ +- mtk_pcs_link_up(&ss->pcs[id], speed, duplex); +-} ++static const struct phylink_pcs_ops mtk_pcs_ops = { ++ .pcs_config = mtk_pcs_config, ++ .pcs_an_restart = mtk_pcs_restart_an, ++ .pcs_link_up = mtk_pcs_link_up, ++}; + + int mtk_sgmii_init(struct mtk_sgmii *ss, struct device_node *r, u32 ana_rgc3) + { +@@ -139,18 +143,17 @@ int mtk_sgmii_init(struct mtk_sgmii *ss, + of_node_put(np); + if (IS_ERR(ss->pcs[i].regmap)) + return PTR_ERR(ss->pcs[i].regmap); ++ ++ ss->pcs[i].pcs.ops = &mtk_pcs_ops; + } + + return 0; + } + +-void mtk_sgmii_restart_an(struct mtk_eth *eth, int mac_id) ++struct phylink_pcs *mtk_sgmii_select_pcs(struct mtk_sgmii *ss, int id) + { +- unsigned int sid; +- +- /* Decide how GMAC and SGMIISYS be mapped */ +- sid = (MTK_HAS_CAPS(eth->soc->caps, MTK_SHARED_SGMII)) ? +- 0 : mac_id; ++ if (!ss->pcs[id].regmap) ++ return NULL; + +- mtk_pcs_restart_an(ð->sgmii->pcs[sid]); ++ return &ss->pcs[id].pcs; + } diff --git a/target/linux/generic/backport-6.1/705-01-v5.17-net-dsa-mt7530-iterate-using-dsa_switch_for_each_use.patch b/target/linux/generic/backport-6.1/705-01-v5.17-net-dsa-mt7530-iterate-using-dsa_switch_for_each_use.patch new file mode 100644 index 0000000000..e3f01ada6d --- /dev/null +++ b/target/linux/generic/backport-6.1/705-01-v5.17-net-dsa-mt7530-iterate-using-dsa_switch_for_each_use.patch @@ -0,0 +1,106 @@ +From 505560028b6deb9b4385cf6100f05ca6f4aacaf8 Mon Sep 17 00:00:00 2001 +From: Vladimir Oltean +Date: Mon, 6 Dec 2021 18:57:49 +0200 +Subject: [PATCH 01/13] net: dsa: mt7530: iterate using + dsa_switch_for_each_user_port in bridging ops + +Avoid repeated calls to dsa_to_port() (some hidden behind dsa_is_user_port +and some in plain sight) by keeping two struct dsa_port references: one +to the port passed as argument, and another to the other ports of the +switch that we're iterating over. + +dsa_to_port(ds, i) gets replaced by other_dp, i gets replaced by +other_port which is derived from other_dp->index, dsa_is_user_port is +handled by the DSA iterator. + +Signed-off-by: Vladimir Oltean +Signed-off-by: Jakub Kicinski +--- + drivers/net/dsa/mt7530.c | 52 +++++++++++++++++++++++----------------- + 1 file changed, 30 insertions(+), 22 deletions(-) + +--- a/drivers/net/dsa/mt7530.c ++++ b/drivers/net/dsa/mt7530.c +@@ -1191,27 +1191,31 @@ static int + mt7530_port_bridge_join(struct dsa_switch *ds, int port, + struct net_device *bridge) + { +- struct mt7530_priv *priv = ds->priv; ++ struct dsa_port *dp = dsa_to_port(ds, port), *other_dp; + u32 port_bitmap = BIT(MT7530_CPU_PORT); +- int i; ++ struct mt7530_priv *priv = ds->priv; + + mutex_lock(&priv->reg_mutex); + +- for (i = 0; i < MT7530_NUM_PORTS; i++) { ++ dsa_switch_for_each_user_port(other_dp, ds) { ++ int other_port = other_dp->index; ++ ++ if (dp == other_dp) ++ continue; ++ + /* Add this port to the port matrix of the other ports in the + * same bridge. If the port is disabled, port matrix is kept + * and not being setup until the port becomes enabled. + */ +- if (dsa_is_user_port(ds, i) && i != port) { +- if (dsa_to_port(ds, i)->bridge_dev != bridge) +- continue; +- if (priv->ports[i].enable) +- mt7530_set(priv, MT7530_PCR_P(i), +- PCR_MATRIX(BIT(port))); +- priv->ports[i].pm |= PCR_MATRIX(BIT(port)); ++ if (other_dp->bridge_dev != bridge) ++ continue; + +- port_bitmap |= BIT(i); +- } ++ if (priv->ports[other_port].enable) ++ mt7530_set(priv, MT7530_PCR_P(other_port), ++ PCR_MATRIX(BIT(port))); ++ priv->ports[other_port].pm |= PCR_MATRIX(BIT(port)); ++ ++ port_bitmap |= BIT(other_port); + } + + /* Add the all other ports to this port matrix. */ +@@ -1304,24 +1308,28 @@ static void + mt7530_port_bridge_leave(struct dsa_switch *ds, int port, + struct net_device *bridge) + { ++ struct dsa_port *dp = dsa_to_port(ds, port), *other_dp; + struct mt7530_priv *priv = ds->priv; +- int i; + + mutex_lock(&priv->reg_mutex); + +- for (i = 0; i < MT7530_NUM_PORTS; i++) { ++ dsa_switch_for_each_user_port(other_dp, ds) { ++ int other_port = other_dp->index; ++ ++ if (dp == other_dp) ++ continue; ++ + /* Remove this port from the port matrix of the other ports + * in the same bridge. If the port is disabled, port matrix + * is kept and not being setup until the port becomes enabled. + */ +- if (dsa_is_user_port(ds, i) && i != port) { +- if (dsa_to_port(ds, i)->bridge_dev != bridge) +- continue; +- if (priv->ports[i].enable) +- mt7530_clear(priv, MT7530_PCR_P(i), +- PCR_MATRIX(BIT(port))); +- priv->ports[i].pm &= ~PCR_MATRIX(BIT(port)); +- } ++ if (other_dp->bridge_dev != bridge) ++ continue; ++ ++ if (priv->ports[other_port].enable) ++ mt7530_clear(priv, MT7530_PCR_P(other_port), ++ PCR_MATRIX(BIT(port))); ++ priv->ports[other_port].pm &= ~PCR_MATRIX(BIT(port)); + } + + /* Set the cpu port to be the only one in the port matrix of diff --git a/target/linux/generic/backport-6.1/705-02-v5.19-net-dsa-mt7530-populate-supported_interfaces-and-mac.patch b/target/linux/generic/backport-6.1/705-02-v5.19-net-dsa-mt7530-populate-supported_interfaces-and-mac.patch new file mode 100644 index 0000000000..94b8cb053d --- /dev/null +++ b/target/linux/generic/backport-6.1/705-02-v5.19-net-dsa-mt7530-populate-supported_interfaces-and-mac.patch @@ -0,0 +1,166 @@ +From a1da54bcd664fc27169386db966575675ac3ccb0 Mon Sep 17 00:00:00 2001 +From: "Russell King (Oracle)" +Date: Mon, 11 Apr 2022 10:46:01 +0100 +Subject: [PATCH 02/13] net: dsa: mt7530: 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 mt7530, +mt7531 and mt7621 DSA switches. Filling this in will enable phylink +to pre-check the PHY interface mode against the the supported +interfaces bitmap prior to calling the validate function, and will +eventually allow us to convert to using the generic validation. + +Tested-by: Marek Behún +Signed-off-by: Russell King (Oracle) +Signed-off-by: Paolo Abeni +--- + drivers/net/dsa/mt7530.c | 74 ++++++++++++++++++++++++++++++++++++++++ + drivers/net/dsa/mt7530.h | 2 ++ + 2 files changed, 76 insertions(+) + +--- a/drivers/net/dsa/mt7530.c ++++ b/drivers/net/dsa/mt7530.c +@@ -2390,6 +2390,32 @@ mt7531_setup(struct dsa_switch *ds) + return 0; + } + ++static void mt7530_mac_port_get_caps(struct dsa_switch *ds, int port, ++ struct phylink_config *config) ++{ ++ switch (port) { ++ case 0 ... 4: /* Internal phy */ ++ __set_bit(PHY_INTERFACE_MODE_GMII, ++ config->supported_interfaces); ++ break; ++ ++ case 5: /* 2nd cpu port with phy of port 0 or 4 / external phy */ ++ phy_interface_set_rgmii(config->supported_interfaces); ++ __set_bit(PHY_INTERFACE_MODE_MII, ++ config->supported_interfaces); ++ __set_bit(PHY_INTERFACE_MODE_GMII, ++ config->supported_interfaces); ++ break; ++ ++ case 6: /* 1st cpu port */ ++ __set_bit(PHY_INTERFACE_MODE_RGMII, ++ config->supported_interfaces); ++ __set_bit(PHY_INTERFACE_MODE_TRGMII, ++ config->supported_interfaces); ++ break; ++ } ++} ++ + static bool + mt7530_phy_mode_supported(struct dsa_switch *ds, int port, + const struct phylink_link_state *state) +@@ -2426,6 +2452,37 @@ static bool mt7531_is_rgmii_port(struct + return (port == 5) && (priv->p5_intf_sel != P5_INTF_SEL_GMAC5_SGMII); + } + ++static void mt7531_mac_port_get_caps(struct dsa_switch *ds, int port, ++ struct phylink_config *config) ++{ ++ struct mt7530_priv *priv = ds->priv; ++ ++ switch (port) { ++ case 0 ... 4: /* Internal phy */ ++ __set_bit(PHY_INTERFACE_MODE_GMII, ++ config->supported_interfaces); ++ break; ++ ++ case 5: /* 2nd cpu port supports either rgmii or sgmii/8023z */ ++ if (mt7531_is_rgmii_port(priv, port)) { ++ phy_interface_set_rgmii(config->supported_interfaces); ++ break; ++ } ++ fallthrough; ++ ++ case 6: /* 1st cpu port supports sgmii/8023z only */ ++ __set_bit(PHY_INTERFACE_MODE_SGMII, ++ config->supported_interfaces); ++ __set_bit(PHY_INTERFACE_MODE_1000BASEX, ++ config->supported_interfaces); ++ __set_bit(PHY_INTERFACE_MODE_2500BASEX, ++ config->supported_interfaces); ++ ++ config->mac_capabilities |= MAC_2500FD; ++ break; ++ } ++} ++ + static bool + mt7531_phy_mode_supported(struct dsa_switch *ds, int port, + const struct phylink_link_state *state) +@@ -2902,6 +2959,18 @@ mt7531_cpu_port_config(struct dsa_switch + return 0; + } + ++static void mt753x_phylink_get_caps(struct dsa_switch *ds, int port, ++ struct phylink_config *config) ++{ ++ struct mt7530_priv *priv = ds->priv; ++ ++ /* This switch only supports full-duplex at 1Gbps */ ++ config->mac_capabilities = MAC_ASYM_PAUSE | MAC_SYM_PAUSE | ++ MAC_10 | MAC_100 | MAC_1000FD; ++ ++ priv->info->mac_port_get_caps(ds, port, config); ++} ++ + static void + mt7530_mac_port_validate(struct dsa_switch *ds, int port, + unsigned long *supported) +@@ -3137,6 +3206,7 @@ static const struct dsa_switch_ops mt753 + .port_vlan_del = mt7530_port_vlan_del, + .port_mirror_add = mt753x_port_mirror_add, + .port_mirror_del = mt753x_port_mirror_del, ++ .phylink_get_caps = mt753x_phylink_get_caps, + .phylink_validate = mt753x_phylink_validate, + .phylink_mac_link_state = mt753x_phylink_mac_link_state, + .phylink_mac_config = mt753x_phylink_mac_config, +@@ -3154,6 +3224,7 @@ static const struct mt753x_info mt753x_t + .phy_read = mt7530_phy_read, + .phy_write = mt7530_phy_write, + .pad_setup = mt7530_pad_clk_setup, ++ .mac_port_get_caps = mt7530_mac_port_get_caps, + .phy_mode_supported = mt7530_phy_mode_supported, + .mac_port_validate = mt7530_mac_port_validate, + .mac_port_get_state = mt7530_phylink_mac_link_state, +@@ -3165,6 +3236,7 @@ static const struct mt753x_info mt753x_t + .phy_read = mt7530_phy_read, + .phy_write = mt7530_phy_write, + .pad_setup = mt7530_pad_clk_setup, ++ .mac_port_get_caps = mt7530_mac_port_get_caps, + .phy_mode_supported = mt7530_phy_mode_supported, + .mac_port_validate = mt7530_mac_port_validate, + .mac_port_get_state = mt7530_phylink_mac_link_state, +@@ -3177,6 +3249,7 @@ static const struct mt753x_info mt753x_t + .phy_write = mt7531_ind_phy_write, + .pad_setup = mt7531_pad_setup, + .cpu_port_config = mt7531_cpu_port_config, ++ .mac_port_get_caps = mt7531_mac_port_get_caps, + .phy_mode_supported = mt7531_phy_mode_supported, + .mac_port_validate = mt7531_mac_port_validate, + .mac_port_get_state = mt7531_phylink_mac_link_state, +@@ -3239,6 +3312,7 @@ mt7530_probe(struct mdio_device *mdiodev + */ + if (!priv->info->sw_setup || !priv->info->pad_setup || + !priv->info->phy_read || !priv->info->phy_write || ++ !priv->info->mac_port_get_caps || + !priv->info->phy_mode_supported || + !priv->info->mac_port_validate || + !priv->info->mac_port_get_state || !priv->info->mac_port_config) +--- a/drivers/net/dsa/mt7530.h ++++ b/drivers/net/dsa/mt7530.h +@@ -769,6 +769,8 @@ struct mt753x_info { + int (*phy_write)(struct mt7530_priv *priv, int port, int regnum, u16 val); + int (*pad_setup)(struct dsa_switch *ds, phy_interface_t interface); + int (*cpu_port_config)(struct dsa_switch *ds, int port); ++ void (*mac_port_get_caps)(struct dsa_switch *ds, int port, ++ struct phylink_config *config); + bool (*phy_mode_supported)(struct dsa_switch *ds, int port, + const struct phylink_link_state *state); + void (*mac_port_validate)(struct dsa_switch *ds, int port, diff --git a/target/linux/generic/backport-6.1/705-03-v5.19-net-dsa-mt7530-remove-interface-checks.patch b/target/linux/generic/backport-6.1/705-03-v5.19-net-dsa-mt7530-remove-interface-checks.patch new file mode 100644 index 0000000000..322dc6f6ef --- /dev/null +++ b/target/linux/generic/backport-6.1/705-03-v5.19-net-dsa-mt7530-remove-interface-checks.patch @@ -0,0 +1,172 @@ +From e3f6719e2269868ca129b05da50cd55786848954 Mon Sep 17 00:00:00 2001 +From: "Russell King (Oracle)" +Date: Mon, 11 Apr 2022 10:46:06 +0100 +Subject: [PATCH 03/13] net: dsa: mt7530: remove interface checks +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +As phylink checks the interface mode against the supported_interfaces +bitmap, we no longer need to validate the interface mode, nor handle +PHY_INTERFACE_MODE_NA in the validation function. Remove these to +simplify the implementation. + +Tested-by: Marek Behún +Signed-off-by: Russell King (Oracle) +Signed-off-by: Paolo Abeni +--- + drivers/net/dsa/mt7530.c | 82 ---------------------------------------- + drivers/net/dsa/mt7530.h | 2 - + 2 files changed, 84 deletions(-) + +--- a/drivers/net/dsa/mt7530.c ++++ b/drivers/net/dsa/mt7530.c +@@ -2416,37 +2416,6 @@ static void mt7530_mac_port_get_caps(str + } + } + +-static bool +-mt7530_phy_mode_supported(struct dsa_switch *ds, int port, +- const struct phylink_link_state *state) +-{ +- struct mt7530_priv *priv = ds->priv; +- +- switch (port) { +- case 0 ... 4: /* Internal phy */ +- if (state->interface != PHY_INTERFACE_MODE_GMII) +- return false; +- break; +- case 5: /* 2nd cpu port with phy of port 0 or 4 / external phy */ +- if (!phy_interface_mode_is_rgmii(state->interface) && +- state->interface != PHY_INTERFACE_MODE_MII && +- state->interface != PHY_INTERFACE_MODE_GMII) +- return false; +- break; +- case 6: /* 1st cpu port */ +- if (state->interface != PHY_INTERFACE_MODE_RGMII && +- state->interface != PHY_INTERFACE_MODE_TRGMII) +- return false; +- break; +- default: +- dev_err(priv->dev, "%s: unsupported port: %i\n", __func__, +- port); +- return false; +- } +- +- return true; +-} +- + static bool mt7531_is_rgmii_port(struct mt7530_priv *priv, u32 port) + { + return (port == 5) && (priv->p5_intf_sel != P5_INTF_SEL_GMAC5_SGMII); +@@ -2483,44 +2452,6 @@ static void mt7531_mac_port_get_caps(str + } + } + +-static bool +-mt7531_phy_mode_supported(struct dsa_switch *ds, int port, +- const struct phylink_link_state *state) +-{ +- struct mt7530_priv *priv = ds->priv; +- +- switch (port) { +- case 0 ... 4: /* Internal phy */ +- if (state->interface != PHY_INTERFACE_MODE_GMII) +- return false; +- break; +- case 5: /* 2nd cpu port supports either rgmii or sgmii/8023z */ +- if (mt7531_is_rgmii_port(priv, port)) +- return phy_interface_mode_is_rgmii(state->interface); +- fallthrough; +- case 6: /* 1st cpu port supports sgmii/8023z only */ +- if (state->interface != PHY_INTERFACE_MODE_SGMII && +- !phy_interface_mode_is_8023z(state->interface)) +- return false; +- break; +- default: +- dev_err(priv->dev, "%s: unsupported port: %i\n", __func__, +- port); +- return false; +- } +- +- return true; +-} +- +-static bool +-mt753x_phy_mode_supported(struct dsa_switch *ds, int port, +- const struct phylink_link_state *state) +-{ +- struct mt7530_priv *priv = ds->priv; +- +- return priv->info->phy_mode_supported(ds, port, state); +-} +- + static int + mt753x_pad_setup(struct dsa_switch *ds, const struct phylink_link_state *state) + { +@@ -2775,9 +2706,6 @@ mt753x_phylink_mac_config(struct dsa_swi + struct mt7530_priv *priv = ds->priv; + u32 mcr_cur, mcr_new; + +- if (!mt753x_phy_mode_supported(ds, port, state)) +- goto unsupported; +- + switch (port) { + case 0 ... 4: /* Internal phy */ + if (state->interface != PHY_INTERFACE_MODE_GMII) +@@ -2993,12 +2921,6 @@ mt753x_phylink_validate(struct dsa_switc + __ETHTOOL_DECLARE_LINK_MODE_MASK(mask) = { 0, }; + struct mt7530_priv *priv = ds->priv; + +- if (state->interface != PHY_INTERFACE_MODE_NA && +- !mt753x_phy_mode_supported(ds, port, state)) { +- linkmode_zero(supported); +- return; +- } +- + phylink_set_port_modes(mask); + + if (state->interface != PHY_INTERFACE_MODE_TRGMII && +@@ -3225,7 +3147,6 @@ static const struct mt753x_info mt753x_t + .phy_write = mt7530_phy_write, + .pad_setup = mt7530_pad_clk_setup, + .mac_port_get_caps = mt7530_mac_port_get_caps, +- .phy_mode_supported = mt7530_phy_mode_supported, + .mac_port_validate = mt7530_mac_port_validate, + .mac_port_get_state = mt7530_phylink_mac_link_state, + .mac_port_config = mt7530_mac_config, +@@ -3237,7 +3158,6 @@ static const struct mt753x_info mt753x_t + .phy_write = mt7530_phy_write, + .pad_setup = mt7530_pad_clk_setup, + .mac_port_get_caps = mt7530_mac_port_get_caps, +- .phy_mode_supported = mt7530_phy_mode_supported, + .mac_port_validate = mt7530_mac_port_validate, + .mac_port_get_state = mt7530_phylink_mac_link_state, + .mac_port_config = mt7530_mac_config, +@@ -3250,7 +3170,6 @@ static const struct mt753x_info mt753x_t + .pad_setup = mt7531_pad_setup, + .cpu_port_config = mt7531_cpu_port_config, + .mac_port_get_caps = mt7531_mac_port_get_caps, +- .phy_mode_supported = mt7531_phy_mode_supported, + .mac_port_validate = mt7531_mac_port_validate, + .mac_port_get_state = mt7531_phylink_mac_link_state, + .mac_port_config = mt7531_mac_config, +@@ -3313,7 +3232,6 @@ mt7530_probe(struct mdio_device *mdiodev + if (!priv->info->sw_setup || !priv->info->pad_setup || + !priv->info->phy_read || !priv->info->phy_write || + !priv->info->mac_port_get_caps || +- !priv->info->phy_mode_supported || + !priv->info->mac_port_validate || + !priv->info->mac_port_get_state || !priv->info->mac_port_config) + return -EINVAL; +--- a/drivers/net/dsa/mt7530.h ++++ b/drivers/net/dsa/mt7530.h +@@ -771,8 +771,6 @@ struct mt753x_info { + int (*cpu_port_config)(struct dsa_switch *ds, int port); + void (*mac_port_get_caps)(struct dsa_switch *ds, int port, + struct phylink_config *config); +- bool (*phy_mode_supported)(struct dsa_switch *ds, int port, +- const struct phylink_link_state *state); + void (*mac_port_validate)(struct dsa_switch *ds, int port, + unsigned long *supported); + int (*mac_port_get_state)(struct dsa_switch *ds, int port, diff --git a/target/linux/generic/backport-6.1/705-04-v5.19-net-dsa-mt7530-drop-use-of-phylink_helper_basex_spee.patch b/target/linux/generic/backport-6.1/705-04-v5.19-net-dsa-mt7530-drop-use-of-phylink_helper_basex_spee.patch new file mode 100644 index 0000000000..36b3aab33b --- /dev/null +++ b/target/linux/generic/backport-6.1/705-04-v5.19-net-dsa-mt7530-drop-use-of-phylink_helper_basex_spee.patch @@ -0,0 +1,34 @@ +From 58344a3b85f1bd5ffddfc2c11f6f2bf688b5f990 Mon Sep 17 00:00:00 2001 +From: "Russell King (Oracle)" +Date: Mon, 11 Apr 2022 10:46:12 +0100 +Subject: [PATCH 04/13] net: dsa: mt7530: drop use of + phylink_helper_basex_speed() +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +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. + +Tested-by: Marek Behún +Signed-off-by: Russell King (Oracle) +Signed-off-by: Paolo Abeni +--- + drivers/net/dsa/mt7530.c | 5 ----- + 1 file changed, 5 deletions(-) + +--- a/drivers/net/dsa/mt7530.c ++++ b/drivers/net/dsa/mt7530.c +@@ -2945,11 +2945,6 @@ mt753x_phylink_validate(struct dsa_switc + + 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 int diff --git a/target/linux/generic/backport-6.1/705-05-v5.19-net-dsa-mt7530-only-indicate-linkmodes-that-can-be-s.patch b/target/linux/generic/backport-6.1/705-05-v5.19-net-dsa-mt7530-only-indicate-linkmodes-that-can-be-s.patch new file mode 100644 index 0000000000..831f509341 --- /dev/null +++ b/target/linux/generic/backport-6.1/705-05-v5.19-net-dsa-mt7530-only-indicate-linkmodes-that-can-be-s.patch @@ -0,0 +1,86 @@ +From 3c1d788a62dc648d1846049b66119ebb69dedd52 Mon Sep 17 00:00:00 2001 +From: "Russell King (Oracle)" +Date: Mon, 11 Apr 2022 10:46:17 +0100 +Subject: [PATCH 05/13] net: dsa: mt7530: only indicate linkmodes that can be + supported +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Now that mt7530 is not using the basex helper, it becomes unnecessary to +indicate support for both 1000baseX and 2500baseX when one of the 803.3z +PHY interface modes is being selected. Ensure that the driver indicates +only those linkmodes that can actually be supported by the PHY interface +mode. + +Tested-by: Marek Behún +Signed-off-by: Russell King (Oracle) +Signed-off-by: Paolo Abeni +--- + drivers/net/dsa/mt7530.c | 12 ++++++++---- + drivers/net/dsa/mt7530.h | 1 + + 2 files changed, 9 insertions(+), 4 deletions(-) + +--- a/drivers/net/dsa/mt7530.c ++++ b/drivers/net/dsa/mt7530.c +@@ -2523,12 +2523,13 @@ static int mt7531_rgmii_setup(struct mt7 + } + + static void mt7531_sgmii_validate(struct mt7530_priv *priv, int port, ++ phy_interface_t interface, + unsigned long *supported) + { + /* Port5 supports ethier RGMII or SGMII. + * Port6 supports SGMII only. + */ +- if (port == 6) { ++ if (port == 6 && interface == PHY_INTERFACE_MODE_2500BASEX) { + phylink_set(supported, 2500baseX_Full); + phylink_set(supported, 2500baseT_Full); + } +@@ -2901,16 +2902,18 @@ static void mt753x_phylink_get_caps(stru + + static void + mt7530_mac_port_validate(struct dsa_switch *ds, int port, ++ phy_interface_t interface, + unsigned long *supported) + { + } + + static void mt7531_mac_port_validate(struct dsa_switch *ds, int port, ++ phy_interface_t interface, + unsigned long *supported) + { + struct mt7530_priv *priv = ds->priv; + +- mt7531_sgmii_validate(priv, port, supported); ++ mt7531_sgmii_validate(priv, port, interface, supported); + } + + static void +@@ -2933,12 +2936,13 @@ mt753x_phylink_validate(struct dsa_switc + } + + /* This switch only supports 1G full-duplex. */ +- if (state->interface != PHY_INTERFACE_MODE_MII) { ++ if (state->interface != PHY_INTERFACE_MODE_MII && ++ state->interface != PHY_INTERFACE_MODE_2500BASEX) { + phylink_set(mask, 1000baseT_Full); + phylink_set(mask, 1000baseX_Full); + } + +- priv->info->mac_port_validate(ds, port, mask); ++ priv->info->mac_port_validate(ds, port, state->interface, mask); + + phylink_set(mask, Pause); + phylink_set(mask, Asym_Pause); +--- a/drivers/net/dsa/mt7530.h ++++ b/drivers/net/dsa/mt7530.h +@@ -772,6 +772,7 @@ struct mt753x_info { + void (*mac_port_get_caps)(struct dsa_switch *ds, int port, + struct phylink_config *config); + void (*mac_port_validate)(struct dsa_switch *ds, int port, ++ phy_interface_t interface, + unsigned long *supported); + int (*mac_port_get_state)(struct dsa_switch *ds, int port, + struct phylink_link_state *state); diff --git a/target/linux/generic/backport-6.1/705-06-v5.19-net-dsa-mt7530-switch-to-use-phylink_get_linkmodes.patch b/target/linux/generic/backport-6.1/705-06-v5.19-net-dsa-mt7530-switch-to-use-phylink_get_linkmodes.patch new file mode 100644 index 0000000000..9f54f2789b --- /dev/null +++ b/target/linux/generic/backport-6.1/705-06-v5.19-net-dsa-mt7530-switch-to-use-phylink_get_linkmodes.patch @@ -0,0 +1,131 @@ +From 1c2211cb15dd3957fb26c0e1615eceb5db851ad6 Mon Sep 17 00:00:00 2001 +From: "Russell King (Oracle)" +Date: Mon, 11 Apr 2022 10:46:22 +0100 +Subject: [PATCH 06/13] net: dsa: mt7530: switch to use phylink_get_linkmodes() +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Switch mt7530 to use phylink_get_linkmodes() to generate the ethtool +linkmodes that can be supported. We are unable to use the generic +helper for this as pause modes are dependent on the interface as +the Autoneg bit depends on the interface mode. + +Tested-by: Marek Behún +Signed-off-by: Russell King (Oracle) +Signed-off-by: Paolo Abeni +--- + drivers/net/dsa/mt7530.c | 57 ++++------------------------------------ + 1 file changed, 5 insertions(+), 52 deletions(-) + +--- a/drivers/net/dsa/mt7530.c ++++ b/drivers/net/dsa/mt7530.c +@@ -2522,19 +2522,6 @@ static int mt7531_rgmii_setup(struct mt7 + return 0; + } + +-static void mt7531_sgmii_validate(struct mt7530_priv *priv, int port, +- phy_interface_t interface, +- unsigned long *supported) +-{ +- /* Port5 supports ethier RGMII or SGMII. +- * Port6 supports SGMII only. +- */ +- if (port == 6 && interface == PHY_INTERFACE_MODE_2500BASEX) { +- phylink_set(supported, 2500baseX_Full); +- phylink_set(supported, 2500baseT_Full); +- } +-} +- + static void + mt7531_sgmii_link_up_force(struct dsa_switch *ds, int port, + unsigned int mode, phy_interface_t interface, +@@ -2901,51 +2888,21 @@ static void mt753x_phylink_get_caps(stru + } + + static void +-mt7530_mac_port_validate(struct dsa_switch *ds, int port, +- phy_interface_t interface, +- unsigned long *supported) +-{ +-} +- +-static void mt7531_mac_port_validate(struct dsa_switch *ds, int port, +- phy_interface_t interface, +- unsigned long *supported) +-{ +- struct mt7530_priv *priv = ds->priv; +- +- mt7531_sgmii_validate(priv, port, interface, supported); +-} +- +-static void + mt753x_phylink_validate(struct dsa_switch *ds, int port, + unsigned long *supported, + struct phylink_link_state *state) + { + __ETHTOOL_DECLARE_LINK_MODE_MASK(mask) = { 0, }; +- struct mt7530_priv *priv = ds->priv; ++ u32 caps; ++ ++ caps = dsa_to_port(ds, port)->pl_config.mac_capabilities; + + phylink_set_port_modes(mask); ++ phylink_get_linkmodes(mask, state->interface, caps); + + if (state->interface != PHY_INTERFACE_MODE_TRGMII && +- !phy_interface_mode_is_8023z(state->interface)) { +- phylink_set(mask, 10baseT_Half); +- phylink_set(mask, 10baseT_Full); +- phylink_set(mask, 100baseT_Half); +- phylink_set(mask, 100baseT_Full); ++ !phy_interface_mode_is_8023z(state->interface)) + phylink_set(mask, Autoneg); +- } +- +- /* This switch only supports 1G full-duplex. */ +- if (state->interface != PHY_INTERFACE_MODE_MII && +- state->interface != PHY_INTERFACE_MODE_2500BASEX) { +- phylink_set(mask, 1000baseT_Full); +- phylink_set(mask, 1000baseX_Full); +- } +- +- priv->info->mac_port_validate(ds, port, state->interface, mask); +- +- phylink_set(mask, Pause); +- phylink_set(mask, Asym_Pause); + + linkmode_and(supported, supported, mask); + linkmode_and(state->advertising, state->advertising, mask); +@@ -3146,7 +3103,6 @@ static const struct mt753x_info mt753x_t + .phy_write = mt7530_phy_write, + .pad_setup = mt7530_pad_clk_setup, + .mac_port_get_caps = mt7530_mac_port_get_caps, +- .mac_port_validate = mt7530_mac_port_validate, + .mac_port_get_state = mt7530_phylink_mac_link_state, + .mac_port_config = mt7530_mac_config, + }, +@@ -3157,7 +3113,6 @@ static const struct mt753x_info mt753x_t + .phy_write = mt7530_phy_write, + .pad_setup = mt7530_pad_clk_setup, + .mac_port_get_caps = mt7530_mac_port_get_caps, +- .mac_port_validate = mt7530_mac_port_validate, + .mac_port_get_state = mt7530_phylink_mac_link_state, + .mac_port_config = mt7530_mac_config, + }, +@@ -3169,7 +3124,6 @@ static const struct mt753x_info mt753x_t + .pad_setup = mt7531_pad_setup, + .cpu_port_config = mt7531_cpu_port_config, + .mac_port_get_caps = mt7531_mac_port_get_caps, +- .mac_port_validate = mt7531_mac_port_validate, + .mac_port_get_state = mt7531_phylink_mac_link_state, + .mac_port_config = mt7531_mac_config, + .mac_pcs_an_restart = mt7531_sgmii_restart_an, +@@ -3231,7 +3185,6 @@ mt7530_probe(struct mdio_device *mdiodev + if (!priv->info->sw_setup || !priv->info->pad_setup || + !priv->info->phy_read || !priv->info->phy_write || + !priv->info->mac_port_get_caps || +- !priv->info->mac_port_validate || + !priv->info->mac_port_get_state || !priv->info->mac_port_config) + return -EINVAL; + diff --git a/target/linux/generic/backport-6.1/705-07-v5.19-net-dsa-mt7530-partially-convert-to-phylink_pcs.patch b/target/linux/generic/backport-6.1/705-07-v5.19-net-dsa-mt7530-partially-convert-to-phylink_pcs.patch new file mode 100644 index 0000000000..c07e627c77 --- /dev/null +++ b/target/linux/generic/backport-6.1/705-07-v5.19-net-dsa-mt7530-partially-convert-to-phylink_pcs.patch @@ -0,0 +1,385 @@ +From fd993fd59d96d5e2d5972ec4ca1f9651025c987b Mon Sep 17 00:00:00 2001 +From: "Russell King (Oracle)" +Date: Mon, 11 Apr 2022 10:46:27 +0100 +Subject: [PATCH 07/13] net: dsa: mt7530: partially convert to phylink_pcs +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Partially convert the mt7530 driver to use phylink's PCS support. This +is a partial implementation as we don't move anything into the +pcs_config method yet - this driver supports SGMII or 1000BASE-X +without in-band. + +Tested-by: Marek Behún +Signed-off-by: Russell King (Oracle) +Signed-off-by: Paolo Abeni +--- + drivers/net/dsa/mt7530.c | 144 +++++++++++++++++++++++---------------- + drivers/net/dsa/mt7530.h | 21 +++--- + 2 files changed, 95 insertions(+), 70 deletions(-) + +--- a/drivers/net/dsa/mt7530.c ++++ b/drivers/net/dsa/mt7530.c +@@ -24,6 +24,11 @@ + + #include "mt7530.h" + ++static struct mt753x_pcs *pcs_to_mt753x_pcs(struct phylink_pcs *pcs) ++{ ++ return container_of(pcs, struct mt753x_pcs, pcs); ++} ++ + /* String, offset, and register size in bytes if different from 4 bytes */ + static const struct mt7530_mib_desc mt7530_mib[] = { + MIB_DESC(1, 0x00, "TxDrop"), +@@ -2522,12 +2527,11 @@ static int mt7531_rgmii_setup(struct mt7 + return 0; + } + +-static void +-mt7531_sgmii_link_up_force(struct dsa_switch *ds, int port, +- unsigned int mode, phy_interface_t interface, +- int speed, int duplex) ++static void mt7531_pcs_link_up(struct phylink_pcs *pcs, unsigned int mode, ++ phy_interface_t interface, int speed, int duplex) + { +- struct mt7530_priv *priv = ds->priv; ++ struct mt7530_priv *priv = pcs_to_mt753x_pcs(pcs)->priv; ++ int port = pcs_to_mt753x_pcs(pcs)->port; + unsigned int val; + + /* For adjusting speed and duplex of SGMII force mode. */ +@@ -2553,6 +2557,9 @@ mt7531_sgmii_link_up_force(struct dsa_sw + + /* MT7531 SGMII 1G force mode can only work in full duplex mode, + * no matter MT7531_SGMII_FORCE_HALF_DUPLEX is set or not. ++ * ++ * The speed check is unnecessary as the MAC capabilities apply ++ * this restriction. --rmk + */ + if ((speed == SPEED_10 || speed == SPEED_100) && + duplex != DUPLEX_FULL) +@@ -2628,9 +2635,10 @@ static int mt7531_sgmii_setup_mode_an(st + return 0; + } + +-static void mt7531_sgmii_restart_an(struct dsa_switch *ds, int port) ++static void mt7531_pcs_an_restart(struct phylink_pcs *pcs) + { +- struct mt7530_priv *priv = ds->priv; ++ struct mt7530_priv *priv = pcs_to_mt753x_pcs(pcs)->priv; ++ int port = pcs_to_mt753x_pcs(pcs)->port; + u32 val; + + /* Only restart AN when AN is enabled */ +@@ -2687,6 +2695,24 @@ mt753x_mac_config(struct dsa_switch *ds, + return priv->info->mac_port_config(ds, port, mode, state->interface); + } + ++static struct phylink_pcs * ++mt753x_phylink_mac_select_pcs(struct dsa_switch *ds, int port, ++ phy_interface_t interface) ++{ ++ struct mt7530_priv *priv = ds->priv; ++ ++ switch (interface) { ++ case PHY_INTERFACE_MODE_TRGMII: ++ case PHY_INTERFACE_MODE_SGMII: ++ case PHY_INTERFACE_MODE_1000BASEX: ++ case PHY_INTERFACE_MODE_2500BASEX: ++ return &priv->pcs[port].pcs; ++ ++ default: ++ return NULL; ++ } ++} ++ + static void + mt753x_phylink_mac_config(struct dsa_switch *ds, int port, unsigned int mode, + const struct phylink_link_state *state) +@@ -2748,17 +2774,6 @@ unsupported: + mt7530_write(priv, MT7530_PMCR_P(port), mcr_new); + } + +-static void +-mt753x_phylink_mac_an_restart(struct dsa_switch *ds, int port) +-{ +- struct mt7530_priv *priv = ds->priv; +- +- if (!priv->info->mac_pcs_an_restart) +- return; +- +- priv->info->mac_pcs_an_restart(ds, port); +-} +- + static void mt753x_phylink_mac_link_down(struct dsa_switch *ds, int port, + unsigned int mode, + phy_interface_t interface) +@@ -2768,16 +2783,13 @@ static void mt753x_phylink_mac_link_down + mt7530_clear(priv, MT7530_PMCR_P(port), PMCR_LINK_SETTINGS_MASK); + } + +-static void mt753x_mac_pcs_link_up(struct dsa_switch *ds, int port, +- unsigned int mode, phy_interface_t interface, +- int speed, int duplex) ++static void mt753x_phylink_pcs_link_up(struct phylink_pcs *pcs, ++ unsigned int mode, ++ phy_interface_t interface, ++ int speed, int duplex) + { +- struct mt7530_priv *priv = ds->priv; +- +- if (!priv->info->mac_pcs_link_up) +- return; +- +- priv->info->mac_pcs_link_up(ds, port, mode, interface, speed, duplex); ++ if (pcs->ops->pcs_link_up) ++ pcs->ops->pcs_link_up(pcs, mode, interface, speed, duplex); + } + + static void mt753x_phylink_mac_link_up(struct dsa_switch *ds, int port, +@@ -2790,8 +2802,6 @@ static void mt753x_phylink_mac_link_up(s + struct mt7530_priv *priv = ds->priv; + u32 mcr; + +- mt753x_mac_pcs_link_up(ds, port, mode, interface, speed, duplex); +- + mcr = PMCR_RX_EN | PMCR_TX_EN | PMCR_FORCE_LNK; + + /* MT753x MAC works in 1G full duplex mode for all up-clocked +@@ -2869,6 +2879,8 @@ mt7531_cpu_port_config(struct dsa_switch + return ret; + mt7530_write(priv, MT7530_PMCR_P(port), + PMCR_CPU_PORT_SETTING(priv->id)); ++ mt753x_phylink_pcs_link_up(&priv->pcs[port].pcs, MLO_AN_FIXED, ++ interface, speed, DUPLEX_FULL); + mt753x_phylink_mac_link_up(ds, port, MLO_AN_FIXED, interface, NULL, + speed, DUPLEX_FULL, true, true); + +@@ -2908,16 +2920,13 @@ mt753x_phylink_validate(struct dsa_switc + linkmode_and(state->advertising, state->advertising, mask); + } + +-static int +-mt7530_phylink_mac_link_state(struct dsa_switch *ds, int port, +- struct phylink_link_state *state) ++static void mt7530_pcs_get_state(struct phylink_pcs *pcs, ++ struct phylink_link_state *state) + { +- struct mt7530_priv *priv = ds->priv; ++ struct mt7530_priv *priv = pcs_to_mt753x_pcs(pcs)->priv; ++ int port = pcs_to_mt753x_pcs(pcs)->port; + u32 pmsr; + +- if (port < 0 || port >= MT7530_NUM_PORTS) +- return -EINVAL; +- + pmsr = mt7530_read(priv, MT7530_PMSR_P(port)); + + state->link = (pmsr & PMSR_LINK); +@@ -2944,8 +2953,6 @@ mt7530_phylink_mac_link_state(struct dsa + state->pause |= MLO_PAUSE_RX; + if (pmsr & PMSR_TX_FC) + state->pause |= MLO_PAUSE_TX; +- +- return 1; + } + + static int +@@ -2987,32 +2994,49 @@ mt7531_sgmii_pcs_get_state_an(struct mt7 + return 0; + } + +-static int +-mt7531_phylink_mac_link_state(struct dsa_switch *ds, int port, +- struct phylink_link_state *state) ++static void mt7531_pcs_get_state(struct phylink_pcs *pcs, ++ struct phylink_link_state *state) + { +- struct mt7530_priv *priv = ds->priv; ++ struct mt7530_priv *priv = pcs_to_mt753x_pcs(pcs)->priv; ++ int port = pcs_to_mt753x_pcs(pcs)->port; + + if (state->interface == PHY_INTERFACE_MODE_SGMII) +- return mt7531_sgmii_pcs_get_state_an(priv, port, state); +- +- return -EOPNOTSUPP; ++ mt7531_sgmii_pcs_get_state_an(priv, port, state); ++ else ++ state->link = false; + } + +-static int +-mt753x_phylink_mac_link_state(struct dsa_switch *ds, int port, +- struct phylink_link_state *state) ++static int mt753x_pcs_config(struct phylink_pcs *pcs, unsigned int mode, ++ phy_interface_t interface, ++ const unsigned long *advertising, ++ bool permit_pause_to_mac) + { +- struct mt7530_priv *priv = ds->priv; ++ return 0; ++} + +- return priv->info->mac_port_get_state(ds, port, state); ++static void mt7530_pcs_an_restart(struct phylink_pcs *pcs) ++{ + } + ++static const struct phylink_pcs_ops mt7530_pcs_ops = { ++ .pcs_get_state = mt7530_pcs_get_state, ++ .pcs_config = mt753x_pcs_config, ++ .pcs_an_restart = mt7530_pcs_an_restart, ++}; ++ ++static const struct phylink_pcs_ops mt7531_pcs_ops = { ++ .pcs_get_state = mt7531_pcs_get_state, ++ .pcs_config = mt753x_pcs_config, ++ .pcs_an_restart = mt7531_pcs_an_restart, ++ .pcs_link_up = mt7531_pcs_link_up, ++}; ++ + static int + mt753x_setup(struct dsa_switch *ds) + { + struct mt7530_priv *priv = ds->priv; + int ret = priv->info->sw_setup(ds); ++ int i; + + if (ret) + return ret; +@@ -3025,6 +3049,13 @@ mt753x_setup(struct dsa_switch *ds) + if (ret && priv->irq) + mt7530_free_irq_common(priv); + ++ /* Initialise the PCS devices */ ++ for (i = 0; i < priv->ds->num_ports; i++) { ++ priv->pcs[i].pcs.ops = priv->info->pcs_ops; ++ priv->pcs[i].priv = priv; ++ priv->pcs[i].port = i; ++ } ++ + return ret; + } + +@@ -3086,9 +3117,8 @@ static const struct dsa_switch_ops mt753 + .port_mirror_del = mt753x_port_mirror_del, + .phylink_get_caps = mt753x_phylink_get_caps, + .phylink_validate = mt753x_phylink_validate, +- .phylink_mac_link_state = mt753x_phylink_mac_link_state, ++ .phylink_mac_select_pcs = mt753x_phylink_mac_select_pcs, + .phylink_mac_config = mt753x_phylink_mac_config, +- .phylink_mac_an_restart = mt753x_phylink_mac_an_restart, + .phylink_mac_link_down = mt753x_phylink_mac_link_down, + .phylink_mac_link_up = mt753x_phylink_mac_link_up, + .get_mac_eee = mt753x_get_mac_eee, +@@ -3098,36 +3128,34 @@ static const struct dsa_switch_ops mt753 + static const struct mt753x_info mt753x_table[] = { + [ID_MT7621] = { + .id = ID_MT7621, ++ .pcs_ops = &mt7530_pcs_ops, + .sw_setup = mt7530_setup, + .phy_read = mt7530_phy_read, + .phy_write = mt7530_phy_write, + .pad_setup = mt7530_pad_clk_setup, + .mac_port_get_caps = mt7530_mac_port_get_caps, +- .mac_port_get_state = mt7530_phylink_mac_link_state, + .mac_port_config = mt7530_mac_config, + }, + [ID_MT7530] = { + .id = ID_MT7530, ++ .pcs_ops = &mt7530_pcs_ops, + .sw_setup = mt7530_setup, + .phy_read = mt7530_phy_read, + .phy_write = mt7530_phy_write, + .pad_setup = mt7530_pad_clk_setup, + .mac_port_get_caps = mt7530_mac_port_get_caps, +- .mac_port_get_state = mt7530_phylink_mac_link_state, + .mac_port_config = mt7530_mac_config, + }, + [ID_MT7531] = { + .id = ID_MT7531, ++ .pcs_ops = &mt7531_pcs_ops, + .sw_setup = mt7531_setup, + .phy_read = mt7531_ind_phy_read, + .phy_write = mt7531_ind_phy_write, + .pad_setup = mt7531_pad_setup, + .cpu_port_config = mt7531_cpu_port_config, + .mac_port_get_caps = mt7531_mac_port_get_caps, +- .mac_port_get_state = mt7531_phylink_mac_link_state, + .mac_port_config = mt7531_mac_config, +- .mac_pcs_an_restart = mt7531_sgmii_restart_an, +- .mac_pcs_link_up = mt7531_sgmii_link_up_force, + }, + }; + +@@ -3185,7 +3213,7 @@ mt7530_probe(struct mdio_device *mdiodev + if (!priv->info->sw_setup || !priv->info->pad_setup || + !priv->info->phy_read || !priv->info->phy_write || + !priv->info->mac_port_get_caps || +- !priv->info->mac_port_get_state || !priv->info->mac_port_config) ++ !priv->info->mac_port_config) + return -EINVAL; + + priv->id = priv->info->id; +--- a/drivers/net/dsa/mt7530.h ++++ b/drivers/net/dsa/mt7530.h +@@ -741,6 +741,12 @@ static const char *p5_intf_modes(unsigne + + struct mt7530_priv; + ++struct mt753x_pcs { ++ struct phylink_pcs pcs; ++ struct mt7530_priv *priv; ++ int port; ++}; ++ + /* struct mt753x_info - This is the main data structure for holding the specific + * part for each supported device + * @sw_setup: Holding the handler to a device initialization +@@ -752,18 +758,14 @@ struct mt7530_priv; + * port + * @mac_port_validate: Holding the way to set addition validate type for a + * certan MAC port +- * @mac_port_get_state: Holding the way getting the MAC/PCS state for a certain +- * MAC port + * @mac_port_config: Holding the way setting up the PHY attribute to a + * certain MAC port +- * @mac_pcs_an_restart Holding the way restarting PCS autonegotiation for a +- * certain MAC port +- * @mac_pcs_link_up: Holding the way setting up the PHY attribute to the pcs +- * of the certain MAC port + */ + struct mt753x_info { + enum mt753x_id id; + ++ const struct phylink_pcs_ops *pcs_ops; ++ + int (*sw_setup)(struct dsa_switch *ds); + int (*phy_read)(struct mt7530_priv *priv, int port, int regnum); + int (*phy_write)(struct mt7530_priv *priv, int port, int regnum, u16 val); +@@ -774,15 +776,9 @@ struct mt753x_info { + void (*mac_port_validate)(struct dsa_switch *ds, int port, + phy_interface_t interface, + unsigned long *supported); +- int (*mac_port_get_state)(struct dsa_switch *ds, int port, +- struct phylink_link_state *state); + int (*mac_port_config)(struct dsa_switch *ds, int port, + unsigned int mode, + phy_interface_t interface); +- void (*mac_pcs_an_restart)(struct dsa_switch *ds, int port); +- void (*mac_pcs_link_up)(struct dsa_switch *ds, int port, +- unsigned int mode, phy_interface_t interface, +- int speed, int duplex); + }; + + /* struct mt7530_priv - This is the main data structure for holding the state +@@ -824,6 +820,7 @@ struct mt7530_priv { + u8 mirror_tx; + + struct mt7530_port ports[MT7530_NUM_PORTS]; ++ struct mt753x_pcs pcs[MT7530_NUM_PORTS]; + /* protect among processes for registers access*/ + struct mutex reg_mutex; + int irq; diff --git a/target/linux/generic/backport-6.1/705-08-v5.19-net-dsa-mt7530-move-autoneg-handling-to-PCS-validati.patch b/target/linux/generic/backport-6.1/705-08-v5.19-net-dsa-mt7530-move-autoneg-handling-to-PCS-validati.patch new file mode 100644 index 0000000000..2d57bc7a03 --- /dev/null +++ b/target/linux/generic/backport-6.1/705-08-v5.19-net-dsa-mt7530-move-autoneg-handling-to-PCS-validati.patch @@ -0,0 +1,80 @@ +From 2b0ee6768f3ac09072e5fd60b36580924e1cfa1c Mon Sep 17 00:00:00 2001 +From: "Russell King (Oracle)" +Date: Mon, 11 Apr 2022 10:46:32 +0100 +Subject: [PATCH 08/13] net: dsa: mt7530: move autoneg handling to PCS + validation +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Move the autoneg bit handling to the PCS validation, which allows us to +get rid of mt753x_phylink_validate() and rely on the default +phylink_generic_validate() implementation for the MAC side. + +Tested-by: Marek Behún +Signed-off-by: Russell King (Oracle) +Signed-off-by: Paolo Abeni +--- + drivers/net/dsa/mt7530.c | 28 ++++++++++------------------ + 1 file changed, 10 insertions(+), 18 deletions(-) + +--- a/drivers/net/dsa/mt7530.c ++++ b/drivers/net/dsa/mt7530.c +@@ -2899,25 +2899,16 @@ static void mt753x_phylink_get_caps(stru + priv->info->mac_port_get_caps(ds, port, config); + } + +-static void +-mt753x_phylink_validate(struct dsa_switch *ds, int port, +- unsigned long *supported, +- struct phylink_link_state *state) +-{ +- __ETHTOOL_DECLARE_LINK_MODE_MASK(mask) = { 0, }; +- u32 caps; +- +- caps = dsa_to_port(ds, port)->pl_config.mac_capabilities; +- +- phylink_set_port_modes(mask); +- phylink_get_linkmodes(mask, state->interface, caps); ++static int mt753x_pcs_validate(struct phylink_pcs *pcs, ++ unsigned long *supported, ++ const struct phylink_link_state *state) ++{ ++ /* Autonegotiation is not supported in TRGMII nor 802.3z modes */ ++ if (state->interface == PHY_INTERFACE_MODE_TRGMII || ++ phy_interface_mode_is_8023z(state->interface)) ++ phylink_clear(supported, Autoneg); + +- if (state->interface != PHY_INTERFACE_MODE_TRGMII && +- !phy_interface_mode_is_8023z(state->interface)) +- phylink_set(mask, Autoneg); +- +- linkmode_and(supported, supported, mask); +- linkmode_and(state->advertising, state->advertising, mask); ++ return 0; + } + + static void mt7530_pcs_get_state(struct phylink_pcs *pcs, +@@ -3019,12 +3010,14 @@ static void mt7530_pcs_an_restart(struct + } + + static const struct phylink_pcs_ops mt7530_pcs_ops = { ++ .pcs_validate = mt753x_pcs_validate, + .pcs_get_state = mt7530_pcs_get_state, + .pcs_config = mt753x_pcs_config, + .pcs_an_restart = mt7530_pcs_an_restart, + }; + + static const struct phylink_pcs_ops mt7531_pcs_ops = { ++ .pcs_validate = mt753x_pcs_validate, + .pcs_get_state = mt7531_pcs_get_state, + .pcs_config = mt753x_pcs_config, + .pcs_an_restart = mt7531_pcs_an_restart, +@@ -3116,7 +3109,6 @@ static const struct dsa_switch_ops mt753 + .port_mirror_add = mt753x_port_mirror_add, + .port_mirror_del = mt753x_port_mirror_del, + .phylink_get_caps = mt753x_phylink_get_caps, +- .phylink_validate = mt753x_phylink_validate, + .phylink_mac_select_pcs = mt753x_phylink_mac_select_pcs, + .phylink_mac_config = mt753x_phylink_mac_config, + .phylink_mac_link_down = mt753x_phylink_mac_link_down, diff --git a/target/linux/generic/backport-6.1/705-09-v5.19-net-dsa-mt7530-mark-as-non-legacy.patch b/target/linux/generic/backport-6.1/705-09-v5.19-net-dsa-mt7530-mark-as-non-legacy.patch new file mode 100644 index 0000000000..bddc984a1f --- /dev/null +++ b/target/linux/generic/backport-6.1/705-09-v5.19-net-dsa-mt7530-mark-as-non-legacy.patch @@ -0,0 +1,34 @@ +From 5bc26de9bfaa6bb5539c09d4435dced98f429cfc Mon Sep 17 00:00:00 2001 +From: "Russell King (Oracle)" +Date: Mon, 11 Apr 2022 10:46:37 +0100 +Subject: [PATCH 09/13] net: dsa: mt7530: mark as non-legacy +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +The mt7530 driver does not make use of the speed, duplex, pause or +advertisement in its phylink_mac_config() implementation, so it can be +marked as a non-legacy driver. + +Tested-by: Marek Behún +Signed-off-by: Russell King (Oracle) +Signed-off-by: Paolo Abeni +--- + drivers/net/dsa/mt7530.c | 6 ++++++ + 1 file changed, 6 insertions(+) + +--- a/drivers/net/dsa/mt7530.c ++++ b/drivers/net/dsa/mt7530.c +@@ -2896,6 +2896,12 @@ static void mt753x_phylink_get_caps(stru + config->mac_capabilities = MAC_ASYM_PAUSE | MAC_SYM_PAUSE | + MAC_10 | MAC_100 | MAC_1000FD; + ++ /* This driver does not make use of the speed, duplex, pause or the ++ * advertisement in its mac_config, so it is safe to mark this driver ++ * as non-legacy. ++ */ ++ config->legacy_pre_march2020 = false; ++ + priv->info->mac_port_get_caps(ds, port, config); + } + diff --git a/target/linux/generic/backport-6.1/705-10-v5.19-net-dsa-mt753x-fix-pcs-conversion-regression.patch b/target/linux/generic/backport-6.1/705-10-v5.19-net-dsa-mt753x-fix-pcs-conversion-regression.patch new file mode 100644 index 0000000000..dcf946120e --- /dev/null +++ b/target/linux/generic/backport-6.1/705-10-v5.19-net-dsa-mt753x-fix-pcs-conversion-regression.patch @@ -0,0 +1,116 @@ +From 1f15b5e8733115cee65342bcaafeaf0368809fae Mon Sep 17 00:00:00 2001 +From: "Russell King (Oracle)" +Date: Mon, 25 Apr 2022 22:28:02 +0100 +Subject: [PATCH 10/13] net: dsa: mt753x: fix pcs conversion regression + +Daniel Golle reports that the conversion of mt753x to phylink PCS caused +an oops as below. + +The problem is with the placement of the PCS initialisation, which +occurs after mt7531_setup() has been called. However, burited in this +function is a call to setup the CPU port, which requires the PCS +structure to be already setup. + +Fix this by changing the initialisation order. + +Unable to handle kernel NULL pointer dereference at virtual address 0000000000000020 +Mem abort info: + ESR = 0x96000005 + EC = 0x25: DABT (current EL), IL = 32 bits + SET = 0, FnV = 0 + EA = 0, S1PTW = 0 + FSC = 0x05: level 1 translation fault +Data abort info: + ISV = 0, ISS = 0x00000005 + CM = 0, WnR = 0 +user pgtable: 4k pages, 39-bit VAs, pgdp=0000000046057000 +[0000000000000020] pgd=0000000000000000, p4d=0000000000000000, pud=0000000000000000 +Internal error: Oops: 96000005 [#1] SMP +Modules linked in: +CPU: 0 PID: 32 Comm: kworker/u4:1 Tainted: G S 5.18.0-rc3-next-20220422+ #0 +Hardware name: Bananapi BPI-R64 (DT) +Workqueue: events_unbound deferred_probe_work_func +pstate: 60000005 (nZCv daif -PAN -UAO -TCO -DIT -SSBS BTYPE=--) +pc : mt7531_cpu_port_config+0xcc/0x1b0 +lr : mt7531_cpu_port_config+0xc0/0x1b0 +sp : ffffffc008d5b980 +x29: ffffffc008d5b990 x28: ffffff80060562c8 x27: 00000000f805633b +x26: ffffff80001a8880 x25: 00000000000009c4 x24: 0000000000000016 +x23: ffffff8005eb6470 x22: 0000000000003600 x21: ffffff8006948080 +x20: 0000000000000000 x19: 0000000000000006 x18: 0000000000000000 +x17: 0000000000000001 x16: 0000000000000001 x15: 02963607fcee069e +x14: 0000000000000000 x13: 0000000000000030 x12: 0101010101010101 +x11: ffffffc037302000 x10: 0000000000000870 x9 : ffffffc008d5b800 +x8 : ffffff800028f950 x7 : 0000000000000001 x6 : 00000000662b3000 +x5 : 00000000000002f0 x4 : 0000000000000000 x3 : ffffff800028f080 +x2 : 0000000000000000 x1 : ffffff800028f080 x0 : 0000000000000000 +Call trace: + mt7531_cpu_port_config+0xcc/0x1b0 + mt753x_cpu_port_enable+0x24/0x1f0 + mt7531_setup+0x49c/0x5c0 + mt753x_setup+0x20/0x31c + dsa_register_switch+0x8bc/0x1020 + mt7530_probe+0x118/0x200 + mdio_probe+0x30/0x64 + really_probe.part.0+0x98/0x280 + __driver_probe_device+0x94/0x140 + driver_probe_device+0x40/0x114 + __device_attach_driver+0xb0/0x10c + bus_for_each_drv+0x64/0xa0 + __device_attach+0xa8/0x16c + device_initial_probe+0x10/0x20 + bus_probe_device+0x94/0x9c + deferred_probe_work_func+0x80/0xb4 + process_one_work+0x200/0x3a0 + worker_thread+0x260/0x4c0 + kthread+0xd4/0xe0 + ret_from_fork+0x10/0x20 +Code: 9409e911 937b7e60 8b0002a0 f9405800 (f9401005) +---[ end trace 0000000000000000 ]--- + +Reported-by: Daniel Golle +Tested-by: Daniel Golle +Fixes: cbd1f243bc41 ("net: dsa: mt7530: partially convert to phylink_pcs") +Signed-off-by: Russell King (Oracle) +Reviewed-by: Florian Fainelli +Link: https://lore.kernel.org/r/E1nj6FW-007WZB-5Y@rmk-PC.armlinux.org.uk +Signed-off-by: Jakub Kicinski +--- + drivers/net/dsa/mt7530.c | 18 +++++++++--------- + 1 file changed, 9 insertions(+), 9 deletions(-) + +--- a/drivers/net/dsa/mt7530.c ++++ b/drivers/net/dsa/mt7530.c +@@ -3034,9 +3034,16 @@ static int + mt753x_setup(struct dsa_switch *ds) + { + struct mt7530_priv *priv = ds->priv; +- int ret = priv->info->sw_setup(ds); +- int i; ++ int i, ret; + ++ /* Initialise the PCS devices */ ++ for (i = 0; i < priv->ds->num_ports; i++) { ++ priv->pcs[i].pcs.ops = priv->info->pcs_ops; ++ priv->pcs[i].priv = priv; ++ priv->pcs[i].port = i; ++ } ++ ++ ret = priv->info->sw_setup(ds); + if (ret) + return ret; + +@@ -3048,13 +3055,6 @@ mt753x_setup(struct dsa_switch *ds) + if (ret && priv->irq) + mt7530_free_irq_common(priv); + +- /* Initialise the PCS devices */ +- for (i = 0; i < priv->ds->num_ports; i++) { +- priv->pcs[i].pcs.ops = priv->info->pcs_ops; +- priv->pcs[i].priv = priv; +- priv->pcs[i].port = i; +- } +- + return ret; + } + diff --git a/target/linux/generic/backport-6.1/705-11-v6.0-net-dsa-mt7530-rework-mt7530_hw_vlan_-add-del.patch b/target/linux/generic/backport-6.1/705-11-v6.0-net-dsa-mt7530-rework-mt7530_hw_vlan_-add-del.patch new file mode 100644 index 0000000000..5fd109329c --- /dev/null +++ b/target/linux/generic/backport-6.1/705-11-v6.0-net-dsa-mt7530-rework-mt7530_hw_vlan_-add-del.patch @@ -0,0 +1,87 @@ +From e26be16262e1fc1e9f1798c12762663bd9c265c6 Mon Sep 17 00:00:00 2001 +From: Frank Wunderlich +Date: Fri, 10 Jun 2022 19:05:37 +0200 +Subject: [PATCH 11/13] net: dsa: mt7530: rework mt7530_hw_vlan_{add,del} + +Rework vlan_add/vlan_del functions in preparation for dynamic cpu port. + +Currently BIT(MT7530_CPU_PORT) is added to new_members, even though +mt7530_port_vlan_add() will be called on the CPU port too. + +Let DSA core decide when to call port_vlan_add for the CPU port, rather +than doing it implicitly. + +We can do autonomous forwarding in a certain VLAN, but not add br0 to that +VLAN and avoid flooding the CPU with those packets, if software knows it +doesn't need to process them. + +Suggested-by: Vladimir Oltean +Signed-off-by: Frank Wunderlich +Reviewed-by: Vladimir Oltean +Reviewed-by: Florian Fainelli +Signed-off-by: Jakub Kicinski +--- + drivers/net/dsa/mt7530.c | 30 ++++++++++++------------------ + 1 file changed, 12 insertions(+), 18 deletions(-) + +--- a/drivers/net/dsa/mt7530.c ++++ b/drivers/net/dsa/mt7530.c +@@ -1525,11 +1525,11 @@ static void + mt7530_hw_vlan_add(struct mt7530_priv *priv, + struct mt7530_hw_vlan_entry *entry) + { ++ struct dsa_port *dp = dsa_to_port(priv->ds, entry->port); + u8 new_members; + u32 val; + +- new_members = entry->old_members | BIT(entry->port) | +- BIT(MT7530_CPU_PORT); ++ new_members = entry->old_members | BIT(entry->port); + + /* Validate the entry with independent learning, create egress tag per + * VLAN and joining the port as one of the port members. +@@ -1540,22 +1540,20 @@ mt7530_hw_vlan_add(struct mt7530_priv *p + + /* Decide whether adding tag or not for those outgoing packets from the + * port inside the VLAN. +- */ +- val = entry->untagged ? MT7530_VLAN_EGRESS_UNTAG : +- MT7530_VLAN_EGRESS_TAG; +- mt7530_rmw(priv, MT7530_VAWD2, +- ETAG_CTRL_P_MASK(entry->port), +- ETAG_CTRL_P(entry->port, val)); +- +- /* CPU port is always taken as a tagged port for serving more than one ++ * CPU port is always taken as a tagged port for serving more than one + * VLANs across and also being applied with egress type stack mode for + * that VLAN tags would be appended after hardware special tag used as + * DSA tag. + */ ++ if (dsa_port_is_cpu(dp)) ++ val = MT7530_VLAN_EGRESS_STACK; ++ else if (entry->untagged) ++ val = MT7530_VLAN_EGRESS_UNTAG; ++ else ++ val = MT7530_VLAN_EGRESS_TAG; + mt7530_rmw(priv, MT7530_VAWD2, +- ETAG_CTRL_P_MASK(MT7530_CPU_PORT), +- ETAG_CTRL_P(MT7530_CPU_PORT, +- MT7530_VLAN_EGRESS_STACK)); ++ ETAG_CTRL_P_MASK(entry->port), ++ ETAG_CTRL_P(entry->port, val)); + } + + static void +@@ -1574,11 +1572,7 @@ mt7530_hw_vlan_del(struct mt7530_priv *p + return; + } + +- /* If certain member apart from CPU port is still alive in the VLAN, +- * the entry would be kept valid. Otherwise, the entry is got to be +- * disabled. +- */ +- if (new_members && new_members != BIT(MT7530_CPU_PORT)) { ++ if (new_members) { + val = IVL_MAC | VTAG_EN | PORT_MEM(new_members) | + VLAN_VALID; + mt7530_write(priv, MT7530_VAWD1, val); diff --git a/target/linux/generic/backport-6.1/705-12-v6.0-net-dsa-mt7530-rework-mt753-01-_setup.patch b/target/linux/generic/backport-6.1/705-12-v6.0-net-dsa-mt7530-rework-mt753-01-_setup.patch new file mode 100644 index 0000000000..165d1d035f --- /dev/null +++ b/target/linux/generic/backport-6.1/705-12-v6.0-net-dsa-mt7530-rework-mt753-01-_setup.patch @@ -0,0 +1,75 @@ +From 1f0dfd443eea7fc3e818e96f7c8264913ba41859 Mon Sep 17 00:00:00 2001 +From: Frank Wunderlich +Date: Fri, 10 Jun 2022 19:05:38 +0200 +Subject: [PATCH 12/13] net: dsa: mt7530: rework mt753[01]_setup + +Enumerate available cpu-ports instead of using hardcoded constant. + +Suggested-by: Vladimir Oltean +Signed-off-by: Frank Wunderlich +Reviewed-by: Vladimir Oltean +Reviewed-by: Florian Fainelli +Signed-off-by: Jakub Kicinski +--- + drivers/net/dsa/mt7530.c | 25 +++++++++++++++++++++---- + 1 file changed, 21 insertions(+), 4 deletions(-) + +--- a/drivers/net/dsa/mt7530.c ++++ b/drivers/net/dsa/mt7530.c +@@ -2090,11 +2090,12 @@ static int + mt7530_setup(struct dsa_switch *ds) + { + struct mt7530_priv *priv = ds->priv; ++ struct device_node *dn = NULL; + struct device_node *phy_node; + struct device_node *mac_np; + struct mt7530_dummy_poll p; + phy_interface_t interface; +- struct device_node *dn; ++ struct dsa_port *cpu_dp; + u32 id, val; + int ret, i; + +@@ -2102,7 +2103,19 @@ mt7530_setup(struct dsa_switch *ds) + * controller also is the container for two GMACs nodes representing + * as two netdev instances. + */ +- dn = dsa_to_port(ds, MT7530_CPU_PORT)->master->dev.of_node->parent; ++ dsa_switch_for_each_cpu_port(cpu_dp, ds) { ++ dn = cpu_dp->master->dev.of_node->parent; ++ /* It doesn't matter which CPU port is found first, ++ * their masters should share the same parent OF node ++ */ ++ break; ++ } ++ ++ if (!dn) { ++ dev_err(ds->dev, "parent OF node of DSA master not found"); ++ return -EINVAL; ++ } ++ + ds->assisted_learning_on_cpu_port = true; + ds->mtu_enforcement_ingress = true; + +@@ -2264,6 +2277,7 @@ mt7531_setup(struct dsa_switch *ds) + { + struct mt7530_priv *priv = ds->priv; + struct mt7530_dummy_poll p; ++ struct dsa_port *cpu_dp; + u32 val, id; + int ret, i; + +@@ -2338,8 +2352,11 @@ mt7531_setup(struct dsa_switch *ds) + CORE_PLL_GROUP4, val); + + /* BPDU to CPU port */ +- mt7530_rmw(priv, MT7531_CFC, MT7531_CPU_PMAP_MASK, +- BIT(MT7530_CPU_PORT)); ++ dsa_switch_for_each_cpu_port(cpu_dp, ds) { ++ mt7530_rmw(priv, MT7531_CFC, MT7531_CPU_PMAP_MASK, ++ BIT(cpu_dp->index)); ++ break; ++ } + mt7530_rmw(priv, MT753X_BPC, MT753X_BPDU_PORT_FW_MASK, + MT753X_BPDU_CPU_ONLY); + diff --git a/target/linux/generic/backport-6.1/705-13-v6.0-net-dsa-mt7530-get-cpu-port-via-dp-cpu_dp-instead-of.patch b/target/linux/generic/backport-6.1/705-13-v6.0-net-dsa-mt7530-get-cpu-port-via-dp-cpu_dp-instead-of.patch new file mode 100644 index 0000000000..5af91a0f7f --- /dev/null +++ b/target/linux/generic/backport-6.1/705-13-v6.0-net-dsa-mt7530-get-cpu-port-via-dp-cpu_dp-instead-of.patch @@ -0,0 +1,117 @@ +From ad2606f6fafae3a7d41c4f2af5554c8f6adecbc7 Mon Sep 17 00:00:00 2001 +From: Frank Wunderlich +Date: Fri, 10 Jun 2022 19:05:39 +0200 +Subject: [PATCH 13/13] net: dsa: mt7530: get cpu-port via dp->cpu_dp instead + of constant + +Replace last occurences of hardcoded cpu-port by cpu_dp member of +dsa_port struct. + +Now the constant can be dropped. + +Suggested-by: Vladimir Oltean +Signed-off-by: Frank Wunderlich +Reviewed-by: Vladimir Oltean +Reviewed-by: Florian Fainelli +Signed-off-by: Jakub Kicinski +--- + drivers/net/dsa/mt7530.c | 27 ++++++++++++++++++++------- + drivers/net/dsa/mt7530.h | 1 - + 2 files changed, 20 insertions(+), 8 deletions(-) + +--- a/drivers/net/dsa/mt7530.c ++++ b/drivers/net/dsa/mt7530.c +@@ -1041,6 +1041,7 @@ static int + mt7530_port_enable(struct dsa_switch *ds, int port, + struct phy_device *phy) + { ++ struct dsa_port *dp = dsa_to_port(ds, port); + struct mt7530_priv *priv = ds->priv; + + mutex_lock(&priv->reg_mutex); +@@ -1049,7 +1050,11 @@ mt7530_port_enable(struct dsa_switch *ds + * restore the port matrix if the port is the member of a certain + * bridge. + */ +- priv->ports[port].pm |= PCR_MATRIX(BIT(MT7530_CPU_PORT)); ++ if (dsa_port_is_user(dp)) { ++ struct dsa_port *cpu_dp = dp->cpu_dp; ++ ++ priv->ports[port].pm |= PCR_MATRIX(BIT(cpu_dp->index)); ++ } + priv->ports[port].enable = true; + mt7530_rmw(priv, MT7530_PCR_P(port), PCR_MATRIX_MASK, + priv->ports[port].pm); +@@ -1197,7 +1202,8 @@ mt7530_port_bridge_join(struct dsa_switc + struct net_device *bridge) + { + struct dsa_port *dp = dsa_to_port(ds, port), *other_dp; +- u32 port_bitmap = BIT(MT7530_CPU_PORT); ++ struct dsa_port *cpu_dp = dp->cpu_dp; ++ u32 port_bitmap = BIT(cpu_dp->index); + struct mt7530_priv *priv = ds->priv; + + mutex_lock(&priv->reg_mutex); +@@ -1274,9 +1280,12 @@ mt7530_port_set_vlan_unaware(struct dsa_ + * the CPU port get out of VLAN filtering mode. + */ + if (all_user_ports_removed) { +- mt7530_write(priv, MT7530_PCR_P(MT7530_CPU_PORT), ++ struct dsa_port *dp = dsa_to_port(ds, port); ++ struct dsa_port *cpu_dp = dp->cpu_dp; ++ ++ mt7530_write(priv, MT7530_PCR_P(cpu_dp->index), + PCR_MATRIX(dsa_user_ports(priv->ds))); +- mt7530_write(priv, MT7530_PVC_P(MT7530_CPU_PORT), PORT_SPEC_TAG ++ mt7530_write(priv, MT7530_PVC_P(cpu_dp->index), PORT_SPEC_TAG + | PVC_EG_TAG(MT7530_VLAN_EG_CONSISTENT)); + } + } +@@ -1314,6 +1323,7 @@ mt7530_port_bridge_leave(struct dsa_swit + struct net_device *bridge) + { + struct dsa_port *dp = dsa_to_port(ds, port), *other_dp; ++ struct dsa_port *cpu_dp = dp->cpu_dp; + struct mt7530_priv *priv = ds->priv; + + mutex_lock(&priv->reg_mutex); +@@ -1342,8 +1352,8 @@ mt7530_port_bridge_leave(struct dsa_swit + */ + if (priv->ports[port].enable) + mt7530_rmw(priv, MT7530_PCR_P(port), PCR_MATRIX_MASK, +- PCR_MATRIX(BIT(MT7530_CPU_PORT))); +- priv->ports[port].pm = PCR_MATRIX(BIT(MT7530_CPU_PORT)); ++ PCR_MATRIX(BIT(cpu_dp->index))); ++ priv->ports[port].pm = PCR_MATRIX(BIT(cpu_dp->index)); + + /* When a port is removed from the bridge, the port would be set up + * back to the default as is at initial boot which is a VLAN-unaware +@@ -1506,6 +1516,9 @@ static int + mt7530_port_vlan_filtering(struct dsa_switch *ds, int port, bool vlan_filtering, + struct netlink_ext_ack *extack) + { ++ struct dsa_port *dp = dsa_to_port(ds, port); ++ struct dsa_port *cpu_dp = dp->cpu_dp; ++ + if (vlan_filtering) { + /* The port is being kept as VLAN-unaware port when bridge is + * set up with vlan_filtering not being set, Otherwise, the +@@ -1513,7 +1526,7 @@ mt7530_port_vlan_filtering(struct dsa_sw + * for becoming a VLAN-aware port. + */ + mt7530_port_set_vlan_aware(ds, port); +- mt7530_port_set_vlan_aware(ds, MT7530_CPU_PORT); ++ mt7530_port_set_vlan_aware(ds, cpu_dp->index); + } else { + mt7530_port_set_vlan_unaware(ds, port); + } +--- a/drivers/net/dsa/mt7530.h ++++ b/drivers/net/dsa/mt7530.h +@@ -8,7 +8,6 @@ + + #define MT7530_NUM_PORTS 7 + #define MT7530_NUM_PHYS 5 +-#define MT7530_CPU_PORT 6 + #define MT7530_NUM_FDB_RECORDS 2048 + #define MT7530_ALL_MEMBERS 0xff + diff --git a/target/linux/generic/backport-6.1/706-00-v6.0-net-ethernet-mtk_eth_soc-rely-on-page_pool-for-singl.patch b/target/linux/generic/backport-6.1/706-00-v6.0-net-ethernet-mtk_eth_soc-rely-on-page_pool-for-singl.patch new file mode 100644 index 0000000000..f9aa6fb67b --- /dev/null +++ b/target/linux/generic/backport-6.1/706-00-v6.0-net-ethernet-mtk_eth_soc-rely-on-page_pool-for-singl.patch @@ -0,0 +1,330 @@ +From 23233e577ef973c2c5d0dd757a0a4605e34ecb57 Mon Sep 17 00:00:00 2001 +From: Lorenzo Bianconi +Date: Fri, 22 Jul 2022 09:19:36 +0200 +Subject: [PATCH] net: ethernet: mtk_eth_soc: rely on page_pool for single page + buffers + +Rely on page_pool allocator for single page buffers in order to keep +them dma mapped and add skb recycling support. + +Signed-off-by: Lorenzo Bianconi +Signed-off-by: David S. Miller +--- + drivers/net/ethernet/mediatek/Kconfig | 1 + + drivers/net/ethernet/mediatek/mtk_eth_soc.c | 185 +++++++++++++++----- + drivers/net/ethernet/mediatek/mtk_eth_soc.h | 10 ++ + 3 files changed, 156 insertions(+), 40 deletions(-) + +--- a/drivers/net/ethernet/mediatek/Kconfig ++++ b/drivers/net/ethernet/mediatek/Kconfig +@@ -16,6 +16,7 @@ config NET_MEDIATEK_SOC + depends on NET_DSA || !NET_DSA + select PHYLINK + select DIMLIB ++ select PAGE_POOL + help + This driver supports the gigabit ethernet MACs in the + MediaTek SoC family. +--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c ++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c +@@ -1388,6 +1388,68 @@ static void mtk_update_rx_cpu_idx(struct + } + } + ++static struct page_pool *mtk_create_page_pool(struct mtk_eth *eth, ++ struct xdp_rxq_info *xdp_q, ++ int id, int size) ++{ ++ struct page_pool_params pp_params = { ++ .order = 0, ++ .flags = PP_FLAG_DMA_MAP | PP_FLAG_DMA_SYNC_DEV, ++ .pool_size = size, ++ .nid = NUMA_NO_NODE, ++ .dev = eth->dma_dev, ++ .dma_dir = DMA_FROM_DEVICE, ++ .offset = MTK_PP_HEADROOM, ++ .max_len = MTK_PP_MAX_BUF_SIZE, ++ }; ++ struct page_pool *pp; ++ int err; ++ ++ pp = page_pool_create(&pp_params); ++ if (IS_ERR(pp)) ++ return pp; ++ ++ err = xdp_rxq_info_reg(xdp_q, ð->dummy_dev, id, ++ eth->rx_napi.napi_id); ++ if (err < 0) ++ goto err_free_pp; ++ ++ err = xdp_rxq_info_reg_mem_model(xdp_q, MEM_TYPE_PAGE_POOL, pp); ++ if (err) ++ goto err_unregister_rxq; ++ ++ return pp; ++ ++err_unregister_rxq: ++ xdp_rxq_info_unreg(xdp_q); ++err_free_pp: ++ page_pool_destroy(pp); ++ ++ return ERR_PTR(err); ++} ++ ++static void *mtk_page_pool_get_buff(struct page_pool *pp, dma_addr_t *dma_addr, ++ gfp_t gfp_mask) ++{ ++ struct page *page; ++ ++ page = page_pool_alloc_pages(pp, gfp_mask | __GFP_NOWARN); ++ if (!page) ++ return NULL; ++ ++ *dma_addr = page_pool_get_dma_addr(page) + MTK_PP_HEADROOM; ++ return page_address(page); ++} ++ ++static void mtk_rx_put_buff(struct mtk_rx_ring *ring, void *data, bool napi) ++{ ++ if (ring->page_pool) ++ page_pool_put_full_page(ring->page_pool, ++ virt_to_head_page(data), napi); ++ else ++ skb_free_frag(data); ++} ++ + static int mtk_poll_rx(struct napi_struct *napi, int budget, + struct mtk_eth *eth) + { +@@ -1401,9 +1463,9 @@ static int mtk_poll_rx(struct napi_struc + + while (done < budget) { + unsigned int pktlen, *rxdcsum; ++ u32 hash, reason, reserve_len; + struct net_device *netdev; + dma_addr_t dma_addr; +- u32 hash, reason; + int mac = 0; + + ring = mtk_get_rx_ring(eth); +@@ -1434,36 +1496,54 @@ static int mtk_poll_rx(struct napi_struc + goto release_desc; + + /* alloc new buffer */ +- if (ring->frag_size <= PAGE_SIZE) +- new_data = napi_alloc_frag(ring->frag_size); +- else +- new_data = mtk_max_lro_buf_alloc(GFP_ATOMIC); +- if (unlikely(!new_data)) { +- netdev->stats.rx_dropped++; +- goto release_desc; +- } +- dma_addr = dma_map_single(eth->dma_dev, +- new_data + NET_SKB_PAD + +- eth->ip_align, +- ring->buf_size, +- DMA_FROM_DEVICE); +- if (unlikely(dma_mapping_error(eth->dma_dev, dma_addr))) { +- skb_free_frag(new_data); +- netdev->stats.rx_dropped++; +- goto release_desc; +- } ++ if (ring->page_pool) { ++ new_data = mtk_page_pool_get_buff(ring->page_pool, ++ &dma_addr, ++ GFP_ATOMIC); ++ if (unlikely(!new_data)) { ++ netdev->stats.rx_dropped++; ++ goto release_desc; ++ } ++ } else { ++ if (ring->frag_size <= PAGE_SIZE) ++ new_data = napi_alloc_frag(ring->frag_size); ++ else ++ new_data = mtk_max_lro_buf_alloc(GFP_ATOMIC); ++ ++ if (unlikely(!new_data)) { ++ netdev->stats.rx_dropped++; ++ goto release_desc; ++ } + +- dma_unmap_single(eth->dma_dev, trxd.rxd1, +- ring->buf_size, DMA_FROM_DEVICE); ++ dma_addr = dma_map_single(eth->dma_dev, ++ new_data + NET_SKB_PAD + eth->ip_align, ++ ring->buf_size, DMA_FROM_DEVICE); ++ if (unlikely(dma_mapping_error(eth->dma_dev, ++ dma_addr))) { ++ skb_free_frag(new_data); ++ netdev->stats.rx_dropped++; ++ goto release_desc; ++ } ++ ++ dma_unmap_single(eth->dma_dev, trxd.rxd1, ++ ring->buf_size, DMA_FROM_DEVICE); ++ } + + /* receive data */ + skb = build_skb(data, ring->frag_size); + if (unlikely(!skb)) { +- skb_free_frag(data); ++ mtk_rx_put_buff(ring, data, true); + netdev->stats.rx_dropped++; + goto skip_rx; + } +- skb_reserve(skb, NET_SKB_PAD + NET_IP_ALIGN); ++ ++ if (ring->page_pool) { ++ reserve_len = MTK_PP_HEADROOM; ++ skb_mark_for_recycle(skb); ++ } else { ++ reserve_len = NET_SKB_PAD + NET_IP_ALIGN; ++ } ++ skb_reserve(skb, reserve_len); + + pktlen = RX_DMA_GET_PLEN0(trxd.rxd2); + skb->dev = netdev; +@@ -1517,7 +1597,6 @@ static int mtk_poll_rx(struct napi_struc + skip_rx: + ring->data[idx] = new_data; + rxd->rxd1 = (unsigned int)dma_addr; +- + release_desc: + if (MTK_HAS_CAPS(eth->soc->caps, MTK_SOC_MT7628)) + rxd->rxd2 = RX_DMA_LSO; +@@ -1525,7 +1604,6 @@ release_desc: + rxd->rxd2 = RX_DMA_PREP_PLEN0(ring->buf_size); + + ring->calc_idx = idx; +- + done++; + } + +@@ -1889,13 +1967,15 @@ static int mtk_rx_alloc(struct mtk_eth * + if (!ring->data) + return -ENOMEM; + +- for (i = 0; i < rx_dma_size; i++) { +- if (ring->frag_size <= PAGE_SIZE) +- ring->data[i] = netdev_alloc_frag(ring->frag_size); +- else +- ring->data[i] = mtk_max_lro_buf_alloc(GFP_KERNEL); +- if (!ring->data[i]) +- return -ENOMEM; ++ if (!eth->hwlro) { ++ struct page_pool *pp; ++ ++ pp = mtk_create_page_pool(eth, &ring->xdp_q, ring_no, ++ rx_dma_size); ++ if (IS_ERR(pp)) ++ return PTR_ERR(pp); ++ ++ ring->page_pool = pp; + } + + ring->dma = dma_alloc_coherent(eth->dma_dev, +@@ -1906,16 +1986,33 @@ static int mtk_rx_alloc(struct mtk_eth * + + for (i = 0; i < rx_dma_size; i++) { + struct mtk_rx_dma_v2 *rxd; +- +- dma_addr_t dma_addr = dma_map_single(eth->dma_dev, +- ring->data[i] + NET_SKB_PAD + eth->ip_align, +- ring->buf_size, +- DMA_FROM_DEVICE); +- if (unlikely(dma_mapping_error(eth->dma_dev, dma_addr))) +- return -ENOMEM; ++ dma_addr_t dma_addr; ++ void *data; + + rxd = ring->dma + i * eth->soc->txrx.rxd_size; ++ if (ring->page_pool) { ++ data = mtk_page_pool_get_buff(ring->page_pool, ++ &dma_addr, GFP_KERNEL); ++ if (!data) ++ return -ENOMEM; ++ } else { ++ if (ring->frag_size <= PAGE_SIZE) ++ data = netdev_alloc_frag(ring->frag_size); ++ else ++ data = mtk_max_lro_buf_alloc(GFP_KERNEL); ++ ++ if (!data) ++ return -ENOMEM; ++ ++ dma_addr = dma_map_single(eth->dma_dev, ++ data + NET_SKB_PAD + eth->ip_align, ++ ring->buf_size, DMA_FROM_DEVICE); ++ if (unlikely(dma_mapping_error(eth->dma_dev, ++ dma_addr))) ++ return -ENOMEM; ++ } + rxd->rxd1 = (unsigned int)dma_addr; ++ ring->data[i] = data; + + if (MTK_HAS_CAPS(eth->soc->caps, MTK_SOC_MT7628)) + rxd->rxd2 = RX_DMA_LSO; +@@ -1931,6 +2028,7 @@ static int mtk_rx_alloc(struct mtk_eth * + rxd->rxd8 = 0; + } + } ++ + ring->dma_size = rx_dma_size; + ring->calc_idx_update = false; + ring->calc_idx = rx_dma_size - 1; +@@ -1982,7 +2080,7 @@ static void mtk_rx_clean(struct mtk_eth + + dma_unmap_single(eth->dma_dev, rxd->rxd1, + ring->buf_size, DMA_FROM_DEVICE); +- skb_free_frag(ring->data[i]); ++ mtk_rx_put_buff(ring, ring->data[i], false); + } + kfree(ring->data); + ring->data = NULL; +@@ -1994,6 +2092,13 @@ static void mtk_rx_clean(struct mtk_eth + ring->dma, ring->phys); + ring->dma = NULL; + } ++ ++ if (ring->page_pool) { ++ if (xdp_rxq_info_is_reg(&ring->xdp_q)) ++ xdp_rxq_info_unreg(&ring->xdp_q); ++ page_pool_destroy(ring->page_pool); ++ ring->page_pool = NULL; ++ } + } + + static int mtk_hwlro_rx_init(struct mtk_eth *eth) +--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h ++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h +@@ -18,6 +18,8 @@ + #include + #include + #include ++#include ++#include + #include "mtk_ppe.h" + + #define MTK_QDMA_PAGE_SIZE 2048 +@@ -49,6 +51,11 @@ + #define MTK_HW_FEATURES_MT7628 (NETIF_F_SG | NETIF_F_RXCSUM) + #define NEXT_DESP_IDX(X, Y) (((X) + 1) & ((Y) - 1)) + ++#define MTK_PP_HEADROOM XDP_PACKET_HEADROOM ++#define MTK_PP_PAD (MTK_PP_HEADROOM + \ ++ SKB_DATA_ALIGN(sizeof(struct skb_shared_info))) ++#define MTK_PP_MAX_BUF_SIZE (PAGE_SIZE - MTK_PP_PAD) ++ + #define MTK_QRX_OFFSET 0x10 + + #define MTK_MAX_RX_RING_NUM 4 +@@ -742,6 +749,9 @@ struct mtk_rx_ring { + bool calc_idx_update; + u16 calc_idx; + u32 crx_idx_reg; ++ /* page_pool */ ++ struct page_pool *page_pool; ++ struct xdp_rxq_info xdp_q; + }; + + enum mkt_eth_capabilities { diff --git a/target/linux/generic/backport-6.1/706-01-v6.0-net-ethernet-mtk_eth_soc-add-basic-XDP-support.patch b/target/linux/generic/backport-6.1/706-01-v6.0-net-ethernet-mtk_eth_soc-add-basic-XDP-support.patch new file mode 100644 index 0000000000..b930152dd1 --- /dev/null +++ b/target/linux/generic/backport-6.1/706-01-v6.0-net-ethernet-mtk_eth_soc-add-basic-XDP-support.patch @@ -0,0 +1,291 @@ +From 7c26c20da5d420cde55618263be4aa2f6de53056 Mon Sep 17 00:00:00 2001 +From: Lorenzo Bianconi +Date: Fri, 22 Jul 2022 09:19:37 +0200 +Subject: [PATCH] net: ethernet: mtk_eth_soc: add basic XDP support + +Introduce basic XDP support to mtk_eth_soc driver. +Supported XDP verdicts: +- XDP_PASS +- XDP_DROP +- XDP_REDIRECT + +Signed-off-by: Lorenzo Bianconi +Signed-off-by: David S. Miller +--- + drivers/net/ethernet/mediatek/mtk_eth_soc.c | 162 +++++++++++++++++--- + drivers/net/ethernet/mediatek/mtk_eth_soc.h | 2 + + 2 files changed, 145 insertions(+), 19 deletions(-) + +--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c ++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c +@@ -1388,6 +1388,11 @@ static void mtk_update_rx_cpu_idx(struct + } + } + ++static bool mtk_page_pool_enabled(struct mtk_eth *eth) ++{ ++ return !eth->hwlro; ++} ++ + static struct page_pool *mtk_create_page_pool(struct mtk_eth *eth, + struct xdp_rxq_info *xdp_q, + int id, int size) +@@ -1450,11 +1455,52 @@ static void mtk_rx_put_buff(struct mtk_r + skb_free_frag(data); + } + ++static u32 mtk_xdp_run(struct mtk_eth *eth, struct mtk_rx_ring *ring, ++ struct xdp_buff *xdp, struct net_device *dev) ++{ ++ struct bpf_prog *prog; ++ u32 act = XDP_PASS; ++ ++ rcu_read_lock(); ++ ++ prog = rcu_dereference(eth->prog); ++ if (!prog) ++ goto out; ++ ++ act = bpf_prog_run_xdp(prog, xdp); ++ switch (act) { ++ case XDP_PASS: ++ goto out; ++ case XDP_REDIRECT: ++ if (unlikely(xdp_do_redirect(dev, xdp, prog))) { ++ act = XDP_DROP; ++ break; ++ } ++ goto out; ++ default: ++ bpf_warn_invalid_xdp_action(act); ++ fallthrough; ++ case XDP_ABORTED: ++ trace_xdp_exception(dev, prog, act); ++ fallthrough; ++ case XDP_DROP: ++ break; ++ } ++ ++ page_pool_put_full_page(ring->page_pool, ++ virt_to_head_page(xdp->data), true); ++out: ++ rcu_read_unlock(); ++ ++ return act; ++} ++ + static int mtk_poll_rx(struct napi_struct *napi, int budget, + struct mtk_eth *eth) + { + struct dim_sample dim_sample = {}; + struct mtk_rx_ring *ring; ++ bool xdp_flush = false; + int idx; + struct sk_buff *skb; + u8 *data, *new_data; +@@ -1463,9 +1509,9 @@ static int mtk_poll_rx(struct napi_struc + + while (done < budget) { + unsigned int pktlen, *rxdcsum; +- u32 hash, reason, reserve_len; + struct net_device *netdev; + dma_addr_t dma_addr; ++ u32 hash, reason; + int mac = 0; + + ring = mtk_get_rx_ring(eth); +@@ -1495,8 +1541,14 @@ static int mtk_poll_rx(struct napi_struc + if (unlikely(test_bit(MTK_RESETTING, ð->state))) + goto release_desc; + ++ pktlen = RX_DMA_GET_PLEN0(trxd.rxd2); ++ + /* alloc new buffer */ + if (ring->page_pool) { ++ struct page *page = virt_to_head_page(data); ++ struct xdp_buff xdp; ++ u32 ret; ++ + new_data = mtk_page_pool_get_buff(ring->page_pool, + &dma_addr, + GFP_ATOMIC); +@@ -1504,6 +1556,34 @@ static int mtk_poll_rx(struct napi_struc + netdev->stats.rx_dropped++; + goto release_desc; + } ++ ++ dma_sync_single_for_cpu(eth->dma_dev, ++ page_pool_get_dma_addr(page) + MTK_PP_HEADROOM, ++ pktlen, page_pool_get_dma_dir(ring->page_pool)); ++ ++ xdp_init_buff(&xdp, PAGE_SIZE, &ring->xdp_q); ++ xdp_prepare_buff(&xdp, data, MTK_PP_HEADROOM, pktlen, ++ false); ++ xdp_buff_clear_frags_flag(&xdp); ++ ++ ret = mtk_xdp_run(eth, ring, &xdp, netdev); ++ if (ret == XDP_REDIRECT) ++ xdp_flush = true; ++ ++ if (ret != XDP_PASS) ++ goto skip_rx; ++ ++ skb = build_skb(data, PAGE_SIZE); ++ if (unlikely(!skb)) { ++ page_pool_put_full_page(ring->page_pool, ++ page, true); ++ netdev->stats.rx_dropped++; ++ goto skip_rx; ++ } ++ ++ skb_reserve(skb, xdp.data - xdp.data_hard_start); ++ skb_put(skb, xdp.data_end - xdp.data); ++ skb_mark_for_recycle(skb); + } else { + if (ring->frag_size <= PAGE_SIZE) + new_data = napi_alloc_frag(ring->frag_size); +@@ -1527,27 +1607,20 @@ static int mtk_poll_rx(struct napi_struc + + dma_unmap_single(eth->dma_dev, trxd.rxd1, + ring->buf_size, DMA_FROM_DEVICE); +- } + +- /* receive data */ +- skb = build_skb(data, ring->frag_size); +- if (unlikely(!skb)) { +- mtk_rx_put_buff(ring, data, true); +- netdev->stats.rx_dropped++; +- goto skip_rx; +- } ++ skb = build_skb(data, ring->frag_size); ++ if (unlikely(!skb)) { ++ netdev->stats.rx_dropped++; ++ skb_free_frag(data); ++ goto skip_rx; ++ } + +- if (ring->page_pool) { +- reserve_len = MTK_PP_HEADROOM; +- skb_mark_for_recycle(skb); +- } else { +- reserve_len = NET_SKB_PAD + NET_IP_ALIGN; ++ skb_reserve(skb, NET_SKB_PAD + NET_IP_ALIGN); ++ skb_put(skb, pktlen); + } +- skb_reserve(skb, reserve_len); + +- pktlen = RX_DMA_GET_PLEN0(trxd.rxd2); + skb->dev = netdev; +- skb_put(skb, pktlen); ++ bytes += skb->len; + + if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V2)) + rxdcsum = &trxd.rxd3; +@@ -1559,7 +1632,6 @@ static int mtk_poll_rx(struct napi_struc + else + skb_checksum_none_assert(skb); + skb->protocol = eth_type_trans(skb, netdev); +- bytes += pktlen; + + hash = trxd.rxd4 & MTK_RXD4_FOE_ENTRY; + if (hash != MTK_RXD4_FOE_ENTRY) { +@@ -1622,6 +1694,9 @@ rx_done: + &dim_sample); + net_dim(ð->rx_dim, dim_sample); + ++ if (xdp_flush) ++ xdp_do_flush_map(); ++ + return done; + } + +@@ -1967,7 +2042,7 @@ static int mtk_rx_alloc(struct mtk_eth * + if (!ring->data) + return -ENOMEM; + +- if (!eth->hwlro) { ++ if (mtk_page_pool_enabled(eth)) { + struct page_pool *pp; + + pp = mtk_create_page_pool(eth, &ring->xdp_q, ring_no, +@@ -2707,6 +2782,48 @@ static int mtk_stop(struct net_device *d + return 0; + } + ++static int mtk_xdp_setup(struct net_device *dev, struct bpf_prog *prog, ++ struct netlink_ext_ack *extack) ++{ ++ struct mtk_mac *mac = netdev_priv(dev); ++ struct mtk_eth *eth = mac->hw; ++ struct bpf_prog *old_prog; ++ bool need_update; ++ ++ if (eth->hwlro) { ++ NL_SET_ERR_MSG_MOD(extack, "XDP not supported with HWLRO"); ++ return -EOPNOTSUPP; ++ } ++ ++ if (dev->mtu > MTK_PP_MAX_BUF_SIZE) { ++ NL_SET_ERR_MSG_MOD(extack, "MTU too large for XDP"); ++ return -EOPNOTSUPP; ++ } ++ ++ need_update = !!eth->prog != !!prog; ++ if (netif_running(dev) && need_update) ++ mtk_stop(dev); ++ ++ old_prog = rcu_replace_pointer(eth->prog, prog, lockdep_rtnl_is_held()); ++ if (old_prog) ++ bpf_prog_put(old_prog); ++ ++ if (netif_running(dev) && need_update) ++ return mtk_open(dev); ++ ++ return 0; ++} ++ ++static int mtk_xdp(struct net_device *dev, struct netdev_bpf *xdp) ++{ ++ switch (xdp->command) { ++ case XDP_SETUP_PROG: ++ return mtk_xdp_setup(dev, xdp->prog, xdp->extack); ++ default: ++ return -EINVAL; ++ } ++} ++ + static void ethsys_reset(struct mtk_eth *eth, u32 reset_bits) + { + regmap_update_bits(eth->ethsys, ETHSYS_RSTCTRL, +@@ -3002,6 +3119,12 @@ static int mtk_change_mtu(struct net_dev + struct mtk_eth *eth = mac->hw; + u32 mcr_cur, mcr_new; + ++ if (rcu_access_pointer(eth->prog) && ++ length > MTK_PP_MAX_BUF_SIZE) { ++ netdev_err(dev, "Invalid MTU for XDP mode\n"); ++ return -EINVAL; ++ } ++ + if (!MTK_HAS_CAPS(eth->soc->caps, MTK_SOC_MT7628)) { + mcr_cur = mtk_r32(mac->hw, MTK_MAC_MCR(mac->id)); + mcr_new = mcr_cur & ~MAC_MCR_MAX_RX_MASK; +@@ -3329,6 +3452,7 @@ static const struct net_device_ops mtk_n + .ndo_poll_controller = mtk_poll_controller, + #endif + .ndo_setup_tc = mtk_eth_setup_tc, ++ .ndo_bpf = mtk_xdp, + }; + + static int mtk_add_mac(struct mtk_eth *eth, struct device_node *np) +--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h ++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h +@@ -1085,6 +1085,8 @@ struct mtk_eth { + + struct mtk_ppe *ppe; + struct rhashtable flow_table; ++ ++ struct bpf_prog __rcu *prog; + }; + + /* struct mtk_mac - the structure that holds the info about the MACs of the diff --git a/target/linux/generic/backport-6.1/706-02-v6.0-net-ethernet-mtk_eth_soc-introduce-xdp-ethtool-count.patch b/target/linux/generic/backport-6.1/706-02-v6.0-net-ethernet-mtk_eth_soc-introduce-xdp-ethtool-count.patch new file mode 100644 index 0000000000..b68f44aae2 --- /dev/null +++ b/target/linux/generic/backport-6.1/706-02-v6.0-net-ethernet-mtk_eth_soc-introduce-xdp-ethtool-count.patch @@ -0,0 +1,110 @@ +From 916a6ee836d6b7b8ef1ed5f0515e256ca60e9968 Mon Sep 17 00:00:00 2001 +From: Lorenzo Bianconi +Date: Fri, 22 Jul 2022 09:19:38 +0200 +Subject: [PATCH] net: ethernet: mtk_eth_soc: introduce xdp ethtool counters + +Report xdp stats through ethtool + +Signed-off-by: Lorenzo Bianconi +Signed-off-by: David S. Miller +--- + drivers/net/ethernet/mediatek/mtk_eth_soc.c | 26 +++++++++++++++++++-- + drivers/net/ethernet/mediatek/mtk_eth_soc.h | 12 ++++++++++ + 2 files changed, 36 insertions(+), 2 deletions(-) + +--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c ++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c +@@ -34,6 +34,10 @@ MODULE_PARM_DESC(msg_level, "Message lev + #define MTK_ETHTOOL_STAT(x) { #x, \ + offsetof(struct mtk_hw_stats, x) / sizeof(u64) } + ++#define MTK_ETHTOOL_XDP_STAT(x) { #x, \ ++ offsetof(struct mtk_hw_stats, xdp_stats.x) / \ ++ sizeof(u64) } ++ + static const struct mtk_reg_map mtk_reg_map = { + .tx_irq_mask = 0x1a1c, + .tx_irq_status = 0x1a18, +@@ -141,6 +145,13 @@ static const struct mtk_ethtool_stats { + MTK_ETHTOOL_STAT(rx_long_errors), + MTK_ETHTOOL_STAT(rx_checksum_errors), + MTK_ETHTOOL_STAT(rx_flow_control_packets), ++ MTK_ETHTOOL_XDP_STAT(rx_xdp_redirect), ++ MTK_ETHTOOL_XDP_STAT(rx_xdp_pass), ++ MTK_ETHTOOL_XDP_STAT(rx_xdp_drop), ++ MTK_ETHTOOL_XDP_STAT(rx_xdp_tx), ++ MTK_ETHTOOL_XDP_STAT(rx_xdp_tx_errors), ++ MTK_ETHTOOL_XDP_STAT(tx_xdp_xmit), ++ MTK_ETHTOOL_XDP_STAT(tx_xdp_xmit_errors), + }; + + static const char * const mtk_clks_source_name[] = { +@@ -1458,6 +1469,9 @@ static void mtk_rx_put_buff(struct mtk_r + static u32 mtk_xdp_run(struct mtk_eth *eth, struct mtk_rx_ring *ring, + struct xdp_buff *xdp, struct net_device *dev) + { ++ struct mtk_mac *mac = netdev_priv(dev); ++ struct mtk_hw_stats *hw_stats = mac->hw_stats; ++ u64 *count = &hw_stats->xdp_stats.rx_xdp_drop; + struct bpf_prog *prog; + u32 act = XDP_PASS; + +@@ -1470,13 +1484,16 @@ static u32 mtk_xdp_run(struct mtk_eth *e + act = bpf_prog_run_xdp(prog, xdp); + switch (act) { + case XDP_PASS: +- goto out; ++ count = &hw_stats->xdp_stats.rx_xdp_pass; ++ goto update_stats; + case XDP_REDIRECT: + if (unlikely(xdp_do_redirect(dev, xdp, prog))) { + act = XDP_DROP; + break; + } +- goto out; ++ ++ count = &hw_stats->xdp_stats.rx_xdp_redirect; ++ goto update_stats; + default: + bpf_warn_invalid_xdp_action(act); + fallthrough; +@@ -1489,6 +1506,11 @@ static u32 mtk_xdp_run(struct mtk_eth *e + + page_pool_put_full_page(ring->page_pool, + virt_to_head_page(xdp->data), true); ++ ++update_stats: ++ u64_stats_update_begin(&hw_stats->syncp); ++ *count = *count + 1; ++ u64_stats_update_end(&hw_stats->syncp); + out: + rcu_read_unlock(); + +--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h ++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h +@@ -567,6 +567,16 @@ struct mtk_tx_dma_v2 { + struct mtk_eth; + struct mtk_mac; + ++struct mtk_xdp_stats { ++ u64 rx_xdp_redirect; ++ u64 rx_xdp_pass; ++ u64 rx_xdp_drop; ++ u64 rx_xdp_tx; ++ u64 rx_xdp_tx_errors; ++ u64 tx_xdp_xmit; ++ u64 tx_xdp_xmit_errors; ++}; ++ + /* struct mtk_hw_stats - the structure that holds the traffic statistics. + * @stats_lock: make sure that stats operations are atomic + * @reg_offset: the status register offset of the SoC +@@ -590,6 +600,8 @@ struct mtk_hw_stats { + u64 rx_checksum_errors; + u64 rx_flow_control_packets; + ++ struct mtk_xdp_stats xdp_stats; ++ + spinlock_t stats_lock; + u32 reg_offset; + struct u64_stats_sync syncp; diff --git a/target/linux/generic/backport-6.1/706-03-v6.0-net-ethernet-mtk_eth_soc-add-xmit-XDP-support.patch b/target/linux/generic/backport-6.1/706-03-v6.0-net-ethernet-mtk_eth_soc-add-xmit-XDP-support.patch new file mode 100644 index 0000000000..42b3ea215d --- /dev/null +++ b/target/linux/generic/backport-6.1/706-03-v6.0-net-ethernet-mtk_eth_soc-add-xmit-XDP-support.patch @@ -0,0 +1,340 @@ +From 5886d26fd25bbe26130e3e5f7474b9b3e98a3469 Mon Sep 17 00:00:00 2001 +From: Lorenzo Bianconi +Date: Fri, 22 Jul 2022 09:19:39 +0200 +Subject: [PATCH] net: ethernet: mtk_eth_soc: add xmit XDP support + +Introduce XDP support for XDP_TX verdict and ndo_xdp_xmit function +pointer. + +Signed-off-by: Lorenzo Bianconi +Signed-off-by: David S. Miller +--- + drivers/net/ethernet/mediatek/mtk_eth_soc.c | 192 +++++++++++++++++--- + drivers/net/ethernet/mediatek/mtk_eth_soc.h | 10 +- + 2 files changed, 180 insertions(+), 22 deletions(-) + +--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c ++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c +@@ -987,15 +987,26 @@ static void mtk_tx_unmap(struct mtk_eth + } + } + +- tx_buf->flags = 0; +- if (tx_buf->skb && +- (tx_buf->skb != (struct sk_buff *)MTK_DMA_DUMMY_DESC)) { +- if (napi) +- napi_consume_skb(tx_buf->skb, napi); ++ if (tx_buf->type == MTK_TYPE_SKB) { ++ if (tx_buf->data && ++ tx_buf->data != (void *)MTK_DMA_DUMMY_DESC) { ++ struct sk_buff *skb = tx_buf->data; ++ ++ if (napi) ++ napi_consume_skb(skb, napi); ++ else ++ dev_kfree_skb_any(skb); ++ } ++ } else if (tx_buf->data) { ++ struct xdp_frame *xdpf = tx_buf->data; ++ ++ if (napi && tx_buf->type == MTK_TYPE_XDP_TX) ++ xdp_return_frame_rx_napi(xdpf); + else +- dev_kfree_skb_any(tx_buf->skb); ++ xdp_return_frame(xdpf); + } +- tx_buf->skb = NULL; ++ tx_buf->flags = 0; ++ tx_buf->data = NULL; + } + + static void setup_tx_buf(struct mtk_eth *eth, struct mtk_tx_buf *tx_buf, +@@ -1012,7 +1023,7 @@ static void setup_tx_buf(struct mtk_eth + dma_unmap_addr_set(tx_buf, dma_addr1, mapped_addr); + dma_unmap_len_set(tx_buf, dma_len1, size); + } else { +- tx_buf->skb = (struct sk_buff *)MTK_DMA_DUMMY_DESC; ++ tx_buf->data = (void *)MTK_DMA_DUMMY_DESC; + txd->txd1 = mapped_addr; + txd->txd2 = TX_DMA_PLEN0(size); + dma_unmap_addr_set(tx_buf, dma_addr0, mapped_addr); +@@ -1188,7 +1199,7 @@ static int mtk_tx_map(struct sk_buff *sk + soc->txrx.txd_size); + if (new_desc) + memset(tx_buf, 0, sizeof(*tx_buf)); +- tx_buf->skb = (struct sk_buff *)MTK_DMA_DUMMY_DESC; ++ tx_buf->data = (void *)MTK_DMA_DUMMY_DESC; + tx_buf->flags |= MTK_TX_FLAGS_PAGE0; + tx_buf->flags |= (!mac->id) ? MTK_TX_FLAGS_FPORT0 : + MTK_TX_FLAGS_FPORT1; +@@ -1202,7 +1213,8 @@ static int mtk_tx_map(struct sk_buff *sk + } + + /* store skb to cleanup */ +- itx_buf->skb = skb; ++ itx_buf->type = MTK_TYPE_SKB; ++ itx_buf->data = skb; + + if (!MTK_HAS_CAPS(soc->caps, MTK_QDMA)) { + if (k & 0x1) +@@ -1414,13 +1426,14 @@ static struct page_pool *mtk_create_page + .pool_size = size, + .nid = NUMA_NO_NODE, + .dev = eth->dma_dev, +- .dma_dir = DMA_FROM_DEVICE, + .offset = MTK_PP_HEADROOM, + .max_len = MTK_PP_MAX_BUF_SIZE, + }; + struct page_pool *pp; + int err; + ++ pp_params.dma_dir = rcu_access_pointer(eth->prog) ? DMA_BIDIRECTIONAL ++ : DMA_FROM_DEVICE; + pp = page_pool_create(&pp_params); + if (IS_ERR(pp)) + return pp; +@@ -1466,6 +1479,122 @@ static void mtk_rx_put_buff(struct mtk_r + skb_free_frag(data); + } + ++static int mtk_xdp_submit_frame(struct mtk_eth *eth, struct xdp_frame *xdpf, ++ struct net_device *dev, bool dma_map) ++{ ++ const struct mtk_soc_data *soc = eth->soc; ++ struct mtk_tx_ring *ring = ð->tx_ring; ++ struct mtk_tx_dma_desc_info txd_info = { ++ .size = xdpf->len, ++ .first = true, ++ .last = true, ++ }; ++ struct mtk_mac *mac = netdev_priv(dev); ++ struct mtk_tx_dma *txd, *txd_pdma; ++ int err = 0, index = 0, n_desc = 1; ++ struct mtk_tx_buf *tx_buf; ++ ++ if (unlikely(test_bit(MTK_RESETTING, ð->state))) ++ return -EBUSY; ++ ++ if (unlikely(atomic_read(&ring->free_count) <= 1)) ++ return -EBUSY; ++ ++ spin_lock(ð->page_lock); ++ ++ txd = ring->next_free; ++ if (txd == ring->last_free) { ++ err = -ENOMEM; ++ goto out; ++ } ++ ++ tx_buf = mtk_desc_to_tx_buf(ring, txd, soc->txrx.txd_size); ++ memset(tx_buf, 0, sizeof(*tx_buf)); ++ ++ if (dma_map) { /* ndo_xdp_xmit */ ++ txd_info.addr = dma_map_single(eth->dma_dev, xdpf->data, ++ txd_info.size, DMA_TO_DEVICE); ++ if (unlikely(dma_mapping_error(eth->dma_dev, txd_info.addr))) { ++ err = -ENOMEM; ++ goto out; ++ } ++ tx_buf->flags |= MTK_TX_FLAGS_SINGLE0; ++ } else { ++ struct page *page = virt_to_head_page(xdpf->data); ++ ++ txd_info.addr = page_pool_get_dma_addr(page) + ++ sizeof(*xdpf) + xdpf->headroom; ++ dma_sync_single_for_device(eth->dma_dev, txd_info.addr, ++ txd_info.size, ++ DMA_BIDIRECTIONAL); ++ } ++ mtk_tx_set_dma_desc(dev, txd, &txd_info); ++ ++ tx_buf->flags |= !mac->id ? MTK_TX_FLAGS_FPORT0 : MTK_TX_FLAGS_FPORT1; ++ ++ txd_pdma = qdma_to_pdma(ring, txd); ++ setup_tx_buf(eth, tx_buf, txd_pdma, txd_info.addr, txd_info.size, ++ index++); ++ ++ /* store xdpf for cleanup */ ++ tx_buf->type = dma_map ? MTK_TYPE_XDP_NDO : MTK_TYPE_XDP_TX; ++ tx_buf->data = xdpf; ++ ++ if (!MTK_HAS_CAPS(soc->caps, MTK_QDMA)) { ++ if (index & 1) ++ txd_pdma->txd2 |= TX_DMA_LS0; ++ else ++ txd_pdma->txd2 |= TX_DMA_LS1; ++ } ++ ++ ring->next_free = mtk_qdma_phys_to_virt(ring, txd->txd2); ++ atomic_sub(n_desc, &ring->free_count); ++ ++ /* make sure that all changes to the dma ring are flushed before we ++ * continue ++ */ ++ wmb(); ++ ++ if (MTK_HAS_CAPS(soc->caps, MTK_QDMA)) { ++ mtk_w32(eth, txd->txd2, soc->reg_map->qdma.ctx_ptr); ++ } else { ++ int idx; ++ ++ idx = txd_to_idx(ring, txd, soc->txrx.txd_size); ++ mtk_w32(eth, NEXT_DESP_IDX(idx, ring->dma_size), ++ MT7628_TX_CTX_IDX0); ++ } ++out: ++ spin_unlock(ð->page_lock); ++ ++ return err; ++} ++ ++static int mtk_xdp_xmit(struct net_device *dev, int num_frame, ++ struct xdp_frame **frames, u32 flags) ++{ ++ struct mtk_mac *mac = netdev_priv(dev); ++ struct mtk_hw_stats *hw_stats = mac->hw_stats; ++ struct mtk_eth *eth = mac->hw; ++ int i, nxmit = 0; ++ ++ if (unlikely(flags & ~XDP_XMIT_FLAGS_MASK)) ++ return -EINVAL; ++ ++ for (i = 0; i < num_frame; i++) { ++ if (mtk_xdp_submit_frame(eth, frames[i], dev, true)) ++ break; ++ nxmit++; ++ } ++ ++ u64_stats_update_begin(&hw_stats->syncp); ++ hw_stats->xdp_stats.tx_xdp_xmit += nxmit; ++ hw_stats->xdp_stats.tx_xdp_xmit_errors += num_frame - nxmit; ++ u64_stats_update_end(&hw_stats->syncp); ++ ++ return nxmit; ++} ++ + static u32 mtk_xdp_run(struct mtk_eth *eth, struct mtk_rx_ring *ring, + struct xdp_buff *xdp, struct net_device *dev) + { +@@ -1494,6 +1623,18 @@ static u32 mtk_xdp_run(struct mtk_eth *e + + count = &hw_stats->xdp_stats.rx_xdp_redirect; + goto update_stats; ++ case XDP_TX: { ++ struct xdp_frame *xdpf = xdp_convert_buff_to_frame(xdp); ++ ++ if (mtk_xdp_submit_frame(eth, xdpf, dev, false)) { ++ count = &hw_stats->xdp_stats.rx_xdp_tx_errors; ++ act = XDP_DROP; ++ break; ++ } ++ ++ count = &hw_stats->xdp_stats.rx_xdp_tx; ++ goto update_stats; ++ } + default: + bpf_warn_invalid_xdp_action(act); + fallthrough; +@@ -1727,9 +1868,8 @@ static int mtk_poll_tx_qdma(struct mtk_e + { + const struct mtk_reg_map *reg_map = eth->soc->reg_map; + struct mtk_tx_ring *ring = ð->tx_ring; +- struct mtk_tx_dma *desc; +- struct sk_buff *skb; + struct mtk_tx_buf *tx_buf; ++ struct mtk_tx_dma *desc; + u32 cpu, dma; + + cpu = ring->last_free_ptr; +@@ -1750,15 +1890,21 @@ static int mtk_poll_tx_qdma(struct mtk_e + if (tx_buf->flags & MTK_TX_FLAGS_FPORT1) + mac = 1; + +- skb = tx_buf->skb; +- if (!skb) ++ if (!tx_buf->data) + break; + +- if (skb != (struct sk_buff *)MTK_DMA_DUMMY_DESC) { ++ if (tx_buf->type == MTK_TYPE_SKB && ++ tx_buf->data != (void *)MTK_DMA_DUMMY_DESC) { ++ struct sk_buff *skb = tx_buf->data; ++ + bytes[mac] += skb->len; + done[mac]++; + budget--; ++ } else if (tx_buf->type == MTK_TYPE_XDP_TX || ++ tx_buf->type == MTK_TYPE_XDP_NDO) { ++ budget--; + } ++ + mtk_tx_unmap(eth, tx_buf, true); + + ring->last_free = desc; +@@ -1777,9 +1923,8 @@ static int mtk_poll_tx_pdma(struct mtk_e + unsigned int *done, unsigned int *bytes) + { + struct mtk_tx_ring *ring = ð->tx_ring; +- struct mtk_tx_dma *desc; +- struct sk_buff *skb; + struct mtk_tx_buf *tx_buf; ++ struct mtk_tx_dma *desc; + u32 cpu, dma; + + cpu = ring->cpu_idx; +@@ -1787,14 +1932,18 @@ static int mtk_poll_tx_pdma(struct mtk_e + + while ((cpu != dma) && budget) { + tx_buf = &ring->buf[cpu]; +- skb = tx_buf->skb; +- if (!skb) ++ if (!tx_buf->data) + break; + +- if (skb != (struct sk_buff *)MTK_DMA_DUMMY_DESC) { ++ if (tx_buf->type == MTK_TYPE_SKB && ++ tx_buf->data != (void *)MTK_DMA_DUMMY_DESC) { ++ struct sk_buff *skb = tx_buf->data; + bytes[0] += skb->len; + done[0]++; + budget--; ++ } else if (tx_buf->type == MTK_TYPE_XDP_TX || ++ tx_buf->type == MTK_TYPE_XDP_NDO) { ++ budget--; + } + + mtk_tx_unmap(eth, tx_buf, true); +@@ -3475,6 +3624,7 @@ static const struct net_device_ops mtk_n + #endif + .ndo_setup_tc = mtk_eth_setup_tc, + .ndo_bpf = mtk_xdp, ++ .ndo_xdp_xmit = mtk_xdp_xmit, + }; + + static int mtk_add_mac(struct mtk_eth *eth, struct device_node *np) +--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h ++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h +@@ -693,6 +693,12 @@ enum mtk_dev_state { + MTK_RESETTING + }; + ++enum mtk_tx_buf_type { ++ MTK_TYPE_SKB, ++ MTK_TYPE_XDP_TX, ++ MTK_TYPE_XDP_NDO, ++}; ++ + /* struct mtk_tx_buf - This struct holds the pointers to the memory pointed at + * by the TX descriptor s + * @skb: The SKB pointer of the packet being sent +@@ -702,7 +708,9 @@ enum mtk_dev_state { + * @dma_len1: The length of the second segment + */ + struct mtk_tx_buf { +- struct sk_buff *skb; ++ enum mtk_tx_buf_type type; ++ void *data; ++ + u32 flags; + DEFINE_DMA_UNMAP_ADDR(dma_addr0); + DEFINE_DMA_UNMAP_LEN(dma_len0); diff --git a/target/linux/generic/backport-6.1/706-04-v6.0-net-ethernet-mtk_eth_soc-add-support-for-page_pool_g.patch b/target/linux/generic/backport-6.1/706-04-v6.0-net-ethernet-mtk_eth_soc-add-support-for-page_pool_g.patch new file mode 100644 index 0000000000..d372f022c7 --- /dev/null +++ b/target/linux/generic/backport-6.1/706-04-v6.0-net-ethernet-mtk_eth_soc-add-support-for-page_pool_g.patch @@ -0,0 +1,95 @@ +From 84b9cd389036d4a262d8cee794d56c04095358a7 Mon Sep 17 00:00:00 2001 +From: Lorenzo Bianconi +Date: Fri, 22 Jul 2022 09:19:40 +0200 +Subject: [PATCH] net: ethernet: mtk_eth_soc: add support for + page_pool_get_stats + +Introduce support for the page_pool stats API into mtk_eth_soc driver. +Report page_pool stats through ethtool. + +Signed-off-by: Lorenzo Bianconi +Signed-off-by: David S. Miller +--- + drivers/net/ethernet/mediatek/Kconfig | 1 + + drivers/net/ethernet/mediatek/mtk_eth_soc.c | 37 +++++++++++++++++++-- + 2 files changed, 35 insertions(+), 3 deletions(-) + +--- a/drivers/net/ethernet/mediatek/Kconfig ++++ b/drivers/net/ethernet/mediatek/Kconfig +@@ -17,6 +17,7 @@ config NET_MEDIATEK_SOC + select PHYLINK + select DIMLIB + select PAGE_POOL ++ select PAGE_POOL_STATS + help + This driver supports the gigabit ethernet MACs in the + MediaTek SoC family. +--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c ++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c +@@ -3485,11 +3485,18 @@ static void mtk_get_strings(struct net_d + int i; + + switch (stringset) { +- case ETH_SS_STATS: ++ case ETH_SS_STATS: { ++ struct mtk_mac *mac = netdev_priv(dev); ++ + for (i = 0; i < ARRAY_SIZE(mtk_ethtool_stats); i++) { + memcpy(data, mtk_ethtool_stats[i].str, ETH_GSTRING_LEN); + data += ETH_GSTRING_LEN; + } ++ if (mtk_page_pool_enabled(mac->hw)) ++ page_pool_ethtool_stats_get_strings(data); ++ break; ++ } ++ default: + break; + } + } +@@ -3497,13 +3504,35 @@ static void mtk_get_strings(struct net_d + static int mtk_get_sset_count(struct net_device *dev, int sset) + { + switch (sset) { +- case ETH_SS_STATS: +- return ARRAY_SIZE(mtk_ethtool_stats); ++ case ETH_SS_STATS: { ++ int count = ARRAY_SIZE(mtk_ethtool_stats); ++ struct mtk_mac *mac = netdev_priv(dev); ++ ++ if (mtk_page_pool_enabled(mac->hw)) ++ count += page_pool_ethtool_stats_get_count(); ++ return count; ++ } + default: + return -EOPNOTSUPP; + } + } + ++static void mtk_ethtool_pp_stats(struct mtk_eth *eth, u64 *data) ++{ ++ struct page_pool_stats stats = {}; ++ int i; ++ ++ for (i = 0; i < ARRAY_SIZE(eth->rx_ring); i++) { ++ struct mtk_rx_ring *ring = ð->rx_ring[i]; ++ ++ if (!ring->page_pool) ++ continue; ++ ++ page_pool_get_stats(ring->page_pool, &stats); ++ } ++ page_pool_ethtool_stats_get(data, &stats); ++} ++ + static void mtk_get_ethtool_stats(struct net_device *dev, + struct ethtool_stats *stats, u64 *data) + { +@@ -3531,6 +3560,8 @@ static void mtk_get_ethtool_stats(struct + + for (i = 0; i < ARRAY_SIZE(mtk_ethtool_stats); i++) + *data_dst++ = *(data_src + mtk_ethtool_stats[i].offset); ++ if (mtk_page_pool_enabled(mac->hw)) ++ mtk_ethtool_pp_stats(mac->hw, data_dst); + } while (u64_stats_fetch_retry_irq(&hwstats->syncp, start)); + } + diff --git a/target/linux/generic/backport-6.1/706-05-v6.0-net-ethernet-mtk_eth_soc-introduce-mtk_xdp_frame_map.patch b/target/linux/generic/backport-6.1/706-05-v6.0-net-ethernet-mtk_eth_soc-introduce-mtk_xdp_frame_map.patch new file mode 100644 index 0000000000..33a7616652 --- /dev/null +++ b/target/linux/generic/backport-6.1/706-05-v6.0-net-ethernet-mtk_eth_soc-introduce-mtk_xdp_frame_map.patch @@ -0,0 +1,113 @@ +From b16fe6d82b71fa0dd5c957bc22d66a694976d6eb Mon Sep 17 00:00:00 2001 +From: Lorenzo Bianconi +Date: Wed, 27 Jul 2022 23:20:50 +0200 +Subject: [PATCH] net: ethernet: mtk_eth_soc: introduce mtk_xdp_frame_map + utility routine + +This is a preliminary patch to add xdp multi-frag support to mtk_eth_soc +driver + +Signed-off-by: Lorenzo Bianconi +Signed-off-by: David S. Miller +--- + drivers/net/ethernet/mediatek/mtk_eth_soc.c | 68 +++++++++++++-------- + 1 file changed, 42 insertions(+), 26 deletions(-) + +--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c ++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c +@@ -1479,6 +1479,41 @@ static void mtk_rx_put_buff(struct mtk_r + skb_free_frag(data); + } + ++static int mtk_xdp_frame_map(struct mtk_eth *eth, struct net_device *dev, ++ struct mtk_tx_dma_desc_info *txd_info, ++ struct mtk_tx_dma *txd, struct mtk_tx_buf *tx_buf, ++ void *data, u16 headroom, int index, bool dma_map) ++{ ++ struct mtk_tx_ring *ring = ð->tx_ring; ++ struct mtk_mac *mac = netdev_priv(dev); ++ struct mtk_tx_dma *txd_pdma; ++ ++ if (dma_map) { /* ndo_xdp_xmit */ ++ txd_info->addr = dma_map_single(eth->dma_dev, data, ++ txd_info->size, DMA_TO_DEVICE); ++ if (unlikely(dma_mapping_error(eth->dma_dev, txd_info->addr))) ++ return -ENOMEM; ++ ++ tx_buf->flags |= MTK_TX_FLAGS_SINGLE0; ++ } else { ++ struct page *page = virt_to_head_page(data); ++ ++ txd_info->addr = page_pool_get_dma_addr(page) + ++ sizeof(struct xdp_frame) + headroom; ++ dma_sync_single_for_device(eth->dma_dev, txd_info->addr, ++ txd_info->size, DMA_BIDIRECTIONAL); ++ } ++ mtk_tx_set_dma_desc(dev, txd, txd_info); ++ ++ tx_buf->flags |= !mac->id ? MTK_TX_FLAGS_FPORT0 : MTK_TX_FLAGS_FPORT1; ++ ++ txd_pdma = qdma_to_pdma(ring, txd); ++ setup_tx_buf(eth, tx_buf, txd_pdma, txd_info->addr, txd_info->size, ++ index); ++ ++ return 0; ++} ++ + static int mtk_xdp_submit_frame(struct mtk_eth *eth, struct xdp_frame *xdpf, + struct net_device *dev, bool dma_map) + { +@@ -1489,9 +1524,8 @@ static int mtk_xdp_submit_frame(struct m + .first = true, + .last = true, + }; +- struct mtk_mac *mac = netdev_priv(dev); +- struct mtk_tx_dma *txd, *txd_pdma; + int err = 0, index = 0, n_desc = 1; ++ struct mtk_tx_dma *txd, *txd_pdma; + struct mtk_tx_buf *tx_buf; + + if (unlikely(test_bit(MTK_RESETTING, ð->state))) +@@ -1511,36 +1545,18 @@ static int mtk_xdp_submit_frame(struct m + tx_buf = mtk_desc_to_tx_buf(ring, txd, soc->txrx.txd_size); + memset(tx_buf, 0, sizeof(*tx_buf)); + +- if (dma_map) { /* ndo_xdp_xmit */ +- txd_info.addr = dma_map_single(eth->dma_dev, xdpf->data, +- txd_info.size, DMA_TO_DEVICE); +- if (unlikely(dma_mapping_error(eth->dma_dev, txd_info.addr))) { +- err = -ENOMEM; +- goto out; +- } +- tx_buf->flags |= MTK_TX_FLAGS_SINGLE0; +- } else { +- struct page *page = virt_to_head_page(xdpf->data); +- +- txd_info.addr = page_pool_get_dma_addr(page) + +- sizeof(*xdpf) + xdpf->headroom; +- dma_sync_single_for_device(eth->dma_dev, txd_info.addr, +- txd_info.size, +- DMA_BIDIRECTIONAL); +- } +- mtk_tx_set_dma_desc(dev, txd, &txd_info); +- +- tx_buf->flags |= !mac->id ? MTK_TX_FLAGS_FPORT0 : MTK_TX_FLAGS_FPORT1; +- +- txd_pdma = qdma_to_pdma(ring, txd); +- setup_tx_buf(eth, tx_buf, txd_pdma, txd_info.addr, txd_info.size, +- index++); ++ err = mtk_xdp_frame_map(eth, dev, &txd_info, txd, tx_buf, ++ xdpf->data, xdpf->headroom, index, ++ dma_map); ++ if (err < 0) ++ goto out; + + /* store xdpf for cleanup */ + tx_buf->type = dma_map ? MTK_TYPE_XDP_NDO : MTK_TYPE_XDP_TX; + tx_buf->data = xdpf; + + if (!MTK_HAS_CAPS(soc->caps, MTK_QDMA)) { ++ txd_pdma = qdma_to_pdma(ring, txd); + if (index & 1) + txd_pdma->txd2 |= TX_DMA_LS0; + else diff --git a/target/linux/generic/backport-6.1/706-06-v6.0-net-ethernet-mtk_eth_soc-introduce-xdp-multi-frag-su.patch b/target/linux/generic/backport-6.1/706-06-v6.0-net-ethernet-mtk_eth_soc-introduce-xdp-multi-frag-su.patch new file mode 100644 index 0000000000..e75861bc82 --- /dev/null +++ b/target/linux/generic/backport-6.1/706-06-v6.0-net-ethernet-mtk_eth_soc-introduce-xdp-multi-frag-su.patch @@ -0,0 +1,218 @@ +From 155738a4f319538a09f734ce1f5a2eac3ada1de2 Mon Sep 17 00:00:00 2001 +From: Lorenzo Bianconi +Date: Wed, 27 Jul 2022 23:20:51 +0200 +Subject: [PATCH] net: ethernet: mtk_eth_soc: introduce xdp multi-frag support + +Add the capability to map non-linear xdp frames in XDP_TX and +ndo_xdp_xmit callback. + +Signed-off-by: Lorenzo Bianconi +Signed-off-by: David S. Miller +--- + drivers/net/ethernet/mediatek/mtk_eth_soc.c | 125 +++++++++++++------- + 1 file changed, 82 insertions(+), 43 deletions(-) + +--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c ++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c +@@ -987,23 +987,22 @@ static void mtk_tx_unmap(struct mtk_eth + } + } + +- if (tx_buf->type == MTK_TYPE_SKB) { +- if (tx_buf->data && +- tx_buf->data != (void *)MTK_DMA_DUMMY_DESC) { ++ if (tx_buf->data && tx_buf->data != (void *)MTK_DMA_DUMMY_DESC) { ++ if (tx_buf->type == MTK_TYPE_SKB) { + struct sk_buff *skb = tx_buf->data; + + if (napi) + napi_consume_skb(skb, napi); + else + dev_kfree_skb_any(skb); +- } +- } else if (tx_buf->data) { +- struct xdp_frame *xdpf = tx_buf->data; ++ } else { ++ struct xdp_frame *xdpf = tx_buf->data; + +- if (napi && tx_buf->type == MTK_TYPE_XDP_TX) +- xdp_return_frame_rx_napi(xdpf); +- else +- xdp_return_frame(xdpf); ++ if (napi && tx_buf->type == MTK_TYPE_XDP_TX) ++ xdp_return_frame_rx_napi(xdpf); ++ else ++ xdp_return_frame(xdpf); ++ } + } + tx_buf->flags = 0; + tx_buf->data = NULL; +@@ -1506,6 +1505,8 @@ static int mtk_xdp_frame_map(struct mtk_ + mtk_tx_set_dma_desc(dev, txd, txd_info); + + tx_buf->flags |= !mac->id ? MTK_TX_FLAGS_FPORT0 : MTK_TX_FLAGS_FPORT1; ++ tx_buf->type = dma_map ? MTK_TYPE_XDP_NDO : MTK_TYPE_XDP_TX; ++ tx_buf->data = (void *)MTK_DMA_DUMMY_DESC; + + txd_pdma = qdma_to_pdma(ring, txd); + setup_tx_buf(eth, tx_buf, txd_pdma, txd_info->addr, txd_info->size, +@@ -1517,43 +1518,69 @@ static int mtk_xdp_frame_map(struct mtk_ + static int mtk_xdp_submit_frame(struct mtk_eth *eth, struct xdp_frame *xdpf, + struct net_device *dev, bool dma_map) + { ++ struct skb_shared_info *sinfo = xdp_get_shared_info_from_frame(xdpf); + const struct mtk_soc_data *soc = eth->soc; + struct mtk_tx_ring *ring = ð->tx_ring; + struct mtk_tx_dma_desc_info txd_info = { + .size = xdpf->len, + .first = true, +- .last = true, ++ .last = !xdp_frame_has_frags(xdpf), + }; +- int err = 0, index = 0, n_desc = 1; +- struct mtk_tx_dma *txd, *txd_pdma; +- struct mtk_tx_buf *tx_buf; ++ int err, index = 0, n_desc = 1, nr_frags; ++ struct mtk_tx_dma *htxd, *txd, *txd_pdma; ++ struct mtk_tx_buf *htx_buf, *tx_buf; ++ void *data = xdpf->data; + + if (unlikely(test_bit(MTK_RESETTING, ð->state))) + return -EBUSY; + +- if (unlikely(atomic_read(&ring->free_count) <= 1)) ++ nr_frags = unlikely(xdp_frame_has_frags(xdpf)) ? sinfo->nr_frags : 0; ++ if (unlikely(atomic_read(&ring->free_count) <= 1 + nr_frags)) + return -EBUSY; + + spin_lock(ð->page_lock); + + txd = ring->next_free; + if (txd == ring->last_free) { +- err = -ENOMEM; +- goto out; ++ spin_unlock(ð->page_lock); ++ return -ENOMEM; + } ++ htxd = txd; + + tx_buf = mtk_desc_to_tx_buf(ring, txd, soc->txrx.txd_size); + memset(tx_buf, 0, sizeof(*tx_buf)); ++ htx_buf = tx_buf; + +- err = mtk_xdp_frame_map(eth, dev, &txd_info, txd, tx_buf, +- xdpf->data, xdpf->headroom, index, +- dma_map); +- if (err < 0) +- goto out; ++ for (;;) { ++ err = mtk_xdp_frame_map(eth, dev, &txd_info, txd, tx_buf, ++ data, xdpf->headroom, index, dma_map); ++ if (err < 0) ++ goto unmap; ++ ++ if (txd_info.last) ++ break; + ++ if (MTK_HAS_CAPS(soc->caps, MTK_QDMA) || (index & 0x1)) { ++ txd = mtk_qdma_phys_to_virt(ring, txd->txd2); ++ txd_pdma = qdma_to_pdma(ring, txd); ++ if (txd == ring->last_free) ++ goto unmap; ++ ++ tx_buf = mtk_desc_to_tx_buf(ring, txd, ++ soc->txrx.txd_size); ++ memset(tx_buf, 0, sizeof(*tx_buf)); ++ n_desc++; ++ } ++ ++ memset(&txd_info, 0, sizeof(struct mtk_tx_dma_desc_info)); ++ txd_info.size = skb_frag_size(&sinfo->frags[index]); ++ txd_info.last = index + 1 == nr_frags; ++ data = skb_frag_address(&sinfo->frags[index]); ++ ++ index++; ++ } + /* store xdpf for cleanup */ +- tx_buf->type = dma_map ? MTK_TYPE_XDP_NDO : MTK_TYPE_XDP_TX; +- tx_buf->data = xdpf; ++ htx_buf->data = xdpf; + + if (!MTK_HAS_CAPS(soc->caps, MTK_QDMA)) { + txd_pdma = qdma_to_pdma(ring, txd); +@@ -1580,7 +1607,24 @@ static int mtk_xdp_submit_frame(struct m + mtk_w32(eth, NEXT_DESP_IDX(idx, ring->dma_size), + MT7628_TX_CTX_IDX0); + } +-out: ++ ++ spin_unlock(ð->page_lock); ++ ++ return 0; ++ ++unmap: ++ while (htxd != txd) { ++ txd_pdma = qdma_to_pdma(ring, htxd); ++ tx_buf = mtk_desc_to_tx_buf(ring, htxd, soc->txrx.txd_size); ++ mtk_tx_unmap(eth, tx_buf, false); ++ ++ htxd->txd3 = TX_DMA_LS0 | TX_DMA_OWNER_CPU; ++ if (!MTK_HAS_CAPS(soc->caps, MTK_QDMA)) ++ txd_pdma->txd2 = TX_DMA_DESP2_DEF; ++ ++ htxd = mtk_qdma_phys_to_virt(ring, htxd->txd2); ++ } ++ + spin_unlock(ð->page_lock); + + return err; +@@ -1909,18 +1953,15 @@ static int mtk_poll_tx_qdma(struct mtk_e + if (!tx_buf->data) + break; + +- if (tx_buf->type == MTK_TYPE_SKB && +- tx_buf->data != (void *)MTK_DMA_DUMMY_DESC) { +- struct sk_buff *skb = tx_buf->data; ++ if (tx_buf->data != (void *)MTK_DMA_DUMMY_DESC) { ++ if (tx_buf->type == MTK_TYPE_SKB) { ++ struct sk_buff *skb = tx_buf->data; + +- bytes[mac] += skb->len; +- done[mac]++; +- budget--; +- } else if (tx_buf->type == MTK_TYPE_XDP_TX || +- tx_buf->type == MTK_TYPE_XDP_NDO) { ++ bytes[mac] += skb->len; ++ done[mac]++; ++ } + budget--; + } +- + mtk_tx_unmap(eth, tx_buf, true); + + ring->last_free = desc; +@@ -1951,17 +1992,15 @@ static int mtk_poll_tx_pdma(struct mtk_e + if (!tx_buf->data) + break; + +- if (tx_buf->type == MTK_TYPE_SKB && +- tx_buf->data != (void *)MTK_DMA_DUMMY_DESC) { +- struct sk_buff *skb = tx_buf->data; +- bytes[0] += skb->len; +- done[0]++; +- budget--; +- } else if (tx_buf->type == MTK_TYPE_XDP_TX || +- tx_buf->type == MTK_TYPE_XDP_NDO) { ++ if (tx_buf->data != (void *)MTK_DMA_DUMMY_DESC) { ++ if (tx_buf->type == MTK_TYPE_SKB) { ++ struct sk_buff *skb = tx_buf->data; ++ ++ bytes[0] += skb->len; ++ done[0]++; ++ } + budget--; + } +- + mtk_tx_unmap(eth, tx_buf, true); + + desc = ring->dma + cpu * eth->soc->txrx.txd_size; diff --git a/target/linux/generic/backport-6.1/710-v6.0-net-ethernet-mtk_eth_soc-fix-hw-hash-reporting-for-M.patch b/target/linux/generic/backport-6.1/710-v6.0-net-ethernet-mtk_eth_soc-fix-hw-hash-reporting-for-M.patch new file mode 100644 index 0000000000..a3842d35f5 --- /dev/null +++ b/target/linux/generic/backport-6.1/710-v6.0-net-ethernet-mtk_eth_soc-fix-hw-hash-reporting-for-M.patch @@ -0,0 +1,74 @@ +From 0cf731f9ebb5bf6f252055bebf4463a5c0bd490b Mon Sep 17 00:00:00 2001 +From: Lorenzo Bianconi +Date: Tue, 23 Aug 2022 14:24:07 +0200 +Subject: [PATCH] net: ethernet: mtk_eth_soc: fix hw hash reporting for + MTK_NETSYS_V2 + +Properly report hw rx hash for mt7986 chipset accroding to the new dma +descriptor layout. + +Fixes: 197c9e9b17b11 ("net: ethernet: mtk_eth_soc: introduce support for mt7986 chipset") +Signed-off-by: Lorenzo Bianconi +Link: https://lore.kernel.org/r/091394ea4e705fbb35f828011d98d0ba33808f69.1661257293.git.lorenzo@kernel.org +Signed-off-by: Paolo Abeni +--- + drivers/net/ethernet/mediatek/mtk_eth_soc.c | 22 +++++++++++---------- + drivers/net/ethernet/mediatek/mtk_eth_soc.h | 5 +++++ + 2 files changed, 17 insertions(+), 10 deletions(-) + +--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c ++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c +@@ -1845,10 +1845,19 @@ static int mtk_poll_rx(struct napi_struc + skb->dev = netdev; + bytes += skb->len; + +- if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V2)) ++ if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V2)) { ++ hash = trxd.rxd5 & MTK_RXD5_FOE_ENTRY; ++ if (hash != MTK_RXD5_FOE_ENTRY) ++ skb_set_hash(skb, jhash_1word(hash, 0), ++ PKT_HASH_TYPE_L4); + rxdcsum = &trxd.rxd3; +- else ++ } else { ++ hash = trxd.rxd4 & MTK_RXD4_FOE_ENTRY; ++ if (hash != MTK_RXD4_FOE_ENTRY) ++ skb_set_hash(skb, jhash_1word(hash, 0), ++ PKT_HASH_TYPE_L4); + rxdcsum = &trxd.rxd4; ++ } + + if (*rxdcsum & eth->soc->txrx.rx_dma_l4_valid) + skb->ip_summed = CHECKSUM_UNNECESSARY; +@@ -1856,16 +1865,9 @@ static int mtk_poll_rx(struct napi_struc + skb_checksum_none_assert(skb); + skb->protocol = eth_type_trans(skb, netdev); + +- hash = trxd.rxd4 & MTK_RXD4_FOE_ENTRY; +- if (hash != MTK_RXD4_FOE_ENTRY) { +- hash = jhash_1word(hash, 0); +- skb_set_hash(skb, hash, PKT_HASH_TYPE_L4); +- } +- + reason = FIELD_GET(MTK_RXD4_PPE_CPU_REASON, trxd.rxd4); + if (reason == MTK_PPE_CPU_REASON_HIT_UNBIND_RATE_REACHED) +- mtk_ppe_check_skb(eth->ppe, skb, +- trxd.rxd4 & MTK_RXD4_FOE_ENTRY); ++ mtk_ppe_check_skb(eth->ppe, skb, hash); + + if (netdev->features & NETIF_F_HW_VLAN_CTAG_RX) { + if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V2)) { +--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h ++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h +@@ -314,6 +314,11 @@ + #define RX_DMA_L4_VALID_PDMA BIT(30) /* when PDMA is used */ + #define RX_DMA_SPECIAL_TAG BIT(22) + ++/* PDMA descriptor rxd5 */ ++#define MTK_RXD5_FOE_ENTRY GENMASK(14, 0) ++#define MTK_RXD5_PPE_CPU_REASON GENMASK(22, 18) ++#define MTK_RXD5_SRC_PORT GENMASK(29, 26) ++ + #define RX_DMA_GET_SPORT(x) (((x) >> 19) & 0xf) + #define RX_DMA_GET_SPORT_V2(x) (((x) >> 26) & 0x7) + diff --git a/target/linux/generic/backport-6.1/711-v6.0-01-net-ethernet-mtk_eth_soc-fix-off-by-one-check-of-ARR.patch b/target/linux/generic/backport-6.1/711-v6.0-01-net-ethernet-mtk_eth_soc-fix-off-by-one-check-of-ARR.patch new file mode 100644 index 0000000000..0de8ab4376 --- /dev/null +++ b/target/linux/generic/backport-6.1/711-v6.0-01-net-ethernet-mtk_eth_soc-fix-off-by-one-check-of-ARR.patch @@ -0,0 +1,31 @@ +From: Tom Rix +Date: Sat, 16 Jul 2022 17:46:54 -0400 +Subject: [PATCH] net: ethernet: mtk_eth_soc: fix off by one check of + ARRAY_SIZE + +In mtk_wed_tx_ring_setup(.., int idx, ..), idx is used as an index here + struct mtk_wed_ring *ring = &dev->tx_ring[idx]; + +The bounds of idx are checked here + BUG_ON(idx > ARRAY_SIZE(dev->tx_ring)); + +If idx is the size of the array, it will pass this check and overflow. +So change the check to >= . + +Fixes: 804775dfc288 ("net: ethernet: mtk_eth_soc: add support for Wireless Ethernet Dispatch (WED)") +Signed-off-by: Tom Rix +Link: https://lore.kernel.org/r/20220716214654.1540240-1-trix@redhat.com +Signed-off-by: Jakub Kicinski +--- + +--- a/drivers/net/ethernet/mediatek/mtk_wed.c ++++ b/drivers/net/ethernet/mediatek/mtk_wed.c +@@ -651,7 +651,7 @@ mtk_wed_tx_ring_setup(struct mtk_wed_dev + * WDMA RX. + */ + +- BUG_ON(idx > ARRAY_SIZE(dev->tx_ring)); ++ BUG_ON(idx >= ARRAY_SIZE(dev->tx_ring)); + + if (mtk_wed_ring_alloc(dev, ring, MTK_WED_TX_RING_SIZE)) + return -ENOMEM; diff --git a/target/linux/generic/backport-6.1/711-v6.0-02-net-ethernet-mtk_ppe-fix-possible-NULL-pointer-deref.patch b/target/linux/generic/backport-6.1/711-v6.0-02-net-ethernet-mtk_ppe-fix-possible-NULL-pointer-deref.patch new file mode 100644 index 0000000000..fc6e246468 --- /dev/null +++ b/target/linux/generic/backport-6.1/711-v6.0-02-net-ethernet-mtk_ppe-fix-possible-NULL-pointer-deref.patch @@ -0,0 +1,27 @@ +From: Lorenzo Bianconi +Date: Mon, 18 Jul 2022 11:51:53 +0200 +Subject: [PATCH] net: ethernet: mtk_ppe: fix possible NULL pointer dereference + in mtk_flow_get_wdma_info + +odev pointer can be NULL in mtk_flow_offload_replace routine according +to the flower action rules. Fix possible NULL pointer dereference in +mtk_flow_get_wdma_info. + +Fixes: a333215e10cb5 ("net: ethernet: mtk_eth_soc: implement flow offloading to WED devices") +Signed-off-by: Lorenzo Bianconi +Link: https://lore.kernel.org/r/4e1685bc4976e21e364055f6bee86261f8f9ee93.1658137753.git.lorenzo@kernel.org +Signed-off-by: Jakub Kicinski +--- + +--- a/drivers/net/ethernet/mediatek/mtk_ppe_offload.c ++++ b/drivers/net/ethernet/mediatek/mtk_ppe_offload.c +@@ -93,6 +93,9 @@ mtk_flow_get_wdma_info(struct net_device + }; + struct net_device_path path = {}; + ++ if (!ctx.dev) ++ return -ENODEV; ++ + memcpy(ctx.daddr, addr, sizeof(ctx.daddr)); + + if (!IS_ENABLED(CONFIG_NET_MEDIATEK_SOC_WED)) diff --git a/target/linux/generic/backport-6.1/711-v6.0-03-net-ethernet-mtk-ppe-fix-traffic-offload-with-bridge.patch b/target/linux/generic/backport-6.1/711-v6.0-03-net-ethernet-mtk-ppe-fix-traffic-offload-with-bridge.patch new file mode 100644 index 0000000000..c0720152d6 --- /dev/null +++ b/target/linux/generic/backport-6.1/711-v6.0-03-net-ethernet-mtk-ppe-fix-traffic-offload-with-bridge.patch @@ -0,0 +1,64 @@ +From: Lorenzo Bianconi +Date: Fri, 22 Jul 2022 09:06:19 +0200 +Subject: [PATCH] net: ethernet: mtk-ppe: fix traffic offload with bridged wlan + +A typical flow offload scenario for OpenWrt users is routed traffic +received by the wan interface that is redirected to a wlan device +belonging to the lan bridge. Current implementation fails to +fill wdma offload info in mtk_flow_get_wdma_info() since odev device is +the local bridge. Fix the issue running dev_fill_forward_path routine in +mtk_flow_get_wdma_info in order to identify the wlan device. + +Tested-by: Paolo Valerio +Signed-off-by: Lorenzo Bianconi +Signed-off-by: David S. Miller +--- + +--- a/drivers/net/ethernet/mediatek/mtk_ppe_offload.c ++++ b/drivers/net/ethernet/mediatek/mtk_ppe_offload.c +@@ -88,32 +88,28 @@ mtk_flow_offload_mangle_eth(const struct + static int + mtk_flow_get_wdma_info(struct net_device *dev, const u8 *addr, struct mtk_wdma_info *info) + { +- struct net_device_path_ctx ctx = { +- .dev = dev, +- }; +- struct net_device_path path = {}; ++ struct net_device_path_stack stack; ++ struct net_device_path *path; ++ int err; + +- if (!ctx.dev) ++ if (!dev) + return -ENODEV; + +- memcpy(ctx.daddr, addr, sizeof(ctx.daddr)); +- + if (!IS_ENABLED(CONFIG_NET_MEDIATEK_SOC_WED)) + return -1; + +- if (!dev->netdev_ops->ndo_fill_forward_path) +- return -1; +- +- if (dev->netdev_ops->ndo_fill_forward_path(&ctx, &path)) +- return -1; ++ err = dev_fill_forward_path(dev, addr, &stack); ++ if (err) ++ return err; + +- if (path.type != DEV_PATH_MTK_WDMA) ++ path = &stack.path[stack.num_paths - 1]; ++ if (path->type != DEV_PATH_MTK_WDMA) + return -1; + +- info->wdma_idx = path.mtk_wdma.wdma_idx; +- info->queue = path.mtk_wdma.queue; +- info->bss = path.mtk_wdma.bss; +- info->wcid = path.mtk_wdma.wcid; ++ info->wdma_idx = path->mtk_wdma.wdma_idx; ++ info->queue = path->mtk_wdma.queue; ++ info->bss = path->mtk_wdma.bss; ++ info->wcid = path->mtk_wdma.wcid; + + return 0; + } diff --git a/target/linux/generic/backport-6.1/711-v6.0-04-net-ethernet-mtk_eth_soc-remove-mtk_foe_entry_timest.patch b/target/linux/generic/backport-6.1/711-v6.0-04-net-ethernet-mtk_eth_soc-remove-mtk_foe_entry_timest.patch new file mode 100644 index 0000000000..3c28e83551 --- /dev/null +++ b/target/linux/generic/backport-6.1/711-v6.0-04-net-ethernet-mtk_eth_soc-remove-mtk_foe_entry_timest.patch @@ -0,0 +1,33 @@ +From c9daab322313087afde8c46f41df3c628410ae20 Mon Sep 17 00:00:00 2001 +From: Lorenzo Bianconi +Date: Mon, 5 Sep 2022 14:46:01 +0200 +Subject: [PATCH] net: ethernet: mtk_eth_soc: remove mtk_foe_entry_timestamp + +Get rid of mtk_foe_entry_timestamp routine since it is no longer used. + +Signed-off-by: Lorenzo Bianconi +Signed-off-by: David S. Miller +--- + drivers/net/ethernet/mediatek/mtk_ppe.h | 11 ----------- + 1 file changed, 11 deletions(-) + +--- a/drivers/net/ethernet/mediatek/mtk_ppe.h ++++ b/drivers/net/ethernet/mediatek/mtk_ppe.h +@@ -302,17 +302,6 @@ mtk_ppe_check_skb(struct mtk_ppe *ppe, s + __mtk_ppe_check_skb(ppe, skb, hash); + } + +-static inline int +-mtk_foe_entry_timestamp(struct mtk_ppe *ppe, u16 hash) +-{ +- u32 ib1 = READ_ONCE(ppe->foe_table[hash].ib1); +- +- if (FIELD_GET(MTK_FOE_IB1_STATE, ib1) != MTK_FOE_STATE_BIND) +- return -1; +- +- return FIELD_GET(MTK_FOE_IB1_BIND_TIMESTAMP, ib1); +-} +- + int mtk_foe_entry_prepare(struct mtk_foe_entry *entry, int type, int l4proto, + u8 pse_port, u8 *src_mac, u8 *dest_mac); + int mtk_foe_entry_set_pse_port(struct mtk_foe_entry *entry, u8 port); diff --git a/target/linux/generic/backport-6.1/712-v6.0-net-ethernet-mtk_eth_soc-enable-XDP-support-just-for.patch b/target/linux/generic/backport-6.1/712-v6.0-net-ethernet-mtk_eth_soc-enable-XDP-support-just-for.patch new file mode 100644 index 0000000000..f4eb030ef6 --- /dev/null +++ b/target/linux/generic/backport-6.1/712-v6.0-net-ethernet-mtk_eth_soc-enable-XDP-support-just-for.patch @@ -0,0 +1,35 @@ +From 5e69163d3b9931098922b3fc2f8e786af8c1f37e Mon Sep 17 00:00:00 2001 +From: Lorenzo Bianconi +Date: Tue, 13 Sep 2022 15:03:05 +0200 +Subject: [PATCH] net: ethernet: mtk_eth_soc: enable XDP support just for + MT7986 SoC +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Disable page_pool/XDP support for MT7621 SoC in order fix a regression +introduce adding XDP for MT7986 SoC. There is no a real use case for XDP +on MT7621 since it is a low-end cpu. Moreover this patch reduces the +memory footprint. + +Tested-by: Sergio Paracuellos +Tested-by: Arınç ÜNAL +Fixes: 23233e577ef9 ("net: ethernet: mtk_eth_soc: rely on page_pool for single page buffers") +Signed-off-by: Lorenzo Bianconi +Link: https://lore.kernel.org/r/2bf31e27b888c43228b0d84dd2ef5033338269e2.1663074002.git.lorenzo@kernel.org +Signed-off-by: Jakub Kicinski +--- + drivers/net/ethernet/mediatek/mtk_eth_soc.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c ++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c +@@ -1412,7 +1412,7 @@ static void mtk_update_rx_cpu_idx(struct + + static bool mtk_page_pool_enabled(struct mtk_eth *eth) + { +- return !eth->hwlro; ++ return MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V2); + } + + static struct page_pool *mtk_create_page_pool(struct mtk_eth *eth, diff --git a/target/linux/generic/backport-6.1/713-v6.0-net-ethernet-mtk_eth_soc-move-gdma_to_ppe-and-ppe_ba.patch b/target/linux/generic/backport-6.1/713-v6.0-net-ethernet-mtk_eth_soc-move-gdma_to_ppe-and-ppe_ba.patch new file mode 100644 index 0000000000..008e20416d --- /dev/null +++ b/target/linux/generic/backport-6.1/713-v6.0-net-ethernet-mtk_eth_soc-move-gdma_to_ppe-and-ppe_ba.patch @@ -0,0 +1,127 @@ +From patchwork Thu Sep 8 19:33:38 2022 +Content-Type: text/plain; charset="utf-8" +MIME-Version: 1.0 +Content-Transfer-Encoding: 7bit +X-Patchwork-Submitter: Lorenzo Bianconi +X-Patchwork-Id: 12970556 +X-Patchwork-Delegate: kuba@kernel.org +From: Lorenzo Bianconi +To: netdev@vger.kernel.org +Cc: nbd@nbd.name, john@phrozen.org, sean.wang@mediatek.com, + Mark-MC.Lee@mediatek.com, davem@davemloft.net, edumazet@google.com, + kuba@kernel.org, pabeni@redhat.com, matthias.bgg@gmail.com, + linux-mediatek@lists.infradead.org, lorenzo.bianconi@redhat.com, + Bo.Jiao@mediatek.com, sujuan.chen@mediatek.com, + ryder.Lee@mediatek.com, evelyn.tsai@mediatek.com, + devicetree@vger.kernel.org, robh@kernel.org +Subject: [PATCH net-next 03/12] net: ethernet: mtk_eth_soc: move gdma_to_ppe + and ppe_base definitions in mtk register map +Date: Thu, 8 Sep 2022 21:33:37 +0200 +Message-Id: + <95938fc9cbe0223714be2658a49ca58e9baace00.1662661555.git.lorenzo@kernel.org> +X-Mailer: git-send-email 2.37.3 +In-Reply-To: +References: +MIME-Version: 1.0 +Precedence: bulk +List-ID: +X-Mailing-List: netdev@vger.kernel.org +X-Patchwork-Delegate: kuba@kernel.org + +This is a preliminary patch to introduce mt7986 hw packet engine. + +Signed-off-by: Lorenzo Bianconi +--- + drivers/net/ethernet/mediatek/mtk_eth_soc.c | 15 +++++++++++---- + drivers/net/ethernet/mediatek/mtk_eth_soc.h | 3 ++- + drivers/net/ethernet/mediatek/mtk_ppe.h | 2 -- + 3 files changed, 13 insertions(+), 7 deletions(-) + +--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c ++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c +@@ -73,6 +73,8 @@ static const struct mtk_reg_map mtk_reg_ + .fq_blen = 0x1b2c, + }, + .gdm1_cnt = 0x2400, ++ .gdma_to_ppe0 = 0x4444, ++ .ppe_base = 0x0c00, + }; + + static const struct mtk_reg_map mt7628_reg_map = { +@@ -126,6 +128,8 @@ static const struct mtk_reg_map mt7986_r + .fq_blen = 0x472c, + }, + .gdm1_cnt = 0x1c00, ++ .gdma_to_ppe0 = 0x3333, ++ .ppe_base = 0x2000, + }; + + /* strings used by ethtool */ +@@ -2924,6 +2928,7 @@ static int mtk_open(struct net_device *d + + /* we run 2 netdevs on the same dma ring so we only bring it up once */ + if (!refcount_read(ð->dma_refcnt)) { ++ const struct mtk_soc_data *soc = eth->soc; + u32 gdm_config = MTK_GDMA_TO_PDMA; + int err; + +@@ -2931,15 +2936,15 @@ static int mtk_open(struct net_device *d + if (err) + return err; + +- if (eth->soc->offload_version && mtk_ppe_start(eth->ppe) == 0) +- gdm_config = MTK_GDMA_TO_PPE; ++ if (soc->offload_version && mtk_ppe_start(eth->ppe) == 0) ++ gdm_config = soc->reg_map->gdma_to_ppe0; + + mtk_gdm_config(eth, gdm_config); + + napi_enable(ð->tx_napi); + napi_enable(ð->rx_napi); + mtk_tx_irq_enable(eth, MTK_TX_DONE_INT); +- mtk_rx_irq_enable(eth, eth->soc->txrx.rx_irq_done_mask); ++ mtk_rx_irq_enable(eth, soc->txrx.rx_irq_done_mask); + refcount_set(ð->dma_refcnt, 1); + } + else +@@ -4045,7 +4050,9 @@ static int mtk_probe(struct platform_dev + } + + if (eth->soc->offload_version) { +- eth->ppe = mtk_ppe_init(eth, eth->base + MTK_ETH_PPE_BASE, 2); ++ u32 ppe_addr = eth->soc->reg_map->ppe_base; ++ ++ eth->ppe = mtk_ppe_init(eth, eth->base + ppe_addr, 2); + if (!eth->ppe) { + err = -ENOMEM; + goto err_free_dev; +--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h ++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h +@@ -105,7 +105,6 @@ + #define MTK_GDMA_TCS_EN BIT(21) + #define MTK_GDMA_UCS_EN BIT(20) + #define MTK_GDMA_TO_PDMA 0x0 +-#define MTK_GDMA_TO_PPE 0x4444 + #define MTK_GDMA_DROP_ALL 0x7777 + + /* Unicast Filter MAC Address Register - Low */ +@@ -952,6 +951,8 @@ struct mtk_reg_map { + u32 fq_blen; /* fq free page buffer length */ + } qdma; + u32 gdm1_cnt; ++ u32 gdma_to_ppe0; ++ u32 ppe_base; + }; + + /* struct mtk_eth_data - This is the structure holding all differences +--- a/drivers/net/ethernet/mediatek/mtk_ppe.h ++++ b/drivers/net/ethernet/mediatek/mtk_ppe.h +@@ -8,8 +8,6 @@ + #include + #include + +-#define MTK_ETH_PPE_BASE 0xc00 +- + #define MTK_PPE_ENTRIES_SHIFT 3 + #define MTK_PPE_ENTRIES (1024 << MTK_PPE_ENTRIES_SHIFT) + #define MTK_PPE_HASH_MASK (MTK_PPE_ENTRIES - 1) diff --git a/target/linux/generic/backport-6.1/714-v6.0-net-ethernet-mtk_eth_soc-move-ppe-table-hash-offset-.patch b/target/linux/generic/backport-6.1/714-v6.0-net-ethernet-mtk_eth_soc-move-ppe-table-hash-offset-.patch new file mode 100644 index 0000000000..dd3bb15800 --- /dev/null +++ b/target/linux/generic/backport-6.1/714-v6.0-net-ethernet-mtk_eth_soc-move-ppe-table-hash-offset-.patch @@ -0,0 +1,199 @@ +From patchwork Thu Sep 8 19:33:38 2022 +Content-Type: text/plain; charset="utf-8" +MIME-Version: 1.0 +Content-Transfer-Encoding: 7bit +X-Patchwork-Submitter: Lorenzo Bianconi +X-Patchwork-Id: 12970557 +X-Patchwork-Delegate: kuba@kernel.org +From: Lorenzo Bianconi +To: netdev@vger.kernel.org +Cc: nbd@nbd.name, john@phrozen.org, sean.wang@mediatek.com, + Mark-MC.Lee@mediatek.com, davem@davemloft.net, edumazet@google.com, + kuba@kernel.org, pabeni@redhat.com, matthias.bgg@gmail.com, + linux-mediatek@lists.infradead.org, lorenzo.bianconi@redhat.com, + Bo.Jiao@mediatek.com, sujuan.chen@mediatek.com, + ryder.Lee@mediatek.com, evelyn.tsai@mediatek.com, + devicetree@vger.kernel.org, robh@kernel.org +Subject: [PATCH net-next 04/12] net: ethernet: mtk_eth_soc: move ppe table + hash offset to mtk_soc_data structure +Date: Thu, 8 Sep 2022 21:33:38 +0200 +Message-Id: + +X-Mailer: git-send-email 2.37.3 +In-Reply-To: +References: +MIME-Version: 1.0 +Precedence: bulk +List-ID: +X-Mailing-List: netdev@vger.kernel.org +X-Patchwork-Delegate: kuba@kernel.org + +This is a preliminary patch to introduce mt7986 hw packet engine. + +Co-developed-by: Bo Jiao +Signed-off-by: Bo Jiao +Co-developed-by: Sujuan Chen +Signed-off-by: Sujuan Chen +Signed-off-by: Lorenzo Bianconi +--- + drivers/net/ethernet/mediatek/mtk_eth_soc.c | 4 ++++ + drivers/net/ethernet/mediatek/mtk_eth_soc.h | 2 ++ + drivers/net/ethernet/mediatek/mtk_ppe.c | 24 +++++++++++++++------ + drivers/net/ethernet/mediatek/mtk_ppe.h | 2 +- + 4 files changed, 25 insertions(+), 7 deletions(-) + +--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c ++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c +@@ -4148,6 +4148,7 @@ static const struct mtk_soc_data mt7621_ + .required_clks = MT7621_CLKS_BITMAP, + .required_pctl = false, + .offload_version = 2, ++ .hash_offset = 2, + .txrx = { + .txd_size = sizeof(struct mtk_tx_dma), + .rxd_size = sizeof(struct mtk_rx_dma), +@@ -4166,6 +4167,7 @@ static const struct mtk_soc_data mt7622_ + .required_clks = MT7622_CLKS_BITMAP, + .required_pctl = false, + .offload_version = 2, ++ .hash_offset = 2, + .txrx = { + .txd_size = sizeof(struct mtk_tx_dma), + .rxd_size = sizeof(struct mtk_rx_dma), +@@ -4183,6 +4185,7 @@ static const struct mtk_soc_data mt7623_ + .required_clks = MT7623_CLKS_BITMAP, + .required_pctl = true, + .offload_version = 2, ++ .hash_offset = 2, + .txrx = { + .txd_size = sizeof(struct mtk_tx_dma), + .rxd_size = sizeof(struct mtk_rx_dma), +@@ -4216,6 +4219,7 @@ static const struct mtk_soc_data mt7986_ + .caps = MT7986_CAPS, + .required_clks = MT7986_CLKS_BITMAP, + .required_pctl = false, ++ .hash_offset = 4, + .txrx = { + .txd_size = sizeof(struct mtk_tx_dma_v2), + .rxd_size = sizeof(struct mtk_rx_dma_v2), +--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h ++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h +@@ -966,6 +966,7 @@ struct mtk_reg_map { + * the target SoC + * @required_pctl A bool value to show whether the SoC requires + * the extra setup for those pins used by GMAC. ++ * @hash_offset Flow table hash offset. + * @txd_size Tx DMA descriptor size. + * @rxd_size Rx DMA descriptor size. + * @rx_irq_done_mask Rx irq done register mask. +@@ -980,6 +981,7 @@ struct mtk_soc_data { + u32 required_clks; + bool required_pctl; + u8 offload_version; ++ u8 hash_offset; + netdev_features_t hw_features; + struct { + u32 txd_size; +--- a/drivers/net/ethernet/mediatek/mtk_ppe.c ++++ b/drivers/net/ethernet/mediatek/mtk_ppe.c +@@ -88,7 +88,7 @@ static void mtk_ppe_cache_enable(struct + enable * MTK_PPE_CACHE_CTL_EN); + } + +-static u32 mtk_ppe_hash_entry(struct mtk_foe_entry *e) ++static u32 mtk_ppe_hash_entry(struct mtk_eth *eth, struct mtk_foe_entry *e) + { + u32 hv1, hv2, hv3; + u32 hash; +@@ -122,7 +122,7 @@ static u32 mtk_ppe_hash_entry(struct mtk + hash = (hash >> 24) | ((hash & 0xffffff) << 8); + hash ^= hv1 ^ hv2 ^ hv3; + hash ^= hash >> 16; +- hash <<= 1; ++ hash <<= (ffs(eth->soc->hash_offset) - 1); + hash &= MTK_PPE_ENTRIES - 1; + + return hash; +@@ -540,15 +540,16 @@ mtk_foe_entry_commit_l2(struct mtk_ppe * + int mtk_foe_entry_commit(struct mtk_ppe *ppe, struct mtk_flow_entry *entry) + { + int type = FIELD_GET(MTK_FOE_IB1_PACKET_TYPE, entry->data.ib1); ++ const struct mtk_soc_data *soc = ppe->eth->soc; + u32 hash; + + if (type == MTK_PPE_PKT_TYPE_BRIDGE) + return mtk_foe_entry_commit_l2(ppe, entry); + +- hash = mtk_ppe_hash_entry(&entry->data); ++ hash = mtk_ppe_hash_entry(ppe->eth, &entry->data); + entry->hash = 0xffff; + spin_lock_bh(&ppe_lock); +- hlist_add_head(&entry->list, &ppe->foe_flow[hash / 2]); ++ hlist_add_head(&entry->list, &ppe->foe_flow[hash / soc->hash_offset]); + spin_unlock_bh(&ppe_lock); + + return 0; +@@ -558,6 +559,7 @@ static void + mtk_foe_entry_commit_subflow(struct mtk_ppe *ppe, struct mtk_flow_entry *entry, + u16 hash) + { ++ const struct mtk_soc_data *soc = ppe->eth->soc; + struct mtk_flow_entry *flow_info; + struct mtk_foe_entry foe, *hwe; + struct mtk_foe_mac_info *l2; +@@ -572,7 +574,8 @@ mtk_foe_entry_commit_subflow(struct mtk_ + flow_info->l2_data.base_flow = entry; + flow_info->type = MTK_FLOW_TYPE_L2_SUBFLOW; + flow_info->hash = hash; +- hlist_add_head(&flow_info->list, &ppe->foe_flow[hash / 2]); ++ hlist_add_head(&flow_info->list, ++ &ppe->foe_flow[hash / soc->hash_offset]); + hlist_add_head(&flow_info->l2_data.list, &entry->l2_flows); + + hwe = &ppe->foe_table[hash]; +@@ -596,7 +599,8 @@ mtk_foe_entry_commit_subflow(struct mtk_ + + void __mtk_ppe_check_skb(struct mtk_ppe *ppe, struct sk_buff *skb, u16 hash) + { +- struct hlist_head *head = &ppe->foe_flow[hash / 2]; ++ const struct mtk_soc_data *soc = ppe->eth->soc; ++ struct hlist_head *head = &ppe->foe_flow[hash / soc->hash_offset]; + struct mtk_foe_entry *hwe = &ppe->foe_table[hash]; + struct mtk_flow_entry *entry; + struct mtk_foe_bridge key = {}; +@@ -680,9 +684,11 @@ int mtk_foe_entry_idle_time(struct mtk_p + struct mtk_ppe *mtk_ppe_init(struct mtk_eth *eth, void __iomem *base, + int version) + { ++ const struct mtk_soc_data *soc = eth->soc; + struct device *dev = eth->dev; + struct mtk_foe_entry *foe; + struct mtk_ppe *ppe; ++ u32 foe_flow_size; + + ppe = devm_kzalloc(dev, sizeof(*ppe), GFP_KERNEL); + if (!ppe) +@@ -705,6 +711,12 @@ struct mtk_ppe *mtk_ppe_init(struct mtk_ + + ppe->foe_table = foe; + ++ foe_flow_size = (MTK_PPE_ENTRIES / soc->hash_offset) * ++ sizeof(*ppe->foe_flow); ++ ppe->foe_flow = devm_kzalloc(dev, foe_flow_size, GFP_KERNEL); ++ if (!ppe->foe_flow) ++ return NULL; ++ + mtk_ppe_debugfs_init(ppe); + + return ppe; +--- a/drivers/net/ethernet/mediatek/mtk_ppe.h ++++ b/drivers/net/ethernet/mediatek/mtk_ppe.h +@@ -270,7 +270,7 @@ struct mtk_ppe { + dma_addr_t foe_phys; + + u16 foe_check_time[MTK_PPE_ENTRIES]; +- struct hlist_head foe_flow[MTK_PPE_ENTRIES / 2]; ++ struct hlist_head *foe_flow; + + struct rhashtable l2_flows; + diff --git a/target/linux/generic/backport-6.1/715-v6.0-net-ethernet-mtk_eth_soc-add-the-capability-to-run-m.patch b/target/linux/generic/backport-6.1/715-v6.0-net-ethernet-mtk_eth_soc-add-the-capability-to-run-m.patch new file mode 100644 index 0000000000..0b207ff6a1 --- /dev/null +++ b/target/linux/generic/backport-6.1/715-v6.0-net-ethernet-mtk_eth_soc-add-the-capability-to-run-m.patch @@ -0,0 +1,317 @@ +From patchwork Thu Sep 8 19:33:39 2022 +Content-Type: text/plain; charset="utf-8" +MIME-Version: 1.0 +Content-Transfer-Encoding: 7bit +X-Patchwork-Submitter: Lorenzo Bianconi +X-Patchwork-Id: 12970559 +X-Patchwork-Delegate: kuba@kernel.org +Return-Path: +From: Lorenzo Bianconi +To: netdev@vger.kernel.org +Cc: nbd@nbd.name, john@phrozen.org, sean.wang@mediatek.com, + Mark-MC.Lee@mediatek.com, davem@davemloft.net, edumazet@google.com, + kuba@kernel.org, pabeni@redhat.com, matthias.bgg@gmail.com, + linux-mediatek@lists.infradead.org, lorenzo.bianconi@redhat.com, + Bo.Jiao@mediatek.com, sujuan.chen@mediatek.com, + ryder.Lee@mediatek.com, evelyn.tsai@mediatek.com, + devicetree@vger.kernel.org, robh@kernel.org +Subject: [PATCH net-next 05/12] net: ethernet: mtk_eth_soc: add the capability + to run multiple ppe +Date: Thu, 8 Sep 2022 21:33:39 +0200 +Message-Id: + +X-Mailer: git-send-email 2.37.3 +In-Reply-To: +References: +MIME-Version: 1.0 +Precedence: bulk +List-ID: +X-Mailing-List: netdev@vger.kernel.org +X-Patchwork-Delegate: kuba@kernel.org + +mt7986 chipset support multiple packet engines for wlan <-> eth +packet forwarding. + +Co-developed-by: Bo Jiao +Signed-off-by: Bo Jiao +Co-developed-by: Sujuan Chen +Signed-off-by: Sujuan Chen +Signed-off-by: Lorenzo Bianconi +--- + drivers/net/ethernet/mediatek/mtk_eth_soc.c | 35 ++++++++++++------- + drivers/net/ethernet/mediatek/mtk_eth_soc.h | 2 +- + drivers/net/ethernet/mediatek/mtk_ppe.c | 14 +++++--- + drivers/net/ethernet/mediatek/mtk_ppe.h | 9 +++-- + .../net/ethernet/mediatek/mtk_ppe_debugfs.c | 8 ++--- + .../net/ethernet/mediatek/mtk_ppe_offload.c | 13 +++---- + 6 files changed, 48 insertions(+), 33 deletions(-) + +--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c ++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c +@@ -1871,7 +1871,7 @@ static int mtk_poll_rx(struct napi_struc + + reason = FIELD_GET(MTK_RXD4_PPE_CPU_REASON, trxd.rxd4); + if (reason == MTK_PPE_CPU_REASON_HIT_UNBIND_RATE_REACHED) +- mtk_ppe_check_skb(eth->ppe, skb, hash); ++ mtk_ppe_check_skb(eth->ppe[0], skb, hash); + + if (netdev->features & NETIF_F_HW_VLAN_CTAG_RX) { + if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V2)) { +@@ -2929,15 +2929,19 @@ static int mtk_open(struct net_device *d + /* we run 2 netdevs on the same dma ring so we only bring it up once */ + if (!refcount_read(ð->dma_refcnt)) { + const struct mtk_soc_data *soc = eth->soc; +- u32 gdm_config = MTK_GDMA_TO_PDMA; ++ u32 gdm_config; ++ int i; + int err; + + err = mtk_start_dma(eth); + if (err) + return err; + +- if (soc->offload_version && mtk_ppe_start(eth->ppe) == 0) +- gdm_config = soc->reg_map->gdma_to_ppe0; ++ for (i = 0; i < ARRAY_SIZE(eth->ppe); i++) ++ mtk_ppe_start(eth->ppe[i]); ++ ++ gdm_config = soc->offload_version ? soc->reg_map->gdma_to_ppe0 ++ : MTK_GDMA_TO_PDMA; + + mtk_gdm_config(eth, gdm_config); + +@@ -2982,6 +2986,7 @@ static int mtk_stop(struct net_device *d + { + struct mtk_mac *mac = netdev_priv(dev); + struct mtk_eth *eth = mac->hw; ++ int i; + + phylink_stop(mac->phylink); + +@@ -3009,8 +3014,8 @@ static int mtk_stop(struct net_device *d + + mtk_dma_free(eth); + +- if (eth->soc->offload_version) +- mtk_ppe_stop(eth->ppe); ++ for (i = 0; i < ARRAY_SIZE(eth->ppe); i++) ++ mtk_ppe_stop(eth->ppe[i]); + + return 0; + } +@@ -4050,12 +4055,19 @@ static int mtk_probe(struct platform_dev + } + + if (eth->soc->offload_version) { +- u32 ppe_addr = eth->soc->reg_map->ppe_base; ++ u32 num_ppe; + +- eth->ppe = mtk_ppe_init(eth, eth->base + ppe_addr, 2); +- if (!eth->ppe) { +- err = -ENOMEM; +- goto err_free_dev; ++ num_ppe = MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V2) ? 2 : 1; ++ num_ppe = min_t(u32, ARRAY_SIZE(eth->ppe), num_ppe); ++ for (i = 0; i < num_ppe; i++) { ++ u32 ppe_addr = eth->soc->reg_map->ppe_base + i * 0x400; ++ ++ eth->ppe[i] = mtk_ppe_init(eth, eth->base + ppe_addr, ++ eth->soc->offload_version, i); ++ if (!eth->ppe[i]) { ++ err = -ENOMEM; ++ goto err_free_dev; ++ } + } + + err = mtk_eth_offload_init(eth); +--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h ++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h +@@ -1111,7 +1111,7 @@ struct mtk_eth { + + int ip_align; + +- struct mtk_ppe *ppe; ++ struct mtk_ppe *ppe[2]; + struct rhashtable flow_table; + + struct bpf_prog __rcu *prog; +--- a/drivers/net/ethernet/mediatek/mtk_ppe.c ++++ b/drivers/net/ethernet/mediatek/mtk_ppe.c +@@ -682,7 +682,7 @@ int mtk_foe_entry_idle_time(struct mtk_p + } + + struct mtk_ppe *mtk_ppe_init(struct mtk_eth *eth, void __iomem *base, +- int version) ++ int version, int index) + { + const struct mtk_soc_data *soc = eth->soc; + struct device *dev = eth->dev; +@@ -717,7 +717,7 @@ struct mtk_ppe *mtk_ppe_init(struct mtk_ + if (!ppe->foe_flow) + return NULL; + +- mtk_ppe_debugfs_init(ppe); ++ mtk_ppe_debugfs_init(ppe, index); + + return ppe; + } +@@ -738,10 +738,13 @@ static void mtk_ppe_init_foe_table(struc + ppe->foe_table[i + skip[k]].ib1 |= MTK_FOE_IB1_STATIC; + } + +-int mtk_ppe_start(struct mtk_ppe *ppe) ++void mtk_ppe_start(struct mtk_ppe *ppe) + { + u32 val; + ++ if (!ppe) ++ return; ++ + mtk_ppe_init_foe_table(ppe); + ppe_w32(ppe, MTK_PPE_TB_BASE, ppe->foe_phys); + +@@ -809,8 +812,6 @@ int mtk_ppe_start(struct mtk_ppe *ppe) + ppe_w32(ppe, MTK_PPE_GLO_CFG, val); + + ppe_w32(ppe, MTK_PPE_DEFAULT_CPU_PORT, 0); +- +- return 0; + } + + int mtk_ppe_stop(struct mtk_ppe *ppe) +@@ -818,6 +819,9 @@ int mtk_ppe_stop(struct mtk_ppe *ppe) + u32 val; + int i; + ++ if (!ppe) ++ return 0; ++ + for (i = 0; i < MTK_PPE_ENTRIES; i++) + ppe->foe_table[i].ib1 = FIELD_PREP(MTK_FOE_IB1_STATE, + MTK_FOE_STATE_INVALID); +--- a/drivers/net/ethernet/mediatek/mtk_ppe.h ++++ b/drivers/net/ethernet/mediatek/mtk_ppe.h +@@ -247,6 +247,7 @@ struct mtk_flow_entry { + }; + u8 type; + s8 wed_index; ++ u8 ppe_index; + u16 hash; + union { + struct mtk_foe_entry data; +@@ -265,6 +266,7 @@ struct mtk_ppe { + struct device *dev; + void __iomem *base; + int version; ++ char dirname[5]; + + struct mtk_foe_entry *foe_table; + dma_addr_t foe_phys; +@@ -277,8 +279,9 @@ struct mtk_ppe { + void *acct_table; + }; + +-struct mtk_ppe *mtk_ppe_init(struct mtk_eth *eth, void __iomem *base, int version); +-int mtk_ppe_start(struct mtk_ppe *ppe); ++struct mtk_ppe *mtk_ppe_init(struct mtk_eth *eth, void __iomem *base, ++ int version, int index); ++void mtk_ppe_start(struct mtk_ppe *ppe); + int mtk_ppe_stop(struct mtk_ppe *ppe); + + void __mtk_ppe_check_skb(struct mtk_ppe *ppe, struct sk_buff *skb, u16 hash); +@@ -317,6 +320,6 @@ int mtk_foe_entry_set_wdma(struct mtk_fo + int mtk_foe_entry_commit(struct mtk_ppe *ppe, struct mtk_flow_entry *entry); + void mtk_foe_entry_clear(struct mtk_ppe *ppe, struct mtk_flow_entry *entry); + int mtk_foe_entry_idle_time(struct mtk_ppe *ppe, struct mtk_flow_entry *entry); +-int mtk_ppe_debugfs_init(struct mtk_ppe *ppe); ++int mtk_ppe_debugfs_init(struct mtk_ppe *ppe, int index); + + #endif +--- a/drivers/net/ethernet/mediatek/mtk_ppe_debugfs.c ++++ b/drivers/net/ethernet/mediatek/mtk_ppe_debugfs.c +@@ -187,7 +187,7 @@ mtk_ppe_debugfs_foe_open_bind(struct ino + inode->i_private); + } + +-int mtk_ppe_debugfs_init(struct mtk_ppe *ppe) ++int mtk_ppe_debugfs_init(struct mtk_ppe *ppe, int index) + { + static const struct file_operations fops_all = { + .open = mtk_ppe_debugfs_foe_open_all, +@@ -195,17 +195,17 @@ int mtk_ppe_debugfs_init(struct mtk_ppe + .llseek = seq_lseek, + .release = single_release, + }; +- + static const struct file_operations fops_bind = { + .open = mtk_ppe_debugfs_foe_open_bind, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, + }; +- + struct dentry *root; + +- root = debugfs_create_dir("mtk_ppe", NULL); ++ snprintf(ppe->dirname, sizeof(ppe->dirname), "ppe%d", index); ++ ++ root = debugfs_create_dir(ppe->dirname, NULL); + if (!root) + return -ENOMEM; + +--- a/drivers/net/ethernet/mediatek/mtk_ppe_offload.c ++++ b/drivers/net/ethernet/mediatek/mtk_ppe_offload.c +@@ -434,7 +434,7 @@ mtk_flow_offload_replace(struct mtk_eth + memcpy(&entry->data, &foe, sizeof(entry->data)); + entry->wed_index = wed_index; + +- err = mtk_foe_entry_commit(eth->ppe, entry); ++ err = mtk_foe_entry_commit(eth->ppe[entry->ppe_index], entry); + if (err < 0) + goto free; + +@@ -446,7 +446,7 @@ mtk_flow_offload_replace(struct mtk_eth + return 0; + + clear: +- mtk_foe_entry_clear(eth->ppe, entry); ++ mtk_foe_entry_clear(eth->ppe[entry->ppe_index], entry); + free: + kfree(entry); + if (wed_index >= 0) +@@ -464,7 +464,7 @@ mtk_flow_offload_destroy(struct mtk_eth + if (!entry) + return -ENOENT; + +- mtk_foe_entry_clear(eth->ppe, entry); ++ mtk_foe_entry_clear(eth->ppe[entry->ppe_index], entry); + rhashtable_remove_fast(ð->flow_table, &entry->node, + mtk_flow_ht_params); + if (entry->wed_index >= 0) +@@ -485,7 +485,7 @@ mtk_flow_offload_stats(struct mtk_eth *e + if (!entry) + return -ENOENT; + +- idle = mtk_foe_entry_idle_time(eth->ppe, entry); ++ idle = mtk_foe_entry_idle_time(eth->ppe[entry->ppe_index], entry); + f->stats.lastused = jiffies - idle * HZ; + + return 0; +@@ -537,7 +537,7 @@ mtk_eth_setup_tc_block(struct net_device + struct flow_block_cb *block_cb; + flow_setup_cb_t *cb; + +- if (!eth->ppe || !eth->ppe->foe_table) ++ if (!eth->soc->offload_version) + return -EOPNOTSUPP; + + if (f->binder_type != FLOW_BLOCK_BINDER_TYPE_CLSACT_INGRESS) +@@ -589,8 +589,5 @@ int mtk_eth_setup_tc(struct net_device * + + int mtk_eth_offload_init(struct mtk_eth *eth) + { +- if (!eth->ppe || !eth->ppe->foe_table) +- return 0; +- + return rhashtable_init(ð->flow_table, &mtk_flow_ht_params); + } diff --git a/target/linux/generic/backport-6.1/716-v6.0-net-ethernet-mtk_eth_soc-move-wdma_base-definitions-.patch b/target/linux/generic/backport-6.1/716-v6.0-net-ethernet-mtk_eth_soc-move-wdma_base-definitions-.patch new file mode 100644 index 0000000000..e984f4b68b --- /dev/null +++ b/target/linux/generic/backport-6.1/716-v6.0-net-ethernet-mtk_eth_soc-move-wdma_base-definitions-.patch @@ -0,0 +1,80 @@ +From 0dcbe607cec32ccae23b02a641b8bd6191a328ae Mon Sep 17 00:00:00 2001 +Message-Id: <0dcbe607cec32ccae23b02a641b8bd6191a328ae.1662243796.git.lorenzo@kernel.org> +In-Reply-To: <43a21841ce0175d29f23c34a65ceaaf9dd7eb8b7.1662243796.git.lorenzo@kernel.org> +References: <43a21841ce0175d29f23c34a65ceaaf9dd7eb8b7.1662243796.git.lorenzo@kernel.org> +From: Lorenzo Bianconi +Date: Tue, 23 Aug 2022 23:09:05 +0200 +Subject: [PATCH net-next 2/4] net: ethernet: mtk_eth_soc: move wdma_base + definitions in mtk register map + +This is a preliminary patch to introduce mt7986 wed support. + +Signed-off-by: Lorenzo Bianconi +--- + drivers/net/ethernet/mediatek/mtk_eth_soc.c | 16 ++++++++++------ + drivers/net/ethernet/mediatek/mtk_eth_soc.h | 4 +--- + 2 files changed, 11 insertions(+), 9 deletions(-) + +--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c ++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c +@@ -75,6 +75,10 @@ static const struct mtk_reg_map mtk_reg_ + .gdm1_cnt = 0x2400, + .gdma_to_ppe0 = 0x4444, + .ppe_base = 0x0c00, ++ .wdma_base = { ++ [0] = 0x2800, ++ [1] = 0x2c00, ++ }, + }; + + static const struct mtk_reg_map mt7628_reg_map = { +@@ -130,6 +134,10 @@ static const struct mtk_reg_map mt7986_r + .gdm1_cnt = 0x1c00, + .gdma_to_ppe0 = 0x3333, + .ppe_base = 0x2000, ++ .wdma_base = { ++ [0] = 0x4800, ++ [1] = 0x4c00, ++ }, + }; + + /* strings used by ethtool */ +@@ -3967,16 +3975,12 @@ static int mtk_probe(struct platform_dev + for (i = 0;; i++) { + struct device_node *np = of_parse_phandle(pdev->dev.of_node, + "mediatek,wed", i); +- static const u32 wdma_regs[] = { +- MTK_WDMA0_BASE, +- MTK_WDMA1_BASE +- }; + void __iomem *wdma; + +- if (!np || i >= ARRAY_SIZE(wdma_regs)) ++ if (!np || i >= ARRAY_SIZE(eth->soc->reg_map->wdma_base)) + break; + +- wdma = eth->base + wdma_regs[i]; ++ wdma = eth->base + eth->soc->reg_map->wdma_base[i]; + mtk_wed_add_hw(np, eth, wdma, i); + } + +--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h ++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h +@@ -268,9 +268,6 @@ + #define TX_DMA_FPORT_MASK_V2 0xf + #define TX_DMA_SWC_V2 BIT(30) + +-#define MTK_WDMA0_BASE 0x2800 +-#define MTK_WDMA1_BASE 0x2c00 +- + /* QDMA descriptor txd4 */ + #define TX_DMA_CHKSUM (0x7 << 29) + #define TX_DMA_TSO BIT(28) +@@ -953,6 +950,7 @@ struct mtk_reg_map { + u32 gdm1_cnt; + u32 gdma_to_ppe0; + u32 ppe_base; ++ u32 wdma_base[2]; + }; + + /* struct mtk_eth_data - This is the structure holding all differences diff --git a/target/linux/generic/backport-6.1/717-v6.0-net-ethernet-mtk_eth_soc-add-foe_entry_size-to-mtk_e.patch b/target/linux/generic/backport-6.1/717-v6.0-net-ethernet-mtk_eth_soc-add-foe_entry_size-to-mtk_e.patch new file mode 100644 index 0000000000..c24bd8022c --- /dev/null +++ b/target/linux/generic/backport-6.1/717-v6.0-net-ethernet-mtk_eth_soc-add-foe_entry_size-to-mtk_e.patch @@ -0,0 +1,251 @@ +From e3c27d869fccc1f2b8d0b4cde4763ab223874e8c Mon Sep 17 00:00:00 2001 +Message-Id: +In-Reply-To: <43a21841ce0175d29f23c34a65ceaaf9dd7eb8b7.1662243796.git.lorenzo@kernel.org> +References: <43a21841ce0175d29f23c34a65ceaaf9dd7eb8b7.1662243796.git.lorenzo@kernel.org> +From: Lorenzo Bianconi +Date: Sun, 21 Aug 2022 17:51:17 +0200 +Subject: [PATCH net-next 3/4] net: ethernet: mtk_eth_soc: add foe_entry_size + to mtk_eth_soc + +Introduce foe_entry_size to mtk_eth_soc data structure since mt7986 +relies on a bigger mtk_foe_entry data structure. + +Signed-off-by: Lorenzo Bianconi +--- + drivers/net/ethernet/mediatek/mtk_eth_soc.c | 3 + + drivers/net/ethernet/mediatek/mtk_eth_soc.h | 10 ++++ + drivers/net/ethernet/mediatek/mtk_ppe.c | 55 +++++++++++-------- + drivers/net/ethernet/mediatek/mtk_ppe.h | 2 +- + .../net/ethernet/mediatek/mtk_ppe_debugfs.c | 2 +- + 5 files changed, 48 insertions(+), 24 deletions(-) + +--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c ++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c +@@ -4165,6 +4165,7 @@ static const struct mtk_soc_data mt7621_ + .required_pctl = false, + .offload_version = 2, + .hash_offset = 2, ++ .foe_entry_size = sizeof(struct mtk_foe_entry), + .txrx = { + .txd_size = sizeof(struct mtk_tx_dma), + .rxd_size = sizeof(struct mtk_rx_dma), +@@ -4184,6 +4185,7 @@ static const struct mtk_soc_data mt7622_ + .required_pctl = false, + .offload_version = 2, + .hash_offset = 2, ++ .foe_entry_size = sizeof(struct mtk_foe_entry), + .txrx = { + .txd_size = sizeof(struct mtk_tx_dma), + .rxd_size = sizeof(struct mtk_rx_dma), +@@ -4202,6 +4204,7 @@ static const struct mtk_soc_data mt7623_ + .required_pctl = true, + .offload_version = 2, + .hash_offset = 2, ++ .foe_entry_size = sizeof(struct mtk_foe_entry), + .txrx = { + .txd_size = sizeof(struct mtk_tx_dma), + .rxd_size = sizeof(struct mtk_rx_dma), +--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h ++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h +@@ -965,6 +965,7 @@ struct mtk_reg_map { + * @required_pctl A bool value to show whether the SoC requires + * the extra setup for those pins used by GMAC. + * @hash_offset Flow table hash offset. ++ * @foe_entry_size Foe table entry size. + * @txd_size Tx DMA descriptor size. + * @rxd_size Rx DMA descriptor size. + * @rx_irq_done_mask Rx irq done register mask. +@@ -980,6 +981,7 @@ struct mtk_soc_data { + bool required_pctl; + u8 offload_version; + u8 hash_offset; ++ u16 foe_entry_size; + netdev_features_t hw_features; + struct { + u32 txd_size; +@@ -1140,6 +1142,14 @@ struct mtk_mac { + /* the struct describing the SoC. these are declared in the soc_xyz.c files */ + extern const struct of_device_id of_mtk_match[]; + ++static inline struct mtk_foe_entry * ++mtk_foe_get_entry(struct mtk_ppe *ppe, u16 hash) ++{ ++ const struct mtk_soc_data *soc = ppe->eth->soc; ++ ++ return ppe->foe_table + hash * soc->foe_entry_size; ++} ++ + /* read the hardware status register */ + void mtk_stats_update_mac(struct mtk_mac *mac); + +--- a/drivers/net/ethernet/mediatek/mtk_ppe.c ++++ b/drivers/net/ethernet/mediatek/mtk_ppe.c +@@ -410,9 +410,10 @@ __mtk_foe_entry_clear(struct mtk_ppe *pp + + hlist_del_init(&entry->list); + if (entry->hash != 0xffff) { +- ppe->foe_table[entry->hash].ib1 &= ~MTK_FOE_IB1_STATE; +- ppe->foe_table[entry->hash].ib1 |= FIELD_PREP(MTK_FOE_IB1_STATE, +- MTK_FOE_STATE_BIND); ++ struct mtk_foe_entry *hwe = mtk_foe_get_entry(ppe, entry->hash); ++ ++ hwe->ib1 &= ~MTK_FOE_IB1_STATE; ++ hwe->ib1 |= FIELD_PREP(MTK_FOE_IB1_STATE, MTK_FOE_STATE_BIND); + dma_wmb(); + } + entry->hash = 0xffff; +@@ -451,7 +452,7 @@ mtk_flow_entry_update_l2(struct mtk_ppe + int cur_idle; + u32 ib1; + +- hwe = &ppe->foe_table[cur->hash]; ++ hwe = mtk_foe_get_entry(ppe, cur->hash); + ib1 = READ_ONCE(hwe->ib1); + + if (FIELD_GET(MTK_FOE_IB1_STATE, ib1) != MTK_FOE_STATE_BIND) { +@@ -473,8 +474,8 @@ mtk_flow_entry_update_l2(struct mtk_ppe + static void + mtk_flow_entry_update(struct mtk_ppe *ppe, struct mtk_flow_entry *entry) + { ++ struct mtk_foe_entry foe = {}; + struct mtk_foe_entry *hwe; +- struct mtk_foe_entry foe; + + spin_lock_bh(&ppe_lock); + +@@ -486,8 +487,8 @@ mtk_flow_entry_update(struct mtk_ppe *pp + if (entry->hash == 0xffff) + goto out; + +- hwe = &ppe->foe_table[entry->hash]; +- memcpy(&foe, hwe, sizeof(foe)); ++ hwe = mtk_foe_get_entry(ppe, entry->hash); ++ memcpy(&foe, hwe, ppe->eth->soc->foe_entry_size); + if (!mtk_flow_entry_match(entry, &foe)) { + entry->hash = 0xffff; + goto out; +@@ -511,8 +512,8 @@ __mtk_foe_entry_commit(struct mtk_ppe *p + entry->ib1 &= ~MTK_FOE_IB1_BIND_TIMESTAMP; + entry->ib1 |= FIELD_PREP(MTK_FOE_IB1_BIND_TIMESTAMP, timestamp); + +- hwe = &ppe->foe_table[hash]; +- memcpy(&hwe->data, &entry->data, sizeof(hwe->data)); ++ hwe = mtk_foe_get_entry(ppe, hash); ++ memcpy(&hwe->data, &entry->data, ppe->eth->soc->foe_entry_size); + wmb(); + hwe->ib1 = entry->ib1; + +@@ -561,7 +562,7 @@ mtk_foe_entry_commit_subflow(struct mtk_ + { + const struct mtk_soc_data *soc = ppe->eth->soc; + struct mtk_flow_entry *flow_info; +- struct mtk_foe_entry foe, *hwe; ++ struct mtk_foe_entry foe = {}, *hwe; + struct mtk_foe_mac_info *l2; + u32 ib1_mask = MTK_FOE_IB1_PACKET_TYPE | MTK_FOE_IB1_UDP; + int type; +@@ -578,8 +579,8 @@ mtk_foe_entry_commit_subflow(struct mtk_ + &ppe->foe_flow[hash / soc->hash_offset]); + hlist_add_head(&flow_info->l2_data.list, &entry->l2_flows); + +- hwe = &ppe->foe_table[hash]; +- memcpy(&foe, hwe, sizeof(foe)); ++ hwe = mtk_foe_get_entry(ppe, hash); ++ memcpy(&foe, hwe, soc->foe_entry_size); + foe.ib1 &= ib1_mask; + foe.ib1 |= entry->data.ib1 & ~ib1_mask; + +@@ -601,7 +602,7 @@ void __mtk_ppe_check_skb(struct mtk_ppe + { + const struct mtk_soc_data *soc = ppe->eth->soc; + struct hlist_head *head = &ppe->foe_flow[hash / soc->hash_offset]; +- struct mtk_foe_entry *hwe = &ppe->foe_table[hash]; ++ struct mtk_foe_entry *hwe = mtk_foe_get_entry(ppe, hash); + struct mtk_flow_entry *entry; + struct mtk_foe_bridge key = {}; + struct hlist_node *n; +@@ -686,9 +687,9 @@ struct mtk_ppe *mtk_ppe_init(struct mtk_ + { + const struct mtk_soc_data *soc = eth->soc; + struct device *dev = eth->dev; +- struct mtk_foe_entry *foe; + struct mtk_ppe *ppe; + u32 foe_flow_size; ++ void *foe; + + ppe = devm_kzalloc(dev, sizeof(*ppe), GFP_KERNEL); + if (!ppe) +@@ -704,7 +705,8 @@ struct mtk_ppe *mtk_ppe_init(struct mtk_ + ppe->dev = dev; + ppe->version = version; + +- foe = dmam_alloc_coherent(ppe->dev, MTK_PPE_ENTRIES * sizeof(*foe), ++ foe = dmam_alloc_coherent(ppe->dev, ++ MTK_PPE_ENTRIES * soc->foe_entry_size, + &ppe->foe_phys, GFP_KERNEL); + if (!foe) + return NULL; +@@ -727,15 +729,21 @@ static void mtk_ppe_init_foe_table(struc + static const u8 skip[] = { 12, 25, 38, 51, 76, 89, 102 }; + int i, k; + +- memset(ppe->foe_table, 0, MTK_PPE_ENTRIES * sizeof(*ppe->foe_table)); ++ memset(ppe->foe_table, 0, ++ MTK_PPE_ENTRIES * ppe->eth->soc->foe_entry_size); + + if (!IS_ENABLED(CONFIG_SOC_MT7621)) + return; + + /* skip all entries that cross the 1024 byte boundary */ +- for (i = 0; i < MTK_PPE_ENTRIES; i += 128) +- for (k = 0; k < ARRAY_SIZE(skip); k++) +- ppe->foe_table[i + skip[k]].ib1 |= MTK_FOE_IB1_STATIC; ++ for (i = 0; i < MTK_PPE_ENTRIES; i += 128) { ++ for (k = 0; k < ARRAY_SIZE(skip); k++) { ++ struct mtk_foe_entry *hwe; ++ ++ hwe = mtk_foe_get_entry(ppe, i + skip[k]); ++ hwe->ib1 |= MTK_FOE_IB1_STATIC; ++ } ++ } + } + + void mtk_ppe_start(struct mtk_ppe *ppe) +@@ -822,9 +830,12 @@ int mtk_ppe_stop(struct mtk_ppe *ppe) + if (!ppe) + return 0; + +- for (i = 0; i < MTK_PPE_ENTRIES; i++) +- ppe->foe_table[i].ib1 = FIELD_PREP(MTK_FOE_IB1_STATE, +- MTK_FOE_STATE_INVALID); ++ for (i = 0; i < MTK_PPE_ENTRIES; i++) { ++ struct mtk_foe_entry *hwe = mtk_foe_get_entry(ppe, i); ++ ++ hwe->ib1 = FIELD_PREP(MTK_FOE_IB1_STATE, ++ MTK_FOE_STATE_INVALID); ++ } + + mtk_ppe_cache_enable(ppe, false); + +--- a/drivers/net/ethernet/mediatek/mtk_ppe.h ++++ b/drivers/net/ethernet/mediatek/mtk_ppe.h +@@ -268,7 +268,7 @@ struct mtk_ppe { + int version; + char dirname[5]; + +- struct mtk_foe_entry *foe_table; ++ void *foe_table; + dma_addr_t foe_phys; + + u16 foe_check_time[MTK_PPE_ENTRIES]; +--- a/drivers/net/ethernet/mediatek/mtk_ppe_debugfs.c ++++ b/drivers/net/ethernet/mediatek/mtk_ppe_debugfs.c +@@ -79,7 +79,7 @@ mtk_ppe_debugfs_foe_show(struct seq_file + int i; + + for (i = 0; i < MTK_PPE_ENTRIES; i++) { +- struct mtk_foe_entry *entry = &ppe->foe_table[i]; ++ struct mtk_foe_entry *entry = mtk_foe_get_entry(ppe, i); + struct mtk_foe_mac_info *l2; + struct mtk_flow_addr_info ai = {}; + unsigned char h_source[ETH_ALEN]; diff --git a/target/linux/generic/backport-6.1/718-v6.0-net-ethernet-mtk_eth_soc-fix-typo-in-__mtk_foe_entry.patch b/target/linux/generic/backport-6.1/718-v6.0-net-ethernet-mtk_eth_soc-fix-typo-in-__mtk_foe_entry.patch new file mode 100644 index 0000000000..77daf6c8bf --- /dev/null +++ b/target/linux/generic/backport-6.1/718-v6.0-net-ethernet-mtk_eth_soc-fix-typo-in-__mtk_foe_entry.patch @@ -0,0 +1,27 @@ +From 12ff69304c83c679ca01ef3db963ab0db9de19fb Mon Sep 17 00:00:00 2001 +Message-Id: <12ff69304c83c679ca01ef3db963ab0db9de19fb.1662332102.git.lorenzo@kernel.org> +In-Reply-To: <2a60545635c2705312299384f4e9fec2f2a3acd6.1662332102.git.lorenzo@kernel.org> +References: <2a60545635c2705312299384f4e9fec2f2a3acd6.1662332102.git.lorenzo@kernel.org> +From: Lorenzo Bianconi +Date: Mon, 5 Sep 2022 00:43:43 +0200 +Subject: [PATCH net-next 2/6] net: ethernet: mtk_eth_soc: fix typo in + __mtk_foe_entry_clear + +Set ib1 state to MTK_FOE_STATE_UNBIND in __mtk_foe_entry_clear routine. + +Signed-off-by: Lorenzo Bianconi +--- + drivers/net/ethernet/mediatek/mtk_ppe.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/drivers/net/ethernet/mediatek/mtk_ppe.c ++++ b/drivers/net/ethernet/mediatek/mtk_ppe.c +@@ -413,7 +413,7 @@ __mtk_foe_entry_clear(struct mtk_ppe *pp + struct mtk_foe_entry *hwe = mtk_foe_get_entry(ppe, entry->hash); + + hwe->ib1 &= ~MTK_FOE_IB1_STATE; +- hwe->ib1 |= FIELD_PREP(MTK_FOE_IB1_STATE, MTK_FOE_STATE_BIND); ++ hwe->ib1 |= FIELD_PREP(MTK_FOE_IB1_STATE, MTK_FOE_STATE_UNBIND); + dma_wmb(); + } + entry->hash = 0xffff; diff --git a/target/linux/generic/backport-6.1/719-v6.0-net-ethernet-mtk_eth_soc-check-max-allowed-value-in-.patch b/target/linux/generic/backport-6.1/719-v6.0-net-ethernet-mtk_eth_soc-check-max-allowed-value-in-.patch new file mode 100644 index 0000000000..7ab6d486b2 --- /dev/null +++ b/target/linux/generic/backport-6.1/719-v6.0-net-ethernet-mtk_eth_soc-check-max-allowed-value-in-.patch @@ -0,0 +1,28 @@ +From 4253e6e2b795a18ab534adcd5c313d3fc4150975 Mon Sep 17 00:00:00 2001 +Message-Id: <4253e6e2b795a18ab534adcd5c313d3fc4150975.1662332102.git.lorenzo@kernel.org> +In-Reply-To: <2a60545635c2705312299384f4e9fec2f2a3acd6.1662332102.git.lorenzo@kernel.org> +References: <2a60545635c2705312299384f4e9fec2f2a3acd6.1662332102.git.lorenzo@kernel.org> +From: Lorenzo Bianconi +Date: Mon, 5 Sep 2022 00:48:52 +0200 +Subject: [PATCH net-next 3/6] net: ethernet: mtk_eth_soc: check max allowed + value in mtk_ppe_check_skb + +Check theoretical OOB accesses in mtk_ppe_check_skb routine + +Signed-off-by: Lorenzo Bianconi +--- + drivers/net/ethernet/mediatek/mtk_ppe.h | 3 +++ + 1 file changed, 3 insertions(+) + +--- a/drivers/net/ethernet/mediatek/mtk_ppe.h ++++ b/drivers/net/ethernet/mediatek/mtk_ppe.h +@@ -294,6 +294,9 @@ mtk_ppe_check_skb(struct mtk_ppe *ppe, s + if (!ppe) + return; + ++ if (hash > MTK_PPE_HASH_MASK) ++ return; ++ + now = (u16)jiffies; + diff = now - ppe->foe_check_time[hash]; + if (diff < HZ / 10) diff --git a/target/linux/generic/backport-6.1/720-v6.0-net-ethernet-mtk_eth_wed-add-mtk_wed_configure_irq-a.patch b/target/linux/generic/backport-6.1/720-v6.0-net-ethernet-mtk_eth_wed-add-mtk_wed_configure_irq-a.patch new file mode 100644 index 0000000000..a8c88daf1f --- /dev/null +++ b/target/linux/generic/backport-6.1/720-v6.0-net-ethernet-mtk_eth_wed-add-mtk_wed_configure_irq-a.patch @@ -0,0 +1,189 @@ +From e5ecb4f619197b93fa682d722452dc8412864cdb Mon Sep 17 00:00:00 2001 +Message-Id: +From: Lorenzo Bianconi +Date: Fri, 26 Aug 2022 01:12:57 +0200 +Subject: [PATCH net-next 1/5] net: ethernet: mtk_eth_wed: add + mtk_wed_configure_irq and mtk_wed_dma_{enable/disable} + +Introduce mtk_wed_configure_irq, mtk_wed_dma_enable and mtk_wed_dma_disable +utility routines. +This is a preliminary patch to introduce mt7986 wed support. + +Co-developed-by: Bo Jiao +Signed-off-by: Bo Jiao +Co-developed-by: Sujuan Chen +Signed-off-by: Sujuan Chen +Signed-off-by: Lorenzo Bianconi +--- + drivers/net/ethernet/mediatek/mtk_wed.c | 87 +++++++++++++------- + drivers/net/ethernet/mediatek/mtk_wed_regs.h | 6 +- + 2 files changed, 64 insertions(+), 29 deletions(-) + +--- a/drivers/net/ethernet/mediatek/mtk_wed.c ++++ b/drivers/net/ethernet/mediatek/mtk_wed.c +@@ -237,9 +237,30 @@ mtk_wed_set_ext_int(struct mtk_wed_devic + } + + static void +-mtk_wed_stop(struct mtk_wed_device *dev) ++mtk_wed_dma_disable(struct mtk_wed_device *dev) + { ++ wed_clr(dev, MTK_WED_WPDMA_GLO_CFG, ++ MTK_WED_WPDMA_GLO_CFG_TX_DRV_EN | ++ MTK_WED_WPDMA_GLO_CFG_RX_DRV_EN); ++ ++ wed_clr(dev, MTK_WED_WDMA_GLO_CFG, MTK_WED_WDMA_GLO_CFG_RX_DRV_EN); ++ ++ wed_clr(dev, MTK_WED_GLO_CFG, ++ MTK_WED_GLO_CFG_TX_DMA_EN | ++ MTK_WED_GLO_CFG_RX_DMA_EN); ++ + regmap_write(dev->hw->mirror, dev->hw->index * 4, 0); ++ wdma_m32(dev, MTK_WDMA_GLO_CFG, ++ MTK_WDMA_GLO_CFG_TX_DMA_EN | ++ MTK_WDMA_GLO_CFG_RX_INFO1_PRERES | ++ MTK_WDMA_GLO_CFG_RX_INFO2_PRERES | ++ MTK_WDMA_GLO_CFG_RX_INFO3_PRERES, 0); ++} ++ ++static void ++mtk_wed_stop(struct mtk_wed_device *dev) ++{ ++ mtk_wed_dma_disable(dev); + mtk_wed_set_ext_int(dev, false); + + wed_clr(dev, MTK_WED_CTRL, +@@ -252,15 +273,6 @@ mtk_wed_stop(struct mtk_wed_device *dev) + wdma_w32(dev, MTK_WDMA_INT_MASK, 0); + wdma_w32(dev, MTK_WDMA_INT_GRP2, 0); + wed_w32(dev, MTK_WED_WPDMA_INT_MASK, 0); +- +- wed_clr(dev, MTK_WED_GLO_CFG, +- MTK_WED_GLO_CFG_TX_DMA_EN | +- MTK_WED_GLO_CFG_RX_DMA_EN); +- wed_clr(dev, MTK_WED_WPDMA_GLO_CFG, +- MTK_WED_WPDMA_GLO_CFG_TX_DRV_EN | +- MTK_WED_WPDMA_GLO_CFG_RX_DRV_EN); +- wed_clr(dev, MTK_WED_WDMA_GLO_CFG, +- MTK_WED_WDMA_GLO_CFG_RX_DRV_EN); + } + + static void +@@ -313,7 +325,10 @@ mtk_wed_hw_init_early(struct mtk_wed_dev + MTK_WED_WDMA_GLO_CFG_IDLE_DMAD_SUPPLY; + wed_m32(dev, MTK_WED_WDMA_GLO_CFG, mask, set); + +- wdma_set(dev, MTK_WDMA_GLO_CFG, MTK_WDMA_GLO_CFG_RX_INFO_PRERES); ++ wdma_set(dev, MTK_WDMA_GLO_CFG, ++ MTK_WDMA_GLO_CFG_RX_INFO1_PRERES | ++ MTK_WDMA_GLO_CFG_RX_INFO2_PRERES | ++ MTK_WDMA_GLO_CFG_RX_INFO3_PRERES); + + offset = dev->hw->index ? 0x04000400 : 0; + wed_w32(dev, MTK_WED_WDMA_OFFSET0, 0x2a042a20 + offset); +@@ -520,43 +535,38 @@ mtk_wed_wdma_ring_setup(struct mtk_wed_d + } + + static void +-mtk_wed_start(struct mtk_wed_device *dev, u32 irq_mask) ++mtk_wed_configure_irq(struct mtk_wed_device *dev, u32 irq_mask) + { +- u32 wdma_mask; +- u32 val; +- int i; +- +- for (i = 0; i < ARRAY_SIZE(dev->tx_wdma); i++) +- if (!dev->tx_wdma[i].desc) +- mtk_wed_wdma_ring_setup(dev, i, 16); +- +- wdma_mask = FIELD_PREP(MTK_WDMA_INT_MASK_RX_DONE, GENMASK(1, 0)); +- +- mtk_wed_hw_init(dev); ++ u32 wdma_mask = FIELD_PREP(MTK_WDMA_INT_MASK_RX_DONE, GENMASK(1, 0)); + ++ /* wed control cr set */ + wed_set(dev, MTK_WED_CTRL, + MTK_WED_CTRL_WDMA_INT_AGENT_EN | + MTK_WED_CTRL_WPDMA_INT_AGENT_EN | + MTK_WED_CTRL_WED_TX_BM_EN | + MTK_WED_CTRL_WED_TX_FREE_AGENT_EN); + +- wed_w32(dev, MTK_WED_PCIE_INT_TRIGGER, MTK_WED_PCIE_INT_TRIGGER_STATUS); ++ wed_w32(dev, MTK_WED_PCIE_INT_TRIGGER, ++ MTK_WED_PCIE_INT_TRIGGER_STATUS); + + wed_w32(dev, MTK_WED_WPDMA_INT_TRIGGER, + MTK_WED_WPDMA_INT_TRIGGER_RX_DONE | + MTK_WED_WPDMA_INT_TRIGGER_TX_DONE); + +- wed_set(dev, MTK_WED_WPDMA_INT_CTRL, +- MTK_WED_WPDMA_INT_CTRL_SUBRT_ADV); +- ++ /* initail wdma interrupt agent */ + wed_w32(dev, MTK_WED_WDMA_INT_TRIGGER, wdma_mask); + wed_clr(dev, MTK_WED_WDMA_INT_CTRL, wdma_mask); + + wdma_w32(dev, MTK_WDMA_INT_MASK, wdma_mask); + wdma_w32(dev, MTK_WDMA_INT_GRP2, wdma_mask); +- + wed_w32(dev, MTK_WED_WPDMA_INT_MASK, irq_mask); + wed_w32(dev, MTK_WED_INT_MASK, irq_mask); ++} ++ ++static void ++mtk_wed_dma_enable(struct mtk_wed_device *dev) ++{ ++ wed_set(dev, MTK_WED_WPDMA_INT_CTRL, MTK_WED_WPDMA_INT_CTRL_SUBRT_ADV); + + wed_set(dev, MTK_WED_GLO_CFG, + MTK_WED_GLO_CFG_TX_DMA_EN | +@@ -567,6 +577,26 @@ mtk_wed_start(struct mtk_wed_device *dev + wed_set(dev, MTK_WED_WDMA_GLO_CFG, + MTK_WED_WDMA_GLO_CFG_RX_DRV_EN); + ++ wdma_set(dev, MTK_WDMA_GLO_CFG, ++ MTK_WDMA_GLO_CFG_TX_DMA_EN | ++ MTK_WDMA_GLO_CFG_RX_INFO1_PRERES | ++ MTK_WDMA_GLO_CFG_RX_INFO2_PRERES | ++ MTK_WDMA_GLO_CFG_RX_INFO3_PRERES); ++} ++ ++static void ++mtk_wed_start(struct mtk_wed_device *dev, u32 irq_mask) ++{ ++ u32 val; ++ int i; ++ ++ for (i = 0; i < ARRAY_SIZE(dev->tx_wdma); i++) ++ if (!dev->tx_wdma[i].desc) ++ mtk_wed_wdma_ring_setup(dev, i, 16); ++ ++ mtk_wed_hw_init(dev); ++ mtk_wed_configure_irq(dev, irq_mask); ++ + mtk_wed_set_ext_int(dev, true); + val = dev->wlan.wpdma_phys | + MTK_PCIE_MIRROR_MAP_EN | +@@ -577,6 +607,7 @@ mtk_wed_start(struct mtk_wed_device *dev + val |= BIT(0); + regmap_write(dev->hw->mirror, dev->hw->index * 4, val); + ++ mtk_wed_dma_enable(dev); + dev->running = true; + } + +--- a/drivers/net/ethernet/mediatek/mtk_wed_regs.h ++++ b/drivers/net/ethernet/mediatek/mtk_wed_regs.h +@@ -224,7 +224,11 @@ struct mtk_wdma_desc { + #define MTK_WDMA_RING_RX(_n) (0x100 + (_n) * 0x10) + + #define MTK_WDMA_GLO_CFG 0x204 +-#define MTK_WDMA_GLO_CFG_RX_INFO_PRERES GENMASK(28, 26) ++#define MTK_WDMA_GLO_CFG_TX_DMA_EN BIT(0) ++#define MTK_WDMA_GLO_CFG_RX_DMA_EN BIT(2) ++#define MTK_WDMA_GLO_CFG_RX_INFO3_PRERES BIT(26) ++#define MTK_WDMA_GLO_CFG_RX_INFO2_PRERES BIT(27) ++#define MTK_WDMA_GLO_CFG_RX_INFO1_PRERES BIT(28) + + #define MTK_WDMA_RESET_IDX 0x208 + #define MTK_WDMA_RESET_IDX_TX GENMASK(3, 0) diff --git a/target/linux/generic/backport-6.1/721-v6.0-net-ethernet-mtk_eth_wed-add-wed-support-for-mt7986-.patch b/target/linux/generic/backport-6.1/721-v6.0-net-ethernet-mtk_eth_wed-add-wed-support-for-mt7986-.patch new file mode 100644 index 0000000000..71bfcdbac8 --- /dev/null +++ b/target/linux/generic/backport-6.1/721-v6.0-net-ethernet-mtk_eth_wed-add-wed-support-for-mt7986-.patch @@ -0,0 +1,942 @@ +From 463a71af080fbc77339bee2037fb1e081e3824f7 Mon Sep 17 00:00:00 2001 +Message-Id: <463a71af080fbc77339bee2037fb1e081e3824f7.1662886034.git.lorenzo@kernel.org> +In-Reply-To: +References: +From: Lorenzo Bianconi +Date: Sat, 27 Aug 2022 16:15:14 +0200 +Subject: [PATCH net-next 2/5] net: ethernet: mtk_eth_wed: add wed support for + mt7986 chipset + +Introduce Wireless Etherne Dispatcher support on transmission side +for mt7986 chipset + +Co-developed-by: Bo Jiao +Signed-off-by: Bo Jiao +Co-developed-by: Sujuan Chen +Signed-off-by: Sujuan Chen +Signed-off-by: Lorenzo Bianconi +--- + drivers/net/ethernet/mediatek/mtk_eth_soc.c | 34 +- + drivers/net/ethernet/mediatek/mtk_wed.c | 371 ++++++++++++++---- + drivers/net/ethernet/mediatek/mtk_wed.h | 8 +- + .../net/ethernet/mediatek/mtk_wed_debugfs.c | 3 + + drivers/net/ethernet/mediatek/mtk_wed_regs.h | 81 +++- + include/linux/soc/mediatek/mtk_wed.h | 8 + + 6 files changed, 408 insertions(+), 97 deletions(-) + +--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c ++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c +@@ -3892,6 +3892,7 @@ void mtk_eth_set_dma_device(struct mtk_e + + static int mtk_probe(struct platform_device *pdev) + { ++ struct resource *res = NULL; + struct device_node *mac_np; + struct mtk_eth *eth; + int err, i; +@@ -3972,16 +3973,31 @@ static int mtk_probe(struct platform_dev + } + } + +- for (i = 0;; i++) { +- struct device_node *np = of_parse_phandle(pdev->dev.of_node, +- "mediatek,wed", i); +- void __iomem *wdma; +- +- if (!np || i >= ARRAY_SIZE(eth->soc->reg_map->wdma_base)) +- break; +- +- wdma = eth->base + eth->soc->reg_map->wdma_base[i]; +- mtk_wed_add_hw(np, eth, wdma, i); ++ if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V2)) { ++ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ if (!res) ++ return -EINVAL; ++ } ++ ++ if (eth->soc->offload_version) { ++ for (i = 0;; i++) { ++ struct device_node *np; ++ phys_addr_t wdma_phy; ++ u32 wdma_base; ++ ++ if (i >= ARRAY_SIZE(eth->soc->reg_map->wdma_base)) ++ break; ++ ++ np = of_parse_phandle(pdev->dev.of_node, ++ "mediatek,wed", i); ++ if (!np) ++ break; ++ ++ wdma_base = eth->soc->reg_map->wdma_base[i]; ++ wdma_phy = res ? res->start + wdma_base : 0; ++ mtk_wed_add_hw(np, eth, eth->base + wdma_base, ++ wdma_phy, i); ++ } + } + + for (i = 0; i < 3; i++) { +--- a/drivers/net/ethernet/mediatek/mtk_wed.c ++++ b/drivers/net/ethernet/mediatek/mtk_wed.c +@@ -25,6 +25,11 @@ + + #define MTK_WED_TX_RING_SIZE 2048 + #define MTK_WED_WDMA_RING_SIZE 1024 ++#define MTK_WED_MAX_GROUP_SIZE 0x100 ++#define MTK_WED_VLD_GROUP_SIZE 0x40 ++#define MTK_WED_PER_GROUP_PKT 128 ++ ++#define MTK_WED_FBUF_SIZE 128 + + static struct mtk_wed_hw *hw_list[2]; + static DEFINE_MUTEX(hw_lock); +@@ -150,10 +155,17 @@ mtk_wed_buffer_alloc(struct mtk_wed_devi + + desc->buf0 = cpu_to_le32(buf_phys); + desc->buf1 = cpu_to_le32(buf_phys + txd_size); +- ctrl = FIELD_PREP(MTK_WDMA_DESC_CTRL_LEN0, txd_size) | +- FIELD_PREP(MTK_WDMA_DESC_CTRL_LEN1, +- MTK_WED_BUF_SIZE - txd_size) | +- MTK_WDMA_DESC_CTRL_LAST_SEG1; ++ ++ if (dev->hw->version == 1) ++ ctrl = FIELD_PREP(MTK_WDMA_DESC_CTRL_LEN0, txd_size) | ++ FIELD_PREP(MTK_WDMA_DESC_CTRL_LEN1, ++ MTK_WED_BUF_SIZE - txd_size) | ++ MTK_WDMA_DESC_CTRL_LAST_SEG1; ++ else ++ ctrl = FIELD_PREP(MTK_WDMA_DESC_CTRL_LEN0, txd_size) | ++ FIELD_PREP(MTK_WDMA_DESC_CTRL_LEN1_V2, ++ MTK_WED_BUF_SIZE - txd_size) | ++ MTK_WDMA_DESC_CTRL_LAST_SEG0; + desc->ctrl = cpu_to_le32(ctrl); + desc->info = 0; + desc++; +@@ -209,7 +221,7 @@ mtk_wed_free_ring(struct mtk_wed_device + if (!ring->desc) + return; + +- dma_free_coherent(dev->hw->dev, ring->size * sizeof(*ring->desc), ++ dma_free_coherent(dev->hw->dev, ring->size * ring->desc_size, + ring->desc, ring->desc_phys); + } + +@@ -229,6 +241,14 @@ mtk_wed_set_ext_int(struct mtk_wed_devic + { + u32 mask = MTK_WED_EXT_INT_STATUS_ERROR_MASK; + ++ if (dev->hw->version == 1) ++ mask |= MTK_WED_EXT_INT_STATUS_TX_DRV_R_RESP_ERR; ++ else ++ mask |= MTK_WED_EXT_INT_STATUS_RX_FBUF_LO_TH | ++ MTK_WED_EXT_INT_STATUS_RX_FBUF_HI_TH | ++ MTK_WED_EXT_INT_STATUS_RX_DRV_COHERENT | ++ MTK_WED_EXT_INT_STATUS_TX_DMA_W_RESP_ERR; ++ + if (!dev->hw->num_flows) + mask &= ~MTK_WED_EXT_INT_STATUS_TKID_WO_PYLD; + +@@ -237,6 +257,20 @@ mtk_wed_set_ext_int(struct mtk_wed_devic + } + + static void ++mtk_wed_set_512_support(struct mtk_wed_device *dev, bool enable) ++{ ++ if (enable) { ++ wed_w32(dev, MTK_WED_TXDP_CTRL, MTK_WED_TXDP_DW9_OVERWR); ++ wed_w32(dev, MTK_WED_TXP_DW1, ++ FIELD_PREP(MTK_WED_WPDMA_WRITE_TXP, 0x0103)); ++ } else { ++ wed_w32(dev, MTK_WED_TXP_DW1, ++ FIELD_PREP(MTK_WED_WPDMA_WRITE_TXP, 0x0100)); ++ wed_clr(dev, MTK_WED_TXDP_CTRL, MTK_WED_TXDP_DW9_OVERWR); ++ } ++} ++ ++static void + mtk_wed_dma_disable(struct mtk_wed_device *dev) + { + wed_clr(dev, MTK_WED_WPDMA_GLO_CFG, +@@ -249,12 +283,22 @@ mtk_wed_dma_disable(struct mtk_wed_devic + MTK_WED_GLO_CFG_TX_DMA_EN | + MTK_WED_GLO_CFG_RX_DMA_EN); + +- regmap_write(dev->hw->mirror, dev->hw->index * 4, 0); + wdma_m32(dev, MTK_WDMA_GLO_CFG, + MTK_WDMA_GLO_CFG_TX_DMA_EN | + MTK_WDMA_GLO_CFG_RX_INFO1_PRERES | +- MTK_WDMA_GLO_CFG_RX_INFO2_PRERES | +- MTK_WDMA_GLO_CFG_RX_INFO3_PRERES, 0); ++ MTK_WDMA_GLO_CFG_RX_INFO2_PRERES, 0); ++ ++ if (dev->hw->version == 1) { ++ regmap_write(dev->hw->mirror, dev->hw->index * 4, 0); ++ wdma_m32(dev, MTK_WDMA_GLO_CFG, ++ MTK_WDMA_GLO_CFG_RX_INFO3_PRERES, 0); ++ } else { ++ wed_clr(dev, MTK_WED_WPDMA_GLO_CFG, ++ MTK_WED_WPDMA_GLO_CFG_RX_DRV_R0_PKT_PROC | ++ MTK_WED_WPDMA_GLO_CFG_RX_DRV_R0_CRX_SYNC); ++ ++ mtk_wed_set_512_support(dev, false); ++ } + } + + static void +@@ -293,7 +337,7 @@ mtk_wed_detach(struct mtk_wed_device *de + mtk_wed_free_buffer(dev); + mtk_wed_free_tx_rings(dev); + +- if (of_dma_is_coherent(wlan_node)) ++ if (of_dma_is_coherent(wlan_node) && hw->hifsys) + regmap_update_bits(hw->hifsys, HIFSYS_DMA_AG_MAP, + BIT(hw->index), BIT(hw->index)); + +@@ -308,14 +352,69 @@ mtk_wed_detach(struct mtk_wed_device *de + mutex_unlock(&hw_lock); + } + ++#define PCIE_BASE_ADDR0 0x11280000 ++static void ++mtk_wed_bus_init(struct mtk_wed_device *dev) ++{ ++ struct device_node *np = dev->hw->eth->dev->of_node; ++ struct regmap *regs; ++ u32 val; ++ ++ regs = syscon_regmap_lookup_by_phandle(np, "mediatek,wed-pcie"); ++ if (IS_ERR(regs)) ++ return; ++ ++ regmap_update_bits(regs, 0, BIT(0), BIT(0)); ++ ++ wed_w32(dev, MTK_WED_PCIE_INT_CTRL, ++ FIELD_PREP(MTK_WED_PCIE_INT_CTRL_POLL_EN, 2)); ++ ++ /* pcie interrupt control: pola/source selection */ ++ wed_set(dev, MTK_WED_PCIE_INT_CTRL, ++ MTK_WED_PCIE_INT_CTRL_MSK_EN_POLA | ++ FIELD_PREP(MTK_WED_PCIE_INT_CTRL_SRC_SEL, 1)); ++ wed_r32(dev, MTK_WED_PCIE_INT_CTRL); ++ ++ val = wed_r32(dev, MTK_WED_PCIE_CFG_INTM); ++ val = wed_r32(dev, MTK_WED_PCIE_CFG_BASE); ++ wed_w32(dev, MTK_WED_PCIE_CFG_INTM, PCIE_BASE_ADDR0 | 0x180); ++ wed_w32(dev, MTK_WED_PCIE_CFG_BASE, PCIE_BASE_ADDR0 | 0x184); ++ ++ val = wed_r32(dev, MTK_WED_PCIE_CFG_INTM); ++ val = wed_r32(dev, MTK_WED_PCIE_CFG_BASE); ++ ++ /* pcie interrupt status trigger register */ ++ wed_w32(dev, MTK_WED_PCIE_INT_TRIGGER, BIT(24)); ++ wed_r32(dev, MTK_WED_PCIE_INT_TRIGGER); ++ ++ /* pola setting */ ++ val = wed_r32(dev, MTK_WED_PCIE_INT_CTRL); ++ wed_set(dev, MTK_WED_PCIE_INT_CTRL, MTK_WED_PCIE_INT_CTRL_MSK_EN_POLA); ++} ++ ++static void ++mtk_wed_set_wpdma(struct mtk_wed_device *dev) ++{ ++ if (dev->hw->version == 1) { ++ wed_w32(dev, MTK_WED_WPDMA_CFG_BASE, dev->wlan.wpdma_phys); ++ } else { ++ mtk_wed_bus_init(dev); ++ ++ wed_w32(dev, MTK_WED_WPDMA_CFG_BASE, dev->wlan.wpdma_int); ++ wed_w32(dev, MTK_WED_WPDMA_CFG_INT_MASK, dev->wlan.wpdma_mask); ++ wed_w32(dev, MTK_WED_WPDMA_CFG_TX, dev->wlan.wpdma_tx); ++ wed_w32(dev, MTK_WED_WPDMA_CFG_TX_FREE, dev->wlan.wpdma_txfree); ++ } ++} ++ + static void + mtk_wed_hw_init_early(struct mtk_wed_device *dev) + { + u32 mask, set; +- u32 offset; + + mtk_wed_stop(dev); + mtk_wed_reset(dev, MTK_WED_RESET_WED); ++ mtk_wed_set_wpdma(dev); + + mask = MTK_WED_WDMA_GLO_CFG_BT_SIZE | + MTK_WED_WDMA_GLO_CFG_DYNAMIC_DMAD_RECYCLE | +@@ -325,17 +424,33 @@ mtk_wed_hw_init_early(struct mtk_wed_dev + MTK_WED_WDMA_GLO_CFG_IDLE_DMAD_SUPPLY; + wed_m32(dev, MTK_WED_WDMA_GLO_CFG, mask, set); + +- wdma_set(dev, MTK_WDMA_GLO_CFG, +- MTK_WDMA_GLO_CFG_RX_INFO1_PRERES | +- MTK_WDMA_GLO_CFG_RX_INFO2_PRERES | +- MTK_WDMA_GLO_CFG_RX_INFO3_PRERES); +- +- offset = dev->hw->index ? 0x04000400 : 0; +- wed_w32(dev, MTK_WED_WDMA_OFFSET0, 0x2a042a20 + offset); +- wed_w32(dev, MTK_WED_WDMA_OFFSET1, 0x29002800 + offset); ++ if (dev->hw->version == 1) { ++ u32 offset = dev->hw->index ? 0x04000400 : 0; + +- wed_w32(dev, MTK_WED_PCIE_CFG_BASE, MTK_PCIE_BASE(dev->hw->index)); +- wed_w32(dev, MTK_WED_WPDMA_CFG_BASE, dev->wlan.wpdma_phys); ++ wdma_set(dev, MTK_WDMA_GLO_CFG, ++ MTK_WDMA_GLO_CFG_RX_INFO1_PRERES | ++ MTK_WDMA_GLO_CFG_RX_INFO2_PRERES | ++ MTK_WDMA_GLO_CFG_RX_INFO3_PRERES); ++ ++ wed_w32(dev, MTK_WED_WDMA_OFFSET0, 0x2a042a20 + offset); ++ wed_w32(dev, MTK_WED_WDMA_OFFSET1, 0x29002800 + offset); ++ wed_w32(dev, MTK_WED_PCIE_CFG_BASE, ++ MTK_PCIE_BASE(dev->hw->index)); ++ } else { ++ wed_w32(dev, MTK_WED_WDMA_CFG_BASE, dev->hw->wdma_phy); ++ wed_set(dev, MTK_WED_CTRL, MTK_WED_CTRL_ETH_DMAD_FMT); ++ wed_w32(dev, MTK_WED_WDMA_OFFSET0, ++ FIELD_PREP(MTK_WED_WDMA_OFST0_GLO_INTS, ++ MTK_WDMA_INT_STATUS) | ++ FIELD_PREP(MTK_WED_WDMA_OFST0_GLO_CFG, ++ MTK_WDMA_GLO_CFG)); ++ ++ wed_w32(dev, MTK_WED_WDMA_OFFSET1, ++ FIELD_PREP(MTK_WED_WDMA_OFST1_TX_CTRL, ++ MTK_WDMA_RING_TX(0)) | ++ FIELD_PREP(MTK_WED_WDMA_OFST1_RX_CTRL, ++ MTK_WDMA_RING_RX(0))); ++ } + } + + static void +@@ -355,37 +470,65 @@ mtk_wed_hw_init(struct mtk_wed_device *d + + wed_w32(dev, MTK_WED_TX_BM_BASE, dev->buf_ring.desc_phys); + +- wed_w32(dev, MTK_WED_TX_BM_TKID, +- FIELD_PREP(MTK_WED_TX_BM_TKID_START, +- dev->wlan.token_start) | +- FIELD_PREP(MTK_WED_TX_BM_TKID_END, +- dev->wlan.token_start + dev->wlan.nbuf - 1)); +- + wed_w32(dev, MTK_WED_TX_BM_BUF_LEN, MTK_WED_PKT_SIZE); + +- wed_w32(dev, MTK_WED_TX_BM_DYN_THR, +- FIELD_PREP(MTK_WED_TX_BM_DYN_THR_LO, 1) | +- MTK_WED_TX_BM_DYN_THR_HI); ++ if (dev->hw->version == 1) { ++ wed_w32(dev, MTK_WED_TX_BM_TKID, ++ FIELD_PREP(MTK_WED_TX_BM_TKID_START, ++ dev->wlan.token_start) | ++ FIELD_PREP(MTK_WED_TX_BM_TKID_END, ++ dev->wlan.token_start + ++ dev->wlan.nbuf - 1)); ++ wed_w32(dev, MTK_WED_TX_BM_DYN_THR, ++ FIELD_PREP(MTK_WED_TX_BM_DYN_THR_LO, 1) | ++ MTK_WED_TX_BM_DYN_THR_HI); ++ } else { ++ wed_w32(dev, MTK_WED_TX_BM_TKID_V2, ++ FIELD_PREP(MTK_WED_TX_BM_TKID_START, ++ dev->wlan.token_start) | ++ FIELD_PREP(MTK_WED_TX_BM_TKID_END, ++ dev->wlan.token_start + ++ dev->wlan.nbuf - 1)); ++ wed_w32(dev, MTK_WED_TX_BM_DYN_THR, ++ FIELD_PREP(MTK_WED_TX_BM_DYN_THR_LO_V2, 0) | ++ MTK_WED_TX_BM_DYN_THR_HI_V2); ++ wed_w32(dev, MTK_WED_TX_TKID_CTRL, ++ MTK_WED_TX_TKID_CTRL_PAUSE | ++ FIELD_PREP(MTK_WED_TX_TKID_CTRL_VLD_GRP_NUM, ++ dev->buf_ring.size / 128) | ++ FIELD_PREP(MTK_WED_TX_TKID_CTRL_RSV_GRP_NUM, ++ dev->buf_ring.size / 128)); ++ wed_w32(dev, MTK_WED_TX_TKID_DYN_THR, ++ FIELD_PREP(MTK_WED_TX_TKID_DYN_THR_LO, 0) | ++ MTK_WED_TX_TKID_DYN_THR_HI); ++ } + + mtk_wed_reset(dev, MTK_WED_RESET_TX_BM); + +- wed_set(dev, MTK_WED_CTRL, +- MTK_WED_CTRL_WED_TX_BM_EN | +- MTK_WED_CTRL_WED_TX_FREE_AGENT_EN); ++ if (dev->hw->version == 1) ++ wed_set(dev, MTK_WED_CTRL, ++ MTK_WED_CTRL_WED_TX_BM_EN | ++ MTK_WED_CTRL_WED_TX_FREE_AGENT_EN); ++ else ++ wed_clr(dev, MTK_WED_TX_TKID_CTRL, MTK_WED_TX_TKID_CTRL_PAUSE); + + wed_clr(dev, MTK_WED_TX_BM_CTRL, MTK_WED_TX_BM_CTRL_PAUSE); + } + + static void +-mtk_wed_ring_reset(struct mtk_wdma_desc *desc, int size) ++mtk_wed_ring_reset(struct mtk_wed_ring *ring, int size) + { ++ void *head = (void *)ring->desc; + int i; + + for (i = 0; i < size; i++) { +- desc[i].buf0 = 0; +- desc[i].ctrl = cpu_to_le32(MTK_WDMA_DESC_CTRL_DMA_DONE); +- desc[i].buf1 = 0; +- desc[i].info = 0; ++ struct mtk_wdma_desc *desc; ++ ++ desc = (struct mtk_wdma_desc *)(head + i * ring->desc_size); ++ desc->buf0 = 0; ++ desc->ctrl = cpu_to_le32(MTK_WDMA_DESC_CTRL_DMA_DONE); ++ desc->buf1 = 0; ++ desc->info = 0; + } + } + +@@ -436,12 +579,10 @@ mtk_wed_reset_dma(struct mtk_wed_device + int i; + + for (i = 0; i < ARRAY_SIZE(dev->tx_ring); i++) { +- struct mtk_wdma_desc *desc = dev->tx_ring[i].desc; +- +- if (!desc) ++ if (!dev->tx_ring[i].desc) + continue; + +- mtk_wed_ring_reset(desc, MTK_WED_TX_RING_SIZE); ++ mtk_wed_ring_reset(&dev->tx_ring[i], MTK_WED_TX_RING_SIZE); + } + + if (mtk_wed_poll_busy(dev)) +@@ -498,16 +639,16 @@ mtk_wed_reset_dma(struct mtk_wed_device + + static int + mtk_wed_ring_alloc(struct mtk_wed_device *dev, struct mtk_wed_ring *ring, +- int size) ++ int size, u32 desc_size) + { +- ring->desc = dma_alloc_coherent(dev->hw->dev, +- size * sizeof(*ring->desc), ++ ring->desc = dma_alloc_coherent(dev->hw->dev, size * desc_size, + &ring->desc_phys, GFP_KERNEL); + if (!ring->desc) + return -ENOMEM; + ++ ring->desc_size = desc_size; + ring->size = size; +- mtk_wed_ring_reset(ring->desc, size); ++ mtk_wed_ring_reset(ring, size); + + return 0; + } +@@ -515,9 +656,10 @@ mtk_wed_ring_alloc(struct mtk_wed_device + static int + mtk_wed_wdma_ring_setup(struct mtk_wed_device *dev, int idx, int size) + { ++ u32 desc_size = sizeof(struct mtk_wdma_desc) * dev->hw->version; + struct mtk_wed_ring *wdma = &dev->tx_wdma[idx]; + +- if (mtk_wed_ring_alloc(dev, wdma, MTK_WED_WDMA_RING_SIZE)) ++ if (mtk_wed_ring_alloc(dev, wdma, MTK_WED_WDMA_RING_SIZE, desc_size)) + return -ENOMEM; + + wdma_w32(dev, MTK_WDMA_RING_RX(idx) + MTK_WED_RING_OFS_BASE, +@@ -546,16 +688,41 @@ mtk_wed_configure_irq(struct mtk_wed_dev + MTK_WED_CTRL_WED_TX_BM_EN | + MTK_WED_CTRL_WED_TX_FREE_AGENT_EN); + +- wed_w32(dev, MTK_WED_PCIE_INT_TRIGGER, +- MTK_WED_PCIE_INT_TRIGGER_STATUS); ++ if (dev->hw->version == 1) { ++ wed_w32(dev, MTK_WED_PCIE_INT_TRIGGER, ++ MTK_WED_PCIE_INT_TRIGGER_STATUS); ++ ++ wed_w32(dev, MTK_WED_WPDMA_INT_TRIGGER, ++ MTK_WED_WPDMA_INT_TRIGGER_RX_DONE | ++ MTK_WED_WPDMA_INT_TRIGGER_TX_DONE); + +- wed_w32(dev, MTK_WED_WPDMA_INT_TRIGGER, +- MTK_WED_WPDMA_INT_TRIGGER_RX_DONE | +- MTK_WED_WPDMA_INT_TRIGGER_TX_DONE); ++ wed_clr(dev, MTK_WED_WDMA_INT_CTRL, wdma_mask); ++ } else { ++ /* initail tx interrupt trigger */ ++ wed_w32(dev, MTK_WED_WPDMA_INT_CTRL_TX, ++ MTK_WED_WPDMA_INT_CTRL_TX0_DONE_EN | ++ MTK_WED_WPDMA_INT_CTRL_TX0_DONE_CLR | ++ MTK_WED_WPDMA_INT_CTRL_TX1_DONE_EN | ++ MTK_WED_WPDMA_INT_CTRL_TX1_DONE_CLR | ++ FIELD_PREP(MTK_WED_WPDMA_INT_CTRL_TX0_DONE_TRIG, ++ dev->wlan.tx_tbit[0]) | ++ FIELD_PREP(MTK_WED_WPDMA_INT_CTRL_TX1_DONE_TRIG, ++ dev->wlan.tx_tbit[1])); ++ ++ /* initail txfree interrupt trigger */ ++ wed_w32(dev, MTK_WED_WPDMA_INT_CTRL_TX_FREE, ++ MTK_WED_WPDMA_INT_CTRL_TX_FREE_DONE_EN | ++ MTK_WED_WPDMA_INT_CTRL_TX_FREE_DONE_CLR | ++ FIELD_PREP(MTK_WED_WPDMA_INT_CTRL_TX_FREE_DONE_TRIG, ++ dev->wlan.txfree_tbit)); ++ ++ wed_w32(dev, MTK_WED_WDMA_INT_CLR, wdma_mask); ++ wed_set(dev, MTK_WED_WDMA_INT_CTRL, ++ FIELD_PREP(MTK_WED_WDMA_INT_CTRL_POLL_SRC_SEL, ++ dev->wdma_idx)); ++ } + +- /* initail wdma interrupt agent */ + wed_w32(dev, MTK_WED_WDMA_INT_TRIGGER, wdma_mask); +- wed_clr(dev, MTK_WED_WDMA_INT_CTRL, wdma_mask); + + wdma_w32(dev, MTK_WDMA_INT_MASK, wdma_mask); + wdma_w32(dev, MTK_WDMA_INT_GRP2, wdma_mask); +@@ -580,14 +747,28 @@ mtk_wed_dma_enable(struct mtk_wed_device + wdma_set(dev, MTK_WDMA_GLO_CFG, + MTK_WDMA_GLO_CFG_TX_DMA_EN | + MTK_WDMA_GLO_CFG_RX_INFO1_PRERES | +- MTK_WDMA_GLO_CFG_RX_INFO2_PRERES | +- MTK_WDMA_GLO_CFG_RX_INFO3_PRERES); ++ MTK_WDMA_GLO_CFG_RX_INFO2_PRERES); ++ ++ if (dev->hw->version == 1) { ++ wdma_set(dev, MTK_WDMA_GLO_CFG, ++ MTK_WDMA_GLO_CFG_RX_INFO3_PRERES); ++ } else { ++ wed_set(dev, MTK_WED_WPDMA_CTRL, ++ MTK_WED_WPDMA_CTRL_SDL1_FIXED); ++ ++ wed_set(dev, MTK_WED_WPDMA_GLO_CFG, ++ MTK_WED_WPDMA_GLO_CFG_RX_DRV_R0_PKT_PROC | ++ MTK_WED_WPDMA_GLO_CFG_RX_DRV_R0_CRX_SYNC); ++ ++ wed_clr(dev, MTK_WED_WPDMA_GLO_CFG, ++ MTK_WED_WPDMA_GLO_CFG_TX_TKID_KEEP | ++ MTK_WED_WPDMA_GLO_CFG_TX_DMAD_DW3_PREV); ++ } + } + + static void + mtk_wed_start(struct mtk_wed_device *dev, u32 irq_mask) + { +- u32 val; + int i; + + for (i = 0; i < ARRAY_SIZE(dev->tx_wdma); i++) +@@ -598,14 +779,17 @@ mtk_wed_start(struct mtk_wed_device *dev + mtk_wed_configure_irq(dev, irq_mask); + + mtk_wed_set_ext_int(dev, true); +- val = dev->wlan.wpdma_phys | +- MTK_PCIE_MIRROR_MAP_EN | +- FIELD_PREP(MTK_PCIE_MIRROR_MAP_WED_ID, dev->hw->index); +- +- if (dev->hw->index) +- val |= BIT(1); +- val |= BIT(0); +- regmap_write(dev->hw->mirror, dev->hw->index * 4, val); ++ ++ if (dev->hw->version == 1) { ++ u32 val = dev->wlan.wpdma_phys | MTK_PCIE_MIRROR_MAP_EN | ++ FIELD_PREP(MTK_PCIE_MIRROR_MAP_WED_ID, ++ dev->hw->index); ++ ++ val |= BIT(0) | (BIT(1) * !!dev->hw->index); ++ regmap_write(dev->hw->mirror, dev->hw->index * 4, val); ++ } else { ++ mtk_wed_set_512_support(dev, true); ++ } + + mtk_wed_dma_enable(dev); + dev->running = true; +@@ -639,7 +823,9 @@ mtk_wed_attach(struct mtk_wed_device *de + goto out; + } + +- dev_info(&dev->wlan.pci_dev->dev, "attaching wed device %d\n", hw->index); ++ dev_info(&dev->wlan.pci_dev->dev, ++ "attaching wed device %d version %d\n", ++ hw->index, hw->version); + + dev->hw = hw; + dev->dev = hw->dev; +@@ -657,7 +843,9 @@ mtk_wed_attach(struct mtk_wed_device *de + } + + mtk_wed_hw_init_early(dev); +- regmap_update_bits(hw->hifsys, HIFSYS_DMA_AG_MAP, BIT(hw->index), 0); ++ if (hw->hifsys) ++ regmap_update_bits(hw->hifsys, HIFSYS_DMA_AG_MAP, ++ BIT(hw->index), 0); + + out: + mutex_unlock(&hw_lock); +@@ -684,7 +872,8 @@ mtk_wed_tx_ring_setup(struct mtk_wed_dev + + BUG_ON(idx >= ARRAY_SIZE(dev->tx_ring)); + +- if (mtk_wed_ring_alloc(dev, ring, MTK_WED_TX_RING_SIZE)) ++ if (mtk_wed_ring_alloc(dev, ring, MTK_WED_TX_RING_SIZE, ++ sizeof(*ring->desc))) + return -ENOMEM; + + if (mtk_wed_wdma_ring_setup(dev, idx, MTK_WED_WDMA_RING_SIZE)) +@@ -711,21 +900,21 @@ static int + mtk_wed_txfree_ring_setup(struct mtk_wed_device *dev, void __iomem *regs) + { + struct mtk_wed_ring *ring = &dev->txfree_ring; +- int i; ++ int i, index = dev->hw->version == 1; + + /* + * For txfree event handling, the same DMA ring is shared between WED + * and WLAN. The WLAN driver accesses the ring index registers through + * WED + */ +- ring->reg_base = MTK_WED_RING_RX(1); ++ ring->reg_base = MTK_WED_RING_RX(index); + ring->wpdma = regs; + + for (i = 0; i < 12; i += 4) { + u32 val = readl(regs + i); + +- wed_w32(dev, MTK_WED_RING_RX(1) + i, val); +- wed_w32(dev, MTK_WED_WPDMA_RING_RX(1) + i, val); ++ wed_w32(dev, MTK_WED_RING_RX(index) + i, val); ++ wed_w32(dev, MTK_WED_WPDMA_RING_RX(index) + i, val); + } + + return 0; +@@ -734,11 +923,19 @@ mtk_wed_txfree_ring_setup(struct mtk_wed + static u32 + mtk_wed_irq_get(struct mtk_wed_device *dev, u32 mask) + { +- u32 val; ++ u32 val, ext_mask = MTK_WED_EXT_INT_STATUS_ERROR_MASK; ++ ++ if (dev->hw->version == 1) ++ ext_mask |= MTK_WED_EXT_INT_STATUS_TX_DRV_R_RESP_ERR; ++ else ++ ext_mask |= MTK_WED_EXT_INT_STATUS_RX_FBUF_LO_TH | ++ MTK_WED_EXT_INT_STATUS_RX_FBUF_HI_TH | ++ MTK_WED_EXT_INT_STATUS_RX_DRV_COHERENT | ++ MTK_WED_EXT_INT_STATUS_TX_DMA_W_RESP_ERR; + + val = wed_r32(dev, MTK_WED_EXT_INT_STATUS); + wed_w32(dev, MTK_WED_EXT_INT_STATUS, val); +- val &= MTK_WED_EXT_INT_STATUS_ERROR_MASK; ++ val &= ext_mask; + if (!dev->hw->num_flows) + val &= ~MTK_WED_EXT_INT_STATUS_TKID_WO_PYLD; + if (val && net_ratelimit()) +@@ -813,7 +1010,8 @@ out: + } + + void mtk_wed_add_hw(struct device_node *np, struct mtk_eth *eth, +- void __iomem *wdma, int index) ++ void __iomem *wdma, phys_addr_t wdma_phy, ++ int index) + { + static const struct mtk_wed_ops wed_ops = { + .attach = mtk_wed_attach, +@@ -860,26 +1058,33 @@ void mtk_wed_add_hw(struct device_node * + hw = kzalloc(sizeof(*hw), GFP_KERNEL); + if (!hw) + goto unlock; ++ + hw->node = np; + hw->regs = regs; + hw->eth = eth; + hw->dev = &pdev->dev; ++ hw->wdma_phy = wdma_phy; + hw->wdma = wdma; + hw->index = index; + hw->irq = irq; +- hw->mirror = syscon_regmap_lookup_by_phandle(eth_np, +- "mediatek,pcie-mirror"); +- hw->hifsys = syscon_regmap_lookup_by_phandle(eth_np, +- "mediatek,hifsys"); +- if (IS_ERR(hw->mirror) || IS_ERR(hw->hifsys)) { +- kfree(hw); +- goto unlock; +- } ++ hw->version = MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V2) ? 2 : 1; + +- if (!index) { +- regmap_write(hw->mirror, 0, 0); +- regmap_write(hw->mirror, 4, 0); ++ if (hw->version == 1) { ++ hw->mirror = syscon_regmap_lookup_by_phandle(eth_np, ++ "mediatek,pcie-mirror"); ++ hw->hifsys = syscon_regmap_lookup_by_phandle(eth_np, ++ "mediatek,hifsys"); ++ if (IS_ERR(hw->mirror) || IS_ERR(hw->hifsys)) { ++ kfree(hw); ++ goto unlock; ++ } ++ ++ if (!index) { ++ regmap_write(hw->mirror, 0, 0); ++ regmap_write(hw->mirror, 4, 0); ++ } + } ++ + mtk_wed_hw_add_debugfs(hw); + + hw_list[index] = hw; +--- a/drivers/net/ethernet/mediatek/mtk_wed.h ++++ b/drivers/net/ethernet/mediatek/mtk_wed.h +@@ -18,11 +18,13 @@ struct mtk_wed_hw { + struct regmap *hifsys; + struct device *dev; + void __iomem *wdma; ++ phys_addr_t wdma_phy; + struct regmap *mirror; + struct dentry *debugfs_dir; + struct mtk_wed_device *wed_dev; + u32 debugfs_reg; + u32 num_flows; ++ u8 version; + char dirname[5]; + int irq; + int index; +@@ -101,14 +103,16 @@ wpdma_txfree_w32(struct mtk_wed_device * + } + + void mtk_wed_add_hw(struct device_node *np, struct mtk_eth *eth, +- void __iomem *wdma, int index); ++ void __iomem *wdma, phys_addr_t wdma_phy, ++ int index); + void mtk_wed_exit(void); + int mtk_wed_flow_add(int index); + void mtk_wed_flow_remove(int index); + #else + static inline void + mtk_wed_add_hw(struct device_node *np, struct mtk_eth *eth, +- void __iomem *wdma, int index) ++ void __iomem *wdma, phys_addr_t wdma_phy, ++ int index) + { + } + static inline void +--- a/drivers/net/ethernet/mediatek/mtk_wed_debugfs.c ++++ b/drivers/net/ethernet/mediatek/mtk_wed_debugfs.c +@@ -116,6 +116,9 @@ wed_txinfo_show(struct seq_file *s, void + DUMP_WDMA(WDMA_GLO_CFG), + DUMP_WDMA_RING(WDMA_RING_RX(0)), + DUMP_WDMA_RING(WDMA_RING_RX(1)), ++ ++ DUMP_STR("TX FREE"), ++ DUMP_WED(WED_RX_MIB(0)), + }; + struct mtk_wed_hw *hw = s->private; + struct mtk_wed_device *dev = hw->wed_dev; +--- a/drivers/net/ethernet/mediatek/mtk_wed_regs.h ++++ b/drivers/net/ethernet/mediatek/mtk_wed_regs.h +@@ -5,6 +5,7 @@ + #define __MTK_WED_REGS_H + + #define MTK_WDMA_DESC_CTRL_LEN1 GENMASK(14, 0) ++#define MTK_WDMA_DESC_CTRL_LEN1_V2 GENMASK(13, 0) + #define MTK_WDMA_DESC_CTRL_LAST_SEG1 BIT(15) + #define MTK_WDMA_DESC_CTRL_BURST BIT(16) + #define MTK_WDMA_DESC_CTRL_LEN0 GENMASK(29, 16) +@@ -41,6 +42,7 @@ struct mtk_wdma_desc { + #define MTK_WED_CTRL_RESERVE_EN BIT(12) + #define MTK_WED_CTRL_RESERVE_BUSY BIT(13) + #define MTK_WED_CTRL_FINAL_DIDX_READ BIT(24) ++#define MTK_WED_CTRL_ETH_DMAD_FMT BIT(25) + #define MTK_WED_CTRL_MIB_READ_CLEAR BIT(28) + + #define MTK_WED_EXT_INT_STATUS 0x020 +@@ -57,7 +59,8 @@ struct mtk_wdma_desc { + #define MTK_WED_EXT_INT_STATUS_RX_DRV_INIT_WDMA_EN BIT(19) + #define MTK_WED_EXT_INT_STATUS_RX_DRV_BM_DMAD_COHERENT BIT(20) + #define MTK_WED_EXT_INT_STATUS_TX_DRV_R_RESP_ERR BIT(21) +-#define MTK_WED_EXT_INT_STATUS_TX_DRV_W_RESP_ERR BIT(22) ++#define MTK_WED_EXT_INT_STATUS_TX_DMA_R_RESP_ERR BIT(22) ++#define MTK_WED_EXT_INT_STATUS_TX_DMA_W_RESP_ERR BIT(23) + #define MTK_WED_EXT_INT_STATUS_RX_DRV_DMA_RECYCLE BIT(24) + #define MTK_WED_EXT_INT_STATUS_ERROR_MASK (MTK_WED_EXT_INT_STATUS_TF_LEN_ERR | \ + MTK_WED_EXT_INT_STATUS_TKID_WO_PYLD | \ +@@ -65,8 +68,7 @@ struct mtk_wdma_desc { + MTK_WED_EXT_INT_STATUS_RX_DRV_R_RESP_ERR | \ + MTK_WED_EXT_INT_STATUS_RX_DRV_W_RESP_ERR | \ + MTK_WED_EXT_INT_STATUS_RX_DRV_INIT_WDMA_EN | \ +- MTK_WED_EXT_INT_STATUS_TX_DRV_R_RESP_ERR | \ +- MTK_WED_EXT_INT_STATUS_TX_DRV_W_RESP_ERR) ++ MTK_WED_EXT_INT_STATUS_TX_DMA_R_RESP_ERR) + + #define MTK_WED_EXT_INT_MASK 0x028 + +@@ -81,6 +83,7 @@ struct mtk_wdma_desc { + #define MTK_WED_TX_BM_BASE 0x084 + + #define MTK_WED_TX_BM_TKID 0x088 ++#define MTK_WED_TX_BM_TKID_V2 0x0c8 + #define MTK_WED_TX_BM_TKID_START GENMASK(15, 0) + #define MTK_WED_TX_BM_TKID_END GENMASK(31, 16) + +@@ -94,7 +97,25 @@ struct mtk_wdma_desc { + + #define MTK_WED_TX_BM_DYN_THR 0x0a0 + #define MTK_WED_TX_BM_DYN_THR_LO GENMASK(6, 0) ++#define MTK_WED_TX_BM_DYN_THR_LO_V2 GENMASK(8, 0) + #define MTK_WED_TX_BM_DYN_THR_HI GENMASK(22, 16) ++#define MTK_WED_TX_BM_DYN_THR_HI_V2 GENMASK(24, 16) ++ ++#define MTK_WED_TX_TKID_CTRL 0x0c0 ++#define MTK_WED_TX_TKID_CTRL_VLD_GRP_NUM GENMASK(6, 0) ++#define MTK_WED_TX_TKID_CTRL_RSV_GRP_NUM GENMASK(22, 16) ++#define MTK_WED_TX_TKID_CTRL_PAUSE BIT(28) ++ ++#define MTK_WED_TX_TKID_DYN_THR 0x0e0 ++#define MTK_WED_TX_TKID_DYN_THR_LO GENMASK(6, 0) ++#define MTK_WED_TX_TKID_DYN_THR_HI GENMASK(22, 16) ++ ++#define MTK_WED_TXP_DW0 0x120 ++#define MTK_WED_TXP_DW1 0x124 ++#define MTK_WED_WPDMA_WRITE_TXP GENMASK(31, 16) ++#define MTK_WED_TXDP_CTRL 0x130 ++#define MTK_WED_TXDP_DW9_OVERWR BIT(9) ++#define MTK_WED_RX_BM_TKID_MIB 0x1cc + + #define MTK_WED_INT_STATUS 0x200 + #define MTK_WED_INT_MASK 0x204 +@@ -125,6 +146,7 @@ struct mtk_wdma_desc { + #define MTK_WED_RESET_IDX_RX GENMASK(17, 16) + + #define MTK_WED_TX_MIB(_n) (0x2a0 + (_n) * 4) ++#define MTK_WED_RX_MIB(_n) (0x2e0 + (_n) * 4) + + #define MTK_WED_RING_TX(_n) (0x300 + (_n) * 0x10) + +@@ -155,21 +177,62 @@ struct mtk_wdma_desc { + #define MTK_WED_WPDMA_GLO_CFG_BYTE_SWAP BIT(29) + #define MTK_WED_WPDMA_GLO_CFG_RX_2B_OFFSET BIT(31) + ++/* CONFIG_MEDIATEK_NETSYS_V2 */ ++#define MTK_WED_WPDMA_GLO_CFG_RX_DRV_R0_PKT_PROC BIT(4) ++#define MTK_WED_WPDMA_GLO_CFG_RX_DRV_R1_PKT_PROC BIT(5) ++#define MTK_WED_WPDMA_GLO_CFG_RX_DRV_R0_CRX_SYNC BIT(6) ++#define MTK_WED_WPDMA_GLO_CFG_RX_DRV_R1_CRX_SYNC BIT(7) ++#define MTK_WED_WPDMA_GLO_CFG_RX_DRV_EVENT_PKT_FMT_VER GENMASK(18, 16) ++#define MTK_WED_WPDMA_GLO_CFG_RX_DRV_UNSUPPORT_FMT BIT(19) ++#define MTK_WED_WPDMA_GLO_CFG_RX_DRV_UEVENT_PKT_FMT_CHK BIT(20) ++#define MTK_WED_WPDMA_GLO_CFG_RX_DDONE2_WR BIT(21) ++#define MTK_WED_WPDMA_GLO_CFG_TX_TKID_KEEP BIT(24) ++#define MTK_WED_WPDMA_GLO_CFG_TX_DMAD_DW3_PREV BIT(28) ++ + #define MTK_WED_WPDMA_RESET_IDX 0x50c + #define MTK_WED_WPDMA_RESET_IDX_TX GENMASK(3, 0) + #define MTK_WED_WPDMA_RESET_IDX_RX GENMASK(17, 16) + ++#define MTK_WED_WPDMA_CTRL 0x518 ++#define MTK_WED_WPDMA_CTRL_SDL1_FIXED BIT(31) ++ + #define MTK_WED_WPDMA_INT_CTRL 0x520 + #define MTK_WED_WPDMA_INT_CTRL_SUBRT_ADV BIT(21) + + #define MTK_WED_WPDMA_INT_MASK 0x524 + ++#define MTK_WED_WPDMA_INT_CTRL_TX 0x530 ++#define MTK_WED_WPDMA_INT_CTRL_TX0_DONE_EN BIT(0) ++#define MTK_WED_WPDMA_INT_CTRL_TX0_DONE_CLR BIT(1) ++#define MTK_WED_WPDMA_INT_CTRL_TX0_DONE_TRIG GENMASK(6, 2) ++#define MTK_WED_WPDMA_INT_CTRL_TX1_DONE_EN BIT(8) ++#define MTK_WED_WPDMA_INT_CTRL_TX1_DONE_CLR BIT(9) ++#define MTK_WED_WPDMA_INT_CTRL_TX1_DONE_TRIG GENMASK(14, 10) ++ ++#define MTK_WED_WPDMA_INT_CTRL_RX 0x534 ++ ++#define MTK_WED_WPDMA_INT_CTRL_TX_FREE 0x538 ++#define MTK_WED_WPDMA_INT_CTRL_TX_FREE_DONE_EN BIT(0) ++#define MTK_WED_WPDMA_INT_CTRL_TX_FREE_DONE_CLR BIT(1) ++#define MTK_WED_WPDMA_INT_CTRL_TX_FREE_DONE_TRIG GENMASK(6, 2) ++ + #define MTK_WED_PCIE_CFG_BASE 0x560 + ++#define MTK_WED_PCIE_CFG_BASE 0x560 ++#define MTK_WED_PCIE_CFG_INTM 0x564 ++#define MTK_WED_PCIE_CFG_MSIS 0x568 + #define MTK_WED_PCIE_INT_TRIGGER 0x570 + #define MTK_WED_PCIE_INT_TRIGGER_STATUS BIT(16) + ++#define MTK_WED_PCIE_INT_CTRL 0x57c ++#define MTK_WED_PCIE_INT_CTRL_MSK_EN_POLA BIT(20) ++#define MTK_WED_PCIE_INT_CTRL_SRC_SEL GENMASK(17, 16) ++#define MTK_WED_PCIE_INT_CTRL_POLL_EN GENMASK(13, 12) ++ + #define MTK_WED_WPDMA_CFG_BASE 0x580 ++#define MTK_WED_WPDMA_CFG_INT_MASK 0x584 ++#define MTK_WED_WPDMA_CFG_TX 0x588 ++#define MTK_WED_WPDMA_CFG_TX_FREE 0x58c + + #define MTK_WED_WPDMA_TX_MIB(_n) (0x5a0 + (_n) * 4) + #define MTK_WED_WPDMA_TX_COHERENT_MIB(_n) (0x5d0 + (_n) * 4) +@@ -203,15 +266,24 @@ struct mtk_wdma_desc { + #define MTK_WED_WDMA_RESET_IDX_RX GENMASK(17, 16) + #define MTK_WED_WDMA_RESET_IDX_DRV GENMASK(25, 24) + ++#define MTK_WED_WDMA_INT_CLR 0xa24 ++#define MTK_WED_WDMA_INT_CLR_RX_DONE GENMASK(17, 16) ++ + #define MTK_WED_WDMA_INT_TRIGGER 0xa28 + #define MTK_WED_WDMA_INT_TRIGGER_RX_DONE GENMASK(17, 16) + + #define MTK_WED_WDMA_INT_CTRL 0xa2c + #define MTK_WED_WDMA_INT_CTRL_POLL_SRC_SEL GENMASK(17, 16) + ++#define MTK_WED_WDMA_CFG_BASE 0xaa0 + #define MTK_WED_WDMA_OFFSET0 0xaa4 + #define MTK_WED_WDMA_OFFSET1 0xaa8 + ++#define MTK_WED_WDMA_OFST0_GLO_INTS GENMASK(15, 0) ++#define MTK_WED_WDMA_OFST0_GLO_CFG GENMASK(31, 16) ++#define MTK_WED_WDMA_OFST1_TX_CTRL GENMASK(15, 0) ++#define MTK_WED_WDMA_OFST1_RX_CTRL GENMASK(31, 16) ++ + #define MTK_WED_WDMA_RX_MIB(_n) (0xae0 + (_n) * 4) + #define MTK_WED_WDMA_RX_RECYCLE_MIB(_n) (0xae8 + (_n) * 4) + #define MTK_WED_WDMA_RX_PROCESSED_MIB(_n) (0xaf0 + (_n) * 4) +@@ -221,6 +293,7 @@ struct mtk_wdma_desc { + #define MTK_WED_RING_OFS_CPU_IDX 0x08 + #define MTK_WED_RING_OFS_DMA_IDX 0x0c + ++#define MTK_WDMA_RING_TX(_n) (0x000 + (_n) * 0x10) + #define MTK_WDMA_RING_RX(_n) (0x100 + (_n) * 0x10) + + #define MTK_WDMA_GLO_CFG 0x204 +@@ -234,6 +307,8 @@ struct mtk_wdma_desc { + #define MTK_WDMA_RESET_IDX_TX GENMASK(3, 0) + #define MTK_WDMA_RESET_IDX_RX GENMASK(17, 16) + ++#define MTK_WDMA_INT_STATUS 0x220 ++ + #define MTK_WDMA_INT_MASK 0x228 + #define MTK_WDMA_INT_MASK_TX_DONE GENMASK(3, 0) + #define MTK_WDMA_INT_MASK_RX_DONE GENMASK(17, 16) +--- a/include/linux/soc/mediatek/mtk_wed.h ++++ b/include/linux/soc/mediatek/mtk_wed.h +@@ -14,6 +14,7 @@ struct mtk_wdma_desc; + struct mtk_wed_ring { + struct mtk_wdma_desc *desc; + dma_addr_t desc_phys; ++ u32 desc_size; + int size; + + u32 reg_base; +@@ -45,10 +46,17 @@ struct mtk_wed_device { + struct pci_dev *pci_dev; + + u32 wpdma_phys; ++ u32 wpdma_int; ++ u32 wpdma_mask; ++ u32 wpdma_tx; ++ u32 wpdma_txfree; + + u16 token_start; + unsigned int nbuf; + ++ u8 tx_tbit[MTK_WED_TX_QUEUES]; ++ u8 txfree_tbit; ++ + u32 (*init_buf)(void *ptr, dma_addr_t phys, int token_id); + int (*offload_enable)(struct mtk_wed_device *wed); + void (*offload_disable)(struct mtk_wed_device *wed); diff --git a/target/linux/generic/backport-6.1/722-v6.0-net-ethernet-mtk_eth_wed-add-axi-bus-support.patch b/target/linux/generic/backport-6.1/722-v6.0-net-ethernet-mtk_eth_wed-add-axi-bus-support.patch new file mode 100644 index 0000000000..70b7bad31f --- /dev/null +++ b/target/linux/generic/backport-6.1/722-v6.0-net-ethernet-mtk_eth_wed-add-axi-bus-support.patch @@ -0,0 +1,237 @@ +From 6e1df49f330dce7c58a39d6772f1385b6887bb03 Mon Sep 17 00:00:00 2001 +Message-Id: <6e1df49f330dce7c58a39d6772f1385b6887bb03.1662990860.git.lorenzo@kernel.org> +From: Lorenzo Bianconi +Date: Thu, 8 Sep 2022 11:26:10 +0200 +Subject: [PATCH net-next] net: ethernet: mtk_eth_wed: add axi bus support + +Other than pcie bus, introduce support for axi bus to mtk wed driver. +Axi bus is used to connect mt7986-wmac soc chip available on mt7986 +device. + +Co-developed-by: Bo Jiao +Signed-off-by: Bo Jiao +Co-developed-by: Sujuan Chen +Signed-off-by: Sujuan Chen +Signed-off-by: Lorenzo Bianconi +--- + drivers/net/ethernet/mediatek/mtk_wed.c | 116 +++++++++++++------ + drivers/net/ethernet/mediatek/mtk_wed_regs.h | 2 + + include/linux/soc/mediatek/mtk_wed.h | 11 +- + 3 files changed, 91 insertions(+), 38 deletions(-) + +--- a/drivers/net/ethernet/mediatek/mtk_wed.c ++++ b/drivers/net/ethernet/mediatek/mtk_wed.c +@@ -85,11 +85,31 @@ static struct mtk_wed_hw * + mtk_wed_assign(struct mtk_wed_device *dev) + { + struct mtk_wed_hw *hw; ++ int i; ++ ++ if (dev->wlan.bus_type == MTK_WED_BUS_PCIE) { ++ hw = hw_list[pci_domain_nr(dev->wlan.pci_dev->bus)]; ++ if (!hw) ++ return NULL; ++ ++ if (!hw->wed_dev) ++ goto out; ++ ++ if (hw->version == 1) ++ return NULL; ++ ++ /* MT7986 WED devices do not have any pcie slot restrictions */ ++ } ++ /* MT7986 PCIE or AXI */ ++ for (i = 0; i < ARRAY_SIZE(hw_list); i++) { ++ hw = hw_list[i]; ++ if (hw && !hw->wed_dev) ++ goto out; ++ } + +- hw = hw_list[pci_domain_nr(dev->wlan.pci_dev->bus)]; +- if (!hw || hw->wed_dev) +- return NULL; ++ return NULL; + ++out: + hw->wed_dev = dev; + return hw; + } +@@ -322,7 +342,6 @@ mtk_wed_stop(struct mtk_wed_device *dev) + static void + mtk_wed_detach(struct mtk_wed_device *dev) + { +- struct device_node *wlan_node = dev->wlan.pci_dev->dev.of_node; + struct mtk_wed_hw *hw = dev->hw; + + mutex_lock(&hw_lock); +@@ -337,9 +356,14 @@ mtk_wed_detach(struct mtk_wed_device *de + mtk_wed_free_buffer(dev); + mtk_wed_free_tx_rings(dev); + +- if (of_dma_is_coherent(wlan_node) && hw->hifsys) +- regmap_update_bits(hw->hifsys, HIFSYS_DMA_AG_MAP, +- BIT(hw->index), BIT(hw->index)); ++ if (dev->wlan.bus_type == MTK_WED_BUS_PCIE) { ++ struct device_node *wlan_node; ++ ++ wlan_node = dev->wlan.pci_dev->dev.of_node; ++ if (of_dma_is_coherent(wlan_node) && hw->hifsys) ++ regmap_update_bits(hw->hifsys, HIFSYS_DMA_AG_MAP, ++ BIT(hw->index), BIT(hw->index)); ++ } + + if (!hw_list[!hw->index]->wed_dev && + hw->eth->dma_dev != hw->eth->dev) +@@ -356,40 +380,54 @@ mtk_wed_detach(struct mtk_wed_device *de + static void + mtk_wed_bus_init(struct mtk_wed_device *dev) + { +- struct device_node *np = dev->hw->eth->dev->of_node; +- struct regmap *regs; +- u32 val; +- +- regs = syscon_regmap_lookup_by_phandle(np, "mediatek,wed-pcie"); +- if (IS_ERR(regs)) +- return; ++ switch (dev->wlan.bus_type) { ++ case MTK_WED_BUS_PCIE: { ++ struct device_node *np = dev->hw->eth->dev->of_node; ++ struct regmap *regs; ++ u32 val; ++ ++ regs = syscon_regmap_lookup_by_phandle(np, ++ "mediatek,wed-pcie"); ++ if (IS_ERR(regs)) ++ break; + +- regmap_update_bits(regs, 0, BIT(0), BIT(0)); ++ regmap_update_bits(regs, 0, BIT(0), BIT(0)); + +- wed_w32(dev, MTK_WED_PCIE_INT_CTRL, +- FIELD_PREP(MTK_WED_PCIE_INT_CTRL_POLL_EN, 2)); ++ wed_w32(dev, MTK_WED_PCIE_INT_CTRL, ++ FIELD_PREP(MTK_WED_PCIE_INT_CTRL_POLL_EN, 2)); + +- /* pcie interrupt control: pola/source selection */ +- wed_set(dev, MTK_WED_PCIE_INT_CTRL, +- MTK_WED_PCIE_INT_CTRL_MSK_EN_POLA | +- FIELD_PREP(MTK_WED_PCIE_INT_CTRL_SRC_SEL, 1)); +- wed_r32(dev, MTK_WED_PCIE_INT_CTRL); +- +- val = wed_r32(dev, MTK_WED_PCIE_CFG_INTM); +- val = wed_r32(dev, MTK_WED_PCIE_CFG_BASE); +- wed_w32(dev, MTK_WED_PCIE_CFG_INTM, PCIE_BASE_ADDR0 | 0x180); +- wed_w32(dev, MTK_WED_PCIE_CFG_BASE, PCIE_BASE_ADDR0 | 0x184); +- +- val = wed_r32(dev, MTK_WED_PCIE_CFG_INTM); +- val = wed_r32(dev, MTK_WED_PCIE_CFG_BASE); +- +- /* pcie interrupt status trigger register */ +- wed_w32(dev, MTK_WED_PCIE_INT_TRIGGER, BIT(24)); +- wed_r32(dev, MTK_WED_PCIE_INT_TRIGGER); +- +- /* pola setting */ +- val = wed_r32(dev, MTK_WED_PCIE_INT_CTRL); +- wed_set(dev, MTK_WED_PCIE_INT_CTRL, MTK_WED_PCIE_INT_CTRL_MSK_EN_POLA); ++ /* pcie interrupt control: pola/source selection */ ++ wed_set(dev, MTK_WED_PCIE_INT_CTRL, ++ MTK_WED_PCIE_INT_CTRL_MSK_EN_POLA | ++ FIELD_PREP(MTK_WED_PCIE_INT_CTRL_SRC_SEL, 1)); ++ wed_r32(dev, MTK_WED_PCIE_INT_CTRL); ++ ++ val = wed_r32(dev, MTK_WED_PCIE_CFG_INTM); ++ val = wed_r32(dev, MTK_WED_PCIE_CFG_BASE); ++ wed_w32(dev, MTK_WED_PCIE_CFG_INTM, PCIE_BASE_ADDR0 | 0x180); ++ wed_w32(dev, MTK_WED_PCIE_CFG_BASE, PCIE_BASE_ADDR0 | 0x184); ++ ++ val = wed_r32(dev, MTK_WED_PCIE_CFG_INTM); ++ val = wed_r32(dev, MTK_WED_PCIE_CFG_BASE); ++ ++ /* pcie interrupt status trigger register */ ++ wed_w32(dev, MTK_WED_PCIE_INT_TRIGGER, BIT(24)); ++ wed_r32(dev, MTK_WED_PCIE_INT_TRIGGER); ++ ++ /* pola setting */ ++ val = wed_r32(dev, MTK_WED_PCIE_INT_CTRL); ++ wed_set(dev, MTK_WED_PCIE_INT_CTRL, ++ MTK_WED_PCIE_INT_CTRL_MSK_EN_POLA); ++ break; ++ } ++ case MTK_WED_BUS_AXI: ++ wed_set(dev, MTK_WED_WPDMA_INT_CTRL, ++ MTK_WED_WPDMA_INT_CTRL_SIG_SRC | ++ FIELD_PREP(MTK_WED_WPDMA_INT_CTRL_SRC_SEL, 0)); ++ break; ++ default: ++ break; ++ } + } + + static void +@@ -800,12 +838,14 @@ mtk_wed_attach(struct mtk_wed_device *de + __releases(RCU) + { + struct mtk_wed_hw *hw; ++ struct device *device; + int ret = 0; + + RCU_LOCKDEP_WARN(!rcu_read_lock_held(), + "mtk_wed_attach without holding the RCU read lock"); + +- if (pci_domain_nr(dev->wlan.pci_dev->bus) > 1 || ++ if ((dev->wlan.bus_type == MTK_WED_BUS_PCIE && ++ pci_domain_nr(dev->wlan.pci_dev->bus) > 1) || + !try_module_get(THIS_MODULE)) + ret = -ENODEV; + +@@ -823,8 +863,10 @@ mtk_wed_attach(struct mtk_wed_device *de + goto out; + } + +- dev_info(&dev->wlan.pci_dev->dev, +- "attaching wed device %d version %d\n", ++ device = dev->wlan.bus_type == MTK_WED_BUS_PCIE ++ ? &dev->wlan.pci_dev->dev ++ : &dev->wlan.platform_dev->dev; ++ dev_info(device, "attaching wed device %d version %d\n", + hw->index, hw->version); + + dev->hw = hw; +--- a/drivers/net/ethernet/mediatek/mtk_wed_regs.h ++++ b/drivers/net/ethernet/mediatek/mtk_wed_regs.h +@@ -198,6 +198,8 @@ struct mtk_wdma_desc { + + #define MTK_WED_WPDMA_INT_CTRL 0x520 + #define MTK_WED_WPDMA_INT_CTRL_SUBRT_ADV BIT(21) ++#define MTK_WED_WPDMA_INT_CTRL_SIG_SRC BIT(22) ++#define MTK_WED_WPDMA_INT_CTRL_SRC_SEL GENMASK(17, 16) + + #define MTK_WED_WPDMA_INT_MASK 0x524 + +--- a/include/linux/soc/mediatek/mtk_wed.h ++++ b/include/linux/soc/mediatek/mtk_wed.h +@@ -11,6 +11,11 @@ + struct mtk_wed_hw; + struct mtk_wdma_desc; + ++enum mtk_wed_bus_tye { ++ MTK_WED_BUS_PCIE, ++ MTK_WED_BUS_AXI, ++}; ++ + struct mtk_wed_ring { + struct mtk_wdma_desc *desc; + dma_addr_t desc_phys; +@@ -43,7 +48,11 @@ struct mtk_wed_device { + + /* filled by driver: */ + struct { +- struct pci_dev *pci_dev; ++ union { ++ struct platform_device *platform_dev; ++ struct pci_dev *pci_dev; ++ }; ++ enum mtk_wed_bus_tye bus_type; + + u32 wpdma_phys; + u32 wpdma_int; diff --git a/target/linux/generic/backport-6.1/723-v6.0-net-ethernet-mtk_eth_soc-introduce-flow-offloading-s.patch b/target/linux/generic/backport-6.1/723-v6.0-net-ethernet-mtk_eth_soc-introduce-flow-offloading-s.patch new file mode 100644 index 0000000000..1ecbf485f1 --- /dev/null +++ b/target/linux/generic/backport-6.1/723-v6.0-net-ethernet-mtk_eth_soc-introduce-flow-offloading-s.patch @@ -0,0 +1,882 @@ +From 93408c858e5dc01d97c55efa721268f63fde2ae5 Mon Sep 17 00:00:00 2001 +Message-Id: <93408c858e5dc01d97c55efa721268f63fde2ae5.1662886034.git.lorenzo@kernel.org> +In-Reply-To: +References: +From: Lorenzo Bianconi +Date: Sat, 3 Sep 2022 18:34:09 +0200 +Subject: [PATCH net-next 4/5] net: ethernet: mtk_eth_soc: introduce flow + offloading support for mt7986 + +Introduce hw flow offload support for mt7986 chipset. PPE is not enabled +yet in mt7986 since mt76 support is not available yet. + +Co-developed-by: Bo Jiao +Signed-off-by: Bo Jiao +Co-developed-by: Sujuan Chen +Signed-off-by: Sujuan Chen +Signed-off-by: Lorenzo Bianconi +--- + drivers/net/ethernet/mediatek/mtk_eth_soc.c | 11 +- + drivers/net/ethernet/mediatek/mtk_eth_soc.h | 72 ++++++ + drivers/net/ethernet/mediatek/mtk_ppe.c | 213 +++++++++++------- + drivers/net/ethernet/mediatek/mtk_ppe.h | 52 ++++- + .../net/ethernet/mediatek/mtk_ppe_offload.c | 49 ++-- + drivers/net/ethernet/mediatek/mtk_ppe_regs.h | 8 + + 6 files changed, 289 insertions(+), 116 deletions(-) + +--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c ++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c +@@ -1858,12 +1858,14 @@ static int mtk_poll_rx(struct napi_struc + bytes += skb->len; + + if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V2)) { ++ reason = FIELD_GET(MTK_RXD5_PPE_CPU_REASON, trxd.rxd5); + hash = trxd.rxd5 & MTK_RXD5_FOE_ENTRY; + if (hash != MTK_RXD5_FOE_ENTRY) + skb_set_hash(skb, jhash_1word(hash, 0), + PKT_HASH_TYPE_L4); + rxdcsum = &trxd.rxd3; + } else { ++ reason = FIELD_GET(MTK_RXD4_PPE_CPU_REASON, trxd.rxd4); + hash = trxd.rxd4 & MTK_RXD4_FOE_ENTRY; + if (hash != MTK_RXD4_FOE_ENTRY) + skb_set_hash(skb, jhash_1word(hash, 0), +@@ -1877,7 +1879,6 @@ static int mtk_poll_rx(struct napi_struc + skb_checksum_none_assert(skb); + skb->protocol = eth_type_trans(skb, netdev); + +- reason = FIELD_GET(MTK_RXD4_PPE_CPU_REASON, trxd.rxd4); + if (reason == MTK_PPE_CPU_REASON_HIT_UNBIND_RATE_REACHED) + mtk_ppe_check_skb(eth->ppe[0], skb, hash); + +@@ -4181,7 +4182,7 @@ static const struct mtk_soc_data mt7621_ + .required_pctl = false, + .offload_version = 2, + .hash_offset = 2, +- .foe_entry_size = sizeof(struct mtk_foe_entry), ++ .foe_entry_size = sizeof(struct mtk_foe_entry) - 16, + .txrx = { + .txd_size = sizeof(struct mtk_tx_dma), + .rxd_size = sizeof(struct mtk_rx_dma), +@@ -4201,7 +4202,7 @@ static const struct mtk_soc_data mt7622_ + .required_pctl = false, + .offload_version = 2, + .hash_offset = 2, +- .foe_entry_size = sizeof(struct mtk_foe_entry), ++ .foe_entry_size = sizeof(struct mtk_foe_entry) - 16, + .txrx = { + .txd_size = sizeof(struct mtk_tx_dma), + .rxd_size = sizeof(struct mtk_rx_dma), +@@ -4220,7 +4221,7 @@ static const struct mtk_soc_data mt7623_ + .required_pctl = true, + .offload_version = 2, + .hash_offset = 2, +- .foe_entry_size = sizeof(struct mtk_foe_entry), ++ .foe_entry_size = sizeof(struct mtk_foe_entry) - 16, + .txrx = { + .txd_size = sizeof(struct mtk_tx_dma), + .rxd_size = sizeof(struct mtk_rx_dma), +@@ -4252,9 +4253,11 @@ static const struct mtk_soc_data mt7986_ + .reg_map = &mt7986_reg_map, + .ana_rgc3 = 0x128, + .caps = MT7986_CAPS, ++ .hw_features = MTK_HW_FEATURES, + .required_clks = MT7986_CLKS_BITMAP, + .required_pctl = false, + .hash_offset = 4, ++ .foe_entry_size = sizeof(struct mtk_foe_entry), + .txrx = { + .txd_size = sizeof(struct mtk_tx_dma_v2), + .rxd_size = sizeof(struct mtk_rx_dma_v2), +--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h ++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h +@@ -1150,6 +1150,78 @@ mtk_foe_get_entry(struct mtk_ppe *ppe, u + return ppe->foe_table + hash * soc->foe_entry_size; + } + ++static inline u32 mtk_get_ib1_ts_mask(struct mtk_eth *eth) ++{ ++ if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V2)) ++ return MTK_FOE_IB1_BIND_TIMESTAMP_V2; ++ ++ return MTK_FOE_IB1_BIND_TIMESTAMP; ++} ++ ++static inline u32 mtk_get_ib1_ppoe_mask(struct mtk_eth *eth) ++{ ++ if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V2)) ++ return MTK_FOE_IB1_BIND_PPPOE_V2; ++ ++ return MTK_FOE_IB1_BIND_PPPOE; ++} ++ ++static inline u32 mtk_get_ib1_vlan_tag_mask(struct mtk_eth *eth) ++{ ++ if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V2)) ++ return MTK_FOE_IB1_BIND_VLAN_TAG_V2; ++ ++ return MTK_FOE_IB1_BIND_VLAN_TAG; ++} ++ ++static inline u32 mtk_get_ib1_vlan_layer_mask(struct mtk_eth *eth) ++{ ++ if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V2)) ++ return MTK_FOE_IB1_BIND_VLAN_LAYER_V2; ++ ++ return MTK_FOE_IB1_BIND_VLAN_LAYER; ++} ++ ++static inline u32 mtk_prep_ib1_vlan_layer(struct mtk_eth *eth, u32 val) ++{ ++ if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V2)) ++ return FIELD_PREP(MTK_FOE_IB1_BIND_VLAN_LAYER_V2, val); ++ ++ return FIELD_PREP(MTK_FOE_IB1_BIND_VLAN_LAYER, val); ++} ++ ++static inline u32 mtk_get_ib1_vlan_layer(struct mtk_eth *eth, u32 val) ++{ ++ if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V2)) ++ return FIELD_GET(MTK_FOE_IB1_BIND_VLAN_LAYER_V2, val); ++ ++ return FIELD_GET(MTK_FOE_IB1_BIND_VLAN_LAYER, val); ++} ++ ++static inline u32 mtk_get_ib1_pkt_type_mask(struct mtk_eth *eth) ++{ ++ if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V2)) ++ return MTK_FOE_IB1_PACKET_TYPE_V2; ++ ++ return MTK_FOE_IB1_PACKET_TYPE; ++} ++ ++static inline u32 mtk_get_ib1_pkt_type(struct mtk_eth *eth, u32 val) ++{ ++ if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V2)) ++ return FIELD_GET(MTK_FOE_IB1_PACKET_TYPE_V2, val); ++ ++ return FIELD_GET(MTK_FOE_IB1_PACKET_TYPE, val); ++} ++ ++static inline u32 mtk_get_ib2_multicast_mask(struct mtk_eth *eth) ++{ ++ if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V2)) ++ return MTK_FOE_IB2_MULTICAST_V2; ++ ++ return MTK_FOE_IB2_MULTICAST; ++} ++ + /* read the hardware status register */ + void mtk_stats_update_mac(struct mtk_mac *mac); + +--- a/drivers/net/ethernet/mediatek/mtk_ppe.c ++++ b/drivers/net/ethernet/mediatek/mtk_ppe.c +@@ -56,7 +56,7 @@ static u32 ppe_clear(struct mtk_ppe *ppe + + static u32 mtk_eth_timestamp(struct mtk_eth *eth) + { +- return mtk_r32(eth, 0x0010) & MTK_FOE_IB1_BIND_TIMESTAMP; ++ return mtk_r32(eth, 0x0010) & mtk_get_ib1_ts_mask(eth); + } + + static int mtk_ppe_wait_busy(struct mtk_ppe *ppe) +@@ -93,7 +93,7 @@ static u32 mtk_ppe_hash_entry(struct mtk + u32 hv1, hv2, hv3; + u32 hash; + +- switch (FIELD_GET(MTK_FOE_IB1_PACKET_TYPE, e->ib1)) { ++ switch (mtk_get_ib1_pkt_type(eth, e->ib1)) { + case MTK_PPE_PKT_TYPE_IPV4_ROUTE: + case MTK_PPE_PKT_TYPE_IPV4_HNAPT: + hv1 = e->ipv4.orig.ports; +@@ -129,9 +129,9 @@ static u32 mtk_ppe_hash_entry(struct mtk + } + + static inline struct mtk_foe_mac_info * +-mtk_foe_entry_l2(struct mtk_foe_entry *entry) ++mtk_foe_entry_l2(struct mtk_eth *eth, struct mtk_foe_entry *entry) + { +- int type = FIELD_GET(MTK_FOE_IB1_PACKET_TYPE, entry->ib1); ++ int type = mtk_get_ib1_pkt_type(eth, entry->ib1); + + if (type == MTK_PPE_PKT_TYPE_BRIDGE) + return &entry->bridge.l2; +@@ -143,9 +143,9 @@ mtk_foe_entry_l2(struct mtk_foe_entry *e + } + + static inline u32 * +-mtk_foe_entry_ib2(struct mtk_foe_entry *entry) ++mtk_foe_entry_ib2(struct mtk_eth *eth, struct mtk_foe_entry *entry) + { +- int type = FIELD_GET(MTK_FOE_IB1_PACKET_TYPE, entry->ib1); ++ int type = mtk_get_ib1_pkt_type(eth, entry->ib1); + + if (type == MTK_PPE_PKT_TYPE_BRIDGE) + return &entry->bridge.ib2; +@@ -156,27 +156,38 @@ mtk_foe_entry_ib2(struct mtk_foe_entry * + return &entry->ipv4.ib2; + } + +-int mtk_foe_entry_prepare(struct mtk_foe_entry *entry, int type, int l4proto, +- u8 pse_port, u8 *src_mac, u8 *dest_mac) ++int mtk_foe_entry_prepare(struct mtk_eth *eth, struct mtk_foe_entry *entry, ++ int type, int l4proto, u8 pse_port, u8 *src_mac, ++ u8 *dest_mac) + { + struct mtk_foe_mac_info *l2; + u32 ports_pad, val; + + memset(entry, 0, sizeof(*entry)); + +- val = FIELD_PREP(MTK_FOE_IB1_STATE, MTK_FOE_STATE_BIND) | +- FIELD_PREP(MTK_FOE_IB1_PACKET_TYPE, type) | +- FIELD_PREP(MTK_FOE_IB1_UDP, l4proto == IPPROTO_UDP) | +- MTK_FOE_IB1_BIND_TTL | +- MTK_FOE_IB1_BIND_CACHE; +- entry->ib1 = val; +- +- val = FIELD_PREP(MTK_FOE_IB2_PORT_MG, 0x3f) | +- FIELD_PREP(MTK_FOE_IB2_PORT_AG, 0x1f) | +- FIELD_PREP(MTK_FOE_IB2_DEST_PORT, pse_port); ++ if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V2)) { ++ val = FIELD_PREP(MTK_FOE_IB1_STATE, MTK_FOE_STATE_BIND) | ++ FIELD_PREP(MTK_FOE_IB1_PACKET_TYPE_V2, type) | ++ FIELD_PREP(MTK_FOE_IB1_UDP, l4proto == IPPROTO_UDP) | ++ MTK_FOE_IB1_BIND_CACHE_V2 | MTK_FOE_IB1_BIND_TTL_V2; ++ entry->ib1 = val; ++ ++ val = FIELD_PREP(MTK_FOE_IB2_DEST_PORT_V2, pse_port) | ++ FIELD_PREP(MTK_FOE_IB2_PORT_AG_V2, 0xf); ++ } else { ++ val = FIELD_PREP(MTK_FOE_IB1_STATE, MTK_FOE_STATE_BIND) | ++ FIELD_PREP(MTK_FOE_IB1_PACKET_TYPE, type) | ++ FIELD_PREP(MTK_FOE_IB1_UDP, l4proto == IPPROTO_UDP) | ++ MTK_FOE_IB1_BIND_CACHE | MTK_FOE_IB1_BIND_TTL; ++ entry->ib1 = val; ++ ++ val = FIELD_PREP(MTK_FOE_IB2_DEST_PORT, pse_port) | ++ FIELD_PREP(MTK_FOE_IB2_PORT_MG, 0x3f) | ++ FIELD_PREP(MTK_FOE_IB2_PORT_AG, 0x1f); ++ } + + if (is_multicast_ether_addr(dest_mac)) +- val |= MTK_FOE_IB2_MULTICAST; ++ val |= mtk_get_ib2_multicast_mask(eth); + + ports_pad = 0xa5a5a500 | (l4proto & 0xff); + if (type == MTK_PPE_PKT_TYPE_IPV4_ROUTE) +@@ -210,24 +221,30 @@ int mtk_foe_entry_prepare(struct mtk_foe + return 0; + } + +-int mtk_foe_entry_set_pse_port(struct mtk_foe_entry *entry, u8 port) ++int mtk_foe_entry_set_pse_port(struct mtk_eth *eth, ++ struct mtk_foe_entry *entry, u8 port) + { +- u32 *ib2 = mtk_foe_entry_ib2(entry); +- u32 val; ++ u32 *ib2 = mtk_foe_entry_ib2(eth, entry); ++ u32 val = *ib2; + +- val = *ib2; +- val &= ~MTK_FOE_IB2_DEST_PORT; +- val |= FIELD_PREP(MTK_FOE_IB2_DEST_PORT, port); ++ if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V2)) { ++ val &= ~MTK_FOE_IB2_DEST_PORT_V2; ++ val |= FIELD_PREP(MTK_FOE_IB2_DEST_PORT_V2, port); ++ } else { ++ val &= ~MTK_FOE_IB2_DEST_PORT; ++ val |= FIELD_PREP(MTK_FOE_IB2_DEST_PORT, port); ++ } + *ib2 = val; + + return 0; + } + +-int mtk_foe_entry_set_ipv4_tuple(struct mtk_foe_entry *entry, bool egress, ++int mtk_foe_entry_set_ipv4_tuple(struct mtk_eth *eth, ++ struct mtk_foe_entry *entry, bool egress, + __be32 src_addr, __be16 src_port, + __be32 dest_addr, __be16 dest_port) + { +- int type = FIELD_GET(MTK_FOE_IB1_PACKET_TYPE, entry->ib1); ++ int type = mtk_get_ib1_pkt_type(eth, entry->ib1); + struct mtk_ipv4_tuple *t; + + switch (type) { +@@ -262,11 +279,12 @@ int mtk_foe_entry_set_ipv4_tuple(struct + return 0; + } + +-int mtk_foe_entry_set_ipv6_tuple(struct mtk_foe_entry *entry, ++int mtk_foe_entry_set_ipv6_tuple(struct mtk_eth *eth, ++ struct mtk_foe_entry *entry, + __be32 *src_addr, __be16 src_port, + __be32 *dest_addr, __be16 dest_port) + { +- int type = FIELD_GET(MTK_FOE_IB1_PACKET_TYPE, entry->ib1); ++ int type = mtk_get_ib1_pkt_type(eth, entry->ib1); + u32 *src, *dest; + int i; + +@@ -297,39 +315,41 @@ int mtk_foe_entry_set_ipv6_tuple(struct + return 0; + } + +-int mtk_foe_entry_set_dsa(struct mtk_foe_entry *entry, int port) ++int mtk_foe_entry_set_dsa(struct mtk_eth *eth, struct mtk_foe_entry *entry, ++ int port) + { +- struct mtk_foe_mac_info *l2 = mtk_foe_entry_l2(entry); ++ struct mtk_foe_mac_info *l2 = mtk_foe_entry_l2(eth, entry); + + l2->etype = BIT(port); + +- if (!(entry->ib1 & MTK_FOE_IB1_BIND_VLAN_LAYER)) +- entry->ib1 |= FIELD_PREP(MTK_FOE_IB1_BIND_VLAN_LAYER, 1); ++ if (!(entry->ib1 & mtk_get_ib1_vlan_layer_mask(eth))) ++ entry->ib1 |= mtk_prep_ib1_vlan_layer(eth, 1); + else + l2->etype |= BIT(8); + +- entry->ib1 &= ~MTK_FOE_IB1_BIND_VLAN_TAG; ++ entry->ib1 &= ~mtk_get_ib1_vlan_tag_mask(eth); + + return 0; + } + +-int mtk_foe_entry_set_vlan(struct mtk_foe_entry *entry, int vid) ++int mtk_foe_entry_set_vlan(struct mtk_eth *eth, struct mtk_foe_entry *entry, ++ int vid) + { +- struct mtk_foe_mac_info *l2 = mtk_foe_entry_l2(entry); ++ struct mtk_foe_mac_info *l2 = mtk_foe_entry_l2(eth, entry); + +- switch (FIELD_GET(MTK_FOE_IB1_BIND_VLAN_LAYER, entry->ib1)) { ++ switch (mtk_prep_ib1_vlan_layer(eth, entry->ib1)) { + case 0: +- entry->ib1 |= MTK_FOE_IB1_BIND_VLAN_TAG | +- FIELD_PREP(MTK_FOE_IB1_BIND_VLAN_LAYER, 1); ++ entry->ib1 |= mtk_get_ib1_vlan_tag_mask(eth) | ++ mtk_prep_ib1_vlan_layer(eth, 1); + l2->vlan1 = vid; + return 0; + case 1: +- if (!(entry->ib1 & MTK_FOE_IB1_BIND_VLAN_TAG)) { ++ if (!(entry->ib1 & mtk_get_ib1_vlan_tag_mask(eth))) { + l2->vlan1 = vid; + l2->etype |= BIT(8); + } else { + l2->vlan2 = vid; +- entry->ib1 += FIELD_PREP(MTK_FOE_IB1_BIND_VLAN_LAYER, 1); ++ entry->ib1 += mtk_prep_ib1_vlan_layer(eth, 1); + } + return 0; + default: +@@ -337,34 +357,42 @@ int mtk_foe_entry_set_vlan(struct mtk_fo + } + } + +-int mtk_foe_entry_set_pppoe(struct mtk_foe_entry *entry, int sid) ++int mtk_foe_entry_set_pppoe(struct mtk_eth *eth, struct mtk_foe_entry *entry, ++ int sid) + { +- struct mtk_foe_mac_info *l2 = mtk_foe_entry_l2(entry); ++ struct mtk_foe_mac_info *l2 = mtk_foe_entry_l2(eth, entry); + +- if (!(entry->ib1 & MTK_FOE_IB1_BIND_VLAN_LAYER) || +- (entry->ib1 & MTK_FOE_IB1_BIND_VLAN_TAG)) ++ if (!(entry->ib1 & mtk_get_ib1_vlan_layer_mask(eth)) || ++ (entry->ib1 & mtk_get_ib1_vlan_tag_mask(eth))) + l2->etype = ETH_P_PPP_SES; + +- entry->ib1 |= MTK_FOE_IB1_BIND_PPPOE; ++ entry->ib1 |= mtk_get_ib1_ppoe_mask(eth); + l2->pppoe_id = sid; + + return 0; + } + +-int mtk_foe_entry_set_wdma(struct mtk_foe_entry *entry, int wdma_idx, int txq, +- int bss, int wcid) ++int mtk_foe_entry_set_wdma(struct mtk_eth *eth, struct mtk_foe_entry *entry, ++ int wdma_idx, int txq, int bss, int wcid) + { +- struct mtk_foe_mac_info *l2 = mtk_foe_entry_l2(entry); +- u32 *ib2 = mtk_foe_entry_ib2(entry); ++ struct mtk_foe_mac_info *l2 = mtk_foe_entry_l2(eth, entry); ++ u32 *ib2 = mtk_foe_entry_ib2(eth, entry); + +- *ib2 &= ~MTK_FOE_IB2_PORT_MG; +- *ib2 |= MTK_FOE_IB2_WDMA_WINFO; +- if (wdma_idx) +- *ib2 |= MTK_FOE_IB2_WDMA_DEVIDX; +- +- l2->vlan2 = FIELD_PREP(MTK_FOE_VLAN2_WINFO_BSS, bss) | +- FIELD_PREP(MTK_FOE_VLAN2_WINFO_WCID, wcid) | +- FIELD_PREP(MTK_FOE_VLAN2_WINFO_RING, txq); ++ if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V2)) { ++ *ib2 &= ~MTK_FOE_IB2_PORT_MG_V2; ++ *ib2 |= FIELD_PREP(MTK_FOE_IB2_RX_IDX, txq) | ++ MTK_FOE_IB2_WDMA_WINFO_V2; ++ l2->winfo = FIELD_PREP(MTK_FOE_WINFO_WCID, wcid) | ++ FIELD_PREP(MTK_FOE_WINFO_BSS, bss); ++ } else { ++ *ib2 &= ~MTK_FOE_IB2_PORT_MG; ++ *ib2 |= MTK_FOE_IB2_WDMA_WINFO; ++ if (wdma_idx) ++ *ib2 |= MTK_FOE_IB2_WDMA_DEVIDX; ++ l2->vlan2 = FIELD_PREP(MTK_FOE_VLAN2_WINFO_BSS, bss) | ++ FIELD_PREP(MTK_FOE_VLAN2_WINFO_WCID, wcid) | ++ FIELD_PREP(MTK_FOE_VLAN2_WINFO_RING, txq); ++ } + + return 0; + } +@@ -376,14 +404,15 @@ static inline bool mtk_foe_entry_usable( + } + + static bool +-mtk_flow_entry_match(struct mtk_flow_entry *entry, struct mtk_foe_entry *data) ++mtk_flow_entry_match(struct mtk_eth *eth, struct mtk_flow_entry *entry, ++ struct mtk_foe_entry *data) + { + int type, len; + + if ((data->ib1 ^ entry->data.ib1) & MTK_FOE_IB1_UDP) + return false; + +- type = FIELD_GET(MTK_FOE_IB1_PACKET_TYPE, entry->data.ib1); ++ type = mtk_get_ib1_pkt_type(eth, entry->data.ib1); + if (type > MTK_PPE_PKT_TYPE_IPV4_DSLITE) + len = offsetof(struct mtk_foe_entry, ipv6._rsv); + else +@@ -427,14 +456,12 @@ __mtk_foe_entry_clear(struct mtk_ppe *pp + + static int __mtk_foe_entry_idle_time(struct mtk_ppe *ppe, u32 ib1) + { +- u16 timestamp; +- u16 now; +- +- now = mtk_eth_timestamp(ppe->eth) & MTK_FOE_IB1_BIND_TIMESTAMP; +- timestamp = ib1 & MTK_FOE_IB1_BIND_TIMESTAMP; ++ u32 ib1_ts_mask = mtk_get_ib1_ts_mask(ppe->eth); ++ u16 now = mtk_eth_timestamp(ppe->eth); ++ u16 timestamp = ib1 & ib1_ts_mask; + + if (timestamp > now) +- return MTK_FOE_IB1_BIND_TIMESTAMP + 1 - timestamp + now; ++ return ib1_ts_mask + 1 - timestamp + now; + else + return now - timestamp; + } +@@ -442,6 +469,7 @@ static int __mtk_foe_entry_idle_time(str + static void + mtk_flow_entry_update_l2(struct mtk_ppe *ppe, struct mtk_flow_entry *entry) + { ++ u32 ib1_ts_mask = mtk_get_ib1_ts_mask(ppe->eth); + struct mtk_flow_entry *cur; + struct mtk_foe_entry *hwe; + struct hlist_node *tmp; +@@ -466,8 +494,8 @@ mtk_flow_entry_update_l2(struct mtk_ppe + continue; + + idle = cur_idle; +- entry->data.ib1 &= ~MTK_FOE_IB1_BIND_TIMESTAMP; +- entry->data.ib1 |= hwe->ib1 & MTK_FOE_IB1_BIND_TIMESTAMP; ++ entry->data.ib1 &= ~ib1_ts_mask; ++ entry->data.ib1 |= hwe->ib1 & ib1_ts_mask; + } + } + +@@ -489,7 +517,7 @@ mtk_flow_entry_update(struct mtk_ppe *pp + + hwe = mtk_foe_get_entry(ppe, entry->hash); + memcpy(&foe, hwe, ppe->eth->soc->foe_entry_size); +- if (!mtk_flow_entry_match(entry, &foe)) { ++ if (!mtk_flow_entry_match(ppe->eth, entry, &foe)) { + entry->hash = 0xffff; + goto out; + } +@@ -504,16 +532,22 @@ static void + __mtk_foe_entry_commit(struct mtk_ppe *ppe, struct mtk_foe_entry *entry, + u16 hash) + { ++ struct mtk_eth *eth = ppe->eth; ++ u16 timestamp = mtk_eth_timestamp(eth); + struct mtk_foe_entry *hwe; +- u16 timestamp; + +- timestamp = mtk_eth_timestamp(ppe->eth); +- timestamp &= MTK_FOE_IB1_BIND_TIMESTAMP; +- entry->ib1 &= ~MTK_FOE_IB1_BIND_TIMESTAMP; +- entry->ib1 |= FIELD_PREP(MTK_FOE_IB1_BIND_TIMESTAMP, timestamp); ++ if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V2)) { ++ entry->ib1 &= ~MTK_FOE_IB1_BIND_TIMESTAMP_V2; ++ entry->ib1 |= FIELD_PREP(MTK_FOE_IB1_BIND_TIMESTAMP_V2, ++ timestamp); ++ } else { ++ entry->ib1 &= ~MTK_FOE_IB1_BIND_TIMESTAMP; ++ entry->ib1 |= FIELD_PREP(MTK_FOE_IB1_BIND_TIMESTAMP, ++ timestamp); ++ } + + hwe = mtk_foe_get_entry(ppe, hash); +- memcpy(&hwe->data, &entry->data, ppe->eth->soc->foe_entry_size); ++ memcpy(&hwe->data, &entry->data, eth->soc->foe_entry_size); + wmb(); + hwe->ib1 = entry->ib1; + +@@ -540,8 +574,8 @@ mtk_foe_entry_commit_l2(struct mtk_ppe * + + int mtk_foe_entry_commit(struct mtk_ppe *ppe, struct mtk_flow_entry *entry) + { +- int type = FIELD_GET(MTK_FOE_IB1_PACKET_TYPE, entry->data.ib1); + const struct mtk_soc_data *soc = ppe->eth->soc; ++ int type = mtk_get_ib1_pkt_type(ppe->eth, entry->data.ib1); + u32 hash; + + if (type == MTK_PPE_PKT_TYPE_BRIDGE) +@@ -564,7 +598,7 @@ mtk_foe_entry_commit_subflow(struct mtk_ + struct mtk_flow_entry *flow_info; + struct mtk_foe_entry foe = {}, *hwe; + struct mtk_foe_mac_info *l2; +- u32 ib1_mask = MTK_FOE_IB1_PACKET_TYPE | MTK_FOE_IB1_UDP; ++ u32 ib1_mask = mtk_get_ib1_pkt_type_mask(ppe->eth) | MTK_FOE_IB1_UDP; + int type; + + flow_info = kzalloc(offsetof(struct mtk_flow_entry, l2_data.end), +@@ -584,16 +618,16 @@ mtk_foe_entry_commit_subflow(struct mtk_ + foe.ib1 &= ib1_mask; + foe.ib1 |= entry->data.ib1 & ~ib1_mask; + +- l2 = mtk_foe_entry_l2(&foe); ++ l2 = mtk_foe_entry_l2(ppe->eth, &foe); + memcpy(l2, &entry->data.bridge.l2, sizeof(*l2)); + +- type = FIELD_GET(MTK_FOE_IB1_PACKET_TYPE, foe.ib1); ++ type = mtk_get_ib1_pkt_type(ppe->eth, foe.ib1); + if (type == MTK_PPE_PKT_TYPE_IPV4_HNAPT) + memcpy(&foe.ipv4.new, &foe.ipv4.orig, sizeof(foe.ipv4.new)); + else if (type >= MTK_PPE_PKT_TYPE_IPV6_ROUTE_3T && l2->etype == ETH_P_IP) + l2->etype = ETH_P_IPV6; + +- *mtk_foe_entry_ib2(&foe) = entry->data.bridge.ib2; ++ *mtk_foe_entry_ib2(ppe->eth, &foe) = entry->data.bridge.ib2; + + __mtk_foe_entry_commit(ppe, &foe, hash); + } +@@ -626,7 +660,7 @@ void __mtk_ppe_check_skb(struct mtk_ppe + continue; + } + +- if (found || !mtk_flow_entry_match(entry, hwe)) { ++ if (found || !mtk_flow_entry_match(ppe->eth, entry, hwe)) { + if (entry->hash != 0xffff) + entry->hash = 0xffff; + continue; +@@ -771,6 +805,8 @@ void mtk_ppe_start(struct mtk_ppe *ppe) + MTK_PPE_SCAN_MODE_KEEPALIVE_AGE) | + FIELD_PREP(MTK_PPE_TB_CFG_ENTRY_NUM, + MTK_PPE_ENTRIES_SHIFT); ++ if (MTK_HAS_CAPS(ppe->eth->soc->caps, MTK_NETSYS_V2)) ++ val |= MTK_PPE_TB_CFG_INFO_SEL; + ppe_w32(ppe, MTK_PPE_TB_CFG, val); + + ppe_w32(ppe, MTK_PPE_IP_PROTO_CHK, +@@ -778,15 +814,21 @@ void mtk_ppe_start(struct mtk_ppe *ppe) + + mtk_ppe_cache_enable(ppe, true); + +- val = MTK_PPE_FLOW_CFG_IP4_TCP_FRAG | +- MTK_PPE_FLOW_CFG_IP4_UDP_FRAG | +- MTK_PPE_FLOW_CFG_IP6_3T_ROUTE | ++ val = MTK_PPE_FLOW_CFG_IP6_3T_ROUTE | + MTK_PPE_FLOW_CFG_IP6_5T_ROUTE | + MTK_PPE_FLOW_CFG_IP6_6RD | + MTK_PPE_FLOW_CFG_IP4_NAT | + MTK_PPE_FLOW_CFG_IP4_NAPT | + MTK_PPE_FLOW_CFG_IP4_DSLITE | + MTK_PPE_FLOW_CFG_IP4_NAT_FRAG; ++ if (MTK_HAS_CAPS(ppe->eth->soc->caps, MTK_NETSYS_V2)) ++ val |= MTK_PPE_MD_TOAP_BYP_CRSN0 | ++ MTK_PPE_MD_TOAP_BYP_CRSN1 | ++ MTK_PPE_MD_TOAP_BYP_CRSN2 | ++ MTK_PPE_FLOW_CFG_IP4_HASH_GRE_KEY; ++ else ++ val |= MTK_PPE_FLOW_CFG_IP4_TCP_FRAG | ++ MTK_PPE_FLOW_CFG_IP4_UDP_FRAG; + ppe_w32(ppe, MTK_PPE_FLOW_CFG, val); + + val = FIELD_PREP(MTK_PPE_UNBIND_AGE_MIN_PACKETS, 1000) | +@@ -820,6 +862,11 @@ void mtk_ppe_start(struct mtk_ppe *ppe) + ppe_w32(ppe, MTK_PPE_GLO_CFG, val); + + ppe_w32(ppe, MTK_PPE_DEFAULT_CPU_PORT, 0); ++ ++ if (MTK_HAS_CAPS(ppe->eth->soc->caps, MTK_NETSYS_V2)) { ++ ppe_w32(ppe, MTK_PPE_DEFAULT_CPU_PORT1, 0xcb777); ++ ppe_w32(ppe, MTK_PPE_SBW_CTRL, 0x7f); ++ } + } + + int mtk_ppe_stop(struct mtk_ppe *ppe) +--- a/drivers/net/ethernet/mediatek/mtk_ppe.h ++++ b/drivers/net/ethernet/mediatek/mtk_ppe.h +@@ -32,6 +32,15 @@ + #define MTK_FOE_IB1_UDP BIT(30) + #define MTK_FOE_IB1_STATIC BIT(31) + ++/* CONFIG_MEDIATEK_NETSYS_V2 */ ++#define MTK_FOE_IB1_BIND_TIMESTAMP_V2 GENMASK(7, 0) ++#define MTK_FOE_IB1_BIND_VLAN_LAYER_V2 GENMASK(16, 14) ++#define MTK_FOE_IB1_BIND_PPPOE_V2 BIT(17) ++#define MTK_FOE_IB1_BIND_VLAN_TAG_V2 BIT(18) ++#define MTK_FOE_IB1_BIND_CACHE_V2 BIT(20) ++#define MTK_FOE_IB1_BIND_TTL_V2 BIT(22) ++#define MTK_FOE_IB1_PACKET_TYPE_V2 GENMASK(27, 23) ++ + enum { + MTK_PPE_PKT_TYPE_IPV4_HNAPT = 0, + MTK_PPE_PKT_TYPE_IPV4_ROUTE = 1, +@@ -53,14 +62,25 @@ enum { + + #define MTK_FOE_IB2_PORT_MG GENMASK(17, 12) + ++#define MTK_FOE_IB2_RX_IDX GENMASK(18, 17) + #define MTK_FOE_IB2_PORT_AG GENMASK(23, 18) + + #define MTK_FOE_IB2_DSCP GENMASK(31, 24) + ++/* CONFIG_MEDIATEK_NETSYS_V2 */ ++#define MTK_FOE_IB2_PORT_MG_V2 BIT(7) ++#define MTK_FOE_IB2_DEST_PORT_V2 GENMASK(12, 9) ++#define MTK_FOE_IB2_MULTICAST_V2 BIT(13) ++#define MTK_FOE_IB2_WDMA_WINFO_V2 BIT(19) ++#define MTK_FOE_IB2_PORT_AG_V2 GENMASK(23, 20) ++ + #define MTK_FOE_VLAN2_WINFO_BSS GENMASK(5, 0) + #define MTK_FOE_VLAN2_WINFO_WCID GENMASK(13, 6) + #define MTK_FOE_VLAN2_WINFO_RING GENMASK(15, 14) + ++#define MTK_FOE_WINFO_BSS GENMASK(5, 0) ++#define MTK_FOE_WINFO_WCID GENMASK(15, 6) ++ + enum { + MTK_FOE_STATE_INVALID, + MTK_FOE_STATE_UNBIND, +@@ -81,6 +101,9 @@ struct mtk_foe_mac_info { + + u16 pppoe_id; + u16 src_mac_lo; ++ ++ u16 minfo; ++ u16 winfo; + }; + + /* software-only entry type */ +@@ -198,7 +221,7 @@ struct mtk_foe_entry { + struct mtk_foe_ipv4_dslite dslite; + struct mtk_foe_ipv6 ipv6; + struct mtk_foe_ipv6_6rd ipv6_6rd; +- u32 data[19]; ++ u32 data[23]; + }; + }; + +@@ -306,20 +329,27 @@ mtk_ppe_check_skb(struct mtk_ppe *ppe, s + __mtk_ppe_check_skb(ppe, skb, hash); + } + +-int mtk_foe_entry_prepare(struct mtk_foe_entry *entry, int type, int l4proto, +- u8 pse_port, u8 *src_mac, u8 *dest_mac); +-int mtk_foe_entry_set_pse_port(struct mtk_foe_entry *entry, u8 port); +-int mtk_foe_entry_set_ipv4_tuple(struct mtk_foe_entry *entry, bool orig, ++int mtk_foe_entry_prepare(struct mtk_eth *eth, struct mtk_foe_entry *entry, ++ int type, int l4proto, u8 pse_port, u8 *src_mac, ++ u8 *dest_mac); ++int mtk_foe_entry_set_pse_port(struct mtk_eth *eth, ++ struct mtk_foe_entry *entry, u8 port); ++int mtk_foe_entry_set_ipv4_tuple(struct mtk_eth *eth, ++ struct mtk_foe_entry *entry, bool orig, + __be32 src_addr, __be16 src_port, + __be32 dest_addr, __be16 dest_port); +-int mtk_foe_entry_set_ipv6_tuple(struct mtk_foe_entry *entry, ++int mtk_foe_entry_set_ipv6_tuple(struct mtk_eth *eth, ++ struct mtk_foe_entry *entry, + __be32 *src_addr, __be16 src_port, + __be32 *dest_addr, __be16 dest_port); +-int mtk_foe_entry_set_dsa(struct mtk_foe_entry *entry, int port); +-int mtk_foe_entry_set_vlan(struct mtk_foe_entry *entry, int vid); +-int mtk_foe_entry_set_pppoe(struct mtk_foe_entry *entry, int sid); +-int mtk_foe_entry_set_wdma(struct mtk_foe_entry *entry, int wdma_idx, int txq, +- int bss, int wcid); ++int mtk_foe_entry_set_dsa(struct mtk_eth *eth, struct mtk_foe_entry *entry, ++ int port); ++int mtk_foe_entry_set_vlan(struct mtk_eth *eth, struct mtk_foe_entry *entry, ++ int vid); ++int mtk_foe_entry_set_pppoe(struct mtk_eth *eth, struct mtk_foe_entry *entry, ++ int sid); ++int mtk_foe_entry_set_wdma(struct mtk_eth *eth, struct mtk_foe_entry *entry, ++ int wdma_idx, int txq, int bss, int wcid); + int mtk_foe_entry_commit(struct mtk_ppe *ppe, struct mtk_flow_entry *entry); + void mtk_foe_entry_clear(struct mtk_ppe *ppe, struct mtk_flow_entry *entry); + int mtk_foe_entry_idle_time(struct mtk_ppe *ppe, struct mtk_flow_entry *entry); +--- a/drivers/net/ethernet/mediatek/mtk_ppe_offload.c ++++ b/drivers/net/ethernet/mediatek/mtk_ppe_offload.c +@@ -52,18 +52,19 @@ static const struct rhashtable_params mt + }; + + static int +-mtk_flow_set_ipv4_addr(struct mtk_foe_entry *foe, struct mtk_flow_data *data, +- bool egress) ++mtk_flow_set_ipv4_addr(struct mtk_eth *eth, struct mtk_foe_entry *foe, ++ struct mtk_flow_data *data, bool egress) + { +- return mtk_foe_entry_set_ipv4_tuple(foe, egress, ++ return mtk_foe_entry_set_ipv4_tuple(eth, foe, egress, + data->v4.src_addr, data->src_port, + data->v4.dst_addr, data->dst_port); + } + + static int +-mtk_flow_set_ipv6_addr(struct mtk_foe_entry *foe, struct mtk_flow_data *data) ++mtk_flow_set_ipv6_addr(struct mtk_eth *eth, struct mtk_foe_entry *foe, ++ struct mtk_flow_data *data) + { +- return mtk_foe_entry_set_ipv6_tuple(foe, ++ return mtk_foe_entry_set_ipv6_tuple(eth, foe, + data->v6.src_addr.s6_addr32, data->src_port, + data->v6.dst_addr.s6_addr32, data->dst_port); + } +@@ -190,16 +191,29 @@ mtk_flow_set_output_device(struct mtk_et + int pse_port, dsa_port; + + if (mtk_flow_get_wdma_info(dev, dest_mac, &info) == 0) { +- mtk_foe_entry_set_wdma(foe, info.wdma_idx, info.queue, info.bss, +- info.wcid); +- pse_port = 3; ++ mtk_foe_entry_set_wdma(eth, foe, info.wdma_idx, info.queue, ++ info.bss, info.wcid); ++ if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V2)) { ++ switch (info.wdma_idx) { ++ case 0: ++ pse_port = 8; ++ break; ++ case 1: ++ pse_port = 9; ++ break; ++ default: ++ return -EINVAL; ++ } ++ } else { ++ pse_port = 3; ++ } + *wed_index = info.wdma_idx; + goto out; + } + + dsa_port = mtk_flow_get_dsa_port(&dev); + if (dsa_port >= 0) +- mtk_foe_entry_set_dsa(foe, dsa_port); ++ mtk_foe_entry_set_dsa(eth, foe, dsa_port); + + if (dev == eth->netdev[0]) + pse_port = 1; +@@ -209,7 +223,7 @@ mtk_flow_set_output_device(struct mtk_et + return -EOPNOTSUPP; + + out: +- mtk_foe_entry_set_pse_port(foe, pse_port); ++ mtk_foe_entry_set_pse_port(eth, foe, pse_port); + + return 0; + } +@@ -333,9 +347,8 @@ mtk_flow_offload_replace(struct mtk_eth + !is_valid_ether_addr(data.eth.h_dest)) + return -EINVAL; + +- err = mtk_foe_entry_prepare(&foe, offload_type, l4proto, 0, +- data.eth.h_source, +- data.eth.h_dest); ++ err = mtk_foe_entry_prepare(eth, &foe, offload_type, l4proto, 0, ++ data.eth.h_source, data.eth.h_dest); + if (err) + return err; + +@@ -360,7 +373,7 @@ mtk_flow_offload_replace(struct mtk_eth + data.v4.src_addr = addrs.key->src; + data.v4.dst_addr = addrs.key->dst; + +- mtk_flow_set_ipv4_addr(&foe, &data, false); ++ mtk_flow_set_ipv4_addr(eth, &foe, &data, false); + } + + if (addr_type == FLOW_DISSECTOR_KEY_IPV6_ADDRS) { +@@ -371,7 +384,7 @@ mtk_flow_offload_replace(struct mtk_eth + data.v6.src_addr = addrs.key->src; + data.v6.dst_addr = addrs.key->dst; + +- mtk_flow_set_ipv6_addr(&foe, &data); ++ mtk_flow_set_ipv6_addr(eth, &foe, &data); + } + + flow_action_for_each(i, act, &rule->action) { +@@ -401,7 +414,7 @@ mtk_flow_offload_replace(struct mtk_eth + } + + if (addr_type == FLOW_DISSECTOR_KEY_IPV4_ADDRS) { +- err = mtk_flow_set_ipv4_addr(&foe, &data, true); ++ err = mtk_flow_set_ipv4_addr(eth, &foe, &data, true); + if (err) + return err; + } +@@ -413,10 +426,10 @@ mtk_flow_offload_replace(struct mtk_eth + if (data.vlan.proto != htons(ETH_P_8021Q)) + return -EOPNOTSUPP; + +- mtk_foe_entry_set_vlan(&foe, data.vlan.id); ++ mtk_foe_entry_set_vlan(eth, &foe, data.vlan.id); + } + if (data.pppoe.num == 1) +- mtk_foe_entry_set_pppoe(&foe, data.pppoe.sid); ++ mtk_foe_entry_set_pppoe(eth, &foe, data.pppoe.sid); + + err = mtk_flow_set_output_device(eth, &foe, odev, data.eth.h_dest, + &wed_index); +--- a/drivers/net/ethernet/mediatek/mtk_ppe_regs.h ++++ b/drivers/net/ethernet/mediatek/mtk_ppe_regs.h +@@ -21,6 +21,9 @@ + #define MTK_PPE_GLO_CFG_BUSY BIT(31) + + #define MTK_PPE_FLOW_CFG 0x204 ++#define MTK_PPE_MD_TOAP_BYP_CRSN0 BIT(1) ++#define MTK_PPE_MD_TOAP_BYP_CRSN1 BIT(2) ++#define MTK_PPE_MD_TOAP_BYP_CRSN2 BIT(3) + #define MTK_PPE_FLOW_CFG_IP4_TCP_FRAG BIT(6) + #define MTK_PPE_FLOW_CFG_IP4_UDP_FRAG BIT(7) + #define MTK_PPE_FLOW_CFG_IP6_3T_ROUTE BIT(8) +@@ -54,6 +57,7 @@ + #define MTK_PPE_TB_CFG_HASH_MODE GENMASK(15, 14) + #define MTK_PPE_TB_CFG_SCAN_MODE GENMASK(17, 16) + #define MTK_PPE_TB_CFG_HASH_DEBUG GENMASK(19, 18) ++#define MTK_PPE_TB_CFG_INFO_SEL BIT(20) + + enum { + MTK_PPE_SCAN_MODE_DISABLED, +@@ -112,6 +116,8 @@ enum { + #define MTK_PPE_DEFAULT_CPU_PORT 0x248 + #define MTK_PPE_DEFAULT_CPU_PORT_MASK(_n) (GENMASK(2, 0) << ((_n) * 4)) + ++#define MTK_PPE_DEFAULT_CPU_PORT1 0x24c ++ + #define MTK_PPE_MTU_DROP 0x308 + + #define MTK_PPE_VLAN_MTU0 0x30c +@@ -141,4 +147,6 @@ enum { + #define MTK_PPE_MIB_CACHE_CTL_EN BIT(0) + #define MTK_PPE_MIB_CACHE_CTL_FLUSH BIT(2) + ++#define MTK_PPE_SBW_CTRL 0x374 ++ + #endif diff --git a/target/linux/generic/backport-6.1/724-v6.0-net-ethernet-mtk_eth_soc-enable-flow-offloading-supp.patch b/target/linux/generic/backport-6.1/724-v6.0-net-ethernet-mtk_eth_soc-enable-flow-offloading-supp.patch new file mode 100644 index 0000000000..037e9d4443 --- /dev/null +++ b/target/linux/generic/backport-6.1/724-v6.0-net-ethernet-mtk_eth_soc-enable-flow-offloading-supp.patch @@ -0,0 +1,26 @@ +From b94b02a270471337bef73c44fa3493a521e31a61 Mon Sep 17 00:00:00 2001 +Message-Id: +In-Reply-To: +References: +From: Lorenzo Bianconi +Date: Mon, 5 Sep 2022 13:56:13 +0200 +Subject: [PATCH net-next 5/5] net: ethernet: mtk_eth_soc: enable flow + offloading support for mt7986 + +Enable hw packet engine and wireless packet dispatcher for mt7986 + +Signed-off-by: Lorenzo Bianconi +--- + drivers/net/ethernet/mediatek/mtk_eth_soc.c | 1 + + 1 file changed, 1 insertion(+) + +--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c ++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c +@@ -4256,6 +4256,7 @@ static const struct mtk_soc_data mt7986_ + .hw_features = MTK_HW_FEATURES, + .required_clks = MT7986_CLKS_BITMAP, + .required_pctl = false, ++ .offload_version = 2, + .hash_offset = 4, + .foe_entry_size = sizeof(struct mtk_foe_entry), + .txrx = { diff --git a/target/linux/generic/backport-6.1/724-v6.0-net-ethernet-mtk_eth_soc-fix-wrong-use-of-new-helper.patch b/target/linux/generic/backport-6.1/724-v6.0-net-ethernet-mtk_eth_soc-fix-wrong-use-of-new-helper.patch new file mode 100644 index 0000000000..c4bd29365c --- /dev/null +++ b/target/linux/generic/backport-6.1/724-v6.0-net-ethernet-mtk_eth_soc-fix-wrong-use-of-new-helper.patch @@ -0,0 +1,46 @@ +From 40350ce3ae8701146aafd79c5f7b5582d9955e58 Mon Sep 17 00:00:00 2001 +From: Daniel Golle +Date: Sun, 25 Sep 2022 15:12:35 +0100 +Subject: [PATCH 1/2] net: ethernet: mtk_eth_soc: fix wrong use of new helper + function +To: linux-mediatek@lists.infradead.org, + netdev@vger.kernel.org, + Lorenzo Bianconi +Cc: Sujuan Chen , + Bo Jiao , + Felix Fietkau , + John Crispin , + Sean Wang , + Mark Lee , + David S. Miller , + Eric Dumazet , + Jakub Kicinski , + Paolo Abeni , + Matthias Brugger , + Chen Minqiang + +In function mtk_foe_entry_set_vlan() the call to field accessor macro +FIELD_GET(MTK_FOE_IB1_BIND_VLAN_LAYER, entry->ib1) +has been wrongly replaced by +mtk_prep_ib1_vlan_layer(eth, entry->ib1) + +Use correct helper function mtk_get_ib1_vlan_layer instead. + +Reported-by: Chen Minqiang +Fixes: 03a3180e5c09e1 ("net: ethernet: mtk_eth_soc: introduce flow offloading support for mt7986") +Signed-off-by: Daniel Golle +--- + drivers/net/ethernet/mediatek/mtk_ppe.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/drivers/net/ethernet/mediatek/mtk_ppe.c ++++ b/drivers/net/ethernet/mediatek/mtk_ppe.c +@@ -337,7 +337,7 @@ int mtk_foe_entry_set_vlan(struct mtk_et + { + struct mtk_foe_mac_info *l2 = mtk_foe_entry_l2(eth, entry); + +- switch (mtk_prep_ib1_vlan_layer(eth, entry->ib1)) { ++ switch (mtk_get_ib1_vlan_layer(eth, entry->ib1)) { + case 0: + entry->ib1 |= mtk_get_ib1_vlan_tag_mask(eth) | + mtk_prep_ib1_vlan_layer(eth, 1); diff --git a/target/linux/generic/backport-6.1/725-v6.0-net-ethernet-mtk_eth_soc-fix-usage-of-foe_entry_size.patch b/target/linux/generic/backport-6.1/725-v6.0-net-ethernet-mtk_eth_soc-fix-usage-of-foe_entry_size.patch new file mode 100644 index 0000000000..bb02f401a2 --- /dev/null +++ b/target/linux/generic/backport-6.1/725-v6.0-net-ethernet-mtk_eth_soc-fix-usage-of-foe_entry_size.patch @@ -0,0 +1,49 @@ +From fcf14c2c5deae8f8c3d25530bab10856f63f8a63 Mon Sep 17 00:00:00 2001 +From: Daniel Golle +Date: Sun, 25 Sep 2022 15:18:54 +0100 +Subject: [PATCH 2/2] net: ethernet: mtk_eth_soc: fix usage of foe_entry_size +To: linux-mediatek@lists.infradead.org, + netdev@vger.kernel.org, + Lorenzo Bianconi +Cc: Sujuan Chen , + Bo Jiao , + Felix Fietkau , + John Crispin , + Sean Wang , + Mark Lee , + David S. Miller , + Eric Dumazet , + Jakub Kicinski , + Paolo Abeni , + Matthias Brugger , + Chen Minqiang + +As sizeof(hwe->data) can now longer be used as the actual size depends +on foe_entry_size, in commit 9d8cb4c096ab02 +("net: ethernet: mtk_eth_soc: add foe_entry_size to mtk_eth_soc") the +use of sizeof(hwe->data) is hence replaced. +However, replacing it with ppe->eth->soc->foe_entry_size is wrong as +foe_entry_size represents the size of the whole descriptor and not just +the 'data' field. +Fix this by subtracing the size of the only other field in the struct +'ib1', so we actually end up with the correct size to be copied to the +data field. + +Reported-by: Chen Minqiang +Fixes: 9d8cb4c096ab02 ("net: ethernet: mtk_eth_soc: add foe_entry_size to mtk_eth_soc") +Signed-off-by: Daniel Golle +--- + drivers/net/ethernet/mediatek/mtk_ppe.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/drivers/net/ethernet/mediatek/mtk_ppe.c ++++ b/drivers/net/ethernet/mediatek/mtk_ppe.c +@@ -547,7 +547,7 @@ __mtk_foe_entry_commit(struct mtk_ppe *p + } + + hwe = mtk_foe_get_entry(ppe, hash); +- memcpy(&hwe->data, &entry->data, eth->soc->foe_entry_size); ++ memcpy(&hwe->data, &entry->data, eth->soc->foe_entry_size - sizeof(hwe->ib1)); + wmb(); + hwe->ib1 = entry->ib1; + diff --git a/target/linux/generic/backport-6.1/734-v5.16-0001-net-bgmac-improve-handling-PHY.patch b/target/linux/generic/backport-6.1/734-v5.16-0001-net-bgmac-improve-handling-PHY.patch new file mode 100644 index 0000000000..6788a2ec35 --- /dev/null +++ b/target/linux/generic/backport-6.1/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?= +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 +Signed-off-by: David S. Miller +--- + 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 + #include + #include ++#include + #include + #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/target/linux/generic/backport-6.1/734-v5.16-0002-net-bgmac-support-MDIO-described-in-DT.patch b/target/linux/generic/backport-6.1/734-v5.16-0002-net-bgmac-support-MDIO-described-in-DT.patch new file mode 100644 index 0000000000..f134828273 --- /dev/null +++ b/target/linux/generic/backport-6.1/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?= +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 +Signed-off-by: David S. Miller +--- + 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 + #include ++#include + #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/target/linux/generic/backport-6.1/742-v5.16-net-phy-at803x-add-support-for-qca-8327-internal-phy.patch b/target/linux/generic/backport-6.1/742-v5.16-net-phy-at803x-add-support-for-qca-8327-internal-phy.patch new file mode 100644 index 0000000000..8f000ba918 --- /dev/null +++ b/target/linux/generic/backport-6.1/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 +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 +Tested-by: Rosen Penev +Tested-by: Andrew Lunn +Signed-off-by: David S. Miller +--- + 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/target/linux/generic/backport-6.1/743-v5.16-0001-net-dsa-b53-Include-all-ports-in-enabled_ports.patch b/target/linux/generic/backport-6.1/743-v5.16-0001-net-dsa-b53-Include-all-ports-in-enabled_ports.patch new file mode 100644 index 0000000000..dc149a742b --- /dev/null +++ b/target/linux/generic/backport-6.1/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?= +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 +Reviewed-by: Florian Fainelli +Tested-by: Florian Fainelli +Signed-off-by: Jakub Kicinski +--- + 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 +@@ -2300,7 +2300,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, +@@ -2311,7 +2311,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, +@@ -2322,7 +2322,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, +@@ -2336,7 +2336,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, +@@ -2350,7 +2350,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, +@@ -2364,7 +2364,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, +@@ -2378,7 +2378,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, +@@ -2392,7 +2392,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, +@@ -2434,7 +2434,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, +@@ -2476,7 +2476,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, +@@ -2490,7 +2490,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, +@@ -2632,7 +2632,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/target/linux/generic/backport-6.1/743-v5.16-0002-net-dsa-b53-Drop-BCM5301x-workaround-for-a-wrong-CPU.patch b/target/linux/generic/backport-6.1/743-v5.16-0002-net-dsa-b53-Drop-BCM5301x-workaround-for-a-wrong-CPU.patch new file mode 100644 index 0000000000..23805a9027 --- /dev/null +++ b/target/linux/generic/backport-6.1/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?= +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 +Reviewed-by: Florian Fainelli +Tested-by: Florian Fainelli +Signed-off-by: Jakub Kicinski +--- + 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/target/linux/generic/backport-6.1/743-v5.16-0003-net-dsa-b53-Improve-flow-control-setup-on-BCM5301x.patch b/target/linux/generic/backport-6.1/743-v5.16-0003-net-dsa-b53-Improve-flow-control-setup-on-BCM5301x.patch new file mode 100644 index 0000000000..941fa23eb4 --- /dev/null +++ b/target/linux/generic/backport-6.1/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?= +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 +Reviewed-by: Florian Fainelli +Tested-by: Florian Fainelli +Signed-off-by: Jakub Kicinski +--- + 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/target/linux/generic/backport-6.1/743-v5.16-0004-net-dsa-b53-Drop-unused-cpu_port-field.patch b/target/linux/generic/backport-6.1/743-v5.16-0004-net-dsa-b53-Drop-unused-cpu_port-field.patch new file mode 100644 index 0000000000..07d0ec03cf --- /dev/null +++ b/target/linux/generic/backport-6.1/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?= +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 +Reviewed-by: Florian Fainelli +Tested-by: Florian Fainelli +Signed-off-by: Jakub Kicinski +--- + 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 +@@ -2298,7 +2298,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, + }, + { +@@ -2309,7 +2308,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, + }, + { +@@ -2320,7 +2318,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, +@@ -2334,7 +2331,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, +@@ -2348,7 +2344,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, +@@ -2362,7 +2357,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, +@@ -2377,7 +2371,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, +@@ -2390,7 +2383,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, +@@ -2404,7 +2396,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, +@@ -2418,7 +2409,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, +@@ -2432,7 +2422,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, +@@ -2446,7 +2435,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, +@@ -2460,7 +2448,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, +@@ -2474,7 +2461,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, +@@ -2488,7 +2474,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, +@@ -2502,7 +2487,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, +@@ -2516,7 +2500,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, +@@ -2545,7 +2528,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, +@@ -2559,7 +2541,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, +@@ -2585,7 +2566,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; +@@ -2617,13 +2597,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/target/linux/generic/backport-6.1/745-v5.16-01-net-phy-at803x-add-support-for-qca-8327-A-variant.patch b/target/linux/generic/backport-6.1/745-v5.16-01-net-phy-at803x-add-support-for-qca-8327-A-variant.patch new file mode 100644 index 0000000000..99d91dfa76 --- /dev/null +++ b/target/linux/generic/backport-6.1/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 +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 +Reviewed-by: Andrew Lunn +Signed-off-by: David S. Miller +--- + 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/target/linux/generic/backport-6.1/745-v5.16-02-net-phy-at803x-add-resume-suspend-function-to-qca83x.patch b/target/linux/generic/backport-6.1/745-v5.16-02-net-phy-at803x-add-resume-suspend-function-to-qca83x.patch new file mode 100644 index 0000000000..cd83fac83c --- /dev/null +++ b/target/linux/generic/backport-6.1/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 +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 +Reviewed-by: Andrew Lunn +Signed-off-by: David S. Miller +--- + 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/target/linux/generic/backport-6.1/745-v5.16-03-net-phy-at803x-fix-spacing-and-improve-name-for-83xx.patch b/target/linux/generic/backport-6.1/745-v5.16-03-net-phy-at803x-fix-spacing-and-improve-name-for-83xx.patch new file mode 100644 index 0000000000..586d8953b2 --- /dev/null +++ b/target/linux/generic/backport-6.1/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 +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 +Reviewed-by: Andrew Lunn +Signed-off-by: David S. Miller +--- + 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/target/linux/generic/backport-6.1/746-v5.16-01-net-phy-at803x-fix-resume-for-QCA8327-phy.patch b/target/linux/generic/backport-6.1/746-v5.16-01-net-phy-at803x-fix-resume-for-QCA8327-phy.patch new file mode 100644 index 0000000000..09797ae83b --- /dev/null +++ b/target/linux/generic/backport-6.1/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 +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 +Signed-off-by: David S. Miller +--- + 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/target/linux/generic/backport-6.1/746-v5.16-02-net-phy-at803x-add-DAC-amplitude-fix-for-8327-phy.patch b/target/linux/generic/backport-6.1/746-v5.16-02-net-phy-at803x-add-DAC-amplitude-fix-for-8327-phy.patch new file mode 100644 index 0000000000..c504c37c84 --- /dev/null +++ b/target/linux/generic/backport-6.1/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 +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 +Signed-off-by: David S. Miller +--- + 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/target/linux/generic/backport-6.1/746-v5.16-03-net-phy-at803x-enable-prefer-master-for-83xx-interna.patch b/target/linux/generic/backport-6.1/746-v5.16-03-net-phy-at803x-enable-prefer-master-for-83xx-interna.patch new file mode 100644 index 0000000000..9f880593f1 --- /dev/null +++ b/target/linux/generic/backport-6.1/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 +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 +Reviewed-by: Andrew Lunn +Signed-off-by: David S. Miller +--- + 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/target/linux/generic/backport-6.1/746-v5.16-04-net-phy-at803x-better-describe-debug-regs.patch b/target/linux/generic/backport-6.1/746-v5.16-04-net-phy-at803x-better-describe-debug-regs.patch new file mode 100644 index 0000000000..89e9b3f662 --- /dev/null +++ b/target/linux/generic/backport-6.1/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 +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 +Reviewed-by: Andrew Lunn +Signed-off-by: David S. Miller +--- + 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/target/linux/generic/backport-6.1/747-v5.16-01-dsa-qca8k-add-mac-power-sel-support.patch b/target/linux/generic/backport-6.1/747-v5.16-01-dsa-qca8k-add-mac-power-sel-support.patch new file mode 100644 index 0000000000..c8d424de38 --- /dev/null +++ b/target/linux/generic/backport-6.1/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 +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 +Reviewed-by: Vladimir Oltean +Reviewed-by: Florian Fainelli +Signed-off-by: David S. Miller +--- + 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/target/linux/generic/backport-6.1/747-v5.16-02-dt-bindings-net-dsa-qca8k-Add-SGMII-clock-phase-prop.patch b/target/linux/generic/backport-6.1/747-v5.16-02-dt-bindings-net-dsa-qca8k-Add-SGMII-clock-phase-prop.patch new file mode 100644 index 0000000000..bd768ec27d --- /dev/null +++ b/target/linux/generic/backport-6.1/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 +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 +Signed-off-by: Matthew Hagan +Signed-off-by: Ansuel Smith +Signed-off-by: David S. Miller +--- + 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/target/linux/generic/backport-6.1/747-v5.16-03-net-dsa-qca8k-add-support-for-sgmii-falling-edge.patch b/target/linux/generic/backport-6.1/747-v5.16-03-net-dsa-qca8k-add-support-for-sgmii-falling-edge.patch new file mode 100644 index 0000000000..e464452d82 --- /dev/null +++ b/target/linux/generic/backport-6.1/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 +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 +Signed-off-by: Matthew Hagan +Signed-off-by: Ansuel Smith +Signed-off-by: David S. Miller +--- + 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/target/linux/generic/backport-6.1/747-v5.16-04-dt-bindings-net-dsa-qca8k-Document-support-for-CPU-p.patch b/target/linux/generic/backport-6.1/747-v5.16-04-dt-bindings-net-dsa-qca8k-Document-support-for-CPU-p.patch new file mode 100644 index 0000000000..606ac0af3d --- /dev/null +++ b/target/linux/generic/backport-6.1/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 +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 +Signed-off-by: David S. Miller +--- + 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/target/linux/generic/backport-6.1/747-v5.16-05-net-dsa-qca8k-add-support-for-cpu-port-6.patch b/target/linux/generic/backport-6.1/747-v5.16-05-net-dsa-qca8k-add-support-for-cpu-port-6.patch new file mode 100644 index 0000000000..320db8fa9f --- /dev/null +++ b/target/linux/generic/backport-6.1/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 +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 +Signed-off-by: David S. Miller +--- + 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/target/linux/generic/backport-6.1/747-v5.16-06-net-dsa-qca8k-rework-rgmii-delay-logic-and-scan-for-.patch b/target/linux/generic/backport-6.1/747-v5.16-06-net-dsa-qca8k-rework-rgmii-delay-logic-and-scan-for-.patch new file mode 100644 index 0000000000..de201764f9 --- /dev/null +++ b/target/linux/generic/backport-6.1/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 +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 +Signed-off-by: David S. Miller +--- + 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 + + #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/target/linux/generic/backport-6.1/747-v5.16-07-dt-bindings-net-dsa-qca8k-Document-qca-sgmii-enable-.patch b/target/linux/generic/backport-6.1/747-v5.16-07-dt-bindings-net-dsa-qca8k-Document-qca-sgmii-enable-.patch new file mode 100644 index 0000000000..8abd264e79 --- /dev/null +++ b/target/linux/generic/backport-6.1/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 +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 +Signed-off-by: David S. Miller +--- + 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/target/linux/generic/backport-6.1/747-v5.16-08-net-dsa-qca8k-add-explicit-SGMII-PLL-enable.patch b/target/linux/generic/backport-6.1/747-v5.16-08-net-dsa-qca8k-add-explicit-SGMII-PLL-enable.patch new file mode 100644 index 0000000000..2b5a84a1b0 --- /dev/null +++ b/target/linux/generic/backport-6.1/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 +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 +Reviewed-by: Florian Fainelli +Signed-off-by: David S. Miller +--- + 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/target/linux/generic/backport-6.1/747-v5.16-09-dt-bindings-net-dsa-qca8k-Document-qca-led-open-drai.patch b/target/linux/generic/backport-6.1/747-v5.16-09-dt-bindings-net-dsa-qca8k-Document-qca-led-open-drai.patch new file mode 100644 index 0000000000..38dc954e8c --- /dev/null +++ b/target/linux/generic/backport-6.1/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 +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 +Signed-off-by: David S. Miller +--- + 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/target/linux/generic/backport-6.1/747-v5.16-10-net-dsa-qca8k-add-support-for-pws-config-reg.patch b/target/linux/generic/backport-6.1/747-v5.16-10-net-dsa-qca8k-add-support-for-pws-config-reg.patch new file mode 100644 index 0000000000..aa5d92a4fd --- /dev/null +++ b/target/linux/generic/backport-6.1/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 +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 +Reviewed-by: Florian Fainelli +Signed-off-by: David S. Miller +--- + 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/target/linux/generic/backport-6.1/747-v5.16-11-dt-bindings-net-dsa-qca8k-document-support-for-qca83.patch b/target/linux/generic/backport-6.1/747-v5.16-11-dt-bindings-net-dsa-qca8k-document-support-for-qca83.patch new file mode 100644 index 0000000000..1bfb00c5b2 --- /dev/null +++ b/target/linux/generic/backport-6.1/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 +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 +Reviewed-by: Florian Fainelli +Signed-off-by: David S. Miller +--- + 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/target/linux/generic/backport-6.1/747-v5.16-12-net-dsa-qca8k-add-support-for-QCA8328.patch b/target/linux/generic/backport-6.1/747-v5.16-12-net-dsa-qca8k-add-support-for-QCA8328.patch new file mode 100644 index 0000000000..b300621e63 --- /dev/null +++ b/target/linux/generic/backport-6.1/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 +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 +Reviewed-by: Florian Fainelli +Signed-off-by: David S. Miller +--- + 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; + } +@@ -2124,7 +2131,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, + }; + +@@ -2133,7 +2145,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/target/linux/generic/backport-6.1/747-v5.16-13-net-dsa-qca8k-set-internal-delay-also-for-sgmii.patch b/target/linux/generic/backport-6.1/747-v5.16-13-net-dsa-qca8k-set-internal-delay-also-for-sgmii.patch new file mode 100644 index 0000000000..27f94dca02 --- /dev/null +++ b/target/linux/generic/backport-6.1/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 +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 +Signed-off-by: David S. Miller +--- + 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/target/linux/generic/backport-6.1/747-v5.16-14-net-dsa-qca8k-move-port-config-to-dedicated-struct.patch b/target/linux/generic/backport-6.1/747-v5.16-14-net-dsa-qca8k-move-port-config-to-dedicated-struct.patch new file mode 100644 index 0000000000..b991798c87 --- /dev/null +++ b/target/linux/generic/backport-6.1/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 +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 +Reviewed-by: Florian Fainelli +Signed-off-by: David S. Miller +--- + 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/target/linux/generic/backport-6.1/747-v5.16-15-dt-bindings-net-ipq8064-mdio-fix-warning-with-new-qc.patch b/target/linux/generic/backport-6.1/747-v5.16-15-dt-bindings-net-ipq8064-mdio-fix-warning-with-new-qc.patch new file mode 100644 index 0000000000..f7cb514176 --- /dev/null +++ b/target/linux/generic/backport-6.1/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 +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 +Signed-off-by: David S. Miller +--- + 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/target/linux/generic/backport-6.1/747-v5.16-16-dt-bindings-net-dsa-qca8k-convert-to-YAML-schema.patch b/target/linux/generic/backport-6.1/747-v5.16-16-dt-bindings-net-dsa-qca8k-convert-to-YAML-schema.patch new file mode 100644 index 0000000000..b9bce97dd3 --- /dev/null +++ b/target/linux/generic/backport-6.1/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 +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 +Co-developed-by: Ansuel Smith +Signed-off-by: Ansuel Smith +Signed-off-by: David S. Miller +--- + .../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 ++ ++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 ++ ++ 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 ++ ++ 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/target/linux/generic/backport-6.1/748-v5.16-net-dsa-qca8k-fix-delay-applied-to-wrong-cpu-in-parse-p.patch b/target/linux/generic/backport-6.1/748-v5.16-net-dsa-qca8k-fix-delay-applied-to-wrong-cpu-in-parse-p.patch new file mode 100644 index 0000000000..a510cfdc18 --- /dev/null +++ b/target/linux/generic/backport-6.1/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 +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 +Signed-off-by: David S. Miller +--- + 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/target/linux/generic/backport-6.1/749-v5.16-net-dsa-qca8k-tidy-for-loop-in-setup-and-add-cpu-port-c.patch b/target/linux/generic/backport-6.1/749-v5.16-net-dsa-qca8k-tidy-for-loop-in-setup-and-add-cpu-port-c.patch new file mode 100644 index 0000000000..71fa3022d5 --- /dev/null +++ b/target/linux/generic/backport-6.1/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 +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 +Signed-off-by: David S. Miller +--- + 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/target/linux/generic/backport-6.1/750-v5.16-net-dsa-qca8k-make-sure-pad0-mac06-exchange-is-disabled.patch b/target/linux/generic/backport-6.1/750-v5.16-net-dsa-qca8k-make-sure-pad0-mac06-exchange-is-disabled.patch new file mode 100644 index 0000000000..4a61703c52 --- /dev/null +++ b/target/linux/generic/backport-6.1/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 +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 +Fixes: 3fcf734aa482 ("net: dsa: qca8k: add support for cpu port 6") +Reviewed-by: Vladimir Oltean +Signed-off-by: David S. Miller +--- + 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/target/linux/generic/backport-6.1/751-v5.16-net-dsa-qca8k-fix-internal-delay-applied-to-the-wrong-PAD.patch b/target/linux/generic/backport-6.1/751-v5.16-net-dsa-qca8k-fix-internal-delay-applied-to-the-wrong-PAD.patch new file mode 100644 index 0000000000..df9518d86c --- /dev/null +++ b/target/linux/generic/backport-6.1/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 +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 +Reviewed-by: Vladimir Oltean +Signed-off-by: David S. Miller +--- + 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/target/linux/generic/backport-6.1/752-v5.16-net-dsa-qca8k-fix-MTU-calculation.patch b/target/linux/generic/backport-6.1/752-v5.16-net-dsa-qca8k-fix-MTU-calculation.patch new file mode 100644 index 0000000000..7348d93ec4 --- /dev/null +++ b/target/linux/generic/backport-6.1/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 +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 +Signed-off-by: Ansuel Smith +Reviewed-by: Vladimir Oltean +Signed-off-by: David S. Miller +--- + 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/target/linux/generic/backport-6.1/753-v5.17-net-next-net-dsa-qca8k-remove-redundant-check-in-parse_port_config.patch b/target/linux/generic/backport-6.1/753-v5.17-net-next-net-dsa-qca8k-remove-redundant-check-in-parse_port_config.patch new file mode 100644 index 0000000000..f477b1b929 --- /dev/null +++ b/target/linux/generic/backport-6.1/753-v5.17-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 +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 +Reported-by: Dan Carpenter +Signed-off-by: Ansuel Smith +Reviewed-by: Vladimir Oltean +Signed-off-by: David S. Miller +--- + 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/target/linux/generic/backport-6.1/754-v5.17-net-next-net-dsa-qca8k-convert-to-GENMASK_FIELD_PREP_FIELD_GET.patch b/target/linux/generic/backport-6.1/754-v5.17-net-next-net-dsa-qca8k-convert-to-GENMASK_FIELD_PREP_FIELD_GET.patch new file mode 100644 index 0000000000..2cea88089d --- /dev/null +++ b/target/linux/generic/backport-6.1/754-v5.17-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 +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 +Signed-off-by: David S. Miller +--- + 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 + #include + #include ++#include + #include + #include + #include +@@ -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); + } + +@@ -1935,11 +1930,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); + } + +@@ -1963,10 +1958,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; + +@@ -2060,7 +2054,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; +@@ -2069,7 +2063,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/target/linux/generic/backport-6.1/755-v5.17-net-next-net-dsa-qca8k-remove-extra-mutex_init-in-qca8k_setup.patch b/target/linux/generic/backport-6.1/755-v5.17-net-next-net-dsa-qca8k-remove-extra-mutex_init-in-qca8k_setup.patch new file mode 100644 index 0000000000..8c39b8ea29 --- /dev/null +++ b/target/linux/generic/backport-6.1/755-v5.17-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 +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 +Reviewed-by: Vladimir Oltean +Signed-off-by: David S. Miller +--- + 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/target/linux/generic/backport-6.1/756-v5.17-net-next-net-dsa-qca8k-move-regmap-init-in-probe-and-set-it.patch b/target/linux/generic/backport-6.1/756-v5.17-net-next-net-dsa-qca8k-move-regmap-init-in-probe-and-set-it.patch new file mode 100644 index 0000000000..44d938c53e --- /dev/null +++ b/target/linux/generic/backport-6.1/756-v5.17-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 +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 +Reviewed-by: Vladimir Oltean +Signed-off-by: David S. Miller +--- + 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; +@@ -2096,6 +2090,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/target/linux/generic/backport-6.1/757-v5.17-net-next-net-dsa-qca8k-initial-conversion-to-regmap-heper.patch b/target/linux/generic/backport-6.1/757-v5.17-net-next-net-dsa-qca8k-initial-conversion-to-regmap-heper.patch new file mode 100644 index 0000000000..4ca9c8ba41 --- /dev/null +++ b/target/linux/generic/backport-6.1/757-v5.17-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 +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 +Signed-off-by: David S. Miller +--- + 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 + #include + #include ++#include + #include + #include + #include +@@ -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/target/linux/generic/backport-6.1/758-v5.17-net-next-net-dsa-qca8k-add-additional-MIB-counter-and-.patch b/target/linux/generic/backport-6.1/758-v5.17-net-next-net-dsa-qca8k-add-additional-MIB-counter-and-.patch new file mode 100644 index 0000000000..c8c050933b --- /dev/null +++ b/target/linux/generic/backport-6.1/758-v5.17-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 +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 +Reviewed-by: Vladimir Oltean +Signed-off-by: David S. Miller +--- + 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 +@@ -2173,14 +2187,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/target/linux/generic/backport-6.1/759-v5.17-net-next-net-dsa-qca8k-add-support-for-port-fast-aging.patch b/target/linux/generic/backport-6.1/759-v5.17-net-next-net-dsa-qca8k-add-support-for-port-fast-aging.patch new file mode 100644 index 0000000000..8ad7ab472d --- /dev/null +++ b/target/linux/generic/backport-6.1/759-v5.17-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 +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 +Reviewed-by: Vladimir Oltean +Signed-off-by: David S. Miller +--- + 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) +@@ -2017,6 +2027,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/target/linux/generic/backport-6.1/760-v5.17-net-next-net-dsa-qca8k-add-set_ageing_time-support.patch b/target/linux/generic/backport-6.1/760-v5.17-net-next-net-dsa-qca8k-add-set_ageing_time-support.patch new file mode 100644 index 0000000000..659e482405 --- /dev/null +++ b/target/linux/generic/backport-6.1/760-v5.17-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 +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 +Reviewed-by: Vladimir Oltean +Signed-off-by: David S. Miller +--- + 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) + { +@@ -2018,6 +2042,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/target/linux/generic/backport-6.1/761-v5.17-net-next-net-dsa-qca8k-add-support-for-mdb_add-del.patch b/target/linux/generic/backport-6.1/761-v5.17-net-next-net-dsa-qca8k-add-support-for-mdb_add-del.patch new file mode 100644 index 0000000000..8b97939ecb --- /dev/null +++ b/target/linux/generic/backport-6.1/761-v5.17-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 +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 +Reviewed-by: Vladimir Oltean +Signed-off-by: David S. Miller +--- + 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; +@@ -1949,6 +2024,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) + { +@@ -2056,6 +2153,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/target/linux/generic/backport-6.1/762-v5.17-net-next-net-dsa-qca8k-add-support-for-mirror-mode.patch b/target/linux/generic/backport-6.1/762-v5.17-net-next-net-dsa-qca8k-add-support-for-mirror-mode.patch new file mode 100644 index 0000000000..dc5a22935f --- /dev/null +++ b/target/linux/generic/backport-6.1/762-v5.17-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 +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 +Signed-off-by: David S. Miller +--- + 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 +@@ -2046,6 +2046,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) + { +@@ -2155,6 +2248,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/target/linux/generic/backport-6.1/763-v5.17-net-next-net-dsa-qca8k-add-LAG-support.patch b/target/linux/generic/backport-6.1/763-v5.17-net-next-net-dsa-qca8k-add-LAG-support.patch new file mode 100644 index 0000000000..b53f1288d5 --- /dev/null +++ b/target/linux/generic/backport-6.1/763-v5.17-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 +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 +Signed-off-by: David S. Miller +--- + 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; + } + +@@ -2226,6 +2229,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, +@@ -2259,6 +2434,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/target/linux/generic/backport-6.1/764-v5.17-net-next-net-dsa-qca8k-fix-warning-in-LAG-feature.patch b/target/linux/generic/backport-6.1/764-v5.17-net-next-net-dsa-qca8k-fix-warning-in-LAG-feature.patch new file mode 100644 index 0000000000..7d811be11c --- /dev/null +++ b/target/linux/generic/backport-6.1/764-v5.17-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 +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 +Fixes: def975307c01 ("net: dsa: qca8k: add LAG support") +Signed-off-by: Ansuel Smith +Reviewed-by: Florian Fainelli +Link: https://lore.kernel.org/r/20211123154446.31019-1-ansuelsmth@gmail.com +Signed-off-by: Jakub Kicinski +--- + 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 +@@ -2251,7 +2251,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; + +@@ -2265,8 +2265,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/target/linux/generic/backport-6.1/765-v5.17-01-net-next-net-dsa-reorder-PHY-initialization-with-MTU-setup-in.patch b/target/linux/generic/backport-6.1/765-v5.17-01-net-next-net-dsa-reorder-PHY-initialization-with-MTU-setup-in.patch new file mode 100644 index 0000000000..77cf63b809 --- /dev/null +++ b/target/linux/generic/backport-6.1/765-v5.17-01-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 +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 +Reviewed-by: Florian Fainelli +Signed-off-by: David S. Miller +--- + net/dsa/slave.c | 14 +++++++------- + 1 file changed, 7 insertions(+), 7 deletions(-) + +--- a/net/dsa/slave.c ++++ b/net/dsa/slave.c +@@ -1977,13 +1977,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); +@@ -1995,6 +1988,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/target/linux/generic/backport-6.1/765-v5.17-02-net-next-net-dsa-merge-rtnl_lock-sections-in-dsa_slave_create.patch b/target/linux/generic/backport-6.1/765-v5.17-02-net-next-net-dsa-merge-rtnl_lock-sections-in-dsa_slave_create.patch new file mode 100644 index 0000000000..50aa5d8f0d --- /dev/null +++ b/target/linux/generic/backport-6.1/765-v5.17-02-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 +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 +Reviewed-by: Florian Fainelli +Signed-off-by: David S. Miller +--- + net/dsa/slave.c | 4 +--- + 1 file changed, 1 insertion(+), 3 deletions(-) + +--- a/net/dsa/slave.c ++++ b/net/dsa/slave.c +@@ -1988,14 +1988,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/target/linux/generic/backport-6.1/765-v5.17-03-net-next-net-dsa-stop-updating-master-MTU-from-master.c.patch b/target/linux/generic/backport-6.1/765-v5.17-03-net-next-net-dsa-stop-updating-master-MTU-from-master.c.patch new file mode 100644 index 0000000000..d1126de5dd --- /dev/null +++ b/target/linux/generic/backport-6.1/765-v5.17-03-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 +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 +Reviewed-by: Florian Fainelli +Signed-off-by: David S. Miller +--- + 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/target/linux/generic/backport-6.1/765-v5.17-04-net-next-net-dsa-hold-rtnl_mutex-when-calling-dsa_master_-set.patch b/target/linux/generic/backport-6.1/765-v5.17-04-net-next-net-dsa-hold-rtnl_mutex-when-calling-dsa_master_-set.patch new file mode 100644 index 0000000000..67d434006b --- /dev/null +++ b/target/linux/generic/backport-6.1/765-v5.17-04-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 +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 +Reviewed-by: Florian Fainelli +Signed-off-by: David S. Miller +--- + 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/target/linux/generic/backport-6.1/765-v5.17-05-net-next-net-dsa-first-set-up-shared-ports-then-non-shared-po.patch b/target/linux/generic/backport-6.1/765-v5.17-05-net-next-net-dsa-first-set-up-shared-ports-then-non-shared-po.patch new file mode 100644 index 0000000000..e6472c61da --- /dev/null +++ b/target/linux/generic/backport-6.1/765-v5.17-05-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 +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 +Signed-off-by: David S. Miller +--- + 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/target/linux/generic/backport-6.1/765-v5.17-06-net-next-net-dsa-setup-master-before-ports.patch b/target/linux/generic/backport-6.1/765-v5.17-06-net-next-net-dsa-setup-master-before-ports.patch new file mode 100644 index 0000000000..93cad0c98a --- /dev/null +++ b/target/linux/generic/backport-6.1/765-v5.17-06-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 +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 +Signed-off-by: David S. Miller +--- + 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/target/linux/generic/backport-6.1/766-v5.18-01-net-dsa-provide-switch-operations-for-tracking-the-m.patch b/target/linux/generic/backport-6.1/766-v5.18-01-net-dsa-provide-switch-operations-for-tracking-the-m.patch new file mode 100644 index 0000000000..7c6a3a3f8d --- /dev/null +++ b/target/linux/generic/backport-6.1/766-v5.18-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 +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 +Signed-off-by: Ansuel Smith +Reviewed-by: Florian Fainelli +Signed-off-by: David S. Miller +--- + 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; +@@ -949,6 +959,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 +@@ -2311,6 +2311,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; +@@ -2322,6 +2352,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/target/linux/generic/backport-6.1/766-v5.18-02-net-dsa-replay-master-state-events-in-dsa_tree_-setu.patch b/target/linux/generic/backport-6.1/766-v5.18-02-net-dsa-replay-master-state-events-in-dsa_tree_-setu.patch new file mode 100644 index 0000000000..6478d580c0 --- /dev/null +++ b/target/linux/generic/backport-6.1/766-v5.18-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 +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 +Signed-off-by: Ansuel Smith +Reviewed-by: Florian Fainelli +Signed-off-by: David S. Miller +--- + 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 + #include + #include ++#include + + #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/target/linux/generic/backport-6.1/766-v5.18-03-net-dsa-tag_qca-convert-to-FIELD-macro.patch b/target/linux/generic/backport-6.1/766-v5.18-03-net-dsa-tag_qca-convert-to-FIELD-macro.patch new file mode 100644 index 0000000000..82c94b385b --- /dev/null +++ b/target/linux/generic/backport-6.1/766-v5.18-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 +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 +Reviewed-by: Vladimir Oltean +Reviewed-by: Florian Fainelli +Signed-off-by: David S. Miller +--- + 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 ++#include + + #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/target/linux/generic/backport-6.1/766-v5.18-04-net-dsa-tag_qca-move-define-to-include-linux-dsa.patch b/target/linux/generic/backport-6.1/766-v5.18-04-net-dsa-tag_qca-move-define-to-include-linux-dsa.patch new file mode 100644 index 0000000000..c1e74ceeeb --- /dev/null +++ b/target/linux/generic/backport-6.1/766-v5.18-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 +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 +Reviewed-by: Vladimir Oltean +Reviewed-by: Florian Fainelli +Signed-off-by: David S. Miller +--- + 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 + #include ++#include + + #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/target/linux/generic/backport-6.1/766-v5.18-05-net-dsa-tag_qca-enable-promisc_on_master-flag.patch b/target/linux/generic/backport-6.1/766-v5.18-05-net-dsa-tag_qca-enable-promisc_on_master-flag.patch new file mode 100644 index 0000000000..9394a0dabb --- /dev/null +++ b/target/linux/generic/backport-6.1/766-v5.18-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 +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 +Reviewed-by: Vladimir Oltean +Reviewed-by: Florian Fainelli +Signed-off-by: David S. Miller +--- + 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/target/linux/generic/backport-6.1/766-v5.18-06-net-dsa-tag_qca-add-define-for-handling-mgmt-Etherne.patch b/target/linux/generic/backport-6.1/766-v5.18-06-net-dsa-tag_qca-add-define-for-handling-mgmt-Etherne.patch new file mode 100644 index 0000000000..459454e03b --- /dev/null +++ b/target/linux/generic/backport-6.1/766-v5.18-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 +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 +Reviewed-by: Florian Fainelli +Signed-off-by: David S. Miller +--- + 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/target/linux/generic/backport-6.1/766-v5.18-07-net-dsa-tag_qca-add-define-for-handling-MIB-packet.patch b/target/linux/generic/backport-6.1/766-v5.18-07-net-dsa-tag_qca-add-define-for-handling-MIB-packet.patch new file mode 100644 index 0000000000..7e5dc65730 --- /dev/null +++ b/target/linux/generic/backport-6.1/766-v5.18-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 +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 +Reviewed-by: Florian Fainelli +Signed-off-by: David S. Miller +--- + 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/target/linux/generic/backport-6.1/766-v5.18-08-net-dsa-tag_qca-add-support-for-handling-mgmt-and-MI.patch b/target/linux/generic/backport-6.1/766-v5.18-08-net-dsa-tag_qca-add-support-for-handling-mgmt-and-MI.patch new file mode 100644 index 0000000000..ad25da30e6 --- /dev/null +++ b/target/linux/generic/backport-6.1/766-v5.18-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 +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 +Reviewed-by: Vladimir Oltean +Reviewed-by: Florian Fainelli +Signed-off-by: David S. Miller +--- + 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 + #include ++#include + #include + + #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/target/linux/generic/backport-6.1/766-v5.18-09-net-dsa-qca8k-add-tracking-state-of-master-port.patch b/target/linux/generic/backport-6.1/766-v5.18-09-net-dsa-qca8k-add-tracking-state-of-master-port.patch new file mode 100644 index 0000000000..eb21cc3912 --- /dev/null +++ b/target/linux/generic/backport-6.1/766-v5.18-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 +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 +Reviewed-by: Florian Fainelli +Signed-off-by: David S. Miller +--- + 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 +@@ -2401,6 +2401,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, +@@ -2436,6 +2450,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/target/linux/generic/backport-6.1/766-v5.18-10-net-dsa-qca8k-add-support-for-mgmt-read-write-in-Eth.patch b/target/linux/generic/backport-6.1/766-v5.18-10-net-dsa-qca8k-add-support-for-mgmt-read-write-in-Eth.patch new file mode 100644 index 0000000000..07c5ba4621 --- /dev/null +++ b/target/linux/generic/backport-6.1/766-v5.18-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 +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 +Signed-off-by: David S. Miller +--- + 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 + #include + #include ++#include + + #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); +@@ -2412,7 +2610,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 = { +@@ -2451,6 +2672,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) +@@ -2530,6 +2752,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 + #include + #include ++#include ++ ++#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/target/linux/generic/backport-6.1/766-v5.18-11-net-dsa-qca8k-add-support-for-mib-autocast-in-Ethern.patch b/target/linux/generic/backport-6.1/766-v5.18-11-net-dsa-qca8k-add-support-for-mib-autocast-in-Ethern.patch new file mode 100644 index 0000000000..0dcf279433 --- /dev/null +++ b/target/linux/generic/backport-6.1/766-v5.18-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 +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 +Signed-off-by: David S. Miller +--- + 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++) { +@@ -2611,9 +2709,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); + } + +@@ -2627,6 +2727,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: +@@ -2755,6 +2856,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/target/linux/generic/backport-6.1/766-v5.18-12-net-dsa-qca8k-add-support-for-phy-read-write-with-mg.patch b/target/linux/generic/backport-6.1/766-v5.18-12-net-dsa-qca8k-add-support-for-phy-read-write-with-mg.patch new file mode 100644 index 0000000000..f5899eb590 --- /dev/null +++ b/target/linux/generic/backport-6.1/766-v5.18-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 +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 +Signed-off-by: David S. Miller +--- + 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 + + #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/target/linux/generic/backport-6.1/766-v5.18-13-net-dsa-qca8k-move-page-cache-to-driver-priv.patch b/target/linux/generic/backport-6.1/766-v5.18-13-net-dsa-qca8k-move-page-cache-to-driver-priv.patch new file mode 100644 index 0000000000..4ac0bc32fd --- /dev/null +++ b/target/linux/generic/backport-6.1/766-v5.18-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 +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 +Reviewed-by: Florian Fainelli +Signed-off-by: David S. Miller +--- + 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; +@@ -3060,6 +3056,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/target/linux/generic/backport-6.1/766-v5.18-14-net-dsa-qca8k-cache-lo-and-hi-for-mdio-write.patch b/target/linux/generic/backport-6.1/766-v5.18-14-net-dsa-qca8k-cache-lo-and-hi-for-mdio-write.patch new file mode 100644 index 0000000000..e2cb2721ce --- /dev/null +++ b/target/linux/generic/backport-6.1/766-v5.18-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 +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 +Reviewed-by: Florian Fainelli +Signed-off-by: David S. Miller +--- + 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); + +@@ -3057,6 +3092,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/target/linux/generic/backport-6.1/766-v5.18-15-net-dsa-qca8k-add-support-for-larger-read-write-size.patch b/target/linux/generic/backport-6.1/766-v5.18-15-net-dsa-qca8k-add-support-for-larger-read-write-size.patch new file mode 100644 index 0000000000..5acd13dbad --- /dev/null +++ b/target/linux/generic/backport-6.1/766-v5.18-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 +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 +Signed-off-by: David S. Miller +--- + 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/target/linux/generic/backport-6.1/766-v5.18-16-net-dsa-qca8k-introduce-qca8k_bulk_read-write-functi.patch b/target/linux/generic/backport-6.1/766-v5.18-16-net-dsa-qca8k-introduce-qca8k_bulk_read-write-functi.patch new file mode 100644 index 0000000000..f26c6b91ac --- /dev/null +++ b/target/linux/generic/backport-6.1/766-v5.18-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 +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 +Signed-off-by: David S. Miller +--- + 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/target/linux/generic/backport-6.1/767-v5.18-net-dsa-qca8k-check-correct-variable-in-qca8k_phy_et.patch b/target/linux/generic/backport-6.1/767-v5.18-net-dsa-qca8k-check-correct-variable-in-qca8k_phy_et.patch new file mode 100644 index 0000000000..34607c223c --- /dev/null +++ b/target/linux/generic/backport-6.1/767-v5.18-net-dsa-qca8k-check-correct-variable-in-qca8k_phy_et.patch @@ -0,0 +1,28 @@ +From c3664d913dc115cab4a5fdb5634df4887048000e Mon Sep 17 00:00:00 2001 +From: Dan Carpenter +Date: Fri, 4 Feb 2022 13:03:36 +0300 +Subject: [PATCH 1/1] net: dsa: qca8k: check correct variable in + qca8k_phy_eth_command() + +This is a copy and paste bug. It was supposed to check "clear_skb" +instead of "write_skb". + +Fixes: 2cd548566384 ("net: dsa: qca8k: add support for phy read/write with mgmt Ethernet") +Signed-off-by: Dan Carpenter +Reviewed-by: Florian Fainelli +Signed-off-by: David S. Miller +--- + 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 +@@ -1018,7 +1018,7 @@ qca8k_phy_eth_command(struct qca8k_priv + + clear_skb = qca8k_alloc_mdio_header(MDIO_WRITE, QCA8K_MDIO_MASTER_CTRL, &clear_val, + QCA8K_ETHERNET_PHY_PRIORITY, sizeof(clear_val)); +- if (!write_skb) { ++ if (!clear_skb) { + ret = -ENOMEM; + goto err_clear_skb; + } diff --git a/target/linux/generic/backport-6.1/768-v5.18-net-dsa-qca8k-fix-noderef.cocci-warnings.patch b/target/linux/generic/backport-6.1/768-v5.18-net-dsa-qca8k-fix-noderef.cocci-warnings.patch new file mode 100644 index 0000000000..d8cf266309 --- /dev/null +++ b/target/linux/generic/backport-6.1/768-v5.18-net-dsa-qca8k-fix-noderef.cocci-warnings.patch @@ -0,0 +1,34 @@ +From 4f5e483b8c7a644733db941a1ae00173baa7b463 Mon Sep 17 00:00:00 2001 +From: kernel test robot +Date: Thu, 10 Feb 2022 06:13:04 +0800 +Subject: [PATCH 1/1] net: dsa: qca8k: fix noderef.cocci warnings + +drivers/net/dsa/qca8k.c:422:37-43: ERROR: application of sizeof to pointer + + sizeof when applied to a pointer typed expression gives the size of + the pointer + +Generated by: scripts/coccinelle/misc/noderef.cocci + +Fixes: 90386223f44e ("net: dsa: qca8k: add support for larger read/write size with mgmt Ethernet") +CC: Ansuel Smith +Reported-by: kernel test robot +Signed-off-by: kernel test robot +Reviewed-by: Florian Fainelli +Link: https://lore.kernel.org/r/20220209221304.GA17529@d2214a582157 +Signed-off-by: Jakub Kicinski +--- + 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 +@@ -456,7 +456,7 @@ qca8k_regmap_read(void *ctx, uint32_t re + u16 r1, r2, page; + int ret; + +- if (!qca8k_read_eth(priv, reg, val, sizeof(val))) ++ if (!qca8k_read_eth(priv, reg, val, sizeof(*val))) + return 0; + + qca8k_split_addr(reg, &r1, &r2, &page); diff --git a/target/linux/generic/backport-6.1/769-v5.19-01-net-dsa-qca8k-drop-MTU-tracking-from-qca8k_priv.patch b/target/linux/generic/backport-6.1/769-v5.19-01-net-dsa-qca8k-drop-MTU-tracking-from-qca8k_priv.patch new file mode 100644 index 0000000000..57df4c126e --- /dev/null +++ b/target/linux/generic/backport-6.1/769-v5.19-01-net-dsa-qca8k-drop-MTU-tracking-from-qca8k_priv.patch @@ -0,0 +1,79 @@ +From 69fd055957a02309ffdc23d887a01988b6e5bab1 Mon Sep 17 00:00:00 2001 +From: Ansuel Smith +Date: Sat, 16 Apr 2022 01:30:12 +0200 +Subject: [PATCH 1/6] net: dsa: qca8k: drop MTU tracking from qca8k_priv + +DSA set the CPU port based on the largest MTU of all the slave ports. +Based on this we can drop the MTU array from qca8k_priv and set the +port_change_mtu logic on DSA changing MTU of the CPU port as the switch +have a global MTU settingfor each port. + +Signed-off-by: Ansuel Smith +Reviewed-by: Vladimir Oltean +Signed-off-by: David S. Miller +--- + drivers/net/dsa/qca8k.c | 26 +++++++++----------------- + drivers/net/dsa/qca8k.h | 1 - + 2 files changed, 9 insertions(+), 18 deletions(-) + +--- a/drivers/net/dsa/qca8k.c ++++ b/drivers/net/dsa/qca8k.c +@@ -1803,16 +1803,6 @@ 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. +- * 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_DATA_LEN; + } + + /* Special GLOBAL_FC_THRESH value are needed for ar8327 switch */ +@@ -2525,13 +2515,16 @@ static int + qca8k_port_change_mtu(struct dsa_switch *ds, int port, int new_mtu) + { + struct qca8k_priv *priv = ds->priv; +- int ret, i, mtu = 0; +- +- priv->port_mtu[port] = new_mtu; ++ int ret; + +- for (i = 0; i < QCA8K_NUM_PORTS; i++) +- if (priv->port_mtu[i] > mtu) +- mtu = priv->port_mtu[i]; ++ /* We have only have a general MTU setting. ++ * DSA always set the CPU port's MTU to the largest MTU of the slave ++ * ports. ++ * Setting MTU just for the CPU port is sufficient to correctly set a ++ * value for every port. ++ */ ++ if (!dsa_is_cpu_port(ds, port)) ++ return 0; + + /* To change the MAX_FRAME_SIZE the cpu ports must be off or + * the switch panics. +@@ -2545,7 +2538,7 @@ qca8k_port_change_mtu(struct dsa_switch + qca8k_port_set_status(priv, 6, 0); + + /* Include L2 header / FCS length */ +- ret = qca8k_write(priv, QCA8K_MAX_FRAME_SIZE, mtu + ETH_HLEN + ETH_FCS_LEN); ++ ret = qca8k_write(priv, QCA8K_MAX_FRAME_SIZE, new_mtu + ETH_HLEN + ETH_FCS_LEN); + + if (priv->port_sts[0].enabled) + qca8k_port_set_status(priv, 0, 1); +--- a/drivers/net/dsa/qca8k.h ++++ b/drivers/net/dsa/qca8k.h +@@ -392,7 +392,6 @@ struct qca8k_priv { + struct device *dev; + 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_mgmt_eth_data mgmt_eth_data; + struct qca8k_mib_eth_data mib_eth_data; diff --git a/target/linux/generic/backport-6.1/769-v5.19-02-net-dsa-qca8k-drop-port_sts-from-qca8k_priv.patch b/target/linux/generic/backport-6.1/769-v5.19-02-net-dsa-qca8k-drop-port_sts-from-qca8k_priv.patch new file mode 100644 index 0000000000..3cacd7e4fd --- /dev/null +++ b/target/linux/generic/backport-6.1/769-v5.19-02-net-dsa-qca8k-drop-port_sts-from-qca8k_priv.patch @@ -0,0 +1,116 @@ +From 2b8fd87af7f156942971789abac8ee2bb60c03bc Mon Sep 17 00:00:00 2001 +From: Ansuel Smith +Date: Sat, 16 Apr 2022 01:30:13 +0200 +Subject: [PATCH 2/6] net: dsa: qca8k: drop port_sts from qca8k_priv + +Port_sts is a thing of the past for this driver. It was something +present on the initial implementation of this driver and parts of the +original struct were dropped over time. Using an array of int to store if +a port is enabled or not to handle PM operation seems overkill. Switch +and use a simple u8 to store the port status where each bit correspond +to a port. (bit is set port is enabled, bit is not set, port is disabled) +Also add some comments to better describe why we need to track port +status. + +Signed-off-by: Ansuel Smith +Reviewed-by: Vladimir Oltean +Signed-off-by: David S. Miller +--- + drivers/net/dsa/qca8k.c | 15 +++++++++------ + drivers/net/dsa/qca8k.h | 9 ++++----- + 2 files changed, 13 insertions(+), 11 deletions(-) + +--- a/drivers/net/dsa/qca8k.c ++++ b/drivers/net/dsa/qca8k.c +@@ -2494,7 +2494,7 @@ qca8k_port_enable(struct dsa_switch *ds, + struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv; + + qca8k_port_set_status(priv, port, 1); +- priv->port_sts[port].enabled = 1; ++ priv->port_enabled_map |= BIT(port); + + if (dsa_is_user_port(ds, port)) + phy_support_asym_pause(phy); +@@ -2508,7 +2508,7 @@ qca8k_port_disable(struct dsa_switch *ds + struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv; + + qca8k_port_set_status(priv, port, 0); +- priv->port_sts[port].enabled = 0; ++ priv->port_enabled_map &= ~BIT(port); + } + + static int +@@ -2531,19 +2531,19 @@ qca8k_port_change_mtu(struct dsa_switch + * Turn off both cpu ports before applying the new value to prevent + * this. + */ +- if (priv->port_sts[0].enabled) ++ if (priv->port_enabled_map & BIT(0)) + qca8k_port_set_status(priv, 0, 0); + +- if (priv->port_sts[6].enabled) ++ if (priv->port_enabled_map & BIT(6)) + qca8k_port_set_status(priv, 6, 0); + + /* Include L2 header / FCS length */ + ret = qca8k_write(priv, QCA8K_MAX_FRAME_SIZE, new_mtu + ETH_HLEN + ETH_FCS_LEN); + +- if (priv->port_sts[0].enabled) ++ if (priv->port_enabled_map & BIT(0)) + qca8k_port_set_status(priv, 0, 1); + +- if (priv->port_sts[6].enabled) ++ if (priv->port_enabled_map & BIT(6)) + qca8k_port_set_status(priv, 6, 1); + + return ret; +@@ -3199,13 +3199,16 @@ static void qca8k_sw_shutdown(struct mdi + static void + qca8k_set_pm(struct qca8k_priv *priv, int enable) + { +- int i; ++ int port; + +- for (i = 0; i < QCA8K_NUM_PORTS; i++) { +- if (!priv->port_sts[i].enabled) ++ for (port = 0; port < QCA8K_NUM_PORTS; port++) { ++ /* Do not enable on resume if the port was ++ * disabled before. ++ */ ++ if (!(priv->port_enabled_map & BIT(port))) + continue; + +- qca8k_port_set_status(priv, i, enable); ++ qca8k_port_set_status(priv, port, enable); + } + } + +--- a/drivers/net/dsa/qca8k.h ++++ b/drivers/net/dsa/qca8k.h +@@ -324,10 +324,6 @@ enum qca8k_mid_cmd { + QCA8K_MIB_CAST = 3, + }; + +-struct ar8xxx_port_status { +- int enabled; +-}; +- + struct qca8k_match_data { + u8 id; + bool reduced_package; +@@ -382,11 +378,14 @@ struct qca8k_priv { + u8 mirror_rx; + u8 mirror_tx; + u8 lag_hash_mode; ++ /* Each bit correspond to a port. This switch can support a max of 7 port. ++ * Bit 1: port enabled. Bit 0: port disabled. ++ */ ++ u8 port_enabled_map; + 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]; + struct dsa_switch *ds; + struct mutex reg_mutex; + struct device *dev; diff --git a/target/linux/generic/backport-6.1/769-v5.19-03-net-dsa-qca8k-rework-and-simplify-mdiobus-logic.patch b/target/linux/generic/backport-6.1/769-v5.19-03-net-dsa-qca8k-rework-and-simplify-mdiobus-logic.patch new file mode 100644 index 0000000000..12c3221077 --- /dev/null +++ b/target/linux/generic/backport-6.1/769-v5.19-03-net-dsa-qca8k-rework-and-simplify-mdiobus-logic.patch @@ -0,0 +1,173 @@ +From 8255212e4130bd2dc1463286a3dddb74797bbdc1 Mon Sep 17 00:00:00 2001 +From: Ansuel Smith +Date: Sat, 16 Apr 2022 01:30:14 +0200 +Subject: [PATCH 3/6] net: dsa: qca8k: rework and simplify mdiobus logic + +In an attempt to reduce qca8k_priv space, rework and simplify mdiobus +logic. +We now declare a mdiobus instead of relying on DSA phy_read/write even +if a mdio node is not present. This is all to make the qca8k ops static +and not switch specific. With a legacy implementation where port doesn't +have a phy map declared in the dts with a mdio node, we declare a +'qca8k-legacy' mdiobus. The conversion logic is used as legacy read and +write ops are used instead of the internal one. +Also drop the legacy_phy_port_mapping as we now declare mdiobus with ops +that already address the workaround. + +Signed-off-by: Ansuel Smith +Signed-off-by: David S. Miller +--- + drivers/net/dsa/qca8k.c | 95 +++++++++++++---------------------------- + drivers/net/dsa/qca8k.h | 1 - + 2 files changed, 29 insertions(+), 67 deletions(-) + +--- a/drivers/net/dsa/qca8k.c ++++ b/drivers/net/dsa/qca8k.c +@@ -1291,83 +1291,63 @@ qca8k_internal_mdio_read(struct mii_bus + } + + static int +-qca8k_phy_write(struct dsa_switch *ds, int port, int regnum, u16 data) ++qca8k_legacy_mdio_write(struct mii_bus *slave_bus, int port, int regnum, u16 data) + { +- struct qca8k_priv *priv = ds->priv; +- int ret; ++ port = qca8k_port_to_phy(port) % PHY_MAX_ADDR; + +- /* Check if the legacy mapping should be used and the +- * port is not correctly mapped to the right PHY in the +- * devicetree +- */ +- 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, port, regnum, data); ++ return qca8k_internal_mdio_write(slave_bus, port, regnum, data); + } + + static int +-qca8k_phy_read(struct dsa_switch *ds, int port, int regnum) ++qca8k_legacy_mdio_read(struct mii_bus *slave_bus, int port, int regnum) + { +- 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 +- * devicetree +- */ +- 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, port, regnum); +- +- if (ret < 0) +- return 0xffff; ++ port = qca8k_port_to_phy(port) % PHY_MAX_ADDR; + +- return ret; ++ return qca8k_internal_mdio_read(slave_bus, port, regnum); + } + + static int +-qca8k_mdio_register(struct qca8k_priv *priv, struct device_node *mdio) ++qca8k_mdio_register(struct qca8k_priv *priv) + { + struct dsa_switch *ds = priv->ds; ++ struct device_node *mdio; + struct mii_bus *bus; + + bus = devm_mdiobus_alloc(ds->dev); +- + if (!bus) + return -ENOMEM; + + bus->priv = (void *)priv; +- bus->name = "qca8k slave mii"; +- bus->read = qca8k_internal_mdio_read; +- bus->write = qca8k_internal_mdio_write; +- snprintf(bus->id, MII_BUS_ID_SIZE, "qca8k-%d", +- ds->index); +- + bus->parent = ds->dev; + bus->phy_mask = ~ds->phys_mii_mask; +- + ds->slave_mii_bus = bus; + +- return devm_of_mdiobus_register(priv->dev, bus, mdio); ++ /* Check if the devicetree declare the port:phy mapping */ ++ mdio = of_get_child_by_name(priv->dev->of_node, "mdio"); ++ if (of_device_is_available(mdio)) { ++ snprintf(bus->id, MII_BUS_ID_SIZE, "qca8k-%d", ds->index); ++ bus->name = "qca8k slave mii"; ++ bus->read = qca8k_internal_mdio_read; ++ bus->write = qca8k_internal_mdio_write; ++ return devm_of_mdiobus_register(priv->dev, bus, mdio); ++ } ++ ++ /* If a mapping can't be found the legacy mapping is used, ++ * using the qca8k_port_to_phy function ++ */ ++ snprintf(bus->id, MII_BUS_ID_SIZE, "qca8k-%d.%d", ++ ds->dst->index, ds->index); ++ bus->name = "qca8k-legacy slave mii"; ++ bus->read = qca8k_legacy_mdio_read; ++ bus->write = qca8k_legacy_mdio_write; ++ return devm_mdiobus_register(priv->dev, bus); + } + + static int + qca8k_setup_mdio_bus(struct qca8k_priv *priv) + { + u32 internal_mdio_mask = 0, external_mdio_mask = 0, reg; +- struct device_node *ports, *port, *mdio; ++ struct device_node *ports, *port; + phy_interface_t mode; + int err; + +@@ -1429,24 +1409,7 @@ qca8k_setup_mdio_bus(struct qca8k_priv * + QCA8K_MDIO_MASTER_EN); + } + +- /* Check if the devicetree declare the port:phy mapping */ +- mdio = of_get_child_by_name(priv->dev->of_node, "mdio"); +- if (of_device_is_available(mdio)) { +- err = qca8k_mdio_register(priv, mdio); +- if (err) +- of_node_put(mdio); +- +- return err; +- } +- +- /* If a mapping can't be found the legacy mapping is used, +- * using the qca8k_port_to_phy function +- */ +- priv->legacy_phy_port_mapping = true; +- priv->ops.phy_read = qca8k_phy_read; +- priv->ops.phy_write = qca8k_phy_write; +- +- return 0; ++ return qca8k_mdio_register(priv); + } + + static int +--- a/drivers/net/dsa/qca8k.h ++++ b/drivers/net/dsa/qca8k.h +@@ -382,7 +382,6 @@ struct qca8k_priv { + * Bit 1: port enabled. Bit 0: port disabled. + */ + u8 port_enabled_map; +- bool legacy_phy_port_mapping; + struct qca8k_ports_config ports_config; + struct regmap *regmap; + struct mii_bus *bus; diff --git a/target/linux/generic/backport-6.1/769-v5.19-04-net-dsa-qca8k-drop-dsa_switch_ops-from-qca8k_priv.patch b/target/linux/generic/backport-6.1/769-v5.19-04-net-dsa-qca8k-drop-dsa_switch_ops-from-qca8k_priv.patch new file mode 100644 index 0000000000..8641000abb --- /dev/null +++ b/target/linux/generic/backport-6.1/769-v5.19-04-net-dsa-qca8k-drop-dsa_switch_ops-from-qca8k_priv.patch @@ -0,0 +1,39 @@ +From 2349b83a2486c55b9dd225326f0172a84a43c5e4 Mon Sep 17 00:00:00 2001 +From: Ansuel Smith +Date: Sat, 16 Apr 2022 01:30:15 +0200 +Subject: [PATCH 4/6] net: dsa: qca8k: drop dsa_switch_ops from qca8k_priv + +Now that dsa_switch_ops is not switch specific anymore, we can drop it +from qca8k_priv and use the static ops directly for the dsa_switch +pointer. + +Signed-off-by: Ansuel Smith +Reviewed-by: Vladimir Oltean +Signed-off-by: David S. Miller +--- + drivers/net/dsa/qca8k.c | 3 +-- + drivers/net/dsa/qca8k.h | 1 - + 2 files changed, 1 insertion(+), 3 deletions(-) + +--- a/drivers/net/dsa/qca8k.c ++++ b/drivers/net/dsa/qca8k.c +@@ -3121,8 +3121,7 @@ qca8k_sw_probe(struct mdio_device *mdiod + priv->ds->dev = &mdiodev->dev; + priv->ds->num_ports = QCA8K_NUM_PORTS; + priv->ds->priv = priv; +- priv->ops = qca8k_switch_ops; +- priv->ds->ops = &priv->ops; ++ priv->ds->ops = &qca8k_switch_ops; + mutex_init(&priv->reg_mutex); + dev_set_drvdata(&mdiodev->dev, priv); + +--- a/drivers/net/dsa/qca8k.h ++++ b/drivers/net/dsa/qca8k.h +@@ -388,7 +388,6 @@ struct qca8k_priv { + struct dsa_switch *ds; + struct mutex reg_mutex; + struct device *dev; +- struct dsa_switch_ops ops; + struct gpio_desc *reset_gpio; + struct net_device *mgmt_master; /* Track if mdio/mib Ethernet is available */ + struct qca8k_mgmt_eth_data mgmt_eth_data; diff --git a/target/linux/generic/backport-6.1/769-v5.19-05-net-dsa-qca8k-correctly-handle-mdio-read-error.patch b/target/linux/generic/backport-6.1/769-v5.19-05-net-dsa-qca8k-correctly-handle-mdio-read-error.patch new file mode 100644 index 0000000000..b14b22091b --- /dev/null +++ b/target/linux/generic/backport-6.1/769-v5.19-05-net-dsa-qca8k-correctly-handle-mdio-read-error.patch @@ -0,0 +1,33 @@ +From 6cfc03b602200c5cbbd8d906fd905547814e83df Mon Sep 17 00:00:00 2001 +From: Ansuel Smith +Date: Sat, 16 Apr 2022 01:30:16 +0200 +Subject: [PATCH 5/6] net: dsa: qca8k: correctly handle mdio read error + +Restore original way to handle mdio read error by returning 0xffff. +This was wrongly changed when the internal_mdio_read was introduced, +now that both legacy and internal use the same function, make sure that +they behave the same way. + +Fixes: ce062a0adbfe ("net: dsa: qca8k: fix kernel panic with legacy mdio mapping") +Signed-off-by: Ansuel Smith +Signed-off-by: David S. Miller +--- + drivers/net/dsa/qca8k.c | 7 ++++++- + 1 file changed, 6 insertions(+), 1 deletion(-) + +--- a/drivers/net/dsa/qca8k.c ++++ b/drivers/net/dsa/qca8k.c +@@ -1287,7 +1287,12 @@ qca8k_internal_mdio_read(struct mii_bus + if (ret >= 0) + return ret; + +- return qca8k_mdio_read(priv, phy, regnum); ++ ret = qca8k_mdio_read(priv, phy, regnum); ++ ++ if (ret < 0) ++ return 0xffff; ++ ++ return ret; + } + + static int diff --git a/target/linux/generic/backport-6.1/769-v5.19-06-net-dsa-qca8k-unify-bus-id-naming-with-legacy-and-OF.patch b/target/linux/generic/backport-6.1/769-v5.19-06-net-dsa-qca8k-unify-bus-id-naming-with-legacy-and-OF.patch new file mode 100644 index 0000000000..094686f11b --- /dev/null +++ b/target/linux/generic/backport-6.1/769-v5.19-06-net-dsa-qca8k-unify-bus-id-naming-with-legacy-and-OF.patch @@ -0,0 +1,44 @@ +From 8d1af50842bf2774f4edc57054206e909117469b Mon Sep 17 00:00:00 2001 +From: Ansuel Smith +Date: Sat, 16 Apr 2022 01:30:17 +0200 +Subject: [PATCH 6/6] net: dsa: qca8k: unify bus id naming with legacy and OF + mdio bus + +Add support for multiple switch with OF mdio bus declaration. +Unify the bus id naming and use the same logic for both legacy and OF +mdio bus. + +Signed-off-by: Ansuel Smith +Signed-off-by: David S. Miller +--- + drivers/net/dsa/qca8k.c | 5 ++--- + 1 file changed, 2 insertions(+), 3 deletions(-) + +--- a/drivers/net/dsa/qca8k.c ++++ b/drivers/net/dsa/qca8k.c +@@ -1323,6 +1323,8 @@ qca8k_mdio_register(struct qca8k_priv *p + return -ENOMEM; + + bus->priv = (void *)priv; ++ snprintf(bus->id, MII_BUS_ID_SIZE, "qca8k-%d.%d", ++ ds->dst->index, ds->index); + bus->parent = ds->dev; + bus->phy_mask = ~ds->phys_mii_mask; + ds->slave_mii_bus = bus; +@@ -1330,7 +1332,6 @@ qca8k_mdio_register(struct qca8k_priv *p + /* Check if the devicetree declare the port:phy mapping */ + mdio = of_get_child_by_name(priv->dev->of_node, "mdio"); + if (of_device_is_available(mdio)) { +- snprintf(bus->id, MII_BUS_ID_SIZE, "qca8k-%d", ds->index); + bus->name = "qca8k slave mii"; + bus->read = qca8k_internal_mdio_read; + bus->write = qca8k_internal_mdio_write; +@@ -1340,8 +1341,6 @@ qca8k_mdio_register(struct qca8k_priv *p + /* If a mapping can't be found the legacy mapping is used, + * using the qca8k_port_to_phy function + */ +- snprintf(bus->id, MII_BUS_ID_SIZE, "qca8k-%d.%d", +- ds->dst->index, ds->index); + bus->name = "qca8k-legacy slave mii"; + bus->read = qca8k_legacy_mdio_read; + bus->write = qca8k_legacy_mdio_write; diff --git a/target/linux/generic/backport-6.1/770-v6.0-net-dsa-qca8k-move-driver-to-qca-dir.patch b/target/linux/generic/backport-6.1/770-v6.0-net-dsa-qca8k-move-driver-to-qca-dir.patch new file mode 100644 index 0000000000..1534113f1d --- /dev/null +++ b/target/linux/generic/backport-6.1/770-v6.0-net-dsa-qca8k-move-driver-to-qca-dir.patch @@ -0,0 +1,7389 @@ +From 4bbaf764e1e1786eb937fdb62172f656f512e116 Mon Sep 17 00:00:00 2001 +From: Christian Marangi +Date: Wed, 13 Jul 2022 22:53:50 +0200 +Subject: [PATCH 1/1] net: dsa: qca8k: move driver to qca dir + +Move qca8k driver to qca dir in preparation for code split and +introduction of ipq4019 switch based on qca8k. + +Signed-off-by: Christian Marangi +Reviewed-by: Florian Fainelli +Signed-off-by: David S. Miller +--- + drivers/net/dsa/Kconfig | 8 -------- + drivers/net/dsa/Makefile | 1 - + drivers/net/dsa/qca/Kconfig | 8 ++++++++ + drivers/net/dsa/qca/Makefile | 1 + + drivers/net/dsa/{ => qca}/qca8k.c | 0 + drivers/net/dsa/{ => qca}/qca8k.h | 0 + 6 files changed, 9 insertions(+), 9 deletions(-) + rename drivers/net/dsa/{ => qca}/qca8k.c (100%) + rename drivers/net/dsa/{ => qca}/qca8k.h (100%) + +--- a/drivers/net/dsa/Kconfig ++++ b/drivers/net/dsa/Kconfig +@@ -60,14 +60,6 @@ source "drivers/net/dsa/sja1105/Kconfig" + + source "drivers/net/dsa/xrs700x/Kconfig" + +-config NET_DSA_QCA8K +- tristate "Qualcomm Atheros QCA8K Ethernet switch family support" +- select NET_DSA_TAG_QCA +- select REGMAP +- help +- This enables support for the Qualcomm Atheros QCA8K Ethernet +- switch chips. +- + config NET_DSA_REALTEK_SMI + tristate "Realtek SMI Ethernet switch family support" + select NET_DSA_TAG_RTL4_A +--- a/drivers/net/dsa/Makefile ++++ b/drivers/net/dsa/Makefile +@@ -8,7 +8,6 @@ endif + obj-$(CONFIG_NET_DSA_LANTIQ_GSWIP) += lantiq_gswip.o + obj-$(CONFIG_NET_DSA_MT7530) += mt7530.o + obj-$(CONFIG_NET_DSA_MV88E6060) += mv88e6060.o +-obj-$(CONFIG_NET_DSA_QCA8K) += qca8k.o + obj-$(CONFIG_NET_DSA_REALTEK_SMI) += realtek-smi.o + realtek-smi-objs := realtek-smi-core.o rtl8366.o rtl8366rb.o + obj-$(CONFIG_NET_DSA_SMSC_LAN9303) += lan9303-core.o +--- a/drivers/net/dsa/qca/Kconfig ++++ b/drivers/net/dsa/qca/Kconfig +@@ -7,3 +7,11 @@ config NET_DSA_AR9331 + help + This enables support for the Qualcomm Atheros AR9331 built-in Ethernet + switch. ++ ++config NET_DSA_QCA8K ++ tristate "Qualcomm Atheros QCA8K Ethernet switch family support" ++ select NET_DSA_TAG_QCA ++ select REGMAP ++ help ++ This enables support for the Qualcomm Atheros QCA8K Ethernet ++ switch chips. +--- a/drivers/net/dsa/qca/Makefile ++++ b/drivers/net/dsa/qca/Makefile +@@ -1,2 +1,3 @@ + # SPDX-License-Identifier: GPL-2.0-only + obj-$(CONFIG_NET_DSA_AR9331) += ar9331.o ++obj-$(CONFIG_NET_DSA_QCA8K) += qca8k.o +--- /dev/null ++++ b/drivers/net/dsa/qca/qca8k.c +@@ -0,0 +1,3243 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (C) 2009 Felix Fietkau ++ * Copyright (C) 2011-2012 Gabor Juhos ++ * Copyright (c) 2015, 2019, The Linux Foundation. All rights reserved. ++ * Copyright (c) 2016 John Crispin ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "qca8k.h" ++ ++#define MIB_DESC(_s, _o, _n) \ ++ { \ ++ .size = (_s), \ ++ .offset = (_o), \ ++ .name = (_n), \ ++ } ++ ++static const struct qca8k_mib_desc ar8327_mib[] = { ++ MIB_DESC(1, 0x00, "RxBroad"), ++ MIB_DESC(1, 0x04, "RxPause"), ++ MIB_DESC(1, 0x08, "RxMulti"), ++ MIB_DESC(1, 0x0c, "RxFcsErr"), ++ MIB_DESC(1, 0x10, "RxAlignErr"), ++ MIB_DESC(1, 0x14, "RxRunt"), ++ MIB_DESC(1, 0x18, "RxFragment"), ++ MIB_DESC(1, 0x1c, "Rx64Byte"), ++ MIB_DESC(1, 0x20, "Rx128Byte"), ++ MIB_DESC(1, 0x24, "Rx256Byte"), ++ MIB_DESC(1, 0x28, "Rx512Byte"), ++ MIB_DESC(1, 0x2c, "Rx1024Byte"), ++ MIB_DESC(1, 0x30, "Rx1518Byte"), ++ MIB_DESC(1, 0x34, "RxMaxByte"), ++ MIB_DESC(1, 0x38, "RxTooLong"), ++ MIB_DESC(2, 0x3c, "RxGoodByte"), ++ MIB_DESC(2, 0x44, "RxBadByte"), ++ MIB_DESC(1, 0x4c, "RxOverFlow"), ++ MIB_DESC(1, 0x50, "Filtered"), ++ MIB_DESC(1, 0x54, "TxBroad"), ++ MIB_DESC(1, 0x58, "TxPause"), ++ MIB_DESC(1, 0x5c, "TxMulti"), ++ MIB_DESC(1, 0x60, "TxUnderRun"), ++ MIB_DESC(1, 0x64, "Tx64Byte"), ++ MIB_DESC(1, 0x68, "Tx128Byte"), ++ MIB_DESC(1, 0x6c, "Tx256Byte"), ++ MIB_DESC(1, 0x70, "Tx512Byte"), ++ MIB_DESC(1, 0x74, "Tx1024Byte"), ++ MIB_DESC(1, 0x78, "Tx1518Byte"), ++ MIB_DESC(1, 0x7c, "TxMaxByte"), ++ MIB_DESC(1, 0x80, "TxOverSize"), ++ MIB_DESC(2, 0x84, "TxByte"), ++ MIB_DESC(1, 0x8c, "TxCollision"), ++ MIB_DESC(1, 0x90, "TxAbortCol"), ++ MIB_DESC(1, 0x94, "TxMultiCol"), ++ MIB_DESC(1, 0x98, "TxSingleCol"), ++ 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"), ++}; ++ ++static void ++qca8k_split_addr(u32 regaddr, u16 *r1, u16 *r2, u16 *page) ++{ ++ regaddr >>= 1; ++ *r1 = regaddr & 0x1e; ++ ++ regaddr >>= 5; ++ *r2 = regaddr & 0x7; ++ ++ regaddr >>= 3; ++ *page = regaddr & 0x3ff; ++} ++ ++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; ++ ++ ret = bus->read(bus, phy_id, regnum); ++ if (ret >= 0) { ++ *val = ret; ++ ret = bus->read(bus, phy_id, regnum + 1); ++ *val |= ret << 16; ++ } ++ ++ if (ret < 0) { ++ dev_err_ratelimited(&bus->dev, ++ "failed to read qca8k 32bit register\n"); ++ *val = 0; ++ return ret; ++ } ++ ++ return 0; ++} ++ ++static void ++qca8k_mii_write32(struct qca8k_priv *priv, int phy_id, u32 regnum, u32 val) ++{ ++ u16 lo, hi; ++ int ret; ++ ++ lo = val & 0xffff; ++ hi = (u16)(val >> 16); ++ ++ ret = qca8k_set_lo(priv, phy_id, regnum, lo); ++ if (ret >= 0) ++ ret = qca8k_set_hi(priv, phy_id, regnum + 1, hi); ++} ++ ++static int ++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 == *cached_page) ++ return 0; ++ ++ ret = bus->write(bus, 0x18, 0, page); ++ if (ret < 0) { ++ dev_err_ratelimited(&bus->dev, ++ "failed to set qca8k page\n"); ++ return ret; ++ } ++ ++ *cached_page = page; ++ usleep_range(1000, 2000); ++ return 0; ++} ++ ++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 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. ++ * 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); ++ } ++ ++ complete(&mgmt_eth_data->rw_done); ++} ++ ++static struct sk_buff *qca8k_alloc_mdio_header(enum mdio_cmd cmd, u32 reg, u32 *val, ++ 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); ++ ++ 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, 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); ++ ++ if (cmd == MDIO_WRITE) ++ mgmt_ethhdr->mdio_data = *val; ++ ++ mgmt_ethhdr->hdr = htons(hdr); ++ ++ 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; ++} ++ ++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, 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_READ, reg, NULL, ++ QCA8K_ETHERNET_MDIO_PRIORITY, len); ++ 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]; ++ 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); ++ ++ if (ret <= 0) ++ return -ETIMEDOUT; ++ ++ if (!ack) ++ return -EINVAL; ++ ++ return 0; ++} ++ ++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, len); ++ 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, sizeof(val)); ++ if (ret) ++ return ret; ++ ++ val &= ~mask; ++ val |= write_val; ++ ++ return qca8k_write_eth(priv, reg, &val, sizeof(val)); ++} ++ ++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; ++ struct mii_bus *bus = priv->bus; ++ u16 r1, r2, page; ++ int ret; ++ ++ if (!qca8k_read_eth(priv, reg, val, sizeof(*val))) ++ return 0; ++ ++ qca8k_split_addr(reg, &r1, &r2, &page); ++ ++ mutex_lock_nested(&bus->mdio_lock, MDIO_MUTEX_NESTED); ++ ++ ret = qca8k_set_page(priv, page); ++ if (ret < 0) ++ goto exit; ++ ++ ret = qca8k_mii_read32(bus, 0x10 | r2, r1, val); ++ ++exit: ++ mutex_unlock(&bus->mdio_lock); ++ return ret; ++} ++ ++static int ++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; ++ ++ if (!qca8k_write_eth(priv, reg, &val, sizeof(val))) ++ return 0; ++ ++ qca8k_split_addr(reg, &r1, &r2, &page); ++ ++ mutex_lock_nested(&bus->mdio_lock, MDIO_MUTEX_NESTED); ++ ++ ret = qca8k_set_page(priv, page); ++ if (ret < 0) ++ goto exit; ++ ++ qca8k_mii_write32(priv, 0x10 | r2, r1, val); ++ ++exit: ++ mutex_unlock(&bus->mdio_lock); ++ return ret; ++} ++ ++static int ++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; ++ 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); ++ ++ ret = qca8k_set_page(priv, page); ++ if (ret < 0) ++ goto exit; ++ ++ ret = qca8k_mii_read32(bus, 0x10 | r2, r1, &val); ++ if (ret < 0) ++ goto exit; ++ ++ val &= ~mask; ++ val |= write_val; ++ qca8k_mii_write32(priv, 0x10 | r2, r1, val); ++ ++exit: ++ mutex_unlock(&bus->mdio_lock); ++ ++ return ret; ++} ++ ++static const struct regmap_range qca8k_readable_ranges[] = { ++ regmap_reg_range(0x0000, 0x00e4), /* Global control */ ++ regmap_reg_range(0x0100, 0x0168), /* EEE control */ ++ regmap_reg_range(0x0200, 0x0270), /* Parser control */ ++ regmap_reg_range(0x0400, 0x0454), /* ACL */ ++ regmap_reg_range(0x0600, 0x0718), /* Lookup */ ++ regmap_reg_range(0x0800, 0x0b70), /* QM */ ++ regmap_reg_range(0x0c00, 0x0c80), /* PKT */ ++ regmap_reg_range(0x0e00, 0x0e98), /* L3 */ ++ regmap_reg_range(0x1000, 0x10ac), /* MIB - Port0 */ ++ regmap_reg_range(0x1100, 0x11ac), /* MIB - Port1 */ ++ regmap_reg_range(0x1200, 0x12ac), /* MIB - Port2 */ ++ regmap_reg_range(0x1300, 0x13ac), /* MIB - Port3 */ ++ regmap_reg_range(0x1400, 0x14ac), /* MIB - Port4 */ ++ regmap_reg_range(0x1500, 0x15ac), /* MIB - Port5 */ ++ regmap_reg_range(0x1600, 0x16ac), /* MIB - Port6 */ ++ ++}; ++ ++static const struct regmap_access_table qca8k_readable_table = { ++ .yes_ranges = qca8k_readable_ranges, ++ .n_yes_ranges = ARRAY_SIZE(qca8k_readable_ranges), ++}; ++ ++static struct regmap_config qca8k_regmap_config = { ++ .reg_bits = 16, ++ .val_bits = 32, ++ .reg_stride = 4, ++ .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) ++{ ++ u32 val; ++ ++ return regmap_read_poll_timeout(priv->regmap, reg, val, !(val & mask), 0, ++ QCA8K_BUSY_WAIT_TIMEOUT * USEC_PER_MSEC); ++} ++ ++static int ++qca8k_fdb_read(struct qca8k_priv *priv, struct qca8k_fdb *fdb) ++{ ++ u32 reg[3]; ++ int ret; ++ ++ /* load the ARL table into an array */ ++ 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]); ++ /* aging - 67:64 */ ++ fdb->aging = FIELD_GET(QCA8K_ATU_STATUS_MASK, reg[2]); ++ /* portmask - 54:48 */ ++ fdb->port_mask = FIELD_GET(QCA8K_ATU_PORT_MASK, reg[1]); ++ /* mac - 47:0 */ ++ 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; ++} ++ ++static void ++qca8k_fdb_write(struct qca8k_priv *priv, u16 vid, u8 port_mask, const u8 *mac, ++ u8 aging) ++{ ++ u32 reg[3] = { 0 }; ++ ++ /* vid - 83:72 */ ++ reg[2] = FIELD_PREP(QCA8K_ATU_VID_MASK, vid); ++ /* aging - 67:64 */ ++ reg[2] |= FIELD_PREP(QCA8K_ATU_STATUS_MASK, aging); ++ /* portmask - 54:48 */ ++ reg[1] = FIELD_PREP(QCA8K_ATU_PORT_MASK, port_mask); ++ /* mac - 47:0 */ ++ 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 */ ++ qca8k_bulk_write(priv, QCA8K_REG_ATU_DATA0, reg, sizeof(reg)); ++} ++ ++static int ++qca8k_fdb_access(struct qca8k_priv *priv, enum qca8k_fdb_cmd cmd, int port) ++{ ++ u32 reg; ++ int ret; ++ ++ /* Set the command and FDB index */ ++ reg = QCA8K_ATU_FUNC_BUSY; ++ reg |= cmd; ++ if (port >= 0) { ++ reg |= QCA8K_ATU_FUNC_PORT_EN; ++ reg |= FIELD_PREP(QCA8K_ATU_FUNC_PORT_MASK, port); ++ } ++ ++ /* Write the function register triggering the table access */ ++ ret = qca8k_write(priv, QCA8K_REG_ATU_FUNC, reg); ++ if (ret) ++ return ret; ++ ++ /* wait for completion */ ++ ret = qca8k_busy_wait(priv, QCA8K_REG_ATU_FUNC, QCA8K_ATU_FUNC_BUSY); ++ if (ret) ++ return ret; ++ ++ /* Check for table full violation when adding an entry */ ++ if (cmd == QCA8K_FDB_LOAD) { ++ ret = qca8k_read(priv, QCA8K_REG_ATU_FUNC, ®); ++ if (ret < 0) ++ return ret; ++ if (reg & QCA8K_ATU_FUNC_FULL) ++ return -1; ++ } ++ ++ return 0; ++} ++ ++static int ++qca8k_fdb_next(struct qca8k_priv *priv, struct qca8k_fdb *fdb, int port) ++{ ++ int ret; ++ ++ qca8k_fdb_write(priv, fdb->vid, fdb->port_mask, fdb->mac, fdb->aging); ++ ret = qca8k_fdb_access(priv, QCA8K_FDB_NEXT, port); ++ if (ret < 0) ++ return ret; ++ ++ return qca8k_fdb_read(priv, fdb); ++} ++ ++static int ++qca8k_fdb_add(struct qca8k_priv *priv, const u8 *mac, u16 port_mask, ++ u16 vid, u8 aging) ++{ ++ int ret; ++ ++ mutex_lock(&priv->reg_mutex); ++ qca8k_fdb_write(priv, vid, port_mask, mac, aging); ++ ret = qca8k_fdb_access(priv, QCA8K_FDB_LOAD, -1); ++ mutex_unlock(&priv->reg_mutex); ++ ++ return ret; ++} ++ ++static int ++qca8k_fdb_del(struct qca8k_priv *priv, const u8 *mac, u16 port_mask, u16 vid) ++{ ++ int ret; ++ ++ mutex_lock(&priv->reg_mutex); ++ qca8k_fdb_write(priv, vid, port_mask, mac, 0); ++ ret = qca8k_fdb_access(priv, QCA8K_FDB_PURGE, -1); ++ mutex_unlock(&priv->reg_mutex); ++ ++ return ret; ++} ++ ++static void ++qca8k_fdb_flush(struct qca8k_priv *priv) ++{ ++ mutex_lock(&priv->reg_mutex); ++ qca8k_fdb_access(priv, QCA8K_FDB_FLUSH, -1); ++ mutex_unlock(&priv->reg_mutex); ++} ++ ++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; ++ int ret; ++ ++ /* Set the command and VLAN index */ ++ reg = QCA8K_VTU_FUNC1_BUSY; ++ reg |= cmd; ++ 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); ++ if (ret) ++ return ret; ++ ++ /* wait for completion */ ++ ret = qca8k_busy_wait(priv, QCA8K_REG_VTU_FUNC1, QCA8K_VTU_FUNC1_BUSY); ++ if (ret) ++ return ret; ++ ++ /* Check for table full violation when adding an entry */ ++ if (cmd == QCA8K_VLAN_LOAD) { ++ ret = qca8k_read(priv, QCA8K_REG_VTU_FUNC1, ®); ++ if (ret < 0) ++ return ret; ++ if (reg & QCA8K_VTU_FUNC1_FULL) ++ return -ENOMEM; ++ } ++ ++ return 0; ++} ++ ++static int ++qca8k_vlan_add(struct qca8k_priv *priv, u8 port, u16 vid, bool untagged) ++{ ++ u32 reg; ++ int ret; ++ ++ /* ++ We do the right thing with VLAN 0 and treat it as untagged while ++ preserving the tag on egress. ++ */ ++ if (vid == 0) ++ return 0; ++ ++ mutex_lock(&priv->reg_mutex); ++ ret = qca8k_vlan_access(priv, QCA8K_VLAN_READ, vid); ++ if (ret < 0) ++ goto out; ++ ++ ret = qca8k_read(priv, QCA8K_REG_VTU_FUNC0, ®); ++ if (ret < 0) ++ goto out; ++ reg |= QCA8K_VTU_FUNC0_VALID | QCA8K_VTU_FUNC0_IVL_EN; ++ reg &= ~QCA8K_VTU_FUNC0_EG_MODE_PORT_MASK(port); ++ if (untagged) ++ reg |= QCA8K_VTU_FUNC0_EG_MODE_PORT_UNTAG(port); ++ else ++ reg |= QCA8K_VTU_FUNC0_EG_MODE_PORT_TAG(port); ++ ++ ret = qca8k_write(priv, QCA8K_REG_VTU_FUNC0, reg); ++ if (ret) ++ goto out; ++ ret = qca8k_vlan_access(priv, QCA8K_VLAN_LOAD, vid); ++ ++out: ++ mutex_unlock(&priv->reg_mutex); ++ ++ return ret; ++} ++ ++static int ++qca8k_vlan_del(struct qca8k_priv *priv, u8 port, u16 vid) ++{ ++ u32 reg, mask; ++ int ret, i; ++ bool del; ++ ++ mutex_lock(&priv->reg_mutex); ++ ret = qca8k_vlan_access(priv, QCA8K_VLAN_READ, vid); ++ if (ret < 0) ++ goto out; ++ ++ ret = qca8k_read(priv, QCA8K_REG_VTU_FUNC0, ®); ++ if (ret < 0) ++ goto out; ++ 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_PORT_NOT(i); ++ ++ if ((reg & mask) != mask) { ++ del = false; ++ break; ++ } ++ } ++ ++ if (del) { ++ ret = qca8k_vlan_access(priv, QCA8K_VLAN_PURGE, vid); ++ } else { ++ ret = qca8k_write(priv, QCA8K_REG_VTU_FUNC0, reg); ++ if (ret) ++ goto out; ++ ret = qca8k_vlan_access(priv, QCA8K_VLAN_LOAD, vid); ++ } ++ ++out: ++ mutex_unlock(&priv->reg_mutex); ++ ++ return ret; ++} ++ ++static int ++qca8k_mib_init(struct qca8k_priv *priv) ++{ ++ int ret; ++ ++ mutex_lock(&priv->reg_mutex); ++ 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; ++ ++ ret = qca8k_busy_wait(priv, QCA8K_REG_MIB, QCA8K_MIB_BUSY); ++ if (ret) ++ goto exit; ++ ++ ret = regmap_set_bits(priv->regmap, QCA8K_REG_MIB, QCA8K_MIB_CPU_KEEP); ++ if (ret) ++ goto exit; ++ ++ ret = qca8k_write(priv, QCA8K_REG_MODULE_EN, QCA8K_MODULE_EN_MIB); ++ ++exit: ++ mutex_unlock(&priv->reg_mutex); ++ return ret; ++} ++ ++static void ++qca8k_port_set_status(struct qca8k_priv *priv, int port, int enable) ++{ ++ u32 mask = QCA8K_PORT_STATUS_TXMAC | QCA8K_PORT_STATUS_RXMAC; ++ ++ /* Port 0 and 6 have no internal PHY */ ++ if (port > 0 && port < 6) ++ mask |= QCA8K_PORT_STATUS_LINK_AUTO; ++ ++ if (enable) ++ regmap_set_bits(priv->regmap, QCA8K_REG_PORT_STATUS(port), mask); ++ else ++ 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, 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, sizeof(clear_val)); ++ if (!clear_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, sizeof(clear_val)); ++ if (!read_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) ++{ ++ /* From Andrew Lunn: ++ * Port 0 has no internal phy. ++ * Port 1 has an internal PHY at MDIO address 0. ++ * Port 2 has an internal PHY at MDIO address 1. ++ * ... ++ * Port 5 has an internal PHY at MDIO address 4. ++ * Port 6 has no internal PHY. ++ */ ++ ++ return port - 1; ++} ++ ++static int ++qca8k_mdio_busy_wait(struct mii_bus *bus, u32 reg, u32 mask) ++{ ++ u16 r1, r2, page; ++ u32 val; ++ int ret, ret1; ++ ++ qca8k_split_addr(reg, &r1, &r2, &page); ++ ++ ret = read_poll_timeout(qca8k_mii_read32, ret1, !(val & mask), 0, ++ QCA8K_BUSY_WAIT_TIMEOUT * USEC_PER_MSEC, false, ++ bus, 0x10 | r2, r1, &val); ++ ++ /* Check if qca8k_read has failed for a different reason ++ * before returnting -ETIMEDOUT ++ */ ++ if (ret < 0 && ret1 < 0) ++ return ret1; ++ ++ return ret; ++} ++ ++static int ++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; ++ ++ if (regnum >= QCA8K_MDIO_MASTER_MAX_REG) ++ return -EINVAL; ++ ++ val = QCA8K_MDIO_MASTER_BUSY | QCA8K_MDIO_MASTER_EN | ++ QCA8K_MDIO_MASTER_WRITE | QCA8K_MDIO_MASTER_PHY_ADDR(phy) | ++ QCA8K_MDIO_MASTER_REG_ADDR(regnum) | ++ QCA8K_MDIO_MASTER_DATA(data); ++ ++ qca8k_split_addr(QCA8K_MDIO_MASTER_CTRL, &r1, &r2, &page); ++ ++ mutex_lock_nested(&bus->mdio_lock, MDIO_MUTEX_NESTED); ++ ++ ret = qca8k_set_page(priv, page); ++ if (ret) ++ goto exit; ++ ++ 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(priv, 0x10 | r2, r1, 0); ++ ++ mutex_unlock(&bus->mdio_lock); ++ ++ return ret; ++} ++ ++static int ++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; ++ ++ if (regnum >= QCA8K_MDIO_MASTER_MAX_REG) ++ return -EINVAL; ++ ++ val = QCA8K_MDIO_MASTER_BUSY | QCA8K_MDIO_MASTER_EN | ++ QCA8K_MDIO_MASTER_READ | QCA8K_MDIO_MASTER_PHY_ADDR(phy) | ++ QCA8K_MDIO_MASTER_REG_ADDR(regnum); ++ ++ qca8k_split_addr(QCA8K_MDIO_MASTER_CTRL, &r1, &r2, &page); ++ ++ mutex_lock_nested(&bus->mdio_lock, MDIO_MUTEX_NESTED); ++ ++ ret = qca8k_set_page(priv, page); ++ if (ret) ++ goto exit; ++ ++ qca8k_mii_write32(priv, 0x10 | r2, r1, val); ++ ++ ret = qca8k_mdio_busy_wait(bus, QCA8K_MDIO_MASTER_CTRL, ++ QCA8K_MDIO_MASTER_BUSY); ++ if (ret) ++ goto exit; ++ ++ ret = qca8k_mii_read32(bus, 0x10 | r2, r1, &val); ++ ++exit: ++ /* even if the busy_wait timeouts try to clear the MASTER_EN */ ++ qca8k_mii_write32(priv, 0x10 | r2, r1, 0); ++ ++ mutex_unlock(&bus->mdio_lock); ++ ++ if (ret >= 0) ++ ret = val & QCA8K_MDIO_MASTER_DATA_MASK; ++ ++ return ret; ++} ++ ++static int ++qca8k_internal_mdio_write(struct mii_bus *slave_bus, int phy, int regnum, u16 data) ++{ ++ struct qca8k_priv *priv = slave_bus->priv; ++ 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(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; ++ 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; ++ ++ ret = qca8k_mdio_read(priv, phy, regnum); ++ ++ if (ret < 0) ++ return 0xffff; ++ ++ return ret; ++} ++ ++static int ++qca8k_legacy_mdio_write(struct mii_bus *slave_bus, int port, int regnum, u16 data) ++{ ++ port = qca8k_port_to_phy(port) % PHY_MAX_ADDR; ++ ++ return qca8k_internal_mdio_write(slave_bus, port, regnum, data); ++} ++ ++static int ++qca8k_legacy_mdio_read(struct mii_bus *slave_bus, int port, int regnum) ++{ ++ port = qca8k_port_to_phy(port) % PHY_MAX_ADDR; ++ ++ return qca8k_internal_mdio_read(slave_bus, port, regnum); ++} ++ ++static int ++qca8k_mdio_register(struct qca8k_priv *priv) ++{ ++ struct dsa_switch *ds = priv->ds; ++ struct device_node *mdio; ++ struct mii_bus *bus; ++ ++ bus = devm_mdiobus_alloc(ds->dev); ++ if (!bus) ++ return -ENOMEM; ++ ++ bus->priv = (void *)priv; ++ snprintf(bus->id, MII_BUS_ID_SIZE, "qca8k-%d.%d", ++ ds->dst->index, ds->index); ++ bus->parent = ds->dev; ++ bus->phy_mask = ~ds->phys_mii_mask; ++ ds->slave_mii_bus = bus; ++ ++ /* Check if the devicetree declare the port:phy mapping */ ++ mdio = of_get_child_by_name(priv->dev->of_node, "mdio"); ++ if (of_device_is_available(mdio)) { ++ bus->name = "qca8k slave mii"; ++ bus->read = qca8k_internal_mdio_read; ++ bus->write = qca8k_internal_mdio_write; ++ return devm_of_mdiobus_register(priv->dev, bus, mdio); ++ } ++ ++ /* If a mapping can't be found the legacy mapping is used, ++ * using the qca8k_port_to_phy function ++ */ ++ bus->name = "qca8k-legacy slave mii"; ++ bus->read = qca8k_legacy_mdio_read; ++ bus->write = qca8k_legacy_mdio_write; ++ return devm_mdiobus_register(priv->dev, bus); ++} ++ ++static int ++qca8k_setup_mdio_bus(struct qca8k_priv *priv) ++{ ++ u32 internal_mdio_mask = 0, external_mdio_mask = 0, reg; ++ struct device_node *ports, *port; ++ phy_interface_t mode; ++ int err; ++ ++ ports = of_get_child_by_name(priv->dev->of_node, "ports"); ++ if (!ports) ++ ports = of_get_child_by_name(priv->dev->of_node, "ethernet-ports"); ++ ++ if (!ports) ++ return -EINVAL; ++ ++ for_each_available_child_of_node(ports, port) { ++ err = of_property_read_u32(port, "reg", ®); ++ if (err) { ++ of_node_put(port); ++ of_node_put(ports); ++ return err; ++ } ++ ++ if (!dsa_is_user_port(priv->ds, reg)) ++ continue; ++ ++ of_get_phy_mode(port, &mode); ++ ++ if (of_property_read_bool(port, "phy-handle") && ++ mode != PHY_INTERFACE_MODE_INTERNAL) ++ external_mdio_mask |= BIT(reg); ++ else ++ internal_mdio_mask |= BIT(reg); ++ } ++ ++ of_node_put(ports); ++ if (!external_mdio_mask && !internal_mdio_mask) { ++ dev_err(priv->dev, "no PHYs are defined.\n"); ++ return -EINVAL; ++ } ++ ++ /* The QCA8K_MDIO_MASTER_EN Bit, which grants access to PHYs through ++ * the MDIO_MASTER register also _disconnects_ the external MDC ++ * passthrough to the internal PHYs. It's not possible to use both ++ * configurations at the same time! ++ * ++ * Because this came up during the review process: ++ * If the external mdio-bus driver is capable magically disabling ++ * the QCA8K_MDIO_MASTER_EN and mutex/spin-locking out the qca8k's ++ * accessors for the time being, it would be possible to pull this ++ * off. ++ */ ++ if (!!external_mdio_mask && !!internal_mdio_mask) { ++ dev_err(priv->dev, "either internal or external mdio bus configuration is supported.\n"); ++ return -EINVAL; ++ } ++ ++ if (external_mdio_mask) { ++ /* Make sure to disable the internal mdio bus in cases ++ * a dt-overlay and driver reload changed the configuration ++ */ ++ ++ return regmap_clear_bits(priv->regmap, QCA8K_MDIO_MASTER_CTRL, ++ QCA8K_MDIO_MASTER_EN); ++ } ++ ++ return qca8k_mdio_register(priv); ++} ++ ++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_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_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; ++ ++ /* 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) { ++ 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, ++ val); ++ 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 = -1, ret; ++ struct device_node *port_dn; ++ phy_interface_t mode; ++ struct dsa_port *dp; ++ u32 delay; ++ ++ /* 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; ++ cpu_port_index++; ++ ++ if (!of_device_is_available(port_dn)) ++ continue; ++ ++ ret = of_get_phy_mode(port_dn, &mode); ++ if (ret) ++ continue; ++ ++ 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: ++ case PHY_INTERFACE_MODE_SGMII: ++ 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 (!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; ++ } ++ ++ priv->ports_config.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 (!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; ++ } ++ ++ priv->ports_config.rgmii_rx_delay[cpu_port_index] = delay; ++ ++ /* 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->ports_config.sgmii_tx_clk_falling_edge = true; ++ ++ if (of_property_read_bool(port_dn, "qca,sgmii-rxclk-falling-edge")) ++ priv->ports_config.sgmii_rx_clk_falling_edge = true; ++ ++ if (of_property_read_bool(port_dn, "qca,sgmii-enable-pll")) { ++ 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->ports_config.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; ++ } ++ } ++ ++ return 0; ++} ++ ++static int ++qca8k_setup(struct dsa_switch *ds) ++{ ++ struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv; ++ int cpu_port, ret, i; ++ u32 mask; ++ ++ 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 */ ++ ret = qca8k_parse_port_config(priv); ++ if (ret) ++ return ret; ++ ++ ret = qca8k_setup_mdio_bus(priv); ++ 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; ++ ++ /* Make sure MAC06 is disabled */ ++ 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 = 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; ++ } ++ ++ /* Enable MIB counters */ ++ ret = qca8k_mib_init(priv); ++ if (ret) ++ dev_warn(priv->dev, "mib init failed"); ++ ++ /* 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; ++ ++ /* 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), ++ 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; ++ } ++ } ++ ++ /* 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 ++ * 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, ++ 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; ++ ++ /* 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(i), ++ QCA8K_PORT_LOOKUP_MEMBER, dsa_user_ports(ds)); ++ if (ret) ++ return ret; ++ } ++ ++ /* Individual user ports get connected to CPU port only */ ++ if (dsa_is_user_port(ds, i)) { ++ ret = qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(i), ++ QCA8K_PORT_LOOKUP_MEMBER, ++ BIT(cpu_port)); ++ if (ret) ++ return ret; ++ ++ /* Enable ARP Auto-learning by default */ ++ ret = regmap_set_bits(priv->regmap, QCA8K_PORT_LOOKUP_CTRL(i), ++ QCA8K_PORT_LOOKUP_LEARN); ++ if (ret) ++ return ret; ++ ++ /* For port based vlans to work we need to set the ++ * default egress vid ++ */ ++ ret = qca8k_rmw(priv, QCA8K_EGRESS_VLAN(i), ++ QCA8K_EGREES_VLAN_PORT_MASK(i), ++ QCA8K_EGREES_VLAN_PORT(i, QCA8K_PORT_VID_DEF)); ++ if (ret) ++ return ret; ++ ++ ret = qca8k_write(priv, QCA8K_REG_PORT_VLAN_CTRL0(i), ++ QCA8K_PORT_VLAN_CVID(QCA8K_PORT_VID_DEF) | ++ QCA8K_PORT_VLAN_SVID(QCA8K_PORT_VID_DEF)); ++ 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) { ++ switch (i) { ++ /* The 2 CPU port and port 5 requires some different ++ * priority than any other ports. ++ */ ++ case 0: ++ case 5: ++ case 6: ++ mask = QCA8K_PORT_HOL_CTRL0_EG_PRI0(0x3) | ++ QCA8K_PORT_HOL_CTRL0_EG_PRI1(0x4) | ++ QCA8K_PORT_HOL_CTRL0_EG_PRI2(0x4) | ++ QCA8K_PORT_HOL_CTRL0_EG_PRI3(0x4) | ++ QCA8K_PORT_HOL_CTRL0_EG_PRI4(0x6) | ++ QCA8K_PORT_HOL_CTRL0_EG_PRI5(0x8) | ++ QCA8K_PORT_HOL_CTRL0_EG_PORT(0x1e); ++ break; ++ default: ++ mask = QCA8K_PORT_HOL_CTRL0_EG_PRI0(0x3) | ++ QCA8K_PORT_HOL_CTRL0_EG_PRI1(0x4) | ++ QCA8K_PORT_HOL_CTRL0_EG_PRI2(0x6) | ++ QCA8K_PORT_HOL_CTRL0_EG_PRI3(0x8) | ++ QCA8K_PORT_HOL_CTRL0_EG_PORT(0x19); ++ } ++ qca8k_write(priv, QCA8K_REG_PORT_HOL_CTRL0(i), mask); ++ ++ mask = QCA8K_PORT_HOL_CTRL1_ING(0x6) | ++ QCA8K_PORT_HOL_CTRL1_EG_PRI_BUF_EN | ++ 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_MASK | ++ QCA8K_PORT_HOL_CTRL1_EG_PRI_BUF_EN | ++ QCA8K_PORT_HOL_CTRL1_EG_PORT_BUF_EN | ++ QCA8K_PORT_HOL_CTRL1_WRED_EN, ++ mask); ++ } ++ } ++ ++ /* Special GLOBAL_FC_THRESH value are needed for ar8327 switch */ ++ if (priv->switch_id == QCA8K_ID_QCA8327) { ++ 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_MASK | ++ QCA8K_GLOBAL_FC_GOL_XOFF_THRES_MASK, ++ mask); ++ } ++ ++ /* Setup our port MTUs to match power on defaults */ ++ ret = qca8k_write(priv, QCA8K_MAX_FRAME_SIZE, ETH_FRAME_LEN + ETH_FCS_LEN); ++ if (ret) ++ dev_warn(priv->dev, "failed setting MTU settings"); ++ ++ /* Flush the FDB table */ ++ qca8k_fdb_flush(priv); ++ ++ /* 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; ++ ++ /* Set max number of LAGs supported */ ++ ds->num_lag_ids = QCA8K_NUM_LAGS; ++ ++ return 0; ++} ++ ++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->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->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; ++ } ++ ++ /* 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; ++ ++ switch (port) { ++ case 0: /* 1st CPU port */ ++ if (state->interface != PHY_INTERFACE_MODE_RGMII && ++ state->interface != PHY_INTERFACE_MODE_RGMII_ID && ++ state->interface != PHY_INTERFACE_MODE_RGMII_TXID && ++ state->interface != PHY_INTERFACE_MODE_RGMII_RXID && ++ state->interface != PHY_INTERFACE_MODE_SGMII) ++ return; ++ ++ reg = QCA8K_REG_PORT0_PAD_CTRL; ++ cpu_port_index = QCA8K_CPU_PORT0; ++ break; ++ case 1: ++ case 2: ++ case 3: ++ case 4: ++ case 5: ++ /* Internal PHY, nothing to do */ ++ return; ++ case 6: /* 2nd CPU port / external PHY */ ++ if (state->interface != PHY_INTERFACE_MODE_RGMII && ++ state->interface != PHY_INTERFACE_MODE_RGMII_ID && ++ state->interface != PHY_INTERFACE_MODE_RGMII_TXID && ++ state->interface != PHY_INTERFACE_MODE_RGMII_RXID && ++ state->interface != PHY_INTERFACE_MODE_SGMII && ++ state->interface != PHY_INTERFACE_MODE_1000BASEX) ++ 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); ++ return; ++ } ++ ++ if (port != 6 && phylink_autoneg_inband(mode)) { ++ dev_err(ds->dev, "%s: in-band negotiation unsupported\n", ++ __func__); ++ return; ++ } ++ ++ switch (state->interface) { ++ case PHY_INTERFACE_MODE_RGMII: ++ case PHY_INTERFACE_MODE_RGMII_ID: ++ case PHY_INTERFACE_MODE_RGMII_TXID: ++ case PHY_INTERFACE_MODE_RGMII_RXID: ++ qca8k_write(priv, reg, QCA8K_PORT_PAD_RGMII_EN); ++ ++ /* 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, ++ * rather than individual port registers. ++ */ ++ if (priv->switch_id == QCA8K_ID_QCA8337) ++ qca8k_write(priv, QCA8K_REG_PORT5_PAD_CTRL, ++ QCA8K_PORT_PAD_RGMII_RX_DELAY_EN); ++ break; ++ case PHY_INTERFACE_MODE_SGMII: ++ case PHY_INTERFACE_MODE_1000BASEX: ++ /* Enable SGMII on the port */ ++ qca8k_write(priv, reg, QCA8K_PORT_PAD_SGMII_EN); ++ ++ /* Enable/disable SerDes auto-negotiation as necessary */ ++ ret = qca8k_read(priv, QCA8K_REG_PWS, &val); ++ if (ret) ++ return; ++ if (phylink_autoneg_inband(mode)) ++ val &= ~QCA8K_PWS_SERDES_AEN_DIS; ++ else ++ val |= QCA8K_PWS_SERDES_AEN_DIS; ++ qca8k_write(priv, QCA8K_REG_PWS, val); ++ ++ /* Configure the SGMII parameters */ ++ ret = qca8k_read(priv, QCA8K_REG_SGMII_CTRL, &val); ++ if (ret) ++ return; ++ ++ val |= QCA8K_SGMII_EN_SD; ++ ++ if (priv->ports_config.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 */ ++ val &= ~QCA8K_SGMII_MODE_CTRL_MASK; ++ val |= QCA8K_SGMII_MODE_CTRL_PHY; ++ } else if (state->interface == PHY_INTERFACE_MODE_SGMII) { ++ val &= ~QCA8K_SGMII_MODE_CTRL_MASK; ++ val |= QCA8K_SGMII_MODE_CTRL_MAC; ++ } else if (state->interface == PHY_INTERFACE_MODE_1000BASEX) { ++ val &= ~QCA8K_SGMII_MODE_CTRL_MASK; ++ val |= QCA8K_SGMII_MODE_CTRL_BASEX; ++ } ++ ++ 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 ++ */ ++ 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->ports_config.sgmii_rx_clk_falling_edge) ++ val |= QCA8K_PORT0_PAD_SGMII_RXCLK_FALLING_EDGE; ++ ++ if (priv->ports_config.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", ++ phy_modes(state->interface), port); ++ return; ++ } ++} ++ ++static void ++qca8k_phylink_validate(struct dsa_switch *ds, int port, ++ unsigned long *supported, ++ struct phylink_link_state *state) ++{ ++ __ETHTOOL_DECLARE_LINK_MODE_MASK(mask) = { 0, }; ++ ++ switch (port) { ++ case 0: /* 1st CPU port */ ++ if (state->interface != PHY_INTERFACE_MODE_NA && ++ state->interface != PHY_INTERFACE_MODE_RGMII && ++ state->interface != PHY_INTERFACE_MODE_RGMII_ID && ++ state->interface != PHY_INTERFACE_MODE_RGMII_TXID && ++ state->interface != PHY_INTERFACE_MODE_RGMII_RXID && ++ state->interface != PHY_INTERFACE_MODE_SGMII) ++ goto unsupported; ++ break; ++ case 1: ++ case 2: ++ case 3: ++ case 4: ++ case 5: ++ /* Internal PHY */ ++ if (state->interface != PHY_INTERFACE_MODE_NA && ++ state->interface != PHY_INTERFACE_MODE_GMII && ++ state->interface != PHY_INTERFACE_MODE_INTERNAL) ++ goto unsupported; ++ break; ++ case 6: /* 2nd CPU port / external PHY */ ++ if (state->interface != PHY_INTERFACE_MODE_NA && ++ state->interface != PHY_INTERFACE_MODE_RGMII && ++ state->interface != PHY_INTERFACE_MODE_RGMII_ID && ++ state->interface != PHY_INTERFACE_MODE_RGMII_TXID && ++ state->interface != PHY_INTERFACE_MODE_RGMII_RXID && ++ state->interface != PHY_INTERFACE_MODE_SGMII && ++ state->interface != PHY_INTERFACE_MODE_1000BASEX) ++ goto unsupported; ++ break; ++ default: ++unsupported: ++ linkmode_zero(supported); ++ return; ++ } ++ ++ phylink_set_port_modes(mask); ++ phylink_set(mask, Autoneg); ++ ++ phylink_set(mask, 1000baseT_Full); ++ phylink_set(mask, 10baseT_Half); ++ phylink_set(mask, 10baseT_Full); ++ phylink_set(mask, 100baseT_Half); ++ phylink_set(mask, 100baseT_Full); ++ ++ if (state->interface == PHY_INTERFACE_MODE_1000BASEX) ++ phylink_set(mask, 1000baseX_Full); ++ ++ phylink_set(mask, Pause); ++ phylink_set(mask, Asym_Pause); ++ ++ linkmode_and(supported, supported, mask); ++ linkmode_and(state->advertising, state->advertising, mask); ++} ++ ++static int ++qca8k_phylink_mac_link_state(struct dsa_switch *ds, int port, ++ struct phylink_link_state *state) ++{ ++ struct qca8k_priv *priv = ds->priv; ++ u32 reg; ++ int ret; ++ ++ ret = qca8k_read(priv, QCA8K_REG_PORT_STATUS(port), ®); ++ if (ret < 0) ++ return ret; ++ ++ state->link = !!(reg & QCA8K_PORT_STATUS_LINK_UP); ++ state->an_complete = state->link; ++ state->an_enabled = !!(reg & QCA8K_PORT_STATUS_LINK_AUTO); ++ state->duplex = (reg & QCA8K_PORT_STATUS_DUPLEX) ? DUPLEX_FULL : ++ DUPLEX_HALF; ++ ++ switch (reg & QCA8K_PORT_STATUS_SPEED) { ++ case QCA8K_PORT_STATUS_SPEED_10: ++ state->speed = SPEED_10; ++ break; ++ case QCA8K_PORT_STATUS_SPEED_100: ++ state->speed = SPEED_100; ++ break; ++ case QCA8K_PORT_STATUS_SPEED_1000: ++ state->speed = SPEED_1000; ++ break; ++ default: ++ state->speed = SPEED_UNKNOWN; ++ break; ++ } ++ ++ state->pause = MLO_PAUSE_NONE; ++ if (reg & QCA8K_PORT_STATUS_RXFLOW) ++ state->pause |= MLO_PAUSE_RX; ++ if (reg & QCA8K_PORT_STATUS_TXFLOW) ++ state->pause |= MLO_PAUSE_TX; ++ ++ return 1; ++} ++ ++static void ++qca8k_phylink_mac_link_down(struct dsa_switch *ds, int port, unsigned int mode, ++ phy_interface_t interface) ++{ ++ struct qca8k_priv *priv = ds->priv; ++ ++ qca8k_port_set_status(priv, port, 0); ++} ++ ++static void ++qca8k_phylink_mac_link_up(struct dsa_switch *ds, int port, unsigned int mode, ++ phy_interface_t interface, struct phy_device *phydev, ++ int speed, int duplex, bool tx_pause, bool rx_pause) ++{ ++ struct qca8k_priv *priv = ds->priv; ++ u32 reg; ++ ++ if (phylink_autoneg_inband(mode)) { ++ reg = QCA8K_PORT_STATUS_LINK_AUTO; ++ } else { ++ switch (speed) { ++ case SPEED_10: ++ reg = QCA8K_PORT_STATUS_SPEED_10; ++ break; ++ case SPEED_100: ++ reg = QCA8K_PORT_STATUS_SPEED_100; ++ break; ++ case SPEED_1000: ++ reg = QCA8K_PORT_STATUS_SPEED_1000; ++ break; ++ default: ++ reg = QCA8K_PORT_STATUS_LINK_AUTO; ++ break; ++ } ++ ++ if (duplex == DUPLEX_FULL) ++ reg |= QCA8K_PORT_STATUS_DUPLEX; ++ ++ if (rx_pause || dsa_is_cpu_port(ds, port)) ++ reg |= QCA8K_PORT_STATUS_RXFLOW; ++ ++ if (tx_pause || dsa_is_cpu_port(ds, port)) ++ reg |= QCA8K_PORT_STATUS_TXFLOW; ++ } ++ ++ reg |= QCA8K_PORT_STATUS_TXMAC | QCA8K_PORT_STATUS_RXMAC; ++ ++ qca8k_write(priv, QCA8K_REG_PORT_STATUS(port), reg); ++} ++ ++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; ++ ++ 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); ++} ++ ++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) ++{ ++ 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; ++ ++ 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++) { ++ mib = &ar8327_mib[i]; ++ reg = QCA8K_PORT_MIB_COUNTER(port) + mib->offset; ++ ++ ret = qca8k_read(priv, reg, &val); ++ if (ret < 0) ++ continue; ++ ++ if (mib->size == 2) { ++ ret = qca8k_read(priv, reg + 4, &hi); ++ if (ret < 0) ++ continue; ++ } ++ ++ data[i] = val; ++ if (mib->size == 2) ++ data[i] |= (u64)hi << 32; ++ } ++} ++ ++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; ++ ++ match_data = of_device_get_match_data(priv->dev); ++ ++ return match_data->mib_count; ++} ++ ++static int ++qca8k_set_mac_eee(struct dsa_switch *ds, int port, struct ethtool_eee *eee) ++{ ++ struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv; ++ u32 lpi_en = QCA8K_REG_EEE_CTRL_LPI_EN(port); ++ u32 reg; ++ int ret; ++ ++ mutex_lock(&priv->reg_mutex); ++ ret = qca8k_read(priv, QCA8K_REG_EEE_CTRL, ®); ++ if (ret < 0) ++ goto exit; ++ ++ if (eee->eee_enabled) ++ reg |= lpi_en; ++ else ++ reg &= ~lpi_en; ++ ret = qca8k_write(priv, QCA8K_REG_EEE_CTRL, reg); ++ ++exit: ++ mutex_unlock(&priv->reg_mutex); ++ return ret; ++} ++ ++static int ++qca8k_get_mac_eee(struct dsa_switch *ds, int port, struct ethtool_eee *e) ++{ ++ /* Nothing to do on the port's MAC */ ++ return 0; ++} ++ ++static void ++qca8k_port_stp_state_set(struct dsa_switch *ds, int port, u8 state) ++{ ++ struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv; ++ u32 stp_state; ++ ++ switch (state) { ++ case BR_STATE_DISABLED: ++ stp_state = QCA8K_PORT_LOOKUP_STATE_DISABLED; ++ break; ++ case BR_STATE_BLOCKING: ++ stp_state = QCA8K_PORT_LOOKUP_STATE_BLOCKING; ++ break; ++ case BR_STATE_LISTENING: ++ stp_state = QCA8K_PORT_LOOKUP_STATE_LISTENING; ++ break; ++ case BR_STATE_LEARNING: ++ stp_state = QCA8K_PORT_LOOKUP_STATE_LEARNING; ++ break; ++ case BR_STATE_FORWARDING: ++ default: ++ stp_state = QCA8K_PORT_LOOKUP_STATE_FORWARD; ++ break; ++ } ++ ++ qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(port), ++ QCA8K_PORT_LOOKUP_STATE_MASK, stp_state); ++} ++ ++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, cpu_port; ++ int i, ret; ++ ++ cpu_port = dsa_to_port(ds, port)->cpu_dp->index; ++ port_mask = BIT(cpu_port); ++ ++ 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 ++ * in the bridge ++ */ ++ ret = regmap_set_bits(priv->regmap, ++ QCA8K_PORT_LOOKUP_CTRL(i), ++ BIT(port)); ++ if (ret) ++ return ret; ++ if (i != port) ++ port_mask |= BIT(i); ++ } ++ ++ /* Add all other ports to this ports portvlan mask */ ++ ret = qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(port), ++ QCA8K_PORT_LOOKUP_MEMBER, port_mask); ++ ++ return ret; ++} ++ ++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 cpu_port, i; ++ ++ cpu_port = dsa_to_port(ds, port)->cpu_dp->index; ++ ++ 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 ++ * in the bridge ++ */ ++ 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 ++ * this port ++ */ ++ qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(port), ++ 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_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) ++{ ++ struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv; ++ ++ qca8k_port_set_status(priv, port, 1); ++ priv->port_enabled_map |= BIT(port); ++ ++ if (dsa_is_user_port(ds, port)) ++ phy_support_asym_pause(phy); ++ ++ return 0; ++} ++ ++static void ++qca8k_port_disable(struct dsa_switch *ds, int port) ++{ ++ struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv; ++ ++ qca8k_port_set_status(priv, port, 0); ++ priv->port_enabled_map &= ~BIT(port); ++} ++ ++static int ++qca8k_port_change_mtu(struct dsa_switch *ds, int port, int new_mtu) ++{ ++ struct qca8k_priv *priv = ds->priv; ++ int ret; ++ ++ /* We have only have a general MTU setting. ++ * DSA always set the CPU port's MTU to the largest MTU of the slave ++ * ports. ++ * Setting MTU just for the CPU port is sufficient to correctly set a ++ * value for every port. ++ */ ++ if (!dsa_is_cpu_port(ds, port)) ++ return 0; ++ ++ /* To change the MAX_FRAME_SIZE the cpu ports must be off or ++ * the switch panics. ++ * Turn off both cpu ports before applying the new value to prevent ++ * this. ++ */ ++ if (priv->port_enabled_map & BIT(0)) ++ qca8k_port_set_status(priv, 0, 0); ++ ++ if (priv->port_enabled_map & BIT(6)) ++ qca8k_port_set_status(priv, 6, 0); ++ ++ /* Include L2 header / FCS length */ ++ ret = qca8k_write(priv, QCA8K_MAX_FRAME_SIZE, new_mtu + ETH_HLEN + ETH_FCS_LEN); ++ ++ if (priv->port_enabled_map & BIT(0)) ++ qca8k_port_set_status(priv, 0, 1); ++ ++ if (priv->port_enabled_map & BIT(6)) ++ qca8k_port_set_status(priv, 6, 1); ++ ++ return ret; ++} ++ ++static int ++qca8k_port_max_mtu(struct dsa_switch *ds, int port) ++{ ++ return QCA8K_MAX_MTU; ++} ++ ++static int ++qca8k_port_fdb_insert(struct qca8k_priv *priv, const u8 *addr, ++ u16 port_mask, u16 vid) ++{ ++ /* Set the vid to the port vlan id if no vid is set */ ++ if (!vid) ++ vid = QCA8K_PORT_VID_DEF; ++ ++ return qca8k_fdb_add(priv, addr, port_mask, vid, ++ QCA8K_ATU_STATUS_STATIC); ++} ++ ++static int ++qca8k_port_fdb_add(struct dsa_switch *ds, int port, ++ const unsigned char *addr, u16 vid) ++{ ++ struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv; ++ u16 port_mask = BIT(port); ++ ++ return qca8k_port_fdb_insert(priv, addr, port_mask, vid); ++} ++ ++static int ++qca8k_port_fdb_del(struct dsa_switch *ds, int port, ++ const unsigned char *addr, u16 vid) ++{ ++ struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv; ++ u16 port_mask = BIT(port); ++ ++ if (!vid) ++ vid = QCA8K_PORT_VID_DEF; ++ ++ return qca8k_fdb_del(priv, addr, port_mask, vid); ++} ++ ++static int ++qca8k_port_fdb_dump(struct dsa_switch *ds, int port, ++ dsa_fdb_dump_cb_t *cb, void *data) ++{ ++ struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv; ++ struct qca8k_fdb _fdb = { 0 }; ++ int cnt = QCA8K_NUM_FDB_RECORDS; ++ bool is_static; ++ int ret = 0; ++ ++ mutex_lock(&priv->reg_mutex); ++ while (cnt-- && !qca8k_fdb_next(priv, &_fdb, port)) { ++ if (!_fdb.aging) ++ break; ++ is_static = (_fdb.aging == QCA8K_ATU_STATUS_STATIC); ++ ret = cb(_fdb.mac, _fdb.vid, is_static, data); ++ if (ret) ++ break; ++ } ++ mutex_unlock(&priv->reg_mutex); ++ ++ return 0; ++} ++ ++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_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) ++{ ++ struct qca8k_priv *priv = ds->priv; ++ int ret; ++ ++ if (vlan_filtering) { ++ ret = qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(port), ++ 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_MASK, ++ QCA8K_PORT_LOOKUP_VLAN_MODE_NONE); ++ } ++ ++ return ret; ++} ++ ++static int ++qca8k_port_vlan_add(struct dsa_switch *ds, int port, ++ const struct switchdev_obj_port_vlan *vlan, ++ struct netlink_ext_ack *extack) ++{ ++ bool untagged = vlan->flags & BRIDGE_VLAN_INFO_UNTAGGED; ++ bool pvid = vlan->flags & BRIDGE_VLAN_INFO_PVID; ++ struct qca8k_priv *priv = ds->priv; ++ int ret; ++ ++ ret = qca8k_vlan_add(priv, port, vlan->vid, untagged); ++ if (ret) { ++ dev_err(priv->dev, "Failed to add VLAN to port %d (%d)", port, ret); ++ return ret; ++ } ++ ++ if (pvid) { ++ ret = qca8k_rmw(priv, QCA8K_EGRESS_VLAN(port), ++ QCA8K_EGREES_VLAN_PORT_MASK(port), ++ QCA8K_EGREES_VLAN_PORT(port, vlan->vid)); ++ if (ret) ++ return ret; ++ ++ ret = qca8k_write(priv, QCA8K_REG_PORT_VLAN_CTRL0(port), ++ QCA8K_PORT_VLAN_CVID(vlan->vid) | ++ QCA8K_PORT_VLAN_SVID(vlan->vid)); ++ } ++ ++ return ret; ++} ++ ++static int ++qca8k_port_vlan_del(struct dsa_switch *ds, int port, ++ const struct switchdev_obj_port_vlan *vlan) ++{ ++ struct qca8k_priv *priv = ds->priv; ++ int ret; ++ ++ ret = qca8k_vlan_del(priv, port, vlan->vid); ++ if (ret) ++ dev_err(priv->dev, "Failed to delete VLAN from port %d (%d)", port, ret); ++ ++ return ret; ++} ++ ++static u32 qca8k_get_phy_flags(struct dsa_switch *ds, int port) ++{ ++ struct qca8k_priv *priv = ds->priv; ++ ++ /* Communicate to the phy internal driver the switch revision. ++ * Based on the switch revision different values needs to be ++ * set to the dbg and mmd reg on the phy. ++ * The first 2 bit are used to communicate the switch revision ++ * to the phy driver. ++ */ ++ if (port > 0 && port < 6) ++ return priv->switch_revision; ++ ++ return 0; ++} ++ ++static enum dsa_tag_protocol ++qca8k_get_tag_protocol(struct dsa_switch *ds, int port, ++ enum dsa_tag_protocol mp) ++{ ++ 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; ++ u32 hash = 0; ++ int i, id; ++ ++ 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 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; ++ ++ 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); ++} ++ ++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; ++ tagger_data->mib_autocast_handler = qca8k_mib_autocast_handler; ++ ++ break; ++ default: ++ return -EOPNOTSUPP; ++ } ++ ++ return 0; ++} ++ ++static const struct dsa_switch_ops qca8k_switch_ops = { ++ .get_tag_protocol = qca8k_get_tag_protocol, ++ .setup = qca8k_setup, ++ .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, ++ .port_disable = qca8k_port_disable, ++ .port_change_mtu = qca8k_port_change_mtu, ++ .port_max_mtu = qca8k_port_max_mtu, ++ .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, ++ .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, ++ .phylink_validate = qca8k_phylink_validate, ++ .phylink_mac_link_state = qca8k_phylink_mac_link_state, ++ .phylink_mac_config = qca8k_phylink_mac_config, ++ .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, ++ .master_state_change = qca8k_master_change, ++ .connect_tag_protocol = qca8k_connect_tag_protocol, ++}; ++ ++static int qca8k_read_switch_id(struct qca8k_priv *priv) ++{ ++ const struct qca8k_match_data *data; ++ u32 val; ++ u8 id; ++ int ret; ++ ++ /* get the switches ID from the compatible */ ++ data = of_device_get_match_data(priv->dev); ++ if (!data) ++ return -ENODEV; ++ ++ ret = qca8k_read(priv, QCA8K_REG_MASK_CTRL, &val); ++ if (ret < 0) ++ return -ENODEV; ++ ++ 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; ++ } ++ ++ priv->switch_id = id; ++ ++ /* Save revision to communicate to the internal PHY driver */ ++ priv->switch_revision = QCA8K_MASK_CTRL_REV_ID(val); ++ ++ return 0; ++} ++ ++static int ++qca8k_sw_probe(struct mdio_device *mdiodev) ++{ ++ struct qca8k_priv *priv; ++ int ret; ++ ++ /* allocate the private data struct so that we can probe the switches ++ * ID register ++ */ ++ priv = devm_kzalloc(&mdiodev->dev, sizeof(*priv), GFP_KERNEL); ++ if (!priv) ++ return -ENOMEM; ++ ++ priv->bus = mdiodev->bus; ++ priv->dev = &mdiodev->dev; ++ ++ priv->reset_gpio = devm_gpiod_get_optional(priv->dev, "reset", ++ GPIOD_ASIS); ++ if (IS_ERR(priv->reset_gpio)) ++ return PTR_ERR(priv->reset_gpio); ++ ++ if (priv->reset_gpio) { ++ gpiod_set_value_cansleep(priv->reset_gpio, 1); ++ /* The active low duration must be greater than 10 ms ++ * and checkpatch.pl wants 20 ms. ++ */ ++ msleep(20); ++ 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); ++ } ++ ++ 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); ++ if (ret) ++ return ret; ++ ++ priv->ds = devm_kzalloc(&mdiodev->dev, sizeof(*priv->ds), GFP_KERNEL); ++ if (!priv->ds) ++ return -ENOMEM; ++ ++ 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; ++ priv->ds->ops = &qca8k_switch_ops; ++ mutex_init(&priv->reg_mutex); ++ dev_set_drvdata(&mdiodev->dev, priv); ++ ++ return dsa_register_switch(priv->ds); ++} ++ ++static void ++qca8k_sw_remove(struct mdio_device *mdiodev) ++{ ++ struct qca8k_priv *priv = dev_get_drvdata(&mdiodev->dev); ++ int i; ++ ++ if (!priv) ++ return; ++ ++ for (i = 0; i < QCA8K_NUM_PORTS; i++) ++ qca8k_port_set_status(priv, i, 0); ++ ++ dsa_unregister_switch(priv->ds); ++ ++ dev_set_drvdata(&mdiodev->dev, NULL); ++} ++ ++static void qca8k_sw_shutdown(struct mdio_device *mdiodev) ++{ ++ struct qca8k_priv *priv = dev_get_drvdata(&mdiodev->dev); ++ ++ if (!priv) ++ return; ++ ++ dsa_switch_shutdown(priv->ds); ++ ++ dev_set_drvdata(&mdiodev->dev, NULL); ++} ++ ++#ifdef CONFIG_PM_SLEEP ++static void ++qca8k_set_pm(struct qca8k_priv *priv, int enable) ++{ ++ int port; ++ ++ for (port = 0; port < QCA8K_NUM_PORTS; port++) { ++ /* Do not enable on resume if the port was ++ * disabled before. ++ */ ++ if (!(priv->port_enabled_map & BIT(port))) ++ continue; ++ ++ qca8k_port_set_status(priv, port, enable); ++ } ++} ++ ++static int qca8k_suspend(struct device *dev) ++{ ++ struct qca8k_priv *priv = dev_get_drvdata(dev); ++ ++ qca8k_set_pm(priv, 0); ++ ++ return dsa_switch_suspend(priv->ds); ++} ++ ++static int qca8k_resume(struct device *dev) ++{ ++ struct qca8k_priv *priv = dev_get_drvdata(dev); ++ ++ qca8k_set_pm(priv, 1); ++ ++ return dsa_switch_resume(priv->ds); ++} ++#endif /* CONFIG_PM_SLEEP */ ++ ++static SIMPLE_DEV_PM_OPS(qca8k_pm_ops, ++ qca8k_suspend, qca8k_resume); ++ ++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[] = { ++ { .compatible = "qca,qca8327", .data = &qca8327 }, ++ { .compatible = "qca,qca8328", .data = &qca8328 }, ++ { .compatible = "qca,qca8334", .data = &qca833x }, ++ { .compatible = "qca,qca8337", .data = &qca833x }, ++ { /* sentinel */ }, ++}; ++ ++static struct mdio_driver qca8kmdio_driver = { ++ .probe = qca8k_sw_probe, ++ .remove = qca8k_sw_remove, ++ .shutdown = qca8k_sw_shutdown, ++ .mdiodrv.driver = { ++ .name = "qca8k", ++ .of_match_table = qca8k_of_match, ++ .pm = &qca8k_pm_ops, ++ }, ++}; ++ ++mdio_module_driver(qca8kmdio_driver); ++ ++MODULE_AUTHOR("Mathieu Olivari, John Crispin "); ++MODULE_DESCRIPTION("Driver for QCA8K ethernet switch family"); ++MODULE_LICENSE("GPL v2"); ++MODULE_ALIAS("platform:qca8k"); +--- /dev/null ++++ b/drivers/net/dsa/qca/qca8k.h +@@ -0,0 +1,411 @@ ++/* SPDX-License-Identifier: GPL-2.0-only */ ++/* ++ * Copyright (C) 2009 Felix Fietkau ++ * Copyright (C) 2011-2012 Gabor Juhos ++ * Copyright (c) 2015, The Linux Foundation. All rights reserved. ++ */ ++ ++#ifndef __QCA8K_H ++#define __QCA8K_H ++ ++#include ++#include ++#include ++#include ++ ++#define QCA8K_ETHERNET_MDIO_PRIORITY 7 ++#define QCA8K_ETHERNET_PHY_PRIORITY 6 ++#define QCA8K_ETHERNET_TIMEOUT 100 ++ ++#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 ++#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 ++ ++#define QCA8K_PORT_VID_DEF 1 ++ ++/* 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) 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) 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) ++#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) ++#define QCA8K_PORT_PAD_RGMII_TX_DELAY_MASK GENMASK(23, 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) 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_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) ++#define QCA8K_REG_MIB 0x034 ++#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 ++#define QCA8K_MDIO_MASTER_BUSY BIT(31) ++#define QCA8K_MDIO_MASTER_EN BIT(30) ++#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_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 ++#define QCA8K_GOL_MAC_ADDR1 0x64 ++#define QCA8K_MAX_FRAME_SIZE 0x78 ++#define QCA8K_REG_PORT_STATUS(_i) (0x07c + (_i) * 4) ++#define QCA8K_PORT_STATUS_SPEED GENMASK(1, 0) ++#define QCA8K_PORT_STATUS_SPEED_10 0 ++#define QCA8K_PORT_STATUS_SPEED_100 0x1 ++#define QCA8K_PORT_STATUS_SPEED_1000 0x2 ++#define QCA8K_PORT_STATUS_TXMAC BIT(2) ++#define QCA8K_PORT_STATUS_RXMAC BIT(3) ++#define QCA8K_PORT_STATUS_TXFLOW BIT(4) ++#define QCA8K_PORT_STATUS_RXFLOW BIT(5) ++#define QCA8K_PORT_STATUS_DUPLEX BIT(6) ++#define QCA8K_PORT_STATUS_LINK_UP BIT(8) ++#define QCA8K_PORT_STATUS_LINK_AUTO BIT(9) ++#define QCA8K_PORT_STATUS_LINK_PAUSE BIT(10) ++#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_TX_MASK GENMASK(1, 0) ++#define QCA8K_PORT_HDR_CTRL_ALL 2 ++#define QCA8K_PORT_HDR_CTRL_MGMT 1 ++#define QCA8K_PORT_HDR_CTRL_NONE 0 ++#define QCA8K_REG_SGMII_CTRL 0x0e0 ++#define QCA8K_SGMII_EN_PLL BIT(1) ++#define QCA8K_SGMII_EN_RX BIT(2) ++#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 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 ++#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) ++ ++/* 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) ++#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_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_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_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_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) ++/* 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_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_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) ++#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_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(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_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) ++#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_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_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 */ ++#define QCA8K_HROUTER_CONTROL 0xe00 ++#define QCA8K_HROUTER_CONTROL_GLB_LOCKTIME_M GENMASK(17, 16) ++#define QCA8K_HROUTER_CONTROL_GLB_LOCKTIME_S 16 ++#define QCA8K_HROUTER_CONTROL_ARP_AGE_MODE 1 ++#define QCA8K_HROUTER_PBASED_CONTROL1 0xe08 ++#define QCA8K_HROUTER_PBASED_CONTROL2 0xe0c ++#define QCA8K_HNAT_CONTROL 0xe38 ++ ++/* MIB registers */ ++#define QCA8K_PORT_MIB_COUNTER(_i) (0x1000 + (_i) * 0x100) ++ ++/* QCA specific MII registers */ ++#define MII_ATH_MMD_ADDR 0x0d ++#define MII_ATH_MMD_DATA 0x0e ++ ++enum { ++ QCA8K_PORT_SPEED_10M = 0, ++ QCA8K_PORT_SPEED_100M = 1, ++ QCA8K_PORT_SPEED_1000M = 2, ++ QCA8K_PORT_SPEED_ERR = 3, ++}; ++ ++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, ++}; ++ ++enum qca8k_vlan_cmd { ++ QCA8K_VLAN_FLUSH = 1, ++ QCA8K_VLAN_LOAD = 2, ++ QCA8K_VLAN_PURGE = 3, ++ QCA8K_VLAN_REMOVE_PORT = 4, ++ QCA8K_VLAN_NEXT = 5, ++ QCA8K_VLAN_READ = 6, ++}; ++ ++enum qca8k_mid_cmd { ++ QCA8K_MIB_FLUSH = 1, ++ QCA8K_MIB_FLUSH_PORT = 2, ++ QCA8K_MIB_CAST = 3, ++}; ++ ++struct qca8k_match_data { ++ u8 id; ++ bool reduced_package; ++ u8 mib_count; ++}; ++ ++enum { ++ QCA8K_CPU_PORT0, ++ 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_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; ++ 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_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; ++/* 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 { ++ u8 switch_id; ++ u8 switch_revision; ++ u8 mirror_rx; ++ u8 mirror_tx; ++ u8 lag_hash_mode; ++ /* Each bit correspond to a port. This switch can support a max of 7 port. ++ * Bit 1: port enabled. Bit 0: port disabled. ++ */ ++ u8 port_enabled_map; ++ struct qca8k_ports_config ports_config; ++ struct regmap *regmap; ++ struct mii_bus *bus; ++ struct dsa_switch *ds; ++ struct mutex reg_mutex; ++ struct device *dev; ++ struct gpio_desc *reset_gpio; ++ 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 { ++ unsigned int size; ++ unsigned int offset; ++ const char *name; ++}; ++ ++struct qca8k_fdb { ++ u16 vid; ++ u8 port_mask; ++ u8 aging; ++ u8 mac[6]; ++}; ++ ++#endif /* __QCA8K_H */ +--- a/drivers/net/dsa/qca8k.c ++++ /dev/null +@@ -1,3243 +0,0 @@ +-// SPDX-License-Identifier: GPL-2.0 +-/* +- * Copyright (C) 2009 Felix Fietkau +- * Copyright (C) 2011-2012 Gabor Juhos +- * Copyright (c) 2015, 2019, The Linux Foundation. All rights reserved. +- * Copyright (c) 2016 John Crispin +- */ +- +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +- +-#include "qca8k.h" +- +-#define MIB_DESC(_s, _o, _n) \ +- { \ +- .size = (_s), \ +- .offset = (_o), \ +- .name = (_n), \ +- } +- +-static const struct qca8k_mib_desc ar8327_mib[] = { +- MIB_DESC(1, 0x00, "RxBroad"), +- MIB_DESC(1, 0x04, "RxPause"), +- MIB_DESC(1, 0x08, "RxMulti"), +- MIB_DESC(1, 0x0c, "RxFcsErr"), +- MIB_DESC(1, 0x10, "RxAlignErr"), +- MIB_DESC(1, 0x14, "RxRunt"), +- MIB_DESC(1, 0x18, "RxFragment"), +- MIB_DESC(1, 0x1c, "Rx64Byte"), +- MIB_DESC(1, 0x20, "Rx128Byte"), +- MIB_DESC(1, 0x24, "Rx256Byte"), +- MIB_DESC(1, 0x28, "Rx512Byte"), +- MIB_DESC(1, 0x2c, "Rx1024Byte"), +- MIB_DESC(1, 0x30, "Rx1518Byte"), +- MIB_DESC(1, 0x34, "RxMaxByte"), +- MIB_DESC(1, 0x38, "RxTooLong"), +- MIB_DESC(2, 0x3c, "RxGoodByte"), +- MIB_DESC(2, 0x44, "RxBadByte"), +- MIB_DESC(1, 0x4c, "RxOverFlow"), +- MIB_DESC(1, 0x50, "Filtered"), +- MIB_DESC(1, 0x54, "TxBroad"), +- MIB_DESC(1, 0x58, "TxPause"), +- MIB_DESC(1, 0x5c, "TxMulti"), +- MIB_DESC(1, 0x60, "TxUnderRun"), +- MIB_DESC(1, 0x64, "Tx64Byte"), +- MIB_DESC(1, 0x68, "Tx128Byte"), +- MIB_DESC(1, 0x6c, "Tx256Byte"), +- MIB_DESC(1, 0x70, "Tx512Byte"), +- MIB_DESC(1, 0x74, "Tx1024Byte"), +- MIB_DESC(1, 0x78, "Tx1518Byte"), +- MIB_DESC(1, 0x7c, "TxMaxByte"), +- MIB_DESC(1, 0x80, "TxOverSize"), +- MIB_DESC(2, 0x84, "TxByte"), +- MIB_DESC(1, 0x8c, "TxCollision"), +- MIB_DESC(1, 0x90, "TxAbortCol"), +- MIB_DESC(1, 0x94, "TxMultiCol"), +- MIB_DESC(1, 0x98, "TxSingleCol"), +- 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"), +-}; +- +-static void +-qca8k_split_addr(u32 regaddr, u16 *r1, u16 *r2, u16 *page) +-{ +- regaddr >>= 1; +- *r1 = regaddr & 0x1e; +- +- regaddr >>= 5; +- *r2 = regaddr & 0x7; +- +- regaddr >>= 3; +- *page = regaddr & 0x3ff; +-} +- +-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; +- +- ret = bus->read(bus, phy_id, regnum); +- if (ret >= 0) { +- *val = ret; +- ret = bus->read(bus, phy_id, regnum + 1); +- *val |= ret << 16; +- } +- +- if (ret < 0) { +- dev_err_ratelimited(&bus->dev, +- "failed to read qca8k 32bit register\n"); +- *val = 0; +- return ret; +- } +- +- return 0; +-} +- +-static void +-qca8k_mii_write32(struct qca8k_priv *priv, int phy_id, u32 regnum, u32 val) +-{ +- u16 lo, hi; +- int ret; +- +- lo = val & 0xffff; +- hi = (u16)(val >> 16); +- +- ret = qca8k_set_lo(priv, phy_id, regnum, lo); +- if (ret >= 0) +- ret = qca8k_set_hi(priv, phy_id, regnum + 1, hi); +-} +- +-static int +-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 == *cached_page) +- return 0; +- +- ret = bus->write(bus, 0x18, 0, page); +- if (ret < 0) { +- dev_err_ratelimited(&bus->dev, +- "failed to set qca8k page\n"); +- return ret; +- } +- +- *cached_page = page; +- usleep_range(1000, 2000); +- return 0; +-} +- +-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 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. +- * 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); +- } +- +- complete(&mgmt_eth_data->rw_done); +-} +- +-static struct sk_buff *qca8k_alloc_mdio_header(enum mdio_cmd cmd, u32 reg, u32 *val, +- 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); +- +- 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, 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); +- +- if (cmd == MDIO_WRITE) +- mgmt_ethhdr->mdio_data = *val; +- +- mgmt_ethhdr->hdr = htons(hdr); +- +- 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; +-} +- +-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, 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_READ, reg, NULL, +- QCA8K_ETHERNET_MDIO_PRIORITY, len); +- 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]; +- 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); +- +- if (ret <= 0) +- return -ETIMEDOUT; +- +- if (!ack) +- return -EINVAL; +- +- return 0; +-} +- +-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, len); +- 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, sizeof(val)); +- if (ret) +- return ret; +- +- val &= ~mask; +- val |= write_val; +- +- return qca8k_write_eth(priv, reg, &val, sizeof(val)); +-} +- +-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; +- struct mii_bus *bus = priv->bus; +- u16 r1, r2, page; +- int ret; +- +- if (!qca8k_read_eth(priv, reg, val, sizeof(*val))) +- return 0; +- +- qca8k_split_addr(reg, &r1, &r2, &page); +- +- mutex_lock_nested(&bus->mdio_lock, MDIO_MUTEX_NESTED); +- +- ret = qca8k_set_page(priv, page); +- if (ret < 0) +- goto exit; +- +- ret = qca8k_mii_read32(bus, 0x10 | r2, r1, val); +- +-exit: +- mutex_unlock(&bus->mdio_lock); +- return ret; +-} +- +-static int +-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; +- +- if (!qca8k_write_eth(priv, reg, &val, sizeof(val))) +- return 0; +- +- qca8k_split_addr(reg, &r1, &r2, &page); +- +- mutex_lock_nested(&bus->mdio_lock, MDIO_MUTEX_NESTED); +- +- ret = qca8k_set_page(priv, page); +- if (ret < 0) +- goto exit; +- +- qca8k_mii_write32(priv, 0x10 | r2, r1, val); +- +-exit: +- mutex_unlock(&bus->mdio_lock); +- return ret; +-} +- +-static int +-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; +- 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); +- +- ret = qca8k_set_page(priv, page); +- if (ret < 0) +- goto exit; +- +- ret = qca8k_mii_read32(bus, 0x10 | r2, r1, &val); +- if (ret < 0) +- goto exit; +- +- val &= ~mask; +- val |= write_val; +- qca8k_mii_write32(priv, 0x10 | r2, r1, val); +- +-exit: +- mutex_unlock(&bus->mdio_lock); +- +- return ret; +-} +- +-static const struct regmap_range qca8k_readable_ranges[] = { +- regmap_reg_range(0x0000, 0x00e4), /* Global control */ +- regmap_reg_range(0x0100, 0x0168), /* EEE control */ +- regmap_reg_range(0x0200, 0x0270), /* Parser control */ +- regmap_reg_range(0x0400, 0x0454), /* ACL */ +- regmap_reg_range(0x0600, 0x0718), /* Lookup */ +- regmap_reg_range(0x0800, 0x0b70), /* QM */ +- regmap_reg_range(0x0c00, 0x0c80), /* PKT */ +- regmap_reg_range(0x0e00, 0x0e98), /* L3 */ +- regmap_reg_range(0x1000, 0x10ac), /* MIB - Port0 */ +- regmap_reg_range(0x1100, 0x11ac), /* MIB - Port1 */ +- regmap_reg_range(0x1200, 0x12ac), /* MIB - Port2 */ +- regmap_reg_range(0x1300, 0x13ac), /* MIB - Port3 */ +- regmap_reg_range(0x1400, 0x14ac), /* MIB - Port4 */ +- regmap_reg_range(0x1500, 0x15ac), /* MIB - Port5 */ +- regmap_reg_range(0x1600, 0x16ac), /* MIB - Port6 */ +- +-}; +- +-static const struct regmap_access_table qca8k_readable_table = { +- .yes_ranges = qca8k_readable_ranges, +- .n_yes_ranges = ARRAY_SIZE(qca8k_readable_ranges), +-}; +- +-static struct regmap_config qca8k_regmap_config = { +- .reg_bits = 16, +- .val_bits = 32, +- .reg_stride = 4, +- .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) +-{ +- u32 val; +- +- return regmap_read_poll_timeout(priv->regmap, reg, val, !(val & mask), 0, +- QCA8K_BUSY_WAIT_TIMEOUT * USEC_PER_MSEC); +-} +- +-static int +-qca8k_fdb_read(struct qca8k_priv *priv, struct qca8k_fdb *fdb) +-{ +- u32 reg[3]; +- int ret; +- +- /* load the ARL table into an array */ +- 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]); +- /* aging - 67:64 */ +- fdb->aging = FIELD_GET(QCA8K_ATU_STATUS_MASK, reg[2]); +- /* portmask - 54:48 */ +- fdb->port_mask = FIELD_GET(QCA8K_ATU_PORT_MASK, reg[1]); +- /* mac - 47:0 */ +- 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; +-} +- +-static void +-qca8k_fdb_write(struct qca8k_priv *priv, u16 vid, u8 port_mask, const u8 *mac, +- u8 aging) +-{ +- u32 reg[3] = { 0 }; +- +- /* vid - 83:72 */ +- reg[2] = FIELD_PREP(QCA8K_ATU_VID_MASK, vid); +- /* aging - 67:64 */ +- reg[2] |= FIELD_PREP(QCA8K_ATU_STATUS_MASK, aging); +- /* portmask - 54:48 */ +- reg[1] = FIELD_PREP(QCA8K_ATU_PORT_MASK, port_mask); +- /* mac - 47:0 */ +- 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 */ +- qca8k_bulk_write(priv, QCA8K_REG_ATU_DATA0, reg, sizeof(reg)); +-} +- +-static int +-qca8k_fdb_access(struct qca8k_priv *priv, enum qca8k_fdb_cmd cmd, int port) +-{ +- u32 reg; +- int ret; +- +- /* Set the command and FDB index */ +- reg = QCA8K_ATU_FUNC_BUSY; +- reg |= cmd; +- if (port >= 0) { +- reg |= QCA8K_ATU_FUNC_PORT_EN; +- reg |= FIELD_PREP(QCA8K_ATU_FUNC_PORT_MASK, port); +- } +- +- /* Write the function register triggering the table access */ +- ret = qca8k_write(priv, QCA8K_REG_ATU_FUNC, reg); +- if (ret) +- return ret; +- +- /* wait for completion */ +- ret = qca8k_busy_wait(priv, QCA8K_REG_ATU_FUNC, QCA8K_ATU_FUNC_BUSY); +- if (ret) +- return ret; +- +- /* Check for table full violation when adding an entry */ +- if (cmd == QCA8K_FDB_LOAD) { +- ret = qca8k_read(priv, QCA8K_REG_ATU_FUNC, ®); +- if (ret < 0) +- return ret; +- if (reg & QCA8K_ATU_FUNC_FULL) +- return -1; +- } +- +- return 0; +-} +- +-static int +-qca8k_fdb_next(struct qca8k_priv *priv, struct qca8k_fdb *fdb, int port) +-{ +- int ret; +- +- qca8k_fdb_write(priv, fdb->vid, fdb->port_mask, fdb->mac, fdb->aging); +- ret = qca8k_fdb_access(priv, QCA8K_FDB_NEXT, port); +- if (ret < 0) +- return ret; +- +- return qca8k_fdb_read(priv, fdb); +-} +- +-static int +-qca8k_fdb_add(struct qca8k_priv *priv, const u8 *mac, u16 port_mask, +- u16 vid, u8 aging) +-{ +- int ret; +- +- mutex_lock(&priv->reg_mutex); +- qca8k_fdb_write(priv, vid, port_mask, mac, aging); +- ret = qca8k_fdb_access(priv, QCA8K_FDB_LOAD, -1); +- mutex_unlock(&priv->reg_mutex); +- +- return ret; +-} +- +-static int +-qca8k_fdb_del(struct qca8k_priv *priv, const u8 *mac, u16 port_mask, u16 vid) +-{ +- int ret; +- +- mutex_lock(&priv->reg_mutex); +- qca8k_fdb_write(priv, vid, port_mask, mac, 0); +- ret = qca8k_fdb_access(priv, QCA8K_FDB_PURGE, -1); +- mutex_unlock(&priv->reg_mutex); +- +- return ret; +-} +- +-static void +-qca8k_fdb_flush(struct qca8k_priv *priv) +-{ +- mutex_lock(&priv->reg_mutex); +- qca8k_fdb_access(priv, QCA8K_FDB_FLUSH, -1); +- mutex_unlock(&priv->reg_mutex); +-} +- +-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; +- int ret; +- +- /* Set the command and VLAN index */ +- reg = QCA8K_VTU_FUNC1_BUSY; +- reg |= cmd; +- 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); +- if (ret) +- return ret; +- +- /* wait for completion */ +- ret = qca8k_busy_wait(priv, QCA8K_REG_VTU_FUNC1, QCA8K_VTU_FUNC1_BUSY); +- if (ret) +- return ret; +- +- /* Check for table full violation when adding an entry */ +- if (cmd == QCA8K_VLAN_LOAD) { +- ret = qca8k_read(priv, QCA8K_REG_VTU_FUNC1, ®); +- if (ret < 0) +- return ret; +- if (reg & QCA8K_VTU_FUNC1_FULL) +- return -ENOMEM; +- } +- +- return 0; +-} +- +-static int +-qca8k_vlan_add(struct qca8k_priv *priv, u8 port, u16 vid, bool untagged) +-{ +- u32 reg; +- int ret; +- +- /* +- We do the right thing with VLAN 0 and treat it as untagged while +- preserving the tag on egress. +- */ +- if (vid == 0) +- return 0; +- +- mutex_lock(&priv->reg_mutex); +- ret = qca8k_vlan_access(priv, QCA8K_VLAN_READ, vid); +- if (ret < 0) +- goto out; +- +- ret = qca8k_read(priv, QCA8K_REG_VTU_FUNC0, ®); +- if (ret < 0) +- goto out; +- reg |= QCA8K_VTU_FUNC0_VALID | QCA8K_VTU_FUNC0_IVL_EN; +- reg &= ~QCA8K_VTU_FUNC0_EG_MODE_PORT_MASK(port); +- if (untagged) +- reg |= QCA8K_VTU_FUNC0_EG_MODE_PORT_UNTAG(port); +- else +- reg |= QCA8K_VTU_FUNC0_EG_MODE_PORT_TAG(port); +- +- ret = qca8k_write(priv, QCA8K_REG_VTU_FUNC0, reg); +- if (ret) +- goto out; +- ret = qca8k_vlan_access(priv, QCA8K_VLAN_LOAD, vid); +- +-out: +- mutex_unlock(&priv->reg_mutex); +- +- return ret; +-} +- +-static int +-qca8k_vlan_del(struct qca8k_priv *priv, u8 port, u16 vid) +-{ +- u32 reg, mask; +- int ret, i; +- bool del; +- +- mutex_lock(&priv->reg_mutex); +- ret = qca8k_vlan_access(priv, QCA8K_VLAN_READ, vid); +- if (ret < 0) +- goto out; +- +- ret = qca8k_read(priv, QCA8K_REG_VTU_FUNC0, ®); +- if (ret < 0) +- goto out; +- 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_PORT_NOT(i); +- +- if ((reg & mask) != mask) { +- del = false; +- break; +- } +- } +- +- if (del) { +- ret = qca8k_vlan_access(priv, QCA8K_VLAN_PURGE, vid); +- } else { +- ret = qca8k_write(priv, QCA8K_REG_VTU_FUNC0, reg); +- if (ret) +- goto out; +- ret = qca8k_vlan_access(priv, QCA8K_VLAN_LOAD, vid); +- } +- +-out: +- mutex_unlock(&priv->reg_mutex); +- +- return ret; +-} +- +-static int +-qca8k_mib_init(struct qca8k_priv *priv) +-{ +- int ret; +- +- mutex_lock(&priv->reg_mutex); +- 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; +- +- ret = qca8k_busy_wait(priv, QCA8K_REG_MIB, QCA8K_MIB_BUSY); +- if (ret) +- goto exit; +- +- ret = regmap_set_bits(priv->regmap, QCA8K_REG_MIB, QCA8K_MIB_CPU_KEEP); +- if (ret) +- goto exit; +- +- ret = qca8k_write(priv, QCA8K_REG_MODULE_EN, QCA8K_MODULE_EN_MIB); +- +-exit: +- mutex_unlock(&priv->reg_mutex); +- return ret; +-} +- +-static void +-qca8k_port_set_status(struct qca8k_priv *priv, int port, int enable) +-{ +- u32 mask = QCA8K_PORT_STATUS_TXMAC | QCA8K_PORT_STATUS_RXMAC; +- +- /* Port 0 and 6 have no internal PHY */ +- if (port > 0 && port < 6) +- mask |= QCA8K_PORT_STATUS_LINK_AUTO; +- +- if (enable) +- regmap_set_bits(priv->regmap, QCA8K_REG_PORT_STATUS(port), mask); +- else +- 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, 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, sizeof(clear_val)); +- if (!clear_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, sizeof(clear_val)); +- if (!read_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) +-{ +- /* From Andrew Lunn: +- * Port 0 has no internal phy. +- * Port 1 has an internal PHY at MDIO address 0. +- * Port 2 has an internal PHY at MDIO address 1. +- * ... +- * Port 5 has an internal PHY at MDIO address 4. +- * Port 6 has no internal PHY. +- */ +- +- return port - 1; +-} +- +-static int +-qca8k_mdio_busy_wait(struct mii_bus *bus, u32 reg, u32 mask) +-{ +- u16 r1, r2, page; +- u32 val; +- int ret, ret1; +- +- qca8k_split_addr(reg, &r1, &r2, &page); +- +- ret = read_poll_timeout(qca8k_mii_read32, ret1, !(val & mask), 0, +- QCA8K_BUSY_WAIT_TIMEOUT * USEC_PER_MSEC, false, +- bus, 0x10 | r2, r1, &val); +- +- /* Check if qca8k_read has failed for a different reason +- * before returnting -ETIMEDOUT +- */ +- if (ret < 0 && ret1 < 0) +- return ret1; +- +- return ret; +-} +- +-static int +-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; +- +- if (regnum >= QCA8K_MDIO_MASTER_MAX_REG) +- return -EINVAL; +- +- val = QCA8K_MDIO_MASTER_BUSY | QCA8K_MDIO_MASTER_EN | +- QCA8K_MDIO_MASTER_WRITE | QCA8K_MDIO_MASTER_PHY_ADDR(phy) | +- QCA8K_MDIO_MASTER_REG_ADDR(regnum) | +- QCA8K_MDIO_MASTER_DATA(data); +- +- qca8k_split_addr(QCA8K_MDIO_MASTER_CTRL, &r1, &r2, &page); +- +- mutex_lock_nested(&bus->mdio_lock, MDIO_MUTEX_NESTED); +- +- ret = qca8k_set_page(priv, page); +- if (ret) +- goto exit; +- +- 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(priv, 0x10 | r2, r1, 0); +- +- mutex_unlock(&bus->mdio_lock); +- +- return ret; +-} +- +-static int +-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; +- +- if (regnum >= QCA8K_MDIO_MASTER_MAX_REG) +- return -EINVAL; +- +- val = QCA8K_MDIO_MASTER_BUSY | QCA8K_MDIO_MASTER_EN | +- QCA8K_MDIO_MASTER_READ | QCA8K_MDIO_MASTER_PHY_ADDR(phy) | +- QCA8K_MDIO_MASTER_REG_ADDR(regnum); +- +- qca8k_split_addr(QCA8K_MDIO_MASTER_CTRL, &r1, &r2, &page); +- +- mutex_lock_nested(&bus->mdio_lock, MDIO_MUTEX_NESTED); +- +- ret = qca8k_set_page(priv, page); +- if (ret) +- goto exit; +- +- qca8k_mii_write32(priv, 0x10 | r2, r1, val); +- +- ret = qca8k_mdio_busy_wait(bus, QCA8K_MDIO_MASTER_CTRL, +- QCA8K_MDIO_MASTER_BUSY); +- if (ret) +- goto exit; +- +- ret = qca8k_mii_read32(bus, 0x10 | r2, r1, &val); +- +-exit: +- /* even if the busy_wait timeouts try to clear the MASTER_EN */ +- qca8k_mii_write32(priv, 0x10 | r2, r1, 0); +- +- mutex_unlock(&bus->mdio_lock); +- +- if (ret >= 0) +- ret = val & QCA8K_MDIO_MASTER_DATA_MASK; +- +- return ret; +-} +- +-static int +-qca8k_internal_mdio_write(struct mii_bus *slave_bus, int phy, int regnum, u16 data) +-{ +- struct qca8k_priv *priv = slave_bus->priv; +- 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(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; +- 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; +- +- ret = qca8k_mdio_read(priv, phy, regnum); +- +- if (ret < 0) +- return 0xffff; +- +- return ret; +-} +- +-static int +-qca8k_legacy_mdio_write(struct mii_bus *slave_bus, int port, int regnum, u16 data) +-{ +- port = qca8k_port_to_phy(port) % PHY_MAX_ADDR; +- +- return qca8k_internal_mdio_write(slave_bus, port, regnum, data); +-} +- +-static int +-qca8k_legacy_mdio_read(struct mii_bus *slave_bus, int port, int regnum) +-{ +- port = qca8k_port_to_phy(port) % PHY_MAX_ADDR; +- +- return qca8k_internal_mdio_read(slave_bus, port, regnum); +-} +- +-static int +-qca8k_mdio_register(struct qca8k_priv *priv) +-{ +- struct dsa_switch *ds = priv->ds; +- struct device_node *mdio; +- struct mii_bus *bus; +- +- bus = devm_mdiobus_alloc(ds->dev); +- if (!bus) +- return -ENOMEM; +- +- bus->priv = (void *)priv; +- snprintf(bus->id, MII_BUS_ID_SIZE, "qca8k-%d.%d", +- ds->dst->index, ds->index); +- bus->parent = ds->dev; +- bus->phy_mask = ~ds->phys_mii_mask; +- ds->slave_mii_bus = bus; +- +- /* Check if the devicetree declare the port:phy mapping */ +- mdio = of_get_child_by_name(priv->dev->of_node, "mdio"); +- if (of_device_is_available(mdio)) { +- bus->name = "qca8k slave mii"; +- bus->read = qca8k_internal_mdio_read; +- bus->write = qca8k_internal_mdio_write; +- return devm_of_mdiobus_register(priv->dev, bus, mdio); +- } +- +- /* If a mapping can't be found the legacy mapping is used, +- * using the qca8k_port_to_phy function +- */ +- bus->name = "qca8k-legacy slave mii"; +- bus->read = qca8k_legacy_mdio_read; +- bus->write = qca8k_legacy_mdio_write; +- return devm_mdiobus_register(priv->dev, bus); +-} +- +-static int +-qca8k_setup_mdio_bus(struct qca8k_priv *priv) +-{ +- u32 internal_mdio_mask = 0, external_mdio_mask = 0, reg; +- struct device_node *ports, *port; +- phy_interface_t mode; +- int err; +- +- ports = of_get_child_by_name(priv->dev->of_node, "ports"); +- if (!ports) +- ports = of_get_child_by_name(priv->dev->of_node, "ethernet-ports"); +- +- if (!ports) +- return -EINVAL; +- +- for_each_available_child_of_node(ports, port) { +- err = of_property_read_u32(port, "reg", ®); +- if (err) { +- of_node_put(port); +- of_node_put(ports); +- return err; +- } +- +- if (!dsa_is_user_port(priv->ds, reg)) +- continue; +- +- of_get_phy_mode(port, &mode); +- +- if (of_property_read_bool(port, "phy-handle") && +- mode != PHY_INTERFACE_MODE_INTERNAL) +- external_mdio_mask |= BIT(reg); +- else +- internal_mdio_mask |= BIT(reg); +- } +- +- of_node_put(ports); +- if (!external_mdio_mask && !internal_mdio_mask) { +- dev_err(priv->dev, "no PHYs are defined.\n"); +- return -EINVAL; +- } +- +- /* The QCA8K_MDIO_MASTER_EN Bit, which grants access to PHYs through +- * the MDIO_MASTER register also _disconnects_ the external MDC +- * passthrough to the internal PHYs. It's not possible to use both +- * configurations at the same time! +- * +- * Because this came up during the review process: +- * If the external mdio-bus driver is capable magically disabling +- * the QCA8K_MDIO_MASTER_EN and mutex/spin-locking out the qca8k's +- * accessors for the time being, it would be possible to pull this +- * off. +- */ +- if (!!external_mdio_mask && !!internal_mdio_mask) { +- dev_err(priv->dev, "either internal or external mdio bus configuration is supported.\n"); +- return -EINVAL; +- } +- +- if (external_mdio_mask) { +- /* Make sure to disable the internal mdio bus in cases +- * a dt-overlay and driver reload changed the configuration +- */ +- +- return regmap_clear_bits(priv->regmap, QCA8K_MDIO_MASTER_CTRL, +- QCA8K_MDIO_MASTER_EN); +- } +- +- return qca8k_mdio_register(priv); +-} +- +-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_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_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; +- +- /* 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) { +- 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, +- val); +- 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 = -1, ret; +- struct device_node *port_dn; +- phy_interface_t mode; +- struct dsa_port *dp; +- u32 delay; +- +- /* 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; +- cpu_port_index++; +- +- if (!of_device_is_available(port_dn)) +- continue; +- +- ret = of_get_phy_mode(port_dn, &mode); +- if (ret) +- continue; +- +- 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: +- case PHY_INTERFACE_MODE_SGMII: +- 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 (!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; +- } +- +- priv->ports_config.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 (!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; +- } +- +- priv->ports_config.rgmii_rx_delay[cpu_port_index] = delay; +- +- /* 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->ports_config.sgmii_tx_clk_falling_edge = true; +- +- if (of_property_read_bool(port_dn, "qca,sgmii-rxclk-falling-edge")) +- priv->ports_config.sgmii_rx_clk_falling_edge = true; +- +- if (of_property_read_bool(port_dn, "qca,sgmii-enable-pll")) { +- 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->ports_config.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; +- } +- } +- +- return 0; +-} +- +-static int +-qca8k_setup(struct dsa_switch *ds) +-{ +- struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv; +- int cpu_port, ret, i; +- u32 mask; +- +- 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 */ +- ret = qca8k_parse_port_config(priv); +- if (ret) +- return ret; +- +- ret = qca8k_setup_mdio_bus(priv); +- 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; +- +- /* Make sure MAC06 is disabled */ +- 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 = 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; +- } +- +- /* Enable MIB counters */ +- ret = qca8k_mib_init(priv); +- if (ret) +- dev_warn(priv->dev, "mib init failed"); +- +- /* 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; +- +- /* 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), +- 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; +- } +- } +- +- /* 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 +- * 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, +- 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; +- +- /* 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(i), +- QCA8K_PORT_LOOKUP_MEMBER, dsa_user_ports(ds)); +- if (ret) +- return ret; +- } +- +- /* Individual user ports get connected to CPU port only */ +- if (dsa_is_user_port(ds, i)) { +- ret = qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(i), +- QCA8K_PORT_LOOKUP_MEMBER, +- BIT(cpu_port)); +- if (ret) +- return ret; +- +- /* Enable ARP Auto-learning by default */ +- ret = regmap_set_bits(priv->regmap, QCA8K_PORT_LOOKUP_CTRL(i), +- QCA8K_PORT_LOOKUP_LEARN); +- if (ret) +- return ret; +- +- /* For port based vlans to work we need to set the +- * default egress vid +- */ +- ret = qca8k_rmw(priv, QCA8K_EGRESS_VLAN(i), +- QCA8K_EGREES_VLAN_PORT_MASK(i), +- QCA8K_EGREES_VLAN_PORT(i, QCA8K_PORT_VID_DEF)); +- if (ret) +- return ret; +- +- ret = qca8k_write(priv, QCA8K_REG_PORT_VLAN_CTRL0(i), +- QCA8K_PORT_VLAN_CVID(QCA8K_PORT_VID_DEF) | +- QCA8K_PORT_VLAN_SVID(QCA8K_PORT_VID_DEF)); +- 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) { +- switch (i) { +- /* The 2 CPU port and port 5 requires some different +- * priority than any other ports. +- */ +- case 0: +- case 5: +- case 6: +- mask = QCA8K_PORT_HOL_CTRL0_EG_PRI0(0x3) | +- QCA8K_PORT_HOL_CTRL0_EG_PRI1(0x4) | +- QCA8K_PORT_HOL_CTRL0_EG_PRI2(0x4) | +- QCA8K_PORT_HOL_CTRL0_EG_PRI3(0x4) | +- QCA8K_PORT_HOL_CTRL0_EG_PRI4(0x6) | +- QCA8K_PORT_HOL_CTRL0_EG_PRI5(0x8) | +- QCA8K_PORT_HOL_CTRL0_EG_PORT(0x1e); +- break; +- default: +- mask = QCA8K_PORT_HOL_CTRL0_EG_PRI0(0x3) | +- QCA8K_PORT_HOL_CTRL0_EG_PRI1(0x4) | +- QCA8K_PORT_HOL_CTRL0_EG_PRI2(0x6) | +- QCA8K_PORT_HOL_CTRL0_EG_PRI3(0x8) | +- QCA8K_PORT_HOL_CTRL0_EG_PORT(0x19); +- } +- qca8k_write(priv, QCA8K_REG_PORT_HOL_CTRL0(i), mask); +- +- mask = QCA8K_PORT_HOL_CTRL1_ING(0x6) | +- QCA8K_PORT_HOL_CTRL1_EG_PRI_BUF_EN | +- 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_MASK | +- QCA8K_PORT_HOL_CTRL1_EG_PRI_BUF_EN | +- QCA8K_PORT_HOL_CTRL1_EG_PORT_BUF_EN | +- QCA8K_PORT_HOL_CTRL1_WRED_EN, +- mask); +- } +- } +- +- /* Special GLOBAL_FC_THRESH value are needed for ar8327 switch */ +- if (priv->switch_id == QCA8K_ID_QCA8327) { +- 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_MASK | +- QCA8K_GLOBAL_FC_GOL_XOFF_THRES_MASK, +- mask); +- } +- +- /* Setup our port MTUs to match power on defaults */ +- ret = qca8k_write(priv, QCA8K_MAX_FRAME_SIZE, ETH_FRAME_LEN + ETH_FCS_LEN); +- if (ret) +- dev_warn(priv->dev, "failed setting MTU settings"); +- +- /* Flush the FDB table */ +- qca8k_fdb_flush(priv); +- +- /* 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; +- +- /* Set max number of LAGs supported */ +- ds->num_lag_ids = QCA8K_NUM_LAGS; +- +- return 0; +-} +- +-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->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->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; +- } +- +- /* 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; +- +- switch (port) { +- case 0: /* 1st CPU port */ +- if (state->interface != PHY_INTERFACE_MODE_RGMII && +- state->interface != PHY_INTERFACE_MODE_RGMII_ID && +- state->interface != PHY_INTERFACE_MODE_RGMII_TXID && +- state->interface != PHY_INTERFACE_MODE_RGMII_RXID && +- state->interface != PHY_INTERFACE_MODE_SGMII) +- return; +- +- reg = QCA8K_REG_PORT0_PAD_CTRL; +- cpu_port_index = QCA8K_CPU_PORT0; +- break; +- case 1: +- case 2: +- case 3: +- case 4: +- case 5: +- /* Internal PHY, nothing to do */ +- return; +- case 6: /* 2nd CPU port / external PHY */ +- if (state->interface != PHY_INTERFACE_MODE_RGMII && +- state->interface != PHY_INTERFACE_MODE_RGMII_ID && +- state->interface != PHY_INTERFACE_MODE_RGMII_TXID && +- state->interface != PHY_INTERFACE_MODE_RGMII_RXID && +- state->interface != PHY_INTERFACE_MODE_SGMII && +- state->interface != PHY_INTERFACE_MODE_1000BASEX) +- 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); +- return; +- } +- +- if (port != 6 && phylink_autoneg_inband(mode)) { +- dev_err(ds->dev, "%s: in-band negotiation unsupported\n", +- __func__); +- return; +- } +- +- switch (state->interface) { +- case PHY_INTERFACE_MODE_RGMII: +- case PHY_INTERFACE_MODE_RGMII_ID: +- case PHY_INTERFACE_MODE_RGMII_TXID: +- case PHY_INTERFACE_MODE_RGMII_RXID: +- qca8k_write(priv, reg, QCA8K_PORT_PAD_RGMII_EN); +- +- /* 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, +- * rather than individual port registers. +- */ +- if (priv->switch_id == QCA8K_ID_QCA8337) +- qca8k_write(priv, QCA8K_REG_PORT5_PAD_CTRL, +- QCA8K_PORT_PAD_RGMII_RX_DELAY_EN); +- break; +- case PHY_INTERFACE_MODE_SGMII: +- case PHY_INTERFACE_MODE_1000BASEX: +- /* Enable SGMII on the port */ +- qca8k_write(priv, reg, QCA8K_PORT_PAD_SGMII_EN); +- +- /* Enable/disable SerDes auto-negotiation as necessary */ +- ret = qca8k_read(priv, QCA8K_REG_PWS, &val); +- if (ret) +- return; +- if (phylink_autoneg_inband(mode)) +- val &= ~QCA8K_PWS_SERDES_AEN_DIS; +- else +- val |= QCA8K_PWS_SERDES_AEN_DIS; +- qca8k_write(priv, QCA8K_REG_PWS, val); +- +- /* Configure the SGMII parameters */ +- ret = qca8k_read(priv, QCA8K_REG_SGMII_CTRL, &val); +- if (ret) +- return; +- +- val |= QCA8K_SGMII_EN_SD; +- +- if (priv->ports_config.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 */ +- val &= ~QCA8K_SGMII_MODE_CTRL_MASK; +- val |= QCA8K_SGMII_MODE_CTRL_PHY; +- } else if (state->interface == PHY_INTERFACE_MODE_SGMII) { +- val &= ~QCA8K_SGMII_MODE_CTRL_MASK; +- val |= QCA8K_SGMII_MODE_CTRL_MAC; +- } else if (state->interface == PHY_INTERFACE_MODE_1000BASEX) { +- val &= ~QCA8K_SGMII_MODE_CTRL_MASK; +- val |= QCA8K_SGMII_MODE_CTRL_BASEX; +- } +- +- 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 +- */ +- 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->ports_config.sgmii_rx_clk_falling_edge) +- val |= QCA8K_PORT0_PAD_SGMII_RXCLK_FALLING_EDGE; +- +- if (priv->ports_config.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", +- phy_modes(state->interface), port); +- return; +- } +-} +- +-static void +-qca8k_phylink_validate(struct dsa_switch *ds, int port, +- unsigned long *supported, +- struct phylink_link_state *state) +-{ +- __ETHTOOL_DECLARE_LINK_MODE_MASK(mask) = { 0, }; +- +- switch (port) { +- case 0: /* 1st CPU port */ +- if (state->interface != PHY_INTERFACE_MODE_NA && +- state->interface != PHY_INTERFACE_MODE_RGMII && +- state->interface != PHY_INTERFACE_MODE_RGMII_ID && +- state->interface != PHY_INTERFACE_MODE_RGMII_TXID && +- state->interface != PHY_INTERFACE_MODE_RGMII_RXID && +- state->interface != PHY_INTERFACE_MODE_SGMII) +- goto unsupported; +- break; +- case 1: +- case 2: +- case 3: +- case 4: +- case 5: +- /* Internal PHY */ +- if (state->interface != PHY_INTERFACE_MODE_NA && +- state->interface != PHY_INTERFACE_MODE_GMII && +- state->interface != PHY_INTERFACE_MODE_INTERNAL) +- goto unsupported; +- break; +- case 6: /* 2nd CPU port / external PHY */ +- if (state->interface != PHY_INTERFACE_MODE_NA && +- state->interface != PHY_INTERFACE_MODE_RGMII && +- state->interface != PHY_INTERFACE_MODE_RGMII_ID && +- state->interface != PHY_INTERFACE_MODE_RGMII_TXID && +- state->interface != PHY_INTERFACE_MODE_RGMII_RXID && +- state->interface != PHY_INTERFACE_MODE_SGMII && +- state->interface != PHY_INTERFACE_MODE_1000BASEX) +- goto unsupported; +- break; +- default: +-unsupported: +- linkmode_zero(supported); +- return; +- } +- +- phylink_set_port_modes(mask); +- phylink_set(mask, Autoneg); +- +- phylink_set(mask, 1000baseT_Full); +- phylink_set(mask, 10baseT_Half); +- phylink_set(mask, 10baseT_Full); +- phylink_set(mask, 100baseT_Half); +- phylink_set(mask, 100baseT_Full); +- +- if (state->interface == PHY_INTERFACE_MODE_1000BASEX) +- phylink_set(mask, 1000baseX_Full); +- +- phylink_set(mask, Pause); +- phylink_set(mask, Asym_Pause); +- +- linkmode_and(supported, supported, mask); +- linkmode_and(state->advertising, state->advertising, mask); +-} +- +-static int +-qca8k_phylink_mac_link_state(struct dsa_switch *ds, int port, +- struct phylink_link_state *state) +-{ +- struct qca8k_priv *priv = ds->priv; +- u32 reg; +- int ret; +- +- ret = qca8k_read(priv, QCA8K_REG_PORT_STATUS(port), ®); +- if (ret < 0) +- return ret; +- +- state->link = !!(reg & QCA8K_PORT_STATUS_LINK_UP); +- state->an_complete = state->link; +- state->an_enabled = !!(reg & QCA8K_PORT_STATUS_LINK_AUTO); +- state->duplex = (reg & QCA8K_PORT_STATUS_DUPLEX) ? DUPLEX_FULL : +- DUPLEX_HALF; +- +- switch (reg & QCA8K_PORT_STATUS_SPEED) { +- case QCA8K_PORT_STATUS_SPEED_10: +- state->speed = SPEED_10; +- break; +- case QCA8K_PORT_STATUS_SPEED_100: +- state->speed = SPEED_100; +- break; +- case QCA8K_PORT_STATUS_SPEED_1000: +- state->speed = SPEED_1000; +- break; +- default: +- state->speed = SPEED_UNKNOWN; +- break; +- } +- +- state->pause = MLO_PAUSE_NONE; +- if (reg & QCA8K_PORT_STATUS_RXFLOW) +- state->pause |= MLO_PAUSE_RX; +- if (reg & QCA8K_PORT_STATUS_TXFLOW) +- state->pause |= MLO_PAUSE_TX; +- +- return 1; +-} +- +-static void +-qca8k_phylink_mac_link_down(struct dsa_switch *ds, int port, unsigned int mode, +- phy_interface_t interface) +-{ +- struct qca8k_priv *priv = ds->priv; +- +- qca8k_port_set_status(priv, port, 0); +-} +- +-static void +-qca8k_phylink_mac_link_up(struct dsa_switch *ds, int port, unsigned int mode, +- phy_interface_t interface, struct phy_device *phydev, +- int speed, int duplex, bool tx_pause, bool rx_pause) +-{ +- struct qca8k_priv *priv = ds->priv; +- u32 reg; +- +- if (phylink_autoneg_inband(mode)) { +- reg = QCA8K_PORT_STATUS_LINK_AUTO; +- } else { +- switch (speed) { +- case SPEED_10: +- reg = QCA8K_PORT_STATUS_SPEED_10; +- break; +- case SPEED_100: +- reg = QCA8K_PORT_STATUS_SPEED_100; +- break; +- case SPEED_1000: +- reg = QCA8K_PORT_STATUS_SPEED_1000; +- break; +- default: +- reg = QCA8K_PORT_STATUS_LINK_AUTO; +- break; +- } +- +- if (duplex == DUPLEX_FULL) +- reg |= QCA8K_PORT_STATUS_DUPLEX; +- +- if (rx_pause || dsa_is_cpu_port(ds, port)) +- reg |= QCA8K_PORT_STATUS_RXFLOW; +- +- if (tx_pause || dsa_is_cpu_port(ds, port)) +- reg |= QCA8K_PORT_STATUS_TXFLOW; +- } +- +- reg |= QCA8K_PORT_STATUS_TXMAC | QCA8K_PORT_STATUS_RXMAC; +- +- qca8k_write(priv, QCA8K_REG_PORT_STATUS(port), reg); +-} +- +-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; +- +- 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); +-} +- +-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) +-{ +- 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; +- +- 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++) { +- mib = &ar8327_mib[i]; +- reg = QCA8K_PORT_MIB_COUNTER(port) + mib->offset; +- +- ret = qca8k_read(priv, reg, &val); +- if (ret < 0) +- continue; +- +- if (mib->size == 2) { +- ret = qca8k_read(priv, reg + 4, &hi); +- if (ret < 0) +- continue; +- } +- +- data[i] = val; +- if (mib->size == 2) +- data[i] |= (u64)hi << 32; +- } +-} +- +-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; +- +- match_data = of_device_get_match_data(priv->dev); +- +- return match_data->mib_count; +-} +- +-static int +-qca8k_set_mac_eee(struct dsa_switch *ds, int port, struct ethtool_eee *eee) +-{ +- struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv; +- u32 lpi_en = QCA8K_REG_EEE_CTRL_LPI_EN(port); +- u32 reg; +- int ret; +- +- mutex_lock(&priv->reg_mutex); +- ret = qca8k_read(priv, QCA8K_REG_EEE_CTRL, ®); +- if (ret < 0) +- goto exit; +- +- if (eee->eee_enabled) +- reg |= lpi_en; +- else +- reg &= ~lpi_en; +- ret = qca8k_write(priv, QCA8K_REG_EEE_CTRL, reg); +- +-exit: +- mutex_unlock(&priv->reg_mutex); +- return ret; +-} +- +-static int +-qca8k_get_mac_eee(struct dsa_switch *ds, int port, struct ethtool_eee *e) +-{ +- /* Nothing to do on the port's MAC */ +- return 0; +-} +- +-static void +-qca8k_port_stp_state_set(struct dsa_switch *ds, int port, u8 state) +-{ +- struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv; +- u32 stp_state; +- +- switch (state) { +- case BR_STATE_DISABLED: +- stp_state = QCA8K_PORT_LOOKUP_STATE_DISABLED; +- break; +- case BR_STATE_BLOCKING: +- stp_state = QCA8K_PORT_LOOKUP_STATE_BLOCKING; +- break; +- case BR_STATE_LISTENING: +- stp_state = QCA8K_PORT_LOOKUP_STATE_LISTENING; +- break; +- case BR_STATE_LEARNING: +- stp_state = QCA8K_PORT_LOOKUP_STATE_LEARNING; +- break; +- case BR_STATE_FORWARDING: +- default: +- stp_state = QCA8K_PORT_LOOKUP_STATE_FORWARD; +- break; +- } +- +- qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(port), +- QCA8K_PORT_LOOKUP_STATE_MASK, stp_state); +-} +- +-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, cpu_port; +- int i, ret; +- +- cpu_port = dsa_to_port(ds, port)->cpu_dp->index; +- port_mask = BIT(cpu_port); +- +- 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 +- * in the bridge +- */ +- ret = regmap_set_bits(priv->regmap, +- QCA8K_PORT_LOOKUP_CTRL(i), +- BIT(port)); +- if (ret) +- return ret; +- if (i != port) +- port_mask |= BIT(i); +- } +- +- /* Add all other ports to this ports portvlan mask */ +- ret = qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(port), +- QCA8K_PORT_LOOKUP_MEMBER, port_mask); +- +- return ret; +-} +- +-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 cpu_port, i; +- +- cpu_port = dsa_to_port(ds, port)->cpu_dp->index; +- +- 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 +- * in the bridge +- */ +- 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 +- * this port +- */ +- qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(port), +- 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_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) +-{ +- struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv; +- +- qca8k_port_set_status(priv, port, 1); +- priv->port_enabled_map |= BIT(port); +- +- if (dsa_is_user_port(ds, port)) +- phy_support_asym_pause(phy); +- +- return 0; +-} +- +-static void +-qca8k_port_disable(struct dsa_switch *ds, int port) +-{ +- struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv; +- +- qca8k_port_set_status(priv, port, 0); +- priv->port_enabled_map &= ~BIT(port); +-} +- +-static int +-qca8k_port_change_mtu(struct dsa_switch *ds, int port, int new_mtu) +-{ +- struct qca8k_priv *priv = ds->priv; +- int ret; +- +- /* We have only have a general MTU setting. +- * DSA always set the CPU port's MTU to the largest MTU of the slave +- * ports. +- * Setting MTU just for the CPU port is sufficient to correctly set a +- * value for every port. +- */ +- if (!dsa_is_cpu_port(ds, port)) +- return 0; +- +- /* To change the MAX_FRAME_SIZE the cpu ports must be off or +- * the switch panics. +- * Turn off both cpu ports before applying the new value to prevent +- * this. +- */ +- if (priv->port_enabled_map & BIT(0)) +- qca8k_port_set_status(priv, 0, 0); +- +- if (priv->port_enabled_map & BIT(6)) +- qca8k_port_set_status(priv, 6, 0); +- +- /* Include L2 header / FCS length */ +- ret = qca8k_write(priv, QCA8K_MAX_FRAME_SIZE, new_mtu + ETH_HLEN + ETH_FCS_LEN); +- +- if (priv->port_enabled_map & BIT(0)) +- qca8k_port_set_status(priv, 0, 1); +- +- if (priv->port_enabled_map & BIT(6)) +- qca8k_port_set_status(priv, 6, 1); +- +- return ret; +-} +- +-static int +-qca8k_port_max_mtu(struct dsa_switch *ds, int port) +-{ +- return QCA8K_MAX_MTU; +-} +- +-static int +-qca8k_port_fdb_insert(struct qca8k_priv *priv, const u8 *addr, +- u16 port_mask, u16 vid) +-{ +- /* Set the vid to the port vlan id if no vid is set */ +- if (!vid) +- vid = QCA8K_PORT_VID_DEF; +- +- return qca8k_fdb_add(priv, addr, port_mask, vid, +- QCA8K_ATU_STATUS_STATIC); +-} +- +-static int +-qca8k_port_fdb_add(struct dsa_switch *ds, int port, +- const unsigned char *addr, u16 vid) +-{ +- struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv; +- u16 port_mask = BIT(port); +- +- return qca8k_port_fdb_insert(priv, addr, port_mask, vid); +-} +- +-static int +-qca8k_port_fdb_del(struct dsa_switch *ds, int port, +- const unsigned char *addr, u16 vid) +-{ +- struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv; +- u16 port_mask = BIT(port); +- +- if (!vid) +- vid = QCA8K_PORT_VID_DEF; +- +- return qca8k_fdb_del(priv, addr, port_mask, vid); +-} +- +-static int +-qca8k_port_fdb_dump(struct dsa_switch *ds, int port, +- dsa_fdb_dump_cb_t *cb, void *data) +-{ +- struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv; +- struct qca8k_fdb _fdb = { 0 }; +- int cnt = QCA8K_NUM_FDB_RECORDS; +- bool is_static; +- int ret = 0; +- +- mutex_lock(&priv->reg_mutex); +- while (cnt-- && !qca8k_fdb_next(priv, &_fdb, port)) { +- if (!_fdb.aging) +- break; +- is_static = (_fdb.aging == QCA8K_ATU_STATUS_STATIC); +- ret = cb(_fdb.mac, _fdb.vid, is_static, data); +- if (ret) +- break; +- } +- mutex_unlock(&priv->reg_mutex); +- +- return 0; +-} +- +-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_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) +-{ +- struct qca8k_priv *priv = ds->priv; +- int ret; +- +- if (vlan_filtering) { +- ret = qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(port), +- 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_MASK, +- QCA8K_PORT_LOOKUP_VLAN_MODE_NONE); +- } +- +- return ret; +-} +- +-static int +-qca8k_port_vlan_add(struct dsa_switch *ds, int port, +- const struct switchdev_obj_port_vlan *vlan, +- struct netlink_ext_ack *extack) +-{ +- bool untagged = vlan->flags & BRIDGE_VLAN_INFO_UNTAGGED; +- bool pvid = vlan->flags & BRIDGE_VLAN_INFO_PVID; +- struct qca8k_priv *priv = ds->priv; +- int ret; +- +- ret = qca8k_vlan_add(priv, port, vlan->vid, untagged); +- if (ret) { +- dev_err(priv->dev, "Failed to add VLAN to port %d (%d)", port, ret); +- return ret; +- } +- +- if (pvid) { +- ret = qca8k_rmw(priv, QCA8K_EGRESS_VLAN(port), +- QCA8K_EGREES_VLAN_PORT_MASK(port), +- QCA8K_EGREES_VLAN_PORT(port, vlan->vid)); +- if (ret) +- return ret; +- +- ret = qca8k_write(priv, QCA8K_REG_PORT_VLAN_CTRL0(port), +- QCA8K_PORT_VLAN_CVID(vlan->vid) | +- QCA8K_PORT_VLAN_SVID(vlan->vid)); +- } +- +- return ret; +-} +- +-static int +-qca8k_port_vlan_del(struct dsa_switch *ds, int port, +- const struct switchdev_obj_port_vlan *vlan) +-{ +- struct qca8k_priv *priv = ds->priv; +- int ret; +- +- ret = qca8k_vlan_del(priv, port, vlan->vid); +- if (ret) +- dev_err(priv->dev, "Failed to delete VLAN from port %d (%d)", port, ret); +- +- return ret; +-} +- +-static u32 qca8k_get_phy_flags(struct dsa_switch *ds, int port) +-{ +- struct qca8k_priv *priv = ds->priv; +- +- /* Communicate to the phy internal driver the switch revision. +- * Based on the switch revision different values needs to be +- * set to the dbg and mmd reg on the phy. +- * The first 2 bit are used to communicate the switch revision +- * to the phy driver. +- */ +- if (port > 0 && port < 6) +- return priv->switch_revision; +- +- return 0; +-} +- +-static enum dsa_tag_protocol +-qca8k_get_tag_protocol(struct dsa_switch *ds, int port, +- enum dsa_tag_protocol mp) +-{ +- 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; +- u32 hash = 0; +- int i, id; +- +- 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 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; +- +- 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); +-} +- +-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; +- tagger_data->mib_autocast_handler = qca8k_mib_autocast_handler; +- +- break; +- default: +- return -EOPNOTSUPP; +- } +- +- return 0; +-} +- +-static const struct dsa_switch_ops qca8k_switch_ops = { +- .get_tag_protocol = qca8k_get_tag_protocol, +- .setup = qca8k_setup, +- .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, +- .port_disable = qca8k_port_disable, +- .port_change_mtu = qca8k_port_change_mtu, +- .port_max_mtu = qca8k_port_max_mtu, +- .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, +- .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, +- .phylink_validate = qca8k_phylink_validate, +- .phylink_mac_link_state = qca8k_phylink_mac_link_state, +- .phylink_mac_config = qca8k_phylink_mac_config, +- .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, +- .master_state_change = qca8k_master_change, +- .connect_tag_protocol = qca8k_connect_tag_protocol, +-}; +- +-static int qca8k_read_switch_id(struct qca8k_priv *priv) +-{ +- const struct qca8k_match_data *data; +- u32 val; +- u8 id; +- int ret; +- +- /* get the switches ID from the compatible */ +- data = of_device_get_match_data(priv->dev); +- if (!data) +- return -ENODEV; +- +- ret = qca8k_read(priv, QCA8K_REG_MASK_CTRL, &val); +- if (ret < 0) +- return -ENODEV; +- +- 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; +- } +- +- priv->switch_id = id; +- +- /* Save revision to communicate to the internal PHY driver */ +- priv->switch_revision = QCA8K_MASK_CTRL_REV_ID(val); +- +- return 0; +-} +- +-static int +-qca8k_sw_probe(struct mdio_device *mdiodev) +-{ +- struct qca8k_priv *priv; +- int ret; +- +- /* allocate the private data struct so that we can probe the switches +- * ID register +- */ +- priv = devm_kzalloc(&mdiodev->dev, sizeof(*priv), GFP_KERNEL); +- if (!priv) +- return -ENOMEM; +- +- priv->bus = mdiodev->bus; +- priv->dev = &mdiodev->dev; +- +- priv->reset_gpio = devm_gpiod_get_optional(priv->dev, "reset", +- GPIOD_ASIS); +- if (IS_ERR(priv->reset_gpio)) +- return PTR_ERR(priv->reset_gpio); +- +- if (priv->reset_gpio) { +- gpiod_set_value_cansleep(priv->reset_gpio, 1); +- /* The active low duration must be greater than 10 ms +- * and checkpatch.pl wants 20 ms. +- */ +- msleep(20); +- 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); +- } +- +- 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); +- if (ret) +- return ret; +- +- priv->ds = devm_kzalloc(&mdiodev->dev, sizeof(*priv->ds), GFP_KERNEL); +- if (!priv->ds) +- return -ENOMEM; +- +- 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; +- priv->ds->ops = &qca8k_switch_ops; +- mutex_init(&priv->reg_mutex); +- dev_set_drvdata(&mdiodev->dev, priv); +- +- return dsa_register_switch(priv->ds); +-} +- +-static void +-qca8k_sw_remove(struct mdio_device *mdiodev) +-{ +- struct qca8k_priv *priv = dev_get_drvdata(&mdiodev->dev); +- int i; +- +- if (!priv) +- return; +- +- for (i = 0; i < QCA8K_NUM_PORTS; i++) +- qca8k_port_set_status(priv, i, 0); +- +- dsa_unregister_switch(priv->ds); +- +- dev_set_drvdata(&mdiodev->dev, NULL); +-} +- +-static void qca8k_sw_shutdown(struct mdio_device *mdiodev) +-{ +- struct qca8k_priv *priv = dev_get_drvdata(&mdiodev->dev); +- +- if (!priv) +- return; +- +- dsa_switch_shutdown(priv->ds); +- +- dev_set_drvdata(&mdiodev->dev, NULL); +-} +- +-#ifdef CONFIG_PM_SLEEP +-static void +-qca8k_set_pm(struct qca8k_priv *priv, int enable) +-{ +- int port; +- +- for (port = 0; port < QCA8K_NUM_PORTS; port++) { +- /* Do not enable on resume if the port was +- * disabled before. +- */ +- if (!(priv->port_enabled_map & BIT(port))) +- continue; +- +- qca8k_port_set_status(priv, port, enable); +- } +-} +- +-static int qca8k_suspend(struct device *dev) +-{ +- struct qca8k_priv *priv = dev_get_drvdata(dev); +- +- qca8k_set_pm(priv, 0); +- +- return dsa_switch_suspend(priv->ds); +-} +- +-static int qca8k_resume(struct device *dev) +-{ +- struct qca8k_priv *priv = dev_get_drvdata(dev); +- +- qca8k_set_pm(priv, 1); +- +- return dsa_switch_resume(priv->ds); +-} +-#endif /* CONFIG_PM_SLEEP */ +- +-static SIMPLE_DEV_PM_OPS(qca8k_pm_ops, +- qca8k_suspend, qca8k_resume); +- +-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[] = { +- { .compatible = "qca,qca8327", .data = &qca8327 }, +- { .compatible = "qca,qca8328", .data = &qca8328 }, +- { .compatible = "qca,qca8334", .data = &qca833x }, +- { .compatible = "qca,qca8337", .data = &qca833x }, +- { /* sentinel */ }, +-}; +- +-static struct mdio_driver qca8kmdio_driver = { +- .probe = qca8k_sw_probe, +- .remove = qca8k_sw_remove, +- .shutdown = qca8k_sw_shutdown, +- .mdiodrv.driver = { +- .name = "qca8k", +- .of_match_table = qca8k_of_match, +- .pm = &qca8k_pm_ops, +- }, +-}; +- +-mdio_module_driver(qca8kmdio_driver); +- +-MODULE_AUTHOR("Mathieu Olivari, John Crispin "); +-MODULE_DESCRIPTION("Driver for QCA8K ethernet switch family"); +-MODULE_LICENSE("GPL v2"); +-MODULE_ALIAS("platform:qca8k"); +--- a/drivers/net/dsa/qca8k.h ++++ /dev/null +@@ -1,411 +0,0 @@ +-/* SPDX-License-Identifier: GPL-2.0-only */ +-/* +- * Copyright (C) 2009 Felix Fietkau +- * Copyright (C) 2011-2012 Gabor Juhos +- * Copyright (c) 2015, The Linux Foundation. All rights reserved. +- */ +- +-#ifndef __QCA8K_H +-#define __QCA8K_H +- +-#include +-#include +-#include +-#include +- +-#define QCA8K_ETHERNET_MDIO_PRIORITY 7 +-#define QCA8K_ETHERNET_PHY_PRIORITY 6 +-#define QCA8K_ETHERNET_TIMEOUT 100 +- +-#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 +-#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 +- +-#define QCA8K_PORT_VID_DEF 1 +- +-/* 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) 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) 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) +-#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) +-#define QCA8K_PORT_PAD_RGMII_TX_DELAY_MASK GENMASK(23, 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) 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_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) +-#define QCA8K_REG_MIB 0x034 +-#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 +-#define QCA8K_MDIO_MASTER_BUSY BIT(31) +-#define QCA8K_MDIO_MASTER_EN BIT(30) +-#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_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 +-#define QCA8K_GOL_MAC_ADDR1 0x64 +-#define QCA8K_MAX_FRAME_SIZE 0x78 +-#define QCA8K_REG_PORT_STATUS(_i) (0x07c + (_i) * 4) +-#define QCA8K_PORT_STATUS_SPEED GENMASK(1, 0) +-#define QCA8K_PORT_STATUS_SPEED_10 0 +-#define QCA8K_PORT_STATUS_SPEED_100 0x1 +-#define QCA8K_PORT_STATUS_SPEED_1000 0x2 +-#define QCA8K_PORT_STATUS_TXMAC BIT(2) +-#define QCA8K_PORT_STATUS_RXMAC BIT(3) +-#define QCA8K_PORT_STATUS_TXFLOW BIT(4) +-#define QCA8K_PORT_STATUS_RXFLOW BIT(5) +-#define QCA8K_PORT_STATUS_DUPLEX BIT(6) +-#define QCA8K_PORT_STATUS_LINK_UP BIT(8) +-#define QCA8K_PORT_STATUS_LINK_AUTO BIT(9) +-#define QCA8K_PORT_STATUS_LINK_PAUSE BIT(10) +-#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_TX_MASK GENMASK(1, 0) +-#define QCA8K_PORT_HDR_CTRL_ALL 2 +-#define QCA8K_PORT_HDR_CTRL_MGMT 1 +-#define QCA8K_PORT_HDR_CTRL_NONE 0 +-#define QCA8K_REG_SGMII_CTRL 0x0e0 +-#define QCA8K_SGMII_EN_PLL BIT(1) +-#define QCA8K_SGMII_EN_RX BIT(2) +-#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 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 +-#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) +- +-/* 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) +-#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_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_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_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_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) +-/* 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_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_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) +-#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_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(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_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) +-#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_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_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 */ +-#define QCA8K_HROUTER_CONTROL 0xe00 +-#define QCA8K_HROUTER_CONTROL_GLB_LOCKTIME_M GENMASK(17, 16) +-#define QCA8K_HROUTER_CONTROL_GLB_LOCKTIME_S 16 +-#define QCA8K_HROUTER_CONTROL_ARP_AGE_MODE 1 +-#define QCA8K_HROUTER_PBASED_CONTROL1 0xe08 +-#define QCA8K_HROUTER_PBASED_CONTROL2 0xe0c +-#define QCA8K_HNAT_CONTROL 0xe38 +- +-/* MIB registers */ +-#define QCA8K_PORT_MIB_COUNTER(_i) (0x1000 + (_i) * 0x100) +- +-/* QCA specific MII registers */ +-#define MII_ATH_MMD_ADDR 0x0d +-#define MII_ATH_MMD_DATA 0x0e +- +-enum { +- QCA8K_PORT_SPEED_10M = 0, +- QCA8K_PORT_SPEED_100M = 1, +- QCA8K_PORT_SPEED_1000M = 2, +- QCA8K_PORT_SPEED_ERR = 3, +-}; +- +-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, +-}; +- +-enum qca8k_vlan_cmd { +- QCA8K_VLAN_FLUSH = 1, +- QCA8K_VLAN_LOAD = 2, +- QCA8K_VLAN_PURGE = 3, +- QCA8K_VLAN_REMOVE_PORT = 4, +- QCA8K_VLAN_NEXT = 5, +- QCA8K_VLAN_READ = 6, +-}; +- +-enum qca8k_mid_cmd { +- QCA8K_MIB_FLUSH = 1, +- QCA8K_MIB_FLUSH_PORT = 2, +- QCA8K_MIB_CAST = 3, +-}; +- +-struct qca8k_match_data { +- u8 id; +- bool reduced_package; +- u8 mib_count; +-}; +- +-enum { +- QCA8K_CPU_PORT0, +- 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_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; +- 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_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; +-/* 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 { +- u8 switch_id; +- u8 switch_revision; +- u8 mirror_rx; +- u8 mirror_tx; +- u8 lag_hash_mode; +- /* Each bit correspond to a port. This switch can support a max of 7 port. +- * Bit 1: port enabled. Bit 0: port disabled. +- */ +- u8 port_enabled_map; +- struct qca8k_ports_config ports_config; +- struct regmap *regmap; +- struct mii_bus *bus; +- struct dsa_switch *ds; +- struct mutex reg_mutex; +- struct device *dev; +- struct gpio_desc *reset_gpio; +- 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 { +- unsigned int size; +- unsigned int offset; +- const char *name; +-}; +- +-struct qca8k_fdb { +- u16 vid; +- u8 port_mask; +- u8 aging; +- u8 mac[6]; +-}; +- +-#endif /* __QCA8K_H */ diff --git a/target/linux/generic/backport-6.1/771-v6.0-01-net-dsa-qca8k-cache-match-data-to-speed-up-access.patch b/target/linux/generic/backport-6.1/771-v6.0-01-net-dsa-qca8k-cache-match-data-to-speed-up-access.patch new file mode 100644 index 0000000000..77fe64632f --- /dev/null +++ b/target/linux/generic/backport-6.1/771-v6.0-01-net-dsa-qca8k-cache-match-data-to-speed-up-access.patch @@ -0,0 +1,157 @@ +From 3bb0844e7bcd0fb0bcfab6202b5edd349ef5250a Mon Sep 17 00:00:00 2001 +From: Christian Marangi +Date: Wed, 27 Jul 2022 13:35:10 +0200 +Subject: [PATCH 01/14] net: dsa: qca8k: cache match data to speed up access + +Using of_device_get_match_data is expensive. Cache match data to speed +up access and rework user of match data to use the new cached value. + +Signed-off-by: Christian Marangi +Reviewed-by: Vladimir Oltean +Signed-off-by: Jakub Kicinski +--- + drivers/net/dsa/qca/qca8k.c | 35 +++++++++++------------------------ + drivers/net/dsa/qca/qca8k.h | 1 + + 2 files changed, 12 insertions(+), 24 deletions(-) + +--- a/drivers/net/dsa/qca/qca8k.c ++++ b/drivers/net/dsa/qca/qca8k.c +@@ -1462,8 +1462,8 @@ static int qca8k_find_cpu_port(struct ds + static int + qca8k_setup_of_pws_reg(struct qca8k_priv *priv) + { ++ const struct qca8k_match_data *data = priv->info; + struct device_node *node = priv->dev->of_node; +- const struct qca8k_match_data *data; + u32 val = 0; + int ret; + +@@ -1472,8 +1472,6 @@ 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; +@@ -2146,23 +2144,19 @@ 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; + +- match_data = of_device_get_match_data(priv->dev); +- +- for (i = 0; i < match_data->mib_count; i++) ++ for (i = 0; i < priv->info->mib_count; i++) + strncpy(data + i * ETH_GSTRING_LEN, ar8327_mib[i].name, + 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; +@@ -2181,10 +2175,9 @@ static void qca8k_mib_autocast_handler(s + 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++) { ++ for (i = 0; i < priv->info->mib_count; i++) { + mib = &ar8327_mib[i]; + + /* First 3 mib are present in the skb head */ +@@ -2256,7 +2249,6 @@ 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; +@@ -2266,9 +2258,7 @@ qca8k_get_ethtool_stats(struct dsa_switc + 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++) { ++ for (i = 0; i < priv->info->mib_count; i++) { + mib = &ar8327_mib[i]; + reg = QCA8K_PORT_MIB_COUNTER(port) + mib->offset; + +@@ -2291,15 +2281,12 @@ 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; + +- match_data = of_device_get_match_data(priv->dev); +- +- return match_data->mib_count; ++ return priv->info->mib_count; + } + + static int +@@ -3037,14 +3024,11 @@ static const struct dsa_switch_ops qca8k + + static int qca8k_read_switch_id(struct qca8k_priv *priv) + { +- const struct qca8k_match_data *data; + u32 val; + u8 id; + int ret; + +- /* get the switches ID from the compatible */ +- data = of_device_get_match_data(priv->dev); +- if (!data) ++ if (!priv->info) + return -ENODEV; + + ret = qca8k_read(priv, QCA8K_REG_MASK_CTRL, &val); +@@ -3052,8 +3036,10 @@ static int qca8k_read_switch_id(struct q + return -ENODEV; + + 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); ++ if (id != priv->info->id) { ++ dev_err(priv->dev, ++ "Switch id detected %x but expected %x", ++ id, priv->info->id); + return -ENODEV; + } + +@@ -3078,6 +3064,7 @@ qca8k_sw_probe(struct mdio_device *mdiod + if (!priv) + return -ENOMEM; + ++ priv->info = of_device_get_match_data(priv->dev); + priv->bus = mdiodev->bus; + priv->dev = &mdiodev->dev; + +--- a/drivers/net/dsa/qca/qca8k.h ++++ b/drivers/net/dsa/qca/qca8k.h +@@ -393,6 +393,7 @@ struct qca8k_priv { + struct qca8k_mgmt_eth_data mgmt_eth_data; + struct qca8k_mib_eth_data mib_eth_data; + struct qca8k_mdio_cache mdio_cache; ++ const struct qca8k_match_data *info; + }; + + struct qca8k_mib_desc { diff --git a/target/linux/generic/backport-6.1/771-v6.0-02-net-dsa-qca8k-make-mib-autocast-feature-optional.patch b/target/linux/generic/backport-6.1/771-v6.0-02-net-dsa-qca8k-make-mib-autocast-feature-optional.patch new file mode 100644 index 0000000000..5b2dce4c55 --- /dev/null +++ b/target/linux/generic/backport-6.1/771-v6.0-02-net-dsa-qca8k-make-mib-autocast-feature-optional.patch @@ -0,0 +1,77 @@ +From 533c64bca62a8654f00698bc893f639013e38c7b Mon Sep 17 00:00:00 2001 +From: Christian Marangi +Date: Wed, 27 Jul 2022 13:35:11 +0200 +Subject: [PATCH 02/14] net: dsa: qca8k: make mib autocast feature optional + +Some switch may not support mib autocast feature and require the legacy +way of reading the regs directly. +Make the mib autocast feature optional and permit to declare support for +it using match_data struct in a dedicated qca8k_info_ops struct. + +Signed-off-by: Christian Marangi +Reviewed-by: Vladimir Oltean +Signed-off-by: Jakub Kicinski +--- + drivers/net/dsa/qca/qca8k.c | 11 +++++++++-- + drivers/net/dsa/qca/qca8k.h | 5 +++++ + 2 files changed, 14 insertions(+), 2 deletions(-) + +--- a/drivers/net/dsa/qca/qca8k.c ++++ b/drivers/net/dsa/qca/qca8k.c +@@ -2254,8 +2254,8 @@ 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) ++ if (priv->mgmt_master && priv->info->ops->autocast_mib && ++ priv->info->ops->autocast_mib(ds, port, data) > 0) + return; + + for (i = 0; i < priv->info->mib_count; i++) { +@@ -3187,20 +3187,27 @@ static int qca8k_resume(struct device *d + static SIMPLE_DEV_PM_OPS(qca8k_pm_ops, + qca8k_suspend, qca8k_resume); + ++static const struct qca8k_info_ops qca8xxx_ops = { ++ .autocast_mib = qca8k_get_ethtool_stats_eth, ++}; ++ + static const struct qca8k_match_data qca8327 = { + .id = QCA8K_ID_QCA8327, + .reduced_package = true, + .mib_count = QCA8K_QCA832X_MIB_COUNT, ++ .ops = &qca8xxx_ops, + }; + + static const struct qca8k_match_data qca8328 = { + .id = QCA8K_ID_QCA8327, + .mib_count = QCA8K_QCA832X_MIB_COUNT, ++ .ops = &qca8xxx_ops, + }; + + static const struct qca8k_match_data qca833x = { + .id = QCA8K_ID_QCA8337, + .mib_count = QCA8K_QCA833X_MIB_COUNT, ++ .ops = &qca8xxx_ops, + }; + + static const struct of_device_id qca8k_of_match[] = { +--- a/drivers/net/dsa/qca/qca8k.h ++++ b/drivers/net/dsa/qca/qca8k.h +@@ -324,10 +324,15 @@ enum qca8k_mid_cmd { + QCA8K_MIB_CAST = 3, + }; + ++struct qca8k_info_ops { ++ int (*autocast_mib)(struct dsa_switch *ds, int port, u64 *data); ++}; ++ + struct qca8k_match_data { + u8 id; + bool reduced_package; + u8 mib_count; ++ const struct qca8k_info_ops *ops; + }; + + enum { diff --git a/target/linux/generic/backport-6.1/771-v6.0-03-net-dsa-qca8k-move-mib-struct-to-common-code.patch b/target/linux/generic/backport-6.1/771-v6.0-03-net-dsa-qca8k-move-mib-struct-to-common-code.patch new file mode 100644 index 0000000000..afa466693a --- /dev/null +++ b/target/linux/generic/backport-6.1/771-v6.0-03-net-dsa-qca8k-move-mib-struct-to-common-code.patch @@ -0,0 +1,6532 @@ +From 027152b830434e3632ad5dd678cc5d4740358dbb Mon Sep 17 00:00:00 2001 +From: Christian Marangi +Date: Wed, 27 Jul 2022 13:35:12 +0200 +Subject: [PATCH 03/14] net: dsa: qca8k: move mib struct to common code + +The same MIB struct is used by drivers based on qca8k family switch. Move +it to common code to make it accessible also by other drivers. + +Signed-off-by: Christian Marangi +Reviewed-by: Vladimir Oltean +Signed-off-by: Jakub Kicinski +--- + drivers/net/dsa/qca/Makefile | 1 + + drivers/net/dsa/qca/{qca8k.c => qca8k-8xxx.c} | 51 --------------- + drivers/net/dsa/qca/qca8k-common.c | 63 +++++++++++++++++++ + drivers/net/dsa/qca/qca8k.h | 3 + + 4 files changed, 67 insertions(+), 51 deletions(-) + rename drivers/net/dsa/qca/{qca8k.c => qca8k-8xxx.c} (98%) + create mode 100644 drivers/net/dsa/qca/qca8k-common.c + +--- a/drivers/net/dsa/qca/Makefile ++++ b/drivers/net/dsa/qca/Makefile +@@ -1,3 +1,4 @@ + # SPDX-License-Identifier: GPL-2.0-only + obj-$(CONFIG_NET_DSA_AR9331) += ar9331.o + obj-$(CONFIG_NET_DSA_QCA8K) += qca8k.o ++qca8k-y += qca8k-common.o qca8k-8xxx.o +--- a/drivers/net/dsa/qca/qca8k.c ++++ /dev/null +@@ -1,3237 +0,0 @@ +-// SPDX-License-Identifier: GPL-2.0 +-/* +- * Copyright (C) 2009 Felix Fietkau +- * Copyright (C) 2011-2012 Gabor Juhos +- * Copyright (c) 2015, 2019, The Linux Foundation. All rights reserved. +- * Copyright (c) 2016 John Crispin +- */ +- +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +- +-#include "qca8k.h" +- +-#define MIB_DESC(_s, _o, _n) \ +- { \ +- .size = (_s), \ +- .offset = (_o), \ +- .name = (_n), \ +- } +- +-static const struct qca8k_mib_desc ar8327_mib[] = { +- MIB_DESC(1, 0x00, "RxBroad"), +- MIB_DESC(1, 0x04, "RxPause"), +- MIB_DESC(1, 0x08, "RxMulti"), +- MIB_DESC(1, 0x0c, "RxFcsErr"), +- MIB_DESC(1, 0x10, "RxAlignErr"), +- MIB_DESC(1, 0x14, "RxRunt"), +- MIB_DESC(1, 0x18, "RxFragment"), +- MIB_DESC(1, 0x1c, "Rx64Byte"), +- MIB_DESC(1, 0x20, "Rx128Byte"), +- MIB_DESC(1, 0x24, "Rx256Byte"), +- MIB_DESC(1, 0x28, "Rx512Byte"), +- MIB_DESC(1, 0x2c, "Rx1024Byte"), +- MIB_DESC(1, 0x30, "Rx1518Byte"), +- MIB_DESC(1, 0x34, "RxMaxByte"), +- MIB_DESC(1, 0x38, "RxTooLong"), +- MIB_DESC(2, 0x3c, "RxGoodByte"), +- MIB_DESC(2, 0x44, "RxBadByte"), +- MIB_DESC(1, 0x4c, "RxOverFlow"), +- MIB_DESC(1, 0x50, "Filtered"), +- MIB_DESC(1, 0x54, "TxBroad"), +- MIB_DESC(1, 0x58, "TxPause"), +- MIB_DESC(1, 0x5c, "TxMulti"), +- MIB_DESC(1, 0x60, "TxUnderRun"), +- MIB_DESC(1, 0x64, "Tx64Byte"), +- MIB_DESC(1, 0x68, "Tx128Byte"), +- MIB_DESC(1, 0x6c, "Tx256Byte"), +- MIB_DESC(1, 0x70, "Tx512Byte"), +- MIB_DESC(1, 0x74, "Tx1024Byte"), +- MIB_DESC(1, 0x78, "Tx1518Byte"), +- MIB_DESC(1, 0x7c, "TxMaxByte"), +- MIB_DESC(1, 0x80, "TxOverSize"), +- MIB_DESC(2, 0x84, "TxByte"), +- MIB_DESC(1, 0x8c, "TxCollision"), +- MIB_DESC(1, 0x90, "TxAbortCol"), +- MIB_DESC(1, 0x94, "TxMultiCol"), +- MIB_DESC(1, 0x98, "TxSingleCol"), +- 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"), +-}; +- +-static void +-qca8k_split_addr(u32 regaddr, u16 *r1, u16 *r2, u16 *page) +-{ +- regaddr >>= 1; +- *r1 = regaddr & 0x1e; +- +- regaddr >>= 5; +- *r2 = regaddr & 0x7; +- +- regaddr >>= 3; +- *page = regaddr & 0x3ff; +-} +- +-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; +- +- ret = bus->read(bus, phy_id, regnum); +- if (ret >= 0) { +- *val = ret; +- ret = bus->read(bus, phy_id, regnum + 1); +- *val |= ret << 16; +- } +- +- if (ret < 0) { +- dev_err_ratelimited(&bus->dev, +- "failed to read qca8k 32bit register\n"); +- *val = 0; +- return ret; +- } +- +- return 0; +-} +- +-static void +-qca8k_mii_write32(struct qca8k_priv *priv, int phy_id, u32 regnum, u32 val) +-{ +- u16 lo, hi; +- int ret; +- +- lo = val & 0xffff; +- hi = (u16)(val >> 16); +- +- ret = qca8k_set_lo(priv, phy_id, regnum, lo); +- if (ret >= 0) +- ret = qca8k_set_hi(priv, phy_id, regnum + 1, hi); +-} +- +-static int +-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 == *cached_page) +- return 0; +- +- ret = bus->write(bus, 0x18, 0, page); +- if (ret < 0) { +- dev_err_ratelimited(&bus->dev, +- "failed to set qca8k page\n"); +- return ret; +- } +- +- *cached_page = page; +- usleep_range(1000, 2000); +- return 0; +-} +- +-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 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. +- * 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); +- } +- +- complete(&mgmt_eth_data->rw_done); +-} +- +-static struct sk_buff *qca8k_alloc_mdio_header(enum mdio_cmd cmd, u32 reg, u32 *val, +- 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); +- +- 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, 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); +- +- if (cmd == MDIO_WRITE) +- mgmt_ethhdr->mdio_data = *val; +- +- mgmt_ethhdr->hdr = htons(hdr); +- +- 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; +-} +- +-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, 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_READ, reg, NULL, +- QCA8K_ETHERNET_MDIO_PRIORITY, len); +- 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]; +- 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); +- +- if (ret <= 0) +- return -ETIMEDOUT; +- +- if (!ack) +- return -EINVAL; +- +- return 0; +-} +- +-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, len); +- 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, sizeof(val)); +- if (ret) +- return ret; +- +- val &= ~mask; +- val |= write_val; +- +- return qca8k_write_eth(priv, reg, &val, sizeof(val)); +-} +- +-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; +- struct mii_bus *bus = priv->bus; +- u16 r1, r2, page; +- int ret; +- +- if (!qca8k_read_eth(priv, reg, val, sizeof(*val))) +- return 0; +- +- qca8k_split_addr(reg, &r1, &r2, &page); +- +- mutex_lock_nested(&bus->mdio_lock, MDIO_MUTEX_NESTED); +- +- ret = qca8k_set_page(priv, page); +- if (ret < 0) +- goto exit; +- +- ret = qca8k_mii_read32(bus, 0x10 | r2, r1, val); +- +-exit: +- mutex_unlock(&bus->mdio_lock); +- return ret; +-} +- +-static int +-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; +- +- if (!qca8k_write_eth(priv, reg, &val, sizeof(val))) +- return 0; +- +- qca8k_split_addr(reg, &r1, &r2, &page); +- +- mutex_lock_nested(&bus->mdio_lock, MDIO_MUTEX_NESTED); +- +- ret = qca8k_set_page(priv, page); +- if (ret < 0) +- goto exit; +- +- qca8k_mii_write32(priv, 0x10 | r2, r1, val); +- +-exit: +- mutex_unlock(&bus->mdio_lock); +- return ret; +-} +- +-static int +-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; +- 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); +- +- ret = qca8k_set_page(priv, page); +- if (ret < 0) +- goto exit; +- +- ret = qca8k_mii_read32(bus, 0x10 | r2, r1, &val); +- if (ret < 0) +- goto exit; +- +- val &= ~mask; +- val |= write_val; +- qca8k_mii_write32(priv, 0x10 | r2, r1, val); +- +-exit: +- mutex_unlock(&bus->mdio_lock); +- +- return ret; +-} +- +-static const struct regmap_range qca8k_readable_ranges[] = { +- regmap_reg_range(0x0000, 0x00e4), /* Global control */ +- regmap_reg_range(0x0100, 0x0168), /* EEE control */ +- regmap_reg_range(0x0200, 0x0270), /* Parser control */ +- regmap_reg_range(0x0400, 0x0454), /* ACL */ +- regmap_reg_range(0x0600, 0x0718), /* Lookup */ +- regmap_reg_range(0x0800, 0x0b70), /* QM */ +- regmap_reg_range(0x0c00, 0x0c80), /* PKT */ +- regmap_reg_range(0x0e00, 0x0e98), /* L3 */ +- regmap_reg_range(0x1000, 0x10ac), /* MIB - Port0 */ +- regmap_reg_range(0x1100, 0x11ac), /* MIB - Port1 */ +- regmap_reg_range(0x1200, 0x12ac), /* MIB - Port2 */ +- regmap_reg_range(0x1300, 0x13ac), /* MIB - Port3 */ +- regmap_reg_range(0x1400, 0x14ac), /* MIB - Port4 */ +- regmap_reg_range(0x1500, 0x15ac), /* MIB - Port5 */ +- regmap_reg_range(0x1600, 0x16ac), /* MIB - Port6 */ +- +-}; +- +-static const struct regmap_access_table qca8k_readable_table = { +- .yes_ranges = qca8k_readable_ranges, +- .n_yes_ranges = ARRAY_SIZE(qca8k_readable_ranges), +-}; +- +-static struct regmap_config qca8k_regmap_config = { +- .reg_bits = 16, +- .val_bits = 32, +- .reg_stride = 4, +- .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) +-{ +- u32 val; +- +- return regmap_read_poll_timeout(priv->regmap, reg, val, !(val & mask), 0, +- QCA8K_BUSY_WAIT_TIMEOUT * USEC_PER_MSEC); +-} +- +-static int +-qca8k_fdb_read(struct qca8k_priv *priv, struct qca8k_fdb *fdb) +-{ +- u32 reg[3]; +- int ret; +- +- /* load the ARL table into an array */ +- 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]); +- /* aging - 67:64 */ +- fdb->aging = FIELD_GET(QCA8K_ATU_STATUS_MASK, reg[2]); +- /* portmask - 54:48 */ +- fdb->port_mask = FIELD_GET(QCA8K_ATU_PORT_MASK, reg[1]); +- /* mac - 47:0 */ +- 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; +-} +- +-static void +-qca8k_fdb_write(struct qca8k_priv *priv, u16 vid, u8 port_mask, const u8 *mac, +- u8 aging) +-{ +- u32 reg[3] = { 0 }; +- +- /* vid - 83:72 */ +- reg[2] = FIELD_PREP(QCA8K_ATU_VID_MASK, vid); +- /* aging - 67:64 */ +- reg[2] |= FIELD_PREP(QCA8K_ATU_STATUS_MASK, aging); +- /* portmask - 54:48 */ +- reg[1] = FIELD_PREP(QCA8K_ATU_PORT_MASK, port_mask); +- /* mac - 47:0 */ +- 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 */ +- qca8k_bulk_write(priv, QCA8K_REG_ATU_DATA0, reg, sizeof(reg)); +-} +- +-static int +-qca8k_fdb_access(struct qca8k_priv *priv, enum qca8k_fdb_cmd cmd, int port) +-{ +- u32 reg; +- int ret; +- +- /* Set the command and FDB index */ +- reg = QCA8K_ATU_FUNC_BUSY; +- reg |= cmd; +- if (port >= 0) { +- reg |= QCA8K_ATU_FUNC_PORT_EN; +- reg |= FIELD_PREP(QCA8K_ATU_FUNC_PORT_MASK, port); +- } +- +- /* Write the function register triggering the table access */ +- ret = qca8k_write(priv, QCA8K_REG_ATU_FUNC, reg); +- if (ret) +- return ret; +- +- /* wait for completion */ +- ret = qca8k_busy_wait(priv, QCA8K_REG_ATU_FUNC, QCA8K_ATU_FUNC_BUSY); +- if (ret) +- return ret; +- +- /* Check for table full violation when adding an entry */ +- if (cmd == QCA8K_FDB_LOAD) { +- ret = qca8k_read(priv, QCA8K_REG_ATU_FUNC, ®); +- if (ret < 0) +- return ret; +- if (reg & QCA8K_ATU_FUNC_FULL) +- return -1; +- } +- +- return 0; +-} +- +-static int +-qca8k_fdb_next(struct qca8k_priv *priv, struct qca8k_fdb *fdb, int port) +-{ +- int ret; +- +- qca8k_fdb_write(priv, fdb->vid, fdb->port_mask, fdb->mac, fdb->aging); +- ret = qca8k_fdb_access(priv, QCA8K_FDB_NEXT, port); +- if (ret < 0) +- return ret; +- +- return qca8k_fdb_read(priv, fdb); +-} +- +-static int +-qca8k_fdb_add(struct qca8k_priv *priv, const u8 *mac, u16 port_mask, +- u16 vid, u8 aging) +-{ +- int ret; +- +- mutex_lock(&priv->reg_mutex); +- qca8k_fdb_write(priv, vid, port_mask, mac, aging); +- ret = qca8k_fdb_access(priv, QCA8K_FDB_LOAD, -1); +- mutex_unlock(&priv->reg_mutex); +- +- return ret; +-} +- +-static int +-qca8k_fdb_del(struct qca8k_priv *priv, const u8 *mac, u16 port_mask, u16 vid) +-{ +- int ret; +- +- mutex_lock(&priv->reg_mutex); +- qca8k_fdb_write(priv, vid, port_mask, mac, 0); +- ret = qca8k_fdb_access(priv, QCA8K_FDB_PURGE, -1); +- mutex_unlock(&priv->reg_mutex); +- +- return ret; +-} +- +-static void +-qca8k_fdb_flush(struct qca8k_priv *priv) +-{ +- mutex_lock(&priv->reg_mutex); +- qca8k_fdb_access(priv, QCA8K_FDB_FLUSH, -1); +- mutex_unlock(&priv->reg_mutex); +-} +- +-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; +- int ret; +- +- /* Set the command and VLAN index */ +- reg = QCA8K_VTU_FUNC1_BUSY; +- reg |= cmd; +- 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); +- if (ret) +- return ret; +- +- /* wait for completion */ +- ret = qca8k_busy_wait(priv, QCA8K_REG_VTU_FUNC1, QCA8K_VTU_FUNC1_BUSY); +- if (ret) +- return ret; +- +- /* Check for table full violation when adding an entry */ +- if (cmd == QCA8K_VLAN_LOAD) { +- ret = qca8k_read(priv, QCA8K_REG_VTU_FUNC1, ®); +- if (ret < 0) +- return ret; +- if (reg & QCA8K_VTU_FUNC1_FULL) +- return -ENOMEM; +- } +- +- return 0; +-} +- +-static int +-qca8k_vlan_add(struct qca8k_priv *priv, u8 port, u16 vid, bool untagged) +-{ +- u32 reg; +- int ret; +- +- /* +- We do the right thing with VLAN 0 and treat it as untagged while +- preserving the tag on egress. +- */ +- if (vid == 0) +- return 0; +- +- mutex_lock(&priv->reg_mutex); +- ret = qca8k_vlan_access(priv, QCA8K_VLAN_READ, vid); +- if (ret < 0) +- goto out; +- +- ret = qca8k_read(priv, QCA8K_REG_VTU_FUNC0, ®); +- if (ret < 0) +- goto out; +- reg |= QCA8K_VTU_FUNC0_VALID | QCA8K_VTU_FUNC0_IVL_EN; +- reg &= ~QCA8K_VTU_FUNC0_EG_MODE_PORT_MASK(port); +- if (untagged) +- reg |= QCA8K_VTU_FUNC0_EG_MODE_PORT_UNTAG(port); +- else +- reg |= QCA8K_VTU_FUNC0_EG_MODE_PORT_TAG(port); +- +- ret = qca8k_write(priv, QCA8K_REG_VTU_FUNC0, reg); +- if (ret) +- goto out; +- ret = qca8k_vlan_access(priv, QCA8K_VLAN_LOAD, vid); +- +-out: +- mutex_unlock(&priv->reg_mutex); +- +- return ret; +-} +- +-static int +-qca8k_vlan_del(struct qca8k_priv *priv, u8 port, u16 vid) +-{ +- u32 reg, mask; +- int ret, i; +- bool del; +- +- mutex_lock(&priv->reg_mutex); +- ret = qca8k_vlan_access(priv, QCA8K_VLAN_READ, vid); +- if (ret < 0) +- goto out; +- +- ret = qca8k_read(priv, QCA8K_REG_VTU_FUNC0, ®); +- if (ret < 0) +- goto out; +- 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_PORT_NOT(i); +- +- if ((reg & mask) != mask) { +- del = false; +- break; +- } +- } +- +- if (del) { +- ret = qca8k_vlan_access(priv, QCA8K_VLAN_PURGE, vid); +- } else { +- ret = qca8k_write(priv, QCA8K_REG_VTU_FUNC0, reg); +- if (ret) +- goto out; +- ret = qca8k_vlan_access(priv, QCA8K_VLAN_LOAD, vid); +- } +- +-out: +- mutex_unlock(&priv->reg_mutex); +- +- return ret; +-} +- +-static int +-qca8k_mib_init(struct qca8k_priv *priv) +-{ +- int ret; +- +- mutex_lock(&priv->reg_mutex); +- 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; +- +- ret = qca8k_busy_wait(priv, QCA8K_REG_MIB, QCA8K_MIB_BUSY); +- if (ret) +- goto exit; +- +- ret = regmap_set_bits(priv->regmap, QCA8K_REG_MIB, QCA8K_MIB_CPU_KEEP); +- if (ret) +- goto exit; +- +- ret = qca8k_write(priv, QCA8K_REG_MODULE_EN, QCA8K_MODULE_EN_MIB); +- +-exit: +- mutex_unlock(&priv->reg_mutex); +- return ret; +-} +- +-static void +-qca8k_port_set_status(struct qca8k_priv *priv, int port, int enable) +-{ +- u32 mask = QCA8K_PORT_STATUS_TXMAC | QCA8K_PORT_STATUS_RXMAC; +- +- /* Port 0 and 6 have no internal PHY */ +- if (port > 0 && port < 6) +- mask |= QCA8K_PORT_STATUS_LINK_AUTO; +- +- if (enable) +- regmap_set_bits(priv->regmap, QCA8K_REG_PORT_STATUS(port), mask); +- else +- 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, 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, sizeof(clear_val)); +- if (!clear_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, sizeof(clear_val)); +- if (!read_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) +-{ +- /* From Andrew Lunn: +- * Port 0 has no internal phy. +- * Port 1 has an internal PHY at MDIO address 0. +- * Port 2 has an internal PHY at MDIO address 1. +- * ... +- * Port 5 has an internal PHY at MDIO address 4. +- * Port 6 has no internal PHY. +- */ +- +- return port - 1; +-} +- +-static int +-qca8k_mdio_busy_wait(struct mii_bus *bus, u32 reg, u32 mask) +-{ +- u16 r1, r2, page; +- u32 val; +- int ret, ret1; +- +- qca8k_split_addr(reg, &r1, &r2, &page); +- +- ret = read_poll_timeout(qca8k_mii_read32, ret1, !(val & mask), 0, +- QCA8K_BUSY_WAIT_TIMEOUT * USEC_PER_MSEC, false, +- bus, 0x10 | r2, r1, &val); +- +- /* Check if qca8k_read has failed for a different reason +- * before returnting -ETIMEDOUT +- */ +- if (ret < 0 && ret1 < 0) +- return ret1; +- +- return ret; +-} +- +-static int +-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; +- +- if (regnum >= QCA8K_MDIO_MASTER_MAX_REG) +- return -EINVAL; +- +- val = QCA8K_MDIO_MASTER_BUSY | QCA8K_MDIO_MASTER_EN | +- QCA8K_MDIO_MASTER_WRITE | QCA8K_MDIO_MASTER_PHY_ADDR(phy) | +- QCA8K_MDIO_MASTER_REG_ADDR(regnum) | +- QCA8K_MDIO_MASTER_DATA(data); +- +- qca8k_split_addr(QCA8K_MDIO_MASTER_CTRL, &r1, &r2, &page); +- +- mutex_lock_nested(&bus->mdio_lock, MDIO_MUTEX_NESTED); +- +- ret = qca8k_set_page(priv, page); +- if (ret) +- goto exit; +- +- 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(priv, 0x10 | r2, r1, 0); +- +- mutex_unlock(&bus->mdio_lock); +- +- return ret; +-} +- +-static int +-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; +- +- if (regnum >= QCA8K_MDIO_MASTER_MAX_REG) +- return -EINVAL; +- +- val = QCA8K_MDIO_MASTER_BUSY | QCA8K_MDIO_MASTER_EN | +- QCA8K_MDIO_MASTER_READ | QCA8K_MDIO_MASTER_PHY_ADDR(phy) | +- QCA8K_MDIO_MASTER_REG_ADDR(regnum); +- +- qca8k_split_addr(QCA8K_MDIO_MASTER_CTRL, &r1, &r2, &page); +- +- mutex_lock_nested(&bus->mdio_lock, MDIO_MUTEX_NESTED); +- +- ret = qca8k_set_page(priv, page); +- if (ret) +- goto exit; +- +- qca8k_mii_write32(priv, 0x10 | r2, r1, val); +- +- ret = qca8k_mdio_busy_wait(bus, QCA8K_MDIO_MASTER_CTRL, +- QCA8K_MDIO_MASTER_BUSY); +- if (ret) +- goto exit; +- +- ret = qca8k_mii_read32(bus, 0x10 | r2, r1, &val); +- +-exit: +- /* even if the busy_wait timeouts try to clear the MASTER_EN */ +- qca8k_mii_write32(priv, 0x10 | r2, r1, 0); +- +- mutex_unlock(&bus->mdio_lock); +- +- if (ret >= 0) +- ret = val & QCA8K_MDIO_MASTER_DATA_MASK; +- +- return ret; +-} +- +-static int +-qca8k_internal_mdio_write(struct mii_bus *slave_bus, int phy, int regnum, u16 data) +-{ +- struct qca8k_priv *priv = slave_bus->priv; +- 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(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; +- 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; +- +- ret = qca8k_mdio_read(priv, phy, regnum); +- +- if (ret < 0) +- return 0xffff; +- +- return ret; +-} +- +-static int +-qca8k_legacy_mdio_write(struct mii_bus *slave_bus, int port, int regnum, u16 data) +-{ +- port = qca8k_port_to_phy(port) % PHY_MAX_ADDR; +- +- return qca8k_internal_mdio_write(slave_bus, port, regnum, data); +-} +- +-static int +-qca8k_legacy_mdio_read(struct mii_bus *slave_bus, int port, int regnum) +-{ +- port = qca8k_port_to_phy(port) % PHY_MAX_ADDR; +- +- return qca8k_internal_mdio_read(slave_bus, port, regnum); +-} +- +-static int +-qca8k_mdio_register(struct qca8k_priv *priv) +-{ +- struct dsa_switch *ds = priv->ds; +- struct device_node *mdio; +- struct mii_bus *bus; +- +- bus = devm_mdiobus_alloc(ds->dev); +- if (!bus) +- return -ENOMEM; +- +- bus->priv = (void *)priv; +- snprintf(bus->id, MII_BUS_ID_SIZE, "qca8k-%d.%d", +- ds->dst->index, ds->index); +- bus->parent = ds->dev; +- bus->phy_mask = ~ds->phys_mii_mask; +- ds->slave_mii_bus = bus; +- +- /* Check if the devicetree declare the port:phy mapping */ +- mdio = of_get_child_by_name(priv->dev->of_node, "mdio"); +- if (of_device_is_available(mdio)) { +- bus->name = "qca8k slave mii"; +- bus->read = qca8k_internal_mdio_read; +- bus->write = qca8k_internal_mdio_write; +- return devm_of_mdiobus_register(priv->dev, bus, mdio); +- } +- +- /* If a mapping can't be found the legacy mapping is used, +- * using the qca8k_port_to_phy function +- */ +- bus->name = "qca8k-legacy slave mii"; +- bus->read = qca8k_legacy_mdio_read; +- bus->write = qca8k_legacy_mdio_write; +- return devm_mdiobus_register(priv->dev, bus); +-} +- +-static int +-qca8k_setup_mdio_bus(struct qca8k_priv *priv) +-{ +- u32 internal_mdio_mask = 0, external_mdio_mask = 0, reg; +- struct device_node *ports, *port; +- phy_interface_t mode; +- int err; +- +- ports = of_get_child_by_name(priv->dev->of_node, "ports"); +- if (!ports) +- ports = of_get_child_by_name(priv->dev->of_node, "ethernet-ports"); +- +- if (!ports) +- return -EINVAL; +- +- for_each_available_child_of_node(ports, port) { +- err = of_property_read_u32(port, "reg", ®); +- if (err) { +- of_node_put(port); +- of_node_put(ports); +- return err; +- } +- +- if (!dsa_is_user_port(priv->ds, reg)) +- continue; +- +- of_get_phy_mode(port, &mode); +- +- if (of_property_read_bool(port, "phy-handle") && +- mode != PHY_INTERFACE_MODE_INTERNAL) +- external_mdio_mask |= BIT(reg); +- else +- internal_mdio_mask |= BIT(reg); +- } +- +- of_node_put(ports); +- if (!external_mdio_mask && !internal_mdio_mask) { +- dev_err(priv->dev, "no PHYs are defined.\n"); +- return -EINVAL; +- } +- +- /* The QCA8K_MDIO_MASTER_EN Bit, which grants access to PHYs through +- * the MDIO_MASTER register also _disconnects_ the external MDC +- * passthrough to the internal PHYs. It's not possible to use both +- * configurations at the same time! +- * +- * Because this came up during the review process: +- * If the external mdio-bus driver is capable magically disabling +- * the QCA8K_MDIO_MASTER_EN and mutex/spin-locking out the qca8k's +- * accessors for the time being, it would be possible to pull this +- * off. +- */ +- if (!!external_mdio_mask && !!internal_mdio_mask) { +- dev_err(priv->dev, "either internal or external mdio bus configuration is supported.\n"); +- return -EINVAL; +- } +- +- if (external_mdio_mask) { +- /* Make sure to disable the internal mdio bus in cases +- * a dt-overlay and driver reload changed the configuration +- */ +- +- return regmap_clear_bits(priv->regmap, QCA8K_MDIO_MASTER_CTRL, +- QCA8K_MDIO_MASTER_EN); +- } +- +- return qca8k_mdio_register(priv); +-} +- +-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_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_setup_of_pws_reg(struct qca8k_priv *priv) +-{ +- const struct qca8k_match_data *data = priv->info; +- 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) { +- /* 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, +- val); +- 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 = -1, ret; +- struct device_node *port_dn; +- phy_interface_t mode; +- struct dsa_port *dp; +- u32 delay; +- +- /* 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; +- cpu_port_index++; +- +- if (!of_device_is_available(port_dn)) +- continue; +- +- ret = of_get_phy_mode(port_dn, &mode); +- if (ret) +- continue; +- +- 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: +- case PHY_INTERFACE_MODE_SGMII: +- 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 (!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; +- } +- +- priv->ports_config.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 (!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; +- } +- +- priv->ports_config.rgmii_rx_delay[cpu_port_index] = delay; +- +- /* 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->ports_config.sgmii_tx_clk_falling_edge = true; +- +- if (of_property_read_bool(port_dn, "qca,sgmii-rxclk-falling-edge")) +- priv->ports_config.sgmii_rx_clk_falling_edge = true; +- +- if (of_property_read_bool(port_dn, "qca,sgmii-enable-pll")) { +- 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->ports_config.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; +- } +- } +- +- return 0; +-} +- +-static int +-qca8k_setup(struct dsa_switch *ds) +-{ +- struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv; +- int cpu_port, ret, i; +- u32 mask; +- +- 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 */ +- ret = qca8k_parse_port_config(priv); +- if (ret) +- return ret; +- +- ret = qca8k_setup_mdio_bus(priv); +- 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; +- +- /* Make sure MAC06 is disabled */ +- 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 = 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; +- } +- +- /* Enable MIB counters */ +- ret = qca8k_mib_init(priv); +- if (ret) +- dev_warn(priv->dev, "mib init failed"); +- +- /* 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; +- +- /* 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), +- 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; +- } +- } +- +- /* 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 +- * 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, +- 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; +- +- /* 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(i), +- QCA8K_PORT_LOOKUP_MEMBER, dsa_user_ports(ds)); +- if (ret) +- return ret; +- } +- +- /* Individual user ports get connected to CPU port only */ +- if (dsa_is_user_port(ds, i)) { +- ret = qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(i), +- QCA8K_PORT_LOOKUP_MEMBER, +- BIT(cpu_port)); +- if (ret) +- return ret; +- +- /* Enable ARP Auto-learning by default */ +- ret = regmap_set_bits(priv->regmap, QCA8K_PORT_LOOKUP_CTRL(i), +- QCA8K_PORT_LOOKUP_LEARN); +- if (ret) +- return ret; +- +- /* For port based vlans to work we need to set the +- * default egress vid +- */ +- ret = qca8k_rmw(priv, QCA8K_EGRESS_VLAN(i), +- QCA8K_EGREES_VLAN_PORT_MASK(i), +- QCA8K_EGREES_VLAN_PORT(i, QCA8K_PORT_VID_DEF)); +- if (ret) +- return ret; +- +- ret = qca8k_write(priv, QCA8K_REG_PORT_VLAN_CTRL0(i), +- QCA8K_PORT_VLAN_CVID(QCA8K_PORT_VID_DEF) | +- QCA8K_PORT_VLAN_SVID(QCA8K_PORT_VID_DEF)); +- 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) { +- switch (i) { +- /* The 2 CPU port and port 5 requires some different +- * priority than any other ports. +- */ +- case 0: +- case 5: +- case 6: +- mask = QCA8K_PORT_HOL_CTRL0_EG_PRI0(0x3) | +- QCA8K_PORT_HOL_CTRL0_EG_PRI1(0x4) | +- QCA8K_PORT_HOL_CTRL0_EG_PRI2(0x4) | +- QCA8K_PORT_HOL_CTRL0_EG_PRI3(0x4) | +- QCA8K_PORT_HOL_CTRL0_EG_PRI4(0x6) | +- QCA8K_PORT_HOL_CTRL0_EG_PRI5(0x8) | +- QCA8K_PORT_HOL_CTRL0_EG_PORT(0x1e); +- break; +- default: +- mask = QCA8K_PORT_HOL_CTRL0_EG_PRI0(0x3) | +- QCA8K_PORT_HOL_CTRL0_EG_PRI1(0x4) | +- QCA8K_PORT_HOL_CTRL0_EG_PRI2(0x6) | +- QCA8K_PORT_HOL_CTRL0_EG_PRI3(0x8) | +- QCA8K_PORT_HOL_CTRL0_EG_PORT(0x19); +- } +- qca8k_write(priv, QCA8K_REG_PORT_HOL_CTRL0(i), mask); +- +- mask = QCA8K_PORT_HOL_CTRL1_ING(0x6) | +- QCA8K_PORT_HOL_CTRL1_EG_PRI_BUF_EN | +- 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_MASK | +- QCA8K_PORT_HOL_CTRL1_EG_PRI_BUF_EN | +- QCA8K_PORT_HOL_CTRL1_EG_PORT_BUF_EN | +- QCA8K_PORT_HOL_CTRL1_WRED_EN, +- mask); +- } +- } +- +- /* Special GLOBAL_FC_THRESH value are needed for ar8327 switch */ +- if (priv->switch_id == QCA8K_ID_QCA8327) { +- 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_MASK | +- QCA8K_GLOBAL_FC_GOL_XOFF_THRES_MASK, +- mask); +- } +- +- /* Setup our port MTUs to match power on defaults */ +- ret = qca8k_write(priv, QCA8K_MAX_FRAME_SIZE, ETH_FRAME_LEN + ETH_FCS_LEN); +- if (ret) +- dev_warn(priv->dev, "failed setting MTU settings"); +- +- /* Flush the FDB table */ +- qca8k_fdb_flush(priv); +- +- /* 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; +- +- /* Set max number of LAGs supported */ +- ds->num_lag_ids = QCA8K_NUM_LAGS; +- +- return 0; +-} +- +-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->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->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; +- } +- +- /* 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; +- +- switch (port) { +- case 0: /* 1st CPU port */ +- if (state->interface != PHY_INTERFACE_MODE_RGMII && +- state->interface != PHY_INTERFACE_MODE_RGMII_ID && +- state->interface != PHY_INTERFACE_MODE_RGMII_TXID && +- state->interface != PHY_INTERFACE_MODE_RGMII_RXID && +- state->interface != PHY_INTERFACE_MODE_SGMII) +- return; +- +- reg = QCA8K_REG_PORT0_PAD_CTRL; +- cpu_port_index = QCA8K_CPU_PORT0; +- break; +- case 1: +- case 2: +- case 3: +- case 4: +- case 5: +- /* Internal PHY, nothing to do */ +- return; +- case 6: /* 2nd CPU port / external PHY */ +- if (state->interface != PHY_INTERFACE_MODE_RGMII && +- state->interface != PHY_INTERFACE_MODE_RGMII_ID && +- state->interface != PHY_INTERFACE_MODE_RGMII_TXID && +- state->interface != PHY_INTERFACE_MODE_RGMII_RXID && +- state->interface != PHY_INTERFACE_MODE_SGMII && +- state->interface != PHY_INTERFACE_MODE_1000BASEX) +- 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); +- return; +- } +- +- if (port != 6 && phylink_autoneg_inband(mode)) { +- dev_err(ds->dev, "%s: in-band negotiation unsupported\n", +- __func__); +- return; +- } +- +- switch (state->interface) { +- case PHY_INTERFACE_MODE_RGMII: +- case PHY_INTERFACE_MODE_RGMII_ID: +- case PHY_INTERFACE_MODE_RGMII_TXID: +- case PHY_INTERFACE_MODE_RGMII_RXID: +- qca8k_write(priv, reg, QCA8K_PORT_PAD_RGMII_EN); +- +- /* 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, +- * rather than individual port registers. +- */ +- if (priv->switch_id == QCA8K_ID_QCA8337) +- qca8k_write(priv, QCA8K_REG_PORT5_PAD_CTRL, +- QCA8K_PORT_PAD_RGMII_RX_DELAY_EN); +- break; +- case PHY_INTERFACE_MODE_SGMII: +- case PHY_INTERFACE_MODE_1000BASEX: +- /* Enable SGMII on the port */ +- qca8k_write(priv, reg, QCA8K_PORT_PAD_SGMII_EN); +- +- /* Enable/disable SerDes auto-negotiation as necessary */ +- ret = qca8k_read(priv, QCA8K_REG_PWS, &val); +- if (ret) +- return; +- if (phylink_autoneg_inband(mode)) +- val &= ~QCA8K_PWS_SERDES_AEN_DIS; +- else +- val |= QCA8K_PWS_SERDES_AEN_DIS; +- qca8k_write(priv, QCA8K_REG_PWS, val); +- +- /* Configure the SGMII parameters */ +- ret = qca8k_read(priv, QCA8K_REG_SGMII_CTRL, &val); +- if (ret) +- return; +- +- val |= QCA8K_SGMII_EN_SD; +- +- if (priv->ports_config.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 */ +- val &= ~QCA8K_SGMII_MODE_CTRL_MASK; +- val |= QCA8K_SGMII_MODE_CTRL_PHY; +- } else if (state->interface == PHY_INTERFACE_MODE_SGMII) { +- val &= ~QCA8K_SGMII_MODE_CTRL_MASK; +- val |= QCA8K_SGMII_MODE_CTRL_MAC; +- } else if (state->interface == PHY_INTERFACE_MODE_1000BASEX) { +- val &= ~QCA8K_SGMII_MODE_CTRL_MASK; +- val |= QCA8K_SGMII_MODE_CTRL_BASEX; +- } +- +- 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 +- */ +- 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->ports_config.sgmii_rx_clk_falling_edge) +- val |= QCA8K_PORT0_PAD_SGMII_RXCLK_FALLING_EDGE; +- +- if (priv->ports_config.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", +- phy_modes(state->interface), port); +- return; +- } +-} +- +-static void +-qca8k_phylink_validate(struct dsa_switch *ds, int port, +- unsigned long *supported, +- struct phylink_link_state *state) +-{ +- __ETHTOOL_DECLARE_LINK_MODE_MASK(mask) = { 0, }; +- +- switch (port) { +- case 0: /* 1st CPU port */ +- if (state->interface != PHY_INTERFACE_MODE_NA && +- state->interface != PHY_INTERFACE_MODE_RGMII && +- state->interface != PHY_INTERFACE_MODE_RGMII_ID && +- state->interface != PHY_INTERFACE_MODE_RGMII_TXID && +- state->interface != PHY_INTERFACE_MODE_RGMII_RXID && +- state->interface != PHY_INTERFACE_MODE_SGMII) +- goto unsupported; +- break; +- case 1: +- case 2: +- case 3: +- case 4: +- case 5: +- /* Internal PHY */ +- if (state->interface != PHY_INTERFACE_MODE_NA && +- state->interface != PHY_INTERFACE_MODE_GMII && +- state->interface != PHY_INTERFACE_MODE_INTERNAL) +- goto unsupported; +- break; +- case 6: /* 2nd CPU port / external PHY */ +- if (state->interface != PHY_INTERFACE_MODE_NA && +- state->interface != PHY_INTERFACE_MODE_RGMII && +- state->interface != PHY_INTERFACE_MODE_RGMII_ID && +- state->interface != PHY_INTERFACE_MODE_RGMII_TXID && +- state->interface != PHY_INTERFACE_MODE_RGMII_RXID && +- state->interface != PHY_INTERFACE_MODE_SGMII && +- state->interface != PHY_INTERFACE_MODE_1000BASEX) +- goto unsupported; +- break; +- default: +-unsupported: +- linkmode_zero(supported); +- return; +- } +- +- phylink_set_port_modes(mask); +- phylink_set(mask, Autoneg); +- +- phylink_set(mask, 1000baseT_Full); +- phylink_set(mask, 10baseT_Half); +- phylink_set(mask, 10baseT_Full); +- phylink_set(mask, 100baseT_Half); +- phylink_set(mask, 100baseT_Full); +- +- if (state->interface == PHY_INTERFACE_MODE_1000BASEX) +- phylink_set(mask, 1000baseX_Full); +- +- phylink_set(mask, Pause); +- phylink_set(mask, Asym_Pause); +- +- linkmode_and(supported, supported, mask); +- linkmode_and(state->advertising, state->advertising, mask); +-} +- +-static int +-qca8k_phylink_mac_link_state(struct dsa_switch *ds, int port, +- struct phylink_link_state *state) +-{ +- struct qca8k_priv *priv = ds->priv; +- u32 reg; +- int ret; +- +- ret = qca8k_read(priv, QCA8K_REG_PORT_STATUS(port), ®); +- if (ret < 0) +- return ret; +- +- state->link = !!(reg & QCA8K_PORT_STATUS_LINK_UP); +- state->an_complete = state->link; +- state->an_enabled = !!(reg & QCA8K_PORT_STATUS_LINK_AUTO); +- state->duplex = (reg & QCA8K_PORT_STATUS_DUPLEX) ? DUPLEX_FULL : +- DUPLEX_HALF; +- +- switch (reg & QCA8K_PORT_STATUS_SPEED) { +- case QCA8K_PORT_STATUS_SPEED_10: +- state->speed = SPEED_10; +- break; +- case QCA8K_PORT_STATUS_SPEED_100: +- state->speed = SPEED_100; +- break; +- case QCA8K_PORT_STATUS_SPEED_1000: +- state->speed = SPEED_1000; +- break; +- default: +- state->speed = SPEED_UNKNOWN; +- break; +- } +- +- state->pause = MLO_PAUSE_NONE; +- if (reg & QCA8K_PORT_STATUS_RXFLOW) +- state->pause |= MLO_PAUSE_RX; +- if (reg & QCA8K_PORT_STATUS_TXFLOW) +- state->pause |= MLO_PAUSE_TX; +- +- return 1; +-} +- +-static void +-qca8k_phylink_mac_link_down(struct dsa_switch *ds, int port, unsigned int mode, +- phy_interface_t interface) +-{ +- struct qca8k_priv *priv = ds->priv; +- +- qca8k_port_set_status(priv, port, 0); +-} +- +-static void +-qca8k_phylink_mac_link_up(struct dsa_switch *ds, int port, unsigned int mode, +- phy_interface_t interface, struct phy_device *phydev, +- int speed, int duplex, bool tx_pause, bool rx_pause) +-{ +- struct qca8k_priv *priv = ds->priv; +- u32 reg; +- +- if (phylink_autoneg_inband(mode)) { +- reg = QCA8K_PORT_STATUS_LINK_AUTO; +- } else { +- switch (speed) { +- case SPEED_10: +- reg = QCA8K_PORT_STATUS_SPEED_10; +- break; +- case SPEED_100: +- reg = QCA8K_PORT_STATUS_SPEED_100; +- break; +- case SPEED_1000: +- reg = QCA8K_PORT_STATUS_SPEED_1000; +- break; +- default: +- reg = QCA8K_PORT_STATUS_LINK_AUTO; +- break; +- } +- +- if (duplex == DUPLEX_FULL) +- reg |= QCA8K_PORT_STATUS_DUPLEX; +- +- if (rx_pause || dsa_is_cpu_port(ds, port)) +- reg |= QCA8K_PORT_STATUS_RXFLOW; +- +- if (tx_pause || dsa_is_cpu_port(ds, port)) +- reg |= QCA8K_PORT_STATUS_TXFLOW; +- } +- +- reg |= QCA8K_PORT_STATUS_TXMAC | QCA8K_PORT_STATUS_RXMAC; +- +- qca8k_write(priv, QCA8K_REG_PORT_STATUS(port), reg); +-} +- +-static void +-qca8k_get_strings(struct dsa_switch *ds, int port, u32 stringset, uint8_t *data) +-{ +- struct qca8k_priv *priv = ds->priv; +- int i; +- +- if (stringset != ETH_SS_STATS) +- return; +- +- for (i = 0; i < priv->info->mib_count; i++) +- strncpy(data + i * ETH_GSTRING_LEN, ar8327_mib[i].name, +- ETH_GSTRING_LEN); +-} +- +-static void qca8k_mib_autocast_handler(struct dsa_switch *ds, struct sk_buff *skb) +-{ +- 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; +- +- data = mib_eth_data->data; +- +- for (i = 0; i < priv->info->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) +-{ +- struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv; +- const struct qca8k_mib_desc *mib; +- u32 reg, i, val; +- u32 hi = 0; +- int ret; +- +- if (priv->mgmt_master && priv->info->ops->autocast_mib && +- priv->info->ops->autocast_mib(ds, port, data) > 0) +- return; +- +- for (i = 0; i < priv->info->mib_count; i++) { +- mib = &ar8327_mib[i]; +- reg = QCA8K_PORT_MIB_COUNTER(port) + mib->offset; +- +- ret = qca8k_read(priv, reg, &val); +- if (ret < 0) +- continue; +- +- if (mib->size == 2) { +- ret = qca8k_read(priv, reg + 4, &hi); +- if (ret < 0) +- continue; +- } +- +- data[i] = val; +- if (mib->size == 2) +- data[i] |= (u64)hi << 32; +- } +-} +- +-static int +-qca8k_get_sset_count(struct dsa_switch *ds, int port, int sset) +-{ +- struct qca8k_priv *priv = ds->priv; +- +- if (sset != ETH_SS_STATS) +- return 0; +- +- return priv->info->mib_count; +-} +- +-static int +-qca8k_set_mac_eee(struct dsa_switch *ds, int port, struct ethtool_eee *eee) +-{ +- struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv; +- u32 lpi_en = QCA8K_REG_EEE_CTRL_LPI_EN(port); +- u32 reg; +- int ret; +- +- mutex_lock(&priv->reg_mutex); +- ret = qca8k_read(priv, QCA8K_REG_EEE_CTRL, ®); +- if (ret < 0) +- goto exit; +- +- if (eee->eee_enabled) +- reg |= lpi_en; +- else +- reg &= ~lpi_en; +- ret = qca8k_write(priv, QCA8K_REG_EEE_CTRL, reg); +- +-exit: +- mutex_unlock(&priv->reg_mutex); +- return ret; +-} +- +-static int +-qca8k_get_mac_eee(struct dsa_switch *ds, int port, struct ethtool_eee *e) +-{ +- /* Nothing to do on the port's MAC */ +- return 0; +-} +- +-static void +-qca8k_port_stp_state_set(struct dsa_switch *ds, int port, u8 state) +-{ +- struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv; +- u32 stp_state; +- +- switch (state) { +- case BR_STATE_DISABLED: +- stp_state = QCA8K_PORT_LOOKUP_STATE_DISABLED; +- break; +- case BR_STATE_BLOCKING: +- stp_state = QCA8K_PORT_LOOKUP_STATE_BLOCKING; +- break; +- case BR_STATE_LISTENING: +- stp_state = QCA8K_PORT_LOOKUP_STATE_LISTENING; +- break; +- case BR_STATE_LEARNING: +- stp_state = QCA8K_PORT_LOOKUP_STATE_LEARNING; +- break; +- case BR_STATE_FORWARDING: +- default: +- stp_state = QCA8K_PORT_LOOKUP_STATE_FORWARD; +- break; +- } +- +- qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(port), +- QCA8K_PORT_LOOKUP_STATE_MASK, stp_state); +-} +- +-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, cpu_port; +- int i, ret; +- +- cpu_port = dsa_to_port(ds, port)->cpu_dp->index; +- port_mask = BIT(cpu_port); +- +- 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 +- * in the bridge +- */ +- ret = regmap_set_bits(priv->regmap, +- QCA8K_PORT_LOOKUP_CTRL(i), +- BIT(port)); +- if (ret) +- return ret; +- if (i != port) +- port_mask |= BIT(i); +- } +- +- /* Add all other ports to this ports portvlan mask */ +- ret = qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(port), +- QCA8K_PORT_LOOKUP_MEMBER, port_mask); +- +- return ret; +-} +- +-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 cpu_port, i; +- +- cpu_port = dsa_to_port(ds, port)->cpu_dp->index; +- +- 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 +- * in the bridge +- */ +- 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 +- * this port +- */ +- qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(port), +- 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_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) +-{ +- struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv; +- +- qca8k_port_set_status(priv, port, 1); +- priv->port_enabled_map |= BIT(port); +- +- if (dsa_is_user_port(ds, port)) +- phy_support_asym_pause(phy); +- +- return 0; +-} +- +-static void +-qca8k_port_disable(struct dsa_switch *ds, int port) +-{ +- struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv; +- +- qca8k_port_set_status(priv, port, 0); +- priv->port_enabled_map &= ~BIT(port); +-} +- +-static int +-qca8k_port_change_mtu(struct dsa_switch *ds, int port, int new_mtu) +-{ +- struct qca8k_priv *priv = ds->priv; +- int ret; +- +- /* We have only have a general MTU setting. +- * DSA always set the CPU port's MTU to the largest MTU of the slave +- * ports. +- * Setting MTU just for the CPU port is sufficient to correctly set a +- * value for every port. +- */ +- if (!dsa_is_cpu_port(ds, port)) +- return 0; +- +- /* To change the MAX_FRAME_SIZE the cpu ports must be off or +- * the switch panics. +- * Turn off both cpu ports before applying the new value to prevent +- * this. +- */ +- if (priv->port_enabled_map & BIT(0)) +- qca8k_port_set_status(priv, 0, 0); +- +- if (priv->port_enabled_map & BIT(6)) +- qca8k_port_set_status(priv, 6, 0); +- +- /* Include L2 header / FCS length */ +- ret = qca8k_write(priv, QCA8K_MAX_FRAME_SIZE, new_mtu + ETH_HLEN + ETH_FCS_LEN); +- +- if (priv->port_enabled_map & BIT(0)) +- qca8k_port_set_status(priv, 0, 1); +- +- if (priv->port_enabled_map & BIT(6)) +- qca8k_port_set_status(priv, 6, 1); +- +- return ret; +-} +- +-static int +-qca8k_port_max_mtu(struct dsa_switch *ds, int port) +-{ +- return QCA8K_MAX_MTU; +-} +- +-static int +-qca8k_port_fdb_insert(struct qca8k_priv *priv, const u8 *addr, +- u16 port_mask, u16 vid) +-{ +- /* Set the vid to the port vlan id if no vid is set */ +- if (!vid) +- vid = QCA8K_PORT_VID_DEF; +- +- return qca8k_fdb_add(priv, addr, port_mask, vid, +- QCA8K_ATU_STATUS_STATIC); +-} +- +-static int +-qca8k_port_fdb_add(struct dsa_switch *ds, int port, +- const unsigned char *addr, u16 vid) +-{ +- struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv; +- u16 port_mask = BIT(port); +- +- return qca8k_port_fdb_insert(priv, addr, port_mask, vid); +-} +- +-static int +-qca8k_port_fdb_del(struct dsa_switch *ds, int port, +- const unsigned char *addr, u16 vid) +-{ +- struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv; +- u16 port_mask = BIT(port); +- +- if (!vid) +- vid = QCA8K_PORT_VID_DEF; +- +- return qca8k_fdb_del(priv, addr, port_mask, vid); +-} +- +-static int +-qca8k_port_fdb_dump(struct dsa_switch *ds, int port, +- dsa_fdb_dump_cb_t *cb, void *data) +-{ +- struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv; +- struct qca8k_fdb _fdb = { 0 }; +- int cnt = QCA8K_NUM_FDB_RECORDS; +- bool is_static; +- int ret = 0; +- +- mutex_lock(&priv->reg_mutex); +- while (cnt-- && !qca8k_fdb_next(priv, &_fdb, port)) { +- if (!_fdb.aging) +- break; +- is_static = (_fdb.aging == QCA8K_ATU_STATUS_STATIC); +- ret = cb(_fdb.mac, _fdb.vid, is_static, data); +- if (ret) +- break; +- } +- mutex_unlock(&priv->reg_mutex); +- +- return 0; +-} +- +-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_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) +-{ +- struct qca8k_priv *priv = ds->priv; +- int ret; +- +- if (vlan_filtering) { +- ret = qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(port), +- 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_MASK, +- QCA8K_PORT_LOOKUP_VLAN_MODE_NONE); +- } +- +- return ret; +-} +- +-static int +-qca8k_port_vlan_add(struct dsa_switch *ds, int port, +- const struct switchdev_obj_port_vlan *vlan, +- struct netlink_ext_ack *extack) +-{ +- bool untagged = vlan->flags & BRIDGE_VLAN_INFO_UNTAGGED; +- bool pvid = vlan->flags & BRIDGE_VLAN_INFO_PVID; +- struct qca8k_priv *priv = ds->priv; +- int ret; +- +- ret = qca8k_vlan_add(priv, port, vlan->vid, untagged); +- if (ret) { +- dev_err(priv->dev, "Failed to add VLAN to port %d (%d)", port, ret); +- return ret; +- } +- +- if (pvid) { +- ret = qca8k_rmw(priv, QCA8K_EGRESS_VLAN(port), +- QCA8K_EGREES_VLAN_PORT_MASK(port), +- QCA8K_EGREES_VLAN_PORT(port, vlan->vid)); +- if (ret) +- return ret; +- +- ret = qca8k_write(priv, QCA8K_REG_PORT_VLAN_CTRL0(port), +- QCA8K_PORT_VLAN_CVID(vlan->vid) | +- QCA8K_PORT_VLAN_SVID(vlan->vid)); +- } +- +- return ret; +-} +- +-static int +-qca8k_port_vlan_del(struct dsa_switch *ds, int port, +- const struct switchdev_obj_port_vlan *vlan) +-{ +- struct qca8k_priv *priv = ds->priv; +- int ret; +- +- ret = qca8k_vlan_del(priv, port, vlan->vid); +- if (ret) +- dev_err(priv->dev, "Failed to delete VLAN from port %d (%d)", port, ret); +- +- return ret; +-} +- +-static u32 qca8k_get_phy_flags(struct dsa_switch *ds, int port) +-{ +- struct qca8k_priv *priv = ds->priv; +- +- /* Communicate to the phy internal driver the switch revision. +- * Based on the switch revision different values needs to be +- * set to the dbg and mmd reg on the phy. +- * The first 2 bit are used to communicate the switch revision +- * to the phy driver. +- */ +- if (port > 0 && port < 6) +- return priv->switch_revision; +- +- return 0; +-} +- +-static enum dsa_tag_protocol +-qca8k_get_tag_protocol(struct dsa_switch *ds, int port, +- enum dsa_tag_protocol mp) +-{ +- 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; +- u32 hash = 0; +- int i, id; +- +- 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 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; +- +- 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); +-} +- +-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; +- tagger_data->mib_autocast_handler = qca8k_mib_autocast_handler; +- +- break; +- default: +- return -EOPNOTSUPP; +- } +- +- return 0; +-} +- +-static const struct dsa_switch_ops qca8k_switch_ops = { +- .get_tag_protocol = qca8k_get_tag_protocol, +- .setup = qca8k_setup, +- .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, +- .port_disable = qca8k_port_disable, +- .port_change_mtu = qca8k_port_change_mtu, +- .port_max_mtu = qca8k_port_max_mtu, +- .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, +- .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, +- .phylink_validate = qca8k_phylink_validate, +- .phylink_mac_link_state = qca8k_phylink_mac_link_state, +- .phylink_mac_config = qca8k_phylink_mac_config, +- .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, +- .master_state_change = qca8k_master_change, +- .connect_tag_protocol = qca8k_connect_tag_protocol, +-}; +- +-static int qca8k_read_switch_id(struct qca8k_priv *priv) +-{ +- u32 val; +- u8 id; +- int ret; +- +- if (!priv->info) +- return -ENODEV; +- +- ret = qca8k_read(priv, QCA8K_REG_MASK_CTRL, &val); +- if (ret < 0) +- return -ENODEV; +- +- id = QCA8K_MASK_CTRL_DEVICE_ID(val); +- if (id != priv->info->id) { +- dev_err(priv->dev, +- "Switch id detected %x but expected %x", +- id, priv->info->id); +- return -ENODEV; +- } +- +- priv->switch_id = id; +- +- /* Save revision to communicate to the internal PHY driver */ +- priv->switch_revision = QCA8K_MASK_CTRL_REV_ID(val); +- +- return 0; +-} +- +-static int +-qca8k_sw_probe(struct mdio_device *mdiodev) +-{ +- struct qca8k_priv *priv; +- int ret; +- +- /* allocate the private data struct so that we can probe the switches +- * ID register +- */ +- priv = devm_kzalloc(&mdiodev->dev, sizeof(*priv), GFP_KERNEL); +- if (!priv) +- return -ENOMEM; +- +- priv->info = of_device_get_match_data(priv->dev); +- priv->bus = mdiodev->bus; +- priv->dev = &mdiodev->dev; +- +- priv->reset_gpio = devm_gpiod_get_optional(priv->dev, "reset", +- GPIOD_ASIS); +- if (IS_ERR(priv->reset_gpio)) +- return PTR_ERR(priv->reset_gpio); +- +- if (priv->reset_gpio) { +- gpiod_set_value_cansleep(priv->reset_gpio, 1); +- /* The active low duration must be greater than 10 ms +- * and checkpatch.pl wants 20 ms. +- */ +- msleep(20); +- 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); +- } +- +- 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); +- if (ret) +- return ret; +- +- priv->ds = devm_kzalloc(&mdiodev->dev, sizeof(*priv->ds), GFP_KERNEL); +- if (!priv->ds) +- return -ENOMEM; +- +- 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; +- priv->ds->ops = &qca8k_switch_ops; +- mutex_init(&priv->reg_mutex); +- dev_set_drvdata(&mdiodev->dev, priv); +- +- return dsa_register_switch(priv->ds); +-} +- +-static void +-qca8k_sw_remove(struct mdio_device *mdiodev) +-{ +- struct qca8k_priv *priv = dev_get_drvdata(&mdiodev->dev); +- int i; +- +- if (!priv) +- return; +- +- for (i = 0; i < QCA8K_NUM_PORTS; i++) +- qca8k_port_set_status(priv, i, 0); +- +- dsa_unregister_switch(priv->ds); +- +- dev_set_drvdata(&mdiodev->dev, NULL); +-} +- +-static void qca8k_sw_shutdown(struct mdio_device *mdiodev) +-{ +- struct qca8k_priv *priv = dev_get_drvdata(&mdiodev->dev); +- +- if (!priv) +- return; +- +- dsa_switch_shutdown(priv->ds); +- +- dev_set_drvdata(&mdiodev->dev, NULL); +-} +- +-#ifdef CONFIG_PM_SLEEP +-static void +-qca8k_set_pm(struct qca8k_priv *priv, int enable) +-{ +- int port; +- +- for (port = 0; port < QCA8K_NUM_PORTS; port++) { +- /* Do not enable on resume if the port was +- * disabled before. +- */ +- if (!(priv->port_enabled_map & BIT(port))) +- continue; +- +- qca8k_port_set_status(priv, port, enable); +- } +-} +- +-static int qca8k_suspend(struct device *dev) +-{ +- struct qca8k_priv *priv = dev_get_drvdata(dev); +- +- qca8k_set_pm(priv, 0); +- +- return dsa_switch_suspend(priv->ds); +-} +- +-static int qca8k_resume(struct device *dev) +-{ +- struct qca8k_priv *priv = dev_get_drvdata(dev); +- +- qca8k_set_pm(priv, 1); +- +- return dsa_switch_resume(priv->ds); +-} +-#endif /* CONFIG_PM_SLEEP */ +- +-static SIMPLE_DEV_PM_OPS(qca8k_pm_ops, +- qca8k_suspend, qca8k_resume); +- +-static const struct qca8k_info_ops qca8xxx_ops = { +- .autocast_mib = qca8k_get_ethtool_stats_eth, +-}; +- +-static const struct qca8k_match_data qca8327 = { +- .id = QCA8K_ID_QCA8327, +- .reduced_package = true, +- .mib_count = QCA8K_QCA832X_MIB_COUNT, +- .ops = &qca8xxx_ops, +-}; +- +-static const struct qca8k_match_data qca8328 = { +- .id = QCA8K_ID_QCA8327, +- .mib_count = QCA8K_QCA832X_MIB_COUNT, +- .ops = &qca8xxx_ops, +-}; +- +-static const struct qca8k_match_data qca833x = { +- .id = QCA8K_ID_QCA8337, +- .mib_count = QCA8K_QCA833X_MIB_COUNT, +- .ops = &qca8xxx_ops, +-}; +- +-static const struct of_device_id qca8k_of_match[] = { +- { .compatible = "qca,qca8327", .data = &qca8327 }, +- { .compatible = "qca,qca8328", .data = &qca8328 }, +- { .compatible = "qca,qca8334", .data = &qca833x }, +- { .compatible = "qca,qca8337", .data = &qca833x }, +- { /* sentinel */ }, +-}; +- +-static struct mdio_driver qca8kmdio_driver = { +- .probe = qca8k_sw_probe, +- .remove = qca8k_sw_remove, +- .shutdown = qca8k_sw_shutdown, +- .mdiodrv.driver = { +- .name = "qca8k", +- .of_match_table = qca8k_of_match, +- .pm = &qca8k_pm_ops, +- }, +-}; +- +-mdio_module_driver(qca8kmdio_driver); +- +-MODULE_AUTHOR("Mathieu Olivari, John Crispin "); +-MODULE_DESCRIPTION("Driver for QCA8K ethernet switch family"); +-MODULE_LICENSE("GPL v2"); +-MODULE_ALIAS("platform:qca8k"); +--- /dev/null ++++ b/drivers/net/dsa/qca/qca8k-8xxx.c +@@ -0,0 +1,3186 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (C) 2009 Felix Fietkau ++ * Copyright (C) 2011-2012 Gabor Juhos ++ * Copyright (c) 2015, 2019, The Linux Foundation. All rights reserved. ++ * Copyright (c) 2016 John Crispin ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "qca8k.h" ++ ++static void ++qca8k_split_addr(u32 regaddr, u16 *r1, u16 *r2, u16 *page) ++{ ++ regaddr >>= 1; ++ *r1 = regaddr & 0x1e; ++ ++ regaddr >>= 5; ++ *r2 = regaddr & 0x7; ++ ++ regaddr >>= 3; ++ *page = regaddr & 0x3ff; ++} ++ ++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; ++ ++ ret = bus->read(bus, phy_id, regnum); ++ if (ret >= 0) { ++ *val = ret; ++ ret = bus->read(bus, phy_id, regnum + 1); ++ *val |= ret << 16; ++ } ++ ++ if (ret < 0) { ++ dev_err_ratelimited(&bus->dev, ++ "failed to read qca8k 32bit register\n"); ++ *val = 0; ++ return ret; ++ } ++ ++ return 0; ++} ++ ++static void ++qca8k_mii_write32(struct qca8k_priv *priv, int phy_id, u32 regnum, u32 val) ++{ ++ u16 lo, hi; ++ int ret; ++ ++ lo = val & 0xffff; ++ hi = (u16)(val >> 16); ++ ++ ret = qca8k_set_lo(priv, phy_id, regnum, lo); ++ if (ret >= 0) ++ ret = qca8k_set_hi(priv, phy_id, regnum + 1, hi); ++} ++ ++static int ++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 == *cached_page) ++ return 0; ++ ++ ret = bus->write(bus, 0x18, 0, page); ++ if (ret < 0) { ++ dev_err_ratelimited(&bus->dev, ++ "failed to set qca8k page\n"); ++ return ret; ++ } ++ ++ *cached_page = page; ++ usleep_range(1000, 2000); ++ return 0; ++} ++ ++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 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. ++ * 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); ++ } ++ ++ complete(&mgmt_eth_data->rw_done); ++} ++ ++static struct sk_buff *qca8k_alloc_mdio_header(enum mdio_cmd cmd, u32 reg, u32 *val, ++ 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); ++ ++ 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, 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); ++ ++ if (cmd == MDIO_WRITE) ++ mgmt_ethhdr->mdio_data = *val; ++ ++ mgmt_ethhdr->hdr = htons(hdr); ++ ++ 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; ++} ++ ++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, 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_READ, reg, NULL, ++ QCA8K_ETHERNET_MDIO_PRIORITY, len); ++ 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]; ++ 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); ++ ++ if (ret <= 0) ++ return -ETIMEDOUT; ++ ++ if (!ack) ++ return -EINVAL; ++ ++ return 0; ++} ++ ++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, len); ++ 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, sizeof(val)); ++ if (ret) ++ return ret; ++ ++ val &= ~mask; ++ val |= write_val; ++ ++ return qca8k_write_eth(priv, reg, &val, sizeof(val)); ++} ++ ++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; ++ struct mii_bus *bus = priv->bus; ++ u16 r1, r2, page; ++ int ret; ++ ++ if (!qca8k_read_eth(priv, reg, val, sizeof(*val))) ++ return 0; ++ ++ qca8k_split_addr(reg, &r1, &r2, &page); ++ ++ mutex_lock_nested(&bus->mdio_lock, MDIO_MUTEX_NESTED); ++ ++ ret = qca8k_set_page(priv, page); ++ if (ret < 0) ++ goto exit; ++ ++ ret = qca8k_mii_read32(bus, 0x10 | r2, r1, val); ++ ++exit: ++ mutex_unlock(&bus->mdio_lock); ++ return ret; ++} ++ ++static int ++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; ++ ++ if (!qca8k_write_eth(priv, reg, &val, sizeof(val))) ++ return 0; ++ ++ qca8k_split_addr(reg, &r1, &r2, &page); ++ ++ mutex_lock_nested(&bus->mdio_lock, MDIO_MUTEX_NESTED); ++ ++ ret = qca8k_set_page(priv, page); ++ if (ret < 0) ++ goto exit; ++ ++ qca8k_mii_write32(priv, 0x10 | r2, r1, val); ++ ++exit: ++ mutex_unlock(&bus->mdio_lock); ++ return ret; ++} ++ ++static int ++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; ++ 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); ++ ++ ret = qca8k_set_page(priv, page); ++ if (ret < 0) ++ goto exit; ++ ++ ret = qca8k_mii_read32(bus, 0x10 | r2, r1, &val); ++ if (ret < 0) ++ goto exit; ++ ++ val &= ~mask; ++ val |= write_val; ++ qca8k_mii_write32(priv, 0x10 | r2, r1, val); ++ ++exit: ++ mutex_unlock(&bus->mdio_lock); ++ ++ return ret; ++} ++ ++static const struct regmap_range qca8k_readable_ranges[] = { ++ regmap_reg_range(0x0000, 0x00e4), /* Global control */ ++ regmap_reg_range(0x0100, 0x0168), /* EEE control */ ++ regmap_reg_range(0x0200, 0x0270), /* Parser control */ ++ regmap_reg_range(0x0400, 0x0454), /* ACL */ ++ regmap_reg_range(0x0600, 0x0718), /* Lookup */ ++ regmap_reg_range(0x0800, 0x0b70), /* QM */ ++ regmap_reg_range(0x0c00, 0x0c80), /* PKT */ ++ regmap_reg_range(0x0e00, 0x0e98), /* L3 */ ++ regmap_reg_range(0x1000, 0x10ac), /* MIB - Port0 */ ++ regmap_reg_range(0x1100, 0x11ac), /* MIB - Port1 */ ++ regmap_reg_range(0x1200, 0x12ac), /* MIB - Port2 */ ++ regmap_reg_range(0x1300, 0x13ac), /* MIB - Port3 */ ++ regmap_reg_range(0x1400, 0x14ac), /* MIB - Port4 */ ++ regmap_reg_range(0x1500, 0x15ac), /* MIB - Port5 */ ++ regmap_reg_range(0x1600, 0x16ac), /* MIB - Port6 */ ++ ++}; ++ ++static const struct regmap_access_table qca8k_readable_table = { ++ .yes_ranges = qca8k_readable_ranges, ++ .n_yes_ranges = ARRAY_SIZE(qca8k_readable_ranges), ++}; ++ ++static struct regmap_config qca8k_regmap_config = { ++ .reg_bits = 16, ++ .val_bits = 32, ++ .reg_stride = 4, ++ .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) ++{ ++ u32 val; ++ ++ return regmap_read_poll_timeout(priv->regmap, reg, val, !(val & mask), 0, ++ QCA8K_BUSY_WAIT_TIMEOUT * USEC_PER_MSEC); ++} ++ ++static int ++qca8k_fdb_read(struct qca8k_priv *priv, struct qca8k_fdb *fdb) ++{ ++ u32 reg[3]; ++ int ret; ++ ++ /* load the ARL table into an array */ ++ 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]); ++ /* aging - 67:64 */ ++ fdb->aging = FIELD_GET(QCA8K_ATU_STATUS_MASK, reg[2]); ++ /* portmask - 54:48 */ ++ fdb->port_mask = FIELD_GET(QCA8K_ATU_PORT_MASK, reg[1]); ++ /* mac - 47:0 */ ++ 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; ++} ++ ++static void ++qca8k_fdb_write(struct qca8k_priv *priv, u16 vid, u8 port_mask, const u8 *mac, ++ u8 aging) ++{ ++ u32 reg[3] = { 0 }; ++ ++ /* vid - 83:72 */ ++ reg[2] = FIELD_PREP(QCA8K_ATU_VID_MASK, vid); ++ /* aging - 67:64 */ ++ reg[2] |= FIELD_PREP(QCA8K_ATU_STATUS_MASK, aging); ++ /* portmask - 54:48 */ ++ reg[1] = FIELD_PREP(QCA8K_ATU_PORT_MASK, port_mask); ++ /* mac - 47:0 */ ++ 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 */ ++ qca8k_bulk_write(priv, QCA8K_REG_ATU_DATA0, reg, sizeof(reg)); ++} ++ ++static int ++qca8k_fdb_access(struct qca8k_priv *priv, enum qca8k_fdb_cmd cmd, int port) ++{ ++ u32 reg; ++ int ret; ++ ++ /* Set the command and FDB index */ ++ reg = QCA8K_ATU_FUNC_BUSY; ++ reg |= cmd; ++ if (port >= 0) { ++ reg |= QCA8K_ATU_FUNC_PORT_EN; ++ reg |= FIELD_PREP(QCA8K_ATU_FUNC_PORT_MASK, port); ++ } ++ ++ /* Write the function register triggering the table access */ ++ ret = qca8k_write(priv, QCA8K_REG_ATU_FUNC, reg); ++ if (ret) ++ return ret; ++ ++ /* wait for completion */ ++ ret = qca8k_busy_wait(priv, QCA8K_REG_ATU_FUNC, QCA8K_ATU_FUNC_BUSY); ++ if (ret) ++ return ret; ++ ++ /* Check for table full violation when adding an entry */ ++ if (cmd == QCA8K_FDB_LOAD) { ++ ret = qca8k_read(priv, QCA8K_REG_ATU_FUNC, ®); ++ if (ret < 0) ++ return ret; ++ if (reg & QCA8K_ATU_FUNC_FULL) ++ return -1; ++ } ++ ++ return 0; ++} ++ ++static int ++qca8k_fdb_next(struct qca8k_priv *priv, struct qca8k_fdb *fdb, int port) ++{ ++ int ret; ++ ++ qca8k_fdb_write(priv, fdb->vid, fdb->port_mask, fdb->mac, fdb->aging); ++ ret = qca8k_fdb_access(priv, QCA8K_FDB_NEXT, port); ++ if (ret < 0) ++ return ret; ++ ++ return qca8k_fdb_read(priv, fdb); ++} ++ ++static int ++qca8k_fdb_add(struct qca8k_priv *priv, const u8 *mac, u16 port_mask, ++ u16 vid, u8 aging) ++{ ++ int ret; ++ ++ mutex_lock(&priv->reg_mutex); ++ qca8k_fdb_write(priv, vid, port_mask, mac, aging); ++ ret = qca8k_fdb_access(priv, QCA8K_FDB_LOAD, -1); ++ mutex_unlock(&priv->reg_mutex); ++ ++ return ret; ++} ++ ++static int ++qca8k_fdb_del(struct qca8k_priv *priv, const u8 *mac, u16 port_mask, u16 vid) ++{ ++ int ret; ++ ++ mutex_lock(&priv->reg_mutex); ++ qca8k_fdb_write(priv, vid, port_mask, mac, 0); ++ ret = qca8k_fdb_access(priv, QCA8K_FDB_PURGE, -1); ++ mutex_unlock(&priv->reg_mutex); ++ ++ return ret; ++} ++ ++static void ++qca8k_fdb_flush(struct qca8k_priv *priv) ++{ ++ mutex_lock(&priv->reg_mutex); ++ qca8k_fdb_access(priv, QCA8K_FDB_FLUSH, -1); ++ mutex_unlock(&priv->reg_mutex); ++} ++ ++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; ++ int ret; ++ ++ /* Set the command and VLAN index */ ++ reg = QCA8K_VTU_FUNC1_BUSY; ++ reg |= cmd; ++ 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); ++ if (ret) ++ return ret; ++ ++ /* wait for completion */ ++ ret = qca8k_busy_wait(priv, QCA8K_REG_VTU_FUNC1, QCA8K_VTU_FUNC1_BUSY); ++ if (ret) ++ return ret; ++ ++ /* Check for table full violation when adding an entry */ ++ if (cmd == QCA8K_VLAN_LOAD) { ++ ret = qca8k_read(priv, QCA8K_REG_VTU_FUNC1, ®); ++ if (ret < 0) ++ return ret; ++ if (reg & QCA8K_VTU_FUNC1_FULL) ++ return -ENOMEM; ++ } ++ ++ return 0; ++} ++ ++static int ++qca8k_vlan_add(struct qca8k_priv *priv, u8 port, u16 vid, bool untagged) ++{ ++ u32 reg; ++ int ret; ++ ++ /* ++ We do the right thing with VLAN 0 and treat it as untagged while ++ preserving the tag on egress. ++ */ ++ if (vid == 0) ++ return 0; ++ ++ mutex_lock(&priv->reg_mutex); ++ ret = qca8k_vlan_access(priv, QCA8K_VLAN_READ, vid); ++ if (ret < 0) ++ goto out; ++ ++ ret = qca8k_read(priv, QCA8K_REG_VTU_FUNC0, ®); ++ if (ret < 0) ++ goto out; ++ reg |= QCA8K_VTU_FUNC0_VALID | QCA8K_VTU_FUNC0_IVL_EN; ++ reg &= ~QCA8K_VTU_FUNC0_EG_MODE_PORT_MASK(port); ++ if (untagged) ++ reg |= QCA8K_VTU_FUNC0_EG_MODE_PORT_UNTAG(port); ++ else ++ reg |= QCA8K_VTU_FUNC0_EG_MODE_PORT_TAG(port); ++ ++ ret = qca8k_write(priv, QCA8K_REG_VTU_FUNC0, reg); ++ if (ret) ++ goto out; ++ ret = qca8k_vlan_access(priv, QCA8K_VLAN_LOAD, vid); ++ ++out: ++ mutex_unlock(&priv->reg_mutex); ++ ++ return ret; ++} ++ ++static int ++qca8k_vlan_del(struct qca8k_priv *priv, u8 port, u16 vid) ++{ ++ u32 reg, mask; ++ int ret, i; ++ bool del; ++ ++ mutex_lock(&priv->reg_mutex); ++ ret = qca8k_vlan_access(priv, QCA8K_VLAN_READ, vid); ++ if (ret < 0) ++ goto out; ++ ++ ret = qca8k_read(priv, QCA8K_REG_VTU_FUNC0, ®); ++ if (ret < 0) ++ goto out; ++ 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_PORT_NOT(i); ++ ++ if ((reg & mask) != mask) { ++ del = false; ++ break; ++ } ++ } ++ ++ if (del) { ++ ret = qca8k_vlan_access(priv, QCA8K_VLAN_PURGE, vid); ++ } else { ++ ret = qca8k_write(priv, QCA8K_REG_VTU_FUNC0, reg); ++ if (ret) ++ goto out; ++ ret = qca8k_vlan_access(priv, QCA8K_VLAN_LOAD, vid); ++ } ++ ++out: ++ mutex_unlock(&priv->reg_mutex); ++ ++ return ret; ++} ++ ++static int ++qca8k_mib_init(struct qca8k_priv *priv) ++{ ++ int ret; ++ ++ mutex_lock(&priv->reg_mutex); ++ 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; ++ ++ ret = qca8k_busy_wait(priv, QCA8K_REG_MIB, QCA8K_MIB_BUSY); ++ if (ret) ++ goto exit; ++ ++ ret = regmap_set_bits(priv->regmap, QCA8K_REG_MIB, QCA8K_MIB_CPU_KEEP); ++ if (ret) ++ goto exit; ++ ++ ret = qca8k_write(priv, QCA8K_REG_MODULE_EN, QCA8K_MODULE_EN_MIB); ++ ++exit: ++ mutex_unlock(&priv->reg_mutex); ++ return ret; ++} ++ ++static void ++qca8k_port_set_status(struct qca8k_priv *priv, int port, int enable) ++{ ++ u32 mask = QCA8K_PORT_STATUS_TXMAC | QCA8K_PORT_STATUS_RXMAC; ++ ++ /* Port 0 and 6 have no internal PHY */ ++ if (port > 0 && port < 6) ++ mask |= QCA8K_PORT_STATUS_LINK_AUTO; ++ ++ if (enable) ++ regmap_set_bits(priv->regmap, QCA8K_REG_PORT_STATUS(port), mask); ++ else ++ 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, 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, sizeof(clear_val)); ++ if (!clear_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, sizeof(clear_val)); ++ if (!read_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) ++{ ++ /* From Andrew Lunn: ++ * Port 0 has no internal phy. ++ * Port 1 has an internal PHY at MDIO address 0. ++ * Port 2 has an internal PHY at MDIO address 1. ++ * ... ++ * Port 5 has an internal PHY at MDIO address 4. ++ * Port 6 has no internal PHY. ++ */ ++ ++ return port - 1; ++} ++ ++static int ++qca8k_mdio_busy_wait(struct mii_bus *bus, u32 reg, u32 mask) ++{ ++ u16 r1, r2, page; ++ u32 val; ++ int ret, ret1; ++ ++ qca8k_split_addr(reg, &r1, &r2, &page); ++ ++ ret = read_poll_timeout(qca8k_mii_read32, ret1, !(val & mask), 0, ++ QCA8K_BUSY_WAIT_TIMEOUT * USEC_PER_MSEC, false, ++ bus, 0x10 | r2, r1, &val); ++ ++ /* Check if qca8k_read has failed for a different reason ++ * before returnting -ETIMEDOUT ++ */ ++ if (ret < 0 && ret1 < 0) ++ return ret1; ++ ++ return ret; ++} ++ ++static int ++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; ++ ++ if (regnum >= QCA8K_MDIO_MASTER_MAX_REG) ++ return -EINVAL; ++ ++ val = QCA8K_MDIO_MASTER_BUSY | QCA8K_MDIO_MASTER_EN | ++ QCA8K_MDIO_MASTER_WRITE | QCA8K_MDIO_MASTER_PHY_ADDR(phy) | ++ QCA8K_MDIO_MASTER_REG_ADDR(regnum) | ++ QCA8K_MDIO_MASTER_DATA(data); ++ ++ qca8k_split_addr(QCA8K_MDIO_MASTER_CTRL, &r1, &r2, &page); ++ ++ mutex_lock_nested(&bus->mdio_lock, MDIO_MUTEX_NESTED); ++ ++ ret = qca8k_set_page(priv, page); ++ if (ret) ++ goto exit; ++ ++ 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(priv, 0x10 | r2, r1, 0); ++ ++ mutex_unlock(&bus->mdio_lock); ++ ++ return ret; ++} ++ ++static int ++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; ++ ++ if (regnum >= QCA8K_MDIO_MASTER_MAX_REG) ++ return -EINVAL; ++ ++ val = QCA8K_MDIO_MASTER_BUSY | QCA8K_MDIO_MASTER_EN | ++ QCA8K_MDIO_MASTER_READ | QCA8K_MDIO_MASTER_PHY_ADDR(phy) | ++ QCA8K_MDIO_MASTER_REG_ADDR(regnum); ++ ++ qca8k_split_addr(QCA8K_MDIO_MASTER_CTRL, &r1, &r2, &page); ++ ++ mutex_lock_nested(&bus->mdio_lock, MDIO_MUTEX_NESTED); ++ ++ ret = qca8k_set_page(priv, page); ++ if (ret) ++ goto exit; ++ ++ qca8k_mii_write32(priv, 0x10 | r2, r1, val); ++ ++ ret = qca8k_mdio_busy_wait(bus, QCA8K_MDIO_MASTER_CTRL, ++ QCA8K_MDIO_MASTER_BUSY); ++ if (ret) ++ goto exit; ++ ++ ret = qca8k_mii_read32(bus, 0x10 | r2, r1, &val); ++ ++exit: ++ /* even if the busy_wait timeouts try to clear the MASTER_EN */ ++ qca8k_mii_write32(priv, 0x10 | r2, r1, 0); ++ ++ mutex_unlock(&bus->mdio_lock); ++ ++ if (ret >= 0) ++ ret = val & QCA8K_MDIO_MASTER_DATA_MASK; ++ ++ return ret; ++} ++ ++static int ++qca8k_internal_mdio_write(struct mii_bus *slave_bus, int phy, int regnum, u16 data) ++{ ++ struct qca8k_priv *priv = slave_bus->priv; ++ 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(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; ++ 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; ++ ++ ret = qca8k_mdio_read(priv, phy, regnum); ++ ++ if (ret < 0) ++ return 0xffff; ++ ++ return ret; ++} ++ ++static int ++qca8k_legacy_mdio_write(struct mii_bus *slave_bus, int port, int regnum, u16 data) ++{ ++ port = qca8k_port_to_phy(port) % PHY_MAX_ADDR; ++ ++ return qca8k_internal_mdio_write(slave_bus, port, regnum, data); ++} ++ ++static int ++qca8k_legacy_mdio_read(struct mii_bus *slave_bus, int port, int regnum) ++{ ++ port = qca8k_port_to_phy(port) % PHY_MAX_ADDR; ++ ++ return qca8k_internal_mdio_read(slave_bus, port, regnum); ++} ++ ++static int ++qca8k_mdio_register(struct qca8k_priv *priv) ++{ ++ struct dsa_switch *ds = priv->ds; ++ struct device_node *mdio; ++ struct mii_bus *bus; ++ ++ bus = devm_mdiobus_alloc(ds->dev); ++ if (!bus) ++ return -ENOMEM; ++ ++ bus->priv = (void *)priv; ++ snprintf(bus->id, MII_BUS_ID_SIZE, "qca8k-%d.%d", ++ ds->dst->index, ds->index); ++ bus->parent = ds->dev; ++ bus->phy_mask = ~ds->phys_mii_mask; ++ ds->slave_mii_bus = bus; ++ ++ /* Check if the devicetree declare the port:phy mapping */ ++ mdio = of_get_child_by_name(priv->dev->of_node, "mdio"); ++ if (of_device_is_available(mdio)) { ++ bus->name = "qca8k slave mii"; ++ bus->read = qca8k_internal_mdio_read; ++ bus->write = qca8k_internal_mdio_write; ++ return devm_of_mdiobus_register(priv->dev, bus, mdio); ++ } ++ ++ /* If a mapping can't be found the legacy mapping is used, ++ * using the qca8k_port_to_phy function ++ */ ++ bus->name = "qca8k-legacy slave mii"; ++ bus->read = qca8k_legacy_mdio_read; ++ bus->write = qca8k_legacy_mdio_write; ++ return devm_mdiobus_register(priv->dev, bus); ++} ++ ++static int ++qca8k_setup_mdio_bus(struct qca8k_priv *priv) ++{ ++ u32 internal_mdio_mask = 0, external_mdio_mask = 0, reg; ++ struct device_node *ports, *port; ++ phy_interface_t mode; ++ int err; ++ ++ ports = of_get_child_by_name(priv->dev->of_node, "ports"); ++ if (!ports) ++ ports = of_get_child_by_name(priv->dev->of_node, "ethernet-ports"); ++ ++ if (!ports) ++ return -EINVAL; ++ ++ for_each_available_child_of_node(ports, port) { ++ err = of_property_read_u32(port, "reg", ®); ++ if (err) { ++ of_node_put(port); ++ of_node_put(ports); ++ return err; ++ } ++ ++ if (!dsa_is_user_port(priv->ds, reg)) ++ continue; ++ ++ of_get_phy_mode(port, &mode); ++ ++ if (of_property_read_bool(port, "phy-handle") && ++ mode != PHY_INTERFACE_MODE_INTERNAL) ++ external_mdio_mask |= BIT(reg); ++ else ++ internal_mdio_mask |= BIT(reg); ++ } ++ ++ of_node_put(ports); ++ if (!external_mdio_mask && !internal_mdio_mask) { ++ dev_err(priv->dev, "no PHYs are defined.\n"); ++ return -EINVAL; ++ } ++ ++ /* The QCA8K_MDIO_MASTER_EN Bit, which grants access to PHYs through ++ * the MDIO_MASTER register also _disconnects_ the external MDC ++ * passthrough to the internal PHYs. It's not possible to use both ++ * configurations at the same time! ++ * ++ * Because this came up during the review process: ++ * If the external mdio-bus driver is capable magically disabling ++ * the QCA8K_MDIO_MASTER_EN and mutex/spin-locking out the qca8k's ++ * accessors for the time being, it would be possible to pull this ++ * off. ++ */ ++ if (!!external_mdio_mask && !!internal_mdio_mask) { ++ dev_err(priv->dev, "either internal or external mdio bus configuration is supported.\n"); ++ return -EINVAL; ++ } ++ ++ if (external_mdio_mask) { ++ /* Make sure to disable the internal mdio bus in cases ++ * a dt-overlay and driver reload changed the configuration ++ */ ++ ++ return regmap_clear_bits(priv->regmap, QCA8K_MDIO_MASTER_CTRL, ++ QCA8K_MDIO_MASTER_EN); ++ } ++ ++ return qca8k_mdio_register(priv); ++} ++ ++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_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_setup_of_pws_reg(struct qca8k_priv *priv) ++{ ++ const struct qca8k_match_data *data = priv->info; ++ 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) { ++ /* 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, ++ val); ++ 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 = -1, ret; ++ struct device_node *port_dn; ++ phy_interface_t mode; ++ struct dsa_port *dp; ++ u32 delay; ++ ++ /* 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; ++ cpu_port_index++; ++ ++ if (!of_device_is_available(port_dn)) ++ continue; ++ ++ ret = of_get_phy_mode(port_dn, &mode); ++ if (ret) ++ continue; ++ ++ 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: ++ case PHY_INTERFACE_MODE_SGMII: ++ 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 (!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; ++ } ++ ++ priv->ports_config.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 (!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; ++ } ++ ++ priv->ports_config.rgmii_rx_delay[cpu_port_index] = delay; ++ ++ /* 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->ports_config.sgmii_tx_clk_falling_edge = true; ++ ++ if (of_property_read_bool(port_dn, "qca,sgmii-rxclk-falling-edge")) ++ priv->ports_config.sgmii_rx_clk_falling_edge = true; ++ ++ if (of_property_read_bool(port_dn, "qca,sgmii-enable-pll")) { ++ 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->ports_config.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; ++ } ++ } ++ ++ return 0; ++} ++ ++static int ++qca8k_setup(struct dsa_switch *ds) ++{ ++ struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv; ++ int cpu_port, ret, i; ++ u32 mask; ++ ++ 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 */ ++ ret = qca8k_parse_port_config(priv); ++ if (ret) ++ return ret; ++ ++ ret = qca8k_setup_mdio_bus(priv); ++ 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; ++ ++ /* Make sure MAC06 is disabled */ ++ 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 = 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; ++ } ++ ++ /* Enable MIB counters */ ++ ret = qca8k_mib_init(priv); ++ if (ret) ++ dev_warn(priv->dev, "mib init failed"); ++ ++ /* 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; ++ ++ /* 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), ++ 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; ++ } ++ } ++ ++ /* 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 ++ * 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, ++ 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; ++ ++ /* 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(i), ++ QCA8K_PORT_LOOKUP_MEMBER, dsa_user_ports(ds)); ++ if (ret) ++ return ret; ++ } ++ ++ /* Individual user ports get connected to CPU port only */ ++ if (dsa_is_user_port(ds, i)) { ++ ret = qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(i), ++ QCA8K_PORT_LOOKUP_MEMBER, ++ BIT(cpu_port)); ++ if (ret) ++ return ret; ++ ++ /* Enable ARP Auto-learning by default */ ++ ret = regmap_set_bits(priv->regmap, QCA8K_PORT_LOOKUP_CTRL(i), ++ QCA8K_PORT_LOOKUP_LEARN); ++ if (ret) ++ return ret; ++ ++ /* For port based vlans to work we need to set the ++ * default egress vid ++ */ ++ ret = qca8k_rmw(priv, QCA8K_EGRESS_VLAN(i), ++ QCA8K_EGREES_VLAN_PORT_MASK(i), ++ QCA8K_EGREES_VLAN_PORT(i, QCA8K_PORT_VID_DEF)); ++ if (ret) ++ return ret; ++ ++ ret = qca8k_write(priv, QCA8K_REG_PORT_VLAN_CTRL0(i), ++ QCA8K_PORT_VLAN_CVID(QCA8K_PORT_VID_DEF) | ++ QCA8K_PORT_VLAN_SVID(QCA8K_PORT_VID_DEF)); ++ 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) { ++ switch (i) { ++ /* The 2 CPU port and port 5 requires some different ++ * priority than any other ports. ++ */ ++ case 0: ++ case 5: ++ case 6: ++ mask = QCA8K_PORT_HOL_CTRL0_EG_PRI0(0x3) | ++ QCA8K_PORT_HOL_CTRL0_EG_PRI1(0x4) | ++ QCA8K_PORT_HOL_CTRL0_EG_PRI2(0x4) | ++ QCA8K_PORT_HOL_CTRL0_EG_PRI3(0x4) | ++ QCA8K_PORT_HOL_CTRL0_EG_PRI4(0x6) | ++ QCA8K_PORT_HOL_CTRL0_EG_PRI5(0x8) | ++ QCA8K_PORT_HOL_CTRL0_EG_PORT(0x1e); ++ break; ++ default: ++ mask = QCA8K_PORT_HOL_CTRL0_EG_PRI0(0x3) | ++ QCA8K_PORT_HOL_CTRL0_EG_PRI1(0x4) | ++ QCA8K_PORT_HOL_CTRL0_EG_PRI2(0x6) | ++ QCA8K_PORT_HOL_CTRL0_EG_PRI3(0x8) | ++ QCA8K_PORT_HOL_CTRL0_EG_PORT(0x19); ++ } ++ qca8k_write(priv, QCA8K_REG_PORT_HOL_CTRL0(i), mask); ++ ++ mask = QCA8K_PORT_HOL_CTRL1_ING(0x6) | ++ QCA8K_PORT_HOL_CTRL1_EG_PRI_BUF_EN | ++ 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_MASK | ++ QCA8K_PORT_HOL_CTRL1_EG_PRI_BUF_EN | ++ QCA8K_PORT_HOL_CTRL1_EG_PORT_BUF_EN | ++ QCA8K_PORT_HOL_CTRL1_WRED_EN, ++ mask); ++ } ++ } ++ ++ /* Special GLOBAL_FC_THRESH value are needed for ar8327 switch */ ++ if (priv->switch_id == QCA8K_ID_QCA8327) { ++ 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_MASK | ++ QCA8K_GLOBAL_FC_GOL_XOFF_THRES_MASK, ++ mask); ++ } ++ ++ /* Setup our port MTUs to match power on defaults */ ++ ret = qca8k_write(priv, QCA8K_MAX_FRAME_SIZE, ETH_FRAME_LEN + ETH_FCS_LEN); ++ if (ret) ++ dev_warn(priv->dev, "failed setting MTU settings"); ++ ++ /* Flush the FDB table */ ++ qca8k_fdb_flush(priv); ++ ++ /* 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; ++ ++ /* Set max number of LAGs supported */ ++ ds->num_lag_ids = QCA8K_NUM_LAGS; ++ ++ return 0; ++} ++ ++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->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->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; ++ } ++ ++ /* 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; ++ ++ switch (port) { ++ case 0: /* 1st CPU port */ ++ if (state->interface != PHY_INTERFACE_MODE_RGMII && ++ state->interface != PHY_INTERFACE_MODE_RGMII_ID && ++ state->interface != PHY_INTERFACE_MODE_RGMII_TXID && ++ state->interface != PHY_INTERFACE_MODE_RGMII_RXID && ++ state->interface != PHY_INTERFACE_MODE_SGMII) ++ return; ++ ++ reg = QCA8K_REG_PORT0_PAD_CTRL; ++ cpu_port_index = QCA8K_CPU_PORT0; ++ break; ++ case 1: ++ case 2: ++ case 3: ++ case 4: ++ case 5: ++ /* Internal PHY, nothing to do */ ++ return; ++ case 6: /* 2nd CPU port / external PHY */ ++ if (state->interface != PHY_INTERFACE_MODE_RGMII && ++ state->interface != PHY_INTERFACE_MODE_RGMII_ID && ++ state->interface != PHY_INTERFACE_MODE_RGMII_TXID && ++ state->interface != PHY_INTERFACE_MODE_RGMII_RXID && ++ state->interface != PHY_INTERFACE_MODE_SGMII && ++ state->interface != PHY_INTERFACE_MODE_1000BASEX) ++ 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); ++ return; ++ } ++ ++ if (port != 6 && phylink_autoneg_inband(mode)) { ++ dev_err(ds->dev, "%s: in-band negotiation unsupported\n", ++ __func__); ++ return; ++ } ++ ++ switch (state->interface) { ++ case PHY_INTERFACE_MODE_RGMII: ++ case PHY_INTERFACE_MODE_RGMII_ID: ++ case PHY_INTERFACE_MODE_RGMII_TXID: ++ case PHY_INTERFACE_MODE_RGMII_RXID: ++ qca8k_write(priv, reg, QCA8K_PORT_PAD_RGMII_EN); ++ ++ /* 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, ++ * rather than individual port registers. ++ */ ++ if (priv->switch_id == QCA8K_ID_QCA8337) ++ qca8k_write(priv, QCA8K_REG_PORT5_PAD_CTRL, ++ QCA8K_PORT_PAD_RGMII_RX_DELAY_EN); ++ break; ++ case PHY_INTERFACE_MODE_SGMII: ++ case PHY_INTERFACE_MODE_1000BASEX: ++ /* Enable SGMII on the port */ ++ qca8k_write(priv, reg, QCA8K_PORT_PAD_SGMII_EN); ++ ++ /* Enable/disable SerDes auto-negotiation as necessary */ ++ ret = qca8k_read(priv, QCA8K_REG_PWS, &val); ++ if (ret) ++ return; ++ if (phylink_autoneg_inband(mode)) ++ val &= ~QCA8K_PWS_SERDES_AEN_DIS; ++ else ++ val |= QCA8K_PWS_SERDES_AEN_DIS; ++ qca8k_write(priv, QCA8K_REG_PWS, val); ++ ++ /* Configure the SGMII parameters */ ++ ret = qca8k_read(priv, QCA8K_REG_SGMII_CTRL, &val); ++ if (ret) ++ return; ++ ++ val |= QCA8K_SGMII_EN_SD; ++ ++ if (priv->ports_config.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 */ ++ val &= ~QCA8K_SGMII_MODE_CTRL_MASK; ++ val |= QCA8K_SGMII_MODE_CTRL_PHY; ++ } else if (state->interface == PHY_INTERFACE_MODE_SGMII) { ++ val &= ~QCA8K_SGMII_MODE_CTRL_MASK; ++ val |= QCA8K_SGMII_MODE_CTRL_MAC; ++ } else if (state->interface == PHY_INTERFACE_MODE_1000BASEX) { ++ val &= ~QCA8K_SGMII_MODE_CTRL_MASK; ++ val |= QCA8K_SGMII_MODE_CTRL_BASEX; ++ } ++ ++ 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 ++ */ ++ 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->ports_config.sgmii_rx_clk_falling_edge) ++ val |= QCA8K_PORT0_PAD_SGMII_RXCLK_FALLING_EDGE; ++ ++ if (priv->ports_config.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", ++ phy_modes(state->interface), port); ++ return; ++ } ++} ++ ++static void ++qca8k_phylink_validate(struct dsa_switch *ds, int port, ++ unsigned long *supported, ++ struct phylink_link_state *state) ++{ ++ __ETHTOOL_DECLARE_LINK_MODE_MASK(mask) = { 0, }; ++ ++ switch (port) { ++ case 0: /* 1st CPU port */ ++ if (state->interface != PHY_INTERFACE_MODE_NA && ++ state->interface != PHY_INTERFACE_MODE_RGMII && ++ state->interface != PHY_INTERFACE_MODE_RGMII_ID && ++ state->interface != PHY_INTERFACE_MODE_RGMII_TXID && ++ state->interface != PHY_INTERFACE_MODE_RGMII_RXID && ++ state->interface != PHY_INTERFACE_MODE_SGMII) ++ goto unsupported; ++ break; ++ case 1: ++ case 2: ++ case 3: ++ case 4: ++ case 5: ++ /* Internal PHY */ ++ if (state->interface != PHY_INTERFACE_MODE_NA && ++ state->interface != PHY_INTERFACE_MODE_GMII && ++ state->interface != PHY_INTERFACE_MODE_INTERNAL) ++ goto unsupported; ++ break; ++ case 6: /* 2nd CPU port / external PHY */ ++ if (state->interface != PHY_INTERFACE_MODE_NA && ++ state->interface != PHY_INTERFACE_MODE_RGMII && ++ state->interface != PHY_INTERFACE_MODE_RGMII_ID && ++ state->interface != PHY_INTERFACE_MODE_RGMII_TXID && ++ state->interface != PHY_INTERFACE_MODE_RGMII_RXID && ++ state->interface != PHY_INTERFACE_MODE_SGMII && ++ state->interface != PHY_INTERFACE_MODE_1000BASEX) ++ goto unsupported; ++ break; ++ default: ++unsupported: ++ linkmode_zero(supported); ++ return; ++ } ++ ++ phylink_set_port_modes(mask); ++ phylink_set(mask, Autoneg); ++ ++ phylink_set(mask, 1000baseT_Full); ++ phylink_set(mask, 10baseT_Half); ++ phylink_set(mask, 10baseT_Full); ++ phylink_set(mask, 100baseT_Half); ++ phylink_set(mask, 100baseT_Full); ++ ++ if (state->interface == PHY_INTERFACE_MODE_1000BASEX) ++ phylink_set(mask, 1000baseX_Full); ++ ++ phylink_set(mask, Pause); ++ phylink_set(mask, Asym_Pause); ++ ++ linkmode_and(supported, supported, mask); ++ linkmode_and(state->advertising, state->advertising, mask); ++} ++ ++static int ++qca8k_phylink_mac_link_state(struct dsa_switch *ds, int port, ++ struct phylink_link_state *state) ++{ ++ struct qca8k_priv *priv = ds->priv; ++ u32 reg; ++ int ret; ++ ++ ret = qca8k_read(priv, QCA8K_REG_PORT_STATUS(port), ®); ++ if (ret < 0) ++ return ret; ++ ++ state->link = !!(reg & QCA8K_PORT_STATUS_LINK_UP); ++ state->an_complete = state->link; ++ state->an_enabled = !!(reg & QCA8K_PORT_STATUS_LINK_AUTO); ++ state->duplex = (reg & QCA8K_PORT_STATUS_DUPLEX) ? DUPLEX_FULL : ++ DUPLEX_HALF; ++ ++ switch (reg & QCA8K_PORT_STATUS_SPEED) { ++ case QCA8K_PORT_STATUS_SPEED_10: ++ state->speed = SPEED_10; ++ break; ++ case QCA8K_PORT_STATUS_SPEED_100: ++ state->speed = SPEED_100; ++ break; ++ case QCA8K_PORT_STATUS_SPEED_1000: ++ state->speed = SPEED_1000; ++ break; ++ default: ++ state->speed = SPEED_UNKNOWN; ++ break; ++ } ++ ++ state->pause = MLO_PAUSE_NONE; ++ if (reg & QCA8K_PORT_STATUS_RXFLOW) ++ state->pause |= MLO_PAUSE_RX; ++ if (reg & QCA8K_PORT_STATUS_TXFLOW) ++ state->pause |= MLO_PAUSE_TX; ++ ++ return 1; ++} ++ ++static void ++qca8k_phylink_mac_link_down(struct dsa_switch *ds, int port, unsigned int mode, ++ phy_interface_t interface) ++{ ++ struct qca8k_priv *priv = ds->priv; ++ ++ qca8k_port_set_status(priv, port, 0); ++} ++ ++static void ++qca8k_phylink_mac_link_up(struct dsa_switch *ds, int port, unsigned int mode, ++ phy_interface_t interface, struct phy_device *phydev, ++ int speed, int duplex, bool tx_pause, bool rx_pause) ++{ ++ struct qca8k_priv *priv = ds->priv; ++ u32 reg; ++ ++ if (phylink_autoneg_inband(mode)) { ++ reg = QCA8K_PORT_STATUS_LINK_AUTO; ++ } else { ++ switch (speed) { ++ case SPEED_10: ++ reg = QCA8K_PORT_STATUS_SPEED_10; ++ break; ++ case SPEED_100: ++ reg = QCA8K_PORT_STATUS_SPEED_100; ++ break; ++ case SPEED_1000: ++ reg = QCA8K_PORT_STATUS_SPEED_1000; ++ break; ++ default: ++ reg = QCA8K_PORT_STATUS_LINK_AUTO; ++ break; ++ } ++ ++ if (duplex == DUPLEX_FULL) ++ reg |= QCA8K_PORT_STATUS_DUPLEX; ++ ++ if (rx_pause || dsa_is_cpu_port(ds, port)) ++ reg |= QCA8K_PORT_STATUS_RXFLOW; ++ ++ if (tx_pause || dsa_is_cpu_port(ds, port)) ++ reg |= QCA8K_PORT_STATUS_TXFLOW; ++ } ++ ++ reg |= QCA8K_PORT_STATUS_TXMAC | QCA8K_PORT_STATUS_RXMAC; ++ ++ qca8k_write(priv, QCA8K_REG_PORT_STATUS(port), reg); ++} ++ ++static void ++qca8k_get_strings(struct dsa_switch *ds, int port, u32 stringset, uint8_t *data) ++{ ++ struct qca8k_priv *priv = ds->priv; ++ int i; ++ ++ if (stringset != ETH_SS_STATS) ++ return; ++ ++ for (i = 0; i < priv->info->mib_count; i++) ++ strncpy(data + i * ETH_GSTRING_LEN, ar8327_mib[i].name, ++ ETH_GSTRING_LEN); ++} ++ ++static void qca8k_mib_autocast_handler(struct dsa_switch *ds, struct sk_buff *skb) ++{ ++ 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; ++ ++ data = mib_eth_data->data; ++ ++ for (i = 0; i < priv->info->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) ++{ ++ struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv; ++ const struct qca8k_mib_desc *mib; ++ u32 reg, i, val; ++ u32 hi = 0; ++ int ret; ++ ++ if (priv->mgmt_master && priv->info->ops->autocast_mib && ++ priv->info->ops->autocast_mib(ds, port, data) > 0) ++ return; ++ ++ for (i = 0; i < priv->info->mib_count; i++) { ++ mib = &ar8327_mib[i]; ++ reg = QCA8K_PORT_MIB_COUNTER(port) + mib->offset; ++ ++ ret = qca8k_read(priv, reg, &val); ++ if (ret < 0) ++ continue; ++ ++ if (mib->size == 2) { ++ ret = qca8k_read(priv, reg + 4, &hi); ++ if (ret < 0) ++ continue; ++ } ++ ++ data[i] = val; ++ if (mib->size == 2) ++ data[i] |= (u64)hi << 32; ++ } ++} ++ ++static int ++qca8k_get_sset_count(struct dsa_switch *ds, int port, int sset) ++{ ++ struct qca8k_priv *priv = ds->priv; ++ ++ if (sset != ETH_SS_STATS) ++ return 0; ++ ++ return priv->info->mib_count; ++} ++ ++static int ++qca8k_set_mac_eee(struct dsa_switch *ds, int port, struct ethtool_eee *eee) ++{ ++ struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv; ++ u32 lpi_en = QCA8K_REG_EEE_CTRL_LPI_EN(port); ++ u32 reg; ++ int ret; ++ ++ mutex_lock(&priv->reg_mutex); ++ ret = qca8k_read(priv, QCA8K_REG_EEE_CTRL, ®); ++ if (ret < 0) ++ goto exit; ++ ++ if (eee->eee_enabled) ++ reg |= lpi_en; ++ else ++ reg &= ~lpi_en; ++ ret = qca8k_write(priv, QCA8K_REG_EEE_CTRL, reg); ++ ++exit: ++ mutex_unlock(&priv->reg_mutex); ++ return ret; ++} ++ ++static int ++qca8k_get_mac_eee(struct dsa_switch *ds, int port, struct ethtool_eee *e) ++{ ++ /* Nothing to do on the port's MAC */ ++ return 0; ++} ++ ++static void ++qca8k_port_stp_state_set(struct dsa_switch *ds, int port, u8 state) ++{ ++ struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv; ++ u32 stp_state; ++ ++ switch (state) { ++ case BR_STATE_DISABLED: ++ stp_state = QCA8K_PORT_LOOKUP_STATE_DISABLED; ++ break; ++ case BR_STATE_BLOCKING: ++ stp_state = QCA8K_PORT_LOOKUP_STATE_BLOCKING; ++ break; ++ case BR_STATE_LISTENING: ++ stp_state = QCA8K_PORT_LOOKUP_STATE_LISTENING; ++ break; ++ case BR_STATE_LEARNING: ++ stp_state = QCA8K_PORT_LOOKUP_STATE_LEARNING; ++ break; ++ case BR_STATE_FORWARDING: ++ default: ++ stp_state = QCA8K_PORT_LOOKUP_STATE_FORWARD; ++ break; ++ } ++ ++ qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(port), ++ QCA8K_PORT_LOOKUP_STATE_MASK, stp_state); ++} ++ ++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, cpu_port; ++ int i, ret; ++ ++ cpu_port = dsa_to_port(ds, port)->cpu_dp->index; ++ port_mask = BIT(cpu_port); ++ ++ 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 ++ * in the bridge ++ */ ++ ret = regmap_set_bits(priv->regmap, ++ QCA8K_PORT_LOOKUP_CTRL(i), ++ BIT(port)); ++ if (ret) ++ return ret; ++ if (i != port) ++ port_mask |= BIT(i); ++ } ++ ++ /* Add all other ports to this ports portvlan mask */ ++ ret = qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(port), ++ QCA8K_PORT_LOOKUP_MEMBER, port_mask); ++ ++ return ret; ++} ++ ++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 cpu_port, i; ++ ++ cpu_port = dsa_to_port(ds, port)->cpu_dp->index; ++ ++ 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 ++ * in the bridge ++ */ ++ 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 ++ * this port ++ */ ++ qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(port), ++ 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_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) ++{ ++ struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv; ++ ++ qca8k_port_set_status(priv, port, 1); ++ priv->port_enabled_map |= BIT(port); ++ ++ if (dsa_is_user_port(ds, port)) ++ phy_support_asym_pause(phy); ++ ++ return 0; ++} ++ ++static void ++qca8k_port_disable(struct dsa_switch *ds, int port) ++{ ++ struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv; ++ ++ qca8k_port_set_status(priv, port, 0); ++ priv->port_enabled_map &= ~BIT(port); ++} ++ ++static int ++qca8k_port_change_mtu(struct dsa_switch *ds, int port, int new_mtu) ++{ ++ struct qca8k_priv *priv = ds->priv; ++ int ret; ++ ++ /* We have only have a general MTU setting. ++ * DSA always set the CPU port's MTU to the largest MTU of the slave ++ * ports. ++ * Setting MTU just for the CPU port is sufficient to correctly set a ++ * value for every port. ++ */ ++ if (!dsa_is_cpu_port(ds, port)) ++ return 0; ++ ++ /* To change the MAX_FRAME_SIZE the cpu ports must be off or ++ * the switch panics. ++ * Turn off both cpu ports before applying the new value to prevent ++ * this. ++ */ ++ if (priv->port_enabled_map & BIT(0)) ++ qca8k_port_set_status(priv, 0, 0); ++ ++ if (priv->port_enabled_map & BIT(6)) ++ qca8k_port_set_status(priv, 6, 0); ++ ++ /* Include L2 header / FCS length */ ++ ret = qca8k_write(priv, QCA8K_MAX_FRAME_SIZE, new_mtu + ETH_HLEN + ETH_FCS_LEN); ++ ++ if (priv->port_enabled_map & BIT(0)) ++ qca8k_port_set_status(priv, 0, 1); ++ ++ if (priv->port_enabled_map & BIT(6)) ++ qca8k_port_set_status(priv, 6, 1); ++ ++ return ret; ++} ++ ++static int ++qca8k_port_max_mtu(struct dsa_switch *ds, int port) ++{ ++ return QCA8K_MAX_MTU; ++} ++ ++static int ++qca8k_port_fdb_insert(struct qca8k_priv *priv, const u8 *addr, ++ u16 port_mask, u16 vid) ++{ ++ /* Set the vid to the port vlan id if no vid is set */ ++ if (!vid) ++ vid = QCA8K_PORT_VID_DEF; ++ ++ return qca8k_fdb_add(priv, addr, port_mask, vid, ++ QCA8K_ATU_STATUS_STATIC); ++} ++ ++static int ++qca8k_port_fdb_add(struct dsa_switch *ds, int port, ++ const unsigned char *addr, u16 vid) ++{ ++ struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv; ++ u16 port_mask = BIT(port); ++ ++ return qca8k_port_fdb_insert(priv, addr, port_mask, vid); ++} ++ ++static int ++qca8k_port_fdb_del(struct dsa_switch *ds, int port, ++ const unsigned char *addr, u16 vid) ++{ ++ struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv; ++ u16 port_mask = BIT(port); ++ ++ if (!vid) ++ vid = QCA8K_PORT_VID_DEF; ++ ++ return qca8k_fdb_del(priv, addr, port_mask, vid); ++} ++ ++static int ++qca8k_port_fdb_dump(struct dsa_switch *ds, int port, ++ dsa_fdb_dump_cb_t *cb, void *data) ++{ ++ struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv; ++ struct qca8k_fdb _fdb = { 0 }; ++ int cnt = QCA8K_NUM_FDB_RECORDS; ++ bool is_static; ++ int ret = 0; ++ ++ mutex_lock(&priv->reg_mutex); ++ while (cnt-- && !qca8k_fdb_next(priv, &_fdb, port)) { ++ if (!_fdb.aging) ++ break; ++ is_static = (_fdb.aging == QCA8K_ATU_STATUS_STATIC); ++ ret = cb(_fdb.mac, _fdb.vid, is_static, data); ++ if (ret) ++ break; ++ } ++ mutex_unlock(&priv->reg_mutex); ++ ++ return 0; ++} ++ ++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_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) ++{ ++ struct qca8k_priv *priv = ds->priv; ++ int ret; ++ ++ if (vlan_filtering) { ++ ret = qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(port), ++ 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_MASK, ++ QCA8K_PORT_LOOKUP_VLAN_MODE_NONE); ++ } ++ ++ return ret; ++} ++ ++static int ++qca8k_port_vlan_add(struct dsa_switch *ds, int port, ++ const struct switchdev_obj_port_vlan *vlan, ++ struct netlink_ext_ack *extack) ++{ ++ bool untagged = vlan->flags & BRIDGE_VLAN_INFO_UNTAGGED; ++ bool pvid = vlan->flags & BRIDGE_VLAN_INFO_PVID; ++ struct qca8k_priv *priv = ds->priv; ++ int ret; ++ ++ ret = qca8k_vlan_add(priv, port, vlan->vid, untagged); ++ if (ret) { ++ dev_err(priv->dev, "Failed to add VLAN to port %d (%d)", port, ret); ++ return ret; ++ } ++ ++ if (pvid) { ++ ret = qca8k_rmw(priv, QCA8K_EGRESS_VLAN(port), ++ QCA8K_EGREES_VLAN_PORT_MASK(port), ++ QCA8K_EGREES_VLAN_PORT(port, vlan->vid)); ++ if (ret) ++ return ret; ++ ++ ret = qca8k_write(priv, QCA8K_REG_PORT_VLAN_CTRL0(port), ++ QCA8K_PORT_VLAN_CVID(vlan->vid) | ++ QCA8K_PORT_VLAN_SVID(vlan->vid)); ++ } ++ ++ return ret; ++} ++ ++static int ++qca8k_port_vlan_del(struct dsa_switch *ds, int port, ++ const struct switchdev_obj_port_vlan *vlan) ++{ ++ struct qca8k_priv *priv = ds->priv; ++ int ret; ++ ++ ret = qca8k_vlan_del(priv, port, vlan->vid); ++ if (ret) ++ dev_err(priv->dev, "Failed to delete VLAN from port %d (%d)", port, ret); ++ ++ return ret; ++} ++ ++static u32 qca8k_get_phy_flags(struct dsa_switch *ds, int port) ++{ ++ struct qca8k_priv *priv = ds->priv; ++ ++ /* Communicate to the phy internal driver the switch revision. ++ * Based on the switch revision different values needs to be ++ * set to the dbg and mmd reg on the phy. ++ * The first 2 bit are used to communicate the switch revision ++ * to the phy driver. ++ */ ++ if (port > 0 && port < 6) ++ return priv->switch_revision; ++ ++ return 0; ++} ++ ++static enum dsa_tag_protocol ++qca8k_get_tag_protocol(struct dsa_switch *ds, int port, ++ enum dsa_tag_protocol mp) ++{ ++ 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; ++ u32 hash = 0; ++ int i, id; ++ ++ 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 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; ++ ++ 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); ++} ++ ++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; ++ tagger_data->mib_autocast_handler = qca8k_mib_autocast_handler; ++ ++ break; ++ default: ++ return -EOPNOTSUPP; ++ } ++ ++ return 0; ++} ++ ++static const struct dsa_switch_ops qca8k_switch_ops = { ++ .get_tag_protocol = qca8k_get_tag_protocol, ++ .setup = qca8k_setup, ++ .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, ++ .port_disable = qca8k_port_disable, ++ .port_change_mtu = qca8k_port_change_mtu, ++ .port_max_mtu = qca8k_port_max_mtu, ++ .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, ++ .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, ++ .phylink_validate = qca8k_phylink_validate, ++ .phylink_mac_link_state = qca8k_phylink_mac_link_state, ++ .phylink_mac_config = qca8k_phylink_mac_config, ++ .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, ++ .master_state_change = qca8k_master_change, ++ .connect_tag_protocol = qca8k_connect_tag_protocol, ++}; ++ ++static int qca8k_read_switch_id(struct qca8k_priv *priv) ++{ ++ u32 val; ++ u8 id; ++ int ret; ++ ++ if (!priv->info) ++ return -ENODEV; ++ ++ ret = qca8k_read(priv, QCA8K_REG_MASK_CTRL, &val); ++ if (ret < 0) ++ return -ENODEV; ++ ++ id = QCA8K_MASK_CTRL_DEVICE_ID(val); ++ if (id != priv->info->id) { ++ dev_err(priv->dev, ++ "Switch id detected %x but expected %x", ++ id, priv->info->id); ++ return -ENODEV; ++ } ++ ++ priv->switch_id = id; ++ ++ /* Save revision to communicate to the internal PHY driver */ ++ priv->switch_revision = QCA8K_MASK_CTRL_REV_ID(val); ++ ++ return 0; ++} ++ ++static int ++qca8k_sw_probe(struct mdio_device *mdiodev) ++{ ++ struct qca8k_priv *priv; ++ int ret; ++ ++ /* allocate the private data struct so that we can probe the switches ++ * ID register ++ */ ++ priv = devm_kzalloc(&mdiodev->dev, sizeof(*priv), GFP_KERNEL); ++ if (!priv) ++ return -ENOMEM; ++ ++ priv->info = of_device_get_match_data(priv->dev); ++ priv->bus = mdiodev->bus; ++ priv->dev = &mdiodev->dev; ++ ++ priv->reset_gpio = devm_gpiod_get_optional(priv->dev, "reset", ++ GPIOD_ASIS); ++ if (IS_ERR(priv->reset_gpio)) ++ return PTR_ERR(priv->reset_gpio); ++ ++ if (priv->reset_gpio) { ++ gpiod_set_value_cansleep(priv->reset_gpio, 1); ++ /* The active low duration must be greater than 10 ms ++ * and checkpatch.pl wants 20 ms. ++ */ ++ msleep(20); ++ 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); ++ } ++ ++ 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); ++ if (ret) ++ return ret; ++ ++ priv->ds = devm_kzalloc(&mdiodev->dev, sizeof(*priv->ds), GFP_KERNEL); ++ if (!priv->ds) ++ return -ENOMEM; ++ ++ 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; ++ priv->ds->ops = &qca8k_switch_ops; ++ mutex_init(&priv->reg_mutex); ++ dev_set_drvdata(&mdiodev->dev, priv); ++ ++ return dsa_register_switch(priv->ds); ++} ++ ++static void ++qca8k_sw_remove(struct mdio_device *mdiodev) ++{ ++ struct qca8k_priv *priv = dev_get_drvdata(&mdiodev->dev); ++ int i; ++ ++ if (!priv) ++ return; ++ ++ for (i = 0; i < QCA8K_NUM_PORTS; i++) ++ qca8k_port_set_status(priv, i, 0); ++ ++ dsa_unregister_switch(priv->ds); ++ ++ dev_set_drvdata(&mdiodev->dev, NULL); ++} ++ ++static void qca8k_sw_shutdown(struct mdio_device *mdiodev) ++{ ++ struct qca8k_priv *priv = dev_get_drvdata(&mdiodev->dev); ++ ++ if (!priv) ++ return; ++ ++ dsa_switch_shutdown(priv->ds); ++ ++ dev_set_drvdata(&mdiodev->dev, NULL); ++} ++ ++#ifdef CONFIG_PM_SLEEP ++static void ++qca8k_set_pm(struct qca8k_priv *priv, int enable) ++{ ++ int port; ++ ++ for (port = 0; port < QCA8K_NUM_PORTS; port++) { ++ /* Do not enable on resume if the port was ++ * disabled before. ++ */ ++ if (!(priv->port_enabled_map & BIT(port))) ++ continue; ++ ++ qca8k_port_set_status(priv, port, enable); ++ } ++} ++ ++static int qca8k_suspend(struct device *dev) ++{ ++ struct qca8k_priv *priv = dev_get_drvdata(dev); ++ ++ qca8k_set_pm(priv, 0); ++ ++ return dsa_switch_suspend(priv->ds); ++} ++ ++static int qca8k_resume(struct device *dev) ++{ ++ struct qca8k_priv *priv = dev_get_drvdata(dev); ++ ++ qca8k_set_pm(priv, 1); ++ ++ return dsa_switch_resume(priv->ds); ++} ++#endif /* CONFIG_PM_SLEEP */ ++ ++static SIMPLE_DEV_PM_OPS(qca8k_pm_ops, ++ qca8k_suspend, qca8k_resume); ++ ++static const struct qca8k_info_ops qca8xxx_ops = { ++ .autocast_mib = qca8k_get_ethtool_stats_eth, ++}; ++ ++static const struct qca8k_match_data qca8327 = { ++ .id = QCA8K_ID_QCA8327, ++ .reduced_package = true, ++ .mib_count = QCA8K_QCA832X_MIB_COUNT, ++ .ops = &qca8xxx_ops, ++}; ++ ++static const struct qca8k_match_data qca8328 = { ++ .id = QCA8K_ID_QCA8327, ++ .mib_count = QCA8K_QCA832X_MIB_COUNT, ++ .ops = &qca8xxx_ops, ++}; ++ ++static const struct qca8k_match_data qca833x = { ++ .id = QCA8K_ID_QCA8337, ++ .mib_count = QCA8K_QCA833X_MIB_COUNT, ++ .ops = &qca8xxx_ops, ++}; ++ ++static const struct of_device_id qca8k_of_match[] = { ++ { .compatible = "qca,qca8327", .data = &qca8327 }, ++ { .compatible = "qca,qca8328", .data = &qca8328 }, ++ { .compatible = "qca,qca8334", .data = &qca833x }, ++ { .compatible = "qca,qca8337", .data = &qca833x }, ++ { /* sentinel */ }, ++}; ++ ++static struct mdio_driver qca8kmdio_driver = { ++ .probe = qca8k_sw_probe, ++ .remove = qca8k_sw_remove, ++ .shutdown = qca8k_sw_shutdown, ++ .mdiodrv.driver = { ++ .name = "qca8k", ++ .of_match_table = qca8k_of_match, ++ .pm = &qca8k_pm_ops, ++ }, ++}; ++ ++mdio_module_driver(qca8kmdio_driver); ++ ++MODULE_AUTHOR("Mathieu Olivari, John Crispin "); ++MODULE_DESCRIPTION("Driver for QCA8K ethernet switch family"); ++MODULE_LICENSE("GPL v2"); ++MODULE_ALIAS("platform:qca8k"); +--- /dev/null ++++ b/drivers/net/dsa/qca/qca8k-common.c +@@ -0,0 +1,63 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (C) 2009 Felix Fietkau ++ * Copyright (C) 2011-2012 Gabor Juhos ++ * Copyright (c) 2015, 2019, The Linux Foundation. All rights reserved. ++ * Copyright (c) 2016 John Crispin ++ */ ++ ++#include ++#include ++ ++#include "qca8k.h" ++ ++#define MIB_DESC(_s, _o, _n) \ ++ { \ ++ .size = (_s), \ ++ .offset = (_o), \ ++ .name = (_n), \ ++ } ++ ++const struct qca8k_mib_desc ar8327_mib[] = { ++ MIB_DESC(1, 0x00, "RxBroad"), ++ MIB_DESC(1, 0x04, "RxPause"), ++ MIB_DESC(1, 0x08, "RxMulti"), ++ MIB_DESC(1, 0x0c, "RxFcsErr"), ++ MIB_DESC(1, 0x10, "RxAlignErr"), ++ MIB_DESC(1, 0x14, "RxRunt"), ++ MIB_DESC(1, 0x18, "RxFragment"), ++ MIB_DESC(1, 0x1c, "Rx64Byte"), ++ MIB_DESC(1, 0x20, "Rx128Byte"), ++ MIB_DESC(1, 0x24, "Rx256Byte"), ++ MIB_DESC(1, 0x28, "Rx512Byte"), ++ MIB_DESC(1, 0x2c, "Rx1024Byte"), ++ MIB_DESC(1, 0x30, "Rx1518Byte"), ++ MIB_DESC(1, 0x34, "RxMaxByte"), ++ MIB_DESC(1, 0x38, "RxTooLong"), ++ MIB_DESC(2, 0x3c, "RxGoodByte"), ++ MIB_DESC(2, 0x44, "RxBadByte"), ++ MIB_DESC(1, 0x4c, "RxOverFlow"), ++ MIB_DESC(1, 0x50, "Filtered"), ++ MIB_DESC(1, 0x54, "TxBroad"), ++ MIB_DESC(1, 0x58, "TxPause"), ++ MIB_DESC(1, 0x5c, "TxMulti"), ++ MIB_DESC(1, 0x60, "TxUnderRun"), ++ MIB_DESC(1, 0x64, "Tx64Byte"), ++ MIB_DESC(1, 0x68, "Tx128Byte"), ++ MIB_DESC(1, 0x6c, "Tx256Byte"), ++ MIB_DESC(1, 0x70, "Tx512Byte"), ++ MIB_DESC(1, 0x74, "Tx1024Byte"), ++ MIB_DESC(1, 0x78, "Tx1518Byte"), ++ MIB_DESC(1, 0x7c, "TxMaxByte"), ++ MIB_DESC(1, 0x80, "TxOverSize"), ++ MIB_DESC(2, 0x84, "TxByte"), ++ MIB_DESC(1, 0x8c, "TxCollision"), ++ MIB_DESC(1, 0x90, "TxAbortCol"), ++ MIB_DESC(1, 0x94, "TxMultiCol"), ++ MIB_DESC(1, 0x98, "TxSingleCol"), ++ 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"), ++}; +--- a/drivers/net/dsa/qca/qca8k.h ++++ b/drivers/net/dsa/qca/qca8k.h +@@ -414,4 +414,7 @@ struct qca8k_fdb { + u8 mac[6]; + }; + ++/* Common setup function */ ++extern const struct qca8k_mib_desc ar8327_mib[]; ++ + #endif /* __QCA8K_H */ diff --git a/target/linux/generic/backport-6.1/771-v6.0-04-net-dsa-qca8k-move-qca8k-read-write-rmw-and-reg-tabl.patch b/target/linux/generic/backport-6.1/771-v6.0-04-net-dsa-qca8k-move-qca8k-read-write-rmw-and-reg-tabl.patch new file mode 100644 index 0000000000..012ab85474 --- /dev/null +++ b/target/linux/generic/backport-6.1/771-v6.0-04-net-dsa-qca8k-move-qca8k-read-write-rmw-and-reg-tabl.patch @@ -0,0 +1,135 @@ +From d5f901eab2e9dfed1095995dfc98f231f4fd2971 Mon Sep 17 00:00:00 2001 +From: Christian Marangi +Date: Wed, 27 Jul 2022 13:35:13 +0200 +Subject: [PATCH 04/14] net: dsa: qca8k: move qca8k read/write/rmw and reg + table to common code + +The same reg table and read/write/rmw function are used by drivers +based on qca8k family switch. +Move them to common code to make it accessible also by other drivers. + +Signed-off-by: Christian Marangi +Reviewed-by: Vladimir Oltean +Signed-off-by: Jakub Kicinski +--- + drivers/net/dsa/qca/qca8k-8xxx.c | 42 ------------------------------ + drivers/net/dsa/qca/qca8k-common.c | 38 +++++++++++++++++++++++++++ + drivers/net/dsa/qca/qca8k.h | 6 +++++ + 3 files changed, 44 insertions(+), 42 deletions(-) + +--- a/drivers/net/dsa/qca/qca8k-8xxx.c ++++ b/drivers/net/dsa/qca/qca8k-8xxx.c +@@ -133,24 +133,6 @@ qca8k_set_page(struct qca8k_priv *priv, + return 0; + } + +-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 void qca8k_rw_reg_ack_handler(struct dsa_switch *ds, struct sk_buff *skb) + { + struct qca8k_mgmt_eth_data *mgmt_eth_data; +@@ -483,30 +465,6 @@ exit: + return ret; + } + +-static const struct regmap_range qca8k_readable_ranges[] = { +- regmap_reg_range(0x0000, 0x00e4), /* Global control */ +- regmap_reg_range(0x0100, 0x0168), /* EEE control */ +- regmap_reg_range(0x0200, 0x0270), /* Parser control */ +- regmap_reg_range(0x0400, 0x0454), /* ACL */ +- regmap_reg_range(0x0600, 0x0718), /* Lookup */ +- regmap_reg_range(0x0800, 0x0b70), /* QM */ +- regmap_reg_range(0x0c00, 0x0c80), /* PKT */ +- regmap_reg_range(0x0e00, 0x0e98), /* L3 */ +- regmap_reg_range(0x1000, 0x10ac), /* MIB - Port0 */ +- regmap_reg_range(0x1100, 0x11ac), /* MIB - Port1 */ +- regmap_reg_range(0x1200, 0x12ac), /* MIB - Port2 */ +- regmap_reg_range(0x1300, 0x13ac), /* MIB - Port3 */ +- regmap_reg_range(0x1400, 0x14ac), /* MIB - Port4 */ +- regmap_reg_range(0x1500, 0x15ac), /* MIB - Port5 */ +- regmap_reg_range(0x1600, 0x16ac), /* MIB - Port6 */ +- +-}; +- +-static const struct regmap_access_table qca8k_readable_table = { +- .yes_ranges = qca8k_readable_ranges, +- .n_yes_ranges = ARRAY_SIZE(qca8k_readable_ranges), +-}; +- + static struct regmap_config qca8k_regmap_config = { + .reg_bits = 16, + .val_bits = 32, +--- a/drivers/net/dsa/qca/qca8k-common.c ++++ b/drivers/net/dsa/qca/qca8k-common.c +@@ -61,3 +61,41 @@ const struct qca8k_mib_desc ar8327_mib[] + MIB_DESC(1, 0xa8, "RXUnicast"), + MIB_DESC(1, 0xac, "TXUnicast"), + }; ++ ++int qca8k_read(struct qca8k_priv *priv, u32 reg, u32 *val) ++{ ++ return regmap_read(priv->regmap, reg, val); ++} ++ ++int qca8k_write(struct qca8k_priv *priv, u32 reg, u32 val) ++{ ++ return regmap_write(priv->regmap, reg, val); ++} ++ ++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 const struct regmap_range qca8k_readable_ranges[] = { ++ regmap_reg_range(0x0000, 0x00e4), /* Global control */ ++ regmap_reg_range(0x0100, 0x0168), /* EEE control */ ++ regmap_reg_range(0x0200, 0x0270), /* Parser control */ ++ regmap_reg_range(0x0400, 0x0454), /* ACL */ ++ regmap_reg_range(0x0600, 0x0718), /* Lookup */ ++ regmap_reg_range(0x0800, 0x0b70), /* QM */ ++ regmap_reg_range(0x0c00, 0x0c80), /* PKT */ ++ regmap_reg_range(0x0e00, 0x0e98), /* L3 */ ++ regmap_reg_range(0x1000, 0x10ac), /* MIB - Port0 */ ++ regmap_reg_range(0x1100, 0x11ac), /* MIB - Port1 */ ++ regmap_reg_range(0x1200, 0x12ac), /* MIB - Port2 */ ++ regmap_reg_range(0x1300, 0x13ac), /* MIB - Port3 */ ++ regmap_reg_range(0x1400, 0x14ac), /* MIB - Port4 */ ++ regmap_reg_range(0x1500, 0x15ac), /* MIB - Port5 */ ++ regmap_reg_range(0x1600, 0x16ac), /* MIB - Port6 */ ++}; ++ ++const struct regmap_access_table qca8k_readable_table = { ++ .yes_ranges = qca8k_readable_ranges, ++ .n_yes_ranges = ARRAY_SIZE(qca8k_readable_ranges), ++}; +--- a/drivers/net/dsa/qca/qca8k.h ++++ b/drivers/net/dsa/qca/qca8k.h +@@ -416,5 +416,11 @@ struct qca8k_fdb { + + /* Common setup function */ + extern const struct qca8k_mib_desc ar8327_mib[]; ++extern const struct regmap_access_table qca8k_readable_table; ++ ++/* Common read/write/rmw function */ ++int qca8k_read(struct qca8k_priv *priv, u32 reg, u32 *val); ++int qca8k_write(struct qca8k_priv *priv, u32 reg, u32 val); ++int qca8k_rmw(struct qca8k_priv *priv, u32 reg, u32 mask, u32 write_val); + + #endif /* __QCA8K_H */ diff --git a/target/linux/generic/backport-6.1/771-v6.0-05-net-dsa-qca8k-move-qca8k-bulk-read-write-helper-to-c.patch b/target/linux/generic/backport-6.1/771-v6.0-05-net-dsa-qca8k-move-qca8k-bulk-read-write-helper-to-c.patch new file mode 100644 index 0000000000..0ed7ed41fb --- /dev/null +++ b/target/linux/generic/backport-6.1/771-v6.0-05-net-dsa-qca8k-move-qca8k-bulk-read-write-helper-to-c.patch @@ -0,0 +1,145 @@ +From 910746444313dc463396cd63024cdf54ef04ef39 Mon Sep 17 00:00:00 2001 +From: Christian Marangi +Date: Wed, 27 Jul 2022 13:35:14 +0200 +Subject: [PATCH 05/14] net: dsa: qca8k: move qca8k bulk read/write helper to + common code + +The same ATU function are used by drivers based on qca8k family switch. +Move the bulk read/write helper to common code to declare these shared +ATU functions in common code. +These helper will be dropped when regmap correctly support bulk +read/write. + +Signed-off-by: Christian Marangi +Reviewed-by: Vladimir Oltean +Signed-off-by: Jakub Kicinski +--- + drivers/net/dsa/qca/qca8k-8xxx.c | 39 ++---------------------------- + drivers/net/dsa/qca/qca8k-common.c | 39 ++++++++++++++++++++++++++++++ + drivers/net/dsa/qca/qca8k.h | 8 ++++++ + 3 files changed, 49 insertions(+), 37 deletions(-) + +--- a/drivers/net/dsa/qca/qca8k-8xxx.c ++++ b/drivers/net/dsa/qca/qca8k-8xxx.c +@@ -343,43 +343,6 @@ 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; +@@ -3096,6 +3059,8 @@ static SIMPLE_DEV_PM_OPS(qca8k_pm_ops, + + static const struct qca8k_info_ops qca8xxx_ops = { + .autocast_mib = qca8k_get_ethtool_stats_eth, ++ .read_eth = qca8k_read_eth, ++ .write_eth = qca8k_write_eth, + }; + + static const struct qca8k_match_data qca8327 = { +--- a/drivers/net/dsa/qca/qca8k-common.c ++++ b/drivers/net/dsa/qca/qca8k-common.c +@@ -99,3 +99,42 @@ const struct regmap_access_table qca8k_r + .yes_ranges = qca8k_readable_ranges, + .n_yes_ranges = ARRAY_SIZE(qca8k_readable_ranges), + }; ++ ++/* TODO: remove these extra ops when we can support regmap bulk read/write */ ++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 && priv->info->ops->read_eth && ++ !priv->info->ops->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; ++} ++ ++/* TODO: remove these extra ops when we can support regmap bulk read/write */ ++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 && priv->info->ops->write_eth && ++ !priv->info->ops->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; ++} +--- a/drivers/net/dsa/qca/qca8k.h ++++ b/drivers/net/dsa/qca/qca8k.h +@@ -324,8 +324,13 @@ enum qca8k_mid_cmd { + QCA8K_MIB_CAST = 3, + }; + ++struct qca8k_priv; ++ + struct qca8k_info_ops { + int (*autocast_mib)(struct dsa_switch *ds, int port, u64 *data); ++ /* TODO: remove these extra ops when we can support regmap bulk read/write */ ++ int (*read_eth)(struct qca8k_priv *priv, u32 reg, u32 *val, int len); ++ int (*write_eth)(struct qca8k_priv *priv, u32 reg, u32 *val, int len); + }; + + struct qca8k_match_data { +@@ -423,4 +428,7 @@ int qca8k_read(struct qca8k_priv *priv, + int qca8k_write(struct qca8k_priv *priv, u32 reg, u32 val); + int qca8k_rmw(struct qca8k_priv *priv, u32 reg, u32 mask, u32 write_val); + ++int qca8k_bulk_read(struct qca8k_priv *priv, u32 reg, u32 *val, int len); ++int qca8k_bulk_write(struct qca8k_priv *priv, u32 reg, u32 *val, int len); ++ + #endif /* __QCA8K_H */ diff --git a/target/linux/generic/backport-6.1/771-v6.0-06-net-dsa-qca8k-move-mib-init-function-to-common-code.patch b/target/linux/generic/backport-6.1/771-v6.0-06-net-dsa-qca8k-move-mib-init-function-to-common-code.patch new file mode 100644 index 0000000000..a39a55b89b --- /dev/null +++ b/target/linux/generic/backport-6.1/771-v6.0-06-net-dsa-qca8k-move-mib-init-function-to-common-code.patch @@ -0,0 +1,137 @@ +From fce1ec0c4e2d03d9c62ffc615a42bdba78eb4c14 Mon Sep 17 00:00:00 2001 +From: Christian Marangi +Date: Wed, 27 Jul 2022 13:35:15 +0200 +Subject: [PATCH 06/14] net: dsa: qca8k: move mib init function to common code + +The same mib function is used by drivers based on qca8k family switch. +Move it to common code to make it accessible also by other drivers. + +Signed-off-by: Christian Marangi +Reviewed-by: Vladimir Oltean +Signed-off-by: Jakub Kicinski +--- + drivers/net/dsa/qca/qca8k-8xxx.c | 37 ------------------------------ + drivers/net/dsa/qca/qca8k-common.c | 35 ++++++++++++++++++++++++++++ + drivers/net/dsa/qca/qca8k.h | 4 ++++ + 3 files changed, 39 insertions(+), 37 deletions(-) + +--- a/drivers/net/dsa/qca/qca8k-8xxx.c ++++ b/drivers/net/dsa/qca/qca8k-8xxx.c +@@ -442,15 +442,6 @@ static struct regmap_config qca8k_regmap + }; + + static int +-qca8k_busy_wait(struct qca8k_priv *priv, u32 reg, u32 mask) +-{ +- u32 val; +- +- return regmap_read_poll_timeout(priv->regmap, reg, val, !(val & mask), 0, +- QCA8K_BUSY_WAIT_TIMEOUT * USEC_PER_MSEC); +-} +- +-static int + qca8k_fdb_read(struct qca8k_priv *priv, struct qca8k_fdb *fdb) + { + u32 reg[3]; +@@ -777,34 +768,6 @@ out: + return ret; + } + +-static int +-qca8k_mib_init(struct qca8k_priv *priv) +-{ +- int ret; +- +- mutex_lock(&priv->reg_mutex); +- 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; +- +- ret = qca8k_busy_wait(priv, QCA8K_REG_MIB, QCA8K_MIB_BUSY); +- if (ret) +- goto exit; +- +- ret = regmap_set_bits(priv->regmap, QCA8K_REG_MIB, QCA8K_MIB_CPU_KEEP); +- if (ret) +- goto exit; +- +- ret = qca8k_write(priv, QCA8K_REG_MODULE_EN, QCA8K_MODULE_EN_MIB); +- +-exit: +- mutex_unlock(&priv->reg_mutex); +- return ret; +-} +- + static void + qca8k_port_set_status(struct qca8k_priv *priv, int port, int enable) + { +--- a/drivers/net/dsa/qca/qca8k-common.c ++++ b/drivers/net/dsa/qca/qca8k-common.c +@@ -7,6 +7,7 @@ + */ + + #include ++#include + #include + + #include "qca8k.h" +@@ -138,3 +139,38 @@ int qca8k_bulk_write(struct qca8k_priv * + + return 0; + } ++ ++int qca8k_busy_wait(struct qca8k_priv *priv, u32 reg, u32 mask) ++{ ++ u32 val; ++ ++ return regmap_read_poll_timeout(priv->regmap, reg, val, !(val & mask), 0, ++ QCA8K_BUSY_WAIT_TIMEOUT * USEC_PER_MSEC); ++} ++ ++int qca8k_mib_init(struct qca8k_priv *priv) ++{ ++ int ret; ++ ++ mutex_lock(&priv->reg_mutex); ++ 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; ++ ++ ret = qca8k_busy_wait(priv, QCA8K_REG_MIB, QCA8K_MIB_BUSY); ++ if (ret) ++ goto exit; ++ ++ ret = regmap_set_bits(priv->regmap, QCA8K_REG_MIB, QCA8K_MIB_CPU_KEEP); ++ if (ret) ++ goto exit; ++ ++ ret = qca8k_write(priv, QCA8K_REG_MODULE_EN, QCA8K_MODULE_EN_MIB); ++ ++exit: ++ mutex_unlock(&priv->reg_mutex); ++ return ret; ++} +--- a/drivers/net/dsa/qca/qca8k.h ++++ b/drivers/net/dsa/qca/qca8k.h +@@ -422,6 +422,7 @@ struct qca8k_fdb { + /* Common setup function */ + extern const struct qca8k_mib_desc ar8327_mib[]; + extern const struct regmap_access_table qca8k_readable_table; ++int qca8k_mib_init(struct qca8k_priv *priv); + + /* Common read/write/rmw function */ + int qca8k_read(struct qca8k_priv *priv, u32 reg, u32 *val); +@@ -431,4 +432,7 @@ int qca8k_rmw(struct qca8k_priv *priv, u + int qca8k_bulk_read(struct qca8k_priv *priv, u32 reg, u32 *val, int len); + int qca8k_bulk_write(struct qca8k_priv *priv, u32 reg, u32 *val, int len); + ++/* Common ops function */ ++int qca8k_busy_wait(struct qca8k_priv *priv, u32 reg, u32 mask); ++ + #endif /* __QCA8K_H */ diff --git a/target/linux/generic/backport-6.1/771-v6.0-07-net-dsa-qca8k-move-port-set-status-eee-ethtool-stats.patch b/target/linux/generic/backport-6.1/771-v6.0-07-net-dsa-qca8k-move-port-set-status-eee-ethtool-stats.patch new file mode 100644 index 0000000000..6fd1c66b0a --- /dev/null +++ b/target/linux/generic/backport-6.1/771-v6.0-07-net-dsa-qca8k-move-port-set-status-eee-ethtool-stats.patch @@ -0,0 +1,281 @@ +From 472fcea160f27a5d9b7526093d9d8d89ba0b6137 Mon Sep 17 00:00:00 2001 +From: Christian Marangi +Date: Wed, 27 Jul 2022 13:35:16 +0200 +Subject: [PATCH 07/14] net: dsa: qca8k: move port set status/eee/ethtool stats + function to common code + +The same logic to disable/enable port, set eee and get ethtool stats is +used by drivers based on qca8k family switch. +Move it to common code to make it accessible also by other drivers. +While at it also drop unnecessary qca8k_priv cast for void pointers. + +Signed-off-by: Christian Marangi +Reviewed-by: Vladimir Oltean +Signed-off-by: Jakub Kicinski +--- + drivers/net/dsa/qca/qca8k-8xxx.c | 105 ----------------------------- + drivers/net/dsa/qca/qca8k-common.c | 102 ++++++++++++++++++++++++++++ + drivers/net/dsa/qca/qca8k.h | 11 +++ + 3 files changed, 113 insertions(+), 105 deletions(-) + +--- a/drivers/net/dsa/qca/qca8k-8xxx.c ++++ b/drivers/net/dsa/qca/qca8k-8xxx.c +@@ -768,21 +768,6 @@ out: + return ret; + } + +-static void +-qca8k_port_set_status(struct qca8k_priv *priv, int port, int enable) +-{ +- u32 mask = QCA8K_PORT_STATUS_TXMAC | QCA8K_PORT_STATUS_RXMAC; +- +- /* Port 0 and 6 have no internal PHY */ +- if (port > 0 && port < 6) +- mask |= QCA8K_PORT_STATUS_LINK_AUTO; +- +- if (enable) +- regmap_set_bits(priv->regmap, QCA8K_REG_PORT_STATUS(port), mask); +- else +- 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) +@@ -1974,20 +1959,6 @@ qca8k_phylink_mac_link_up(struct dsa_swi + qca8k_write(priv, QCA8K_REG_PORT_STATUS(port), reg); + } + +-static void +-qca8k_get_strings(struct dsa_switch *ds, int port, u32 stringset, uint8_t *data) +-{ +- struct qca8k_priv *priv = ds->priv; +- int i; +- +- if (stringset != ETH_SS_STATS) +- return; +- +- for (i = 0; i < priv->info->mib_count; i++) +- strncpy(data + i * ETH_GSTRING_LEN, ar8327_mib[i].name, +- ETH_GSTRING_LEN); +-} +- + static void qca8k_mib_autocast_handler(struct dsa_switch *ds, struct sk_buff *skb) + { + struct qca8k_mib_eth_data *mib_eth_data; +@@ -2078,82 +2049,6 @@ exit: + } + + static void +-qca8k_get_ethtool_stats(struct dsa_switch *ds, int port, +- uint64_t *data) +-{ +- struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv; +- const struct qca8k_mib_desc *mib; +- u32 reg, i, val; +- u32 hi = 0; +- int ret; +- +- if (priv->mgmt_master && priv->info->ops->autocast_mib && +- priv->info->ops->autocast_mib(ds, port, data) > 0) +- return; +- +- for (i = 0; i < priv->info->mib_count; i++) { +- mib = &ar8327_mib[i]; +- reg = QCA8K_PORT_MIB_COUNTER(port) + mib->offset; +- +- ret = qca8k_read(priv, reg, &val); +- if (ret < 0) +- continue; +- +- if (mib->size == 2) { +- ret = qca8k_read(priv, reg + 4, &hi); +- if (ret < 0) +- continue; +- } +- +- data[i] = val; +- if (mib->size == 2) +- data[i] |= (u64)hi << 32; +- } +-} +- +-static int +-qca8k_get_sset_count(struct dsa_switch *ds, int port, int sset) +-{ +- struct qca8k_priv *priv = ds->priv; +- +- if (sset != ETH_SS_STATS) +- return 0; +- +- return priv->info->mib_count; +-} +- +-static int +-qca8k_set_mac_eee(struct dsa_switch *ds, int port, struct ethtool_eee *eee) +-{ +- struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv; +- u32 lpi_en = QCA8K_REG_EEE_CTRL_LPI_EN(port); +- u32 reg; +- int ret; +- +- mutex_lock(&priv->reg_mutex); +- ret = qca8k_read(priv, QCA8K_REG_EEE_CTRL, ®); +- if (ret < 0) +- goto exit; +- +- if (eee->eee_enabled) +- reg |= lpi_en; +- else +- reg &= ~lpi_en; +- ret = qca8k_write(priv, QCA8K_REG_EEE_CTRL, reg); +- +-exit: +- mutex_unlock(&priv->reg_mutex); +- return ret; +-} +- +-static int +-qca8k_get_mac_eee(struct dsa_switch *ds, int port, struct ethtool_eee *e) +-{ +- /* Nothing to do on the port's MAC */ +- return 0; +-} +- +-static void + qca8k_port_stp_state_set(struct dsa_switch *ds, int port, u8 state) + { + struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv; +--- a/drivers/net/dsa/qca/qca8k-common.c ++++ b/drivers/net/dsa/qca/qca8k-common.c +@@ -174,3 +174,105 @@ exit: + mutex_unlock(&priv->reg_mutex); + return ret; + } ++ ++void qca8k_port_set_status(struct qca8k_priv *priv, int port, int enable) ++{ ++ u32 mask = QCA8K_PORT_STATUS_TXMAC | QCA8K_PORT_STATUS_RXMAC; ++ ++ /* Port 0 and 6 have no internal PHY */ ++ if (port > 0 && port < 6) ++ mask |= QCA8K_PORT_STATUS_LINK_AUTO; ++ ++ if (enable) ++ regmap_set_bits(priv->regmap, QCA8K_REG_PORT_STATUS(port), mask); ++ else ++ regmap_clear_bits(priv->regmap, QCA8K_REG_PORT_STATUS(port), mask); ++} ++ ++void qca8k_get_strings(struct dsa_switch *ds, int port, u32 stringset, ++ uint8_t *data) ++{ ++ struct qca8k_priv *priv = ds->priv; ++ int i; ++ ++ if (stringset != ETH_SS_STATS) ++ return; ++ ++ for (i = 0; i < priv->info->mib_count; i++) ++ strncpy(data + i * ETH_GSTRING_LEN, ar8327_mib[i].name, ++ ETH_GSTRING_LEN); ++} ++ ++void qca8k_get_ethtool_stats(struct dsa_switch *ds, int port, ++ uint64_t *data) ++{ ++ struct qca8k_priv *priv = ds->priv; ++ const struct qca8k_mib_desc *mib; ++ u32 reg, i, val; ++ u32 hi = 0; ++ int ret; ++ ++ if (priv->mgmt_master && priv->info->ops->autocast_mib && ++ priv->info->ops->autocast_mib(ds, port, data) > 0) ++ return; ++ ++ for (i = 0; i < priv->info->mib_count; i++) { ++ mib = &ar8327_mib[i]; ++ reg = QCA8K_PORT_MIB_COUNTER(port) + mib->offset; ++ ++ ret = qca8k_read(priv, reg, &val); ++ if (ret < 0) ++ continue; ++ ++ if (mib->size == 2) { ++ ret = qca8k_read(priv, reg + 4, &hi); ++ if (ret < 0) ++ continue; ++ } ++ ++ data[i] = val; ++ if (mib->size == 2) ++ data[i] |= (u64)hi << 32; ++ } ++} ++ ++int qca8k_get_sset_count(struct dsa_switch *ds, int port, int sset) ++{ ++ struct qca8k_priv *priv = ds->priv; ++ ++ if (sset != ETH_SS_STATS) ++ return 0; ++ ++ return priv->info->mib_count; ++} ++ ++int qca8k_set_mac_eee(struct dsa_switch *ds, int port, ++ struct ethtool_eee *eee) ++{ ++ u32 lpi_en = QCA8K_REG_EEE_CTRL_LPI_EN(port); ++ struct qca8k_priv *priv = ds->priv; ++ u32 reg; ++ int ret; ++ ++ mutex_lock(&priv->reg_mutex); ++ ret = qca8k_read(priv, QCA8K_REG_EEE_CTRL, ®); ++ if (ret < 0) ++ goto exit; ++ ++ if (eee->eee_enabled) ++ reg |= lpi_en; ++ else ++ reg &= ~lpi_en; ++ ret = qca8k_write(priv, QCA8K_REG_EEE_CTRL, reg); ++ ++exit: ++ mutex_unlock(&priv->reg_mutex); ++ return ret; ++} ++ ++int qca8k_get_mac_eee(struct dsa_switch *ds, int port, ++ struct ethtool_eee *e) ++{ ++ /* Nothing to do on the port's MAC */ ++ return 0; ++} +--- a/drivers/net/dsa/qca/qca8k.h ++++ b/drivers/net/dsa/qca/qca8k.h +@@ -423,6 +423,7 @@ struct qca8k_fdb { + extern const struct qca8k_mib_desc ar8327_mib[]; + extern const struct regmap_access_table qca8k_readable_table; + int qca8k_mib_init(struct qca8k_priv *priv); ++void qca8k_port_set_status(struct qca8k_priv *priv, int port, int enable); + + /* Common read/write/rmw function */ + int qca8k_read(struct qca8k_priv *priv, u32 reg, u32 *val); +@@ -435,4 +436,14 @@ int qca8k_bulk_write(struct qca8k_priv * + /* Common ops function */ + int qca8k_busy_wait(struct qca8k_priv *priv, u32 reg, u32 mask); + ++/* Common ethtool stats function */ ++void qca8k_get_strings(struct dsa_switch *ds, int port, u32 stringset, uint8_t *data); ++void qca8k_get_ethtool_stats(struct dsa_switch *ds, int port, ++ uint64_t *data); ++int qca8k_get_sset_count(struct dsa_switch *ds, int port, int sset); ++ ++/* Common eee function */ ++int qca8k_set_mac_eee(struct dsa_switch *ds, int port, struct ethtool_eee *eee); ++int qca8k_get_mac_eee(struct dsa_switch *ds, int port, struct ethtool_eee *e); ++ + #endif /* __QCA8K_H */ diff --git a/target/linux/generic/backport-6.1/771-v6.0-08-net-dsa-qca8k-move-bridge-functions-to-common-code.patch b/target/linux/generic/backport-6.1/771-v6.0-08-net-dsa-qca8k-move-bridge-functions-to-common-code.patch new file mode 100644 index 0000000000..3ca682d72c --- /dev/null +++ b/target/linux/generic/backport-6.1/771-v6.0-08-net-dsa-qca8k-move-bridge-functions-to-common-code.patch @@ -0,0 +1,237 @@ +From fd3cae2f3ac190d06e48f43739237e02f9dc51ff Mon Sep 17 00:00:00 2001 +From: Christian Marangi +Date: Wed, 27 Jul 2022 13:35:17 +0200 +Subject: [PATCH 08/14] net: dsa: qca8k: move bridge functions to common code + +The same bridge functions are used by drivers based on qca8k family +switch. Move them to common code to make them accessible also by other +drivers. +While at it also drop unnecessary qca8k_priv cast for void pointers. + +Signed-off-by: Christian Marangi +Reviewed-by: Vladimir Oltean +Signed-off-by: Jakub Kicinski +--- + drivers/net/dsa/qca/qca8k-8xxx.c | 93 ------------------------------ + drivers/net/dsa/qca/qca8k-common.c | 93 ++++++++++++++++++++++++++++++ + drivers/net/dsa/qca/qca8k.h | 9 +++ + 3 files changed, 102 insertions(+), 93 deletions(-) + +--- a/drivers/net/dsa/qca/qca8k-8xxx.c ++++ b/drivers/net/dsa/qca/qca8k-8xxx.c +@@ -2049,97 +2049,6 @@ exit: + } + + static void +-qca8k_port_stp_state_set(struct dsa_switch *ds, int port, u8 state) +-{ +- struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv; +- u32 stp_state; +- +- switch (state) { +- case BR_STATE_DISABLED: +- stp_state = QCA8K_PORT_LOOKUP_STATE_DISABLED; +- break; +- case BR_STATE_BLOCKING: +- stp_state = QCA8K_PORT_LOOKUP_STATE_BLOCKING; +- break; +- case BR_STATE_LISTENING: +- stp_state = QCA8K_PORT_LOOKUP_STATE_LISTENING; +- break; +- case BR_STATE_LEARNING: +- stp_state = QCA8K_PORT_LOOKUP_STATE_LEARNING; +- break; +- case BR_STATE_FORWARDING: +- default: +- stp_state = QCA8K_PORT_LOOKUP_STATE_FORWARD; +- break; +- } +- +- qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(port), +- QCA8K_PORT_LOOKUP_STATE_MASK, stp_state); +-} +- +-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, cpu_port; +- int i, ret; +- +- cpu_port = dsa_to_port(ds, port)->cpu_dp->index; +- port_mask = BIT(cpu_port); +- +- 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 +- * in the bridge +- */ +- ret = regmap_set_bits(priv->regmap, +- QCA8K_PORT_LOOKUP_CTRL(i), +- BIT(port)); +- if (ret) +- return ret; +- if (i != port) +- port_mask |= BIT(i); +- } +- +- /* Add all other ports to this ports portvlan mask */ +- ret = qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(port), +- QCA8K_PORT_LOOKUP_MEMBER, port_mask); +- +- return ret; +-} +- +-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 cpu_port, i; +- +- cpu_port = dsa_to_port(ds, port)->cpu_dp->index; +- +- 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 +- * in the bridge +- */ +- 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 +- * this port +- */ +- qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(port), +- 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; +--- a/drivers/net/dsa/qca/qca8k-common.c ++++ b/drivers/net/dsa/qca/qca8k-common.c +@@ -9,6 +9,7 @@ + #include + #include + #include ++#include + + #include "qca8k.h" + +@@ -276,3 +277,93 @@ int qca8k_get_mac_eee(struct dsa_switch + /* Nothing to do on the port's MAC */ + return 0; + } ++ ++void qca8k_port_stp_state_set(struct dsa_switch *ds, int port, u8 state) ++{ ++ struct qca8k_priv *priv = ds->priv; ++ u32 stp_state; ++ ++ switch (state) { ++ case BR_STATE_DISABLED: ++ stp_state = QCA8K_PORT_LOOKUP_STATE_DISABLED; ++ break; ++ case BR_STATE_BLOCKING: ++ stp_state = QCA8K_PORT_LOOKUP_STATE_BLOCKING; ++ break; ++ case BR_STATE_LISTENING: ++ stp_state = QCA8K_PORT_LOOKUP_STATE_LISTENING; ++ break; ++ case BR_STATE_LEARNING: ++ stp_state = QCA8K_PORT_LOOKUP_STATE_LEARNING; ++ break; ++ case BR_STATE_FORWARDING: ++ default: ++ stp_state = QCA8K_PORT_LOOKUP_STATE_FORWARD; ++ break; ++ } ++ ++ qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(port), ++ QCA8K_PORT_LOOKUP_STATE_MASK, stp_state); ++} ++ ++int qca8k_port_bridge_join(struct dsa_switch *ds, int port, ++ struct net_device *br) ++{ ++ struct qca8k_priv *priv = ds->priv; ++ 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 = 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 ++ * in the bridge ++ */ ++ ret = regmap_set_bits(priv->regmap, ++ QCA8K_PORT_LOOKUP_CTRL(i), ++ BIT(port)); ++ if (ret) ++ return ret; ++ if (i != port) ++ port_mask |= BIT(i); ++ } ++ ++ /* Add all other ports to this ports portvlan mask */ ++ ret = qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(port), ++ QCA8K_PORT_LOOKUP_MEMBER, port_mask); ++ ++ return ret; ++} ++ ++void qca8k_port_bridge_leave(struct dsa_switch *ds, int port, ++ struct net_device *br) ++{ ++ struct qca8k_priv *priv = ds->priv; ++ int cpu_port, i; ++ ++ cpu_port = dsa_to_port(ds, port)->cpu_dp->index; ++ ++ 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 ++ * in the bridge ++ */ ++ 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 ++ * this port ++ */ ++ qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(port), ++ QCA8K_PORT_LOOKUP_MEMBER, BIT(cpu_port)); ++} +--- a/drivers/net/dsa/qca/qca8k.h ++++ b/drivers/net/dsa/qca/qca8k.h +@@ -446,4 +446,11 @@ int qca8k_get_sset_count(struct dsa_swit + int qca8k_set_mac_eee(struct dsa_switch *ds, int port, struct ethtool_eee *eee); + int qca8k_get_mac_eee(struct dsa_switch *ds, int port, struct ethtool_eee *e); + ++/* Common bridge function */ ++void qca8k_port_stp_state_set(struct dsa_switch *ds, int port, u8 state); ++int qca8k_port_bridge_join(struct dsa_switch *ds, int port, ++ struct net_device *br); ++void qca8k_port_bridge_leave(struct dsa_switch *ds, int port, ++ struct net_device *br); ++ + #endif /* __QCA8K_H */ diff --git a/target/linux/generic/backport-6.1/771-v6.0-09-net-dsa-qca8k-move-set-age-MTU-port-enable-disable-f.patch b/target/linux/generic/backport-6.1/771-v6.0-09-net-dsa-qca8k-move-set-age-MTU-port-enable-disable-f.patch new file mode 100644 index 0000000000..e3414408d6 --- /dev/null +++ b/target/linux/generic/backport-6.1/771-v6.0-09-net-dsa-qca8k-move-set-age-MTU-port-enable-disable-f.patch @@ -0,0 +1,227 @@ +From b3a302b171f73425b41de8d3357fae3fa7057322 Mon Sep 17 00:00:00 2001 +From: Christian Marangi +Date: Wed, 27 Jul 2022 13:35:18 +0200 +Subject: [PATCH 09/14] net: dsa: qca8k: move set age/MTU/port enable/disable + functions to common code + +The same set age, MTU and port enable/disable function are used by +driver based on qca8k family switch. +Move them to common code to make them accessible also by other drivers. +While at it also drop unnecessary qca8k_priv cast for void pointers. + +Signed-off-by: Christian Marangi +Reviewed-by: Vladimir Oltean +Signed-off-by: Jakub Kicinski +--- + drivers/net/dsa/qca/qca8k-8xxx.c | 88 ------------------------------ + drivers/net/dsa/qca/qca8k-common.c | 85 +++++++++++++++++++++++++++++ + drivers/net/dsa/qca/qca8k.h | 12 ++++ + 3 files changed, 97 insertions(+), 88 deletions(-) + +--- a/drivers/net/dsa/qca/qca8k-8xxx.c ++++ b/drivers/net/dsa/qca/qca8k-8xxx.c +@@ -2059,94 +2059,6 @@ 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) +-{ +- struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv; +- +- qca8k_port_set_status(priv, port, 1); +- priv->port_enabled_map |= BIT(port); +- +- if (dsa_is_user_port(ds, port)) +- phy_support_asym_pause(phy); +- +- return 0; +-} +- +-static void +-qca8k_port_disable(struct dsa_switch *ds, int port) +-{ +- struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv; +- +- qca8k_port_set_status(priv, port, 0); +- priv->port_enabled_map &= ~BIT(port); +-} +- +-static int +-qca8k_port_change_mtu(struct dsa_switch *ds, int port, int new_mtu) +-{ +- struct qca8k_priv *priv = ds->priv; +- int ret; +- +- /* We have only have a general MTU setting. +- * DSA always set the CPU port's MTU to the largest MTU of the slave +- * ports. +- * Setting MTU just for the CPU port is sufficient to correctly set a +- * value for every port. +- */ +- if (!dsa_is_cpu_port(ds, port)) +- return 0; +- +- /* To change the MAX_FRAME_SIZE the cpu ports must be off or +- * the switch panics. +- * Turn off both cpu ports before applying the new value to prevent +- * this. +- */ +- if (priv->port_enabled_map & BIT(0)) +- qca8k_port_set_status(priv, 0, 0); +- +- if (priv->port_enabled_map & BIT(6)) +- qca8k_port_set_status(priv, 6, 0); +- +- /* Include L2 header / FCS length */ +- ret = qca8k_write(priv, QCA8K_MAX_FRAME_SIZE, new_mtu + ETH_HLEN + ETH_FCS_LEN); +- +- if (priv->port_enabled_map & BIT(0)) +- qca8k_port_set_status(priv, 0, 1); +- +- if (priv->port_enabled_map & BIT(6)) +- qca8k_port_set_status(priv, 6, 1); +- +- return ret; +-} +- +-static int +-qca8k_port_max_mtu(struct dsa_switch *ds, int port) +-{ +- return QCA8K_MAX_MTU; +-} +- +-static int + qca8k_port_fdb_insert(struct qca8k_priv *priv, const u8 *addr, + u16 port_mask, u16 vid) + { +--- a/drivers/net/dsa/qca/qca8k-common.c ++++ b/drivers/net/dsa/qca/qca8k-common.c +@@ -367,3 +367,88 @@ void qca8k_port_bridge_leave(struct dsa_ + qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(port), + QCA8K_PORT_LOOKUP_MEMBER, BIT(cpu_port)); + } ++ ++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)); ++} ++ ++int qca8k_port_enable(struct dsa_switch *ds, int port, ++ struct phy_device *phy) ++{ ++ struct qca8k_priv *priv = ds->priv; ++ ++ qca8k_port_set_status(priv, port, 1); ++ priv->port_enabled_map |= BIT(port); ++ ++ if (dsa_is_user_port(ds, port)) ++ phy_support_asym_pause(phy); ++ ++ return 0; ++} ++ ++void qca8k_port_disable(struct dsa_switch *ds, int port) ++{ ++ struct qca8k_priv *priv = ds->priv; ++ ++ qca8k_port_set_status(priv, port, 0); ++ priv->port_enabled_map &= ~BIT(port); ++} ++ ++int qca8k_port_change_mtu(struct dsa_switch *ds, int port, int new_mtu) ++{ ++ struct qca8k_priv *priv = ds->priv; ++ int ret; ++ ++ /* We have only have a general MTU setting. ++ * DSA always set the CPU port's MTU to the largest MTU of the slave ++ * ports. ++ * Setting MTU just for the CPU port is sufficient to correctly set a ++ * value for every port. ++ */ ++ if (!dsa_is_cpu_port(ds, port)) ++ return 0; ++ ++ /* To change the MAX_FRAME_SIZE the cpu ports must be off or ++ * the switch panics. ++ * Turn off both cpu ports before applying the new value to prevent ++ * this. ++ */ ++ if (priv->port_enabled_map & BIT(0)) ++ qca8k_port_set_status(priv, 0, 0); ++ ++ if (priv->port_enabled_map & BIT(6)) ++ qca8k_port_set_status(priv, 6, 0); ++ ++ /* Include L2 header / FCS length */ ++ ret = qca8k_write(priv, QCA8K_MAX_FRAME_SIZE, new_mtu + ++ ETH_HLEN + ETH_FCS_LEN); ++ ++ if (priv->port_enabled_map & BIT(0)) ++ qca8k_port_set_status(priv, 0, 1); ++ ++ if (priv->port_enabled_map & BIT(6)) ++ qca8k_port_set_status(priv, 6, 1); ++ ++ return ret; ++} ++ ++int qca8k_port_max_mtu(struct dsa_switch *ds, int port) ++{ ++ return QCA8K_MAX_MTU; ++} +--- a/drivers/net/dsa/qca/qca8k.h ++++ b/drivers/net/dsa/qca/qca8k.h +@@ -453,4 +453,16 @@ int qca8k_port_bridge_join(struct dsa_sw + void qca8k_port_bridge_leave(struct dsa_switch *ds, int port, + struct net_device *br); + ++/* Common port enable/disable function */ ++int qca8k_port_enable(struct dsa_switch *ds, int port, ++ struct phy_device *phy); ++void qca8k_port_disable(struct dsa_switch *ds, int port); ++ ++/* Common MTU function */ ++int qca8k_port_change_mtu(struct dsa_switch *ds, int port, int new_mtu); ++int qca8k_port_max_mtu(struct dsa_switch *ds, int port); ++ ++/* Common fast age function */ ++int qca8k_set_ageing_time(struct dsa_switch *ds, unsigned int msecs); ++ + #endif /* __QCA8K_H */ diff --git a/target/linux/generic/backport-6.1/771-v6.0-10-net-dsa-qca8k-move-port-FDB-MDB-function-to-common-c.patch b/target/linux/generic/backport-6.1/771-v6.0-10-net-dsa-qca8k-move-port-FDB-MDB-function-to-common-c.patch new file mode 100644 index 0000000000..96468ae74e --- /dev/null +++ b/target/linux/generic/backport-6.1/771-v6.0-10-net-dsa-qca8k-move-port-FDB-MDB-function-to-common-c.patch @@ -0,0 +1,704 @@ +From 2e5bd96eea86a246b4de3bf756f7a11b43e6187d Mon Sep 17 00:00:00 2001 +From: Christian Marangi +Date: Wed, 27 Jul 2022 13:35:19 +0200 +Subject: [PATCH 10/14] net: dsa: qca8k: move port FDB/MDB function to common + code + +The same port FDB/MDB function are used by drivers based on qca8k family +switch. Move them to common code to make them accessible also by other +drivers. +Also drop bulk read/write functions and make them static + +Signed-off-by: Christian Marangi +Reviewed-by: Vladimir Oltean +Signed-off-by: Jakub Kicinski +--- + drivers/net/dsa/qca/qca8k-8xxx.c | 306 ----------------------------- + drivers/net/dsa/qca/qca8k-common.c | 297 +++++++++++++++++++++++++++- + drivers/net/dsa/qca/qca8k.h | 25 ++- + 3 files changed, 317 insertions(+), 311 deletions(-) + +--- a/drivers/net/dsa/qca/qca8k-8xxx.c ++++ b/drivers/net/dsa/qca/qca8k-8xxx.c +@@ -442,217 +442,6 @@ static struct regmap_config qca8k_regmap + }; + + static int +-qca8k_fdb_read(struct qca8k_priv *priv, struct qca8k_fdb *fdb) +-{ +- u32 reg[3]; +- int ret; +- +- /* load the ARL table into an array */ +- 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]); +- /* aging - 67:64 */ +- fdb->aging = FIELD_GET(QCA8K_ATU_STATUS_MASK, reg[2]); +- /* portmask - 54:48 */ +- fdb->port_mask = FIELD_GET(QCA8K_ATU_PORT_MASK, reg[1]); +- /* mac - 47:0 */ +- 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; +-} +- +-static void +-qca8k_fdb_write(struct qca8k_priv *priv, u16 vid, u8 port_mask, const u8 *mac, +- u8 aging) +-{ +- u32 reg[3] = { 0 }; +- +- /* vid - 83:72 */ +- reg[2] = FIELD_PREP(QCA8K_ATU_VID_MASK, vid); +- /* aging - 67:64 */ +- reg[2] |= FIELD_PREP(QCA8K_ATU_STATUS_MASK, aging); +- /* portmask - 54:48 */ +- reg[1] = FIELD_PREP(QCA8K_ATU_PORT_MASK, port_mask); +- /* mac - 47:0 */ +- 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 */ +- qca8k_bulk_write(priv, QCA8K_REG_ATU_DATA0, reg, sizeof(reg)); +-} +- +-static int +-qca8k_fdb_access(struct qca8k_priv *priv, enum qca8k_fdb_cmd cmd, int port) +-{ +- u32 reg; +- int ret; +- +- /* Set the command and FDB index */ +- reg = QCA8K_ATU_FUNC_BUSY; +- reg |= cmd; +- if (port >= 0) { +- reg |= QCA8K_ATU_FUNC_PORT_EN; +- reg |= FIELD_PREP(QCA8K_ATU_FUNC_PORT_MASK, port); +- } +- +- /* Write the function register triggering the table access */ +- ret = qca8k_write(priv, QCA8K_REG_ATU_FUNC, reg); +- if (ret) +- return ret; +- +- /* wait for completion */ +- ret = qca8k_busy_wait(priv, QCA8K_REG_ATU_FUNC, QCA8K_ATU_FUNC_BUSY); +- if (ret) +- return ret; +- +- /* Check for table full violation when adding an entry */ +- if (cmd == QCA8K_FDB_LOAD) { +- ret = qca8k_read(priv, QCA8K_REG_ATU_FUNC, ®); +- if (ret < 0) +- return ret; +- if (reg & QCA8K_ATU_FUNC_FULL) +- return -1; +- } +- +- return 0; +-} +- +-static int +-qca8k_fdb_next(struct qca8k_priv *priv, struct qca8k_fdb *fdb, int port) +-{ +- int ret; +- +- qca8k_fdb_write(priv, fdb->vid, fdb->port_mask, fdb->mac, fdb->aging); +- ret = qca8k_fdb_access(priv, QCA8K_FDB_NEXT, port); +- if (ret < 0) +- return ret; +- +- return qca8k_fdb_read(priv, fdb); +-} +- +-static int +-qca8k_fdb_add(struct qca8k_priv *priv, const u8 *mac, u16 port_mask, +- u16 vid, u8 aging) +-{ +- int ret; +- +- mutex_lock(&priv->reg_mutex); +- qca8k_fdb_write(priv, vid, port_mask, mac, aging); +- ret = qca8k_fdb_access(priv, QCA8K_FDB_LOAD, -1); +- mutex_unlock(&priv->reg_mutex); +- +- return ret; +-} +- +-static int +-qca8k_fdb_del(struct qca8k_priv *priv, const u8 *mac, u16 port_mask, u16 vid) +-{ +- int ret; +- +- mutex_lock(&priv->reg_mutex); +- qca8k_fdb_write(priv, vid, port_mask, mac, 0); +- ret = qca8k_fdb_access(priv, QCA8K_FDB_PURGE, -1); +- mutex_unlock(&priv->reg_mutex); +- +- return ret; +-} +- +-static void +-qca8k_fdb_flush(struct qca8k_priv *priv) +-{ +- mutex_lock(&priv->reg_mutex); +- qca8k_fdb_access(priv, QCA8K_FDB_FLUSH, -1); +- mutex_unlock(&priv->reg_mutex); +-} +- +-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; +@@ -2048,97 +1837,6 @@ exit: + return ret; + } + +-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_fdb_insert(struct qca8k_priv *priv, const u8 *addr, +- u16 port_mask, u16 vid) +-{ +- /* Set the vid to the port vlan id if no vid is set */ +- if (!vid) +- vid = QCA8K_PORT_VID_DEF; +- +- return qca8k_fdb_add(priv, addr, port_mask, vid, +- QCA8K_ATU_STATUS_STATIC); +-} +- +-static int +-qca8k_port_fdb_add(struct dsa_switch *ds, int port, +- const unsigned char *addr, u16 vid) +-{ +- struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv; +- u16 port_mask = BIT(port); +- +- return qca8k_port_fdb_insert(priv, addr, port_mask, vid); +-} +- +-static int +-qca8k_port_fdb_del(struct dsa_switch *ds, int port, +- const unsigned char *addr, u16 vid) +-{ +- struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv; +- u16 port_mask = BIT(port); +- +- if (!vid) +- vid = QCA8K_PORT_VID_DEF; +- +- return qca8k_fdb_del(priv, addr, port_mask, vid); +-} +- +-static int +-qca8k_port_fdb_dump(struct dsa_switch *ds, int port, +- dsa_fdb_dump_cb_t *cb, void *data) +-{ +- struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv; +- struct qca8k_fdb _fdb = { 0 }; +- int cnt = QCA8K_NUM_FDB_RECORDS; +- bool is_static; +- int ret = 0; +- +- mutex_lock(&priv->reg_mutex); +- while (cnt-- && !qca8k_fdb_next(priv, &_fdb, port)) { +- if (!_fdb.aging) +- break; +- is_static = (_fdb.aging == QCA8K_ATU_STATUS_STATIC); +- ret = cb(_fdb.mac, _fdb.vid, is_static, data); +- if (ret) +- break; +- } +- mutex_unlock(&priv->reg_mutex); +- +- return 0; +-} +- +-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_mirror_add(struct dsa_switch *ds, int port, + struct dsa_mall_mirror_tc_entry *mirror, +--- a/drivers/net/dsa/qca/qca8k-common.c ++++ b/drivers/net/dsa/qca/qca8k-common.c +@@ -103,7 +103,7 @@ const struct regmap_access_table qca8k_r + }; + + /* TODO: remove these extra ops when we can support regmap bulk read/write */ +-int qca8k_bulk_read(struct qca8k_priv *priv, u32 reg, u32 *val, int len) ++static int qca8k_bulk_read(struct qca8k_priv *priv, u32 reg, u32 *val, int len) + { + int i, count = len / sizeof(u32), ret; + +@@ -121,7 +121,7 @@ int qca8k_bulk_read(struct qca8k_priv *p + } + + /* TODO: remove these extra ops when we can support regmap bulk read/write */ +-int qca8k_bulk_write(struct qca8k_priv *priv, u32 reg, u32 *val, int len) ++static int qca8k_bulk_write(struct qca8k_priv *priv, u32 reg, u32 *val, int len) + { + int i, count = len / sizeof(u32), ret; + u32 tmp; +@@ -149,6 +149,211 @@ int qca8k_busy_wait(struct qca8k_priv *p + QCA8K_BUSY_WAIT_TIMEOUT * USEC_PER_MSEC); + } + ++static int qca8k_fdb_read(struct qca8k_priv *priv, struct qca8k_fdb *fdb) ++{ ++ u32 reg[3]; ++ int ret; ++ ++ /* load the ARL table into an array */ ++ 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]); ++ /* aging - 67:64 */ ++ fdb->aging = FIELD_GET(QCA8K_ATU_STATUS_MASK, reg[2]); ++ /* portmask - 54:48 */ ++ fdb->port_mask = FIELD_GET(QCA8K_ATU_PORT_MASK, reg[1]); ++ /* mac - 47:0 */ ++ 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; ++} ++ ++static void qca8k_fdb_write(struct qca8k_priv *priv, u16 vid, u8 port_mask, ++ const u8 *mac, u8 aging) ++{ ++ u32 reg[3] = { 0 }; ++ ++ /* vid - 83:72 */ ++ reg[2] = FIELD_PREP(QCA8K_ATU_VID_MASK, vid); ++ /* aging - 67:64 */ ++ reg[2] |= FIELD_PREP(QCA8K_ATU_STATUS_MASK, aging); ++ /* portmask - 54:48 */ ++ reg[1] = FIELD_PREP(QCA8K_ATU_PORT_MASK, port_mask); ++ /* mac - 47:0 */ ++ 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 */ ++ qca8k_bulk_write(priv, QCA8K_REG_ATU_DATA0, reg, sizeof(reg)); ++} ++ ++static int qca8k_fdb_access(struct qca8k_priv *priv, enum qca8k_fdb_cmd cmd, ++ int port) ++{ ++ u32 reg; ++ int ret; ++ ++ /* Set the command and FDB index */ ++ reg = QCA8K_ATU_FUNC_BUSY; ++ reg |= cmd; ++ if (port >= 0) { ++ reg |= QCA8K_ATU_FUNC_PORT_EN; ++ reg |= FIELD_PREP(QCA8K_ATU_FUNC_PORT_MASK, port); ++ } ++ ++ /* Write the function register triggering the table access */ ++ ret = qca8k_write(priv, QCA8K_REG_ATU_FUNC, reg); ++ if (ret) ++ return ret; ++ ++ /* wait for completion */ ++ ret = qca8k_busy_wait(priv, QCA8K_REG_ATU_FUNC, QCA8K_ATU_FUNC_BUSY); ++ if (ret) ++ return ret; ++ ++ /* Check for table full violation when adding an entry */ ++ if (cmd == QCA8K_FDB_LOAD) { ++ ret = qca8k_read(priv, QCA8K_REG_ATU_FUNC, ®); ++ if (ret < 0) ++ return ret; ++ if (reg & QCA8K_ATU_FUNC_FULL) ++ return -1; ++ } ++ ++ return 0; ++} ++ ++static int qca8k_fdb_next(struct qca8k_priv *priv, struct qca8k_fdb *fdb, ++ int port) ++{ ++ int ret; ++ ++ qca8k_fdb_write(priv, fdb->vid, fdb->port_mask, fdb->mac, fdb->aging); ++ ret = qca8k_fdb_access(priv, QCA8K_FDB_NEXT, port); ++ if (ret < 0) ++ return ret; ++ ++ return qca8k_fdb_read(priv, fdb); ++} ++ ++static int qca8k_fdb_add(struct qca8k_priv *priv, const u8 *mac, ++ u16 port_mask, u16 vid, u8 aging) ++{ ++ int ret; ++ ++ mutex_lock(&priv->reg_mutex); ++ qca8k_fdb_write(priv, vid, port_mask, mac, aging); ++ ret = qca8k_fdb_access(priv, QCA8K_FDB_LOAD, -1); ++ mutex_unlock(&priv->reg_mutex); ++ ++ return ret; ++} ++ ++static int qca8k_fdb_del(struct qca8k_priv *priv, const u8 *mac, ++ u16 port_mask, u16 vid) ++{ ++ int ret; ++ ++ mutex_lock(&priv->reg_mutex); ++ qca8k_fdb_write(priv, vid, port_mask, mac, 0); ++ ret = qca8k_fdb_access(priv, QCA8K_FDB_PURGE, -1); ++ mutex_unlock(&priv->reg_mutex); ++ ++ return ret; ++} ++ ++void qca8k_fdb_flush(struct qca8k_priv *priv) ++{ ++ mutex_lock(&priv->reg_mutex); ++ qca8k_fdb_access(priv, QCA8K_FDB_FLUSH, -1); ++ mutex_unlock(&priv->reg_mutex); ++} ++ ++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; ++} ++ + int qca8k_mib_init(struct qca8k_priv *priv) + { + int ret; +@@ -368,6 +573,15 @@ void qca8k_port_bridge_leave(struct dsa_ + QCA8K_PORT_LOOKUP_MEMBER, BIT(cpu_port)); + } + ++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); ++} ++ + int qca8k_set_ageing_time(struct dsa_switch *ds, unsigned int msecs) + { + struct qca8k_priv *priv = ds->priv; +@@ -452,3 +666,78 @@ int qca8k_port_max_mtu(struct dsa_switch + { + return QCA8K_MAX_MTU; + } ++ ++int qca8k_port_fdb_insert(struct qca8k_priv *priv, const u8 *addr, ++ u16 port_mask, u16 vid) ++{ ++ /* Set the vid to the port vlan id if no vid is set */ ++ if (!vid) ++ vid = QCA8K_PORT_VID_DEF; ++ ++ return qca8k_fdb_add(priv, addr, port_mask, vid, ++ QCA8K_ATU_STATUS_STATIC); ++} ++ ++int qca8k_port_fdb_add(struct dsa_switch *ds, int port, ++ const unsigned char *addr, u16 vid) ++{ ++ struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv; ++ u16 port_mask = BIT(port); ++ ++ return qca8k_port_fdb_insert(priv, addr, port_mask, vid); ++} ++ ++int qca8k_port_fdb_del(struct dsa_switch *ds, int port, ++ const unsigned char *addr, u16 vid) ++{ ++ struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv; ++ u16 port_mask = BIT(port); ++ ++ if (!vid) ++ vid = QCA8K_PORT_VID_DEF; ++ ++ return qca8k_fdb_del(priv, addr, port_mask, vid); ++} ++ ++int qca8k_port_fdb_dump(struct dsa_switch *ds, int port, ++ dsa_fdb_dump_cb_t *cb, void *data) ++{ ++ struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv; ++ struct qca8k_fdb _fdb = { 0 }; ++ int cnt = QCA8K_NUM_FDB_RECORDS; ++ bool is_static; ++ int ret = 0; ++ ++ mutex_lock(&priv->reg_mutex); ++ while (cnt-- && !qca8k_fdb_next(priv, &_fdb, port)) { ++ if (!_fdb.aging) ++ break; ++ is_static = (_fdb.aging == QCA8K_ATU_STATUS_STATIC); ++ ret = cb(_fdb.mac, _fdb.vid, is_static, data); ++ if (ret) ++ break; ++ } ++ mutex_unlock(&priv->reg_mutex); ++ ++ return 0; ++} ++ ++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); ++} ++ ++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); ++} +--- a/drivers/net/dsa/qca/qca8k.h ++++ b/drivers/net/dsa/qca/qca8k.h +@@ -430,11 +430,9 @@ int qca8k_read(struct qca8k_priv *priv, + int qca8k_write(struct qca8k_priv *priv, u32 reg, u32 val); + int qca8k_rmw(struct qca8k_priv *priv, u32 reg, u32 mask, u32 write_val); + +-int qca8k_bulk_read(struct qca8k_priv *priv, u32 reg, u32 *val, int len); +-int qca8k_bulk_write(struct qca8k_priv *priv, u32 reg, u32 *val, int len); +- + /* Common ops function */ + int qca8k_busy_wait(struct qca8k_priv *priv, u32 reg, u32 mask); ++void qca8k_fdb_flush(struct qca8k_priv *priv); + + /* Common ethtool stats function */ + void qca8k_get_strings(struct dsa_switch *ds, int port, u32 stringset, uint8_t *data); +@@ -463,6 +461,23 @@ int qca8k_port_change_mtu(struct dsa_swi + int qca8k_port_max_mtu(struct dsa_switch *ds, int port); + + /* Common fast age function */ ++void qca8k_port_fast_age(struct dsa_switch *ds, int port); + int qca8k_set_ageing_time(struct dsa_switch *ds, unsigned int msecs); + ++/* Common FDB function */ ++int qca8k_port_fdb_insert(struct qca8k_priv *priv, const u8 *addr, ++ u16 port_mask, u16 vid); ++int qca8k_port_fdb_add(struct dsa_switch *ds, int port, ++ const unsigned char *addr, u16 vid); ++int qca8k_port_fdb_del(struct dsa_switch *ds, int port, ++ const unsigned char *addr, u16 vid); ++int qca8k_port_fdb_dump(struct dsa_switch *ds, int port, ++ dsa_fdb_dump_cb_t *cb, void *data); ++ ++/* Common MDB function */ ++int qca8k_port_mdb_add(struct dsa_switch *ds, int port, ++ const struct switchdev_obj_port_mdb *mdb); ++int qca8k_port_mdb_del(struct dsa_switch *ds, int port, ++ const struct switchdev_obj_port_mdb *mdb); ++ + #endif /* __QCA8K_H */ diff --git a/target/linux/generic/backport-6.1/771-v6.0-11-net-dsa-qca8k-move-port-mirror-functions-to-common-c.patch b/target/linux/generic/backport-6.1/771-v6.0-11-net-dsa-qca8k-move-port-mirror-functions-to-common-c.patch new file mode 100644 index 0000000000..c1336d4a92 --- /dev/null +++ b/target/linux/generic/backport-6.1/771-v6.0-11-net-dsa-qca8k-move-port-mirror-functions-to-common-c.patch @@ -0,0 +1,232 @@ +From 742d37a84d3f7bb60d9b2d9ada9ad4e599f65ebf Mon Sep 17 00:00:00 2001 +From: Christian Marangi +Date: Wed, 27 Jul 2022 13:35:20 +0200 +Subject: [PATCH 11/14] net: dsa: qca8k: move port mirror functions to common + code + +The same port mirror functions are used by drivers based on qca8k family +switch. Move them to common code to make them accessible also by other +drivers. + +Signed-off-by: Christian Marangi +Reviewed-by: Vladimir Oltean +Signed-off-by: Jakub Kicinski +--- + drivers/net/dsa/qca/qca8k-8xxx.c | 93 ------------------------------ + drivers/net/dsa/qca/qca8k-common.c | 91 +++++++++++++++++++++++++++++ + drivers/net/dsa/qca/qca8k.h | 7 +++ + 3 files changed, 98 insertions(+), 93 deletions(-) + +--- a/drivers/net/dsa/qca/qca8k-8xxx.c ++++ b/drivers/net/dsa/qca/qca8k-8xxx.c +@@ -1838,99 +1838,6 @@ exit: + } + + 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) + { +--- a/drivers/net/dsa/qca/qca8k-common.c ++++ b/drivers/net/dsa/qca/qca8k-common.c +@@ -741,3 +741,94 @@ int qca8k_port_mdb_del(struct dsa_switch + + return qca8k_fdb_search_and_del(priv, BIT(port), addr, vid); + } ++ ++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; ++} ++ ++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); ++} +--- a/drivers/net/dsa/qca/qca8k.h ++++ b/drivers/net/dsa/qca/qca8k.h +@@ -480,4 +480,11 @@ int qca8k_port_mdb_add(struct dsa_switch + int qca8k_port_mdb_del(struct dsa_switch *ds, int port, + const struct switchdev_obj_port_mdb *mdb); + ++/* Common port mirror function */ ++int qca8k_port_mirror_add(struct dsa_switch *ds, int port, ++ struct dsa_mall_mirror_tc_entry *mirror, ++ bool ingress); ++void qca8k_port_mirror_del(struct dsa_switch *ds, int port, ++ struct dsa_mall_mirror_tc_entry *mirror); ++ + #endif /* __QCA8K_H */ diff --git a/target/linux/generic/backport-6.1/771-v6.0-12-net-dsa-qca8k-move-port-VLAN-functions-to-common-cod.patch b/target/linux/generic/backport-6.1/771-v6.0-12-net-dsa-qca8k-move-port-VLAN-functions-to-common-cod.patch new file mode 100644 index 0000000000..898010f950 --- /dev/null +++ b/target/linux/generic/backport-6.1/771-v6.0-12-net-dsa-qca8k-move-port-VLAN-functions-to-common-cod.patch @@ -0,0 +1,448 @@ +From c5290f636624b98e76a82bd63ffec0a8a9daa620 Mon Sep 17 00:00:00 2001 +From: Christian Marangi +Date: Wed, 27 Jul 2022 13:35:21 +0200 +Subject: [PATCH 12/14] net: dsa: qca8k: move port VLAN functions to common + code + +The same port VLAN functions are used by drivers based on qca8k family +switch. Move them to common code to make them accessible also by other +drivers. +Also drop exposing busy_wait and make it static. + +Signed-off-by: Christian Marangi +Reviewed-by: Vladimir Oltean +Signed-off-by: Jakub Kicinski +--- + drivers/net/dsa/qca/qca8k-8xxx.c | 182 ----------------------------- + drivers/net/dsa/qca/qca8k-common.c | 179 +++++++++++++++++++++++++++- + drivers/net/dsa/qca/qca8k.h | 10 +- + 3 files changed, 187 insertions(+), 184 deletions(-) + +--- a/drivers/net/dsa/qca/qca8k-8xxx.c ++++ b/drivers/net/dsa/qca/qca8k-8xxx.c +@@ -15,7 +15,6 @@ + #include + #include + #include +-#include + #include + #include + #include +@@ -442,122 +441,6 @@ static struct regmap_config qca8k_regmap + }; + + static int +-qca8k_vlan_access(struct qca8k_priv *priv, enum qca8k_vlan_cmd cmd, u16 vid) +-{ +- u32 reg; +- int ret; +- +- /* Set the command and VLAN index */ +- reg = QCA8K_VTU_FUNC1_BUSY; +- reg |= cmd; +- 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); +- if (ret) +- return ret; +- +- /* wait for completion */ +- ret = qca8k_busy_wait(priv, QCA8K_REG_VTU_FUNC1, QCA8K_VTU_FUNC1_BUSY); +- if (ret) +- return ret; +- +- /* Check for table full violation when adding an entry */ +- if (cmd == QCA8K_VLAN_LOAD) { +- ret = qca8k_read(priv, QCA8K_REG_VTU_FUNC1, ®); +- if (ret < 0) +- return ret; +- if (reg & QCA8K_VTU_FUNC1_FULL) +- return -ENOMEM; +- } +- +- return 0; +-} +- +-static int +-qca8k_vlan_add(struct qca8k_priv *priv, u8 port, u16 vid, bool untagged) +-{ +- u32 reg; +- int ret; +- +- /* +- We do the right thing with VLAN 0 and treat it as untagged while +- preserving the tag on egress. +- */ +- if (vid == 0) +- return 0; +- +- mutex_lock(&priv->reg_mutex); +- ret = qca8k_vlan_access(priv, QCA8K_VLAN_READ, vid); +- if (ret < 0) +- goto out; +- +- ret = qca8k_read(priv, QCA8K_REG_VTU_FUNC0, ®); +- if (ret < 0) +- goto out; +- reg |= QCA8K_VTU_FUNC0_VALID | QCA8K_VTU_FUNC0_IVL_EN; +- reg &= ~QCA8K_VTU_FUNC0_EG_MODE_PORT_MASK(port); +- if (untagged) +- reg |= QCA8K_VTU_FUNC0_EG_MODE_PORT_UNTAG(port); +- else +- reg |= QCA8K_VTU_FUNC0_EG_MODE_PORT_TAG(port); +- +- ret = qca8k_write(priv, QCA8K_REG_VTU_FUNC0, reg); +- if (ret) +- goto out; +- ret = qca8k_vlan_access(priv, QCA8K_VLAN_LOAD, vid); +- +-out: +- mutex_unlock(&priv->reg_mutex); +- +- return ret; +-} +- +-static int +-qca8k_vlan_del(struct qca8k_priv *priv, u8 port, u16 vid) +-{ +- u32 reg, mask; +- int ret, i; +- bool del; +- +- mutex_lock(&priv->reg_mutex); +- ret = qca8k_vlan_access(priv, QCA8K_VLAN_READ, vid); +- if (ret < 0) +- goto out; +- +- ret = qca8k_read(priv, QCA8K_REG_VTU_FUNC0, ®); +- if (ret < 0) +- goto out; +- 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_PORT_NOT(i); +- +- if ((reg & mask) != mask) { +- del = false; +- break; +- } +- } +- +- if (del) { +- ret = qca8k_vlan_access(priv, QCA8K_VLAN_PURGE, vid); +- } else { +- ret = qca8k_write(priv, QCA8K_REG_VTU_FUNC0, reg); +- if (ret) +- goto out; +- ret = qca8k_vlan_access(priv, QCA8K_VLAN_LOAD, vid); +- } +- +-out: +- mutex_unlock(&priv->reg_mutex); +- +- return ret; +-} +- +-static int + qca8k_phy_eth_busy_wait(struct qca8k_mgmt_eth_data *mgmt_eth_data, + struct sk_buff *read_skb, u32 *val) + { +@@ -1836,71 +1719,6 @@ exit: + + return ret; + } +- +-static int +-qca8k_port_vlan_filtering(struct dsa_switch *ds, int port, bool vlan_filtering, +- struct netlink_ext_ack *extack) +-{ +- struct qca8k_priv *priv = ds->priv; +- int ret; +- +- if (vlan_filtering) { +- ret = qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(port), +- 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_MASK, +- QCA8K_PORT_LOOKUP_VLAN_MODE_NONE); +- } +- +- return ret; +-} +- +-static int +-qca8k_port_vlan_add(struct dsa_switch *ds, int port, +- const struct switchdev_obj_port_vlan *vlan, +- struct netlink_ext_ack *extack) +-{ +- bool untagged = vlan->flags & BRIDGE_VLAN_INFO_UNTAGGED; +- bool pvid = vlan->flags & BRIDGE_VLAN_INFO_PVID; +- struct qca8k_priv *priv = ds->priv; +- int ret; +- +- ret = qca8k_vlan_add(priv, port, vlan->vid, untagged); +- if (ret) { +- dev_err(priv->dev, "Failed to add VLAN to port %d (%d)", port, ret); +- return ret; +- } +- +- if (pvid) { +- ret = qca8k_rmw(priv, QCA8K_EGRESS_VLAN(port), +- QCA8K_EGREES_VLAN_PORT_MASK(port), +- QCA8K_EGREES_VLAN_PORT(port, vlan->vid)); +- if (ret) +- return ret; +- +- ret = qca8k_write(priv, QCA8K_REG_PORT_VLAN_CTRL0(port), +- QCA8K_PORT_VLAN_CVID(vlan->vid) | +- QCA8K_PORT_VLAN_SVID(vlan->vid)); +- } +- +- return ret; +-} +- +-static int +-qca8k_port_vlan_del(struct dsa_switch *ds, int port, +- const struct switchdev_obj_port_vlan *vlan) +-{ +- struct qca8k_priv *priv = ds->priv; +- int ret; +- +- ret = qca8k_vlan_del(priv, port, vlan->vid); +- if (ret) +- dev_err(priv->dev, "Failed to delete VLAN from port %d (%d)", port, ret); +- +- return ret; +-} + + static u32 qca8k_get_phy_flags(struct dsa_switch *ds, int port) + { +--- a/drivers/net/dsa/qca/qca8k-common.c ++++ b/drivers/net/dsa/qca/qca8k-common.c +@@ -141,7 +141,7 @@ static int qca8k_bulk_write(struct qca8k + return 0; + } + +-int qca8k_busy_wait(struct qca8k_priv *priv, u32 reg, u32 mask) ++static int qca8k_busy_wait(struct qca8k_priv *priv, u32 reg, u32 mask) + { + u32 val; + +@@ -354,6 +354,120 @@ exit: + return ret; + } + ++static int qca8k_vlan_access(struct qca8k_priv *priv, ++ enum qca8k_vlan_cmd cmd, u16 vid) ++{ ++ u32 reg; ++ int ret; ++ ++ /* Set the command and VLAN index */ ++ reg = QCA8K_VTU_FUNC1_BUSY; ++ reg |= cmd; ++ 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); ++ if (ret) ++ return ret; ++ ++ /* wait for completion */ ++ ret = qca8k_busy_wait(priv, QCA8K_REG_VTU_FUNC1, QCA8K_VTU_FUNC1_BUSY); ++ if (ret) ++ return ret; ++ ++ /* Check for table full violation when adding an entry */ ++ if (cmd == QCA8K_VLAN_LOAD) { ++ ret = qca8k_read(priv, QCA8K_REG_VTU_FUNC1, ®); ++ if (ret < 0) ++ return ret; ++ if (reg & QCA8K_VTU_FUNC1_FULL) ++ return -ENOMEM; ++ } ++ ++ return 0; ++} ++ ++static int qca8k_vlan_add(struct qca8k_priv *priv, u8 port, u16 vid, ++ bool untagged) ++{ ++ u32 reg; ++ int ret; ++ ++ /* We do the right thing with VLAN 0 and treat it as untagged while ++ * preserving the tag on egress. ++ */ ++ if (vid == 0) ++ return 0; ++ ++ mutex_lock(&priv->reg_mutex); ++ ret = qca8k_vlan_access(priv, QCA8K_VLAN_READ, vid); ++ if (ret < 0) ++ goto out; ++ ++ ret = qca8k_read(priv, QCA8K_REG_VTU_FUNC0, ®); ++ if (ret < 0) ++ goto out; ++ reg |= QCA8K_VTU_FUNC0_VALID | QCA8K_VTU_FUNC0_IVL_EN; ++ reg &= ~QCA8K_VTU_FUNC0_EG_MODE_PORT_MASK(port); ++ if (untagged) ++ reg |= QCA8K_VTU_FUNC0_EG_MODE_PORT_UNTAG(port); ++ else ++ reg |= QCA8K_VTU_FUNC0_EG_MODE_PORT_TAG(port); ++ ++ ret = qca8k_write(priv, QCA8K_REG_VTU_FUNC0, reg); ++ if (ret) ++ goto out; ++ ret = qca8k_vlan_access(priv, QCA8K_VLAN_LOAD, vid); ++ ++out: ++ mutex_unlock(&priv->reg_mutex); ++ ++ return ret; ++} ++ ++static int qca8k_vlan_del(struct qca8k_priv *priv, u8 port, u16 vid) ++{ ++ u32 reg, mask; ++ int ret, i; ++ bool del; ++ ++ mutex_lock(&priv->reg_mutex); ++ ret = qca8k_vlan_access(priv, QCA8K_VLAN_READ, vid); ++ if (ret < 0) ++ goto out; ++ ++ ret = qca8k_read(priv, QCA8K_REG_VTU_FUNC0, ®); ++ if (ret < 0) ++ goto out; ++ 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_PORT_NOT(i); ++ ++ if ((reg & mask) != mask) { ++ del = false; ++ break; ++ } ++ } ++ ++ if (del) { ++ ret = qca8k_vlan_access(priv, QCA8K_VLAN_PURGE, vid); ++ } else { ++ ret = qca8k_write(priv, QCA8K_REG_VTU_FUNC0, reg); ++ if (ret) ++ goto out; ++ ret = qca8k_vlan_access(priv, QCA8K_VLAN_LOAD, vid); ++ } ++ ++out: ++ mutex_unlock(&priv->reg_mutex); ++ ++ return ret; ++} ++ + int qca8k_mib_init(struct qca8k_priv *priv) + { + int ret; +@@ -832,3 +946,66 @@ void qca8k_port_mirror_del(struct dsa_sw + err: + dev_err(priv->dev, "Failed to del mirror port from %d", port); + } ++ ++int qca8k_port_vlan_filtering(struct dsa_switch *ds, int port, ++ bool vlan_filtering, ++ struct netlink_ext_ack *extack) ++{ ++ struct qca8k_priv *priv = ds->priv; ++ int ret; ++ ++ if (vlan_filtering) { ++ ret = qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(port), ++ 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_MASK, ++ QCA8K_PORT_LOOKUP_VLAN_MODE_NONE); ++ } ++ ++ return ret; ++} ++ ++int qca8k_port_vlan_add(struct dsa_switch *ds, int port, ++ const struct switchdev_obj_port_vlan *vlan, ++ struct netlink_ext_ack *extack) ++{ ++ bool untagged = vlan->flags & BRIDGE_VLAN_INFO_UNTAGGED; ++ bool pvid = vlan->flags & BRIDGE_VLAN_INFO_PVID; ++ struct qca8k_priv *priv = ds->priv; ++ int ret; ++ ++ ret = qca8k_vlan_add(priv, port, vlan->vid, untagged); ++ if (ret) { ++ dev_err(priv->dev, "Failed to add VLAN to port %d (%d)", port, ret); ++ return ret; ++ } ++ ++ if (pvid) { ++ ret = qca8k_rmw(priv, QCA8K_EGRESS_VLAN(port), ++ QCA8K_EGREES_VLAN_PORT_MASK(port), ++ QCA8K_EGREES_VLAN_PORT(port, vlan->vid)); ++ if (ret) ++ return ret; ++ ++ ret = qca8k_write(priv, QCA8K_REG_PORT_VLAN_CTRL0(port), ++ QCA8K_PORT_VLAN_CVID(vlan->vid) | ++ QCA8K_PORT_VLAN_SVID(vlan->vid)); ++ } ++ ++ return ret; ++} ++ ++int qca8k_port_vlan_del(struct dsa_switch *ds, int port, ++ const struct switchdev_obj_port_vlan *vlan) ++{ ++ struct qca8k_priv *priv = ds->priv; ++ int ret; ++ ++ ret = qca8k_vlan_del(priv, port, vlan->vid); ++ if (ret) ++ dev_err(priv->dev, "Failed to delete VLAN from port %d (%d)", port, ret); ++ ++ return ret; ++} +--- a/drivers/net/dsa/qca/qca8k.h ++++ b/drivers/net/dsa/qca/qca8k.h +@@ -431,7 +431,6 @@ int qca8k_write(struct qca8k_priv *priv, + int qca8k_rmw(struct qca8k_priv *priv, u32 reg, u32 mask, u32 write_val); + + /* Common ops function */ +-int qca8k_busy_wait(struct qca8k_priv *priv, u32 reg, u32 mask); + void qca8k_fdb_flush(struct qca8k_priv *priv); + + /* Common ethtool stats function */ +@@ -487,4 +486,13 @@ int qca8k_port_mirror_add(struct dsa_swi + void qca8k_port_mirror_del(struct dsa_switch *ds, int port, + struct dsa_mall_mirror_tc_entry *mirror); + ++/* Common port VLAN function */ ++int qca8k_port_vlan_filtering(struct dsa_switch *ds, int port, bool vlan_filtering, ++ struct netlink_ext_ack *extack); ++int qca8k_port_vlan_add(struct dsa_switch *ds, int port, ++ const struct switchdev_obj_port_vlan *vlan, ++ struct netlink_ext_ack *extack); ++int qca8k_port_vlan_del(struct dsa_switch *ds, int port, ++ const struct switchdev_obj_port_vlan *vlan); ++ + #endif /* __QCA8K_H */ diff --git a/target/linux/generic/backport-6.1/771-v6.0-13-net-dsa-qca8k-move-port-LAG-functions-to-common-code.patch b/target/linux/generic/backport-6.1/771-v6.0-13-net-dsa-qca8k-move-port-LAG-functions-to-common-code.patch new file mode 100644 index 0000000000..1802b17eaa --- /dev/null +++ b/target/linux/generic/backport-6.1/771-v6.0-13-net-dsa-qca8k-move-port-LAG-functions-to-common-code.patch @@ -0,0 +1,384 @@ +From e9bbf019af44b204b71ef8edf224002550aab641 Mon Sep 17 00:00:00 2001 +From: Christian Marangi +Date: Wed, 27 Jul 2022 13:35:22 +0200 +Subject: [PATCH 13/14] net: dsa: qca8k: move port LAG functions to common code + +The same port LAG functions are used by drivers based on qca8k family +switch. Move them to common code to make them accessible also by other +drivers. + +Signed-off-by: Christian Marangi +Reviewed-by: Vladimir Oltean +Signed-off-by: Jakub Kicinski +--- + drivers/net/dsa/qca/qca8k-8xxx.c | 168 ----------------------------- + drivers/net/dsa/qca/qca8k-common.c | 165 ++++++++++++++++++++++++++++ + drivers/net/dsa/qca/qca8k.h | 6 ++ + 3 files changed, 171 insertions(+), 168 deletions(-) + +--- a/drivers/net/dsa/qca/qca8k-8xxx.c ++++ b/drivers/net/dsa/qca/qca8k-8xxx.c +@@ -1743,178 +1743,6 @@ 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; +- u32 hash = 0; +- int i, id; +- +- 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 void + qca8k_master_change(struct dsa_switch *ds, const struct net_device *master, + bool operational) +--- a/drivers/net/dsa/qca/qca8k-common.c ++++ b/drivers/net/dsa/qca/qca8k-common.c +@@ -1009,3 +1009,169 @@ int qca8k_port_vlan_del(struct dsa_switc + + return ret; + } ++ ++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; ++ u32 hash = 0; ++ int i, id; ++ ++ 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: Mismatched 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)); ++} ++ ++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); ++} ++ ++int qca8k_port_lag_leave(struct dsa_switch *ds, int port, ++ struct net_device *lag) ++{ ++ return qca8k_lag_refresh_portmap(ds, port, lag, true); ++} +--- a/drivers/net/dsa/qca/qca8k.h ++++ b/drivers/net/dsa/qca/qca8k.h +@@ -495,4 +495,10 @@ int qca8k_port_vlan_add(struct dsa_switc + int qca8k_port_vlan_del(struct dsa_switch *ds, int port, + const struct switchdev_obj_port_vlan *vlan); + ++/* Common port LAG function */ ++int qca8k_port_lag_join(struct dsa_switch *ds, int port, struct net_device *lag, ++ struct netdev_lag_upper_info *info); ++int qca8k_port_lag_leave(struct dsa_switch *ds, int port, ++ struct net_device *lag); ++ + #endif /* __QCA8K_H */ diff --git a/target/linux/generic/backport-6.1/771-v6.0-14-net-dsa-qca8k-move-read_switch_id-function-to-common.patch b/target/linux/generic/backport-6.1/771-v6.0-14-net-dsa-qca8k-move-read_switch_id-function-to-common.patch new file mode 100644 index 0000000000..d6ec8b77e0 --- /dev/null +++ b/target/linux/generic/backport-6.1/771-v6.0-14-net-dsa-qca8k-move-read_switch_id-function-to-common.patch @@ -0,0 +1,102 @@ +From 9d1bcb1f293f1391302a109c9819c3705c804700 Mon Sep 17 00:00:00 2001 +From: Christian Marangi +Date: Wed, 27 Jul 2022 13:35:23 +0200 +Subject: [PATCH 14/14] net: dsa: qca8k: move read_switch_id function to common + code + +The same function to read the switch id is used by drivers based on +qca8k family switch. Move them to common code to make them accessible +also by other drivers. + +Signed-off-by: Christian Marangi +Reviewed-by: Vladimir Oltean +Signed-off-by: Jakub Kicinski +--- + drivers/net/dsa/qca/qca8k-8xxx.c | 29 ----------------------------- + drivers/net/dsa/qca/qca8k-common.c | 29 +++++++++++++++++++++++++++++ + drivers/net/dsa/qca/qca8k.h | 1 + + 3 files changed, 30 insertions(+), 29 deletions(-) + +--- a/drivers/net/dsa/qca/qca8k-8xxx.c ++++ b/drivers/net/dsa/qca/qca8k-8xxx.c +@@ -1822,35 +1822,6 @@ static const struct dsa_switch_ops qca8k + .connect_tag_protocol = qca8k_connect_tag_protocol, + }; + +-static int qca8k_read_switch_id(struct qca8k_priv *priv) +-{ +- u32 val; +- u8 id; +- int ret; +- +- if (!priv->info) +- return -ENODEV; +- +- ret = qca8k_read(priv, QCA8K_REG_MASK_CTRL, &val); +- if (ret < 0) +- return -ENODEV; +- +- id = QCA8K_MASK_CTRL_DEVICE_ID(val); +- if (id != priv->info->id) { +- dev_err(priv->dev, +- "Switch id detected %x but expected %x", +- id, priv->info->id); +- return -ENODEV; +- } +- +- priv->switch_id = id; +- +- /* Save revision to communicate to the internal PHY driver */ +- priv->switch_revision = QCA8K_MASK_CTRL_REV_ID(val); +- +- return 0; +-} +- + static int + qca8k_sw_probe(struct mdio_device *mdiodev) + { +--- a/drivers/net/dsa/qca/qca8k-common.c ++++ b/drivers/net/dsa/qca/qca8k-common.c +@@ -1175,3 +1175,32 @@ int qca8k_port_lag_leave(struct dsa_swit + { + return qca8k_lag_refresh_portmap(ds, port, lag, true); + } ++ ++int qca8k_read_switch_id(struct qca8k_priv *priv) ++{ ++ u32 val; ++ u8 id; ++ int ret; ++ ++ if (!priv->info) ++ return -ENODEV; ++ ++ ret = qca8k_read(priv, QCA8K_REG_MASK_CTRL, &val); ++ if (ret < 0) ++ return -ENODEV; ++ ++ id = QCA8K_MASK_CTRL_DEVICE_ID(val); ++ if (id != priv->info->id) { ++ dev_err(priv->dev, ++ "Switch id detected %x but expected %x", ++ id, priv->info->id); ++ return -ENODEV; ++ } ++ ++ priv->switch_id = id; ++ ++ /* Save revision to communicate to the internal PHY driver */ ++ priv->switch_revision = QCA8K_MASK_CTRL_REV_ID(val); ++ ++ return 0; ++} +--- a/drivers/net/dsa/qca/qca8k.h ++++ b/drivers/net/dsa/qca/qca8k.h +@@ -424,6 +424,7 @@ extern const struct qca8k_mib_desc ar832 + extern const struct regmap_access_table qca8k_readable_table; + int qca8k_mib_init(struct qca8k_priv *priv); + void qca8k_port_set_status(struct qca8k_priv *priv, int port, int enable); ++int qca8k_read_switch_id(struct qca8k_priv *priv); + + /* Common read/write/rmw function */ + int qca8k_read(struct qca8k_priv *priv, u32 reg, u32 *val); diff --git a/target/linux/generic/backport-6.1/772-v6.0-net-dsa-qca8k-fix-NULL-pointer-dereference-for-of_de.patch b/target/linux/generic/backport-6.1/772-v6.0-net-dsa-qca8k-fix-NULL-pointer-dereference-for-of_de.patch new file mode 100644 index 0000000000..0cca2788f6 --- /dev/null +++ b/target/linux/generic/backport-6.1/772-v6.0-net-dsa-qca8k-fix-NULL-pointer-dereference-for-of_de.patch @@ -0,0 +1,29 @@ +From 057bcf15db8e625276ddf02b2b7c668a3cb43f81 Mon Sep 17 00:00:00 2001 +From: Christian Marangi +Date: Sun, 4 Sep 2022 23:46:24 +0200 +Subject: [net PATCH] net: dsa: qca8k: fix NULL pointer dereference for + of_device_get_match_data + +of_device_get_match_data is called on priv->dev before priv->dev is +actually set. Move of_device_get_match_data after priv->dev is correctly +set to fix this kernel panic. + +Fixes: 3bb0844e7bcd ("net: dsa: qca8k: cache match data to speed up access") +Signed-off-by: Christian Marangi +--- + drivers/net/dsa/qca/qca8k-8xxx.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/drivers/net/dsa/qca/qca8k-8xxx.c ++++ b/drivers/net/dsa/qca/qca8k-8xxx.c +@@ -1835,9 +1835,9 @@ qca8k_sw_probe(struct mdio_device *mdiod + if (!priv) + return -ENOMEM; + +- priv->info = of_device_get_match_data(priv->dev); + priv->bus = mdiodev->bus; + priv->dev = &mdiodev->dev; ++ priv->info = of_device_get_match_data(priv->dev); + + priv->reset_gpio = devm_gpiod_get_optional(priv->dev, "reset", + GPIOD_ASIS); diff --git a/target/linux/generic/backport-6.1/773-v5.18-1-net-dsa-Move-VLAN-filtering-syncing-out-of-dsa_switc.patch b/target/linux/generic/backport-6.1/773-v5.18-1-net-dsa-Move-VLAN-filtering-syncing-out-of-dsa_switc.patch new file mode 100644 index 0000000000..44093eab95 --- /dev/null +++ b/target/linux/generic/backport-6.1/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 +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 +Reviewed-by: Vladimir Oltean +Signed-off-by: David S. Miller +--- + 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/target/linux/generic/backport-6.1/773-v5.18-2-net-dsa-Avoid-cross-chip-syncing-of-VLAN-filtering.patch b/target/linux/generic/backport-6.1/773-v5.18-2-net-dsa-Avoid-cross-chip-syncing-of-VLAN-filtering.patch new file mode 100644 index 0000000000..cdddbcf14e --- /dev/null +++ b/target/linux/generic/backport-6.1/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 +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 +Reviewed-by: Vladimir Oltean +Signed-off-by: David S. Miller +--- + 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/target/linux/generic/backport-6.1/774-v5.16-01-net-dsa-rtl8366rb-Support-bridge-offloading.patch b/target/linux/generic/backport-6.1/774-v5.16-01-net-dsa-rtl8366rb-Support-bridge-offloading.patch new file mode 100644 index 0000000000..78570c5e6e --- /dev/null +++ b/target/linux/generic/backport-6.1/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 +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 +Cc: Mauri Sandberg +Reviewed-by: Vladimir Oltean +Reviewed-by: Alvin Å ipraga +Reviewed-by: Florian Fainelli +Signed-off-by: DENG Qingfang +Signed-off-by: Linus Walleij +Signed-off-by: David S. Miller +--- + 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/target/linux/generic/backport-6.1/774-v5.16-02-net-dsa-rtl8366-Drop-custom-VLAN-set-up.patch b/target/linux/generic/backport-6.1/774-v5.16-02-net-dsa-rtl8366-Drop-custom-VLAN-set-up.patch new file mode 100644 index 0000000000..e61349a32c --- /dev/null +++ b/target/linux/generic/backport-6.1/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 +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 +Cc: DENG Qingfang +Reviewed-by: Vladimir Oltean +Reviewed-by: Alvin Å ipraga +Reviewed-by: Florian Fainelli +Signed-off-by: Linus Walleij +Signed-off-by: David S. Miller +--- + 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/target/linux/generic/backport-6.1/774-v5.16-03-net-dsa-rtl8366rb-Rewrite-weird-VLAN-filering-enable.patch b/target/linux/generic/backport-6.1/774-v5.16-03-net-dsa-rtl8366rb-Rewrite-weird-VLAN-filering-enable.patch new file mode 100644 index 0000000000..2b19752399 --- /dev/null +++ b/target/linux/generic/backport-6.1/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 +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 +Cc: Mauri Sandberg +Cc: Alvin Å ipraga +Cc: Florian Fainelli +Cc: DENG Qingfang +Signed-off-by: Linus Walleij +Signed-off-by: David S. Miller +--- + 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/target/linux/generic/backport-6.1/774-v5.16-06-net-dsa-rtl8366-Drop-and-depromote-pointless-prints.patch b/target/linux/generic/backport-6.1/774-v5.16-06-net-dsa-rtl8366-Drop-and-depromote-pointless-prints.patch new file mode 100644 index 0000000000..b56032c366 --- /dev/null +++ b/target/linux/generic/backport-6.1/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 +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 +Cc: Mauri Sandberg +Cc: DENG Qingfang +Reviewed-by: Alvin Å ipraga +Reviewed-by: Florian Fainelli +Signed-off-by: Linus Walleij +Signed-off-by: David S. Miller +--- + 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/target/linux/generic/backport-6.1/774-v5.16-07-net-dsa-rtl8366rb-Use-core-filtering-tracking.patch b/target/linux/generic/backport-6.1/774-v5.16-07-net-dsa-rtl8366rb-Use-core-filtering-tracking.patch new file mode 100644 index 0000000000..8cd1df97f2 --- /dev/null +++ b/target/linux/generic/backport-6.1/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 +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 +Cc: Mauri Sandberg +Cc: DENG Qingfang +Cc: Alvin Å ipraga +Cc: Reviewed-by: Florian Fainelli +Signed-off-by: Linus Walleij +Signed-off-by: David S. Miller +--- + 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/target/linux/generic/backport-6.1/774-v5.16-08-net-dsa-rtl8366rb-Support-disabling-learning.patch b/target/linux/generic/backport-6.1/774-v5.16-08-net-dsa-rtl8366rb-Support-disabling-learning.patch new file mode 100644 index 0000000000..8306eb5aef --- /dev/null +++ b/target/linux/generic/backport-6.1/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 +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 +Cc: Alvin Å ipraga +Cc: Mauri Sandberg +Cc: Florian Fainelli +Cc: DENG Qingfang +Reviewed-by: Vladimir Oltean +Signed-off-by: Linus Walleij +Signed-off-by: David S. Miller +--- + 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 + #include ++#include + #include + #include + #include +@@ -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/target/linux/generic/backport-6.1/774-v5.16-09-net-dsa-rtl8366rb-Support-fast-aging.patch b/target/linux/generic/backport-6.1/774-v5.16-09-net-dsa-rtl8366rb-Support-fast-aging.patch new file mode 100644 index 0000000000..a74108e23d --- /dev/null +++ b/target/linux/generic/backport-6.1/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 +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 +Cc: Mauri Sandberg +Cc: DENG Qingfang +Cc: Florian Fainelli +Reviewed-by: Alvin Å ipraga +Signed-off-by: Linus Walleij +Reviewed-by: Vladimir Oltean +Signed-off-by: David S. Miller +--- + 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/target/linux/generic/backport-6.1/774-v5.16-10-net-dsa-rtl8366rb-Support-setting-STP-state.patch b/target/linux/generic/backport-6.1/774-v5.16-10-net-dsa-rtl8366rb-Support-setting-STP-state.patch new file mode 100644 index 0000000000..e787ce9481 --- /dev/null +++ b/target/linux/generic/backport-6.1/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 +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 +Cc: Alvin Å ipraga +Cc: Mauri Sandberg +Cc: DENG Qingfang +Signed-off-by: Linus Walleij +Reviewed-by: Vladimir Oltean +Signed-off-by: David S. Miller +--- + 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/target/linux/generic/backport-6.1/775-v6.0-01-net-ethernet-stmicro-stmmac-move-queue-reset-to-dedi.patch b/target/linux/generic/backport-6.1/775-v6.0-01-net-ethernet-stmicro-stmmac-move-queue-reset-to-dedi.patch new file mode 100644 index 0000000000..b6b94ba6c2 --- /dev/null +++ b/target/linux/generic/backport-6.1/775-v6.0-01-net-ethernet-stmicro-stmmac-move-queue-reset-to-dedi.patch @@ -0,0 +1,142 @@ +From f9ec5723c3dbfcede9c7b0dcdf85e401ce16316c Mon Sep 17 00:00:00 2001 +From: Christian Marangi +Date: Sat, 23 Jul 2022 16:29:29 +0200 +Subject: [PATCH 1/5] net: ethernet: stmicro: stmmac: move queue reset to + dedicated functions + +Move queue reset to dedicated functions. This aside from a simple +cleanup is also required to allocate a dma conf without resetting the tx +queue while the device is temporarily detached as now the reset is not +part of the dma init function and can be done later in the code flow. + +Signed-off-by: Christian Marangi +Signed-off-by: Jakub Kicinski +--- + .../net/ethernet/stmicro/stmmac/stmmac_main.c | 59 ++++++++++--------- + 1 file changed, 31 insertions(+), 28 deletions(-) + +--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c ++++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +@@ -130,6 +130,9 @@ static irqreturn_t stmmac_mac_interrupt( + static irqreturn_t stmmac_safety_interrupt(int irq, void *dev_id); + static irqreturn_t stmmac_msi_intr_tx(int irq, void *data); + static irqreturn_t stmmac_msi_intr_rx(int irq, void *data); ++static void stmmac_reset_rx_queue(struct stmmac_priv *priv, u32 queue); ++static void stmmac_reset_tx_queue(struct stmmac_priv *priv, u32 queue); ++static void stmmac_reset_queues_param(struct stmmac_priv *priv); + static void stmmac_tx_timer_arm(struct stmmac_priv *priv, u32 queue); + static void stmmac_flush_tx_descriptors(struct stmmac_priv *priv, int queue); + +@@ -1697,9 +1700,6 @@ static int __init_dma_rx_desc_rings(stru + return -ENOMEM; + } + +- rx_q->cur_rx = 0; +- rx_q->dirty_rx = 0; +- + /* Setup the chained descriptor addresses */ + if (priv->mode == STMMAC_CHAIN_MODE) { + if (priv->extend_desc) +@@ -1805,12 +1805,6 @@ static int __init_dma_tx_desc_rings(stru + tx_q->tx_skbuff[i] = NULL; + } + +- tx_q->dirty_tx = 0; +- tx_q->cur_tx = 0; +- tx_q->mss = 0; +- +- netdev_tx_reset_queue(netdev_get_tx_queue(priv->dev, queue)); +- + return 0; + } + +@@ -2679,10 +2673,7 @@ static void stmmac_tx_err(struct stmmac_ + stmmac_stop_tx_dma(priv, chan); + dma_free_tx_skbufs(priv, chan); + stmmac_clear_tx_descriptors(priv, chan); +- tx_q->dirty_tx = 0; +- tx_q->cur_tx = 0; +- tx_q->mss = 0; +- netdev_tx_reset_queue(netdev_get_tx_queue(priv->dev, chan)); ++ stmmac_reset_tx_queue(priv, chan); + stmmac_init_tx_chan(priv, priv->ioaddr, priv->plat->dma_cfg, + tx_q->dma_tx_phy, chan); + stmmac_start_tx_dma(priv, chan); +@@ -3766,6 +3757,8 @@ static int stmmac_open(struct net_device + } + } + ++ stmmac_reset_queues_param(priv); ++ + ret = stmmac_hw_setup(dev, true); + if (ret < 0) { + netdev_err(priv->dev, "%s: Hw setup failed\n", __func__); +@@ -6409,6 +6402,7 @@ void stmmac_enable_rx_queue(struct stmma + return; + } + ++ stmmac_reset_rx_queue(priv, queue); + stmmac_clear_rx_descriptors(priv, queue); + + stmmac_init_rx_chan(priv, priv->ioaddr, priv->plat->dma_cfg, +@@ -6470,6 +6464,7 @@ void stmmac_enable_tx_queue(struct stmma + return; + } + ++ stmmac_reset_tx_queue(priv, queue); + stmmac_clear_tx_descriptors(priv, queue); + + stmmac_init_tx_chan(priv, priv->ioaddr, priv->plat->dma_cfg, +@@ -7387,6 +7382,25 @@ int stmmac_suspend(struct device *dev) + } + EXPORT_SYMBOL_GPL(stmmac_suspend); + ++static void stmmac_reset_rx_queue(struct stmmac_priv *priv, u32 queue) ++{ ++ struct stmmac_rx_queue *rx_q = &priv->rx_queue[queue]; ++ ++ rx_q->cur_rx = 0; ++ rx_q->dirty_rx = 0; ++} ++ ++static void stmmac_reset_tx_queue(struct stmmac_priv *priv, u32 queue) ++{ ++ struct stmmac_tx_queue *tx_q = &priv->tx_queue[queue]; ++ ++ tx_q->cur_tx = 0; ++ tx_q->dirty_tx = 0; ++ tx_q->mss = 0; ++ ++ netdev_tx_reset_queue(netdev_get_tx_queue(priv->dev, queue)); ++} ++ + /** + * stmmac_reset_queues_param - reset queue parameters + * @priv: device pointer +@@ -7397,22 +7411,11 @@ static void stmmac_reset_queues_param(st + u32 tx_cnt = priv->plat->tx_queues_to_use; + u32 queue; + +- for (queue = 0; queue < rx_cnt; queue++) { +- struct stmmac_rx_queue *rx_q = &priv->rx_queue[queue]; ++ for (queue = 0; queue < rx_cnt; queue++) ++ stmmac_reset_rx_queue(priv, queue); + +- rx_q->cur_rx = 0; +- rx_q->dirty_rx = 0; +- } +- +- for (queue = 0; queue < tx_cnt; queue++) { +- struct stmmac_tx_queue *tx_q = &priv->tx_queue[queue]; +- +- tx_q->cur_tx = 0; +- tx_q->dirty_tx = 0; +- tx_q->mss = 0; +- +- netdev_tx_reset_queue(netdev_get_tx_queue(priv->dev, queue)); +- } ++ for (queue = 0; queue < tx_cnt; queue++) ++ stmmac_reset_tx_queue(priv, queue); + } + + /** diff --git a/target/linux/generic/backport-6.1/775-v6.0-02-net-ethernet-stmicro-stmmac-first-disable-all-queues.patch b/target/linux/generic/backport-6.1/775-v6.0-02-net-ethernet-stmicro-stmmac-first-disable-all-queues.patch new file mode 100644 index 0000000000..0bb28c79e7 --- /dev/null +++ b/target/linux/generic/backport-6.1/775-v6.0-02-net-ethernet-stmicro-stmmac-first-disable-all-queues.patch @@ -0,0 +1,37 @@ +From 7028471edb646bfc532fec0973e50e784cdcb7c6 Mon Sep 17 00:00:00 2001 +From: Christian Marangi +Date: Sat, 23 Jul 2022 16:29:30 +0200 +Subject: [PATCH 2/5] net: ethernet: stmicro: stmmac: first disable all queues + and disconnect in release + +Disable all queues and disconnect before tx_disable in stmmac_release to +prevent a corner case where packet may be still queued at the same time +tx_disable is called resulting in kernel panic if some packet still has +to be processed. + +Signed-off-by: Christian Marangi +Signed-off-by: Jakub Kicinski +--- + drivers/net/ethernet/stmicro/stmmac/stmmac_main.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c ++++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +@@ -3818,8 +3818,6 @@ static int stmmac_release(struct net_dev + struct stmmac_priv *priv = netdev_priv(dev); + u32 chan; + +- netif_tx_disable(dev); +- + if (device_may_wakeup(priv->device)) + phylink_speed_down(priv->phylink, false); + /* Stop and disconnect the PHY */ +@@ -3831,6 +3829,8 @@ static int stmmac_release(struct net_dev + for (chan = 0; chan < priv->plat->tx_queues_to_use; chan++) + hrtimer_cancel(&priv->tx_queue[chan].txtimer); + ++ netif_tx_disable(dev); ++ + /* Free the IRQ lines */ + stmmac_free_irq(dev, REQ_IRQ_ERR_ALL, 0); + diff --git a/target/linux/generic/backport-6.1/775-v6.0-03-net-ethernet-stmicro-stmmac-move-dma-conf-to-dedicat.patch b/target/linux/generic/backport-6.1/775-v6.0-03-net-ethernet-stmicro-stmmac-move-dma-conf-to-dedicat.patch new file mode 100644 index 0000000000..ec3607feeb --- /dev/null +++ b/target/linux/generic/backport-6.1/775-v6.0-03-net-ethernet-stmicro-stmmac-move-dma-conf-to-dedicat.patch @@ -0,0 +1,1289 @@ +From 8531c80800c10e8ef7952022326c2f983e1314bf Mon Sep 17 00:00:00 2001 +From: Christian Marangi +Date: Sat, 23 Jul 2022 16:29:31 +0200 +Subject: [PATCH 3/5] net: ethernet: stmicro: stmmac: move dma conf to + dedicated struct + +Move dma buf conf to dedicated struct. This in preparation for code +rework that will permit to allocate separate dma_conf without affecting +the priv struct. + +Signed-off-by: Christian Marangi +Signed-off-by: Jakub Kicinski +--- + .../net/ethernet/stmicro/stmmac/chain_mode.c | 6 +- + .../net/ethernet/stmicro/stmmac/ring_mode.c | 4 +- + drivers/net/ethernet/stmicro/stmmac/stmmac.h | 21 +- + .../ethernet/stmicro/stmmac/stmmac_ethtool.c | 4 +- + .../net/ethernet/stmicro/stmmac/stmmac_main.c | 286 +++++++++--------- + .../stmicro/stmmac/stmmac_selftests.c | 8 +- + .../net/ethernet/stmicro/stmmac/stmmac_tc.c | 6 +- + 7 files changed, 172 insertions(+), 163 deletions(-) + +--- a/drivers/net/ethernet/stmicro/stmmac/chain_mode.c ++++ b/drivers/net/ethernet/stmicro/stmmac/chain_mode.c +@@ -46,7 +46,7 @@ static int jumbo_frm(void *p, struct sk_ + + while (len != 0) { + tx_q->tx_skbuff[entry] = NULL; +- entry = STMMAC_GET_ENTRY(entry, priv->dma_tx_size); ++ entry = STMMAC_GET_ENTRY(entry, priv->dma_conf.dma_tx_size); + desc = tx_q->dma_tx + entry; + + if (len > bmax) { +@@ -137,7 +137,7 @@ static void refill_desc3(void *priv_ptr, + */ + p->des3 = cpu_to_le32((unsigned int)(rx_q->dma_rx_phy + + (((rx_q->dirty_rx) + 1) % +- priv->dma_rx_size) * ++ priv->dma_conf.dma_rx_size) * + sizeof(struct dma_desc))); + } + +@@ -155,7 +155,7 @@ static void clean_desc3(void *priv_ptr, + */ + p->des3 = cpu_to_le32((unsigned int)((tx_q->dma_tx_phy + + ((tx_q->dirty_tx + 1) % +- priv->dma_tx_size)) ++ priv->dma_conf.dma_tx_size)) + * sizeof(struct dma_desc))); + } + +--- a/drivers/net/ethernet/stmicro/stmmac/ring_mode.c ++++ b/drivers/net/ethernet/stmicro/stmmac/ring_mode.c +@@ -51,7 +51,7 @@ static int jumbo_frm(void *p, struct sk_ + stmmac_prepare_tx_desc(priv, desc, 1, bmax, csum, + STMMAC_RING_MODE, 0, false, skb->len); + tx_q->tx_skbuff[entry] = NULL; +- entry = STMMAC_GET_ENTRY(entry, priv->dma_tx_size); ++ entry = STMMAC_GET_ENTRY(entry, priv->dma_conf.dma_tx_size); + + if (priv->extend_desc) + desc = (struct dma_desc *)(tx_q->dma_etx + entry); +@@ -107,7 +107,7 @@ static void refill_desc3(void *priv_ptr, + struct stmmac_priv *priv = rx_q->priv_data; + + /* Fill DES3 in case of RING mode */ +- if (priv->dma_buf_sz == BUF_SIZE_16KiB) ++ if (priv->dma_conf.dma_buf_sz == BUF_SIZE_16KiB) + p->des3 = cpu_to_le32(le32_to_cpu(p->des2) + BUF_SIZE_8KiB); + } + +--- a/drivers/net/ethernet/stmicro/stmmac/stmmac.h ++++ b/drivers/net/ethernet/stmicro/stmmac/stmmac.h +@@ -185,6 +185,18 @@ struct stmmac_rfs_entry { + int tc; + }; + ++struct stmmac_dma_conf { ++ unsigned int dma_buf_sz; ++ ++ /* RX Queue */ ++ struct stmmac_rx_queue rx_queue[MTL_MAX_RX_QUEUES]; ++ unsigned int dma_rx_size; ++ ++ /* TX Queue */ ++ struct stmmac_tx_queue tx_queue[MTL_MAX_TX_QUEUES]; ++ unsigned int dma_tx_size; ++}; ++ + struct stmmac_priv { + /* Frequently used values are kept adjacent for cache effect */ + u32 tx_coal_frames[MTL_MAX_TX_QUEUES]; +@@ -199,7 +211,6 @@ struct stmmac_priv { + int sph_cap; + u32 sarc_type; + +- unsigned int dma_buf_sz; + unsigned int rx_copybreak; + u32 rx_riwt[MTL_MAX_TX_QUEUES]; + int hwts_rx_en; +@@ -211,13 +222,7 @@ struct stmmac_priv { + int (*hwif_quirks)(struct stmmac_priv *priv); + struct mutex lock; + +- /* RX Queue */ +- struct stmmac_rx_queue rx_queue[MTL_MAX_RX_QUEUES]; +- unsigned int dma_rx_size; +- +- /* TX Queue */ +- struct stmmac_tx_queue tx_queue[MTL_MAX_TX_QUEUES]; +- unsigned int dma_tx_size; ++ struct stmmac_dma_conf dma_conf; + + /* Generic channel for NAPI */ + struct stmmac_channel channel[STMMAC_CH_MAX]; +--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c ++++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c +@@ -484,8 +484,8 @@ static void stmmac_get_ringparam(struct + + ring->rx_max_pending = DMA_MAX_RX_SIZE; + ring->tx_max_pending = DMA_MAX_TX_SIZE; +- ring->rx_pending = priv->dma_rx_size; +- ring->tx_pending = priv->dma_tx_size; ++ ring->rx_pending = priv->dma_conf.dma_rx_size; ++ ring->tx_pending = priv->dma_conf.dma_tx_size; + } + + static int stmmac_set_ringparam(struct net_device *netdev, +--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c ++++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +@@ -74,8 +74,8 @@ static int phyaddr = -1; + module_param(phyaddr, int, 0444); + MODULE_PARM_DESC(phyaddr, "Physical device address"); + +-#define STMMAC_TX_THRESH(x) ((x)->dma_tx_size / 4) +-#define STMMAC_RX_THRESH(x) ((x)->dma_rx_size / 4) ++#define STMMAC_TX_THRESH(x) ((x)->dma_conf.dma_tx_size / 4) ++#define STMMAC_RX_THRESH(x) ((x)->dma_conf.dma_rx_size / 4) + + /* Limit to make sure XDP TX and slow path can coexist */ + #define STMMAC_XSK_TX_BUDGET_MAX 256 +@@ -232,7 +232,7 @@ static void stmmac_disable_all_queues(st + + /* synchronize_rcu() needed for pending XDP buffers to drain */ + for (queue = 0; queue < rx_queues_cnt; queue++) { +- rx_q = &priv->rx_queue[queue]; ++ rx_q = &priv->dma_conf.rx_queue[queue]; + if (rx_q->xsk_pool) { + synchronize_rcu(); + break; +@@ -358,13 +358,13 @@ static void print_pkt(unsigned char *buf + + static inline u32 stmmac_tx_avail(struct stmmac_priv *priv, u32 queue) + { +- struct stmmac_tx_queue *tx_q = &priv->tx_queue[queue]; ++ struct stmmac_tx_queue *tx_q = &priv->dma_conf.tx_queue[queue]; + u32 avail; + + if (tx_q->dirty_tx > tx_q->cur_tx) + avail = tx_q->dirty_tx - tx_q->cur_tx - 1; + else +- avail = priv->dma_tx_size - tx_q->cur_tx + tx_q->dirty_tx - 1; ++ avail = priv->dma_conf.dma_tx_size - tx_q->cur_tx + tx_q->dirty_tx - 1; + + return avail; + } +@@ -376,13 +376,13 @@ static inline u32 stmmac_tx_avail(struct + */ + static inline u32 stmmac_rx_dirty(struct stmmac_priv *priv, u32 queue) + { +- struct stmmac_rx_queue *rx_q = &priv->rx_queue[queue]; ++ struct stmmac_rx_queue *rx_q = &priv->dma_conf.rx_queue[queue]; + u32 dirty; + + if (rx_q->dirty_rx <= rx_q->cur_rx) + dirty = rx_q->cur_rx - rx_q->dirty_rx; + else +- dirty = priv->dma_rx_size - rx_q->dirty_rx + rx_q->cur_rx; ++ dirty = priv->dma_conf.dma_rx_size - rx_q->dirty_rx + rx_q->cur_rx; + + return dirty; + } +@@ -410,7 +410,7 @@ static int stmmac_enable_eee_mode(struct + + /* check if all TX queues have the work finished */ + for (queue = 0; queue < tx_cnt; queue++) { +- struct stmmac_tx_queue *tx_q = &priv->tx_queue[queue]; ++ struct stmmac_tx_queue *tx_q = &priv->dma_conf.tx_queue[queue]; + + if (tx_q->dirty_tx != tx_q->cur_tx) + return -EBUSY; /* still unfinished work */ +@@ -1294,7 +1294,7 @@ static void stmmac_display_rx_rings(stru + + /* Display RX rings */ + for (queue = 0; queue < rx_cnt; queue++) { +- struct stmmac_rx_queue *rx_q = &priv->rx_queue[queue]; ++ struct stmmac_rx_queue *rx_q = &priv->dma_conf.rx_queue[queue]; + + pr_info("\tRX Queue %u rings\n", queue); + +@@ -1307,7 +1307,7 @@ static void stmmac_display_rx_rings(stru + } + + /* Display RX ring */ +- stmmac_display_ring(priv, head_rx, priv->dma_rx_size, true, ++ stmmac_display_ring(priv, head_rx, priv->dma_conf.dma_rx_size, true, + rx_q->dma_rx_phy, desc_size); + } + } +@@ -1321,7 +1321,7 @@ static void stmmac_display_tx_rings(stru + + /* Display TX rings */ + for (queue = 0; queue < tx_cnt; queue++) { +- struct stmmac_tx_queue *tx_q = &priv->tx_queue[queue]; ++ struct stmmac_tx_queue *tx_q = &priv->dma_conf.tx_queue[queue]; + + pr_info("\tTX Queue %d rings\n", queue); + +@@ -1336,7 +1336,7 @@ static void stmmac_display_tx_rings(stru + desc_size = sizeof(struct dma_desc); + } + +- stmmac_display_ring(priv, head_tx, priv->dma_tx_size, false, ++ stmmac_display_ring(priv, head_tx, priv->dma_conf.dma_tx_size, false, + tx_q->dma_tx_phy, desc_size); + } + } +@@ -1377,21 +1377,21 @@ static int stmmac_set_bfsize(int mtu, in + */ + static void stmmac_clear_rx_descriptors(struct stmmac_priv *priv, u32 queue) + { +- struct stmmac_rx_queue *rx_q = &priv->rx_queue[queue]; ++ struct stmmac_rx_queue *rx_q = &priv->dma_conf.rx_queue[queue]; + int i; + + /* Clear the RX descriptors */ +- for (i = 0; i < priv->dma_rx_size; i++) ++ for (i = 0; i < priv->dma_conf.dma_rx_size; i++) + if (priv->extend_desc) + stmmac_init_rx_desc(priv, &rx_q->dma_erx[i].basic, + priv->use_riwt, priv->mode, +- (i == priv->dma_rx_size - 1), +- priv->dma_buf_sz); ++ (i == priv->dma_conf.dma_rx_size - 1), ++ priv->dma_conf.dma_buf_sz); + else + stmmac_init_rx_desc(priv, &rx_q->dma_rx[i], + priv->use_riwt, priv->mode, +- (i == priv->dma_rx_size - 1), +- priv->dma_buf_sz); ++ (i == priv->dma_conf.dma_rx_size - 1), ++ priv->dma_conf.dma_buf_sz); + } + + /** +@@ -1403,12 +1403,12 @@ static void stmmac_clear_rx_descriptors( + */ + static void stmmac_clear_tx_descriptors(struct stmmac_priv *priv, u32 queue) + { +- struct stmmac_tx_queue *tx_q = &priv->tx_queue[queue]; ++ struct stmmac_tx_queue *tx_q = &priv->dma_conf.tx_queue[queue]; + int i; + + /* Clear the TX descriptors */ +- for (i = 0; i < priv->dma_tx_size; i++) { +- int last = (i == (priv->dma_tx_size - 1)); ++ for (i = 0; i < priv->dma_conf.dma_tx_size; i++) { ++ int last = (i == (priv->dma_conf.dma_tx_size - 1)); + struct dma_desc *p; + + if (priv->extend_desc) +@@ -1456,7 +1456,7 @@ static void stmmac_clear_descriptors(str + static int stmmac_init_rx_buffers(struct stmmac_priv *priv, struct dma_desc *p, + int i, gfp_t flags, u32 queue) + { +- struct stmmac_rx_queue *rx_q = &priv->rx_queue[queue]; ++ struct stmmac_rx_queue *rx_q = &priv->dma_conf.rx_queue[queue]; + struct stmmac_rx_buffer *buf = &rx_q->buf_pool[i]; + + if (!buf->page) { +@@ -1481,7 +1481,7 @@ static int stmmac_init_rx_buffers(struct + buf->addr = page_pool_get_dma_addr(buf->page) + buf->page_offset; + + stmmac_set_desc_addr(priv, p, buf->addr); +- if (priv->dma_buf_sz == BUF_SIZE_16KiB) ++ if (priv->dma_conf.dma_buf_sz == BUF_SIZE_16KiB) + stmmac_init_desc3(priv, p); + + return 0; +@@ -1495,7 +1495,7 @@ static int stmmac_init_rx_buffers(struct + */ + static void stmmac_free_rx_buffer(struct stmmac_priv *priv, u32 queue, int i) + { +- struct stmmac_rx_queue *rx_q = &priv->rx_queue[queue]; ++ struct stmmac_rx_queue *rx_q = &priv->dma_conf.rx_queue[queue]; + struct stmmac_rx_buffer *buf = &rx_q->buf_pool[i]; + + if (buf->page) +@@ -1515,7 +1515,7 @@ static void stmmac_free_rx_buffer(struct + */ + static void stmmac_free_tx_buffer(struct stmmac_priv *priv, u32 queue, int i) + { +- struct stmmac_tx_queue *tx_q = &priv->tx_queue[queue]; ++ struct stmmac_tx_queue *tx_q = &priv->dma_conf.tx_queue[queue]; + + if (tx_q->tx_skbuff_dma[i].buf && + tx_q->tx_skbuff_dma[i].buf_type != STMMAC_TXBUF_T_XDP_TX) { +@@ -1560,17 +1560,17 @@ static void dma_free_rx_skbufs(struct st + { + int i; + +- for (i = 0; i < priv->dma_rx_size; i++) ++ for (i = 0; i < priv->dma_conf.dma_rx_size; i++) + stmmac_free_rx_buffer(priv, queue, i); + } + + static int stmmac_alloc_rx_buffers(struct stmmac_priv *priv, u32 queue, + gfp_t flags) + { +- struct stmmac_rx_queue *rx_q = &priv->rx_queue[queue]; ++ struct stmmac_rx_queue *rx_q = &priv->dma_conf.rx_queue[queue]; + int i; + +- for (i = 0; i < priv->dma_rx_size; i++) { ++ for (i = 0; i < priv->dma_conf.dma_rx_size; i++) { + struct dma_desc *p; + int ret; + +@@ -1597,10 +1597,10 @@ static int stmmac_alloc_rx_buffers(struc + */ + static void dma_free_rx_xskbufs(struct stmmac_priv *priv, u32 queue) + { +- struct stmmac_rx_queue *rx_q = &priv->rx_queue[queue]; ++ struct stmmac_rx_queue *rx_q = &priv->dma_conf.rx_queue[queue]; + int i; + +- for (i = 0; i < priv->dma_rx_size; i++) { ++ for (i = 0; i < priv->dma_conf.dma_rx_size; i++) { + struct stmmac_rx_buffer *buf = &rx_q->buf_pool[i]; + + if (!buf->xdp) +@@ -1613,10 +1613,10 @@ static void dma_free_rx_xskbufs(struct s + + static int stmmac_alloc_rx_buffers_zc(struct stmmac_priv *priv, u32 queue) + { +- struct stmmac_rx_queue *rx_q = &priv->rx_queue[queue]; ++ struct stmmac_rx_queue *rx_q = &priv->dma_conf.rx_queue[queue]; + int i; + +- for (i = 0; i < priv->dma_rx_size; i++) { ++ for (i = 0; i < priv->dma_conf.dma_rx_size; i++) { + struct stmmac_rx_buffer *buf; + dma_addr_t dma_addr; + struct dma_desc *p; +@@ -1659,7 +1659,7 @@ static struct xsk_buff_pool *stmmac_get_ + */ + static int __init_dma_rx_desc_rings(struct stmmac_priv *priv, u32 queue, gfp_t flags) + { +- struct stmmac_rx_queue *rx_q = &priv->rx_queue[queue]; ++ struct stmmac_rx_queue *rx_q = &priv->dma_conf.rx_queue[queue]; + int ret; + + netif_dbg(priv, probe, priv->dev, +@@ -1705,11 +1705,11 @@ static int __init_dma_rx_desc_rings(stru + if (priv->extend_desc) + stmmac_mode_init(priv, rx_q->dma_erx, + rx_q->dma_rx_phy, +- priv->dma_rx_size, 1); ++ priv->dma_conf.dma_rx_size, 1); + else + stmmac_mode_init(priv, rx_q->dma_rx, + rx_q->dma_rx_phy, +- priv->dma_rx_size, 0); ++ priv->dma_conf.dma_rx_size, 0); + } + + return 0; +@@ -1736,7 +1736,7 @@ static int init_dma_rx_desc_rings(struct + + err_init_rx_buffers: + while (queue >= 0) { +- struct stmmac_rx_queue *rx_q = &priv->rx_queue[queue]; ++ struct stmmac_rx_queue *rx_q = &priv->dma_conf.rx_queue[queue]; + + if (rx_q->xsk_pool) + dma_free_rx_xskbufs(priv, queue); +@@ -1765,7 +1765,7 @@ err_init_rx_buffers: + */ + static int __init_dma_tx_desc_rings(struct stmmac_priv *priv, u32 queue) + { +- struct stmmac_tx_queue *tx_q = &priv->tx_queue[queue]; ++ struct stmmac_tx_queue *tx_q = &priv->dma_conf.tx_queue[queue]; + int i; + + netif_dbg(priv, probe, priv->dev, +@@ -1777,16 +1777,16 @@ static int __init_dma_tx_desc_rings(stru + if (priv->extend_desc) + stmmac_mode_init(priv, tx_q->dma_etx, + tx_q->dma_tx_phy, +- priv->dma_tx_size, 1); ++ priv->dma_conf.dma_tx_size, 1); + else if (!(tx_q->tbs & STMMAC_TBS_AVAIL)) + stmmac_mode_init(priv, tx_q->dma_tx, + tx_q->dma_tx_phy, +- priv->dma_tx_size, 0); ++ priv->dma_conf.dma_tx_size, 0); + } + + tx_q->xsk_pool = stmmac_get_xsk_pool(priv, queue); + +- for (i = 0; i < priv->dma_tx_size; i++) { ++ for (i = 0; i < priv->dma_conf.dma_tx_size; i++) { + struct dma_desc *p; + + if (priv->extend_desc) +@@ -1856,12 +1856,12 @@ static int init_dma_desc_rings(struct ne + */ + static void dma_free_tx_skbufs(struct stmmac_priv *priv, u32 queue) + { +- struct stmmac_tx_queue *tx_q = &priv->tx_queue[queue]; ++ struct stmmac_tx_queue *tx_q = &priv->dma_conf.tx_queue[queue]; + int i; + + tx_q->xsk_frames_done = 0; + +- for (i = 0; i < priv->dma_tx_size; i++) ++ for (i = 0; i < priv->dma_conf.dma_tx_size; i++) + stmmac_free_tx_buffer(priv, queue, i); + + if (tx_q->xsk_pool && tx_q->xsk_frames_done) { +@@ -1891,7 +1891,7 @@ static void stmmac_free_tx_skbufs(struct + */ + static void __free_dma_rx_desc_resources(struct stmmac_priv *priv, u32 queue) + { +- struct stmmac_rx_queue *rx_q = &priv->rx_queue[queue]; ++ struct stmmac_rx_queue *rx_q = &priv->dma_conf.rx_queue[queue]; + + /* Release the DMA RX socket buffers */ + if (rx_q->xsk_pool) +@@ -1904,11 +1904,11 @@ static void __free_dma_rx_desc_resources + + /* Free DMA regions of consistent memory previously allocated */ + if (!priv->extend_desc) +- dma_free_coherent(priv->device, priv->dma_rx_size * ++ dma_free_coherent(priv->device, priv->dma_conf.dma_rx_size * + sizeof(struct dma_desc), + rx_q->dma_rx, rx_q->dma_rx_phy); + else +- dma_free_coherent(priv->device, priv->dma_rx_size * ++ dma_free_coherent(priv->device, priv->dma_conf.dma_rx_size * + sizeof(struct dma_extended_desc), + rx_q->dma_erx, rx_q->dma_rx_phy); + +@@ -1937,7 +1937,7 @@ static void free_dma_rx_desc_resources(s + */ + static void __free_dma_tx_desc_resources(struct stmmac_priv *priv, u32 queue) + { +- struct stmmac_tx_queue *tx_q = &priv->tx_queue[queue]; ++ struct stmmac_tx_queue *tx_q = &priv->dma_conf.tx_queue[queue]; + size_t size; + void *addr; + +@@ -1955,7 +1955,7 @@ static void __free_dma_tx_desc_resources + addr = tx_q->dma_tx; + } + +- size *= priv->dma_tx_size; ++ size *= priv->dma_conf.dma_tx_size; + + dma_free_coherent(priv->device, size, addr, tx_q->dma_tx_phy); + +@@ -1984,7 +1984,7 @@ static void free_dma_tx_desc_resources(s + */ + static int __alloc_dma_rx_desc_resources(struct stmmac_priv *priv, u32 queue) + { +- struct stmmac_rx_queue *rx_q = &priv->rx_queue[queue]; ++ struct stmmac_rx_queue *rx_q = &priv->dma_conf.rx_queue[queue]; + struct stmmac_channel *ch = &priv->channel[queue]; + bool xdp_prog = stmmac_xdp_is_enabled(priv); + struct page_pool_params pp_params = { 0 }; +@@ -1996,8 +1996,8 @@ static int __alloc_dma_rx_desc_resources + rx_q->priv_data = priv; + + pp_params.flags = PP_FLAG_DMA_MAP | PP_FLAG_DMA_SYNC_DEV; +- pp_params.pool_size = priv->dma_rx_size; +- num_pages = DIV_ROUND_UP(priv->dma_buf_sz, PAGE_SIZE); ++ pp_params.pool_size = priv->dma_conf.dma_rx_size; ++ num_pages = DIV_ROUND_UP(priv->dma_conf.dma_buf_sz, PAGE_SIZE); + pp_params.order = ilog2(num_pages); + pp_params.nid = dev_to_node(priv->device); + pp_params.dev = priv->device; +@@ -2012,7 +2012,7 @@ static int __alloc_dma_rx_desc_resources + return ret; + } + +- rx_q->buf_pool = kcalloc(priv->dma_rx_size, ++ rx_q->buf_pool = kcalloc(priv->dma_conf.dma_rx_size, + sizeof(*rx_q->buf_pool), + GFP_KERNEL); + if (!rx_q->buf_pool) +@@ -2020,7 +2020,7 @@ static int __alloc_dma_rx_desc_resources + + if (priv->extend_desc) { + rx_q->dma_erx = dma_alloc_coherent(priv->device, +- priv->dma_rx_size * ++ priv->dma_conf.dma_rx_size * + sizeof(struct dma_extended_desc), + &rx_q->dma_rx_phy, + GFP_KERNEL); +@@ -2029,7 +2029,7 @@ static int __alloc_dma_rx_desc_resources + + } else { + rx_q->dma_rx = dma_alloc_coherent(priv->device, +- priv->dma_rx_size * ++ priv->dma_conf.dma_rx_size * + sizeof(struct dma_desc), + &rx_q->dma_rx_phy, + GFP_KERNEL); +@@ -2086,20 +2086,20 @@ err_dma: + */ + static int __alloc_dma_tx_desc_resources(struct stmmac_priv *priv, u32 queue) + { +- struct stmmac_tx_queue *tx_q = &priv->tx_queue[queue]; ++ struct stmmac_tx_queue *tx_q = &priv->dma_conf.tx_queue[queue]; + size_t size; + void *addr; + + tx_q->queue_index = queue; + tx_q->priv_data = priv; + +- tx_q->tx_skbuff_dma = kcalloc(priv->dma_tx_size, ++ tx_q->tx_skbuff_dma = kcalloc(priv->dma_conf.dma_tx_size, + sizeof(*tx_q->tx_skbuff_dma), + GFP_KERNEL); + if (!tx_q->tx_skbuff_dma) + return -ENOMEM; + +- tx_q->tx_skbuff = kcalloc(priv->dma_tx_size, ++ tx_q->tx_skbuff = kcalloc(priv->dma_conf.dma_tx_size, + sizeof(struct sk_buff *), + GFP_KERNEL); + if (!tx_q->tx_skbuff) +@@ -2112,7 +2112,7 @@ static int __alloc_dma_tx_desc_resources + else + size = sizeof(struct dma_desc); + +- size *= priv->dma_tx_size; ++ size *= priv->dma_conf.dma_tx_size; + + addr = dma_alloc_coherent(priv->device, size, + &tx_q->dma_tx_phy, GFP_KERNEL); +@@ -2356,7 +2356,7 @@ static void stmmac_dma_operation_mode(st + + /* configure all channels */ + for (chan = 0; chan < rx_channels_count; chan++) { +- struct stmmac_rx_queue *rx_q = &priv->rx_queue[chan]; ++ struct stmmac_rx_queue *rx_q = &priv->dma_conf.rx_queue[chan]; + u32 buf_size; + + qmode = priv->plat->rx_queues_cfg[chan].mode_to_use; +@@ -2371,7 +2371,7 @@ static void stmmac_dma_operation_mode(st + chan); + } else { + stmmac_set_dma_bfsize(priv, priv->ioaddr, +- priv->dma_buf_sz, ++ priv->dma_conf.dma_buf_sz, + chan); + } + } +@@ -2387,7 +2387,7 @@ static void stmmac_dma_operation_mode(st + static bool stmmac_xdp_xmit_zc(struct stmmac_priv *priv, u32 queue, u32 budget) + { + struct netdev_queue *nq = netdev_get_tx_queue(priv->dev, queue); +- struct stmmac_tx_queue *tx_q = &priv->tx_queue[queue]; ++ struct stmmac_tx_queue *tx_q = &priv->dma_conf.tx_queue[queue]; + struct xsk_buff_pool *pool = tx_q->xsk_pool; + unsigned int entry = tx_q->cur_tx; + struct dma_desc *tx_desc = NULL; +@@ -2462,7 +2462,7 @@ static bool stmmac_xdp_xmit_zc(struct st + + stmmac_enable_dma_transmission(priv, priv->ioaddr); + +- tx_q->cur_tx = STMMAC_GET_ENTRY(tx_q->cur_tx, priv->dma_tx_size); ++ tx_q->cur_tx = STMMAC_GET_ENTRY(tx_q->cur_tx, priv->dma_conf.dma_tx_size); + entry = tx_q->cur_tx; + } + +@@ -2488,7 +2488,7 @@ static bool stmmac_xdp_xmit_zc(struct st + */ + static int stmmac_tx_clean(struct stmmac_priv *priv, int budget, u32 queue) + { +- struct stmmac_tx_queue *tx_q = &priv->tx_queue[queue]; ++ struct stmmac_tx_queue *tx_q = &priv->dma_conf.tx_queue[queue]; + unsigned int bytes_compl = 0, pkts_compl = 0; + unsigned int entry, xmits = 0, count = 0; + +@@ -2501,7 +2501,7 @@ static int stmmac_tx_clean(struct stmmac + entry = tx_q->dirty_tx; + + /* Try to clean all TX complete frame in 1 shot */ +- while ((entry != tx_q->cur_tx) && count < priv->dma_tx_size) { ++ while ((entry != tx_q->cur_tx) && count < priv->dma_conf.dma_tx_size) { + struct xdp_frame *xdpf; + struct sk_buff *skb; + struct dma_desc *p; +@@ -2601,7 +2601,7 @@ static int stmmac_tx_clean(struct stmmac + + stmmac_release_tx_desc(priv, p, priv->mode); + +- entry = STMMAC_GET_ENTRY(entry, priv->dma_tx_size); ++ entry = STMMAC_GET_ENTRY(entry, priv->dma_conf.dma_tx_size); + } + tx_q->dirty_tx = entry; + +@@ -2666,7 +2666,7 @@ static int stmmac_tx_clean(struct stmmac + */ + static void stmmac_tx_err(struct stmmac_priv *priv, u32 chan) + { +- struct stmmac_tx_queue *tx_q = &priv->tx_queue[chan]; ++ struct stmmac_tx_queue *tx_q = &priv->dma_conf.tx_queue[chan]; + + netif_tx_stop_queue(netdev_get_tx_queue(priv->dev, chan)); + +@@ -2733,8 +2733,8 @@ static int stmmac_napi_check(struct stmm + { + int status = stmmac_dma_interrupt_status(priv, priv->ioaddr, + &priv->xstats, chan, dir); +- struct stmmac_rx_queue *rx_q = &priv->rx_queue[chan]; +- struct stmmac_tx_queue *tx_q = &priv->tx_queue[chan]; ++ struct stmmac_rx_queue *rx_q = &priv->dma_conf.rx_queue[chan]; ++ struct stmmac_tx_queue *tx_q = &priv->dma_conf.tx_queue[chan]; + struct stmmac_channel *ch = &priv->channel[chan]; + struct napi_struct *rx_napi; + struct napi_struct *tx_napi; +@@ -2910,7 +2910,7 @@ static int stmmac_init_dma_engine(struct + + /* DMA RX Channel Configuration */ + for (chan = 0; chan < rx_channels_count; chan++) { +- rx_q = &priv->rx_queue[chan]; ++ rx_q = &priv->dma_conf.rx_queue[chan]; + + stmmac_init_rx_chan(priv, priv->ioaddr, priv->plat->dma_cfg, + rx_q->dma_rx_phy, chan); +@@ -2924,7 +2924,7 @@ static int stmmac_init_dma_engine(struct + + /* DMA TX Channel Configuration */ + for (chan = 0; chan < tx_channels_count; chan++) { +- tx_q = &priv->tx_queue[chan]; ++ tx_q = &priv->dma_conf.tx_queue[chan]; + + stmmac_init_tx_chan(priv, priv->ioaddr, priv->plat->dma_cfg, + tx_q->dma_tx_phy, chan); +@@ -2939,7 +2939,7 @@ static int stmmac_init_dma_engine(struct + + static void stmmac_tx_timer_arm(struct stmmac_priv *priv, u32 queue) + { +- struct stmmac_tx_queue *tx_q = &priv->tx_queue[queue]; ++ struct stmmac_tx_queue *tx_q = &priv->dma_conf.tx_queue[queue]; + + hrtimer_start(&tx_q->txtimer, + STMMAC_COAL_TIMER(priv->tx_coal_timer[queue]), +@@ -2989,7 +2989,7 @@ static void stmmac_init_coalesce(struct + u32 chan; + + for (chan = 0; chan < tx_channel_count; chan++) { +- struct stmmac_tx_queue *tx_q = &priv->tx_queue[chan]; ++ struct stmmac_tx_queue *tx_q = &priv->dma_conf.tx_queue[chan]; + + priv->tx_coal_frames[chan] = STMMAC_TX_FRAMES; + priv->tx_coal_timer[chan] = STMMAC_COAL_TX_TIMER; +@@ -3011,12 +3011,12 @@ static void stmmac_set_rings_length(stru + /* set TX ring length */ + for (chan = 0; chan < tx_channels_count; chan++) + stmmac_set_tx_ring_len(priv, priv->ioaddr, +- (priv->dma_tx_size - 1), chan); ++ (priv->dma_conf.dma_tx_size - 1), chan); + + /* set RX ring length */ + for (chan = 0; chan < rx_channels_count; chan++) + stmmac_set_rx_ring_len(priv, priv->ioaddr, +- (priv->dma_rx_size - 1), chan); ++ (priv->dma_conf.dma_rx_size - 1), chan); + } + + /** +@@ -3351,7 +3351,7 @@ static int stmmac_hw_setup(struct net_de + /* Enable TSO */ + if (priv->tso) { + for (chan = 0; chan < tx_cnt; chan++) { +- struct stmmac_tx_queue *tx_q = &priv->tx_queue[chan]; ++ struct stmmac_tx_queue *tx_q = &priv->dma_conf.tx_queue[chan]; + + /* TSO and TBS cannot co-exist */ + if (tx_q->tbs & STMMAC_TBS_AVAIL) +@@ -3373,7 +3373,7 @@ static int stmmac_hw_setup(struct net_de + + /* TBS */ + for (chan = 0; chan < tx_cnt; chan++) { +- struct stmmac_tx_queue *tx_q = &priv->tx_queue[chan]; ++ struct stmmac_tx_queue *tx_q = &priv->dma_conf.tx_queue[chan]; + int enable = tx_q->tbs & STMMAC_TBS_AVAIL; + + stmmac_enable_tbs(priv, priv->ioaddr, enable, chan); +@@ -3417,7 +3417,7 @@ static void stmmac_free_irq(struct net_d + for (j = irq_idx - 1; j >= 0; j--) { + if (priv->tx_irq[j] > 0) { + irq_set_affinity_hint(priv->tx_irq[j], NULL); +- free_irq(priv->tx_irq[j], &priv->tx_queue[j]); ++ free_irq(priv->tx_irq[j], &priv->dma_conf.tx_queue[j]); + } + } + irq_idx = priv->plat->rx_queues_to_use; +@@ -3426,7 +3426,7 @@ static void stmmac_free_irq(struct net_d + for (j = irq_idx - 1; j >= 0; j--) { + if (priv->rx_irq[j] > 0) { + irq_set_affinity_hint(priv->rx_irq[j], NULL); +- free_irq(priv->rx_irq[j], &priv->rx_queue[j]); ++ free_irq(priv->rx_irq[j], &priv->dma_conf.rx_queue[j]); + } + } + +@@ -3559,7 +3559,7 @@ static int stmmac_request_irq_multi_msi( + sprintf(int_name, "%s:%s-%d", dev->name, "rx", i); + ret = request_irq(priv->rx_irq[i], + stmmac_msi_intr_rx, +- 0, int_name, &priv->rx_queue[i]); ++ 0, int_name, &priv->dma_conf.rx_queue[i]); + if (unlikely(ret < 0)) { + netdev_err(priv->dev, + "%s: alloc rx-%d MSI %d (error: %d)\n", +@@ -3582,7 +3582,7 @@ static int stmmac_request_irq_multi_msi( + sprintf(int_name, "%s:%s-%d", dev->name, "tx", i); + ret = request_irq(priv->tx_irq[i], + stmmac_msi_intr_tx, +- 0, int_name, &priv->tx_queue[i]); ++ 0, int_name, &priv->dma_conf.tx_queue[i]); + if (unlikely(ret < 0)) { + netdev_err(priv->dev, + "%s: alloc tx-%d MSI %d (error: %d)\n", +@@ -3713,21 +3713,21 @@ static int stmmac_open(struct net_device + bfsize = 0; + + if (bfsize < BUF_SIZE_16KiB) +- bfsize = stmmac_set_bfsize(dev->mtu, priv->dma_buf_sz); ++ bfsize = stmmac_set_bfsize(dev->mtu, priv->dma_conf.dma_buf_sz); + +- priv->dma_buf_sz = bfsize; ++ priv->dma_conf.dma_buf_sz = bfsize; + buf_sz = bfsize; + + priv->rx_copybreak = STMMAC_RX_COPYBREAK; + +- if (!priv->dma_tx_size) +- priv->dma_tx_size = DMA_DEFAULT_TX_SIZE; +- if (!priv->dma_rx_size) +- priv->dma_rx_size = DMA_DEFAULT_RX_SIZE; ++ if (!priv->dma_conf.dma_tx_size) ++ priv->dma_conf.dma_tx_size = DMA_DEFAULT_TX_SIZE; ++ if (!priv->dma_conf.dma_rx_size) ++ priv->dma_conf.dma_rx_size = DMA_DEFAULT_RX_SIZE; + + /* Earlier check for TBS */ + for (chan = 0; chan < priv->plat->tx_queues_to_use; chan++) { +- struct stmmac_tx_queue *tx_q = &priv->tx_queue[chan]; ++ struct stmmac_tx_queue *tx_q = &priv->dma_conf.tx_queue[chan]; + int tbs_en = priv->plat->tx_queues_cfg[chan].tbs_en; + + /* Setup per-TXQ tbs flag before TX descriptor alloc */ +@@ -3785,7 +3785,7 @@ irq_error: + phylink_stop(priv->phylink); + + for (chan = 0; chan < priv->plat->tx_queues_to_use; chan++) +- hrtimer_cancel(&priv->tx_queue[chan].txtimer); ++ hrtimer_cancel(&priv->dma_conf.tx_queue[chan].txtimer); + + stmmac_hw_teardown(dev); + init_error: +@@ -3827,7 +3827,7 @@ static int stmmac_release(struct net_dev + stmmac_disable_all_queues(priv); + + for (chan = 0; chan < priv->plat->tx_queues_to_use; chan++) +- hrtimer_cancel(&priv->tx_queue[chan].txtimer); ++ hrtimer_cancel(&priv->dma_conf.tx_queue[chan].txtimer); + + netif_tx_disable(dev); + +@@ -3891,7 +3891,7 @@ static bool stmmac_vlan_insert(struct st + return false; + + stmmac_set_tx_owner(priv, p); +- tx_q->cur_tx = STMMAC_GET_ENTRY(tx_q->cur_tx, priv->dma_tx_size); ++ tx_q->cur_tx = STMMAC_GET_ENTRY(tx_q->cur_tx, priv->dma_conf.dma_tx_size); + return true; + } + +@@ -3909,7 +3909,7 @@ static bool stmmac_vlan_insert(struct st + static void stmmac_tso_allocator(struct stmmac_priv *priv, dma_addr_t des, + int total_len, bool last_segment, u32 queue) + { +- struct stmmac_tx_queue *tx_q = &priv->tx_queue[queue]; ++ struct stmmac_tx_queue *tx_q = &priv->dma_conf.tx_queue[queue]; + struct dma_desc *desc; + u32 buff_size; + int tmp_len; +@@ -3920,7 +3920,7 @@ static void stmmac_tso_allocator(struct + dma_addr_t curr_addr; + + tx_q->cur_tx = STMMAC_GET_ENTRY(tx_q->cur_tx, +- priv->dma_tx_size); ++ priv->dma_conf.dma_tx_size); + WARN_ON(tx_q->tx_skbuff[tx_q->cur_tx]); + + if (tx_q->tbs & STMMAC_TBS_AVAIL) +@@ -3948,7 +3948,7 @@ static void stmmac_tso_allocator(struct + + static void stmmac_flush_tx_descriptors(struct stmmac_priv *priv, int queue) + { +- struct stmmac_tx_queue *tx_q = &priv->tx_queue[queue]; ++ struct stmmac_tx_queue *tx_q = &priv->dma_conf.tx_queue[queue]; + int desc_size; + + if (likely(priv->extend_desc)) +@@ -4010,7 +4010,7 @@ static netdev_tx_t stmmac_tso_xmit(struc + dma_addr_t des; + int i; + +- tx_q = &priv->tx_queue[queue]; ++ tx_q = &priv->dma_conf.tx_queue[queue]; + first_tx = tx_q->cur_tx; + + /* Compute header lengths */ +@@ -4050,7 +4050,7 @@ static netdev_tx_t stmmac_tso_xmit(struc + stmmac_set_mss(priv, mss_desc, mss); + tx_q->mss = mss; + tx_q->cur_tx = STMMAC_GET_ENTRY(tx_q->cur_tx, +- priv->dma_tx_size); ++ priv->dma_conf.dma_tx_size); + WARN_ON(tx_q->tx_skbuff[tx_q->cur_tx]); + } + +@@ -4162,7 +4162,7 @@ static netdev_tx_t stmmac_tso_xmit(struc + * ndo_start_xmit will fill this descriptor the next time it's + * called and stmmac_tx_clean may clean up to this descriptor. + */ +- tx_q->cur_tx = STMMAC_GET_ENTRY(tx_q->cur_tx, priv->dma_tx_size); ++ tx_q->cur_tx = STMMAC_GET_ENTRY(tx_q->cur_tx, priv->dma_conf.dma_tx_size); + + if (unlikely(stmmac_tx_avail(priv, queue) <= (MAX_SKB_FRAGS + 1))) { + netif_dbg(priv, hw, priv->dev, "%s: stop transmitted packets\n", +@@ -4250,7 +4250,7 @@ static netdev_tx_t stmmac_xmit(struct sk + int entry, first_tx; + dma_addr_t des; + +- tx_q = &priv->tx_queue[queue]; ++ tx_q = &priv->dma_conf.tx_queue[queue]; + first_tx = tx_q->cur_tx; + + if (priv->tx_path_in_lpi_mode && priv->eee_sw_timer_en) +@@ -4313,7 +4313,7 @@ static netdev_tx_t stmmac_xmit(struct sk + int len = skb_frag_size(frag); + bool last_segment = (i == (nfrags - 1)); + +- entry = STMMAC_GET_ENTRY(entry, priv->dma_tx_size); ++ entry = STMMAC_GET_ENTRY(entry, priv->dma_conf.dma_tx_size); + WARN_ON(tx_q->tx_skbuff[entry]); + + if (likely(priv->extend_desc)) +@@ -4384,7 +4384,7 @@ static netdev_tx_t stmmac_xmit(struct sk + * ndo_start_xmit will fill this descriptor the next time it's + * called and stmmac_tx_clean may clean up to this descriptor. + */ +- entry = STMMAC_GET_ENTRY(entry, priv->dma_tx_size); ++ entry = STMMAC_GET_ENTRY(entry, priv->dma_conf.dma_tx_size); + tx_q->cur_tx = entry; + + if (netif_msg_pktdata(priv)) { +@@ -4499,7 +4499,7 @@ static void stmmac_rx_vlan(struct net_de + */ + static inline void stmmac_rx_refill(struct stmmac_priv *priv, u32 queue) + { +- struct stmmac_rx_queue *rx_q = &priv->rx_queue[queue]; ++ struct stmmac_rx_queue *rx_q = &priv->dma_conf.rx_queue[queue]; + int dirty = stmmac_rx_dirty(priv, queue); + unsigned int entry = rx_q->dirty_rx; + +@@ -4549,7 +4549,7 @@ static inline void stmmac_rx_refill(stru + dma_wmb(); + stmmac_set_rx_owner(priv, p, use_rx_wd); + +- entry = STMMAC_GET_ENTRY(entry, priv->dma_rx_size); ++ entry = STMMAC_GET_ENTRY(entry, priv->dma_conf.dma_rx_size); + } + rx_q->dirty_rx = entry; + rx_q->rx_tail_addr = rx_q->dma_rx_phy + +@@ -4577,12 +4577,12 @@ static unsigned int stmmac_rx_buf1_len(s + + /* First descriptor, not last descriptor and not split header */ + if (status & rx_not_ls) +- return priv->dma_buf_sz; ++ return priv->dma_conf.dma_buf_sz; + + plen = stmmac_get_rx_frame_len(priv, p, coe); + + /* First descriptor and last descriptor and not split header */ +- return min_t(unsigned int, priv->dma_buf_sz, plen); ++ return min_t(unsigned int, priv->dma_conf.dma_buf_sz, plen); + } + + static unsigned int stmmac_rx_buf2_len(struct stmmac_priv *priv, +@@ -4598,7 +4598,7 @@ static unsigned int stmmac_rx_buf2_len(s + + /* Not last descriptor */ + if (status & rx_not_ls) +- return priv->dma_buf_sz; ++ return priv->dma_conf.dma_buf_sz; + + plen = stmmac_get_rx_frame_len(priv, p, coe); + +@@ -4609,7 +4609,7 @@ static unsigned int stmmac_rx_buf2_len(s + static int stmmac_xdp_xmit_xdpf(struct stmmac_priv *priv, int queue, + struct xdp_frame *xdpf, bool dma_map) + { +- struct stmmac_tx_queue *tx_q = &priv->tx_queue[queue]; ++ struct stmmac_tx_queue *tx_q = &priv->dma_conf.tx_queue[queue]; + unsigned int entry = tx_q->cur_tx; + struct dma_desc *tx_desc; + dma_addr_t dma_addr; +@@ -4672,7 +4672,7 @@ static int stmmac_xdp_xmit_xdpf(struct s + + stmmac_enable_dma_transmission(priv, priv->ioaddr); + +- entry = STMMAC_GET_ENTRY(entry, priv->dma_tx_size); ++ entry = STMMAC_GET_ENTRY(entry, priv->dma_conf.dma_tx_size); + tx_q->cur_tx = entry; + + return STMMAC_XDP_TX; +@@ -4846,7 +4846,7 @@ static void stmmac_dispatch_skb_zc(struc + + static bool stmmac_rx_refill_zc(struct stmmac_priv *priv, u32 queue, u32 budget) + { +- struct stmmac_rx_queue *rx_q = &priv->rx_queue[queue]; ++ struct stmmac_rx_queue *rx_q = &priv->dma_conf.rx_queue[queue]; + unsigned int entry = rx_q->dirty_rx; + struct dma_desc *rx_desc = NULL; + bool ret = true; +@@ -4889,7 +4889,7 @@ static bool stmmac_rx_refill_zc(struct s + dma_wmb(); + stmmac_set_rx_owner(priv, rx_desc, use_rx_wd); + +- entry = STMMAC_GET_ENTRY(entry, priv->dma_rx_size); ++ entry = STMMAC_GET_ENTRY(entry, priv->dma_conf.dma_rx_size); + } + + if (rx_desc) { +@@ -4904,7 +4904,7 @@ static bool stmmac_rx_refill_zc(struct s + + static int stmmac_rx_zc(struct stmmac_priv *priv, int limit, u32 queue) + { +- struct stmmac_rx_queue *rx_q = &priv->rx_queue[queue]; ++ struct stmmac_rx_queue *rx_q = &priv->dma_conf.rx_queue[queue]; + unsigned int count = 0, error = 0, len = 0; + int dirty = stmmac_rx_dirty(priv, queue); + unsigned int next_entry = rx_q->cur_rx; +@@ -4926,7 +4926,7 @@ static int stmmac_rx_zc(struct stmmac_pr + desc_size = sizeof(struct dma_desc); + } + +- stmmac_display_ring(priv, rx_head, priv->dma_rx_size, true, ++ stmmac_display_ring(priv, rx_head, priv->dma_conf.dma_rx_size, true, + rx_q->dma_rx_phy, desc_size); + } + while (count < limit) { +@@ -4973,7 +4973,7 @@ read_again: + + /* Prefetch the next RX descriptor */ + rx_q->cur_rx = STMMAC_GET_ENTRY(rx_q->cur_rx, +- priv->dma_rx_size); ++ priv->dma_conf.dma_rx_size); + next_entry = rx_q->cur_rx; + + if (priv->extend_desc) +@@ -5094,7 +5094,7 @@ read_again: + */ + static int stmmac_rx(struct stmmac_priv *priv, int limit, u32 queue) + { +- struct stmmac_rx_queue *rx_q = &priv->rx_queue[queue]; ++ struct stmmac_rx_queue *rx_q = &priv->dma_conf.rx_queue[queue]; + struct stmmac_channel *ch = &priv->channel[queue]; + unsigned int count = 0, error = 0, len = 0; + int status = 0, coe = priv->hw->rx_csum; +@@ -5107,7 +5107,7 @@ static int stmmac_rx(struct stmmac_priv + int buf_sz; + + dma_dir = page_pool_get_dma_dir(rx_q->page_pool); +- buf_sz = DIV_ROUND_UP(priv->dma_buf_sz, PAGE_SIZE) * PAGE_SIZE; ++ buf_sz = DIV_ROUND_UP(priv->dma_conf.dma_buf_sz, PAGE_SIZE) * PAGE_SIZE; + + if (netif_msg_rx_status(priv)) { + void *rx_head; +@@ -5121,7 +5121,7 @@ static int stmmac_rx(struct stmmac_priv + desc_size = sizeof(struct dma_desc); + } + +- stmmac_display_ring(priv, rx_head, priv->dma_rx_size, true, ++ stmmac_display_ring(priv, rx_head, priv->dma_conf.dma_rx_size, true, + rx_q->dma_rx_phy, desc_size); + } + while (count < limit) { +@@ -5165,7 +5165,7 @@ read_again: + break; + + rx_q->cur_rx = STMMAC_GET_ENTRY(rx_q->cur_rx, +- priv->dma_rx_size); ++ priv->dma_conf.dma_rx_size); + next_entry = rx_q->cur_rx; + + if (priv->extend_desc) +@@ -5299,7 +5299,7 @@ read_again: + buf1_len, dma_dir); + skb_add_rx_frag(skb, skb_shinfo(skb)->nr_frags, + buf->page, buf->page_offset, buf1_len, +- priv->dma_buf_sz); ++ priv->dma_conf.dma_buf_sz); + + /* Data payload appended into SKB */ + page_pool_release_page(rx_q->page_pool, buf->page); +@@ -5311,7 +5311,7 @@ read_again: + buf2_len, dma_dir); + skb_add_rx_frag(skb, skb_shinfo(skb)->nr_frags, + buf->sec_page, 0, buf2_len, +- priv->dma_buf_sz); ++ priv->dma_conf.dma_buf_sz); + + /* Data payload appended into SKB */ + page_pool_release_page(rx_q->page_pool, buf->sec_page); +@@ -5753,11 +5753,13 @@ static irqreturn_t stmmac_safety_interru + static irqreturn_t stmmac_msi_intr_tx(int irq, void *data) + { + struct stmmac_tx_queue *tx_q = (struct stmmac_tx_queue *)data; ++ struct stmmac_dma_conf *dma_conf; + int chan = tx_q->queue_index; + struct stmmac_priv *priv; + int status; + +- priv = container_of(tx_q, struct stmmac_priv, tx_queue[chan]); ++ dma_conf = container_of(tx_q, struct stmmac_dma_conf, tx_queue[chan]); ++ priv = container_of(dma_conf, struct stmmac_priv, dma_conf); + + if (unlikely(!data)) { + netdev_err(priv->dev, "%s: invalid dev pointer\n", __func__); +@@ -5797,10 +5799,12 @@ static irqreturn_t stmmac_msi_intr_tx(in + static irqreturn_t stmmac_msi_intr_rx(int irq, void *data) + { + struct stmmac_rx_queue *rx_q = (struct stmmac_rx_queue *)data; ++ struct stmmac_dma_conf *dma_conf; + int chan = rx_q->queue_index; + struct stmmac_priv *priv; + +- priv = container_of(rx_q, struct stmmac_priv, rx_queue[chan]); ++ dma_conf = container_of(rx_q, struct stmmac_dma_conf, rx_queue[chan]); ++ priv = container_of(dma_conf, struct stmmac_priv, dma_conf); + + if (unlikely(!data)) { + netdev_err(priv->dev, "%s: invalid dev pointer\n", __func__); +@@ -5831,10 +5835,10 @@ static void stmmac_poll_controller(struc + + if (priv->plat->multi_msi_en) { + for (i = 0; i < priv->plat->rx_queues_to_use; i++) +- stmmac_msi_intr_rx(0, &priv->rx_queue[i]); ++ stmmac_msi_intr_rx(0, &priv->dma_conf.rx_queue[i]); + + for (i = 0; i < priv->plat->tx_queues_to_use; i++) +- stmmac_msi_intr_tx(0, &priv->tx_queue[i]); ++ stmmac_msi_intr_tx(0, &priv->dma_conf.tx_queue[i]); + } else { + disable_irq(dev->irq); + stmmac_interrupt(dev->irq, dev); +@@ -6015,34 +6019,34 @@ static int stmmac_rings_status_show(stru + return 0; + + for (queue = 0; queue < rx_count; queue++) { +- struct stmmac_rx_queue *rx_q = &priv->rx_queue[queue]; ++ struct stmmac_rx_queue *rx_q = &priv->dma_conf.rx_queue[queue]; + + seq_printf(seq, "RX Queue %d:\n", queue); + + if (priv->extend_desc) { + seq_printf(seq, "Extended descriptor ring:\n"); + sysfs_display_ring((void *)rx_q->dma_erx, +- priv->dma_rx_size, 1, seq, rx_q->dma_rx_phy); ++ priv->dma_conf.dma_rx_size, 1, seq, rx_q->dma_rx_phy); + } else { + seq_printf(seq, "Descriptor ring:\n"); + sysfs_display_ring((void *)rx_q->dma_rx, +- priv->dma_rx_size, 0, seq, rx_q->dma_rx_phy); ++ priv->dma_conf.dma_rx_size, 0, seq, rx_q->dma_rx_phy); + } + } + + for (queue = 0; queue < tx_count; queue++) { +- struct stmmac_tx_queue *tx_q = &priv->tx_queue[queue]; ++ struct stmmac_tx_queue *tx_q = &priv->dma_conf.tx_queue[queue]; + + seq_printf(seq, "TX Queue %d:\n", queue); + + if (priv->extend_desc) { + seq_printf(seq, "Extended descriptor ring:\n"); + sysfs_display_ring((void *)tx_q->dma_etx, +- priv->dma_tx_size, 1, seq, tx_q->dma_tx_phy); ++ priv->dma_conf.dma_tx_size, 1, seq, tx_q->dma_tx_phy); + } else if (!(tx_q->tbs & STMMAC_TBS_AVAIL)) { + seq_printf(seq, "Descriptor ring:\n"); + sysfs_display_ring((void *)tx_q->dma_tx, +- priv->dma_tx_size, 0, seq, tx_q->dma_tx_phy); ++ priv->dma_conf.dma_tx_size, 0, seq, tx_q->dma_tx_phy); + } + } + +@@ -6383,7 +6387,7 @@ void stmmac_disable_rx_queue(struct stmm + + void stmmac_enable_rx_queue(struct stmmac_priv *priv, u32 queue) + { +- struct stmmac_rx_queue *rx_q = &priv->rx_queue[queue]; ++ struct stmmac_rx_queue *rx_q = &priv->dma_conf.rx_queue[queue]; + struct stmmac_channel *ch = &priv->channel[queue]; + unsigned long flags; + u32 buf_size; +@@ -6420,7 +6424,7 @@ void stmmac_enable_rx_queue(struct stmma + rx_q->queue_index); + } else { + stmmac_set_dma_bfsize(priv, priv->ioaddr, +- priv->dma_buf_sz, ++ priv->dma_conf.dma_buf_sz, + rx_q->queue_index); + } + +@@ -6446,7 +6450,7 @@ void stmmac_disable_tx_queue(struct stmm + + void stmmac_enable_tx_queue(struct stmmac_priv *priv, u32 queue) + { +- struct stmmac_tx_queue *tx_q = &priv->tx_queue[queue]; ++ struct stmmac_tx_queue *tx_q = &priv->dma_conf.tx_queue[queue]; + struct stmmac_channel *ch = &priv->channel[queue]; + unsigned long flags; + int ret; +@@ -6493,7 +6497,7 @@ void stmmac_xdp_release(struct net_devic + stmmac_disable_all_queues(priv); + + for (chan = 0; chan < priv->plat->tx_queues_to_use; chan++) +- hrtimer_cancel(&priv->tx_queue[chan].txtimer); ++ hrtimer_cancel(&priv->dma_conf.tx_queue[chan].txtimer); + + /* Free the IRQ lines */ + stmmac_free_irq(dev, REQ_IRQ_ERR_ALL, 0); +@@ -6552,7 +6556,7 @@ int stmmac_xdp_open(struct net_device *d + + /* DMA RX Channel Configuration */ + for (chan = 0; chan < rx_cnt; chan++) { +- rx_q = &priv->rx_queue[chan]; ++ rx_q = &priv->dma_conf.rx_queue[chan]; + + stmmac_init_rx_chan(priv, priv->ioaddr, priv->plat->dma_cfg, + rx_q->dma_rx_phy, chan); +@@ -6570,7 +6574,7 @@ int stmmac_xdp_open(struct net_device *d + rx_q->queue_index); + } else { + stmmac_set_dma_bfsize(priv, priv->ioaddr, +- priv->dma_buf_sz, ++ priv->dma_conf.dma_buf_sz, + rx_q->queue_index); + } + +@@ -6579,7 +6583,7 @@ int stmmac_xdp_open(struct net_device *d + + /* DMA TX Channel Configuration */ + for (chan = 0; chan < tx_cnt; chan++) { +- tx_q = &priv->tx_queue[chan]; ++ tx_q = &priv->dma_conf.tx_queue[chan]; + + stmmac_init_tx_chan(priv, priv->ioaddr, priv->plat->dma_cfg, + tx_q->dma_tx_phy, chan); +@@ -6612,7 +6616,7 @@ int stmmac_xdp_open(struct net_device *d + + irq_error: + for (chan = 0; chan < priv->plat->tx_queues_to_use; chan++) +- hrtimer_cancel(&priv->tx_queue[chan].txtimer); ++ hrtimer_cancel(&priv->dma_conf.tx_queue[chan].txtimer); + + stmmac_hw_teardown(dev); + init_error: +@@ -6639,8 +6643,8 @@ int stmmac_xsk_wakeup(struct net_device + queue >= priv->plat->tx_queues_to_use) + return -EINVAL; + +- rx_q = &priv->rx_queue[queue]; +- tx_q = &priv->tx_queue[queue]; ++ rx_q = &priv->dma_conf.rx_queue[queue]; ++ tx_q = &priv->dma_conf.tx_queue[queue]; + ch = &priv->channel[queue]; + + if (!rx_q->xsk_pool && !tx_q->xsk_pool) +@@ -6896,8 +6900,8 @@ int stmmac_reinit_ringparam(struct net_d + if (netif_running(dev)) + stmmac_release(dev); + +- priv->dma_rx_size = rx_size; +- priv->dma_tx_size = tx_size; ++ priv->dma_conf.dma_rx_size = rx_size; ++ priv->dma_conf.dma_tx_size = tx_size; + + if (netif_running(dev)) + ret = stmmac_open(dev); +@@ -7333,7 +7337,7 @@ int stmmac_suspend(struct device *dev) + stmmac_disable_all_queues(priv); + + for (chan = 0; chan < priv->plat->tx_queues_to_use; chan++) +- hrtimer_cancel(&priv->tx_queue[chan].txtimer); ++ hrtimer_cancel(&priv->dma_conf.tx_queue[chan].txtimer); + + if (priv->eee_enabled) { + priv->tx_path_in_lpi_mode = false; +@@ -7384,7 +7388,7 @@ EXPORT_SYMBOL_GPL(stmmac_suspend); + + static void stmmac_reset_rx_queue(struct stmmac_priv *priv, u32 queue) + { +- struct stmmac_rx_queue *rx_q = &priv->rx_queue[queue]; ++ struct stmmac_rx_queue *rx_q = &priv->dma_conf.rx_queue[queue]; + + rx_q->cur_rx = 0; + rx_q->dirty_rx = 0; +@@ -7392,7 +7396,7 @@ static void stmmac_reset_rx_queue(struct + + static void stmmac_reset_tx_queue(struct stmmac_priv *priv, u32 queue) + { +- struct stmmac_tx_queue *tx_q = &priv->tx_queue[queue]; ++ struct stmmac_tx_queue *tx_q = &priv->dma_conf.tx_queue[queue]; + + tx_q->cur_tx = 0; + tx_q->dirty_tx = 0; +--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_selftests.c ++++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_selftests.c +@@ -795,8 +795,8 @@ static int stmmac_test_flowctrl(struct s + struct stmmac_channel *ch = &priv->channel[i]; + u32 tail; + +- tail = priv->rx_queue[i].dma_rx_phy + +- (priv->dma_rx_size * sizeof(struct dma_desc)); ++ tail = priv->dma_conf.rx_queue[i].dma_rx_phy + ++ (priv->dma_conf.dma_rx_size * sizeof(struct dma_desc)); + + stmmac_set_rx_tail_ptr(priv, priv->ioaddr, tail, i); + stmmac_start_rx(priv, priv->ioaddr, i); +@@ -1680,7 +1680,7 @@ cleanup: + static int __stmmac_test_jumbo(struct stmmac_priv *priv, u16 queue) + { + struct stmmac_packet_attrs attr = { }; +- int size = priv->dma_buf_sz; ++ int size = priv->dma_conf.dma_buf_sz; + + attr.dst = priv->dev->dev_addr; + attr.max_size = size - ETH_FCS_LEN; +@@ -1763,7 +1763,7 @@ static int stmmac_test_tbs(struct stmmac + + /* Find first TBS enabled Queue, if any */ + for (i = 0; i < priv->plat->tx_queues_to_use; i++) +- if (priv->tx_queue[i].tbs & STMMAC_TBS_AVAIL) ++ if (priv->dma_conf.tx_queue[i].tbs & STMMAC_TBS_AVAIL) + break; + + if (i >= priv->plat->tx_queues_to_use) +--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_tc.c ++++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_tc.c +@@ -970,13 +970,13 @@ static int tc_setup_etf(struct stmmac_pr + return -EOPNOTSUPP; + if (qopt->queue >= priv->plat->tx_queues_to_use) + return -EINVAL; +- if (!(priv->tx_queue[qopt->queue].tbs & STMMAC_TBS_AVAIL)) ++ if (!(priv->dma_conf.tx_queue[qopt->queue].tbs & STMMAC_TBS_AVAIL)) + return -EINVAL; + + if (qopt->enable) +- priv->tx_queue[qopt->queue].tbs |= STMMAC_TBS_EN; ++ priv->dma_conf.tx_queue[qopt->queue].tbs |= STMMAC_TBS_EN; + else +- priv->tx_queue[qopt->queue].tbs &= ~STMMAC_TBS_EN; ++ priv->dma_conf.tx_queue[qopt->queue].tbs &= ~STMMAC_TBS_EN; + + netdev_info(priv->dev, "%s ETF for Queue %d\n", + qopt->enable ? "enabled" : "disabled", qopt->queue); diff --git a/target/linux/generic/backport-6.1/775-v6.0-04-net-ethernet-stmicro-stmmac-generate-stmmac-dma-conf.patch b/target/linux/generic/backport-6.1/775-v6.0-04-net-ethernet-stmicro-stmmac-generate-stmmac-dma-conf.patch new file mode 100644 index 0000000000..0635693d55 --- /dev/null +++ b/target/linux/generic/backport-6.1/775-v6.0-04-net-ethernet-stmicro-stmmac-generate-stmmac-dma-conf.patch @@ -0,0 +1,1161 @@ +From ba39b344e9240a4a5fd4ab8178200b85cd1809da Mon Sep 17 00:00:00 2001 +From: Christian Marangi +Date: Sat, 23 Jul 2022 16:29:32 +0200 +Subject: [PATCH 4/5] net: ethernet: stmicro: stmmac: generate stmmac dma conf + before open + +Rework the driver to generate the stmmac dma_conf before stmmac_open. +This permits a function to first check if it's possible to allocate a +new dma_config and then pass it directly to __stmmac_open and "open" the +interface with the new configuration. + +Signed-off-by: Christian Marangi +Signed-off-by: Jakub Kicinski +--- + .../net/ethernet/stmicro/stmmac/stmmac_main.c | 462 +++++++++++------- + 1 file changed, 289 insertions(+), 173 deletions(-) + +--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c ++++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +@@ -1285,7 +1285,8 @@ static int stmmac_phy_setup(struct stmma + return 0; + } + +-static void stmmac_display_rx_rings(struct stmmac_priv *priv) ++static void stmmac_display_rx_rings(struct stmmac_priv *priv, ++ struct stmmac_dma_conf *dma_conf) + { + u32 rx_cnt = priv->plat->rx_queues_to_use; + unsigned int desc_size; +@@ -1294,7 +1295,7 @@ static void stmmac_display_rx_rings(stru + + /* Display RX rings */ + for (queue = 0; queue < rx_cnt; queue++) { +- struct stmmac_rx_queue *rx_q = &priv->dma_conf.rx_queue[queue]; ++ struct stmmac_rx_queue *rx_q = &dma_conf->rx_queue[queue]; + + pr_info("\tRX Queue %u rings\n", queue); + +@@ -1307,12 +1308,13 @@ static void stmmac_display_rx_rings(stru + } + + /* Display RX ring */ +- stmmac_display_ring(priv, head_rx, priv->dma_conf.dma_rx_size, true, ++ stmmac_display_ring(priv, head_rx, dma_conf->dma_rx_size, true, + rx_q->dma_rx_phy, desc_size); + } + } + +-static void stmmac_display_tx_rings(struct stmmac_priv *priv) ++static void stmmac_display_tx_rings(struct stmmac_priv *priv, ++ struct stmmac_dma_conf *dma_conf) + { + u32 tx_cnt = priv->plat->tx_queues_to_use; + unsigned int desc_size; +@@ -1321,7 +1323,7 @@ static void stmmac_display_tx_rings(stru + + /* Display TX rings */ + for (queue = 0; queue < tx_cnt; queue++) { +- struct stmmac_tx_queue *tx_q = &priv->dma_conf.tx_queue[queue]; ++ struct stmmac_tx_queue *tx_q = &dma_conf->tx_queue[queue]; + + pr_info("\tTX Queue %d rings\n", queue); + +@@ -1336,18 +1338,19 @@ static void stmmac_display_tx_rings(stru + desc_size = sizeof(struct dma_desc); + } + +- stmmac_display_ring(priv, head_tx, priv->dma_conf.dma_tx_size, false, ++ stmmac_display_ring(priv, head_tx, dma_conf->dma_tx_size, false, + tx_q->dma_tx_phy, desc_size); + } + } + +-static void stmmac_display_rings(struct stmmac_priv *priv) ++static void stmmac_display_rings(struct stmmac_priv *priv, ++ struct stmmac_dma_conf *dma_conf) + { + /* Display RX ring */ +- stmmac_display_rx_rings(priv); ++ stmmac_display_rx_rings(priv, dma_conf); + + /* Display TX ring */ +- stmmac_display_tx_rings(priv); ++ stmmac_display_tx_rings(priv, dma_conf); + } + + static int stmmac_set_bfsize(int mtu, int bufsize) +@@ -1371,44 +1374,50 @@ static int stmmac_set_bfsize(int mtu, in + /** + * stmmac_clear_rx_descriptors - clear RX descriptors + * @priv: driver private structure ++ * @dma_conf: structure to take the dma data + * @queue: RX queue index + * Description: this function is called to clear the RX descriptors + * in case of both basic and extended descriptors are used. + */ +-static void stmmac_clear_rx_descriptors(struct stmmac_priv *priv, u32 queue) ++static void stmmac_clear_rx_descriptors(struct stmmac_priv *priv, ++ struct stmmac_dma_conf *dma_conf, ++ u32 queue) + { +- struct stmmac_rx_queue *rx_q = &priv->dma_conf.rx_queue[queue]; ++ struct stmmac_rx_queue *rx_q = &dma_conf->rx_queue[queue]; + int i; + + /* Clear the RX descriptors */ +- for (i = 0; i < priv->dma_conf.dma_rx_size; i++) ++ for (i = 0; i < dma_conf->dma_rx_size; i++) + if (priv->extend_desc) + stmmac_init_rx_desc(priv, &rx_q->dma_erx[i].basic, + priv->use_riwt, priv->mode, +- (i == priv->dma_conf.dma_rx_size - 1), +- priv->dma_conf.dma_buf_sz); ++ (i == dma_conf->dma_rx_size - 1), ++ dma_conf->dma_buf_sz); + else + stmmac_init_rx_desc(priv, &rx_q->dma_rx[i], + priv->use_riwt, priv->mode, +- (i == priv->dma_conf.dma_rx_size - 1), +- priv->dma_conf.dma_buf_sz); ++ (i == dma_conf->dma_rx_size - 1), ++ dma_conf->dma_buf_sz); + } + + /** + * stmmac_clear_tx_descriptors - clear tx descriptors + * @priv: driver private structure ++ * @dma_conf: structure to take the dma data + * @queue: TX queue index. + * Description: this function is called to clear the TX descriptors + * in case of both basic and extended descriptors are used. + */ +-static void stmmac_clear_tx_descriptors(struct stmmac_priv *priv, u32 queue) ++static void stmmac_clear_tx_descriptors(struct stmmac_priv *priv, ++ struct stmmac_dma_conf *dma_conf, ++ u32 queue) + { +- struct stmmac_tx_queue *tx_q = &priv->dma_conf.tx_queue[queue]; ++ struct stmmac_tx_queue *tx_q = &dma_conf->tx_queue[queue]; + int i; + + /* Clear the TX descriptors */ +- for (i = 0; i < priv->dma_conf.dma_tx_size; i++) { +- int last = (i == (priv->dma_conf.dma_tx_size - 1)); ++ for (i = 0; i < dma_conf->dma_tx_size; i++) { ++ int last = (i == (dma_conf->dma_tx_size - 1)); + struct dma_desc *p; + + if (priv->extend_desc) +@@ -1425,10 +1434,12 @@ static void stmmac_clear_tx_descriptors( + /** + * stmmac_clear_descriptors - clear descriptors + * @priv: driver private structure ++ * @dma_conf: structure to take the dma data + * Description: this function is called to clear the TX and RX descriptors + * in case of both basic and extended descriptors are used. + */ +-static void stmmac_clear_descriptors(struct stmmac_priv *priv) ++static void stmmac_clear_descriptors(struct stmmac_priv *priv, ++ struct stmmac_dma_conf *dma_conf) + { + u32 rx_queue_cnt = priv->plat->rx_queues_to_use; + u32 tx_queue_cnt = priv->plat->tx_queues_to_use; +@@ -1436,16 +1447,17 @@ static void stmmac_clear_descriptors(str + + /* Clear the RX descriptors */ + for (queue = 0; queue < rx_queue_cnt; queue++) +- stmmac_clear_rx_descriptors(priv, queue); ++ stmmac_clear_rx_descriptors(priv, dma_conf, queue); + + /* Clear the TX descriptors */ + for (queue = 0; queue < tx_queue_cnt; queue++) +- stmmac_clear_tx_descriptors(priv, queue); ++ stmmac_clear_tx_descriptors(priv, dma_conf, queue); + } + + /** + * stmmac_init_rx_buffers - init the RX descriptor buffer. + * @priv: driver private structure ++ * @dma_conf: structure to take the dma data + * @p: descriptor pointer + * @i: descriptor index + * @flags: gfp flag +@@ -1453,10 +1465,12 @@ static void stmmac_clear_descriptors(str + * Description: this function is called to allocate a receive buffer, perform + * the DMA mapping and init the descriptor. + */ +-static int stmmac_init_rx_buffers(struct stmmac_priv *priv, struct dma_desc *p, ++static int stmmac_init_rx_buffers(struct stmmac_priv *priv, ++ struct stmmac_dma_conf *dma_conf, ++ struct dma_desc *p, + int i, gfp_t flags, u32 queue) + { +- struct stmmac_rx_queue *rx_q = &priv->dma_conf.rx_queue[queue]; ++ struct stmmac_rx_queue *rx_q = &dma_conf->rx_queue[queue]; + struct stmmac_rx_buffer *buf = &rx_q->buf_pool[i]; + + if (!buf->page) { +@@ -1481,7 +1495,7 @@ static int stmmac_init_rx_buffers(struct + buf->addr = page_pool_get_dma_addr(buf->page) + buf->page_offset; + + stmmac_set_desc_addr(priv, p, buf->addr); +- if (priv->dma_conf.dma_buf_sz == BUF_SIZE_16KiB) ++ if (dma_conf->dma_buf_sz == BUF_SIZE_16KiB) + stmmac_init_desc3(priv, p); + + return 0; +@@ -1490,12 +1504,13 @@ static int stmmac_init_rx_buffers(struct + /** + * stmmac_free_rx_buffer - free RX dma buffers + * @priv: private structure +- * @queue: RX queue index ++ * @rx_q: RX queue + * @i: buffer index. + */ +-static void stmmac_free_rx_buffer(struct stmmac_priv *priv, u32 queue, int i) ++static void stmmac_free_rx_buffer(struct stmmac_priv *priv, ++ struct stmmac_rx_queue *rx_q, ++ int i) + { +- struct stmmac_rx_queue *rx_q = &priv->dma_conf.rx_queue[queue]; + struct stmmac_rx_buffer *buf = &rx_q->buf_pool[i]; + + if (buf->page) +@@ -1510,12 +1525,15 @@ static void stmmac_free_rx_buffer(struct + /** + * stmmac_free_tx_buffer - free RX dma buffers + * @priv: private structure ++ * @dma_conf: structure to take the dma data + * @queue: RX queue index + * @i: buffer index. + */ +-static void stmmac_free_tx_buffer(struct stmmac_priv *priv, u32 queue, int i) ++static void stmmac_free_tx_buffer(struct stmmac_priv *priv, ++ struct stmmac_dma_conf *dma_conf, ++ u32 queue, int i) + { +- struct stmmac_tx_queue *tx_q = &priv->dma_conf.tx_queue[queue]; ++ struct stmmac_tx_queue *tx_q = &dma_conf->tx_queue[queue]; + + if (tx_q->tx_skbuff_dma[i].buf && + tx_q->tx_skbuff_dma[i].buf_type != STMMAC_TXBUF_T_XDP_TX) { +@@ -1554,23 +1572,28 @@ static void stmmac_free_tx_buffer(struct + /** + * dma_free_rx_skbufs - free RX dma buffers + * @priv: private structure ++ * @dma_conf: structure to take the dma data + * @queue: RX queue index + */ +-static void dma_free_rx_skbufs(struct stmmac_priv *priv, u32 queue) ++static void dma_free_rx_skbufs(struct stmmac_priv *priv, ++ struct stmmac_dma_conf *dma_conf, ++ u32 queue) + { ++ struct stmmac_rx_queue *rx_q = &dma_conf->rx_queue[queue]; + int i; + +- for (i = 0; i < priv->dma_conf.dma_rx_size; i++) +- stmmac_free_rx_buffer(priv, queue, i); ++ for (i = 0; i < dma_conf->dma_rx_size; i++) ++ stmmac_free_rx_buffer(priv, rx_q, i); + } + +-static int stmmac_alloc_rx_buffers(struct stmmac_priv *priv, u32 queue, +- gfp_t flags) ++static int stmmac_alloc_rx_buffers(struct stmmac_priv *priv, ++ struct stmmac_dma_conf *dma_conf, ++ u32 queue, gfp_t flags) + { +- struct stmmac_rx_queue *rx_q = &priv->dma_conf.rx_queue[queue]; ++ struct stmmac_rx_queue *rx_q = &dma_conf->rx_queue[queue]; + int i; + +- for (i = 0; i < priv->dma_conf.dma_rx_size; i++) { ++ for (i = 0; i < dma_conf->dma_rx_size; i++) { + struct dma_desc *p; + int ret; + +@@ -1579,7 +1602,7 @@ static int stmmac_alloc_rx_buffers(struc + else + p = rx_q->dma_rx + i; + +- ret = stmmac_init_rx_buffers(priv, p, i, flags, ++ ret = stmmac_init_rx_buffers(priv, dma_conf, p, i, flags, + queue); + if (ret) + return ret; +@@ -1593,14 +1616,17 @@ static int stmmac_alloc_rx_buffers(struc + /** + * dma_free_rx_xskbufs - free RX dma buffers from XSK pool + * @priv: private structure ++ * @dma_conf: structure to take the dma data + * @queue: RX queue index + */ +-static void dma_free_rx_xskbufs(struct stmmac_priv *priv, u32 queue) ++static void dma_free_rx_xskbufs(struct stmmac_priv *priv, ++ struct stmmac_dma_conf *dma_conf, ++ u32 queue) + { +- struct stmmac_rx_queue *rx_q = &priv->dma_conf.rx_queue[queue]; ++ struct stmmac_rx_queue *rx_q = &dma_conf->rx_queue[queue]; + int i; + +- for (i = 0; i < priv->dma_conf.dma_rx_size; i++) { ++ for (i = 0; i < dma_conf->dma_rx_size; i++) { + struct stmmac_rx_buffer *buf = &rx_q->buf_pool[i]; + + if (!buf->xdp) +@@ -1611,12 +1637,14 @@ static void dma_free_rx_xskbufs(struct s + } + } + +-static int stmmac_alloc_rx_buffers_zc(struct stmmac_priv *priv, u32 queue) ++static int stmmac_alloc_rx_buffers_zc(struct stmmac_priv *priv, ++ struct stmmac_dma_conf *dma_conf, ++ u32 queue) + { +- struct stmmac_rx_queue *rx_q = &priv->dma_conf.rx_queue[queue]; ++ struct stmmac_rx_queue *rx_q = &dma_conf->rx_queue[queue]; + int i; + +- for (i = 0; i < priv->dma_conf.dma_rx_size; i++) { ++ for (i = 0; i < dma_conf->dma_rx_size; i++) { + struct stmmac_rx_buffer *buf; + dma_addr_t dma_addr; + struct dma_desc *p; +@@ -1651,22 +1679,25 @@ static struct xsk_buff_pool *stmmac_get_ + /** + * __init_dma_rx_desc_rings - init the RX descriptor ring (per queue) + * @priv: driver private structure ++ * @dma_conf: structure to take the dma data + * @queue: RX queue index + * @flags: gfp flag. + * Description: this function initializes the DMA RX descriptors + * and allocates the socket buffers. It supports the chained and ring + * modes. + */ +-static int __init_dma_rx_desc_rings(struct stmmac_priv *priv, u32 queue, gfp_t flags) ++static int __init_dma_rx_desc_rings(struct stmmac_priv *priv, ++ struct stmmac_dma_conf *dma_conf, ++ u32 queue, gfp_t flags) + { +- struct stmmac_rx_queue *rx_q = &priv->dma_conf.rx_queue[queue]; ++ struct stmmac_rx_queue *rx_q = &dma_conf->rx_queue[queue]; + int ret; + + netif_dbg(priv, probe, priv->dev, + "(%s) dma_rx_phy=0x%08x\n", __func__, + (u32)rx_q->dma_rx_phy); + +- stmmac_clear_rx_descriptors(priv, queue); ++ stmmac_clear_rx_descriptors(priv, dma_conf, queue); + + xdp_rxq_info_unreg_mem_model(&rx_q->xdp_rxq); + +@@ -1693,9 +1724,9 @@ static int __init_dma_rx_desc_rings(stru + /* RX XDP ZC buffer pool may not be populated, e.g. + * xdpsock TX-only. + */ +- stmmac_alloc_rx_buffers_zc(priv, queue); ++ stmmac_alloc_rx_buffers_zc(priv, dma_conf, queue); + } else { +- ret = stmmac_alloc_rx_buffers(priv, queue, flags); ++ ret = stmmac_alloc_rx_buffers(priv, dma_conf, queue, flags); + if (ret < 0) + return -ENOMEM; + } +@@ -1705,17 +1736,19 @@ static int __init_dma_rx_desc_rings(stru + if (priv->extend_desc) + stmmac_mode_init(priv, rx_q->dma_erx, + rx_q->dma_rx_phy, +- priv->dma_conf.dma_rx_size, 1); ++ dma_conf->dma_rx_size, 1); + else + stmmac_mode_init(priv, rx_q->dma_rx, + rx_q->dma_rx_phy, +- priv->dma_conf.dma_rx_size, 0); ++ dma_conf->dma_rx_size, 0); + } + + return 0; + } + +-static int init_dma_rx_desc_rings(struct net_device *dev, gfp_t flags) ++static int init_dma_rx_desc_rings(struct net_device *dev, ++ struct stmmac_dma_conf *dma_conf, ++ gfp_t flags) + { + struct stmmac_priv *priv = netdev_priv(dev); + u32 rx_count = priv->plat->rx_queues_to_use; +@@ -1727,7 +1760,7 @@ static int init_dma_rx_desc_rings(struct + "SKB addresses:\nskb\t\tskb data\tdma data\n"); + + for (queue = 0; queue < rx_count; queue++) { +- ret = __init_dma_rx_desc_rings(priv, queue, flags); ++ ret = __init_dma_rx_desc_rings(priv, dma_conf, queue, flags); + if (ret) + goto err_init_rx_buffers; + } +@@ -1736,12 +1769,12 @@ static int init_dma_rx_desc_rings(struct + + err_init_rx_buffers: + while (queue >= 0) { +- struct stmmac_rx_queue *rx_q = &priv->dma_conf.rx_queue[queue]; ++ struct stmmac_rx_queue *rx_q = &dma_conf->rx_queue[queue]; + + if (rx_q->xsk_pool) +- dma_free_rx_xskbufs(priv, queue); ++ dma_free_rx_xskbufs(priv, dma_conf, queue); + else +- dma_free_rx_skbufs(priv, queue); ++ dma_free_rx_skbufs(priv, dma_conf, queue); + + rx_q->buf_alloc_num = 0; + rx_q->xsk_pool = NULL; +@@ -1758,14 +1791,17 @@ err_init_rx_buffers: + /** + * __init_dma_tx_desc_rings - init the TX descriptor ring (per queue) + * @priv: driver private structure +- * @queue : TX queue index ++ * @dma_conf: structure to take the dma data ++ * @queue: TX queue index + * Description: this function initializes the DMA TX descriptors + * and allocates the socket buffers. It supports the chained and ring + * modes. + */ +-static int __init_dma_tx_desc_rings(struct stmmac_priv *priv, u32 queue) ++static int __init_dma_tx_desc_rings(struct stmmac_priv *priv, ++ struct stmmac_dma_conf *dma_conf, ++ u32 queue) + { +- struct stmmac_tx_queue *tx_q = &priv->dma_conf.tx_queue[queue]; ++ struct stmmac_tx_queue *tx_q = &dma_conf->tx_queue[queue]; + int i; + + netif_dbg(priv, probe, priv->dev, +@@ -1777,16 +1813,16 @@ static int __init_dma_tx_desc_rings(stru + if (priv->extend_desc) + stmmac_mode_init(priv, tx_q->dma_etx, + tx_q->dma_tx_phy, +- priv->dma_conf.dma_tx_size, 1); ++ dma_conf->dma_tx_size, 1); + else if (!(tx_q->tbs & STMMAC_TBS_AVAIL)) + stmmac_mode_init(priv, tx_q->dma_tx, + tx_q->dma_tx_phy, +- priv->dma_conf.dma_tx_size, 0); ++ dma_conf->dma_tx_size, 0); + } + + tx_q->xsk_pool = stmmac_get_xsk_pool(priv, queue); + +- for (i = 0; i < priv->dma_conf.dma_tx_size; i++) { ++ for (i = 0; i < dma_conf->dma_tx_size; i++) { + struct dma_desc *p; + + if (priv->extend_desc) +@@ -1808,7 +1844,8 @@ static int __init_dma_tx_desc_rings(stru + return 0; + } + +-static int init_dma_tx_desc_rings(struct net_device *dev) ++static int init_dma_tx_desc_rings(struct net_device *dev, ++ struct stmmac_dma_conf *dma_conf) + { + struct stmmac_priv *priv = netdev_priv(dev); + u32 tx_queue_cnt; +@@ -1817,7 +1854,7 @@ static int init_dma_tx_desc_rings(struct + tx_queue_cnt = priv->plat->tx_queues_to_use; + + for (queue = 0; queue < tx_queue_cnt; queue++) +- __init_dma_tx_desc_rings(priv, queue); ++ __init_dma_tx_desc_rings(priv, dma_conf, queue); + + return 0; + } +@@ -1825,26 +1862,29 @@ static int init_dma_tx_desc_rings(struct + /** + * init_dma_desc_rings - init the RX/TX descriptor rings + * @dev: net device structure ++ * @dma_conf: structure to take the dma data + * @flags: gfp flag. + * Description: this function initializes the DMA RX/TX descriptors + * and allocates the socket buffers. It supports the chained and ring + * modes. + */ +-static int init_dma_desc_rings(struct net_device *dev, gfp_t flags) ++static int init_dma_desc_rings(struct net_device *dev, ++ struct stmmac_dma_conf *dma_conf, ++ gfp_t flags) + { + struct stmmac_priv *priv = netdev_priv(dev); + int ret; + +- ret = init_dma_rx_desc_rings(dev, flags); ++ ret = init_dma_rx_desc_rings(dev, dma_conf, flags); + if (ret) + return ret; + +- ret = init_dma_tx_desc_rings(dev); ++ ret = init_dma_tx_desc_rings(dev, dma_conf); + +- stmmac_clear_descriptors(priv); ++ stmmac_clear_descriptors(priv, dma_conf); + + if (netif_msg_hw(priv)) +- stmmac_display_rings(priv); ++ stmmac_display_rings(priv, dma_conf); + + return ret; + } +@@ -1852,17 +1892,20 @@ static int init_dma_desc_rings(struct ne + /** + * dma_free_tx_skbufs - free TX dma buffers + * @priv: private structure ++ * @dma_conf: structure to take the dma data + * @queue: TX queue index + */ +-static void dma_free_tx_skbufs(struct stmmac_priv *priv, u32 queue) ++static void dma_free_tx_skbufs(struct stmmac_priv *priv, ++ struct stmmac_dma_conf *dma_conf, ++ u32 queue) + { +- struct stmmac_tx_queue *tx_q = &priv->dma_conf.tx_queue[queue]; ++ struct stmmac_tx_queue *tx_q = &dma_conf->tx_queue[queue]; + int i; + + tx_q->xsk_frames_done = 0; + +- for (i = 0; i < priv->dma_conf.dma_tx_size; i++) +- stmmac_free_tx_buffer(priv, queue, i); ++ for (i = 0; i < dma_conf->dma_tx_size; i++) ++ stmmac_free_tx_buffer(priv, dma_conf, queue, i); + + if (tx_q->xsk_pool && tx_q->xsk_frames_done) { + xsk_tx_completed(tx_q->xsk_pool, tx_q->xsk_frames_done); +@@ -1881,34 +1924,37 @@ static void stmmac_free_tx_skbufs(struct + u32 queue; + + for (queue = 0; queue < tx_queue_cnt; queue++) +- dma_free_tx_skbufs(priv, queue); ++ dma_free_tx_skbufs(priv, &priv->dma_conf, queue); + } + + /** + * __free_dma_rx_desc_resources - free RX dma desc resources (per queue) + * @priv: private structure ++ * @dma_conf: structure to take the dma data + * @queue: RX queue index + */ +-static void __free_dma_rx_desc_resources(struct stmmac_priv *priv, u32 queue) ++static void __free_dma_rx_desc_resources(struct stmmac_priv *priv, ++ struct stmmac_dma_conf *dma_conf, ++ u32 queue) + { +- struct stmmac_rx_queue *rx_q = &priv->dma_conf.rx_queue[queue]; ++ struct stmmac_rx_queue *rx_q = &dma_conf->rx_queue[queue]; + + /* Release the DMA RX socket buffers */ + if (rx_q->xsk_pool) +- dma_free_rx_xskbufs(priv, queue); ++ dma_free_rx_xskbufs(priv, dma_conf, queue); + else +- dma_free_rx_skbufs(priv, queue); ++ dma_free_rx_skbufs(priv, dma_conf, queue); + + rx_q->buf_alloc_num = 0; + rx_q->xsk_pool = NULL; + + /* Free DMA regions of consistent memory previously allocated */ + if (!priv->extend_desc) +- dma_free_coherent(priv->device, priv->dma_conf.dma_rx_size * ++ dma_free_coherent(priv->device, dma_conf->dma_rx_size * + sizeof(struct dma_desc), + rx_q->dma_rx, rx_q->dma_rx_phy); + else +- dma_free_coherent(priv->device, priv->dma_conf.dma_rx_size * ++ dma_free_coherent(priv->device, dma_conf->dma_rx_size * + sizeof(struct dma_extended_desc), + rx_q->dma_erx, rx_q->dma_rx_phy); + +@@ -1920,29 +1966,33 @@ static void __free_dma_rx_desc_resources + page_pool_destroy(rx_q->page_pool); + } + +-static void free_dma_rx_desc_resources(struct stmmac_priv *priv) ++static void free_dma_rx_desc_resources(struct stmmac_priv *priv, ++ struct stmmac_dma_conf *dma_conf) + { + u32 rx_count = priv->plat->rx_queues_to_use; + u32 queue; + + /* Free RX queue resources */ + for (queue = 0; queue < rx_count; queue++) +- __free_dma_rx_desc_resources(priv, queue); ++ __free_dma_rx_desc_resources(priv, dma_conf, queue); + } + + /** + * __free_dma_tx_desc_resources - free TX dma desc resources (per queue) + * @priv: private structure ++ * @dma_conf: structure to take the dma data + * @queue: TX queue index + */ +-static void __free_dma_tx_desc_resources(struct stmmac_priv *priv, u32 queue) ++static void __free_dma_tx_desc_resources(struct stmmac_priv *priv, ++ struct stmmac_dma_conf *dma_conf, ++ u32 queue) + { +- struct stmmac_tx_queue *tx_q = &priv->dma_conf.tx_queue[queue]; ++ struct stmmac_tx_queue *tx_q = &dma_conf->tx_queue[queue]; + size_t size; + void *addr; + + /* Release the DMA TX socket buffers */ +- dma_free_tx_skbufs(priv, queue); ++ dma_free_tx_skbufs(priv, dma_conf, queue); + + if (priv->extend_desc) { + size = sizeof(struct dma_extended_desc); +@@ -1955,7 +2005,7 @@ static void __free_dma_tx_desc_resources + addr = tx_q->dma_tx; + } + +- size *= priv->dma_conf.dma_tx_size; ++ size *= dma_conf->dma_tx_size; + + dma_free_coherent(priv->device, size, addr, tx_q->dma_tx_phy); + +@@ -1963,28 +2013,32 @@ static void __free_dma_tx_desc_resources + kfree(tx_q->tx_skbuff); + } + +-static void free_dma_tx_desc_resources(struct stmmac_priv *priv) ++static void free_dma_tx_desc_resources(struct stmmac_priv *priv, ++ struct stmmac_dma_conf *dma_conf) + { + u32 tx_count = priv->plat->tx_queues_to_use; + u32 queue; + + /* Free TX queue resources */ + for (queue = 0; queue < tx_count; queue++) +- __free_dma_tx_desc_resources(priv, queue); ++ __free_dma_tx_desc_resources(priv, dma_conf, queue); + } + + /** + * __alloc_dma_rx_desc_resources - alloc RX resources (per queue). + * @priv: private structure ++ * @dma_conf: structure to take the dma data + * @queue: RX queue index + * Description: according to which descriptor can be used (extend or basic) + * this function allocates the resources for TX and RX paths. In case of + * reception, for example, it pre-allocated the RX socket buffer in order to + * allow zero-copy mechanism. + */ +-static int __alloc_dma_rx_desc_resources(struct stmmac_priv *priv, u32 queue) ++static int __alloc_dma_rx_desc_resources(struct stmmac_priv *priv, ++ struct stmmac_dma_conf *dma_conf, ++ u32 queue) + { +- struct stmmac_rx_queue *rx_q = &priv->dma_conf.rx_queue[queue]; ++ struct stmmac_rx_queue *rx_q = &dma_conf->rx_queue[queue]; + struct stmmac_channel *ch = &priv->channel[queue]; + bool xdp_prog = stmmac_xdp_is_enabled(priv); + struct page_pool_params pp_params = { 0 }; +@@ -1996,8 +2050,8 @@ static int __alloc_dma_rx_desc_resources + rx_q->priv_data = priv; + + pp_params.flags = PP_FLAG_DMA_MAP | PP_FLAG_DMA_SYNC_DEV; +- pp_params.pool_size = priv->dma_conf.dma_rx_size; +- num_pages = DIV_ROUND_UP(priv->dma_conf.dma_buf_sz, PAGE_SIZE); ++ pp_params.pool_size = dma_conf->dma_rx_size; ++ num_pages = DIV_ROUND_UP(dma_conf->dma_buf_sz, PAGE_SIZE); + pp_params.order = ilog2(num_pages); + pp_params.nid = dev_to_node(priv->device); + pp_params.dev = priv->device; +@@ -2012,7 +2066,7 @@ static int __alloc_dma_rx_desc_resources + return ret; + } + +- rx_q->buf_pool = kcalloc(priv->dma_conf.dma_rx_size, ++ rx_q->buf_pool = kcalloc(dma_conf->dma_rx_size, + sizeof(*rx_q->buf_pool), + GFP_KERNEL); + if (!rx_q->buf_pool) +@@ -2020,7 +2074,7 @@ static int __alloc_dma_rx_desc_resources + + if (priv->extend_desc) { + rx_q->dma_erx = dma_alloc_coherent(priv->device, +- priv->dma_conf.dma_rx_size * ++ dma_conf->dma_rx_size * + sizeof(struct dma_extended_desc), + &rx_q->dma_rx_phy, + GFP_KERNEL); +@@ -2029,7 +2083,7 @@ static int __alloc_dma_rx_desc_resources + + } else { + rx_q->dma_rx = dma_alloc_coherent(priv->device, +- priv->dma_conf.dma_rx_size * ++ dma_conf->dma_rx_size * + sizeof(struct dma_desc), + &rx_q->dma_rx_phy, + GFP_KERNEL); +@@ -2054,7 +2108,8 @@ static int __alloc_dma_rx_desc_resources + return 0; + } + +-static int alloc_dma_rx_desc_resources(struct stmmac_priv *priv) ++static int alloc_dma_rx_desc_resources(struct stmmac_priv *priv, ++ struct stmmac_dma_conf *dma_conf) + { + u32 rx_count = priv->plat->rx_queues_to_use; + u32 queue; +@@ -2062,7 +2117,7 @@ static int alloc_dma_rx_desc_resources(s + + /* RX queues buffers and DMA */ + for (queue = 0; queue < rx_count; queue++) { +- ret = __alloc_dma_rx_desc_resources(priv, queue); ++ ret = __alloc_dma_rx_desc_resources(priv, dma_conf, queue); + if (ret) + goto err_dma; + } +@@ -2070,7 +2125,7 @@ static int alloc_dma_rx_desc_resources(s + return 0; + + err_dma: +- free_dma_rx_desc_resources(priv); ++ free_dma_rx_desc_resources(priv, dma_conf); + + return ret; + } +@@ -2078,28 +2133,31 @@ err_dma: + /** + * __alloc_dma_tx_desc_resources - alloc TX resources (per queue). + * @priv: private structure ++ * @dma_conf: structure to take the dma data + * @queue: TX queue index + * Description: according to which descriptor can be used (extend or basic) + * this function allocates the resources for TX and RX paths. In case of + * reception, for example, it pre-allocated the RX socket buffer in order to + * allow zero-copy mechanism. + */ +-static int __alloc_dma_tx_desc_resources(struct stmmac_priv *priv, u32 queue) ++static int __alloc_dma_tx_desc_resources(struct stmmac_priv *priv, ++ struct stmmac_dma_conf *dma_conf, ++ u32 queue) + { +- struct stmmac_tx_queue *tx_q = &priv->dma_conf.tx_queue[queue]; ++ struct stmmac_tx_queue *tx_q = &dma_conf->tx_queue[queue]; + size_t size; + void *addr; + + tx_q->queue_index = queue; + tx_q->priv_data = priv; + +- tx_q->tx_skbuff_dma = kcalloc(priv->dma_conf.dma_tx_size, ++ tx_q->tx_skbuff_dma = kcalloc(dma_conf->dma_tx_size, + sizeof(*tx_q->tx_skbuff_dma), + GFP_KERNEL); + if (!tx_q->tx_skbuff_dma) + return -ENOMEM; + +- tx_q->tx_skbuff = kcalloc(priv->dma_conf.dma_tx_size, ++ tx_q->tx_skbuff = kcalloc(dma_conf->dma_tx_size, + sizeof(struct sk_buff *), + GFP_KERNEL); + if (!tx_q->tx_skbuff) +@@ -2112,7 +2170,7 @@ static int __alloc_dma_tx_desc_resources + else + size = sizeof(struct dma_desc); + +- size *= priv->dma_conf.dma_tx_size; ++ size *= dma_conf->dma_tx_size; + + addr = dma_alloc_coherent(priv->device, size, + &tx_q->dma_tx_phy, GFP_KERNEL); +@@ -2129,7 +2187,8 @@ static int __alloc_dma_tx_desc_resources + return 0; + } + +-static int alloc_dma_tx_desc_resources(struct stmmac_priv *priv) ++static int alloc_dma_tx_desc_resources(struct stmmac_priv *priv, ++ struct stmmac_dma_conf *dma_conf) + { + u32 tx_count = priv->plat->tx_queues_to_use; + u32 queue; +@@ -2137,7 +2196,7 @@ static int alloc_dma_tx_desc_resources(s + + /* TX queues buffers and DMA */ + for (queue = 0; queue < tx_count; queue++) { +- ret = __alloc_dma_tx_desc_resources(priv, queue); ++ ret = __alloc_dma_tx_desc_resources(priv, dma_conf, queue); + if (ret) + goto err_dma; + } +@@ -2145,27 +2204,29 @@ static int alloc_dma_tx_desc_resources(s + return 0; + + err_dma: +- free_dma_tx_desc_resources(priv); ++ free_dma_tx_desc_resources(priv, dma_conf); + return ret; + } + + /** + * alloc_dma_desc_resources - alloc TX/RX resources. + * @priv: private structure ++ * @dma_conf: structure to take the dma data + * Description: according to which descriptor can be used (extend or basic) + * this function allocates the resources for TX and RX paths. In case of + * reception, for example, it pre-allocated the RX socket buffer in order to + * allow zero-copy mechanism. + */ +-static int alloc_dma_desc_resources(struct stmmac_priv *priv) ++static int alloc_dma_desc_resources(struct stmmac_priv *priv, ++ struct stmmac_dma_conf *dma_conf) + { + /* RX Allocation */ +- int ret = alloc_dma_rx_desc_resources(priv); ++ int ret = alloc_dma_rx_desc_resources(priv, dma_conf); + + if (ret) + return ret; + +- ret = alloc_dma_tx_desc_resources(priv); ++ ret = alloc_dma_tx_desc_resources(priv, dma_conf); + + return ret; + } +@@ -2173,16 +2234,18 @@ static int alloc_dma_desc_resources(stru + /** + * free_dma_desc_resources - free dma desc resources + * @priv: private structure ++ * @dma_conf: structure to take the dma data + */ +-static void free_dma_desc_resources(struct stmmac_priv *priv) ++static void free_dma_desc_resources(struct stmmac_priv *priv, ++ struct stmmac_dma_conf *dma_conf) + { + /* Release the DMA TX socket buffers */ +- free_dma_tx_desc_resources(priv); ++ free_dma_tx_desc_resources(priv, dma_conf); + + /* Release the DMA RX socket buffers later + * to ensure all pending XDP_TX buffers are returned. + */ +- free_dma_rx_desc_resources(priv); ++ free_dma_rx_desc_resources(priv, dma_conf); + } + + /** +@@ -2671,8 +2734,8 @@ static void stmmac_tx_err(struct stmmac_ + netif_tx_stop_queue(netdev_get_tx_queue(priv->dev, chan)); + + stmmac_stop_tx_dma(priv, chan); +- dma_free_tx_skbufs(priv, chan); +- stmmac_clear_tx_descriptors(priv, chan); ++ dma_free_tx_skbufs(priv, &priv->dma_conf, chan); ++ stmmac_clear_tx_descriptors(priv, &priv->dma_conf, chan); + stmmac_reset_tx_queue(priv, chan); + stmmac_init_tx_chan(priv, priv->ioaddr, priv->plat->dma_cfg, + tx_q->dma_tx_phy, chan); +@@ -3669,19 +3732,93 @@ static int stmmac_request_irq(struct net + } + + /** +- * stmmac_open - open entry point of the driver ++ * stmmac_setup_dma_desc - Generate a dma_conf and allocate DMA queue ++ * @priv: driver private structure ++ * @mtu: MTU to setup the dma queue and buf with ++ * Description: Allocate and generate a dma_conf based on the provided MTU. ++ * Allocate the Tx/Rx DMA queue and init them. ++ * Return value: ++ * the dma_conf allocated struct on success and an appropriate ERR_PTR on failure. ++ */ ++static struct stmmac_dma_conf * ++stmmac_setup_dma_desc(struct stmmac_priv *priv, unsigned int mtu) ++{ ++ struct stmmac_dma_conf *dma_conf; ++ int chan, bfsize, ret; ++ ++ dma_conf = kzalloc(sizeof(*dma_conf), GFP_KERNEL); ++ if (!dma_conf) { ++ netdev_err(priv->dev, "%s: DMA conf allocation failed\n", ++ __func__); ++ return ERR_PTR(-ENOMEM); ++ } ++ ++ bfsize = stmmac_set_16kib_bfsize(priv, mtu); ++ if (bfsize < 0) ++ bfsize = 0; ++ ++ if (bfsize < BUF_SIZE_16KiB) ++ bfsize = stmmac_set_bfsize(mtu, 0); ++ ++ dma_conf->dma_buf_sz = bfsize; ++ /* Chose the tx/rx size from the already defined one in the ++ * priv struct. (if defined) ++ */ ++ dma_conf->dma_tx_size = priv->dma_conf.dma_tx_size; ++ dma_conf->dma_rx_size = priv->dma_conf.dma_rx_size; ++ ++ if (!dma_conf->dma_tx_size) ++ dma_conf->dma_tx_size = DMA_DEFAULT_TX_SIZE; ++ if (!dma_conf->dma_rx_size) ++ dma_conf->dma_rx_size = DMA_DEFAULT_RX_SIZE; ++ ++ /* Earlier check for TBS */ ++ for (chan = 0; chan < priv->plat->tx_queues_to_use; chan++) { ++ struct stmmac_tx_queue *tx_q = &dma_conf->tx_queue[chan]; ++ int tbs_en = priv->plat->tx_queues_cfg[chan].tbs_en; ++ ++ /* Setup per-TXQ tbs flag before TX descriptor alloc */ ++ tx_q->tbs |= tbs_en ? STMMAC_TBS_AVAIL : 0; ++ } ++ ++ ret = alloc_dma_desc_resources(priv, dma_conf); ++ if (ret < 0) { ++ netdev_err(priv->dev, "%s: DMA descriptors allocation failed\n", ++ __func__); ++ goto alloc_error; ++ } ++ ++ ret = init_dma_desc_rings(priv->dev, dma_conf, GFP_KERNEL); ++ if (ret < 0) { ++ netdev_err(priv->dev, "%s: DMA descriptors initialization failed\n", ++ __func__); ++ goto init_error; ++ } ++ ++ return dma_conf; ++ ++init_error: ++ free_dma_desc_resources(priv, dma_conf); ++alloc_error: ++ kfree(dma_conf); ++ return ERR_PTR(ret); ++} ++ ++/** ++ * __stmmac_open - open entry point of the driver + * @dev : pointer to the device structure. ++ * @dma_conf : structure to take the dma data + * Description: + * This function is the open entry point of the driver. + * Return value: + * 0 on success and an appropriate (-)ve integer as defined in errno.h + * file on failure. + */ +-static int stmmac_open(struct net_device *dev) ++static int __stmmac_open(struct net_device *dev, ++ struct stmmac_dma_conf *dma_conf) + { + struct stmmac_priv *priv = netdev_priv(dev); + int mode = priv->plat->phy_interface; +- int bfsize = 0; + u32 chan; + int ret; + +@@ -3708,45 +3845,10 @@ static int stmmac_open(struct net_device + memset(&priv->xstats, 0, sizeof(struct stmmac_extra_stats)); + priv->xstats.threshold = tc; + +- bfsize = stmmac_set_16kib_bfsize(priv, dev->mtu); +- if (bfsize < 0) +- bfsize = 0; +- +- if (bfsize < BUF_SIZE_16KiB) +- bfsize = stmmac_set_bfsize(dev->mtu, priv->dma_conf.dma_buf_sz); +- +- priv->dma_conf.dma_buf_sz = bfsize; +- buf_sz = bfsize; +- + priv->rx_copybreak = STMMAC_RX_COPYBREAK; + +- if (!priv->dma_conf.dma_tx_size) +- priv->dma_conf.dma_tx_size = DMA_DEFAULT_TX_SIZE; +- if (!priv->dma_conf.dma_rx_size) +- priv->dma_conf.dma_rx_size = DMA_DEFAULT_RX_SIZE; +- +- /* Earlier check for TBS */ +- for (chan = 0; chan < priv->plat->tx_queues_to_use; chan++) { +- struct stmmac_tx_queue *tx_q = &priv->dma_conf.tx_queue[chan]; +- int tbs_en = priv->plat->tx_queues_cfg[chan].tbs_en; +- +- /* Setup per-TXQ tbs flag before TX descriptor alloc */ +- tx_q->tbs |= tbs_en ? STMMAC_TBS_AVAIL : 0; +- } +- +- ret = alloc_dma_desc_resources(priv); +- if (ret < 0) { +- netdev_err(priv->dev, "%s: DMA descriptors allocation failed\n", +- __func__); +- goto dma_desc_error; +- } +- +- ret = init_dma_desc_rings(dev, GFP_KERNEL); +- if (ret < 0) { +- netdev_err(priv->dev, "%s: DMA descriptors initialization failed\n", +- __func__); +- goto init_error; +- } ++ buf_sz = dma_conf->dma_buf_sz; ++ memcpy(&priv->dma_conf, dma_conf, sizeof(*dma_conf)); + + if (priv->plat->serdes_powerup) { + ret = priv->plat->serdes_powerup(dev, priv->plat->bsp_priv); +@@ -3789,14 +3891,28 @@ irq_error: + + stmmac_hw_teardown(dev); + init_error: +- free_dma_desc_resources(priv); +-dma_desc_error: ++ free_dma_desc_resources(priv, &priv->dma_conf); + phylink_disconnect_phy(priv->phylink); + init_phy_error: + pm_runtime_put(priv->device); + return ret; + } + ++static int stmmac_open(struct net_device *dev) ++{ ++ struct stmmac_priv *priv = netdev_priv(dev); ++ struct stmmac_dma_conf *dma_conf; ++ int ret; ++ ++ dma_conf = stmmac_setup_dma_desc(priv, dev->mtu); ++ if (IS_ERR(dma_conf)) ++ return PTR_ERR(dma_conf); ++ ++ ret = __stmmac_open(dev, dma_conf); ++ kfree(dma_conf); ++ return ret; ++} ++ + static void stmmac_fpe_stop_wq(struct stmmac_priv *priv) + { + set_bit(__FPE_REMOVING, &priv->fpe_task_state); +@@ -3843,7 +3959,7 @@ static int stmmac_release(struct net_dev + stmmac_stop_all_dma(priv); + + /* Release and free the Rx/Tx resources */ +- free_dma_desc_resources(priv); ++ free_dma_desc_resources(priv, &priv->dma_conf); + + /* Disable the MAC Rx/Tx */ + stmmac_mac_set(priv, priv->ioaddr, false); +@@ -6382,7 +6498,7 @@ void stmmac_disable_rx_queue(struct stmm + spin_unlock_irqrestore(&ch->lock, flags); + + stmmac_stop_rx_dma(priv, queue); +- __free_dma_rx_desc_resources(priv, queue); ++ __free_dma_rx_desc_resources(priv, &priv->dma_conf, queue); + } + + void stmmac_enable_rx_queue(struct stmmac_priv *priv, u32 queue) +@@ -6393,21 +6509,21 @@ void stmmac_enable_rx_queue(struct stmma + u32 buf_size; + int ret; + +- ret = __alloc_dma_rx_desc_resources(priv, queue); ++ ret = __alloc_dma_rx_desc_resources(priv, &priv->dma_conf, queue); + if (ret) { + netdev_err(priv->dev, "Failed to alloc RX desc.\n"); + return; + } + +- ret = __init_dma_rx_desc_rings(priv, queue, GFP_KERNEL); ++ ret = __init_dma_rx_desc_rings(priv, &priv->dma_conf, queue, GFP_KERNEL); + if (ret) { +- __free_dma_rx_desc_resources(priv, queue); ++ __free_dma_rx_desc_resources(priv, &priv->dma_conf, queue); + netdev_err(priv->dev, "Failed to init RX desc.\n"); + return; + } + + stmmac_reset_rx_queue(priv, queue); +- stmmac_clear_rx_descriptors(priv, queue); ++ stmmac_clear_rx_descriptors(priv, &priv->dma_conf, queue); + + stmmac_init_rx_chan(priv, priv->ioaddr, priv->plat->dma_cfg, + rx_q->dma_rx_phy, rx_q->queue_index); +@@ -6445,7 +6561,7 @@ void stmmac_disable_tx_queue(struct stmm + spin_unlock_irqrestore(&ch->lock, flags); + + stmmac_stop_tx_dma(priv, queue); +- __free_dma_tx_desc_resources(priv, queue); ++ __free_dma_tx_desc_resources(priv, &priv->dma_conf, queue); + } + + void stmmac_enable_tx_queue(struct stmmac_priv *priv, u32 queue) +@@ -6455,21 +6571,21 @@ void stmmac_enable_tx_queue(struct stmma + unsigned long flags; + int ret; + +- ret = __alloc_dma_tx_desc_resources(priv, queue); ++ ret = __alloc_dma_tx_desc_resources(priv, &priv->dma_conf, queue); + if (ret) { + netdev_err(priv->dev, "Failed to alloc TX desc.\n"); + return; + } + +- ret = __init_dma_tx_desc_rings(priv, queue); ++ ret = __init_dma_tx_desc_rings(priv, &priv->dma_conf, queue); + if (ret) { +- __free_dma_tx_desc_resources(priv, queue); ++ __free_dma_tx_desc_resources(priv, &priv->dma_conf, queue); + netdev_err(priv->dev, "Failed to init TX desc.\n"); + return; + } + + stmmac_reset_tx_queue(priv, queue); +- stmmac_clear_tx_descriptors(priv, queue); ++ stmmac_clear_tx_descriptors(priv, &priv->dma_conf, queue); + + stmmac_init_tx_chan(priv, priv->ioaddr, priv->plat->dma_cfg, + tx_q->dma_tx_phy, tx_q->queue_index); +@@ -6506,7 +6622,7 @@ void stmmac_xdp_release(struct net_devic + stmmac_stop_all_dma(priv); + + /* Release and free the Rx/Tx resources */ +- free_dma_desc_resources(priv); ++ free_dma_desc_resources(priv, &priv->dma_conf); + + /* Disable the MAC Rx/Tx */ + stmmac_mac_set(priv, priv->ioaddr, false); +@@ -6531,14 +6647,14 @@ int stmmac_xdp_open(struct net_device *d + u32 chan; + int ret; + +- ret = alloc_dma_desc_resources(priv); ++ ret = alloc_dma_desc_resources(priv, &priv->dma_conf); + if (ret < 0) { + netdev_err(dev, "%s: DMA descriptors allocation failed\n", + __func__); + goto dma_desc_error; + } + +- ret = init_dma_desc_rings(dev, GFP_KERNEL); ++ ret = init_dma_desc_rings(dev, &priv->dma_conf, GFP_KERNEL); + if (ret < 0) { + netdev_err(dev, "%s: DMA descriptors initialization failed\n", + __func__); +@@ -6620,7 +6736,7 @@ irq_error: + + stmmac_hw_teardown(dev); + init_error: +- free_dma_desc_resources(priv); ++ free_dma_desc_resources(priv, &priv->dma_conf); + dma_desc_error: + return ret; + } +@@ -7479,7 +7595,7 @@ int stmmac_resume(struct device *dev) + stmmac_reset_queues_param(priv); + + stmmac_free_tx_skbufs(priv); +- stmmac_clear_descriptors(priv); ++ stmmac_clear_descriptors(priv, &priv->dma_conf); + + stmmac_hw_setup(ndev, false); + stmmac_init_coalesce(priv); diff --git a/target/linux/generic/backport-6.1/775-v6.0-05-net-ethernet-stmicro-stmmac-permit-MTU-change-with-i.patch b/target/linux/generic/backport-6.1/775-v6.0-05-net-ethernet-stmicro-stmmac-permit-MTU-change-with-i.patch new file mode 100644 index 0000000000..996c28119e --- /dev/null +++ b/target/linux/generic/backport-6.1/775-v6.0-05-net-ethernet-stmicro-stmmac-permit-MTU-change-with-i.patch @@ -0,0 +1,73 @@ +From 3470079687448abac42deb62774253be1d6bdef3 Mon Sep 17 00:00:00 2001 +From: Christian Marangi +Date: Sat, 23 Jul 2022 16:29:33 +0200 +Subject: [PATCH 5/5] net: ethernet: stmicro: stmmac: permit MTU change with + interface up + +Remove the limitation where the interface needs to be down to change +MTU by releasing and opening the stmmac driver to set the new MTU. +Also call the set_filter function to correctly init the port. +This permits to remove the EBUSY error while the ethernet port is +running permitting a correct MTU change if for example a DSA request +a MTU change for a switch CPU port. + +Signed-off-by: Christian Marangi +Signed-off-by: Jakub Kicinski +--- + .../net/ethernet/stmicro/stmmac/stmmac_main.c | 30 +++++++++++++++---- + 1 file changed, 24 insertions(+), 6 deletions(-) + +--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c ++++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +@@ -5611,18 +5611,15 @@ static int stmmac_change_mtu(struct net_ + { + struct stmmac_priv *priv = netdev_priv(dev); + int txfifosz = priv->plat->tx_fifo_size; ++ struct stmmac_dma_conf *dma_conf; + const int mtu = new_mtu; ++ int ret; + + if (txfifosz == 0) + txfifosz = priv->dma_cap.tx_fifo_size; + + txfifosz /= priv->plat->tx_queues_to_use; + +- if (netif_running(dev)) { +- netdev_err(priv->dev, "must be stopped to change its MTU\n"); +- return -EBUSY; +- } +- + if (stmmac_xdp_is_enabled(priv) && new_mtu > ETH_DATA_LEN) { + netdev_dbg(priv->dev, "Jumbo frames not supported for XDP\n"); + return -EINVAL; +@@ -5634,8 +5631,29 @@ static int stmmac_change_mtu(struct net_ + if ((txfifosz < new_mtu) || (new_mtu > BUF_SIZE_16KiB)) + return -EINVAL; + +- dev->mtu = mtu; ++ if (netif_running(dev)) { ++ netdev_dbg(priv->dev, "restarting interface to change its MTU\n"); ++ /* Try to allocate the new DMA conf with the new mtu */ ++ dma_conf = stmmac_setup_dma_desc(priv, mtu); ++ if (IS_ERR(dma_conf)) { ++ netdev_err(priv->dev, "failed allocating new dma conf for new MTU %d\n", ++ mtu); ++ return PTR_ERR(dma_conf); ++ } ++ ++ stmmac_release(dev); ++ ++ ret = __stmmac_open(dev, dma_conf); ++ kfree(dma_conf); ++ if (ret) { ++ netdev_err(priv->dev, "failed reopening the interface after MTU change\n"); ++ return ret; ++ } ++ ++ stmmac_set_rx_mode(dev); ++ } + ++ dev->mtu = mtu; + netdev_update_features(dev); + + return 0; diff --git a/target/linux/generic/backport-6.1/776-v6.1-01-net-dsa-qca8k-fix-inband-mgmt-for-big-endian-systems.patch b/target/linux/generic/backport-6.1/776-v6.1-01-net-dsa-qca8k-fix-inband-mgmt-for-big-endian-systems.patch new file mode 100644 index 0000000000..4f0d95f456 --- /dev/null +++ b/target/linux/generic/backport-6.1/776-v6.1-01-net-dsa-qca8k-fix-inband-mgmt-for-big-endian-systems.patch @@ -0,0 +1,156 @@ +From a2550d3ce53c68f54042bc5e468c4d07491ffe0e Mon Sep 17 00:00:00 2001 +From: Christian Marangi +Date: Wed, 12 Oct 2022 19:18:36 +0200 +Subject: [PATCH 1/2] net: dsa: qca8k: fix inband mgmt for big-endian systems + +The header and the data of the skb for the inband mgmt requires +to be in little-endian. This is problematic for big-endian system +as the mgmt header is written in the cpu byte order. + +Fix this by converting each value for the mgmt header and data to +little-endian, and convert to cpu byte order the mgmt header and +data sent by the switch. + +Fixes: 5950c7c0a68c ("net: dsa: qca8k: add support for mgmt read/write in Ethernet packet") +Tested-by: Pawel Dembicki +Tested-by: Lech Perczak +Signed-off-by: Christian Marangi +Reviewed-by: Lech Perczak +Signed-off-by: David S. Miller +--- + drivers/net/dsa/qca/qca8k-8xxx.c | 63 ++++++++++++++++++++++++-------- + include/linux/dsa/tag_qca.h | 6 +-- + 2 files changed, 50 insertions(+), 19 deletions(-) + +--- a/drivers/net/dsa/qca/qca8k-8xxx.c ++++ b/drivers/net/dsa/qca/qca8k-8xxx.c +@@ -137,27 +137,42 @@ static void qca8k_rw_reg_ack_handler(str + struct qca8k_mgmt_eth_data *mgmt_eth_data; + struct qca8k_priv *priv = ds->priv; + struct qca_mgmt_ethhdr *mgmt_ethhdr; ++ u32 command; + u8 len, cmd; ++ int i; + + 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); ++ command = get_unaligned_le32(&mgmt_ethhdr->command); ++ cmd = FIELD_GET(QCA_HDR_MGMT_CMD, command); ++ len = FIELD_GET(QCA_HDR_MGMT_LENGTH, command); + + /* Make sure the seq match the requested packet */ +- if (mgmt_ethhdr->seq == mgmt_eth_data->seq) ++ if (get_unaligned_le32(&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; ++ u32 *val = mgmt_eth_data->data; ++ ++ *val = get_unaligned_le32(&mgmt_ethhdr->mdio_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); ++ if (len > QCA_HDR_MGMT_DATA1_LEN) { ++ __le32 *data2 = (__le32 *)skb->data; ++ int data_len = min_t(int, QCA_HDR_MGMT_DATA2_LEN, ++ len - QCA_HDR_MGMT_DATA1_LEN); ++ ++ val++; ++ ++ for (i = sizeof(u32); i <= data_len; i += sizeof(u32)) { ++ *val = get_unaligned_le32(data2); ++ val++; ++ data2++; ++ } ++ } + } + + complete(&mgmt_eth_data->rw_done); +@@ -169,8 +184,10 @@ static struct sk_buff *qca8k_alloc_mdio_ + struct qca_mgmt_ethhdr *mgmt_ethhdr; + unsigned int real_len; + struct sk_buff *skb; +- u32 *data2; ++ __le32 *data2; ++ u32 command; + u16 hdr; ++ int i; + + skb = dev_alloc_skb(QCA_HDR_MGMT_PKT_LEN); + if (!skb) +@@ -199,20 +216,32 @@ static struct sk_buff *qca8k_alloc_mdio_ + 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, real_len); +- mgmt_ethhdr->command |= FIELD_PREP(QCA_HDR_MGMT_CMD, cmd); +- mgmt_ethhdr->command |= FIELD_PREP(QCA_HDR_MGMT_CHECK_CODE, ++ command = FIELD_PREP(QCA_HDR_MGMT_ADDR, reg); ++ command |= FIELD_PREP(QCA_HDR_MGMT_LENGTH, real_len); ++ command |= FIELD_PREP(QCA_HDR_MGMT_CMD, cmd); ++ command |= FIELD_PREP(QCA_HDR_MGMT_CHECK_CODE, + QCA_HDR_MGMT_CHECK_CODE_VAL); + ++ put_unaligned_le32(command, &mgmt_ethhdr->command); ++ + if (cmd == MDIO_WRITE) +- mgmt_ethhdr->mdio_data = *val; ++ put_unaligned_le32(*val, &mgmt_ethhdr->mdio_data); + + mgmt_ethhdr->hdr = htons(hdr); + + 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); ++ if (cmd == MDIO_WRITE && len > QCA_HDR_MGMT_DATA1_LEN) { ++ int data_len = min_t(int, QCA_HDR_MGMT_DATA2_LEN, ++ len - QCA_HDR_MGMT_DATA1_LEN); ++ ++ val++; ++ ++ for (i = sizeof(u32); i <= data_len; i += sizeof(u32)) { ++ put_unaligned_le32(*val, data2); ++ data2++; ++ val++; ++ } ++ } + + return skb; + } +@@ -220,9 +249,11 @@ static struct sk_buff *qca8k_alloc_mdio_ + static void qca8k_mdio_header_fill_seq_num(struct sk_buff *skb, u32 seq_num) + { + struct qca_mgmt_ethhdr *mgmt_ethhdr; ++ u32 seq; + ++ seq = FIELD_PREP(QCA_HDR_MGMT_SEQ_NUM, seq_num); + mgmt_ethhdr = (struct qca_mgmt_ethhdr *)skb->data; +- mgmt_ethhdr->seq = FIELD_PREP(QCA_HDR_MGMT_SEQ_NUM, seq_num); ++ put_unaligned_le32(seq, &mgmt_ethhdr->seq); + } + + static int qca8k_read_eth(struct qca8k_priv *priv, u32 reg, u32 *val, int len) +--- a/include/linux/dsa/tag_qca.h ++++ b/include/linux/dsa/tag_qca.h +@@ -56,9 +56,9 @@ + + /* 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 */ ++ __le32 command; /* command bit 31:0 */ ++ __le32 seq; /* seq 63:32 */ ++ __le32 mdio_data; /* first 4byte mdio */ + __be16 hdr; /* qca hdr */ + } __packed; + diff --git a/target/linux/generic/backport-6.1/776-v6.1-02-net-dsa-qca8k-fix-ethtool-autocast-mib-for-big-endia.patch b/target/linux/generic/backport-6.1/776-v6.1-02-net-dsa-qca8k-fix-ethtool-autocast-mib-for-big-endia.patch new file mode 100644 index 0000000000..d13014bf93 --- /dev/null +++ b/target/linux/generic/backport-6.1/776-v6.1-02-net-dsa-qca8k-fix-ethtool-autocast-mib-for-big-endia.patch @@ -0,0 +1,81 @@ +From 0d4636f7d72df3179b20a2d32b647881917a5e2a Mon Sep 17 00:00:00 2001 +From: Christian Marangi +Date: Wed, 12 Oct 2022 19:18:37 +0200 +Subject: [PATCH 2/2] net: dsa: qca8k: fix ethtool autocast mib for big-endian + systems + +The switch sends autocast mib in little-endian. This is problematic for +big-endian system as the values needs to be converted. + +Fix this by converting each mib value to cpu byte order. + +Fixes: 5c957c7ca78c ("net: dsa: qca8k: add support for mib autocast in Ethernet packet") +Tested-by: Pawel Dembicki +Tested-by: Lech Perczak +Signed-off-by: Christian Marangi +Signed-off-by: David S. Miller +--- + drivers/net/dsa/qca/qca8k-8xxx.c | 20 ++++++++------------ + include/linux/dsa/tag_qca.h | 2 +- + 2 files changed, 9 insertions(+), 13 deletions(-) + +--- a/drivers/net/dsa/qca/qca8k-8xxx.c ++++ b/drivers/net/dsa/qca/qca8k-8xxx.c +@@ -1668,9 +1668,9 @@ static void qca8k_mib_autocast_handler(s + 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; ++ __le32 *data2; + u8 port; ++ int i; + + mib_ethhdr = (struct mib_ethhdr *)skb_mac_header(skb); + mib_eth_data = &priv->mib_eth_data; +@@ -1682,28 +1682,24 @@ static void qca8k_mib_autocast_handler(s + if (port != mib_eth_data->req_port) + goto exit; + +- data = mib_eth_data->data; ++ data2 = (__le32 *)skb->data; + + for (i = 0; i < priv->info->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]; ++ mib_eth_data->data[i] = get_unaligned_le32(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); ++ mib_eth_data->data[i] = get_unaligned_le64((__le64 *)data2); ++ else ++ mib_eth_data->data[i] = get_unaligned_le32(data2); + +- /* Set the offset for the next mib */ +- offset += mib_len; ++ data2 += mib->size; + } + + exit: +--- a/include/linux/dsa/tag_qca.h ++++ b/include/linux/dsa/tag_qca.h +@@ -68,7 +68,7 @@ enum mdio_cmd { + }; + + struct mib_ethhdr { +- u32 data[3]; /* first 3 mib counter */ ++ __le32 data[3]; /* first 3 mib counter */ + __be16 hdr; /* qca hdr */ + } __packed; + diff --git a/target/linux/generic/backport-6.1/780-v5.16-bus-mhi-pci_generic-Introduce-Sierra-EM919X-support.patch b/target/linux/generic/backport-6.1/780-v5.16-bus-mhi-pci_generic-Introduce-Sierra-EM919X-support.patch new file mode 100644 index 0000000000..5fe10e0caa --- /dev/null +++ b/target/linux/generic/backport-6.1/780-v5.16-bus-mhi-pci_generic-Introduce-Sierra-EM919X-support.patch @@ -0,0 +1,80 @@ +From 789d3eeb2367f92193a0882f7cdab03f0f9d6930 Mon Sep 17 00:00:00 2001 +From: Thomas Perrot +Date: Thu, 16 Dec 2021 13:42:27 +0530 +Subject: [PATCH] bus: mhi: pci_generic: Introduce Sierra EM919X support + +Add support for EM919X modems, this modem series is based on SDX55 +qcom chip. + +It is mandatory to use the same ring for control+data and diag events. + +Link: https://lore.kernel.org/r/20211123081541.648426-1-thomas.perrot@bootlin.com +Tested-by: Aleksander Morgado +Reviewed-by: Manivannan Sadhasivam +Signed-off-by: Thomas Perrot +Signed-off-by: Manivannan Sadhasivam +Link: https://lore.kernel.org/r/20211216081227.237749-11-manivannan.sadhasivam@linaro.org +Signed-off-by: Greg Kroah-Hartman +--- + drivers/bus/mhi/pci_generic.c | 43 +++++++++++++++++++++++++++++++++++ + 1 file changed, 43 insertions(+) + +--- a/drivers/bus/mhi/pci_generic.c ++++ b/drivers/bus/mhi/pci_generic.c +@@ -406,6 +406,46 @@ static const struct mhi_pci_dev_info mhi + .mru_default = 32768, + }; + ++static const struct mhi_channel_config mhi_sierra_em919x_channels[] = { ++ MHI_CHANNEL_CONFIG_UL_SBL(2, "SAHARA", 32, 0), ++ MHI_CHANNEL_CONFIG_DL_SBL(3, "SAHARA", 256, 0), ++ MHI_CHANNEL_CONFIG_UL(4, "DIAG", 32, 0), ++ MHI_CHANNEL_CONFIG_DL(5, "DIAG", 32, 0), ++ MHI_CHANNEL_CONFIG_UL(12, "MBIM", 128, 0), ++ MHI_CHANNEL_CONFIG_DL(13, "MBIM", 128, 0), ++ MHI_CHANNEL_CONFIG_UL(14, "QMI", 32, 0), ++ MHI_CHANNEL_CONFIG_DL(15, "QMI", 32, 0), ++ MHI_CHANNEL_CONFIG_UL(32, "DUN", 32, 0), ++ MHI_CHANNEL_CONFIG_DL(33, "DUN", 32, 0), ++ MHI_CHANNEL_CONFIG_HW_UL(100, "IP_HW0", 512, 1), ++ MHI_CHANNEL_CONFIG_HW_DL(101, "IP_HW0", 512, 2), ++}; ++ ++static struct mhi_event_config modem_sierra_em919x_mhi_events[] = { ++ /* first ring is control+data and DIAG ring */ ++ MHI_EVENT_CONFIG_CTRL(0, 2048), ++ /* Hardware channels request dedicated hardware event rings */ ++ MHI_EVENT_CONFIG_HW_DATA(1, 2048, 100), ++ MHI_EVENT_CONFIG_HW_DATA(2, 2048, 101) ++}; ++ ++static const struct mhi_controller_config modem_sierra_em919x_config = { ++ .max_channels = 128, ++ .timeout_ms = 24000, ++ .num_channels = ARRAY_SIZE(mhi_sierra_em919x_channels), ++ .ch_cfg = mhi_sierra_em919x_channels, ++ .num_events = ARRAY_SIZE(modem_sierra_em919x_mhi_events), ++ .event_cfg = modem_sierra_em919x_mhi_events, ++}; ++ ++static const struct mhi_pci_dev_info mhi_sierra_em919x_info = { ++ .name = "sierra-em919x", ++ .config = &modem_sierra_em919x_config, ++ .bar_num = MHI_PCI_DEFAULT_BAR_NUM, ++ .dma_data_width = 32, ++ .sideband_wake = false, ++}; ++ + static const struct mhi_channel_config mhi_telit_fn980_hw_v1_channels[] = { + MHI_CHANNEL_CONFIG_UL(14, "QMI", 32, 0), + MHI_CHANNEL_CONFIG_DL(15, "QMI", 32, 0), +@@ -480,6 +520,9 @@ static const struct mhi_pci_dev_info mhi + }; + + static const struct pci_device_id mhi_pci_id_table[] = { ++ /* EM919x (sdx55), use the same vid:pid as qcom-sdx55m */ ++ { PCI_DEVICE_SUB(PCI_VENDOR_ID_QCOM, 0x0306, 0x18d7, 0x0200), ++ .driver_data = (kernel_ulong_t) &mhi_sierra_em919x_info }, + /* Telit FN980 hardware revision v1 */ + { PCI_DEVICE_SUB(PCI_VENDOR_ID_QCOM, 0x0306, 0x1C5D, 0x2000), + .driver_data = (kernel_ulong_t) &mhi_telit_fn980_hw_v1_info }, diff --git a/target/linux/generic/backport-6.1/781-v6.1-bus-mhi-host-always-print-detected-modem-name.patch b/target/linux/generic/backport-6.1/781-v6.1-bus-mhi-host-always-print-detected-modem-name.patch new file mode 100644 index 0000000000..e974c21cb7 --- /dev/null +++ b/target/linux/generic/backport-6.1/781-v6.1-bus-mhi-host-always-print-detected-modem-name.patch @@ -0,0 +1,37 @@ +From f369e9ad52ec9361827e21a631b7198c9fca438e Mon Sep 17 00:00:00 2001 +From: Koen Vandeputte +Date: Wed, 31 Aug 2022 12:03:49 +0200 +Subject: [PATCH] bus: mhi: host: always print detected modem name + +This harmless print provides a very easy way of knowing +if the modem is detected properly during probing. + +Promote it to an informational print so no hassle is required +enabling kernel debugging info to obtain it. + +The rationale here is that: +On a lot of low-storage embedded devices, extensive kernel +debugging info is not always present as this would +increase it's size to much causing partition size issues. + +Signed-off-by: Koen Vandeputte +Reviewed-by: Manivannan Sadhasivam +Reviewed-by: Loic Poulain +Link: https://lore.kernel.org/r/20220831100349.1488762-1-koen.vandeputte@citymesh.com +[mani: added missing review tags] +Signed-off-by: Manivannan Sadhasivam +--- + drivers/bus/mhi/pci_generic.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/drivers/bus/mhi/pci_generic.c ++++ b/drivers/bus/mhi/pci_generic.c +@@ -806,7 +806,7 @@ static int mhi_pci_probe(struct pci_dev + struct mhi_controller *mhi_cntrl; + int err; + +- dev_dbg(&pdev->dev, "MHI PCI device found: %s\n", info->name); ++ dev_info(&pdev->dev, "MHI PCI device found: %s\n", info->name); + + /* mhi_pdev.mhi_cntrl must be zero-initialized */ + mhi_pdev = devm_kzalloc(&pdev->dev, sizeof(*mhi_pdev), GFP_KERNEL); diff --git a/target/linux/generic/backport-6.1/782-v6.1-net-dsa-mt7530-add-support-for-in-band-link-status.patch b/target/linux/generic/backport-6.1/782-v6.1-net-dsa-mt7530-add-support-for-in-band-link-status.patch new file mode 100644 index 0000000000..7bb6530266 --- /dev/null +++ b/target/linux/generic/backport-6.1/782-v6.1-net-dsa-mt7530-add-support-for-in-band-link-status.patch @@ -0,0 +1,130 @@ +From e19de30d20809af3221ef8a2648b8a8a52e02d90 Mon Sep 17 00:00:00 2001 +From: Daniel Golle +Date: Wed, 21 Sep 2022 01:23:14 +0100 +Subject: [PATCH 1/1] net: dsa: mt7530: add support for in-band link status + +Read link status from SGMII PCS for in-band managed 2500Base-X and +1000Base-X connection on a MAC port of the MT7531. This is needed to +get the SFP cage working which is connected to SGMII interface of +port 5 of the MT7531 switch IC on the Bananapi BPi-R3 board. +While at it also handle an_complete for both the autoneg and the +non-autoneg codepath. + +Signed-off-by: Daniel Golle +Signed-off-by: David S. Miller +--- + drivers/net/dsa/mt7530.c | 50 +++++++++++++++++++++++++++++----------- + drivers/net/dsa/mt7530.h | 1 + + 2 files changed, 38 insertions(+), 13 deletions(-) + +--- a/drivers/net/dsa/mt7530.c ++++ b/drivers/net/dsa/mt7530.c +@@ -2699,9 +2699,6 @@ mt7531_mac_config(struct dsa_switch *ds, + case PHY_INTERFACE_MODE_NA: + case PHY_INTERFACE_MODE_1000BASEX: + case PHY_INTERFACE_MODE_2500BASEX: +- if (phylink_autoneg_inband(mode)) +- return -EINVAL; +- + return mt7531_sgmii_setup_mode_force(priv, port, interface); + default: + return -EINVAL; +@@ -2777,13 +2774,6 @@ unsupported: + return; + } + +- if (phylink_autoneg_inband(mode) && +- state->interface != PHY_INTERFACE_MODE_SGMII) { +- dev_err(ds->dev, "%s: in-band negotiation unsupported\n", +- __func__); +- return; +- } +- + mcr_cur = mt7530_read(priv, MT7530_PMCR_P(port)); + mcr_new = mcr_cur; + mcr_new &= ~PMCR_LINK_SETTINGS_MASK; +@@ -2920,6 +2910,9 @@ static void mt753x_phylink_get_caps(stru + config->mac_capabilities = MAC_ASYM_PAUSE | MAC_SYM_PAUSE | + MAC_10 | MAC_100 | MAC_1000FD; + ++ if ((priv->id == ID_MT7531) && mt753x_is_mac_port(port)) ++ config->mac_capabilities |= MAC_2500FD; ++ + /* This driver does not make use of the speed, duplex, pause or the + * advertisement in its mac_config, so it is safe to mark this driver + * as non-legacy. +@@ -2985,6 +2978,7 @@ mt7531_sgmii_pcs_get_state_an(struct mt7 + + status = mt7530_read(priv, MT7531_PCS_CONTROL_1(port)); + state->link = !!(status & MT7531_SGMII_LINK_STATUS); ++ state->an_complete = !!(status & MT7531_SGMII_AN_COMPLETE); + if (state->interface == PHY_INTERFACE_MODE_SGMII && + (status & MT7531_SGMII_AN_ENABLE)) { + val = mt7530_read(priv, MT7531_PCS_SPEED_ABILITY(port)); +@@ -3015,16 +3009,44 @@ mt7531_sgmii_pcs_get_state_an(struct mt7 + return 0; + } + ++static void ++mt7531_sgmii_pcs_get_state_inband(struct mt7530_priv *priv, int port, ++ struct phylink_link_state *state) ++{ ++ unsigned int val; ++ ++ val = mt7530_read(priv, MT7531_PCS_CONTROL_1(port)); ++ state->link = !!(val & MT7531_SGMII_LINK_STATUS); ++ if (!state->link) ++ return; ++ ++ state->an_complete = state->link; ++ ++ if (state->interface == PHY_INTERFACE_MODE_2500BASEX) ++ state->speed = SPEED_2500; ++ else ++ state->speed = SPEED_1000; ++ ++ state->duplex = DUPLEX_FULL; ++ state->pause = MLO_PAUSE_NONE; ++} ++ + static void mt7531_pcs_get_state(struct phylink_pcs *pcs, + struct phylink_link_state *state) + { + struct mt7530_priv *priv = pcs_to_mt753x_pcs(pcs)->priv; + int port = pcs_to_mt753x_pcs(pcs)->port; + +- if (state->interface == PHY_INTERFACE_MODE_SGMII) ++ if (state->interface == PHY_INTERFACE_MODE_SGMII) { + mt7531_sgmii_pcs_get_state_an(priv, port, state); +- else +- state->link = false; ++ return; ++ } else if ((state->interface == PHY_INTERFACE_MODE_1000BASEX) || ++ (state->interface == PHY_INTERFACE_MODE_2500BASEX)) { ++ mt7531_sgmii_pcs_get_state_inband(priv, port, state); ++ return; ++ } ++ ++ state->link = false; + } + + static int mt753x_pcs_config(struct phylink_pcs *pcs, unsigned int mode, +@@ -3065,6 +3087,8 @@ mt753x_setup(struct dsa_switch *ds) + priv->pcs[i].pcs.ops = priv->info->pcs_ops; + priv->pcs[i].priv = priv; + priv->pcs[i].port = i; ++ if (mt753x_is_mac_port(i)) ++ priv->pcs[i].pcs.poll = 1; + } + + ret = priv->info->sw_setup(ds); +--- a/drivers/net/dsa/mt7530.h ++++ b/drivers/net/dsa/mt7530.h +@@ -373,6 +373,7 @@ enum mt7530_vlan_port_acc_frm { + #define MT7531_SGMII_LINK_STATUS BIT(18) + #define MT7531_SGMII_AN_ENABLE BIT(12) + #define MT7531_SGMII_AN_RESTART BIT(9) ++#define MT7531_SGMII_AN_COMPLETE BIT(21) + + /* Register for SGMII PCS_SPPED_ABILITY */ + #define MT7531_PCS_SPEED_ABILITY(p) MT7531_SGMII_REG(p, 0x08) diff --git a/target/linux/generic/backport-6.1/783-v6.1-net-sfp-re-implement-soft-state-polling-setup.patch b/target/linux/generic/backport-6.1/783-v6.1-net-sfp-re-implement-soft-state-polling-setup.patch new file mode 100644 index 0000000000..6f69b7ddfe --- /dev/null +++ b/target/linux/generic/backport-6.1/783-v6.1-net-sfp-re-implement-soft-state-polling-setup.patch @@ -0,0 +1,98 @@ +From 8475c4b70b040f9d8cbc308100f2c4d865f810b3 Mon Sep 17 00:00:00 2001 +From: "Russell King (Oracle)" +Date: Tue, 13 Sep 2022 20:06:27 +0100 +Subject: [PATCH 1/1] net: sfp: re-implement soft state polling setup + +Re-implement the decision making for soft state polling. Instead of +generating the soft state mask in sfp_soft_start_poll() by looking at +which GPIOs are available, record their availability in +sfp_sm_mod_probe() in sfp->state_hw_mask. + +This will then allow us to clear bits in sfp->state_hw_mask in module +specific quirks when the hardware signals should not be used, thereby +allowing us to switch to using the software state polling. + +Signed-off-by: Russell King (Oracle) +Signed-off-by: Jakub Kicinski +--- + drivers/net/phy/sfp.c | 38 ++++++++++++++++++++++++++------------ + 1 file changed, 26 insertions(+), 12 deletions(-) + +--- 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_hw_mask; + unsigned int state_soft_mask; + unsigned int state; + struct delayed_work poll; +@@ -499,17 +500,18 @@ static void sfp_soft_set_state(struct sf + 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_hw_mask & mask; + + if (sfp->state_soft_mask & (SFP_F_LOS | SFP_F_TX_FAULT) && + !sfp->need_poll) +@@ -523,10 +525,11 @@ static void sfp_soft_stop_poll(struct sf + + static unsigned int sfp_get_state(struct sfp *sfp) + { +- unsigned int state = sfp->get_state(sfp); ++ unsigned int soft = sfp->state_soft_mask & (SFP_F_LOS | SFP_F_TX_FAULT); ++ unsigned int state; + +- if (state & SFP_F_PRESENT && +- sfp->state_soft_mask & (SFP_F_LOS | SFP_F_TX_FAULT)) ++ state = sfp->get_state(sfp) & sfp->state_hw_mask; ++ if (state & SFP_F_PRESENT && soft) + state |= sfp_soft_get_state(sfp); + + return state; +@@ -1940,6 +1943,15 @@ static int sfp_sm_mod_probe(struct sfp * + if (ret < 0) + return ret; + ++ /* Initialise state bits to use from hardware */ ++ sfp->state_hw_mask = SFP_F_PRESENT; ++ if (sfp->gpio[GPIO_TX_DISABLE]) ++ sfp->state_hw_mask |= SFP_F_TX_DISABLE; ++ if (sfp->gpio[GPIO_TX_FAULT]) ++ sfp->state_hw_mask |= SFP_F_TX_FAULT; ++ if (sfp->gpio[GPIO_LOS]) ++ sfp->state_hw_mask |= SFP_F_LOS; ++ + 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; +@@ -2565,6 +2577,8 @@ static int sfp_probe(struct platform_dev + return PTR_ERR(sfp->gpio[i]); + } + ++ sfp->state_hw_mask = SFP_F_PRESENT; ++ + sfp->get_state = sfp_gpio_get_state; + sfp->set_state = sfp_gpio_set_state; + diff --git a/target/linux/generic/backport-6.1/784-v6.1-net-sfp-move-quirk-handling-into-sfp.c.patch b/target/linux/generic/backport-6.1/784-v6.1-net-sfp-move-quirk-handling-into-sfp.c.patch new file mode 100644 index 0000000000..e5f8031636 --- /dev/null +++ b/target/linux/generic/backport-6.1/784-v6.1-net-sfp-move-quirk-handling-into-sfp.c.patch @@ -0,0 +1,291 @@ +From 23571c7b96437483d28a990c906cc81f5f66374e Mon Sep 17 00:00:00 2001 +From: "Russell King (Oracle)" +Date: Tue, 13 Sep 2022 20:06:32 +0100 +Subject: [PATCH 1/1] 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) +Signed-off-by: Jakub Kicinski +--- + 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(-) + +--- 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); +--- a/drivers/net/phy/sfp.c ++++ b/drivers/net/phy/sfp.c +@@ -253,6 +253,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; +@@ -309,6 +311,93 @@ static const struct of_device_id sfp_of_ + }; + 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) +@@ -1964,6 +2053,8 @@ static int sfp_sm_mod_probe(struct sfp * + else + sfp->tx_fault_ignore = false; + ++ sfp->quirk = sfp_lookup_quirk(&id); ++ + return 0; + } + +@@ -2075,7 +2166,8 @@ static void sfp_sm_module(struct sfp *sf + 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; +--- 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, str + 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); diff --git a/target/linux/generic/backport-6.1/785-v6.1-net-sfp-move-Alcatel-Lucent-3FE46541AA-fixup.patch b/target/linux/generic/backport-6.1/785-v6.1-net-sfp-move-Alcatel-Lucent-3FE46541AA-fixup.patch new file mode 100644 index 0000000000..aa3112e585 --- /dev/null +++ b/target/linux/generic/backport-6.1/785-v6.1-net-sfp-move-Alcatel-Lucent-3FE46541AA-fixup.patch @@ -0,0 +1,69 @@ +From 275416754e9a262c97a1ad6f806a4bc6e0464aa2 Mon Sep 17 00:00:00 2001 +From: "Russell King (Oracle)" +Date: Tue, 13 Sep 2022 20:06:37 +0100 +Subject: [PATCH 1/1] 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) +Signed-off-by: Jakub Kicinski +--- + drivers/net/phy/sfp.c | 14 +++++++++----- + drivers/net/phy/sfp.h | 1 + + 2 files changed, 10 insertions(+), 5 deletions(-) + +--- a/drivers/net/phy/sfp.c ++++ b/drivers/net/phy/sfp.c +@@ -311,6 +311,11 @@ static const struct of_device_id sfp_of_ + }; + 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) + { +@@ -341,6 +346,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 +@@ -2041,11 +2047,7 @@ static int sfp_sm_mod_probe(struct sfp * + if (sfp->gpio[GPIO_LOS]) + sfp->state_hw_mask |= SFP_F_LOS; + +- 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)) +@@ -2054,6 +2056,8 @@ static int sfp_sm_mod_probe(struct sfp * + sfp->tx_fault_ignore = false; + + sfp->quirk = sfp_lookup_quirk(&id); ++ if (sfp->quirk && sfp->quirk->fixup) ++ sfp->quirk->fixup(sfp); + + return 0; + } +--- 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 { diff --git a/target/linux/generic/backport-6.1/786-v6.1-net-sfp-move-Huawei-MA5671A-fixup.patch b/target/linux/generic/backport-6.1/786-v6.1-net-sfp-move-Huawei-MA5671A-fixup.patch new file mode 100644 index 0000000000..14b0f9b8c3 --- /dev/null +++ b/target/linux/generic/backport-6.1/786-v6.1-net-sfp-move-Huawei-MA5671A-fixup.patch @@ -0,0 +1,48 @@ +From 5029be761161374a3624aa7b4670174c35449bf5 Mon Sep 17 00:00:00 2001 +From: "Russell King (Oracle)" +Date: Tue, 13 Sep 2022 20:06:42 +0100 +Subject: [PATCH 1/1] net: sfp: move Huawei MA5671A fixup + +Move this module over to the new fixup mechanism. + +Signed-off-by: Russell King (Oracle) +Signed-off-by: Jakub Kicinski +--- + drivers/net/phy/sfp.c | 12 +++++++----- + 1 file changed, 7 insertions(+), 5 deletions(-) + +--- a/drivers/net/phy/sfp.c ++++ b/drivers/net/phy/sfp.c +@@ -316,6 +316,11 @@ static void sfp_fixup_long_startup(struc + 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) + { +@@ -353,6 +358,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 +@@ -2049,11 +2055,7 @@ static int sfp_sm_mod_probe(struct sfp * + + 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) diff --git a/target/linux/generic/backport-6.1/787-v6.1-net-sfp-add-support-for-HALNy-GPON-SFP.patch b/target/linux/generic/backport-6.1/787-v6.1-net-sfp-add-support-for-HALNy-GPON-SFP.patch new file mode 100644 index 0000000000..0c65de5ab8 --- /dev/null +++ b/target/linux/generic/backport-6.1/787-v6.1-net-sfp-add-support-for-HALNy-GPON-SFP.patch @@ -0,0 +1,79 @@ +From 73472c830eae5fce2107f7f086f1e6827d215caf Mon Sep 17 00:00:00 2001 +From: "Russell King (Oracle)" +Date: Tue, 13 Sep 2022 20:06:48 +0100 +Subject: [PATCH 1/1] 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 maybe uses TX_FAULT as a serial port +transmit pin. Rather than use these hardware signals, switch to +using software polling for these status signals. + +Signed-off-by: Russell King (Oracle) +Signed-off-by: Jakub Kicinski +--- + drivers/net/phy/sfp-bus.c | 2 +- + drivers/net/phy/sfp.c | 21 ++++++++++++++++++--- + 2 files changed, 19 insertions(+), 4 deletions(-) + +--- 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 *b + phylink_set(modes, 2500baseX_Full); + } + +- if (bus->sfp_quirk) ++ if (bus->sfp_quirk && bus->sfp_quirk->modes) + bus->sfp_quirk->modes(id, modes); + + linkmode_or(support, support, modes); +--- a/drivers/net/phy/sfp.c ++++ b/drivers/net/phy/sfp.c +@@ -321,6 +321,15 @@ static void sfp_fixup_ignore_tx_fault(st + sfp->tx_fault_ignore = true; + } + ++static void sfp_fixup_halny_gsfp(struct sfp *sfp) ++{ ++ /* Ignore the TX_FAULT and LOS signals on this module. ++ * these are possibly used for other purposes on this ++ * module, e.g. a serial port. ++ */ ++ sfp->state_hw_mask &= ~(SFP_F_TX_FAULT | SFP_F_LOS); ++} ++ + static void sfp_quirk_2500basex(const struct sfp_eeprom_id *id, + unsigned long *modes) + { +@@ -353,6 +362,10 @@ static const struct sfp_quirk sfp_quirks + .modes = sfp_quirk_2500basex, + .fixup = sfp_fixup_long_startup, + }, { ++ .vendor = "HALNy", ++ .part = "HL-GSFP", ++ .fixup = sfp_fixup_halny_gsfp, ++ }, { + // Huawei MA5671A can operate at 2500base-X, but report 1.2GBd + // NRZ in their EEPROM + .vendor = "HUAWEI", +@@ -369,16 +382,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; diff --git a/target/linux/generic/backport-6.1/788-v6.0-net-ethernet-mtk_eth_soc-fix-mask-of-RX_DMA_GET_SPOR.patch b/target/linux/generic/backport-6.1/788-v6.0-net-ethernet-mtk_eth_soc-fix-mask-of-RX_DMA_GET_SPOR.patch new file mode 100644 index 0000000000..27c719b663 --- /dev/null +++ b/target/linux/generic/backport-6.1/788-v6.0-net-ethernet-mtk_eth_soc-fix-mask-of-RX_DMA_GET_SPOR.patch @@ -0,0 +1,32 @@ +From c9da02bfb1112461e048d3b736afb1873f6f4ccf Mon Sep 17 00:00:00 2001 +From: Daniel Golle +Date: Tue, 27 Sep 2022 16:30:02 +0100 +Subject: [PATCH 1/1] net: ethernet: mtk_eth_soc: fix mask of + RX_DMA_GET_SPORT{,_V2} + +The bitmasks applied in RX_DMA_GET_SPORT and RX_DMA_GET_SPORT_V2 macros +were swapped. Fix that. + +Reported-by: Chen Minqiang +Fixes: 160d3a9b192985 ("net: ethernet: mtk_eth_soc: introduce MTK_NETSYS_V2 support") +Acked-by: Lorenzo Bianconi +Signed-off-by: Daniel Golle +Link: https://lore.kernel.org/r/YzMW+mg9UsaCdKRQ@makrotopia.org +Signed-off-by: Jakub Kicinski +--- + drivers/net/ethernet/mediatek/mtk_eth_soc.h | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h ++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h +@@ -315,8 +315,8 @@ + #define MTK_RXD5_PPE_CPU_REASON GENMASK(22, 18) + #define MTK_RXD5_SRC_PORT GENMASK(29, 26) + +-#define RX_DMA_GET_SPORT(x) (((x) >> 19) & 0xf) +-#define RX_DMA_GET_SPORT_V2(x) (((x) >> 26) & 0x7) ++#define RX_DMA_GET_SPORT(x) (((x) >> 19) & 0x7) ++#define RX_DMA_GET_SPORT_V2(x) (((x) >> 26) & 0xf) + + /* PDMA V2 descriptor rxd3 */ + #define RX_DMA_VTAG_V2 BIT(0) diff --git a/target/linux/generic/backport-6.1/789-v6.1-net-ethernet-mtk_eth_soc-fix-state-in-__mtk_foe_entr.patch b/target/linux/generic/backport-6.1/789-v6.1-net-ethernet-mtk_eth_soc-fix-state-in-__mtk_foe_entr.patch new file mode 100644 index 0000000000..11465c1c5b --- /dev/null +++ b/target/linux/generic/backport-6.1/789-v6.1-net-ethernet-mtk_eth_soc-fix-state-in-__mtk_foe_entr.patch @@ -0,0 +1,37 @@ +From ae3ed15da5889263de372ff9df2e83e16acca4cb Mon Sep 17 00:00:00 2001 +From: Daniel Golle +Date: Fri, 30 Sep 2022 01:56:53 +0100 +Subject: [PATCH 1/1] net: ethernet: mtk_eth_soc: fix state in + __mtk_foe_entry_clear + +Setting ib1 state to MTK_FOE_STATE_UNBIND in __mtk_foe_entry_clear +routine as done by commit 0e80707d94e4c8 ("net: ethernet: mtk_eth_soc: +fix typo in __mtk_foe_entry_clear") breaks flow offloading, at least +on older MTK_NETSYS_V1 SoCs, OpenWrt users have confirmed the bug on +MT7622 and MT7621 systems. +Felix Fietkau suggested to use MTK_FOE_STATE_INVALID instead which +works well on both, MTK_NETSYS_V1 and MTK_NETSYS_V2. + +Tested on MT7622 (Linksys E8450) and MT7986 (BananaPi BPI-R3). + +Suggested-by: Felix Fietkau +Fixes: 0e80707d94e4c8 ("net: ethernet: mtk_eth_soc: fix typo in __mtk_foe_entry_clear") +Fixes: 33fc42de33278b ("net: ethernet: mtk_eth_soc: support creating mac address based offload entries") +Signed-off-by: Daniel Golle +Link: https://lore.kernel.org/r/YzY+1Yg0FBXcnrtc@makrotopia.org +Signed-off-by: Jakub Kicinski +--- + drivers/net/ethernet/mediatek/mtk_ppe.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/drivers/net/ethernet/mediatek/mtk_ppe.c ++++ b/drivers/net/ethernet/mediatek/mtk_ppe.c +@@ -442,7 +442,7 @@ __mtk_foe_entry_clear(struct mtk_ppe *pp + struct mtk_foe_entry *hwe = mtk_foe_get_entry(ppe, entry->hash); + + hwe->ib1 &= ~MTK_FOE_IB1_STATE; +- hwe->ib1 |= FIELD_PREP(MTK_FOE_IB1_STATE, MTK_FOE_STATE_UNBIND); ++ hwe->ib1 |= FIELD_PREP(MTK_FOE_IB1_STATE, MTK_FOE_STATE_INVALID); + dma_wmb(); + } + entry->hash = 0xffff; diff --git a/target/linux/generic/backport-6.1/800-v6.0-0001-dt-bindings-leds-add-Broadcom-s-BCM63138-controller.patch b/target/linux/generic/backport-6.1/800-v6.0-0001-dt-bindings-leds-add-Broadcom-s-BCM63138-controller.patch new file mode 100644 index 0000000000..b1072ce640 --- /dev/null +++ b/target/linux/generic/backport-6.1/800-v6.0-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?= +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 +Reviewed-by: Rob Herring +Reviewed-by: Florian Fainelli +Signed-off-by: Pavel Machek +--- + .../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 ++ ++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 ++ ++ 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 = ; ++ default-state = "on"; ++ }; ++ ++ led@3 { ++ reg = <0x3>; ++ function = LED_FUNCTION_STATUS; ++ color = ; ++ active-low; ++ }; ++ }; diff --git a/target/linux/generic/backport-6.1/800-v6.0-0002-leds-bcm63138-add-support-for-BCM63138-controller.patch b/target/linux/generic/backport-6.1/800-v6.0-0002-leds-bcm63138-add-support-for-BCM63138-controller.patch new file mode 100644 index 0000000000..376584574f --- /dev/null +++ b/target/linux/generic/backport-6.1/800-v6.0-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?= +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 +Reviewed-by: Florian Fainelli +Signed-off-by: Pavel Machek +--- + 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 ++ */ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#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/target/linux/generic/backport-6.1/801-v6.0-0001-dt-bindings-leds-leds-bcm63138-unify-full-stops-in-d.patch b/target/linux/generic/backport-6.1/801-v6.0-0001-dt-bindings-leds-leds-bcm63138-unify-full-stops-in-d.patch new file mode 100644 index 0000000000..483826abed --- /dev/null +++ b/target/linux/generic/backport-6.1/801-v6.0-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?= +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 +Signed-off-by: Rafał Miłecki +Signed-off-by: Pavel Machek +--- + 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/target/linux/generic/backport-6.1/801-v6.0-0002-leds-add-help-info-about-BCM63138-module-name.patch b/target/linux/generic/backport-6.1/801-v6.0-0002-leds-add-help-info-about-BCM63138-module-name.patch new file mode 100644 index 0000000000..5430b1f084 --- /dev/null +++ b/target/linux/generic/backport-6.1/801-v6.0-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?= +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 +Signed-off-by: Rafał Miłecki +Signed-off-by: Pavel Machek +--- + 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/target/linux/generic/backport-6.1/801-v6.0-0003-leds-leds-bcm63138-get-rid-of-LED_OFF.patch b/target/linux/generic/backport-6.1/801-v6.0-0003-leds-leds-bcm63138-get-rid-of-LED_OFF.patch new file mode 100644 index 0000000000..e125a54613 --- /dev/null +++ b/target/linux/generic/backport-6.1/801-v6.0-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?= +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 +Signed-off-by: Rafał Miłecki +Signed-off-by: Pavel Machek +--- + 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/target/linux/generic/backport-6.1/802-v6.1-0001-nvmem-add-driver-handling-U-Boot-environment-variabl.patch b/target/linux/generic/backport-6.1/802-v6.1-0001-nvmem-add-driver-handling-U-Boot-environment-variabl.patch new file mode 100644 index 0000000000..a40c61f929 --- /dev/null +++ b/target/linux/generic/backport-6.1/802-v6.1-0001-nvmem-add-driver-handling-U-Boot-environment-variabl.patch @@ -0,0 +1,278 @@ +From f955dc14450695564926711cf9fa8e1d5d854302 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= +Date: Wed, 15 Jun 2022 21:43:00 +0200 +Subject: [PATCH] nvmem: add driver handling U-Boot environment variables +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +U-Boot stores its setup as environment variables. It's a list of +key-value pairs stored on flash device with a custom header. + +This commit adds an NVMEM driver that: +1. Provides NVMEM access to environment vars binary data +2. Extracts variables as NVMEM cells + +Current Linux's NVMEM sysfs API allows reading whole NVMEM data block. +It can be used by user-space tools for reading U-Boot env vars block +without the hassle of finding its location. Parsing will still need to +be re-done there. + +Kernel-parsed NVMEM cells can be read however by Linux drivers. This may +be useful for Ethernet drivers for reading device MAC address which is +often stored as U-Boot env variable. + +Signed-off-by: Rafał Miłecki +Reviewed-by: Ahmad Fatoum +Signed-off-by: Srinivas Kandagatla +--- + +--- a/drivers/nvmem/Kconfig ++++ b/drivers/nvmem/Kconfig +@@ -300,4 +300,17 @@ config NVMEM_BRCM_NVRAM + This driver provides support for Broadcom's NVRAM that can be accessed + using I/O mapping. + ++config NVMEM_U_BOOT_ENV ++ tristate "U-Boot environment variables support" ++ depends on OF && MTD ++ select CRC32 ++ help ++ U-Boot stores its setup as environment variables. This driver adds ++ support for verifying & exporting such data. It also exposes variables ++ as NVMEM cells so they can be referenced by other drivers. ++ ++ Currently this drivers works only with env variables on top of MTD. ++ ++ If compiled as module it will be called nvmem_u-boot-env. ++ + endif +--- a/drivers/nvmem/Makefile ++++ b/drivers/nvmem/Makefile +@@ -61,3 +61,5 @@ obj-$(CONFIG_NVMEM_RMEM) += nvmem-rmem. + nvmem-rmem-y := rmem.o + obj-$(CONFIG_NVMEM_BRCM_NVRAM) += nvmem_brcm_nvram.o + nvmem_brcm_nvram-y := brcm_nvram.o ++obj-$(CONFIG_NVMEM_U_BOOT_ENV) += nvmem_u-boot-env.o ++nvmem_u-boot-env-y := u-boot-env.o +--- /dev/null ++++ b/drivers/nvmem/u-boot-env.c +@@ -0,0 +1,218 @@ ++// SPDX-License-Identifier: GPL-2.0-only ++/* ++ * Copyright (C) 2022 Rafał Miłecki ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++enum u_boot_env_format { ++ U_BOOT_FORMAT_SINGLE, ++ U_BOOT_FORMAT_REDUNDANT, ++}; ++ ++struct u_boot_env { ++ struct device *dev; ++ enum u_boot_env_format format; ++ ++ struct mtd_info *mtd; ++ ++ /* Cells */ ++ struct nvmem_cell_info *cells; ++ int ncells; ++}; ++ ++struct u_boot_env_image_single { ++ __le32 crc32; ++ uint8_t data[]; ++} __packed; ++ ++struct u_boot_env_image_redundant { ++ __le32 crc32; ++ u8 mark; ++ uint8_t data[]; ++} __packed; ++ ++static int u_boot_env_read(void *context, unsigned int offset, void *val, ++ size_t bytes) ++{ ++ struct u_boot_env *priv = context; ++ struct device *dev = priv->dev; ++ size_t bytes_read; ++ int err; ++ ++ err = mtd_read(priv->mtd, offset, bytes, &bytes_read, val); ++ if (err && !mtd_is_bitflip(err)) { ++ dev_err(dev, "Failed to read from mtd: %d\n", err); ++ return err; ++ } ++ ++ if (bytes_read != bytes) { ++ dev_err(dev, "Failed to read %zu bytes\n", bytes); ++ return -EIO; ++ } ++ ++ return 0; ++} ++ ++static int u_boot_env_add_cells(struct u_boot_env *priv, uint8_t *buf, ++ size_t data_offset, size_t data_len) ++{ ++ struct device *dev = priv->dev; ++ char *data = buf + data_offset; ++ char *var, *value, *eq; ++ int idx; ++ ++ priv->ncells = 0; ++ for (var = data; var < data + data_len && *var; var += strlen(var) + 1) ++ priv->ncells++; ++ ++ priv->cells = devm_kcalloc(dev, priv->ncells, sizeof(*priv->cells), GFP_KERNEL); ++ if (!priv->cells) ++ return -ENOMEM; ++ ++ for (var = data, idx = 0; ++ var < data + data_len && *var; ++ var = value + strlen(value) + 1, idx++) { ++ eq = strchr(var, '='); ++ if (!eq) ++ break; ++ *eq = '\0'; ++ value = eq + 1; ++ ++ priv->cells[idx].name = devm_kstrdup(dev, var, GFP_KERNEL); ++ if (!priv->cells[idx].name) ++ return -ENOMEM; ++ priv->cells[idx].offset = data_offset + value - data; ++ priv->cells[idx].bytes = strlen(value); ++ } ++ ++ if (WARN_ON(idx != priv->ncells)) ++ priv->ncells = idx; ++ ++ return 0; ++} ++ ++static int u_boot_env_parse(struct u_boot_env *priv) ++{ ++ struct device *dev = priv->dev; ++ size_t crc32_data_offset; ++ size_t crc32_data_len; ++ size_t crc32_offset; ++ size_t data_offset; ++ size_t data_len; ++ uint32_t crc32; ++ uint32_t calc; ++ size_t bytes; ++ uint8_t *buf; ++ int err; ++ ++ buf = kcalloc(1, priv->mtd->size, GFP_KERNEL); ++ if (!buf) { ++ err = -ENOMEM; ++ goto err_out; ++ } ++ ++ err = mtd_read(priv->mtd, 0, priv->mtd->size, &bytes, buf); ++ if ((err && !mtd_is_bitflip(err)) || bytes != priv->mtd->size) { ++ dev_err(dev, "Failed to read from mtd: %d\n", err); ++ goto err_kfree; ++ } ++ ++ switch (priv->format) { ++ case U_BOOT_FORMAT_SINGLE: ++ crc32_offset = offsetof(struct u_boot_env_image_single, crc32); ++ crc32_data_offset = offsetof(struct u_boot_env_image_single, data); ++ data_offset = offsetof(struct u_boot_env_image_single, data); ++ break; ++ case U_BOOT_FORMAT_REDUNDANT: ++ crc32_offset = offsetof(struct u_boot_env_image_redundant, crc32); ++ crc32_data_offset = offsetof(struct u_boot_env_image_redundant, mark); ++ data_offset = offsetof(struct u_boot_env_image_redundant, data); ++ break; ++ } ++ crc32 = le32_to_cpu(*(uint32_t *)(buf + crc32_offset)); ++ crc32_data_len = priv->mtd->size - crc32_data_offset; ++ data_len = priv->mtd->size - data_offset; ++ ++ calc = crc32(~0, buf + crc32_data_offset, crc32_data_len) ^ ~0L; ++ if (calc != crc32) { ++ dev_err(dev, "Invalid calculated CRC32: 0x%08x (expected: 0x%08x)\n", calc, crc32); ++ err = -EINVAL; ++ goto err_kfree; ++ } ++ ++ buf[priv->mtd->size - 1] = '\0'; ++ err = u_boot_env_add_cells(priv, buf, data_offset, data_len); ++ if (err) ++ dev_err(dev, "Failed to add cells: %d\n", err); ++ ++err_kfree: ++ kfree(buf); ++err_out: ++ return err; ++} ++ ++static int u_boot_env_probe(struct platform_device *pdev) ++{ ++ struct nvmem_config config = { ++ .name = "u-boot-env", ++ .reg_read = u_boot_env_read, ++ }; ++ struct device *dev = &pdev->dev; ++ struct device_node *np = dev->of_node; ++ struct u_boot_env *priv; ++ int err; ++ ++ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); ++ if (!priv) ++ return -ENOMEM; ++ priv->dev = dev; ++ ++ priv->format = (uintptr_t)of_device_get_match_data(dev); ++ ++ priv->mtd = of_get_mtd_device_by_node(np); ++ if (IS_ERR(priv->mtd)) { ++ dev_err_probe(dev, PTR_ERR(priv->mtd), "Failed to get %pOF MTD\n", np); ++ return PTR_ERR(priv->mtd); ++ } ++ ++ err = u_boot_env_parse(priv); ++ if (err) ++ return err; ++ ++ config.dev = dev; ++ config.cells = priv->cells; ++ config.ncells = priv->ncells; ++ config.priv = priv; ++ config.size = priv->mtd->size; ++ ++ return PTR_ERR_OR_ZERO(devm_nvmem_register(dev, &config)); ++} ++ ++static const struct of_device_id u_boot_env_of_match_table[] = { ++ { .compatible = "u-boot,env", .data = (void *)U_BOOT_FORMAT_SINGLE, }, ++ { .compatible = "u-boot,env-redundant-bool", .data = (void *)U_BOOT_FORMAT_REDUNDANT, }, ++ { .compatible = "u-boot,env-redundant-count", .data = (void *)U_BOOT_FORMAT_REDUNDANT, }, ++ {}, ++}; ++ ++static struct platform_driver u_boot_env_driver = { ++ .probe = u_boot_env_probe, ++ .driver = { ++ .name = "u_boot_env", ++ .of_match_table = u_boot_env_of_match_table, ++ }, ++}; ++module_platform_driver(u_boot_env_driver); ++ ++MODULE_AUTHOR("Rafał Miłecki"); ++MODULE_LICENSE("GPL"); ++MODULE_DEVICE_TABLE(of, u_boot_env_of_match_table); diff --git a/target/linux/generic/backport-6.1/802-v6.1-0002-nvmem-u-boot-env-find-Device-Tree-nodes-for-NVMEM-ce.patch b/target/linux/generic/backport-6.1/802-v6.1-0002-nvmem-u-boot-env-find-Device-Tree-nodes-for-NVMEM-ce.patch new file mode 100644 index 0000000000..3a6b76a221 --- /dev/null +++ b/target/linux/generic/backport-6.1/802-v6.1-0002-nvmem-u-boot-env-find-Device-Tree-nodes-for-NVMEM-ce.patch @@ -0,0 +1,29 @@ +From d69efcf951df4dcc74a0e1554969c533aec8aa9b Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= +Date: Thu, 15 Sep 2022 22:06:29 +0200 +Subject: [PATCH] nvmem: u-boot-env: find Device Tree nodes for NVMEM cells +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +DT binding allows specifying NVMEM cells as NVMEM device (provider) +subnodes. Looks for such subnodes when building NVMEM cells. + +This allows NVMEM consumers to use U-Boot environment variables. + +Signed-off-by: Rafał Miłecki +Signed-off-by: Srinivas Kandagatla +--- + drivers/nvmem/u-boot-env.c | 1 + + 1 file changed, 1 insertion(+) + +--- a/drivers/nvmem/u-boot-env.c ++++ b/drivers/nvmem/u-boot-env.c +@@ -92,6 +92,7 @@ static int u_boot_env_add_cells(struct u + return -ENOMEM; + priv->cells[idx].offset = data_offset + value - data; + priv->cells[idx].bytes = strlen(value); ++ priv->cells[idx].np = of_get_child_by_name(dev->of_node, priv->cells[idx].name); + } + + if (WARN_ON(idx != priv->ncells)) diff --git a/target/linux/generic/backport-6.1/802-v6.1-0003-nvmem-u-boot-env-fix-crc32-casting-type.patch b/target/linux/generic/backport-6.1/802-v6.1-0003-nvmem-u-boot-env-fix-crc32-casting-type.patch new file mode 100644 index 0000000000..6b40557116 --- /dev/null +++ b/target/linux/generic/backport-6.1/802-v6.1-0003-nvmem-u-boot-env-fix-crc32-casting-type.patch @@ -0,0 +1,30 @@ +From 60bbaad38109684b156e21112322e0a922f92cde Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= +Date: Thu, 18 Aug 2022 06:38:37 +0200 +Subject: [PATCH] nvmem: u-boot-env: fix crc32 casting type +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +This fixes: +drivers/nvmem/u-boot-env.c:141:17: sparse: sparse: cast to restricted __le32 + +Reported-by: kernel test robot +Fixes: f955dc1445069 ("nvmem: add driver handling U-Boot environment variables") +Signed-off-by: Rafał Miłecki +Signed-off-by: Srinivas Kandagatla +--- + drivers/nvmem/u-boot-env.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/drivers/nvmem/u-boot-env.c ++++ b/drivers/nvmem/u-boot-env.c +@@ -139,7 +139,7 @@ static int u_boot_env_parse(struct u_boo + data_offset = offsetof(struct u_boot_env_image_redundant, data); + break; + } +- crc32 = le32_to_cpu(*(uint32_t *)(buf + crc32_offset)); ++ crc32 = le32_to_cpu(*(__le32 *)(buf + crc32_offset)); + crc32_data_len = priv->mtd->size - crc32_data_offset; + data_len = priv->mtd->size - data_offset; + diff --git a/target/linux/generic/backport-6.1/803-v5.19-nvmem-core-support-passing-DT-node-in-cell-info.patch b/target/linux/generic/backport-6.1/803-v5.19-nvmem-core-support-passing-DT-node-in-cell-info.patch new file mode 100644 index 0000000000..1acc6947fe --- /dev/null +++ b/target/linux/generic/backport-6.1/803-v5.19-nvmem-core-support-passing-DT-node-in-cell-info.patch @@ -0,0 +1,41 @@ +From dbc2f62061c6bfba0aee93161ee3194dcee84bd0 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= +Date: Fri, 29 Apr 2022 17:26:46 +0100 +Subject: [PATCH] nvmem: core: support passing DT node in cell info +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Some hardware may have NVMEM cells described in Device Tree using +individual nodes. Let drivers pass such nodes to the NVMEM subsystem so +they can be later used by NVMEM consumers. + +Signed-off-by: Rafał Miłecki +Signed-off-by: Srinivas Kandagatla +Link: https://lore.kernel.org/r/20220429162701.2222-2-srinivas.kandagatla@linaro.org +Signed-off-by: Greg Kroah-Hartman +--- + drivers/nvmem/core.c | 1 + + include/linux/nvmem-consumer.h | 1 + + 2 files changed, 2 insertions(+) + +--- a/drivers/nvmem/core.c ++++ b/drivers/nvmem/core.c +@@ -462,6 +462,7 @@ static int nvmem_cell_info_to_nvmem_cell + + cell->bit_offset = info->bit_offset; + cell->nbits = info->nbits; ++ cell->np = info->np; + + if (cell->nbits) + cell->bytes = DIV_ROUND_UP(cell->nbits + cell->bit_offset, +--- a/include/linux/nvmem-consumer.h ++++ b/include/linux/nvmem-consumer.h +@@ -25,6 +25,7 @@ struct nvmem_cell_info { + unsigned int bytes; + unsigned int bit_offset; + unsigned int nbits; ++ struct device_node *np; + }; + + /** diff --git a/target/linux/generic/backport-6.1/810-v5.17-net-qmi_wwan-add-ZTE-MF286D-modem-19d2-1485.patch b/target/linux/generic/backport-6.1/810-v5.17-net-qmi_wwan-add-ZTE-MF286D-modem-19d2-1485.patch new file mode 100644 index 0000000000..25802be7e3 --- /dev/null +++ b/target/linux/generic/backport-6.1/810-v5.17-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 +Date: Tue, 11 Jan 2022 23:11:32 +0100 +Subject: [PATCH 1/1] 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 +Acked-by: Bjørn Mork +Signed-off-by: David S. Miller +--- + 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 +@@ -1314,6 +1314,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/target/linux/generic/backport-6.1/821-v5.16-Bluetooth-btusb-Support-public-address-configuration.patch b/target/linux/generic/backport-6.1/821-v5.16-Bluetooth-btusb-Support-public-address-configuration.patch new file mode 100644 index 0000000000..feaabc9d11 --- /dev/null +++ b/target/linux/generic/backport-6.1/821-v5.16-Bluetooth-btusb-Support-public-address-configuration.patch @@ -0,0 +1,51 @@ +From 5cb03751455c299b1bf10cb48631bf359cfb11b5 Mon Sep 17 00:00:00 2001 +From: "mark-yw.chen" +Date: Wed, 1 Sep 2021 11:32:25 +0800 +Subject: [PATCH 1/5] Bluetooth: btusb: Support public address configuration + for MediaTek Chip. + +The MediaTek chip support vendor specific HCI command(0xfc1a) to +change the public address. Add hdev->set_bdaddr handler for MediaTek +Chip. After doing a power cycle or MediaTek Bluetooth reset, BD_ADDR +will bring back the original one. + +Signed-off-by: mark-yw.chen +Signed-off-by: Marcel Holtmann +--- + drivers/bluetooth/btusb.c | 18 ++++++++++++++++++ + 1 file changed, 18 insertions(+) + +--- a/drivers/bluetooth/btusb.c ++++ b/drivers/bluetooth/btusb.c +@@ -2263,6 +2263,23 @@ struct btmtk_section_map { + }; + } __packed; + ++static int btusb_set_bdaddr_mtk(struct hci_dev *hdev, const bdaddr_t *bdaddr) ++{ ++ struct sk_buff *skb; ++ long ret; ++ ++ skb = __hci_cmd_sync(hdev, 0xfc1a, sizeof(bdaddr), bdaddr, HCI_INIT_TIMEOUT); ++ if (IS_ERR(skb)) { ++ ret = PTR_ERR(skb); ++ bt_dev_err(hdev, "changing Mediatek device address failed (%ld)", ++ ret); ++ return ret; ++ } ++ kfree_skb(skb); ++ ++ return 0; ++} ++ + static void btusb_mtk_wmt_recv(struct urb *urb) + { + struct hci_dev *hdev = urb->context; +@@ -3900,6 +3917,7 @@ static int btusb_probe(struct usb_interf + hdev->shutdown = btusb_mtk_shutdown; + hdev->manufacturer = 70; + hdev->cmd_timeout = btusb_mtk_cmd_timeout; ++ hdev->set_bdaddr = btusb_set_bdaddr_mtk; + set_bit(HCI_QUIRK_NON_PERSISTENT_SETUP, &hdev->quirks); + data->recv_acl = btusb_recv_acl_mtk; + } diff --git a/target/linux/generic/backport-6.1/822-v5.17-Bluetooth-btusb-Fix-application-of-sizeof-to-pointer.patch b/target/linux/generic/backport-6.1/822-v5.17-Bluetooth-btusb-Fix-application-of-sizeof-to-pointer.patch new file mode 100644 index 0000000000..b324cb1aa8 --- /dev/null +++ b/target/linux/generic/backport-6.1/822-v5.17-Bluetooth-btusb-Fix-application-of-sizeof-to-pointer.patch @@ -0,0 +1,29 @@ +From af774a731f7b4c2a90a8476cd44045ba8d1263ba Mon Sep 17 00:00:00 2001 +From: David Yang +Date: Wed, 13 Oct 2021 08:56:33 +0800 +Subject: [PATCH 2/5] Bluetooth: btusb: Fix application of sizeof to pointer + +The coccinelle check report: +"./drivers/bluetooth/btusb.c:2239:36-42: +ERROR: application of sizeof to pointer". +Using the real size to fix it. + +Fixes: 5a87679ffd443 ("Bluetooth: btusb: Support public address configuration for MediaTek Chip.") +Reported-by: Zeal Robot +Signed-off-by: David Yang +Signed-off-by: Marcel Holtmann +--- + drivers/bluetooth/btusb.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/drivers/bluetooth/btusb.c ++++ b/drivers/bluetooth/btusb.c +@@ -2268,7 +2268,7 @@ static int btusb_set_bdaddr_mtk(struct h + struct sk_buff *skb; + long ret; + +- skb = __hci_cmd_sync(hdev, 0xfc1a, sizeof(bdaddr), bdaddr, HCI_INIT_TIMEOUT); ++ skb = __hci_cmd_sync(hdev, 0xfc1a, 6, bdaddr, HCI_INIT_TIMEOUT); + if (IS_ERR(skb)) { + ret = PTR_ERR(skb); + bt_dev_err(hdev, "changing Mediatek device address failed (%ld)", diff --git a/target/linux/generic/backport-6.1/823-v5.18-Bluetooth-btusb-Add-a-new-PID-VID-13d3-3567-for-MT79.patch b/target/linux/generic/backport-6.1/823-v5.18-Bluetooth-btusb-Add-a-new-PID-VID-13d3-3567-for-MT79.patch new file mode 100644 index 0000000000..549005c5a7 --- /dev/null +++ b/target/linux/generic/backport-6.1/823-v5.18-Bluetooth-btusb-Add-a-new-PID-VID-13d3-3567-for-MT79.patch @@ -0,0 +1,70 @@ +From e57186fc02cedff191c469a26cce615371e41740 Mon Sep 17 00:00:00 2001 +From: Yake Yang +Date: Wed, 23 Feb 2022 07:55:59 +0800 +Subject: [PATCH 3/5] Bluetooth: btusb: Add a new PID/VID 13d3/3567 for MT7921 + +Add VID 13D3 & PID 3567 for MediaTek MT7921 USB Bluetooth chip. + +The information in /sys/kernel/debug/usb/devices about the Bluetooth +device is listed as the below. + +T: Bus=05 Lev=01 Prnt=01 Port=00 Cnt=01 Dev#= 2 Spd=480 MxCh= 0 +D: Ver= 2.10 Cls=ef(misc ) Sub=02 Prot=01 MxPS=64 #Cfgs= 1 +P: Vendor=13d3 ProdID=3567 Rev= 1.00 +S: Manufacturer=MediaTek Inc. +S: Product=Wireless_Device +S: SerialNumber=000000000 +C:* #Ifs= 3 Cfg#= 1 Atr=e0 MxPwr=100mA +A: FirstIf#= 0 IfCount= 3 Cls=e0(wlcon) Sub=01 Prot=01 +I:* If#= 0 Alt= 0 #EPs= 3 Cls=e0(wlcon) Sub=01 Prot=01 Driver=btusb +E: Ad=81(I) Atr=03(Int.) MxPS= 16 Ivl=125us +E: Ad=82(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms +E: Ad=02(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms +I:* If#= 1 Alt= 0 #EPs= 2 Cls=e0(wlcon) Sub=01 Prot=01 Driver=btusb +E: Ad=83(I) Atr=01(Isoc) MxPS= 0 Ivl=1ms +E: Ad=03(O) Atr=01(Isoc) MxPS= 0 Ivl=1ms +I: If#= 1 Alt= 1 #EPs= 2 Cls=e0(wlcon) Sub=01 Prot=01 Driver=btusb +E: Ad=83(I) Atr=01(Isoc) MxPS= 9 Ivl=1ms +E: Ad=03(O) Atr=01(Isoc) MxPS= 9 Ivl=1ms +I: If#= 1 Alt= 2 #EPs= 2 Cls=e0(wlcon) Sub=01 Prot=01 Driver=btusb +E: Ad=83(I) Atr=01(Isoc) MxPS= 17 Ivl=1ms +E: Ad=03(O) Atr=01(Isoc) MxPS= 17 Ivl=1ms +I: If#= 1 Alt= 3 #EPs= 2 Cls=e0(wlcon) Sub=01 Prot=01 Driver=btusb +E: Ad=83(I) Atr=01(Isoc) MxPS= 25 Ivl=1ms +E: Ad=03(O) Atr=01(Isoc) MxPS= 25 Ivl=1ms +I: If#= 1 Alt= 4 #EPs= 2 Cls=e0(wlcon) Sub=01 Prot=01 Driver=btusb +E: Ad=83(I) Atr=01(Isoc) MxPS= 33 Ivl=1ms +E: Ad=03(O) Atr=01(Isoc) MxPS= 33 Ivl=1ms +I: If#= 1 Alt= 5 #EPs= 2 Cls=e0(wlcon) Sub=01 Prot=01 Driver=btusb +E: Ad=83(I) Atr=01(Isoc) MxPS= 49 Ivl=1ms +E: Ad=03(O) Atr=01(Isoc) MxPS= 49 Ivl=1ms +I: If#= 1 Alt= 6 #EPs= 2 Cls=e0(wlcon) Sub=01 Prot=01 Driver=btusb +E: Ad=83(I) Atr=01(Isoc) MxPS= 63 Ivl=1ms +E: Ad=03(O) Atr=01(Isoc) MxPS= 63 Ivl=1ms +I:* If#= 2 Alt= 0 #EPs= 2 Cls=e0(wlcon) Sub=01 Prot=01 Driver=(none) +E: Ad=8a(I) Atr=03(Int.) MxPS= 64 Ivl=125us +E: Ad=0a(O) Atr=03(Int.) MxPS= 64 Ivl=125us +I: If#= 2 Alt= 1 #EPs= 2 Cls=e0(wlcon) Sub=01 Prot=01 Driver=(none) +E: Ad=8a(I) Atr=03(Int.) MxPS= 64 Ivl=125us +E: Ad=0a(O) Atr=03(Int.) MxPS= 64 Ivl=125us + +Co-developed-by: Sean Wang +Signed-off-by: Sean Wang +Signed-off-by: Yake Yang +Signed-off-by: Marcel Holtmann +--- + drivers/bluetooth/btusb.c | 3 +++ + 1 file changed, 3 insertions(+) + +--- a/drivers/bluetooth/btusb.c ++++ b/drivers/bluetooth/btusb.c +@@ -460,6 +460,9 @@ static const struct usb_device_id blackl + { USB_DEVICE(0x13d3, 0x3564), .driver_info = BTUSB_MEDIATEK | + BTUSB_WIDEBAND_SPEECH | + BTUSB_VALID_LE_STATES }, ++ { USB_DEVICE(0x13d3, 0x3567), .driver_info = BTUSB_MEDIATEK | ++ BTUSB_WIDEBAND_SPEECH | ++ BTUSB_VALID_LE_STATES }, + { USB_DEVICE(0x0489, 0xe0cd), .driver_info = BTUSB_MEDIATEK | + BTUSB_WIDEBAND_SPEECH | + BTUSB_VALID_LE_STATES }, diff --git a/target/linux/generic/backport-6.1/824-v5.19-Bluetooth-btusb-Add-a-new-PID-VID-0489-e0c8-for-MT79.patch b/target/linux/generic/backport-6.1/824-v5.19-Bluetooth-btusb-Add-a-new-PID-VID-0489-e0c8-for-MT79.patch new file mode 100644 index 0000000000..54e3388860 --- /dev/null +++ b/target/linux/generic/backport-6.1/824-v5.19-Bluetooth-btusb-Add-a-new-PID-VID-0489-e0c8-for-MT79.patch @@ -0,0 +1,68 @@ +From e507366cd1e8e1d4eebe537c08fd142cf0b617fa Mon Sep 17 00:00:00 2001 +From: Sean Wang +Date: Thu, 28 Apr 2022 02:38:39 +0800 +Subject: [PATCH 4/5] Bluetooth: btusb: Add a new PID/VID 0489/e0c8 for MT7921 + +Add VID 0489 & PID e0c8 for MediaTek MT7921 USB Bluetooth chip. + +The information in /sys/kernel/debug/usb/devices about the Bluetooth +device is listed as the below. + +T: Bus=01 Lev=01 Prnt=01 Port=13 Cnt=03 Dev#= 4 Spd=480 MxCh= 0 +D: Ver= 2.10 Cls=ef(misc ) Sub=02 Prot=01 MxPS=64 #Cfgs= 1 +P: Vendor=0489 ProdID=e0c8 Rev= 1.00 +S: Manufacturer=MediaTek Inc. +S: Product=Wireless_Device +S: SerialNumber=000000000 +C:* #Ifs= 3 Cfg#= 1 Atr=e0 MxPwr=100mA +A: FirstIf#= 0 IfCount= 3 Cls=e0(wlcon) Sub=01 Prot=01 +I:* If#= 0 Alt= 0 #EPs= 3 Cls=e0(wlcon) Sub=01 Prot=01 Driver=btusb +E: Ad=81(I) Atr=03(Int.) MxPS= 16 Ivl=125us +E: Ad=82(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms +E: Ad=02(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms +I:* If#= 1 Alt= 0 #EPs= 2 Cls=e0(wlcon) Sub=01 Prot=01 Driver=btusb +E: Ad=83(I) Atr=01(Isoc) MxPS= 0 Ivl=1ms +E: Ad=03(O) Atr=01(Isoc) MxPS= 0 Ivl=1ms +I: If#= 1 Alt= 1 #EPs= 2 Cls=e0(wlcon) Sub=01 Prot=01 Driver=btusb +E: Ad=83(I) Atr=01(Isoc) MxPS= 9 Ivl=1ms +E: Ad=03(O) Atr=01(Isoc) MxPS= 9 Ivl=1ms +I: If#= 1 Alt= 2 #EPs= 2 Cls=e0(wlcon) Sub=01 Prot=01 Driver=btusb +E: Ad=83(I) Atr=01(Isoc) MxPS= 17 Ivl=1ms +E: Ad=03(O) Atr=01(Isoc) MxPS= 17 Ivl=1ms +I: If#= 1 Alt= 3 #EPs= 2 Cls=e0(wlcon) Sub=01 Prot=01 Driver=btusb +E: Ad=83(I) Atr=01(Isoc) MxPS= 25 Ivl=1ms +E: Ad=03(O) Atr=01(Isoc) MxPS= 25 Ivl=1ms +I: If#= 1 Alt= 4 #EPs= 2 Cls=e0(wlcon) Sub=01 Prot=01 Driver=btusb +E: Ad=83(I) Atr=01(Isoc) MxPS= 33 Ivl=1ms +E: Ad=03(O) Atr=01(Isoc) MxPS= 33 Ivl=1ms +I: If#= 1 Alt= 5 #EPs= 2 Cls=e0(wlcon) Sub=01 Prot=01 Driver=btusb +E: Ad=83(I) Atr=01(Isoc) MxPS= 49 Ivl=1ms +E: Ad=03(O) Atr=01(Isoc) MxPS= 49 Ivl=1ms +I: If#= 1 Alt= 6 #EPs= 2 Cls=e0(wlcon) Sub=01 Prot=01 Driver=btusb +E: Ad=83(I) Atr=01(Isoc) MxPS= 63 Ivl=1ms +E: Ad=03(O) Atr=01(Isoc) MxPS= 63 Ivl=1ms +I:* If#= 2 Alt= 0 #EPs= 2 Cls=e0(wlcon) Sub=01 Prot=01 Driver=(none) +E: Ad=8a(I) Atr=03(Int.) MxPS= 64 Ivl=125us +E: Ad=0a(O) Atr=03(Int.) MxPS= 64 Ivl=125us +I: If#= 2 Alt= 1 #EPs= 2 Cls=e0(wlcon) Sub=01 Prot=01 Driver=(none) +E: Ad=8a(I) Atr=03(Int.) MxPS= 512 Ivl=125us +E: Ad=0a(O) Atr=03(Int.) MxPS= 512 Ivl=125us + +Signed-off-by: Sean Wang +Signed-off-by: Marcel Holtmann +--- + drivers/bluetooth/btusb.c | 3 +++ + 1 file changed, 3 insertions(+) + +--- a/drivers/bluetooth/btusb.c ++++ b/drivers/bluetooth/btusb.c +@@ -451,6 +451,9 @@ static const struct usb_device_id blackl + BTUSB_VALID_LE_STATES }, + + /* Additional MediaTek MT7921 Bluetooth devices */ ++ { USB_DEVICE(0x0489, 0xe0c8), .driver_info = BTUSB_MEDIATEK | ++ BTUSB_WIDEBAND_SPEECH | ++ BTUSB_VALID_LE_STATES }, + { USB_DEVICE(0x04ca, 0x3802), .driver_info = BTUSB_MEDIATEK | + BTUSB_WIDEBAND_SPEECH | + BTUSB_VALID_LE_STATES }, diff --git a/target/linux/generic/backport-6.1/825-v6.1-Bluetooth-btusb-Add-a-new-VID-PID-0e8d-0608-for-MT79.patch b/target/linux/generic/backport-6.1/825-v6.1-Bluetooth-btusb-Add-a-new-VID-PID-0e8d-0608-for-MT79.patch new file mode 100644 index 0000000000..8957ff8e22 --- /dev/null +++ b/target/linux/generic/backport-6.1/825-v6.1-Bluetooth-btusb-Add-a-new-VID-PID-0e8d-0608-for-MT79.patch @@ -0,0 +1,66 @@ +From be55622ce673f9692cc15d26d77a050cda42a3d3 Mon Sep 17 00:00:00 2001 +From: Daniel Golle +Date: Fri, 9 Sep 2022 21:00:30 +0100 +Subject: [PATCH 1/1] Bluetooth: btusb: Add a new VID/PID 0e8d/0608 for MT7921 + +Add a new PID/VID 0e8d/0608 for MT7921K chip found on AMD RZ608 module. + +From /sys/kernel/debug/usb/devices: +T: Bus=01 Lev=02 Prnt=02 Port=01 Cnt=01 Dev#= 3 Spd=480 MxCh= 0 +D: Ver= 2.10 Cls=ef(misc ) Sub=02 Prot=01 MxPS=64 #Cfgs= 1 +P: Vendor=0e8d ProdID=0608 Rev= 1.00 +S: Manufacturer=MediaTek Inc. +S: Product=Wireless_Device +S: SerialNumber=000000000 +C:* #Ifs= 3 Cfg#= 1 Atr=e0 MxPwr=100mA +A: FirstIf#= 0 IfCount= 3 Cls=e0(wlcon) Sub=01 Prot=01 +I:* If#= 0 Alt= 0 #EPs= 3 Cls=e0(wlcon) Sub=01 Prot=01 Driver=btusb +E: Ad=81(I) Atr=03(Int.) MxPS= 16 Ivl=125us +E: Ad=82(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms +E: Ad=02(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms +I:* If#= 1 Alt= 0 #EPs= 2 Cls=e0(wlcon) Sub=01 Prot=01 Driver=btusb +E: Ad=83(I) Atr=01(Isoc) MxPS= 0 Ivl=1ms +E: Ad=03(O) Atr=01(Isoc) MxPS= 0 Ivl=1ms +I: If#= 1 Alt= 1 #EPs= 2 Cls=e0(wlcon) Sub=01 Prot=01 Driver=btusb +E: Ad=83(I) Atr=01(Isoc) MxPS= 9 Ivl=1ms +E: Ad=03(O) Atr=01(Isoc) MxPS= 9 Ivl=1ms +I: If#= 1 Alt= 2 #EPs= 2 Cls=e0(wlcon) Sub=01 Prot=01 Driver=btusb +E: Ad=83(I) Atr=01(Isoc) MxPS= 17 Ivl=1ms +E: Ad=03(O) Atr=01(Isoc) MxPS= 17 Ivl=1ms +I: If#= 1 Alt= 3 #EPs= 2 Cls=e0(wlcon) Sub=01 Prot=01 Driver=btusb +E: Ad=83(I) Atr=01(Isoc) MxPS= 25 Ivl=1ms +E: Ad=03(O) Atr=01(Isoc) MxPS= 25 Ivl=1ms +I: If#= 1 Alt= 4 #EPs= 2 Cls=e0(wlcon) Sub=01 Prot=01 Driver=btusb +E: Ad=83(I) Atr=01(Isoc) MxPS= 33 Ivl=1ms +E: Ad=03(O) Atr=01(Isoc) MxPS= 33 Ivl=1ms +I: If#= 1 Alt= 5 #EPs= 2 Cls=e0(wlcon) Sub=01 Prot=01 Driver=btusb +E: Ad=83(I) Atr=01(Isoc) MxPS= 49 Ivl=1ms +E: Ad=03(O) Atr=01(Isoc) MxPS= 49 Ivl=1ms +I: If#= 1 Alt= 6 #EPs= 2 Cls=e0(wlcon) Sub=01 Prot=01 Driver=btusb +E: Ad=83(I) Atr=01(Isoc) MxPS= 63 Ivl=1ms +E: Ad=03(O) Atr=01(Isoc) MxPS= 63 Ivl=1ms +I:* If#= 2 Alt= 0 #EPs= 2 Cls=e0(wlcon) Sub=01 Prot=01 Driver=(none) +E: Ad=8a(I) Atr=03(Int.) MxPS= 64 Ivl=125us +E: Ad=0a(O) Atr=03(Int.) MxPS= 64 Ivl=125us +I: If#= 2 Alt= 1 #EPs= 2 Cls=e0(wlcon) Sub=01 Prot=01 Driver=(none) +E: Ad=8a(I) Atr=03(Int.) MxPS= 64 Ivl=125us +E: Ad=0a(O) Atr=03(Int.) MxPS= 64 Ivl=125us + +Signed-off-by: Daniel Golle +Signed-off-by: Luiz Augusto von Dentz +--- + drivers/bluetooth/btusb.c | 3 +++ + 1 file changed, 3 insertions(+) + +--- a/drivers/bluetooth/btusb.c ++++ b/drivers/bluetooth/btusb.c +@@ -469,6 +469,9 @@ static const struct usb_device_id blackl + { USB_DEVICE(0x0489, 0xe0cd), .driver_info = BTUSB_MEDIATEK | + BTUSB_WIDEBAND_SPEECH | + BTUSB_VALID_LE_STATES }, ++ { USB_DEVICE(0x0e8d, 0x0608), .driver_info = BTUSB_MEDIATEK | ++ BTUSB_WIDEBAND_SPEECH | ++ BTUSB_VALID_LE_STATES }, + + /* MediaTek MT7922A Bluetooth devices */ + { USB_DEVICE(0x0489, 0xe0d8), .driver_info = BTUSB_MEDIATEK | diff --git a/target/linux/generic/backport-6.1/860-v5.17-MIPS-ath79-drop-_machine_restart-again.patch b/target/linux/generic/backport-6.1/860-v5.17-MIPS-ath79-drop-_machine_restart-again.patch new file mode 100644 index 0000000000..e9d692b651 --- /dev/null +++ b/target/linux/generic/backport-6.1/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 +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 +Cc: Florian Fainelli +Signed-off-by: Lech Perczak +Signed-off-by: Thomas Bogendoerfer +--- + 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/target/linux/generic/backport-6.1/870-v5.18-hwmon-lm70-Add-ti-tmp125-support.patch b/target/linux/generic/backport-6.1/870-v5.18-hwmon-lm70-Add-ti-tmp125-support.patch new file mode 100644 index 0000000000..fabf177628 --- /dev/null +++ b/target/linux/generic/backport-6.1/870-v5.18-hwmon-lm70-Add-ti-tmp125-support.patch @@ -0,0 +1,71 @@ +From 31d8f414e1596ba54a4315418e4c0086fda9e428 Mon Sep 17 00:00:00 2001 +From: Christian Lamparter +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 +Link: https://lore.kernel.org/r/43b19cbd4e7f51e9509e561b02b5d8d0e7079fac.1645175187.git.chunkeey@gmail.com +Signed-off-by: Guenter Roeck +--- +--- 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/target/linux/generic/backport-6.1/880-v5.19-cdc_ether-export-usbnet_cdc_zte_rx_fixup.patch b/target/linux/generic/backport-6.1/880-v5.19-cdc_ether-export-usbnet_cdc_zte_rx_fixup.patch new file mode 100644 index 0000000000..39fdb32773 --- /dev/null +++ b/target/linux/generic/backport-6.1/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 +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 +Cc: Bjørn Mork +Cc: Oliver Neukum +Signed-off-by: Lech Perczak +--- + 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/target/linux/generic/backport-6.1/881-v5.19-rndis_host-enable-the-bogus-MAC-fixup-for-ZTE-device.patch b/target/linux/generic/backport-6.1/881-v5.19-rndis_host-enable-the-bogus-MAC-fixup-for-ZTE-device.patch new file mode 100644 index 0000000000..7da2280cd9 --- /dev/null +++ b/target/linux/generic/backport-6.1/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 +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 +Cc: Kristian Evensen +Cc: Oliver Neukum +Signed-off-by: Lech Perczak +--- + 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/target/linux/generic/backport-6.1/882-v5.19-rndis_host-limit-scope-of-bogus-MAC-address-detectio.patch b/target/linux/generic/backport-6.1/882-v5.19-rndis_host-limit-scope-of-bogus-MAC-address-detectio.patch new file mode 100644 index 0000000000..ebe7a43fd3 --- /dev/null +++ b/target/linux/generic/backport-6.1/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 +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 +Cc: Kristian Evensen +Cc: Oliver Neukum +Signed-off-by: Lech Perczak +--- + 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/target/linux/generic/config-6.1 b/target/linux/generic/config-6.1 new file mode 100644 index 0000000000..e8b0b3a168 --- /dev/null +++ b/target/linux/generic/config-6.1 @@ -0,0 +1,7559 @@ +# CONFIG_104_QUAD_8 is not set +CONFIG_32BIT=y +CONFIG_64BIT_TIME=y +# CONFIG_6LOWPAN is not set +# CONFIG_6LOWPAN_DEBUGFS is not set +# CONFIG_6PACK is not set +# CONFIG_8139CP is not set +# CONFIG_8139TOO is not set +# CONFIG_9P_FS is not set +# CONFIG_AB3100_CORE is not set +# CONFIG_AB8500_CORE is not set +# CONFIG_ABP060MG is not set +# CONFIG_ABX500_CORE is not set +# CONFIG_ACCESSIBILITY is not set +# CONFIG_ACENIC is not set +# CONFIG_ACERHDF is not set +# CONFIG_ACER_WIRELESS is not set +# CONFIG_ACORN_PARTITION is not set +# CONFIG_ACPI_ALS is not set +# CONFIG_ACPI_APEI is not set +# CONFIG_ACPI_APEI_PCIEAER is not set +# CONFIG_ACPI_BUTTON is not set +# CONFIG_ACPI_CONFIGFS is not set +# CONFIG_ACPI_CUSTOM_METHOD is not set +# CONFIG_ACPI_EXTLOG is not set +# CONFIG_ACPI_HED is not set +# CONFIG_ACPI_NFIT is not set +# CONFIG_ACPI_REDUCED_HARDWARE_ONLY is not set +# CONFIG_ACPI_TABLE_UPGRADE is not set +# CONFIG_ACPI_VIDEO is not set +# CONFIG_AD2S1200 is not set +# CONFIG_AD2S1210 is not set +# CONFIG_AD2S90 is not set +# CONFIG_AD5064 is not set +# CONFIG_AD5110 is not set +# CONFIG_AD525X_DPOT is not set +# CONFIG_AD5272 is not set +# CONFIG_AD5360 is not set +# CONFIG_AD5380 is not set +# CONFIG_AD5421 is not set +# CONFIG_AD5446 is not set +# CONFIG_AD5449 is not set +# CONFIG_AD5504 is not set +# CONFIG_AD5592R is not set +# CONFIG_AD5593R is not set +# CONFIG_AD5624R_SPI is not set +# CONFIG_AD5686 is not set +# CONFIG_AD5686_SPI is not set +# CONFIG_AD5696_I2C is not set +# CONFIG_AD5755 is not set +# CONFIG_AD5758 is not set +# CONFIG_AD5761 is not set +# CONFIG_AD5764 is not set +# CONFIG_AD5766 is not set +# CONFIG_AD5770R is not set +# CONFIG_AD5791 is not set +# CONFIG_AD5933 is not set +# CONFIG_AD7091R5 is not set +# CONFIG_AD7124 is not set +# CONFIG_AD7150 is not set +# CONFIG_AD7152 is not set +# CONFIG_AD7192 is not set +# CONFIG_AD7266 is not set +# CONFIG_AD7280 is not set +# CONFIG_AD7291 is not set +# CONFIG_AD7292 is not set +# CONFIG_AD7298 is not set +# CONFIG_AD7303 is not set +# CONFIG_AD7476 is not set +# CONFIG_AD7606 is not set +# CONFIG_AD7606_IFACE_PARALLEL is not set +# CONFIG_AD7606_IFACE_SPI is not set +# CONFIG_AD7746 is not set +# CONFIG_AD7766 is not set +# CONFIG_AD7768_1 is not set +# CONFIG_AD7780 is not set +# CONFIG_AD7791 is not set +# CONFIG_AD7793 is not set +# CONFIG_AD7816 is not set +# CONFIG_AD7887 is not set +# CONFIG_AD7923 is not set +# CONFIG_AD7949 is not set +# CONFIG_AD799X is not set +# CONFIG_AD8366 is not set +# CONFIG_AD8801 is not set +# CONFIG_AD9467 is not set +# CONFIG_AD9523 is not set +# CONFIG_AD9832 is not set +# CONFIG_AD9834 is not set +# CONFIG_ADAPTEC_STARFIRE is not set +# CONFIG_ADE7854 is not set +# CONFIG_ADF4350 is not set +# CONFIG_ADF4371 is not set +# CONFIG_ADFS_FS is not set +# CONFIG_ADIN_PHY is not set +# CONFIG_ADIS16080 is not set +# CONFIG_ADIS16130 is not set +# CONFIG_ADIS16136 is not set +# CONFIG_ADIS16201 is not set +# CONFIG_ADIS16203 is not set +# CONFIG_ADIS16209 is not set +# CONFIG_ADIS16240 is not set +# CONFIG_ADIS16260 is not set +# CONFIG_ADIS16400 is not set +# CONFIG_ADIS16460 is not set +# CONFIG_ADIS16475 is not set +# CONFIG_ADIS16480 is not set +# CONFIG_ADI_AXI_ADC is not set +# CONFIG_ADJD_S311 is not set +# CONFIG_ADM6996_PHY is not set +# CONFIG_ADM8211 is not set +# CONFIG_ADT7316 is not set +# CONFIG_ADUX1020 is not set +CONFIG_ADVISE_SYSCALLS=y +# CONFIG_ADXL345_I2C is not set +# CONFIG_ADXL345_SPI is not set +# CONFIG_ADXL372_I2C is not set +# CONFIG_ADXL372_SPI is not set +# CONFIG_ADXRS290 is not set +# CONFIG_ADXRS450 is not set +CONFIG_AEABI=y +# CONFIG_AFE4403 is not set +# CONFIG_AFE4404 is not set +# CONFIG_AFFS_FS is not set +# CONFIG_AFS_DEBUG_CURSOR is not set +# CONFIG_AFS_FS is not set +# CONFIG_AF_KCM is not set +# CONFIG_AF_RXRPC is not set +# CONFIG_AF_RXRPC_INJECT_LOSS is not set +# CONFIG_AF_RXRPC_IPV6 is not set +CONFIG_AF_UNIX_OOB=y +# CONFIG_AGP is not set +# CONFIG_AHCI_CEVA is not set +# CONFIG_AHCI_IMX is not set +# CONFIG_AHCI_MVEBU is not set +# CONFIG_AHCI_QORIQ is not set +# CONFIG_AHCI_XGENE is not set +CONFIG_AIO=y +# CONFIG_AIRO is not set +# CONFIG_AIRO_CS is not set +# CONFIG_AIX_PARTITION is not set +# CONFIG_AK09911 is not set +# CONFIG_AK8974 is not set +# CONFIG_AK8975 is not set +# CONFIG_AL3010 is not set +# CONFIG_AL3320A is not set +# CONFIG_ALIM7101_WDT is not set +CONFIG_ALLOW_DEV_COREDUMP=y +# CONFIG_ALTERA_MBOX is not set +# CONFIG_ALTERA_MSGDMA is not set +# CONFIG_ALTERA_STAPL is not set +# CONFIG_ALTERA_TSE is not set +# CONFIG_ALX is not set +# CONFIG_AL_FIC is not set +# CONFIG_AM2315 is not set +# CONFIG_AM335X_PHY_USB is not set +# CONFIG_AMBA_PL08X is not set +# CONFIG_AMD8111_ETH is not set +# CONFIG_AMD_MEM_ENCRYPT is not set +# CONFIG_AMD_PHY is not set +# CONFIG_AMD_XGBE is not set +# CONFIG_AMD_XGBE_HAVE_ECC is not set +# CONFIG_AMIGA_PARTITION is not set +# CONFIG_AMILO_RFKILL is not set +# CONFIG_ANDROID is not set +CONFIG_ANON_INODES=y +# CONFIG_APDS9300 is not set +# CONFIG_APDS9802ALS is not set +# CONFIG_APDS9960 is not set +# CONFIG_APM8018X is not set +# CONFIG_APM_EMULATION is not set +# CONFIG_APPLE_GMUX is not set +# CONFIG_APPLE_MFI_FASTCHARGE is not set +# CONFIG_APPLE_PROPERTIES is not set +# CONFIG_APPLICOM is not set +# CONFIG_AQTION is not set +# CONFIG_AQUANTIA_PHY is not set +# CONFIG_AR5523 is not set +# CONFIG_AR7 is not set +# CONFIG_AR8216_PHY is not set +# CONFIG_AR8216_PHY_LEDS is not set +# CONFIG_ARCH_ACTIONS is not set +# CONFIG_ARCH_AGILEX is not set +# CONFIG_ARCH_ALPINE is not set +# CONFIG_ARCH_APPLE is not set +# CONFIG_ARCH_ARTPEC is not set +# CONFIG_ARCH_ASPEED is not set +# CONFIG_ARCH_AT91 is not set +# CONFIG_ARCH_AXXIA is not set +# CONFIG_ARCH_BCM is not set +# CONFIG_ARCH_BCM2835 is not set +# CONFIG_ARCH_BCM4908 is not set +# CONFIG_ARCH_BCM_21664 is not set +# CONFIG_ARCH_BCM_23550 is not set +# CONFIG_ARCH_BCM_281XX is not set +# CONFIG_ARCH_BCM_5301X is not set +# CONFIG_ARCH_BCM_53573 is not set +# CONFIG_ARCH_BCM_63XX is not set +# CONFIG_ARCH_BCM_CYGNUS is not set +# CONFIG_ARCH_BCM_IPROC is not set +# CONFIG_ARCH_BCM_NSP is not set +# CONFIG_ARCH_BERLIN is not set +CONFIG_ARCH_BINFMT_ELF_STATE=y +# CONFIG_ARCH_BITMAIN is not set +# CONFIG_ARCH_BRCMSTB is not set +# CONFIG_ARCH_CLPS711X is not set +# CONFIG_ARCH_CNS3XXX is not set +# CONFIG_ARCH_DAVINCI is not set +# CONFIG_ARCH_DIGICOLOR is not set +# CONFIG_ARCH_DMA_ADDR_T_64BIT is not set +# CONFIG_ARCH_DOVE is not set +# CONFIG_ARCH_EBSA110 is not set +# CONFIG_ARCH_EP93XX is not set +# CONFIG_ARCH_EXYNOS is not set +CONFIG_ARCH_FLATMEM_ENABLE=y +# CONFIG_ARCH_FOOTBRIDGE is not set +# CONFIG_ARCH_GEMINI is not set +# CONFIG_ARCH_HI3xxx is not set +# CONFIG_ARCH_HIGHBANK is not set +# CONFIG_ARCH_HISI is not set +# CONFIG_ARCH_INTEGRATOR is not set +# CONFIG_ARCH_INTEL_SOCFPGA is not set +# CONFIG_ARCH_IOP13XX is not set +# CONFIG_ARCH_IOP32X is not set +# CONFIG_ARCH_IOP33X is not set +# CONFIG_ARCH_IXP4XX is not set +# CONFIG_ARCH_K3 is not set +# CONFIG_ARCH_KEEMBAY is not set +# CONFIG_ARCH_KEYSTONE is not set +# CONFIG_ARCH_KS8695 is not set +# CONFIG_ARCH_LAYERSCAPE is not set +# CONFIG_ARCH_LG1K is not set +# CONFIG_ARCH_LPC32XX is not set +# CONFIG_ARCH_MEDIATEK is not set +# CONFIG_ARCH_MESON is not set +# CONFIG_ARCH_MILBEAUT is not set +CONFIG_ARCH_MMAP_RND_BITS=8 +CONFIG_ARCH_MMAP_RND_BITS_MAX=16 +CONFIG_ARCH_MMAP_RND_BITS_MIN=8 +CONFIG_ARCH_MMAP_RND_COMPAT_BITS_MAX=16 +CONFIG_ARCH_MMAP_RND_COMPAT_BITS_MIN=8 +# CONFIG_ARCH_MMP is not set +# CONFIG_ARCH_MSTARV7 is not set +# CONFIG_ARCH_MULTIPLATFORM is not set +# CONFIG_ARCH_MULTI_V6 is not set +# CONFIG_ARCH_MULTI_V7 is not set +# CONFIG_ARCH_MV78XX0 is not set +# CONFIG_ARCH_MVEBU is not set +# CONFIG_ARCH_MXC is not set +# CONFIG_ARCH_MXS is not set +# CONFIG_ARCH_NEEDS_CPU_IDLE_COUPLED is not set +# CONFIG_ARCH_NETX is not set +# CONFIG_ARCH_NOMADIK is not set +# CONFIG_ARCH_NPCM is not set +# CONFIG_ARCH_NSPIRE is not set +# CONFIG_ARCH_OMAP is not set +# CONFIG_ARCH_OMAP1 is not set +# CONFIG_ARCH_OMAP2 is not set +# CONFIG_ARCH_OMAP2PLUS is not set +# CONFIG_ARCH_OMAP3 is not set +# CONFIG_ARCH_OMAP4 is not set +# CONFIG_ARCH_ORION5X is not set +# CONFIG_ARCH_OXNAS is not set +# CONFIG_ARCH_PICOXCELL is not set +# CONFIG_ARCH_PRIMA2 is not set +# CONFIG_ARCH_PXA is not set +# CONFIG_ARCH_QCOM is not set +# CONFIG_ARCH_RANDOM is not set +# CONFIG_ARCH_RDA is not set +# CONFIG_ARCH_REALTEK is not set +# CONFIG_ARCH_REALVIEW is not set +# CONFIG_ARCH_RENESAS is not set +# CONFIG_ARCH_ROCKCHIP is not set +# CONFIG_ARCH_RPC is not set +# CONFIG_ARCH_S32 is not set +# CONFIG_ARCH_S3C24XX is not set +# CONFIG_ARCH_S3C64XX is not set +# CONFIG_ARCH_S5PV210 is not set +# CONFIG_ARCH_SA1100 is not set +# CONFIG_ARCH_SEATTLE is not set +# CONFIG_ARCH_SHMOBILE is not set +# CONFIG_ARCH_SIRF is not set +# CONFIG_ARCH_SOCFPGA is not set +# CONFIG_ARCH_SPARX5 is not set +# CONFIG_ARCH_SPRD is not set +# CONFIG_ARCH_STI is not set +# CONFIG_ARCH_STM32 is not set +# CONFIG_ARCH_STRATIX10 is not set +# CONFIG_ARCH_SUNXI is not set +# CONFIG_ARCH_SYNQUACER is not set +# CONFIG_ARCH_TANGO is not set +# CONFIG_ARCH_TEGRA is not set +# CONFIG_ARCH_THUNDER is not set +# CONFIG_ARCH_THUNDER2 is not set +# CONFIG_ARCH_U300 is not set +# CONFIG_ARCH_U8500 is not set +# CONFIG_ARCH_UNIPHIER is not set +# CONFIG_ARCH_VERSATILE is not set +# CONFIG_ARCH_VEXPRESS is not set +# CONFIG_ARCH_VIRT is not set +# CONFIG_ARCH_VISCONTI is not set +# CONFIG_ARCH_VT8500 is not set +# CONFIG_ARCH_VULCAN is not set +# CONFIG_ARCH_W90X900 is not set +# CONFIG_ARCH_WANTS_THP_SWAP is not set +# CONFIG_ARCH_WM8505 is not set +# CONFIG_ARCH_WM8750 is not set +# CONFIG_ARCH_WM8850 is not set +# CONFIG_ARCH_XGENE is not set +# CONFIG_ARCH_ZX is not set +# CONFIG_ARCH_ZYNQ is not set +# CONFIG_ARCH_ZYNQMP is not set +# CONFIG_ARCNET is not set +# CONFIG_ARC_EMAC is not set +# CONFIG_ARC_IRQ_NO_AUTOSAVE is not set +# CONFIG_ARM64_16K_PAGES is not set +# CONFIG_ARM64_64K_PAGES is not set +# CONFIG_ARM64_AMU_EXTN is not set +# CONFIG_ARM64_BTI is not set +# CONFIG_ARM64_CRYPTO is not set +# CONFIG_ARM64_E0PD is not set +# CONFIG_ARM64_ERRATUM_1024718 is not set +# CONFIG_ARM64_ERRATUM_1165522 is not set +# CONFIG_ARM64_ERRATUM_1286807 is not set +# CONFIG_ARM64_ERRATUM_1319367 is not set +# CONFIG_ARM64_ERRATUM_1418040 is not set +# CONFIG_ARM64_ERRATUM_1463225 is not set +# CONFIG_ARM64_ERRATUM_1508412 is not set +# CONFIG_ARM64_ERRATUM_1530923 is not set +# CONFIG_ARM64_ERRATUM_1542419 is not set +# CONFIG_ARM64_ERRATUM_2441009 is not set +# CONFIG_ARM64_ERRATUM_819472 is not set +# CONFIG_ARM64_ERRATUM_824069 is not set +# CONFIG_ARM64_ERRATUM_826319 is not set +# CONFIG_ARM64_ERRATUM_827319 is not set +# CONFIG_ARM64_ERRATUM_832075 is not set +# CONFIG_ARM64_ERRATUM_834220 is not set +# CONFIG_ARM64_ERRATUM_843419 is not set +# CONFIG_ARM64_ERRATUM_845719 is not set +# CONFIG_ARM64_ERRATUM_858921 is not set +# CONFIG_ARM64_HW_AFDBM is not set +# CONFIG_ARM64_LSE_ATOMICS is not set +# CONFIG_ARM64_MODULE_PLTS is not set +# CONFIG_ARM64_MTE is not set +# CONFIG_ARM64_PAN is not set +# CONFIG_ARM64_PMEM is not set +# CONFIG_ARM64_PSEUDO_NMI is not set +# CONFIG_ARM64_PTDUMP_DEBUGFS is not set +# CONFIG_ARM64_PTR_AUTH is not set +# CONFIG_ARM64_RANDOMIZE_TEXT_OFFSET is not set +# CONFIG_ARM64_RAS_EXTN is not set +# CONFIG_ARM64_RELOC_TEST is not set +# CONFIG_ARM64_SVE is not set +CONFIG_ARM64_SW_TTBR0_PAN=y +# CONFIG_ARM64_TLB_RANGE is not set +# CONFIG_ARM64_UAO is not set +# CONFIG_ARM64_USE_LSE_ATOMICS is not set +# CONFIG_ARM64_VA_BITS_48 is not set +# CONFIG_ARM64_VHE is not set +# CONFIG_ARM_APPENDED_DTB is not set +# CONFIG_ARM_ARCH_TIMER is not set +# CONFIG_ARM_ATAG_DTB_COMPAT_CMDLINE_EXTEND is not set +# CONFIG_ARM_BIG_LITTLE_CPUFREQ is not set +# CONFIG_ARM_CCI is not set +# CONFIG_ARM_CCI400_PMU is not set +# CONFIG_ARM_CCI5xx_PMU is not set +# CONFIG_ARM_CCI_PMU is not set +# CONFIG_ARM_CCN is not set +# CONFIG_ARM_CMN is not set +# CONFIG_ARM_CPUIDLE is not set +CONFIG_ARM_CPU_TOPOLOGY=y +# CONFIG_ARM_CRYPTO is not set +CONFIG_ARM_DMA_MEM_BUFFERABLE=y +# CONFIG_ARM_DSU_PMU is not set +# CONFIG_ARM_ERRATA_326103 is not set +# CONFIG_ARM_ERRATA_364296 is not set +# CONFIG_ARM_ERRATA_411920 is not set +# CONFIG_ARM_ERRATA_430973 is not set +# CONFIG_ARM_ERRATA_458693 is not set +# CONFIG_ARM_ERRATA_460075 is not set +# CONFIG_ARM_ERRATA_643719 is not set +# CONFIG_ARM_ERRATA_720789 is not set +# CONFIG_ARM_ERRATA_742230 is not set +# CONFIG_ARM_ERRATA_742231 is not set +# CONFIG_ARM_ERRATA_743622 is not set +# CONFIG_ARM_ERRATA_751472 is not set +# CONFIG_ARM_ERRATA_754322 is not set +# CONFIG_ARM_ERRATA_754327 is not set +# CONFIG_ARM_ERRATA_764369 is not set +# CONFIG_ARM_ERRATA_773022 is not set +# CONFIG_ARM_ERRATA_775420 is not set +# CONFIG_ARM_ERRATA_798181 is not set +# CONFIG_ARM_ERRATA_814220 is not set +# CONFIG_ARM_ERRATA_818325_852422 is not set +# CONFIG_ARM_ERRATA_821420 is not set +# CONFIG_ARM_ERRATA_825619 is not set +# CONFIG_ARM_ERRATA_852421 is not set +# CONFIG_ARM_ERRATA_852423 is not set +# CONFIG_ARM_ERRATA_857271 is not set +# CONFIG_ARM_ERRATA_857272 is not set +# CONFIG_ARM_FFA_TRANSPORT is not set +CONFIG_ARM_GIC_MAX_NR=1 +# CONFIG_ARM_KIRKWOOD_CPUFREQ is not set +# CONFIG_ARM_KPROBES_TEST is not set +# CONFIG_ARM_LPAE is not set +# CONFIG_ARM_MEDIATEK_CPUFREQ_HW is not set +# CONFIG_ARM_MHU is not set +# CONFIG_ARM_MODULE_PLTS is not set +# CONFIG_ARM_PATCH_PHYS_VIRT is not set +# CONFIG_ARM_PSCI is not set +# CONFIG_ARM_PSCI_CHECKER is not set +# CONFIG_ARM_PSCI_CPUIDLE is not set +# CONFIG_ARM_PTDUMP_DEBUGFS is not set +# CONFIG_ARM_SBSA_WATCHDOG is not set +# CONFIG_ARM_SCMI_PROTOCOL is not set +# CONFIG_ARM_SCPI_PROTOCOL is not set +# CONFIG_ARM_SDE_INTERFACE is not set +# CONFIG_ARM_SMCCC_SOC_ID is not set +# CONFIG_ARM_SMC_WATCHDOG is not set +# CONFIG_ARM_SP805_WATCHDOG is not set +# CONFIG_ARM_SPE_PMU is not set +# CONFIG_ARM_THUMBEE is not set +# CONFIG_ARM_TIMER_SP804 is not set +# CONFIG_ARM_UNWIND is not set +# CONFIG_ARM_VIRT_EXT is not set +# CONFIG_AS3935 is not set +# CONFIG_AS73211 is not set +# CONFIG_ASM9260_TIMER is not set +# CONFIG_ASN1 is not set +# CONFIG_ASUS_LAPTOP is not set +# CONFIG_ASUS_WIRELESS is not set +# CONFIG_ASYMMETRIC_KEY_TYPE is not set +# CONFIG_ASYMMETRIC_PUBLIC_KEY_SUBTYPE is not set +# CONFIG_ASYNC_RAID6_TEST is not set +# CONFIG_ASYNC_TX_DMA is not set +# CONFIG_AT76C50X_USB is not set +# CONFIG_AT803X_PHY is not set +# CONFIG_AT91_SAMA5D2_ADC is not set +# CONFIG_ATA is not set +# CONFIG_ATAGS is not set +CONFIG_ATAGS_PROC=y +# CONFIG_ATALK is not set +# CONFIG_ATARI_PARTITION is not set +# CONFIG_ATA_ACPI is not set +CONFIG_ATA_BMDMA=y +# CONFIG_ATA_FORCE is not set +# CONFIG_ATA_GENERIC is not set +# CONFIG_ATA_LEDS is not set +# CONFIG_ATA_NONSTANDARD is not set +# CONFIG_ATA_OVER_ETH is not set +# CONFIG_ATA_PIIX is not set +CONFIG_ATA_SFF=y +# CONFIG_ATA_VERBOSE_ERROR is not set +# CONFIG_ATH10K is not set +# CONFIG_ATH25 is not set +# CONFIG_ATH5K is not set +# CONFIG_ATH6KL is not set +# CONFIG_ATH79 is not set +# CONFIG_ATH9K is not set +# CONFIG_ATH9K_HTC is not set +# CONFIG_ATH_DEBUG is not set +# CONFIG_ATL1 is not set +# CONFIG_ATL1C is not set +# CONFIG_ATL1E is not set +# CONFIG_ATL2 is not set +# CONFIG_ATLAS_EZO_SENSOR is not set +# CONFIG_ATLAS_PH_SENSOR is not set +# CONFIG_ATM is not set +# CONFIG_ATMEL is not set +# CONFIG_ATMEL_PIT is not set +# CONFIG_ATMEL_SSC is not set +# CONFIG_ATM_AMBASSADOR is not set +# CONFIG_ATM_BR2684 is not set +CONFIG_ATM_BR2684_IPFILTER=y +# CONFIG_ATM_CLIP is not set +CONFIG_ATM_CLIP_NO_ICMP=y +# CONFIG_ATM_DRIVERS is not set +# CONFIG_ATM_DUMMY is not set +# CONFIG_ATM_ENI is not set +# CONFIG_ATM_FIRESTREAM is not set +# CONFIG_ATM_FORE200E is not set +# CONFIG_ATM_HE is not set +# CONFIG_ATM_HORIZON is not set +# CONFIG_ATM_IA is not set +# CONFIG_ATM_IDT77252 is not set +# CONFIG_ATM_LANAI is not set +# CONFIG_ATM_LANE is not set +# CONFIG_ATM_MPOA is not set +# CONFIG_ATM_NICSTAR is not set +# CONFIG_ATM_SOLOS is not set +# CONFIG_ATM_TCP is not set +# CONFIG_ATM_ZATM is not set +# CONFIG_ATOMIC64_SELFTEST is not set +# CONFIG_ATP is not set +# CONFIG_AUDIT is not set +# CONFIG_AUDIT_ARCH_COMPAT_GENERIC is not set +# CONFIG_AURORA_NB8800 is not set +# CONFIG_AUTOFS4_FS is not set +# CONFIG_AUTOFS_FS is not set +# CONFIG_AUTO_ZRELADDR is not set +# CONFIG_AUXDISPLAY is not set +# CONFIG_AX25 is not set +# CONFIG_AX25_DAMA_SLAVE is not set +# CONFIG_AX88796 is not set +# CONFIG_AX88796B_PHY is not set +# CONFIG_AXP20X_ADC is not set +# CONFIG_AXP20X_POWER is not set +# CONFIG_AXP288_ADC is not set +# CONFIG_AXP288_FUEL_GAUGE is not set +# CONFIG_B43 is not set +# CONFIG_B43LEGACY is not set +# CONFIG_B44 is not set +# CONFIG_B53 is not set +# CONFIG_B53_MDIO_DRIVER is not set +# CONFIG_B53_MMAP_DRIVER is not set +# CONFIG_B53_SERDES is not set +# CONFIG_B53_SPI_DRIVER is not set +# CONFIG_B53_SRAB_DRIVER is not set +# CONFIG_BACKLIGHT_ADP8860 is not set +# CONFIG_BACKLIGHT_ADP8870 is not set +# CONFIG_BACKLIGHT_APPLE is not set +# CONFIG_BACKLIGHT_ARCXCNN is not set +# CONFIG_BACKLIGHT_BD6107 is not set +# CONFIG_BACKLIGHT_CLASS_DEVICE is not set +# CONFIG_BACKLIGHT_GENERIC is not set +# CONFIG_BACKLIGHT_GPIO is not set +# CONFIG_BACKLIGHT_KTD253 is not set +# CONFIG_BACKLIGHT_LCD_SUPPORT is not set +# CONFIG_BACKLIGHT_LED is not set +# CONFIG_BACKLIGHT_LM3630A is not set +# CONFIG_BACKLIGHT_LM3639 is not set +# CONFIG_BACKLIGHT_LP855X is not set +# CONFIG_BACKLIGHT_LV5207LP is not set +# CONFIG_BACKLIGHT_PANDORA is not set +# CONFIG_BACKLIGHT_PM8941_WLED is not set +# CONFIG_BACKLIGHT_PWM is not set +# CONFIG_BACKLIGHT_QCOM_WLED is not set +# CONFIG_BACKLIGHT_RPI is not set +# CONFIG_BACKLIGHT_SAHARA is not set +# CONFIG_BACKTRACE_SELF_TEST is not set +# CONFIG_BAREUDP is not set +CONFIG_BASE_FULL=y +CONFIG_BASE_SMALL=0 +# CONFIG_BATMAN_ADV is not set +# CONFIG_BATTERY_BQ27XXX is not set +# CONFIG_BATTERY_BQ27XXX_HDQ is not set +# CONFIG_BATTERY_CW2015 is not set +# CONFIG_BATTERY_DS2760 is not set +# CONFIG_BATTERY_DS2780 is not set +# CONFIG_BATTERY_DS2781 is not set +# CONFIG_BATTERY_DS2782 is not set +# CONFIG_BATTERY_GAUGE_LTC2941 is not set +# CONFIG_BATTERY_GOLDFISH is not set +# CONFIG_BATTERY_LEGO_EV3 is not set +# CONFIG_BATTERY_MAX17040 is not set +# CONFIG_BATTERY_MAX17042 is not set +# CONFIG_BATTERY_MAX1721X is not set +# CONFIG_BATTERY_RT5033 is not set +# CONFIG_BATTERY_SBS is not set +# CONFIG_BAYCOM_EPP is not set +# CONFIG_BAYCOM_PAR is not set +# CONFIG_BAYCOM_SER_FDX is not set +# CONFIG_BAYCOM_SER_HDX is not set +# CONFIG_BCACHE is not set +# CONFIG_BCM47XX is not set +# CONFIG_BCM54140_PHY is not set +# CONFIG_BCM63XX is not set +# CONFIG_BCM63XX_PHY is not set +# CONFIG_BCM7038_WDT is not set +# CONFIG_BCM7XXX_PHY is not set +# CONFIG_BCM84881_PHY is not set +# CONFIG_BCM87XX_PHY is not set +# CONFIG_BCMA is not set +# CONFIG_BCMA_DRIVER_GPIO is not set +CONFIG_BCMA_POSSIBLE=y +# CONFIG_BCMGENET is not set +# CONFIG_BCM_IPROC_ADC is not set +# CONFIG_BCM_KONA_USB2_PHY is not set +# CONFIG_BCM_SBA_RAID is not set +# CONFIG_BCM_VK is not set +# CONFIG_BDI_SWITCH is not set +# CONFIG_BE2ISCSI is not set +# CONFIG_BE2NET is not set +# CONFIG_BEFS_FS is not set +# CONFIG_BFS_FS is not set +# CONFIG_BGMAC is not set +# CONFIG_BH1750 is not set +# CONFIG_BH1780 is not set +# CONFIG_BIG_KEYS is not set +# CONFIG_BIG_LITTLE is not set +CONFIG_BINARY_PRINTF=y +# CONFIG_BINFMT_AOUT is not set +CONFIG_BINFMT_ELF=y +# CONFIG_BINFMT_ELF_FDPIC is not set +# CONFIG_BINFMT_FLAT is not set +# CONFIG_BINFMT_MISC is not set +CONFIG_BINFMT_SCRIPT=y +CONFIG_BITREVERSE=y +# CONFIG_BLK_CGROUP_IOCOST is not set +# CONFIG_BLK_CGROUP_IOLATENCY is not set +# CONFIG_BLK_CGROUP_IOPRIO is not set +# CONFIG_BLK_CMDLINE_PARSER is not set +# CONFIG_BLK_DEBUG_FS is not set +CONFIG_BLK_DEV=y +# CONFIG_BLK_DEV_3W_XXXX_RAID is not set +# CONFIG_BLK_DEV_4DRIVES is not set +# CONFIG_BLK_DEV_AEC62XX is not set +# CONFIG_BLK_DEV_ALI14XX is not set +# CONFIG_BLK_DEV_ALI15X3 is not set +# CONFIG_BLK_DEV_AMD74XX is not set +# CONFIG_BLK_DEV_ATIIXP is not set +# CONFIG_BLK_DEV_BSG is not set +# CONFIG_BLK_DEV_BSGLIB is not set +# CONFIG_BLK_DEV_CMD640 is not set +# CONFIG_BLK_DEV_CMD64X is not set +# CONFIG_BLK_DEV_COW_COMMON is not set +# CONFIG_BLK_DEV_CRYPTOLOOP is not set +# CONFIG_BLK_DEV_CS5520 is not set +# CONFIG_BLK_DEV_CS5530 is not set +# CONFIG_BLK_DEV_CS5535 is not set +# CONFIG_BLK_DEV_CS5536 is not set +# CONFIG_BLK_DEV_CY82C693 is not set +# CONFIG_BLK_DEV_DAC960 is not set +# CONFIG_BLK_DEV_DELKIN is not set +# CONFIG_BLK_DEV_DM is not set +# CONFIG_BLK_DEV_DRBD is not set +# CONFIG_BLK_DEV_DTC2278 is not set +# CONFIG_BLK_DEV_FD is not set +# CONFIG_BLK_DEV_GENERIC is not set +# CONFIG_BLK_DEV_HPT366 is not set +# CONFIG_BLK_DEV_HT6560B is not set +# CONFIG_BLK_DEV_IDEACPI is not set +# CONFIG_BLK_DEV_IDECD is not set +# CONFIG_BLK_DEV_IDECS is not set +# CONFIG_BLK_DEV_IDEPCI is not set +# CONFIG_BLK_DEV_IDEPNP is not set +# CONFIG_BLK_DEV_IDETAPE is not set +# CONFIG_BLK_DEV_IDE_AU1XXX is not set +# CONFIG_BLK_DEV_IDE_SATA is not set +CONFIG_BLK_DEV_INITRD=y +# CONFIG_BLK_DEV_INTEGRITY is not set +# CONFIG_BLK_DEV_IO_TRACE is not set +# CONFIG_BLK_DEV_IT8172 is not set +# CONFIG_BLK_DEV_IT8213 is not set +# CONFIG_BLK_DEV_IT821X is not set +# CONFIG_BLK_DEV_JMICRON is not set +# CONFIG_BLK_DEV_LOOP is not set +CONFIG_BLK_DEV_LOOP_MIN_COUNT=8 +# CONFIG_BLK_DEV_MD is not set +# CONFIG_BLK_DEV_NBD is not set +# CONFIG_BLK_DEV_NS87415 is not set +# CONFIG_BLK_DEV_NULL_BLK is not set +# CONFIG_BLK_DEV_NVME is not set +# CONFIG_BLK_DEV_OFFBOARD is not set +# CONFIG_BLK_DEV_OPTI621 is not set +# CONFIG_BLK_DEV_PCIESSD_MTIP32XX is not set +# CONFIG_BLK_DEV_PDC202XX_NEW is not set +# CONFIG_BLK_DEV_PDC202XX_OLD is not set +# CONFIG_BLK_DEV_PIIX is not set +# CONFIG_BLK_DEV_PLATFORM is not set +# CONFIG_BLK_DEV_PMEM is not set +# CONFIG_BLK_DEV_QD65XX is not set +# CONFIG_BLK_DEV_RAM is not set +# CONFIG_BLK_DEV_RBD is not set +# CONFIG_BLK_DEV_RSXX is not set +# CONFIG_BLK_DEV_RZ1000 is not set +# CONFIG_BLK_DEV_SC1200 is not set +# CONFIG_BLK_DEV_SD is not set +# CONFIG_BLK_DEV_SIIMAGE is not set +# CONFIG_BLK_DEV_SIS5513 is not set +# CONFIG_BLK_DEV_SKD is not set +# CONFIG_BLK_DEV_SL82C105 is not set +# CONFIG_BLK_DEV_SLC90E66 is not set +# CONFIG_BLK_DEV_SR is not set +# CONFIG_BLK_DEV_SVWKS is not set +# CONFIG_BLK_DEV_SX8 is not set +# CONFIG_BLK_DEV_TC86C001 is not set +# CONFIG_BLK_DEV_THROTTLING is not set +# CONFIG_BLK_DEV_TRIFLEX is not set +# CONFIG_BLK_DEV_TRM290 is not set +# CONFIG_BLK_DEV_UMC8672 is not set +# CONFIG_BLK_DEV_UMEM is not set +# CONFIG_BLK_DEV_VIA82CXXX is not set +# CONFIG_BLK_DEV_ZONED is not set +# CONFIG_BLK_INLINE_ENCRYPTION is not set +# CONFIG_BLK_SED_OPAL is not set +# CONFIG_BLK_WBT is not set +CONFIG_BLOCK=y +# CONFIG_BMA180 is not set +# CONFIG_BMA220 is not set +# CONFIG_BMA400 is not set +# CONFIG_BMC150_ACCEL is not set +# CONFIG_BMC150_MAGN is not set +# CONFIG_BMC150_MAGN_I2C is not set +# CONFIG_BMC150_MAGN_SPI is not set +# CONFIG_BME680 is not set +# CONFIG_BMG160 is not set +# CONFIG_BMI088_ACCEL is not set +# CONFIG_BMI160_I2C is not set +# CONFIG_BMI160_SPI is not set +# CONFIG_BMIPS_GENERIC is not set +# CONFIG_BMP280 is not set +# CONFIG_BNA is not set +# CONFIG_BNX2 is not set +# CONFIG_BNX2X is not set +# CONFIG_BNX2X_SRIOV is not set +# CONFIG_BNXT is not set +# CONFIG_BONDING is not set +# CONFIG_BOOKE_WDT is not set +CONFIG_BOOKE_WDT_DEFAULT_TIMEOUT=3 +# CONFIG_BOOTPARAM_HUNG_TASK_PANIC is not set +# CONFIG_BOOTPARAM_SOFTLOCKUP_PANIC is not set +# CONFIG_BOOTTIME_TRACING is not set +# CONFIG_BOOT_CONFIG is not set +# CONFIG_BOOT_PRINTK_DELAY is not set +CONFIG_BOOT_RAW=y +# CONFIG_BOUNCE is not set +CONFIG_BPF=y +# CONFIG_BPFILTER is not set +CONFIG_BPF_JIT=y +# CONFIG_BPF_JIT_ALWAYS_ON is not set +CONFIG_BPF_JIT_DEFAULT_ON=y +# CONFIG_BPF_PRELOAD is not set +# CONFIG_BPF_STREAM_PARSER is not set +CONFIG_BPF_SYSCALL=y +CONFIG_BPF_UNPRIV_DEFAULT_OFF=y +# CONFIG_BPQETHER is not set +CONFIG_BQL=y +CONFIG_BRANCH_PROFILE_NONE=y +# CONFIG_BRCMFMAC is not set +# CONFIG_BRCMSMAC is not set +# CONFIG_BRCMSTB_GISB_ARB is not set +CONFIG_BRIDGE=y +# CONFIG_BRIDGE_CFM is not set +# CONFIG_BRIDGE_EBT_802_3 is not set +# CONFIG_BRIDGE_EBT_AMONG is not set +# CONFIG_BRIDGE_EBT_ARP is not set +# CONFIG_BRIDGE_EBT_ARPREPLY is not set +# CONFIG_BRIDGE_EBT_BROUTE is not set +# CONFIG_BRIDGE_EBT_DNAT is not set +# CONFIG_BRIDGE_EBT_IP is not set +# CONFIG_BRIDGE_EBT_IP6 is not set +# CONFIG_BRIDGE_EBT_LIMIT is not set +# CONFIG_BRIDGE_EBT_LOG is not set +# CONFIG_BRIDGE_EBT_MARK is not set +# CONFIG_BRIDGE_EBT_MARK_T is not set +# CONFIG_BRIDGE_EBT_NFLOG is not set +# CONFIG_BRIDGE_EBT_PKTTYPE is not set +# CONFIG_BRIDGE_EBT_REDIRECT is not set +# CONFIG_BRIDGE_EBT_SNAT is not set +# CONFIG_BRIDGE_EBT_STP is not set +# CONFIG_BRIDGE_EBT_T_FILTER is not set +# CONFIG_BRIDGE_EBT_T_NAT is not set +# CONFIG_BRIDGE_EBT_VLAN is not set +CONFIG_BRIDGE_IGMP_SNOOPING=y +# CONFIG_BRIDGE_MRP is not set +# CONFIG_BRIDGE_NETFILTER is not set +# CONFIG_BRIDGE_NF_EBTABLES is not set +CONFIG_BRIDGE_VLAN_FILTERING=y +# CONFIG_BROADCOM_PHY is not set +CONFIG_BROKEN_ON_SMP=y +# CONFIG_BSD_DISKLABEL is not set +# CONFIG_BSD_PROCESS_ACCT is not set +# CONFIG_BSD_PROCESS_ACCT_V3 is not set +# CONFIG_BT is not set +# CONFIG_BTRFS_ASSERT is not set +# CONFIG_BTRFS_DEBUG is not set +# CONFIG_BTRFS_FS is not set +# CONFIG_BTRFS_FS_POSIX_ACL is not set +# CONFIG_BTRFS_FS_REF_VERIFY is not set +# CONFIG_BTRFS_FS_RUN_SANITY_TESTS is not set +# CONFIG_BT_AOSPEXT is not set +# CONFIG_BT_ATH3K is not set +# CONFIG_BT_BNEP is not set +CONFIG_BT_BNEP_MC_FILTER=y +CONFIG_BT_BNEP_PROTO_FILTER=y +# CONFIG_BT_BREDR is not set +# CONFIG_BT_CMTP is not set +# CONFIG_BT_FEATURE_DEBUG is not set +# CONFIG_BT_HCIBCM203X is not set +# CONFIG_BT_HCIBFUSB is not set +# CONFIG_BT_HCIBLUECARD is not set +# CONFIG_BT_HCIBPA10X is not set +# CONFIG_BT_HCIBT3C is not set +# CONFIG_BT_HCIBTSDIO is not set +# CONFIG_BT_HCIBTUSB is not set +# CONFIG_BT_HCIBTUSB_AUTOSUSPEND is not set +# CONFIG_BT_HCIBTUSB_MTK is not set +# CONFIG_BT_HCIBTUSB_RTL is not set +# CONFIG_BT_HCIDTL1 is not set +# CONFIG_BT_HCIUART is not set +# CONFIG_BT_HCIUART_3WIRE is not set +# CONFIG_BT_HCIUART_AG6XX is not set +# CONFIG_BT_HCIUART_ATH3K is not set +CONFIG_BT_HCIUART_BCSP=y +CONFIG_BT_HCIUART_H4=y +# CONFIG_BT_HCIUART_LL is not set +# CONFIG_BT_HCIUART_MRVL is not set +# CONFIG_BT_HCIUART_QCA is not set +# CONFIG_BT_HCIUART_RTL is not set +# CONFIG_BT_HCIVHCI is not set +# CONFIG_BT_HIDP is not set +# CONFIG_BT_HS is not set +# CONFIG_BT_LE is not set +# CONFIG_BT_LEDS is not set +# CONFIG_BT_MRVL is not set +# CONFIG_BT_MSFTEXT is not set +# CONFIG_BT_MTKSDIO is not set +# CONFIG_BT_MTKUART is not set +# CONFIG_BT_RFCOMM is not set +CONFIG_BT_RFCOMM_TTY=y +# CONFIG_BT_SELFTEST is not set +# CONFIG_BT_VIRTIO is not set +CONFIG_BUG=y +# CONFIG_BUG_ON_DATA_CORRUPTION is not set +CONFIG_BUILDTIME_EXTABLE_SORT=y +CONFIG_BUILDTIME_TABLE_SORT=y +# CONFIG_BUILD_BIN2C is not set +CONFIG_BUILD_SALT="" +# CONFIG_C2PORT is not set +CONFIG_CACHE_L2X0_PMU=y +# CONFIG_CADENCE_WATCHDOG is not set +# CONFIG_CAIF is not set +# CONFIG_CAN is not set +# CONFIG_CAN_BCM is not set +# CONFIG_CAN_DEBUG_DEVICES is not set +# CONFIG_CAN_DEV is not set +# CONFIG_CAN_ETAS_ES58X is not set +# CONFIG_CAN_GS_USB is not set +# CONFIG_CAN_GW is not set +# CONFIG_CAN_HI311X is not set +# CONFIG_CAN_IFI_CANFD is not set +# CONFIG_CAN_ISOTP is not set +# CONFIG_CAN_J1939 is not set +# CONFIG_CAN_KVASER_PCIEFD is not set +# CONFIG_CAN_MCBA_USB is not set +# CONFIG_CAN_MCP251XFD is not set +# CONFIG_CAN_M_CAN is not set +# CONFIG_CAN_PEAK_PCIEFD is not set +# CONFIG_CAN_RAW is not set +# CONFIG_CAN_RCAR is not set +# CONFIG_CAN_RCAR_CANFD is not set +# CONFIG_CAN_SLCAN is not set +# CONFIG_CAN_SUN4I is not set +# CONFIG_CAN_UCAN is not set +# CONFIG_CAN_VCAN is not set +# CONFIG_CAN_VXCAN is not set +# CONFIG_CAPI_AVM is not set +# CONFIG_CAPI_EICON is not set +# CONFIG_CAPI_TRACE is not set +CONFIG_CARDBUS=y +# CONFIG_CARDMAN_4000 is not set +# CONFIG_CARDMAN_4040 is not set +# CONFIG_CARL9170 is not set +# CONFIG_CASSINI is not set +# CONFIG_CAVIUM_CPT is not set +# CONFIG_CAVIUM_ERRATUM_22375 is not set +# CONFIG_CAVIUM_ERRATUM_23144 is not set +# CONFIG_CAVIUM_ERRATUM_23154 is not set +# CONFIG_CAVIUM_ERRATUM_27456 is not set +# CONFIG_CAVIUM_ERRATUM_30115 is not set +# CONFIG_CAVIUM_OCTEON_SOC is not set +# CONFIG_CAVIUM_PTP is not set +# CONFIG_CAVIUM_TX2_ERRATUM_219 is not set +# CONFIG_CB710_CORE is not set +# CONFIG_CC10001_ADC is not set +# CONFIG_CCS811 is not set +CONFIG_CC_CAN_LINK=y +CONFIG_CC_OPTIMIZE_FOR_PERFORMANCE=y +# CONFIG_CC_OPTIMIZE_FOR_SIZE is not set +# CONFIG_CDROM_PKTCDVD is not set +# CONFIG_CEPH_FS is not set +# CONFIG_CEPH_LIB is not set +# CONFIG_CFG80211 is not set +# CONFIG_CFG80211_CERTIFICATION_ONUS is not set +# CONFIG_CGROUPS is not set +# CONFIG_CGROUP_MISC is not set +# CONFIG_CHARGER_ADP5061 is not set +# CONFIG_CHARGER_BD99954 is not set +# CONFIG_CHARGER_BQ2415X is not set +# CONFIG_CHARGER_BQ24190 is not set +# CONFIG_CHARGER_BQ24257 is not set +# CONFIG_CHARGER_BQ24735 is not set +# CONFIG_CHARGER_BQ2515X is not set +# CONFIG_CHARGER_BQ256XX is not set +# CONFIG_CHARGER_BQ25890 is not set +# CONFIG_CHARGER_BQ25980 is not set +# CONFIG_CHARGER_DETECTOR_MAX14656 is not set +# CONFIG_CHARGER_GPIO is not set +# CONFIG_CHARGER_ISP1704 is not set +# CONFIG_CHARGER_LP8727 is not set +# CONFIG_CHARGER_LT3651 is not set +# CONFIG_CHARGER_LTC3651 is not set +# CONFIG_CHARGER_LTC4162L is not set +# CONFIG_CHARGER_MANAGER is not set +# CONFIG_CHARGER_MAX8903 is not set +# CONFIG_CHARGER_RT9455 is not set +# CONFIG_CHARGER_SBS is not set +# CONFIG_CHARGER_SMB347 is not set +# CONFIG_CHARGER_TWL4030 is not set +# CONFIG_CHARGER_UCS1002 is not set +# CONFIG_CHASH_SELFTEST is not set +# CONFIG_CHASH_STATS is not set +# CONFIG_CHECKPOINT_RESTORE is not set +# CONFIG_CHELSIO_T1 is not set +# CONFIG_CHELSIO_T3 is not set +# CONFIG_CHELSIO_T4 is not set +# CONFIG_CHELSIO_T4VF is not set +# CONFIG_CHROME_PLATFORMS is not set +# CONFIG_CHR_DEV_OSST is not set +# CONFIG_CHR_DEV_SCH is not set +# CONFIG_CHR_DEV_SG is not set +# CONFIG_CHR_DEV_ST is not set +# CONFIG_CICADA_PHY is not set +# CONFIG_CIFS is not set +# CONFIG_CIFS_ACL is not set +CONFIG_CIFS_ALLOW_INSECURE_LEGACY=y +# CONFIG_CIFS_DEBUG is not set +# CONFIG_CIFS_DEBUG2 is not set +# CONFIG_CIFS_FSCACHE is not set +# CONFIG_CIFS_NFSD_EXPORT is not set +CONFIG_CIFS_POSIX=y +# CONFIG_CIFS_SMB2 is not set +# CONFIG_CIFS_STATS is not set +# CONFIG_CIFS_STATS2 is not set +# CONFIG_CIFS_SWN_UPCALL is not set +# CONFIG_CIFS_WEAK_PW_HASH is not set +CONFIG_CIFS_XATTR=y +# CONFIG_CIO_DAC is not set +# CONFIG_CLEANCACHE is not set +# CONFIG_CLKSRC_PISTACHIO is not set +# CONFIG_CLKSRC_VERSATILE is not set +# CONFIG_CLK_GFM_LPASS_SM8250 is not set +# CONFIG_CLK_HSDK is not set +# CONFIG_CLK_QORIQ is not set +# CONFIG_CLK_SP810 is not set +# CONFIG_CLOCK_THERMAL is not set +CONFIG_CLS_U32_MARK=y +# CONFIG_CLS_U32_PERF is not set +# CONFIG_CM32181 is not set +# CONFIG_CM3232 is not set +# CONFIG_CM3323 is not set +# CONFIG_CM3605 is not set +# CONFIG_CM36651 is not set +# CONFIG_CMA is not set +CONFIG_CMDLINE="" +# CONFIG_CMDLINE_BOOL is not set +# CONFIG_CMDLINE_EXTEND is not set +# CONFIG_CMDLINE_FORCE is not set +# CONFIG_CMDLINE_FROM_BOOTLOADER is not set +# CONFIG_CMDLINE_PARTITION is not set +# CONFIG_CNIC is not set +# CONFIG_CODA_FS is not set +# CONFIG_CODE_PATCHING_SELFTEST is not set +# CONFIG_COMEDI is not set +# CONFIG_COMMON_CLK_AXI_CLKGEN is not set +# CONFIG_COMMON_CLK_BOSTON is not set +# CONFIG_COMMON_CLK_CDCE706 is not set +# CONFIG_COMMON_CLK_CDCE925 is not set +# CONFIG_COMMON_CLK_CS2000_CP is not set +# CONFIG_COMMON_CLK_FIXED_MMIO is not set +# CONFIG_COMMON_CLK_IPROC is not set +# CONFIG_COMMON_CLK_MAX9485 is not set +# CONFIG_COMMON_CLK_MT6765 is not set +# CONFIG_COMMON_CLK_MT8167 is not set +# CONFIG_COMMON_CLK_MT8167_AUDSYS is not set +# CONFIG_COMMON_CLK_MT8167_IMGSYS is not set +# CONFIG_COMMON_CLK_MT8167_MFGCFG is not set +# CONFIG_COMMON_CLK_MT8167_MMSYS is not set +# CONFIG_COMMON_CLK_MT8167_VDECSYS is not set +# CONFIG_COMMON_CLK_MT8192 is not set +# CONFIG_COMMON_CLK_NXP is not set +# CONFIG_COMMON_CLK_PIC32 is not set +# CONFIG_COMMON_CLK_PISTACHIO is not set +# CONFIG_COMMON_CLK_PWM is not set +# CONFIG_COMMON_CLK_PXA is not set +# CONFIG_COMMON_CLK_QCOM is not set +# CONFIG_COMMON_CLK_SI514 is not set +# CONFIG_COMMON_CLK_SI5341 is not set +# CONFIG_COMMON_CLK_SI5351 is not set +# CONFIG_COMMON_CLK_SI544 is not set +# CONFIG_COMMON_CLK_SI570 is not set +# CONFIG_COMMON_CLK_VC5 is not set +# CONFIG_COMMON_CLK_XGENE is not set +# CONFIG_COMMON_CLK_XLNX_CLKWZRD is not set +CONFIG_COMPACTION=y +# CONFIG_COMPAL_LAPTOP is not set +# CONFIG_COMPAT is not set +# CONFIG_COMPAT_BRK is not set +# CONFIG_COMPILE_TEST is not set +# CONFIG_CONFIGFS_FS is not set +# CONFIG_CONNECTOR is not set +CONFIG_CONSOLE_LOGLEVEL_DEFAULT=7 +CONFIG_CONSOLE_LOGLEVEL_QUIET=4 +CONFIG_CONSTRUCTORS=y +# CONFIG_CONTEXT_SWITCH_TRACER is not set +# CONFIG_COPS is not set +# CONFIG_CORDIC is not set +# CONFIG_COREDUMP is not set +# CONFIG_CORESIGHT is not set +# CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set +# CONFIG_CORTINA_PHY is not set +# CONFIG_COUNTER is not set +# CONFIG_CPA_DEBUG is not set +# CONFIG_CPU_BIG_ENDIAN is not set +# CONFIG_CPU_BPREDICT_DISABLE is not set +# CONFIG_CPU_DCACHE_DISABLE is not set +# CONFIG_CPU_FREQ is not set +# CONFIG_CPU_FREQ_DEFAULT_GOV_CONSERVATIVE is not set +# CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMAND is not set +# CONFIG_CPU_FREQ_DEFAULT_GOV_POWERSAVE is not set +# CONFIG_CPU_FREQ_DEFAULT_GOV_SCHEDUTIL is not set +# CONFIG_CPU_FREQ_DEFAULT_GOV_USERSPACE is not set +# CONFIG_CPU_FREQ_GOV_SCHEDUTIL is not set +# CONFIG_CPU_FREQ_STAT_DETAILS is not set +# CONFIG_CPU_FREQ_THERMAL is not set +# CONFIG_CPU_HOTPLUG_STATE_CONTROL is not set +# CONFIG_CPU_ICACHE_DISABLE is not set +# CONFIG_CPU_ICACHE_MISMATCH_WORKAROUND is not set +# CONFIG_CPU_IDLE is not set +# CONFIG_CPU_IDLE_GOV_LADDER is not set +# CONFIG_CPU_IDLE_GOV_MENU is not set +# CONFIG_CPU_IDLE_GOV_TEO is not set +# CONFIG_CPU_IDLE_MULTIPLE_DRIVERS is not set +# CONFIG_CPU_ISOLATION is not set +# CONFIG_CPU_LITTLE_ENDIAN is not set +# CONFIG_CPU_NO_EFFICIENT_FFS is not set +CONFIG_CPU_SW_DOMAIN_PAN=y +# CONFIG_CPU_THERMAL is not set +# CONFIG_CRAMFS is not set +CONFIG_CRAMFS_BLOCKDEV=y +# CONFIG_CRAMFS_MTD is not set +# CONFIG_CRASH_DUMP is not set +# CONFIG_CRC16 is not set +CONFIG_CRC32=y +# CONFIG_CRC32_BIT is not set +CONFIG_CRC32_SARWATE=y +# CONFIG_CRC32_SELFTEST is not set +# CONFIG_CRC32_SLICEBY4 is not set +# CONFIG_CRC32_SLICEBY8 is not set +# CONFIG_CRC4 is not set +# CONFIG_CRC64 is not set +# CONFIG_CRC7 is not set +# CONFIG_CRC8 is not set +# CONFIG_CRC_CCITT is not set +# CONFIG_CRC_ITU_T is not set +# CONFIG_CRC_T10DIF is not set +CONFIG_CROSS_COMPILE="" +# CONFIG_CROSS_MEMORY_ATTACH is not set +CONFIG_CRYPTO=y +# CONFIG_CRYPTO_842 is not set +CONFIG_CRYPTO_ACOMP2=y +# CONFIG_CRYPTO_ADIANTUM is not set +CONFIG_CRYPTO_AEAD=y +CONFIG_CRYPTO_AEAD2=y +# CONFIG_CRYPTO_AEGIS128 is not set +# CONFIG_CRYPTO_AEGIS128L is not set +# CONFIG_CRYPTO_AEGIS128L_AESNI_SSE2 is not set +# CONFIG_CRYPTO_AEGIS128_AESNI_SSE2 is not set +# CONFIG_CRYPTO_AEGIS256 is not set +# CONFIG_CRYPTO_AEGIS256_AESNI_SSE2 is not set +CONFIG_CRYPTO_AES=y +# CONFIG_CRYPTO_AES_586 is not set +# CONFIG_CRYPTO_AES_ARM is not set +# CONFIG_CRYPTO_AES_ARM64 is not set +# CONFIG_CRYPTO_AES_ARM64_BS is not set +# CONFIG_CRYPTO_AES_ARM64_CE is not set +# CONFIG_CRYPTO_AES_ARM64_CE_BLK is not set +# CONFIG_CRYPTO_AES_ARM64_CE_CCM is not set +# CONFIG_CRYPTO_AES_ARM64_NEON_BLK is not set +# CONFIG_CRYPTO_AES_ARM_BS is not set +# CONFIG_CRYPTO_AES_ARM_CE is not set +# CONFIG_CRYPTO_AES_NI_INTEL is not set +# CONFIG_CRYPTO_AES_TI is not set +CONFIG_CRYPTO_AKCIPHER=y +CONFIG_CRYPTO_AKCIPHER2=y +CONFIG_CRYPTO_ALGAPI=y +CONFIG_CRYPTO_ALGAPI2=y +# CONFIG_CRYPTO_ANSI_CPRNG is not set +# CONFIG_CRYPTO_ANUBIS is not set +# CONFIG_CRYPTO_ARC4 is not set +# CONFIG_CRYPTO_AUTHENC is not set +# CONFIG_CRYPTO_BLAKE2B is not set +# CONFIG_CRYPTO_BLAKE2B_NEON is not set +# CONFIG_CRYPTO_BLAKE2S is not set +# CONFIG_CRYPTO_BLAKE2S_ARM is not set +# CONFIG_CRYPTO_BLAKE2S_X86 is not set +# CONFIG_CRYPTO_BLOWFISH is not set +# CONFIG_CRYPTO_CAMELLIA is not set +# CONFIG_CRYPTO_CAST5 is not set +# CONFIG_CRYPTO_CAST6 is not set +# CONFIG_CRYPTO_CBC is not set +CONFIG_CRYPTO_CCM=y +# CONFIG_CRYPTO_CFB is not set +# CONFIG_CRYPTO_CHACHA20 is not set +# CONFIG_CRYPTO_CHACHA20POLY1305 is not set +# CONFIG_CRYPTO_CHACHA20_NEON is not set +# CONFIG_CRYPTO_CHACHA20_X86_64 is not set +# CONFIG_CRYPTO_CHACHA_MIPS is not set +# CONFIG_CRYPTO_CMAC is not set +# CONFIG_CRYPTO_CRC32 is not set +# CONFIG_CRYPTO_CRC32C is not set +# CONFIG_CRYPTO_CRC32C_INTEL is not set +# CONFIG_CRYPTO_CRC32_ARM_CE is not set +# CONFIG_CRYPTO_CRCT10DIF is not set +# CONFIG_CRYPTO_CRCT10DIF_ARM64_CE is not set +# CONFIG_CRYPTO_CRCT10DIF_ARM_CE is not set +# CONFIG_CRYPTO_CRYPTD is not set +CONFIG_CRYPTO_CTR=y +# CONFIG_CRYPTO_CTS is not set +# CONFIG_CRYPTO_CURVE25519 is not set +# CONFIG_CRYPTO_CURVE25519_NEON is not set +# CONFIG_CRYPTO_CURVE25519_X86 is not set +# CONFIG_CRYPTO_DEFLATE is not set +# CONFIG_CRYPTO_DES is not set +# CONFIG_CRYPTO_DEV_AMLOGIC_GXL is not set +# CONFIG_CRYPTO_DEV_ATMEL_AES is not set +# CONFIG_CRYPTO_DEV_ATMEL_AUTHENC is not set +# CONFIG_CRYPTO_DEV_ATMEL_ECC is not set +# CONFIG_CRYPTO_DEV_ATMEL_SHA is not set +# CONFIG_CRYPTO_DEV_ATMEL_SHA204A is not set +# CONFIG_CRYPTO_DEV_ATMEL_TDES is not set +# CONFIG_CRYPTO_DEV_CAVIUM_ZIP is not set +# CONFIG_CRYPTO_DEV_CCP is not set +# CONFIG_CRYPTO_DEV_CCP_DEBUGFS is not set +# CONFIG_CRYPTO_DEV_CCREE is not set +# CONFIG_CRYPTO_DEV_FSL_CAAM is not set +# CONFIG_CRYPTO_DEV_FSL_CAAM_CRYPTO_API_DESC is not set +# CONFIG_CRYPTO_DEV_HIFN_795X is not set +# CONFIG_CRYPTO_DEV_HISI_SEC is not set +# CONFIG_CRYPTO_DEV_HISI_ZIP is not set +# CONFIG_CRYPTO_DEV_IMGTEC_HASH is not set +# CONFIG_CRYPTO_DEV_MARVELL_CESA is not set +# CONFIG_CRYPTO_DEV_MEDIATEK is not set +# CONFIG_CRYPTO_DEV_MV_CESA is not set +# CONFIG_CRYPTO_DEV_MXC_SCC is not set +# CONFIG_CRYPTO_DEV_MXS_DCP is not set +# CONFIG_CRYPTO_DEV_NITROX_CNN55XX is not set +# CONFIG_CRYPTO_DEV_OCTEONTX_CPT is not set +# CONFIG_CRYPTO_DEV_QAT_4XXX is not set +# CONFIG_CRYPTO_DEV_QAT_C3XXX is not set +# CONFIG_CRYPTO_DEV_QAT_C3XXXVF is not set +# CONFIG_CRYPTO_DEV_QAT_C62X is not set +# CONFIG_CRYPTO_DEV_QAT_C62XVF is not set +# CONFIG_CRYPTO_DEV_QAT_DH895xCC is not set +# CONFIG_CRYPTO_DEV_QAT_DH895xCCVF is not set +# CONFIG_CRYPTO_DEV_QCE is not set +# CONFIG_CRYPTO_DEV_S5P is not set +# CONFIG_CRYPTO_DEV_SAFEXCEL is not set +# CONFIG_CRYPTO_DEV_SAHARA is not set +# CONFIG_CRYPTO_DEV_SP_PSP is not set +# CONFIG_CRYPTO_DEV_TALITOS is not set +# CONFIG_CRYPTO_DEV_VIRTIO is not set +# CONFIG_CRYPTO_DH is not set +# CONFIG_CRYPTO_DRBG_CTR is not set +# CONFIG_CRYPTO_DRBG_HASH is not set +# CONFIG_CRYPTO_DRBG_MENU is not set +# CONFIG_CRYPTO_ECB is not set +# CONFIG_CRYPTO_ECDH is not set +# CONFIG_CRYPTO_ECDSA is not set +# CONFIG_CRYPTO_ECHAINIV is not set +# CONFIG_CRYPTO_ECRDSA is not set +# CONFIG_CRYPTO_ESSIV is not set +# CONFIG_CRYPTO_FCRYPT is not set +# CONFIG_CRYPTO_FIPS is not set +CONFIG_CRYPTO_GCM=y +CONFIG_CRYPTO_GF128MUL=y +CONFIG_CRYPTO_GHASH=y +# CONFIG_CRYPTO_GHASH_ARM64_CE is not set +# CONFIG_CRYPTO_GHASH_ARM_CE is not set +# CONFIG_CRYPTO_GHASH_CLMUL_NI_INTEL is not set +CONFIG_CRYPTO_HASH=y +CONFIG_CRYPTO_HASH2=y +# CONFIG_CRYPTO_HMAC is not set +# CONFIG_CRYPTO_HW is not set +# CONFIG_CRYPTO_JITTERENTROPY is not set +# CONFIG_CRYPTO_KEYWRAP is not set +# CONFIG_CRYPTO_KHAZAD is not set +CONFIG_CRYPTO_KPP=y +CONFIG_CRYPTO_KPP2=y +CONFIG_CRYPTO_LIB_AES=y +CONFIG_CRYPTO_LIB_ARC4=y +# CONFIG_CRYPTO_LIB_BLAKE2S_GENERIC is not set +# CONFIG_CRYPTO_LIB_CHACHA is not set +# CONFIG_CRYPTO_LIB_CHACHA20POLY1305 is not set +# CONFIG_CRYPTO_LIB_CURVE25519 is not set +# CONFIG_CRYPTO_LIB_POLY1305 is not set +CONFIG_CRYPTO_LIB_POLY1305_RSIZE=9 +# CONFIG_CRYPTO_LRW is not set +# CONFIG_CRYPTO_LZ4 is not set +# CONFIG_CRYPTO_LZ4HC is not set +# CONFIG_CRYPTO_LZO is not set +CONFIG_CRYPTO_MANAGER=y +CONFIG_CRYPTO_MANAGER2=y +CONFIG_CRYPTO_MANAGER_DISABLE_TESTS=y +# CONFIG_CRYPTO_MCRYPTD is not set +# CONFIG_CRYPTO_MD4 is not set +# CONFIG_CRYPTO_MD5 is not set +# CONFIG_CRYPTO_MICHAEL_MIC is not set +# CONFIG_CRYPTO_MORUS1280 is not set +# CONFIG_CRYPTO_MORUS1280_AVX2 is not set +# CONFIG_CRYPTO_MORUS1280_SSE2 is not set +# CONFIG_CRYPTO_MORUS640 is not set +# CONFIG_CRYPTO_MORUS640_SSE2 is not set +# CONFIG_CRYPTO_NHPOLY1305_NEON is not set +CONFIG_CRYPTO_NULL=y +CONFIG_CRYPTO_NULL2=y +# CONFIG_CRYPTO_OFB is not set +# CONFIG_CRYPTO_PCBC is not set +# CONFIG_CRYPTO_PCOMP is not set +# CONFIG_CRYPTO_PCOMP2 is not set +CONFIG_CRYPTO_PCRYPT=y +# CONFIG_CRYPTO_POLY1305 is not set +# CONFIG_CRYPTO_POLY1305_ARM is not set +# CONFIG_CRYPTO_POLY1305_MIPS is not set +# CONFIG_CRYPTO_POLY1305_NEON is not set +# CONFIG_CRYPTO_POLY1305_X86_64 is not set +# CONFIG_CRYPTO_RMD128 is not set +# CONFIG_CRYPTO_RMD160 is not set +# CONFIG_CRYPTO_RMD256 is not set +# CONFIG_CRYPTO_RMD320 is not set +# CONFIG_CRYPTO_RNG is not set +# CONFIG_CRYPTO_RSA is not set +# CONFIG_CRYPTO_SALSA20 is not set +# CONFIG_CRYPTO_SALSA20_586 is not set +# CONFIG_CRYPTO_SEED is not set +# CONFIG_CRYPTO_SEQIV is not set +# CONFIG_CRYPTO_SERPENT is not set +# CONFIG_CRYPTO_SHA1 is not set +# CONFIG_CRYPTO_SHA1_ARM is not set +# CONFIG_CRYPTO_SHA1_ARM64_CE is not set +# CONFIG_CRYPTO_SHA1_ARM_CE is not set +# CONFIG_CRYPTO_SHA1_ARM_NEON is not set +# CONFIG_CRYPTO_SHA256 is not set +# CONFIG_CRYPTO_SHA256_ARM is not set +# CONFIG_CRYPTO_SHA256_ARM64 is not set +# CONFIG_CRYPTO_SHA2_ARM64_CE is not set +# CONFIG_CRYPTO_SHA2_ARM_CE is not set +# CONFIG_CRYPTO_SHA3 is not set +# CONFIG_CRYPTO_SHA3_ARM64 is not set +# CONFIG_CRYPTO_SHA512 is not set +# CONFIG_CRYPTO_SHA512_ARM is not set +# CONFIG_CRYPTO_SHA512_ARM64 is not set +# CONFIG_CRYPTO_SHA512_ARM64_CE is not set +# CONFIG_CRYPTO_SIMD is not set +CONFIG_CRYPTO_SKCIPHER=y +CONFIG_CRYPTO_SKCIPHER2=y +# CONFIG_CRYPTO_SM2 is not set +# CONFIG_CRYPTO_SM3 is not set +# CONFIG_CRYPTO_SM3_ARM64_CE is not set +# CONFIG_CRYPTO_SM4 is not set +# CONFIG_CRYPTO_SM4_ARM64_CE is not set +# CONFIG_CRYPTO_SPECK is not set +# CONFIG_CRYPTO_STATS is not set +# CONFIG_CRYPTO_STREEBOG is not set +# CONFIG_CRYPTO_TEA is not set +# CONFIG_CRYPTO_TEST is not set +# CONFIG_CRYPTO_TGR192 is not set +# CONFIG_CRYPTO_TWOFISH is not set +# CONFIG_CRYPTO_TWOFISH_586 is not set +# CONFIG_CRYPTO_TWOFISH_COMMON is not set +# CONFIG_CRYPTO_USER is not set +# CONFIG_CRYPTO_USER_API_AEAD is not set +# CONFIG_CRYPTO_USER_API_ENABLE_OBSOLETE is not set +# CONFIG_CRYPTO_USER_API_HASH is not set +# CONFIG_CRYPTO_USER_API_RNG is not set +# CONFIG_CRYPTO_USER_API_RNG_CAVP is not set +# CONFIG_CRYPTO_USER_API_SKCIPHER is not set +# CONFIG_CRYPTO_VMAC is not set +# CONFIG_CRYPTO_WP512 is not set +# CONFIG_CRYPTO_XCBC is not set +# CONFIG_CRYPTO_XTS is not set +# CONFIG_CRYPTO_XXHASH is not set +# CONFIG_CRYPTO_ZLIB is not set +# CONFIG_CRYPTO_ZSTD is not set +# CONFIG_CS5535_MFGPT is not set +# CONFIG_CS89x0 is not set +# CONFIG_CS89x0_PLATFORM is not set +# CONFIG_CSD_LOCK_WAIT_DEBUG is not set +# CONFIG_CUSE is not set +# CONFIG_CW1200 is not set +# CONFIG_CXD2880_SPI_DRV is not set +# CONFIG_CXL_AFU_DRIVER_OPS is not set +# CONFIG_CXL_BASE is not set +# CONFIG_CXL_BUS is not set +# CONFIG_CXL_EEH is not set +# CONFIG_CXL_KERNEL_API is not set +# CONFIG_CXL_LIB is not set +# CONFIG_CYPRESS_FIRMWARE is not set +# CONFIG_DA280 is not set +# CONFIG_DA311 is not set +# CONFIG_DAMON is not set +# CONFIG_DAVICOM_PHY is not set +# CONFIG_DAX is not set +# CONFIG_DCB is not set +# CONFIG_DDR is not set +# CONFIG_DEBUG_ALIGN_RODATA is not set +# CONFIG_DEBUG_ATOMIC_SLEEP is not set +# CONFIG_DEBUG_BLOCK_EXT_DEVT is not set +# CONFIG_DEBUG_BUGVERBOSE is not set +# CONFIG_DEBUG_CREDENTIALS is not set +# CONFIG_DEBUG_DEVRES is not set +# CONFIG_DEBUG_DRIVER is not set +# CONFIG_DEBUG_EFI is not set +# CONFIG_DEBUG_FORCE_FUNCTION_ALIGN_32B is not set +# CONFIG_DEBUG_FORCE_FUNCTION_ALIGN_64B is not set +# CONFIG_DEBUG_FORCE_WEAK_PER_CPU is not set +CONFIG_DEBUG_FS=y +CONFIG_DEBUG_FS_ALLOW_ALL=y +# CONFIG_DEBUG_FS_ALLOW_NONE is not set +# CONFIG_DEBUG_FS_DISALLOW_MOUNT is not set +# CONFIG_DEBUG_GPIO is not set +# CONFIG_DEBUG_HIGHMEM is not set +# CONFIG_DEBUG_ICEDCC is not set +# CONFIG_DEBUG_INFO is not set +# CONFIG_DEBUG_INFO_BTF is not set +# CONFIG_DEBUG_INFO_COMPRESSED is not set +# CONFIG_DEBUG_INFO_DWARF4 is not set +# CONFIG_DEBUG_INFO_DWARF5 is not set +CONFIG_DEBUG_INFO_DWARF_TOOLCHAIN_DEFAULT=y +# CONFIG_DEBUG_INFO_REDUCED is not set +# CONFIG_DEBUG_INFO_SPLIT is not set +# CONFIG_DEBUG_IRQFLAGS is not set +CONFIG_DEBUG_KERNEL=y +# CONFIG_DEBUG_KMAP_LOCAL is not set +# CONFIG_DEBUG_KMEMLEAK is not set +# CONFIG_DEBUG_KOBJECT is not set +# CONFIG_DEBUG_KOBJECT_RELEASE is not set +# CONFIG_DEBUG_LIST is not set +# CONFIG_DEBUG_LL is not set +# CONFIG_DEBUG_LL_UART_8250 is not set +# CONFIG_DEBUG_LL_UART_PL01X is not set +# CONFIG_DEBUG_LOCKDEP is not set +# CONFIG_DEBUG_LOCKING_API_SELFTESTS is not set +# CONFIG_DEBUG_LOCK_ALLOC is not set +# CONFIG_DEBUG_MEMORY_INIT is not set +# CONFIG_DEBUG_MISC is not set +# CONFIG_DEBUG_MUTEXES is not set +# CONFIG_DEBUG_NOTIFIERS is not set +# CONFIG_DEBUG_NX_TEST is not set +# CONFIG_DEBUG_OBJECTS is not set +# CONFIG_DEBUG_PAGEALLOC is not set +# CONFIG_DEBUG_PAGE_REF is not set +# CONFIG_DEBUG_PERF_USE_VMALLOC is not set +# CONFIG_DEBUG_PER_CPU_MAPS is not set +# CONFIG_DEBUG_PINCTRL is not set +# CONFIG_DEBUG_PI_LIST is not set +# CONFIG_DEBUG_PLIST is not set +# CONFIG_DEBUG_PREEMPT is not set +# CONFIG_DEBUG_RODATA_TEST is not set +# CONFIG_DEBUG_RSEQ is not set +# CONFIG_DEBUG_RT_MUTEXES is not set +# CONFIG_DEBUG_RWSEMS is not set +# CONFIG_DEBUG_SECTION_MISMATCH is not set +# CONFIG_DEBUG_SEMIHOSTING is not set +# CONFIG_DEBUG_SG is not set +# CONFIG_DEBUG_SHIRQ is not set +# CONFIG_DEBUG_SLAB is not set +# CONFIG_DEBUG_SPINLOCK is not set +# CONFIG_DEBUG_STACKOVERFLOW is not set +# CONFIG_DEBUG_STACK_USAGE is not set +# CONFIG_DEBUG_STRICT_USER_COPY_CHECKS is not set +# CONFIG_DEBUG_TEST_DRIVER_REMOVE is not set +# CONFIG_DEBUG_TIMEKEEPING is not set +# CONFIG_DEBUG_UART_8250_PALMCHIP is not set +# CONFIG_DEBUG_UART_8250_WORD is not set +# CONFIG_DEBUG_UART_BCM63XX is not set +# CONFIG_DEBUG_UART_FLOW_CONTROL is not set +# CONFIG_DEBUG_USER is not set +# CONFIG_DEBUG_VIRTUAL is not set +# CONFIG_DEBUG_VM is not set +# CONFIG_DEBUG_VM_PGTABLE is not set +# CONFIG_DEBUG_WQ_FORCE_RR_CPU is not set +# CONFIG_DEBUG_WW_MUTEX_SLOWPATH is not set +# CONFIG_DEBUG_WX is not set +# CONFIG_DEBUG_ZBOOT is not set +# CONFIG_DECNET is not set +# CONFIG_DEFAULT_CODEL is not set +CONFIG_DEFAULT_CUBIC=y +CONFIG_DEFAULT_DEADLINE=y +# CONFIG_DEFAULT_FQ is not set +CONFIG_DEFAULT_FQ_CODEL=y +# CONFIG_DEFAULT_FQ_PIE is not set +CONFIG_DEFAULT_HOSTNAME="(none)" +CONFIG_DEFAULT_HUNG_TASK_TIMEOUT=120 +CONFIG_DEFAULT_INIT="" +CONFIG_DEFAULT_MMAP_MIN_ADDR=4096 +CONFIG_DEFAULT_NET_SCH="fq_codel" +# CONFIG_DEFAULT_NOOP is not set +# CONFIG_DEFAULT_PFIFO_FAST is not set +# CONFIG_DEFAULT_RENO is not set +CONFIG_DEFAULT_SECURITY="" +CONFIG_DEFAULT_SECURITY_DAC=y +# CONFIG_DEFAULT_SECURITY_SELINUX is not set +# CONFIG_DEFAULT_SFQ is not set +CONFIG_DEFAULT_TCP_CONG="cubic" +CONFIG_DEFCONFIG_LIST="/lib/modules/$UNAME_RELEASE/.config" +# CONFIG_DEFERRED_STRUCT_PAGE_INIT is not set +# CONFIG_DELL_LAPTOP is not set +# CONFIG_DELL_RBTN is not set +# CONFIG_DELL_SMBIOS is not set +# CONFIG_DELL_SMO8800 is not set +# CONFIG_DEPRECATED_PARAM_STRUCT is not set +# CONFIG_DETECT_HUNG_TASK is not set +# CONFIG_DEVKMEM is not set +# CONFIG_DEVMEM is not set +CONFIG_DEVPORT=y +# CONFIG_DEVPTS_MULTIPLE_INSTANCES is not set +# CONFIG_DEVTMPFS is not set +# CONFIG_DEVTMPFS_MOUNT is not set +# CONFIG_DEV_DAX is not set +# CONFIG_DGAP is not set +# CONFIG_DGNC is not set +# CONFIG_DHT11 is not set +# CONFIG_DISCONTIGMEM_MANUAL is not set +# CONFIG_DISPLAY_CONNECTOR_ANALOG_TV is not set +# CONFIG_DISPLAY_CONNECTOR_DVI is not set +# CONFIG_DISPLAY_CONNECTOR_HDMI is not set +# CONFIG_DISPLAY_ENCODER_TFP410 is not set +# CONFIG_DISPLAY_ENCODER_TPD12S015 is not set +# CONFIG_DISPLAY_PANEL_DPI is not set +# CONFIG_DISPLAY_PANEL_LGPHILIPS_LB035Q02 is not set +# CONFIG_DISPLAY_PANEL_TPO_TD028TTEC1 is not set +# CONFIG_DISPLAY_PANEL_TPO_TD043MTEA1 is not set +# CONFIG_DL2K is not set +# CONFIG_DLHL60D is not set +# CONFIG_DLM is not set +# CONFIG_DM9000 is not set +# CONFIG_DMABUF_DEBUG is not set +# CONFIG_DMABUF_HEAPS is not set +# CONFIG_DMABUF_MOVE_NOTIFY is not set +# CONFIG_DMABUF_SELFTESTS is not set +# CONFIG_DMABUF_SYSFS_STATS is not set +# CONFIG_DMADEVICES is not set +# CONFIG_DMADEVICES_DEBUG is not set +# CONFIG_DMARD06 is not set +# CONFIG_DMARD09 is not set +# CONFIG_DMARD10 is not set +# CONFIG_DMASCC is not set +# CONFIG_DMATEST is not set +# CONFIG_DMA_API_DEBUG is not set +CONFIG_DMA_COHERENT_POOL=y +CONFIG_DMA_DECLARE_COHERENT=y +# CONFIG_DMA_ENGINE is not set +# CONFIG_DMA_FENCE_TRACE is not set +# CONFIG_DMA_JZ4780 is not set +# CONFIG_DMA_MAP_BENCHMARK is not set +CONFIG_DMA_NONCOHERENT_MMAP=y +# CONFIG_DMA_NOOP_OPS is not set +# CONFIG_DMA_PERNUMA_CMA is not set +# CONFIG_DMA_RESTRICTED_POOL is not set +# CONFIG_DMA_SHARED_BUFFER is not set +# CONFIG_DMA_VIRT_OPS is not set +# CONFIG_DM_CACHE is not set +# CONFIG_DM_CLONE is not set +# CONFIG_DM_DEBUG is not set +# CONFIG_DM_DELAY is not set +# CONFIG_DM_DUST is not set +# CONFIG_DM_EBS is not set +# CONFIG_DM_ERA is not set +# CONFIG_DM_FLAKEY is not set +# CONFIG_DM_INTEGRITY is not set +# CONFIG_DM_LOG_USERSPACE is not set +# CONFIG_DM_LOG_WRITES is not set +# CONFIG_DM_MQ_DEFAULT is not set +# CONFIG_DM_MULTIPATH is not set +# CONFIG_DM_RAID is not set +# CONFIG_DM_SWITCH is not set +# CONFIG_DM_THIN_PROVISIONING is not set +# CONFIG_DM_UEVENT is not set +# CONFIG_DM_UNSTRIPED is not set +# CONFIG_DM_VERITY is not set +# CONFIG_DM_WRITECACHE is not set +# CONFIG_DM_ZERO is not set +# CONFIG_DNET is not set +# CONFIG_DNOTIFY is not set +# CONFIG_DNS_RESOLVER is not set +CONFIG_DOUBLEFAULT=y +# CONFIG_DP83640_PHY is not set +# CONFIG_DP83822_PHY is not set +# CONFIG_DP83848_PHY is not set +# CONFIG_DP83867_PHY is not set +# CONFIG_DP83869_PHY is not set +# CONFIG_DP83TC811_PHY is not set +# CONFIG_DPOT_DAC is not set +# CONFIG_DPS310 is not set +CONFIG_DQL=y +# CONFIG_DRAGONRISE_FF is not set +# CONFIG_DRM is not set +# CONFIG_DRM_AMDGPU is not set +# CONFIG_DRM_AMDGPU_CIK is not set +# CONFIG_DRM_AMDGPU_GART_DEBUGFS is not set +# CONFIG_DRM_AMDGPU_SI is not set +# CONFIG_DRM_AMDGPU_USERPTR is not set +# CONFIG_DRM_AMD_ACP is not set +# CONFIG_DRM_AMD_DC_DCN2_0 is not set +# CONFIG_DRM_AMD_DC_DCN3_0 is not set +# CONFIG_DRM_AMD_DC_HDCP is not set +# CONFIG_DRM_AMD_DC_SI is not set +# CONFIG_DRM_AMD_SECURE_DISPLAY is not set +# CONFIG_DRM_ANALOGIX_ANX6345 is not set +# CONFIG_DRM_ANALOGIX_ANX7625 is not set +# CONFIG_DRM_ANALOGIX_ANX78XX is not set +# CONFIG_DRM_ARCPGU is not set +# CONFIG_DRM_ARMADA is not set +# CONFIG_DRM_AST is not set +# CONFIG_DRM_ATMEL_HLCDC is not set +# CONFIG_DRM_BOCHS is not set +# CONFIG_DRM_CDNS_DSI is not set +# CONFIG_DRM_CDNS_MHDP8546 is not set +# CONFIG_DRM_CHIPONE_ICN6211 is not set +# CONFIG_DRM_CHRONTEL_CH7033 is not set +# CONFIG_DRM_CIRRUS_QEMU is not set +# CONFIG_DRM_DEBUG_DP_MST_TOPOLOGY_REFS is not set +# CONFIG_DRM_DEBUG_MM is not set +# CONFIG_DRM_DEBUG_SELFTEST is not set +# CONFIG_DRM_DISPLAY_CONNECTOR is not set +# CONFIG_DRM_DP_AUX_CHARDEV is not set +# CONFIG_DRM_DP_CEC is not set +# CONFIG_DRM_DUMB_VGA_DAC is not set +# CONFIG_DRM_DW_HDMI_CEC is not set +# CONFIG_DRM_ETNAVIV is not set +# CONFIG_DRM_EXYNOS is not set +# CONFIG_DRM_FBDEV_EMULATION is not set +# CONFIG_DRM_FBDEV_LEAK_PHYS_SMEM is not set +# CONFIG_DRM_FSL_DCU is not set +# CONFIG_DRM_GM12U320 is not set +# CONFIG_DRM_GMA500 is not set +# CONFIG_DRM_GUD is not set +# CONFIG_DRM_HDLCD is not set +# CONFIG_DRM_HISI_HIBMC is not set +# CONFIG_DRM_HISI_KIRIN is not set +# CONFIG_DRM_I2C_ADV7511 is not set +# CONFIG_DRM_I2C_CH7006 is not set +# CONFIG_DRM_I2C_NXP_TDA9950 is not set +# CONFIG_DRM_I2C_NXP_TDA998X is not set +# CONFIG_DRM_I2C_SIL164 is not set +# CONFIG_DRM_I915 is not set +# CONFIG_DRM_ITE_IT66121 is not set +# CONFIG_DRM_KOMEDA is not set +# CONFIG_DRM_LEGACY is not set +# CONFIG_DRM_LIB_RANDOM is not set +# CONFIG_DRM_LIMA is not set +# CONFIG_DRM_LOAD_EDID_FIRMWARE is not set +# CONFIG_DRM_LONTIUM_LT8912B is not set +# CONFIG_DRM_LONTIUM_LT9611 is not set +# CONFIG_DRM_LONTIUM_LT9611UXC is not set +# CONFIG_DRM_LVDS_CODEC is not set +# CONFIG_DRM_LVDS_ENCODER is not set +# CONFIG_DRM_MALI_DISPLAY is not set +# CONFIG_DRM_MCDE is not set +# CONFIG_DRM_MEGACHIPS_STDPXXXX_GE_B850V3_FW is not set +# CONFIG_DRM_MGAG200 is not set +# CONFIG_DRM_MXSFB is not set +# CONFIG_DRM_NOUVEAU is not set +# CONFIG_DRM_NWL_MIPI_DSI is not set +# CONFIG_DRM_NXP_PTN3460 is not set +# CONFIG_DRM_OMAP is not set +# CONFIG_DRM_PANEL_ABT_Y030XX067A is not set +# CONFIG_DRM_PANEL_ARM_VERSATILE is not set +# CONFIG_DRM_PANEL_ASUS_Z00T_TM5P5_NT35596 is not set +# CONFIG_DRM_PANEL_BOE_HIMAX8279D is not set +# CONFIG_DRM_PANEL_BOE_TV101WUM_NL6 is not set +# CONFIG_DRM_PANEL_DSI_CM is not set +# CONFIG_DRM_PANEL_ELIDA_KD35T133 is not set +# CONFIG_DRM_PANEL_FEIXIN_K101_IM2BA02 is not set +# CONFIG_DRM_PANEL_FEIYANG_FY07024DI26A30D is not set +# CONFIG_DRM_PANEL_ILITEK_IL9322 is not set +# CONFIG_DRM_PANEL_ILITEK_ILI9341 is not set +# CONFIG_DRM_PANEL_ILITEK_ILI9806E is not set +# CONFIG_DRM_PANEL_ILITEK_ILI9881C is not set +# CONFIG_DRM_PANEL_INNOLUX_EJ030NA is not set +# CONFIG_DRM_PANEL_INNOLUX_P079ZCA is not set +# CONFIG_DRM_PANEL_JDI_LT070ME05000 is not set +# CONFIG_DRM_PANEL_KHADAS_TS050 is not set +# CONFIG_DRM_PANEL_KINGDISPLAY_KD097D04 is not set +# CONFIG_DRM_PANEL_LEADTEK_LTK050H3146W is not set +# CONFIG_DRM_PANEL_LEADTEK_LTK500HD1829 is not set +# CONFIG_DRM_PANEL_LG_LB035Q02 is not set +# CONFIG_DRM_PANEL_LG_LG4573 is not set +# CONFIG_DRM_PANEL_LVDS is not set +# CONFIG_DRM_PANEL_MANTIX_MLAF057WE51 is not set +# CONFIG_DRM_PANEL_MIPI_DBI is not set +# CONFIG_DRM_PANEL_NEC_NL8048HL11 is not set +# CONFIG_DRM_PANEL_NOVATEK_NT35510 is not set +# CONFIG_DRM_PANEL_NOVATEK_NT36672A is not set +# CONFIG_DRM_PANEL_NOVATEK_NT39016 is not set +# CONFIG_DRM_PANEL_OLIMEX_LCD_OLINUXINO is not set +# CONFIG_DRM_PANEL_ORISETECH_OTM8009A is not set +# CONFIG_DRM_PANEL_OSD_OSD101T2587_53TS is not set +# CONFIG_DRM_PANEL_PANASONIC_VVX10F034N00 is not set +# CONFIG_DRM_PANEL_RASPBERRYPI_TOUCHSCREEN is not set +# CONFIG_DRM_PANEL_RAYDIUM_RM67191 is not set +# CONFIG_DRM_PANEL_RAYDIUM_RM68200 is not set +# CONFIG_DRM_PANEL_ROCKTECH_JH057N00900 is not set +# CONFIG_DRM_PANEL_RONBO_RB070D30 is not set +# CONFIG_DRM_PANEL_SAMSUNG_ATNA33XC20 is not set +# CONFIG_DRM_PANEL_SAMSUNG_DB7430 is not set +# CONFIG_DRM_PANEL_SAMSUNG_LD9040 is not set +# CONFIG_DRM_PANEL_SAMSUNG_S6D16D0 is not set +# CONFIG_DRM_PANEL_SAMSUNG_S6E3HA2 is not set +# CONFIG_DRM_PANEL_SAMSUNG_S6E63J0X03 is not set +# CONFIG_DRM_PANEL_SAMSUNG_S6E63M0 is not set +# CONFIG_DRM_PANEL_SAMSUNG_S6E88A0_AMS452EF01 is not set +# CONFIG_DRM_PANEL_SAMSUNG_S6E8AA0 is not set +# CONFIG_DRM_PANEL_SAMSUNG_SOFEF00 is not set +# CONFIG_DRM_PANEL_SEIKO_43WVF1G is not set +# CONFIG_DRM_PANEL_SHARP_LQ101R1SX01 is not set +# CONFIG_DRM_PANEL_SHARP_LS037V7DW01 is not set +# CONFIG_DRM_PANEL_SHARP_LS043T1LE01 is not set +# CONFIG_DRM_PANEL_SIMPLE is not set +# CONFIG_DRM_PANEL_SITRONIX_ST7701 is not set +# CONFIG_DRM_PANEL_SITRONIX_ST7703 is not set +# CONFIG_DRM_PANEL_SITRONIX_ST7789V is not set +# CONFIG_DRM_PANEL_SONY_ACX424AKP is not set +# CONFIG_DRM_PANEL_SONY_ACX565AKM is not set +# CONFIG_DRM_PANEL_TDO_TL070WSH30 is not set +# CONFIG_DRM_PANEL_TPO_TD028TTEC1 is not set +# CONFIG_DRM_PANEL_TPO_TD043MTEA1 is not set +# CONFIG_DRM_PANEL_TPO_TPG110 is not set +# CONFIG_DRM_PANEL_TPO_Y17P is not set +# CONFIG_DRM_PANEL_TRULY_NT35597_WQXGA is not set +# CONFIG_DRM_PANEL_VISIONOX_RM69299 is not set +# CONFIG_DRM_PANEL_WIDECHIPS_WS2401 is not set +# CONFIG_DRM_PANEL_XINPENG_XPP055C272 is not set +# CONFIG_DRM_PANFROST is not set +# CONFIG_DRM_PARADE_PS8622 is not set +# CONFIG_DRM_PARADE_PS8640 is not set +# CONFIG_DRM_PL111 is not set +# CONFIG_DRM_QXL is not set +# CONFIG_DRM_RADEON is not set +# CONFIG_DRM_RADEON_USERPTR is not set +# CONFIG_DRM_RCAR_DW_HDMI is not set +# CONFIG_DRM_RCAR_LVDS is not set +# CONFIG_DRM_ROCKCHIP is not set +# CONFIG_DRM_SII902X is not set +# CONFIG_DRM_SII9234 is not set +# CONFIG_DRM_SIL_SII8620 is not set +# CONFIG_DRM_SIMPLEDRM is not set +# CONFIG_DRM_SIMPLE_BRIDGE is not set +# CONFIG_DRM_STI is not set +# CONFIG_DRM_STM is not set +# CONFIG_DRM_SUN4I is not set +# CONFIG_DRM_THINE_THC63LVD1024 is not set +# CONFIG_DRM_TIDSS is not set +# CONFIG_DRM_TILCDC is not set +# CONFIG_DRM_TINYDRM is not set +# CONFIG_DRM_TI_SN65DSI83 is not set +# CONFIG_DRM_TI_SN65DSI86 is not set +# CONFIG_DRM_TI_TFP410 is not set +# CONFIG_DRM_TI_TPD12S015 is not set +# CONFIG_DRM_TOSHIBA_TC358762 is not set +# CONFIG_DRM_TOSHIBA_TC358764 is not set +# CONFIG_DRM_TOSHIBA_TC358767 is not set +# CONFIG_DRM_TOSHIBA_TC358768 is not set +# CONFIG_DRM_TOSHIBA_TC358775 is not set +# CONFIG_DRM_TVE200 is not set +# CONFIG_DRM_UDL is not set +# CONFIG_DRM_VBOXVIDEO is not set +# CONFIG_DRM_VC4_HDMI_CEC is not set +# CONFIG_DRM_VGEM is not set +# CONFIG_DRM_VIRTIO_GPU is not set +# CONFIG_DRM_VKMS is not set +# CONFIG_DRM_VMWGFX is not set +# CONFIG_DRM_XEN is not set +# CONFIG_DS1682 is not set +# CONFIG_DS1803 is not set +# CONFIG_DS4424 is not set +# CONFIG_DST_CACHE is not set +# CONFIG_DTLK is not set +# CONFIG_DUMMY is not set +CONFIG_DUMMY_CONSOLE_COLUMNS=80 +CONFIG_DUMMY_CONSOLE_ROWS=25 +# CONFIG_DUMMY_IRQ is not set +# CONFIG_DVB_A8293 is not set +# CONFIG_DVB_AF9013 is not set +# CONFIG_DVB_AF9033 is not set +# CONFIG_DVB_AS102 is not set +# CONFIG_DVB_ASCOT2E is not set +# CONFIG_DVB_ATBM8830 is not set +# CONFIG_DVB_AU8522_DTV is not set +# CONFIG_DVB_AU8522_V4L is not set +# CONFIG_DVB_B2C2_FLEXCOP_USB is not set +# CONFIG_DVB_BCM3510 is not set +# CONFIG_DVB_CORE is not set +# CONFIG_DVB_CX22700 is not set +# CONFIG_DVB_CX22702 is not set +# CONFIG_DVB_CX24110 is not set +# CONFIG_DVB_CX24116 is not set +# CONFIG_DVB_CX24117 is not set +# CONFIG_DVB_CX24120 is not set +# CONFIG_DVB_CX24123 is not set +# CONFIG_DVB_CXD2099 is not set +# CONFIG_DVB_CXD2820R is not set +# CONFIG_DVB_CXD2841ER is not set +# CONFIG_DVB_CXD2880 is not set +# CONFIG_DVB_DEMUX_SECTION_LOSS_LOG is not set +# CONFIG_DVB_DIB3000MB is not set +# CONFIG_DVB_DIB3000MC is not set +# CONFIG_DVB_DIB7000M is not set +# CONFIG_DVB_DIB7000P is not set +# CONFIG_DVB_DIB8000 is not set +# CONFIG_DVB_DIB9000 is not set +# CONFIG_DVB_DRX39XYJ is not set +# CONFIG_DVB_DRXD is not set +# CONFIG_DVB_DRXK is not set +# CONFIG_DVB_DS3000 is not set +# CONFIG_DVB_DUMMY_FE is not set +# CONFIG_DVB_DYNAMIC_MINORS is not set +# CONFIG_DVB_EC100 is not set +# CONFIG_DVB_FIREDTV is not set +# CONFIG_DVB_HELENE is not set +# CONFIG_DVB_HORUS3A is not set +# CONFIG_DVB_ISL6405 is not set +# CONFIG_DVB_ISL6421 is not set +# CONFIG_DVB_ISL6423 is not set +# CONFIG_DVB_IX2505V is not set +# CONFIG_DVB_L64781 is not set +# CONFIG_DVB_LG2160 is not set +# CONFIG_DVB_LGDT3305 is not set +# CONFIG_DVB_LGDT3306A is not set +# CONFIG_DVB_LGDT330X is not set +# CONFIG_DVB_LGS8GL5 is not set +# CONFIG_DVB_LGS8GXX is not set +# CONFIG_DVB_LNBH25 is not set +# CONFIG_DVB_LNBH29 is not set +# CONFIG_DVB_LNBP21 is not set +# CONFIG_DVB_LNBP22 is not set +# CONFIG_DVB_M88DS3103 is not set +# CONFIG_DVB_M88RS2000 is not set +CONFIG_DVB_MAX_ADAPTERS=16 +# CONFIG_DVB_MB86A16 is not set +# CONFIG_DVB_MB86A20S is not set +# CONFIG_DVB_MMAP is not set +# CONFIG_DVB_MN88443X is not set +# CONFIG_DVB_MN88472 is not set +# CONFIG_DVB_MN88473 is not set +# CONFIG_DVB_MT312 is not set +# CONFIG_DVB_MT352 is not set +# CONFIG_DVB_MXL5XX is not set +# CONFIG_DVB_MXL692 is not set +# CONFIG_DVB_NET is not set +# CONFIG_DVB_NXT200X is not set +# CONFIG_DVB_NXT6000 is not set +# CONFIG_DVB_OR51132 is not set +# CONFIG_DVB_OR51211 is not set +# CONFIG_DVB_PLATFORM_DRIVERS is not set +# CONFIG_DVB_PLL is not set +# CONFIG_DVB_RTL2830 is not set +# CONFIG_DVB_RTL2832 is not set +# CONFIG_DVB_RTL2832_SDR is not set +# CONFIG_DVB_S5H1409 is not set +# CONFIG_DVB_S5H1411 is not set +# CONFIG_DVB_S5H1420 is not set +# CONFIG_DVB_S5H1432 is not set +# CONFIG_DVB_S921 is not set +# CONFIG_DVB_SI2165 is not set +# CONFIG_DVB_SI2168 is not set +# CONFIG_DVB_SI21XX is not set +# CONFIG_DVB_SP2 is not set +# CONFIG_DVB_SP8870 is not set +# CONFIG_DVB_SP887X is not set +# CONFIG_DVB_STB0899 is not set +# CONFIG_DVB_STB6000 is not set +# CONFIG_DVB_STB6100 is not set +# CONFIG_DVB_STV0288 is not set +# CONFIG_DVB_STV0297 is not set +# CONFIG_DVB_STV0299 is not set +# CONFIG_DVB_STV0367 is not set +# CONFIG_DVB_STV0900 is not set +# CONFIG_DVB_STV090x is not set +# CONFIG_DVB_STV0910 is not set +# CONFIG_DVB_STV6110 is not set +# CONFIG_DVB_STV6110x is not set +# CONFIG_DVB_STV6111 is not set +# CONFIG_DVB_TC90522 is not set +# CONFIG_DVB_TDA10021 is not set +# CONFIG_DVB_TDA10023 is not set +# CONFIG_DVB_TDA10048 is not set +# CONFIG_DVB_TDA1004X is not set +# CONFIG_DVB_TDA10071 is not set +# CONFIG_DVB_TDA10086 is not set +# CONFIG_DVB_TDA18271C2DD is not set +# CONFIG_DVB_TDA665x is not set +# CONFIG_DVB_TDA8083 is not set +# CONFIG_DVB_TDA8261 is not set +# CONFIG_DVB_TDA826X is not set +# CONFIG_DVB_TEST_DRIVERS is not set +# CONFIG_DVB_TS2020 is not set +# CONFIG_DVB_TTUSB_BUDGET is not set +# CONFIG_DVB_TTUSB_DEC is not set +# CONFIG_DVB_TUA6100 is not set +# CONFIG_DVB_TUNER_CX24113 is not set +# CONFIG_DVB_TUNER_DIB0070 is not set +# CONFIG_DVB_TUNER_DIB0090 is not set +# CONFIG_DVB_TUNER_ITD1000 is not set +# CONFIG_DVB_ULE_DEBUG is not set +# CONFIG_DVB_USB_V2 is not set +# CONFIG_DVB_VES1820 is not set +# CONFIG_DVB_VES1X93 is not set +# CONFIG_DVB_ZD1301_DEMOD is not set +# CONFIG_DVB_ZL10036 is not set +# CONFIG_DVB_ZL10039 is not set +# CONFIG_DVB_ZL10353 is not set +# CONFIG_DWC_XLGMAC is not set +# CONFIG_DWMAC_DWC_QOS_ETH is not set +# CONFIG_DWMAC_INTEL_PLAT is not set +# CONFIG_DWMAC_IPQ806X is not set +# CONFIG_DWMAC_LOONGSON is not set +# CONFIG_DWMAC_LPC18XX is not set +# CONFIG_DWMAC_MESON is not set +# CONFIG_DWMAC_ROCKCHIP is not set +# CONFIG_DWMAC_SOCFPGA is not set +# CONFIG_DWMAC_STI is not set +# CONFIG_DW_AXI_DMAC is not set +# CONFIG_DW_DMAC is not set +# CONFIG_DW_DMAC_PCI is not set +# CONFIG_DW_EDMA is not set +# CONFIG_DW_EDMA_PCIE is not set +# CONFIG_DW_WATCHDOG is not set +# CONFIG_DW_XDATA_PCIE is not set +# CONFIG_DYNAMIC_DEBUG is not set +CONFIG_DYNAMIC_DEBUG_CORE=y +# CONFIG_E100 is not set +# CONFIG_E1000 is not set +# CONFIG_E1000E is not set +# CONFIG_E1000E_HWTS is not set +# CONFIG_EARLY_PRINTK_8250 is not set +# CONFIG_EARLY_PRINTK_USB_XDBC is not set +# CONFIG_EBC_C384_WDT is not set +# CONFIG_ECHO is not set +# CONFIG_ECRYPT_FS is not set +# CONFIG_EDAC is not set +# CONFIG_EEEPC_LAPTOP is not set +# CONFIG_EEPROM_93CX6 is not set +# CONFIG_EEPROM_93XX46 is not set +# CONFIG_EEPROM_AT24 is not set +# CONFIG_EEPROM_AT25 is not set +# CONFIG_EEPROM_DIGSY_MTC_CFG is not set +# CONFIG_EEPROM_EE1004 is not set +# CONFIG_EEPROM_IDT_89HPESX is not set +# CONFIG_EEPROM_LEGACY is not set +# CONFIG_EEPROM_MAX6875 is not set +# CONFIG_EFI is not set +CONFIG_EFI_PARTITION=y +# CONFIG_EFI_VARS_PSTORE is not set +# CONFIG_EFS_FS is not set +CONFIG_ELFCORE=y +# CONFIG_ELF_CORE is not set +# CONFIG_EMAC_ROCKCHIP is not set +CONFIG_EMBEDDED=y +# CONFIG_EM_TIMER_STI is not set +# CONFIG_ENABLE_MUST_CHECK is not set +CONFIG_ENABLE_WARN_DEPRECATED=y +# CONFIG_ENA_ETHERNET is not set +# CONFIG_ENC28J60 is not set +# CONFIG_ENCLOSURE_SERVICES is not set +# CONFIG_ENCRYPTED_KEYS is not set +# CONFIG_ENCX24J600 is not set +# CONFIG_ENERGY_MODEL is not set +# CONFIG_ENIC is not set +# CONFIG_ENVELOPE_DETECTOR is not set +# CONFIG_EPAPR_PARAVIRT is not set +# CONFIG_EPIC100 is not set +CONFIG_EPOLL=y +# CONFIG_EQUALIZER is not set +# CONFIG_EROFS_FS is not set +# CONFIG_ET131X is not set +CONFIG_ETHERNET=y +# CONFIG_ETHOC is not set +CONFIG_ETHTOOL_NETLINK=y +CONFIG_EVENTFD=y +# CONFIG_EVM is not set +# CONFIG_EXFAT_FS is not set +CONFIG_EXPERT=y +CONFIG_EXPORTFS=y +# CONFIG_EXPORTFS_BLOCK_OPS is not set +# CONFIG_EXT2_FS is not set +CONFIG_EXT2_FS_XATTR=y +# CONFIG_EXT3_FS is not set +# CONFIG_EXT4_DEBUG is not set +# CONFIG_EXT4_ENCRYPTION is not set +# CONFIG_EXT4_FS is not set +# CONFIG_EXT4_FS_POSIX_ACL is not set +# CONFIG_EXT4_FS_SECURITY is not set +CONFIG_EXT4_USE_FOR_EXT2=y +# CONFIG_EXTCON is not set +# CONFIG_EXTCON_ADC_JACK is not set +# CONFIG_EXTCON_ARIZONA is not set +# CONFIG_EXTCON_AXP288 is not set +# CONFIG_EXTCON_FSA9480 is not set +# CONFIG_EXTCON_GPIO is not set +# CONFIG_EXTCON_INTEL_INT3496 is not set +# CONFIG_EXTCON_MAX3355 is not set +# CONFIG_EXTCON_PTN5150 is not set +# CONFIG_EXTCON_QCOM_SPMI_MISC is not set +# CONFIG_EXTCON_RT8973A is not set +# CONFIG_EXTCON_SM5502 is not set +# CONFIG_EXTCON_USBC_TUSB320 is not set +# CONFIG_EXTCON_USB_GPIO is not set +CONFIG_EXTRA_FIRMWARE="" +CONFIG_EXTRA_TARGETS="" +# CONFIG_EXYNOS_ADC is not set +# CONFIG_EXYNOS_VIDEO is not set +# CONFIG_EZCHIP_NPS_MANAGEMENT_ENET is not set +# CONFIG_EZX_PCAP is not set +# CONFIG_F2FS_CHECK_FS is not set +# CONFIG_F2FS_FAULT_INJECTION is not set +# CONFIG_F2FS_FS is not set +# CONFIG_F2FS_FS_COMPRESSION is not set +# CONFIG_F2FS_FS_ENCRYPTION is not set +# CONFIG_F2FS_FS_POSIX_ACL is not set +# CONFIG_F2FS_FS_SECURITY is not set +CONFIG_F2FS_FS_XATTR=y +# CONFIG_F2FS_IOSTAT is not set +# CONFIG_F2FS_IO_TRACE is not set +CONFIG_F2FS_STAT_FS=y +# CONFIG_FAILOVER is not set +# CONFIG_FAIR_GROUP_SCHED is not set +# CONFIG_FANOTIFY is not set +# CONFIG_FANOTIFY_ACCESS_PERMISSIONS is not set +CONFIG_FAT_DEFAULT_CODEPAGE=437 +CONFIG_FAT_DEFAULT_IOCHARSET="iso8859-1" +# CONFIG_FAT_DEFAULT_UTF8 is not set +# CONFIG_FAT_FS is not set +# CONFIG_FAULT_INJECTION is not set +# CONFIG_FB is not set +# CONFIG_FB_3DFX is not set +# CONFIG_FB_ARC is not set +# CONFIG_FB_ARK is not set +# CONFIG_FB_ARMCLCD is not set +# CONFIG_FB_ASILIANT is not set +# CONFIG_FB_ATMEL is not set +# CONFIG_FB_ATY is not set +# CONFIG_FB_ATY128 is not set +# CONFIG_FB_AUO_K190X is not set +# CONFIG_FB_BACKLIGHT is not set +# CONFIG_FB_BIG_ENDIAN is not set +# CONFIG_FB_BOOT_VESA_SUPPORT is not set +# CONFIG_FB_BOTH_ENDIAN is not set +# CONFIG_FB_BROADSHEET is not set +# CONFIG_FB_CARMINE is not set +# CONFIG_FB_CFB_COPYAREA is not set +# CONFIG_FB_CFB_FILLRECT is not set +# CONFIG_FB_CFB_IMAGEBLIT is not set +# CONFIG_FB_CFB_REV_PIXELS_IN_BYTE is not set +# CONFIG_FB_CIRRUS is not set +# CONFIG_FB_CYBER2000 is not set +# CONFIG_FB_DA8XX is not set +# CONFIG_FB_DDC is not set +# CONFIG_FB_FLEX is not set +# CONFIG_FB_FOREIGN_ENDIAN is not set +# CONFIG_FB_FSL_DIU is not set +# CONFIG_FB_GEODE is not set +# CONFIG_FB_GOLDFISH is not set +# CONFIG_FB_HGA is not set +# CONFIG_FB_I740 is not set +# CONFIG_FB_IBM_GXT4500 is not set +# CONFIG_FB_IMSTT is not set +# CONFIG_FB_IMX is not set +# CONFIG_FB_KYRO is not set +# CONFIG_FB_LE80578 is not set +# CONFIG_FB_LITTLE_ENDIAN is not set +# CONFIG_FB_MACMODES is not set +# CONFIG_FB_MATROX is not set +# CONFIG_FB_MB862XX is not set +# CONFIG_FB_METRONOME is not set +# CONFIG_FB_MODE_HELPERS is not set +# CONFIG_FB_MXS is not set +# CONFIG_FB_N411 is not set +# CONFIG_FB_NEOMAGIC is not set +CONFIG_FB_NOTIFY=y +# CONFIG_FB_NVIDIA is not set +# CONFIG_FB_OF is not set +# CONFIG_FB_OMAP2 is not set +# CONFIG_FB_OPENCORES is not set +# CONFIG_FB_PM2 is not set +# CONFIG_FB_PM3 is not set +# CONFIG_FB_PS3 is not set +# CONFIG_FB_PXA is not set +# CONFIG_FB_RADEON is not set +# CONFIG_FB_RIVA is not set +# CONFIG_FB_S1D13XXX is not set +# CONFIG_FB_S3 is not set +# CONFIG_FB_SAVAGE is not set +# CONFIG_FB_SIMPLE is not set +# CONFIG_FB_SIS is not set +# CONFIG_FB_SM712 is not set +# CONFIG_FB_SM750 is not set +# CONFIG_FB_SMSCUFX is not set +# CONFIG_FB_SSD1307 is not set +# CONFIG_FB_SVGALIB is not set +# CONFIG_FB_SYS_COPYAREA is not set +# CONFIG_FB_SYS_FILLRECT is not set +# CONFIG_FB_SYS_FOPS is not set +# CONFIG_FB_SYS_IMAGEBLIT is not set +# CONFIG_FB_TFT is not set +# CONFIG_FB_TFT_AGM1264K_FL is not set +# CONFIG_FB_TFT_BD663474 is not set +# CONFIG_FB_TFT_FBTFT_DEVICE is not set +# CONFIG_FB_TFT_HX8340BN is not set +# CONFIG_FB_TFT_HX8347D is not set +# CONFIG_FB_TFT_HX8353D is not set +# CONFIG_FB_TFT_HX8357D is not set +# CONFIG_FB_TFT_ILI9163 is not set +# CONFIG_FB_TFT_ILI9320 is not set +# CONFIG_FB_TFT_ILI9325 is not set +# CONFIG_FB_TFT_ILI9340 is not set +# CONFIG_FB_TFT_ILI9341 is not set +# CONFIG_FB_TFT_ILI9481 is not set +# CONFIG_FB_TFT_ILI9486 is not set +# CONFIG_FB_TFT_PCD8544 is not set +# CONFIG_FB_TFT_RA8875 is not set +# CONFIG_FB_TFT_S6D02A1 is not set +# CONFIG_FB_TFT_S6D1121 is not set +# CONFIG_FB_TFT_SEPS525 is not set +# CONFIG_FB_TFT_SH1106 is not set +# CONFIG_FB_TFT_SSD1289 is not set +# CONFIG_FB_TFT_SSD1305 is not set +# CONFIG_FB_TFT_SSD1306 is not set +# CONFIG_FB_TFT_SSD1325 is not set +# CONFIG_FB_TFT_SSD1331 is not set +# CONFIG_FB_TFT_SSD1351 is not set +# CONFIG_FB_TFT_ST7735R is not set +# CONFIG_FB_TFT_ST7789V is not set +# CONFIG_FB_TFT_TINYLCD is not set +# CONFIG_FB_TFT_TLS8204 is not set +# CONFIG_FB_TFT_UC1611 is not set +# CONFIG_FB_TFT_UC1701 is not set +# CONFIG_FB_TFT_UPD161704 is not set +# CONFIG_FB_TFT_WATTEROTT is not set +# CONFIG_FB_TILEBLITTING is not set +# CONFIG_FB_TMIO is not set +# CONFIG_FB_TRIDENT is not set +# CONFIG_FB_UDL is not set +# CONFIG_FB_UVESA is not set +# CONFIG_FB_VGA16 is not set +# CONFIG_FB_VIA is not set +# CONFIG_FB_VIRTUAL is not set +# CONFIG_FB_VOODOO1 is not set +# CONFIG_FB_VT8623 is not set +# CONFIG_FB_XGI is not set +# CONFIG_FCOE is not set +# CONFIG_FCOE_FNIC is not set +# CONFIG_FDDI is not set +# CONFIG_FEALNX is not set +# CONFIG_FENCE_TRACE is not set +# CONFIG_FHANDLE is not set +CONFIG_FIB_RULES=y +# CONFIG_FIELDBUS_DEV is not set +CONFIG_FILE_LOCKING=y +# CONFIG_FIND_BIT_BENCHMARK is not set +# CONFIG_FIREWIRE is not set +# CONFIG_FIREWIRE_NOSY is not set +# CONFIG_FIREWIRE_SERIAL is not set +# CONFIG_FIRMWARE_EDID is not set +# CONFIG_FIRMWARE_IN_KERNEL is not set +# CONFIG_FIRMWARE_MEMMAP is not set +# CONFIG_FIT_PARTITION is not set +# CONFIG_FIXED_PHY is not set +CONFIG_FLATMEM=y +CONFIG_FLATMEM_MANUAL=y +CONFIG_FLAT_NODE_MEM_MAP=y +# CONFIG_FM10K is not set +# CONFIG_FMC is not set +# CONFIG_FONTS is not set +# CONFIG_FONT_6x8 is not set +# CONFIG_FONT_TER16x32 is not set +# CONFIG_FORCEDETH is not set +CONFIG_FORCE_MAX_ZONEORDER=11 +CONFIG_FORTIFY_SOURCE=y +# CONFIG_FPGA is not set +# CONFIG_FRAMEBUFFER_CONSOLE is not set +# CONFIG_FRAMEBUFFER_CONSOLE_DEFERRED_TAKEOVER is not set +# CONFIG_FRAMEBUFFER_CONSOLE_LEGACY_ACCELERATION is not set +# CONFIG_FRAME_POINTER is not set +CONFIG_FRAME_WARN=1024 +# CONFIG_FREEZER is not set +# CONFIG_FRONTSWAP is not set +# CONFIG_FSCACHE is not set +# CONFIG_FSI is not set +# CONFIG_FSL_EDMA is not set +# CONFIG_FSL_ENETC is not set +# CONFIG_FSL_ENETC_IERB is not set +# CONFIG_FSL_ENETC_MDIO is not set +# CONFIG_FSL_ENETC_VF is not set +# CONFIG_FSL_ERRATUM_A008585 is not set +# CONFIG_FSL_MC_BUS is not set +# CONFIG_FSL_PQ_MDIO is not set +# CONFIG_FSL_QDMA is not set +# CONFIG_FSL_RCPM is not set +# CONFIG_FSL_XGMAC_MDIO is not set +CONFIG_FSNOTIFY=y +# CONFIG_FS_DAX is not set +# CONFIG_FS_ENCRYPTION is not set +# CONFIG_FS_POSIX_ACL is not set +# CONFIG_FS_VERITY is not set +# CONFIG_FTGMAC100 is not set +# CONFIG_FTL is not set +# CONFIG_FTMAC100 is not set +# CONFIG_FTRACE is not set +# CONFIG_FTRACE_RECORD_RECURSION is not set +# CONFIG_FTRACE_STARTUP_TEST is not set +# CONFIG_FTR_FIXUP_SELFTEST is not set +# CONFIG_FTWDT010_WATCHDOG is not set +# CONFIG_FUJITSU_ERRATUM_010001 is not set +# CONFIG_FUJITSU_ES is not set +# CONFIG_FUJITSU_LAPTOP is not set +# CONFIG_FUJITSU_TABLET is not set +# CONFIG_FUNCTION_TRACER is not set +# CONFIG_FUSE_FS is not set +# CONFIG_FUSION is not set +# CONFIG_FUSION_FC is not set +# CONFIG_FUSION_SAS is not set +# CONFIG_FUSION_SPI is not set +CONFIG_FUTEX=y +CONFIG_FUTEX_PI=y +# CONFIG_FW_CFG_SYSFS is not set +CONFIG_FW_LOADER=y +# CONFIG_FW_LOADER_COMPRESS is not set +CONFIG_FW_LOADER_USER_HELPER=y +CONFIG_FW_LOADER_USER_HELPER_FALLBACK=y +# CONFIG_FXAS21002C is not set +# CONFIG_FXLS8962AF_I2C is not set +# CONFIG_FXLS8962AF_SPI is not set +# CONFIG_FXOS8700_I2C is not set +# CONFIG_FXOS8700_SPI is not set +CONFIG_GACT_PROB=y +# CONFIG_GADGET_UAC1 is not set +# CONFIG_GAMEPORT is not set +# CONFIG_GATEWORKS_GW16083 is not set +# CONFIG_GCC_PLUGINS is not set +# CONFIG_GCOV is not set +# CONFIG_GCOV_KERNEL is not set +# CONFIG_GDB_SCRIPTS is not set +# CONFIG_GEMINI_ETHERNET is not set +# CONFIG_GENERIC_ADC_BATTERY is not set +# CONFIG_GENERIC_ADC_THERMAL is not set +CONFIG_GENERIC_CALIBRATE_DELAY=y +# CONFIG_GENERIC_CPU_DEVICES is not set +CONFIG_GENERIC_HWEIGHT=y +# CONFIG_GENERIC_IRQ_DEBUGFS is not set +CONFIG_GENERIC_IRQ_IPI=y +CONFIG_GENERIC_IRQ_PROBE=y +CONFIG_GENERIC_NET_UTILS=y +# CONFIG_GENERIC_PHY is not set +CONFIG_GENERIC_PTDUMP=y +CONFIG_GENERIC_VDSO_TIME_NS=y +# CONFIG_GENEVE is not set +# CONFIG_GENWQE is not set +# CONFIG_GFS2_FS is not set +# CONFIG_GIGASET_CAPI is not set +# CONFIG_GIGASET_DEBUG is not set +# CONFIG_GIGASET_DUMMYLL is not set +# CONFIG_GLOB_SELFTEST is not set +# CONFIG_GNSS is not set +# CONFIG_GOLDFISH is not set +# CONFIG_GOOGLE_FIRMWARE is not set +# CONFIG_GOOGLE_FRAMEBUFFER_COREBOOT is not set +# CONFIG_GOOGLE_MEMCONSOLE_X86_LEGACY is not set +# CONFIG_GOOGLE_SMI is not set +# CONFIG_GP2AP002 is not set +# CONFIG_GP2AP020A00F is not set +# CONFIG_GPD_POCKET_FAN is not set +CONFIG_GPIOLIB=y +CONFIG_GPIOLIB_FASTPATH_LIMIT=512 +# CONFIG_GPIO_104_DIO_48E is not set +# CONFIG_GPIO_104_IDIO_16 is not set +# CONFIG_GPIO_104_IDI_48 is not set +# CONFIG_GPIO_74X164 is not set +# CONFIG_GPIO_74XX_MMIO is not set +# CONFIG_GPIO_ADNP is not set +# CONFIG_GPIO_ADP5588 is not set +# CONFIG_GPIO_AGGREGATOR is not set +# CONFIG_GPIO_ALTERA is not set +# CONFIG_GPIO_AMD8111 is not set +# CONFIG_GPIO_AMDPT is not set +# CONFIG_GPIO_AMD_FCH is not set +# CONFIG_GPIO_BCM_KONA is not set +# CONFIG_GPIO_BT8XX is not set +# CONFIG_GPIO_CADENCE is not set +# CONFIG_GPIO_CASCADE is not set +# CONFIG_GPIO_CDEV is not set +# CONFIG_GPIO_CDEV_V1 is not set +# CONFIG_GPIO_CS5535 is not set +# CONFIG_GPIO_DWAPB is not set +# CONFIG_GPIO_EM is not set +# CONFIG_GPIO_EXAR is not set +# CONFIG_GPIO_F7188X is not set +# CONFIG_GPIO_FTGPIO010 is not set +# CONFIG_GPIO_GENERIC_PLATFORM is not set +# CONFIG_GPIO_GPIO_MM is not set +# CONFIG_GPIO_GRGPIO is not set +# CONFIG_GPIO_GW_PLD is not set +# CONFIG_GPIO_HLWD is not set +# CONFIG_GPIO_ICH is not set +# CONFIG_GPIO_IT87 is not set +# CONFIG_GPIO_LOGICVC is not set +# CONFIG_GPIO_LYNXPOINT is not set +# CONFIG_GPIO_MAX3191X is not set +# CONFIG_GPIO_MAX7300 is not set +# CONFIG_GPIO_MAX7301 is not set +# CONFIG_GPIO_MAX732X is not set +# CONFIG_GPIO_MB86S7X is not set +# CONFIG_GPIO_MC33880 is not set +# CONFIG_GPIO_MCP23S08 is not set +# CONFIG_GPIO_ML_IOH is not set +# CONFIG_GPIO_MOCKUP is not set +# CONFIG_GPIO_MPC8XXX is not set +# CONFIG_GPIO_PCA953X is not set +# CONFIG_GPIO_PCA953X_IRQ is not set +# CONFIG_GPIO_PCA9570 is not set +# CONFIG_GPIO_PCF857X is not set +# CONFIG_GPIO_PCH is not set +# CONFIG_GPIO_PCIE_IDIO_24 is not set +# CONFIG_GPIO_PCI_IDIO_16 is not set +# CONFIG_GPIO_PISOSR is not set +# CONFIG_GPIO_PL061 is not set +# CONFIG_GPIO_PWM is not set +# CONFIG_GPIO_RCAR is not set +# CONFIG_GPIO_RDC321X is not set +# CONFIG_GPIO_SAMA5D2_PIOBU is not set +# CONFIG_GPIO_SCH is not set +# CONFIG_GPIO_SCH311X is not set +# CONFIG_GPIO_SIFIVE is not set +# CONFIG_GPIO_SX150X is not set +# CONFIG_GPIO_SYSCON is not set +CONFIG_GPIO_SYSFS=y +# CONFIG_GPIO_TPIC2810 is not set +# CONFIG_GPIO_TS4900 is not set +# CONFIG_GPIO_TS5500 is not set +# CONFIG_GPIO_VIRTIO is not set +# CONFIG_GPIO_VX855 is not set +# CONFIG_GPIO_WATCHDOG is not set +# CONFIG_GPIO_WINBOND is not set +# CONFIG_GPIO_WS16C48 is not set +# CONFIG_GPIO_XGENE is not set +# CONFIG_GPIO_XILINX is not set +# CONFIG_GPIO_XRA1403 is not set +# CONFIG_GPIO_ZEVIO is not set +# CONFIG_GPIO_ZX is not set +# CONFIG_GREENASIA_FF is not set +# CONFIG_GREYBUS is not set +# CONFIG_GS_FPGABOOT is not set +# CONFIG_GTP is not set +# CONFIG_GUP_BENCHMARK is not set +# CONFIG_GUP_TEST is not set +# CONFIG_GVE is not set +# CONFIG_HABANA_AI is not set +# CONFIG_HAMACHI is not set +# CONFIG_HAMRADIO is not set +# CONFIG_HAPPYMEAL is not set +CONFIG_HARDENED_USERCOPY=y +# CONFIG_HARDENED_USERCOPY_FALLBACK is not set +# CONFIG_HARDENED_USERCOPY_PAGESPAN is not set +CONFIG_HARDEN_BRANCH_HISTORY=y +CONFIG_HARDEN_EL2_VECTORS=y +# CONFIG_HARDLOCKUP_DETECTOR is not set +# CONFIG_HAVE_ARM_ARCH_TIMER is not set +# CONFIG_HCALL_STATS is not set +# CONFIG_HDC100X is not set +# CONFIG_HDC2010 is not set +# CONFIG_HDLC is not set +# CONFIG_HDLC_CISCO is not set +# CONFIG_HDLC_FR is not set +# CONFIG_HDLC_PPP is not set +# CONFIG_HDLC_RAW is not set +# CONFIG_HDLC_RAW_ETH is not set +# CONFIG_HDMI_LPE_AUDIO is not set +# CONFIG_HDQ_MASTER_OMAP is not set +# CONFIG_HEADERS_CHECK is not set +# CONFIG_HEADERS_INSTALL is not set +# CONFIG_HEADER_TEST is not set +# CONFIG_HERMES is not set +# CONFIG_HFSPLUS_FS is not set +# CONFIG_HFSPLUS_FS_POSIX_ACL is not set +# CONFIG_HFS_FS is not set +# CONFIG_HFS_FS_POSIX_ACL is not set +# CONFIG_HI6421V600_IRQ is not set +# CONFIG_HI8435 is not set +# CONFIG_HIBERNATION is not set +# CONFIG_HID is not set +# CONFIG_HIDRAW is not set +# CONFIG_HID_A4TECH is not set +# CONFIG_HID_ACCUTOUCH is not set +# CONFIG_HID_ACRUX is not set +# CONFIG_HID_ACRUX_FF is not set +# CONFIG_HID_ALPS is not set +# CONFIG_HID_APPLE is not set +# CONFIG_HID_APPLEIR is not set +# CONFIG_HID_ASUS is not set +# CONFIG_HID_AUREAL is not set +# CONFIG_HID_BATTERY_STRENGTH is not set +# CONFIG_HID_BELKIN is not set +# CONFIG_HID_BETOP_FF is not set +# CONFIG_HID_BIGBEN_FF is not set +# CONFIG_HID_CHERRY is not set +# CONFIG_HID_CHICONY is not set +# CONFIG_HID_CMEDIA is not set +# CONFIG_HID_CORSAIR is not set +# CONFIG_HID_COUGAR is not set +# CONFIG_HID_CP2112 is not set +# CONFIG_HID_CREATIVE_SB0540 is not set +# CONFIG_HID_CYPRESS is not set +# CONFIG_HID_DRAGONRISE is not set +# CONFIG_HID_ELAN is not set +# CONFIG_HID_ELECOM is not set +# CONFIG_HID_ELO is not set +# CONFIG_HID_EMS_FF is not set +# CONFIG_HID_EZKEY is not set +# CONFIG_HID_FT260 is not set +# CONFIG_HID_GEMBIRD is not set +# CONFIG_HID_GENERIC is not set +# CONFIG_HID_GFRM is not set +# CONFIG_HID_GLORIOUS is not set +# CONFIG_HID_GOOGLE_HAMMER is not set +# CONFIG_HID_GREENASIA is not set +# CONFIG_HID_GT683R is not set +# CONFIG_HID_GYRATION is not set +# CONFIG_HID_HOLTEK is not set +# CONFIG_HID_ICADE is not set +# CONFIG_HID_ITE is not set +# CONFIG_HID_JABRA is not set +# CONFIG_HID_KENSINGTON is not set +# CONFIG_HID_KEYTOUCH is not set +# CONFIG_HID_KYE is not set +# CONFIG_HID_LCPOWER is not set +# CONFIG_HID_LED is not set +# CONFIG_HID_LENOVO is not set +# CONFIG_HID_LOGITECH is not set +# CONFIG_HID_LOGITECH_DJ is not set +# CONFIG_HID_LOGITECH_HIDPP is not set +# CONFIG_HID_MACALLY is not set +# CONFIG_HID_MAGICMOUSE is not set +# CONFIG_HID_MALTRON is not set +# CONFIG_HID_MAYFLASH is not set +# CONFIG_HID_MCP2221 is not set +# CONFIG_HID_MICROSOFT is not set +# CONFIG_HID_MONTEREY is not set +# CONFIG_HID_MULTITOUCH is not set +# CONFIG_HID_NTI is not set +# CONFIG_HID_NTRIG is not set +# CONFIG_HID_ORTEK is not set +# CONFIG_HID_PANTHERLORD is not set +# CONFIG_HID_PENMOUNT is not set +# CONFIG_HID_PETALYNX is not set +# CONFIG_HID_PICOLCD is not set +# CONFIG_HID_PID is not set +# CONFIG_HID_PLANTRONICS is not set +# CONFIG_HID_PLAYSTATION is not set +# CONFIG_HID_PRIMAX is not set +# CONFIG_HID_PRODIKEYS is not set +# CONFIG_HID_REDRAGON is not set +# CONFIG_HID_RETRODE is not set +# CONFIG_HID_RMI is not set +# CONFIG_HID_ROCCAT is not set +# CONFIG_HID_SAITEK is not set +# CONFIG_HID_SAMSUNG is not set +# CONFIG_HID_SEMITEK is not set +# CONFIG_HID_SENSOR_HUB is not set +# CONFIG_HID_SMARTJOYPLUS is not set +# CONFIG_HID_SONY is not set +# CONFIG_HID_SPEEDLINK is not set +# CONFIG_HID_STEAM is not set +# CONFIG_HID_STEELSERIES is not set +# CONFIG_HID_SUNPLUS is not set +# CONFIG_HID_THINGM is not set +# CONFIG_HID_THRUSTMASTER is not set +# CONFIG_HID_TIVO is not set +# CONFIG_HID_TOPSEED is not set +# CONFIG_HID_TWINHAN is not set +# CONFIG_HID_U2FZERO is not set +# CONFIG_HID_UCLOGIC is not set +# CONFIG_HID_UDRAW_PS3 is not set +# CONFIG_HID_VIEWSONIC is not set +# CONFIG_HID_VIVALDI is not set +# CONFIG_HID_WACOM is not set +# CONFIG_HID_WALTOP is not set +# CONFIG_HID_WIIMOTE is not set +# CONFIG_HID_XINMO is not set +# CONFIG_HID_ZEROPLUS is not set +# CONFIG_HID_ZYDACRON is not set +# CONFIG_HIGHMEM is not set +CONFIG_HIGH_RES_TIMERS=y +# CONFIG_HINIC is not set +# CONFIG_HIP04_ETH is not set +# CONFIG_HIPPI is not set +# CONFIG_HISILICON_ERRATUM_161010101 is not set +# CONFIG_HISILICON_ERRATUM_161600802 is not set +# CONFIG_HISI_DMA is not set +# CONFIG_HISI_FEMAC is not set +# CONFIG_HISI_HIKEY_USB is not set +# CONFIG_HIX5HD2_GMAC is not set +# CONFIG_HMC425 is not set +# CONFIG_HMC6352 is not set +# CONFIG_HNS is not set +# CONFIG_HNS3 is not set +# CONFIG_HNS_DSAF is not set +# CONFIG_HNS_ENET is not set +# CONFIG_HOSTAP is not set +# CONFIG_HOSTAP_CS is not set +# CONFIG_HOSTAP_PCI is not set +# CONFIG_HOSTAP_PLX is not set +# CONFIG_HOTPLUG_CPU is not set +# CONFIG_HOTPLUG_PCI is not set +# CONFIG_HOTPLUG_PCI_ACPI_IBM is not set +# CONFIG_HOTPLUG_PCI_COMPAQ is not set +# CONFIG_HOTPLUG_PCI_CPCI is not set +# CONFIG_HOTPLUG_PCI_IBM is not set +# CONFIG_HOTPLUG_PCI_SHPC is not set +# CONFIG_HP03 is not set +# CONFIG_HP100 is not set +# CONFIG_HP206C is not set +CONFIG_HPET_MMAP_DEFAULT=y +# CONFIG_HPFS_FS is not set +# CONFIG_HP_ILO is not set +# CONFIG_HP_WIRELESS is not set +# CONFIG_HSA_AMD is not set +# CONFIG_HSI is not set +# CONFIG_HSR is not set +# CONFIG_HTC_EGPIO is not set +# CONFIG_HTC_I2CPLD is not set +# CONFIG_HTC_PASIC3 is not set +# CONFIG_HTS221 is not set +# CONFIG_HTU21 is not set +# CONFIG_HUGETLBFS is not set +# CONFIG_HUGETLB_PAGE is not set +# CONFIG_HVC_DCC is not set +# CONFIG_HVC_UDBG is not set +# CONFIG_HWLAT_TRACER is not set +# CONFIG_HWMON is not set +# CONFIG_HWMON_DEBUG_CHIP is not set +# CONFIG_HWMON_VID is not set +# CONFIG_HWSPINLOCK is not set +# CONFIG_HWSPINLOCK_OMAP is not set +CONFIG_HW_PERF_EVENTS=y +# CONFIG_HW_RANDOM is not set +# CONFIG_HW_RANDOM_AMD is not set +# CONFIG_HW_RANDOM_ARM_SMCCC_TRNG is not set +# CONFIG_HW_RANDOM_ATMEL is not set +# CONFIG_HW_RANDOM_BA431 is not set +# CONFIG_HW_RANDOM_CAVIUM is not set +# CONFIG_HW_RANDOM_CCTRNG is not set +# CONFIG_HW_RANDOM_EXYNOS is not set +# CONFIG_HW_RANDOM_GEODE is not set +# CONFIG_HW_RANDOM_INTEL is not set +# CONFIG_HW_RANDOM_IPROC_RNG200 is not set +# CONFIG_HW_RANDOM_MTK is not set +# CONFIG_HW_RANDOM_OMAP is not set +# CONFIG_HW_RANDOM_OMAP3_ROM is not set +# CONFIG_HW_RANDOM_PPC4XX is not set +# CONFIG_HW_RANDOM_TIMERIOMEM is not set +CONFIG_HW_RANDOM_TPM=y +# CONFIG_HW_RANDOM_VIA is not set +# CONFIG_HW_RANDOM_VIRTIO is not set +# CONFIG_HW_RANDOM_XIPHERA is not set +# CONFIG_HX711 is not set +# CONFIG_HYPERV is not set +# CONFIG_HYPERV_TSCPAGE is not set +# CONFIG_HYSDN is not set +CONFIG_HZ=100 +CONFIG_HZ_100=y +# CONFIG_HZ_1000 is not set +# CONFIG_HZ_1024 is not set +# CONFIG_HZ_128 is not set +# CONFIG_HZ_200 is not set +# CONFIG_HZ_24 is not set +# CONFIG_HZ_250 is not set +# CONFIG_HZ_256 is not set +# CONFIG_HZ_300 is not set +# CONFIG_HZ_48 is not set +# CONFIG_HZ_500 is not set +# CONFIG_HZ_PERIODIC is not set +# CONFIG_I2C is not set +# CONFIG_I2C_ALGOBIT is not set +# CONFIG_I2C_ALGOPCA is not set +# CONFIG_I2C_ALGOPCF is not set +# CONFIG_I2C_ALI1535 is not set +# CONFIG_I2C_ALI1563 is not set +# CONFIG_I2C_ALI15X3 is not set +# CONFIG_I2C_AMD756 is not set +# CONFIG_I2C_AMD8111 is not set +# CONFIG_I2C_ARB_GPIO_CHALLENGE is not set +# CONFIG_I2C_AU1550 is not set +# CONFIG_I2C_BCM2835 is not set +# CONFIG_I2C_BCM_IPROC is not set +# CONFIG_I2C_CADENCE is not set +# CONFIG_I2C_CBUS_GPIO is not set +# CONFIG_I2C_CHARDEV is not set +# CONFIG_I2C_COMPAT is not set +# CONFIG_I2C_CP2615 is not set +# CONFIG_I2C_DEBUG_ALGO is not set +# CONFIG_I2C_DEBUG_BUS is not set +# CONFIG_I2C_DEBUG_CORE is not set +# CONFIG_I2C_DEMUX_PINCTRL is not set +# CONFIG_I2C_DESIGNWARE_PCI is not set +# CONFIG_I2C_DESIGNWARE_PLATFORM is not set +# CONFIG_I2C_DESIGNWARE_SLAVE is not set +# CONFIG_I2C_DIOLAN_U2C is not set +# CONFIG_I2C_EG20T is not set +# CONFIG_I2C_ELEKTOR is not set +# CONFIG_I2C_EMEV2 is not set +# CONFIG_I2C_GPIO is not set +# CONFIG_I2C_GPIO_FAULT_INJECTOR is not set +# CONFIG_I2C_HELPER_AUTO is not set +# CONFIG_I2C_HID is not set +# CONFIG_I2C_HID_OF is not set +# CONFIG_I2C_HID_OF_GOODIX is not set +# CONFIG_I2C_I801 is not set +# CONFIG_I2C_IBM_IIC is not set +# CONFIG_I2C_IMG is not set +# CONFIG_I2C_ISCH is not set +# CONFIG_I2C_ISMT is not set +# CONFIG_I2C_JZ4780 is not set +# CONFIG_I2C_MLXCPLD is not set +# CONFIG_I2C_MPC is not set +# CONFIG_I2C_MT65XX is not set +# CONFIG_I2C_MUX is not set +# CONFIG_I2C_MUX_GPIO is not set +# CONFIG_I2C_MUX_GPMUX is not set +# CONFIG_I2C_MUX_LTC4306 is not set +# CONFIG_I2C_MUX_MLXCPLD is not set +# CONFIG_I2C_MUX_PCA9541 is not set +# CONFIG_I2C_MUX_PCA954x is not set +# CONFIG_I2C_MUX_PINCTRL is not set +# CONFIG_I2C_MUX_REG is not set +# CONFIG_I2C_MV64XXX is not set +# CONFIG_I2C_NFORCE2 is not set +# CONFIG_I2C_NOMADIK is not set +# CONFIG_I2C_NVIDIA_GPU is not set +# CONFIG_I2C_OCORES is not set +# CONFIG_I2C_OCTEON is not set +# CONFIG_I2C_PARPORT is not set +# CONFIG_I2C_PARPORT_LIGHT is not set +# CONFIG_I2C_PCA_ISA is not set +# CONFIG_I2C_PCA_PLATFORM is not set +# CONFIG_I2C_PIIX4 is not set +# CONFIG_I2C_PXA_PCI is not set +# CONFIG_I2C_PXA_SLAVE is not set +# CONFIG_I2C_RCAR is not set +# CONFIG_I2C_RK3X is not set +# CONFIG_I2C_ROBOTFUZZ_OSIF is not set +# CONFIG_I2C_S3C2410 is not set +# CONFIG_I2C_SCMI is not set +# CONFIG_I2C_SH_MOBILE is not set +# CONFIG_I2C_SIMTEC is not set +# CONFIG_I2C_SIS5595 is not set +# CONFIG_I2C_SIS630 is not set +# CONFIG_I2C_SIS96X is not set +# CONFIG_I2C_SLAVE is not set +# CONFIG_I2C_SLAVE_EEPROM is not set +# CONFIG_I2C_SMBUS is not set +# CONFIG_I2C_STUB is not set +# CONFIG_I2C_TAOS_EVM is not set +# CONFIG_I2C_THUNDERX is not set +# CONFIG_I2C_TINY_USB is not set +# CONFIG_I2C_VERSATILE is not set +# CONFIG_I2C_VIA is not set +# CONFIG_I2C_VIAPRO is not set +# CONFIG_I2C_VIRTIO is not set +# CONFIG_I2C_XILINX is not set +# CONFIG_I3C is not set +# CONFIG_I40E is not set +# CONFIG_I40EVF is not set +# CONFIG_I6300ESB_WDT is not set +# CONFIG_I82092 is not set +# CONFIG_I82365 is not set +# CONFIG_IAQCORE is not set +# CONFIG_IBM_ASM is not set +# CONFIG_IBM_EMAC_DEBUG is not set +# CONFIG_IBM_EMAC_EMAC4 is not set +# CONFIG_IBM_EMAC_MAL_CLR_ICINTSTAT is not set +# CONFIG_IBM_EMAC_MAL_COMMON_ERR is not set +# CONFIG_IBM_EMAC_NO_FLOW_CTRL is not set +# CONFIG_IBM_EMAC_RGMII is not set +# CONFIG_IBM_EMAC_TAH is not set +# CONFIG_IBM_EMAC_ZMII is not set +# CONFIG_ICE is not set +# CONFIG_ICP10100 is not set +# CONFIG_ICPLUS_PHY is not set +# CONFIG_ICS932S401 is not set +# CONFIG_ICST is not set +# CONFIG_IDE is not set +# CONFIG_IDEAPAD_LAPTOP is not set +# CONFIG_IDE_GD is not set +# CONFIG_IDE_PROC_FS is not set +# CONFIG_IDE_TASK_IOCTL is not set +# CONFIG_IDLE_PAGE_TRACKING is not set +# CONFIG_IEEE802154 is not set +# CONFIG_IEEE802154_ADF7242 is not set +# CONFIG_IEEE802154_ATUSB is not set +# CONFIG_IEEE802154_CA8210 is not set +# CONFIG_IEEE802154_HWSIM is not set +# CONFIG_IEEE802154_MCR20A is not set +# CONFIG_IFB is not set +# CONFIG_IGB is not set +# CONFIG_IGBVF is not set +# CONFIG_IGC is not set +# CONFIG_IIO is not set +# CONFIG_IIO_BUFFER is not set +# CONFIG_IIO_BUFFER_CB is not set +# CONFIG_IIO_BUFFER_DMA is not set +# CONFIG_IIO_BUFFER_DMAENGINE is not set +# CONFIG_IIO_BUFFER_HDC2010 is not set +# CONFIG_IIO_BUFFER_HW_CONSUMER is not set +# CONFIG_IIO_CONFIGFS is not set +CONFIG_IIO_CONSUMERS_PER_TRIGGER=2 +# CONFIG_IIO_CROS_EC_ACCEL_LEGACY is not set +# CONFIG_IIO_INTERRUPT_TRIGGER is not set +# CONFIG_IIO_MUX is not set +# CONFIG_IIO_PERIODIC_RTC_TRIGGER is not set +# CONFIG_IIO_RESCALE is not set +# CONFIG_IIO_SIMPLE_DUMMY is not set +# CONFIG_IIO_SSP_SENSORHUB is not set +# CONFIG_IIO_ST_ACCEL_3AXIS is not set +# CONFIG_IIO_ST_GYRO_3AXIS is not set +# CONFIG_IIO_ST_LSM6DSX is not set +# CONFIG_IIO_ST_LSM9DS0 is not set +# CONFIG_IIO_ST_MAGN_3AXIS is not set +# CONFIG_IIO_ST_PRESS is not set +# CONFIG_IIO_SW_DEVICE is not set +# CONFIG_IIO_SW_TRIGGER is not set +# CONFIG_IIO_SYSFS_TRIGGER is not set +# CONFIG_IIO_TRIGGER is not set +# CONFIG_IIO_TRIGGERED_EVENT is not set +# CONFIG_IKCONFIG is not set +# CONFIG_IKCONFIG_PROC is not set +# CONFIG_IKHEADERS is not set +# CONFIG_IMA is not set +# CONFIG_IMGPDC_WDT is not set +# CONFIG_IMG_MDC_DMA is not set +# CONFIG_IMX7D_ADC is not set +# CONFIG_IMX_IPUV3_CORE is not set +# CONFIG_IMX_THERMAL is not set +# CONFIG_INA2XX_ADC is not set +# CONFIG_INDIRECT_PIO is not set +CONFIG_INET=y +# CONFIG_INET6_AH is not set +# CONFIG_INET6_ESP is not set +# CONFIG_INET6_ESPINTCP is not set +# CONFIG_INET6_IPCOMP is not set +# CONFIG_INET6_TUNNEL is not set +# CONFIG_INET6_XFRM_MODE_BEET is not set +# CONFIG_INET6_XFRM_MODE_ROUTEOPTIMIZATION is not set +# CONFIG_INET6_XFRM_MODE_TRANSPORT is not set +# CONFIG_INET6_XFRM_MODE_TUNNEL is not set +# CONFIG_INET6_XFRM_TUNNEL is not set +# CONFIG_INET_AH is not set +# CONFIG_INET_DIAG is not set +# CONFIG_INET_ESP is not set +# CONFIG_INET_ESPINTCP is not set +# CONFIG_INET_IPCOMP is not set +# CONFIG_INET_LRO is not set +# CONFIG_INET_TCP_DIAG is not set +# CONFIG_INET_TUNNEL is not set +# CONFIG_INET_UDP_DIAG is not set +# CONFIG_INET_XFRM_MODE_BEET is not set +# CONFIG_INET_XFRM_MODE_TRANSPORT is not set +# CONFIG_INET_XFRM_MODE_TUNNEL is not set +# CONFIG_INET_XFRM_TUNNEL is not set +# CONFIG_INFINIBAND is not set +# CONFIG_INFTL is not set +# CONFIG_INGENIC_ADC is not set +# CONFIG_INGENIC_CGU_JZ4725B is not set +# CONFIG_INGENIC_CGU_JZ4740 is not set +# CONFIG_INGENIC_CGU_JZ4760 is not set +# CONFIG_INGENIC_CGU_JZ4770 is not set +# CONFIG_INGENIC_CGU_JZ4780 is not set +# CONFIG_INGENIC_CGU_X1000 is not set +# CONFIG_INGENIC_CGU_X1830 is not set +# CONFIG_INGENIC_OST is not set +# CONFIG_INGENIC_SYSOST is not set +# CONFIG_INGENIC_TCU_CLK is not set +# CONFIG_INGENIC_TCU_IRQ is not set +# CONFIG_INGENIC_TIMER is not set +CONFIG_INIT_ENV_ARG_LIMIT=32 +# CONFIG_INIT_ON_ALLOC_DEFAULT_ON is not set +# CONFIG_INIT_ON_FREE_DEFAULT_ON is not set +# CONFIG_INIT_STACK_ALL_PATTERN is not set +# CONFIG_INIT_STACK_ALL_ZERO is not set +CONFIG_INIT_STACK_NONE=y +CONFIG_INOTIFY_USER=y +# CONFIG_INPUT is not set +# CONFIG_INPUT_AD714X is not set +# CONFIG_INPUT_ADXL34X is not set +# CONFIG_INPUT_APANEL is not set +# CONFIG_INPUT_ATI_REMOTE2 is not set +# CONFIG_INPUT_ATLAS_BTNS is not set +# CONFIG_INPUT_ATMEL_CAPTOUCH is not set +# CONFIG_INPUT_AXP20X_PEK is not set +# CONFIG_INPUT_BMA150 is not set +# CONFIG_INPUT_CM109 is not set +# CONFIG_INPUT_CMA3000 is not set +# CONFIG_INPUT_DA7280_HAPTICS is not set +# CONFIG_INPUT_DRV260X_HAPTICS is not set +# CONFIG_INPUT_DRV2665_HAPTICS is not set +# CONFIG_INPUT_DRV2667_HAPTICS is not set +# CONFIG_INPUT_E3X0_BUTTON is not set +# CONFIG_INPUT_EVBUG is not set +# CONFIG_INPUT_EVDEV is not set +# CONFIG_INPUT_FF_MEMLESS is not set +# CONFIG_INPUT_GP2A is not set +# CONFIG_INPUT_GPIO_BEEPER is not set +# CONFIG_INPUT_GPIO_DECODER is not set +# CONFIG_INPUT_GPIO_ROTARY_ENCODER is not set +# CONFIG_INPUT_GPIO_TILT_POLLED is not set +# CONFIG_INPUT_GPIO_VIBRA is not set +# CONFIG_INPUT_IDEAPAD_SLIDEBAR is not set +# CONFIG_INPUT_IMS_PCU is not set +# CONFIG_INPUT_IQS269A is not set +# CONFIG_INPUT_IQS626A is not set +# CONFIG_INPUT_JOYDEV is not set +# CONFIG_INPUT_JOYSTICK is not set +# CONFIG_INPUT_KEYBOARD is not set +# CONFIG_INPUT_KEYSPAN_REMOTE is not set +# CONFIG_INPUT_KXTJ9 is not set +# CONFIG_INPUT_LEDS is not set +# CONFIG_INPUT_MATRIXKMAP is not set +# CONFIG_INPUT_MAX8997_HAPTIC is not set +CONFIG_INPUT_MISC=y +# CONFIG_INPUT_MMA8450 is not set +# CONFIG_INPUT_MOUSE is not set +# CONFIG_INPUT_MOUSEDEV is not set +# CONFIG_INPUT_MPU3050 is not set +# CONFIG_INPUT_MSM_VIBRATOR is not set +# CONFIG_INPUT_PALMAS_PWRBUTTON is not set +# CONFIG_INPUT_PCF8574 is not set +# CONFIG_INPUT_PCSPKR is not set +# CONFIG_INPUT_POLLDEV is not set +# CONFIG_INPUT_POWERMATE is not set +# CONFIG_INPUT_PWM_BEEPER is not set +# CONFIG_INPUT_PWM_VIBRA is not set +# CONFIG_INPUT_REGULATOR_HAPTIC is not set +# CONFIG_INPUT_SOC_BUTTON_ARRAY is not set +# CONFIG_INPUT_SPARSEKMAP is not set +# CONFIG_INPUT_TABLET is not set +# CONFIG_INPUT_TOUCHSCREEN is not set +# CONFIG_INPUT_TPS65218_PWRBUTTON is not set +# CONFIG_INPUT_TWL4030_PWRBUTTON is not set +# CONFIG_INPUT_TWL4030_VIBRA is not set +# CONFIG_INPUT_TWL6040_VIBRA is not set +# CONFIG_INPUT_UINPUT is not set +# CONFIG_INPUT_WISTRON_BTNS is not set +# CONFIG_INPUT_YEALINK is not set +# CONFIG_INT340X_THERMAL is not set +# CONFIG_INTEGRITY is not set +# CONFIG_INTEGRITY_AUDIT is not set +# CONFIG_INTEGRITY_SIGNATURE is not set +# CONFIG_INTEL_ATOMISP2_LED is not set +# CONFIG_INTEL_ATOMISP2_PM is not set +# CONFIG_INTEL_CHT_INT33FE is not set +# CONFIG_INTEL_HID_EVENT is not set +# CONFIG_INTEL_IDLE is not set +# CONFIG_INTEL_IDMA64 is not set +# CONFIG_INTEL_IDXD is not set +# CONFIG_INTEL_IDXD_COMPAT is not set +# CONFIG_INTEL_INT0002_VGPIO is not set +# CONFIG_INTEL_IOATDMA is not set +# CONFIG_INTEL_ISH_HID is not set +# CONFIG_INTEL_MEI is not set +# CONFIG_INTEL_MEI_ME is not set +# CONFIG_INTEL_MEI_TXE is not set +# CONFIG_INTEL_MIC_CARD is not set +# CONFIG_INTEL_MIC_HOST is not set +# CONFIG_INTEL_MID_PTI is not set +# CONFIG_INTEL_OAKTRAIL is not set +# CONFIG_INTEL_PMC_CORE is not set +# CONFIG_INTEL_PUNIT_IPC is not set +# CONFIG_INTEL_RST is not set +# CONFIG_INTEL_SMARTCONNECT is not set +# CONFIG_INTEL_SOC_PMIC is not set +# CONFIG_INTEL_SOC_PMIC_CHTDC_TI is not set +# CONFIG_INTEL_SOC_PMIC_CHTWC is not set +# CONFIG_INTEL_TH is not set +# CONFIG_INTEL_VBTN is not set +# CONFIG_INTEL_XWAY_PHY is not set +# CONFIG_INTERCONNECT is not set +# CONFIG_INTERVAL_TREE_TEST is not set +# CONFIG_INV_ICM42600_I2C is not set +# CONFIG_INV_ICM42600_SPI is not set +# CONFIG_INV_MPU6050_I2C is not set +# CONFIG_INV_MPU6050_IIO is not set +# CONFIG_INV_MPU6050_SPI is not set +# CONFIG_IOMMU_DEFAULT_DMA_LAZY is not set +# CONFIG_IOMMU_DEFAULT_DMA_STRICT is not set +# CONFIG_IOMMU_DEFAULT_PASSTHROUGH is not set +# CONFIG_IOMMU_SUPPORT is not set +# CONFIG_IONIC is not set +# CONFIG_IOSCHED_BFQ is not set +# CONFIG_IOSM is not set +CONFIG_IO_STRICT_DEVMEM=y +# CONFIG_IO_URING is not set +CONFIG_IO_WQ=y +# CONFIG_IP17XX_PHY is not set +# CONFIG_IP6_NF_FILTER is not set +# CONFIG_IP6_NF_IPTABLES is not set +# CONFIG_IP6_NF_MANGLE is not set +# CONFIG_IP6_NF_MATCH_AH is not set +# CONFIG_IP6_NF_MATCH_EUI64 is not set +# CONFIG_IP6_NF_MATCH_FRAG is not set +# CONFIG_IP6_NF_MATCH_HL is not set +# CONFIG_IP6_NF_MATCH_IPV6HEADER is not set +# CONFIG_IP6_NF_MATCH_MH is not set +# CONFIG_IP6_NF_MATCH_OPTS is not set +# CONFIG_IP6_NF_MATCH_RPFILTER is not set +# CONFIG_IP6_NF_MATCH_RT is not set +# CONFIG_IP6_NF_MATCH_SRH is not set +# CONFIG_IP6_NF_NAT is not set +# CONFIG_IP6_NF_RAW is not set +# CONFIG_IP6_NF_SECURITY is not set +# CONFIG_IP6_NF_TARGET_HL is not set +# CONFIG_IP6_NF_TARGET_MASQUERADE is not set +# CONFIG_IP6_NF_TARGET_REJECT is not set +# CONFIG_IP6_NF_TARGET_SYNPROXY is not set +# CONFIG_IPACK_BUS is not set +# CONFIG_IPC_NS is not set +# CONFIG_IPMB_DEVICE_INTERFACE is not set +# CONFIG_IPMI_HANDLER is not set +# CONFIG_IPV6 is not set +# CONFIG_IPV6_FOU is not set +# CONFIG_IPV6_FOU_TUNNEL is not set +# CONFIG_IPV6_ILA is not set +# CONFIG_IPV6_IOAM6_LWTUNNEL is not set +# CONFIG_IPV6_MIP6 is not set +# CONFIG_IPV6_MROUTE is not set +# CONFIG_IPV6_MROUTE_MULTIPLE_TABLES is not set +# CONFIG_IPV6_MULTIPLE_TABLES is not set +CONFIG_IPV6_NDISC_NODETYPE=y +# CONFIG_IPV6_OPTIMISTIC_DAD is not set +# CONFIG_IPV6_ROUTER_PREF is not set +# CONFIG_IPV6_ROUTE_INFO is not set +# CONFIG_IPV6_RPL_LWTUNNEL is not set +# CONFIG_IPV6_SEG6_HMAC is not set +# CONFIG_IPV6_SEG6_LWTUNNEL is not set +# CONFIG_IPV6_SIT is not set +# CONFIG_IPV6_SIT_6RD is not set +# CONFIG_IPV6_TUNNEL is not set +# CONFIG_IPV6_VTI is not set +# CONFIG_IPVLAN is not set +# CONFIG_IPVTAP is not set +# CONFIG_IPW2100 is not set +# CONFIG_IPW2100_DEBUG is not set +CONFIG_IPW2100_MONITOR=y +# CONFIG_IPW2200 is not set +# CONFIG_IPW2200_DEBUG is not set +CONFIG_IPW2200_MONITOR=y +# CONFIG_IPW2200_PROMISCUOUS is not set +# CONFIG_IPW2200_QOS is not set +# CONFIG_IPW2200_RADIOTAP is not set +# CONFIG_IPWIRELESS is not set +# CONFIG_IPX is not set +CONFIG_IP_ADVANCED_ROUTER=y +# CONFIG_IP_DCCP is not set +# CONFIG_IP_FIB_TRIE_STATS is not set +# CONFIG_IP_MROUTE is not set +CONFIG_IP_MROUTE_MULTIPLE_TABLES=y +CONFIG_IP_MULTICAST=y +CONFIG_IP_MULTIPLE_TABLES=y +# CONFIG_IP_NF_ARPFILTER is not set +# CONFIG_IP_NF_ARPTABLES is not set +# CONFIG_IP_NF_ARP_MANGLE is not set +# CONFIG_IP_NF_FILTER is not set +# CONFIG_IP_NF_IPTABLES is not set +# CONFIG_IP_NF_MANGLE is not set +# CONFIG_IP_NF_MATCH_AH is not set +# CONFIG_IP_NF_MATCH_ECN is not set +# CONFIG_IP_NF_MATCH_RPFILTER is not set +# CONFIG_IP_NF_MATCH_TTL is not set +# CONFIG_IP_NF_RAW is not set +# CONFIG_IP_NF_SECURITY is not set +# CONFIG_IP_NF_TARGET_CLUSTERIP is not set +# CONFIG_IP_NF_TARGET_ECN is not set +# CONFIG_IP_NF_TARGET_MASQUERADE is not set +# CONFIG_IP_NF_TARGET_NETMAP is not set +# CONFIG_IP_NF_TARGET_REDIRECT is not set +# CONFIG_IP_NF_TARGET_REJECT is not set +# CONFIG_IP_NF_TARGET_SYNPROXY is not set +# CONFIG_IP_NF_TARGET_TTL is not set +# CONFIG_IP_PIMSM_V1 is not set +# CONFIG_IP_PIMSM_V2 is not set +# CONFIG_IP_PNP is not set +CONFIG_IP_ROUTE_MULTIPATH=y +CONFIG_IP_ROUTE_VERBOSE=y +# CONFIG_IP_SCTP is not set +# CONFIG_IP_SET is not set +# CONFIG_IP_SET_HASH_IPMAC is not set +# CONFIG_IP_VS is not set +# CONFIG_IP_VS_MH is not set +CONFIG_IP_VS_MH_TAB_INDEX=10 +# CONFIG_IP_VS_TWOS is not set +# CONFIG_IRDA is not set +# CONFIG_IRQSOFF_TRACER is not set +# CONFIG_IRQ_ALL_CPUS is not set +# CONFIG_IRQ_DOMAIN_DEBUG is not set +# CONFIG_IRQ_POLL is not set +# CONFIG_IRQ_TIME_ACCOUNTING is not set +# CONFIG_IR_GPIO_CIR is not set +# CONFIG_IR_HIX5HD2 is not set +# CONFIG_IR_IGORPLUGUSB is not set +# CONFIG_IR_IGUANA is not set +# CONFIG_IR_IMG is not set +# CONFIG_IR_IMON is not set +# CONFIG_IR_IMON_RAW is not set +# CONFIG_IR_JVC_DECODER is not set +# CONFIG_IR_LIRC_CODEC is not set +# CONFIG_IR_MCEUSB is not set +# CONFIG_IR_NEC_DECODER is not set +# CONFIG_IR_RC5_DECODER is not set +# CONFIG_IR_RC6_DECODER is not set +# CONFIG_IR_REDRAT3 is not set +# CONFIG_IR_SONY_DECODER is not set +# CONFIG_IR_STREAMZAP is not set +# CONFIG_IR_TTUSBIR is not set +# CONFIG_ISA_BUS is not set +# CONFIG_ISA_BUS_API is not set +# CONFIG_ISCSI_BOOT_SYSFS is not set +# CONFIG_ISCSI_TCP is not set +CONFIG_ISDN=y +# CONFIG_ISDN_AUDIO is not set +# CONFIG_ISDN_CAPI is not set +# CONFIG_ISDN_CAPI_CAPIDRV is not set +# CONFIG_ISDN_DIVERSION is not set +# CONFIG_ISDN_DRV_ACT2000 is not set +# CONFIG_ISDN_DRV_GIGASET is not set +# CONFIG_ISDN_DRV_HISAX is not set +# CONFIG_ISDN_DRV_ICN is not set +# CONFIG_ISDN_DRV_LOOP is not set +# CONFIG_ISDN_DRV_PCBIT is not set +# CONFIG_ISDN_DRV_SC is not set +# CONFIG_ISDN_I4L is not set +# CONFIG_ISL29003 is not set +# CONFIG_ISL29020 is not set +# CONFIG_ISL29125 is not set +# CONFIG_ISL29501 is not set +# CONFIG_ISO9660_FS is not set +# CONFIG_ISS4xx is not set +# CONFIG_ITG3200 is not set +# CONFIG_IWL3945 is not set +# CONFIG_IWLWIFI is not set +# CONFIG_IXGB is not set +# CONFIG_IXGBE is not set +# CONFIG_IXGBEVF is not set +# CONFIG_JAILHOUSE_GUEST is not set +# CONFIG_JBD2_DEBUG is not set +# CONFIG_JFFS2_CMODE_FAVOURLZO is not set +# CONFIG_JFFS2_CMODE_NONE is not set +CONFIG_JFFS2_CMODE_PRIORITY=y +# CONFIG_JFFS2_CMODE_SIZE is not set +CONFIG_JFFS2_COMPRESSION_OPTIONS=y +CONFIG_JFFS2_FS=y +CONFIG_JFFS2_FS_DEBUG=0 +# CONFIG_JFFS2_FS_POSIX_ACL is not set +# CONFIG_JFFS2_FS_SECURITY is not set +# CONFIG_JFFS2_FS_WBUF_VERIFY is not set +CONFIG_JFFS2_FS_WRITEBUFFER=y +CONFIG_JFFS2_FS_XATTR=y +CONFIG_JFFS2_LZMA=y +# CONFIG_JFFS2_LZO is not set +CONFIG_JFFS2_RTIME=y +# CONFIG_JFFS2_RUBIN is not set +CONFIG_JFFS2_SUMMARY=y +# CONFIG_JFFS2_ZLIB is not set +# CONFIG_JFS_DEBUG is not set +# CONFIG_JFS_FS is not set +# CONFIG_JFS_POSIX_ACL is not set +# CONFIG_JFS_SECURITY is not set +# CONFIG_JFS_STATISTICS is not set +# CONFIG_JME is not set +CONFIG_JOLIET=y +# CONFIG_JSA1212 is not set +# CONFIG_JUMP_LABEL is not set +# CONFIG_JZ4740_WDT is not set +# CONFIG_JZ4770_PHY is not set +# CONFIG_KALLSYMS is not set +# CONFIG_KALLSYMS_ABSOLUTE_PERCPU is not set +# CONFIG_KALLSYMS_ALL is not set +CONFIG_KALLSYMS_BASE_RELATIVE=y +# CONFIG_KALLSYMS_UNCOMPRESSED is not set +# CONFIG_KARMA_PARTITION is not set +# CONFIG_KASAN is not set +CONFIG_KASAN_STACK=y +# CONFIG_KCMP is not set +# CONFIG_KCOV is not set +# CONFIG_KCSAN is not set +# CONFIG_KERNEL_BZIP2 is not set +# CONFIG_KERNEL_CAT is not set +# CONFIG_KERNEL_GZIP is not set +# CONFIG_KERNEL_LZ4 is not set +# CONFIG_KERNEL_LZMA is not set +# CONFIG_KERNEL_LZO is not set +CONFIG_KERNEL_MODE_NEON=y +CONFIG_KERNEL_XZ=y +# CONFIG_KERNEL_ZSTD is not set +CONFIG_KERNFS=y +# CONFIG_KEXEC is not set +# CONFIG_KEXEC_FILE is not set +# CONFIG_KEXEC_SIG is not set +# CONFIG_KEYBOARD_ADC is not set +# CONFIG_KEYBOARD_ADP5588 is not set +# CONFIG_KEYBOARD_ADP5589 is not set +# CONFIG_KEYBOARD_APPLESPI is not set +# CONFIG_KEYBOARD_ATKBD is not set +# CONFIG_KEYBOARD_BCM is not set +# CONFIG_KEYBOARD_CAP11XX is not set +# CONFIG_KEYBOARD_DLINK_DIR685 is not set +# CONFIG_KEYBOARD_GPIO is not set +# CONFIG_KEYBOARD_GPIO_POLLED is not set +# CONFIG_KEYBOARD_LKKBD is not set +# CONFIG_KEYBOARD_LM8323 is not set +# CONFIG_KEYBOARD_LM8333 is not set +# CONFIG_KEYBOARD_MATRIX is not set +# CONFIG_KEYBOARD_MAX7359 is not set +# CONFIG_KEYBOARD_MCS is not set +# CONFIG_KEYBOARD_MPR121 is not set +# CONFIG_KEYBOARD_NEWTON is not set +# CONFIG_KEYBOARD_OMAP4 is not set +# CONFIG_KEYBOARD_OPENCORES is not set +# CONFIG_KEYBOARD_PXA27x is not set +# CONFIG_KEYBOARD_QT1050 is not set +# CONFIG_KEYBOARD_QT1070 is not set +# CONFIG_KEYBOARD_QT2160 is not set +# CONFIG_KEYBOARD_SAMSUNG is not set +# CONFIG_KEYBOARD_SH_KEYSC is not set +# CONFIG_KEYBOARD_SNVS_PWRKEY is not set +# CONFIG_KEYBOARD_STMPE is not set +# CONFIG_KEYBOARD_STOWAWAY is not set +# CONFIG_KEYBOARD_SUNKBD is not set +# CONFIG_KEYBOARD_TCA6416 is not set +# CONFIG_KEYBOARD_TCA8418 is not set +# CONFIG_KEYBOARD_TEGRA is not set +# CONFIG_KEYBOARD_TM2_TOUCHKEY is not set +# CONFIG_KEYBOARD_TWL4030 is not set +# CONFIG_KEYBOARD_XTKBD is not set +# CONFIG_KEYS is not set +# CONFIG_KEYS_REQUEST_CACHE is not set +# CONFIG_KEY_DH_OPERATIONS is not set +# CONFIG_KFENCE is not set +# CONFIG_KGDB is not set +# CONFIG_KMEMCHECK is not set +# CONFIG_KMX61 is not set +# CONFIG_KPC2000 is not set +# CONFIG_KPROBES is not set +# CONFIG_KPROBES_SANITY_TEST is not set +# CONFIG_KPROBE_EVENTS_ON_NOTRACE is not set +# CONFIG_KPROBE_EVENT_GEN_TEST is not set +# CONFIG_KS7010 is not set +# CONFIG_KS8842 is not set +# CONFIG_KS8851 is not set +# CONFIG_KS8851_MLL is not set +# CONFIG_KSM is not set +# CONFIG_KSZ884X_PCI is not set +# CONFIG_KUNIT is not set +CONFIG_KUSER_HELPERS=y +# CONFIG_KVM_AMD is not set +# CONFIG_KVM_AMD_SEV is not set +# CONFIG_KVM_GUEST is not set +# CONFIG_KVM_INTEL is not set +# CONFIG_KVM_WERROR is not set +# CONFIG_KVM_XEN is not set +# CONFIG_KXCJK1013 is not set +# CONFIG_KXSD9 is not set +# CONFIG_L2TP is not set +# CONFIG_L2TP_ETH is not set +# CONFIG_L2TP_IP is not set +# CONFIG_L2TP_V3 is not set +# CONFIG_LAN743X is not set +# CONFIG_LANMEDIA is not set +# CONFIG_LANTIQ is not set +# CONFIG_LAPB is not set +# CONFIG_LASAT is not set +# CONFIG_LATENCYTOP is not set +# CONFIG_LATTICE_ECP3_CONFIG is not set +CONFIG_LBDAF=y +# CONFIG_LCD_AMS369FG06 is not set +# CONFIG_LCD_CLASS_DEVICE is not set +# CONFIG_LCD_HX8357 is not set +# CONFIG_LCD_ILI922X is not set +# CONFIG_LCD_ILI9320 is not set +# CONFIG_LCD_L4F00242T03 is not set +# CONFIG_LCD_LD9040 is not set +# CONFIG_LCD_LMS283GF05 is not set +# CONFIG_LCD_LMS501KF03 is not set +# CONFIG_LCD_LTV350QV is not set +# CONFIG_LCD_OTM3225A is not set +# CONFIG_LCD_S6E63M0 is not set +# CONFIG_LCD_TDO24M is not set +# CONFIG_LCD_VGG2432A4 is not set +CONFIG_LDISC_AUTOLOAD=y +# CONFIG_LDM_PARTITION is not set +CONFIG_LD_DEAD_CODE_DATA_ELIMINATION=y +# CONFIG_LEDS_AN30259A is not set +# CONFIG_LEDS_APU is not set +# CONFIG_LEDS_AW2013 is not set +# CONFIG_LEDS_BCM6328 is not set +# CONFIG_LEDS_BCM6358 is not set +# CONFIG_LEDS_BD2802 is not set +# CONFIG_LEDS_BLINKM is not set +CONFIG_LEDS_BRIGHTNESS_HW_CHANGED=y +CONFIG_LEDS_CLASS=y +# CONFIG_LEDS_CLASS_FLASH is not set +CONFIG_LEDS_CLASS_MULTICOLOR=y +# CONFIG_LEDS_CR0014114 is not set +# CONFIG_LEDS_DAC124S085 is not set +# CONFIG_LEDS_EL15203000 is not set +# CONFIG_LEDS_GPIO is not set +# CONFIG_LEDS_INTEL_SS4200 is not set +# CONFIG_LEDS_IS31FL319X is not set +# CONFIG_LEDS_IS31FL32XX is not set +# CONFIG_LEDS_LM3530 is not set +# CONFIG_LEDS_LM3532 is not set +# CONFIG_LEDS_LM355x is not set +# CONFIG_LEDS_LM3642 is not set +# CONFIG_LEDS_LM3692X is not set +# CONFIG_LEDS_LP3944 is not set +# CONFIG_LEDS_LP3952 is not set +# CONFIG_LEDS_LP50XX is not set +# CONFIG_LEDS_LP5521 is not set +# CONFIG_LEDS_LP5523 is not set +# CONFIG_LEDS_LP5562 is not set +# CONFIG_LEDS_LP55XX_COMMON is not set +# CONFIG_LEDS_LP8501 is not set +# CONFIG_LEDS_LP8860 is not set +# CONFIG_LEDS_LT3593 is not set +# CONFIG_LEDS_MLXCPLD is not set +# CONFIG_LEDS_MLXREG is not set +# CONFIG_LEDS_NIC78BX is not set +# CONFIG_LEDS_NS2 is not set +# CONFIG_LEDS_OT200 is not set +# CONFIG_LEDS_PCA9532 is not set +# CONFIG_LEDS_PCA955X is not set +# CONFIG_LEDS_PCA963X is not set +# CONFIG_LEDS_PWM is not set +# CONFIG_LEDS_REGULATOR is not set +# CONFIG_LEDS_SPI_BYTE is not set +# CONFIG_LEDS_SYSCON is not set +# CONFIG_LEDS_TCA6507 is not set +# CONFIG_LEDS_TI_LMU_COMMON is not set +# CONFIG_LEDS_TLC591XX is not set +CONFIG_LEDS_TRIGGERS=y +# CONFIG_LEDS_TRIGGER_ACTIVITY is not set +# CONFIG_LEDS_TRIGGER_AUDIO is not set +# CONFIG_LEDS_TRIGGER_BACKLIGHT is not set +# CONFIG_LEDS_TRIGGER_CAMERA is not set +# CONFIG_LEDS_TRIGGER_CPU is not set +CONFIG_LEDS_TRIGGER_DEFAULT_ON=y +# CONFIG_LEDS_TRIGGER_DISK is not set +# CONFIG_LEDS_TRIGGER_GPIO is not set +CONFIG_LEDS_TRIGGER_HEARTBEAT=y +# CONFIG_LEDS_TRIGGER_MTD is not set +CONFIG_LEDS_TRIGGER_NETDEV=y +# CONFIG_LEDS_TRIGGER_ONESHOT is not set +# CONFIG_LEDS_TRIGGER_PANIC is not set +# CONFIG_LEDS_TRIGGER_PATTERN is not set +CONFIG_LEDS_TRIGGER_TIMER=y +# CONFIG_LEDS_TRIGGER_TRANSIENT is not set +# CONFIG_LEDS_TRIGGER_TTY is not set +# CONFIG_LEDS_TURRIS_OMNIA is not set +# CONFIG_LEDS_USER is not set +# CONFIG_LED_TRIGGER_PHY is not set +# CONFIG_LEGACY_PTYS is not set +# CONFIG_LGUEST is not set +# CONFIG_LIB80211 is not set +# CONFIG_LIB80211_CRYPT_CCMP is not set +# CONFIG_LIB80211_CRYPT_TKIP is not set +# CONFIG_LIB80211_CRYPT_WEP is not set +# CONFIG_LIB80211_DEBUG is not set +# CONFIG_LIBCRC32C is not set +# CONFIG_LIBERTAS is not set +# CONFIG_LIBERTAS_THINFIRM is not set +# CONFIG_LIBERTAS_USB is not set +# CONFIG_LIBFC is not set +# CONFIG_LIBFCOE is not set +# CONFIG_LIBIPW_DEBUG is not set +# CONFIG_LIBNVDIMM is not set +CONFIG_LIB_MEMNEQ=y +# CONFIG_LIDAR_LITE_V2 is not set +CONFIG_LINEAR_RANGES=y +# CONFIG_LIQUIDIO is not set +# CONFIG_LIQUIDIO_VF is not set +# CONFIG_LIS3L02DQ is not set +# CONFIG_LITEX_LITEETH is not set +# CONFIG_LITEX_SOC_CONTROLLER is not set +# CONFIG_LKDTM is not set +CONFIG_LLC=y +# CONFIG_LLC2 is not set +# CONFIG_LMK04832 is not set +# CONFIG_LMP91000 is not set +# CONFIG_LNET is not set +CONFIG_LOCALVERSION="" +# CONFIG_LOCALVERSION_AUTO is not set +# CONFIG_LOCKD is not set +CONFIG_LOCKDEP_BITS=15 +CONFIG_LOCKDEP_CIRCULAR_QUEUE_BITS=12 +CONFIG_LOCKDEP_CHAINS_BITS=16 +CONFIG_LOCKDEP_STACK_TRACE_BITS=19 +CONFIG_LOCKDEP_STACK_TRACE_HASH_BITS=14 +CONFIG_LOCKDEP_SUPPORT=y +CONFIG_LOCKD_V4=y +# CONFIG_LOCKUP_DETECTOR is not set +# CONFIG_LOCK_EVENT_COUNTS is not set +# CONFIG_LOCK_STAT is not set +# CONFIG_LOCK_TORTURE_TEST is not set +# CONFIG_LOGFS is not set +# CONFIG_LOGIG940_FF is not set +# CONFIG_LOGIRUMBLEPAD2_FF is not set +# CONFIG_LOGITECH_FF is not set +# CONFIG_LOGIWHEELS_FF is not set +# CONFIG_LOGO is not set +CONFIG_LOG_BUF_SHIFT=17 +CONFIG_LOG_CPU_MAX_BUF_SHIFT=12 +# CONFIG_LOONGSON_MC146818 is not set +# CONFIG_LPC_ICH is not set +# CONFIG_LPC_SCH is not set +# CONFIG_LP_CONSOLE is not set +CONFIG_LRU_GEN=y +CONFIG_LRU_GEN_ENABLED=y +# CONFIG_LRU_GEN_STATS is not set +# CONFIG_LSI_ET1011C_PHY is not set +CONFIG_LSM="lockdown,yama,loadpin,safesetid,integrity" +CONFIG_LSM_MMAP_MIN_ADDR=65536 +# CONFIG_LTC1660 is not set +# CONFIG_LTC2471 is not set +# CONFIG_LTC2485 is not set +# CONFIG_LTC2496 is not set +# CONFIG_LTC2497 is not set +# CONFIG_LTC2632 is not set +# CONFIG_LTC2983 is not set +# CONFIG_LTE_GDM724X is not set +CONFIG_LTO_NONE=y +# CONFIG_LTPC is not set +# CONFIG_LTR501 is not set +# CONFIG_LUSTRE_FS is not set +# CONFIG_LV0104CS is not set +# CONFIG_LWTUNNEL is not set +# CONFIG_LXT_PHY is not set +# CONFIG_LZ4HC_COMPRESS is not set +# CONFIG_LZ4_COMPRESS is not set +# CONFIG_LZ4_DECOMPRESS is not set +CONFIG_LZMA_COMPRESS=y +CONFIG_LZMA_DECOMPRESS=y +# CONFIG_LZO_COMPRESS is not set +# CONFIG_LZO_DECOMPRESS is not set +# CONFIG_M62332 is not set +# CONFIG_MAC80211 is not set +# CONFIG_MAC80211_MESSAGE_TRACING is not set +CONFIG_MAC80211_STA_HASH_MAX_SIZE=0 +# CONFIG_MACB is not set +# CONFIG_MACH_ASM9260 is not set +# CONFIG_MACH_DECSTATION is not set +# CONFIG_MACH_INGENIC is not set +# CONFIG_MACH_INGENIC_SOC is not set +# CONFIG_MACH_JAZZ is not set +# CONFIG_MACH_JZ4740 is not set +# CONFIG_MACH_LOONGSON2EF is not set +# CONFIG_MACH_LOONGSON32 is not set +# CONFIG_MACH_LOONGSON64 is not set +# CONFIG_MACH_NINTENDO64 is not set +# CONFIG_MACH_PIC32 is not set +# CONFIG_MACH_PISTACHIO is not set +# CONFIG_MACH_REALTEK_RTL is not set +# CONFIG_MACH_TX39XX is not set +# CONFIG_MACH_TX49XX is not set +# CONFIG_MACH_VR41XX is not set +# CONFIG_MACH_XILFPGA is not set +# CONFIG_MACINTOSH_DRIVERS is not set +# CONFIG_MACSEC is not set +# CONFIG_MACVLAN is not set +# CONFIG_MACVTAP is not set +# CONFIG_MAC_EMUMOUSEBTN is not set +# CONFIG_MAC_PARTITION is not set +# CONFIG_MAG3110 is not set +# CONFIG_MAGIC_SYSRQ is not set +CONFIG_MAGIC_SYSRQ_DEFAULT_ENABLE=0x1 +# CONFIG_MAGIC_SYSRQ_SERIAL is not set +CONFIG_MAGIC_SYSRQ_SERIAL_SEQUENCE="" +# CONFIG_MAILBOX is not set +# CONFIG_MANAGER_SBS is not set +# CONFIG_MANDATORY_FILE_LOCKING is not set +# CONFIG_MANGLE_BOOTARGS is not set +# CONFIG_MARVELL_10G_PHY is not set +# CONFIG_MARVELL_88X2222_PHY is not set +# CONFIG_MARVELL_PHY is not set +# CONFIG_MAX1027 is not set +# CONFIG_MAX11100 is not set +# CONFIG_MAX1118 is not set +# CONFIG_MAX1241 is not set +# CONFIG_MAX1363 is not set +# CONFIG_MAX30100 is not set +# CONFIG_MAX30102 is not set +# CONFIG_MAX31856 is not set +# CONFIG_MAX44000 is not set +# CONFIG_MAX44009 is not set +# CONFIG_MAX517 is not set +# CONFIG_MAX5432 is not set +# CONFIG_MAX5481 is not set +# CONFIG_MAX5487 is not set +# CONFIG_MAX5821 is not set +# CONFIG_MAX63XX_WATCHDOG is not set +# CONFIG_MAX9611 is not set +# CONFIG_MAXIM_THERMOCOUPLE is not set +# CONFIG_MAXLINEAR_GPHY is not set +CONFIG_MAY_USE_DEVLINK=y +# CONFIG_MB1232 is not set +# CONFIG_MC3230 is not set +# CONFIG_MCB is not set +# CONFIG_MCP320X is not set +# CONFIG_MCP3422 is not set +# CONFIG_MCP3911 is not set +# CONFIG_MCP4018 is not set +# CONFIG_MCP41010 is not set +# CONFIG_MCP4131 is not set +# CONFIG_MCP4531 is not set +# CONFIG_MCP4725 is not set +# CONFIG_MCP4922 is not set +# CONFIG_MCPM is not set +# CONFIG_MCTP is not set +# CONFIG_MD is not set +# CONFIG_MDIO_BCM_UNIMAC is not set +# CONFIG_MDIO_BITBANG is not set +# CONFIG_MDIO_BUS_MUX_GPIO is not set +# CONFIG_MDIO_BUS_MUX_MMIOREG is not set +# CONFIG_MDIO_BUS_MUX_MULTIPLEXER is not set +# CONFIG_MDIO_DEVICE is not set +# CONFIG_MDIO_DEVRES is not set +# CONFIG_MDIO_HISI_FEMAC is not set +# CONFIG_MDIO_IPQ4019 is not set +# CONFIG_MDIO_IPQ8064 is not set +# CONFIG_MDIO_MSCC_MIIM is not set +# CONFIG_MDIO_MVUSB is not set +# CONFIG_MDIO_OCTEON is not set +# CONFIG_MDIO_THUNDER is not set +# CONFIG_MDIO_XPCS is not set +# CONFIG_MDM_GCC_9607 is not set +# CONFIG_MD_FAULTY is not set +# CONFIG_MEDIATEK_GE_PHY is not set +# CONFIG_MEDIATEK_MT6577_AUXADC is not set +# CONFIG_MEDIA_ANALOG_TV_SUPPORT is not set +# CONFIG_MEDIA_ATTACH is not set +# CONFIG_MEDIA_CAMERA_SUPPORT is not set +# CONFIG_MEDIA_CEC_SUPPORT is not set +# CONFIG_MEDIA_CONTROLLER is not set +# CONFIG_MEDIA_DIGITAL_TV_SUPPORT is not set +# CONFIG_MEDIA_PCI_SUPPORT is not set +# CONFIG_MEDIA_PLATFORM_SUPPORT is not set +# CONFIG_MEDIA_RADIO_SUPPORT is not set +# CONFIG_MEDIA_RC_SUPPORT is not set +# CONFIG_MEDIA_SDR_SUPPORT is not set +# CONFIG_MEDIA_SUBDRV_AUTOSELECT is not set +# CONFIG_MEDIA_SUPPORT is not set +# CONFIG_MEDIA_SUPPORT_FILTER is not set +# CONFIG_MEDIA_TEST_SUPPORT is not set +# CONFIG_MEDIA_TUNER_E4000 is not set +# CONFIG_MEDIA_TUNER_FC0011 is not set +# CONFIG_MEDIA_TUNER_FC0012 is not set +# CONFIG_MEDIA_TUNER_FC0013 is not set +# CONFIG_MEDIA_TUNER_FC2580 is not set +# CONFIG_MEDIA_TUNER_IT913X is not set +# CONFIG_MEDIA_TUNER_M88RS6000T is not set +# CONFIG_MEDIA_TUNER_MAX2165 is not set +# CONFIG_MEDIA_TUNER_MC44S803 is not set +# CONFIG_MEDIA_TUNER_MSI001 is not set +# CONFIG_MEDIA_TUNER_MT2060 is not set +# CONFIG_MEDIA_TUNER_MT2063 is not set +# CONFIG_MEDIA_TUNER_MT20XX is not set +# CONFIG_MEDIA_TUNER_MT2131 is not set +# CONFIG_MEDIA_TUNER_MT2266 is not set +# CONFIG_MEDIA_TUNER_MXL301RF is not set +# CONFIG_MEDIA_TUNER_MXL5005S is not set +# CONFIG_MEDIA_TUNER_MXL5007T is not set +# CONFIG_MEDIA_TUNER_QM1D1B0004 is not set +# CONFIG_MEDIA_TUNER_QM1D1C0042 is not set +# CONFIG_MEDIA_TUNER_QT1010 is not set +# CONFIG_MEDIA_TUNER_R820T is not set +# CONFIG_MEDIA_TUNER_SI2157 is not set +# CONFIG_MEDIA_TUNER_SIMPLE is not set +# CONFIG_MEDIA_TUNER_TDA18212 is not set +# CONFIG_MEDIA_TUNER_TDA18218 is not set +# CONFIG_MEDIA_TUNER_TDA18250 is not set +# CONFIG_MEDIA_TUNER_TDA18271 is not set +# CONFIG_MEDIA_TUNER_TDA827X is not set +# CONFIG_MEDIA_TUNER_TDA8290 is not set +# CONFIG_MEDIA_TUNER_TDA9887 is not set +# CONFIG_MEDIA_TUNER_TEA5761 is not set +# CONFIG_MEDIA_TUNER_TEA5767 is not set +# CONFIG_MEDIA_TUNER_TUA9001 is not set +# CONFIG_MEDIA_TUNER_XC2028 is not set +# CONFIG_MEDIA_TUNER_XC4000 is not set +# CONFIG_MEDIA_TUNER_XC5000 is not set +# CONFIG_MEDIA_USB_SUPPORT is not set +# CONFIG_MEGARAID_LEGACY is not set +# CONFIG_MEGARAID_NEWGEN is not set +# CONFIG_MEGARAID_SAS is not set +# CONFIG_MELLANOX_PLATFORM is not set +CONFIG_MEMBARRIER=y +# CONFIG_MEMORY is not set +# CONFIG_MEMORY_FAILURE is not set +# CONFIG_MEMORY_HOTPLUG is not set +# CONFIG_MEMSTICK is not set +# CONFIG_MEMTEST is not set +# CONFIG_MEN_A21_WDT is not set +# CONFIG_MESON_SM is not set +CONFIG_MESSAGE_LOGLEVEL_DEFAULT=4 +# CONFIG_MFD_88PM800 is not set +# CONFIG_MFD_88PM805 is not set +# CONFIG_MFD_88PM860X is not set +# CONFIG_MFD_AAT2870_CORE is not set +# CONFIG_MFD_AC100 is not set +# CONFIG_MFD_ACT8945A is not set +# CONFIG_MFD_ARIZONA_I2C is not set +# CONFIG_MFD_ARIZONA_SPI is not set +# CONFIG_MFD_AS3711 is not set +# CONFIG_MFD_AS3722 is not set +# CONFIG_MFD_ASIC3 is not set +# CONFIG_MFD_ATC260X_I2C is not set +# CONFIG_MFD_ATMEL_FLEXCOM is not set +# CONFIG_MFD_ATMEL_HLCDC is not set +# CONFIG_MFD_AXP20X is not set +# CONFIG_MFD_AXP20X_I2C is not set +# CONFIG_MFD_BCM590XX is not set +# CONFIG_MFD_BD9571MWV is not set +# CONFIG_MFD_CORE is not set +# CONFIG_MFD_CPCAP is not set +# CONFIG_MFD_CROS_EC is not set +# CONFIG_MFD_CS5535 is not set +# CONFIG_MFD_DA9052_I2C is not set +# CONFIG_MFD_DA9052_SPI is not set +# CONFIG_MFD_DA9055 is not set +# CONFIG_MFD_DA9062 is not set +# CONFIG_MFD_DA9063 is not set +# CONFIG_MFD_DA9150 is not set +# CONFIG_MFD_DLN2 is not set +# CONFIG_MFD_EXYNOS_LPASS is not set +# CONFIG_MFD_GATEWORKS_GSC is not set +# CONFIG_MFD_HI6421_PMIC is not set +# CONFIG_MFD_INTEL_M10_BMC is not set +# CONFIG_MFD_INTEL_PMT is not set +# CONFIG_MFD_INTEL_QUARK_I2C_GPIO is not set +# CONFIG_MFD_IQS62X is not set +# CONFIG_MFD_JANZ_CMODIO is not set +# CONFIG_MFD_KEMPLD is not set +# CONFIG_MFD_LM3533 is not set +# CONFIG_MFD_LOCHNAGAR is not set +# CONFIG_MFD_LP3943 is not set +# CONFIG_MFD_LP8788 is not set +# CONFIG_MFD_MADERA is not set +# CONFIG_MFD_MAX14577 is not set +# CONFIG_MFD_MAX77620 is not set +# CONFIG_MFD_MAX77650 is not set +# CONFIG_MFD_MAX77686 is not set +# CONFIG_MFD_MAX77693 is not set +# CONFIG_MFD_MAX77843 is not set +# CONFIG_MFD_MAX8907 is not set +# CONFIG_MFD_MAX8925 is not set +# CONFIG_MFD_MAX8997 is not set +# CONFIG_MFD_MAX8998 is not set +# CONFIG_MFD_MC13XXX is not set +# CONFIG_MFD_MC13XXX_I2C is not set +# CONFIG_MFD_MC13XXX_SPI is not set +# CONFIG_MFD_MENF21BMC is not set +# CONFIG_MFD_MP2629 is not set +# CONFIG_MFD_MT6360 is not set +# CONFIG_MFD_MT6397 is not set +# CONFIG_MFD_NTXEC is not set +# CONFIG_MFD_OMAP_USB_HOST is not set +# CONFIG_MFD_PALMAS is not set +# CONFIG_MFD_PCF50633 is not set +# CONFIG_MFD_PM8921_CORE is not set +# CONFIG_MFD_PM8XXX is not set +# CONFIG_MFD_QCOM_PM8008 is not set +# CONFIG_MFD_RC5T583 is not set +# CONFIG_MFD_RDC321X is not set +# CONFIG_MFD_RETU is not set +# CONFIG_MFD_RK808 is not set +# CONFIG_MFD_RN5T618 is not set +# CONFIG_MFD_ROHM_BD70528 is not set +# CONFIG_MFD_ROHM_BD71828 is not set +# CONFIG_MFD_ROHM_BD718XX is not set +# CONFIG_MFD_ROHM_BD957XMUF is not set +# CONFIG_MFD_RSMU_I2C is not set +# CONFIG_MFD_RSMU_SPI is not set +# CONFIG_MFD_RT4831 is not set +# CONFIG_MFD_RT5033 is not set +# CONFIG_MFD_RTSX_PCI is not set +# CONFIG_MFD_RTSX_USB is not set +# CONFIG_MFD_SEC_CORE is not set +# CONFIG_MFD_SI476X_CORE is not set +# CONFIG_MFD_SKY81452 is not set +# CONFIG_MFD_SL28CPLD is not set +# CONFIG_MFD_SM501 is not set +# CONFIG_MFD_SMSC is not set +# CONFIG_MFD_STMFX is not set +# CONFIG_MFD_STMPE is not set +# CONFIG_MFD_STPMIC1 is not set +# CONFIG_MFD_SYSCON is not set +# CONFIG_MFD_T7L66XB is not set +# CONFIG_MFD_TC3589X is not set +# CONFIG_MFD_TC6387XB is not set +# CONFIG_MFD_TC6393XB is not set +# CONFIG_MFD_TIMBERDALE is not set +# CONFIG_MFD_TI_AM335X_TSCADC is not set +# CONFIG_MFD_TI_LMU is not set +# CONFIG_MFD_TI_LP873X is not set +# CONFIG_MFD_TI_LP87565 is not set +# CONFIG_MFD_TMIO is not set +# CONFIG_MFD_TPS65086 is not set +# CONFIG_MFD_TPS65090 is not set +# CONFIG_MFD_TPS65217 is not set +# CONFIG_MFD_TPS65218 is not set +# CONFIG_MFD_TPS6586X is not set +# CONFIG_MFD_TPS65910 is not set +# CONFIG_MFD_TPS65912 is not set +# CONFIG_MFD_TPS65912_I2C is not set +# CONFIG_MFD_TPS65912_SPI is not set +# CONFIG_MFD_TPS68470 is not set +# CONFIG_MFD_TPS80031 is not set +# CONFIG_MFD_TQMX86 is not set +# CONFIG_MFD_VIPERBOARD is not set +# CONFIG_MFD_VX855 is not set +# CONFIG_MFD_WL1273_CORE is not set +# CONFIG_MFD_WM831X is not set +# CONFIG_MFD_WM831X_I2C is not set +# CONFIG_MFD_WM831X_SPI is not set +# CONFIG_MFD_WM8350_I2C is not set +# CONFIG_MFD_WM8400 is not set +# CONFIG_MFD_WM8994 is not set +# CONFIG_MG_DISK is not set +# CONFIG_MHI_BUS is not set +# CONFIG_MHI_BUS_DEBUG is not set +# CONFIG_MHI_BUS_PCI_GENERIC is not set +# CONFIG_MHI_NET is not set +# CONFIG_MHI_WWAN_CTRL is not set +# CONFIG_MHI_WWAN_MBIM is not set +# CONFIG_MICREL_KS8995MA is not set +# CONFIG_MICREL_PHY is not set +# CONFIG_MICROCHIP_KSZ is not set +# CONFIG_MICROCHIP_PHY is not set +# CONFIG_MICROCHIP_PIT64B is not set +# CONFIG_MICROCHIP_T1_PHY is not set +# CONFIG_MICROSEMI_PHY is not set +# CONFIG_MIGRATION is not set +CONFIG_MII=y +# CONFIG_MIKROTIK is not set +# CONFIG_MIKROTIK_RB532 is not set +# CONFIG_MINIX_FS is not set +# CONFIG_MINIX_FS_NATIVE_ENDIAN is not set +# CONFIG_MINIX_SUBPARTITION is not set +# CONFIG_MIPS32_N32 is not set +# CONFIG_MIPS32_O32 is not set +# CONFIG_MIPS_ALCHEMY is not set +# CONFIG_MIPS_CDMM is not set +# CONFIG_MIPS_CMDLINE_DTB_EXTEND is not set +# CONFIG_MIPS_CMDLINE_FROM_DTB is not set +# CONFIG_MIPS_CMP is not set +# CONFIG_MIPS_COBALT is not set +# CONFIG_MIPS_CPS is not set +# CONFIG_MIPS_ELF_APPENDED_DTB is not set +# CONFIG_MIPS_FPU_EMULATOR is not set +# CONFIG_MIPS_FP_SUPPORT is not set +# CONFIG_MIPS_GENERIC is not set +# CONFIG_MIPS_GENERIC_KERNEL is not set +# CONFIG_MIPS_MALTA is not set +# CONFIG_MIPS_O32_FP64_SUPPORT is not set +# CONFIG_MIPS_PARAVIRT is not set +# CONFIG_MIPS_PLATFORM_DEVICES is not set +# CONFIG_MIPS_RAW_APPENDED_DTB is not set +# CONFIG_MIPS_SEAD3 is not set +# CONFIG_MIPS_VA_BITS_48 is not set +# CONFIG_MIPS_VPE_LOADER is not set +# CONFIG_MISC_ALCOR_PCI is not set +CONFIG_MISC_FILESYSTEMS=y +# CONFIG_MISC_RTSX_PCI is not set +# CONFIG_MISC_RTSX_USB is not set +# CONFIG_MISDN is not set +# CONFIG_MISDN_AVMFRITZ is not set +# CONFIG_MISDN_HFCPCI is not set +# CONFIG_MISDN_HFCUSB is not set +# CONFIG_MISDN_INFINEON is not set +# CONFIG_MISDN_NETJET is not set +# CONFIG_MISDN_SPEEDFAX is not set +# CONFIG_MISDN_W6692 is not set +CONFIG_MITIGATE_SPECTRE_BRANCH_HISTORY=y +# CONFIG_MKISS is not set +# CONFIG_MLX4_CORE is not set +# CONFIG_MLX4_EN is not set +# CONFIG_MLX5_CORE is not set +# CONFIG_MLX5_SF is not set +# CONFIG_MLX90614 is not set +# CONFIG_MLX90632 is not set +# CONFIG_MLXFW is not set +# CONFIG_MLXSW_CORE is not set +# CONFIG_MLX_CPLD_PLATFORM is not set +# CONFIG_MLX_PLATFORM is not set +# CONFIG_MMA7455_I2C is not set +# CONFIG_MMA7455_SPI is not set +# CONFIG_MMA7660 is not set +# CONFIG_MMA8452 is not set +# CONFIG_MMA9551 is not set +# CONFIG_MMA9553 is not set +# CONFIG_MMC is not set +# CONFIG_MMC35240 is not set +# CONFIG_MMC_ARMMMCI is not set +# CONFIG_MMC_AU1X is not set +# CONFIG_MMC_BLOCK is not set +CONFIG_MMC_BLOCK_MINORS=8 +# CONFIG_MMC_CAVIUM_THUNDERX is not set +# CONFIG_MMC_CB710 is not set +# CONFIG_MMC_CQHCI is not set +# CONFIG_MMC_DEBUG is not set +# CONFIG_MMC_DW is not set +# CONFIG_MMC_HSQ is not set +# CONFIG_MMC_JZ4740 is not set +# CONFIG_MMC_MTK is not set +# CONFIG_MMC_MVSDIO is not set +# CONFIG_MMC_S3C is not set +# CONFIG_MMC_SDHCI is not set +# CONFIG_MMC_SDHCI_ACPI is not set +# CONFIG_MMC_SDHCI_AM654 is not set +# CONFIG_MMC_SDHCI_BCM_KONA is not set +# CONFIG_MMC_SDHCI_CADENCE is not set +# CONFIG_MMC_SDHCI_F_SDH30 is not set +# CONFIG_MMC_SDHCI_IPROC is not set +# CONFIG_MMC_SDHCI_MILBEAUT is not set +# CONFIG_MMC_SDHCI_MSM is not set +# CONFIG_MMC_SDHCI_OF_ARASAN is not set +# CONFIG_MMC_SDHCI_OF_ASPEED is not set +# CONFIG_MMC_SDHCI_OF_AT91 is not set +# CONFIG_MMC_SDHCI_OF_DWCMSHC is not set +# CONFIG_MMC_SDHCI_OF_ESDHC is not set +# CONFIG_MMC_SDHCI_OF_HLWD is not set +# CONFIG_MMC_SDHCI_OMAP is not set +# CONFIG_MMC_SDHCI_PXAV2 is not set +# CONFIG_MMC_SDHCI_PXAV3 is not set +# CONFIG_MMC_SDHCI_S3C is not set +# CONFIG_MMC_SDHCI_XENON is not set +# CONFIG_MMC_SDRICOH_CS is not set +# CONFIG_MMC_SPI is not set +# CONFIG_MMC_STM32_SDMMC is not set +# CONFIG_MMC_TEST is not set +# CONFIG_MMC_TIFM_SD is not set +# CONFIG_MMC_TOSHIBA_PCI is not set +# CONFIG_MMC_USDHI6ROL0 is not set +# CONFIG_MMC_USHC is not set +# CONFIG_MMC_VIA_SDMMC is not set +# CONFIG_MMC_VUB300 is not set +# CONFIG_MMIOTRACE is not set +CONFIG_MMU=y +CONFIG_MMU_GATHER_RCU_TABLE_FREE=y +CONFIG_MMU_GATHER_TABLE_FREE=y +CONFIG_MODPROBE_PATH="/sbin/modprobe" +CONFIG_MODULES=y +# CONFIG_MODULE_ALLOW_MISSING_NAMESPACE_IMPORTS is not set +# CONFIG_MODULE_COMPRESS is not set +# CONFIG_MODULE_COMPRESS_GZIP is not set +CONFIG_MODULE_COMPRESS_NONE=y +# CONFIG_MODULE_COMPRESS_XZ is not set +# CONFIG_MODULE_COMPRESS_ZSTD is not set +# CONFIG_MODULE_FORCE_LOAD is not set +# CONFIG_MODULE_FORCE_UNLOAD is not set +# CONFIG_MODULE_SIG is not set +# CONFIG_MODULE_SRCVERSION_ALL is not set +CONFIG_MODULE_STRIPPED=y +CONFIG_MODULE_UNLOAD=y +# CONFIG_MODVERSIONS is not set +# CONFIG_MOST is not set +# CONFIG_MOTORCOMM_PHY is not set +# CONFIG_MOUSE_APPLETOUCH is not set +# CONFIG_MOUSE_ELAN_I2C is not set +# CONFIG_MOUSE_GPIO is not set +# CONFIG_MOUSE_INPORT is not set +# CONFIG_MOUSE_LOGIBM is not set +# CONFIG_MOUSE_PC110PAD is not set +# CONFIG_MOUSE_PS2_FOCALTECH is not set +# CONFIG_MOUSE_PS2_SENTELIC is not set +# CONFIG_MOUSE_SYNAPTICS_I2C is not set +# CONFIG_MOUSE_SYNAPTICS_USB is not set +# CONFIG_MOXTET is not set +# CONFIG_MPL115 is not set +# CONFIG_MPL115_I2C is not set +# CONFIG_MPL115_SPI is not set +# CONFIG_MPL3115 is not set +# CONFIG_MPLS is not set +# CONFIG_MPLS_IPTUNNEL is not set +# CONFIG_MPLS_ROUTING is not set +# CONFIG_MPTCP is not set +# CONFIG_MPU3050_I2C is not set +# CONFIG_MQ_IOSCHED_DEADLINE is not set +# CONFIG_MQ_IOSCHED_KYBER is not set +# CONFIG_MS5611 is not set +# CONFIG_MS5637 is not set +# CONFIG_MSCC_OCELOT_SWITCH is not set +# CONFIG_MSDOS_FS is not set +CONFIG_MSDOS_PARTITION=y +# CONFIG_MSI_BITMAP_SELFTEST is not set +# CONFIG_MSI_LAPTOP is not set +# CONFIG_MSM_GCC_8953 is not set +# CONFIG_MSM_MMCC_8994 is not set +# CONFIG_MST_IRQ is not set +CONFIG_MTD=y +# CONFIG_MTD_ABSENT is not set +# CONFIG_MTD_AFS_PARTS is not set +# CONFIG_MTD_AR7_PARTS is not set +CONFIG_MTD_BLKDEVS=y +CONFIG_MTD_BLOCK=y +# CONFIG_MTD_BLOCK2MTD is not set +CONFIG_MTD_CFI=y +# CONFIG_MTD_CFI_ADV_OPTIONS is not set +CONFIG_MTD_CFI_AMDSTD=y +# CONFIG_MTD_CFI_BE_BYTE_SWAP is not set +CONFIG_MTD_CFI_I1=y +CONFIG_MTD_CFI_I2=y +# CONFIG_MTD_CFI_I4 is not set +# CONFIG_MTD_CFI_I8 is not set +CONFIG_MTD_CFI_INTELEXT=y +# CONFIG_MTD_CFI_LE_BYTE_SWAP is not set +CONFIG_MTD_CFI_NOSWAP=y +# CONFIG_MTD_CFI_STAA is not set +CONFIG_MTD_CFI_UTIL=y +# CONFIG_MTD_CMDLINE_PARTS is not set +CONFIG_MTD_COMPLEX_MAPPINGS=y +# CONFIG_MTD_DATAFLASH is not set +# CONFIG_MTD_DOCG3 is not set +CONFIG_MTD_GEN_PROBE=y +# CONFIG_MTD_GPIO_ADDR is not set +# CONFIG_MTD_HYPERBUS is not set +# CONFIG_MTD_IMPA7 is not set +# CONFIG_MTD_INTEL_VR_NOR is not set +# CONFIG_MTD_JEDECPROBE is not set +# CONFIG_MTD_LATCH_ADDR is not set +# CONFIG_MTD_LPDDR is not set +# CONFIG_MTD_LPDDR2_NVM is not set +# CONFIG_MTD_M25P80 is not set +CONFIG_MTD_MAP_BANK_WIDTH_1=y +# CONFIG_MTD_MAP_BANK_WIDTH_16 is not set +CONFIG_MTD_MAP_BANK_WIDTH_2=y +# CONFIG_MTD_MAP_BANK_WIDTH_32 is not set +CONFIG_MTD_MAP_BANK_WIDTH_4=y +# CONFIG_MTD_MAP_BANK_WIDTH_8 is not set +# CONFIG_MTD_MCHP23K256 is not set +# CONFIG_MTD_MCHP48L640 is not set +# CONFIG_MTD_MT81xx_NOR is not set +# CONFIG_MTD_MTDRAM is not set +# CONFIG_MTD_MYLOADER_PARTS is not set +# CONFIG_MTD_NAND is not set +# CONFIG_MTD_NAND_AMS_DELTA is not set +# CONFIG_MTD_NAND_AR934X is not set +# CONFIG_MTD_NAND_AR934X_HW_ECC is not set +# CONFIG_MTD_NAND_ARASAN is not set +# CONFIG_MTD_NAND_ATMEL is not set +# CONFIG_MTD_NAND_AU1550 is not set +# CONFIG_MTD_NAND_BCH is not set +# CONFIG_MTD_NAND_BF5XX is not set +# CONFIG_MTD_NAND_BRCMNAND is not set +# CONFIG_MTD_NAND_CADENCE is not set +# CONFIG_MTD_NAND_CAFE is not set +# CONFIG_MTD_NAND_CM_X270 is not set +# CONFIG_MTD_NAND_CS553X is not set +# CONFIG_MTD_NAND_DAVINCI is not set +# CONFIG_MTD_NAND_DENALI is not set +# CONFIG_MTD_NAND_DENALI_DT is not set +# CONFIG_MTD_NAND_DENALI_PCI is not set +CONFIG_MTD_NAND_DENALI_SCRATCH_REG_ADDR=0xff108018 +# CONFIG_MTD_NAND_DISKONCHIP is not set +# CONFIG_MTD_NAND_DOCG4 is not set +# CONFIG_MTD_NAND_ECC is not set +# CONFIG_MTD_NAND_ECC_BCH is not set +# CONFIG_MTD_NAND_ECC_SMC is not set +# CONFIG_MTD_NAND_ECC_SW_BCH is not set +# CONFIG_MTD_NAND_ECC_SW_HAMMING is not set +# CONFIG_MTD_NAND_ECC_SW_HAMMING_SMC is not set +# CONFIG_MTD_NAND_FSL_ELBC is not set +# CONFIG_MTD_NAND_FSL_IFC is not set +# CONFIG_MTD_NAND_FSL_UPM is not set +# CONFIG_MTD_NAND_FSMC is not set +# CONFIG_MTD_NAND_GPIO is not set +# CONFIG_MTD_NAND_GPMI_NAND is not set +# CONFIG_MTD_NAND_HISI504 is not set +CONFIG_MTD_NAND_IDS=y +# CONFIG_MTD_NAND_INTEL_LGM is not set +# CONFIG_MTD_NAND_JZ4740 is not set +# CONFIG_MTD_NAND_MPC5121_NFC is not set +# CONFIG_MTD_NAND_MTK is not set +# CONFIG_MTD_NAND_MTK_BMT is not set +# CONFIG_MTD_NAND_MXC is not set +# CONFIG_MTD_NAND_MXIC is not set +# CONFIG_MTD_NAND_NANDSIM is not set +# CONFIG_MTD_NAND_NDFC is not set +# CONFIG_MTD_NAND_NUC900 is not set +# CONFIG_MTD_NAND_OMAP2 is not set +# CONFIG_MTD_NAND_OMAP_BCH_BUILD is not set +# CONFIG_MTD_NAND_ORION is not set +# CONFIG_MTD_NAND_PASEMI is not set +# CONFIG_MTD_NAND_PLATFORM is not set +# CONFIG_MTD_NAND_PXA3xx is not set +# CONFIG_MTD_NAND_RB4XX is not set +# CONFIG_MTD_NAND_RB750 is not set +# CONFIG_MTD_NAND_RB91X is not set +# CONFIG_MTD_NAND_RICOH is not set +# CONFIG_MTD_NAND_S3C2410 is not set +# CONFIG_MTD_NAND_SHARPSL is not set +# CONFIG_MTD_NAND_SH_FLCTL is not set +# CONFIG_MTD_NAND_SOCRATES is not set +# CONFIG_MTD_NAND_TMIO is not set +# CONFIG_MTD_NAND_TXX9NDFMC is not set +CONFIG_MTD_OF_PARTS=y +# CONFIG_MTD_ONENAND is not set +# CONFIG_MTD_OOPS is not set +# CONFIG_MTD_OTP is not set +# CONFIG_MTD_PARSER_TRX is not set +# CONFIG_MTD_PARTITIONED_MASTER is not set +# CONFIG_MTD_PCI is not set +# CONFIG_MTD_PCMCIA is not set +# CONFIG_MTD_PHRAM is not set +# CONFIG_MTD_PHYSMAP is not set +# CONFIG_MTD_PHYSMAP_COMPAT is not set +# CONFIG_MTD_PHYSMAP_GEMINI is not set +# CONFIG_MTD_PHYSMAP_GPIO_ADDR is not set +# CONFIG_MTD_PHYSMAP_IXP4XX is not set +CONFIG_MTD_PHYSMAP_OF=y +# CONFIG_MTD_PHYSMAP_OF_GEMINI is not set +# CONFIG_MTD_PHYSMAP_OF_VERSATILE is not set +# CONFIG_MTD_PHYSMAP_VERSATILE is not set +# CONFIG_MTD_PLATRAM is not set +# CONFIG_MTD_PMC551 is not set +# CONFIG_MTD_RAM is not set +# CONFIG_MTD_RAW_NAND is not set +CONFIG_MTD_REDBOOT_DIRECTORY_BLOCK=-1 +# CONFIG_MTD_REDBOOT_PARTS is not set +# CONFIG_MTD_REDBOOT_PARTS_READONLY is not set +# CONFIG_MTD_REDBOOT_PARTS_UNALLOCATED is not set +# CONFIG_MTD_ROM is not set +CONFIG_MTD_ROOTFS_ROOT_DEV=y +# CONFIG_MTD_ROUTERBOOT_PARTS is not set +# CONFIG_MTD_SERCOMM_PARTS is not set +# CONFIG_MTD_SLRAM is not set +# CONFIG_MTD_SM_COMMON is not set +# CONFIG_MTD_SPINAND_MT29F is not set +# CONFIG_MTD_SPI_NAND is not set +# CONFIG_MTD_SPI_NOR is not set +# CONFIG_MTD_SPI_NOR_SWP_DISABLE is not set +CONFIG_MTD_SPI_NOR_SWP_DISABLE_ON_VOLATILE=y +# CONFIG_MTD_SPI_NOR_SWP_KEEP is not set +# CONFIG_MTD_SPI_NOR_USE_4K_SECTORS is not set +# CONFIG_MTD_SPI_NOR_USE_VARIABLE_ERASE is not set +CONFIG_MTD_SPLIT=y +# CONFIG_MTD_SPLIT_BCM63XX_FW is not set +# CONFIG_MTD_SPLIT_BCM_WFI_FW is not set +# CONFIG_MTD_SPLIT_BRNIMAGE_FW is not set +# CONFIG_MTD_SPLIT_ELF_FW is not set +# CONFIG_MTD_SPLIT_EVA_FW is not set +# CONFIG_MTD_SPLIT_FIRMWARE is not set +CONFIG_MTD_SPLIT_FIRMWARE_NAME="firmware" +# CONFIG_MTD_SPLIT_FIT_FW is not set +# CONFIG_MTD_SPLIT_H3C_VFS is not set +# CONFIG_MTD_SPLIT_JIMAGE_FW is not set +# CONFIG_MTD_SPLIT_LZMA_FW is not set +# CONFIG_MTD_SPLIT_MINOR_FW is not set +# CONFIG_MTD_SPLIT_SEAMA_FW is not set +CONFIG_MTD_SPLIT_SQUASHFS_ROOT=y +CONFIG_MTD_SPLIT_SUPPORT=y +# CONFIG_MTD_SPLIT_TPLINK_FW is not set +# CONFIG_MTD_SPLIT_TRX_FW is not set +# CONFIG_MTD_SPLIT_UIMAGE_FW is not set +# CONFIG_MTD_SPLIT_WRGG_FW is not set +# CONFIG_MTD_SST25L is not set +# CONFIG_MTD_SWAP is not set +# CONFIG_MTD_TESTS is not set +# CONFIG_MTD_UBI is not set +# CONFIG_MTD_UBI_FASTMAP is not set +# CONFIG_MTD_UBI_GLUEBI is not set +# CONFIG_MTD_UIMAGE_SPLIT is not set +# CONFIG_MTD_VIRT_CONCAT is not set +# CONFIG_MTK_DEVAPC is not set +# CONFIG_MTK_MMC is not set +# CONFIG_MTK_MMSYS is not set +# CONFIG_MTK_THERMAL is not set +# CONFIG_MULTIPLEXER is not set +CONFIG_MULTIUSER=y +# CONFIG_MUTEX_SPIN_ON_OWNER is not set +# CONFIG_MUX_ADG792A is not set +# CONFIG_MUX_ADGS1408 is not set +# CONFIG_MUX_GPIO is not set +# CONFIG_MUX_MMIO is not set +# CONFIG_MV643XX_ETH is not set +# CONFIG_MVMDIO is not set +# CONFIG_MVNETA_BM is not set +# CONFIG_MVSW61XX_PHY is not set +# CONFIG_MV_XOR_V2 is not set +# CONFIG_MWAVE is not set +# CONFIG_MWL8K is not set +# CONFIG_MXC4005 is not set +# CONFIG_MXC6255 is not set +# CONFIG_MYRI10GE is not set +# CONFIG_NAMESPACES is not set +# CONFIG_NATIONAL_PHY is not set +# CONFIG_NATSEMI is not set +# CONFIG_NAU7802 is not set +# CONFIG_NBPFAXI_DMA is not set +# CONFIG_NCP_FS is not set +# CONFIG_NE2000 is not set +# CONFIG_NE2K_PCI is not set +# CONFIG_NEC_MARKEINS is not set +CONFIG_NET=y +# CONFIG_NETCONSOLE is not set +CONFIG_NETDEVICES=y +# CONFIG_NETDEVSIM is not set +# CONFIG_NETFILTER is not set +# CONFIG_NETFILTER_ADVANCED is not set +# CONFIG_NETFILTER_DEBUG is not set +# CONFIG_NETFILTER_INGRESS is not set +# CONFIG_NETFILTER_NETLINK is not set +# CONFIG_NETFILTER_NETLINK_ACCT is not set +# CONFIG_NETFILTER_NETLINK_GLUE_CT is not set +# CONFIG_NETFILTER_NETLINK_HOOK is not set +# CONFIG_NETFILTER_NETLINK_LOG is not set +# CONFIG_NETFILTER_NETLINK_OSF is not set +# CONFIG_NETFILTER_NETLINK_QUEUE is not set +# CONFIG_NETFILTER_XTABLES is not set +# CONFIG_NETFILTER_XTABLES_COMPAT is not set +# CONFIG_NETFILTER_XT_CONNMARK is not set +# CONFIG_NETFILTER_XT_MARK is not set +# CONFIG_NETFILTER_XT_MATCH_ADDRTYPE is not set +# CONFIG_NETFILTER_XT_MATCH_BPF is not set +# CONFIG_NETFILTER_XT_MATCH_CGROUP is not set +# CONFIG_NETFILTER_XT_MATCH_CLUSTER is not set +# CONFIG_NETFILTER_XT_MATCH_COMMENT is not set +# CONFIG_NETFILTER_XT_MATCH_CONNBYTES is not set +# CONFIG_NETFILTER_XT_MATCH_CONNLABEL is not set +# CONFIG_NETFILTER_XT_MATCH_CONNLIMIT is not set +# CONFIG_NETFILTER_XT_MATCH_CONNMARK is not set +# CONFIG_NETFILTER_XT_MATCH_CONNTRACK is not set +# CONFIG_NETFILTER_XT_MATCH_CPU is not set +# CONFIG_NETFILTER_XT_MATCH_DCCP is not set +# CONFIG_NETFILTER_XT_MATCH_DEVGROUP is not set +# CONFIG_NETFILTER_XT_MATCH_DSCP is not set +# CONFIG_NETFILTER_XT_MATCH_ECN is not set +# CONFIG_NETFILTER_XT_MATCH_ESP is not set +# CONFIG_NETFILTER_XT_MATCH_HASHLIMIT is not set +# CONFIG_NETFILTER_XT_MATCH_HELPER is not set +# CONFIG_NETFILTER_XT_MATCH_HL is not set +# CONFIG_NETFILTER_XT_MATCH_IPCOMP is not set +# CONFIG_NETFILTER_XT_MATCH_IPRANGE is not set +# CONFIG_NETFILTER_XT_MATCH_L2TP is not set +# CONFIG_NETFILTER_XT_MATCH_LENGTH is not set +# CONFIG_NETFILTER_XT_MATCH_LIMIT is not set +# CONFIG_NETFILTER_XT_MATCH_MAC is not set +# CONFIG_NETFILTER_XT_MATCH_MARK is not set +# CONFIG_NETFILTER_XT_MATCH_MULTIPORT is not set +# CONFIG_NETFILTER_XT_MATCH_NFACCT is not set +# CONFIG_NETFILTER_XT_MATCH_OSF is not set +# CONFIG_NETFILTER_XT_MATCH_OWNER is not set +# CONFIG_NETFILTER_XT_MATCH_PHYSDEV is not set +# CONFIG_NETFILTER_XT_MATCH_PKTTYPE is not set +# CONFIG_NETFILTER_XT_MATCH_POLICY is not set +# CONFIG_NETFILTER_XT_MATCH_QUOTA is not set +# CONFIG_NETFILTER_XT_MATCH_RATEEST is not set +# CONFIG_NETFILTER_XT_MATCH_REALM is not set +# CONFIG_NETFILTER_XT_MATCH_RECENT is not set +# CONFIG_NETFILTER_XT_MATCH_SCTP is not set +# CONFIG_NETFILTER_XT_MATCH_SOCKET is not set +# CONFIG_NETFILTER_XT_MATCH_STATE is not set +# CONFIG_NETFILTER_XT_MATCH_STATISTIC is not set +# CONFIG_NETFILTER_XT_MATCH_STRING is not set +# CONFIG_NETFILTER_XT_MATCH_TCPMSS is not set +# CONFIG_NETFILTER_XT_MATCH_TIME is not set +# CONFIG_NETFILTER_XT_MATCH_U32 is not set +# CONFIG_NETFILTER_XT_TARGET_AUDIT is not set +# CONFIG_NETFILTER_XT_TARGET_CHECKSUM is not set +# CONFIG_NETFILTER_XT_TARGET_CLASSIFY is not set +# CONFIG_NETFILTER_XT_TARGET_CONNMARK is not set +# CONFIG_NETFILTER_XT_TARGET_CT is not set +# CONFIG_NETFILTER_XT_TARGET_DSCP is not set +# CONFIG_NETFILTER_XT_TARGET_HL is not set +# CONFIG_NETFILTER_XT_TARGET_HMARK is not set +# CONFIG_NETFILTER_XT_TARGET_IDLETIMER is not set +# CONFIG_NETFILTER_XT_TARGET_LED is not set +# CONFIG_NETFILTER_XT_TARGET_LOG is not set +# CONFIG_NETFILTER_XT_TARGET_MARK is not set +# CONFIG_NETFILTER_XT_TARGET_NETMAP is not set +# CONFIG_NETFILTER_XT_TARGET_NFLOG is not set +# CONFIG_NETFILTER_XT_TARGET_NFQUEUE is not set +# CONFIG_NETFILTER_XT_TARGET_NOTRACK is not set +# CONFIG_NETFILTER_XT_TARGET_RATEEST is not set +# CONFIG_NETFILTER_XT_TARGET_REDIRECT is not set +# CONFIG_NETFILTER_XT_TARGET_SECMARK is not set +# CONFIG_NETFILTER_XT_TARGET_TCPMSS is not set +# CONFIG_NETFILTER_XT_TARGET_TCPOPTSTRIP is not set +# CONFIG_NETFILTER_XT_TARGET_TEE is not set +# CONFIG_NETFILTER_XT_TARGET_TPROXY is not set +# CONFIG_NETFILTER_XT_TARGET_TRACE is not set +# CONFIG_NETLABEL is not set +# CONFIG_NETLINK_DIAG is not set +# CONFIG_NETLINK_MMAP is not set +# CONFIG_NETPOLL is not set +# CONFIG_NETROM is not set +CONFIG_NETWORK_FILESYSTEMS=y +# CONFIG_NETWORK_PHY_TIMESTAMPING is not set +# CONFIG_NETWORK_SECMARK is not set +# CONFIG_NETXEN_NIC is not set +# CONFIG_NET_9P is not set +# CONFIG_NET_ACT_BPF is not set +# CONFIG_NET_ACT_CSUM is not set +# CONFIG_NET_ACT_CT is not set +# CONFIG_NET_ACT_GACT is not set +# CONFIG_NET_ACT_GATE is not set +# CONFIG_NET_ACT_IFE is not set +# CONFIG_NET_ACT_IPT is not set +# CONFIG_NET_ACT_MIRRED is not set +# CONFIG_NET_ACT_MPLS is not set +# CONFIG_NET_ACT_NAT is not set +# CONFIG_NET_ACT_PEDIT is not set +# CONFIG_NET_ACT_POLICE is not set +# CONFIG_NET_ACT_SAMPLE is not set +# CONFIG_NET_ACT_SIMP is not set +# CONFIG_NET_ACT_SKBEDIT is not set +# CONFIG_NET_ACT_SKBMOD is not set +# CONFIG_NET_ACT_TUNNEL_KEY is not set +# CONFIG_NET_ACT_VLAN is not set +CONFIG_NET_CADENCE=y +# CONFIG_NET_CALXEDA_XGMAC is not set +CONFIG_NET_CLS=y +# CONFIG_NET_CLS_ACT is not set +# CONFIG_NET_CLS_BASIC is not set +# CONFIG_NET_CLS_BPF is not set +# CONFIG_NET_CLS_FLOW is not set +# CONFIG_NET_CLS_FLOWER is not set +# CONFIG_NET_CLS_FW is not set +CONFIG_NET_CLS_IND=y +# CONFIG_NET_CLS_MATCHALL is not set +# CONFIG_NET_CLS_ROUTE4 is not set +# CONFIG_NET_CLS_RSVP is not set +# CONFIG_NET_CLS_RSVP6 is not set +# CONFIG_NET_CLS_TCINDEX is not set +# CONFIG_NET_CLS_U32 is not set +CONFIG_NET_CORE=y +# CONFIG_NET_DEVLINK is not set +# CONFIG_NET_DROP_MONITOR is not set +# CONFIG_NET_DSA is not set +# CONFIG_NET_DSA_AR9331 is not set +# CONFIG_NET_DSA_BCM_SF2 is not set +# CONFIG_NET_DSA_LANTIQ_GSWIP is not set +# CONFIG_NET_DSA_LEGACY is not set +# CONFIG_NET_DSA_LOOP is not set +# CONFIG_NET_DSA_MICROCHIP_KSZ8795 is not set +# CONFIG_NET_DSA_MICROCHIP_KSZ9477 is not set +# CONFIG_NET_DSA_MSCC_FELIX is not set +# CONFIG_NET_DSA_MSCC_SEVILLE is not set +# CONFIG_NET_DSA_MT7530 is not set +# CONFIG_NET_DSA_MV88E6060 is not set +# CONFIG_NET_DSA_MV88E6123_61_65 is not set +# CONFIG_NET_DSA_MV88E6131 is not set +# CONFIG_NET_DSA_MV88E6171 is not set +# CONFIG_NET_DSA_MV88E6352 is not set +# CONFIG_NET_DSA_MV88E6XXX is not set +# CONFIG_NET_DSA_MV88E6XXX_NEED_PPU is not set +# CONFIG_NET_DSA_MV88E6XXX_PTP is not set +# CONFIG_NET_DSA_QCA8K is not set +# CONFIG_NET_DSA_REALTEK_SMI is not set +# CONFIG_NET_DSA_SJA1105 is not set +# CONFIG_NET_DSA_SMSC_LAN9303_I2C is not set +# CONFIG_NET_DSA_SMSC_LAN9303_MDIO is not set +# CONFIG_NET_DSA_TAG_8021Q is not set +# CONFIG_NET_DSA_TAG_AR9331 is not set +# CONFIG_NET_DSA_TAG_BRCM is not set +# CONFIG_NET_DSA_TAG_BRCM_LEGACY is not set +# CONFIG_NET_DSA_TAG_BRCM_PREPEND is not set +# CONFIG_NET_DSA_TAG_DSA is not set +# CONFIG_NET_DSA_TAG_EDSA is not set +# CONFIG_NET_DSA_TAG_GSWIP is not set +# CONFIG_NET_DSA_TAG_HELLCREEK is not set +# CONFIG_NET_DSA_TAG_KSZ is not set +# CONFIG_NET_DSA_TAG_LAN9303 is not set +# CONFIG_NET_DSA_TAG_MTK is not set +# CONFIG_NET_DSA_TAG_OCELOT is not set +# CONFIG_NET_DSA_TAG_OCELOT_8021Q is not set +# CONFIG_NET_DSA_TAG_QCA is not set +# CONFIG_NET_DSA_TAG_RTL4_A is not set +# CONFIG_NET_DSA_TAG_SJA1105 is not set +# CONFIG_NET_DSA_TAG_TRAILER is not set +# CONFIG_NET_DSA_TAG_XRS700X is not set +# CONFIG_NET_DSA_VITESSE_VSC73XX is not set +# CONFIG_NET_DSA_VITESSE_VSC73XX_PLATFORM is not set +# CONFIG_NET_DSA_VITESSE_VSC73XX_SPI is not set +# CONFIG_NET_DSA_XRS700X_I2C is not set +# CONFIG_NET_DSA_XRS700X_MDIO is not set +# CONFIG_NET_EMATCH is not set +# CONFIG_NET_EMATCH_CANID is not set +# CONFIG_NET_EMATCH_CMP is not set +# CONFIG_NET_EMATCH_IPT is not set +# CONFIG_NET_EMATCH_META is not set +# CONFIG_NET_EMATCH_NBYTE is not set +CONFIG_NET_EMATCH_STACK=32 +# CONFIG_NET_EMATCH_TEXT is not set +# CONFIG_NET_EMATCH_U32 is not set +# CONFIG_NET_FAILOVER is not set +# CONFIG_NET_FC is not set +# CONFIG_NET_FOU is not set +# CONFIG_NET_FOU_IP_TUNNELS is not set +# CONFIG_NET_IFE is not set +# CONFIG_NET_IPGRE is not set +CONFIG_NET_IPGRE_BROADCAST=y +# CONFIG_NET_IPGRE_DEMUX is not set +# CONFIG_NET_IPIP is not set +# CONFIG_NET_IPVTI is not set +# CONFIG_NET_IP_TUNNEL is not set +# CONFIG_NET_KEY is not set +# CONFIG_NET_KEY_MIGRATE is not set +# CONFIG_NET_L3_MASTER_DEV is not set +# CONFIG_NET_MEDIATEK_STAR_EMAC is not set +# CONFIG_NET_MPLS_GSO is not set +# CONFIG_NET_NCSI is not set +# CONFIG_NET_NSH is not set +# CONFIG_NET_PACKET_ENGINE is not set +# CONFIG_NET_PKTGEN is not set +# CONFIG_NET_POLL_CONTROLLER is not set +# CONFIG_NET_PTP_CLASSIFY is not set +CONFIG_NET_RX_BUSY_POLL=y +# CONFIG_NET_SB1000 is not set +CONFIG_NET_SCHED=y +# CONFIG_NET_SCH_ATM is not set +# CONFIG_NET_SCH_CAKE is not set +# CONFIG_NET_SCH_CBQ is not set +# CONFIG_NET_SCH_CBS is not set +# CONFIG_NET_SCH_CHOKE is not set +# CONFIG_NET_SCH_CODEL is not set +CONFIG_NET_SCH_DEFAULT=y +# CONFIG_NET_SCH_DRR is not set +# CONFIG_NET_SCH_DSMARK is not set +# CONFIG_NET_SCH_ETF is not set +# CONFIG_NET_SCH_ETS is not set +CONFIG_NET_SCH_FIFO=y +# CONFIG_NET_SCH_FQ is not set +CONFIG_NET_SCH_FQ_CODEL=y +# CONFIG_NET_SCH_FQ_PIE is not set +# CONFIG_NET_SCH_GRED is not set +# CONFIG_NET_SCH_HFSC is not set +# CONFIG_NET_SCH_HHF is not set +# CONFIG_NET_SCH_HTB is not set +# CONFIG_NET_SCH_INGRESS is not set +# CONFIG_NET_SCH_MQPRIO is not set +# CONFIG_NET_SCH_MULTIQ is not set +# CONFIG_NET_SCH_NETEM is not set +# CONFIG_NET_SCH_PIE is not set +# CONFIG_NET_SCH_PLUG is not set +# CONFIG_NET_SCH_PRIO is not set +# CONFIG_NET_SCH_QFQ is not set +# CONFIG_NET_SCH_RED is not set +# CONFIG_NET_SCH_SFB is not set +# CONFIG_NET_SCH_SFQ is not set +# CONFIG_NET_SCH_SKBPRIO is not set +# CONFIG_NET_SCH_TAPRIO is not set +# CONFIG_NET_SCH_TBF is not set +# CONFIG_NET_SCH_TEQL is not set +# CONFIG_NET_SCTPPROBE is not set +# CONFIG_NET_SELFTESTS is not set +CONFIG_NET_SOCK_MSG=y +# CONFIG_NET_SWITCHDEV is not set +# CONFIG_NET_TCPPROBE is not set +# CONFIG_NET_TC_SKB_EXT is not set +# CONFIG_NET_TEAM is not set +# CONFIG_NET_TULIP is not set +# CONFIG_NET_UDP_TUNNEL is not set +CONFIG_NET_VENDOR_3COM=y +CONFIG_NET_VENDOR_8390=y +CONFIG_NET_VENDOR_ADAPTEC=y +CONFIG_NET_VENDOR_AGERE=y +CONFIG_NET_VENDOR_ALACRITECH=y +CONFIG_NET_VENDOR_ALTEON=y +CONFIG_NET_VENDOR_AMAZON=y +CONFIG_NET_VENDOR_AMD=y +CONFIG_NET_VENDOR_AQUANTIA=y +CONFIG_NET_VENDOR_ARC=y +CONFIG_NET_VENDOR_ATHEROS=y +CONFIG_NET_VENDOR_AURORA=y +CONFIG_NET_VENDOR_BROADCOM=y +CONFIG_NET_VENDOR_BROCADE=y +CONFIG_NET_VENDOR_CADENCE=y +CONFIG_NET_VENDOR_CAVIUM=y +CONFIG_NET_VENDOR_CHELSIO=y +CONFIG_NET_VENDOR_CIRRUS=y +CONFIG_NET_VENDOR_CISCO=y +CONFIG_NET_VENDOR_CORTINA=y +CONFIG_NET_VENDOR_DEC=y +CONFIG_NET_VENDOR_DLINK=y +CONFIG_NET_VENDOR_EMULEX=y +CONFIG_NET_VENDOR_EXAR=y +CONFIG_NET_VENDOR_EZCHIP=y +CONFIG_NET_VENDOR_FARADAY=y +CONFIG_NET_VENDOR_FREESCALE=y +CONFIG_NET_VENDOR_FUJITSU=y +CONFIG_NET_VENDOR_GOOGLE=y +CONFIG_NET_VENDOR_HISILICON=y +CONFIG_NET_VENDOR_HP=y +CONFIG_NET_VENDOR_HUAWEI=y +CONFIG_NET_VENDOR_I825XX=y +CONFIG_NET_VENDOR_IBM=y +CONFIG_NET_VENDOR_INTEL=y +# CONFIG_NET_VENDOR_LITEX is not set +CONFIG_NET_VENDOR_MARVELL=y +CONFIG_NET_VENDOR_MELLANOX=y +CONFIG_NET_VENDOR_MICREL=y +CONFIG_NET_VENDOR_MICROCHIP=y +CONFIG_NET_VENDOR_MICROSEMI=y +# CONFIG_NET_VENDOR_MICROSOFT is not set +CONFIG_NET_VENDOR_MYRI=y +CONFIG_NET_VENDOR_NATSEMI=y +CONFIG_NET_VENDOR_NETERION=y +CONFIG_NET_VENDOR_NETRONOME=y +CONFIG_NET_VENDOR_NI=y +CONFIG_NET_VENDOR_NVIDIA=y +CONFIG_NET_VENDOR_OKI=y +CONFIG_NET_VENDOR_PACKET_ENGINES=y +CONFIG_NET_VENDOR_PENSANDO=y +CONFIG_NET_VENDOR_QLOGIC=y +CONFIG_NET_VENDOR_QUALCOMM=y +CONFIG_NET_VENDOR_RDC=y +CONFIG_NET_VENDOR_REALTEK=y +CONFIG_NET_VENDOR_RENESAS=y +CONFIG_NET_VENDOR_ROCKER=y +CONFIG_NET_VENDOR_SAMSUNG=y +CONFIG_NET_VENDOR_SEEQ=y +CONFIG_NET_VENDOR_SILAN=y +CONFIG_NET_VENDOR_SIS=y +CONFIG_NET_VENDOR_SMSC=y +CONFIG_NET_VENDOR_SOCIONEXT=y +CONFIG_NET_VENDOR_SOLARFLARE=y +CONFIG_NET_VENDOR_STMICRO=y +CONFIG_NET_VENDOR_SUN=y +CONFIG_NET_VENDOR_SYNOPSYS=y +CONFIG_NET_VENDOR_TEHUTI=y +CONFIG_NET_VENDOR_TI=y +CONFIG_NET_VENDOR_TOSHIBA=y +CONFIG_NET_VENDOR_VIA=y +CONFIG_NET_VENDOR_WIZNET=y +CONFIG_NET_VENDOR_XILINX=y +CONFIG_NET_VENDOR_XIRCOM=y +# CONFIG_NET_VRF is not set +# CONFIG_NET_XGENE is not set +CONFIG_NEW_LEDS=y +# CONFIG_NFC is not set +# CONFIG_NFP is not set +# CONFIG_NFSD is not set +# CONFIG_NFSD_V2_ACL is not set +CONFIG_NFSD_V3=y +# CONFIG_NFSD_V3_ACL is not set +# CONFIG_NFSD_V4 is not set +# CONFIG_NFS_ACL_SUPPORT is not set +CONFIG_NFS_COMMON=y +# CONFIG_NFS_DISABLE_UDP_SUPPORT is not set +# CONFIG_NFS_FS is not set +# CONFIG_NFS_FSCACHE is not set +# CONFIG_NFS_SWAP is not set +# CONFIG_NFS_V2 is not set +CONFIG_NFS_V3=y +# CONFIG_NFS_V3_ACL is not set +# CONFIG_NFS_V4 is not set +# CONFIG_NFS_V4_1 is not set +# CONFIG_NFTL is not set +# CONFIG_NFT_BRIDGE_META is not set +# CONFIG_NFT_BRIDGE_REJECT is not set +# CONFIG_NFT_CONNLIMIT is not set +# CONFIG_NFT_DUP_IPV4 is not set +# CONFIG_NFT_DUP_IPV6 is not set +# CONFIG_NFT_FIB_IPV4 is not set +# CONFIG_NFT_FIB_IPV6 is not set +# CONFIG_NFT_FIB_NETDEV is not set +# CONFIG_NFT_FLOW_OFFLOAD is not set +# CONFIG_NFT_OBJREF is not set +# CONFIG_NFT_OSF is not set +# CONFIG_NFT_REJECT_NETDEV is not set +# CONFIG_NFT_RT is not set +# CONFIG_NFT_SET_BITMAP is not set +# CONFIG_NFT_SOCKET is not set +# CONFIG_NFT_SYNPROXY is not set +# CONFIG_NFT_TPROXY is not set +# CONFIG_NFT_TUNNEL is not set +# CONFIG_NFT_XFRM is not set +# CONFIG_NF_CONNTRACK is not set +# CONFIG_NF_CONNTRACK_AMANDA is not set +# CONFIG_NF_CONNTRACK_BRIDGE is not set +# CONFIG_NF_CONNTRACK_EVENTS is not set +# CONFIG_NF_CONNTRACK_FTP is not set +# CONFIG_NF_CONNTRACK_H323 is not set +# CONFIG_NF_CONNTRACK_IRC is not set +# CONFIG_NF_CONNTRACK_LABELS is not set +# CONFIG_NF_CONNTRACK_MARK is not set +# CONFIG_NF_CONNTRACK_NETBIOS_NS is not set +# CONFIG_NF_CONNTRACK_PPTP is not set +CONFIG_NF_CONNTRACK_PROCFS=y +# CONFIG_NF_CONNTRACK_PROC_COMPAT is not set +# CONFIG_NF_CONNTRACK_SANE is not set +# CONFIG_NF_CONNTRACK_SECMARK is not set +# CONFIG_NF_CONNTRACK_SIP is not set +# CONFIG_NF_CONNTRACK_SNMP is not set +# CONFIG_NF_CONNTRACK_TFTP is not set +# CONFIG_NF_CONNTRACK_TIMEOUT is not set +# CONFIG_NF_CONNTRACK_TIMESTAMP is not set +# CONFIG_NF_CONNTRACK_ZONES is not set +# CONFIG_NF_CT_NETLINK is not set +# CONFIG_NF_CT_NETLINK_HELPER is not set +# CONFIG_NF_CT_NETLINK_TIMEOUT is not set +# CONFIG_NF_CT_PROTO_DCCP is not set +# CONFIG_NF_CT_PROTO_GRE is not set +# CONFIG_NF_CT_PROTO_SCTP is not set +# CONFIG_NF_CT_PROTO_UDPLITE is not set +# CONFIG_NF_DEFRAG_IPV4 is not set +# CONFIG_NF_DUP_IPV4 is not set +# CONFIG_NF_DUP_IPV6 is not set +# CONFIG_NF_FLOW_TABLE is not set +# CONFIG_NF_LOG_ARP is not set +# CONFIG_NF_LOG_BRIDGE is not set +# CONFIG_NF_LOG_IPV4 is not set +# CONFIG_NF_LOG_NETDEV is not set +# CONFIG_NF_LOG_SYSLOG is not set +# CONFIG_NF_NAT is not set +# CONFIG_NF_NAT_AMANDA is not set +# CONFIG_NF_NAT_FTP is not set +# CONFIG_NF_NAT_H323 is not set +# CONFIG_NF_NAT_IRC is not set +# CONFIG_NF_NAT_NEEDED is not set +# CONFIG_NF_NAT_PPTP is not set +# CONFIG_NF_NAT_PROTO_GRE is not set +# CONFIG_NF_NAT_SIP is not set +# CONFIG_NF_NAT_SNMP_BASIC is not set +# CONFIG_NF_NAT_TFTP is not set +# CONFIG_NF_REJECT_IPV4 is not set +# CONFIG_NF_REJECT_IPV6 is not set +# CONFIG_NF_SOCKET_IPV4 is not set +# CONFIG_NF_SOCKET_IPV6 is not set +# CONFIG_NF_TABLES is not set +CONFIG_NF_TABLES_ARP=y +CONFIG_NF_TABLES_BRIDGE=y +CONFIG_NF_TABLES_INET=y +CONFIG_NF_TABLES_IPV4=y +CONFIG_NF_TABLES_IPV6=y +CONFIG_NF_TABLES_NETDEV=y +# CONFIG_NF_TABLES_SET is not set +# CONFIG_NF_TPROXY_IPV4 is not set +# CONFIG_NF_TPROXY_IPV6 is not set +# CONFIG_NI65 is not set +# CONFIG_NI903X_WDT is not set +# CONFIG_NIC7018_WDT is not set +# CONFIG_NILFS2_FS is not set +# CONFIG_NIU is not set +# CONFIG_NI_XGE_MANAGEMENT_ENET is not set +CONFIG_NLATTR=y +# CONFIG_NLMON is not set +# CONFIG_NLM_XLP_BOARD is not set +# CONFIG_NLM_XLR_BOARD is not set +# CONFIG_NLS is not set +# CONFIG_NLS_ASCII is not set +# CONFIG_NLS_CODEPAGE_1250 is not set +# CONFIG_NLS_CODEPAGE_1251 is not set +# CONFIG_NLS_CODEPAGE_437 is not set +# CONFIG_NLS_CODEPAGE_737 is not set +# CONFIG_NLS_CODEPAGE_775 is not set +# CONFIG_NLS_CODEPAGE_850 is not set +# CONFIG_NLS_CODEPAGE_852 is not set +# CONFIG_NLS_CODEPAGE_855 is not set +# CONFIG_NLS_CODEPAGE_857 is not set +# CONFIG_NLS_CODEPAGE_860 is not set +# CONFIG_NLS_CODEPAGE_861 is not set +# CONFIG_NLS_CODEPAGE_862 is not set +# CONFIG_NLS_CODEPAGE_863 is not set +# CONFIG_NLS_CODEPAGE_864 is not set +# CONFIG_NLS_CODEPAGE_865 is not set +# CONFIG_NLS_CODEPAGE_866 is not set +# CONFIG_NLS_CODEPAGE_869 is not set +# CONFIG_NLS_CODEPAGE_874 is not set +# CONFIG_NLS_CODEPAGE_932 is not set +# CONFIG_NLS_CODEPAGE_936 is not set +# CONFIG_NLS_CODEPAGE_949 is not set +# CONFIG_NLS_CODEPAGE_950 is not set +CONFIG_NLS_DEFAULT="iso8859-1" +# CONFIG_NLS_ISO8859_1 is not set +# CONFIG_NLS_ISO8859_13 is not set +# CONFIG_NLS_ISO8859_14 is not set +# CONFIG_NLS_ISO8859_15 is not set +# CONFIG_NLS_ISO8859_2 is not set +# CONFIG_NLS_ISO8859_3 is not set +# CONFIG_NLS_ISO8859_4 is not set +# CONFIG_NLS_ISO8859_5 is not set +# CONFIG_NLS_ISO8859_6 is not set +# CONFIG_NLS_ISO8859_7 is not set +# CONFIG_NLS_ISO8859_8 is not set +# CONFIG_NLS_ISO8859_9 is not set +# CONFIG_NLS_KOI8_R is not set +# CONFIG_NLS_KOI8_U is not set +# CONFIG_NLS_MAC_CELTIC is not set +# CONFIG_NLS_MAC_CENTEURO is not set +# CONFIG_NLS_MAC_CROATIAN is not set +# CONFIG_NLS_MAC_CYRILLIC is not set +# CONFIG_NLS_MAC_GAELIC is not set +# CONFIG_NLS_MAC_GREEK is not set +# CONFIG_NLS_MAC_ICELAND is not set +# CONFIG_NLS_MAC_INUIT is not set +# CONFIG_NLS_MAC_ROMAN is not set +# CONFIG_NLS_MAC_ROMANIAN is not set +# CONFIG_NLS_MAC_TURKISH is not set +# CONFIG_NLS_UTF8 is not set +CONFIG_NMI_LOG_BUF_SHIFT=13 +# CONFIG_NOA1305 is not set +# CONFIG_NOP_USB_XCEIV is not set +# CONFIG_NORTEL_HERMES is not set +# CONFIG_NOTIFIER_ERROR_INJECTION is not set +# CONFIG_NOUVEAU_LEGACY_CTX_SUPPORT is not set +# CONFIG_NOZOMI is not set +# CONFIG_NO_BOOTMEM is not set +# CONFIG_NO_HZ is not set +# CONFIG_NO_HZ_FULL is not set +# CONFIG_NO_HZ_IDLE is not set +CONFIG_NR_LRU_GENS=7 +# CONFIG_NS83820 is not set +# CONFIG_NTB is not set +# CONFIG_NTFS3_64BIT_CLUSTER is not set +# CONFIG_NTFS3_FS is not set +# CONFIG_NTFS3_FS_POSIX_ACL is not set +# CONFIG_NTFS3_LZX_XPRESS is not set +# CONFIG_NTFS_DEBUG is not set +# CONFIG_NTFS_FS is not set +# CONFIG_NTFS_RW is not set +# CONFIG_NTP_PPS is not set +# CONFIG_NULL_TTY is not set +# CONFIG_NUMA is not set +# CONFIG_NVIDIA_CARMEL_CNP_ERRATUM is not set +# CONFIG_NVM is not set +# CONFIG_NVMEM is not set +# CONFIG_NVMEM_BCM_OCOTP is not set +# CONFIG_NVMEM_IMX_OCOTP is not set +# CONFIG_NVMEM_REBOOT_MODE is not set +# CONFIG_NVMEM_RMEM is not set +# CONFIG_NVMEM_SYSFS is not set +# CONFIG_NVMEM_U_BOOT_ENV is not set +# CONFIG_NVME_FC is not set +# CONFIG_NVME_TARGET is not set +# CONFIG_NVME_TCP is not set +# CONFIG_NVRAM is not set +# CONFIG_NV_TCO is not set +# CONFIG_NXP_C45_TJA11XX_PHY is not set +# CONFIG_NXP_STB220 is not set +# CONFIG_NXP_STB225 is not set +# CONFIG_NXP_TJA11XX_PHY is not set +# CONFIG_N_GSM is not set +# CONFIG_OABI_COMPAT is not set +# CONFIG_OBS600 is not set +# CONFIG_OCFS2_FS is not set +# CONFIG_OCTEONTX2_AF is not set +# CONFIG_OCTEONTX2_PF is not set +# CONFIG_OF_OVERLAY is not set +CONFIG_OF_RESERVED_MEM=y +# CONFIG_OF_UNITTEST is not set +# CONFIG_OID_REGISTRY is not set +# CONFIG_OMAP2_DSS_DEBUG is not set +# CONFIG_OMAP2_DSS_DEBUGFS is not set +# CONFIG_OMAP2_DSS_SDI is not set +# CONFIG_OMAP_OCP2SCP is not set +# CONFIG_OMAP_USB2 is not set +# CONFIG_OMFS_FS is not set +# CONFIG_OPENVSWITCH is not set +# CONFIG_OPROFILE is not set +# CONFIG_OPROFILE_EVENT_MULTIPLEX is not set +# CONFIG_OPT3001 is not set +CONFIG_OPTIMIZE_INLINING=y +# CONFIG_ORANGEFS_FS is not set +# CONFIG_ORION_WATCHDOG is not set +# CONFIG_OSF_PARTITION is not set +# CONFIG_OSNOISE_TRACER is not set +CONFIG_OVERLAY_FS=y +# CONFIG_OVERLAY_FS_INDEX is not set +# CONFIG_OVERLAY_FS_METACOPY is not set +CONFIG_OVERLAY_FS_REDIRECT_ALWAYS_FOLLOW=y +# CONFIG_OVERLAY_FS_REDIRECT_DIR is not set +CONFIG_OVERLAY_FS_XINO_AUTO=y +# CONFIG_OWL_LOADER is not set +# CONFIG_P54_COMMON is not set +# CONFIG_PA12203001 is not set +CONFIG_PACKET=y +# CONFIG_PACKET_DIAG is not set +# CONFIG_PACKING is not set +# CONFIG_PAGE_EXTENSION is not set +# CONFIG_PAGE_OWNER is not set +# CONFIG_PAGE_POISONING is not set +# CONFIG_PAGE_POOL_STATS is not set +# CONFIG_PAGE_REPORTING is not set +# CONFIG_PAGE_SIZE_16KB is not set +# CONFIG_PAGE_SIZE_32KB is not set +CONFIG_PAGE_SIZE_4KB=y +# CONFIG_PAGE_SIZE_64KB is not set +# CONFIG_PAGE_SIZE_8KB is not set +# CONFIG_PALMAS_GPADC is not set +# CONFIG_PANASONIC_LAPTOP is not set +# CONFIG_PANEL is not set +CONFIG_PANIC_ON_OOPS=y +CONFIG_PANIC_ON_OOPS_VALUE=1 +CONFIG_PANIC_TIMEOUT=1 +# CONFIG_PANTHERLORD_FF is not set +# CONFIG_PARAVIRT is not set +# CONFIG_PARAVIRT_TIME_ACCOUNTING is not set +# CONFIG_PARPORT is not set +# CONFIG_PARPORT_1284 is not set +# CONFIG_PARPORT_AX88796 is not set +# CONFIG_PARPORT_GSC is not set +# CONFIG_PARPORT_PC is not set +CONFIG_PARTITION_ADVANCED=y +# CONFIG_PATA_ALI is not set +# CONFIG_PATA_AMD is not set +# CONFIG_PATA_ARASAN_CF is not set +# CONFIG_PATA_ARTOP is not set +# CONFIG_PATA_ATIIXP is not set +# CONFIG_PATA_ATP867X is not set +# CONFIG_PATA_CMD640_PCI is not set +# CONFIG_PATA_CMD64X is not set +# CONFIG_PATA_CS5520 is not set +# CONFIG_PATA_CS5530 is not set +# CONFIG_PATA_CS5535 is not set +# CONFIG_PATA_CS5536 is not set +# CONFIG_PATA_CYPRESS is not set +# CONFIG_PATA_EFAR is not set +# CONFIG_PATA_HPT366 is not set +# CONFIG_PATA_HPT37X is not set +# CONFIG_PATA_HPT3X2N is not set +# CONFIG_PATA_HPT3X3 is not set +# CONFIG_PATA_IMX is not set +# CONFIG_PATA_ISAPNP is not set +# CONFIG_PATA_IT8213 is not set +# CONFIG_PATA_IT821X is not set +# CONFIG_PATA_JMICRON is not set +# CONFIG_PATA_LEGACY is not set +# CONFIG_PATA_MARVELL is not set +# CONFIG_PATA_MPIIX is not set +# CONFIG_PATA_NETCELL is not set +# CONFIG_PATA_NINJA32 is not set +# CONFIG_PATA_NS87410 is not set +# CONFIG_PATA_NS87415 is not set +# CONFIG_PATA_OCTEON_CF is not set +# CONFIG_PATA_OF_PLATFORM is not set +# CONFIG_PATA_OLDPIIX is not set +# CONFIG_PATA_OPTI is not set +# CONFIG_PATA_OPTIDMA is not set +# CONFIG_PATA_PCMCIA is not set +# CONFIG_PATA_PDC2027X is not set +# CONFIG_PATA_PDC_OLD is not set +# CONFIG_PATA_PLATFORM is not set +# CONFIG_PATA_QDI is not set +# CONFIG_PATA_RADISYS is not set +# CONFIG_PATA_RDC is not set +# CONFIG_PATA_RZ1000 is not set +# CONFIG_PATA_SC1200 is not set +# CONFIG_PATA_SCH is not set +# CONFIG_PATA_SERVERWORKS is not set +# CONFIG_PATA_SIL680 is not set +# CONFIG_PATA_SIS is not set +# CONFIG_PATA_TOSHIBA is not set +# CONFIG_PATA_TRIFLEX is not set +# CONFIG_PATA_VIA is not set +# CONFIG_PATA_WINBOND is not set +# CONFIG_PATA_WINBOND_VLB is not set +# CONFIG_PC104 is not set +# CONFIG_PC300TOO is not set +# CONFIG_PCCARD is not set +# CONFIG_PCH_DMA is not set +# CONFIG_PCH_GBE is not set +# CONFIG_PCH_PHUB is not set +# CONFIG_PCI is not set +# CONFIG_PCI200SYN is not set +# CONFIG_PCIEAER is not set +# CONFIG_PCIEAER_INJECT is not set +# CONFIG_PCIEASPM is not set +# CONFIG_PCIEPORTBUS is not set +# CONFIG_PCIE_AL is not set +# CONFIG_PCIE_ALTERA is not set +# CONFIG_PCIE_ARMADA_8K is not set +CONFIG_PCIE_BUS_DEFAULT=y +# CONFIG_PCIE_BUS_PEER2PEER is not set +# CONFIG_PCIE_BUS_PERFORMANCE is not set +# CONFIG_PCIE_BUS_SAFE is not set +# CONFIG_PCIE_BUS_TUNE_OFF is not set +# CONFIG_PCIE_BW is not set +# CONFIG_PCIE_CADENCE_HOST is not set +# CONFIG_PCIE_CADENCE_PLAT_HOST is not set +# CONFIG_PCIE_DPC is not set +# CONFIG_PCIE_DW_PLAT is not set +# CONFIG_PCIE_DW_PLAT_HOST is not set +# CONFIG_PCIE_ECRC is not set +# CONFIG_PCIE_IPROC is not set +# CONFIG_PCIE_KIRIN is not set +# CONFIG_PCIE_LAYERSCAPE_GEN4 is not set +# CONFIG_PCIE_MEDIATEK_GEN3 is not set +# CONFIG_PCIE_MICROCHIP_HOST is not set +# CONFIG_PCIE_PTM is not set +# CONFIG_PCIE_XILINX is not set +# CONFIG_PCIPCWATCHDOG is not set +# CONFIG_PCI_ATMEL is not set +# CONFIG_PCI_CNB20LE_QUIRK is not set +# CONFIG_PCI_DEBUG is not set +# CONFIG_PCI_DISABLE_COMMON_QUIRKS is not set +# CONFIG_PCI_ENDPOINT is not set +# CONFIG_PCI_ENDPOINT_TEST is not set +# CONFIG_PCI_FTPCI100 is not set +# CONFIG_PCI_HERMES is not set +# CONFIG_PCI_HISI is not set +# CONFIG_PCI_HOST_GENERIC is not set +# CONFIG_PCI_HOST_THUNDER_ECAM is not set +# CONFIG_PCI_HOST_THUNDER_PEM is not set +# CONFIG_PCI_IOV is not set +# CONFIG_PCI_J721E_HOST is not set +# CONFIG_PCI_LAYERSCAPE is not set +# CONFIG_PCI_MESON is not set +# CONFIG_PCI_MSI is not set +# CONFIG_PCI_PASID is not set +# CONFIG_PCI_PF_STUB is not set +# CONFIG_PCI_PRI is not set +CONFIG_PCI_QUIRKS=y +# CONFIG_PCI_REALLOC_ENABLE_AUTO is not set +# CONFIG_PCI_STUB is not set +# CONFIG_PCI_SW_SWITCHTEC is not set +CONFIG_PCI_SYSCALL=y +# CONFIG_PCI_V3_SEMI is not set +# CONFIG_PCI_XGENE is not set +# CONFIG_PCMCIA is not set +# CONFIG_PCMCIA_3C574 is not set +# CONFIG_PCMCIA_3C589 is not set +# CONFIG_PCMCIA_AHA152X is not set +# CONFIG_PCMCIA_ATMEL is not set +# CONFIG_PCMCIA_AXNET is not set +# CONFIG_PCMCIA_DEBUG is not set +# CONFIG_PCMCIA_FDOMAIN is not set +# CONFIG_PCMCIA_FMVJ18X is not set +# CONFIG_PCMCIA_HERMES is not set +# CONFIG_PCMCIA_LOAD_CIS is not set +# CONFIG_PCMCIA_NINJA_SCSI is not set +# CONFIG_PCMCIA_NMCLAN is not set +# CONFIG_PCMCIA_PCNET is not set +# CONFIG_PCMCIA_QLOGIC is not set +# CONFIG_PCMCIA_RAYCS is not set +# CONFIG_PCMCIA_SMC91C92 is not set +# CONFIG_PCMCIA_SPECTRUM is not set +# CONFIG_PCMCIA_SYM53C500 is not set +# CONFIG_PCMCIA_WL3501 is not set +# CONFIG_PCMCIA_XIRC2PS is not set +# CONFIG_PCMCIA_XIRCOM is not set +# CONFIG_PCNET32 is not set +# CONFIG_PCPU_DEV_REFCNT is not set +# CONFIG_PCSPKR_PLATFORM is not set +# CONFIG_PCS_XPCS is not set +# CONFIG_PD6729 is not set +# CONFIG_PDA_POWER is not set +# CONFIG_PDC_ADMA is not set +# CONFIG_PERCPU_STATS is not set +# CONFIG_PERCPU_TEST is not set +# CONFIG_PERF_EVENTS is not set +# CONFIG_PERF_EVENTS_AMD_POWER is not set +# CONFIG_PERSISTENT_KEYRINGS is not set +# CONFIG_PHANTOM is not set +# CONFIG_PHONET is not set +# CONFIG_PHYLIB is not set +# CONFIG_PHYS_ADDR_T_64BIT is not set +# CONFIG_PHY_CADENCE_DP is not set +# CONFIG_PHY_CADENCE_DPHY is not set +# CONFIG_PHY_CADENCE_SALVO is not set +# CONFIG_PHY_CADENCE_SIERRA is not set +# CONFIG_PHY_CADENCE_TORRENT is not set +# CONFIG_PHY_CAN_TRANSCEIVER is not set +# CONFIG_PHY_CPCAP_USB is not set +# CONFIG_PHY_EXYNOS_DP_VIDEO is not set +# CONFIG_PHY_EXYNOS_MIPI_VIDEO is not set +# CONFIG_PHY_FSL_IMX8MQ_USB is not set +# CONFIG_PHY_INGENIC_USB is not set +# CONFIG_PHY_INTEL_KEEMBAY_EMMC is not set +# CONFIG_PHY_MAPPHONE_MDM6600 is not set +# CONFIG_PHY_MIXEL_MIPI_DPHY is not set +# CONFIG_PHY_MTK_HDMI is not set +# CONFIG_PHY_MTK_MIPI_DSI is not set +# CONFIG_PHY_MVEBU_CP110_UTMI is not set +# CONFIG_PHY_OCELOT_SERDES is not set +# CONFIG_PHY_PISTACHIO_USB is not set +# CONFIG_PHY_PXA_28NM_HSIC is not set +# CONFIG_PHY_PXA_28NM_USB2 is not set +# CONFIG_PHY_QCOM_DWC3 is not set +# CONFIG_PHY_QCOM_USB_HS is not set +# CONFIG_PHY_QCOM_USB_HSIC is not set +# CONFIG_PHY_SAMSUNG_USB2 is not set +# CONFIG_PHY_TUSB1210 is not set +# CONFIG_PHY_XGENE is not set +# CONFIG_PI433 is not set +# CONFIG_PID_IN_CONTEXTIDR is not set +# CONFIG_PID_NS is not set +CONFIG_PINCONF=y +# CONFIG_PINCTRL is not set +# CONFIG_PINCTRL_AMD is not set +# CONFIG_PINCTRL_AXP209 is not set +# CONFIG_PINCTRL_CEDARFORK is not set +# CONFIG_PINCTRL_EXYNOS is not set +# CONFIG_PINCTRL_EXYNOS5440 is not set +# CONFIG_PINCTRL_ICELAKE is not set +# CONFIG_PINCTRL_INGENIC is not set +# CONFIG_PINCTRL_LPASS_LPI is not set +# CONFIG_PINCTRL_MCP23S08 is not set +# CONFIG_PINCTRL_MDM9607 is not set +# CONFIG_PINCTRL_MICROCHIP_SGPIO is not set +# CONFIG_PINCTRL_MSM8953 is not set +# CONFIG_PINCTRL_MSM8X74 is not set +# CONFIG_PINCTRL_MT6779 is not set +# CONFIG_PINCTRL_MT8167 is not set +# CONFIG_PINCTRL_MT8192 is not set +# CONFIG_PINCTRL_MT8195 is not set +# CONFIG_PINCTRL_MT8365 is not set +# CONFIG_PINCTRL_MTK_V2 is not set +# CONFIG_PINCTRL_OCELOT is not set +# CONFIG_PINCTRL_PISTACHIO is not set +# CONFIG_PINCTRL_SC7280 is not set +# CONFIG_PINCTRL_SC8180X is not set +# CONFIG_PINCTRL_SDX55 is not set +CONFIG_PINCTRL_SINGLE=y +# CONFIG_PINCTRL_SM6115 is not set +# CONFIG_PINCTRL_SM6125 is not set +# CONFIG_PINCTRL_SM8350 is not set +# CONFIG_PINCTRL_STMFX is not set +# CONFIG_PINCTRL_SX150X is not set +# CONFIG_PING is not set +CONFIG_PINMUX=y +# CONFIG_PKCS7_MESSAGE_PARSER is not set +# CONFIG_PL310_ERRATA_588369 is not set +# CONFIG_PL310_ERRATA_727915 is not set +# CONFIG_PL310_ERRATA_753970 is not set +# CONFIG_PL310_ERRATA_769419 is not set +# CONFIG_PL320_MBOX is not set +# CONFIG_PL330_DMA is not set +# CONFIG_PLATFORM_MHU is not set +# CONFIG_PLAT_SPEAR is not set +# CONFIG_PLIP is not set +# CONFIG_PLX_DMA is not set +# CONFIG_PLX_HERMES is not set +# CONFIG_PM is not set +# CONFIG_PMBUS is not set +# CONFIG_PMC_MSP is not set +# CONFIG_PMIC_ADP5520 is not set +# CONFIG_PMIC_DA903X is not set +# CONFIG_PMS7003 is not set +# CONFIG_PM_AUTOSLEEP is not set +# CONFIG_PM_DEBUG is not set +# CONFIG_PM_DEVFREQ is not set +# CONFIG_PM_WAKELOCKS is not set +# CONFIG_POSIX_MQUEUE is not set +CONFIG_POSIX_TIMERS=y +# CONFIG_POWERCAP is not set +# CONFIG_POWER_AVS is not set +# CONFIG_POWER_RESET is not set +# CONFIG_POWER_RESET_BRCMKONA is not set +# CONFIG_POWER_RESET_BRCMSTB is not set +# CONFIG_POWER_RESET_GPIO is not set +# CONFIG_POWER_RESET_GPIO_RESTART is not set +# CONFIG_POWER_RESET_LINKSTATION is not set +# CONFIG_POWER_RESET_LTC2952 is not set +# CONFIG_POWER_RESET_PIIX4_POWEROFF is not set +# CONFIG_POWER_RESET_QNAP is not set +# CONFIG_POWER_RESET_REGULATOR is not set +# CONFIG_POWER_RESET_RESTART is not set +# CONFIG_POWER_RESET_SYSCON is not set +# CONFIG_POWER_RESET_SYSCON_POWEROFF is not set +# CONFIG_POWER_RESET_VERSATILE is not set +# CONFIG_POWER_RESET_XGENE is not set +# CONFIG_POWER_SUPPLY is not set +# CONFIG_POWER_SUPPLY_DEBUG is not set +# CONFIG_POWER_SUPPLY_HWMON is not set +# CONFIG_PPC4xx_GPIO is not set +# CONFIG_PPC_16K_PAGES is not set +# CONFIG_PPC_256K_PAGES is not set +CONFIG_PPC_4K_PAGES=y +# CONFIG_PPC_64K_PAGES is not set +# CONFIG_PPC_DISABLE_WERROR is not set +# CONFIG_PPC_EMULATED_STATS is not set +# CONFIG_PPC_EPAPR_HV_BYTECHAN is not set +# CONFIG_PPP is not set +# CONFIG_PPPOATM is not set +# CONFIG_PPPOE is not set +# CONFIG_PPPOL2TP is not set +# CONFIG_PPP_ASYNC is not set +# CONFIG_PPP_BSDCOMP is not set +# CONFIG_PPP_DEFLATE is not set +CONFIG_PPP_FILTER=y +# CONFIG_PPP_MPPE is not set +CONFIG_PPP_MULTILINK=y +# CONFIG_PPP_SYNC_TTY is not set +# CONFIG_PPS is not set +# CONFIG_PPS_CLIENT_GPIO is not set +# CONFIG_PPS_CLIENT_KTIMER is not set +# CONFIG_PPS_CLIENT_LDISC is not set +# CONFIG_PPS_CLIENT_PARPORT is not set +# CONFIG_PPS_DEBUG is not set +# CONFIG_PPTP is not set +# CONFIG_PREEMPT is not set +# CONFIG_PREEMPTIRQ_DELAY_TEST is not set +# CONFIG_PREEMPTIRQ_EVENTS is not set +CONFIG_PREEMPT_NONE=y +# CONFIG_PREEMPT_TRACER is not set +# CONFIG_PREEMPT_VOLUNTARY is not set +# CONFIG_PRESTERA is not set +CONFIG_PREVENT_FIRMWARE_BUILD=y +# CONFIG_PRIME_NUMBERS is not set +CONFIG_PRINTK=y +# CONFIG_PRINTK_CALLER is not set +# CONFIG_PRINTK_INDEX is not set +CONFIG_PRINTK_NMI=y +CONFIG_PRINTK_SAFE_LOG_BUF_SHIFT=13 +# CONFIG_PRINTK_TIME is not set +CONFIG_PRINT_STACK_DEPTH=64 +# CONFIG_PRISM2_USB is not set +# CONFIG_PRISM54 is not set +# CONFIG_PROC_CHILDREN is not set +CONFIG_PROC_FS=y +# CONFIG_PROC_KCORE is not set +# CONFIG_PROC_PAGE_MONITOR is not set +# CONFIG_PROC_STRIPPED is not set +CONFIG_PROC_SYSCTL=y +# CONFIG_PROC_VMCORE_DEVICE_DUMP is not set +# CONFIG_PROFILE_ALL_BRANCHES is not set +# CONFIG_PROFILE_ANNOTATED_BRANCHES is not set +# CONFIG_PROFILING is not set +# CONFIG_PROVE_LOCKING is not set +# CONFIG_PROVE_RAW_LOCK_NESTING is not set +# CONFIG_PROVE_RCU is not set +# CONFIG_PROVE_RCU_LIST is not set +# CONFIG_PROVE_RCU_REPEATEDLY is not set +# CONFIG_PSAMPLE is not set +# CONFIG_PSB6970_PHY is not set +# CONFIG_PSI is not set +# CONFIG_PSTORE is not set +# CONFIG_PSTORE_842_COMPRESS is not set +# CONFIG_PSTORE_BLK is not set +# CONFIG_PSTORE_COMPRESS is not set +# CONFIG_PSTORE_CONSOLE is not set +CONFIG_PSTORE_DEFAULT_KMSG_BYTES=10240 +# CONFIG_PSTORE_DEFLATE_COMPRESS is not set +# CONFIG_PSTORE_DEFLATE_COMPRESS_DEFAULT is not set +# CONFIG_PSTORE_FTRACE is not set +# CONFIG_PSTORE_LZ4HC_COMPRESS is not set +# CONFIG_PSTORE_LZ4_COMPRESS is not set +# CONFIG_PSTORE_LZO_COMPRESS is not set +# CONFIG_PSTORE_PMSG is not set +# CONFIG_PSTORE_RAM is not set +# CONFIG_PSTORE_ZSTD_COMPRESS is not set +# CONFIG_PTDUMP_DEBUGFS is not set +# CONFIG_PTP_1588_CLOCK is not set +# CONFIG_PTP_1588_CLOCK_IDT82P33 is not set +# CONFIG_PTP_1588_CLOCK_IDTCM is not set +# CONFIG_PTP_1588_CLOCK_IXP46X is not set +# CONFIG_PTP_1588_CLOCK_KVM is not set +# CONFIG_PTP_1588_CLOCK_OCP is not set +# CONFIG_PTP_1588_CLOCK_PCH is not set +# CONFIG_PTP_1588_CLOCK_VMW is not set +# CONFIG_PUBLIC_KEY_ALGO_RSA is not set +# CONFIG_PVPANIC is not set +# CONFIG_PWM is not set +# CONFIG_PWM_ATMEL_TCB is not set +# CONFIG_PWM_DEBUG is not set +# CONFIG_PWM_DWC is not set +# CONFIG_PWM_FSL_FTM is not set +# CONFIG_PWM_JZ4740 is not set +# CONFIG_PWM_PCA9685 is not set +# CONFIG_PWM_RASPBERRYPI_POE is not set +CONFIG_PWRSEQ_EMMC=y +# CONFIG_PWRSEQ_SD8787 is not set +CONFIG_PWRSEQ_SIMPLE=y +# CONFIG_QCA7000 is not set +# CONFIG_QCA7000_SPI is not set +# CONFIG_QCA7000_UART is not set +# CONFIG_QCOM_A7PLL is not set +# CONFIG_QCOM_EMAC is not set +# CONFIG_QCOM_FALKOR_ERRATUM_1003 is not set +# CONFIG_QCOM_FALKOR_ERRATUM_1009 is not set +# CONFIG_QCOM_FALKOR_ERRATUM_E1041 is not set +# CONFIG_QCOM_GPI_DMA is not set +# CONFIG_QCOM_HIDMA is not set +# CONFIG_QCOM_HIDMA_MGMT is not set +# CONFIG_QCOM_LMH is not set +# CONFIG_QCOM_QDF2400_ERRATUM_0065 is not set +# CONFIG_QCOM_SPMI_ADC5 is not set +# CONFIG_QCOM_SPMI_ADC_TM5 is not set +# CONFIG_QCOM_SPMI_IADC is not set +# CONFIG_QCOM_SPMI_TEMP_ALARM is not set +# CONFIG_QCOM_SPMI_VADC is not set +# CONFIG_QED is not set +# CONFIG_QFMT_V1 is not set +# CONFIG_QLA3XXX is not set +# CONFIG_QLCNIC is not set +# CONFIG_QLGE is not set +# CONFIG_QNX4FS_FS is not set +# CONFIG_QNX6FS_FS is not set +# CONFIG_QORIQ_CPUFREQ is not set +# CONFIG_QORIQ_THERMAL is not set +# CONFIG_QRTR is not set +# CONFIG_QRTR_MHI is not set +# CONFIG_QRTR_TUN is not set +# CONFIG_QSEMI_PHY is not set +# CONFIG_QUEUED_LOCK_STAT is not set +# CONFIG_QUICC_ENGINE is not set +# CONFIG_QUOTA is not set +# CONFIG_QUOTACTL is not set +# CONFIG_QUOTA_DEBUG is not set +# CONFIG_QUOTA_NETLINK_INTERFACE is not set +# CONFIG_R3964 is not set +# CONFIG_R6040 is not set +# CONFIG_R8169 is not set +# CONFIG_R8188EU is not set +# CONFIG_R8712U is not set +# CONFIG_R8723AU is not set +# CONFIG_RADIO_ADAPTERS is not set +# CONFIG_RADIO_AZTECH is not set +# CONFIG_RADIO_CADET is not set +# CONFIG_RADIO_GEMTEK is not set +# CONFIG_RADIO_MAXIRADIO is not set +# CONFIG_RADIO_RTRACK is not set +# CONFIG_RADIO_RTRACK2 is not set +# CONFIG_RADIO_SF16FMI is not set +# CONFIG_RADIO_SF16FMR2 is not set +# CONFIG_RADIO_TERRATEC is not set +# CONFIG_RADIO_TRUST is not set +# CONFIG_RADIO_TYPHOON is not set +# CONFIG_RADIO_ZOLTRIX is not set +# CONFIG_RAID6_PQ_BENCHMARK is not set +# CONFIG_RAID_ATTRS is not set +# CONFIG_RALINK is not set +# CONFIG_RANDOM32_SELFTEST is not set +# CONFIG_RANDOMIZE_BASE is not set +# CONFIG_RANDOMIZE_KSTACK_OFFSET_DEFAULT is not set +# CONFIG_RANDOM_TRUST_BOOTLOADER is not set +# CONFIG_RANDOM_TRUST_CPU is not set +# CONFIG_RAPIDIO is not set +# CONFIG_RAS is not set +# CONFIG_RAW_DRIVER is not set +# CONFIG_RBTREE_TEST is not set +# CONFIG_RCU_BOOST is not set +CONFIG_RCU_CPU_STALL_TIMEOUT=60 +# CONFIG_RCU_EQS_DEBUG is not set +# CONFIG_RCU_EXPEDITE_BOOT is not set +# CONFIG_RCU_EXPERT is not set +CONFIG_RCU_KTHREAD_PRIO=0 +CONFIG_RCU_NEED_SEGCBLIST=y +# CONFIG_RCU_PERF_TEST is not set +# CONFIG_RCU_REF_SCALE_TEST is not set +# CONFIG_RCU_SCALE_TEST is not set +CONFIG_RCU_STALL_COMMON=y +# CONFIG_RCU_STRICT_GRACE_PERIOD is not set +# CONFIG_RCU_TORTURE_TEST is not set +CONFIG_RCU_TORTURE_TEST_SLOW_INIT_DELAY=3 +# CONFIG_RCU_TRACE is not set +# CONFIG_RC_ATI_REMOTE is not set +# CONFIG_RC_CORE is not set +# CONFIG_RC_DECODERS is not set +# CONFIG_RC_LOOPBACK is not set +# CONFIG_RC_MAP is not set +# CONFIG_RDS is not set +# CONFIG_RD_BZIP2 is not set +# CONFIG_RD_GZIP is not set +# CONFIG_RD_LZ4 is not set +# CONFIG_RD_LZMA is not set +# CONFIG_RD_LZO is not set +# CONFIG_RD_XZ is not set +# CONFIG_RD_ZSTD is not set +# CONFIG_READABLE_ASM is not set +# CONFIG_READ_ONLY_THP_FOR_FS is not set +# CONFIG_REALTEK_PHY is not set +# CONFIG_REDWOOD is not set +# CONFIG_REED_SOLOMON is not set +# CONFIG_REED_SOLOMON_DEC8 is not set +# CONFIG_REED_SOLOMON_ENC8 is not set +# CONFIG_REED_SOLOMON_TEST is not set +# CONFIG_REGMAP is not set +# CONFIG_REGMAP_I2C is not set +# CONFIG_REGMAP_MMIO is not set +# CONFIG_REGMAP_SPI is not set +# CONFIG_REGULATOR is not set +# CONFIG_REGULATOR_88PG86X is not set +# CONFIG_REGULATOR_ACT8865 is not set +# CONFIG_REGULATOR_AD5398 is not set +# CONFIG_REGULATOR_ANATOP is not set +# CONFIG_REGULATOR_DA9121 is not set +# CONFIG_REGULATOR_DA9210 is not set +# CONFIG_REGULATOR_DA9211 is not set +# CONFIG_REGULATOR_DEBUG is not set +# CONFIG_REGULATOR_FAN53555 is not set +# CONFIG_REGULATOR_FAN53880 is not set +# CONFIG_REGULATOR_FIXED_VOLTAGE is not set +# CONFIG_REGULATOR_GPIO is not set +# CONFIG_REGULATOR_ISL6271A is not set +# CONFIG_REGULATOR_ISL9305 is not set +# CONFIG_REGULATOR_LP3971 is not set +# CONFIG_REGULATOR_LP3972 is not set +# CONFIG_REGULATOR_LP872X is not set +# CONFIG_REGULATOR_LP8755 is not set +# CONFIG_REGULATOR_LTC3589 is not set +# CONFIG_REGULATOR_LTC3676 is not set +# CONFIG_REGULATOR_MAX1586 is not set +# CONFIG_REGULATOR_MAX77620 is not set +# CONFIG_REGULATOR_MAX77826 is not set +# CONFIG_REGULATOR_MAX8649 is not set +# CONFIG_REGULATOR_MAX8660 is not set +# CONFIG_REGULATOR_MAX8893 is not set +# CONFIG_REGULATOR_MAX8952 is not set +# CONFIG_REGULATOR_MAX8973 is not set +# CONFIG_REGULATOR_MCP16502 is not set +# CONFIG_REGULATOR_MP5416 is not set +# CONFIG_REGULATOR_MP8859 is not set +# CONFIG_REGULATOR_MP886X is not set +# CONFIG_REGULATOR_MPQ7920 is not set +# CONFIG_REGULATOR_MT6311 is not set +# CONFIG_REGULATOR_MT6315 is not set +# CONFIG_REGULATOR_MT6359 is not set +# CONFIG_REGULATOR_PCA9450 is not set +# CONFIG_REGULATOR_PF8X00 is not set +# CONFIG_REGULATOR_PFUZE100 is not set +# CONFIG_REGULATOR_PV88060 is not set +# CONFIG_REGULATOR_PV88080 is not set +# CONFIG_REGULATOR_PV88090 is not set +# CONFIG_REGULATOR_PWM is not set +# CONFIG_REGULATOR_RASPBERRYPI_TOUCHSCREEN_ATTINY is not set +# CONFIG_REGULATOR_RT4801 is not set +# CONFIG_REGULATOR_RT6160 is not set +# CONFIG_REGULATOR_RT6245 is not set +# CONFIG_REGULATOR_RTMV20 is not set +# CONFIG_REGULATOR_RTQ2134 is not set +# CONFIG_REGULATOR_RTQ6752 is not set +# CONFIG_REGULATOR_SLG51000 is not set +# CONFIG_REGULATOR_SY8106A is not set +# CONFIG_REGULATOR_SY8824X is not set +# CONFIG_REGULATOR_SY8827N is not set +# CONFIG_REGULATOR_TI_ABB is not set +# CONFIG_REGULATOR_TPS51632 is not set +# CONFIG_REGULATOR_TPS62360 is not set +# CONFIG_REGULATOR_TPS65023 is not set +# CONFIG_REGULATOR_TPS6507X is not set +# CONFIG_REGULATOR_TPS65132 is not set +# CONFIG_REGULATOR_TPS6524X is not set +# CONFIG_REGULATOR_USERSPACE_CONSUMER is not set +# CONFIG_REGULATOR_VCTRL is not set +# CONFIG_REGULATOR_VIRTUAL_CONSUMER is not set +# CONFIG_REISERFS_CHECK is not set +# CONFIG_REISERFS_FS is not set +# CONFIG_REISERFS_FS_POSIX_ACL is not set +# CONFIG_REISERFS_FS_SECURITY is not set +CONFIG_REISERFS_FS_XATTR=y +# CONFIG_REISERFS_PROC_INFO is not set +# CONFIG_RELAY is not set +# CONFIG_RELOCATABLE is not set +# CONFIG_REMOTEPROC is not set +# CONFIG_RENESAS_PHY is not set +# CONFIG_RESET_ATH79 is not set +# CONFIG_RESET_BERLIN is not set +# CONFIG_RESET_BRCMSTB_RESCAL is not set +# CONFIG_RESET_CONTROLLER is not set +# CONFIG_RESET_IMX7 is not set +# CONFIG_RESET_INTEL_GW is not set +# CONFIG_RESET_LANTIQ is not set +# CONFIG_RESET_LPC18XX is not set +# CONFIG_RESET_MESON is not set +# CONFIG_RESET_PISTACHIO is not set +# CONFIG_RESET_SOCFPGA is not set +# CONFIG_RESET_STM32 is not set +# CONFIG_RESET_SUNXI is not set +# CONFIG_RESET_TEGRA_BPMP is not set +# CONFIG_RESET_TI_SYSCON is not set +# CONFIG_RESET_ZYNQ is not set +# CONFIG_RFD77402 is not set +# CONFIG_RFD_FTL is not set +CONFIG_RFKILL=y +# CONFIG_RFKILL_FULL is not set +# CONFIG_RFKILL_GPIO is not set +# CONFIG_RFKILL_INPUT is not set +# CONFIG_RFKILL_LEDS is not set +# CONFIG_RFKILL_REGULATOR is not set +# CONFIG_RING_BUFFER_BENCHMARK is not set +# CONFIG_RING_BUFFER_STARTUP_TEST is not set +# CONFIG_RING_BUFFER_VALIDATE_TIME_DELTAS is not set +# CONFIG_RMI4_CORE is not set +# CONFIG_RMNET is not set +# CONFIG_ROCKCHIP_PHY is not set +# CONFIG_ROCKER is not set +# CONFIG_ROMFS_FS is not set +# CONFIG_ROSE is not set +# CONFIG_RPCSEC_GSS_KRB5 is not set +# CONFIG_RPMSG_QCOM_GLINK_RPM is not set +# CONFIG_RPMSG_VIRTIO is not set +# CONFIG_RPMSG_WWAN_CTRL is not set +# CONFIG_RPR0521 is not set +# CONFIG_RSEQ is not set +# CONFIG_RT2X00 is not set +# CONFIG_RTC_CLASS is not set +# CONFIG_RTC_DEBUG is not set +# CONFIG_RTC_DRV_ABB5ZES3 is not set +# CONFIG_RTC_DRV_ABEOZ9 is not set +# CONFIG_RTC_DRV_ABX80X is not set +# CONFIG_RTC_DRV_ARMADA38X is not set +# CONFIG_RTC_DRV_AU1XXX is not set +# CONFIG_RTC_DRV_BQ32K is not set +# CONFIG_RTC_DRV_BQ4802 is not set +# CONFIG_RTC_DRV_CADENCE is not set +CONFIG_RTC_DRV_CMOS=y +# CONFIG_RTC_DRV_DS1286 is not set +# CONFIG_RTC_DRV_DS1302 is not set +# CONFIG_RTC_DRV_DS1305 is not set +# CONFIG_RTC_DRV_DS1307 is not set +# CONFIG_RTC_DRV_DS1307_CENTURY is not set +# CONFIG_RTC_DRV_DS1307_HWMON is not set +# CONFIG_RTC_DRV_DS1343 is not set +# CONFIG_RTC_DRV_DS1347 is not set +# CONFIG_RTC_DRV_DS1374 is not set +# CONFIG_RTC_DRV_DS1390 is not set +# CONFIG_RTC_DRV_DS1511 is not set +# CONFIG_RTC_DRV_DS1553 is not set +# CONFIG_RTC_DRV_DS1672 is not set +# CONFIG_RTC_DRV_DS1685_FAMILY is not set +# CONFIG_RTC_DRV_DS1742 is not set +# CONFIG_RTC_DRV_DS2404 is not set +# CONFIG_RTC_DRV_DS3232 is not set +# CONFIG_RTC_DRV_DS3234 is not set +# CONFIG_RTC_DRV_EM3027 is not set +# CONFIG_RTC_DRV_EP93XX is not set +# CONFIG_RTC_DRV_FM3130 is not set +# CONFIG_RTC_DRV_FTRTC010 is not set +# CONFIG_RTC_DRV_GENERIC is not set +# CONFIG_RTC_DRV_GOLDFISH is not set +# CONFIG_RTC_DRV_HID_SENSOR_TIME is not set +# CONFIG_RTC_DRV_HYM8563 is not set +# CONFIG_RTC_DRV_ISL12022 is not set +# CONFIG_RTC_DRV_ISL12026 is not set +# CONFIG_RTC_DRV_ISL12057 is not set +# CONFIG_RTC_DRV_ISL1208 is not set +# CONFIG_RTC_DRV_JZ4740 is not set +# CONFIG_RTC_DRV_M41T80 is not set +# CONFIG_RTC_DRV_M41T93 is not set +# CONFIG_RTC_DRV_M41T94 is not set +# CONFIG_RTC_DRV_M48T35 is not set +# CONFIG_RTC_DRV_M48T59 is not set +# CONFIG_RTC_DRV_M48T86 is not set +# CONFIG_RTC_DRV_MAX6900 is not set +# CONFIG_RTC_DRV_MAX6902 is not set +# CONFIG_RTC_DRV_MAX6916 is not set +# CONFIG_RTC_DRV_MAX77686 is not set +# CONFIG_RTC_DRV_MCP795 is not set +# CONFIG_RTC_DRV_MOXART is not set +# CONFIG_RTC_DRV_MPC5121 is not set +# CONFIG_RTC_DRV_MSM6242 is not set +# CONFIG_RTC_DRV_MT2712 is not set +# CONFIG_RTC_DRV_OMAP is not set +# CONFIG_RTC_DRV_PCF2123 is not set +# CONFIG_RTC_DRV_PCF2127 is not set +# CONFIG_RTC_DRV_PCF85063 is not set +# CONFIG_RTC_DRV_PCF8523 is not set +# CONFIG_RTC_DRV_PCF85363 is not set +# CONFIG_RTC_DRV_PCF8563 is not set +# CONFIG_RTC_DRV_PCF8583 is not set +# CONFIG_RTC_DRV_PL030 is not set +# CONFIG_RTC_DRV_PL031 is not set +# CONFIG_RTC_DRV_PS3 is not set +# CONFIG_RTC_DRV_PT7C4338 is not set +# CONFIG_RTC_DRV_R7301 is not set +# CONFIG_RTC_DRV_R9701 is not set +# CONFIG_RTC_DRV_RP5C01 is not set +# CONFIG_RTC_DRV_RS5C348 is not set +# CONFIG_RTC_DRV_RS5C372 is not set +# CONFIG_RTC_DRV_RTC7301 is not set +# CONFIG_RTC_DRV_RV3028 is not set +# CONFIG_RTC_DRV_RV3029C2 is not set +# CONFIG_RTC_DRV_RV3032 is not set +# CONFIG_RTC_DRV_RV8803 is not set +# CONFIG_RTC_DRV_RX4581 is not set +# CONFIG_RTC_DRV_RX6110 is not set +# CONFIG_RTC_DRV_RX8010 is not set +# CONFIG_RTC_DRV_RX8025 is not set +# CONFIG_RTC_DRV_RX8581 is not set +# CONFIG_RTC_DRV_S35390A is not set +# CONFIG_RTC_DRV_SD3078 is not set +# CONFIG_RTC_DRV_SNVS is not set +# CONFIG_RTC_DRV_STK17TA8 is not set +# CONFIG_RTC_DRV_SUN6I is not set +# CONFIG_RTC_DRV_TEGRA is not set +# CONFIG_RTC_DRV_TEST is not set +# CONFIG_RTC_DRV_V3020 is not set +# CONFIG_RTC_DRV_X1205 is not set +# CONFIG_RTC_DRV_XGENE is not set +# CONFIG_RTC_DRV_ZYNQMP is not set +CONFIG_RTC_HCTOSYS=y +CONFIG_RTC_HCTOSYS_DEVICE="rtc0" +CONFIG_RTC_INTF_DEV=y +# CONFIG_RTC_INTF_DEV_UIE_EMUL is not set +CONFIG_RTC_INTF_PROC=y +CONFIG_RTC_INTF_SYSFS=y +CONFIG_RTC_LIB=y +# CONFIG_RTC_NVMEM is not set +CONFIG_RTC_SYSTOHC=y +CONFIG_RTC_SYSTOHC_DEVICE="rtc0" +# CONFIG_RTL8180 is not set +# CONFIG_RTL8187 is not set +# CONFIG_RTL8192E is not set +# CONFIG_RTL8192U is not set +# CONFIG_RTL8306_PHY is not set +# CONFIG_RTL8366RB_PHY is not set +# CONFIG_RTL8366S_PHY is not set +# CONFIG_RTL8366_SMI is not set +# CONFIG_RTL8366_SMI_DEBUG_FS is not set +# CONFIG_RTL8367B_PHY is not set +# CONFIG_RTL8367_PHY is not set +# CONFIG_RTLLIB is not set +# CONFIG_RTL_CARDS is not set +# CONFIG_RTS5208 is not set +CONFIG_RT_MUTEXES=y +# CONFIG_RUNTIME_DEBUG is not set +CONFIG_RUNTIME_TESTING_MENU=y +CONFIG_RWSEM_GENERIC_SPINLOCK=y +CONFIG_RXKAD=y +# CONFIG_S2IO is not set +# CONFIG_SAMPLES is not set +# CONFIG_SAMSUNG_LAPTOP is not set +# CONFIG_SATA_ACARD_AHCI is not set +# CONFIG_SATA_AHCI is not set +# CONFIG_SATA_AHCI_PLATFORM is not set +# CONFIG_SATA_DWC is not set +# CONFIG_SATA_FSL is not set +# CONFIG_SATA_HIGHBANK is not set +# CONFIG_SATA_HOST is not set +# CONFIG_SATA_INIC162X is not set +CONFIG_SATA_MOBILE_LPM_POLICY=0 +# CONFIG_SATA_MV is not set +# CONFIG_SATA_NV is not set +# CONFIG_SATA_PMP is not set +# CONFIG_SATA_PROMISE is not set +# CONFIG_SATA_QSTOR is not set +# CONFIG_SATA_RCAR is not set +# CONFIG_SATA_SIL is not set +# CONFIG_SATA_SIL24 is not set +# CONFIG_SATA_SIS is not set +# CONFIG_SATA_SVW is not set +# CONFIG_SATA_SX4 is not set +# CONFIG_SATA_ULI is not set +# CONFIG_SATA_VIA is not set +# CONFIG_SATA_VITESSE is not set +# CONFIG_SBC_FITPC2_WATCHDOG is not set +CONFIG_SBITMAP=y +# CONFIG_SC92031 is not set +# CONFIG_SCA3000 is not set +# CONFIG_SCA3300 is not set +# CONFIG_SCACHE_DEBUGFS is not set +# CONFIG_SCC is not set +# CONFIG_SCD30_CORE is not set +# CONFIG_SCF_TORTURE_TEST is not set +# CONFIG_SCHEDSTATS is not set +# CONFIG_SCHED_AUTOGROUP is not set +# CONFIG_SCHED_DEBUG is not set +CONFIG_SCHED_HRTICK=y +# CONFIG_SCHED_MC is not set +CONFIG_SCHED_OMIT_FRAME_POINTER=y +# CONFIG_SCHED_SMT is not set +# CONFIG_SCHED_STACK_END_CHECK is not set +# CONFIG_SCHED_TRACER is not set +# CONFIG_SCR24X is not set +# CONFIG_SCSI is not set +# CONFIG_SCSI_3W_9XXX is not set +# CONFIG_SCSI_3W_SAS is not set +# CONFIG_SCSI_7000FASST is not set +# CONFIG_SCSI_AACRAID is not set +# CONFIG_SCSI_ACARD is not set +# CONFIG_SCSI_ADVANSYS is not set +# CONFIG_SCSI_AHA152X is not set +# CONFIG_SCSI_AHA1542 is not set +# CONFIG_SCSI_AIC79XX is not set +# CONFIG_SCSI_AIC7XXX is not set +# CONFIG_SCSI_AIC94XX is not set +# CONFIG_SCSI_AM53C974 is not set +# CONFIG_SCSI_ARCMSR is not set +# CONFIG_SCSI_BFA_FC is not set +# CONFIG_SCSI_BNX2X_FCOE is not set +# CONFIG_SCSI_BNX2_ISCSI is not set +# CONFIG_SCSI_BUSLOGIC is not set +# CONFIG_SCSI_CHELSIO_FCOE is not set +# CONFIG_SCSI_CONSTANTS is not set +# CONFIG_SCSI_CXGB3_ISCSI is not set +# CONFIG_SCSI_CXGB4_ISCSI is not set +# CONFIG_SCSI_DC395x is not set +# CONFIG_SCSI_DEBUG is not set +# CONFIG_SCSI_DH is not set +CONFIG_SCSI_DMA=y +# CONFIG_SCSI_DMX3191D is not set +# CONFIG_SCSI_DPT_I2O is not set +# CONFIG_SCSI_DTC3280 is not set +# CONFIG_SCSI_EATA is not set +# CONFIG_SCSI_ESAS2R is not set +# CONFIG_SCSI_FC_ATTRS is not set +# CONFIG_SCSI_FDOMAIN_PCI is not set +# CONFIG_SCSI_FUTURE_DOMAIN is not set +# CONFIG_SCSI_GDTH is not set +# CONFIG_SCSI_GENERIC_NCR5380 is not set +# CONFIG_SCSI_GENERIC_NCR5380_MMIO is not set +# CONFIG_SCSI_HISI_SAS is not set +# CONFIG_SCSI_HPSA is not set +# CONFIG_SCSI_HPTIOP is not set +# CONFIG_SCSI_IN2000 is not set +# CONFIG_SCSI_INIA100 is not set +# CONFIG_SCSI_INITIO is not set +# CONFIG_SCSI_IPR is not set +# CONFIG_SCSI_IPS is not set +# CONFIG_SCSI_ISCI is not set +# CONFIG_SCSI_ISCSI_ATTRS is not set +# CONFIG_SCSI_LOGGING is not set +CONFIG_SCSI_LOWLEVEL=y +# CONFIG_SCSI_LOWLEVEL_PCMCIA is not set +# CONFIG_SCSI_LPFC is not set +CONFIG_SCSI_MOD=y +# CONFIG_SCSI_MPI3MR is not set +# CONFIG_SCSI_MPT2SAS is not set +# CONFIG_SCSI_MPT3SAS is not set +# CONFIG_SCSI_MQ_DEFAULT is not set +# CONFIG_SCSI_MVSAS is not set +# CONFIG_SCSI_MVSAS_DEBUG is not set +# CONFIG_SCSI_MVUMI is not set +# CONFIG_SCSI_MYRB is not set +# CONFIG_SCSI_MYRS is not set +# CONFIG_SCSI_NCR53C406A is not set +# CONFIG_SCSI_NETLINK is not set +# CONFIG_SCSI_NSP32 is not set +# CONFIG_SCSI_OSD_INITIATOR is not set +# CONFIG_SCSI_PAS16 is not set +# CONFIG_SCSI_PM8001 is not set +# CONFIG_SCSI_PMCRAID is not set +CONFIG_SCSI_PROC_FS=y +# CONFIG_SCSI_QLA_FC is not set +# CONFIG_SCSI_QLA_ISCSI is not set +# CONFIG_SCSI_QLOGIC_1280 is not set +# CONFIG_SCSI_QLOGIC_FAS is not set +# CONFIG_SCSI_SAS_ATTRS is not set +# CONFIG_SCSI_SAS_LIBSAS is not set +# CONFIG_SCSI_SCAN_ASYNC is not set +# CONFIG_SCSI_SMARTPQI is not set +# CONFIG_SCSI_SNIC is not set +# CONFIG_SCSI_SPI_ATTRS is not set +# CONFIG_SCSI_SRP_ATTRS is not set +# CONFIG_SCSI_STEX is not set +# CONFIG_SCSI_SYM53C416 is not set +# CONFIG_SCSI_SYM53C8XX_2 is not set +# CONFIG_SCSI_T128 is not set +# CONFIG_SCSI_U14_34F is not set +# CONFIG_SCSI_UFSHCD is not set +# CONFIG_SCSI_ULTRASTOR is not set +# CONFIG_SCSI_VIRTIO is not set +# CONFIG_SCSI_WD719X is not set +# CONFIG_SC_CAMCC_7180 is not set +# CONFIG_SC_DISPCC_7280 is not set +# CONFIG_SC_GCC_7280 is not set +# CONFIG_SC_GCC_8180X is not set +# CONFIG_SC_GPUCC_7280 is not set +# CONFIG_SC_VIDEOCC_7280 is not set +# CONFIG_SCx200_ACB is not set +# CONFIG_SDIO_UART is not set +# CONFIG_SDM_GPUCC_660 is not set +# CONFIG_SDM_MMCC_660 is not set +# CONFIG_SDR_MAX2175 is not set +# CONFIG_SDR_PLATFORM_DRIVERS is not set +# CONFIG_SDX_GCC_55 is not set +# CONFIG_SD_ADC_MODULATOR is not set +# CONFIG_SECCOMP is not set +# CONFIG_SECCOMP_CACHE_DEBUG is not set +CONFIG_SECTION_MISMATCH_WARN_ONLY=y +# CONFIG_SECURITY is not set +# CONFIG_SECURITYFS is not set +# CONFIG_SECURITY_APPARMOR is not set +CONFIG_SECURITY_DMESG_RESTRICT=y +# CONFIG_SECURITY_LANDLOCK is not set +# CONFIG_SECURITY_LOADPIN is not set +# CONFIG_SECURITY_LOCKDOWN_LSM is not set +# CONFIG_SECURITY_NETWORK_XFRM is not set +# CONFIG_SECURITY_PATH is not set +# CONFIG_SECURITY_SAFESETID is not set +# CONFIG_SECURITY_SELINUX_AVC_STATS is not set +# CONFIG_SECURITY_SELINUX_BOOTPARAM is not set +CONFIG_SECURITY_SELINUX_CHECKREQPROT_VALUE=0 +# CONFIG_SECURITY_SELINUX_DEVELOP is not set +# CONFIG_SECURITY_SELINUX_DISABLE is not set +# CONFIG_SECURITY_SMACK is not set +# CONFIG_SECURITY_TOMOYO is not set +# CONFIG_SECURITY_YAMA is not set +CONFIG_SELECT_MEMORY_MODEL=y +# CONFIG_SENSIRION_SGP30 is not set +# CONFIG_SENSIRION_SGP40 is not set +# CONFIG_SENSORS_ABITUGURU is not set +# CONFIG_SENSORS_ABITUGURU3 is not set +# CONFIG_SENSORS_ACPI_POWER is not set +# CONFIG_SENSORS_AD7314 is not set +# CONFIG_SENSORS_AD7414 is not set +# CONFIG_SENSORS_AD7418 is not set +# CONFIG_SENSORS_ADC128D818 is not set +# CONFIG_SENSORS_ADCXX is not set +# CONFIG_SENSORS_ADM1021 is not set +# CONFIG_SENSORS_ADM1025 is not set +# CONFIG_SENSORS_ADM1026 is not set +# CONFIG_SENSORS_ADM1029 is not set +# CONFIG_SENSORS_ADM1031 is not set +# CONFIG_SENSORS_ADM1177 is not set +# CONFIG_SENSORS_ADM1266 is not set +# CONFIG_SENSORS_ADM1275 is not set +# CONFIG_SENSORS_ADM9240 is not set +# CONFIG_SENSORS_ADS1015 is not set +# CONFIG_SENSORS_ADS7828 is not set +# CONFIG_SENSORS_ADS7871 is not set +# CONFIG_SENSORS_ADT7310 is not set +# CONFIG_SENSORS_ADT7410 is not set +# CONFIG_SENSORS_ADT7411 is not set +# CONFIG_SENSORS_ADT7462 is not set +# CONFIG_SENSORS_ADT7470 is not set +# CONFIG_SENSORS_ADT7475 is not set +# CONFIG_SENSORS_AHT10 is not set +# CONFIG_SENSORS_AMC6821 is not set +# CONFIG_SENSORS_APDS990X is not set +# CONFIG_SENSORS_APPLESMC is not set +# CONFIG_SENSORS_AQUACOMPUTER_D5NEXT is not set +# CONFIG_SENSORS_AS370 is not set +# CONFIG_SENSORS_ASB100 is not set +# CONFIG_SENSORS_ASC7621 is not set +# CONFIG_SENSORS_ASPEED is not set +# CONFIG_SENSORS_ATK0110 is not set +# CONFIG_SENSORS_ATXP1 is not set +# CONFIG_SENSORS_AXI_FAN_CONTROL is not set +# CONFIG_SENSORS_BEL_PFE is not set +# CONFIG_SENSORS_BH1770 is not set +# CONFIG_SENSORS_BH1780 is not set +# CONFIG_SENSORS_BPA_RS600 is not set +# CONFIG_SENSORS_CORETEMP is not set +# CONFIG_SENSORS_CORSAIR_CPRO is not set +# CONFIG_SENSORS_CORSAIR_PSU is not set +# CONFIG_SENSORS_DELL_SMM is not set +# CONFIG_SENSORS_DME1737 is not set +# CONFIG_SENSORS_DPS920AB is not set +# CONFIG_SENSORS_DRIVETEMP is not set +# CONFIG_SENSORS_DS1621 is not set +# CONFIG_SENSORS_DS620 is not set +# CONFIG_SENSORS_EMC1403 is not set +# CONFIG_SENSORS_EMC2103 is not set +# CONFIG_SENSORS_EMC2305 is not set +# CONFIG_SENSORS_EMC6W201 is not set +# CONFIG_SENSORS_F71805F is not set +# CONFIG_SENSORS_F71882FG is not set +# CONFIG_SENSORS_F75375S is not set +# CONFIG_SENSORS_FAM15H_POWER is not set +# CONFIG_SENSORS_FSCHMD is not set +# CONFIG_SENSORS_FSP_3Y is not set +# CONFIG_SENSORS_FTSTEUTATES is not set +# CONFIG_SENSORS_G760A is not set +# CONFIG_SENSORS_G762 is not set +# CONFIG_SENSORS_GL518SM is not set +# CONFIG_SENSORS_GL520SM is not set +# CONFIG_SENSORS_GPIO_FAN is not set +# CONFIG_SENSORS_GSC is not set +# CONFIG_SENSORS_HDAPS is not set +# CONFIG_SENSORS_HIH6130 is not set +# CONFIG_SENSORS_HMC5843 is not set +# CONFIG_SENSORS_HMC5843_I2C is not set +# CONFIG_SENSORS_HMC5843_SPI is not set +# CONFIG_SENSORS_HTU21 is not set +# CONFIG_SENSORS_I5500 is not set +# CONFIG_SENSORS_I5K_AMB is not set +# CONFIG_SENSORS_IBM_CFFPS is not set +# CONFIG_SENSORS_IIO_HWMON is not set +# CONFIG_SENSORS_INA209 is not set +# CONFIG_SENSORS_INA2XX is not set +# CONFIG_SENSORS_INA3221 is not set +# CONFIG_SENSORS_INSPUR_IPSPS is not set +# CONFIG_SENSORS_IR35221 is not set +# CONFIG_SENSORS_IR36021 is not set +# CONFIG_SENSORS_IR38064 is not set +# CONFIG_SENSORS_IRPS5401 is not set +# CONFIG_SENSORS_ISL29018 is not set +# CONFIG_SENSORS_ISL29028 is not set +# CONFIG_SENSORS_ISL68137 is not set +# CONFIG_SENSORS_IT87 is not set +# CONFIG_SENSORS_JC42 is not set +# CONFIG_SENSORS_K10TEMP is not set +# CONFIG_SENSORS_K8TEMP is not set +# CONFIG_SENSORS_LINEAGE is not set +# CONFIG_SENSORS_LIS3LV02D is not set +# CONFIG_SENSORS_LIS3_I2C is not set +# CONFIG_SENSORS_LIS3_SPI is not set +# CONFIG_SENSORS_LM25066 is not set +# CONFIG_SENSORS_LM63 is not set +# CONFIG_SENSORS_LM70 is not set +# CONFIG_SENSORS_LM73 is not set +# CONFIG_SENSORS_LM75 is not set +# CONFIG_SENSORS_LM77 is not set +# CONFIG_SENSORS_LM78 is not set +# CONFIG_SENSORS_LM80 is not set +# CONFIG_SENSORS_LM83 is not set +# CONFIG_SENSORS_LM85 is not set +# CONFIG_SENSORS_LM87 is not set +# CONFIG_SENSORS_LM90 is not set +# CONFIG_SENSORS_LM92 is not set +# CONFIG_SENSORS_LM93 is not set +# CONFIG_SENSORS_LM95234 is not set +# CONFIG_SENSORS_LM95241 is not set +# CONFIG_SENSORS_LM95245 is not set +# CONFIG_SENSORS_LTC2945 is not set +# CONFIG_SENSORS_LTC2947_I2C is not set +# CONFIG_SENSORS_LTC2947_SPI is not set +# CONFIG_SENSORS_LTC2978 is not set +# CONFIG_SENSORS_LTC2990 is not set +# CONFIG_SENSORS_LTC2992 is not set +# CONFIG_SENSORS_LTC3815 is not set +# CONFIG_SENSORS_LTC4151 is not set +# CONFIG_SENSORS_LTC4215 is not set +# CONFIG_SENSORS_LTC4222 is not set +# CONFIG_SENSORS_LTC4245 is not set +# CONFIG_SENSORS_LTC4260 is not set +# CONFIG_SENSORS_LTC4261 is not set +# CONFIG_SENSORS_LTQ_CPUTEMP is not set +# CONFIG_SENSORS_MAX1111 is not set +# CONFIG_SENSORS_MAX127 is not set +# CONFIG_SENSORS_MAX15301 is not set +# CONFIG_SENSORS_MAX16064 is not set +# CONFIG_SENSORS_MAX16065 is not set +# CONFIG_SENSORS_MAX1619 is not set +# CONFIG_SENSORS_MAX16601 is not set +# CONFIG_SENSORS_MAX1668 is not set +# CONFIG_SENSORS_MAX197 is not set +# CONFIG_SENSORS_MAX20730 is not set +# CONFIG_SENSORS_MAX20751 is not set +# CONFIG_SENSORS_MAX31722 is not set +# CONFIG_SENSORS_MAX31730 is not set +# CONFIG_SENSORS_MAX31785 is not set +# CONFIG_SENSORS_MAX31790 is not set +# CONFIG_SENSORS_MAX34440 is not set +# CONFIG_SENSORS_MAX6621 is not set +# CONFIG_SENSORS_MAX6639 is not set +# CONFIG_SENSORS_MAX6642 is not set +# CONFIG_SENSORS_MAX6650 is not set +# CONFIG_SENSORS_MAX6697 is not set +# CONFIG_SENSORS_MAX8688 is not set +# CONFIG_SENSORS_MCP3021 is not set +# CONFIG_SENSORS_MP2888 is not set +# CONFIG_SENSORS_MP2975 is not set +# CONFIG_SENSORS_MR75203 is not set +# CONFIG_SENSORS_NCT6683 is not set +# CONFIG_SENSORS_NCT6775 is not set +# CONFIG_SENSORS_NCT7802 is not set +# CONFIG_SENSORS_NCT7904 is not set +# CONFIG_SENSORS_NPCM7XX is not set +# CONFIG_SENSORS_NSA320 is not set +# CONFIG_SENSORS_NTC_THERMISTOR is not set +# CONFIG_SENSORS_NZXT_KRAKEN2 is not set +# CONFIG_SENSORS_OCC_P8_I2C is not set +# CONFIG_SENSORS_PC87360 is not set +# CONFIG_SENSORS_PC87427 is not set +# CONFIG_SENSORS_PCF8591 is not set +# CONFIG_SENSORS_PIM4328 is not set +# CONFIG_SENSORS_PM6764TR is not set +# CONFIG_SENSORS_PMBUS is not set +# CONFIG_SENSORS_POWR1220 is not set +# CONFIG_SENSORS_PWM_FAN is not set +# CONFIG_SENSORS_PXE1610 is not set +# CONFIG_SENSORS_Q54SJ108A2 is not set +# CONFIG_SENSORS_RM3100_I2C is not set +# CONFIG_SENSORS_RM3100_SPI is not set +# CONFIG_SENSORS_SBRMI is not set +# CONFIG_SENSORS_SBTSI is not set +# CONFIG_SENSORS_SCH5627 is not set +# CONFIG_SENSORS_SCH5636 is not set +# CONFIG_SENSORS_SCH56XX_COMMON is not set +# CONFIG_SENSORS_SHT15 is not set +# CONFIG_SENSORS_SHT21 is not set +# CONFIG_SENSORS_SHT3x is not set +# CONFIG_SENSORS_SHT4x is not set +# CONFIG_SENSORS_SHTC1 is not set +# CONFIG_SENSORS_SIS5595 is not set +# CONFIG_SENSORS_SMM665 is not set +# CONFIG_SENSORS_SMSC47B397 is not set +# CONFIG_SENSORS_SMSC47M1 is not set +# CONFIG_SENSORS_SMSC47M192 is not set +# CONFIG_SENSORS_STPDDC60 is not set +# CONFIG_SENSORS_STTS751 is not set +# CONFIG_SENSORS_TC654 is not set +# CONFIG_SENSORS_TC74 is not set +# CONFIG_SENSORS_THMC50 is not set +# CONFIG_SENSORS_TMP102 is not set +# CONFIG_SENSORS_TMP103 is not set +# CONFIG_SENSORS_TMP108 is not set +# CONFIG_SENSORS_TMP401 is not set +# CONFIG_SENSORS_TMP421 is not set +# CONFIG_SENSORS_TMP513 is not set +# CONFIG_SENSORS_TPS23861 is not set +# CONFIG_SENSORS_TPS40422 is not set +# CONFIG_SENSORS_TPS53679 is not set +# CONFIG_SENSORS_TSL2550 is not set +# CONFIG_SENSORS_TSL2563 is not set +# CONFIG_SENSORS_UCD9000 is not set +# CONFIG_SENSORS_UCD9200 is not set +# CONFIG_SENSORS_VEXPRESS is not set +# CONFIG_SENSORS_VIA686A is not set +# CONFIG_SENSORS_VIA_CPUTEMP is not set +# CONFIG_SENSORS_VT1211 is not set +# CONFIG_SENSORS_VT8231 is not set +# CONFIG_SENSORS_W83627EHF is not set +# CONFIG_SENSORS_W83627HF is not set +# CONFIG_SENSORS_W83773G is not set +# CONFIG_SENSORS_W83781D is not set +# CONFIG_SENSORS_W83791D is not set +# CONFIG_SENSORS_W83792D is not set +# CONFIG_SENSORS_W83793 is not set +# CONFIG_SENSORS_W83795 is not set +# CONFIG_SENSORS_W83L785TS is not set +# CONFIG_SENSORS_W83L786NG is not set +# CONFIG_SENSORS_XDPE122 is not set +# CONFIG_SENSORS_XGENE is not set +# CONFIG_SENSORS_ZL6100 is not set +CONFIG_SERIAL_8250=y +CONFIG_SERIAL_8250_16550A_VARIANTS=y +# CONFIG_SERIAL_8250_ACCENT is not set +# CONFIG_SERIAL_8250_ASPEED_VUART is not set +# CONFIG_SERIAL_8250_BOCA is not set +CONFIG_SERIAL_8250_CONSOLE=y +# CONFIG_SERIAL_8250_CS is not set +# CONFIG_SERIAL_8250_DEPRECATED_OPTIONS is not set +# CONFIG_SERIAL_8250_DETECT_IRQ is not set +CONFIG_SERIAL_8250_DMA=y +# CONFIG_SERIAL_8250_DW is not set +# CONFIG_SERIAL_8250_EM is not set +# CONFIG_SERIAL_8250_EXAR is not set +# CONFIG_SERIAL_8250_EXAR_ST16C554 is not set +# CONFIG_SERIAL_8250_EXTENDED is not set +# CONFIG_SERIAL_8250_FINTEK is not set +# CONFIG_SERIAL_8250_FOURPORT is not set +# CONFIG_SERIAL_8250_HUB6 is not set +# CONFIG_SERIAL_8250_INGENIC is not set +# CONFIG_SERIAL_8250_LPSS is not set +# CONFIG_SERIAL_8250_MANY_PORTS is not set +# CONFIG_SERIAL_8250_MID is not set +# CONFIG_SERIAL_8250_MOXA is not set +CONFIG_SERIAL_8250_NR_UARTS=2 +# CONFIG_SERIAL_8250_PCI is not set +# CONFIG_SERIAL_8250_RSA is not set +# CONFIG_SERIAL_8250_RT288X is not set +CONFIG_SERIAL_8250_RUNTIME_UARTS=2 +# CONFIG_SERIAL_ALTERA_JTAGUART is not set +# CONFIG_SERIAL_ALTERA_UART is not set +# CONFIG_SERIAL_AMBA_PL010 is not set +# CONFIG_SERIAL_AMBA_PL011 is not set +# CONFIG_SERIAL_ARC is not set +# CONFIG_SERIAL_BCM63XX is not set +# CONFIG_SERIAL_CONEXANT_DIGICOLOR is not set +CONFIG_SERIAL_CORE=y +CONFIG_SERIAL_CORE_CONSOLE=y +# CONFIG_SERIAL_DEV_BUS is not set +CONFIG_SERIAL_EARLYCON=y +# CONFIG_SERIAL_EARLYCON_ARM_SEMIHOST is not set +# CONFIG_SERIAL_FSL_LINFLEXUART is not set +# CONFIG_SERIAL_FSL_LPUART is not set +# CONFIG_SERIAL_GRLIB_GAISLER_APBUART is not set +# CONFIG_SERIAL_IFX6X60 is not set +# CONFIG_SERIAL_JSM is not set +# CONFIG_SERIAL_MAX3100 is not set +# CONFIG_SERIAL_MAX310X is not set +# CONFIG_SERIAL_NONSTANDARD is not set +# CONFIG_SERIAL_OF_PLATFORM is not set +# CONFIG_SERIAL_OF_PLATFORM_NWPSERIAL is not set +# CONFIG_SERIAL_PCH_UART is not set +# CONFIG_SERIAL_RP2 is not set +# CONFIG_SERIAL_SC16IS7XX is not set +# CONFIG_SERIAL_SCCNXP is not set +# CONFIG_SERIAL_SH_SCI is not set +# CONFIG_SERIAL_SIFIVE is not set +# CONFIG_SERIAL_SPRD is not set +# CONFIG_SERIAL_STM32 is not set +# CONFIG_SERIAL_ST_ASC is not set +# CONFIG_SERIAL_TIMBERDALE is not set +# CONFIG_SERIAL_UARTLITE is not set +# CONFIG_SERIAL_XILINX_PS_UART is not set +# CONFIG_SERIO is not set +# CONFIG_SERIO_ALTERA_PS2 is not set +# CONFIG_SERIO_AMBAKMI is not set +# CONFIG_SERIO_APBPS2 is not set +# CONFIG_SERIO_ARC_PS2 is not set +# CONFIG_SERIO_CT82C710 is not set +# CONFIG_SERIO_GPIO_PS2 is not set +# CONFIG_SERIO_I8042 is not set +# CONFIG_SERIO_LIBPS2 is not set +# CONFIG_SERIO_PARKBD is not set +# CONFIG_SERIO_PCIPS2 is not set +# CONFIG_SERIO_PS2MULT is not set +# CONFIG_SERIO_RAW is not set +# CONFIG_SERIO_SERPORT is not set +# CONFIG_SERIO_SUN4I_PS2 is not set +# CONFIG_SFC is not set +# CONFIG_SFC_FALCON is not set +# CONFIG_SFI is not set +# CONFIG_SFP is not set +# CONFIG_SF_PDMA is not set +# CONFIG_SGETMASK_SYSCALL is not set +# CONFIG_SGI_IOC4 is not set +# CONFIG_SGI_IP22 is not set +# CONFIG_SGI_IP27 is not set +# CONFIG_SGI_IP28 is not set +# CONFIG_SGI_IP30 is not set +# CONFIG_SGI_IP32 is not set +# CONFIG_SGI_MFD_IOC3 is not set +# CONFIG_SGI_PARTITION is not set +# CONFIG_SG_POOL is not set +# CONFIG_SG_SPLIT is not set +CONFIG_SHMEM=y +# CONFIG_SHUFFLE_PAGE_ALLOCATOR is not set +# CONFIG_SH_ETH is not set +# CONFIG_SH_TIMER_CMT is not set +# CONFIG_SH_TIMER_MTU2 is not set +# CONFIG_SH_TIMER_TMU is not set +# CONFIG_SI1133 is not set +# CONFIG_SI1145 is not set +# CONFIG_SI7005 is not set +# CONFIG_SI7020 is not set +# CONFIG_SIBYTE_BIGSUR is not set +# CONFIG_SIBYTE_CARMEL is not set +# CONFIG_SIBYTE_CRHINE is not set +# CONFIG_SIBYTE_CRHONE is not set +# CONFIG_SIBYTE_LITTLESUR is not set +# CONFIG_SIBYTE_RHONE is not set +# CONFIG_SIBYTE_SENTOSA is not set +# CONFIG_SIBYTE_SWARM is not set +CONFIG_SIGNALFD=y +# CONFIG_SIGNED_PE_FILE_VERIFICATION is not set +# CONFIG_SIMPLE_GPIO is not set +# CONFIG_SIMPLE_PM_BUS is not set +# CONFIG_SIOX is not set +# CONFIG_SIS190 is not set +# CONFIG_SIS900 is not set +# CONFIG_SKGE is not set +# CONFIG_SKY2 is not set +# CONFIG_SKY2_DEBUG is not set +# CONFIG_SLAB is not set +CONFIG_SLABINFO=y +# CONFIG_SLAB_FREELIST_HARDENED is not set +# CONFIG_SLAB_FREELIST_RANDOM is not set +CONFIG_SLAB_MERGE_DEFAULT=y +# CONFIG_SLHC is not set +# CONFIG_SLICOSS is not set +# CONFIG_SLIMBUS is not set +# CONFIG_SLIP is not set +# CONFIG_SLOB is not set +CONFIG_SLUB=y +CONFIG_SLUB_CPU_PARTIAL=y +# CONFIG_SLUB_DEBUG is not set +# CONFIG_SLUB_DEBUG_ON is not set +# CONFIG_SLUB_MEMCG_SYSFS_ON is not set +# CONFIG_SLUB_STATS is not set +# CONFIG_SMARTJOYPLUS_FF is not set +# CONFIG_SMB_SERVER is not set +# CONFIG_SMC911X is not set +# CONFIG_SMC9194 is not set +# CONFIG_SMC91X is not set +# CONFIG_SMP is not set +# CONFIG_SMSC911X is not set +# CONFIG_SMSC9420 is not set +# CONFIG_SMSC_PHY is not set +# CONFIG_SMS_SDIO_DRV is not set +# CONFIG_SMS_USB_DRV is not set +# CONFIG_SM_CAMCC_8250 is not set +# CONFIG_SM_FTL is not set +# CONFIG_SM_GCC_6115 is not set +# CONFIG_SM_GCC_6125 is not set +# CONFIG_SM_GCC_6350 is not set +# CONFIG_SM_GCC_8350 is not set +# CONFIG_SND is not set +# CONFIG_SND_AC97_POWER_SAVE is not set +# CONFIG_SND_AD1816A is not set +# CONFIG_SND_AD1848 is not set +# CONFIG_SND_AD1889 is not set +# CONFIG_SND_ADLIB is not set +# CONFIG_SND_ALI5451 is not set +# CONFIG_SND_ALOOP is not set +# CONFIG_SND_ALS100 is not set +# CONFIG_SND_ALS300 is not set +# CONFIG_SND_ALS4000 is not set +# CONFIG_SND_ARM is not set +# CONFIG_SND_ASIHPI is not set +# CONFIG_SND_ATIIXP is not set +# CONFIG_SND_ATIIXP_MODEM is not set +# CONFIG_SND_ATMEL_AC97C is not set +# CONFIG_SND_ATMEL_SOC is not set +# CONFIG_SND_AU8810 is not set +# CONFIG_SND_AU8820 is not set +# CONFIG_SND_AU8830 is not set +# CONFIG_SND_AUDIO_GRAPH_CARD is not set +# CONFIG_SND_AUDIO_GRAPH_SCU_CARD is not set +# CONFIG_SND_AW2 is not set +# CONFIG_SND_AZT2320 is not set +# CONFIG_SND_AZT3328 is not set +# CONFIG_SND_BCD2000 is not set +# CONFIG_SND_BCM63XX_I2S_WHISTLER is not set +# CONFIG_SND_BT87X is not set +# CONFIG_SND_CA0106 is not set +# CONFIG_SND_CMI8330 is not set +# CONFIG_SND_CMIPCI is not set +# CONFIG_SND_CS4231 is not set +# CONFIG_SND_CS4236 is not set +# CONFIG_SND_CS4281 is not set +# CONFIG_SND_CS46XX is not set +# CONFIG_SND_CS5530 is not set +# CONFIG_SND_CS5535AUDIO is not set +# CONFIG_SND_CTXFI is not set +# CONFIG_SND_DARLA20 is not set +# CONFIG_SND_DARLA24 is not set +# CONFIG_SND_DEBUG is not set +# CONFIG_SND_DESIGNWARE_I2S is not set +CONFIG_SND_DRIVERS=y +# CONFIG_SND_DUMMY is not set +# CONFIG_SND_DYNAMIC_MINORS is not set +# CONFIG_SND_ECHO3G is not set +# CONFIG_SND_EDMA_SOC is not set +# CONFIG_SND_EMU10K1 is not set +# CONFIG_SND_EMU10K1X is not set +# CONFIG_SND_EMU10K1_SEQ is not set +# CONFIG_SND_ENS1370 is not set +# CONFIG_SND_ENS1371 is not set +# CONFIG_SND_ES1688 is not set +# CONFIG_SND_ES18XX is not set +# CONFIG_SND_ES1938 is not set +# CONFIG_SND_ES1968 is not set +# CONFIG_SND_FIREWIRE is not set +# CONFIG_SND_FM801 is not set +# CONFIG_SND_GINA20 is not set +# CONFIG_SND_GINA24 is not set +# CONFIG_SND_GUSCLASSIC is not set +# CONFIG_SND_GUSEXTREME is not set +# CONFIG_SND_GUSMAX is not set +# CONFIG_SND_HDA_CODEC_CS8409 is not set +# CONFIG_SND_HDA_INTEL is not set +# CONFIG_SND_HDA_INTEL_DETECT_DMIC is not set +# CONFIG_SND_HDA_INTEL_HDMI_SILENT_STREAM is not set +CONFIG_SND_HDA_POWER_SAVE_DEFAULT=0 +CONFIG_SND_HDA_PREALLOC_SIZE=64 +# CONFIG_SND_HDSP is not set +# CONFIG_SND_HDSPM is not set +# CONFIG_SND_HRTIMER is not set +# CONFIG_SND_HWDEP is not set +# CONFIG_SND_I2S_HI6210_I2S is not set +# CONFIG_SND_ICE1712 is not set +# CONFIG_SND_ICE1724 is not set +# CONFIG_SND_INDIGO is not set +# CONFIG_SND_INDIGODJ is not set +# CONFIG_SND_INDIGODJX is not set +# CONFIG_SND_INDIGOIO is not set +# CONFIG_SND_INDIGOIOX is not set +# CONFIG_SND_INTEL8X0 is not set +# CONFIG_SND_INTEL8X0M is not set +# CONFIG_SND_INTERWAVE is not set +# CONFIG_SND_INTERWAVE_STB is not set +# CONFIG_SND_ISA is not set +# CONFIG_SND_JZ4740_SOC_I2S is not set +# CONFIG_SND_KIRKWOOD_SOC is not set +# CONFIG_SND_KORG1212 is not set +# CONFIG_SND_LAYLA20 is not set +# CONFIG_SND_LAYLA24 is not set +# CONFIG_SND_LOLA is not set +# CONFIG_SND_LX6464ES is not set +# CONFIG_SND_MAESTRO3 is not set +CONFIG_SND_MAX_CARDS=16 +# CONFIG_SND_MIA is not set +# CONFIG_SND_MIPS is not set +# CONFIG_SND_MIRO is not set +# CONFIG_SND_MIXART is not set +# CONFIG_SND_MIXER_OSS is not set +# CONFIG_SND_MONA is not set +# CONFIG_SND_MPC52xx_SOC_EFIKA is not set +# CONFIG_SND_MPU401 is not set +# CONFIG_SND_MTPAV is not set +# CONFIG_SND_MTS64 is not set +# CONFIG_SND_MXS_SOC is not set +# CONFIG_SND_NM256 is not set +# CONFIG_SND_OPL3SA2 is not set +# CONFIG_SND_OPL3_LIB_SEQ is not set +# CONFIG_SND_OPL4_LIB_SEQ is not set +# CONFIG_SND_OPTI92X_AD1848 is not set +# CONFIG_SND_OPTI92X_CS4231 is not set +# CONFIG_SND_OPTI93X is not set +CONFIG_SND_OSSEMUL=y +# CONFIG_SND_OXYGEN is not set +CONFIG_SND_PCI=y +# CONFIG_SND_PCM is not set +# CONFIG_SND_PCMCIA is not set +# CONFIG_SND_PCM_OSS is not set +CONFIG_SND_PCM_OSS_PLUGINS=y +# CONFIG_SND_PCM_TIMER is not set +# CONFIG_SND_PCM_XRUN_DEBUG is not set +# CONFIG_SND_PCXHR is not set +# CONFIG_SND_PDAUDIOCF is not set +# CONFIG_SND_PORTMAN2X4 is not set +# CONFIG_SND_POWERPC_SOC is not set +# CONFIG_SND_PPC is not set +CONFIG_SND_PROC_FS=y +# CONFIG_SND_RAWMIDI is not set +# CONFIG_SND_RAWMIDI_SEQ is not set +# CONFIG_SND_RIPTIDE is not set +# CONFIG_SND_RME32 is not set +# CONFIG_SND_RME96 is not set +# CONFIG_SND_RME9652 is not set +# CONFIG_SND_RTCTIMER is not set +# CONFIG_SND_SB16 is not set +# CONFIG_SND_SB8 is not set +# CONFIG_SND_SBAWE is not set +# CONFIG_SND_SBAWE_SEQ is not set +# CONFIG_SND_SE6X is not set +# CONFIG_SND_SEQUENCER is not set +# CONFIG_SND_SERIAL_U16550 is not set +# CONFIG_SND_SIMPLE_CARD is not set +# CONFIG_SND_SIMPLE_SCU_CARD is not set +# CONFIG_SND_SIS7019 is not set +# CONFIG_SND_SOC is not set +# CONFIG_SND_SOC_AC97_CODEC is not set +# CONFIG_SND_SOC_AD193X_I2C is not set +# CONFIG_SND_SOC_AD193X_SPI is not set +# CONFIG_SND_SOC_ADAU1372_I2C is not set +# CONFIG_SND_SOC_ADAU1372_SPI is not set +# CONFIG_SND_SOC_ADAU1701 is not set +# CONFIG_SND_SOC_ADAU1761_I2C is not set +# CONFIG_SND_SOC_ADAU1761_SPI is not set +# CONFIG_SND_SOC_ADAU7002 is not set +# CONFIG_SND_SOC_ADAU7118_HW is not set +# CONFIG_SND_SOC_ADAU7118_I2C is not set +# CONFIG_SND_SOC_ADI is not set +# CONFIG_SND_SOC_AK4104 is not set +# CONFIG_SND_SOC_AK4118 is not set +# CONFIG_SND_SOC_AK4458 is not set +# CONFIG_SND_SOC_AK4554 is not set +# CONFIG_SND_SOC_AK4613 is not set +# CONFIG_SND_SOC_AK4642 is not set +# CONFIG_SND_SOC_AK5386 is not set +# CONFIG_SND_SOC_AK5558 is not set +# CONFIG_SND_SOC_ALC5623 is not set +# CONFIG_SND_SOC_AMD_ACP is not set +# CONFIG_SND_SOC_AMD_ACP3x is not set +# CONFIG_SND_SOC_AMD_ACP5x is not set +# CONFIG_SND_SOC_AMD_RENOIR is not set +# CONFIG_SND_SOC_AU1XAUDIO is not set +# CONFIG_SND_SOC_AU1XPSC is not set +# CONFIG_SND_SOC_BD28623 is not set +# CONFIG_SND_SOC_BT_SCO is not set +# CONFIG_SND_SOC_CS35L32 is not set +# CONFIG_SND_SOC_CS35L33 is not set +# CONFIG_SND_SOC_CS35L34 is not set +# CONFIG_SND_SOC_CS35L35 is not set +# CONFIG_SND_SOC_CS35L36 is not set +# CONFIG_SND_SOC_CS4234 is not set +# CONFIG_SND_SOC_CS4265 is not set +# CONFIG_SND_SOC_CS4270 is not set +# CONFIG_SND_SOC_CS4271 is not set +# CONFIG_SND_SOC_CS4271_I2C is not set +# CONFIG_SND_SOC_CS4271_SPI is not set +# CONFIG_SND_SOC_CS42L42 is not set +# CONFIG_SND_SOC_CS42L51_I2C is not set +# CONFIG_SND_SOC_CS42L52 is not set +# CONFIG_SND_SOC_CS42L56 is not set +# CONFIG_SND_SOC_CS42L73 is not set +# CONFIG_SND_SOC_CS42XX8_I2C is not set +# CONFIG_SND_SOC_CS43130 is not set +# CONFIG_SND_SOC_CS4341 is not set +# CONFIG_SND_SOC_CS4349 is not set +# CONFIG_SND_SOC_CS53L30 is not set +# CONFIG_SND_SOC_CX2072X is not set +# CONFIG_SND_SOC_DA7213 is not set +# CONFIG_SND_SOC_DIO2125 is not set +# CONFIG_SND_SOC_DMIC is not set +# CONFIG_SND_SOC_ES7134 is not set +# CONFIG_SND_SOC_ES7241 is not set +# CONFIG_SND_SOC_ES8316 is not set +# CONFIG_SND_SOC_ES8328 is not set +# CONFIG_SND_SOC_ES8328_I2C is not set +# CONFIG_SND_SOC_ES8328_SPI is not set +# CONFIG_SND_SOC_EUKREA_TLV320 is not set +# CONFIG_SND_SOC_FSL_ASOC_CARD is not set +# CONFIG_SND_SOC_FSL_ASRC is not set +# CONFIG_SND_SOC_FSL_AUD2HTX is not set +# CONFIG_SND_SOC_FSL_AUDMIX is not set +# CONFIG_SND_SOC_FSL_ESAI is not set +# CONFIG_SND_SOC_FSL_MICFIL is not set +# CONFIG_SND_SOC_FSL_RPMSG is not set +# CONFIG_SND_SOC_FSL_SAI is not set +# CONFIG_SND_SOC_FSL_SPDIF is not set +# CONFIG_SND_SOC_FSL_SSI is not set +# CONFIG_SND_SOC_FSL_XCVR is not set +# CONFIG_SND_SOC_GTM601 is not set +# CONFIG_SND_SOC_ICS43432 is not set +# CONFIG_SND_SOC_IMG is not set +# CONFIG_SND_SOC_IMX_AUDMIX is not set +# CONFIG_SND_SOC_IMX_AUDMUX is not set +# CONFIG_SND_SOC_IMX_CARD is not set +# CONFIG_SND_SOC_IMX_ES8328 is not set +# CONFIG_SND_SOC_IMX_HDMI is not set +# CONFIG_SND_SOC_IMX_RPMSG is not set +# CONFIG_SND_SOC_IMX_SPDIF is not set +# CONFIG_SND_SOC_IMX_WM8962 is not set +# CONFIG_SND_SOC_INNO_RK3036 is not set +# CONFIG_SND_SOC_INTEL_APL is not set +# CONFIG_SND_SOC_INTEL_BAYTRAIL is not set +# CONFIG_SND_SOC_INTEL_BDW_RT5677_MACH is not set +# CONFIG_SND_SOC_INTEL_BXT_DA7219_MAX98357A_MACH is not set +# CONFIG_SND_SOC_INTEL_BXT_RT298_MACH is not set +# CONFIG_SND_SOC_INTEL_BYTCR_RT5640_MACH is not set +# CONFIG_SND_SOC_INTEL_BYTCR_RT5651_MACH is not set +# CONFIG_SND_SOC_INTEL_BYT_CHT_DA7213_MACH is not set +# CONFIG_SND_SOC_INTEL_BYT_CHT_ES8316_MACH is not set +# CONFIG_SND_SOC_INTEL_BYT_CHT_NOCODEC_MACH is not set +# CONFIG_SND_SOC_INTEL_BYT_MAX98090_MACH is not set +# CONFIG_SND_SOC_INTEL_BYT_RT5640_MACH is not set +# CONFIG_SND_SOC_INTEL_CATPT is not set +# CONFIG_SND_SOC_INTEL_CFL is not set +# CONFIG_SND_SOC_INTEL_CHT_BSW_MAX98090_TI_MACH is not set +# CONFIG_SND_SOC_INTEL_CHT_BSW_NAU8824_MACH is not set +# CONFIG_SND_SOC_INTEL_CHT_BSW_RT5645_MACH is not set +# CONFIG_SND_SOC_INTEL_CHT_BSW_RT5672_MACH is not set +# CONFIG_SND_SOC_INTEL_CML_H is not set +# CONFIG_SND_SOC_INTEL_CML_LP is not set +# CONFIG_SND_SOC_INTEL_CNL is not set +# CONFIG_SND_SOC_INTEL_GLK is not set +# CONFIG_SND_SOC_INTEL_HASWELL is not set +# CONFIG_SND_SOC_INTEL_KBL is not set +# CONFIG_SND_SOC_INTEL_KBL_RT5663_MAX98927_MACH is not set +# CONFIG_SND_SOC_INTEL_KBL_RT5663_RT5514_MAX98927_MACH is not set +# CONFIG_SND_SOC_INTEL_KEEMBAY is not set +# CONFIG_SND_SOC_INTEL_SKL is not set +# CONFIG_SND_SOC_INTEL_SKL_NAU88L25_MAX98357A_MACH is not set +# CONFIG_SND_SOC_INTEL_SKL_NAU88L25_SSM4567_MACH is not set +# CONFIG_SND_SOC_INTEL_SKL_RT286_MACH is not set +# CONFIG_SND_SOC_INTEL_SKYLAKE is not set +# CONFIG_SND_SOC_INTEL_SST is not set +CONFIG_SND_SOC_INTEL_SST_TOPLEVEL=y +# CONFIG_SND_SOC_INTEL_USER_FRIENDLY_LONG_NAMES is not set +# CONFIG_SND_SOC_JZ4725B_CODEC is not set +# CONFIG_SND_SOC_JZ4740_CODEC is not set +# CONFIG_SND_SOC_JZ4770_CODEC is not set +# CONFIG_SND_SOC_LPASS_RX_MACRO is not set +# CONFIG_SND_SOC_LPASS_TX_MACRO is not set +# CONFIG_SND_SOC_LPASS_VA_MACRO is not set +# CONFIG_SND_SOC_LPASS_WSA_MACRO is not set +# CONFIG_SND_SOC_MA120X0P is not set +# CONFIG_SND_SOC_MAX9759 is not set +# CONFIG_SND_SOC_MAX98088 is not set +# CONFIG_SND_SOC_MAX98357A is not set +# CONFIG_SND_SOC_MAX98373 is not set +# CONFIG_SND_SOC_MAX98373_I2C is not set +# CONFIG_SND_SOC_MAX98390 is not set +# CONFIG_SND_SOC_MAX98504 is not set +# CONFIG_SND_SOC_MAX9860 is not set +# CONFIG_SND_SOC_MAX9867 is not set +# CONFIG_SND_SOC_MAX98927 is not set +# CONFIG_SND_SOC_MEDIATEK is not set +# CONFIG_SND_SOC_MPC5200_AC97 is not set +# CONFIG_SND_SOC_MPC5200_I2S is not set +# CONFIG_SND_SOC_MSM8916_WCD_ANALOG is not set +# CONFIG_SND_SOC_MSM8916_WCD_DIGITAL is not set +# CONFIG_SND_SOC_MT2701 is not set +# CONFIG_SND_SOC_MT6351 is not set +# CONFIG_SND_SOC_MT6358 is not set +# CONFIG_SND_SOC_MT6359 is not set +# CONFIG_SND_SOC_MT6359_ACCDET is not set +# CONFIG_SND_SOC_MT6660 is not set +# CONFIG_SND_SOC_MT6797 is not set +# CONFIG_SND_SOC_MT8173 is not set +# CONFIG_SND_SOC_MT8183 is not set +# CONFIG_SND_SOC_MT8192 is not set +# CONFIG_SND_SOC_MT8195 is not set +# CONFIG_SND_SOC_MTK_BTCVSD is not set +# CONFIG_SND_SOC_NAU8315 is not set +# CONFIG_SND_SOC_NAU8540 is not set +# CONFIG_SND_SOC_NAU8810 is not set +# CONFIG_SND_SOC_NAU8822 is not set +# CONFIG_SND_SOC_NAU8824 is not set +# CONFIG_SND_SOC_PCM1681 is not set +# CONFIG_SND_SOC_PCM1789_I2C is not set +# CONFIG_SND_SOC_PCM1792A is not set +# CONFIG_SND_SOC_PCM179X_I2C is not set +# CONFIG_SND_SOC_PCM179X_SPI is not set +# CONFIG_SND_SOC_PCM186X_I2C is not set +# CONFIG_SND_SOC_PCM186X_SPI is not set +# CONFIG_SND_SOC_PCM3060_I2C is not set +# CONFIG_SND_SOC_PCM3060_SPI is not set +# CONFIG_SND_SOC_PCM3168A_I2C is not set +# CONFIG_SND_SOC_PCM3168A_SPI is not set +# CONFIG_SND_SOC_PCM5102A is not set +# CONFIG_SND_SOC_PCM512x_I2C is not set +# CONFIG_SND_SOC_PCM512x_SPI is not set +# CONFIG_SND_SOC_QCOM is not set +# CONFIG_SND_SOC_RK3328 is not set +# CONFIG_SND_SOC_RK817 is not set +# CONFIG_SND_SOC_ROCKCHIP is not set +# CONFIG_SND_SOC_RT5616 is not set +# CONFIG_SND_SOC_RT5631 is not set +# CONFIG_SND_SOC_RT5640 is not set +# CONFIG_SND_SOC_RT5659 is not set +# CONFIG_SND_SOC_RT5677_SPI is not set +# CONFIG_SND_SOC_SGTL5000 is not set +# CONFIG_SND_SOC_SIMPLE_AMPLIFIER is not set +# CONFIG_SND_SOC_SIMPLE_MUX is not set +# CONFIG_SND_SOC_SIRF_AUDIO_CODEC is not set +# CONFIG_SND_SOC_SOF_TOPLEVEL is not set +# CONFIG_SND_SOC_SPDIF is not set +# CONFIG_SND_SOC_SSM2305 is not set +# CONFIG_SND_SOC_SSM2518 is not set +# CONFIG_SND_SOC_SSM2602_I2C is not set +# CONFIG_SND_SOC_SSM2602_SPI is not set +# CONFIG_SND_SOC_SSM4567 is not set +# CONFIG_SND_SOC_STA32X is not set +# CONFIG_SND_SOC_STA350 is not set +# CONFIG_SND_SOC_STI_SAS is not set +# CONFIG_SND_SOC_TAS2552 is not set +# CONFIG_SND_SOC_TAS2562 is not set +# CONFIG_SND_SOC_TAS2764 is not set +# CONFIG_SND_SOC_TAS2770 is not set +# CONFIG_SND_SOC_TAS5086 is not set +# CONFIG_SND_SOC_TAS571X is not set +# CONFIG_SND_SOC_TAS5720 is not set +# CONFIG_SND_SOC_TAS6424 is not set +# CONFIG_SND_SOC_TDA7419 is not set +# CONFIG_SND_SOC_TFA9879 is not set +# CONFIG_SND_SOC_TFA989X is not set +# CONFIG_SND_SOC_TLV320ADCX140 is not set +# CONFIG_SND_SOC_TLV320AIC23_I2C is not set +# CONFIG_SND_SOC_TLV320AIC23_SPI is not set +# CONFIG_SND_SOC_TLV320AIC31XX is not set +# CONFIG_SND_SOC_TLV320AIC32X4_I2C is not set +# CONFIG_SND_SOC_TLV320AIC32X4_SPI is not set +# CONFIG_SND_SOC_TLV320AIC3X is not set +# CONFIG_SND_SOC_TLV320AIC3X_I2C is not set +# CONFIG_SND_SOC_TLV320AIC3X_SPI is not set +# CONFIG_SND_SOC_TPA6130A2 is not set +# CONFIG_SND_SOC_TS3A227E is not set +# CONFIG_SND_SOC_TSCS42XX is not set +# CONFIG_SND_SOC_TSCS454 is not set +# CONFIG_SND_SOC_UDA1334 is not set +# CONFIG_SND_SOC_WM8510 is not set +# CONFIG_SND_SOC_WM8523 is not set +# CONFIG_SND_SOC_WM8524 is not set +# CONFIG_SND_SOC_WM8580 is not set +# CONFIG_SND_SOC_WM8711 is not set +# CONFIG_SND_SOC_WM8728 is not set +# CONFIG_SND_SOC_WM8731 is not set +# CONFIG_SND_SOC_WM8737 is not set +# CONFIG_SND_SOC_WM8741 is not set +# CONFIG_SND_SOC_WM8750 is not set +# CONFIG_SND_SOC_WM8753 is not set +# CONFIG_SND_SOC_WM8770 is not set +# CONFIG_SND_SOC_WM8776 is not set +# CONFIG_SND_SOC_WM8782 is not set +# CONFIG_SND_SOC_WM8804_I2C is not set +# CONFIG_SND_SOC_WM8804_SPI is not set +# CONFIG_SND_SOC_WM8903 is not set +# CONFIG_SND_SOC_WM8904 is not set +# CONFIG_SND_SOC_WM8960 is not set +# CONFIG_SND_SOC_WM8962 is not set +# CONFIG_SND_SOC_WM8974 is not set +# CONFIG_SND_SOC_WM8978 is not set +# CONFIG_SND_SOC_WM8985 is not set +# CONFIG_SND_SOC_XILINX_AUDIO_FORMATTER is not set +# CONFIG_SND_SOC_XILINX_I2S is not set +# CONFIG_SND_SOC_XILINX_SPDIF is not set +# CONFIG_SND_SOC_XTFPGA_I2S is not set +# CONFIG_SND_SOC_ZL38060 is not set +# CONFIG_SND_SOC_ZX_AUD96P22 is not set +# CONFIG_SND_SONICVIBES is not set +# CONFIG_SND_SPI is not set +# CONFIG_SND_SSCAPE is not set +# CONFIG_SND_SST_ATOM_HIFI2_PLATFORM_ACPI is not set +# CONFIG_SND_SST_ATOM_HIFI2_PLATFORM_PCI is not set +# CONFIG_SND_SUN4I_CODEC is not set +# CONFIG_SND_SUPPORT_OLD_API is not set +# CONFIG_SND_TIMER is not set +# CONFIG_SND_TRIDENT is not set +CONFIG_SND_USB=y +# CONFIG_SND_USB_6FIRE is not set +# CONFIG_SND_USB_AUDIO is not set +# CONFIG_SND_USB_CAIAQ is not set +# CONFIG_SND_USB_HIFACE is not set +# CONFIG_SND_USB_POD is not set +# CONFIG_SND_USB_PODHD is not set +# CONFIG_SND_USB_TONEPORT is not set +# CONFIG_SND_USB_UA101 is not set +# CONFIG_SND_USB_US122L is not set +# CONFIG_SND_USB_USX2Y is not set +# CONFIG_SND_USB_VARIAX is not set +# CONFIG_SND_VERBOSE_PRINTK is not set +CONFIG_SND_VERBOSE_PROCFS=y +# CONFIG_SND_VIA82XX is not set +# CONFIG_SND_VIA82XX_MODEM is not set +# CONFIG_SND_VIRTIO is not set +# CONFIG_SND_VIRTUOSO is not set +# CONFIG_SND_VX222 is not set +# CONFIG_SND_VXPOCKET is not set +# CONFIG_SND_WAVEFRONT is not set +CONFIG_SND_X86=y +# CONFIG_SND_XEN_FRONTEND is not set +# CONFIG_SND_YMFPCI is not set +# CONFIG_SNI_RM is not set +# CONFIG_SOCIONEXT_SYNQUACER_PREITS is not set +# CONFIG_SOCK_CGROUP_DATA is not set +# CONFIG_SOC_AM33XX is not set +# CONFIG_SOC_AM43XX is not set +# CONFIG_SOC_BRCMSTB is not set +# CONFIG_SOC_CAMERA is not set +# CONFIG_SOC_DRA7XX is not set +# CONFIG_SOC_HAS_OMAP2_SDRC is not set +# CONFIG_SOC_OMAP5 is not set +# CONFIG_SOC_TI is not set +# CONFIG_SOFTLOCKUP_DETECTOR is not set +# CONFIG_SOFT_WATCHDOG is not set +# CONFIG_SOLARIS_X86_PARTITION is not set +# CONFIG_SONYPI is not set +# CONFIG_SONY_LAPTOP is not set +# CONFIG_SOUND is not set +# CONFIG_SOUNDWIRE is not set +# CONFIG_SOUND_OSS_CORE is not set +# CONFIG_SOUND_OSS_CORE_PRECLAIM is not set +# CONFIG_SOUND_PRIME is not set +# CONFIG_SP5100_TCO is not set +# CONFIG_SPARSEMEM_MANUAL is not set +# CONFIG_SPARSEMEM_STATIC is not set +# CONFIG_SPARSEMEM_VMEMMAP_ENABLE is not set +# CONFIG_SPARSE_IRQ is not set +# CONFIG_SPARSE_RCU_POINTER is not set +# CONFIG_SPEAKUP is not set +# CONFIG_SPI is not set +# CONFIG_SPINLOCK_TEST is not set +# CONFIG_SPI_ALTERA is not set +# CONFIG_SPI_AMD is not set +# CONFIG_SPI_AU1550 is not set +# CONFIG_SPI_AXI_SPI_ENGINE is not set +# CONFIG_SPI_BCM2835 is not set +# CONFIG_SPI_BCM_QSPI is not set +# CONFIG_SPI_BITBANG is not set +# CONFIG_SPI_BUTTERFLY is not set +# CONFIG_SPI_CADENCE is not set +# CONFIG_SPI_CADENCE_QUADSPI is not set +# CONFIG_SPI_DEBUG is not set +# CONFIG_SPI_DESIGNWARE is not set +# CONFIG_SPI_FSL_DSPI is not set +# CONFIG_SPI_FSL_ESPI is not set +# CONFIG_SPI_FSL_SPI is not set +# CONFIG_SPI_GPIO is not set +# CONFIG_SPI_GPIO_OLD is not set +# CONFIG_SPI_IMG_SPFI is not set +# CONFIG_SPI_LANTIQ_SSC is not set +# CONFIG_SPI_LM70_LLP is not set +# CONFIG_SPI_LOOPBACK_TEST is not set +# CONFIG_SPI_MASTER is not set +# CONFIG_SPI_MEM is not set +# CONFIG_SPI_MPC52xx is not set +# CONFIG_SPI_MPC52xx_PSC is not set +# CONFIG_SPI_MTK_QUADSPI is not set +# CONFIG_SPI_MUX is not set +# CONFIG_SPI_MXIC is not set +# CONFIG_SPI_NXP_FLEXSPI is not set +# CONFIG_SPI_OCTEON is not set +# CONFIG_SPI_OC_TINY is not set +# CONFIG_SPI_ORION is not set +# CONFIG_SPI_PL022 is not set +# CONFIG_SPI_PPC4xx is not set +# CONFIG_SPI_PXA2XX is not set +# CONFIG_SPI_PXA2XX_PCI is not set +# CONFIG_SPI_QCOM_QSPI is not set +# CONFIG_SPI_ROCKCHIP is not set +# CONFIG_SPI_S3C64XX is not set +# CONFIG_SPI_SC18IS602 is not set +# CONFIG_SPI_SIFIVE is not set +# CONFIG_SPI_SLAVE is not set +# CONFIG_SPI_SPIDEV is not set +# CONFIG_SPI_THUNDERX is not set +# CONFIG_SPI_TI_QSPI is not set +# CONFIG_SPI_TLE62X0 is not set +# CONFIG_SPI_TOPCLIFF_PCH is not set +# CONFIG_SPI_XCOMM is not set +# CONFIG_SPI_XILINX is not set +# CONFIG_SPI_XWAY is not set +# CONFIG_SPI_ZYNQMP_GQSPI is not set +CONFIG_SPLIT_PTLOCK_CPUS=4 +# CONFIG_SPMI is not set +# CONFIG_SPS30 is not set +# CONFIG_SPS30_I2C is not set +# CONFIG_SPS30_SERIAL is not set +CONFIG_SQUASHFS=y +# CONFIG_SQUASHFS_4K_DEVBLK_SIZE is not set +# CONFIG_SQUASHFS_DECOMP_MULTI is not set +CONFIG_SQUASHFS_DECOMP_MULTI_PERCPU=y +# CONFIG_SQUASHFS_DECOMP_SINGLE is not set +CONFIG_SQUASHFS_EMBEDDED=y +# CONFIG_SQUASHFS_FILE_CACHE is not set +CONFIG_SQUASHFS_FILE_DIRECT=y +CONFIG_SQUASHFS_FRAGMENT_CACHE_SIZE=3 +# CONFIG_SQUASHFS_LZ4 is not set +# CONFIG_SQUASHFS_LZO is not set +# CONFIG_SQUASHFS_XATTR is not set +CONFIG_SQUASHFS_XZ=y +# CONFIG_SQUASHFS_ZLIB is not set +# CONFIG_SQUASHFS_ZSTD is not set +# CONFIG_SRAM is not set +# CONFIG_SRF04 is not set +# CONFIG_SRF08 is not set +# CONFIG_SSB is not set +# CONFIG_SSB_DEBUG is not set +# CONFIG_SSB_DRIVER_GPIO is not set +# CONFIG_SSB_HOST_SOC is not set +# CONFIG_SSB_PCMCIAHOST is not set +CONFIG_SSB_POSSIBLE=y +# CONFIG_SSB_SDIOHOST is not set +# CONFIG_SSB_SILENT is not set +# CONFIG_SSFDC is not set +# CONFIG_STACKPROTECTOR is not set +# CONFIG_STACKPROTECTOR_STRONG is not set +# CONFIG_STACKTRACE is not set +# CONFIG_STACKTRACE_BUILD_ID is not set +CONFIG_STACKTRACE_SUPPORT=y +# CONFIG_STACK_TRACER is not set +# CONFIG_STACK_VALIDATION is not set +CONFIG_STAGING=y +# CONFIG_STAGING_BOARD is not set +# CONFIG_STAGING_GASKET_FRAMEWORK is not set +# CONFIG_STAGING_MEDIA is not set +CONFIG_STANDALONE=y +# CONFIG_STATIC_KEYS_SELFTEST is not set +# CONFIG_STATIC_USERMODEHELPER is not set +CONFIG_STDBINUTILS=y +# CONFIG_STE10XP is not set +# CONFIG_STE_MODEM_RPROC is not set +# CONFIG_STK3310 is not set +# CONFIG_STK8312 is not set +# CONFIG_STK8BA50 is not set +# CONFIG_STM is not set +# CONFIG_STMMAC_ETH is not set +# CONFIG_STMMAC_PCI is not set +# CONFIG_STMMAC_PLATFORM is not set +# CONFIG_STMMAC_SELFTESTS is not set +# CONFIG_STM_DUMMY is not set +# CONFIG_STM_SOURCE_CONSOLE is not set +CONFIG_STP=y +# CONFIG_STREAM_PARSER is not set +# CONFIG_STRICT_DEVMEM is not set +CONFIG_STRICT_KERNEL_RWX=y +CONFIG_STRICT_MODULE_RWX=y +# CONFIG_STRING_SELFTEST is not set +CONFIG_STRIP_ASM_SYMS=y +# CONFIG_STX104 is not set +# CONFIG_ST_UVIS25 is not set +# CONFIG_SUN4I_GPADC is not set +# CONFIG_SUN50I_DE2_BUS is not set +# CONFIG_SUN50I_ERRATUM_UNKNOWN1 is not set +# CONFIG_SUNDANCE is not set +# CONFIG_SUNGEM is not set +# CONFIG_SUNRPC is not set +# CONFIG_SUNRPC_DEBUG is not set +CONFIG_SUNRPC_DISABLE_INSECURE_ENCTYPES=y +# CONFIG_SUNRPC_GSS is not set +# CONFIG_SUNXI_SRAM is not set +# CONFIG_SUN_PARTITION is not set +# CONFIG_SURFACE_3_BUTTON is not set +# CONFIG_SUSPEND is not set +# CONFIG_SUSPEND_SKIP_SYNC is not set +CONFIG_SWAP=y +# CONFIG_SWCONFIG is not set +# CONFIG_SWCONFIG_B53 is not set +# CONFIG_SWCONFIG_B53_MDIO_DRIVER is not set +# CONFIG_SWCONFIG_B53_MMAP_DRIVER is not set +# CONFIG_SWCONFIG_B53_SPI_DRIVER is not set +# CONFIG_SWCONFIG_B53_SRAB_DRIVER is not set +# CONFIG_SWCONFIG_LEDS is not set +# CONFIG_SW_SYNC is not set +# CONFIG_SX9310 is not set +# CONFIG_SX9500 is not set +# CONFIG_SXGBE_ETH is not set +CONFIG_SYMBOLIC_ERRNAME=y +# CONFIG_SYNCLINK_CS is not set +# CONFIG_SYNC_FILE is not set +# CONFIG_SYNOPSYS_DWC_ETH_QOS is not set +# CONFIG_SYNTH_EVENTS is not set +CONFIG_SYN_COOKIES=y +# CONFIG_SYSCON_REBOOT_MODE is not set +CONFIG_SYSCTL=y +# CONFIG_SYSCTL_SYSCALL is not set +CONFIG_SYSFS=y +# CONFIG_SYSFS_DEPRECATED is not set +# CONFIG_SYSFS_DEPRECATED_V2 is not set +# CONFIG_SYSFS_SYSCALL is not set +# CONFIG_SYSTEMPORT is not set +# CONFIG_SYSTEM_BLACKLIST_KEYRING is not set +# CONFIG_SYSTEM_DATA_VERIFICATION is not set +# CONFIG_SYSTEM_TRUSTED_KEYRING is not set +CONFIG_SYSTEM_TRUSTED_KEYS="" +# CONFIG_SYSV68_PARTITION is not set +CONFIG_SYSVIPC=y +CONFIG_SYSVIPC_SYSCTL=y +# CONFIG_SYSV_FS is not set +# CONFIG_SYS_HYPERVISOR is not set +# CONFIG_T5403 is not set +# CONFIG_TARGET_CORE is not set +# CONFIG_TASKSTATS is not set +# CONFIG_TASKS_RCU is not set +# CONFIG_TASK_XACCT is not set +# CONFIG_TC35815 is not set +# CONFIG_TCG_ATMEL is not set +# CONFIG_TCG_CRB is not set +# CONFIG_TCG_FTPM_TEE is not set +# CONFIG_TCG_INFINEON is not set +# CONFIG_TCG_NSC is not set +# CONFIG_TCG_ST33_I2C is not set +# CONFIG_TCG_TIS is not set +# CONFIG_TCG_TIS_I2C_ATMEL is not set +# CONFIG_TCG_TIS_I2C_CR50 is not set +# CONFIG_TCG_TIS_I2C_INFINEON is not set +# CONFIG_TCG_TIS_I2C_NUVOTON is not set +# CONFIG_TCG_TIS_SPI is not set +# CONFIG_TCG_TIS_ST33ZP24_I2C is not set +# CONFIG_TCG_TIS_ST33ZP24_SPI is not set +# CONFIG_TCG_TPM is not set +# CONFIG_TCG_VTPM_PROXY is not set +# CONFIG_TCG_XEN is not set +# CONFIG_TCIC is not set +CONFIG_TCP_CONG_ADVANCED=y +# CONFIG_TCP_CONG_BBR is not set +# CONFIG_TCP_CONG_BIC is not set +# CONFIG_TCP_CONG_CDG is not set +CONFIG_TCP_CONG_CUBIC=y +# CONFIG_TCP_CONG_DCTCP is not set +# CONFIG_TCP_CONG_HSTCP is not set +# CONFIG_TCP_CONG_HTCP is not set +# CONFIG_TCP_CONG_HYBLA is not set +# CONFIG_TCP_CONG_ILLINOIS is not set +# CONFIG_TCP_CONG_LP is not set +# CONFIG_TCP_CONG_NV is not set +# CONFIG_TCP_CONG_SCALABLE is not set +# CONFIG_TCP_CONG_VEGAS is not set +# CONFIG_TCP_CONG_VENO is not set +# CONFIG_TCP_CONG_WESTWOOD is not set +# CONFIG_TCP_CONG_YEAH is not set +# CONFIG_TCP_MD5SIG is not set +# CONFIG_TCS3414 is not set +# CONFIG_TCS3472 is not set +# CONFIG_TEE is not set +# CONFIG_TEGRA_AHB is not set +# CONFIG_TEGRA_HOST1X is not set +# CONFIG_TEHUTI is not set +# CONFIG_TERANETICS_PHY is not set +# CONFIG_TEST_ASYNC_DRIVER_PROBE is not set +# CONFIG_TEST_BITFIELD is not set +# CONFIG_TEST_BITMAP is not set +# CONFIG_TEST_BITOPS is not set +# CONFIG_TEST_BLACKHOLE_DEV is not set +# CONFIG_TEST_BPF is not set +# CONFIG_TEST_CLOCKSOURCE_WATCHDOG is not set +# CONFIG_TEST_DIV64 is not set +# CONFIG_TEST_FIRMWARE is not set +# CONFIG_TEST_FREE_PAGES is not set +# CONFIG_TEST_HASH is not set +# CONFIG_TEST_HEXDUMP is not set +# CONFIG_TEST_IDA is not set +# CONFIG_TEST_KASAN_MODULE is not set +# CONFIG_TEST_KMOD is not set +# CONFIG_TEST_KSTRTOX is not set +# CONFIG_TEST_LIST_SORT is not set +# CONFIG_TEST_LKM is not set +# CONFIG_TEST_LOCKUP is not set +# CONFIG_TEST_MEMCAT_P is not set +# CONFIG_TEST_MEMINIT is not set +# CONFIG_TEST_MIN_HEAP is not set +# CONFIG_TEST_OVERFLOW is not set +# CONFIG_TEST_POWER is not set +# CONFIG_TEST_PRINTF is not set +# CONFIG_TEST_RHASHTABLE is not set +# CONFIG_TEST_SCANF is not set +# CONFIG_TEST_SORT is not set +# CONFIG_TEST_STACKINIT is not set +# CONFIG_TEST_STATIC_KEYS is not set +# CONFIG_TEST_STRING_HELPERS is not set +# CONFIG_TEST_STRSCPY is not set +# CONFIG_TEST_SYSCTL is not set +# CONFIG_TEST_UBSAN is not set +# CONFIG_TEST_UDELAY is not set +# CONFIG_TEST_USER_COPY is not set +# CONFIG_TEST_UUID is not set +# CONFIG_TEST_VMALLOC is not set +# CONFIG_TEST_XARRAY is not set +CONFIG_TEXTSEARCH=y +# CONFIG_TEXTSEARCH_BM is not set +# CONFIG_TEXTSEARCH_FSM is not set +# CONFIG_TEXTSEARCH_KMP is not set +# CONFIG_THERMAL is not set +# CONFIG_THERMAL_DEFAULT_GOV_FAIR_SHARE is not set +# CONFIG_THERMAL_DEFAULT_GOV_POWER_ALLOCATOR is not set +# CONFIG_THERMAL_DEFAULT_GOV_USER_SPACE is not set +# CONFIG_THERMAL_EMULATION is not set +# CONFIG_THERMAL_GOV_BANG_BANG is not set +# CONFIG_THERMAL_GOV_FAIR_SHARE is not set +# CONFIG_THERMAL_GOV_POWER_ALLOCATOR is not set +# CONFIG_THERMAL_GOV_USER_SPACE is not set +# CONFIG_THERMAL_HWMON is not set +# CONFIG_THERMAL_MMIO is not set +# CONFIG_THERMAL_NETLINK is not set +# CONFIG_THERMAL_STATISTICS is not set +# CONFIG_THERMAL_WRITABLE_TRIPS is not set +# CONFIG_THINKPAD_ACPI is not set +CONFIG_THIN_ARCHIVES=y +# CONFIG_THRUSTMASTER_FF is not set +# CONFIG_THUMB2_KERNEL is not set +# CONFIG_THUNDERBOLT is not set +# CONFIG_THUNDER_NIC_BGX is not set +# CONFIG_THUNDER_NIC_PF is not set +# CONFIG_THUNDER_NIC_RGX is not set +# CONFIG_THUNDER_NIC_VF is not set +# CONFIG_TICK_CPU_ACCOUNTING is not set +CONFIG_TICK_ONESHOT=y +CONFIG_TIERS_PER_GEN=4 +# CONFIG_TIFM_CORE is not set +# CONFIG_TIGON3 is not set +# CONFIG_TIMB_DMA is not set +CONFIG_TIMERFD=y +# CONFIG_TIMERLAT_TRACER is not set +# CONFIG_TIMER_STATS is not set +# CONFIG_TIME_NS is not set +# CONFIG_TINYDRM_HX8357D is not set +# CONFIG_TINYDRM_ILI9225 is not set +# CONFIG_TINYDRM_ILI9341 is not set +# CONFIG_TINYDRM_ILI9486 is not set +# CONFIG_TINYDRM_MI0283QT is not set +# CONFIG_TINYDRM_REPAPER is not set +# CONFIG_TINYDRM_ST7586 is not set +# CONFIG_TINYDRM_ST7735R is not set +CONFIG_TINY_RCU=y +# CONFIG_TIPC is not set +# CONFIG_TI_ADC081C is not set +# CONFIG_TI_ADC0832 is not set +# CONFIG_TI_ADC084S021 is not set +# CONFIG_TI_ADC108S102 is not set +# CONFIG_TI_ADC12138 is not set +# CONFIG_TI_ADC128S052 is not set +# CONFIG_TI_ADC161S626 is not set +# CONFIG_TI_ADS1015 is not set +# CONFIG_TI_ADS124S08 is not set +# CONFIG_TI_ADS131E08 is not set +# CONFIG_TI_ADS7950 is not set +# CONFIG_TI_ADS8344 is not set +# CONFIG_TI_ADS8688 is not set +# CONFIG_TI_AM335X_ADC is not set +# CONFIG_TI_CPSW is not set +# CONFIG_TI_CPSW_ALE is not set +# CONFIG_TI_CPSW_PHY_SEL is not set +# CONFIG_TI_CPTS is not set +# CONFIG_TI_DAC082S085 is not set +# CONFIG_TI_DAC5571 is not set +# CONFIG_TI_DAC7311 is not set +# CONFIG_TI_DAC7512 is not set +# CONFIG_TI_DAC7612 is not set +# CONFIG_TI_DAVINCI_CPDMA is not set +# CONFIG_TI_DAVINCI_MDIO is not set +# CONFIG_TI_ST is not set +# CONFIG_TI_SYSCON_RESET is not set +# CONFIG_TI_TLC4541 is not set +# CONFIG_TI_TSC2046 is not set +# CONFIG_TLAN is not set +# CONFIG_TLS is not set +# CONFIG_TMD_HERMES is not set +# CONFIG_TMP006 is not set +# CONFIG_TMP007 is not set +# CONFIG_TMP117 is not set +CONFIG_TMPFS=y +# CONFIG_TMPFS_INODE64 is not set +# CONFIG_TMPFS_POSIX_ACL is not set +CONFIG_TMPFS_XATTR=y +# CONFIG_TOPSTAR_LAPTOP is not set +# CONFIG_TORTURE_TEST is not set +# CONFIG_TOSHIBA_HAPS is not set +# CONFIG_TOUCHSCREEN_88PM860X is not set +# CONFIG_TOUCHSCREEN_AD7877 is not set +# CONFIG_TOUCHSCREEN_AD7879 is not set +# CONFIG_TOUCHSCREEN_AD7879_I2C is not set +# CONFIG_TOUCHSCREEN_AD7879_SPI is not set +# CONFIG_TOUCHSCREEN_ADC is not set +# CONFIG_TOUCHSCREEN_ADS7846 is not set +# CONFIG_TOUCHSCREEN_AR1021_I2C is not set +# CONFIG_TOUCHSCREEN_ATMEL_MXT is not set +# CONFIG_TOUCHSCREEN_ATMEL_MXT_T37 is not set +# CONFIG_TOUCHSCREEN_AUO_PIXCIR is not set +# CONFIG_TOUCHSCREEN_BU21013 is not set +# CONFIG_TOUCHSCREEN_BU21029 is not set +# CONFIG_TOUCHSCREEN_CHIPONE_ICN8318 is not set +# CONFIG_TOUCHSCREEN_CHIPONE_ICN8505 is not set +# CONFIG_TOUCHSCREEN_COLIBRI_VF50 is not set +# CONFIG_TOUCHSCREEN_CY8CTMA140 is not set +# CONFIG_TOUCHSCREEN_CY8CTMG110 is not set +# CONFIG_TOUCHSCREEN_CYTTSP4_CORE is not set +# CONFIG_TOUCHSCREEN_CYTTSP4_I2C is not set +# CONFIG_TOUCHSCREEN_CYTTSP4_SPI is not set +# CONFIG_TOUCHSCREEN_CYTTSP_CORE is not set +# CONFIG_TOUCHSCREEN_CYTTSP_I2C is not set +# CONFIG_TOUCHSCREEN_CYTTSP_SPI is not set +# CONFIG_TOUCHSCREEN_DA9034 is not set +# CONFIG_TOUCHSCREEN_DA9052 is not set +# CONFIG_TOUCHSCREEN_DYNAPRO is not set +# CONFIG_TOUCHSCREEN_EDT_FT5X06 is not set +# CONFIG_TOUCHSCREEN_EETI is not set +# CONFIG_TOUCHSCREEN_EGALAX is not set +# CONFIG_TOUCHSCREEN_EGALAX_SERIAL is not set +# CONFIG_TOUCHSCREEN_EKTF2127 is not set +# CONFIG_TOUCHSCREEN_ELAN is not set +# CONFIG_TOUCHSCREEN_ELO is not set +# CONFIG_TOUCHSCREEN_EXC3000 is not set +# CONFIG_TOUCHSCREEN_FUJITSU is not set +# CONFIG_TOUCHSCREEN_GOODIX is not set +# CONFIG_TOUCHSCREEN_GUNZE is not set +# CONFIG_TOUCHSCREEN_HAMPSHIRE is not set +# CONFIG_TOUCHSCREEN_HIDEEP is not set +# CONFIG_TOUCHSCREEN_HP600 is not set +# CONFIG_TOUCHSCREEN_HP7XX is not set +# CONFIG_TOUCHSCREEN_HTCPEN is not set +# CONFIG_TOUCHSCREEN_HYCON_HY46XX is not set +# CONFIG_TOUCHSCREEN_ILI210X is not set +# CONFIG_TOUCHSCREEN_ILITEK is not set +# CONFIG_TOUCHSCREEN_IMX6UL_TSC is not set +# CONFIG_TOUCHSCREEN_INEXIO is not set +# CONFIG_TOUCHSCREEN_IPAQ_MICRO is not set +# CONFIG_TOUCHSCREEN_IPROC is not set +# CONFIG_TOUCHSCREEN_IQS5XX is not set +# CONFIG_TOUCHSCREEN_LPC32XX is not set +# CONFIG_TOUCHSCREEN_MAX11801 is not set +# CONFIG_TOUCHSCREEN_MC13783 is not set +# CONFIG_TOUCHSCREEN_MCS5000 is not set +# CONFIG_TOUCHSCREEN_MELFAS_MIP4 is not set +# CONFIG_TOUCHSCREEN_MIGOR is not set +# CONFIG_TOUCHSCREEN_MK712 is not set +# CONFIG_TOUCHSCREEN_MMS114 is not set +# CONFIG_TOUCHSCREEN_MSG2638 is not set +# CONFIG_TOUCHSCREEN_MTOUCH is not set +# CONFIG_TOUCHSCREEN_MX25 is not set +# CONFIG_TOUCHSCREEN_MXS_LRADC is not set +# CONFIG_TOUCHSCREEN_PCAP is not set +# CONFIG_TOUCHSCREEN_PENMOUNT is not set +# CONFIG_TOUCHSCREEN_PIXCIR is not set +# CONFIG_TOUCHSCREEN_PROPERTIES is not set +# CONFIG_TOUCHSCREEN_RASPBERRYPI_FW is not set +# CONFIG_TOUCHSCREEN_RM_TS is not set +# CONFIG_TOUCHSCREEN_ROHM_BU21023 is not set +# CONFIG_TOUCHSCREEN_RPI_FT5406 is not set +# CONFIG_TOUCHSCREEN_S3C2410 is not set +# CONFIG_TOUCHSCREEN_S6SY761 is not set +# CONFIG_TOUCHSCREEN_SILEAD is not set +# CONFIG_TOUCHSCREEN_SIS_I2C is not set +# CONFIG_TOUCHSCREEN_ST1232 is not set +# CONFIG_TOUCHSCREEN_STMFTS is not set +# CONFIG_TOUCHSCREEN_STMPE is not set +# CONFIG_TOUCHSCREEN_SUN4I is not set +# CONFIG_TOUCHSCREEN_SUR40 is not set +# CONFIG_TOUCHSCREEN_SURFACE3_SPI is not set +# CONFIG_TOUCHSCREEN_SX8654 is not set +# CONFIG_TOUCHSCREEN_TI_AM335X_TSC is not set +# CONFIG_TOUCHSCREEN_TOUCHIT213 is not set +# CONFIG_TOUCHSCREEN_TOUCHRIGHT is not set +# CONFIG_TOUCHSCREEN_TOUCHWIN is not set +# CONFIG_TOUCHSCREEN_TPS6507X is not set +# CONFIG_TOUCHSCREEN_TS4800 is not set +# CONFIG_TOUCHSCREEN_TSC2004 is not set +# CONFIG_TOUCHSCREEN_TSC2005 is not set +# CONFIG_TOUCHSCREEN_TSC2007 is not set +# CONFIG_TOUCHSCREEN_TSC2007_IIO is not set +# CONFIG_TOUCHSCREEN_TSC200X_CORE is not set +# CONFIG_TOUCHSCREEN_TSC_SERIO is not set +# CONFIG_TOUCHSCREEN_UCB1400 is not set +# CONFIG_TOUCHSCREEN_USB_3M is not set +# CONFIG_TOUCHSCREEN_USB_COMPOSITE is not set +# CONFIG_TOUCHSCREEN_USB_DMC_TSC10 is not set +# CONFIG_TOUCHSCREEN_USB_E2I is not set +# CONFIG_TOUCHSCREEN_USB_EASYTOUCH is not set +# CONFIG_TOUCHSCREEN_USB_EGALAX is not set +# CONFIG_TOUCHSCREEN_USB_ELO is not set +# CONFIG_TOUCHSCREEN_USB_ETT_TC45USB is not set +# CONFIG_TOUCHSCREEN_USB_ETURBO is not set +# CONFIG_TOUCHSCREEN_USB_GENERAL_TOUCH is not set +# CONFIG_TOUCHSCREEN_USB_GOTOP is not set +# CONFIG_TOUCHSCREEN_USB_GUNZE is not set +# CONFIG_TOUCHSCREEN_USB_IDEALTEK is not set +# CONFIG_TOUCHSCREEN_USB_IRTOUCH is not set +# CONFIG_TOUCHSCREEN_USB_ITM is not set +# CONFIG_TOUCHSCREEN_USB_JASTEC is not set +# CONFIG_TOUCHSCREEN_USB_NEXIO is not set +# CONFIG_TOUCHSCREEN_USB_PANJIT is not set +# CONFIG_TOUCHSCREEN_USB_ZYTRONIC is not set +# CONFIG_TOUCHSCREEN_W90X900 is not set +# CONFIG_TOUCHSCREEN_WACOM_I2C is not set +# CONFIG_TOUCHSCREEN_WACOM_W8001 is not set +# CONFIG_TOUCHSCREEN_WDT87XX_I2C is not set +# CONFIG_TOUCHSCREEN_WM831X is not set +# CONFIG_TOUCHSCREEN_WM9705 is not set +# CONFIG_TOUCHSCREEN_WM9712 is not set +# CONFIG_TOUCHSCREEN_WM9713 is not set +# CONFIG_TOUCHSCREEN_WM97XX is not set +# CONFIG_TOUCHSCREEN_WM97XX_MAINSTONE is not set +# CONFIG_TOUCHSCREEN_WM97XX_ZYLONITE is not set +# CONFIG_TOUCHSCREEN_ZET6223 is not set +# CONFIG_TOUCHSCREEN_ZFORCE is not set +# CONFIG_TOUCHSCREEN_ZINITIX is not set +# CONFIG_TPL0102 is not set +# CONFIG_TPS6105X is not set +# CONFIG_TPS65010 is not set +# CONFIG_TPS6507X is not set +# CONFIG_TRACEPOINT_BENCHMARK is not set +# CONFIG_TRACER_SNAPSHOT is not set +# CONFIG_TRACER_SNAPSHOT_PER_CPU_SWAP is not set +# CONFIG_TRACE_BRANCH_PROFILING is not set +# CONFIG_TRACE_EVAL_MAP_FILE is not set +# CONFIG_TRACE_EVENT_INJECT is not set +CONFIG_TRACE_IRQFLAGS_SUPPORT=y +# CONFIG_TRACE_SINK is not set +# CONFIG_TRACING_EVENTS_GPIO is not set +CONFIG_TRACING_SUPPORT=y +CONFIG_TRAD_SIGNALS=y +# CONFIG_TRANSPARENT_HUGEPAGE is not set +# CONFIG_TREE_RCU is not set +# CONFIG_TREE_RCU_TRACE is not set +# CONFIG_TRIM_UNUSED_KSYMS is not set +# CONFIG_TRUSTED_FOUNDATIONS is not set +# CONFIG_TRUSTED_KEYS is not set +# CONFIG_TSL2583 is not set +# CONFIG_TSL2591 is not set +# CONFIG_TSL2772 is not set +# CONFIG_TSL2x7x is not set +# CONFIG_TSL4531 is not set +# CONFIG_TSYS01 is not set +# CONFIG_TSYS02D is not set +# CONFIG_TTPCI_EEPROM is not set +CONFIG_TTY=y +# CONFIG_TTY_PRINTK is not set +# CONFIG_TUN is not set +# CONFIG_TUN_VNET_CROSS_LE is not set +# CONFIG_TWL4030_CORE is not set +# CONFIG_TWL4030_MADC is not set +# CONFIG_TWL6030_GPADC is not set +# CONFIG_TWL6040_CORE is not set +# CONFIG_TYPEC is not set +# CONFIG_TYPEC_TCPM is not set +# CONFIG_TYPEC_UCSI is not set +# CONFIG_TYPHOON is not set +# CONFIG_UACCESS_WITH_MEMCPY is not set +# CONFIG_UBIFS_ATIME_SUPPORT is not set +# CONFIG_UBIFS_FS_ADVANCED_COMPR is not set +# CONFIG_UBIFS_FS_AUTHENTICATION is not set +# CONFIG_UBIFS_FS_ENCRYPTION is not set +CONFIG_UBIFS_FS_LZO=y +# CONFIG_UBIFS_FS_SECURITY is not set +CONFIG_UBIFS_FS_XATTR=y +CONFIG_UBIFS_FS_ZLIB=y +CONFIG_UBIFS_FS_ZSTD=y +# CONFIG_UBSAN is not set +CONFIG_UBSAN_ALIGNMENT=y +# CONFIG_UBSAN_MISC is not set +# CONFIG_UCB1400_CORE is not set +# CONFIG_UCSI is not set +# CONFIG_UDF_FS is not set +# CONFIG_UDMABUF is not set +CONFIG_UEVENT_HELPER=y +CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug" +# CONFIG_UFS_FS is not set +# CONFIG_UHID is not set +CONFIG_UID16=y +# CONFIG_UIO is not set +# CONFIG_ULTRA is not set +# CONFIG_ULTRIX_PARTITION is not set +# CONFIG_UNICODE is not set +# CONFIG_UNISYSSPAR is not set +# CONFIG_UNISYS_VISORBUS is not set +CONFIG_UNIX=y +CONFIG_UNIX98_PTYS=y +# CONFIG_UNIXWARE_DISKLABEL is not set +# CONFIG_UNIX_DIAG is not set +CONFIG_UNIX_SCM=y +# CONFIG_UNUSED_SYMBOLS is not set +# CONFIG_UNWINDER_FRAME_POINTER is not set +# CONFIG_UPROBES is not set +# CONFIG_UPROBE_EVENTS is not set +# CONFIG_US5182D is not set +# CONFIG_USB is not set +# CONFIG_USB4 is not set +# CONFIG_USBIP_CORE is not set +CONFIG_USBIP_VHCI_HC_PORTS=8 +CONFIG_USBIP_VHCI_NR_HCS=1 +# CONFIG_USBIP_VUDC is not set +# CONFIG_USBPCWATCHDOG is not set +# CONFIG_USB_ACM is not set +# CONFIG_USB_ADUTUX is not set +# CONFIG_USB_AIRSPY is not set +CONFIG_USB_ALI_M5632=y +# CONFIG_USB_AMD5536UDC is not set +CONFIG_USB_AN2720=y +# CONFIG_USB_ANNOUNCE_NEW_DEVICES is not set +# CONFIG_USB_APPLEDISPLAY is not set +CONFIG_USB_ARCH_HAS_HCD=y +CONFIG_USB_ARMLINUX=y +# CONFIG_USB_ATM is not set +# CONFIG_USB_AUDIO is not set +CONFIG_USB_AUTOSUSPEND_DELAY=2 +# CONFIG_USB_BDC_UDC is not set +CONFIG_USB_BELKIN=y +# CONFIG_USB_C67X00_HCD is not set +# CONFIG_USB_CATC is not set +# CONFIG_USB_CDC_COMPOSITE is not set +# CONFIG_USB_CDNS3 is not set +# CONFIG_USB_CDNS_SUPPORT is not set +# CONFIG_USB_CHAOSKEY is not set +# CONFIG_USB_CHIPIDEA is not set +# CONFIG_USB_CHIPIDEA_GENERIC is not set +# CONFIG_USB_CHIPIDEA_IMX is not set +# CONFIG_USB_CHIPIDEA_MSM is not set +# CONFIG_USB_CHIPIDEA_PCI is not set +# CONFIG_USB_CHIPIDEA_TEGRA is not set +# CONFIG_USB_CONFIGFS is not set +# CONFIG_USB_CONN_GPIO is not set +# CONFIG_USB_CXACRU is not set +# CONFIG_USB_CYPRESS_CY7C63 is not set +# CONFIG_USB_CYTHERM is not set +CONFIG_USB_DEFAULT_PERSIST=y +# CONFIG_USB_DSBR is not set +# CONFIG_USB_DUMMY_HCD is not set +# CONFIG_USB_DWC2 is not set +# CONFIG_USB_DWC2_DEBUG is not set +# CONFIG_USB_DWC2_DUAL_ROLE is not set +# CONFIG_USB_DWC2_HOST is not set +# CONFIG_USB_DWC2_PERIPHERAL is not set +# CONFIG_USB_DWC2_TRACK_MISSED_SOFS is not set +# CONFIG_USB_DWC3 is not set +# CONFIG_USB_DWC3_EXYNOS is not set +# CONFIG_USB_DWC3_HAPS is not set +# CONFIG_USB_DWC3_KEYSTONE is not set +# CONFIG_USB_DWC3_OF_SIMPLE is not set +# CONFIG_USB_DWC3_PCI is not set +# CONFIG_USB_DWC3_QCOM is not set +# CONFIG_USB_DWC3_ULPI is not set +# CONFIG_USB_DYNAMIC_MINORS is not set +# CONFIG_USB_EG20T is not set +# CONFIG_USB_EHCI_ATH79 is not set +# CONFIG_USB_EHCI_FSL is not set +# CONFIG_USB_EHCI_HCD is not set +# CONFIG_USB_EHCI_HCD_AT91 is not set +# CONFIG_USB_EHCI_HCD_OMAP is not set +# CONFIG_USB_EHCI_HCD_PPC_OF is not set +# CONFIG_USB_EHCI_MSM is not set +# CONFIG_USB_EHCI_MV is not set +CONFIG_USB_EHCI_ROOT_HUB_TT=y +CONFIG_USB_EHCI_TT_NEWSCHED=y +# CONFIG_USB_EHSET_TEST_FIXTURE is not set +# CONFIG_USB_EMI26 is not set +# CONFIG_USB_EMI62 is not set +# CONFIG_USB_EPSON2888 is not set +# CONFIG_USB_ETH is not set +# CONFIG_USB_EZUSB_FX2 is not set +# CONFIG_USB_FEW_INIT_RETRIES is not set +# CONFIG_USB_FOTG210_HCD is not set +# CONFIG_USB_FOTG210_UDC is not set +# CONFIG_USB_FSL_USB2 is not set +# CONFIG_USB_FTDI_ELAN is not set +# CONFIG_USB_FUNCTIONFS is not set +# CONFIG_USB_FUSB300 is not set +# CONFIG_USB_GADGET is not set +# CONFIG_USB_GADGETFS is not set +# CONFIG_USB_GADGET_DEBUG is not set +# CONFIG_USB_GADGET_DEBUG_FILES is not set +# CONFIG_USB_GADGET_DEBUG_FS is not set +CONFIG_USB_GADGET_STORAGE_NUM_BUFFERS=2 +CONFIG_USB_GADGET_VBUS_DRAW=2 +# CONFIG_USB_GADGET_XILINX is not set +# CONFIG_USB_GL860 is not set +# CONFIG_USB_GOKU is not set +# CONFIG_USB_GPIO_VBUS is not set +# CONFIG_USB_GR_UDC is not set +# CONFIG_USB_GSPCA is not set +# CONFIG_USB_GSPCA_BENQ is not set +# CONFIG_USB_GSPCA_CONEX is not set +# CONFIG_USB_GSPCA_CPIA1 is not set +# CONFIG_USB_GSPCA_DTCS033 is not set +# CONFIG_USB_GSPCA_ETOMS is not set +# CONFIG_USB_GSPCA_FINEPIX is not set +# CONFIG_USB_GSPCA_JEILINJ is not set +# CONFIG_USB_GSPCA_JL2005BCD is not set +# CONFIG_USB_GSPCA_KINECT is not set +# CONFIG_USB_GSPCA_KONICA is not set +# CONFIG_USB_GSPCA_MARS is not set +# CONFIG_USB_GSPCA_MR97310A is not set +# CONFIG_USB_GSPCA_NW80X is not set +# CONFIG_USB_GSPCA_OV519 is not set +# CONFIG_USB_GSPCA_OV534 is not set +# CONFIG_USB_GSPCA_OV534_9 is not set +# CONFIG_USB_GSPCA_PAC207 is not set +# CONFIG_USB_GSPCA_PAC7302 is not set +# CONFIG_USB_GSPCA_PAC7311 is not set +# CONFIG_USB_GSPCA_SE401 is not set +# CONFIG_USB_GSPCA_SN9C2028 is not set +# CONFIG_USB_GSPCA_SN9C20X is not set +# CONFIG_USB_GSPCA_SONIXB is not set +# CONFIG_USB_GSPCA_SONIXJ is not set +# CONFIG_USB_GSPCA_SPCA1528 is not set +# CONFIG_USB_GSPCA_SPCA500 is not set +# CONFIG_USB_GSPCA_SPCA501 is not set +# CONFIG_USB_GSPCA_SPCA505 is not set +# CONFIG_USB_GSPCA_SPCA506 is not set +# CONFIG_USB_GSPCA_SPCA508 is not set +# CONFIG_USB_GSPCA_SPCA561 is not set +# CONFIG_USB_GSPCA_SQ905 is not set +# CONFIG_USB_GSPCA_SQ905C is not set +# CONFIG_USB_GSPCA_SQ930X is not set +# CONFIG_USB_GSPCA_STK014 is not set +# CONFIG_USB_GSPCA_STK1135 is not set +# CONFIG_USB_GSPCA_STV0680 is not set +# CONFIG_USB_GSPCA_SUNPLUS is not set +# CONFIG_USB_GSPCA_T613 is not set +# CONFIG_USB_GSPCA_TOPRO is not set +# CONFIG_USB_GSPCA_TOUPTEK is not set +# CONFIG_USB_GSPCA_TV8532 is not set +# CONFIG_USB_GSPCA_VC032X is not set +# CONFIG_USB_GSPCA_VICAM is not set +# CONFIG_USB_GSPCA_XIRLINK_CIT is not set +# CONFIG_USB_GSPCA_ZC3XX is not set +# CONFIG_USB_G_ACM_MS is not set +# CONFIG_USB_G_DBGP is not set +# CONFIG_USB_G_HID is not set +# CONFIG_USB_G_MULTI is not set +# CONFIG_USB_G_NCM is not set +# CONFIG_USB_G_NOKIA is not set +# CONFIG_USB_G_PRINTER is not set +# CONFIG_USB_G_SERIAL is not set +# CONFIG_USB_G_WEBCAM is not set +# CONFIG_USB_HACKRF is not set +# CONFIG_USB_HCD_TEST_MODE is not set +# CONFIG_USB_HID is not set +# CONFIG_USB_HIDDEV is not set +# CONFIG_USB_HSIC_USB3503 is not set +# CONFIG_USB_HSIC_USB4604 is not set +# CONFIG_USB_HSO is not set +# CONFIG_USB_HUB_USB251XB is not set +# CONFIG_USB_HWA_HCD is not set +# CONFIG_USB_IDMOUSE is not set +# CONFIG_USB_IMX21_HCD is not set +# CONFIG_USB_IOWARRIOR is not set +# CONFIG_USB_IPHETH is not set +# CONFIG_USB_ISIGHTFW is not set +# CONFIG_USB_ISP116X_HCD is not set +# CONFIG_USB_ISP1301 is not set +# CONFIG_USB_ISP1362_HCD is not set +# CONFIG_USB_ISP1760 is not set +# CONFIG_USB_ISP1760_HCD is not set +# CONFIG_USB_KAWETH is not set +# CONFIG_USB_KBD is not set +# CONFIG_USB_KC2190 is not set +# CONFIG_USB_LAN78XX is not set +# CONFIG_USB_LCD is not set +# CONFIG_USB_LD is not set +# CONFIG_USB_LED is not set +# CONFIG_USB_LEDS_TRIGGER_USBPORT is not set +# CONFIG_USB_LED_TRIG is not set +# CONFIG_USB_LEGOTOWER is not set +# CONFIG_USB_LGM_PHY is not set +# CONFIG_USB_LINK_LAYER_TEST is not set +# CONFIG_USB_M5602 is not set +# CONFIG_USB_M66592 is not set +# CONFIG_USB_MASS_STORAGE is not set +# CONFIG_USB_MAX3420_UDC is not set +# CONFIG_USB_MAX3421_HCD is not set +# CONFIG_USB_MDC800 is not set +# CONFIG_USB_MICROTEK is not set +# CONFIG_USB_MIDI_GADGET is not set +# CONFIG_USB_MON is not set +# CONFIG_USB_MOUSE is not set +# CONFIG_USB_MSI2500 is not set +# CONFIG_USB_MSM_OTG is not set +# CONFIG_USB_MTU3 is not set +# CONFIG_USB_MUSB_GADGET is not set +# CONFIG_USB_MUSB_HDRC is not set +# CONFIG_USB_MUSB_HOST is not set +# CONFIG_USB_MV_U3D is not set +# CONFIG_USB_MV_UDC is not set +# CONFIG_USB_MXS_PHY is not set +# CONFIG_USB_NET2272 is not set +# CONFIG_USB_NET2280 is not set +# CONFIG_USB_NET_AQC111 is not set +# CONFIG_USB_NET_AX88179_178A is not set +# CONFIG_USB_NET_AX8817X is not set +# CONFIG_USB_NET_CDCETHER is not set +# CONFIG_USB_NET_CDC_EEM is not set +# CONFIG_USB_NET_CDC_MBIM is not set +# CONFIG_USB_NET_CDC_NCM is not set +# CONFIG_USB_NET_CDC_SUBSET is not set +# CONFIG_USB_NET_CH9200 is not set +# CONFIG_USB_NET_CX82310_ETH is not set +# CONFIG_USB_NET_DM9601 is not set +# CONFIG_USB_NET_DRIVERS is not set +# CONFIG_USB_NET_GL620A is not set +# CONFIG_USB_NET_HUAWEI_CDC_NCM is not set +# CONFIG_USB_NET_INT51X1 is not set +# CONFIG_USB_NET_KALMIA is not set +# CONFIG_USB_NET_MCS7830 is not set +# CONFIG_USB_NET_NET1080 is not set +# CONFIG_USB_NET_PLUSB is not set +# CONFIG_USB_NET_QMI_WWAN is not set +# CONFIG_USB_NET_RNDIS_HOST is not set +# CONFIG_USB_NET_RNDIS_WLAN is not set +# CONFIG_USB_NET_SMSC75XX is not set +# CONFIG_USB_NET_SMSC95XX is not set +# CONFIG_USB_NET_SR9700 is not set +# CONFIG_USB_NET_SR9800 is not set +# CONFIG_USB_NET_ZAURUS is not set +# CONFIG_USB_OHCI_HCD is not set +# CONFIG_USB_OHCI_HCD_PCI is not set +# CONFIG_USB_OHCI_HCD_PPC_OF is not set +# CONFIG_USB_OHCI_HCD_PPC_OF_BE is not set +# CONFIG_USB_OHCI_HCD_PPC_OF_LE is not set +# CONFIG_USB_OHCI_HCD_SSB is not set +CONFIG_USB_OHCI_LITTLE_ENDIAN=y +# CONFIG_USB_OTG is not set +# CONFIG_USB_OTG_BLACKLIST_HUB is not set +# CONFIG_USB_OTG_DISABLE_EXTERNAL_HUB is not set +# CONFIG_USB_OTG_FSM is not set +# CONFIG_USB_OTG_PRODUCTLIST is not set +# CONFIG_USB_OTG_WHITELIST is not set +# CONFIG_USB_OXU210HP_HCD is not set +# CONFIG_USB_PCI is not set +# CONFIG_USB_PEGASUS is not set +# CONFIG_USB_PHY is not set +# CONFIG_USB_PRINTER is not set +# CONFIG_USB_PWC_INPUT_EVDEV is not set +# CONFIG_USB_PXA27X is not set +# CONFIG_USB_R8A66597 is not set +# CONFIG_USB_R8A66597_HCD is not set +# CONFIG_USB_RAW_GADGET is not set +# CONFIG_USB_RCAR_PHY is not set +# CONFIG_USB_RENESAS_USBHS is not set +# CONFIG_USB_RIO500 is not set +# CONFIG_USB_ROLES_INTEL_XHCI is not set +# CONFIG_USB_ROLE_SWITCH is not set +# CONFIG_USB_RTL8150 is not set +# CONFIG_USB_RTL8152 is not set +# CONFIG_USB_RTL8153_ECM is not set +# CONFIG_USB_S2255 is not set +# CONFIG_USB_SERIAL is not set +# CONFIG_USB_SERIAL_AIRCABLE is not set +# CONFIG_USB_SERIAL_ARK3116 is not set +# CONFIG_USB_SERIAL_BELKIN is not set +# CONFIG_USB_SERIAL_CH341 is not set +# CONFIG_USB_SERIAL_CP210X is not set +# CONFIG_USB_SERIAL_CYBERJACK is not set +# CONFIG_USB_SERIAL_CYPRESS_M8 is not set +# CONFIG_USB_SERIAL_DEBUG is not set +# CONFIG_USB_SERIAL_DIGI_ACCELEPORT is not set +# CONFIG_USB_SERIAL_EDGEPORT is not set +# CONFIG_USB_SERIAL_EDGEPORT_TI is not set +# CONFIG_USB_SERIAL_EMPEG is not set +# CONFIG_USB_SERIAL_F81232 is not set +# CONFIG_USB_SERIAL_F8153X is not set +# CONFIG_USB_SERIAL_FTDI_SIO is not set +# CONFIG_USB_SERIAL_GARMIN is not set +CONFIG_USB_SERIAL_GENERIC=y +# CONFIG_USB_SERIAL_IPAQ is not set +# CONFIG_USB_SERIAL_IPW is not set +# CONFIG_USB_SERIAL_IR is not set +# CONFIG_USB_SERIAL_IUU is not set +# CONFIG_USB_SERIAL_KEYSPAN is not set +CONFIG_USB_SERIAL_KEYSPAN_MPR=y +# CONFIG_USB_SERIAL_KEYSPAN_PDA is not set +CONFIG_USB_SERIAL_KEYSPAN_USA18X=y +CONFIG_USB_SERIAL_KEYSPAN_USA19=y +CONFIG_USB_SERIAL_KEYSPAN_USA19QI=y +CONFIG_USB_SERIAL_KEYSPAN_USA19QW=y +CONFIG_USB_SERIAL_KEYSPAN_USA19W=y +CONFIG_USB_SERIAL_KEYSPAN_USA28=y +CONFIG_USB_SERIAL_KEYSPAN_USA28X=y +CONFIG_USB_SERIAL_KEYSPAN_USA28XA=y +CONFIG_USB_SERIAL_KEYSPAN_USA28XB=y +CONFIG_USB_SERIAL_KEYSPAN_USA49W=y +CONFIG_USB_SERIAL_KEYSPAN_USA49WLC=y +# CONFIG_USB_SERIAL_KLSI is not set +# CONFIG_USB_SERIAL_KOBIL_SCT is not set +# CONFIG_USB_SERIAL_MCT_U232 is not set +# CONFIG_USB_SERIAL_METRO is not set +# CONFIG_USB_SERIAL_MOS7715_PARPORT is not set +# CONFIG_USB_SERIAL_MOS7720 is not set +# CONFIG_USB_SERIAL_MOS7840 is not set +# CONFIG_USB_SERIAL_MXUPORT is not set +# CONFIG_USB_SERIAL_NAVMAN is not set +# CONFIG_USB_SERIAL_OMNINET is not set +# CONFIG_USB_SERIAL_OPTICON is not set +# CONFIG_USB_SERIAL_OPTION is not set +# CONFIG_USB_SERIAL_OTI6858 is not set +# CONFIG_USB_SERIAL_PL2303 is not set +# CONFIG_USB_SERIAL_QCAUX is not set +# CONFIG_USB_SERIAL_QT2 is not set +# CONFIG_USB_SERIAL_QUALCOMM is not set +# CONFIG_USB_SERIAL_SAFE is not set +CONFIG_USB_SERIAL_SAFE_PADDED=y +# CONFIG_USB_SERIAL_SIERRAWIRELESS is not set +# CONFIG_USB_SERIAL_SIMPLE is not set +# CONFIG_USB_SERIAL_SPCP8X5 is not set +# CONFIG_USB_SERIAL_SSU100 is not set +# CONFIG_USB_SERIAL_SYMBOL is not set +# CONFIG_USB_SERIAL_TI is not set +# CONFIG_USB_SERIAL_UPD78F0730 is not set +# CONFIG_USB_SERIAL_VISOR is not set +# CONFIG_USB_SERIAL_WHITEHEAT is not set +# CONFIG_USB_SERIAL_WISHBONE is not set +# CONFIG_USB_SERIAL_XIRCOM is not set +# CONFIG_USB_SERIAL_XR is not set +# CONFIG_USB_SERIAL_XSENS_MT is not set +# CONFIG_USB_SEVSEG is not set +# CONFIG_USB_SIERRA_NET is not set +# CONFIG_USB_SISUSBVGA is not set +# CONFIG_USB_SL811_HCD is not set +# CONFIG_USB_SNP_UDC_PLAT is not set +# CONFIG_USB_SPEEDTOUCH is not set +# CONFIG_USB_STKWEBCAM is not set +# CONFIG_USB_STORAGE is not set +# CONFIG_USB_STORAGE_ALAUDA is not set +# CONFIG_USB_STORAGE_CYPRESS_ATACB is not set +# CONFIG_USB_STORAGE_DATAFAB is not set +# CONFIG_USB_STORAGE_DEBUG is not set +# CONFIG_USB_STORAGE_ENE_UB6250 is not set +# CONFIG_USB_STORAGE_FREECOM is not set +# CONFIG_USB_STORAGE_ISD200 is not set +# CONFIG_USB_STORAGE_JUMPSHOT is not set +# CONFIG_USB_STORAGE_KARMA is not set +# CONFIG_USB_STORAGE_ONETOUCH is not set +# CONFIG_USB_STORAGE_REALTEK is not set +# CONFIG_USB_STORAGE_SDDR09 is not set +# CONFIG_USB_STORAGE_SDDR55 is not set +# CONFIG_USB_STORAGE_USBAT is not set +# CONFIG_USB_STV06XX is not set +# CONFIG_USB_SUPPORT is not set +# CONFIG_USB_SWITCH_FSA9480 is not set +# CONFIG_USB_TEST is not set +# CONFIG_USB_TMC is not set +# CONFIG_USB_TRANCEVIBRATOR is not set +# CONFIG_USB_UAS is not set +# CONFIG_USB_UEAGLEATM is not set +# CONFIG_USB_ULPI is not set +# CONFIG_USB_ULPI_BUS is not set +# CONFIG_USB_USBNET is not set +# CONFIG_USB_USS720 is not set +# CONFIG_USB_VIDEO_CLASS is not set +CONFIG_USB_VIDEO_CLASS_INPUT_EVDEV=y +# CONFIG_USB_VL600 is not set +# CONFIG_USB_WDM is not set +# CONFIG_USB_WHCI_HCD is not set +# CONFIG_USB_WUSB is not set +# CONFIG_USB_WUSB_CBAF is not set +# CONFIG_USB_XHCI_DBGCAP is not set +# CONFIG_USB_XHCI_HCD is not set +# CONFIG_USB_XHCI_MVEBU is not set +# CONFIG_USB_XHCI_PCI_RENESAS is not set +# CONFIG_USB_XUSBATM is not set +# CONFIG_USB_YUREX is not set +# CONFIG_USB_ZD1201 is not set +# CONFIG_USB_ZERO is not set +# CONFIG_USB_ZR364XX is not set +# CONFIG_USELIB is not set +# CONFIG_USERFAULTFD is not set +# CONFIG_USERIO is not set +# CONFIG_USE_OF is not set +# CONFIG_UTS_NS is not set +# CONFIG_UWB is not set +# CONFIG_U_SERIAL_CONSOLE is not set +# CONFIG_V4L_MEM2MEM_DRIVERS is not set +# CONFIG_V4L_PLATFORM_DRIVERS is not set +# CONFIG_V4L_TEST_DRIVERS is not set +# CONFIG_VALIDATE_FS_PARSER is not set +# CONFIG_VBOXGUEST is not set +# CONFIG_VCNL3020 is not set +# CONFIG_VCNL4000 is not set +# CONFIG_VCNL4035 is not set +# CONFIG_VDPA is not set +CONFIG_VDSO=y +# CONFIG_VEML6030 is not set +# CONFIG_VEML6070 is not set +# CONFIG_VETH is not set +# CONFIG_VEXPRESS_CONFIG is not set +# CONFIG_VF610_ADC is not set +# CONFIG_VF610_DAC is not set +# CONFIG_VFAT_FS is not set +# CONFIG_VFIO is not set +# CONFIG_VGASTATE is not set +# CONFIG_VGA_ARB is not set +# CONFIG_VGA_CONSOLE is not set +# CONFIG_VGA_SWITCHEROO is not set +# CONFIG_VHOST_CROSS_ENDIAN_LEGACY is not set +CONFIG_VHOST_MENU=y +# CONFIG_VHOST_NET is not set +# CONFIG_VHOST_VSOCK is not set +# CONFIG_VIA_RHINE is not set +# CONFIG_VIA_VELOCITY is not set +# CONFIG_VIDEO_AD5398 is not set +# CONFIG_VIDEO_AD5820 is not set +# CONFIG_VIDEO_AD9389B is not set +# CONFIG_VIDEO_ADP1653 is not set +# CONFIG_VIDEO_ADV7170 is not set +# CONFIG_VIDEO_ADV7175 is not set +# CONFIG_VIDEO_ADV7180 is not set +# CONFIG_VIDEO_ADV7183 is not set +# CONFIG_VIDEO_ADV7343 is not set +# CONFIG_VIDEO_ADV7393 is not set +# CONFIG_VIDEO_ADV748X is not set +# CONFIG_VIDEO_ADV7511 is not set +# CONFIG_VIDEO_ADV7604 is not set +# CONFIG_VIDEO_ADV7842 is not set +# CONFIG_VIDEO_ADV_DEBUG is not set +# CONFIG_VIDEO_AK7375 is not set +# CONFIG_VIDEO_AK881X is not set +# CONFIG_VIDEO_AM437X_VPFE is not set +# CONFIG_VIDEO_ASPEED is not set +# CONFIG_VIDEO_ATMEL_ISC is not set +# CONFIG_VIDEO_ATMEL_ISI is not set +# CONFIG_VIDEO_AU0828 is not set +# CONFIG_VIDEO_BT819 is not set +# CONFIG_VIDEO_BT848 is not set +# CONFIG_VIDEO_BT856 is not set +# CONFIG_VIDEO_BT866 is not set +# CONFIG_VIDEO_CADENCE is not set +# CONFIG_VIDEO_CAFE_CCIC is not set +# CONFIG_VIDEO_CCS is not set +# CONFIG_VIDEO_CS3308 is not set +# CONFIG_VIDEO_CS5345 is not set +# CONFIG_VIDEO_CS53L32A is not set +# CONFIG_VIDEO_CX231XX is not set +# CONFIG_VIDEO_CX2341X is not set +# CONFIG_VIDEO_CX25840 is not set +# CONFIG_VIDEO_CX88 is not set +# CONFIG_VIDEO_DEV is not set +# CONFIG_VIDEO_DM6446_CCDC is not set +# CONFIG_VIDEO_DT3155 is not set +# CONFIG_VIDEO_DW9714 is not set +# CONFIG_VIDEO_DW9768 is not set +# CONFIG_VIDEO_DW9807_VCM is not set +# CONFIG_VIDEO_EM28XX is not set +# CONFIG_VIDEO_ET8EK8 is not set +# CONFIG_VIDEO_FIXED_MINOR_RANGES is not set +# CONFIG_VIDEO_GO7007 is not set +# CONFIG_VIDEO_GS1662 is not set +# CONFIG_VIDEO_HDPVR is not set +# CONFIG_VIDEO_HEXIUM_GEMINI is not set +# CONFIG_VIDEO_HEXIUM_ORION is not set +# CONFIG_VIDEO_HI556 is not set +# CONFIG_VIDEO_I2C is not set +# CONFIG_VIDEO_IMX214 is not set +# CONFIG_VIDEO_IMX219 is not set +# CONFIG_VIDEO_IMX258 is not set +# CONFIG_VIDEO_IMX274 is not set +# CONFIG_VIDEO_IMX290 is not set +# CONFIG_VIDEO_IMX319 is not set +# CONFIG_VIDEO_IMX334 is not set +# CONFIG_VIDEO_IMX335 is not set +# CONFIG_VIDEO_IMX355 is not set +# CONFIG_VIDEO_IMX412 is not set +# CONFIG_VIDEO_IMX477 is not set +# CONFIG_VIDEO_IRS1125 is not set +# CONFIG_VIDEO_IR_I2C is not set +# CONFIG_VIDEO_IVTV is not set +# CONFIG_VIDEO_KS0127 is not set +# CONFIG_VIDEO_LM3560 is not set +# CONFIG_VIDEO_LM3646 is not set +# CONFIG_VIDEO_M52790 is not set +# CONFIG_VIDEO_M5MOLS is not set +# CONFIG_VIDEO_MAX9286 is not set +# CONFIG_VIDEO_ML86V7667 is not set +# CONFIG_VIDEO_MSP3400 is not set +# CONFIG_VIDEO_MT9M001 is not set +# CONFIG_VIDEO_MT9M032 is not set +# CONFIG_VIDEO_MT9M111 is not set +# CONFIG_VIDEO_MT9P031 is not set +# CONFIG_VIDEO_MT9T001 is not set +# CONFIG_VIDEO_MT9T112 is not set +# CONFIG_VIDEO_MT9V011 is not set +# CONFIG_VIDEO_MT9V032 is not set +# CONFIG_VIDEO_MT9V111 is not set +# CONFIG_VIDEO_MUX is not set +# CONFIG_VIDEO_MXB is not set +# CONFIG_VIDEO_NOON010PC30 is not set +# CONFIG_VIDEO_OMAP2_VOUT is not set +# CONFIG_VIDEO_OV02A10 is not set +# CONFIG_VIDEO_OV13858 is not set +# CONFIG_VIDEO_OV2311 is not set +# CONFIG_VIDEO_OV2640 is not set +# CONFIG_VIDEO_OV2659 is not set +# CONFIG_VIDEO_OV2680 is not set +# CONFIG_VIDEO_OV2685 is not set +# CONFIG_VIDEO_OV2740 is not set +# CONFIG_VIDEO_OV5640 is not set +# CONFIG_VIDEO_OV5645 is not set +# CONFIG_VIDEO_OV5647 is not set +# CONFIG_VIDEO_OV5648 is not set +# CONFIG_VIDEO_OV5670 is not set +# CONFIG_VIDEO_OV5675 is not set +# CONFIG_VIDEO_OV5695 is not set +# CONFIG_VIDEO_OV6650 is not set +# CONFIG_VIDEO_OV7251 is not set +# CONFIG_VIDEO_OV7640 is not set +# CONFIG_VIDEO_OV7670 is not set +# CONFIG_VIDEO_OV772X is not set +# CONFIG_VIDEO_OV7740 is not set +# CONFIG_VIDEO_OV8856 is not set +# CONFIG_VIDEO_OV8865 is not set +# CONFIG_VIDEO_OV9281 is not set +# CONFIG_VIDEO_OV9282 is not set +# CONFIG_VIDEO_OV9640 is not set +# CONFIG_VIDEO_OV9650 is not set +# CONFIG_VIDEO_OV9734 is not set +# CONFIG_VIDEO_PVRUSB2 is not set +# CONFIG_VIDEO_RDACM20 is not set +# CONFIG_VIDEO_RDACM21 is not set +# CONFIG_VIDEO_RJ54N1 is not set +# CONFIG_VIDEO_ROCKCHIP_ISP1 is not set +# CONFIG_VIDEO_S5C73M3 is not set +# CONFIG_VIDEO_S5K4ECGX is not set +# CONFIG_VIDEO_S5K5BAF is not set +# CONFIG_VIDEO_S5K6A3 is not set +# CONFIG_VIDEO_S5K6AA is not set +# CONFIG_VIDEO_SAA6588 is not set +# CONFIG_VIDEO_SAA6752HS is not set +# CONFIG_VIDEO_SAA7110 is not set +# CONFIG_VIDEO_SAA711X is not set +# CONFIG_VIDEO_SAA7127 is not set +# CONFIG_VIDEO_SAA7134 is not set +# CONFIG_VIDEO_SAA717X is not set +# CONFIG_VIDEO_SAA7185 is not set +# CONFIG_VIDEO_SH_MOBILE_CEU is not set +# CONFIG_VIDEO_SMIAPP is not set +# CONFIG_VIDEO_SONY_BTF_MPX is not set +# CONFIG_VIDEO_SR030PC30 is not set +# CONFIG_VIDEO_STK1160_COMMON is not set +# CONFIG_VIDEO_ST_MIPID02 is not set +# CONFIG_VIDEO_TC358743 is not set +# CONFIG_VIDEO_TDA1997X is not set +# CONFIG_VIDEO_TDA7432 is not set +# CONFIG_VIDEO_TDA9840 is not set +# CONFIG_VIDEO_TEA6415C is not set +# CONFIG_VIDEO_TEA6420 is not set +# CONFIG_VIDEO_THS7303 is not set +# CONFIG_VIDEO_THS8200 is not set +# CONFIG_VIDEO_TIMBERDALE is not set +# CONFIG_VIDEO_TLV320AIC23B is not set +# CONFIG_VIDEO_TM6000 is not set +# CONFIG_VIDEO_TVAUDIO is not set +# CONFIG_VIDEO_TVP514X is not set +# CONFIG_VIDEO_TVP5150 is not set +# CONFIG_VIDEO_TVP7002 is not set +# CONFIG_VIDEO_TW2804 is not set +# CONFIG_VIDEO_TW9903 is not set +# CONFIG_VIDEO_TW9906 is not set +# CONFIG_VIDEO_TW9910 is not set +# CONFIG_VIDEO_UDA1342 is not set +# CONFIG_VIDEO_UPD64031A is not set +# CONFIG_VIDEO_UPD64083 is not set +# CONFIG_VIDEO_USBTV is not set +# CONFIG_VIDEO_USBVISION is not set +# CONFIG_VIDEO_V4L2 is not set +# CONFIG_VIDEO_VP27SMPX is not set +# CONFIG_VIDEO_VPX3220 is not set +# CONFIG_VIDEO_VS6624 is not set +# CONFIG_VIDEO_WM8739 is not set +# CONFIG_VIDEO_WM8775 is not set +# CONFIG_VIDEO_XILINX is not set +# CONFIG_VIDEO_ZORAN is not set +# CONFIG_VIRTIO_BALLOON is not set +# CONFIG_VIRTIO_BLK_SCSI is not set +# CONFIG_VIRTIO_CONSOLE is not set +# CONFIG_VIRTIO_FS is not set +# CONFIG_VIRTIO_INPUT is not set +# CONFIG_VIRTIO_IOMMU is not set +CONFIG_VIRTIO_MENU=y +# CONFIG_VIRTIO_MMIO is not set +# CONFIG_VIRTIO_MMIO_CMDLINE_DEVICES is not set +# CONFIG_VIRTIO_PCI is not set +# CONFIG_VIRTUALIZATION is not set +# CONFIG_VIRT_CPU_ACCOUNTING_GEN is not set +# CONFIG_VIRT_DRIVERS is not set +CONFIG_VIRT_TO_BUS=y +# CONFIG_VITESSE_PHY is not set +# CONFIG_VL53L0X_I2C is not set +# CONFIG_VL6180 is not set +CONFIG_VLAN_8021Q=y +# CONFIG_VLAN_8021Q_GVRP is not set +# CONFIG_VLAN_8021Q_MVRP is not set +# CONFIG_VME_BUS is not set +# CONFIG_VMLINUX_MAP is not set +# CONFIG_VMSPLIT_1G is not set +# CONFIG_VMSPLIT_2G is not set +# CONFIG_VMSPLIT_2G_OPT is not set +CONFIG_VMSPLIT_3G=y +# CONFIG_VMSPLIT_3G_OPT is not set +# CONFIG_VMWARE_PVSCSI is not set +# CONFIG_VMXNET3 is not set +# CONFIG_VM_EVENT_COUNTERS is not set +# CONFIG_VOP_BUS is not set +# CONFIG_VORTEX is not set +# CONFIG_VSOCKETS is not set +# CONFIG_VSOCKETS_DIAG is not set +# CONFIG_VT is not set +# CONFIG_VT6655 is not set +# CONFIG_VT6656 is not set +# CONFIG_VXFS_FS is not set +# CONFIG_VXGE is not set +# CONFIG_VXLAN is not set +# CONFIG_VZ89X is not set +# CONFIG_W1 is not set +# CONFIG_W1_CON is not set +# CONFIG_W1_MASTER_DS1WM is not set +# CONFIG_W1_MASTER_DS2482 is not set +# CONFIG_W1_MASTER_DS2490 is not set +# CONFIG_W1_MASTER_GPIO is not set +# CONFIG_W1_MASTER_MATROX is not set +# CONFIG_W1_MASTER_SGI is not set +# CONFIG_W1_SLAVE_DS2405 is not set +# CONFIG_W1_SLAVE_DS2406 is not set +# CONFIG_W1_SLAVE_DS2408 is not set +# CONFIG_W1_SLAVE_DS2413 is not set +# CONFIG_W1_SLAVE_DS2423 is not set +# CONFIG_W1_SLAVE_DS2430 is not set +# CONFIG_W1_SLAVE_DS2431 is not set +# CONFIG_W1_SLAVE_DS2433 is not set +# CONFIG_W1_SLAVE_DS2438 is not set +# CONFIG_W1_SLAVE_DS250X is not set +# CONFIG_W1_SLAVE_DS2780 is not set +# CONFIG_W1_SLAVE_DS2781 is not set +# CONFIG_W1_SLAVE_DS2805 is not set +# CONFIG_W1_SLAVE_DS28E04 is not set +# CONFIG_W1_SLAVE_DS28E17 is not set +# CONFIG_W1_SLAVE_SMEM is not set +# CONFIG_W1_SLAVE_THERM is not set +# CONFIG_W83627HF_WDT is not set +# CONFIG_W83877F_WDT is not set +# CONFIG_W83977F_WDT is not set +# CONFIG_WAN is not set +# CONFIG_WANXL is not set +# CONFIG_WARN_ALL_UNSEEDED_RANDOM is not set +CONFIG_WATCHDOG=y +# CONFIG_WATCHDOG_CORE is not set +CONFIG_WATCHDOG_HANDLE_BOOT_ENABLED=y +# CONFIG_WATCHDOG_HRTIMER_PRETIMEOUT is not set +# CONFIG_WATCHDOG_NOWAYOUT is not set +CONFIG_WATCHDOG_OPEN_TIMEOUT=0 +# CONFIG_WATCHDOG_PRETIMEOUT_GOV is not set +# CONFIG_WATCHDOG_SYSFS is not set +# CONFIG_WATCH_QUEUE is not set +# CONFIG_WD80x3 is not set +# CONFIG_WDAT_WDT is not set +# CONFIG_WDTPCI is not set +# CONFIG_WERROR is not set +# CONFIG_WEXT_CORE is not set +# CONFIG_WEXT_PRIV is not set +# CONFIG_WEXT_PROC is not set +# CONFIG_WEXT_SPY is not set +CONFIG_WILINK_PLATFORM_DATA=y +# CONFIG_WIMAX is not set +# CONFIG_WIREGUARD is not set +CONFIG_WIRELESS=y +# CONFIG_WIRELESS_EXT is not set +# CONFIG_WIRELESS_WDS is not set +# CONFIG_WIZNET_W5100 is not set +# CONFIG_WIZNET_W5300 is not set +# CONFIG_WL1251 is not set +# CONFIG_WL12XX is not set +# CONFIG_WL18XX is not set +CONFIG_WLAN=y +# CONFIG_WLAN_VENDOR_ADMTEK is not set +# CONFIG_WLAN_VENDOR_ATH is not set +# CONFIG_WLAN_VENDOR_ATMEL is not set +# CONFIG_WLAN_VENDOR_BROADCOM is not set +# CONFIG_WLAN_VENDOR_CISCO is not set +# CONFIG_WLAN_VENDOR_INTEL is not set +# CONFIG_WLAN_VENDOR_INTERSIL is not set +# CONFIG_WLAN_VENDOR_MARVELL is not set +# CONFIG_WLAN_VENDOR_MEDIATEK is not set +# CONFIG_WLAN_VENDOR_MICROCHIP is not set +# CONFIG_WLAN_VENDOR_QUANTENNA is not set +# CONFIG_WLAN_VENDOR_RALINK is not set +# CONFIG_WLAN_VENDOR_REALTEK is not set +# CONFIG_WLAN_VENDOR_RSI is not set +# CONFIG_WLAN_VENDOR_ST is not set +# CONFIG_WLAN_VENDOR_TI is not set +# CONFIG_WLAN_VENDOR_ZYDAS is not set +# CONFIG_WLCORE is not set +CONFIG_WQ_POWER_EFFICIENT_DEFAULT=y +# CONFIG_WQ_WATCHDOG is not set +# CONFIG_WWAN is not set +# CONFIG_WWAN_HWSIM is not set +# CONFIG_WW_MUTEX_SELFTEST is not set +# CONFIG_X25 is not set +# CONFIG_X509_CERTIFICATE_PARSER is not set +# CONFIG_X86_PKG_TEMP_THERMAL is not set +CONFIG_X86_SYSFB=y +# CONFIG_XDP_SOCKETS is not set +# CONFIG_XEN is not set +# CONFIG_XEN_GRANT_DMA_ALLOC is not set +# CONFIG_XEN_PVCALLS_FRONTEND is not set +CONFIG_XEN_SCRUB_PAGES_DEFAULT=y +CONFIG_XFRM=y +# CONFIG_XFRM_INTERFACE is not set +# CONFIG_XFRM_IPCOMP is not set +# CONFIG_XFRM_MIGRATE is not set +# CONFIG_XFRM_STATISTICS is not set +# CONFIG_XFRM_SUB_POLICY is not set +# CONFIG_XFRM_USER is not set +# CONFIG_XFS_DEBUG is not set +# CONFIG_XFS_FS is not set +# CONFIG_XFS_ONLINE_SCRUB is not set +# CONFIG_XFS_POSIX_ACL is not set +# CONFIG_XFS_QUOTA is not set +# CONFIG_XFS_RT is not set +# CONFIG_XFS_SUPPORT_V4 is not set +# CONFIG_XFS_WARN is not set +# CONFIG_XILINX_AXI_EMAC is not set +# CONFIG_XILINX_DMA is not set +# CONFIG_XILINX_EMACLITE is not set +# CONFIG_XILINX_GMII2RGMII is not set +# CONFIG_XILINX_LL_TEMAC is not set +# CONFIG_XILINX_SDFEC is not set +# CONFIG_XILINX_VCU is not set +# CONFIG_XILINX_WATCHDOG is not set +# CONFIG_XILINX_XADC is not set +# CONFIG_XILINX_ZYNQMP_DMA is not set +# CONFIG_XILINX_ZYNQMP_DPDMA is not set +# CONFIG_XILLYBUS is not set +# CONFIG_XILLYUSB is not set +# CONFIG_XIL_AXIS_FIFO is not set +# CONFIG_XIP_KERNEL is not set +# CONFIG_XMON is not set +CONFIG_XZ_DEC=y +# CONFIG_XZ_DEC_ARM is not set +# CONFIG_XZ_DEC_ARMTHUMB is not set +# CONFIG_XZ_DEC_BCJ is not set +# CONFIG_XZ_DEC_IA64 is not set +# CONFIG_XZ_DEC_POWERPC is not set +# CONFIG_XZ_DEC_SPARC is not set +# CONFIG_XZ_DEC_TEST is not set +# CONFIG_XZ_DEC_X86 is not set +# CONFIG_YAM is not set +# CONFIG_YAMAHA_YAS530 is not set +# CONFIG_YELLOWFIN is not set +# CONFIG_YENTA is not set +# CONFIG_YENTA_O2 is not set +# CONFIG_YENTA_RICOH is not set +# CONFIG_YENTA_TI is not set +# CONFIG_YENTA_TOSHIBA is not set +# CONFIG_ZBUD is not set +# CONFIG_ZD1211RW is not set +# CONFIG_ZD1211RW_DEBUG is not set +# CONFIG_ZEROPLUS_FF is not set +# CONFIG_ZERO_CALL_USED_REGS is not set +# CONFIG_ZIIRAVE_WATCHDOG is not set +# CONFIG_ZISOFS is not set +# CONFIG_ZLIB_DEFLATE is not set +# CONFIG_ZLIB_INFLATE is not set +CONFIG_ZONE_DMA=y +# CONFIG_ZOPT2201 is not set +# CONFIG_ZPA2326 is not set +# CONFIG_ZPOOL is not set +# CONFIG_ZRAM is not set +# CONFIG_ZRAM_DEF_COMP_LZ4 is not set +# CONFIG_ZRAM_DEF_COMP_LZO is not set +# CONFIG_ZRAM_DEF_COMP_LZORLE is not set +# CONFIG_ZRAM_DEF_COMP_ZSTD is not set +# CONFIG_ZRAM_MEMORY_TRACKING is not set +# CONFIG_ZSMALLOC is not set +# CONFIG_ZX_TDM is not set diff --git a/target/linux/generic/hack-6.1/204-module_strip.patch b/target/linux/generic/hack-6.1/204-module_strip.patch new file mode 100644 index 0000000000..979daa4727 --- /dev/null +++ b/target/linux/generic/hack-6.1/204-module_strip.patch @@ -0,0 +1,212 @@ +From a779a482fb9b9f8fcdf8b2519c789b4b9bb5dd05 Mon Sep 17 00:00:00 2001 +From: Felix Fietkau +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 +--- + 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 " 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 +@@ -2036,7 +2036,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++) { +@@ -2215,8 +2217,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"); +@@ -2233,8 +2237,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 */ +@@ -2247,8 +2253,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 + } + + /** +@@ -2328,11 +2336,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) +@@ -2581,7 +2591,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/target/linux/generic/hack-6.1/205-kconfig-exit.patch b/target/linux/generic/hack-6.1/205-kconfig-exit.patch new file mode 100644 index 0000000000..9f3bb8f311 --- /dev/null +++ b/target/linux/generic/hack-6.1/205-kconfig-exit.patch @@ -0,0 +1,20 @@ +From 300d26562ce4dc427154cb247beb75db4b1f0774 Mon Sep 17 00:00:00 2001 +From: OpenWrt community +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/target/linux/generic/hack-6.1/210-darwin_scripts_include.patch b/target/linux/generic/hack-6.1/210-darwin_scripts_include.patch new file mode 100644 index 0000000000..be6adc0d11 --- /dev/null +++ b/target/linux/generic/hack-6.1/210-darwin_scripts_include.patch @@ -0,0 +1,3053 @@ +From db7c30dcd9a0391bf13b62c9f91e144d762ef43a Mon Sep 17 00:00:00 2001 +From: Florian Fainelli +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 +--- + 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 ++ . */ ++ ++#ifndef _ELF_H ++#define _ELF_H 1 ++ ++/* Standard ELF types. */ ++ ++#include ++ ++/* 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 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 + #include + #include ++#ifndef __APPLE__ + #include ++#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 + #include + #include ++#if !(defined(__APPLE__) || defined(__CYGWIN__)) + #include ++#else ++#include "elf.h" ++#endif + + #include "elfconfig.h" + diff --git a/target/linux/generic/hack-6.1/211-darwin-uuid-typedef-clash.patch b/target/linux/generic/hack-6.1/211-darwin-uuid-typedef-clash.patch new file mode 100644 index 0000000000..50a6227148 --- /dev/null +++ b/target/linux/generic/hack-6.1/211-darwin-uuid-typedef-clash.patch @@ -0,0 +1,22 @@ +From e44fc2af1ddc452b6659d08c16973d65c73b7d0a Mon Sep 17 00:00:00 2001 +From: Kevin Darbyshire-Bryant +Date: Wed, 5 Feb 2020 18:36:43 +0000 +Subject: [PATCH] file2alias: build on macos + +Signed-off-by: Kevin Darbyshire-Bryant +--- + 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/target/linux/generic/hack-6.1/212-tools_portability.patch b/target/linux/generic/hack-6.1/212-tools_portability.patch new file mode 100644 index 0000000000..b488155f94 --- /dev/null +++ b/target/linux/generic/hack-6.1/212-tools_portability.patch @@ -0,0 +1,110 @@ +From 48232d3d931c95953ce2ddfe7da7bb164aef6a73 Mon Sep 17 00:00:00 2001 +From: Felix Fietkau +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 +--- + 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 + + 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 + + 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 ++ ++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 ++#else + #include + #include ++#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 + #include + #include ++#include + #include + #include + #include +--- a/tools/perf/pmu-events/json.c ++++ b/tools/perf/pmu-events/json.c +@@ -38,7 +38,6 @@ + #include + #include "jsmn.h" + #include "json.h" +-#include + + + static char *mapfile(const char *fn, size_t *size) diff --git a/target/linux/generic/hack-6.1/214-spidev_h_portability.patch b/target/linux/generic/hack-6.1/214-spidev_h_portability.patch new file mode 100644 index 0000000000..db754a2903 --- /dev/null +++ b/target/linux/generic/hack-6.1/214-spidev_h_portability.patch @@ -0,0 +1,24 @@ +From be9be95ff10e16a5b4ad36f903978d0cc5747024 Mon Sep 17 00:00:00 2001 +From: Felix Fietkau +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 +--- + 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 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/target/linux/generic/hack-6.1/220-arm-gc_sections.patch b/target/linux/generic/hack-6.1/220-arm-gc_sections.patch new file mode 100644 index 0000000000..0aa3eb840c --- /dev/null +++ b/target/linux/generic/hack-6.1/220-arm-gc_sections.patch @@ -0,0 +1,123 @@ +From e3d8676f5722b7622685581e06e8f53e6138e3ab Mon Sep 17 00:00:00 2001 +From: Felix Fietkau +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 +Signed-off-by: Jonas Gorski +Signed-off-by: Gabor Juhos +--- +--- 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/target/linux/generic/hack-6.1/221-module_exports.patch b/target/linux/generic/hack-6.1/221-module_exports.patch new file mode 100644 index 0000000000..af21a2db8b --- /dev/null +++ b/target/linux/generic/hack-6.1/221-module_exports.patch @@ -0,0 +1,126 @@ +From b14784e7883390c20ed3ff904892255404a5914b Mon Sep 17 00:00:00 2001 +From: Felix Fietkau +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 +--- + 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 */ \ +@@ -1017,6 +1027,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/target/linux/generic/hack-6.1/230-openwrt_lzma_options.patch b/target/linux/generic/hack-6.1/230-openwrt_lzma_options.patch new file mode 100644 index 0000000000..f9361b060b --- /dev/null +++ b/target/linux/generic/hack-6.1/230-openwrt_lzma_options.patch @@ -0,0 +1,34 @@ +From b3d00b452467f621317953d9e4c6f9ae8dcfd271 Mon Sep 17 00:00:00 2001 +From: Imre Kaloz +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 +--- + 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/target/linux/generic/hack-6.1/250-netfilter_depends.patch b/target/linux/generic/hack-6.1/250-netfilter_depends.patch new file mode 100644 index 0000000000..d9a2b81d74 --- /dev/null +++ b/target/linux/generic/hack-6.1/250-netfilter_depends.patch @@ -0,0 +1,27 @@ +From: Felix Fietkau +Subject: hack: net: remove bogus netfilter dependencies + +lede-commit: 589d2a377dee27d206fc3725325309cf649e4df6 +Signed-off-by: Felix Fietkau +--- + 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/target/linux/generic/hack-6.1/251-kconfig.patch b/target/linux/generic/hack-6.1/251-kconfig.patch new file mode 100644 index 0000000000..5a8a820252 --- /dev/null +++ b/target/linux/generic/hack-6.1/251-kconfig.patch @@ -0,0 +1,199 @@ +From da3c50704f14132f4adf80d48e9a4cd5d46e54c9 Mon Sep 17 00:00:00 2001 +From: John Crispin +Date: Fri, 7 Jul 2017 17:09:21 +0200 +Subject: kconfig: owrt specifc dependencies + +Signed-off-by: John Crispin +--- + 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/target/linux/generic/hack-6.1/253-ksmbd-config.patch b/target/linux/generic/hack-6.1/253-ksmbd-config.patch new file mode 100644 index 0000000000..a29cf3bf61 --- /dev/null +++ b/target/linux/generic/hack-6.1/253-ksmbd-config.patch @@ -0,0 +1,32 @@ +From dcd966fa7ca63f38cf7147e1184d13d66e2ca340 Mon Sep 17 00:00:00 2001 +From: OpenWrt community +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/target/linux/generic/hack-6.1/259-regmap_dynamic.patch b/target/linux/generic/hack-6.1/259-regmap_dynamic.patch new file mode 100644 index 0000000000..6be5875177 --- /dev/null +++ b/target/linux/generic/hack-6.1/259-regmap_dynamic.patch @@ -0,0 +1,144 @@ +From 811d9e2268a62b830cfe93cd8bc929afcb8b198b Mon Sep 17 00:00:00 2001 +From: Felix Fietkau +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 +--- + 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 + #include + #include ++#include + #include + #include + #include +@@ -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/target/linux/generic/hack-6.1/260-crypto_test_dependencies.patch b/target/linux/generic/hack-6.1/260-crypto_test_dependencies.patch new file mode 100644 index 0000000000..64daa82743 --- /dev/null +++ b/target/linux/generic/hack-6.1/260-crypto_test_dependencies.patch @@ -0,0 +1,52 @@ +From fd1799b0bf5efa46dd3e6dfbbf3955564807e508 Mon Sep 17 00:00:00 2001 +From: Felix Fietkau +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 +--- + 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/target/linux/generic/hack-6.1/261-lib-arc4-unhide.patch b/target/linux/generic/hack-6.1/261-lib-arc4-unhide.patch new file mode 100644 index 0000000000..ee923c73f7 --- /dev/null +++ b/target/linux/generic/hack-6.1/261-lib-arc4-unhide.patch @@ -0,0 +1,24 @@ +From 241e5d3f7b0dd3c01f8c7fa83cbc9a3882286d53 Mon Sep 17 00:00:00 2001 +From: OpenWrt community +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/target/linux/generic/hack-6.1/280-rfkill-stubs.patch b/target/linux/generic/hack-6.1/280-rfkill-stubs.patch new file mode 100644 index 0000000000..7a650d132e --- /dev/null +++ b/target/linux/generic/hack-6.1/280-rfkill-stubs.patch @@ -0,0 +1,84 @@ +From 236c1acdfef5958010ac9814a9872e0a46fd78ee Mon Sep 17 00:00:00 2001 +From: John Crispin +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 +--- + 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/target/linux/generic/hack-6.1/300-MIPS-r4k_cache-use-more-efficient-cache-blast.patch b/target/linux/generic/hack-6.1/300-MIPS-r4k_cache-use-more-efficient-cache-blast.patch new file mode 100644 index 0000000000..f21f200136 --- /dev/null +++ b/target/linux/generic/hack-6.1/300-MIPS-r4k_cache-use-more-efficient-cache-blast.patch @@ -0,0 +1,64 @@ +From: Ben Menchaca +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 +Signed-off-by: Felix Fietkau +--- +--- 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/target/linux/generic/hack-6.1/321-powerpc_crtsavres_prereq.patch b/target/linux/generic/hack-6.1/321-powerpc_crtsavres_prereq.patch new file mode 100644 index 0000000000..f1942e2037 --- /dev/null +++ b/target/linux/generic/hack-6.1/321-powerpc_crtsavres_prereq.patch @@ -0,0 +1,38 @@ +From 107c0964cb8db7ca28ac5199426414fdab3c274d Mon Sep 17 00:00:00 2001 +From: "Alexandros C. Couloumbis" +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 +--- + 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/target/linux/generic/hack-6.1/402-mtd-blktrans-call-add-disks-after-mtd-device.patch b/target/linux/generic/hack-6.1/402-mtd-blktrans-call-add-disks-after-mtd-device.patch new file mode 100644 index 0000000000..dcb1aaa6e4 --- /dev/null +++ b/target/linux/generic/hack-6.1/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 +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 , + Richard Weinberger , + Miquel Raynal , + David Woodhouse + +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 +--- + 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 + #include ++#include + + #include "mtdcore.h" + +@@ -1067,6 +1068,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/target/linux/generic/hack-6.1/410-block-fit-partition-parser.patch b/target/linux/generic/hack-6.1/410-block-fit-partition-parser.patch new file mode 100644 index 0000000000..96c89f41c4 --- /dev/null +++ b/target/linux/generic/hack-6.1/410-block-fit-partition-parser.patch @@ -0,0 +1,214 @@ +From 69357074558daf6ff24c9f58714935e9e095a865 Mon Sep 17 00:00:00 2001 +From: OpenWrt community +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 + #include + #include ++#ifdef CONFIG_FIT_PARTITION ++#include ++#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/target/linux/generic/hack-6.1/420-mtd-set-rootfs-to-be-root-dev.patch b/target/linux/generic/hack-6.1/420-mtd-set-rootfs-to-be-root-dev.patch new file mode 100644 index 0000000000..f3004b60fd --- /dev/null +++ b/target/linux/generic/hack-6.1/420-mtd-set-rootfs-to-be-root-dev.patch @@ -0,0 +1,39 @@ +From: Gabor Juhos +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 +--- + 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 + #include + #include ++#include + #include + + #include +@@ -762,6 +763,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/target/linux/generic/hack-6.1/421-drivers-mtd-parsers-add-nvmem-support-to-cmdlinepart.patch b/target/linux/generic/hack-6.1/421-drivers-mtd-parsers-add-nvmem-support-to-cmdlinepart.patch new file mode 100644 index 0000000000..965a331a19 --- /dev/null +++ b/target/linux/generic/hack-6.1/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 +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 +--- + 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 + #include + #include ++#include + + /* 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/target/linux/generic/hack-6.1/430-mtk-bmt-support.patch b/target/linux/generic/hack-6.1/430-mtk-bmt-support.patch new file mode 100644 index 0000000000..2a83f46e0e --- /dev/null +++ b/target/linux/generic/hack-6.1/430-mtk-bmt-support.patch @@ -0,0 +1,33 @@ +From ac84397efb3b3868c71c10ad7521161773228a17 Mon Sep 17 00:00:00 2001 +From: OpenWrt community +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/target/linux/generic/hack-6.1/600-bridge_offload.patch b/target/linux/generic/hack-6.1/600-bridge_offload.patch new file mode 100644 index 0000000000..9d71a741b2 --- /dev/null +++ b/target/linux/generic/hack-6.1/600-bridge_offload.patch @@ -0,0 +1,846 @@ +From 11c3fae5afa6cac444d12622e2cf5af60a99c1ef Mon Sep 17 00:00:00 2001 +From: OpenWrt community +Date: Wed, 13 Jul 2022 13:43:15 +0200 +Subject: [PATCH] net/bridge: add bridge offload + +--- + include/linux/if_bridge.h | 1 + + net/bridge/Makefile | 2 +- + net/bridge/br.c | 8 + + net/bridge/br_device.c | 2 + + net/bridge/br_fdb.c | 5 + + net/bridge/br_forward.c | 3 + + net/bridge/br_if.c | 6 +- + net/bridge/br_input.c | 5 + + net/bridge/br_offload.c | 438 ++++++++++++++++++++++++++++++++ + net/bridge/br_private.h | 22 +- + net/bridge/br_private_offload.h | 23 ++ + net/bridge/br_stp.c | 3 + + net/bridge/br_sysfs_br.c | 35 +++ + net/bridge/br_sysfs_if.c | 2 + + net/bridge/br_vlan_tunnel.c | 3 + + 15 files changed, 555 insertions(+), 3 deletions(-) + create mode 100644 net/bridge/br_offload.c + create mode 100644 net/bridge/br_private_offload.h + +--- a/include/linux/if_bridge.h ++++ b/include/linux/if_bridge.h +@@ -59,6 +59,7 @@ struct br_ip_list { + #define BR_MRP_LOST_IN_CONT BIT(19) + #define BR_TX_FWD_OFFLOAD BIT(20) + #define BR_BPDU_FILTER BIT(21) ++#define BR_OFFLOAD BIT(22) + + #define BR_DEFAULT_AGEING_TIME (300 * HZ) + +--- a/net/bridge/Makefile ++++ b/net/bridge/Makefile +@@ -5,7 +5,7 @@ + + obj-$(CONFIG_BRIDGE) += bridge.o + +-bridge-y := br.o br_device.o br_fdb.o br_forward.o br_if.o br_input.o \ ++bridge-y := br.o br_device.o br_fdb.o br_forward.o br_if.o br_input.o br_offload.o \ + br_ioctl.o br_stp.o br_stp_bpdu.o \ + br_stp_if.o br_stp_timer.o br_netlink.o \ + br_netlink_tunnel.o br_arp_nd_proxy.o +--- a/net/bridge/br.c ++++ b/net/bridge/br.c +@@ -18,6 +18,7 @@ + #include + + #include "br_private.h" ++#include "br_private_offload.h" + + /* + * Handle changes in state of network devices enslaved to a bridge. +@@ -381,6 +382,10 @@ static int __init br_init(void) + if (err) + goto err_out; + ++ err = br_offload_init(); ++ if (err) ++ goto err_out0; ++ + err = register_pernet_subsys(&br_net_ops); + if (err) + goto err_out1; +@@ -430,6 +435,8 @@ err_out3: + err_out2: + unregister_pernet_subsys(&br_net_ops); + err_out1: ++ br_offload_fini(); ++err_out0: + br_fdb_fini(); + err_out: + stp_proto_unregister(&br_stp_proto); +@@ -452,6 +459,7 @@ static void __exit br_deinit(void) + #if IS_ENABLED(CONFIG_ATM_LANE) + br_fdb_test_addr_hook = NULL; + #endif ++ br_offload_fini(); + br_fdb_fini(); + } + +--- a/net/bridge/br_device.c ++++ b/net/bridge/br_device.c +@@ -524,6 +524,8 @@ void br_dev_setup(struct net_device *dev + br->bridge_hello_time = br->hello_time = 2 * HZ; + br->bridge_forward_delay = br->forward_delay = 15 * HZ; + br->bridge_ageing_time = br->ageing_time = BR_DEFAULT_AGEING_TIME; ++ br->offload_cache_size = 128; ++ br->offload_cache_reserved = 8; + dev->max_mtu = ETH_MAX_MTU; + + br_netfilter_rtable_init(br); +--- a/net/bridge/br_fdb.c ++++ b/net/bridge/br_fdb.c +@@ -23,6 +23,7 @@ + #include + #include + #include "br_private.h" ++#include "br_private_offload.h" + + static const struct rhashtable_params br_fdb_rht_params = { + .head_offset = offsetof(struct net_bridge_fdb_entry, rhnode), +@@ -518,6 +519,8 @@ static struct net_bridge_fdb_entry *fdb_ + fdb->key.vlan_id = vid; + fdb->flags = flags; + fdb->updated = fdb->used = jiffies; ++ INIT_HLIST_HEAD(&fdb->offload_in); ++ INIT_HLIST_HEAD(&fdb->offload_out); + if (rhashtable_lookup_insert_fast(&br->fdb_hash_tbl, + &fdb->rhnode, + br_fdb_rht_params)) { +@@ -794,6 +797,8 @@ static void fdb_notify(struct net_bridge + struct sk_buff *skb; + int err = -ENOBUFS; + ++ br_offload_fdb_update(fdb); ++ + if (swdev_notify) + br_switchdev_fdb_notify(br, fdb, type); + +--- a/net/bridge/br_forward.c ++++ b/net/bridge/br_forward.c +@@ -16,6 +16,7 @@ + #include + #include + #include "br_private.h" ++#include "br_private_offload.h" + + /* Don't forward packets to originating port or forwarding disabled */ + static inline int should_deliver(const struct net_bridge_port *p, +@@ -32,6 +33,8 @@ static inline int should_deliver(const s + + int br_dev_queue_push_xmit(struct net *net, struct sock *sk, struct sk_buff *skb) + { ++ br_offload_output(skb); ++ + skb_push(skb, ETH_HLEN); + if (!is_skb_forwardable(skb->dev, skb)) + goto drop; +--- a/net/bridge/br_if.c ++++ b/net/bridge/br_if.c +@@ -25,6 +25,7 @@ + #include + + #include "br_private.h" ++#include "br_private_offload.h" + + /* + * Determine initial path cost based on speed. +@@ -428,7 +429,7 @@ static struct net_bridge_port *new_nbp(s + p->path_cost = port_cost(dev); + p->priority = 0x8000 >> BR_PORT_BITS; + p->port_no = index; +- p->flags = BR_LEARNING | BR_FLOOD | BR_MCAST_FLOOD | BR_BCAST_FLOOD; ++ p->flags = BR_LEARNING | BR_FLOOD | BR_MCAST_FLOOD | BR_BCAST_FLOOD | BR_OFFLOAD; + br_init_port(p); + br_set_state(p, BR_STATE_DISABLED); + br_stp_port_timer_init(p); +@@ -771,6 +772,9 @@ void br_port_flags_change(struct net_bri + + if (mask & BR_NEIGH_SUPPRESS) + br_recalculate_neigh_suppress_enabled(br); ++ ++ if (mask & BR_OFFLOAD) ++ br_offload_port_state(p); + } + + bool br_port_flag_is_set(const struct net_device *dev, unsigned long flag) +--- a/net/bridge/br_input.c ++++ b/net/bridge/br_input.c +@@ -22,6 +22,7 @@ + #include + #include "br_private.h" + #include "br_private_tunnel.h" ++#include "br_private_offload.h" + + static int + br_netif_receive_skb(struct net *net, struct sock *sk, struct sk_buff *skb) +@@ -171,6 +172,7 @@ int br_handle_frame_finish(struct net *n + dst->used = now; + br_forward(dst->dst, skb, local_rcv, false); + } else { ++ br_offload_skb_disable(skb); + if (!mcast_hit) + br_flood(br, skb, pkt_type, local_rcv, false); + else +@@ -304,6 +306,9 @@ static rx_handler_result_t br_handle_fra + memset(skb->cb, 0, sizeof(struct br_input_skb_cb)); + + p = br_port_get_rcu(skb->dev); ++ if (br_offload_input(p, skb)) ++ return RX_HANDLER_CONSUMED; ++ + if (p->flags & BR_VLAN_TUNNEL) + br_handle_ingress_vlan_tunnel(skb, p, nbp_vlan_group_rcu(p)); + +--- /dev/null ++++ b/net/bridge/br_offload.c +@@ -0,0 +1,438 @@ ++// SPDX-License-Identifier: GPL-2.0-only ++#include ++#include ++#include "br_private.h" ++#include "br_private_offload.h" ++ ++static DEFINE_SPINLOCK(offload_lock); ++ ++struct bridge_flow_key { ++ u8 dest[ETH_ALEN]; ++ u8 src[ETH_ALEN]; ++#ifdef CONFIG_BRIDGE_VLAN_FILTERING ++ u16 vlan_tag; ++ bool vlan_present; ++#endif ++}; ++ ++struct bridge_flow { ++ struct net_bridge_port *port; ++ struct rhash_head node; ++ struct bridge_flow_key key; ++#ifdef CONFIG_BRIDGE_VLAN_FILTERING ++ bool vlan_out_present; ++ u16 vlan_out; ++#endif ++ ++ unsigned long used; ++ struct net_bridge_fdb_entry *fdb_in, *fdb_out; ++ struct hlist_node fdb_list_in, fdb_list_out; ++ ++ struct rcu_head rcu; ++}; ++ ++static const struct rhashtable_params flow_params = { ++ .automatic_shrinking = true, ++ .head_offset = offsetof(struct bridge_flow, node), ++ .key_len = sizeof(struct bridge_flow_key), ++ .key_offset = offsetof(struct bridge_flow, key), ++}; ++ ++static struct kmem_cache *offload_cache __read_mostly; ++ ++static void ++flow_rcu_free(struct rcu_head *head) ++{ ++ struct bridge_flow *flow; ++ ++ flow = container_of(head, struct bridge_flow, rcu); ++ kmem_cache_free(offload_cache, flow); ++} ++ ++static void ++__br_offload_flow_free(struct bridge_flow *flow) ++{ ++ flow->used = 0; ++ hlist_del(&flow->fdb_list_in); ++ hlist_del(&flow->fdb_list_out); ++ ++ call_rcu(&flow->rcu, flow_rcu_free); ++} ++ ++static void ++br_offload_flow_free(struct bridge_flow *flow) ++{ ++ if (rhashtable_remove_fast(&flow->port->offload.rht, &flow->node, ++ flow_params) != 0) ++ return; ++ ++ __br_offload_flow_free(flow); ++} ++ ++static bool ++br_offload_flow_fdb_refresh_time(struct bridge_flow *flow, ++ struct net_bridge_fdb_entry *fdb) ++{ ++ if (!time_after(flow->used, fdb->updated)) ++ return false; ++ ++ fdb->updated = flow->used; ++ ++ return true; ++} ++ ++ ++static void ++br_offload_flow_refresh_time(struct bridge_flow *flow) ++{ ++ br_offload_flow_fdb_refresh_time(flow, flow->fdb_in); ++ br_offload_flow_fdb_refresh_time(flow, flow->fdb_out); ++} ++ ++static void ++br_offload_destroy_cb(void *ptr, void *arg) ++{ ++ struct bridge_flow *flow = ptr; ++ ++ __br_offload_flow_free(flow); ++} ++ ++static bool ++br_offload_need_gc(struct net_bridge_port *p) ++{ ++ return (atomic_read(&p->offload.rht.nelems) + ++ p->br->offload_cache_reserved) >= p->br->offload_cache_size; ++} ++ ++static void ++br_offload_gc_work(struct work_struct *work) ++{ ++ struct rhashtable_iter hti; ++ struct net_bridge_port *p; ++ struct bridge_flow *gc_flow = NULL; ++ struct bridge_flow *flow; ++ unsigned long gc_used; ++ ++ p = container_of(work, struct net_bridge_port, offload.gc_work); ++ ++ if (!br_offload_need_gc(p)) ++ return; ++ ++ rhashtable_walk_enter(&p->offload.rht, &hti); ++ rhashtable_walk_start(&hti); ++ while ((flow = rhashtable_walk_next(&hti)) != NULL) { ++ unsigned long used; ++ ++ if (IS_ERR(flow)) ++ continue; ++ ++ used = READ_ONCE(flow->used); ++ if (!used) ++ continue; ++ ++ if (gc_flow && !time_before(used, gc_used)) ++ continue; ++ ++ gc_flow = flow; ++ gc_used = used; ++ } ++ rhashtable_walk_stop(&hti); ++ rhashtable_walk_exit(&hti); ++ ++ if (!gc_flow) ++ return; ++ ++ spin_lock_bh(&offload_lock); ++ if (br_offload_need_gc(p) && gc_flow && ++ gc_flow->used == gc_used) ++ br_offload_flow_free(gc_flow); ++ if (p->offload.enabled && br_offload_need_gc(p)) ++ queue_work(system_long_wq, work); ++ spin_unlock_bh(&offload_lock); ++ ++} ++ ++void br_offload_port_state(struct net_bridge_port *p) ++{ ++ struct net_bridge_port_offload *o = &p->offload; ++ bool enabled = true; ++ bool flush = false; ++ ++ if (p->state != BR_STATE_FORWARDING || ++ !(p->flags & BR_OFFLOAD)) ++ enabled = false; ++ ++ spin_lock_bh(&offload_lock); ++ if (o->enabled == enabled) ++ goto out; ++ ++ if (enabled) { ++ if (!o->gc_work.func) ++ INIT_WORK(&o->gc_work, br_offload_gc_work); ++ rhashtable_init(&o->rht, &flow_params); ++ } else { ++ flush = true; ++ rhashtable_free_and_destroy(&o->rht, br_offload_destroy_cb, o); ++ } ++ ++ o->enabled = enabled; ++ ++out: ++ spin_unlock_bh(&offload_lock); ++ ++ if (flush) ++ flush_work(&o->gc_work); ++} ++ ++void br_offload_fdb_update(const struct net_bridge_fdb_entry *fdb) ++{ ++ struct bridge_flow *f; ++ struct hlist_node *tmp; ++ ++ spin_lock_bh(&offload_lock); ++ ++ hlist_for_each_entry_safe(f, tmp, &fdb->offload_in, fdb_list_in) ++ br_offload_flow_free(f); ++ ++ hlist_for_each_entry_safe(f, tmp, &fdb->offload_out, fdb_list_out) ++ br_offload_flow_free(f); ++ ++ spin_unlock_bh(&offload_lock); ++} ++ ++static void ++br_offload_prepare_key(struct net_bridge_port *p, struct bridge_flow_key *key, ++ struct sk_buff *skb) ++{ ++ memset(key, 0, sizeof(*key)); ++ memcpy(key, eth_hdr(skb), 2 * ETH_ALEN); ++#ifdef CONFIG_BRIDGE_VLAN_FILTERING ++ if (!br_opt_get(p->br, BROPT_VLAN_ENABLED)) ++ return; ++ ++ if (!skb_vlan_tag_present(skb) || skb->vlan_proto != p->br->vlan_proto) ++ return; ++ ++ key->vlan_present = true; ++ key->vlan_tag = skb_vlan_tag_get_id(skb); ++#endif ++} ++ ++void br_offload_output(struct sk_buff *skb) ++{ ++ struct net_bridge_port_offload *o; ++ struct br_input_skb_cb *cb = (struct br_input_skb_cb *)skb->cb; ++ struct net_bridge_port *p, *inp; ++ struct net_device *dev; ++ struct net_bridge_fdb_entry *fdb_in, *fdb_out; ++ struct net_bridge_vlan_group *vg; ++ struct bridge_flow_key key; ++ struct bridge_flow *flow; ++ u16 vlan; ++ ++ if (!cb->offload) ++ return; ++ ++ rcu_read_lock(); ++ ++ p = br_port_get_rcu(skb->dev); ++ if (!p) ++ goto out; ++ ++ o = &p->offload; ++ if (!o->enabled) ++ goto out; ++ ++ if (atomic_read(&p->offload.rht.nelems) >= p->br->offload_cache_size) ++ goto out; ++ ++ dev = dev_get_by_index_rcu(dev_net(p->br->dev), cb->input_ifindex); ++ if (!dev) ++ goto out; ++ ++ inp = br_port_get_rcu(dev); ++ if (!inp) ++ goto out; ++ ++ vg = nbp_vlan_group_rcu(inp); ++ vlan = cb->input_vlan_present ? cb->input_vlan_tag : br_get_pvid(vg); ++ fdb_in = br_fdb_find_rcu(p->br, eth_hdr(skb)->h_source, vlan); ++ if (!fdb_in || !fdb_in->dst) ++ goto out; ++ ++ vg = nbp_vlan_group_rcu(p); ++ vlan = skb_vlan_tag_present(skb) ? skb_vlan_tag_get_id(skb) : br_get_pvid(vg); ++ fdb_out = br_fdb_find_rcu(p->br, eth_hdr(skb)->h_dest, vlan); ++ if (!fdb_out || !fdb_out->dst) ++ goto out; ++ ++ br_offload_prepare_key(p, &key, skb); ++#ifdef CONFIG_BRIDGE_VLAN_FILTERING ++ key.vlan_present = cb->input_vlan_present; ++ key.vlan_tag = cb->input_vlan_tag; ++#endif ++ ++ flow = kmem_cache_alloc(offload_cache, GFP_ATOMIC); ++ flow->port = inp; ++ memcpy(&flow->key, &key, sizeof(key)); ++ ++#ifdef CONFIG_BRIDGE_VLAN_FILTERING ++ flow->vlan_out_present = skb_vlan_tag_present(skb); ++ flow->vlan_out = skb_vlan_tag_get(skb); ++#endif ++ ++ flow->fdb_in = fdb_in; ++ flow->fdb_out = fdb_out; ++ flow->used = jiffies; ++ ++ spin_lock_bh(&offload_lock); ++ if (!o->enabled || ++ atomic_read(&p->offload.rht.nelems) >= p->br->offload_cache_size || ++ rhashtable_insert_fast(&inp->offload.rht, &flow->node, flow_params)) { ++ kmem_cache_free(offload_cache, flow); ++ goto out_unlock; ++ } ++ ++ hlist_add_head(&flow->fdb_list_in, &fdb_in->offload_in); ++ hlist_add_head(&flow->fdb_list_out, &fdb_out->offload_out); ++ ++ if (br_offload_need_gc(p)) ++ queue_work(system_long_wq, &p->offload.gc_work); ++ ++out_unlock: ++ spin_unlock_bh(&offload_lock); ++ ++out: ++ rcu_read_unlock(); ++} ++ ++bool br_offload_input(struct net_bridge_port *p, struct sk_buff *skb) ++{ ++ struct net_bridge_port_offload *o = &p->offload; ++ struct br_input_skb_cb *cb = (struct br_input_skb_cb *)skb->cb; ++ struct bridge_flow_key key; ++ struct net_bridge_port *dst; ++ struct bridge_flow *flow; ++ unsigned long now = jiffies; ++ bool ret = false; ++ ++ if (skb->len < sizeof(key)) ++ return false; ++ ++ if (!o->enabled) ++ return false; ++ ++ if (is_multicast_ether_addr(eth_hdr(skb)->h_dest)) ++ return false; ++ ++ br_offload_prepare_key(p, &key, skb); ++ ++ rcu_read_lock(); ++ flow = rhashtable_lookup(&o->rht, &key, flow_params); ++ if (!flow) { ++ cb->offload = 1; ++#ifdef CONFIG_BRIDGE_VLAN_FILTERING ++ cb->input_vlan_present = key.vlan_present != 0; ++ cb->input_vlan_tag = key.vlan_tag; ++#endif ++ cb->input_ifindex = p->dev->ifindex; ++ goto out; ++ } ++ ++ if (flow->fdb_in->dst != p) ++ goto out; ++ ++ dst = flow->fdb_out->dst; ++ if (!dst) ++ goto out; ++ ++ ret = true; ++#ifdef CONFIG_BRIDGE_VLAN_FILTERING ++ if (!flow->vlan_out_present && key.vlan_present) { ++ __vlan_hwaccel_clear_tag(skb); ++ } else if (flow->vlan_out_present) { ++ if (skb_vlan_tag_present(skb) && ++ skb->vlan_proto != p->br->vlan_proto) { ++ /* Protocol-mismatch, empty out vlan_tci for new tag */ ++ skb_push(skb, ETH_HLEN); ++ skb = vlan_insert_tag_set_proto(skb, skb->vlan_proto, ++ skb_vlan_tag_get(skb)); ++ if (unlikely(!skb)) ++ goto out; ++ ++ skb_pull(skb, ETH_HLEN); ++ skb_reset_mac_len(skb); ++ } ++ ++ __vlan_hwaccel_put_tag(skb, p->br->vlan_proto, ++ flow->vlan_out); ++ } ++#endif ++ ++ skb->dev = dst->dev; ++ skb_push(skb, ETH_HLEN); ++ ++ if (skb_warn_if_lro(skb) || !is_skb_forwardable(skb->dev, skb)) { ++ kfree_skb(skb); ++ goto out; ++ } ++ ++ if (now - flow->used >= HZ) { ++ flow->used = now; ++ br_offload_flow_refresh_time(flow); ++ } ++ ++ skb_forward_csum(skb); ++ dev_queue_xmit(skb); ++ ++out: ++ rcu_read_unlock(); ++ return ret; ++} ++ ++static void ++br_offload_check_gc(struct net_bridge *br) ++{ ++ struct net_bridge_port *p; ++ ++ spin_lock_bh(&br->lock); ++ list_for_each_entry(p, &br->port_list, list) ++ if (br_offload_need_gc(p)) ++ queue_work(system_long_wq, &p->offload.gc_work); ++ spin_unlock_bh(&br->lock); ++} ++ ++ ++int br_offload_set_cache_size(struct net_bridge *br, unsigned long val, ++ struct netlink_ext_ack *extack) ++{ ++ br->offload_cache_size = val; ++ br_offload_check_gc(br); ++ ++ return 0; ++} ++ ++int br_offload_set_cache_reserved(struct net_bridge *br, unsigned long val, ++ struct netlink_ext_ack *extack) ++{ ++ br->offload_cache_reserved = val; ++ br_offload_check_gc(br); ++ ++ return 0; ++} ++ ++int __init br_offload_init(void) ++{ ++ offload_cache = kmem_cache_create("bridge_offload_cache", ++ sizeof(struct bridge_flow), ++ 0, SLAB_HWCACHE_ALIGN, NULL); ++ if (!offload_cache) ++ return -ENOMEM; ++ ++ return 0; ++} ++ ++void br_offload_fini(void) ++{ ++ kmem_cache_destroy(offload_cache); ++} +--- a/net/bridge/br_private.h ++++ b/net/bridge/br_private.h +@@ -268,7 +268,13 @@ struct net_bridge_fdb_entry { + unsigned long updated ____cacheline_aligned_in_smp; + unsigned long used; + +- struct rcu_head rcu; ++ union { ++ struct { ++ struct hlist_head offload_in; ++ struct hlist_head offload_out; ++ }; ++ struct rcu_head rcu; ++ }; + }; + + #define MDB_PG_FLAGS_PERMANENT BIT(0) +@@ -343,6 +349,12 @@ struct net_bridge_mdb_entry { + struct rcu_head rcu; + }; + ++struct net_bridge_port_offload { ++ struct rhashtable rht; ++ struct work_struct gc_work; ++ bool enabled; ++}; ++ + struct net_bridge_port { + struct net_bridge *br; + struct net_device *dev; +@@ -403,6 +415,7 @@ struct net_bridge_port { + u16 backup_redirected_cnt; + + struct bridge_stp_xstats stp_xstats; ++ struct net_bridge_port_offload offload; + }; + + #define kobj_to_brport(obj) container_of(obj, struct net_bridge_port, kobj) +@@ -519,6 +532,9 @@ struct net_bridge { + struct kobject *ifobj; + u32 auto_cnt; + ++ u32 offload_cache_size; ++ u32 offload_cache_reserved; ++ + #ifdef CONFIG_NET_SWITCHDEV + /* Counter used to make sure that hardware domains get unique + * identifiers in case a bridge spans multiple switchdev instances. +@@ -553,6 +569,10 @@ struct br_input_skb_cb { + #ifdef CONFIG_NETFILTER_FAMILY_BRIDGE + u8 br_netfilter_broute:1; + #endif ++ u8 offload:1; ++ u8 input_vlan_present:1; ++ u16 input_vlan_tag; ++ int input_ifindex; + + #ifdef CONFIG_NET_SWITCHDEV + /* Set if TX data plane offloading is used towards at least one +--- /dev/null ++++ b/net/bridge/br_private_offload.h +@@ -0,0 +1,23 @@ ++#ifndef __BR_OFFLOAD_H ++#define __BR_OFFLOAD_H ++ ++bool br_offload_input(struct net_bridge_port *p, struct sk_buff *skb); ++void br_offload_output(struct sk_buff *skb); ++void br_offload_port_state(struct net_bridge_port *p); ++void br_offload_fdb_update(const struct net_bridge_fdb_entry *fdb); ++int br_offload_init(void); ++void br_offload_fini(void); ++int br_offload_set_cache_size(struct net_bridge *br, unsigned long val, ++ struct netlink_ext_ack *extack); ++int br_offload_set_cache_reserved(struct net_bridge *br, unsigned long val, ++ struct netlink_ext_ack *extack); ++ ++static inline void br_offload_skb_disable(struct sk_buff *skb) ++{ ++ struct br_input_skb_cb *cb = (struct br_input_skb_cb *)skb->cb; ++ ++ if (cb->offload) ++ cb->offload = 0; ++} ++ ++#endif +--- a/net/bridge/br_stp.c ++++ b/net/bridge/br_stp.c +@@ -12,6 +12,7 @@ + + #include "br_private.h" + #include "br_private_stp.h" ++#include "br_private_offload.h" + + /* since time values in bpdu are in jiffies and then scaled (1/256) + * before sending, make sure that is at least one STP tick. +@@ -52,6 +53,8 @@ void br_set_state(struct net_bridge_port + (unsigned int) p->port_no, p->dev->name, + br_port_state_names[p->state]); + ++ br_offload_port_state(p); ++ + if (p->br->stp_enabled == BR_KERNEL_STP) { + switch (p->state) { + case BR_STATE_BLOCKING: +--- a/net/bridge/br_sysfs_br.c ++++ b/net/bridge/br_sysfs_br.c +@@ -18,6 +18,7 @@ + #include + + #include "br_private.h" ++#include "br_private_offload.h" + + /* IMPORTANT: new bridge options must be added with netlink support only + * please do not add new sysfs entries +@@ -930,6 +931,38 @@ static ssize_t vlan_stats_per_port_store + static DEVICE_ATTR_RW(vlan_stats_per_port); + #endif + ++static ssize_t offload_cache_size_show(struct device *d, ++ struct device_attribute *attr, ++ char *buf) ++{ ++ struct net_bridge *br = to_bridge(d); ++ return sprintf(buf, "%u\n", br->offload_cache_size); ++} ++ ++static ssize_t offload_cache_size_store(struct device *d, ++ struct device_attribute *attr, ++ const char *buf, size_t len) ++{ ++ return store_bridge_parm(d, buf, len, br_offload_set_cache_size); ++} ++static DEVICE_ATTR_RW(offload_cache_size); ++ ++static ssize_t offload_cache_reserved_show(struct device *d, ++ struct device_attribute *attr, ++ char *buf) ++{ ++ struct net_bridge *br = to_bridge(d); ++ return sprintf(buf, "%u\n", br->offload_cache_reserved); ++} ++ ++static ssize_t offload_cache_reserved_store(struct device *d, ++ struct device_attribute *attr, ++ const char *buf, size_t len) ++{ ++ return store_bridge_parm(d, buf, len, br_offload_set_cache_reserved); ++} ++static DEVICE_ATTR_RW(offload_cache_reserved); ++ + static struct attribute *bridge_attrs[] = { + &dev_attr_forward_delay.attr, + &dev_attr_hello_time.attr, +@@ -984,6 +1017,8 @@ static struct attribute *bridge_attrs[] + &dev_attr_vlan_stats_enabled.attr, + &dev_attr_vlan_stats_per_port.attr, + #endif ++ &dev_attr_offload_cache_size.attr, ++ &dev_attr_offload_cache_reserved.attr, + NULL + }; + +--- a/net/bridge/br_sysfs_if.c ++++ b/net/bridge/br_sysfs_if.c +@@ -241,6 +241,7 @@ BRPORT_ATTR_FLAG(broadcast_flood, BR_BCA + BRPORT_ATTR_FLAG(neigh_suppress, BR_NEIGH_SUPPRESS); + BRPORT_ATTR_FLAG(isolated, BR_ISOLATED); + BRPORT_ATTR_FLAG(bpdu_filter, BR_BPDU_FILTER); ++BRPORT_ATTR_FLAG(offload, BR_OFFLOAD); + + #ifdef CONFIG_BRIDGE_IGMP_SNOOPING + static ssize_t show_multicast_router(struct net_bridge_port *p, char *buf) +@@ -295,6 +296,7 @@ static const struct brport_attribute *br + &brport_attr_isolated, + &brport_attr_bpdu_filter, + &brport_attr_backup_port, ++ &brport_attr_offload, + NULL + }; + +--- a/net/bridge/br_vlan_tunnel.c ++++ b/net/bridge/br_vlan_tunnel.c +@@ -15,6 +15,7 @@ + + #include "br_private.h" + #include "br_private_tunnel.h" ++#include "br_private_offload.h" + + static inline int br_vlan_tunid_cmp(struct rhashtable_compare_arg *arg, + const void *ptr) +@@ -180,6 +181,7 @@ void br_handle_ingress_vlan_tunnel(struc + skb_dst_drop(skb); + + __vlan_hwaccel_put_tag(skb, p->br->vlan_proto, vlan->vid); ++ br_offload_skb_disable(skb); + } + + int br_handle_egress_vlan_tunnel(struct sk_buff *skb, +@@ -201,6 +203,7 @@ int br_handle_egress_vlan_tunnel(struct + if (err) + return err; + ++ br_offload_skb_disable(skb); + tunnel_dst = rcu_dereference(vlan->tinfo.tunnel_dst); + if (tunnel_dst && dst_hold_safe(&tunnel_dst->dst)) + skb_dst_set(skb, &tunnel_dst->dst); diff --git a/target/linux/generic/hack-6.1/601-of_net-add-mac-address-ascii-support.patch b/target/linux/generic/hack-6.1/601-of_net-add-mac-address-ascii-support.patch new file mode 100644 index 0000000000..83483751f8 --- /dev/null +++ b/target/linux/generic/hack-6.1/601-of_net-add-mac-address-ascii-support.patch @@ -0,0 +1,112 @@ +From: Yousong Zhou +Subject: [PATCH] ath79: add nvmem cell mac-address-ascii support + +This is needed for devices with mac address stored in ascii format, e.g. +HiWiFi HC6361 to be ported in the following patch. + +Submitted-by: Yousong Zhou +--- + net/ethernet/eth.c | 83 ++++++++++++------ + 1 files changed, 72 insertions(+), 11 deletions(-) + +--- a/net/ethernet/eth.c ++++ b/net/ethernet/eth.c +@@ -538,6 +538,63 @@ int eth_platform_get_mac_address(struct + } + EXPORT_SYMBOL(eth_platform_get_mac_address); + ++static void *nvmem_cell_get_mac_address(struct nvmem_cell *cell) ++{ ++ size_t len; ++ void *mac; ++ ++ mac = nvmem_cell_read(cell, &len); ++ if (IS_ERR(mac)) ++ return PTR_ERR(mac); ++ if (len != ETH_ALEN) { ++ kfree(mac); ++ return ERR_PTR(-EINVAL); ++ } ++ return mac; ++} ++ ++static void *nvmem_cell_get_mac_address_ascii(struct nvmem_cell *cell) ++{ ++ size_t len; ++ int ret; ++ void *mac_ascii; ++ u8 *mac; ++ ++ mac_ascii = nvmem_cell_read(cell, &len); ++ if (IS_ERR(mac_ascii)) ++ return PTR_ERR(mac_ascii); ++ if (len != ETH_ALEN*2+5) { ++ kfree(mac_ascii); ++ return ERR_PTR(-EINVAL); ++ } ++ mac = kmalloc(ETH_ALEN, GFP_KERNEL); ++ if (!mac) { ++ kfree(mac_ascii); ++ return ERR_PTR(-ENOMEM); ++ } ++ ret = sscanf(mac_ascii, "%2hhx:%2hhx:%2hhx:%2hhx:%2hhx:%2hhx", ++ &mac[0], &mac[1], &mac[2], ++ &mac[3], &mac[4], &mac[5]); ++ kfree(mac_ascii); ++ if (ret == ETH_ALEN) ++ return mac; ++ kfree(mac); ++ return ERR_PTR(-EINVAL); ++} ++ ++static struct nvmem_cell_mac_address_property { ++ char *name; ++ void *(*read)(struct nvmem_cell *); ++} nvmem_cell_mac_address_properties[] = { ++ { ++ .name = "mac-address", ++ .read = nvmem_cell_get_mac_address, ++ }, { ++ .name = "mac-address-ascii", ++ .read = nvmem_cell_get_mac_address_ascii, ++ }, ++}; ++ + /** + * nvmem_get_mac_address - Obtain the MAC address from an nvmem cell named + * 'mac-address' associated with given device. +@@ -551,19 +608,23 @@ int nvmem_get_mac_address(struct device + { + struct nvmem_cell *cell; + const void *mac; +- size_t len; ++ struct nvmem_cell_mac_address_property *property; ++ int i; + +- cell = nvmem_cell_get(dev, "mac-address"); +- if (IS_ERR(cell)) +- return PTR_ERR(cell); +- +- mac = nvmem_cell_read(cell, &len); +- nvmem_cell_put(cell); +- +- if (IS_ERR(mac)) +- return PTR_ERR(mac); ++ for (i = 0; i < ARRAY_SIZE(nvmem_cell_mac_address_properties); i++) { ++ property = &nvmem_cell_mac_address_properties[i]; ++ cell = nvmem_cell_get(dev, property->name); ++ if (IS_ERR(cell)) { ++ if (i == ARRAY_SIZE(nvmem_cell_mac_address_properties) - 1) ++ return PTR_ERR(cell); ++ continue; ++ } ++ mac = property->read(cell); ++ nvmem_cell_put(cell); ++ break; ++ } + +- if (len != ETH_ALEN || !is_valid_ether_addr(mac)) { ++ if (!is_valid_ether_addr(mac)) { + kfree(mac); + return -EINVAL; + } diff --git a/target/linux/generic/hack-6.1/645-netfilter-connmark-introduce-set-dscpmark.patch b/target/linux/generic/hack-6.1/645-netfilter-connmark-introduce-set-dscpmark.patch new file mode 100644 index 0000000000..2d3fe01a75 --- /dev/null +++ b/target/linux/generic/hack-6.1/645-netfilter-connmark-introduce-set-dscpmark.patch @@ -0,0 +1,212 @@ +From eda40b8c8c82e0f2789d6bc8bf63846dce2e8f32 Mon Sep 17 00:00:00 2001 +From: Kevin Darbyshire-Bryant +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 +--- + 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/target/linux/generic/hack-6.1/650-netfilter-add-xt_FLOWOFFLOAD-target.patch b/target/linux/generic/hack-6.1/650-netfilter-add-xt_FLOWOFFLOAD-target.patch new file mode 100644 index 0000000000..961a930bb3 --- /dev/null +++ b/target/linux/generic/hack-6.1/650-netfilter-add-xt_FLOWOFFLOAD-target.patch @@ -0,0 +1,860 @@ +From: Felix Fietkau +Date: Tue, 20 Feb 2018 15:56:02 +0100 +Subject: [PATCH] netfilter: add xt_FLOWOFFLOAD target + +Signed-off-by: Felix Fietkau +--- + 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 ++ * ++ * 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 ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++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 + #include + #include +-#include + #include + #include + #include +@@ -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 ++ ++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/target/linux/generic/hack-6.1/651-wireless_mesh_header.patch b/target/linux/generic/hack-6.1/651-wireless_mesh_header.patch new file mode 100644 index 0000000000..12a031ec84 --- /dev/null +++ b/target/linux/generic/hack-6.1/651-wireless_mesh_header.patch @@ -0,0 +1,24 @@ +From 6d3bc769657b0ee7c7506dad9911111c4226a7ea Mon Sep 17 00:00:00 2001 +From: Imre Kaloz +Date: Fri, 7 Jul 2017 17:21:05 +0200 +Subject: mac80211: increase wireless mesh header size + +lede-commit 3d4466cfd8f75f717efdb1f96fdde3c70d865fc1 +Signed-off-by: Imre Kaloz +--- + 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/target/linux/generic/hack-6.1/660-fq_codel_defaults.patch b/target/linux/generic/hack-6.1/660-fq_codel_defaults.patch new file mode 100644 index 0000000000..5541c0bc89 --- /dev/null +++ b/target/linux/generic/hack-6.1/660-fq_codel_defaults.patch @@ -0,0 +1,27 @@ +From a6ccb238939b25851474a279b20367fd24a0e816 Mon Sep 17 00:00:00 2001 +From: Felix Fietkau +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 +--- + 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/target/linux/generic/hack-6.1/661-kernel-ct-size-the-hashtable-more-adequately.patch b/target/linux/generic/hack-6.1/661-kernel-ct-size-the-hashtable-more-adequately.patch new file mode 100644 index 0000000000..134e2100c8 --- /dev/null +++ b/target/linux/generic/hack-6.1/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 +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 +--- + 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/target/linux/generic/hack-6.1/700-swconfig_switch_drivers.patch b/target/linux/generic/hack-6.1/700-swconfig_switch_drivers.patch new file mode 100644 index 0000000000..48be440025 --- /dev/null +++ b/target/linux/generic/hack-6.1/700-swconfig_switch_drivers.patch @@ -0,0 +1,131 @@ +From 36e516290611e613aa92996cb4339561452695b4 Mon Sep 17 00:00:00 2001 +From: Felix Fietkau +Date: Fri, 7 Jul 2017 17:24:23 +0200 +Subject: net: swconfig: adds openwrt switch layer + +Signed-off-by: Felix Fietkau +--- + drivers/net/phy/Kconfig | 83 +++++++++++++++++++++++++++++++++++++++++++++++ + drivers/net/phy/Makefile | 15 +++++++++ + include/uapi/linux/Kbuild | 1 + + 3 files changed, 99 insertions(+) + +--- a/drivers/net/phy/Kconfig ++++ b/drivers/net/phy/Kconfig +@@ -61,6 +61,80 @@ config SFP + depends on HWMON || HWMON=n + select MDIO_I2C + ++comment "Switch configuration API + drivers" ++ ++config SWCONFIG ++ tristate "Switch configuration API" ++ help ++ Switch configuration API using netlink. This allows ++ you to configure the VLAN features of certain switches. ++ ++config SWCONFIG_LEDS ++ bool "Switch LED trigger support" ++ depends on (SWCONFIG && LEDS_TRIGGERS) ++ ++config ADM6996_PHY ++ tristate "Driver for ADM6996 switches" ++ select SWCONFIG ++ help ++ Currently supports the ADM6996FC and ADM6996M switches. ++ Support for FC is very limited. ++ ++config AR8216_PHY ++ tristate "Driver for Atheros AR8216/8327 switches" ++ select SWCONFIG ++ select ETHERNET_PACKET_MANGLE ++ ++config AR8216_PHY_LEDS ++ bool "Atheros AR8216 switch LED support" ++ depends on (AR8216_PHY && LEDS_CLASS) ++ ++source "drivers/net/phy/b53/Kconfig" ++ ++config IP17XX_PHY ++ tristate "Driver for IC+ IP17xx switches" ++ select SWCONFIG ++ ++config PSB6970_PHY ++ tristate "Lantiq XWAY Tantos (PSB6970) Ethernet switch" ++ select SWCONFIG ++ ++config RTL8306_PHY ++ tristate "Driver for Realtek RTL8306S switches" ++ select SWCONFIG ++ ++config RTL8366_SMI ++ tristate "Driver for the RTL8366 SMI interface" ++ depends on GPIOLIB ++ help ++ This module implements the SMI interface protocol which is used ++ by some RTL8366 ethernet switch devices via the generic GPIO API. ++ ++if RTL8366_SMI ++ ++config RTL8366_SMI_DEBUG_FS ++ bool "RTL8366 SMI interface debugfs support" ++ depends on DEBUG_FS ++ default n ++ ++config RTL8366S_PHY ++ tristate "Driver for the Realtek RTL8366S switch" ++ select SWCONFIG ++ ++config RTL8366RB_PHY ++ tristate "Driver for the Realtek RTL8366RB switch" ++ select SWCONFIG ++ ++config RTL8367_PHY ++ tristate "Driver for the Realtek RTL8367R/M switches" ++ select SWCONFIG ++ ++config RTL8367B_PHY ++ tristate "Driver fot the Realtek RTL8367R-VB switch" ++ select SWCONFIG ++ ++endif # RTL8366_SMI ++ + comment "MII PHY device drivers" + + config AMD_PHY +--- a/drivers/net/phy/Makefile ++++ b/drivers/net/phy/Makefile +@@ -24,6 +24,21 @@ libphy-$(CONFIG_LED_TRIGGER_PHY) += phy_ + obj-$(CONFIG_PHYLINK) += phylink.o + obj-$(CONFIG_PHYLIB) += libphy.o + ++obj-$(CONFIG_SWCONFIG) += swconfig.o ++obj-$(CONFIG_ADM6996_PHY) += adm6996.o ++obj-$(CONFIG_AR8216_PHY) += ar8xxx.o ++ar8xxx-y += ar8216.o ++ar8xxx-y += ar8327.o ++obj-$(CONFIG_SWCONFIG_B53) += b53/ ++obj-$(CONFIG_IP17XX_PHY) += ip17xx.o ++obj-$(CONFIG_PSB6970_PHY) += psb6970.o ++obj-$(CONFIG_RTL8306_PHY) += rtl8306.o ++obj-$(CONFIG_RTL8366_SMI) += rtl8366_smi.o ++obj-$(CONFIG_RTL8366S_PHY) += rtl8366s.o ++obj-$(CONFIG_RTL8366RB_PHY) += rtl8366rb.o ++obj-$(CONFIG_RTL8367_PHY) += rtl8367.o ++obj-$(CONFIG_RTL8367B_PHY) += rtl8367b.o ++ + obj-$(CONFIG_NETWORK_PHY_TIMESTAMPING) += mii_timestamper.o + + obj-$(CONFIG_SFP) += sfp.o +--- a/include/linux/platform_data/b53.h ++++ b/include/linux/platform_data/b53.h +@@ -29,6 +29,9 @@ struct b53_platform_data { + u32 chip_id; + u16 enabled_ports; + ++ /* allow to specify an ethX alias */ ++ const char *alias; ++ + /* only used by MMAP'd driver */ + unsigned big_endian:1; + void __iomem *regs; diff --git a/target/linux/generic/hack-6.1/711-net-dsa-mv88e6xxx-disable-ATU-violation.patch b/target/linux/generic/hack-6.1/711-net-dsa-mv88e6xxx-disable-ATU-violation.patch new file mode 100644 index 0000000000..ffa44f0960 --- /dev/null +++ b/target/linux/generic/hack-6.1/711-net-dsa-mv88e6xxx-disable-ATU-violation.patch @@ -0,0 +1,21 @@ +From ebd924d773223593142d417c41d4ee6fa16f1805 Mon Sep 17 00:00:00 2001 +From: OpenWrt community +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/target/linux/generic/hack-6.1/720-net-phy-add-aqr-phys.patch b/target/linux/generic/hack-6.1/720-net-phy-add-aqr-phys.patch new file mode 100644 index 0000000000..29050d38ea --- /dev/null +++ b/target/linux/generic/hack-6.1/720-net-phy-add-aqr-phys.patch @@ -0,0 +1,142 @@ +From: Birger Koblitz +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 + +--- 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) +@@ -381,6 +383,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; +@@ -511,7 +556,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); + } + +@@ -719,6 +764,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, +@@ -744,6 +807,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); +@@ -754,8 +835,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/target/linux/generic/hack-6.1/721-net-add-packet-mangeling.patch b/target/linux/generic/hack-6.1/721-net-add-packet-mangeling.patch new file mode 100644 index 0000000000..206e16cb17 --- /dev/null +++ b/target/linux/generic/hack-6.1/721-net-add-packet-mangeling.patch @@ -0,0 +1,178 @@ +From ffe387740bbe88dd88bbe04d6375902708003d6e Mon Sep 17 00:00:00 2001 +From: Felix Fietkau +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 +--- + 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 + #include + #include ++#include + + #include + #include +@@ -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/target/linux/generic/hack-6.1/722-net-phy-aquantia-enable-AQR112-and-AQR412.patch b/target/linux/generic/hack-6.1/722-net-phy-aquantia-enable-AQR112-and-AQR412.patch new file mode 100644 index 0000000000..211696c568 --- /dev/null +++ b/target/linux/generic/hack-6.1/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 +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 +--- + 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 +@@ -135,6 +137,29 @@ + #define AQR107_OP_IN_PROG_SLEEP 1000 + #define AQR107_OP_IN_PROG_TIMEOUT 100000 + ++/* 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; +@@ -266,6 +291,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; +@@ -825,6 +895,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); +@@ -835,9 +929,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/target/linux/generic/hack-6.1/723-net-phy-aquantia-fix-system-side-protocol-mi.patch b/target/linux/generic/hack-6.1/723-net-phy-aquantia-fix-system-side-protocol-mi.patch new file mode 100644 index 0000000000..7d16c8aa28 --- /dev/null +++ b/target/linux/generic/hack-6.1/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 +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 +--- + 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 +@@ -324,10 +324,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/target/linux/generic/hack-6.1/724-net-phy-aquantia-Add-AQR113-driver-support.patch b/target/linux/generic/hack-6.1/724-net-phy-aquantia-Add-AQR113-driver-support.patch new file mode 100644 index 0000000000..d03c3430fa --- /dev/null +++ b/target/linux/generic/hack-6.1/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" +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 +@@ -914,6 +915,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, +@@ -936,6 +945,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/target/linux/generic/hack-6.1/725-net-phy-aquantia-add-PHY_IDs-for-AQR112-variants.patch b/target/linux/generic/hack-6.1/725-net-phy-aquantia-add-PHY_IDs-for-AQR112-variants.patch new file mode 100644 index 0000000000..9821c0c86e --- /dev/null +++ b/target/linux/generic/hack-6.1/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 +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 +--- + 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 +@@ -915,6 +917,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, +@@ -945,6 +971,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/target/linux/generic/hack-6.1/760-net-usb-r8152-add-LED-configuration-from-OF.patch b/target/linux/generic/hack-6.1/760-net-usb-r8152-add-LED-configuration-from-OF.patch new file mode 100644 index 0000000000..c54332f71c --- /dev/null +++ b/target/linux/generic/hack-6.1/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 +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 +--- + 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 + #include + #include ++#include + #include + #include + #include +@@ -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/target/linux/generic/hack-6.1/761-dt-bindings-net-add-RTL8152-binding-documentation.patch b/target/linux/generic/hack-6.1/761-dt-bindings-net-add-RTL8152-binding-documentation.patch new file mode 100644 index 0000000000..be262b993c --- /dev/null +++ b/target/linux/generic/hack-6.1/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 +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 +--- + .../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 ++ ++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/target/linux/generic/hack-6.1/773-bgmac-add-srab-switch.patch b/target/linux/generic/hack-6.1/773-bgmac-add-srab-switch.patch new file mode 100644 index 0000000000..1e4fc446ce --- /dev/null +++ b/target/linux/generic/hack-6.1/773-bgmac-add-srab-switch.patch @@ -0,0 +1,98 @@ +From 3cb240533ab787899dc7f17aa7d6c5b4810e2e58 Mon Sep 17 00:00:00 2001 +From: Hauke Mehrtens +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 +--- + 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 + #include + #include ++#include + #include + #include + #include +@@ -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/target/linux/generic/hack-6.1/780-usb-net-MeigLink_modem_support.patch b/target/linux/generic/hack-6.1/780-usb-net-MeigLink_modem_support.patch new file mode 100644 index 0000000000..34a741b1c7 --- /dev/null +++ b/target/linux/generic/hack-6.1/780-usb-net-MeigLink_modem_support.patch @@ -0,0 +1,43 @@ +From f81700b6bb2eda3756247bce472d8eaf6f466f61 Mon Sep 17 00:00:00 2001 +From: OpenWrt community +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 */ +@@ -1143,6 +1145,11 @@ static const struct usb_device_id option + .driver_info = ZLP }, + { 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/target/linux/generic/hack-6.1/790-SFP-GE-T-ignore-TX_FAULT.patch b/target/linux/generic/hack-6.1/790-SFP-GE-T-ignore-TX_FAULT.patch new file mode 100644 index 0000000000..83b2c304e2 --- /dev/null +++ b/target/linux/generic/hack-6.1/790-SFP-GE-T-ignore-TX_FAULT.patch @@ -0,0 +1,65 @@ +From 7cc39a6bedbd85f3ff7e16845f310e4ce8d9833f Mon Sep 17 00:00:00 2001 +From: Daniel Golle +Date: Tue, 6 Sep 2022 00:31:19 +0100 +Subject: [PATCH] net: sfp: add quirk for ATS SFP-GE-T 1000Base-TX module +To: netdev@vger.kernel.org, + linux-kernel@vger.kernel.org, + Russell King , + Andrew Lunn , + Heiner Kallweit +Cc: David S. Miller , + Eric Dumazet , + Jakub Kicinski , + Paolo Abeni , + Josef Schlehofer + +This copper module comes with broken TX_FAULT indicator which must be +ignored for it to work. Implement ignoring TX_FAULT state bit also +during reset/insertion and mute the warning telling the user that the +module indicates TX_FAULT. + +Co-authored-by: Josef Schlehofer +Signed-off-by: Daniel Golle +--- + drivers/net/phy/sfp.c | 14 +++++++++++--- + 1 file changed, 11 insertions(+), 3 deletions(-) + +--- a/drivers/net/phy/sfp.c ++++ b/drivers/net/phy/sfp.c +@@ -373,6 +373,11 @@ static const struct sfp_quirk sfp_quirks + .modes = sfp_quirk_2500basex, + .fixup = sfp_fixup_ignore_tx_fault, + }, { ++ // OEM SFP-GE-T is 1000Base-T module ++ .vendor = "OEM", ++ .part = "SFP-GE-T", ++ .fixup = sfp_fixup_ignore_tx_fault, ++ }, { + // Lantech 8330-262D-E can operate at 2500base-X, but + // incorrectly report 2500MBd NRZ in their EEPROM + .vendor = "Lantech", +@@ -2306,7 +2311,8 @@ static void sfp_sm_main(struct sfp *sfp, + * or t_start_up, so assume there is a fault. + */ + sfp_sm_fault(sfp, SFP_S_INIT_TX_FAULT, +- sfp->sm_fault_retries == N_FAULT_INIT); ++ !sfp->tx_fault_ignore && ++ (sfp->sm_fault_retries == N_FAULT_INIT)); + } else if (event == SFP_E_TIMEOUT || event == SFP_E_TX_CLEAR) { + init_done: + sfp->sm_phy_retries = R_PHY_RETRY; +@@ -2529,10 +2535,12 @@ static void sfp_check_state(struct sfp * + mutex_lock(&sfp->st_mutex); + state = sfp_get_state(sfp); + changed = state ^ sfp->state; +- if (sfp->tx_fault_ignore) ++ if (sfp->tx_fault_ignore) { + changed &= SFP_F_PRESENT | SFP_F_LOS; +- else ++ state &= ~SFP_F_TX_FAULT; ++ } else { + changed &= SFP_F_PRESENT | SFP_F_LOS | SFP_F_TX_FAULT; ++ } + + for (i = 0; i < GPIO_MAX; i++) + if (changed & BIT(i)) diff --git a/target/linux/generic/hack-6.1/800-GPIO-add-named-gpio-exports.patch b/target/linux/generic/hack-6.1/800-GPIO-add-named-gpio-exports.patch new file mode 100644 index 0000000000..9fde1eaabb --- /dev/null +++ b/target/linux/generic/hack-6.1/800-GPIO-add-named-gpio-exports.patch @@ -0,0 +1,162 @@ +From cc809a441d8f2924f785eb863dfa6aef47a25b0b Mon Sep 17 00:00:00 2001 +From: John Crispin +Date: Tue, 12 Aug 2014 20:49:27 +0200 +Subject: [PATCH 30/36] GPIO: add named gpio exports + +Signed-off-by: John Crispin +--- a/drivers/gpio/gpiolib-of.c ++++ b/drivers/gpio/gpiolib-of.c +@@ -19,6 +19,8 @@ + #include + #include + #include ++#include ++#include + + #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/target/linux/generic/hack-6.1/901-debloat_sock_diag.patch b/target/linux/generic/hack-6.1/901-debloat_sock_diag.patch new file mode 100644 index 0000000000..ab629d598f --- /dev/null +++ b/target/linux/generic/hack-6.1/901-debloat_sock_diag.patch @@ -0,0 +1,162 @@ +From 3b6115d6b57a263bdc8c9b1df273bd4a7955eead Mon Sep 17 00:00:00 2001 +From: Felix Fietkau +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 +--- + 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 + #include + #include ++#include + + #include + +@@ -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 + #include + #include +-#include + #include + #include + +@@ -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/target/linux/generic/hack-6.1/902-debloat_proc.patch b/target/linux/generic/hack-6.1/902-debloat_proc.patch new file mode 100644 index 0000000000..c58370eff1 --- /dev/null +++ b/target/linux/generic/hack-6.1/902-debloat_proc.patch @@ -0,0 +1,408 @@ +From 9e3f1d0805b2d919904dd9a4ff0d956314cc3cba Mon Sep 17 00:00:00 2001 +From: Felix Fietkau +Date: Sat, 8 Jul 2017 08:20:09 +0200 +Subject: debloat: procfs + +Signed-off-by: Felix Fietkau +--- + 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/target/linux/generic/hack-6.1/904-debloat_dma_buf.patch b/target/linux/generic/hack-6.1/904-debloat_dma_buf.patch new file mode 100644 index 0000000000..302c0007f2 --- /dev/null +++ b/target/linux/generic/hack-6.1/904-debloat_dma_buf.patch @@ -0,0 +1,92 @@ +From e3692cb2fcd5ba1244512a0f43b8118f65f1c375 Mon Sep 17 00:00:00 2001 +From: Felix Fietkau +Date: Sat, 8 Jul 2017 08:20:43 +0200 +Subject: debloat: dmabuf + +Signed-off-by: Felix Fietkau +--- + 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/target/linux/generic/hack-6.1/910-kobject_uevent.patch b/target/linux/generic/hack-6.1/910-kobject_uevent.patch new file mode 100644 index 0000000000..c4c41ca400 --- /dev/null +++ b/target/linux/generic/hack-6.1/910-kobject_uevent.patch @@ -0,0 +1,32 @@ +From 0d37e6edc09c99e683dd91ca0e83bbc0df8477b3 Mon Sep 17 00:00:00 2001 +From: Felix Fietkau +Date: Sun, 16 Jul 2017 16:56:10 +0200 +Subject: lib: add uevent_next_seqnum() + +Signed-off-by: Felix Fietkau +--- + 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/target/linux/generic/hack-6.1/911-kobject_add_broadcast_uevent.patch b/target/linux/generic/hack-6.1/911-kobject_add_broadcast_uevent.patch new file mode 100644 index 0000000000..a487d55193 --- /dev/null +++ b/target/linux/generic/hack-6.1/911-kobject_add_broadcast_uevent.patch @@ -0,0 +1,76 @@ +From 0d37e6edc09c99e683dd91ca0e83bbc0df8477b3 Mon Sep 17 00:00:00 2001 +From: Felix Fietkau +Date: Sun, 16 Jul 2017 16:56:10 +0200 +Subject: lib: add uevent_next_seqnum() + +Signed-off-by: Felix Fietkau +--- + 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/target/linux/generic/hack-6.1/920-device_tree_cmdline.patch b/target/linux/generic/hack-6.1/920-device_tree_cmdline.patch new file mode 100644 index 0000000000..d1f36e716e --- /dev/null +++ b/target/linux/generic/hack-6.1/920-device_tree_cmdline.patch @@ -0,0 +1,21 @@ +From e08bcbbaa52fcc41f02743fd2e62a33255ce52da Mon Sep 17 00:00:00 2001 +From: OpenWrt community +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/target/linux/generic/hack-6.1/930-Revert-Revert-Revert-driver-core-Set-fw_devlink-on-b.patch b/target/linux/generic/hack-6.1/930-Revert-Revert-Revert-driver-core-Set-fw_devlink-on-b.patch new file mode 100644 index 0000000000..4f4d6c7509 --- /dev/null +++ b/target/linux/generic/hack-6.1/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?= +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 +--- + 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) diff --git a/target/linux/generic/pending-6.1/100-compiler.h-only-include-asm-rwonce.h-for-kernel-code.patch b/target/linux/generic/pending-6.1/100-compiler.h-only-include-asm-rwonce.h-for-kernel-code.patch new file mode 100644 index 0000000000..22f52c1d46 --- /dev/null +++ b/target/linux/generic/pending-6.1/100-compiler.h-only-include-asm-rwonce.h-for-kernel-code.patch @@ -0,0 +1,29 @@ +From: Felix Fietkau +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 +--- + +--- 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 ++ + #endif /* __KERNEL__ */ + + /* +@@ -252,6 +254,4 @@ static inline void *offset_to_ptr(const + */ + #define prevent_tail_call_optimization() mb() + +-#include +- + #endif /* __LINUX_COMPILER_H */ diff --git a/target/linux/generic/pending-6.1/101-Use-stddefs.h-instead-of-compiler.h.patch b/target/linux/generic/pending-6.1/101-Use-stddefs.h-instead-of-compiler.h.patch new file mode 100644 index 0000000000..55388fb58a --- /dev/null +++ b/target/linux/generic/pending-6.1/101-Use-stddefs.h-instead-of-compiler.h.patch @@ -0,0 +1,22 @@ +From d3c5b26768dbe990c4e1bd79e420c11ce7491d51 Mon Sep 17 00:00:00 2001 +From: OpenWrt community +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(-) + +--- a/include/uapi/linux/swab.h ++++ b/include/uapi/linux/swab.h +@@ -3,7 +3,7 @@ + #define _UAPI_LINUX_SWAB_H + + #include +-#include ++#include + #include + #include + diff --git a/target/linux/generic/pending-6.1/102-MIPS-only-process-negative-stack-offsets-on-stack-tr.patch b/target/linux/generic/pending-6.1/102-MIPS-only-process-negative-stack-offsets-on-stack-tr.patch new file mode 100644 index 0000000000..95a9656d26 --- /dev/null +++ b/target/linux/generic/pending-6.1/102-MIPS-only-process-negative-stack-offsets-on-stack-tr.patch @@ -0,0 +1,57 @@ +From: Felix Fietkau +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 +--- + +--- 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/target/linux/generic/pending-6.1/103-kbuild-export-SUBARCH.patch b/target/linux/generic/pending-6.1/103-kbuild-export-SUBARCH.patch new file mode 100644 index 0000000000..d378dabdda --- /dev/null +++ b/target/linux/generic/pending-6.1/103-kbuild-export-SUBARCH.patch @@ -0,0 +1,21 @@ +From 173019b66dcc9d68ad9333aa744dad1e369b5aa8 Mon Sep 17 00:00:00 2001 +From: Felix Fietkau +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 +--- + Makefile | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +--- a/Makefile ++++ b/Makefile +@@ -526,7 +526,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/target/linux/generic/pending-6.1/120-Fix-alloc_node_mem_map-with-ARCH_PFN_OFFSET-calcu.patch b/target/linux/generic/pending-6.1/120-Fix-alloc_node_mem_map-with-ARCH_PFN_OFFSET-calcu.patch new file mode 100644 index 0000000000..1ba95d3ffa --- /dev/null +++ b/target/linux/generic/pending-6.1/120-Fix-alloc_node_mem_map-with-ARCH_PFN_OFFSET-calcu.patch @@ -0,0 +1,82 @@ +From: Tobias Wolf +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 +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 +--- + +--- a/mm/page_alloc.c ++++ b/mm/page_alloc.c +@@ -7602,7 +7602,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/target/linux/generic/pending-6.1/130-add-linux-spidev-compatible-si3210.patch b/target/linux/generic/pending-6.1/130-add-linux-spidev-compatible-si3210.patch new file mode 100644 index 0000000000..d260cf1f9a --- /dev/null +++ b/target/linux/generic/pending-6.1/130-add-linux-spidev-compatible-si3210.patch @@ -0,0 +1,18 @@ +From: Giuseppe Lippolis +Subject: Add the linux,spidev compatible in spidev Several device in ramips have this binding in the dts + +Signed-off-by: Giuseppe Lippolis +--- + 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/target/linux/generic/pending-6.1/140-jffs2-use-.rename2-and-add-RENAME_WHITEOUT-support.patch b/target/linux/generic/pending-6.1/140-jffs2-use-.rename2-and-add-RENAME_WHITEOUT-support.patch new file mode 100644 index 0000000000..8f40ae3ba2 --- /dev/null +++ b/target/linux/generic/pending-6.1/140-jffs2-use-.rename2-and-add-RENAME_WHITEOUT-support.patch @@ -0,0 +1,81 @@ +From: Felix Fietkau +Subject: jffs2: use .rename2 and add RENAME_WHITEOUT support + +It is required for renames on overlayfs + +Signed-off-by: Felix Fietkau +--- + +--- 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/target/linux/generic/pending-6.1/141-jffs2-add-RENAME_EXCHANGE-support.patch b/target/linux/generic/pending-6.1/141-jffs2-add-RENAME_EXCHANGE-support.patch new file mode 100644 index 0000000000..f58fc791d2 --- /dev/null +++ b/target/linux/generic/pending-6.1/141-jffs2-add-RENAME_EXCHANGE-support.patch @@ -0,0 +1,73 @@ +From: Felix Fietkau +Subject: jffs2: add RENAME_EXCHANGE support + +Signed-off-by: Felix Fietkau +--- + +--- 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/target/linux/generic/pending-6.1/142-jffs2-add-splice-ops.patch b/target/linux/generic/pending-6.1/142-jffs2-add-splice-ops.patch new file mode 100644 index 0000000000..de847a1f5c --- /dev/null +++ b/target/linux/generic/pending-6.1/142-jffs2-add-splice-ops.patch @@ -0,0 +1,20 @@ +From: Felix Fietkau +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 +--- + +--- 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/target/linux/generic/pending-6.1/150-bridge_allow_receiption_on_disabled_port.patch b/target/linux/generic/pending-6.1/150-bridge_allow_receiption_on_disabled_port.patch new file mode 100644 index 0000000000..9968a79699 --- /dev/null +++ b/target/linux/generic/pending-6.1/150-bridge_allow_receiption_on_disabled_port.patch @@ -0,0 +1,45 @@ +From: Stephen Hemminger +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 +Signed-off-by: Felix Fietkau + +--- 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/target/linux/generic/pending-6.1/190-rtc-rs5c372-support_alarms_up_to_1_week.patch b/target/linux/generic/pending-6.1/190-rtc-rs5c372-support_alarms_up_to_1_week.patch new file mode 100644 index 0000000000..13b79b5c09 --- /dev/null +++ b/target/linux/generic/pending-6.1/190-rtc-rs5c372-support_alarms_up_to_1_week.patch @@ -0,0 +1,94 @@ +From: Daniel González Cabanelas +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 +--- + 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/target/linux/generic/pending-6.1/191-rtc-rs5c372-let_the_alarm_to_be_used_as_wakeup_source.patch b/target/linux/generic/pending-6.1/191-rtc-rs5c372-let_the_alarm_to_be_used_as_wakeup_source.patch new file mode 100644 index 0000000000..7e9d0e66c0 --- /dev/null +++ b/target/linux/generic/pending-6.1/191-rtc-rs5c372-let_the_alarm_to_be_used_as_wakeup_source.patch @@ -0,0 +1,70 @@ +From: Daniel González Cabanelas +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 +--- + 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/target/linux/generic/pending-6.1/203-kallsyms_uncompressed.patch b/target/linux/generic/pending-6.1/203-kallsyms_uncompressed.patch new file mode 100644 index 0000000000..b525976fc9 --- /dev/null +++ b/target/linux/generic/pending-6.1/203-kallsyms_uncompressed.patch @@ -0,0 +1,119 @@ +From: Felix Fietkau +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 +--- + 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 +@@ -257,6 +257,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/target/linux/generic/pending-6.1/205-backtrace_module_info.patch b/target/linux/generic/pending-6.1/205-backtrace_module_info.patch new file mode 100644 index 0000000000..6379ce071d --- /dev/null +++ b/target/linux/generic/pending-6.1/205-backtrace_module_info.patch @@ -0,0 +1,41 @@ +From: Felix Fietkau +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 +--- + 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/target/linux/generic/pending-6.1/240-remove-unsane-filenames-from-deps_initramfs-list.patch b/target/linux/generic/pending-6.1/240-remove-unsane-filenames-from-deps_initramfs-list.patch new file mode 100644 index 0000000000..29cfade716 --- /dev/null +++ b/target/linux/generic/pending-6.1/240-remove-unsane-filenames-from-deps_initramfs-list.patch @@ -0,0 +1,30 @@ +From: Gabor Juhos +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 +Signed-off-by: Felix Fietkau +--- + 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/target/linux/generic/pending-6.1/261-enable_wilink_platform_without_drivers.patch b/target/linux/generic/pending-6.1/261-enable_wilink_platform_without_drivers.patch new file mode 100644 index 0000000000..cd31f9d934 --- /dev/null +++ b/target/linux/generic/pending-6.1/261-enable_wilink_platform_without_drivers.patch @@ -0,0 +1,20 @@ +From: Imre Kaloz +Subject: [PATCH] hack: net: wireless: make the wl12xx glue code available with + compat-wireless, too + +Signed-off-by: Imre Kaloz +--- + 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/target/linux/generic/pending-6.1/270-platform-mikrotik-build-bits.patch b/target/linux/generic/pending-6.1/270-platform-mikrotik-build-bits.patch new file mode 100644 index 0000000000..99f83bb2c4 --- /dev/null +++ b/target/linux/generic/pending-6.1/270-platform-mikrotik-build-bits.patch @@ -0,0 +1,31 @@ +From c2deb5ef01a0ef09088832744cbace9e239a6ee0 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Thibaut=20VAR=C3=88NE?= +Date: Sat, 28 Mar 2020 12:11:50 +0100 +Subject: [PATCH] generic: platform/mikrotik build bits (5.4) +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +This patch adds platform/mikrotik kernel build bits + +Signed-off-by: Thibaut VARÈNE +--- + drivers/platform/Kconfig | 2 ++ + drivers/platform/Makefile | 1 + + 2 files changed, 3 insertions(+) + +--- a/drivers/platform/Kconfig ++++ b/drivers/platform/Kconfig +@@ -15,3 +15,5 @@ source "drivers/platform/mellanox/Kconfi + source "drivers/platform/olpc/Kconfig" + + source "drivers/platform/surface/Kconfig" ++ ++source "drivers/platform/mikrotik/Kconfig" +--- a/drivers/platform/Makefile ++++ b/drivers/platform/Makefile +@@ -10,3 +10,4 @@ obj-$(CONFIG_OLPC_EC) += olpc/ + obj-$(CONFIG_GOLDFISH) += goldfish/ + obj-$(CONFIG_CHROME_PLATFORMS) += chrome/ + obj-$(CONFIG_SURFACE_PLATFORMS) += surface/ ++obj-$(CONFIG_MIKROTIK) += mikrotik/ diff --git a/target/linux/generic/pending-6.1/300-mips_expose_boot_raw.patch b/target/linux/generic/pending-6.1/300-mips_expose_boot_raw.patch new file mode 100644 index 0000000000..05d024925d --- /dev/null +++ b/target/linux/generic/pending-6.1/300-mips_expose_boot_raw.patch @@ -0,0 +1,40 @@ +From: Mark Miller +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 +Acked-by: Rob Landley +--- +--- 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/target/linux/generic/pending-6.1/302-mips_no_branch_likely.patch b/target/linux/generic/pending-6.1/302-mips_no_branch_likely.patch new file mode 100644 index 0000000000..271923fca8 --- /dev/null +++ b/target/linux/generic/pending-6.1/302-mips_no_branch_likely.patch @@ -0,0 +1,22 @@ +From: Felix Fietkau +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 +--- + 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/target/linux/generic/pending-6.1/305-mips_module_reloc.patch b/target/linux/generic/pending-6.1/305-mips_module_reloc.patch new file mode 100644 index 0000000000..bbea1f61c1 --- /dev/null +++ b/target/linux/generic/pending-6.1/305-mips_module_reloc.patch @@ -0,0 +1,370 @@ +From: Felix Fietkau +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 +--- + 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/target/linux/generic/pending-6.1/307-mips_highmem_offset.patch b/target/linux/generic/pending-6.1/307-mips_highmem_offset.patch new file mode 100644 index 0000000000..0529b0c5c8 --- /dev/null +++ b/target/linux/generic/pending-6.1/307-mips_highmem_offset.patch @@ -0,0 +1,19 @@ +From: Felix Fietkau +Subject: kernel: adjust mips highmem offset to avoid the need for -mlong-calls on systems with >256M RAM + +Signed-off-by: Felix Fietkau +--- + 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/target/linux/generic/pending-6.1/308-mips32r2_tune.patch b/target/linux/generic/pending-6.1/308-mips32r2_tune.patch new file mode 100644 index 0000000000..ef92a5dfb6 --- /dev/null +++ b/target/linux/generic/pending-6.1/308-mips32r2_tune.patch @@ -0,0 +1,22 @@ +From: Felix Fietkau +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 +--- + 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/target/linux/generic/pending-6.1/310-arm_module_unresolved_weak_sym.patch b/target/linux/generic/pending-6.1/310-arm_module_unresolved_weak_sym.patch new file mode 100644 index 0000000000..191dc6ac3c --- /dev/null +++ b/target/linux/generic/pending-6.1/310-arm_module_unresolved_weak_sym.patch @@ -0,0 +1,22 @@ +From: Felix Fietkau +Subject: fix errors in unresolved weak symbols on arm + +lede-commit: 570699d4838a907c3ef9f2819bf19eb72997b32f +Signed-off-by: Felix Fietkau +--- + 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/target/linux/generic/pending-6.1/330-MIPS-kexec-Accept-command-line-parameters-from-users.patch b/target/linux/generic/pending-6.1/330-MIPS-kexec-Accept-command-line-parameters-from-users.patch new file mode 100644 index 0000000000..3a5f5a238d --- /dev/null +++ b/target/linux/generic/pending-6.1/330-MIPS-kexec-Accept-command-line-parameters-from-users.patch @@ -0,0 +1,283 @@ +From: Yousong Zhou +Subject: MIPS: kexec: Accept command line parameters from userspace. + +Signed-off-by: Yousong Zhou +--- + 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 + #include + ++#include + #include + #include +- +-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 ++#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 + #include + #include ++#include "machine_kexec.h" + + #include + +-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/target/linux/generic/pending-6.1/332-arc-add-OWRTDTB-section.patch b/target/linux/generic/pending-6.1/332-arc-add-OWRTDTB-section.patch new file mode 100644 index 0000000000..30158cf399 --- /dev/null +++ b/target/linux/generic/pending-6.1/332-arc-add-OWRTDTB-section.patch @@ -0,0 +1,84 @@ +From bb0c3b0175240bf152fd7c644821a0cf9f77c37c Mon Sep 17 00:00:00 2001 +From: Evgeniy Didin +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 +Signed-off-by: Alexey Brodkin +Signed-off-by: Evgeniy Didin +--- + 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/target/linux/generic/pending-6.1/333-arc-enable-unaligned-access-in-kernel-mode.patch b/target/linux/generic/pending-6.1/333-arc-enable-unaligned-access-in-kernel-mode.patch new file mode 100644 index 0000000000..1848a84cc4 --- /dev/null +++ b/target/linux/generic/pending-6.1/333-arc-enable-unaligned-access-in-kernel-mode.patch @@ -0,0 +1,24 @@ +From: Alexey Brodkin +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 +--- + 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/target/linux/generic/pending-6.1/342-powerpc-Enable-kernel-XZ-compression-option-on-PPC_8.patch b/target/linux/generic/pending-6.1/342-powerpc-Enable-kernel-XZ-compression-option-on-PPC_8.patch new file mode 100644 index 0000000000..082b122cb4 --- /dev/null +++ b/target/linux/generic/pending-6.1/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 +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 +Signed-off-by: Pawel Dembicki +--- + 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/target/linux/generic/pending-6.1/400-mtd-mtdsplit-support.patch b/target/linux/generic/pending-6.1/400-mtd-mtdsplit-support.patch new file mode 100644 index 0000000000..d07afd70ee --- /dev/null +++ b/target/linux/generic/pending-6.1/400-mtd-mtdsplit-support.patch @@ -0,0 +1,327 @@ +From 39717277d5c87bdb183cf2f258957b44ba99b4df Mon Sep 17 00:00:00 2001 +From: OpenWrt community +Date: Wed, 13 Jul 2022 11:47:35 +0200 +Subject: [PATCH] mtd: mtdsplit support + +--- + drivers/mtd/Kconfig | 19 ++++ + drivers/mtd/Makefile | 2 + + drivers/mtd/mtdpart.c | 169 ++++++++++++++++++++++++++++----- + include/linux/mtd/mtd.h | 25 +++++ + include/linux/mtd/partitions.h | 7 ++ + 5 files changed, 197 insertions(+), 25 deletions(-) + +--- a/drivers/mtd/Kconfig ++++ b/drivers/mtd/Kconfig +@@ -12,6 +12,25 @@ menuconfig MTD + + if MTD + ++menu "OpenWrt specific MTD options" ++ ++config MTD_ROOTFS_ROOT_DEV ++ bool "Automatically set 'rootfs' partition to be root filesystem" ++ default y ++ ++config MTD_SPLIT_FIRMWARE ++ bool "Automatically split firmware partition for kernel+rootfs" ++ default y ++ ++config MTD_SPLIT_FIRMWARE_NAME ++ string "Firmware partition name" ++ depends on MTD_SPLIT_FIRMWARE ++ default "firmware" ++ ++source "drivers/mtd/mtdsplit/Kconfig" ++ ++endmenu ++ + config MTD_TESTS + tristate "MTD tests support (DANGEROUS)" + depends on m +--- a/drivers/mtd/Makefile ++++ b/drivers/mtd/Makefile +@@ -9,6 +9,8 @@ mtd-y := mtdcore.o mtdsuper.o mtdconc + + obj-y += parsers/ + ++obj-$(CONFIG_MTD_SPLIT) += mtdsplit/ ++ + # 'Users' - code which presents functionality to userspace. + obj-$(CONFIG_MTD_BLKDEVS) += mtd_blkdevs.o + obj-$(CONFIG_MTD_BLOCK) += mtdblock.o +--- a/drivers/mtd/mtdpart.c ++++ b/drivers/mtd/mtdpart.c +@@ -15,11 +15,13 @@ + #include + #include + #include ++#include + #include + #include + #include + + #include "mtdcore.h" ++#include "mtdsplit/mtdsplit.h" + + /* + * MTD methods which simply translate the effective address and pass through +@@ -236,6 +238,146 @@ static int mtd_add_partition_attrs(struc + return ret; + } + ++static DEFINE_SPINLOCK(part_parser_lock); ++static LIST_HEAD(part_parsers); ++ ++static struct mtd_part_parser *mtd_part_parser_get(const char *name) ++{ ++ struct mtd_part_parser *p, *ret = NULL; ++ ++ spin_lock(&part_parser_lock); ++ ++ list_for_each_entry(p, &part_parsers, list) ++ if (!strcmp(p->name, name) && try_module_get(p->owner)) { ++ ret = p; ++ break; ++ } ++ ++ spin_unlock(&part_parser_lock); ++ ++ return ret; ++} ++ ++static inline void mtd_part_parser_put(const struct mtd_part_parser *p) ++{ ++ module_put(p->owner); ++} ++ ++static struct mtd_part_parser * ++get_partition_parser_by_type(enum mtd_parser_type type, ++ struct mtd_part_parser *start) ++{ ++ struct mtd_part_parser *p, *ret = NULL; ++ ++ spin_lock(&part_parser_lock); ++ ++ p = list_prepare_entry(start, &part_parsers, list); ++ if (start) ++ mtd_part_parser_put(start); ++ ++ list_for_each_entry_continue(p, &part_parsers, list) { ++ if (p->type == type && try_module_get(p->owner)) { ++ ret = p; ++ break; ++ } ++ } ++ ++ spin_unlock(&part_parser_lock); ++ ++ return ret; ++} ++ ++static int parse_mtd_partitions_by_type(struct mtd_info *master, ++ enum mtd_parser_type type, ++ const struct mtd_partition **pparts, ++ struct mtd_part_parser_data *data) ++{ ++ struct mtd_part_parser *prev = NULL; ++ int ret = 0; ++ ++ while (1) { ++ struct mtd_part_parser *parser; ++ ++ parser = get_partition_parser_by_type(type, prev); ++ if (!parser) ++ break; ++ ++ ret = (*parser->parse_fn)(master, pparts, data); ++ ++ if (ret > 0) { ++ mtd_part_parser_put(parser); ++ printk(KERN_NOTICE ++ "%d %s partitions found on MTD device %s\n", ++ ret, parser->name, master->name); ++ break; ++ } ++ ++ prev = parser; ++ } ++ ++ return ret; ++} ++ ++static int ++run_parsers_by_type(struct mtd_info *child, enum mtd_parser_type type) ++{ ++ struct mtd_partition *parts; ++ int nr_parts; ++ int i; ++ ++ nr_parts = parse_mtd_partitions_by_type(child, type, (const struct mtd_partition **)&parts, ++ NULL); ++ if (nr_parts <= 0) ++ return nr_parts; ++ ++ if (WARN_ON(!parts)) ++ return 0; ++ ++ for (i = 0; i < nr_parts; i++) { ++ /* adjust partition offsets */ ++ parts[i].offset += child->part.offset; ++ ++ mtd_add_partition(child->parent, ++ parts[i].name, ++ parts[i].offset, ++ parts[i].size); ++ } ++ ++ kfree(parts); ++ ++ return nr_parts; ++} ++ ++#ifdef CONFIG_MTD_SPLIT_FIRMWARE_NAME ++#define SPLIT_FIRMWARE_NAME CONFIG_MTD_SPLIT_FIRMWARE_NAME ++#else ++#define SPLIT_FIRMWARE_NAME "unused" ++#endif ++ ++static void split_firmware(struct mtd_info *master, struct mtd_info *part) ++{ ++ run_parsers_by_type(part, MTD_PARSER_TYPE_FIRMWARE); ++} ++ ++static void mtd_partition_split(struct mtd_info *master, struct mtd_info *part) ++{ ++ static int rootfs_found = 0; ++ ++ if (rootfs_found) ++ return; ++ ++ if (!strcmp(part->name, "rootfs")) { ++ run_parsers_by_type(part, MTD_PARSER_TYPE_ROOTFS); ++ ++ rootfs_found = 1; ++ } ++ ++ if (IS_ENABLED(CONFIG_MTD_SPLIT_FIRMWARE) && ++ !strcmp(part->name, SPLIT_FIRMWARE_NAME) && ++ !of_find_property(mtd_get_of_node(part), "compatible", NULL)) ++ split_firmware(master, part); ++} ++ + int mtd_add_partition(struct mtd_info *parent, const char *name, + long long offset, long long length) + { +@@ -274,6 +416,7 @@ int mtd_add_partition(struct mtd_info *p + if (ret) + goto err_remove_part; + ++ mtd_partition_split(parent, child); + mtd_add_partition_attrs(child); + + return 0; +@@ -422,6 +565,7 @@ int add_mtd_partitions(struct mtd_info * + goto err_del_partitions; + } + ++ mtd_partition_split(master, child); + mtd_add_partition_attrs(child); + + /* Look for subpartitions */ +@@ -438,31 +582,6 @@ err_del_partitions: + return ret; + } + +-static DEFINE_SPINLOCK(part_parser_lock); +-static LIST_HEAD(part_parsers); +- +-static struct mtd_part_parser *mtd_part_parser_get(const char *name) +-{ +- struct mtd_part_parser *p, *ret = NULL; +- +- spin_lock(&part_parser_lock); +- +- list_for_each_entry(p, &part_parsers, list) +- if (!strcmp(p->name, name) && try_module_get(p->owner)) { +- ret = p; +- break; +- } +- +- spin_unlock(&part_parser_lock); +- +- return ret; +-} +- +-static inline void mtd_part_parser_put(const struct mtd_part_parser *p) +-{ +- module_put(p->owner); +-} +- + /* + * Many partition parsers just expected the core to kfree() all their data in + * one chunk. Do that by default. +--- a/include/linux/mtd/mtd.h ++++ b/include/linux/mtd/mtd.h +@@ -613,6 +613,24 @@ static inline void mtd_align_erase_req(s + req->len += mtd->erasesize - mod; + } + ++static inline uint64_t mtd_roundup_to_eb(uint64_t sz, struct mtd_info *mtd) ++{ ++ if (mtd_mod_by_eb(sz, mtd) == 0) ++ return sz; ++ ++ /* Round up to next erase block */ ++ return (mtd_div_by_eb(sz, mtd) + 1) * mtd->erasesize; ++} ++ ++static inline uint64_t mtd_rounddown_to_eb(uint64_t sz, struct mtd_info *mtd) ++{ ++ if (mtd_mod_by_eb(sz, mtd) == 0) ++ return sz; ++ ++ /* Round down to the start of the current erase block */ ++ return (mtd_div_by_eb(sz, mtd)) * mtd->erasesize; ++} ++ + static inline uint32_t mtd_div_by_ws(uint64_t sz, struct mtd_info *mtd) + { + if (mtd->writesize_shift) +@@ -686,6 +704,13 @@ extern struct mtd_info *of_get_mtd_devic + extern struct mtd_info *get_mtd_device_nm(const char *name); + extern void put_mtd_device(struct mtd_info *mtd); + ++static inline uint64_t mtdpart_get_offset(const struct mtd_info *mtd) ++{ ++ if (!mtd_is_partition(mtd)) ++ return 0; ++ ++ return mtd->part.offset; ++} + + struct mtd_notifier { + void (*add)(struct mtd_info *mtd); +--- a/include/linux/mtd/partitions.h ++++ b/include/linux/mtd/partitions.h +@@ -75,6 +75,12 @@ struct mtd_part_parser_data { + * Functions dealing with the various ways of partitioning the space + */ + ++enum mtd_parser_type { ++ MTD_PARSER_TYPE_DEVICE = 0, ++ MTD_PARSER_TYPE_ROOTFS, ++ MTD_PARSER_TYPE_FIRMWARE, ++}; ++ + struct mtd_part_parser { + struct list_head list; + struct module *owner; +@@ -83,6 +89,7 @@ struct mtd_part_parser { + int (*parse_fn)(struct mtd_info *, const struct mtd_partition **, + struct mtd_part_parser_data *); + void (*cleanup)(const struct mtd_partition *pparts, int nr_parts); ++ enum mtd_parser_type type; + }; + + /* Container for passing around a set of parsed partitions */ diff --git a/target/linux/generic/pending-6.1/402-mtd-spi-nor-write-support-for-minor-aligned-partitions.patch b/target/linux/generic/pending-6.1/402-mtd-spi-nor-write-support-for-minor-aligned-partitions.patch new file mode 100644 index 0000000000..ed9d1c9dc1 --- /dev/null +++ b/target/linux/generic/pending-6.1/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 +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 +Signed-off-by: Thibaut VARÈNE + +--- + +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 +Reported-by: Dan Carpenter +--- + 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/target/linux/generic/pending-6.1/420-mtd-redboot_space.patch b/target/linux/generic/pending-6.1/420-mtd-redboot_space.patch new file mode 100644 index 0000000000..5518ea71dd --- /dev/null +++ b/target/linux/generic/pending-6.1/420-mtd-redboot_space.patch @@ -0,0 +1,41 @@ +From: Felix Fietkau +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 +--- + drivers/mtd/redboot.c | 19 +++++++++++++------ + 1 file changed, 13 insertions(+), 6 deletions(-) + +--- a/drivers/mtd/parsers/redboot.c ++++ b/drivers/mtd/parsers/redboot.c +@@ -278,14 +278,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/target/linux/generic/pending-6.1/430-mtd-add-myloader-partition-parser.patch b/target/linux/generic/pending-6.1/430-mtd-add-myloader-partition-parser.patch new file mode 100644 index 0000000000..8a6e630530 --- /dev/null +++ b/target/linux/generic/pending-6.1/430-mtd-add-myloader-partition-parser.patch @@ -0,0 +1,229 @@ +From: Florian Fainelli +Subject: Add myloader partition table parser + +[john@phozen.org: shoud be upstreamable] + +lede-commit: d8bf22859b51faa09d22c056fe221a45d2f7a3b8 +Signed-off-by: Florian Fainelli +[adjust for kernel 5.4, add myloader.c to patch] +Signed-off-by: Adrian Schmutzler + +--- 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 ++ * ++ * This file was based on drivers/mtd/redboot.c ++ * Author: Red Hat, Inc. - David Woodhouse ++ * ++ * 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 ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#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 "); ++MODULE_DESCRIPTION("Parsing code for MyLoader partition tables"); ++MODULE_LICENSE("GPL v2"); diff --git a/target/linux/generic/pending-6.1/431-mtd-bcm47xxpart-check-for-bad-blocks-when-calculatin.patch b/target/linux/generic/pending-6.1/431-mtd-bcm47xxpart-check-for-bad-blocks-when-calculatin.patch new file mode 100644 index 0000000000..bcea45d009 --- /dev/null +++ b/target/linux/generic/pending-6.1/431-mtd-bcm47xxpart-check-for-bad-blocks-when-calculatin.patch @@ -0,0 +1,68 @@ +From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= +Subject: [PATCH] mtd: bcm47xxpart: check for bad blocks when calculating offsets + +Signed-off-by: Rafał Miłecki +--- + +--- 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/target/linux/generic/pending-6.1/432-mtd-bcm47xxpart-detect-T_Meter-partition.patch b/target/linux/generic/pending-6.1/432-mtd-bcm47xxpart-detect-T_Meter-partition.patch new file mode 100644 index 0000000000..852654d924 --- /dev/null +++ b/target/linux/generic/pending-6.1/432-mtd-bcm47xxpart-detect-T_Meter-partition.patch @@ -0,0 +1,37 @@ +From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= +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 +--- + 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/target/linux/generic/pending-6.1/435-mtd-add-routerbootpart-parser-config.patch b/target/linux/generic/pending-6.1/435-mtd-add-routerbootpart-parser-config.patch new file mode 100644 index 0000000000..ee949f73c0 --- /dev/null +++ b/target/linux/generic/pending-6.1/435-mtd-add-routerbootpart-parser-config.patch @@ -0,0 +1,38 @@ +From 4437e01fb6bca63fccdba5d6c44888b0935885c2 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Thibaut=20VAR=C3=88NE?= +Date: Tue, 24 Mar 2020 11:45:07 +0100 +Subject: [PATCH] generic: routerboot partition build bits (5.4) +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +This patch adds routerbootpart kernel build bits + +Signed-off-by: Thibaut VARÈNE +--- + drivers/mtd/parsers/Kconfig | 9 +++++++++ + drivers/mtd/parsers/Makefile | 1 + + 2 files changed, 10 insertions(+) + +--- a/drivers/mtd/parsers/Kconfig ++++ b/drivers/mtd/parsers/Kconfig +@@ -226,3 +226,12 @@ config MTD_SERCOMM_PARTS + 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. ++ ++config MTD_ROUTERBOOT_PARTS ++ tristate "RouterBoot flash partition parser" ++ depends on MTD && OF ++ help ++ MikroTik RouterBoot is implemented as a multi segment system on the ++ flash, some of which are fixed and some of which are located at ++ variable offsets. This parser handles both cases via properly ++ formatted DTS. +--- a/drivers/mtd/parsers/Makefile ++++ b/drivers/mtd/parsers/Makefile +@@ -16,3 +16,4 @@ obj-$(CONFIG_MTD_SERCOMM_PARTS) += scpa + obj-$(CONFIG_MTD_SHARPSL_PARTS) += sharpslpart.o + obj-$(CONFIG_MTD_REDBOOT_PARTS) += redboot.o + obj-$(CONFIG_MTD_QCOMSMEM_PARTS) += qcomsmempart.o ++obj-$(CONFIG_MTD_ROUTERBOOT_PARTS) += routerbootpart.o diff --git a/target/linux/generic/pending-6.1/460-mtd-cfi_cmdset_0002-no-erase_suspend.patch b/target/linux/generic/pending-6.1/460-mtd-cfi_cmdset_0002-no-erase_suspend.patch new file mode 100644 index 0000000000..0be74a5977 --- /dev/null +++ b/target/linux/generic/pending-6.1/460-mtd-cfi_cmdset_0002-no-erase_suspend.patch @@ -0,0 +1,25 @@ +From: Felix Fietkau +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 +--- + 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/target/linux/generic/pending-6.1/461-mtd-cfi_cmdset_0002-add-buffer-write-cmd-timeout.patch b/target/linux/generic/pending-6.1/461-mtd-cfi_cmdset_0002-add-buffer-write-cmd-timeout.patch new file mode 100644 index 0000000000..ca56de8271 --- /dev/null +++ b/target/linux/generic/pending-6.1/461-mtd-cfi_cmdset_0002-add-buffer-write-cmd-timeout.patch @@ -0,0 +1,17 @@ +From: George Kashperko +Subject: Issue map read after Write Buffer Load command to ensure chip is ready to receive data. + +Signed-off-by: George Kashperko +--- + 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/target/linux/generic/pending-6.1/465-m25p80-mx-disable-software-protection.patch b/target/linux/generic/pending-6.1/465-m25p80-mx-disable-software-protection.patch new file mode 100644 index 0000000000..f58d5452ab --- /dev/null +++ b/target/linux/generic/pending-6.1/465-m25p80-mx-disable-software-protection.patch @@ -0,0 +1,18 @@ +From: Felix Fietkau +Subject: Disable software protection bits for Macronix flashes. + +Signed-off-by: Felix Fietkau +--- + 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/target/linux/generic/pending-6.1/476-mtd-spi-nor-add-eon-en25q128.patch b/target/linux/generic/pending-6.1/476-mtd-spi-nor-add-eon-en25q128.patch new file mode 100644 index 0000000000..325fca62f3 --- /dev/null +++ b/target/linux/generic/pending-6.1/476-mtd-spi-nor-add-eon-en25q128.patch @@ -0,0 +1,18 @@ +From: Piotr Dymacz +Subject: kernel/mtd: add support for EON EN25Q128 + +Signed-off-by: Piotr Dymacz +--- + 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/target/linux/generic/pending-6.1/479-mtd-spi-nor-add-xtx-xt25f128b.patch b/target/linux/generic/pending-6.1/479-mtd-spi-nor-add-xtx-xt25f128b.patch new file mode 100644 index 0000000000..58a336a4bd --- /dev/null +++ b/target/linux/generic/pending-6.1/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 +X-Patchwork-Id: 1234465 +Date: Thu, 6 Feb 2020 19:19:41 +0200 +From: Daniel Golle +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: , + +Cc: Eitan Cohen , Piotr Dymacz , + Tudor Ambarus +Sender: "linux-mtd" +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 +Signed-off-by: Felix Fietkau +--- + 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 ++ ++#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/target/linux/generic/pending-6.1/481-mtd-spi-nor-add-support-for-Gigadevice-GD25D05.patch b/target/linux/generic/pending-6.1/481-mtd-spi-nor-add-support-for-Gigadevice-GD25D05.patch new file mode 100644 index 0000000000..c32cde559d --- /dev/null +++ b/target/linux/generic/pending-6.1/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 +Date: Mon, 6 Jan 2020 13:07:56 +0100 +Subject: [PATCH] mtd: spi-nor: add support for Gigadevice GD25D05 + +Signed-off-by: Koen Vandeputte +--- + 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/target/linux/generic/pending-6.1/482-mtd-spi-nor-add-gd25q512.patch b/target/linux/generic/pending-6.1/482-mtd-spi-nor-add-gd25q512.patch new file mode 100644 index 0000000000..7c8b686277 --- /dev/null +++ b/target/linux/generic/pending-6.1/482-mtd-spi-nor-add-gd25q512.patch @@ -0,0 +1,21 @@ +From f8943df3beb0d3f9754bb35320c3a378727175a8 Mon Sep 17 00:00:00 2001 +From: OpenWrt community +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(+) + +--- a/drivers/mtd/spi-nor/gigadevice.c ++++ b/drivers/mtd/spi-nor/gigadevice.c +@@ -53,6 +53,9 @@ static const struct flash_info gigadevic + 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/target/linux/generic/pending-6.1/484-mtd-spi-nor-add-esmt-f25l16pa.patch b/target/linux/generic/pending-6.1/484-mtd-spi-nor-add-esmt-f25l16pa.patch new file mode 100644 index 0000000000..ab402e622a --- /dev/null +++ b/target/linux/generic/pending-6.1/484-mtd-spi-nor-add-esmt-f25l16pa.patch @@ -0,0 +1,23 @@ +From 87363cc0e522de3294ea6ae10fb468d2a8d6fb2f Mon Sep 17 00:00:00 2001 +From: OpenWrt community +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(+) + +--- 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/target/linux/generic/pending-6.1/485-mtd-spi-nor-add-xmc-xm25qh128c.patch b/target/linux/generic/pending-6.1/485-mtd-spi-nor-add-xmc-xm25qh128c.patch new file mode 100644 index 0000000000..68e373ea23 --- /dev/null +++ b/target/linux/generic/pending-6.1/485-mtd-spi-nor-add-xmc-xm25qh128c.patch @@ -0,0 +1,24 @@ +From f6b33d850f7f12555df2fa0e3349b33427bf5890 Mon Sep 17 00:00:00 2001 +From: OpenWrt community +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(+) + +--- 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/target/linux/generic/pending-6.1/486-01-mtd-spinand-add-support-for-ESMT-F50x1G41LB.patch b/target/linux/generic/pending-6.1/486-01-mtd-spinand-add-support-for-ESMT-F50x1G41LB.patch new file mode 100644 index 0000000000..c170fedc67 --- /dev/null +++ b/target/linux/generic/pending-6.1/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 +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 +--- + 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 ++ */ ++ ++#include ++#include ++#include ++ ++/* 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/target/linux/generic/pending-6.1/487-mtd-spinand-Add-support-for-Etron-EM73D044VCx.patch b/target/linux/generic/pending-6.1/487-mtd-spinand-Add-support-for-Etron-EM73D044VCx.patch new file mode 100644 index 0000000000..2f604cfa98 --- /dev/null +++ b/target/linux/generic/pending-6.1/487-mtd-spinand-Add-support-for-Etron-EM73D044VCx.patch @@ -0,0 +1,168 @@ +From f32085fc0b87049491b07e198d924d738a1a2834 Mon Sep 17 00:00:00 2001 +From: Daniel Danzberger +Date: Wed, 3 Aug 2022 17:31:03 +0200 +Subject: [PATCH] mtd: spinand: Add support for Etron EM73D044VCx + +Airoha is a new ARM platform based on Cortex-A53 which has recently been +merged into linux-next. + +Due to BootROM limitations on this platform, the Cortex-A53 can't run in +Aarch64 mode and code must be compiled for 32-Bit ARM. + +This support is based mostly on those linux-next commits backported +for kernel 5.15. + +Patches: +1 - platform support = linux-next +2 - clock driver = linux-next +3 - gpio driver = linux-next +4 - linux,usable-memory-range dts support = linux-next +5 - mtd spinand driver +6 - spi driver +7 - pci driver (kconfig only, uses mediatek PCI) = linux-next + +Still missing: +- Ethernet driver +- Sysupgrade support + +A.t.m there exists one subtarget EN7523 with only one evaluation +board. + +The initramfs can be run with the following commands from u-boot: +- +u-boot> setenv bootfile \ + openwrt-airoha-airoha_en7523-evb-initramfs-kernel.bin +u-boot> tftpboot +u-boot> bootm 0x81800000 +- + +Submitted-by: Daniel Danzberger + +--- 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 esmt.o gigadevice.o macronix.o micron.o paragon.o toshiba.o winbond.o xtx.o ++spinand-objs := core.o esmt.o etron.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 +@@ -898,6 +898,7 @@ static const struct nand_ops spinand_ops + static const struct spinand_manufacturer *spinand_manufacturers[] = { + &esmt_c8_spinand_manufacturer, + &gigadevice_spinand_manufacturer, ++ &etron_spinand_manufacturer, + ¯onix_spinand_manufacturer, + µn_spinand_manufacturer, + ¶gon_spinand_manufacturer, +--- /dev/null ++++ b/drivers/mtd/nand/spi/etron.c +@@ -0,0 +1,98 @@ ++// SPDX-License-Identifier: GPL-2.0 ++ ++#include ++#include ++#include ++ ++#define SPINAND_MFR_ETRON 0xd5 ++ ++ ++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 etron_ooblayout_ecc(struct mtd_info *mtd, int section, ++ struct mtd_oob_region *oobregion) ++{ ++ if (section) ++ return -ERANGE; ++ ++ oobregion->offset = 72; ++ oobregion->length = 56; ++ ++ return 0; ++} ++ ++static int etron_ooblayout_free(struct mtd_info *mtd, int section, ++ struct mtd_oob_region *oobregion) ++{ ++ if (section) ++ return -ERANGE; ++ ++ oobregion->offset = 1; ++ oobregion->length = 71; ++ ++ return 0; ++} ++ ++static int etron_ecc_get_status(struct spinand_device *spinand, u8 status) ++{ ++ switch (status & STATUS_ECC_MASK) { ++ case STATUS_ECC_NO_BITFLIPS: ++ return 0; ++ ++ case STATUS_ECC_HAS_BITFLIPS: ++ /* Between 1-7 bitflips were corrected */ ++ return 7; ++ ++ case STATUS_ECC_MASK: ++ /* Maximum bitflips were corrected */ ++ return 8; ++ ++ case STATUS_ECC_UNCOR_ERROR: ++ return -EBADMSG; ++ } ++ ++ return -EINVAL; ++} ++ ++static const struct mtd_ooblayout_ops etron_ooblayout = { ++ .ecc = etron_ooblayout_ecc, ++ .free = etron_ooblayout_free, ++}; ++ ++static const struct spinand_info etron_spinand_table[] = { ++ SPINAND_INFO("EM73D044VCx", ++ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_ADDR, 0x1f), ++ // bpc, pagesize, oobsize, pagesperblock, bperlun, maxbadplun, ppl, lpt, #t ++ 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(&etron_ooblayout, etron_ecc_get_status)), ++}; ++ ++static const struct spinand_manufacturer_ops etron_spinand_manuf_ops = { ++}; ++ ++const struct spinand_manufacturer etron_spinand_manufacturer = { ++ .id = SPINAND_MFR_ETRON, ++ .name = "Etron", ++ .chips = etron_spinand_table, ++ .nchips = ARRAY_SIZE(etron_spinand_table), ++ .ops = &etron_spinand_manuf_ops, ++}; +--- a/include/linux/mtd/spinand.h ++++ b/include/linux/mtd/spinand.h +@@ -261,6 +261,7 @@ struct spinand_manufacturer { + + /* SPI NAND manufacturers */ + extern const struct spinand_manufacturer esmt_c8_spinand_manufacturer; ++extern const struct spinand_manufacturer etron_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/target/linux/generic/pending-6.1/490-ubi-auto-attach-mtd-device-named-ubi-or-data-on-boot.patch b/target/linux/generic/pending-6.1/490-ubi-auto-attach-mtd-device-named-ubi-or-data-on-boot.patch new file mode 100644 index 0000000000..b120548d2e --- /dev/null +++ b/target/linux/generic/pending-6.1/490-ubi-auto-attach-mtd-device-named-ubi-or-data-on-boot.patch @@ -0,0 +1,97 @@ +From: Daniel Golle +Subject: ubi: auto-attach mtd device named "ubi" or "data" on boot + +Signed-off-by: Daniel Golle +--- + 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/target/linux/generic/pending-6.1/491-ubi-auto-create-ubiblock-device-for-rootfs.patch b/target/linux/generic/pending-6.1/491-ubi-auto-create-ubiblock-device-for-rootfs.patch new file mode 100644 index 0000000000..ae53770c11 --- /dev/null +++ b/target/linux/generic/pending-6.1/491-ubi-auto-create-ubiblock-device-for-rootfs.patch @@ -0,0 +1,69 @@ +From: Daniel Golle +Subject: ubi: auto-create ubiblock device for rootfs + +Signed-off-by: Daniel Golle +--- + 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/target/linux/generic/pending-6.1/492-try-auto-mounting-ubi0-rootfs-in-init-do_mounts.c.patch b/target/linux/generic/pending-6.1/492-try-auto-mounting-ubi0-rootfs-in-init-do_mounts.c.patch new file mode 100644 index 0000000000..cf41c8cad3 --- /dev/null +++ b/target/linux/generic/pending-6.1/492-try-auto-mounting-ubi0-rootfs-in-init-do_mounts.c.patch @@ -0,0 +1,53 @@ +From: Daniel Golle +Subject: try auto-mounting ubi0:rootfs in init/do_mounts.c + +Signed-off-by: Daniel Golle +--- + 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/target/linux/generic/pending-6.1/493-ubi-set-ROOT_DEV-to-ubiblock-rootfs-if-unset.patch b/target/linux/generic/pending-6.1/493-ubi-set-ROOT_DEV-to-ubiblock-rootfs-if-unset.patch new file mode 100644 index 0000000000..266a6331c2 --- /dev/null +++ b/target/linux/generic/pending-6.1/493-ubi-set-ROOT_DEV-to-ubiblock-rootfs-if-unset.patch @@ -0,0 +1,34 @@ +From: Daniel Golle +Subject: ubi: set ROOT_DEV to ubiblock "rootfs" if unset + +Signed-off-by: Daniel Golle +--- + 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 + #include + #include ++#include + + #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/target/linux/generic/pending-6.1/494-mtd-ubi-add-EOF-marker-support.patch b/target/linux/generic/pending-6.1/494-mtd-ubi-add-EOF-marker-support.patch new file mode 100644 index 0000000000..413431755f --- /dev/null +++ b/target/linux/generic/pending-6.1/494-mtd-ubi-add-EOF-marker-support.patch @@ -0,0 +1,60 @@ +From: Gabor Juhos +Subject: mtd: add EOF marker support to the UBI layer + +Signed-off-by: Gabor Juhos +--- + 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/target/linux/generic/pending-6.1/496-dt-bindings-add-bindings-for-mtd-concat-devices.patch b/target/linux/generic/pending-6.1/496-dt-bindings-add-bindings-for-mtd-concat-devices.patch new file mode 100644 index 0000000000..01f3b9ec2d --- /dev/null +++ b/target/linux/generic/pending-6.1/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 +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 +--- + .../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/target/linux/generic/pending-6.1/497-mtd-mtdconcat-add-dt-driver-for-concat-devices.patch b/target/linux/generic/pending-6.1/497-mtd-mtdconcat-add-dt-driver-for-concat-devices.patch new file mode 100644 index 0000000000..e0cbc4508b --- /dev/null +++ b/target/linux/generic/pending-6.1/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 +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 +--- + 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 ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++/* ++ * 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 = of_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 "); ++MODULE_DESCRIPTION("Virtual concat MTD device driver"); diff --git a/target/linux/generic/pending-6.1/498-mtd-spi-nor-locking-support-for-MX25L6405D.patch b/target/linux/generic/pending-6.1/498-mtd-spi-nor-locking-support-for-MX25L6405D.patch new file mode 100644 index 0000000000..81de764876 --- /dev/null +++ b/target/linux/generic/pending-6.1/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 +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 +Signed-off-by: Nick Hainke +--- + 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/target/linux/generic/pending-6.1/499-mtd-spi-nor-disable-16-bit-sr-for-macronix.patch b/target/linux/generic/pending-6.1/499-mtd-spi-nor-disable-16-bit-sr-for-macronix.patch new file mode 100644 index 0000000000..ec14f6341c --- /dev/null +++ b/target/linux/generic/pending-6.1/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 +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 +Signed-off-by: Nick Hainke +--- + 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/target/linux/generic/pending-6.1/500-fs_cdrom_dependencies.patch b/target/linux/generic/pending-6.1/500-fs_cdrom_dependencies.patch new file mode 100644 index 0000000000..2053c0fbe2 --- /dev/null +++ b/target/linux/generic/pending-6.1/500-fs_cdrom_dependencies.patch @@ -0,0 +1,52 @@ +From af7b91bcecce0eae24e90acd35d96ecee73e1407 Mon Sep 17 00:00:00 2001 +From: OpenWrt community +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(+) + +--- 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 +--- 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 +--- 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 +--- 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/target/linux/generic/pending-6.1/530-jffs2_make_lzma_available.patch b/target/linux/generic/pending-6.1/530-jffs2_make_lzma_available.patch new file mode 100644 index 0000000000..ac784d0b0b --- /dev/null +++ b/target/linux/generic/pending-6.1/530-jffs2_make_lzma_available.patch @@ -0,0 +1,4581 @@ +From: Alexandros C. Couloumbis +Subject: fs: add jffs2/lzma support (not activated by default yet) + +lede-commit: c2c88d315fa0e881f8b19da07b62859b915b11b2 +Signed-off-by: Alexandros C. Couloumbis +--- + 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 ++#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 ++ #include ++ #include ++ #include ++ #include ++ #define LZMA_MALLOC vmalloc ++ #define LZMA_FREE vfree ++ #define PRINT_ERROR(msg) printk(KERN_WARNING #msg) ++ #define INIT __init ++ #define STATIC static ++#else ++ #include ++ #include ++ #include ++ #include ++ #include ++ #include ++ #include ++ #include ++ #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,98 @@ ++/* 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) ++ ++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); ++ ++/* ++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); ++ ++#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,130 @@ ++/* 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; ++ ++ ++/* ---------- 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; } ++ ++/* 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 */ ++ ++/* ---------- 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,60 @@ ++/* 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); ++ ++/* ---------- 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_MemEncode(CLzmaEncHandle p, Byte *dest, SizeT *destLen, const Byte *src, SizeT srcLen, ++ 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 ++ ++#ifdef _WIN32 ++#include ++#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,522 @@ ++/* LzFind.c -- Match finder for LZ algorithms ++2009-04-22 : Igor Pavlov : Public domain */ ++ ++#include ++ ++#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 ++ ++#if 0 ++#define DIRECT_INPUT p->directInput ++#else ++#define DIRECT_INPUT 1 ++#endif ++ ++static void LzInWindow_Free(CMatchFinder *p, ISzAlloc *alloc) ++{ ++ if (!DIRECT_INPUT) ++ { ++ 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 (DIRECT_INPUT) ++ { ++ 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); ++} ++ ++static Byte *MatchFinder_GetPointerToCurrentPos(CMatchFinder *p) { return p->buffer; } ++static Byte MatchFinder_GetIndexByte(CMatchFinder *p, Int32 index) { return p->buffer[index]; } ++ ++static UInt32 MatchFinder_GetNumAvailableBytes(CMatchFinder *p) { return p->streamPos - p->pos; } ++ ++static 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 (DIRECT_INPUT) ++ { ++ 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; ++ } ++} ++ ++static 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; ++} ++ ++static int MatchFinder_NeedMove(CMatchFinder *p) ++{ ++ if (DIRECT_INPUT) ++ return 0; ++ /* if (p->streamEndWasReached) return 0; */ ++ return ((size_t)(p->bufferBase + p->blockSize - p->buffer) <= p->keepSizeAfter); ++} ++ ++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; ++} ++ ++static 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; ++} ++ ++static 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 * 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); ++ ++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; }} \ ++ 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)); MatchFinder_MovePos(p); ++ ++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 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); ++} ++ ++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; ++ 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,925 @@ ++/* LzmaDec.c -- LZMA Decoder ++2009-09-20 : Igor Pavlov : Public domain */ ++ ++#include "LzmaDec.h" ++ ++#include ++ ++#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; ++} ++ ++static 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; ++} ++ ++static 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; ++} ++ ++static 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; ++} ++ ++static void LzmaDec_FreeProbs(CLzmaDec *p, ISzAlloc *alloc) ++{ ++ alloc->Free(alloc, p->probs); ++ p->probs = 0; ++} ++ ++static 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; ++} ++ ++static 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 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,2123 @@ ++/* LzmaEnc.c -- LZMA Encoder ++2009-11-24 : Igor Pavlov : Public domain */ ++ ++#include ++ ++/* #define SHOW_STAT */ ++/* #define SHOW_STAT2 */ ++ ++#if defined(SHOW_STAT) || defined(SHOW_STAT2) ++#include ++#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; ++} ++ ++static 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 ++} ++ ++static UInt32 __maybe_unused 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); } ++ ++static 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) ++ ++static 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; ++ ++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); ++} ++ ++static 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; ++} ++ ++static 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; ++} ++ ++static 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; ++} ++ ++static 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; ++} ++ ++static 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; ++} ++ ++static 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 void LzmaEnc_SetInputBuf(CLzmaEnc *p, const Byte *src, SizeT srcLen) ++{ ++ p->matchFinderBase.directInput = 1; ++ p->matchFinderBase.bufferBase = (Byte *)src; ++ p->matchFinderBase.directInputRem = srcLen; ++} ++ ++static 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); ++} ++ ++static 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; ++} ++ ++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_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; ++} +--- /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/target/linux/generic/pending-6.1/532-jffs2_eofdetect.patch b/target/linux/generic/pending-6.1/532-jffs2_eofdetect.patch new file mode 100644 index 0000000000..744fbd0e21 --- /dev/null +++ b/target/linux/generic/pending-6.1/532-jffs2_eofdetect.patch @@ -0,0 +1,65 @@ +From: Felix Fietkau +Subject: fs: jffs2: EOF marker + +Signed-off-by: Felix Fietkau +--- + 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/target/linux/generic/pending-6.1/600-netfilter_conntrack_flush.patch b/target/linux/generic/pending-6.1/600-netfilter_conntrack_flush.patch new file mode 100644 index 0000000000..a88e3d7d9a --- /dev/null +++ b/target/linux/generic/pending-6.1/600-netfilter_conntrack_flush.patch @@ -0,0 +1,88 @@ +From: Felix Fietkau +Subject: netfilter: add support for flushing conntrack via /proc + +lede-commit 8193bbe59a74d34d6a26d4a8cb857b1952905314 +Signed-off-by: Felix Fietkau +--- + 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 + #include + #include ++#include + #include + #ifdef CONFIG_SYSCTL + #include +@@ -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/target/linux/generic/pending-6.1/610-netfilter_match_bypass_default_checks.patch b/target/linux/generic/pending-6.1/610-netfilter_match_bypass_default_checks.patch new file mode 100644 index 0000000000..457703121c --- /dev/null +++ b/target/linux/generic/pending-6.1/610-netfilter_match_bypass_default_checks.patch @@ -0,0 +1,110 @@ +From: Felix Fietkau +Subject: kernel: add a new version of my netfilter speedup patches for linux 2.6.39 and 3.0 + +Signed-off-by: Felix Fietkau +--- + 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/target/linux/generic/pending-6.1/611-netfilter_match_bypass_default_table.patch b/target/linux/generic/pending-6.1/611-netfilter_match_bypass_default_table.patch new file mode 100644 index 0000000000..baf738a8d2 --- /dev/null +++ b/target/linux/generic/pending-6.1/611-netfilter_match_bypass_default_table.patch @@ -0,0 +1,106 @@ +From: Felix Fietkau +Subject: netfilter: match bypass default table + +Signed-off-by: Felix Fietkau +--- + 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/target/linux/generic/pending-6.1/612-netfilter_match_reduce_memory_access.patch b/target/linux/generic/pending-6.1/612-netfilter_match_reduce_memory_access.patch new file mode 100644 index 0000000000..79da6778b6 --- /dev/null +++ b/target/linux/generic/pending-6.1/612-netfilter_match_reduce_memory_access.patch @@ -0,0 +1,22 @@ +From: Felix Fietkau +Subject: netfilter: reduce match memory access + +Signed-off-by: Felix Fietkau +--- + 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/target/linux/generic/pending-6.1/613-netfilter_optional_tcp_window_check.patch b/target/linux/generic/pending-6.1/613-netfilter_optional_tcp_window_check.patch new file mode 100644 index 0000000000..93c52e8842 --- /dev/null +++ b/target/linux/generic/pending-6.1/613-netfilter_optional_tcp_window_check.patch @@ -0,0 +1,83 @@ +From: Felix Fietkau +Subject: netfilter: optional tcp window check + +Signed-off-by: Felix Fietkau +Signed-off-by: Christian 'Ansuel' Marangi +--- + 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. + */ +@@ -1191,7 +1194,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 +@@ -1507,6 +1510,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/target/linux/generic/pending-6.1/620-net_sched-codel-do-not-defer-queue-length-update.patch b/target/linux/generic/pending-6.1/620-net_sched-codel-do-not-defer-queue-length-update.patch new file mode 100644 index 0000000000..4b4825ae3b --- /dev/null +++ b/target/linux/generic/pending-6.1/620-net_sched-codel-do-not-defer-queue-length-update.patch @@ -0,0 +1,86 @@ +From: Konstantin Khlebnikov +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 +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/target/linux/generic/pending-6.1/630-packet_socket_type.patch b/target/linux/generic/pending-6.1/630-packet_socket_type.patch new file mode 100644 index 0000000000..c61935ffb0 --- /dev/null +++ b/target/linux/generic/pending-6.1/630-packet_socket_type.patch @@ -0,0 +1,138 @@ +From: Felix Fietkau +Subject: net: add an optimization for dealing with raw sockets + +lede-commit: 4898039703d7315f0f3431c860123338ec3be0f6 +Signed-off-by: Felix Fietkau +--- + 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/target/linux/generic/pending-6.1/655-increase_skb_pad.patch b/target/linux/generic/pending-6.1/655-increase_skb_pad.patch new file mode 100644 index 0000000000..7fc1c588b3 --- /dev/null +++ b/target/linux/generic/pending-6.1/655-increase_skb_pad.patch @@ -0,0 +1,20 @@ +From: Felix Fietkau +Subject: kernel: add a few patches for avoiding unnecessary skb reallocations - significantly improves ethernet<->wireless performance + +lede-commit: 6f89cffc9add6939d44a6b54cf9a5e77849aa7fd +Signed-off-by: Felix Fietkau +--- + include/linux/skbuff.h | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/include/linux/skbuff.h ++++ b/include/linux/skbuff.h +@@ -2818,7 +2818,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/target/linux/generic/pending-6.1/666-Add-support-for-MAP-E-FMRs-mesh-mode.patch b/target/linux/generic/pending-6.1/666-Add-support-for-MAP-E-FMRs-mesh-mode.patch new file mode 100644 index 0000000000..09efa1e72f --- /dev/null +++ b/target/linux/generic/pending-6.1/666-Add-support-for-MAP-E-FMRs-mesh-mode.patch @@ -0,0 +1,511 @@ +From: Steven Barth +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 +--- + 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 : 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/target/linux/generic/pending-6.1/670-ipv6-allow-rejecting-with-source-address-failed-policy.patch b/target/linux/generic/pending-6.1/670-ipv6-allow-rejecting-with-source-address-failed-policy.patch new file mode 100644 index 0000000000..ac4396209e --- /dev/null +++ b/target/linux/generic/pending-6.1/670-ipv6-allow-rejecting-with-source-address-failed-policy.patch @@ -0,0 +1,263 @@ +From: Jonas Gorski +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 +--- + 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 +@@ -2770,6 +2770,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/target/linux/generic/pending-6.1/671-net-provide-defines-for-_POLICY_FAILED-until-all-cod.patch b/target/linux/generic/pending-6.1/671-net-provide-defines-for-_POLICY_FAILED-until-all-cod.patch new file mode 100644 index 0000000000..bea43b2bad --- /dev/null +++ b/target/linux/generic/pending-6.1/671-net-provide-defines-for-_POLICY_FAILED-until-all-cod.patch @@ -0,0 +1,50 @@ +From: Jonas Gorski +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 +--- + 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/target/linux/generic/pending-6.1/680-NET-skip-GRO-for-foreign-MAC-addresses.patch b/target/linux/generic/pending-6.1/680-NET-skip-GRO-for-foreign-MAC-addresses.patch new file mode 100644 index 0000000000..41190fcfd4 --- /dev/null +++ b/target/linux/generic/pending-6.1/680-NET-skip-GRO-for-foreign-MAC-addresses.patch @@ -0,0 +1,149 @@ +From: Felix Fietkau +Subject: net: replace GRO optimization patch with a new one that supports VLANs/bridges with different MAC addresses + +Signed-off-by: Felix Fietkau +--- + 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 +@@ -2089,6 +2089,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 +@@ -891,6 +891,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 +@@ -6063,6 +6063,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; + +@@ -8077,6 +8080,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, +@@ -8128,6 +8173,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); +@@ -8224,6 +8270,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); + +@@ -9043,6 +9090,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/target/linux/generic/pending-6.1/682-of_net-add-mac-address-increment-support.patch b/target/linux/generic/pending-6.1/682-of_net-add-mac-address-increment-support.patch new file mode 100644 index 0000000000..fe6faddc7d --- /dev/null +++ b/target/linux/generic/pending-6.1/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 +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 +--- + 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/target/linux/generic/pending-6.1/683-of_net-add-mac-address-to-of-tree.patch b/target/linux/generic/pending-6.1/683-of_net-add-mac-address-to-of-tree.patch new file mode 100644 index 0000000000..f7ef06a14a --- /dev/null +++ b/target/linux/generic/pending-6.1/683-of_net-add-mac-address-to-of-tree.patch @@ -0,0 +1,55 @@ +From 8585756342caa6d27008d1ad0c18023e4211a40a Mon Sep 17 00:00:00 2001 +From: OpenWrt community +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(+) + +--- a/net/core/of_net.c ++++ b/net/core/of_net.c +@@ -95,6 +95,27 @@ static int of_get_mac_addr_nvmem(struct + 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 @@ found: + addr[5] = (mac_val >> 0) & 0xff; + } + ++ of_add_mac_address(np, addr); + return ret; + } + EXPORT_SYMBOL(of_get_mac_address); diff --git a/target/linux/generic/pending-6.1/684-of_net-do-mac-address-increment-only-once.patch b/target/linux/generic/pending-6.1/684-of_net-do-mac-address-increment-only-once.patch new file mode 100644 index 0000000000..44d88e31a2 --- /dev/null +++ b/target/linux/generic/pending-6.1/684-of_net-do-mac-address-increment-only-once.patch @@ -0,0 +1,31 @@ +From dd07dd394d8bfdb5d527fab18ca54f20815ec4e4 Mon Sep 17 00:00:00 2001 +From: Will Moss +Date: Wed, 3 Aug 2022 13:48:55 +0000 +Subject: [PATCH] of_net: do mac-address-increment only once + +Remove mac-address-increment and mac-address-increment-byte +DT property after incrementing process to make sure MAC address +would not get incremented more if this function is stared again. +It could happen if device initialization is deferred after +unsuccessful attempt. + +Signed-off-by: Will Moss +--- + drivers/of/of_net.c | 6 ++++++ + 1 file changed, 6 insertions(+) + +--- a/net/core/of_net.c ++++ b/net/core/of_net.c +@@ -194,6 +194,12 @@ found: + addr[3] = (mac_val >> 16) & 0xff; + addr[4] = (mac_val >> 8) & 0xff; + addr[5] = (mac_val >> 0) & 0xff; ++ ++ /* Remove mac-address-increment and mac-address-increment-byte ++ * DT property to make sure MAC address would not get incremented ++ * more if this function is stared again. */ ++ of_remove_property(np, of_find_property(np, "mac-address-increment", NULL)); ++ of_remove_property(np, of_find_property(np, "mac-address-increment-byte", NULL)); + } + + of_add_mac_address(np, addr); diff --git a/target/linux/generic/pending-6.1/700-netfilter-nft_flow_offload-handle-netdevice-events-f.patch b/target/linux/generic/pending-6.1/700-netfilter-nft_flow_offload-handle-netdevice-events-f.patch new file mode 100644 index 0000000000..488c6a8d92 --- /dev/null +++ b/target/linux/generic/pending-6.1/700-netfilter-nft_flow_offload-handle-netdevice-events-f.patch @@ -0,0 +1,106 @@ +From: Pablo Neira Ayuso +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 +--- + +--- a/net/netfilter/nf_flow_table_core.c ++++ b/net/netfilter/nf_flow_table_core.c +@@ -613,13 +613,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/target/linux/generic/pending-6.1/702-net-ethernet-mtk_eth_soc-enable-threaded-NAPI.patch b/target/linux/generic/pending-6.1/702-net-ethernet-mtk_eth_soc-enable-threaded-NAPI.patch new file mode 100644 index 0000000000..670c2b04a8 --- /dev/null +++ b/target/linux/generic/pending-6.1/702-net-ethernet-mtk_eth_soc-enable-threaded-NAPI.patch @@ -0,0 +1,41 @@ +From: Felix Fietkau +Date: Mon, 21 Mar 2022 20:39:59 +0100 +Subject: [PATCH] net: ethernet: mtk_eth_soc: enable threaded NAPI + +This can improve performance under load by ensuring that NAPI processing is +not pinned on CPU 0. + +Signed-off-by: Felix Fietkau +--- + +--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c ++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c +@@ -2802,8 +2802,8 @@ static irqreturn_t mtk_handle_irq_rx(int + + eth->rx_events++; + if (likely(napi_schedule_prep(ð->rx_napi))) { +- __napi_schedule(ð->rx_napi); + mtk_rx_irq_disable(eth, eth->soc->txrx.rx_irq_done_mask); ++ __napi_schedule(ð->rx_napi); + } + + return IRQ_HANDLED; +@@ -2815,8 +2815,8 @@ static irqreturn_t mtk_handle_irq_tx(int + + eth->tx_events++; + if (likely(napi_schedule_prep(ð->tx_napi))) { +- __napi_schedule(ð->tx_napi); + mtk_tx_irq_disable(eth, MTK_TX_DONE_INT); ++ __napi_schedule(ð->tx_napi); + } + + return IRQ_HANDLED; +@@ -4114,6 +4114,8 @@ static int mtk_probe(struct platform_dev + * for NAPI to work + */ + init_dummy_netdev(ð->dummy_dev); ++ eth->dummy_dev.threaded = 1; ++ strcpy(eth->dummy_dev.name, "mtk_eth"); + netif_napi_add(ð->dummy_dev, ð->tx_napi, mtk_napi_tx, + NAPI_POLL_WEIGHT); + netif_napi_add(ð->dummy_dev, ð->rx_napi, mtk_napi_rx, diff --git a/target/linux/generic/pending-6.1/703-phy-add-detach-callback-to-struct-phy_driver.patch b/target/linux/generic/pending-6.1/703-phy-add-detach-callback-to-struct-phy_driver.patch new file mode 100644 index 0000000000..976239c128 --- /dev/null +++ b/target/linux/generic/pending-6.1/703-phy-add-detach-callback-to-struct-phy_driver.patch @@ -0,0 +1,38 @@ +From: Gabor Juhos +Subject: generic: add detach callback to struct phy_driver + +lede-commit: fe61fc2d7d0b3fb348b502f68f98243b3ddf5867 + +Signed-off-by: Gabor Juhos +--- + 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 +@@ -1746,6 +1746,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 +@@ -823,6 +823,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/target/linux/generic/pending-6.1/705-net-dsa-tag_mtk-add-padding-for-tx-packets.patch b/target/linux/generic/pending-6.1/705-net-dsa-tag_mtk-add-padding-for-tx-packets.patch new file mode 100644 index 0000000000..e27ac3595f --- /dev/null +++ b/target/linux/generic/pending-6.1/705-net-dsa-tag_mtk-add-padding-for-tx-packets.patch @@ -0,0 +1,29 @@ +From: Felix Fietkau +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 +--- + +--- 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/target/linux/generic/pending-6.1/710-bridge-add-knob-for-filtering-rx-tx-BPDU-pack.patch b/target/linux/generic/pending-6.1/710-bridge-add-knob-for-filtering-rx-tx-BPDU-pack.patch new file mode 100644 index 0000000000..2a2ca7f1fa --- /dev/null +++ b/target/linux/generic/pending-6.1/710-bridge-add-knob-for-filtering-rx-tx-BPDU-pack.patch @@ -0,0 +1,174 @@ +From: Felix Fietkau +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 +--- + +--- 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 + + #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/target/linux/generic/pending-6.1/721-net-phy-realtek-rtl8221-allow-to-configure-SERDES-mo.patch b/target/linux/generic/pending-6.1/721-net-phy-realtek-rtl8221-allow-to-configure-SERDES-mo.patch new file mode 100644 index 0000000000..703a0b8b72 --- /dev/null +++ b/target/linux/generic/pending-6.1/721-net-phy-realtek-rtl8221-allow-to-configure-SERDES-mo.patch @@ -0,0 +1,101 @@ +From ace6abaa0f9203083fe4c0a6a74da2d96410b625 Mon Sep 17 00:00:00 2001 +From: Alexander Couzens +Date: Sat, 13 Aug 2022 12:49:33 +0200 +Subject: [PATCH 01/10] net: phy: realtek: rtl8221: allow to configure SERDES + mode + +The rtl8221 supports multiple SERDES modes: +- SGMII +- 2500base-x +- HiSGMII + +Further it supports rate adaption on SERDES links to allow +slow ethernet speeds (10/100/1000mbit) to work on 2500base-x/HiSGMII +links without reducing the SERDES speed. + +When operating without rate adapters the SERDES link will follow the +ethernet speed. + +Signed-off-by: Alexander Couzens +--- + drivers/net/phy/realtek.c | 48 +++++++++++++++++++++++++++++++++++++++ + 1 file changed, 48 insertions(+) + +--- a/drivers/net/phy/realtek.c ++++ b/drivers/net/phy/realtek.c +@@ -53,6 +53,15 @@ + RTL8201F_ISR_LINK) + #define RTL8201F_IER 0x13 + ++#define RTL8221B_MMD_SERDES_CTRL MDIO_MMD_VEND1 ++#define RTL8221B_MMD_PHY_CTRL MDIO_MMD_VEND2 ++#define RTL8221B_SERDES_OPTION 0x697a ++#define RTL8221B_SERDES_OPTION_MODE_MASK GENMASK(5, 0) ++#define RTL8221B_SERDES_OPTION_MODE_2500BASEX_SGMII 0 ++#define RTL8221B_SERDES_OPTION_MODE_HISGMII_SGMII 1 ++#define RTL8221B_SERDES_OPTION_MODE_2500BASEX 2 ++#define RTL8221B_SERDES_OPTION_MODE_HISGMII 3 ++ + #define RTL8366RB_POWER_SAVE 0x15 + #define RTL8366RB_POWER_SAVE_ON BIT(12) + +@@ -841,6 +850,43 @@ static irqreturn_t rtl9000a_handle_inter + return IRQ_HANDLED; + } + ++static int rtl8221b_config_init(struct phy_device *phydev) ++{ ++ u16 option_mode; ++ ++ switch (phydev->interface) { ++ case PHY_INTERFACE_MODE_SGMII: ++ case PHY_INTERFACE_MODE_2500BASEX: ++ option_mode = RTL8221B_SERDES_OPTION_MODE_2500BASEX_SGMII; ++ break; ++ default: ++ return 0; ++ } ++ ++ phy_write_mmd(phydev, RTL8221B_MMD_SERDES_CTRL, ++ 0x75f3, 0); ++ ++ phy_modify_mmd_changed(phydev, RTL8221B_MMD_SERDES_CTRL, ++ RTL8221B_SERDES_OPTION, ++ RTL8221B_SERDES_OPTION_MODE_MASK, option_mode); ++ switch (option_mode) { ++ case RTL8221B_SERDES_OPTION_MODE_2500BASEX_SGMII: ++ case RTL8221B_SERDES_OPTION_MODE_2500BASEX: ++ phy_write_mmd(phydev, RTL8221B_MMD_SERDES_CTRL, 0x6a04, 0x0503); ++ phy_write_mmd(phydev, RTL8221B_MMD_SERDES_CTRL, 0x6f10, 0xd455); ++ phy_write_mmd(phydev, RTL8221B_MMD_SERDES_CTRL, 0x6f11, 0x8020); ++ break; ++ case RTL8221B_SERDES_OPTION_MODE_HISGMII_SGMII: ++ case RTL8221B_SERDES_OPTION_MODE_HISGMII: ++ phy_write_mmd(phydev, RTL8221B_MMD_SERDES_CTRL, 0x6a04, 0x0503); ++ phy_write_mmd(phydev, RTL8221B_MMD_SERDES_CTRL, 0x6f10, 0xd433); ++ phy_write_mmd(phydev, RTL8221B_MMD_SERDES_CTRL, 0x6f11, 0x8020); ++ break; ++ } ++ ++ return 0; ++} ++ + static struct phy_driver realtek_drvs[] = { + { + PHY_ID_MATCH_EXACT(0x00008201), +@@ -981,6 +1027,7 @@ static struct phy_driver realtek_drvs[] + PHY_ID_MATCH_EXACT(0x001cc849), + .name = "RTL8221B-VB-CG 2.5Gbps PHY", + .get_features = rtl822x_get_features, ++ .config_init = rtl8221b_config_init, + .config_aneg = rtl822x_config_aneg, + .read_status = rtl822x_read_status, + .suspend = genphy_suspend, +@@ -992,6 +1039,7 @@ static struct phy_driver realtek_drvs[] + .name = "RTL8221B-VM-CG 2.5Gbps PHY", + .get_features = rtl822x_get_features, + .config_aneg = rtl822x_config_aneg, ++ .config_init = rtl8221b_config_init, + .read_status = rtl822x_read_status, + .suspend = genphy_suspend, + .resume = rtlgen_resume, diff --git a/target/linux/generic/pending-6.1/723-net-mt7531-ensure-all-MACs-are-powered-down-before-r.patch b/target/linux/generic/pending-6.1/723-net-mt7531-ensure-all-MACs-are-powered-down-before-r.patch new file mode 100644 index 0000000000..cc12d72110 --- /dev/null +++ b/target/linux/generic/pending-6.1/723-net-mt7531-ensure-all-MACs-are-powered-down-before-r.patch @@ -0,0 +1,28 @@ +From 3fb8841513c4ec3a2e5d366df86230c45f239a57 Mon Sep 17 00:00:00 2001 +From: Alexander Couzens +Date: Sat, 13 Aug 2022 13:08:22 +0200 +Subject: [PATCH 03/10] net: mt7531: ensure all MACs are powered down before + reset + +The datasheet [1] explicit describes it as requirement for a reset. + +[1] MT7531 Reference Manual for Development Board rev 1.0, page 735 + +Signed-off-by: Alexander Couzens +--- + drivers/net/dsa/mt7530.c | 4 ++++ + 1 file changed, 4 insertions(+) + +--- a/drivers/net/dsa/mt7530.c ++++ b/drivers/net/dsa/mt7530.c +@@ -2324,6 +2324,10 @@ mt7531_setup(struct dsa_switch *ds) + return -ENODEV; + } + ++ /* all MACs must be forced link-down before sw reset */ ++ for (i = 0; i < MT7530_NUM_PORTS; i++) ++ mt7530_write(priv, MT7530_PMCR_P(i), MT7531_FORCE_LNK); ++ + /* Reset the switch through internal reset */ + mt7530_write(priv, MT7530_SYS_CTRL, + SYS_CTRL_PHY_RST | SYS_CTRL_SW_RST | diff --git a/target/linux/generic/pending-6.1/724-net-mtk_sgmii-implement-mtk_pcs_ops.patch b/target/linux/generic/pending-6.1/724-net-mtk_sgmii-implement-mtk_pcs_ops.patch new file mode 100644 index 0000000000..cd97706658 --- /dev/null +++ b/target/linux/generic/pending-6.1/724-net-mtk_sgmii-implement-mtk_pcs_ops.patch @@ -0,0 +1,46 @@ +From cbfed00575d15eafd85efd9619b7ecc0836a4aa7 Mon Sep 17 00:00:00 2001 +From: Alexander Couzens +Date: Sat, 13 Aug 2022 14:42:12 +0200 +Subject: [PATCH 04/10] net: mtk_sgmii: implement mtk_pcs_ops + +Implement mtk_pcs_ops for the SGMII pcs to read the current state +of the hardware. + +Signed-off-by: Alexander Couzens +[added DUPLEX_FULL] +Signed-off-by: Daniel Golle +--- + drivers/net/ethernet/mediatek/mtk_sgmii.c | 15 +++++++++++++++ + 1 file changed, 15 insertions(+) + +--- a/drivers/net/ethernet/mediatek/mtk_sgmii.c ++++ b/drivers/net/ethernet/mediatek/mtk_sgmii.c +@@ -122,10 +122,28 @@ static void mtk_pcs_link_up(struct phyli + regmap_write(mpcs->regmap, SGMSYS_SGMII_MODE, val); + } + ++static void mtk_pcs_get_state(struct phylink_pcs *pcs, struct phylink_link_state *state) ++{ ++ struct mtk_pcs *mpcs = pcs_to_mtk_pcs(pcs); ++ unsigned int val; ++ ++ regmap_read(mpcs->regmap, SGMSYS_PCS_CONTROL_1, &val); ++ state->an_complete = !!(val & SGMII_AN_COMPLETE); ++ state->link = !!(val & SGMII_LINK_STATYS); ++ if (!state->link) ++ return; ++ ++ regmap_read(mpcs->regmap, mpcs->ana_rgc3, &val); ++ state->speed = val & RG_PHY_SPEED_3_125G ? SPEED_2500 : SPEED_1000; ++ state->duplex = DUPLEX_FULL; ++ state->pause = 0; ++} ++ + static const struct phylink_pcs_ops mtk_pcs_ops = { + .pcs_config = mtk_pcs_config, + .pcs_an_restart = mtk_pcs_restart_an, + .pcs_link_up = mtk_pcs_link_up, ++ .pcs_get_state = mtk_pcs_get_state, + }; + + int mtk_sgmii_init(struct mtk_sgmii *ss, struct device_node *r, u32 ana_rgc3) diff --git a/target/linux/generic/pending-6.1/725-net-mtk_sgmii-fix-powering-up-the-SGMII-phy.patch b/target/linux/generic/pending-6.1/725-net-mtk_sgmii-fix-powering-up-the-SGMII-phy.patch new file mode 100644 index 0000000000..0fa357d48f --- /dev/null +++ b/target/linux/generic/pending-6.1/725-net-mtk_sgmii-fix-powering-up-the-SGMII-phy.patch @@ -0,0 +1,39 @@ +From 7f75f43fe2159123baa101fcc8c6faa0b0a4c598 Mon Sep 17 00:00:00 2001 +From: Alexander Couzens +Date: Sat, 13 Aug 2022 14:48:51 +0200 +Subject: [PATCH 05/10] net: mtk_sgmii: fix powering up the SGMII phy + +There are certain race condition when the SGMII_PHYA_PWD register still +contains 0x9 which prevents the SGMII from working properly. + +The SGMII still shows link but no traffic can flow. + +Signed-off-by: Alexander Couzens +--- + drivers/net/ethernet/mediatek/mtk_sgmii.c | 8 ++------ + 1 file changed, 2 insertions(+), 6 deletions(-) + +--- a/drivers/net/ethernet/mediatek/mtk_sgmii.c ++++ b/drivers/net/ethernet/mediatek/mtk_sgmii.c +@@ -36,9 +36,7 @@ static int mtk_pcs_setup_mode_an(struct + val |= SGMII_AN_RESTART; + regmap_write(mpcs->regmap, SGMSYS_PCS_CONTROL_1, val); + +- regmap_read(mpcs->regmap, SGMSYS_QPHY_PWR_STATE_CTRL, &val); +- val &= ~SGMII_PHYA_PWD; +- regmap_write(mpcs->regmap, SGMSYS_QPHY_PWR_STATE_CTRL, val); ++ regmap_write(mpcs->regmap, SGMSYS_QPHY_PWR_STATE_CTRL, 0); + + return 0; + +@@ -70,9 +68,7 @@ static int mtk_pcs_setup_mode_force(stru + regmap_write(mpcs->regmap, SGMSYS_SGMII_MODE, val); + + /* Release PHYA power down state */ +- regmap_read(mpcs->regmap, SGMSYS_QPHY_PWR_STATE_CTRL, &val); +- val &= ~SGMII_PHYA_PWD; +- regmap_write(mpcs->regmap, SGMSYS_QPHY_PWR_STATE_CTRL, val); ++ regmap_write(mpcs->regmap, SGMSYS_QPHY_PWR_STATE_CTRL, 0); + + return 0; + } diff --git a/target/linux/generic/pending-6.1/726-net-mtk_sgmii-ensure-the-SGMII-PHY-is-powered-down-o.patch b/target/linux/generic/pending-6.1/726-net-mtk_sgmii-ensure-the-SGMII-PHY-is-powered-down-o.patch new file mode 100644 index 0000000000..329b41cf03 --- /dev/null +++ b/target/linux/generic/pending-6.1/726-net-mtk_sgmii-ensure-the-SGMII-PHY-is-powered-down-o.patch @@ -0,0 +1,65 @@ +From 9daea9b71d060d93d7394ac465b2e5ee0b7e7bca Mon Sep 17 00:00:00 2001 +From: Alexander Couzens +Date: Mon, 15 Aug 2022 16:02:01 +0200 +Subject: [PATCH 06/10] net: mtk_sgmii: ensure the SGMII PHY is powered down on + configuration + +The code expect the PHY to be in power down (which is only true after reset). +Allow the changes of SGMII parameters more than once. +--- + drivers/net/ethernet/mediatek/mtk_sgmii.c | 16 +++++++++++++++- + 1 file changed, 15 insertions(+), 1 deletion(-) + +--- a/drivers/net/ethernet/mediatek/mtk_sgmii.c ++++ b/drivers/net/ethernet/mediatek/mtk_sgmii.c +@@ -7,6 +7,7 @@ + * + */ + ++#include + #include + #include + #include +@@ -24,6 +25,9 @@ static int mtk_pcs_setup_mode_an(struct + { + unsigned int val; + ++ /* PHYA power down */ ++ regmap_write(mpcs->regmap, SGMSYS_QPHY_PWR_STATE_CTRL, SGMII_PHYA_PWD); ++ + /* Setup the link timer and QPHY power up inside SGMIISYS */ + regmap_write(mpcs->regmap, SGMSYS_PCS_LINK_TIMER, + SGMII_LINK_TIMER_DEFAULT); +@@ -36,6 +40,10 @@ static int mtk_pcs_setup_mode_an(struct + val |= SGMII_AN_RESTART; + regmap_write(mpcs->regmap, SGMSYS_PCS_CONTROL_1, val); + ++ /* Release PHYA power down state ++ * unknown how much the QPHY needs but it is racy without a sleep ++ */ ++ usleep_range(50, 100); + regmap_write(mpcs->regmap, SGMSYS_QPHY_PWR_STATE_CTRL, 0); + + return 0; +@@ -50,6 +58,9 @@ static int mtk_pcs_setup_mode_force(stru + { + unsigned int val; + ++ /* PHYA power down */ ++ regmap_write(mpcs->regmap, SGMSYS_QPHY_PWR_STATE_CTRL, SGMII_PHYA_PWD); ++ + regmap_read(mpcs->regmap, mpcs->ana_rgc3, &val); + val &= ~RG_PHY_SPEED_MASK; + if (interface == PHY_INTERFACE_MODE_2500BASEX) +@@ -67,7 +78,10 @@ static int mtk_pcs_setup_mode_force(stru + val |= SGMII_SPEED_1000; + regmap_write(mpcs->regmap, SGMSYS_SGMII_MODE, val); + +- /* Release PHYA power down state */ ++ /* Release PHYA power down state ++ * unknown how much the QPHY needs but it is racy without a sleep ++ */ ++ usleep_range(50, 100); + regmap_write(mpcs->regmap, SGMSYS_QPHY_PWR_STATE_CTRL, 0); + + return 0; diff --git a/target/linux/generic/pending-6.1/727-net-mtk_sgmii-mtk_pcs_setup_mode_an-don-t-rely-on-re.patch b/target/linux/generic/pending-6.1/727-net-mtk_sgmii-mtk_pcs_setup_mode_an-don-t-rely-on-re.patch new file mode 100644 index 0000000000..5bc187cfc8 --- /dev/null +++ b/target/linux/generic/pending-6.1/727-net-mtk_sgmii-mtk_pcs_setup_mode_an-don-t-rely-on-re.patch @@ -0,0 +1,31 @@ +From e4dca7affb8c03438b63bdb5fddefd6ad2431cfd Mon Sep 17 00:00:00 2001 +From: Alexander Couzens +Date: Mon, 15 Aug 2022 14:59:29 +0200 +Subject: [PATCH 07/10] net: mtk_sgmii: mtk_pcs_setup_mode_an: don't rely on + register defaults + +Ensure autonegotiation is enabled. + +Signed-off-by: Alexander Couzens +--- + drivers/net/ethernet/mediatek/mtk_sgmii.c | 5 +++-- + 1 file changed, 3 insertions(+), 2 deletions(-) + +--- a/drivers/net/ethernet/mediatek/mtk_sgmii.c ++++ b/drivers/net/ethernet/mediatek/mtk_sgmii.c +@@ -32,12 +32,13 @@ static int mtk_pcs_setup_mode_an(struct + regmap_write(mpcs->regmap, SGMSYS_PCS_LINK_TIMER, + SGMII_LINK_TIMER_DEFAULT); + ++ /* disable remote fault & enable auto neg */ + regmap_read(mpcs->regmap, SGMSYS_SGMII_MODE, &val); +- val |= SGMII_REMOTE_FAULT_DIS; ++ val |= SGMII_REMOTE_FAULT_DIS | SGMII_SPEED_DUPLEX_AN; + regmap_write(mpcs->regmap, SGMSYS_SGMII_MODE, val); + + regmap_read(mpcs->regmap, SGMSYS_PCS_CONTROL_1, &val); +- val |= SGMII_AN_RESTART; ++ val |= SGMII_AN_RESTART | SGMII_AN_ENABLE; + regmap_write(mpcs->regmap, SGMSYS_PCS_CONTROL_1, val); + + /* Release PHYA power down state diff --git a/target/linux/generic/pending-6.1/728-net-mtk_sgmii-set-the-speed-according-to-the-phy-int.patch b/target/linux/generic/pending-6.1/728-net-mtk_sgmii-set-the-speed-according-to-the-phy-int.patch new file mode 100644 index 0000000000..0b17f77eef --- /dev/null +++ b/target/linux/generic/pending-6.1/728-net-mtk_sgmii-set-the-speed-according-to-the-phy-int.patch @@ -0,0 +1,47 @@ +From 952b64575613d26163a5afa5ff8bfdb57840091b Mon Sep 17 00:00:00 2001 +From: Alexander Couzens +Date: Mon, 15 Aug 2022 15:00:14 +0200 +Subject: [PATCH 08/10] net: mtk_sgmii: set the speed according to the phy + interface in AN + +The non auto-negotioting code path is setting the correct speed for the +interface. Ensure auto-negotiation code path is doing it as well. + +Signed-off-by: Alexander Couzens +--- + drivers/net/ethernet/mediatek/mtk_sgmii.c | 11 +++++++++-- + 1 file changed, 9 insertions(+), 2 deletions(-) + +--- a/drivers/net/ethernet/mediatek/mtk_sgmii.c ++++ b/drivers/net/ethernet/mediatek/mtk_sgmii.c +@@ -21,13 +21,20 @@ static struct mtk_pcs *pcs_to_mtk_pcs(st + } + + /* For SGMII interface mode */ +-static int mtk_pcs_setup_mode_an(struct mtk_pcs *mpcs) ++static int mtk_pcs_setup_mode_an(struct mtk_pcs *mpcs, phy_interface_t interface) + { + unsigned int val; + + /* PHYA power down */ + regmap_write(mpcs->regmap, SGMSYS_QPHY_PWR_STATE_CTRL, SGMII_PHYA_PWD); + ++ /* Set SGMII phy speed */ ++ regmap_read(mpcs->regmap, mpcs->ana_rgc3, &val); ++ val &= ~RG_PHY_SPEED_MASK; ++ if (interface == PHY_INTERFACE_MODE_2500BASEX) ++ val |= RG_PHY_SPEED_3_125G; ++ regmap_write(mpcs->regmap, mpcs->ana_rgc3, val); ++ + /* Setup the link timer and QPHY power up inside SGMIISYS */ + regmap_write(mpcs->regmap, SGMSYS_PCS_LINK_TIMER, + SGMII_LINK_TIMER_DEFAULT); +@@ -100,7 +107,7 @@ static int mtk_pcs_config(struct phylink + if (interface != PHY_INTERFACE_MODE_SGMII) + err = mtk_pcs_setup_mode_force(mpcs, interface); + else if (phylink_autoneg_inband(mode)) +- err = mtk_pcs_setup_mode_an(mpcs); ++ err = mtk_pcs_setup_mode_an(mpcs, interface); + + return err; + } diff --git a/target/linux/generic/pending-6.1/729-net-mtk_eth_soc-improve-comment.patch b/target/linux/generic/pending-6.1/729-net-mtk_eth_soc-improve-comment.patch new file mode 100644 index 0000000000..80144850ec --- /dev/null +++ b/target/linux/generic/pending-6.1/729-net-mtk_eth_soc-improve-comment.patch @@ -0,0 +1,22 @@ +From 06773f19cffd6c9d34dcbc8320169afef5ab60ba Mon Sep 17 00:00:00 2001 +From: Alexander Couzens +Date: Mon, 15 Aug 2022 13:58:07 +0200 +Subject: [PATCH 09/10] net: mtk_eth_soc: improve comment + +Signed-off-by: Alexander Couzens +--- + drivers/net/ethernet/mediatek/mtk_sgmii.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +--- a/drivers/net/ethernet/mediatek/mtk_sgmii.c ++++ b/drivers/net/ethernet/mediatek/mtk_sgmii.c +@@ -80,7 +80,8 @@ static int mtk_pcs_setup_mode_force(stru + val &= ~SGMII_AN_ENABLE; + regmap_write(mpcs->regmap, SGMSYS_PCS_CONTROL_1, val); + +- /* Set the speed etc but leave the duplex unchanged */ ++ /* Set the speed etc but leave the duplex unchanged. ++ * The SGMII mode for 2.5gbit is the same as for 1gbit, expect the speed in ANA_RGC3 */ + regmap_read(mpcs->regmap, SGMSYS_SGMII_MODE, &val); + val &= SGMII_DUPLEX_FULL | ~SGMII_IF_MODE_MASK; + val |= SGMII_SPEED_1000; diff --git a/target/linux/generic/pending-6.1/730-mtk_sgmii-enable-PCS-polling-to-allow-SFP-work.patch b/target/linux/generic/pending-6.1/730-mtk_sgmii-enable-PCS-polling-to-allow-SFP-work.patch new file mode 100644 index 0000000000..d185aed7bd --- /dev/null +++ b/target/linux/generic/pending-6.1/730-mtk_sgmii-enable-PCS-polling-to-allow-SFP-work.patch @@ -0,0 +1,23 @@ +From 95dcd0f223d7cab6e25bc19088016e5eb4ca1804 Mon Sep 17 00:00:00 2001 +From: Alexander Couzens +Date: Tue, 16 Aug 2022 00:22:11 +0200 +Subject: [PATCH 10/10] mtk_sgmii: enable PCS polling to allow SFP work + +Currently there is no IRQ handling (even the SGMII supports it). +Enable polling to support SFP ports. + +Signed-off-by: Alexander Couzens +--- + drivers/net/ethernet/mediatek/mtk_sgmii.c | 1 + + 1 file changed, 1 insertion(+) + +--- a/drivers/net/ethernet/mediatek/mtk_sgmii.c ++++ b/drivers/net/ethernet/mediatek/mtk_sgmii.c +@@ -182,6 +182,7 @@ int mtk_sgmii_init(struct mtk_sgmii *ss, + return PTR_ERR(ss->pcs[i].regmap); + + ss->pcs[i].pcs.ops = &mtk_pcs_ops; ++ ss->pcs[i].pcs.poll = 1; + } + + return 0; diff --git a/target/linux/generic/pending-6.1/768-net-dsa-mv88e6xxx-Request-assisted-learning-on-CPU-port.patch b/target/linux/generic/pending-6.1/768-net-dsa-mv88e6xxx-Request-assisted-learning-on-CPU-port.patch new file mode 100644 index 0000000000..8a718a02f2 --- /dev/null +++ b/target/linux/generic/pending-6.1/768-net-dsa-mv88e6xxx-Request-assisted-learning-on-CPU-port.patch @@ -0,0 +1,27 @@ +From: Tobias Waldekranz +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: + +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 +--- + 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 +@@ -6319,6 +6319,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/target/linux/generic/pending-6.1/780-ARM-kirkwood-add-missing-linux-if_ether.h-for-ETH_AL.patch b/target/linux/generic/pending-6.1/780-ARM-kirkwood-add-missing-linux-if_ether.h-for-ETH_AL.patch new file mode 100644 index 0000000000..fcf7892c04 --- /dev/null +++ b/target/linux/generic/pending-6.1/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 +X-Patchwork-Id: 12422209 +Date: Thu, 5 Aug 2021 23:23:30 +0100 +From: Daniel Golle +To: linux-arm-kernel@lists.infradead.org, netdev@vger.kernel.org, + linux-kernel@vger.kernel.org +Cc: "David S. Miller" , Andrew Lunn , + Michael Walle +Subject: [PATCH] ARM: kirkwood: add missing for ETH_ALEN +Message-ID: +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: +List-Archive: +Sender: "linux-arm-kernel" + +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 to fix this. + +Cc: David S. Miller +Cc: Andrew Lunn +Cc: Michael Walle +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 +--- + 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 + #include + #include ++#include + #include + #include + #include diff --git a/target/linux/generic/pending-6.1/800-bcma-get-SoC-device-struct-copy-its-DMA-params-to-th.patch b/target/linux/generic/pending-6.1/800-bcma-get-SoC-device-struct-copy-its-DMA-params-to-th.patch new file mode 100644 index 0000000000..478a2cb27d --- /dev/null +++ b/target/linux/generic/pending-6.1/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?= +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 +Cc: Linus Walleij +Signed-off-by: Rafał Miłecki +--- + +--- 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/target/linux/generic/pending-6.1/801-gpio-gpio-cascade-add-generic-GPIO-cascade.patch b/target/linux/generic/pending-6.1/801-gpio-gpio-cascade-add-generic-GPIO-cascade.patch new file mode 100644 index 0000000000..c1e14b9271 --- /dev/null +++ b/target/linux/generic/pending-6.1/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 +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 +Reviewed-by: Linus Walleij +Reviewed-by: Andy Shevchenko +--- +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 ++ * ++ * 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 ++#include ++#include ++#include ++ ++#include ++#include ++ ++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 "); ++MODULE_DESCRIPTION("Generic GPIO cascade"); ++MODULE_LICENSE("GPL"); diff --git a/target/linux/generic/pending-6.1/810-pci_disable_common_quirks.patch b/target/linux/generic/pending-6.1/810-pci_disable_common_quirks.patch new file mode 100644 index 0000000000..7edbd94f76 --- /dev/null +++ b/target/linux/generic/pending-6.1/810-pci_disable_common_quirks.patch @@ -0,0 +1,62 @@ +From: Gabor Juhos +Subject: debloat: add kernel config option to disabling common PCI quirks + +Signed-off-by: Gabor Juhos +--- + 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/target/linux/generic/pending-6.1/811-pci_disable_usb_common_quirks.patch b/target/linux/generic/pending-6.1/811-pci_disable_usb_common_quirks.patch new file mode 100644 index 0000000000..b498d9f700 --- /dev/null +++ b/target/linux/generic/pending-6.1/811-pci_disable_usb_common_quirks.patch @@ -0,0 +1,115 @@ +From: Felix Fietkau +Subject: debloat: disable common USB quirks + +Signed-off-by: Felix Fietkau +--- + 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 +@@ -498,7 +498,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/target/linux/generic/pending-6.1/820-w1-gpio-fix-problem-with-platfom-data-in-w1-gpio.patch b/target/linux/generic/pending-6.1/820-w1-gpio-fix-problem-with-platfom-data-in-w1-gpio.patch new file mode 100644 index 0000000000..33eb34c913 --- /dev/null +++ b/target/linux/generic/pending-6.1/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 +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 +--- + 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/target/linux/generic/pending-6.1/834-ledtrig-libata.patch b/target/linux/generic/pending-6.1/834-ledtrig-libata.patch new file mode 100644 index 0000000000..58b837c084 --- /dev/null +++ b/target/linux/generic/pending-6.1/834-ledtrig-libata.patch @@ -0,0 +1,149 @@ +From: Daniel Golle +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 +--- + 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 +@@ -4580,6 +4593,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; +@@ -5358,6 +5374,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; +@@ -5393,6 +5412,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; + } +@@ -5799,7 +5824,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 + #include + #include ++#ifdef CONFIG_ATA_LEDS ++#include ++#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/target/linux/generic/pending-6.1/840-hwrng-bcm2835-set-quality-to-1000.patch b/target/linux/generic/pending-6.1/840-hwrng-bcm2835-set-quality-to-1000.patch new file mode 100644 index 0000000000..5ca8933d6f --- /dev/null +++ b/target/linux/generic/pending-6.1/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?= +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 +--- + 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/target/linux/generic/pending-6.1/850-0023-PCI-aardvark-Make-main-irq_chip-structure-a-static-d.patch b/target/linux/generic/pending-6.1/850-0023-PCI-aardvark-Make-main-irq_chip-structure-a-static-d.patch new file mode 100644 index 0000000000..e180a385e1 --- /dev/null +++ b/target/linux/generic/pending-6.1/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?= +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 +--- + 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 +@@ -275,7 +275,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; +@@ -1345,14 +1344,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; +@@ -1411,7 +1415,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); +@@ -1422,28 +1425,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/target/linux/generic/pending-6.1/920-mangle_bootargs.patch b/target/linux/generic/pending-6.1/920-mangle_bootargs.patch new file mode 100644 index 0000000000..2a02efe0aa --- /dev/null +++ b/target/linux/generic/pending-6.1/920-mangle_bootargs.patch @@ -0,0 +1,71 @@ +From: Imre Kaloz +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 +--- + 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 +@@ -616,6 +616,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 +@@ -956,6 +979,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(); -- 2.30.2