From aa0101a6490708e5876d4850362b1c52080f24e3 Mon Sep 17 00:00:00 2001 From: lean Date: Mon, 11 Apr 2022 12:47:31 +0800 Subject: [PATCH] kernel: bump 5.10 to 5.10.110 --- include/kernel-5.10 | 4 +- ...station-poweroff-add-missing-put_dev.patch | 27 + ...-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 + ...SSC-helper-should-use-its-own-config.patch | 152 - ...ve-ifdef-CONFIG_NFSD-from-client-SSC.patch | 92 - ...ings-mtd-brcm-trx-Add-brcm-trx-magic.patch | 32 + ...Allow-to-specify-brcm-trx-magic-in-D.patch | 50 + ...Allow-to-use-TRX-parser-on-Mediatek-.patch | 25 + ...x-allow-to-use-on-MediaTek-MIPS-SoCs.patch | 33 + ...and-gigadevice-Support-GD5F1GQ5UExxG.patch | 172 + ...device-fix-Quad-IO-for-GD5F1GQ5UExxG.patch | 44 + ...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 + ...soc-add-ipv6-flow-offloading-support.patch | 105 - ...ack-Introduce-tcp-offload-timeout-co.patch | 2 +- ...thernet-mediatek-support-setting-MTU.patch | 138 + ...ack-sanitize-table-size-default-sett.patch | 100 + ...-select-correct-page-on-config-init.patch} | 0 ...x-probe-error-if-copper-page-is-sel.patch} | 0 ...10-v5.12-net-phy-Add-100-base-x-mode.patch | 56 + ...-sfp-add-support-for-100-base-x-SFPs.patch | 40 + ...phy-marvell-refactor-HWMON-OOP-style.patch | 549 +++ ...-marvell-add-SFP-support-for-88E1510.patch | 161 + ...ally-bring-up-DSA-master-when-openi.patch} | 0 ...-switchdev-of-disappearance-of-old-.patch} | 0 ...-when-a-non-legacy-FDB-operation-fa.patch} | 0 ...-switchdev_notifier_fdb_info-in-dsa.patch} | 0 ...chdev-event-implementation-under-th.patch} | 0 ...y-in-dsa_slave_switchdev_event-if-w.patch} | 0 ...r-SWITCHDEV_-FDB-DEL-_ADD_TO_DEVICE.patch} | 0 ...etup-core-clock-even-in-TRGMII-mode.patch} | 0 ...-mt7530-MT7530-optional-GPIO-support.patch | 181 + ...mt7530-Add-support-for-EEE-features.patch} | 6 +- ...he-dst-buffer-to-of_get_mac_address.patch} | 38 +- ...addr_nvmem-for-non-platform-devices.patch} | 0 ...andle-deferred-probe-error-due-to-ma.patch | 42 + ...platform-handle-mac-address-deferral.patch | 36 + ...-0001-net-bgmac-improve-handling-PHY.patch | 84 + ...t-bgmac-support-MDIO-described-in-DT.patch | 54 + ...k-change-simple-print-to-dev-variant.patch | 35 + ...use-iopoll-macro-for-qca8k_busy_wait.patch | 61 + ...prove-qca8k-read-write-rmw-bus-acces.patch | 86 + ...a-qca8k-handle-qca8k_set_page-errors.patch | 101 + ...ndle-error-with-qca8k_read-operation.patch | 207 ++ ...ndle-error-with-qca8k_write-operatio.patch | 263 ++ ...andle-error-with-qca8k_rmw-operation.patch | 226 ++ ...8k-handle-error-from-qca8k_busy_wait.patch | 66 + ...qca8k-add-support-for-qca8327-switch.patch | 96 + ...sa-qca8k-Document-new-compatible-qca.patch | 26 + ...add-priority-tweak-to-qca8337-switch.patch | 130 + ...a-qca8k-limit-port5-delay-to-qca8337.patch | 31 + ...d-GLOBAL_FC-settings-needed-for-qca8.patch | 48 + ...dsa-qca8k-add-support-for-switch-rev.patch | 114 + ...d-ethernet-ports-fallback-to-setup_m.patch | 28 + ...-qca8k-make-rgmii-delay-configurable.patch | 188 + ...clear-MASTER_EN-after-phy-read-write.patch | 50 + ...a-qca8k-protect-MASTER-busy_wait-wit.patch | 128 + ...qca8k-enlarge-mdio-delay-and-timeout.patch | 39 + ...d-support-for-internal-phy-and-inter.patch | 267 ++ ...ngs-dsa-qca8k-Document-internal-mdio.patch | 93 + ...prove-internal-mdio-read-write-bus-a.patch | 95 + ...ss-switch_revision-info-to-phy-dev_f.patch | 48 + ...ort-for-qca8k-switch-internal-PHY-in.patch | 229 ++ ...issing-unlock-on-error-in-qca8k-vlan.patch | 64 + ...eck-return-value-of-read-functions-c.patch | 348 ++ ...d-missing-check-return-value-in-qca8.patch | 47 + ...x-an-endian-bug-in-qca8k-get-ethtool.patch | 47 + ...eck-the-correct-variable-in-qca8k-se.patch | 31 + ...ernel-panic-with-legacy-mdio-mapping.patch | 80 + ...-Add-debug-prints-in-b53_vlan_enable.patch | 65 + ...sa-b53-spi-allow-device-tree-probing.patch | 41 + ...3-net-dsa-b53-relax-is63xx-condition.patch | 31 + ...tag_brcm-add-support-for-legacy-tags.patch | 180 + ...0005-net-dsa-b53-support-legacy-tags.patch | 53 + ...dsa-b53-mmap-Add-device-tree-support.patch | 92 + ...-spi-add-missing-MODULE_DEVICE_TABLE.patch | 27 + ...Do-not-force-CPU-to-be-always-tagged.patch | 86 + ...3-remove-redundant-null-check-on-dev.patch | 30 + ...Create-default-VLAN-entry-explicitly.patch | 71 + ...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 ++ ...t-skb-offload_fwd_mark-when-not-offl.patch | 138 + ...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 | 508 +++ ...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 + ...1-net-dsa-mt7530-support-setting-MTU.patch | 112 + ...-dsa-mt7530-enable-MTU-normalization.patch | 36 + ...a-mt7530-support-setting-ageing-time.patch | 99 + ...et-dsa-mt7530-support-MDB-operations.patch | 171 + ...MediaTek-Gigabit-Ethernet-PHY-driver.patch | 159 + ...net-dsa-mt7530-add-interrupt-support.patch | 425 +++ ...5.15-1-igc-remove-_I_PHY_ID-checking.patch | 73 + ...v5.15-2-igc-remove-phy-type-checking.patch | 43 + ...x-keep-the-pvid-at-0-when-VLAN-unawa.patch | 225 ++ ...ovide-missing-documentation-for-some.patch | 72 + ...x-a-couple-of-spelling-errors-in-fw_.patch | 34 + ...153_ecm-support-ECM-mode-for-RTL8153.patch | 320 ++ ...12-net-usb-r8152-use-new-tasklet-API.patch | 41 + ...veral-functions-about-phy-patch-requ.patch | 198 ++ ...t-the-flow-of-power-cut-for-RTL8153B.patch | 134 + ...152-enable-U1-U2-for-USB_SPEED_SUPER.patch | 47 + ...f-the-pointer-of-the-function-exists.patch | 51 + ...r8152-replace-netif_err-with-dev_err.patch | 36 + ...rtl_set_eee_plus-and-r8153b_green_en.patch | 89 + ...ter-fram-gap-time-depending-on-speed.patch | 75 + ...just-rtl8152_check_firmware-function.patch | 152 + ...8152-add-help-function-to-change-mtu.patch | 157 + .../793-v5.13-r8152-support-new-chips.patch | 2886 ++++++++++++++++ ...port-PHY-firmware-for-RTL8156-series.patch | 691 ++++ ...rch-the-configuration-of-vendor-mode.patch | 79 + ...-declare-its-reliance-on-msi-domains.patch | 34 + ...declar-their-reliance-on-msi-domains.patch | 44 + ...ertise-lack-of-built-in-msi-handling.patch | 59 + ...ialize-enable-GPIO-direction-to-outp.patch | 28 + ...mul-Add-description-for-class_revisi.patch | 44 + ...mul-Add-definitions-for-missing-capa.patch | 73 + ...-support-for-DEVCAP2-DEVCTL2-LNKCAP2.patch | 61 + ...PCI-aardvark-Clear-all-MSIs-at-setup.patch | 59 + ...ment-actions-in-driver-remove-method.patch | 34 + ...able-bus-mastering-when-unbinding-dr.patch | 41 + ...k-all-interrupts-when-unbinding-driv.patch | 48 + ...ark-Fix-memory-leak-in-driver-unbind.patch | 33 + ...k-Assert-PERST-when-unbinding-driver.patch | 33 + ...able-link-training-when-unbinding-dr.patch | 34 + ...able-common-PHY-when-unbinding-drive.patch | 30 + ...mvebu-a3700-comphy-Rename-HS-SGMMI-t.patch | 67 + ...mvebu-a3700-comphy-Remove-unsupporte.patch | 40 + ...PS-ath79-drop-_machine_restart-again.patch | 49 + ...ext-hwmon-lm70-Add-ti-tmp125-support.patch | 71 + .../generic/files/drivers/mtd/nand/mtk_bmt.c | 465 +++ .../generic/files/drivers/mtd/nand/mtk_bmt.h | 131 + .../files/drivers/mtd/nand/mtk_bmt_bbt.c | 203 ++ .../files/drivers/mtd/nand/mtk_bmt_nmbm.c | 2348 +++++++++++++ .../files/drivers/mtd/nand/mtk_bmt_v2.c | 513 +++ .../generic/files/include/linux/mtd/mtk_bmt.h | 18 + 181 files changed, 26565 insertions(+), 374 deletions(-) create mode 100644 target/linux/generic/backport-5.10/026-power-reset-linkstation-poweroff-add-missing-put_dev.patch create mode 100644 target/linux/generic/backport-5.10/050-v5.16-00-MIPS-uasm-Enable-muhu-opcode-for-MIPS-R6.patch create mode 100644 target/linux/generic/backport-5.10/050-v5.16-01-mips-uasm-Add-workaround-for-Loongson-2F-nop-CPU-err.patch create mode 100644 target/linux/generic/backport-5.10/050-v5.16-02-mips-bpf-Add-eBPF-JIT-for-32-bit-MIPS.patch create mode 100644 target/linux/generic/backport-5.10/050-v5.16-03-mips-bpf-Add-new-eBPF-JIT-for-64-bit-MIPS.patch create mode 100644 target/linux/generic/backport-5.10/050-v5.16-04-mips-bpf-Add-JIT-workarounds-for-CPU-errata.patch create mode 100644 target/linux/generic/backport-5.10/050-v5.16-05-mips-bpf-Enable-eBPF-JITs.patch create mode 100644 target/linux/generic/backport-5.10/050-v5.16-06-mips-bpf-Remove-old-BPF-JIT-implementations.patch create mode 100644 target/linux/generic/backport-5.10/081-net-next-regmap-allow-to-define-reg_update_bits-for-no-bus.patch delete mode 100644 target/linux/generic/backport-5.10/350-v5.12-NFSv4_2-SSC-helper-should-use-its-own-config.patch delete mode 100644 target/linux/generic/backport-5.10/351-v5.13-NFSv4_2-Remove-ifdef-CONFIG_NFSD-from-client-SSC.patch create mode 100644 target/linux/generic/backport-5.10/409-v5.14-0001-dt-bindings-mtd-brcm-trx-Add-brcm-trx-magic.patch create mode 100644 target/linux/generic/backport-5.10/409-v5.14-0002-mtd-parsers-trx-Allow-to-specify-brcm-trx-magic-in-D.patch create mode 100644 target/linux/generic/backport-5.10/409-v5.14-0003-mtd-parsers-trx-Allow-to-use-TRX-parser-on-Mediatek-.patch create mode 100644 target/linux/generic/backport-5.10/410-mtd-next-mtd-parsers-trx-allow-to-use-on-MediaTek-MIPS-SoCs.patch create mode 100644 target/linux/generic/backport-5.10/419-v5.14-mtd-spinand-gigadevice-Support-GD5F1GQ5UExxG.patch create mode 100644 target/linux/generic/backport-5.10/420-v5.19-01-mtd-spinand-gigadevice-fix-Quad-IO-for-GD5F1GQ5UExxG.patch create mode 100644 target/linux/generic/backport-5.10/420-v5.19-02-mtd-spinand-gigadevice-add-support-for-GD5FxGQ4xExxG.patch create mode 100644 target/linux/generic/backport-5.10/420-v5.19-03-mtd-spinand-gigadevice-add-support-for-GD5F1GQ5RExxG.patch create mode 100644 target/linux/generic/backport-5.10/420-v5.19-04-mtd-spinand-gigadevice-add-support-for-GD5F-2-4-GQ5x.patch create mode 100644 target/linux/generic/backport-5.10/420-v5.19-05-mtd-spinand-gigadevice-add-support-for-GD5FxGM7xExxG.patch delete mode 100644 target/linux/generic/backport-5.10/610-v5.13-54-net-ethernet-mtk_eth_soc-add-ipv6-flow-offloading-support.patch create mode 100644 target/linux/generic/backport-5.10/611-v5.12-net-ethernet-mediatek-support-setting-MTU.patch create mode 100644 target/linux/generic/backport-5.10/612-v5.15-netfilter-conntrack-sanitize-table-size-default-sett.patch rename target/linux/generic/backport-5.10/{730-net-phy-at803x-select-correct-page-on-config-init.patch => 705-net-phy-at803x-select-correct-page-on-config-init.patch} (100%) rename target/linux/generic/backport-5.10/{731-net-phy-at803x-fix-probe-error-if-copper-page-is-sel.patch => 706-net-phy-at803x-fix-probe-error-if-copper-page-is-sel.patch} (100%) create mode 100644 target/linux/generic/backport-5.10/710-v5.12-net-phy-Add-100-base-x-mode.patch create mode 100644 target/linux/generic/backport-5.10/711-v5.12-sfp-add-support-for-100-base-x-SFPs.patch create mode 100644 target/linux/generic/backport-5.10/712-v5.13-net-phy-marvell-refactor-HWMON-OOP-style.patch create mode 100644 target/linux/generic/backport-5.10/713-v5.15-net-phy-marvell-add-SFP-support-for-88E1510.patch rename target/linux/generic/backport-5.10/{765-v5.12-net-dsa-automatically-bring-up-DSA-master-when-openi.patch => 719-v5.12-net-dsa-automatically-bring-up-DSA-master-when-openi.patch} (100%) rename target/linux/generic/backport-5.10/{770-v5.12-net-bridge-notify-switchdev-of-disappearance-of-old-.patch => 720-v5.12-net-bridge-notify-switchdev-of-disappearance-of-old-.patch} (100%) rename target/linux/generic/backport-5.10/{771-v5.12-net-dsa-be-louder-when-a-non-legacy-FDB-operation-fa.patch => 721-v5.12-net-dsa-be-louder-when-a-non-legacy-FDB-operation-fa.patch} (100%) rename target/linux/generic/backport-5.10/{772-v5.12-net-dsa-don-t-use-switchdev_notifier_fdb_info-in-dsa.patch => 722-v5.12-net-dsa-don-t-use-switchdev_notifier_fdb_info-in-dsa.patch} (100%) rename target/linux/generic/backport-5.10/{773-v5.12-net-dsa-move-switchdev-event-implementation-under-th.patch => 723-v5.12-net-dsa-move-switchdev-event-implementation-under-th.patch} (100%) rename target/linux/generic/backport-5.10/{774-v5.12-net-dsa-exit-early-in-dsa_slave_switchdev_event-if-w.patch => 724-v5.12-net-dsa-exit-early-in-dsa_slave_switchdev_event-if-w.patch} (100%) rename target/linux/generic/backport-5.10/{775-v5.12-net-dsa-listen-for-SWITCHDEV_-FDB-DEL-_ADD_TO_DEVICE.patch => 725-v5.12-net-dsa-listen-for-SWITCHDEV_-FDB-DEL-_ADD_TO_DEVICE.patch} (100%) rename target/linux/generic/backport-5.10/{780-net-dsa-mt7530-setup-core-clock-even-in-TRGMII-mode.patch => 730-net-dsa-mt7530-setup-core-clock-even-in-TRGMII-mode.patch} (100%) create mode 100644 target/linux/generic/backport-5.10/731-v5.12-net-dsa-mt7530-MT7530-optional-GPIO-support.patch rename target/linux/generic/backport-5.10/{781-v5.13-net-dsa-mt7530-Add-support-for-EEE-features.patch => 731-v5.13-net-dsa-mt7530-Add-support-for-EEE-features.patch} (95%) rename target/linux/generic/backport-5.10/{782-net-next-1-of-net-pass-the-dst-buffer-to-of_get_mac_address.patch => 732-net-next-1-of-net-pass-the-dst-buffer-to-of_get_mac_address.patch} (98%) rename target/linux/generic/backport-5.10/{782-net-next-2-of-net-fix-of_get_mac_addr_nvmem-for-non-platform-devices.patch => 732-net-next-2-of-net-fix-of_get_mac_addr_nvmem-for-non-platform-devices.patch} (100%) create mode 100644 target/linux/generic/backport-5.10/733-v5.15-0001-net-bgmac-bcma-handle-deferred-probe-error-due-to-ma.patch create mode 100644 target/linux/generic/backport-5.10/733-v5.15-0002-net-bgmac-platform-handle-mac-address-deferral.patch create mode 100644 target/linux/generic/backport-5.10/734-v5.16-0001-net-bgmac-improve-handling-PHY.patch create mode 100644 target/linux/generic/backport-5.10/734-v5.16-0002-net-bgmac-support-MDIO-described-in-DT.patch create mode 100644 target/linux/generic/backport-5.10/735-v5.14-01-net-dsa-qca8k-change-simple-print-to-dev-variant.patch create mode 100644 target/linux/generic/backport-5.10/735-v5.14-02-net-dsa-qca8k-use-iopoll-macro-for-qca8k_busy_wait.patch create mode 100644 target/linux/generic/backport-5.10/735-v5.14-03-net-dsa-qca8k-improve-qca8k-read-write-rmw-bus-acces.patch create mode 100644 target/linux/generic/backport-5.10/735-v5.14-04-net-dsa-qca8k-handle-qca8k_set_page-errors.patch create mode 100644 target/linux/generic/backport-5.10/735-v5.14-05-net-dsa-qca8k-handle-error-with-qca8k_read-operation.patch create mode 100644 target/linux/generic/backport-5.10/735-v5.14-06-net-dsa-qca8k-handle-error-with-qca8k_write-operatio.patch create mode 100644 target/linux/generic/backport-5.10/735-v5.14-07-net-dsa-qca8k-handle-error-with-qca8k_rmw-operation.patch create mode 100644 target/linux/generic/backport-5.10/735-v5.14-08-net-dsa-qca8k-handle-error-from-qca8k_busy_wait.patch create mode 100644 target/linux/generic/backport-5.10/735-v5.14-09-net-dsa-qca8k-add-support-for-qca8327-switch.patch create mode 100644 target/linux/generic/backport-5.10/735-v5.14-10-devicetree-net-dsa-qca8k-Document-new-compatible-qca.patch create mode 100644 target/linux/generic/backport-5.10/735-v5.14-11-net-dsa-qca8k-add-priority-tweak-to-qca8337-switch.patch create mode 100644 target/linux/generic/backport-5.10/735-v5.14-12-net-dsa-qca8k-limit-port5-delay-to-qca8337.patch create mode 100644 target/linux/generic/backport-5.10/735-v5.14-13-net-dsa-qca8k-add-GLOBAL_FC-settings-needed-for-qca8.patch create mode 100644 target/linux/generic/backport-5.10/735-v5.14-14-net-dsa-qca8k-add-support-for-switch-rev.patch create mode 100644 target/linux/generic/backport-5.10/735-v5.14-15-net-dsa-qca8k-add-ethernet-ports-fallback-to-setup_m.patch create mode 100644 target/linux/generic/backport-5.10/735-v5.14-16-net-dsa-qca8k-make-rgmii-delay-configurable.patch create mode 100644 target/linux/generic/backport-5.10/735-v5.14-17-net-dsa-qca8k-clear-MASTER_EN-after-phy-read-write.patch create mode 100644 target/linux/generic/backport-5.10/735-v5.14-18-net-dsa-qca8k-dsa-qca8k-protect-MASTER-busy_wait-wit.patch create mode 100644 target/linux/generic/backport-5.10/735-v5.14-19-net-dsa-qca8k-enlarge-mdio-delay-and-timeout.patch create mode 100644 target/linux/generic/backport-5.10/735-v5.14-20-net-dsa-qca8k-add-support-for-internal-phy-and-inter.patch create mode 100644 target/linux/generic/backport-5.10/735-v5.14-21-devicetree-bindings-dsa-qca8k-Document-internal-mdio.patch create mode 100644 target/linux/generic/backport-5.10/735-v5.14-22-net-dsa-qca8k-improve-internal-mdio-read-write-bus-a.patch create mode 100644 target/linux/generic/backport-5.10/735-v5.14-23-net-dsa-qca8k-pass-switch_revision-info-to-phy-dev_f.patch create mode 100644 target/linux/generic/backport-5.10/735-v5.14-25-net-phy-add-support-for-qca8k-switch-internal-PHY-in.patch create mode 100644 target/linux/generic/backport-5.10/736-v5.14-net-dsa-qca8k-fix-missing-unlock-on-error-in-qca8k-vlan.patch create mode 100644 target/linux/generic/backport-5.10/737-v5.14-01-net-dsa-qca8k-check-return-value-of-read-functions-c.patch create mode 100644 target/linux/generic/backport-5.10/737-v5.14-02-net-dsa-qca8k-add-missing-check-return-value-in-qca8.patch create mode 100644 target/linux/generic/backport-5.10/738-v5.14-01-net-dsa-qca8k-fix-an-endian-bug-in-qca8k-get-ethtool.patch create mode 100644 target/linux/generic/backport-5.10/738-v5.14-02-net-dsa-qca8k-check-the-correct-variable-in-qca8k-se.patch create mode 100644 target/linux/generic/backport-5.10/739-v5.15-net-dsa-qca8k-fix-kernel-panic-with-legacy-mdio-mapping.patch create mode 100644 target/linux/generic/backport-5.10/740-v5.13-0001-net-dsa-b53-Add-debug-prints-in-b53_vlan_enable.patch create mode 100644 target/linux/generic/backport-5.10/740-v5.13-0002-net-dsa-b53-spi-allow-device-tree-probing.patch create mode 100644 target/linux/generic/backport-5.10/740-v5.13-0003-net-dsa-b53-relax-is63xx-condition.patch create mode 100644 target/linux/generic/backport-5.10/740-v5.13-0004-net-dsa-tag_brcm-add-support-for-legacy-tags.patch create mode 100644 target/linux/generic/backport-5.10/740-v5.13-0005-net-dsa-b53-support-legacy-tags.patch create mode 100644 target/linux/generic/backport-5.10/740-v5.13-0006-net-dsa-b53-mmap-Add-device-tree-support.patch create mode 100644 target/linux/generic/backport-5.10/740-v5.13-0007-net-dsa-b53-spi-add-missing-MODULE_DEVICE_TABLE.patch create mode 100644 target/linux/generic/backport-5.10/741-v5.14-0001-net-dsa-b53-Do-not-force-CPU-to-be-always-tagged.patch create mode 100644 target/linux/generic/backport-5.10/741-v5.14-0002-net-dsa-b53-remove-redundant-null-check-on-dev.patch create mode 100644 target/linux/generic/backport-5.10/741-v5.14-0003-net-dsa-b53-Create-default-VLAN-entry-explicitly.patch create mode 100644 target/linux/generic/backport-5.10/742-v5.16-net-phy-at803x-add-support-for-qca-8327-internal-phy.patch create mode 100644 target/linux/generic/backport-5.10/743-v5.16-0001-net-dsa-b53-Include-all-ports-in-enabled_ports.patch create mode 100644 target/linux/generic/backport-5.10/743-v5.16-0002-net-dsa-b53-Drop-BCM5301x-workaround-for-a-wrong-CPU.patch create mode 100644 target/linux/generic/backport-5.10/743-v5.16-0003-net-dsa-b53-Improve-flow-control-setup-on-BCM5301x.patch create mode 100644 target/linux/generic/backport-5.10/743-v5.16-0004-net-dsa-b53-Drop-unused-cpu_port-field.patch create mode 100644 target/linux/generic/backport-5.10/744-v5.15-net-dsa-don-t-set-skb-offload_fwd_mark-when-not-offl.patch create mode 100644 target/linux/generic/backport-5.10/745-v5.16-01-net-phy-at803x-add-support-for-qca-8327-A-variant.patch create mode 100644 target/linux/generic/backport-5.10/745-v5.16-02-net-phy-at803x-add-resume-suspend-function-to-qca83x.patch create mode 100644 target/linux/generic/backport-5.10/745-v5.16-03-net-phy-at803x-fix-spacing-and-improve-name-for-83xx.patch create mode 100644 target/linux/generic/backport-5.10/746-v5.16-01-net-phy-at803x-fix-resume-for-QCA8327-phy.patch create mode 100644 target/linux/generic/backport-5.10/746-v5.16-02-net-phy-at803x-add-DAC-amplitude-fix-for-8327-phy.patch create mode 100644 target/linux/generic/backport-5.10/746-v5.16-03-net-phy-at803x-enable-prefer-master-for-83xx-interna.patch create mode 100644 target/linux/generic/backport-5.10/746-v5.16-04-net-phy-at803x-better-describe-debug-regs.patch create mode 100644 target/linux/generic/backport-5.10/747-v5.16-01-dsa-qca8k-add-mac-power-sel-support.patch create mode 100644 target/linux/generic/backport-5.10/747-v5.16-02-dt-bindings-net-dsa-qca8k-Add-SGMII-clock-phase-prop.patch create mode 100644 target/linux/generic/backport-5.10/747-v5.16-03-net-dsa-qca8k-add-support-for-sgmii-falling-edge.patch create mode 100644 target/linux/generic/backport-5.10/747-v5.16-04-dt-bindings-net-dsa-qca8k-Document-support-for-CPU-p.patch create mode 100644 target/linux/generic/backport-5.10/747-v5.16-05-net-dsa-qca8k-add-support-for-cpu-port-6.patch create mode 100644 target/linux/generic/backport-5.10/747-v5.16-06-net-dsa-qca8k-rework-rgmii-delay-logic-and-scan-for-.patch create mode 100644 target/linux/generic/backport-5.10/747-v5.16-07-dt-bindings-net-dsa-qca8k-Document-qca-sgmii-enable-.patch create mode 100644 target/linux/generic/backport-5.10/747-v5.16-08-net-dsa-qca8k-add-explicit-SGMII-PLL-enable.patch create mode 100644 target/linux/generic/backport-5.10/747-v5.16-09-dt-bindings-net-dsa-qca8k-Document-qca-led-open-drai.patch create mode 100644 target/linux/generic/backport-5.10/747-v5.16-10-net-dsa-qca8k-add-support-for-pws-config-reg.patch create mode 100644 target/linux/generic/backport-5.10/747-v5.16-11-dt-bindings-net-dsa-qca8k-document-support-for-qca83.patch create mode 100644 target/linux/generic/backport-5.10/747-v5.16-12-net-dsa-qca8k-add-support-for-QCA8328.patch create mode 100644 target/linux/generic/backport-5.10/747-v5.16-13-net-dsa-qca8k-set-internal-delay-also-for-sgmii.patch create mode 100644 target/linux/generic/backport-5.10/747-v5.16-14-net-dsa-qca8k-move-port-config-to-dedicated-struct.patch create mode 100644 target/linux/generic/backport-5.10/747-v5.16-15-dt-bindings-net-ipq8064-mdio-fix-warning-with-new-qc.patch create mode 100644 target/linux/generic/backport-5.10/747-v5.16-16-dt-bindings-net-dsa-qca8k-convert-to-YAML-schema.patch create mode 100644 target/linux/generic/backport-5.10/748-v5.16-net-dsa-qca8k-fix-delay-applied-to-wrong-cpu-in-parse-p.patch create mode 100644 target/linux/generic/backport-5.10/749-v5.16-net-dsa-qca8k-tidy-for-loop-in-setup-and-add-cpu-port-c.patch create mode 100644 target/linux/generic/backport-5.10/750-v5.16-net-dsa-qca8k-make-sure-pad0-mac06-exchange-is-disabled.patch create mode 100644 target/linux/generic/backport-5.10/751-v5.16-net-dsa-qca8k-fix-internal-delay-applied-to-the-wrong-PAD.patch create mode 100644 target/linux/generic/backport-5.10/752-v5.16-net-dsa-qca8k-fix-MTU-calculation.patch create mode 100644 target/linux/generic/backport-5.10/753-net-next-net-dsa-qca8k-remove-redundant-check-in-parse_port_config.patch create mode 100644 target/linux/generic/backport-5.10/754-net-next-net-dsa-qca8k-convert-to-GENMASK_FIELD_PREP_FIELD_GET.patch create mode 100644 target/linux/generic/backport-5.10/755-net-next-net-dsa-qca8k-remove-extra-mutex_init-in-qca8k_setup.patch create mode 100644 target/linux/generic/backport-5.10/756-net-next-net-dsa-qca8k-move-regmap-init-in-probe-and-set-it.patch create mode 100644 target/linux/generic/backport-5.10/757-net-next-net-dsa-qca8k-initial-conversion-to-regmap-heper.patch create mode 100644 target/linux/generic/backport-5.10/758-net-next-net-dsa-qca8k-add-additional-MIB-counter-and-.patch create mode 100644 target/linux/generic/backport-5.10/759-net-next-net-dsa-qca8k-add-support-for-port-fast-aging.patch create mode 100644 target/linux/generic/backport-5.10/760-net-next-net-dsa-qca8k-add-set_ageing_time-support.patch create mode 100644 target/linux/generic/backport-5.10/761-net-next-net-dsa-qca8k-add-support-for-mdb_add-del.patch create mode 100644 target/linux/generic/backport-5.10/762-v5.11-net-dsa-mt7530-support-setting-MTU.patch create mode 100644 target/linux/generic/backport-5.10/763-v5.11-net-dsa-mt7530-enable-MTU-normalization.patch create mode 100644 target/linux/generic/backport-5.10/764-v5.11-net-dsa-mt7530-support-setting-ageing-time.patch create mode 100644 target/linux/generic/backport-5.10/770-v5.15-net-dsa-mt7530-support-MDB-operations.patch create mode 100644 target/linux/generic/backport-5.10/771-v5.14-net-phy-add-MediaTek-Gigabit-Ethernet-PHY-driver.patch create mode 100644 target/linux/generic/backport-5.10/772-v5.14-net-dsa-mt7530-add-interrupt-support.patch create mode 100644 target/linux/generic/backport-5.10/774-v5.15-1-igc-remove-_I_PHY_ID-checking.patch create mode 100644 target/linux/generic/backport-5.10/774-v5.15-2-igc-remove-phy-type-checking.patch create mode 100644 target/linux/generic/backport-5.10/774-v5.15-net-dsa-mv88e6xxx-keep-the-pvid-at-0-when-VLAN-unawa.patch create mode 100644 target/linux/generic/backport-5.10/780-v5.11-net-usb-r8152-Provide-missing-documentation-for-some.patch create mode 100644 target/linux/generic/backport-5.10/781-v5.11-net-usb-r8152-Fix-a-couple-of-spelling-errors-in-fw_.patch create mode 100644 target/linux/generic/backport-5.10/782-v5.11-net-usb-r8153_ecm-support-ECM-mode-for-RTL8153.patch create mode 100644 target/linux/generic/backport-5.10/783-v5.12-net-usb-r8152-use-new-tasklet-API.patch create mode 100644 target/linux/generic/backport-5.10/784-v5.12-r8152-replace-several-functions-about-phy-patch-requ.patch create mode 100644 target/linux/generic/backport-5.10/785-v5.12-r8152-adjust-the-flow-of-power-cut-for-RTL8153B.patch create mode 100644 target/linux/generic/backport-5.10/786-v5.12-r8152-enable-U1-U2-for-USB_SPEED_SUPER.patch create mode 100644 target/linux/generic/backport-5.10/787-v5.12-r8152-check-if-the-pointer-of-the-function-exists.patch create mode 100644 target/linux/generic/backport-5.10/788-v5.12-r8152-replace-netif_err-with-dev_err.patch create mode 100644 target/linux/generic/backport-5.10/789-v5.12-r8152-spilt-rtl_set_eee_plus-and-r8153b_green_en.patch create mode 100644 target/linux/generic/backport-5.10/790-v5.13-r8152-set-inter-fram-gap-time-depending-on-speed.patch create mode 100644 target/linux/generic/backport-5.10/791-v5.13-r8152-adjust-rtl8152_check_firmware-function.patch create mode 100644 target/linux/generic/backport-5.10/792-v5.13-r8152-add-help-function-to-change-mtu.patch create mode 100644 target/linux/generic/backport-5.10/793-v5.13-r8152-support-new-chips.patch create mode 100644 target/linux/generic/backport-5.10/794-v5.13-r8152-support-PHY-firmware-for-RTL8156-series.patch create mode 100644 target/linux/generic/backport-5.10/795-v5.13-r8152-search-the-configuration-of-vendor-mode.patch create mode 100644 target/linux/generic/backport-5.10/820-v5.13-make-pci_host_common_probe-declare-its-reliance-on-msi-domains.patch create mode 100644 target/linux/generic/backport-5.10/821-v5.13-let-pci-host-bridges-declar-their-reliance-on-msi-domains.patch create mode 100644 target/linux/generic/backport-5.10/822-v5.13-advertise-lack-of-built-in-msi-handling.patch create mode 100644 target/linux/generic/backport-5.10/830-v5.14-leds-lp55xx-Initialize-enable-GPIO-direction-to-outp.patch create mode 100644 target/linux/generic/backport-5.10/850-v5.17-0001-PCI-pci-bridge-emul-Add-description-for-class_revisi.patch create mode 100644 target/linux/generic/backport-5.10/850-v5.17-0002-PCI-pci-bridge-emul-Add-definitions-for-missing-capa.patch create mode 100644 target/linux/generic/backport-5.10/850-v5.17-0003-PCI-aardvark-Add-support-for-DEVCAP2-DEVCTL2-LNKCAP2.patch create mode 100644 target/linux/generic/backport-5.10/850-v5.17-0004-PCI-aardvark-Clear-all-MSIs-at-setup.patch create mode 100644 target/linux/generic/backport-5.10/850-v5.17-0005-PCI-aardvark-Comment-actions-in-driver-remove-method.patch create mode 100644 target/linux/generic/backport-5.10/850-v5.17-0006-PCI-aardvark-Disable-bus-mastering-when-unbinding-dr.patch create mode 100644 target/linux/generic/backport-5.10/850-v5.17-0007-PCI-aardvark-Mask-all-interrupts-when-unbinding-driv.patch create mode 100644 target/linux/generic/backport-5.10/850-v5.17-0008-PCI-aardvark-Fix-memory-leak-in-driver-unbind.patch create mode 100644 target/linux/generic/backport-5.10/850-v5.17-0009-PCI-aardvark-Assert-PERST-when-unbinding-driver.patch create mode 100644 target/linux/generic/backport-5.10/850-v5.17-0010-PCI-aardvark-Disable-link-training-when-unbinding-dr.patch create mode 100644 target/linux/generic/backport-5.10/850-v5.17-0011-PCI-aardvark-Disable-common-PHY-when-unbinding-drive.patch create mode 100644 target/linux/generic/backport-5.10/851-v5.15-0001-phy-marvell-phy-mvebu-a3700-comphy-Rename-HS-SGMMI-t.patch create mode 100644 target/linux/generic/backport-5.10/851-v5.15-0002-phy-marvell-phy-mvebu-a3700-comphy-Remove-unsupporte.patch create mode 100644 target/linux/generic/backport-5.10/860-v5.17-MIPS-ath79-drop-_machine_restart-again.patch create mode 100644 target/linux/generic/backport-5.10/870-hwmon-next-hwmon-lm70-Add-ti-tmp125-support.patch create mode 100644 target/linux/generic/files/drivers/mtd/nand/mtk_bmt.c create mode 100644 target/linux/generic/files/drivers/mtd/nand/mtk_bmt.h create mode 100644 target/linux/generic/files/drivers/mtd/nand/mtk_bmt_bbt.c create mode 100644 target/linux/generic/files/drivers/mtd/nand/mtk_bmt_nmbm.c create mode 100644 target/linux/generic/files/drivers/mtd/nand/mtk_bmt_v2.c create mode 100644 target/linux/generic/files/include/linux/mtd/mtk_bmt.h diff --git a/include/kernel-5.10 b/include/kernel-5.10 index c5eee97a3..2a8fa3c4e 100644 --- a/include/kernel-5.10 +++ b/include/kernel-5.10 @@ -1,2 +1,2 @@ -LINUX_VERSION-5.10 = .109 -LINUX_KERNEL_HASH-5.10.109 = 18fb22ecb249669ea775474aee614dcb0697cab299068074fd9f0bafa32113dc +LINUX_VERSION-5.10 = .110 +LINUX_KERNEL_HASH-5.10.110 = dbef6a06325433481551cb8cfca9254d908d0ae950bc809f3da8ade00c485693 diff --git a/target/linux/generic/backport-5.10/026-power-reset-linkstation-poweroff-add-missing-put_dev.patch b/target/linux/generic/backport-5.10/026-power-reset-linkstation-poweroff-add-missing-put_dev.patch new file mode 100644 index 000000000..66e75bf51 --- /dev/null +++ b/target/linux/generic/backport-5.10/026-power-reset-linkstation-poweroff-add-missing-put_dev.patch @@ -0,0 +1,27 @@ +From 1027a42c25cbf8cfc4ade6503c5110aae04866af Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Daniel=20Gonz=C3=A1lez=20Cabanelas?= +Date: Fri, 16 Oct 2020 20:22:37 +0200 +Subject: [PATCH] power: reset: linkstation-poweroff: add missing put_device() +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +The of_mdio_find_bus() takes a reference to the underlying device +structure, we should release that reference using a put_device() call. + +Signed-off-by: Daniel González Cabanelas +Signed-off-by: Sebastian Reichel +--- + drivers/power/reset/linkstation-poweroff.c | 1 + + 1 file changed, 1 insertion(+) + +--- a/drivers/power/reset/linkstation-poweroff.c ++++ b/drivers/power/reset/linkstation-poweroff.c +@@ -113,6 +113,7 @@ static int __init linkstation_poweroff_i + return -EPROBE_DEFER; + + phydev = phy_find_first(bus); ++ put_device(&bus->dev); + if (!phydev) + return -EPROBE_DEFER; + diff --git a/target/linux/generic/backport-5.10/050-v5.16-00-MIPS-uasm-Enable-muhu-opcode-for-MIPS-R6.patch b/target/linux/generic/backport-5.10/050-v5.16-00-MIPS-uasm-Enable-muhu-opcode-for-MIPS-R6.patch new file mode 100644 index 000000000..82feb7421 --- /dev/null +++ b/target/linux/generic/backport-5.10/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-5.10/050-v5.16-01-mips-uasm-Add-workaround-for-Loongson-2F-nop-CPU-err.patch b/target/linux/generic/backport-5.10/050-v5.16-01-mips-uasm-Add-workaround-for-Loongson-2F-nop-CPU-err.patch new file mode 100644 index 000000000..3a4d573f8 --- /dev/null +++ b/target/linux/generic/backport-5.10/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-5.10/050-v5.16-02-mips-bpf-Add-eBPF-JIT-for-32-bit-MIPS.patch b/target/linux/generic/backport-5.10/050-v5.16-02-mips-bpf-Add-eBPF-JIT-for-32-bit-MIPS.patch new file mode 100644 index 000000000..798065996 --- /dev/null +++ b/target/linux/generic/backport-5.10/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-5.10/050-v5.16-03-mips-bpf-Add-new-eBPF-JIT-for-64-bit-MIPS.patch b/target/linux/generic/backport-5.10/050-v5.16-03-mips-bpf-Add-new-eBPF-JIT-for-64-bit-MIPS.patch new file mode 100644 index 000000000..38b46c0b7 --- /dev/null +++ b/target/linux/generic/backport-5.10/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-5.10/050-v5.16-04-mips-bpf-Add-JIT-workarounds-for-CPU-errata.patch b/target/linux/generic/backport-5.10/050-v5.16-04-mips-bpf-Add-JIT-workarounds-for-CPU-errata.patch new file mode 100644 index 000000000..63553ebe5 --- /dev/null +++ b/target/linux/generic/backport-5.10/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-5.10/050-v5.16-05-mips-bpf-Enable-eBPF-JITs.patch b/target/linux/generic/backport-5.10/050-v5.16-05-mips-bpf-Enable-eBPF-JITs.patch new file mode 100644 index 000000000..10685c5f3 --- /dev/null +++ b/target/linux/generic/backport-5.10/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 +@@ -3294,6 +3294,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 +@@ -49,7 +49,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 +@@ -57,7 +56,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-5.10/050-v5.16-06-mips-bpf-Remove-old-BPF-JIT-implementations.patch b/target/linux/generic/backport-5.10/050-v5.16-06-mips-bpf-Remove-old-BPF-JIT-implementations.patch new file mode 100644 index 000000000..e25c33683 --- /dev/null +++ b/target/linux/generic/backport-5.10/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-5.10/081-net-next-regmap-allow-to-define-reg_update_bits-for-no-bus.patch b/target/linux/generic/backport-5.10/081-net-next-regmap-allow-to-define-reg_update_bits-for-no-bus.patch new file mode 100644 index 000000000..6e274acb1 --- /dev/null +++ b/target/linux/generic/backport-5.10/081-net-next-regmap-allow-to-define-reg_update_bits-for-no-bus.patch @@ -0,0 +1,52 @@ +From 02d6fdecb9c38de19065f6bed8d5214556fd061d Mon Sep 17 00:00:00 2001 +From: Ansuel Smith +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 +@@ -842,6 +842,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 +@@ -289,6 +289,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). +@@ -366,6 +371,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-5.10/350-v5.12-NFSv4_2-SSC-helper-should-use-its-own-config.patch b/target/linux/generic/backport-5.10/350-v5.12-NFSv4_2-SSC-helper-should-use-its-own-config.patch deleted file mode 100644 index 4639fdbc3..000000000 --- a/target/linux/generic/backport-5.10/350-v5.12-NFSv4_2-SSC-helper-should-use-its-own-config.patch +++ /dev/null @@ -1,152 +0,0 @@ -From 02591f9febd5f69bb4c266a4abf899c4cf21964f Mon Sep 17 00:00:00 2001 -From: Dai Ngo -Date: Thu, 28 Jan 2021 01:42:26 -0500 -Subject: [PATCH] NFSv4_2: SSC helper should use its own config. - -Currently NFSv4_2 SSC helper, nfs_ssc, incorrectly uses GRACE_PERIOD -as its config. Fix by adding new config NFS_V4_2_SSC_HELPER which -depends on NFS_V4_2 and is automatically selected when NFSD_V4 is -enabled. Also removed the file name from a comment in nfs_ssc.c. - -Signed-off-by: Dai Ngo -Signed-off-by: Chuck Lever ---- - fs/Kconfig | 4 ++++ - fs/nfs/nfs4file.c | 4 ++++ - fs/nfs/super.c | 12 ++++++++++++ - fs/nfs_common/Makefile | 2 +- - fs/nfs_common/nfs_ssc.c | 2 -- - fs/nfsd/Kconfig | 1 + - 6 files changed, 22 insertions(+), 3 deletions(-) - -diff --git a/fs/Kconfig b/fs/Kconfig -index aa4c122823018b..a55bda4233bbea 100644 ---- a/fs/Kconfig -+++ b/fs/Kconfig -@@ -333,6 +333,10 @@ config NFS_COMMON - depends on NFSD || NFS_FS || LOCKD - default y - -+config NFS_V4_2_SSC_HELPER -+ tristate -+ default y if NFS_V4=y || NFS_FS=y -+ - source "net/sunrpc/Kconfig" - source "fs/ceph/Kconfig" - source "fs/cifs/Kconfig" -diff --git a/fs/nfs/nfs4file.c b/fs/nfs/nfs4file.c -index 57b3821d975a35..441a2fa073c8f2 100644 ---- a/fs/nfs/nfs4file.c -+++ b/fs/nfs/nfs4file.c -@@ -420,7 +420,9 @@ static const struct nfs4_ssc_client_ops nfs4_ssc_clnt_ops_tbl = { - */ - void nfs42_ssc_register_ops(void) - { -+#ifdef CONFIG_NFSD_V4 - nfs42_ssc_register(&nfs4_ssc_clnt_ops_tbl); -+#endif - } - - /** -@@ -431,7 +433,9 @@ void nfs42_ssc_register_ops(void) - */ - void nfs42_ssc_unregister_ops(void) - { -+#ifdef CONFIG_NFSD_V4 - nfs42_ssc_unregister(&nfs4_ssc_clnt_ops_tbl); -+#endif - } - #endif /* CONFIG_NFS_V4_2 */ - -diff --git a/fs/nfs/super.c b/fs/nfs/super.c -index 4034102010f055..c7a924580eec9c 100644 ---- a/fs/nfs/super.c -+++ b/fs/nfs/super.c -@@ -86,9 +86,11 @@ const struct super_operations nfs_sops = { - }; - EXPORT_SYMBOL_GPL(nfs_sops); - -+#ifdef CONFIG_NFS_V4_2 - static const struct nfs_ssc_client_ops nfs_ssc_clnt_ops_tbl = { - .sco_sb_deactive = nfs_sb_deactive, - }; -+#endif - - #if IS_ENABLED(CONFIG_NFS_V4) - static int __init register_nfs4_fs(void) -@@ -111,15 +113,21 @@ static void unregister_nfs4_fs(void) - } - #endif - -+#ifdef CONFIG_NFS_V4_2 - static void nfs_ssc_register_ops(void) - { -+#ifdef CONFIG_NFSD_V4 - nfs_ssc_register(&nfs_ssc_clnt_ops_tbl); -+#endif - } - - static void nfs_ssc_unregister_ops(void) - { -+#ifdef CONFIG_NFSD_V4 - nfs_ssc_unregister(&nfs_ssc_clnt_ops_tbl); -+#endif - } -+#endif /* CONFIG_NFS_V4_2 */ - - static struct shrinker acl_shrinker = { - .count_objects = nfs_access_cache_count, -@@ -148,7 +156,9 @@ int __init register_nfs_fs(void) - ret = register_shrinker(&acl_shrinker); - if (ret < 0) - goto error_3; -+#ifdef CONFIG_NFS_V4_2 - nfs_ssc_register_ops(); -+#endif - return 0; - error_3: - nfs_unregister_sysctl(); -@@ -168,7 +178,9 @@ void __exit unregister_nfs_fs(void) - unregister_shrinker(&acl_shrinker); - nfs_unregister_sysctl(); - unregister_nfs4_fs(); -+#ifdef CONFIG_NFS_V4_2 - nfs_ssc_unregister_ops(); -+#endif - unregister_filesystem(&nfs_fs_type); - } - -diff --git a/fs/nfs_common/Makefile b/fs/nfs_common/Makefile -index fa82f5aaa6d95b..119c75ab9fd08e 100644 ---- a/fs/nfs_common/Makefile -+++ b/fs/nfs_common/Makefile -@@ -7,4 +7,4 @@ obj-$(CONFIG_NFS_ACL_SUPPORT) += nfs_acl.o - nfs_acl-objs := nfsacl.o - - obj-$(CONFIG_GRACE_PERIOD) += grace.o --obj-$(CONFIG_GRACE_PERIOD) += nfs_ssc.o -+obj-$(CONFIG_NFS_V4_2_SSC_HELPER) += nfs_ssc.o -diff --git a/fs/nfs_common/nfs_ssc.c b/fs/nfs_common/nfs_ssc.c -index f43bbb3739134b..7c1509e968c817 100644 ---- a/fs/nfs_common/nfs_ssc.c -+++ b/fs/nfs_common/nfs_ssc.c -@@ -1,7 +1,5 @@ - // SPDX-License-Identifier: GPL-2.0-only - /* -- * fs/nfs_common/nfs_ssc_comm.c -- * - * Helper for knfsd's SSC to access ops in NFS client modules - * - * Author: Dai Ngo -diff --git a/fs/nfsd/Kconfig b/fs/nfsd/Kconfig -index dbbc583d627306..821e5913faee49 100644 ---- a/fs/nfsd/Kconfig -+++ b/fs/nfsd/Kconfig -@@ -77,6 +77,7 @@ config NFSD_V4 - select CRYPTO_MD5 - select CRYPTO_SHA256 - select GRACE_PERIOD -+ select NFS_V4_2_SSC_HELPER if NFS_V4_2 - help - This option enables support in your system's NFS server for - version 4 of the NFS protocol (RFC 3530). diff --git a/target/linux/generic/backport-5.10/351-v5.13-NFSv4_2-Remove-ifdef-CONFIG_NFSD-from-client-SSC.patch b/target/linux/generic/backport-5.10/351-v5.13-NFSv4_2-Remove-ifdef-CONFIG_NFSD-from-client-SSC.patch deleted file mode 100644 index 9762d165d..000000000 --- a/target/linux/generic/backport-5.10/351-v5.13-NFSv4_2-Remove-ifdef-CONFIG_NFSD-from-client-SSC.patch +++ /dev/null @@ -1,92 +0,0 @@ -From d9092b4bb2109502eb8972021a3f74febc931a63 Mon Sep 17 00:00:00 2001 -From: Dai Ngo -Date: Thu, 22 Apr 2021 03:37:49 -0400 -Subject: [PATCH] NFSv4.2: Remove ifdef CONFIG_NFSD from NFSv4.2 client SSC - code. - -The client SSC code should not depend on any of the CONFIG_NFSD config. -This patch removes all CONFIG_NFSD from NFSv4.2 client SSC code and -simplifies the config of CONFIG_NFS_V4_2_SSC_HELPER, NFSD_V4_2_INTER_SSC. - -Signed-off-by: Dai Ngo -Signed-off-by: Trond Myklebust ---- - fs/Kconfig | 4 ++-- - fs/nfs/nfs4file.c | 4 ---- - fs/nfs/super.c | 4 ---- - fs/nfsd/Kconfig | 2 +- - 4 files changed, 3 insertions(+), 11 deletions(-) - -diff --git a/fs/Kconfig b/fs/Kconfig -index a55bda4233bbea..afa585e62332bf 100644 ---- a/fs/Kconfig -+++ b/fs/Kconfig -@@ -334,8 +334,8 @@ config NFS_COMMON - default y - - config NFS_V4_2_SSC_HELPER -- tristate -- default y if NFS_V4=y || NFS_FS=y -+ bool -+ default y if NFS_V4_2 - - source "net/sunrpc/Kconfig" - source "fs/ceph/Kconfig" -diff --git a/fs/nfs/nfs4file.c b/fs/nfs/nfs4file.c -index 441a2fa073c8f2..57b3821d975a35 100644 ---- a/fs/nfs/nfs4file.c -+++ b/fs/nfs/nfs4file.c -@@ -420,9 +420,7 @@ static const struct nfs4_ssc_client_ops nfs4_ssc_clnt_ops_tbl = { - */ - void nfs42_ssc_register_ops(void) - { --#ifdef CONFIG_NFSD_V4 - nfs42_ssc_register(&nfs4_ssc_clnt_ops_tbl); --#endif - } - - /** -@@ -433,9 +431,7 @@ void nfs42_ssc_register_ops(void) - */ - void nfs42_ssc_unregister_ops(void) - { --#ifdef CONFIG_NFSD_V4 - nfs42_ssc_unregister(&nfs4_ssc_clnt_ops_tbl); --#endif - } - #endif /* CONFIG_NFS_V4_2 */ - -diff --git a/fs/nfs/super.c b/fs/nfs/super.c -index 13a650750f04a8..5bfcc25571879a 100644 ---- a/fs/nfs/super.c -+++ b/fs/nfs/super.c -@@ -116,16 +116,12 @@ static void unregister_nfs4_fs(void) - #ifdef CONFIG_NFS_V4_2 - static void nfs_ssc_register_ops(void) - { --#ifdef CONFIG_NFSD_V4 - nfs_ssc_register(&nfs_ssc_clnt_ops_tbl); --#endif - } - - static void nfs_ssc_unregister_ops(void) - { --#ifdef CONFIG_NFSD_V4 - nfs_ssc_unregister(&nfs_ssc_clnt_ops_tbl); --#endif - } - #endif /* CONFIG_NFS_V4_2 */ - -diff --git a/fs/nfsd/Kconfig b/fs/nfsd/Kconfig -index d6cff5fbe705b4..c5346196c46f7e 100644 ---- a/fs/nfsd/Kconfig -+++ b/fs/nfsd/Kconfig -@@ -138,7 +138,7 @@ config NFSD_FLEXFILELAYOUT - - config NFSD_V4_2_INTER_SSC - bool "NFSv4.2 inter server to server COPY" -- depends on NFSD_V4 && NFS_V4_1 && NFS_V4_2 -+ depends on NFSD_V4 && NFS_V4_2 - help - This option enables support for NFSv4.2 inter server to - server copy where the destination server calls the NFSv4.2 diff --git a/target/linux/generic/backport-5.10/409-v5.14-0001-dt-bindings-mtd-brcm-trx-Add-brcm-trx-magic.patch b/target/linux/generic/backport-5.10/409-v5.14-0001-dt-bindings-mtd-brcm-trx-Add-brcm-trx-magic.patch new file mode 100644 index 000000000..1f3465214 --- /dev/null +++ b/target/linux/generic/backport-5.10/409-v5.14-0001-dt-bindings-mtd-brcm-trx-Add-brcm-trx-magic.patch @@ -0,0 +1,32 @@ +From a4d82940ff85a7e307953dfa715f65d5ab487e10 Mon Sep 17 00:00:00 2001 +From: Hauke Mehrtens +Date: Sun, 18 Apr 2021 23:46:14 +0200 +Subject: dt-bindings: mtd: brcm,trx: Add brcm,trx-magic + +This adds the description of an additional property which allows to +specify a custom partition parser magic to detect a trx partition. +Buffalo has multiple device which are using the trx format, but with +different magic values. + +Signed-off-by: Hauke Mehrtens +Acked-by: Rob Herring +Signed-off-by: Miquel Raynal +Link: https://lore.kernel.org/linux-mtd/20210418214616.239574-2-hauke@hauke-m.de +--- + .../devicetree/bindings/mtd/partitions/brcm,trx.txt | 5 +++++ + 1 file changed, 5 insertions(+) + +--- a/Documentation/devicetree/bindings/mtd/partitions/brcm,trx.txt ++++ b/Documentation/devicetree/bindings/mtd/partitions/brcm,trx.txt +@@ -28,6 +28,11 @@ detected by a software parsing TRX heade + Required properties: + - compatible : (required) must be "brcm,trx" + ++Optional properties: ++ ++- brcm,trx-magic: TRX magic, if it is different from the default magic ++ 0x30524448 as a u32. ++ + Example: + + flash@0 { diff --git a/target/linux/generic/backport-5.10/409-v5.14-0002-mtd-parsers-trx-Allow-to-specify-brcm-trx-magic-in-D.patch b/target/linux/generic/backport-5.10/409-v5.14-0002-mtd-parsers-trx-Allow-to-specify-brcm-trx-magic-in-D.patch new file mode 100644 index 000000000..de2d91485 --- /dev/null +++ b/target/linux/generic/backport-5.10/409-v5.14-0002-mtd-parsers-trx-Allow-to-specify-brcm-trx-magic-in-D.patch @@ -0,0 +1,50 @@ +From d7f7e04f8b67571a4bf5a0dcd4f9da4214f5262c Mon Sep 17 00:00:00 2001 +From: Hauke Mehrtens +Date: Sun, 18 Apr 2021 23:46:15 +0200 +Subject: mtd: parsers: trx: Allow to specify brcm, trx-magic in DT + +Buffalo uses a different TRX magic for every device, to be able to use +this trx parser, make it possible to specify the TRX magic in device +tree. If no TRX magic is specified in device tree, the standard value +will be used. This value should only be specified if a vendor chooses to +use a non standard TRX magic. + +Signed-off-by: Hauke Mehrtens +Signed-off-by: Miquel Raynal +Link: https://lore.kernel.org/linux-mtd/20210418214616.239574-3-hauke@hauke-m.de +--- + drivers/mtd/parsers/parser_trx.c | 9 ++++++++- + 1 file changed, 8 insertions(+), 1 deletion(-) + +--- a/drivers/mtd/parsers/parser_trx.c ++++ b/drivers/mtd/parsers/parser_trx.c +@@ -51,13 +51,20 @@ static int parser_trx_parse(struct mtd_i + const struct mtd_partition **pparts, + struct mtd_part_parser_data *data) + { ++ struct device_node *np = mtd_get_of_node(mtd); + struct mtd_partition *parts; + struct mtd_partition *part; + struct trx_header trx; + size_t bytes_read; + uint8_t curr_part = 0, i = 0; ++ uint32_t trx_magic = TRX_MAGIC; + int err; + ++ /* Get different magic from device tree if specified */ ++ err = of_property_read_u32(np, "brcm,trx-magic", &trx_magic); ++ if (err != 0 && err != -EINVAL) ++ pr_err("failed to parse \"brcm,trx-magic\" DT attribute, using default: %d\n", err); ++ + parts = kcalloc(TRX_PARSER_MAX_PARTS, sizeof(struct mtd_partition), + GFP_KERNEL); + if (!parts) +@@ -70,7 +77,7 @@ static int parser_trx_parse(struct mtd_i + return err; + } + +- if (trx.magic != TRX_MAGIC) { ++ if (trx.magic != trx_magic) { + kfree(parts); + return -ENOENT; + } diff --git a/target/linux/generic/backport-5.10/409-v5.14-0003-mtd-parsers-trx-Allow-to-use-TRX-parser-on-Mediatek-.patch b/target/linux/generic/backport-5.10/409-v5.14-0003-mtd-parsers-trx-Allow-to-use-TRX-parser-on-Mediatek-.patch new file mode 100644 index 000000000..faac53527 --- /dev/null +++ b/target/linux/generic/backport-5.10/409-v5.14-0003-mtd-parsers-trx-Allow-to-use-TRX-parser-on-Mediatek-.patch @@ -0,0 +1,25 @@ +From 81bb218c829246962a6327c64eec18ddcc049936 Mon Sep 17 00:00:00 2001 +From: Hauke Mehrtens +Date: Sun, 18 Apr 2021 23:46:16 +0200 +Subject: mtd: parsers: trx: Allow to use TRX parser on Mediatek SoCs + +Buffalo uses the TRX partition format also on Mediatek MT7622 SoCs. + +Signed-off-by: Hauke Mehrtens +Signed-off-by: Miquel Raynal +Link: https://lore.kernel.org/linux-mtd/20210418214616.239574-4-hauke@hauke-m.de +--- + 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 || COMPILE_TEST) ++ depends on MTD && (BCM47XX || ARCH_BCM_5301X || ARCH_MEDIATEK || 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-5.10/410-mtd-next-mtd-parsers-trx-allow-to-use-on-MediaTek-MIPS-SoCs.patch b/target/linux/generic/backport-5.10/410-mtd-next-mtd-parsers-trx-allow-to-use-on-MediaTek-MIPS-SoCs.patch new file mode 100644 index 000000000..5c4984176 --- /dev/null +++ b/target/linux/generic/backport-5.10/410-mtd-next-mtd-parsers-trx-allow-to-use-on-MediaTek-MIPS-SoCs.patch @@ -0,0 +1,33 @@ +From 2365f91c861cbfeef7141c69842848c7b2d3c2db Mon Sep 17 00:00:00 2001 +From: INAGAKI Hiroshi +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-5.10/419-v5.14-mtd-spinand-gigadevice-Support-GD5F1GQ5UExxG.patch b/target/linux/generic/backport-5.10/419-v5.14-mtd-spinand-gigadevice-Support-GD5F1GQ5UExxG.patch new file mode 100644 index 000000000..00c43879f --- /dev/null +++ b/target/linux/generic/backport-5.10/419-v5.14-mtd-spinand-gigadevice-Support-GD5F1GQ5UExxG.patch @@ -0,0 +1,172 @@ +From bd568cc04c675b7fa97214d278a54794c2ecc2ad Mon Sep 17 00:00:00 2001 +From: Reto Schneider +Date: Thu, 11 Feb 2021 12:36:19 +0100 +Subject: [PATCH] mtd: spinand: gigadevice: Support GD5F1GQ5UExxG + +The relevant changes to the already existing GD5F1GQ4UExxG support has +been determined by consulting the GigaDevice product change notice +AN-0392-10, version 1.0 from November 30, 2020. + +As the overlaps are huge, variable names have been generalized +accordingly. + +Apart from the lowered ECC strength (4 instead of 8 bits per 512 bytes), +the new device ID, and the extra quad IO dummy byte, no changes had to +be taken into account. + +New hardware features are not supported, namely: + - Power on reset + - Unique ID + - Double transfer rate (DTR) + - Parameter page + - Random data quad IO + +The inverted semantic of the "driver strength" register bits, defaulting +to 100% instead of 50% for the Q5 devices, got ignored as the driver has +never touched them anyway. + +The no longer supported "read from cache during block erase" +functionality is not reflected as the current SPI NAND core does not +support it anyway. + +Implementation has been tested on MediaTek MT7688 based GARDENA smart +Gateways using both, GigaDevice GD5F1GQ5UEYIG and GD5F1GQ4UBYIG. + +Signed-off-by: Reto Schneider +Reviewed-by: Frieder Schrempf +Reviewed-by: Stefan Roese +Signed-off-by: Miquel Raynal +Link: https://lore.kernel.org/linux-mtd/20210211113619.3502-1-code@reto-schneider.ch +(cherry picked from commit 469b992489852b500d39048aa0013639dfe9f2e6) +--- + drivers/mtd/nand/spi/gigadevice.c | 69 +++++++++++++++++++++++++++---- + 1 file changed, 60 insertions(+), 9 deletions(-) + +--- a/drivers/mtd/nand/spi/gigadevice.c ++++ b/drivers/mtd/nand/spi/gigadevice.c +@@ -13,7 +13,10 @@ + #define GD5FXGQ4XA_STATUS_ECC_1_7_BITFLIPS (1 << 4) + #define GD5FXGQ4XA_STATUS_ECC_8_BITFLIPS (3 << 4) + +-#define GD5FXGQ4UEXXG_REG_STATUS2 0xf0 ++#define GD5FXGQ5XE_STATUS_ECC_1_4_BITFLIPS (1 << 4) ++#define GD5FXGQ5XE_STATUS_ECC_4_BITFLIPS (3 << 4) ++ ++#define GD5FXGQXXEXXG_REG_STATUS2 0xf0 + + #define GD5FXGQ4UXFXXG_STATUS_ECC_MASK (7 << 4) + #define GD5FXGQ4UXFXXG_STATUS_ECC_NO_BITFLIPS (0 << 4) +@@ -102,7 +105,7 @@ static int gd5fxgq4xa_ecc_get_status(str + return -EINVAL; + } + +-static int gd5fxgq4_variant2_ooblayout_ecc(struct mtd_info *mtd, int section, ++static int gd5fxgqx_variant2_ooblayout_ecc(struct mtd_info *mtd, int section, + struct mtd_oob_region *region) + { + if (section) +@@ -114,7 +117,7 @@ static int gd5fxgq4_variant2_ooblayout_e + return 0; + } + +-static int gd5fxgq4_variant2_ooblayout_free(struct mtd_info *mtd, int section, ++static int gd5fxgqx_variant2_ooblayout_free(struct mtd_info *mtd, int section, + struct mtd_oob_region *region) + { + if (section) +@@ -127,9 +130,10 @@ static int gd5fxgq4_variant2_ooblayout_f + return 0; + } + +-static const struct mtd_ooblayout_ops gd5fxgq4_variant2_ooblayout = { +- .ecc = gd5fxgq4_variant2_ooblayout_ecc, +- .free = gd5fxgq4_variant2_ooblayout_free, ++/* Valid for Q4/Q5 and Q6 (untested) devices */ ++static const struct mtd_ooblayout_ops gd5fxgqx_variant2_ooblayout = { ++ .ecc = gd5fxgqx_variant2_ooblayout_ecc, ++ .free = gd5fxgqx_variant2_ooblayout_free, + }; + + static int gd5fxgq4xc_ooblayout_256_ecc(struct mtd_info *mtd, int section, +@@ -165,7 +169,7 @@ static int gd5fxgq4uexxg_ecc_get_status( + u8 status) + { + u8 status2; +- struct spi_mem_op op = SPINAND_GET_FEATURE_OP(GD5FXGQ4UEXXG_REG_STATUS2, ++ struct spi_mem_op op = SPINAND_GET_FEATURE_OP(GD5FXGQXXEXXG_REG_STATUS2, + &status2); + int ret; + +@@ -203,6 +207,43 @@ static int gd5fxgq4uexxg_ecc_get_status( + return -EINVAL; + } + ++static int gd5fxgq5xexxg_ecc_get_status(struct spinand_device *spinand, ++ u8 status) ++{ ++ u8 status2; ++ struct spi_mem_op op = SPINAND_GET_FEATURE_OP(GD5FXGQXXEXXG_REG_STATUS2, ++ &status2); ++ int ret; ++ ++ switch (status & STATUS_ECC_MASK) { ++ case STATUS_ECC_NO_BITFLIPS: ++ return 0; ++ ++ case GD5FXGQ5XE_STATUS_ECC_1_4_BITFLIPS: ++ /* ++ * Read status2 register to determine a more fine grained ++ * bit error status ++ */ ++ ret = spi_mem_exec_op(spinand->spimem, &op); ++ if (ret) ++ return ret; ++ ++ /* ++ * 1 ... 4 bits are flipped (and corrected) ++ */ ++ /* bits sorted this way (1...0): ECCSE1, ECCSE0 */ ++ return ((status2 & STATUS_ECC_MASK) >> 4) + 1; ++ ++ case STATUS_ECC_UNCOR_ERROR: ++ return -EBADMSG; ++ ++ default: ++ break; ++ } ++ ++ return -EINVAL; ++} ++ + static int gd5fxgq4ufxxg_ecc_get_status(struct spinand_device *spinand, + u8 status) + { +@@ -282,7 +323,7 @@ static const struct spinand_info gigadev + &write_cache_variants, + &update_cache_variants), + SPINAND_HAS_QE_BIT, +- SPINAND_ECCINFO(&gd5fxgq4_variant2_ooblayout, ++ SPINAND_ECCINFO(&gd5fxgqx_variant2_ooblayout, + gd5fxgq4uexxg_ecc_get_status)), + SPINAND_INFO("GD5F1GQ4UFxxG", + SPINAND_ID(SPINAND_READID_METHOD_OPCODE, 0xb1, 0x48), +@@ -292,8 +333,18 @@ static const struct spinand_info gigadev + &write_cache_variants, + &update_cache_variants), + SPINAND_HAS_QE_BIT, +- SPINAND_ECCINFO(&gd5fxgq4_variant2_ooblayout, ++ SPINAND_ECCINFO(&gd5fxgqx_variant2_ooblayout, + gd5fxgq4ufxxg_ecc_get_status)), ++ SPINAND_INFO("GD5F1GQ5UExxG", ++ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x51), ++ NAND_MEMORG(1, 2048, 128, 64, 1024, 20, 1, 1, 1), ++ NAND_ECCREQ(4, 512), ++ SPINAND_INFO_OP_VARIANTS(&read_cache_variants, ++ &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-5.10/420-v5.19-01-mtd-spinand-gigadevice-fix-Quad-IO-for-GD5F1GQ5UExxG.patch b/target/linux/generic/backport-5.10/420-v5.19-01-mtd-spinand-gigadevice-fix-Quad-IO-for-GD5F1GQ5UExxG.patch new file mode 100644 index 000000000..1b0b57c2b --- /dev/null +++ b/target/linux/generic/backport-5.10/420-v5.19-01-mtd-spinand-gigadevice-fix-Quad-IO-for-GD5F1GQ5UExxG.patch @@ -0,0 +1,44 @@ +From a4f9dd55c5e1bb951db6f1dee20e62e0103f3438 Mon Sep 17 00:00:00 2001 +From: Chuanhong Guo +Date: Sun, 20 Mar 2022 17:59:57 +0800 +Subject: [PATCH 1/5] mtd: spinand: gigadevice: fix Quad IO for GD5F1GQ5UExxG + +Read From Cache Quad IO (EBH) uses 2 dummy bytes on this chip according +to page 23 of the datasheet[0]. + +[0]: https://www.gigadevice.com/datasheet/gd5f1gq5xexxg/ + +Fixes: 469b99248985 ("mtd: spinand: gigadevice: Support GD5F1GQ5UExxG") +Signed-off-by: Chuanhong Guo +Signed-off-by: Miquel Raynal +Link: https://lore.kernel.org/linux-mtd/20220320100001.247905-2-gch981213@gmail.com +--- + drivers/mtd/nand/spi/gigadevice.c | 10 +++++++++- + 1 file changed, 9 insertions(+), 1 deletion(-) + +--- a/drivers/mtd/nand/spi/gigadevice.c ++++ b/drivers/mtd/nand/spi/gigadevice.c +@@ -39,6 +39,14 @@ static SPINAND_OP_VARIANTS(read_cache_va + SPINAND_PAGE_READ_FROM_CACHE_OP_3A(true, 0, 1, NULL, 0), + SPINAND_PAGE_READ_FROM_CACHE_OP_3A(false, 0, 0, NULL, 0)); + ++static SPINAND_OP_VARIANTS(read_cache_variants_1gq5, ++ 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)); +@@ -339,7 +347,7 @@ static const struct spinand_info gigadev + SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x51), + NAND_MEMORG(1, 2048, 128, 64, 1024, 20, 1, 1, 1), + NAND_ECCREQ(4, 512), +- SPINAND_INFO_OP_VARIANTS(&read_cache_variants, ++ SPINAND_INFO_OP_VARIANTS(&read_cache_variants_1gq5, + &write_cache_variants, + &update_cache_variants), + SPINAND_HAS_QE_BIT, diff --git a/target/linux/generic/backport-5.10/420-v5.19-02-mtd-spinand-gigadevice-add-support-for-GD5FxGQ4xExxG.patch b/target/linux/generic/backport-5.10/420-v5.19-02-mtd-spinand-gigadevice-add-support-for-GD5FxGQ4xExxG.patch new file mode 100644 index 000000000..181c912fb --- /dev/null +++ b/target/linux/generic/backport-5.10/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-5.10/420-v5.19-03-mtd-spinand-gigadevice-add-support-for-GD5F1GQ5RExxG.patch b/target/linux/generic/backport-5.10/420-v5.19-03-mtd-spinand-gigadevice-add-support-for-GD5F1GQ5RExxG.patch new file mode 100644 index 000000000..3a1cc9efc --- /dev/null +++ b/target/linux/generic/backport-5.10/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-5.10/420-v5.19-04-mtd-spinand-gigadevice-add-support-for-GD5F-2-4-GQ5x.patch b/target/linux/generic/backport-5.10/420-v5.19-04-mtd-spinand-gigadevice-add-support-for-GD5F-2-4-GQ5x.patch new file mode 100644 index 000000000..cee9d9db3 --- /dev/null +++ b/target/linux/generic/backport-5.10/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-5.10/420-v5.19-05-mtd-spinand-gigadevice-add-support-for-GD5FxGM7xExxG.patch b/target/linux/generic/backport-5.10/420-v5.19-05-mtd-spinand-gigadevice-add-support-for-GD5FxGM7xExxG.patch new file mode 100644 index 000000000..d63113e1a --- /dev/null +++ b/target/linux/generic/backport-5.10/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-5.10/610-v5.13-54-net-ethernet-mtk_eth_soc-add-ipv6-flow-offloading-support.patch b/target/linux/generic/backport-5.10/610-v5.13-54-net-ethernet-mtk_eth_soc-add-ipv6-flow-offloading-support.patch deleted file mode 100644 index 33859b6cc..000000000 --- a/target/linux/generic/backport-5.10/610-v5.13-54-net-ethernet-mtk_eth_soc-add-ipv6-flow-offloading-support.patch +++ /dev/null @@ -1,105 +0,0 @@ ---- a/drivers/net/ethernet/mediatek/mtk_ppe_offload.c -+++ b/drivers/net/ethernet/mediatek/mtk_ppe_offload.c -@@ -7,6 +7,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; -@@ -64,6 +70,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) - { -@@ -128,6 +142,29 @@ mtk_flow_mangle_ipv4(const struct flow_a - } - - static int -+mtk_flow_mangle_ipv6(const struct flow_action_entry *act, -+ struct mtk_flow_data *data) -+{ -+ __be32 *dest; -+ size_t offset_of_ip6_daddr = offsetof(struct ipv6hdr, daddr); -+ size_t offset_of_ip6_saddr = offsetof(struct ipv6hdr, saddr); -+ u32 idx; -+ -+ if (act->mangle.offset >= offset_of_ip6_daddr && act->mangle.offset < offset_of_ip6_daddr) { -+ idx = (act->mangle.offset - offset_of_ip6_saddr) / 4; -+ dest = &data->v6.src_addr.s6_addr32[idx]; -+ } else if (act->mangle.offset >= offset_of_ip6_daddr && -+ act->mangle.offset < offset_of_ip6_daddr + 16) { -+ idx = (act->mangle.offset - offset_of_ip6_daddr) / 4; -+ dest = &data->v6.dst_addr.s6_addr32[idx]; -+ } -+ -+ memcpy(dest, &act->mangle.val, sizeof(u32)); -+ -+ return 0; -+} -+ -+static int - mtk_flow_get_dsa_port(struct net_device **dev) - { - #if IS_ENABLED(CONFIG_NET_DSA) -@@ -251,6 +288,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; - } -@@ -286,6 +326,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; -@@ -289,6 +349,9 @@ mtk_flow_offload_replace(struct mtk_eth - case FLOW_ACT_MANGLE_HDR_TYPE_IP4: - err = mtk_flow_mangle_ipv4(act, &data); - break; -+ case FLOW_ACT_MANGLE_HDR_TYPE_IP6: -+ err = mtk_flow_mangle_ipv6(act, &data); -+ break; - case FLOW_ACT_MANGLE_HDR_TYPE_ETH: - /* handled earlier */ - break; diff --git a/target/linux/generic/backport-5.10/610-v5.13-55-netfilter-conntrack-Introduce-tcp-offload-timeout-co.patch b/target/linux/generic/backport-5.10/610-v5.13-55-netfilter-conntrack-Introduce-tcp-offload-timeout-co.patch index e5a346cac..abc58d2a7 100644 --- a/target/linux/generic/backport-5.10/610-v5.13-55-netfilter-conntrack-Introduce-tcp-offload-timeout-co.patch +++ b/target/linux/generic/backport-5.10/610-v5.13-55-netfilter-conntrack-Introduce-tcp-offload-timeout-co.patch @@ -33,7 +33,7 @@ Signed-off-by: Pablo Neira Ayuso enum udp_conntrack { --- a/net/netfilter/nf_conntrack_proto_tcp.c +++ b/net/netfilter/nf_conntrack_proto_tcp.c -@@ -1438,6 +1438,11 @@ void nf_conntrack_tcp_init_net(struct ne +@@ -1447,6 +1447,11 @@ void nf_conntrack_tcp_init_net(struct ne tn->tcp_loose = nf_ct_tcp_loose; tn->tcp_be_liberal = nf_ct_tcp_be_liberal; tn->tcp_max_retrans = nf_ct_tcp_max_retrans; diff --git a/target/linux/generic/backport-5.10/611-v5.12-net-ethernet-mediatek-support-setting-MTU.patch b/target/linux/generic/backport-5.10/611-v5.12-net-ethernet-mediatek-support-setting-MTU.patch new file mode 100644 index 000000000..289d140f3 --- /dev/null +++ b/target/linux/generic/backport-5.10/611-v5.12-net-ethernet-mediatek-support-setting-MTU.patch @@ -0,0 +1,138 @@ +From 4fd59792097a6b2fb949d41264386a7ecade469e Mon Sep 17 00:00:00 2001 +From: DENG Qingfang +Date: Mon, 25 Jan 2021 12:20:46 +0800 +Subject: [PATCH] net: ethernet: mediatek: support setting MTU + +MT762x HW, except for MT7628, supports frame length up to 2048 +(maximum length on GDM), so allow setting MTU up to 2030. + +Also set the default frame length to the hardware default 1518. + +Signed-off-by: DENG Qingfang +Reviewed-by: Andrew Lunn +Link: https://lore.kernel.org/r/20210125042046.5599-1-dqfext@gmail.com +Signed-off-by: Jakub Kicinski +--- + drivers/net/ethernet/mediatek/mtk_eth_soc.c | 43 ++++++++++++++++++--- + drivers/net/ethernet/mediatek/mtk_eth_soc.h | 12 ++++-- + 2 files changed, 47 insertions(+), 8 deletions(-) + +--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c ++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c +@@ -355,7 +355,7 @@ static void mtk_mac_config(struct phylin + /* Setup gmac */ + mcr_cur = mtk_r32(mac->hw, MTK_MAC_MCR(mac->id)); + mcr_new = mcr_cur; +- mcr_new |= MAC_MCR_MAX_RX_1536 | MAC_MCR_IPG_CFG | MAC_MCR_FORCE_MODE | ++ 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! */ +@@ -782,8 +782,8 @@ static void mtk_get_stats64(struct net_d + static inline int mtk_max_frag_size(int mtu) + { + /* make sure buf_size will be at least MTK_MAX_RX_LENGTH */ +- if (mtu + MTK_RX_ETH_HLEN < MTK_MAX_RX_LENGTH) +- mtu = MTK_MAX_RX_LENGTH - MTK_RX_ETH_HLEN; ++ if (mtu + MTK_RX_ETH_HLEN < MTK_MAX_RX_LENGTH_2K) ++ mtu = MTK_MAX_RX_LENGTH_2K - MTK_RX_ETH_HLEN; + + return SKB_DATA_ALIGN(MTK_RX_HLEN + mtu) + + SKB_DATA_ALIGN(sizeof(struct skb_shared_info)); +@@ -794,7 +794,7 @@ static inline int mtk_max_buf_size(int f + int buf_size = frag_size - NET_SKB_PAD - NET_IP_ALIGN - + SKB_DATA_ALIGN(sizeof(struct skb_shared_info)); + +- WARN_ON(buf_size < MTK_MAX_RX_LENGTH); ++ WARN_ON(buf_size < MTK_MAX_RX_LENGTH_2K); + + return buf_size; + } +@@ -2606,6 +2606,35 @@ static void mtk_uninit(struct net_device + mtk_rx_irq_disable(eth, ~0); + } + ++static int mtk_change_mtu(struct net_device *dev, int new_mtu) ++{ ++ int length = new_mtu + MTK_RX_ETH_HLEN; ++ struct mtk_mac *mac = netdev_priv(dev); ++ struct mtk_eth *eth = mac->hw; ++ u32 mcr_cur, mcr_new; ++ ++ 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; ++ ++ if (length <= 1518) ++ mcr_new |= MAC_MCR_MAX_RX(MAC_MCR_MAX_RX_1518); ++ else if (length <= 1536) ++ mcr_new |= MAC_MCR_MAX_RX(MAC_MCR_MAX_RX_1536); ++ else if (length <= 1552) ++ mcr_new |= MAC_MCR_MAX_RX(MAC_MCR_MAX_RX_1552); ++ else ++ mcr_new |= MAC_MCR_MAX_RX(MAC_MCR_MAX_RX_2048); ++ ++ if (mcr_new != mcr_cur) ++ mtk_w32(mac->hw, mcr_new, MTK_MAC_MCR(mac->id)); ++ } ++ ++ dev->mtu = new_mtu; ++ ++ return 0; ++} ++ + static int mtk_do_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) + { + struct mtk_mac *mac = netdev_priv(dev); +@@ -2902,6 +2931,7 @@ static const struct net_device_ops mtk_n + .ndo_set_mac_address = mtk_set_mac_address, + .ndo_validate_addr = eth_validate_addr, + .ndo_do_ioctl = mtk_do_ioctl, ++ .ndo_change_mtu = mtk_change_mtu, + .ndo_tx_timeout = mtk_tx_timeout, + .ndo_get_stats64 = mtk_get_stats64, + .ndo_fix_features = mtk_fix_features, +@@ -3004,7 +3034,10 @@ static int mtk_add_mac(struct mtk_eth *e + eth->netdev[id]->irq = eth->irq[0]; + eth->netdev[id]->dev.of_node = np; + +- eth->netdev[id]->max_mtu = MTK_MAX_RX_LENGTH - MTK_RX_ETH_HLEN; ++ if (MTK_HAS_CAPS(eth->soc->caps, MTK_SOC_MT7628)) ++ eth->netdev[id]->max_mtu = MTK_MAX_RX_LENGTH - MTK_RX_ETH_HLEN; ++ else ++ eth->netdev[id]->max_mtu = MTK_MAX_RX_LENGTH_2K - MTK_RX_ETH_HLEN; + + return 0; + +--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h ++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h +@@ -20,12 +20,13 @@ + #include "mtk_ppe.h" + + #define MTK_QDMA_PAGE_SIZE 2048 +-#define MTK_MAX_RX_LENGTH 1536 ++#define MTK_MAX_RX_LENGTH 1536 ++#define MTK_MAX_RX_LENGTH_2K 2048 + #define MTK_TX_DMA_BUF_LEN 0x3fff + #define MTK_DMA_SIZE 512 + #define MTK_NAPI_WEIGHT 64 + #define MTK_MAC_COUNT 2 +-#define MTK_RX_ETH_HLEN (VLAN_ETH_HLEN + VLAN_HLEN + ETH_FCS_LEN) ++#define MTK_RX_ETH_HLEN (ETH_HLEN + ETH_FCS_LEN) + #define MTK_RX_HLEN (NET_SKB_PAD + MTK_RX_ETH_HLEN + NET_IP_ALIGN) + #define MTK_DMA_DUMMY_DESC 0xffffffff + #define MTK_DEFAULT_MSG_ENABLE (NETIF_MSG_DRV | \ +@@ -352,7 +353,12 @@ + + /* Mac control registers */ + #define MTK_MAC_MCR(x) (0x10100 + (x * 0x100)) +-#define MAC_MCR_MAX_RX_1536 BIT(24) ++#define MAC_MCR_MAX_RX_MASK GENMASK(25, 24) ++#define MAC_MCR_MAX_RX(_x) (MAC_MCR_MAX_RX_MASK & ((_x) << 24)) ++#define MAC_MCR_MAX_RX_1518 0x0 ++#define MAC_MCR_MAX_RX_1536 0x1 ++#define MAC_MCR_MAX_RX_1552 0x2 ++#define MAC_MCR_MAX_RX_2048 0x3 + #define MAC_MCR_IPG_CFG (BIT(18) | BIT(16)) + #define MAC_MCR_FORCE_MODE BIT(15) + #define MAC_MCR_TX_EN BIT(14) diff --git a/target/linux/generic/backport-5.10/612-v5.15-netfilter-conntrack-sanitize-table-size-default-sett.patch b/target/linux/generic/backport-5.10/612-v5.15-netfilter-conntrack-sanitize-table-size-default-sett.patch new file mode 100644 index 000000000..55bf0f612 --- /dev/null +++ b/target/linux/generic/backport-5.10/612-v5.15-netfilter-conntrack-sanitize-table-size-default-sett.patch @@ -0,0 +1,100 @@ +From d532bcd0b2699d84d71a0c71d37157ac6eb3be25 Mon Sep 17 00:00:00 2001 +Message-Id: +From: Florian Westphal +Date: Thu, 26 Aug 2021 15:54:19 +0200 +Subject: [PATCH] netfilter: conntrack: sanitize table size default settings + +conntrack has two distinct table size settings: +nf_conntrack_max and nf_conntrack_buckets. + +The former limits how many conntrack objects are allowed to exist +in each namespace. + +The second sets the size of the hashtable. + +As all entries are inserted twice (once for original direction, once for +reply), there should be at least twice as many buckets in the table than +the maximum number of conntrack objects that can exist at the same time. + +Change the default multiplier to 1 and increase the chosen bucket sizes. +This results in the same nf_conntrack_max settings as before but reduces +the average bucket list length. + +Signed-off-by: Florian Westphal +Signed-off-by: Pablo Neira Ayuso +--- + .../networking/nf_conntrack-sysctl.rst | 13 ++++---- + net/netfilter/nf_conntrack_core.c | 30 +++++++++---------- + 2 files changed, 22 insertions(+), 21 deletions(-) + +--- a/Documentation/networking/nf_conntrack-sysctl.rst ++++ b/Documentation/networking/nf_conntrack-sysctl.rst +@@ -17,9 +17,8 @@ nf_conntrack_acct - BOOLEAN + nf_conntrack_buckets - INTEGER + Size of hash table. If not specified as parameter during module + loading, the default size is calculated by dividing total memory +- by 16384 to determine the number of buckets but the hash table will +- never have fewer than 32 and limited to 16384 buckets. For systems +- with more than 4GB of memory it will be 65536 buckets. ++ by 16384 to determine the number of buckets. The hash table will ++ never have fewer than 1024 and never more than 262144 buckets. + This sysctl is only writeable in the initial net namespace. + + nf_conntrack_checksum - BOOLEAN +@@ -100,8 +99,12 @@ nf_conntrack_log_invalid - INTEGER + Log invalid packets of a type specified by value. + + nf_conntrack_max - INTEGER +- Size of connection tracking table. Default value is +- nf_conntrack_buckets value * 4. ++ Maximum number of allowed connection tracking entries. This value is set ++ to nf_conntrack_buckets by default. ++ Note that connection tracking entries are added to the table twice -- once ++ for the original direction and once for the reply direction (i.e., with ++ the reversed address). This means that with default settings a maxed-out ++ table will have a average hash chain length of 2, not 1. + + nf_conntrack_tcp_be_liberal - BOOLEAN + - 0 - disabled (default) +--- a/net/netfilter/nf_conntrack_core.c ++++ b/net/netfilter/nf_conntrack_core.c +@@ -2575,26 +2575,24 @@ int nf_conntrack_init_start(void) + spin_lock_init(&nf_conntrack_locks[i]); + + if (!nf_conntrack_htable_size) { +- /* Idea from tcp.c: use 1/16384 of memory. +- * On i386: 32MB machine has 512 buckets. +- * >= 1GB machines have 16384 buckets. +- * >= 4GB machines have 65536 buckets. +- */ + nf_conntrack_htable_size + = (((nr_pages << PAGE_SHIFT) / 16384) + / sizeof(struct hlist_head)); +- if (nr_pages > (4 * (1024 * 1024 * 1024 / PAGE_SIZE))) +- nf_conntrack_htable_size = 65536; ++ if (BITS_PER_LONG >= 64 && ++ nr_pages > (4 * (1024 * 1024 * 1024 / PAGE_SIZE))) ++ nf_conntrack_htable_size = 262144; + else if (nr_pages > (1024 * 1024 * 1024 / PAGE_SIZE)) +- nf_conntrack_htable_size = 16384; +- if (nf_conntrack_htable_size < 32) +- nf_conntrack_htable_size = 32; ++ nf_conntrack_htable_size = 65536; + +- /* Use a max. factor of four by default to get the same max as +- * with the old struct list_heads. When a table size is given +- * we use the old value of 8 to avoid reducing the max. +- * entries. */ +- max_factor = 4; ++ if (nf_conntrack_htable_size < 1024) ++ nf_conntrack_htable_size = 1024; ++ /* Use a max. factor of one by default to keep the average ++ * hash chain length at 2 entries. Each entry has to be added ++ * twice (once for original direction, once for reply). ++ * When a table size is given we use the old value of 8 to ++ * avoid implicit reduction of the max entries setting. ++ */ ++ max_factor = 1; + } + + nf_conntrack_hash = nf_ct_alloc_hashtable(&nf_conntrack_htable_size, 1); diff --git a/target/linux/generic/backport-5.10/730-net-phy-at803x-select-correct-page-on-config-init.patch b/target/linux/generic/backport-5.10/705-net-phy-at803x-select-correct-page-on-config-init.patch similarity index 100% rename from target/linux/generic/backport-5.10/730-net-phy-at803x-select-correct-page-on-config-init.patch rename to target/linux/generic/backport-5.10/705-net-phy-at803x-select-correct-page-on-config-init.patch diff --git a/target/linux/generic/backport-5.10/731-net-phy-at803x-fix-probe-error-if-copper-page-is-sel.patch b/target/linux/generic/backport-5.10/706-net-phy-at803x-fix-probe-error-if-copper-page-is-sel.patch similarity index 100% rename from target/linux/generic/backport-5.10/731-net-phy-at803x-fix-probe-error-if-copper-page-is-sel.patch rename to target/linux/generic/backport-5.10/706-net-phy-at803x-fix-probe-error-if-copper-page-is-sel.patch diff --git a/target/linux/generic/backport-5.10/710-v5.12-net-phy-Add-100-base-x-mode.patch b/target/linux/generic/backport-5.10/710-v5.12-net-phy-Add-100-base-x-mode.patch new file mode 100644 index 000000000..5c7f97ea9 --- /dev/null +++ b/target/linux/generic/backport-5.10/710-v5.12-net-phy-Add-100-base-x-mode.patch @@ -0,0 +1,56 @@ +From b1ae3587d16a8c8fc9453e147c8708d6f006ffbb Mon Sep 17 00:00:00 2001 +From: Bjarni Jonasson +Date: Wed, 13 Jan 2021 12:56:25 +0100 +Subject: [PATCH] net: phy: Add 100 base-x mode + +Sparx-5 supports this mode and it is missing in the PHY core. + +Signed-off-by: Bjarni Jonasson +Reviewed-by: Russell King +Signed-off-by: Jakub Kicinski +--- + Documentation/networking/phy.rst | 5 +++++ + include/linux/phy.h | 4 ++++ + 2 files changed, 9 insertions(+) + +--- a/Documentation/networking/phy.rst ++++ b/Documentation/networking/phy.rst +@@ -286,6 +286,11 @@ Some of the interface modes are describe + Note: due to legacy usage, some 10GBASE-R usage incorrectly makes + use of this definition. + ++``PHY_INTERFACE_MODE_100BASEX`` ++ This defines IEEE 802.3 Clause 24. The link operates at a fixed data ++ rate of 125Mpbs using a 4B/5B encoding scheme, resulting in an underlying ++ data rate of 100Mpbs. ++ + Pause frames / flow control + =========================== + +--- a/include/linux/phy.h ++++ b/include/linux/phy.h +@@ -104,6 +104,7 @@ extern const int phy_10gbit_features_arr + * @PHY_INTERFACE_MODE_MOCA: Multimedia over Coax + * @PHY_INTERFACE_MODE_QSGMII: Quad SGMII + * @PHY_INTERFACE_MODE_TRGMII: Turbo RGMII ++ * @PHY_INTERFACE_MODE_100BASEX: 100 BaseX + * @PHY_INTERFACE_MODE_1000BASEX: 1000 BaseX + * @PHY_INTERFACE_MODE_2500BASEX: 2500 BaseX + * @PHY_INTERFACE_MODE_RXAUI: Reduced XAUI +@@ -135,6 +136,7 @@ typedef enum { + PHY_INTERFACE_MODE_MOCA, + PHY_INTERFACE_MODE_QSGMII, + PHY_INTERFACE_MODE_TRGMII, ++ PHY_INTERFACE_MODE_100BASEX, + PHY_INTERFACE_MODE_1000BASEX, + PHY_INTERFACE_MODE_2500BASEX, + PHY_INTERFACE_MODE_RXAUI, +@@ -217,6 +219,8 @@ static inline const char *phy_modes(phy_ + return "usxgmii"; + case PHY_INTERFACE_MODE_10GKR: + return "10gbase-kr"; ++ case PHY_INTERFACE_MODE_100BASEX: ++ return "100base-x"; + default: + return "unknown"; + } diff --git a/target/linux/generic/backport-5.10/711-v5.12-sfp-add-support-for-100-base-x-SFPs.patch b/target/linux/generic/backport-5.10/711-v5.12-sfp-add-support-for-100-base-x-SFPs.patch new file mode 100644 index 000000000..7d06c235d --- /dev/null +++ b/target/linux/generic/backport-5.10/711-v5.12-sfp-add-support-for-100-base-x-SFPs.patch @@ -0,0 +1,40 @@ +From 6e12f35cef6b8a458d7ecf507ae330e0bffaad8c Mon Sep 17 00:00:00 2001 +From: Bjarni Jonasson +Date: Wed, 13 Jan 2021 12:56:26 +0100 +Subject: [PATCH] sfp: add support for 100 base-x SFPs + +Add support for 100Base-FX, 100Base-LX, 100Base-PX and 100Base-BX10 modules +This is needed for Sparx-5 switch. + +Signed-off-by: Bjarni Jonasson +Reviewed-by: Russell King +Signed-off-by: Jakub Kicinski +--- + drivers/net/phy/sfp-bus.c | 9 +++++++++ + 1 file changed, 9 insertions(+) + +--- a/drivers/net/phy/sfp-bus.c ++++ b/drivers/net/phy/sfp-bus.c +@@ -280,6 +280,12 @@ void sfp_parse_support(struct sfp_bus *b + br_min <= 1300 && br_max >= 1200) + phylink_set(modes, 1000baseX_Full); + ++ /* 100Base-FX, 100Base-LX, 100Base-PX, 100Base-BX10 */ ++ if (id->base.e100_base_fx || id->base.e100_base_lx) ++ phylink_set(modes, 100baseFX_Full); ++ if ((id->base.e_base_px || id->base.e_base_bx10) && br_nom == 100) ++ phylink_set(modes, 100baseFX_Full); ++ + /* For active or passive cables, select the link modes + * based on the bit rates and the cable compliance bytes. + */ +@@ -399,6 +405,9 @@ phy_interface_t sfp_select_interface(str + if (phylink_test(link_modes, 1000baseX_Full)) + return PHY_INTERFACE_MODE_1000BASEX; + ++ if (phylink_test(link_modes, 100baseFX_Full)) ++ return PHY_INTERFACE_MODE_100BASEX; ++ + dev_warn(bus->sfp_dev, "Unable to ascertain link mode\n"); + + return PHY_INTERFACE_MODE_NA; diff --git a/target/linux/generic/backport-5.10/712-v5.13-net-phy-marvell-refactor-HWMON-OOP-style.patch b/target/linux/generic/backport-5.10/712-v5.13-net-phy-marvell-refactor-HWMON-OOP-style.patch new file mode 100644 index 000000000..278df4631 --- /dev/null +++ b/target/linux/generic/backport-5.10/712-v5.13-net-phy-marvell-refactor-HWMON-OOP-style.patch @@ -0,0 +1,549 @@ +From 41d26bf4aba070dfd2ab48866cc27a48ee6228c7 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Marek=20Beh=C3=BAn?= +Date: Tue, 20 Apr 2021 09:53:59 +0200 +Subject: [PATCH] net: phy: marvell: refactor HWMON OOP style +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Use a structure of Marvell PHY specific HWMON methods to reduce code +duplication. Store a pointer to this structure into the PHY driver's +driver_data member. + +Signed-off-by: Marek Behún +Signed-off-by: David S. Miller +--- + drivers/net/phy/marvell.c | 369 +++++++++++++------------------------- + 1 file changed, 125 insertions(+), 244 deletions(-) + +--- a/drivers/net/phy/marvell.c ++++ b/drivers/net/phy/marvell.c +@@ -2141,6 +2141,19 @@ static int marvell_vct7_cable_test_get_s + } + + #ifdef CONFIG_HWMON ++struct marvell_hwmon_ops { ++ int (*get_temp)(struct phy_device *phydev, long *temp); ++ int (*get_temp_critical)(struct phy_device *phydev, long *temp); ++ int (*set_temp_critical)(struct phy_device *phydev, long temp); ++ int (*get_temp_alarm)(struct phy_device *phydev, long *alarm); ++}; ++ ++static const struct marvell_hwmon_ops * ++to_marvell_hwmon_ops(const struct phy_device *phydev) ++{ ++ return phydev->drv->driver_data; ++} ++ + static int m88e1121_get_temp(struct phy_device *phydev, long *temp) + { + int oldpage; +@@ -2184,75 +2197,6 @@ error: + return phy_restore_page(phydev, oldpage, ret); + } + +-static int m88e1121_hwmon_read(struct device *dev, +- enum hwmon_sensor_types type, +- u32 attr, int channel, long *temp) +-{ +- struct phy_device *phydev = dev_get_drvdata(dev); +- int err; +- +- switch (attr) { +- case hwmon_temp_input: +- err = m88e1121_get_temp(phydev, temp); +- break; +- default: +- return -EOPNOTSUPP; +- } +- +- return err; +-} +- +-static umode_t m88e1121_hwmon_is_visible(const void *data, +- enum hwmon_sensor_types type, +- u32 attr, int channel) +-{ +- if (type != hwmon_temp) +- return 0; +- +- switch (attr) { +- case hwmon_temp_input: +- return 0444; +- default: +- return 0; +- } +-} +- +-static u32 m88e1121_hwmon_chip_config[] = { +- HWMON_C_REGISTER_TZ, +- 0 +-}; +- +-static const struct hwmon_channel_info m88e1121_hwmon_chip = { +- .type = hwmon_chip, +- .config = m88e1121_hwmon_chip_config, +-}; +- +-static u32 m88e1121_hwmon_temp_config[] = { +- HWMON_T_INPUT, +- 0 +-}; +- +-static const struct hwmon_channel_info m88e1121_hwmon_temp = { +- .type = hwmon_temp, +- .config = m88e1121_hwmon_temp_config, +-}; +- +-static const struct hwmon_channel_info *m88e1121_hwmon_info[] = { +- &m88e1121_hwmon_chip, +- &m88e1121_hwmon_temp, +- NULL +-}; +- +-static const struct hwmon_ops m88e1121_hwmon_hwmon_ops = { +- .is_visible = m88e1121_hwmon_is_visible, +- .read = m88e1121_hwmon_read, +-}; +- +-static const struct hwmon_chip_info m88e1121_hwmon_chip_info = { +- .ops = &m88e1121_hwmon_hwmon_ops, +- .info = m88e1121_hwmon_info, +-}; +- + static int m88e1510_get_temp(struct phy_device *phydev, long *temp) + { + int ret; +@@ -2315,92 +2259,6 @@ static int m88e1510_get_temp_alarm(struc + return 0; + } + +-static int m88e1510_hwmon_read(struct device *dev, +- enum hwmon_sensor_types type, +- u32 attr, int channel, long *temp) +-{ +- struct phy_device *phydev = dev_get_drvdata(dev); +- int err; +- +- switch (attr) { +- case hwmon_temp_input: +- err = m88e1510_get_temp(phydev, temp); +- break; +- case hwmon_temp_crit: +- err = m88e1510_get_temp_critical(phydev, temp); +- break; +- case hwmon_temp_max_alarm: +- err = m88e1510_get_temp_alarm(phydev, temp); +- break; +- default: +- return -EOPNOTSUPP; +- } +- +- return err; +-} +- +-static int m88e1510_hwmon_write(struct device *dev, +- enum hwmon_sensor_types type, +- u32 attr, int channel, long temp) +-{ +- struct phy_device *phydev = dev_get_drvdata(dev); +- int err; +- +- switch (attr) { +- case hwmon_temp_crit: +- err = m88e1510_set_temp_critical(phydev, temp); +- break; +- default: +- return -EOPNOTSUPP; +- } +- return err; +-} +- +-static umode_t m88e1510_hwmon_is_visible(const void *data, +- enum hwmon_sensor_types type, +- u32 attr, int channel) +-{ +- if (type != hwmon_temp) +- return 0; +- +- switch (attr) { +- case hwmon_temp_input: +- case hwmon_temp_max_alarm: +- return 0444; +- case hwmon_temp_crit: +- return 0644; +- default: +- return 0; +- } +-} +- +-static u32 m88e1510_hwmon_temp_config[] = { +- HWMON_T_INPUT | HWMON_T_CRIT | HWMON_T_MAX_ALARM, +- 0 +-}; +- +-static const struct hwmon_channel_info m88e1510_hwmon_temp = { +- .type = hwmon_temp, +- .config = m88e1510_hwmon_temp_config, +-}; +- +-static const struct hwmon_channel_info *m88e1510_hwmon_info[] = { +- &m88e1121_hwmon_chip, +- &m88e1510_hwmon_temp, +- NULL +-}; +- +-static const struct hwmon_ops m88e1510_hwmon_hwmon_ops = { +- .is_visible = m88e1510_hwmon_is_visible, +- .read = m88e1510_hwmon_read, +- .write = m88e1510_hwmon_write, +-}; +- +-static const struct hwmon_chip_info m88e1510_hwmon_chip_info = { +- .ops = &m88e1510_hwmon_hwmon_ops, +- .info = m88e1510_hwmon_info, +-}; +- + static int m88e6390_get_temp(struct phy_device *phydev, long *temp) + { + int sum = 0; +@@ -2459,63 +2317,112 @@ error: + return ret; + } + +-static int m88e6390_hwmon_read(struct device *dev, +- enum hwmon_sensor_types type, +- u32 attr, int channel, long *temp) ++static int marvell_hwmon_read(struct device *dev, enum hwmon_sensor_types type, ++ u32 attr, int channel, long *temp) + { + struct phy_device *phydev = dev_get_drvdata(dev); +- int err; ++ const struct marvell_hwmon_ops *ops = to_marvell_hwmon_ops(phydev); ++ int err = -EOPNOTSUPP; + + switch (attr) { + case hwmon_temp_input: +- err = m88e6390_get_temp(phydev, temp); ++ if (ops->get_temp) ++ err = ops->get_temp(phydev, temp); ++ break; ++ case hwmon_temp_crit: ++ if (ops->get_temp_critical) ++ err = ops->get_temp_critical(phydev, temp); ++ break; ++ case hwmon_temp_max_alarm: ++ if (ops->get_temp_alarm) ++ err = ops->get_temp_alarm(phydev, temp); ++ break; ++ } ++ ++ return err; ++} ++ ++static int marvell_hwmon_write(struct device *dev, enum hwmon_sensor_types type, ++ u32 attr, int channel, long temp) ++{ ++ struct phy_device *phydev = dev_get_drvdata(dev); ++ const struct marvell_hwmon_ops *ops = to_marvell_hwmon_ops(phydev); ++ int err = -EOPNOTSUPP; ++ ++ switch (attr) { ++ case hwmon_temp_crit: ++ if (ops->set_temp_critical) ++ err = ops->set_temp_critical(phydev, temp); + break; + default: +- return -EOPNOTSUPP; ++ fallthrough; + } + + return err; + } + +-static umode_t m88e6390_hwmon_is_visible(const void *data, +- enum hwmon_sensor_types type, +- u32 attr, int channel) ++static umode_t marvell_hwmon_is_visible(const void *data, ++ enum hwmon_sensor_types type, ++ u32 attr, int channel) + { ++ const struct phy_device *phydev = data; ++ const struct marvell_hwmon_ops *ops = to_marvell_hwmon_ops(phydev); ++ + if (type != hwmon_temp) + return 0; + + switch (attr) { + case hwmon_temp_input: +- return 0444; ++ return ops->get_temp ? 0444 : 0; ++ case hwmon_temp_max_alarm: ++ return ops->get_temp_alarm ? 0444 : 0; ++ case hwmon_temp_crit: ++ return (ops->get_temp_critical ? 0444 : 0) | ++ (ops->set_temp_critical ? 0200 : 0); + default: + return 0; + } + } + +-static u32 m88e6390_hwmon_temp_config[] = { +- HWMON_T_INPUT, ++static u32 marvell_hwmon_chip_config[] = { ++ HWMON_C_REGISTER_TZ, + 0 + }; + +-static const struct hwmon_channel_info m88e6390_hwmon_temp = { ++static const struct hwmon_channel_info marvell_hwmon_chip = { ++ .type = hwmon_chip, ++ .config = marvell_hwmon_chip_config, ++}; ++ ++/* we can define HWMON_T_CRIT and HWMON_T_MAX_ALARM even though these are not ++ * defined for all PHYs, because the hwmon code checks whether the attributes ++ * exists via the .is_visible method ++ */ ++static u32 marvell_hwmon_temp_config[] = { ++ HWMON_T_INPUT | HWMON_T_CRIT | HWMON_T_MAX_ALARM, ++ 0 ++}; ++ ++static const struct hwmon_channel_info marvell_hwmon_temp = { + .type = hwmon_temp, +- .config = m88e6390_hwmon_temp_config, ++ .config = marvell_hwmon_temp_config, + }; + +-static const struct hwmon_channel_info *m88e6390_hwmon_info[] = { +- &m88e1121_hwmon_chip, +- &m88e6390_hwmon_temp, ++static const struct hwmon_channel_info *marvell_hwmon_info[] = { ++ &marvell_hwmon_chip, ++ &marvell_hwmon_temp, + NULL + }; + +-static const struct hwmon_ops m88e6390_hwmon_hwmon_ops = { +- .is_visible = m88e6390_hwmon_is_visible, +- .read = m88e6390_hwmon_read, ++static const struct hwmon_ops marvell_hwmon_hwmon_ops = { ++ .is_visible = marvell_hwmon_is_visible, ++ .read = marvell_hwmon_read, ++ .write = marvell_hwmon_write, + }; + +-static const struct hwmon_chip_info m88e6390_hwmon_chip_info = { +- .ops = &m88e6390_hwmon_hwmon_ops, +- .info = m88e6390_hwmon_info, ++static const struct hwmon_chip_info marvell_hwmon_chip_info = { ++ .ops = &marvell_hwmon_hwmon_ops, ++ .info = marvell_hwmon_info, + }; + + static int marvell_hwmon_name(struct phy_device *phydev) +@@ -2538,49 +2445,48 @@ static int marvell_hwmon_name(struct phy + return 0; + } + +-static int marvell_hwmon_probe(struct phy_device *phydev, +- const struct hwmon_chip_info *chip) ++static int marvell_hwmon_probe(struct phy_device *phydev) + { ++ const struct marvell_hwmon_ops *ops = to_marvell_hwmon_ops(phydev); + struct marvell_priv *priv = phydev->priv; + struct device *dev = &phydev->mdio.dev; + int err; + ++ if (!ops) ++ return 0; ++ + err = marvell_hwmon_name(phydev); + if (err) + return err; + + priv->hwmon_dev = devm_hwmon_device_register_with_info( +- dev, priv->hwmon_name, phydev, chip, NULL); ++ dev, priv->hwmon_name, phydev, &marvell_hwmon_chip_info, NULL); + + return PTR_ERR_OR_ZERO(priv->hwmon_dev); + } + +-static int m88e1121_hwmon_probe(struct phy_device *phydev) +-{ +- return marvell_hwmon_probe(phydev, &m88e1121_hwmon_chip_info); +-} ++static const struct marvell_hwmon_ops m88e1121_hwmon_ops = { ++ .get_temp = m88e1121_get_temp, ++}; + +-static int m88e1510_hwmon_probe(struct phy_device *phydev) +-{ +- return marvell_hwmon_probe(phydev, &m88e1510_hwmon_chip_info); +-} ++static const struct marvell_hwmon_ops m88e1510_hwmon_ops = { ++ .get_temp = m88e1510_get_temp, ++ .get_temp_critical = m88e1510_get_temp_critical, ++ .set_temp_critical = m88e1510_set_temp_critical, ++ .get_temp_alarm = m88e1510_get_temp_alarm, ++}; ++ ++static const struct marvell_hwmon_ops m88e6390_hwmon_ops = { ++ .get_temp = m88e6390_get_temp, ++}; ++ ++#define DEF_MARVELL_HWMON_OPS(s) (&(s)) + +-static int m88e6390_hwmon_probe(struct phy_device *phydev) +-{ +- return marvell_hwmon_probe(phydev, &m88e6390_hwmon_chip_info); +-} + #else +-static int m88e1121_hwmon_probe(struct phy_device *phydev) +-{ +- return 0; +-} + +-static int m88e1510_hwmon_probe(struct phy_device *phydev) +-{ +- return 0; +-} ++#define DEF_MARVELL_HWMON_OPS(s) NULL + +-static int m88e6390_hwmon_probe(struct phy_device *phydev) ++static int marvell_hwmon_probe(struct phy_device *phydev) + { + return 0; + } +@@ -2596,40 +2502,7 @@ static int marvell_probe(struct phy_devi + + phydev->priv = priv; + +- return 0; +-} +- +-static int m88e1121_probe(struct phy_device *phydev) +-{ +- int err; +- +- err = marvell_probe(phydev); +- if (err) +- return err; +- +- return m88e1121_hwmon_probe(phydev); +-} +- +-static int m88e1510_probe(struct phy_device *phydev) +-{ +- int err; +- +- err = marvell_probe(phydev); +- if (err) +- return err; +- +- return m88e1510_hwmon_probe(phydev); +-} +- +-static int m88e6390_probe(struct phy_device *phydev) +-{ +- int err; +- +- err = marvell_probe(phydev); +- if (err) +- return err; +- +- return m88e6390_hwmon_probe(phydev); ++ return marvell_hwmon_probe(phydev); + } + + static struct phy_driver marvell_drivers[] = { +@@ -2714,8 +2587,9 @@ static struct phy_driver marvell_drivers + .phy_id = MARVELL_PHY_ID_88E1121R, + .phy_id_mask = MARVELL_PHY_ID_MASK, + .name = "Marvell 88E1121R", ++ .driver_data = DEF_MARVELL_HWMON_OPS(m88e1121_hwmon_ops), + /* PHY_GBIT_FEATURES */ +- .probe = m88e1121_probe, ++ .probe = marvell_probe, + .config_init = marvell_config_init, + .config_aneg = m88e1121_config_aneg, + .read_status = marvell_read_status, +@@ -2834,9 +2708,10 @@ static struct phy_driver marvell_drivers + .phy_id = MARVELL_PHY_ID_88E1510, + .phy_id_mask = MARVELL_PHY_ID_MASK, + .name = "Marvell 88E1510", ++ .driver_data = DEF_MARVELL_HWMON_OPS(m88e1510_hwmon_ops), + .features = PHY_GBIT_FIBRE_FEATURES, + .flags = PHY_POLL_CABLE_TEST, +- .probe = m88e1510_probe, ++ .probe = marvell_probe, + .config_init = m88e1510_config_init, + .config_aneg = m88e1510_config_aneg, + .read_status = marvell_read_status, +@@ -2863,9 +2738,10 @@ static struct phy_driver marvell_drivers + .phy_id = MARVELL_PHY_ID_88E1540, + .phy_id_mask = MARVELL_PHY_ID_MASK, + .name = "Marvell 88E1540", ++ .driver_data = DEF_MARVELL_HWMON_OPS(m88e1510_hwmon_ops), + /* PHY_GBIT_FEATURES */ + .flags = PHY_POLL_CABLE_TEST, +- .probe = m88e1510_probe, ++ .probe = marvell_probe, + .config_init = marvell_config_init, + .config_aneg = m88e1510_config_aneg, + .read_status = marvell_read_status, +@@ -2889,7 +2765,8 @@ static struct phy_driver marvell_drivers + .phy_id = MARVELL_PHY_ID_88E1545, + .phy_id_mask = MARVELL_PHY_ID_MASK, + .name = "Marvell 88E1545", +- .probe = m88e1510_probe, ++ .driver_data = DEF_MARVELL_HWMON_OPS(m88e1510_hwmon_ops), ++ .probe = marvell_probe, + /* PHY_GBIT_FEATURES */ + .flags = PHY_POLL_CABLE_TEST, + .config_init = marvell_config_init, +@@ -2935,9 +2812,10 @@ static struct phy_driver marvell_drivers + .phy_id = MARVELL_PHY_ID_88E6341_FAMILY, + .phy_id_mask = MARVELL_PHY_ID_MASK, + .name = "Marvell 88E6341 Family", ++ .driver_data = DEF_MARVELL_HWMON_OPS(m88e1510_hwmon_ops), + /* PHY_GBIT_FEATURES */ + .flags = PHY_POLL_CABLE_TEST, +- .probe = m88e1510_probe, ++ .probe = marvell_probe, + .config_init = marvell_config_init, + .config_aneg = m88e6390_config_aneg, + .read_status = marvell_read_status, +@@ -2961,9 +2839,10 @@ static struct phy_driver marvell_drivers + .phy_id = MARVELL_PHY_ID_88E6390_FAMILY, + .phy_id_mask = MARVELL_PHY_ID_MASK, + .name = "Marvell 88E6390 Family", ++ .driver_data = DEF_MARVELL_HWMON_OPS(m88e6390_hwmon_ops), + /* PHY_GBIT_FEATURES */ + .flags = PHY_POLL_CABLE_TEST, +- .probe = m88e6390_probe, ++ .probe = marvell_probe, + .config_init = marvell_config_init, + .config_aneg = m88e6390_config_aneg, + .read_status = marvell_read_status, +@@ -2987,7 +2866,8 @@ static struct phy_driver marvell_drivers + .phy_id = MARVELL_PHY_ID_88E1340S, + .phy_id_mask = MARVELL_PHY_ID_MASK, + .name = "Marvell 88E1340S", +- .probe = m88e1510_probe, ++ .driver_data = DEF_MARVELL_HWMON_OPS(m88e1510_hwmon_ops), ++ .probe = marvell_probe, + /* PHY_GBIT_FEATURES */ + .config_init = marvell_config_init, + .config_aneg = m88e1510_config_aneg, +@@ -3009,7 +2889,8 @@ static struct phy_driver marvell_drivers + .phy_id = MARVELL_PHY_ID_88E1548P, + .phy_id_mask = MARVELL_PHY_ID_MASK, + .name = "Marvell 88E1548P", +- .probe = m88e1510_probe, ++ .driver_data = DEF_MARVELL_HWMON_OPS(m88e1510_hwmon_ops), ++ .probe = marvell_probe, + .features = PHY_GBIT_FIBRE_FEATURES, + .config_init = marvell_config_init, + .config_aneg = m88e1510_config_aneg, diff --git a/target/linux/generic/backport-5.10/713-v5.15-net-phy-marvell-add-SFP-support-for-88E1510.patch b/target/linux/generic/backport-5.10/713-v5.15-net-phy-marvell-add-SFP-support-for-88E1510.patch new file mode 100644 index 000000000..b86e9bf64 --- /dev/null +++ b/target/linux/generic/backport-5.10/713-v5.15-net-phy-marvell-add-SFP-support-for-88E1510.patch @@ -0,0 +1,161 @@ +From b697d9d38a5a5ab405d7cc4743d39fe2c5d7517c Mon Sep 17 00:00:00 2001 +From: Ivan Bornyakov +Date: Thu, 12 Aug 2021 16:42:56 +0300 +Subject: [PATCH] net: phy: marvell: add SFP support for 88E1510 + +Add support for SFP cages connected to the Marvell 88E1512 transceiver. +88E1512 supports for SGMII/1000Base-X/100Base-FX media type with RGMII +on system interface. Configure PHY to appropriate mode depending on the +type of SFP inserted. On SFP removal configure PHY to the RGMII-copper +mode so RJ-45 port can still work. + +Signed-off-by: Ivan Bornyakov +Link: https://lore.kernel.org/r/20210812134256.2436-1-i.bornyakov@metrotek.ru +Signed-off-by: Jakub Kicinski +--- + drivers/net/phy/marvell.c | 105 +++++++++++++++++++++++++++++++++++++- + 1 file changed, 104 insertions(+), 1 deletion(-) + +--- a/drivers/net/phy/marvell.c ++++ b/drivers/net/phy/marvell.c +@@ -32,6 +32,7 @@ + #include + #include + #include ++#include + + #include + #include +@@ -46,6 +47,7 @@ + #define MII_MARVELL_MISC_TEST_PAGE 0x06 + #define MII_MARVELL_VCT7_PAGE 0x07 + #define MII_MARVELL_WOL_PAGE 0x11 ++#define MII_MARVELL_MODE_PAGE 0x12 + + #define MII_M1011_IEVENT 0x13 + #define MII_M1011_IEVENT_CLEAR 0x0000 +@@ -162,7 +164,14 @@ + + #define MII_88E1510_GEN_CTRL_REG_1 0x14 + #define MII_88E1510_GEN_CTRL_REG_1_MODE_MASK 0x7 ++#define MII_88E1510_GEN_CTRL_REG_1_MODE_RGMII 0x0 /* RGMII to copper */ + #define MII_88E1510_GEN_CTRL_REG_1_MODE_SGMII 0x1 /* SGMII to copper */ ++/* RGMII to 1000BASE-X */ ++#define MII_88E1510_GEN_CTRL_REG_1_MODE_RGMII_1000X 0x2 ++/* RGMII to 100BASE-FX */ ++#define MII_88E1510_GEN_CTRL_REG_1_MODE_RGMII_100FX 0x3 ++/* RGMII to SGMII */ ++#define MII_88E1510_GEN_CTRL_REG_1_MODE_RGMII_SGMII 0x4 + #define MII_88E1510_GEN_CTRL_REG_1_RESET 0x8000 /* Soft reset */ + + #define MII_VCT5_TX_RX_MDI0_COUPLING 0x10 +@@ -2505,6 +2514,100 @@ static int marvell_probe(struct phy_devi + return marvell_hwmon_probe(phydev); + } + ++static int m88e1510_sfp_insert(void *upstream, const struct sfp_eeprom_id *id) ++{ ++ struct phy_device *phydev = upstream; ++ phy_interface_t interface; ++ struct device *dev; ++ int oldpage; ++ int ret = 0; ++ u16 mode; ++ ++ __ETHTOOL_DECLARE_LINK_MODE_MASK(supported) = { 0, }; ++ ++ dev = &phydev->mdio.dev; ++ ++ sfp_parse_support(phydev->sfp_bus, id, supported); ++ interface = sfp_select_interface(phydev->sfp_bus, supported); ++ ++ dev_info(dev, "%s SFP module inserted\n", phy_modes(interface)); ++ ++ switch (interface) { ++ case PHY_INTERFACE_MODE_1000BASEX: ++ mode = MII_88E1510_GEN_CTRL_REG_1_MODE_RGMII_1000X; ++ ++ break; ++ case PHY_INTERFACE_MODE_100BASEX: ++ mode = MII_88E1510_GEN_CTRL_REG_1_MODE_RGMII_100FX; ++ ++ break; ++ case PHY_INTERFACE_MODE_SGMII: ++ mode = MII_88E1510_GEN_CTRL_REG_1_MODE_RGMII_SGMII; ++ ++ break; ++ default: ++ dev_err(dev, "Incompatible SFP module inserted\n"); ++ ++ return -EINVAL; ++ } ++ ++ oldpage = phy_select_page(phydev, MII_MARVELL_MODE_PAGE); ++ if (oldpage < 0) ++ goto error; ++ ++ ret = __phy_modify(phydev, MII_88E1510_GEN_CTRL_REG_1, ++ MII_88E1510_GEN_CTRL_REG_1_MODE_MASK, mode); ++ if (ret < 0) ++ goto error; ++ ++ ret = __phy_set_bits(phydev, MII_88E1510_GEN_CTRL_REG_1, ++ MII_88E1510_GEN_CTRL_REG_1_RESET); ++ ++error: ++ return phy_restore_page(phydev, oldpage, ret); ++} ++ ++static void m88e1510_sfp_remove(void *upstream) ++{ ++ struct phy_device *phydev = upstream; ++ int oldpage; ++ int ret = 0; ++ ++ oldpage = phy_select_page(phydev, MII_MARVELL_MODE_PAGE); ++ if (oldpage < 0) ++ goto error; ++ ++ ret = __phy_modify(phydev, MII_88E1510_GEN_CTRL_REG_1, ++ MII_88E1510_GEN_CTRL_REG_1_MODE_MASK, ++ MII_88E1510_GEN_CTRL_REG_1_MODE_RGMII); ++ if (ret < 0) ++ goto error; ++ ++ ret = __phy_set_bits(phydev, MII_88E1510_GEN_CTRL_REG_1, ++ MII_88E1510_GEN_CTRL_REG_1_RESET); ++ ++error: ++ phy_restore_page(phydev, oldpage, ret); ++} ++ ++static const struct sfp_upstream_ops m88e1510_sfp_ops = { ++ .module_insert = m88e1510_sfp_insert, ++ .module_remove = m88e1510_sfp_remove, ++ .attach = phy_sfp_attach, ++ .detach = phy_sfp_detach, ++}; ++ ++static int m88e1510_probe(struct phy_device *phydev) ++{ ++ int err; ++ ++ err = marvell_probe(phydev); ++ if (err) ++ return err; ++ ++ return phy_sfp_probe(phydev, &m88e1510_sfp_ops); ++} ++ + static struct phy_driver marvell_drivers[] = { + { + .phy_id = MARVELL_PHY_ID_88E1101, +@@ -2711,7 +2814,7 @@ static struct phy_driver marvell_drivers + .driver_data = DEF_MARVELL_HWMON_OPS(m88e1510_hwmon_ops), + .features = PHY_GBIT_FIBRE_FEATURES, + .flags = PHY_POLL_CABLE_TEST, +- .probe = marvell_probe, ++ .probe = m88e1510_probe, + .config_init = m88e1510_config_init, + .config_aneg = m88e1510_config_aneg, + .read_status = marvell_read_status, diff --git a/target/linux/generic/backport-5.10/765-v5.12-net-dsa-automatically-bring-up-DSA-master-when-openi.patch b/target/linux/generic/backport-5.10/719-v5.12-net-dsa-automatically-bring-up-DSA-master-when-openi.patch similarity index 100% rename from target/linux/generic/backport-5.10/765-v5.12-net-dsa-automatically-bring-up-DSA-master-when-openi.patch rename to target/linux/generic/backport-5.10/719-v5.12-net-dsa-automatically-bring-up-DSA-master-when-openi.patch diff --git a/target/linux/generic/backport-5.10/770-v5.12-net-bridge-notify-switchdev-of-disappearance-of-old-.patch b/target/linux/generic/backport-5.10/720-v5.12-net-bridge-notify-switchdev-of-disappearance-of-old-.patch similarity index 100% rename from target/linux/generic/backport-5.10/770-v5.12-net-bridge-notify-switchdev-of-disappearance-of-old-.patch rename to target/linux/generic/backport-5.10/720-v5.12-net-bridge-notify-switchdev-of-disappearance-of-old-.patch diff --git a/target/linux/generic/backport-5.10/771-v5.12-net-dsa-be-louder-when-a-non-legacy-FDB-operation-fa.patch b/target/linux/generic/backport-5.10/721-v5.12-net-dsa-be-louder-when-a-non-legacy-FDB-operation-fa.patch similarity index 100% rename from target/linux/generic/backport-5.10/771-v5.12-net-dsa-be-louder-when-a-non-legacy-FDB-operation-fa.patch rename to target/linux/generic/backport-5.10/721-v5.12-net-dsa-be-louder-when-a-non-legacy-FDB-operation-fa.patch diff --git a/target/linux/generic/backport-5.10/772-v5.12-net-dsa-don-t-use-switchdev_notifier_fdb_info-in-dsa.patch b/target/linux/generic/backport-5.10/722-v5.12-net-dsa-don-t-use-switchdev_notifier_fdb_info-in-dsa.patch similarity index 100% rename from target/linux/generic/backport-5.10/772-v5.12-net-dsa-don-t-use-switchdev_notifier_fdb_info-in-dsa.patch rename to target/linux/generic/backport-5.10/722-v5.12-net-dsa-don-t-use-switchdev_notifier_fdb_info-in-dsa.patch diff --git a/target/linux/generic/backport-5.10/773-v5.12-net-dsa-move-switchdev-event-implementation-under-th.patch b/target/linux/generic/backport-5.10/723-v5.12-net-dsa-move-switchdev-event-implementation-under-th.patch similarity index 100% rename from target/linux/generic/backport-5.10/773-v5.12-net-dsa-move-switchdev-event-implementation-under-th.patch rename to target/linux/generic/backport-5.10/723-v5.12-net-dsa-move-switchdev-event-implementation-under-th.patch diff --git a/target/linux/generic/backport-5.10/774-v5.12-net-dsa-exit-early-in-dsa_slave_switchdev_event-if-w.patch b/target/linux/generic/backport-5.10/724-v5.12-net-dsa-exit-early-in-dsa_slave_switchdev_event-if-w.patch similarity index 100% rename from target/linux/generic/backport-5.10/774-v5.12-net-dsa-exit-early-in-dsa_slave_switchdev_event-if-w.patch rename to target/linux/generic/backport-5.10/724-v5.12-net-dsa-exit-early-in-dsa_slave_switchdev_event-if-w.patch diff --git a/target/linux/generic/backport-5.10/775-v5.12-net-dsa-listen-for-SWITCHDEV_-FDB-DEL-_ADD_TO_DEVICE.patch b/target/linux/generic/backport-5.10/725-v5.12-net-dsa-listen-for-SWITCHDEV_-FDB-DEL-_ADD_TO_DEVICE.patch similarity index 100% rename from target/linux/generic/backport-5.10/775-v5.12-net-dsa-listen-for-SWITCHDEV_-FDB-DEL-_ADD_TO_DEVICE.patch rename to target/linux/generic/backport-5.10/725-v5.12-net-dsa-listen-for-SWITCHDEV_-FDB-DEL-_ADD_TO_DEVICE.patch diff --git a/target/linux/generic/backport-5.10/780-net-dsa-mt7530-setup-core-clock-even-in-TRGMII-mode.patch b/target/linux/generic/backport-5.10/730-net-dsa-mt7530-setup-core-clock-even-in-TRGMII-mode.patch similarity index 100% rename from target/linux/generic/backport-5.10/780-net-dsa-mt7530-setup-core-clock-even-in-TRGMII-mode.patch rename to target/linux/generic/backport-5.10/730-net-dsa-mt7530-setup-core-clock-even-in-TRGMII-mode.patch diff --git a/target/linux/generic/backport-5.10/731-v5.12-net-dsa-mt7530-MT7530-optional-GPIO-support.patch b/target/linux/generic/backport-5.10/731-v5.12-net-dsa-mt7530-MT7530-optional-GPIO-support.patch new file mode 100644 index 000000000..9e5047a16 --- /dev/null +++ b/target/linux/generic/backport-5.10/731-v5.12-net-dsa-mt7530-MT7530-optional-GPIO-support.patch @@ -0,0 +1,181 @@ +From 429a0edeefd88cbfca5c417dfb8561047bb50769 Mon Sep 17 00:00:00 2001 +From: DENG Qingfang +Date: Mon, 25 Jan 2021 12:43:22 +0800 +Subject: [PATCH] net: dsa: mt7530: MT7530 optional GPIO support + +MT7530's LED controller can drive up to 15 LED/GPIOs. + +Add support for GPIO control and allow users to use its GPIOs by +setting gpio-controller property in device tree. + +Signed-off-by: DENG Qingfang +Reviewed-by: Linus Walleij +Reviewed-by: Andrew Lunn +Signed-off-by: Jakub Kicinski +--- + drivers/net/dsa/mt7530.c | 110 +++++++++++++++++++++++++++++++++++++++ + drivers/net/dsa/mt7530.h | 20 +++++++ + 2 files changed, 130 insertions(+) + +--- a/drivers/net/dsa/mt7530.c ++++ b/drivers/net/dsa/mt7530.c +@@ -18,6 +18,7 @@ + #include + #include + #include ++#include + #include + + #include "mt7530.h" +@@ -1534,6 +1535,109 @@ mtk_get_tag_protocol(struct dsa_switch * + } + } + ++static inline u32 ++mt7530_gpio_to_bit(unsigned int offset) ++{ ++ /* Map GPIO offset to register bit ++ * [ 2: 0] port 0 LED 0..2 as GPIO 0..2 ++ * [ 6: 4] port 1 LED 0..2 as GPIO 3..5 ++ * [10: 8] port 2 LED 0..2 as GPIO 6..8 ++ * [14:12] port 3 LED 0..2 as GPIO 9..11 ++ * [18:16] port 4 LED 0..2 as GPIO 12..14 ++ */ ++ return BIT(offset + offset / 3); ++} ++ ++static int ++mt7530_gpio_get(struct gpio_chip *gc, unsigned int offset) ++{ ++ struct mt7530_priv *priv = gpiochip_get_data(gc); ++ u32 bit = mt7530_gpio_to_bit(offset); ++ ++ return !!(mt7530_read(priv, MT7530_LED_GPIO_DATA) & bit); ++} ++ ++static void ++mt7530_gpio_set(struct gpio_chip *gc, unsigned int offset, int value) ++{ ++ struct mt7530_priv *priv = gpiochip_get_data(gc); ++ u32 bit = mt7530_gpio_to_bit(offset); ++ ++ if (value) ++ mt7530_set(priv, MT7530_LED_GPIO_DATA, bit); ++ else ++ mt7530_clear(priv, MT7530_LED_GPIO_DATA, bit); ++} ++ ++static int ++mt7530_gpio_get_direction(struct gpio_chip *gc, unsigned int offset) ++{ ++ struct mt7530_priv *priv = gpiochip_get_data(gc); ++ u32 bit = mt7530_gpio_to_bit(offset); ++ ++ return (mt7530_read(priv, MT7530_LED_GPIO_DIR) & bit) ? ++ GPIO_LINE_DIRECTION_OUT : GPIO_LINE_DIRECTION_IN; ++} ++ ++static int ++mt7530_gpio_direction_input(struct gpio_chip *gc, unsigned int offset) ++{ ++ struct mt7530_priv *priv = gpiochip_get_data(gc); ++ u32 bit = mt7530_gpio_to_bit(offset); ++ ++ mt7530_clear(priv, MT7530_LED_GPIO_OE, bit); ++ mt7530_clear(priv, MT7530_LED_GPIO_DIR, bit); ++ ++ return 0; ++} ++ ++static int ++mt7530_gpio_direction_output(struct gpio_chip *gc, unsigned int offset, int value) ++{ ++ struct mt7530_priv *priv = gpiochip_get_data(gc); ++ u32 bit = mt7530_gpio_to_bit(offset); ++ ++ mt7530_set(priv, MT7530_LED_GPIO_DIR, bit); ++ ++ if (value) ++ mt7530_set(priv, MT7530_LED_GPIO_DATA, bit); ++ else ++ mt7530_clear(priv, MT7530_LED_GPIO_DATA, bit); ++ ++ mt7530_set(priv, MT7530_LED_GPIO_OE, bit); ++ ++ return 0; ++} ++ ++static int ++mt7530_setup_gpio(struct mt7530_priv *priv) ++{ ++ struct device *dev = priv->dev; ++ struct gpio_chip *gc; ++ ++ gc = devm_kzalloc(dev, sizeof(*gc), GFP_KERNEL); ++ if (!gc) ++ return -ENOMEM; ++ ++ mt7530_write(priv, MT7530_LED_GPIO_OE, 0); ++ mt7530_write(priv, MT7530_LED_GPIO_DIR, 0); ++ mt7530_write(priv, MT7530_LED_IO_MODE, 0); ++ ++ gc->label = "mt7530"; ++ gc->parent = dev; ++ gc->owner = THIS_MODULE; ++ gc->get_direction = mt7530_gpio_get_direction; ++ gc->direction_input = mt7530_gpio_direction_input; ++ gc->direction_output = mt7530_gpio_direction_output; ++ gc->get = mt7530_gpio_get; ++ gc->set = mt7530_gpio_set; ++ gc->base = -1; ++ gc->ngpio = 15; ++ gc->can_sleep = true; ++ ++ return devm_gpiochip_add_data(dev, gc, priv); ++} ++ + static int + mt7530_setup(struct dsa_switch *ds) + { +@@ -1675,6 +1779,12 @@ mt7530_setup(struct dsa_switch *ds) + } + } + ++ if (of_property_read_bool(priv->dev->of_node, "gpio-controller")) { ++ ret = mt7530_setup_gpio(priv); ++ if (ret) ++ return ret; ++ } ++ + mt7530_setup_port5(ds, interface); + + /* Flush the FDB table */ +--- a/drivers/net/dsa/mt7530.h ++++ b/drivers/net/dsa/mt7530.h +@@ -529,6 +529,26 @@ enum mt7531_clk_skew { + #define MT7531_GPIO12_RG_RXD3_MASK GENMASK(19, 16) + #define MT7531_EXT_P_MDIO_12 (2 << 16) + ++/* Registers for LED GPIO control (MT7530 only) ++ * All registers follow this pattern: ++ * [ 2: 0] port 0 ++ * [ 6: 4] port 1 ++ * [10: 8] port 2 ++ * [14:12] port 3 ++ * [18:16] port 4 ++ */ ++ ++/* LED enable, 0: Disable, 1: Enable (Default) */ ++#define MT7530_LED_EN 0x7d00 ++/* LED mode, 0: GPIO mode, 1: PHY mode (Default) */ ++#define MT7530_LED_IO_MODE 0x7d04 ++/* GPIO direction, 0: Input, 1: Output */ ++#define MT7530_LED_GPIO_DIR 0x7d10 ++/* GPIO output enable, 0: Disable, 1: Enable */ ++#define MT7530_LED_GPIO_OE 0x7d14 ++/* GPIO value, 0: Low, 1: High */ ++#define MT7530_LED_GPIO_DATA 0x7d18 ++ + #define MT7530_CREV 0x7ffc + #define CHIP_NAME_SHIFT 16 + #define MT7530_ID 0x7530 diff --git a/target/linux/generic/backport-5.10/781-v5.13-net-dsa-mt7530-Add-support-for-EEE-features.patch b/target/linux/generic/backport-5.10/731-v5.13-net-dsa-mt7530-Add-support-for-EEE-features.patch similarity index 95% rename from target/linux/generic/backport-5.10/781-v5.13-net-dsa-mt7530-Add-support-for-EEE-features.patch rename to target/linux/generic/backport-5.10/731-v5.13-net-dsa-mt7530-Add-support-for-EEE-features.patch index 87a054b64..38dd8a729 100644 --- a/target/linux/generic/backport-5.10/781-v5.13-net-dsa-mt7530-Add-support-for-EEE-features.patch +++ b/target/linux/generic/backport-5.10/731-v5.13-net-dsa-mt7530-Add-support-for-EEE-features.patch @@ -17,7 +17,7 @@ Signed-off-by: David S. Miller --- a/drivers/net/dsa/mt7530.c +++ b/drivers/net/dsa/mt7530.c -@@ -2261,6 +2261,17 @@ static void mt753x_phylink_mac_link_up(s +@@ -2371,6 +2371,17 @@ static void mt753x_phylink_mac_link_up(s mcr |= PMCR_RX_FC_EN; } @@ -35,7 +35,7 @@ Signed-off-by: David S. Miller mt7530_set(priv, MT7530_PMCR_P(port), mcr); } -@@ -2491,6 +2502,36 @@ mt753x_phy_write(struct dsa_switch *ds, +@@ -2601,6 +2612,36 @@ mt753x_phy_write(struct dsa_switch *ds, return priv->info->phy_write(ds, port, regnum, val); } @@ -72,7 +72,7 @@ Signed-off-by: David S. Miller static const struct dsa_switch_ops mt7530_switch_ops = { .get_tag_protocol = mtk_get_tag_protocol, .setup = mt753x_setup, -@@ -2519,6 +2560,8 @@ static const struct dsa_switch_ops mt753 +@@ -2629,6 +2670,8 @@ static const struct dsa_switch_ops mt753 .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, diff --git a/target/linux/generic/backport-5.10/782-net-next-1-of-net-pass-the-dst-buffer-to-of_get_mac_address.patch b/target/linux/generic/backport-5.10/732-net-next-1-of-net-pass-the-dst-buffer-to-of_get_mac_address.patch similarity index 98% rename from target/linux/generic/backport-5.10/782-net-next-1-of-net-pass-the-dst-buffer-to-of_get_mac_address.patch rename to target/linux/generic/backport-5.10/732-net-next-1-of-net-pass-the-dst-buffer-to-of_get_mac_address.patch index e7e9d19a2..ab3566558 100644 --- a/target/linux/generic/backport-5.10/782-net-next-1-of-net-pass-the-dst-buffer-to-of_get_mac_address.patch +++ b/target/linux/generic/backport-5.10/732-net-next-1-of-net-pass-the-dst-buffer-to-of_get_mac_address.patch @@ -272,7 +272,7 @@ Signed-off-by: David S. Miller void __iomem *descmap; const struct of_device_id *of_id = NULL; -@@ -1525,10 +1524,8 @@ static int altera_tse_probe(struct platf +@@ -1528,10 +1527,8 @@ static int altera_tse_probe(struct platf priv->rx_dma_buf_sz = ALTERA_RXDMABUFFER_SIZE; /* get default MAC address from device tree */ @@ -319,7 +319,7 @@ Signed-off-by: David S. Miller int tx_size, err, i; struct ag71xx *ag; -@@ -1957,10 +1956,8 @@ static int ag71xx_probe(struct platform_ +@@ -1952,10 +1951,8 @@ static int ag71xx_probe(struct platform_ ag->stop_desc->ctrl = 0; ag->stop_desc->next = (u32)ag->stop_desc_dma; @@ -334,7 +334,7 @@ Signed-off-by: David S. Miller } --- a/drivers/net/ethernet/broadcom/bcmsysport.c +++ b/drivers/net/ethernet/broadcom/bcmsysport.c -@@ -2465,7 +2465,6 @@ static int bcm_sysport_probe(struct plat +@@ -2468,7 +2468,6 @@ static int bcm_sysport_probe(struct plat struct bcm_sysport_priv *priv; struct device_node *dn; struct net_device *dev; @@ -342,7 +342,7 @@ Signed-off-by: David S. Miller u32 txq, rxq; int ret; -@@ -2560,12 +2559,10 @@ static int bcm_sysport_probe(struct plat +@@ -2563,12 +2562,10 @@ static int bcm_sysport_probe(struct plat } /* Initialize netdevice members */ @@ -423,7 +423,7 @@ Signed-off-by: David S. Miller if (bgmac->irq < 0) --- a/drivers/net/ethernet/cadence/macb_main.c +++ b/drivers/net/ethernet/cadence/macb_main.c -@@ -4456,7 +4456,6 @@ static int macb_probe(struct platform_de +@@ -4479,7 +4479,6 @@ static int macb_probe(struct platform_de struct net_device *dev; struct resource *regs; void __iomem *mem; @@ -431,7 +431,7 @@ Signed-off-by: David S. Miller struct macb *bp; int err, val; -@@ -4569,15 +4568,11 @@ static int macb_probe(struct platform_de +@@ -4592,15 +4591,11 @@ static int macb_probe(struct platform_de if (bp->caps & MACB_CAPS_NEEDS_RSTONUBR) bp->rx_intr_mask |= MACB_BIT(RXUBR); @@ -607,7 +607,7 @@ Signed-off-by: David S. Miller /* --- a/drivers/net/ethernet/freescale/fman/mac.c +++ b/drivers/net/ethernet/freescale/fman/mac.c -@@ -605,7 +605,6 @@ static int mac_probe(struct platform_dev +@@ -616,7 +616,6 @@ static int mac_probe(struct platform_dev struct platform_device *of_dev; struct resource res; struct mac_priv_s *priv; @@ -615,7 +615,7 @@ Signed-off-by: David S. Miller u32 val; u8 fman_id; phy_interface_t phy_if; -@@ -723,11 +722,9 @@ static int mac_probe(struct platform_dev +@@ -734,11 +733,9 @@ static int mac_probe(struct platform_dev priv->cell_index = (u8)val; /* Get the MAC address */ @@ -629,7 +629,7 @@ Signed-off-by: David S. Miller /* Get the port handles */ nph = of_count_phandle_with_args(mac_node, "fsl,fman-ports", NULL); -@@ -853,7 +850,7 @@ static int mac_probe(struct platform_dev +@@ -864,7 +861,7 @@ static int mac_probe(struct platform_dev if (err < 0) dev_err(dev, "fman_set_mac_active_pause() = %d\n", err); @@ -819,7 +819,7 @@ Signed-off-by: David S. Miller if (is_valid_ether_addr(hw_mac_addr)) { --- a/drivers/net/ethernet/marvell/prestera/prestera_main.c +++ b/drivers/net/ethernet/marvell/prestera/prestera_main.c -@@ -462,20 +462,17 @@ static int prestera_switch_set_base_mac_ +@@ -466,20 +466,17 @@ static int prestera_switch_set_base_mac_ { struct device_node *base_mac_np; struct device_node *np; @@ -933,7 +933,7 @@ Signed-off-by: David S. Miller } --- a/drivers/net/ethernet/microchip/lan743x_main.c +++ b/drivers/net/ethernet/microchip/lan743x_main.c -@@ -2837,7 +2837,6 @@ static int lan743x_pcidev_probe(struct p +@@ -2831,7 +2831,6 @@ static int lan743x_pcidev_probe(struct p { struct lan743x_adapter *adapter = NULL; struct net_device *netdev = NULL; @@ -941,7 +941,7 @@ Signed-off-by: David S. Miller int ret = -ENODEV; netdev = devm_alloc_etherdev(&pdev->dev, -@@ -2854,9 +2853,7 @@ static int lan743x_pcidev_probe(struct p +@@ -2848,9 +2847,7 @@ static int lan743x_pcidev_probe(struct p NETIF_MSG_IFDOWN | NETIF_MSG_TX_QUEUED; netdev->max_mtu = LAN743X_MAX_FRAME_SIZE; @@ -1360,7 +1360,7 @@ Signed-off-by: David S. Miller int irq; --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c -@@ -4957,7 +4957,7 @@ int stmmac_dvr_probe(struct device *devi +@@ -4988,7 +4988,7 @@ int stmmac_dvr_probe(struct device *devi priv->wol_irq = res->wol_irq; priv->lpi_irq = res->lpi_irq; @@ -1371,7 +1371,7 @@ Signed-off-by: David S. Miller dev_set_drvdata(device, priv->dev); --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c -@@ -394,7 +394,7 @@ static int stmmac_of_get_mac_mode(struct +@@ -395,7 +395,7 @@ static int stmmac_of_get_mac_mode(struct * set some private fields that will be used by the main at runtime. */ struct plat_stmmacenet_data * @@ -1380,7 +1380,7 @@ Signed-off-by: David S. Miller { struct device_node *np = pdev->dev.of_node; struct plat_stmmacenet_data *plat; -@@ -406,12 +406,12 @@ stmmac_probe_config_dt(struct platform_d +@@ -407,12 +407,12 @@ stmmac_probe_config_dt(struct platform_d if (!plat) return ERR_PTR(-ENOMEM); @@ -1398,7 +1398,7 @@ Signed-off-by: David S. Miller } phy_mode = device_get_phy_mode(&pdev->dev); -@@ -643,7 +643,7 @@ void stmmac_remove_config_dt(struct plat +@@ -644,7 +644,7 @@ void stmmac_remove_config_dt(struct plat } #else struct plat_stmmacenet_data * @@ -1595,7 +1595,7 @@ Signed-off-by: David S. Miller __be32 *p; bool little_endian; int rc = 0; -@@ -1561,8 +1561,8 @@ static int temac_probe(struct platform_d +@@ -1563,8 +1563,8 @@ static int temac_probe(struct platform_d if (temac_np) { /* Retrieve the MAC address */ @@ -1608,7 +1608,7 @@ Signed-off-by: David S. Miller } --- a/drivers/net/ethernet/xilinx/xilinx_axienet_main.c +++ b/drivers/net/ethernet/xilinx/xilinx_axienet_main.c -@@ -1803,8 +1803,8 @@ static int axienet_probe(struct platform +@@ -1843,8 +1843,8 @@ static int axienet_probe(struct platform struct device_node *np; struct axienet_local *lp; struct net_device *ndev; @@ -1618,7 +1618,7 @@ Signed-off-by: David S. Miller int addr_width = 32; u32 value; -@@ -2004,13 +2004,14 @@ static int axienet_probe(struct platform +@@ -2044,13 +2044,14 @@ static int axienet_probe(struct platform dev_info(&pdev->dev, "Ethernet core IRQ not defined\n"); /* Retrieve the MAC address */ diff --git a/target/linux/generic/backport-5.10/782-net-next-2-of-net-fix-of_get_mac_addr_nvmem-for-non-platform-devices.patch b/target/linux/generic/backport-5.10/732-net-next-2-of-net-fix-of_get_mac_addr_nvmem-for-non-platform-devices.patch similarity index 100% rename from target/linux/generic/backport-5.10/782-net-next-2-of-net-fix-of_get_mac_addr_nvmem-for-non-platform-devices.patch rename to target/linux/generic/backport-5.10/732-net-next-2-of-net-fix-of_get_mac_addr_nvmem-for-non-platform-devices.patch diff --git a/target/linux/generic/backport-5.10/733-v5.15-0001-net-bgmac-bcma-handle-deferred-probe-error-due-to-ma.patch b/target/linux/generic/backport-5.10/733-v5.15-0001-net-bgmac-bcma-handle-deferred-probe-error-due-to-ma.patch new file mode 100644 index 000000000..6e7f20634 --- /dev/null +++ b/target/linux/generic/backport-5.10/733-v5.15-0001-net-bgmac-bcma-handle-deferred-probe-error-due-to-ma.patch @@ -0,0 +1,42 @@ +From 029497e66bdc762e001880e4c85a91f35a54b1e2 Mon Sep 17 00:00:00 2001 +From: Christian Lamparter +Date: Sun, 19 Sep 2021 13:57:25 +0200 +Subject: [PATCH] net: bgmac-bcma: handle deferred probe error due to + mac-address +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Due to the inclusion of nvmem handling into the mac-address getter +function of_get_mac_address() by +commit d01f449c008a ("of_net: add NVMEM support to of_get_mac_address") +it is now possible to get a -EPROBE_DEFER return code. Which did cause +bgmac to assign a random ethernet address. + +This exact issue happened on my Meraki MR32. The nvmem provider is +an EEPROM (at24c64) which gets instantiated once the module +driver is loaded... This happens once the filesystem becomes available. + +With this patch, bgmac_probe() will propagate the -EPROBE_DEFER error. +Then the driver subsystem will reschedule the probe at a later time. + +Cc: Petr Štetiar +Cc: Michael Walle +Fixes: d01f449c008a ("of_net: add NVMEM support to of_get_mac_address") +Signed-off-by: Christian Lamparter +Signed-off-by: David S. Miller +--- + drivers/net/ethernet/broadcom/bgmac-bcma.c | 2 ++ + 1 file changed, 2 insertions(+) + +--- a/drivers/net/ethernet/broadcom/bgmac-bcma.c ++++ b/drivers/net/ethernet/broadcom/bgmac-bcma.c +@@ -129,6 +129,8 @@ static int bgmac_probe(struct bcma_devic + bcma_set_drvdata(core, bgmac); + + err = of_get_mac_address(bgmac->dev->of_node, bgmac->net_dev->dev_addr); ++ if (err == -EPROBE_DEFER) ++ return err; + + /* If no MAC address assigned via device tree, check SPROM */ + if (err) { diff --git a/target/linux/generic/backport-5.10/733-v5.15-0002-net-bgmac-platform-handle-mac-address-deferral.patch b/target/linux/generic/backport-5.10/733-v5.15-0002-net-bgmac-platform-handle-mac-address-deferral.patch new file mode 100644 index 000000000..bde62f3b1 --- /dev/null +++ b/target/linux/generic/backport-5.10/733-v5.15-0002-net-bgmac-platform-handle-mac-address-deferral.patch @@ -0,0 +1,36 @@ +From 763716a55cb1f480ffe1a9702e6b5d9ea1a80a24 Mon Sep 17 00:00:00 2001 +From: Matthew Hagan +Date: Sat, 25 Sep 2021 11:36:27 +0000 +Subject: [PATCH] net: bgmac-platform: handle mac-address deferral + +This patch is a replication of Christian Lamparter's "net: bgmac-bcma: +handle deferred probe error due to mac-address" patch for the +bgmac-platform driver [1]. + +As is the case with the bgmac-bcma driver, this change is to cover the +scenario where the MAC address cannot yet be discovered due to reliance +on an nvmem provider which is yet to be instantiated, resulting in a +random address being assigned that has to be manually overridden. + +[1] https://lore.kernel.org/netdev/20210919115725.29064-1-chunkeey@gmail.com + +Signed-off-by: Matthew Hagan +Reviewed-by: Andrew Lunn +Reviewed-by: Florian Fainelli +Signed-off-by: David S. Miller +--- + drivers/net/ethernet/broadcom/bgmac-platform.c | 3 +++ + 1 file changed, 3 insertions(+) + +--- a/drivers/net/ethernet/broadcom/bgmac-platform.c ++++ b/drivers/net/ethernet/broadcom/bgmac-platform.c +@@ -193,6 +193,9 @@ static int bgmac_probe(struct platform_d + bgmac->dma_dev = &pdev->dev; + + ret = of_get_mac_address(np, bgmac->net_dev->dev_addr); ++ if (ret == -EPROBE_DEFER) ++ return ret; ++ + if (ret) + dev_warn(&pdev->dev, + "MAC address not present in device tree\n"); diff --git a/target/linux/generic/backport-5.10/734-v5.16-0001-net-bgmac-improve-handling-PHY.patch b/target/linux/generic/backport-5.10/734-v5.16-0001-net-bgmac-improve-handling-PHY.patch new file mode 100644 index 000000000..6788a2ec3 --- /dev/null +++ b/target/linux/generic/backport-5.10/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-5.10/734-v5.16-0002-net-bgmac-support-MDIO-described-in-DT.patch b/target/linux/generic/backport-5.10/734-v5.16-0002-net-bgmac-support-MDIO-described-in-DT.patch new file mode 100644 index 000000000..f13482827 --- /dev/null +++ b/target/linux/generic/backport-5.10/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-5.10/735-v5.14-01-net-dsa-qca8k-change-simple-print-to-dev-variant.patch b/target/linux/generic/backport-5.10/735-v5.14-01-net-dsa-qca8k-change-simple-print-to-dev-variant.patch new file mode 100644 index 000000000..b8e6d9b61 --- /dev/null +++ b/target/linux/generic/backport-5.10/735-v5.14-01-net-dsa-qca8k-change-simple-print-to-dev-variant.patch @@ -0,0 +1,35 @@ +From 5d9e068402dcf7354cc8ee66c2152845306d2ccb Mon Sep 17 00:00:00 2001 +From: Ansuel Smith +Date: Fri, 14 May 2021 22:59:51 +0200 +Subject: [PATCH] net: dsa: qca8k: change simple print to dev variant + +Change pr_err and pr_warn to dev variant. + +Signed-off-by: Ansuel Smith +Reviewed-by: Florian Fainelli +Reviewed-by: Andrew Lunn +Signed-off-by: David S. Miller +--- + 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 +@@ -701,7 +701,7 @@ qca8k_setup(struct dsa_switch *ds) + + /* Make sure that port 0 is the cpu port */ + if (!dsa_is_cpu_port(ds, 0)) { +- pr_err("port 0 is not the CPU port\n"); ++ dev_err(priv->dev, "port 0 is not the CPU port"); + return -EINVAL; + } + +@@ -711,7 +711,7 @@ qca8k_setup(struct dsa_switch *ds) + priv->regmap = devm_regmap_init(ds->dev, NULL, priv, + &qca8k_regmap_config); + if (IS_ERR(priv->regmap)) +- pr_warn("regmap initialization failed"); ++ dev_warn(priv->dev, "regmap initialization failed"); + + ret = qca8k_setup_mdio_bus(priv); + if (ret) diff --git a/target/linux/generic/backport-5.10/735-v5.14-02-net-dsa-qca8k-use-iopoll-macro-for-qca8k_busy_wait.patch b/target/linux/generic/backport-5.10/735-v5.14-02-net-dsa-qca8k-use-iopoll-macro-for-qca8k_busy_wait.patch new file mode 100644 index 000000000..ff8288d48 --- /dev/null +++ b/target/linux/generic/backport-5.10/735-v5.14-02-net-dsa-qca8k-use-iopoll-macro-for-qca8k_busy_wait.patch @@ -0,0 +1,61 @@ +From 2ad255f2faaffb3af786031fba2e7955454b558a Mon Sep 17 00:00:00 2001 +From: Ansuel Smith +Date: Fri, 14 May 2021 22:59:52 +0200 +Subject: [PATCH] net: dsa: qca8k: use iopoll macro for qca8k_busy_wait + +Use iopoll macro instead of while loop. + +Signed-off-by: Ansuel Smith +Reviewed-by: Andrew Lunn +Signed-off-by: David S. Miller +--- + drivers/net/dsa/qca8k.c | 23 +++++++++++------------ + drivers/net/dsa/qca8k.h | 2 ++ + 2 files changed, 13 insertions(+), 12 deletions(-) + +--- a/drivers/net/dsa/qca8k.c ++++ b/drivers/net/dsa/qca8k.c +@@ -262,21 +262,20 @@ static struct regmap_config qca8k_regmap + static int + qca8k_busy_wait(struct qca8k_priv *priv, u32 reg, u32 mask) + { +- unsigned long timeout; ++ u32 val; ++ int ret; + +- timeout = jiffies + msecs_to_jiffies(20); ++ ret = read_poll_timeout(qca8k_read, val, !(val & mask), ++ 0, QCA8K_BUSY_WAIT_TIMEOUT * USEC_PER_MSEC, false, ++ priv, reg); + +- /* loop until the busy flag has cleared */ +- do { +- u32 val = qca8k_read(priv, reg); +- int busy = val & mask; ++ /* Check if qca8k_read has failed for a different reason ++ * before returning -ETIMEDOUT ++ */ ++ if (ret < 0 && val < 0) ++ return val; + +- if (!busy) +- break; +- cond_resched(); +- } while (!time_after_eq(jiffies, timeout)); +- +- return time_after_eq(jiffies, timeout); ++ return ret; + } + + static void +--- a/drivers/net/dsa/qca8k.h ++++ b/drivers/net/dsa/qca8k.h +@@ -18,6 +18,8 @@ + #define PHY_ID_QCA8337 0x004dd036 + #define QCA8K_ID_QCA8337 0x13 + ++#define QCA8K_BUSY_WAIT_TIMEOUT 20 ++ + #define QCA8K_NUM_FDB_RECORDS 2048 + + #define QCA8K_CPU_PORT 0 diff --git a/target/linux/generic/backport-5.10/735-v5.14-03-net-dsa-qca8k-improve-qca8k-read-write-rmw-bus-acces.patch b/target/linux/generic/backport-5.10/735-v5.14-03-net-dsa-qca8k-improve-qca8k-read-write-rmw-bus-acces.patch new file mode 100644 index 000000000..c40358987 --- /dev/null +++ b/target/linux/generic/backport-5.10/735-v5.14-03-net-dsa-qca8k-improve-qca8k-read-write-rmw-bus-acces.patch @@ -0,0 +1,86 @@ +From 504bf65931824eda83494e5b5d75686e27ace03e Mon Sep 17 00:00:00 2001 +From: Ansuel Smith +Date: Fri, 14 May 2021 22:59:53 +0200 +Subject: [PATCH] net: dsa: qca8k: improve qca8k read/write/rmw bus access + +Put bus in local variable to improve faster access to the mdio bus. + +Signed-off-by: Ansuel Smith +Reviewed-by: Andrew Lunn +Signed-off-by: David S. Miller +--- + drivers/net/dsa/qca8k.c | 29 ++++++++++++++++------------- + 1 file changed, 16 insertions(+), 13 deletions(-) + +--- a/drivers/net/dsa/qca8k.c ++++ b/drivers/net/dsa/qca8k.c +@@ -142,17 +142,18 @@ qca8k_set_page(struct mii_bus *bus, u16 + static u32 + qca8k_read(struct qca8k_priv *priv, u32 reg) + { ++ struct mii_bus *bus = priv->bus; + u16 r1, r2, page; + u32 val; + + qca8k_split_addr(reg, &r1, &r2, &page); + +- mutex_lock_nested(&priv->bus->mdio_lock, MDIO_MUTEX_NESTED); ++ mutex_lock_nested(&bus->mdio_lock, MDIO_MUTEX_NESTED); + +- qca8k_set_page(priv->bus, page); +- val = qca8k_mii_read32(priv->bus, 0x10 | r2, r1); ++ qca8k_set_page(bus, page); ++ val = qca8k_mii_read32(bus, 0x10 | r2, r1); + +- mutex_unlock(&priv->bus->mdio_lock); ++ mutex_unlock(&bus->mdio_lock); + + return val; + } +@@ -160,35 +161,37 @@ qca8k_read(struct qca8k_priv *priv, u32 + static void + qca8k_write(struct qca8k_priv *priv, u32 reg, u32 val) + { ++ struct mii_bus *bus = priv->bus; + u16 r1, r2, page; + + qca8k_split_addr(reg, &r1, &r2, &page); + +- mutex_lock_nested(&priv->bus->mdio_lock, MDIO_MUTEX_NESTED); ++ mutex_lock_nested(&bus->mdio_lock, MDIO_MUTEX_NESTED); + +- qca8k_set_page(priv->bus, page); +- qca8k_mii_write32(priv->bus, 0x10 | r2, r1, val); ++ qca8k_set_page(bus, page); ++ qca8k_mii_write32(bus, 0x10 | r2, r1, val); + +- mutex_unlock(&priv->bus->mdio_lock); ++ mutex_unlock(&bus->mdio_lock); + } + + static u32 + qca8k_rmw(struct qca8k_priv *priv, u32 reg, u32 mask, u32 val) + { ++ struct mii_bus *bus = priv->bus; + u16 r1, r2, page; + u32 ret; + + qca8k_split_addr(reg, &r1, &r2, &page); + +- mutex_lock_nested(&priv->bus->mdio_lock, MDIO_MUTEX_NESTED); ++ mutex_lock_nested(&bus->mdio_lock, MDIO_MUTEX_NESTED); + +- qca8k_set_page(priv->bus, page); +- ret = qca8k_mii_read32(priv->bus, 0x10 | r2, r1); ++ qca8k_set_page(bus, page); ++ ret = qca8k_mii_read32(bus, 0x10 | r2, r1); + ret &= ~mask; + ret |= val; +- qca8k_mii_write32(priv->bus, 0x10 | r2, r1, ret); ++ qca8k_mii_write32(bus, 0x10 | r2, r1, ret); + +- mutex_unlock(&priv->bus->mdio_lock); ++ mutex_unlock(&bus->mdio_lock); + + return ret; + } diff --git a/target/linux/generic/backport-5.10/735-v5.14-04-net-dsa-qca8k-handle-qca8k_set_page-errors.patch b/target/linux/generic/backport-5.10/735-v5.14-04-net-dsa-qca8k-handle-qca8k_set_page-errors.patch new file mode 100644 index 000000000..6be494a8c --- /dev/null +++ b/target/linux/generic/backport-5.10/735-v5.14-04-net-dsa-qca8k-handle-qca8k_set_page-errors.patch @@ -0,0 +1,101 @@ +From ba5707ec58cfb6853dff41c2aae72deb6a03d389 Mon Sep 17 00:00:00 2001 +From: Ansuel Smith +Date: Fri, 14 May 2021 22:59:54 +0200 +Subject: [PATCH] net: dsa: qca8k: handle qca8k_set_page errors + +With a remote possibility, the set_page function can fail. Since this is +a critical part of the write/read qca8k regs, propagate the error and +terminate any read/write operation. + +Signed-off-by: Ansuel Smith +Reviewed-by: Andrew Lunn +Signed-off-by: David S. Miller +--- + drivers/net/dsa/qca8k.c | 33 ++++++++++++++++++++++++++------- + 1 file changed, 26 insertions(+), 7 deletions(-) + +--- a/drivers/net/dsa/qca8k.c ++++ b/drivers/net/dsa/qca8k.c +@@ -127,16 +127,23 @@ qca8k_mii_write32(struct mii_bus *bus, i + "failed to write qca8k 32bit register\n"); + } + +-static void ++static int + qca8k_set_page(struct mii_bus *bus, u16 page) + { ++ int ret; ++ + if (page == qca8k_current_page) +- return; ++ return 0; + +- if (bus->write(bus, 0x18, 0, page) < 0) ++ ret = bus->write(bus, 0x18, 0, page); ++ if (ret < 0) { + dev_err_ratelimited(&bus->dev, + "failed to set qca8k page\n"); ++ return ret; ++ } ++ + qca8k_current_page = page; ++ return 0; + } + + static u32 +@@ -150,11 +157,14 @@ qca8k_read(struct qca8k_priv *priv, u32 + + mutex_lock_nested(&bus->mdio_lock, MDIO_MUTEX_NESTED); + +- qca8k_set_page(bus, page); ++ val = qca8k_set_page(bus, page); ++ if (val < 0) ++ goto exit; ++ + val = qca8k_mii_read32(bus, 0x10 | r2, r1); + ++exit: + mutex_unlock(&bus->mdio_lock); +- + return val; + } + +@@ -163,14 +173,19 @@ qca8k_write(struct qca8k_priv *priv, u32 + { + struct mii_bus *bus = priv->bus; + u16 r1, r2, page; ++ int ret; + + qca8k_split_addr(reg, &r1, &r2, &page); + + mutex_lock_nested(&bus->mdio_lock, MDIO_MUTEX_NESTED); + +- qca8k_set_page(bus, page); ++ ret = qca8k_set_page(bus, page); ++ if (ret < 0) ++ goto exit; ++ + qca8k_mii_write32(bus, 0x10 | r2, r1, val); + ++exit: + mutex_unlock(&bus->mdio_lock); + } + +@@ -185,12 +200,16 @@ qca8k_rmw(struct qca8k_priv *priv, u32 r + + mutex_lock_nested(&bus->mdio_lock, MDIO_MUTEX_NESTED); + +- qca8k_set_page(bus, page); ++ ret = qca8k_set_page(bus, page); ++ if (ret < 0) ++ goto exit; ++ + ret = qca8k_mii_read32(bus, 0x10 | r2, r1); + ret &= ~mask; + ret |= val; + qca8k_mii_write32(bus, 0x10 | r2, r1, ret); + ++exit: + mutex_unlock(&bus->mdio_lock); + + return ret; diff --git a/target/linux/generic/backport-5.10/735-v5.14-05-net-dsa-qca8k-handle-error-with-qca8k_read-operation.patch b/target/linux/generic/backport-5.10/735-v5.14-05-net-dsa-qca8k-handle-error-with-qca8k_read-operation.patch new file mode 100644 index 000000000..3349b7897 --- /dev/null +++ b/target/linux/generic/backport-5.10/735-v5.14-05-net-dsa-qca8k-handle-error-with-qca8k_read-operation.patch @@ -0,0 +1,207 @@ +From 028f5f8ef44fcf87a456772cbb9f0d90a0a22884 Mon Sep 17 00:00:00 2001 +From: Ansuel Smith +Date: Fri, 14 May 2021 22:59:55 +0200 +Subject: [PATCH] net: dsa: qca8k: handle error with qca8k_read operation + +qca8k_read can fail. Rework any user to handle error values and +correctly return. + +Signed-off-by: Ansuel Smith +Reviewed-by: Andrew Lunn +Signed-off-by: David S. Miller +--- + drivers/net/dsa/qca8k.c | 73 ++++++++++++++++++++++++++++++++--------- + 1 file changed, 58 insertions(+), 15 deletions(-) + +--- a/drivers/net/dsa/qca8k.c ++++ b/drivers/net/dsa/qca8k.c +@@ -231,8 +231,13 @@ static int + qca8k_regmap_read(void *ctx, uint32_t reg, uint32_t *val) + { + struct qca8k_priv *priv = (struct qca8k_priv *)ctx; ++ int ret; ++ ++ ret = qca8k_read(priv, reg); ++ if (ret < 0) ++ return ret; + +- *val = qca8k_read(priv, reg); ++ *val = ret; + + return 0; + } +@@ -300,15 +305,20 @@ qca8k_busy_wait(struct qca8k_priv *priv, + return ret; + } + +-static void ++static int + qca8k_fdb_read(struct qca8k_priv *priv, struct qca8k_fdb *fdb) + { +- u32 reg[4]; ++ u32 reg[4], val; + int i; + + /* load the ARL table into an array */ +- for (i = 0; i < 4; i++) +- reg[i] = qca8k_read(priv, QCA8K_REG_ATU_DATA0 + (i * 4)); ++ for (i = 0; i < 4; i++) { ++ val = qca8k_read(priv, QCA8K_REG_ATU_DATA0 + (i * 4)); ++ if (val < 0) ++ return val; ++ ++ reg[i] = val; ++ } + + /* vid - 83:72 */ + fdb->vid = (reg[2] >> QCA8K_ATU_VID_S) & QCA8K_ATU_VID_M; +@@ -323,6 +333,8 @@ qca8k_fdb_read(struct qca8k_priv *priv, + 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; ++ ++ return 0; + } + + static void +@@ -374,6 +386,8 @@ qca8k_fdb_access(struct qca8k_priv *priv + /* Check for table full violation when adding an entry */ + if (cmd == QCA8K_FDB_LOAD) { + reg = qca8k_read(priv, QCA8K_REG_ATU_FUNC); ++ if (reg < 0) ++ return reg; + if (reg & QCA8K_ATU_FUNC_FULL) + return -1; + } +@@ -388,10 +402,10 @@ qca8k_fdb_next(struct qca8k_priv *priv, + + 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) +- qca8k_fdb_read(priv, fdb); ++ if (ret < 0) ++ return ret; + +- return ret; ++ return qca8k_fdb_read(priv, fdb); + } + + static int +@@ -449,6 +463,8 @@ qca8k_vlan_access(struct qca8k_priv *pri + /* Check for table full violation when adding an entry */ + if (cmd == QCA8K_VLAN_LOAD) { + reg = qca8k_read(priv, QCA8K_REG_VTU_FUNC1); ++ if (reg < 0) ++ return reg; + if (reg & QCA8K_VTU_FUNC1_FULL) + return -ENOMEM; + } +@@ -475,6 +491,8 @@ qca8k_vlan_add(struct qca8k_priv *priv, + goto out; + + reg = qca8k_read(priv, QCA8K_REG_VTU_FUNC0); ++ if (reg < 0) ++ return reg; + reg |= QCA8K_VTU_FUNC0_VALID | QCA8K_VTU_FUNC0_IVL_EN; + reg &= ~(QCA8K_VTU_FUNC0_EG_MODE_MASK << QCA8K_VTU_FUNC0_EG_MODE_S(port)); + if (untagged) +@@ -506,6 +524,8 @@ qca8k_vlan_del(struct qca8k_priv *priv, + goto out; + + reg = qca8k_read(priv, QCA8K_REG_VTU_FUNC0); ++ if (reg < 0) ++ return reg; + reg &= ~(3 << QCA8K_VTU_FUNC0_EG_MODE_S(port)); + reg |= QCA8K_VTU_FUNC0_EG_MODE_NOT << + QCA8K_VTU_FUNC0_EG_MODE_S(port); +@@ -621,8 +641,11 @@ qca8k_mdio_read(struct qca8k_priv *priv, + QCA8K_MDIO_MASTER_BUSY)) + return -ETIMEDOUT; + +- val = (qca8k_read(priv, QCA8K_MDIO_MASTER_CTRL) & +- QCA8K_MDIO_MASTER_DATA_MASK); ++ val = qca8k_read(priv, QCA8K_MDIO_MASTER_CTRL); ++ if (val < 0) ++ return val; ++ ++ val &= QCA8K_MDIO_MASTER_DATA_MASK; + + return val; + } +@@ -978,6 +1001,8 @@ qca8k_phylink_mac_link_state(struct dsa_ + u32 reg; + + reg = qca8k_read(priv, QCA8K_REG_PORT_STATUS(port)); ++ if (reg < 0) ++ return reg; + + state->link = !!(reg & QCA8K_PORT_STATUS_LINK_UP); + state->an_complete = state->link; +@@ -1078,18 +1103,26 @@ qca8k_get_ethtool_stats(struct dsa_switc + { + struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv; + const struct qca8k_mib_desc *mib; +- u32 reg, i; ++ u32 reg, i, val; + u64 hi; + + for (i = 0; i < ARRAY_SIZE(ar8327_mib); i++) { + mib = &ar8327_mib[i]; + reg = QCA8K_PORT_MIB_COUNTER(port) + mib->offset; + +- data[i] = qca8k_read(priv, reg); ++ val = qca8k_read(priv, reg); ++ if (val < 0) ++ continue; ++ + if (mib->size == 2) { + hi = qca8k_read(priv, reg + 4); +- data[i] |= hi << 32; ++ if (hi < 0) ++ continue; + } ++ ++ data[i] = val; ++ if (mib->size == 2) ++ data[i] |= hi << 32; + } + } + +@@ -1107,18 +1140,25 @@ qca8k_set_mac_eee(struct dsa_switch *ds, + { + struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv; + u32 lpi_en = QCA8K_REG_EEE_CTRL_LPI_EN(port); ++ int ret = 0; + u32 reg; + + mutex_lock(&priv->reg_mutex); + reg = qca8k_read(priv, QCA8K_REG_EEE_CTRL); ++ if (reg < 0) { ++ ret = reg; ++ goto exit; ++ } ++ + if (eee->eee_enabled) + reg |= lpi_en; + else + reg &= ~lpi_en; + qca8k_write(priv, QCA8K_REG_EEE_CTRL, reg); +- mutex_unlock(&priv->reg_mutex); + +- return 0; ++exit: ++ mutex_unlock(&priv->reg_mutex); ++ return ret; + } + + static int +@@ -1456,6 +1496,9 @@ qca8k_sw_probe(struct mdio_device *mdiod + + /* read the switches ID register */ + id = qca8k_read(priv, QCA8K_REG_MASK_CTRL); ++ if (id < 0) ++ return id; ++ + id >>= QCA8K_MASK_CTRL_ID_S; + id &= QCA8K_MASK_CTRL_ID_M; + if (id != QCA8K_ID_QCA8337) diff --git a/target/linux/generic/backport-5.10/735-v5.14-06-net-dsa-qca8k-handle-error-with-qca8k_write-operatio.patch b/target/linux/generic/backport-5.10/735-v5.14-06-net-dsa-qca8k-handle-error-with-qca8k_write-operatio.patch new file mode 100644 index 000000000..1e0e224c3 --- /dev/null +++ b/target/linux/generic/backport-5.10/735-v5.14-06-net-dsa-qca8k-handle-error-with-qca8k_write-operatio.patch @@ -0,0 +1,263 @@ +From d7805757c75c76e9518fc1023a29f0c4eed5b581 Mon Sep 17 00:00:00 2001 +From: Ansuel Smith +Date: Fri, 14 May 2021 22:59:56 +0200 +Subject: [PATCH] net: dsa: qca8k: handle error with qca8k_write operation + +qca8k_write can fail. Rework any user to handle error values and +correctly return. + +Signed-off-by: Ansuel Smith +Reviewed-by: Andrew Lunn +Signed-off-by: David S. Miller +--- + drivers/net/dsa/qca8k.c | 102 ++++++++++++++++++++++++++-------------- + 1 file changed, 67 insertions(+), 35 deletions(-) + +--- a/drivers/net/dsa/qca8k.c ++++ b/drivers/net/dsa/qca8k.c +@@ -168,7 +168,7 @@ exit: + return val; + } + +-static void ++static int + qca8k_write(struct qca8k_priv *priv, u32 reg, u32 val) + { + struct mii_bus *bus = priv->bus; +@@ -187,6 +187,7 @@ qca8k_write(struct qca8k_priv *priv, u32 + + exit: + mutex_unlock(&bus->mdio_lock); ++ return ret; + } + + static u32 +@@ -247,9 +248,7 @@ qca8k_regmap_write(void *ctx, uint32_t r + { + struct qca8k_priv *priv = (struct qca8k_priv *)ctx; + +- qca8k_write(priv, reg, val); +- +- return 0; ++ return qca8k_write(priv, reg, val); + } + + static const struct regmap_range qca8k_readable_ranges[] = { +@@ -367,6 +366,7 @@ 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; +@@ -377,7 +377,9 @@ qca8k_fdb_access(struct qca8k_priv *priv + } + + /* Write the function register triggering the table access */ +- qca8k_write(priv, QCA8K_REG_ATU_FUNC, reg); ++ ret = qca8k_write(priv, QCA8K_REG_ATU_FUNC, reg); ++ if (ret) ++ return ret; + + /* wait for completion */ + if (qca8k_busy_wait(priv, QCA8K_REG_ATU_FUNC, QCA8K_ATU_FUNC_BUSY)) +@@ -447,6 +449,7 @@ 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; +@@ -454,7 +457,9 @@ qca8k_vlan_access(struct qca8k_priv *pri + reg |= vid << QCA8K_VTU_FUNC1_VID_S; + + /* Write the function register triggering the table access */ +- qca8k_write(priv, QCA8K_REG_VTU_FUNC1, reg); ++ ret = qca8k_write(priv, QCA8K_REG_VTU_FUNC1, reg); ++ if (ret) ++ return ret; + + /* wait for completion */ + if (qca8k_busy_wait(priv, QCA8K_REG_VTU_FUNC1, QCA8K_VTU_FUNC1_BUSY)) +@@ -502,7 +507,9 @@ qca8k_vlan_add(struct qca8k_priv *priv, + reg |= QCA8K_VTU_FUNC0_EG_MODE_TAG << + QCA8K_VTU_FUNC0_EG_MODE_S(port); + +- qca8k_write(priv, QCA8K_REG_VTU_FUNC0, reg); ++ ret = qca8k_write(priv, QCA8K_REG_VTU_FUNC0, reg); ++ if (ret) ++ return ret; + ret = qca8k_vlan_access(priv, QCA8K_VLAN_LOAD, vid); + + out: +@@ -545,7 +552,9 @@ qca8k_vlan_del(struct qca8k_priv *priv, + if (del) { + ret = qca8k_vlan_access(priv, QCA8K_VLAN_PURGE, vid); + } else { +- qca8k_write(priv, QCA8K_REG_VTU_FUNC0, reg); ++ ret = qca8k_write(priv, QCA8K_REG_VTU_FUNC0, reg); ++ if (ret) ++ return ret; + ret = qca8k_vlan_access(priv, QCA8K_VLAN_LOAD, vid); + } + +@@ -555,15 +564,20 @@ out: + return ret; + } + +-static void ++static int + qca8k_mib_init(struct qca8k_priv *priv) + { ++ int ret; ++ + mutex_lock(&priv->reg_mutex); + qca8k_reg_set(priv, QCA8K_REG_MIB, QCA8K_MIB_FLUSH | QCA8K_MIB_BUSY); + qca8k_busy_wait(priv, QCA8K_REG_MIB, QCA8K_MIB_BUSY); + qca8k_reg_set(priv, QCA8K_REG_MIB, QCA8K_MIB_CPU_KEEP); +- qca8k_write(priv, QCA8K_REG_MODULE_EN, QCA8K_MODULE_EN_MIB); ++ ++ ret = qca8k_write(priv, QCA8K_REG_MODULE_EN, QCA8K_MODULE_EN_MIB); ++ + mutex_unlock(&priv->reg_mutex); ++ return ret; + } + + static void +@@ -600,6 +614,7 @@ static int + qca8k_mdio_write(struct qca8k_priv *priv, int port, u32 regnum, u16 data) + { + u32 phy, val; ++ int ret; + + if (regnum >= QCA8K_MDIO_MASTER_MAX_REG) + return -EINVAL; +@@ -613,7 +628,9 @@ qca8k_mdio_write(struct qca8k_priv *priv + QCA8K_MDIO_MASTER_REG_ADDR(regnum) | + QCA8K_MDIO_MASTER_DATA(data); + +- qca8k_write(priv, QCA8K_MDIO_MASTER_CTRL, val); ++ ret = qca8k_write(priv, QCA8K_MDIO_MASTER_CTRL, val); ++ if (ret) ++ return ret; + + return qca8k_busy_wait(priv, QCA8K_MDIO_MASTER_CTRL, + QCA8K_MDIO_MASTER_BUSY); +@@ -623,6 +640,7 @@ static int + qca8k_mdio_read(struct qca8k_priv *priv, int port, u32 regnum) + { + u32 phy, val; ++ int ret; + + if (regnum >= QCA8K_MDIO_MASTER_MAX_REG) + return -EINVAL; +@@ -635,7 +653,9 @@ qca8k_mdio_read(struct qca8k_priv *priv, + QCA8K_MDIO_MASTER_READ | QCA8K_MDIO_MASTER_PHY_ADDR(phy) | + QCA8K_MDIO_MASTER_REG_ADDR(regnum); + +- qca8k_write(priv, QCA8K_MDIO_MASTER_CTRL, val); ++ ret = qca8k_write(priv, QCA8K_MDIO_MASTER_CTRL, val); ++ if (ret) ++ return ret; + + if (qca8k_busy_wait(priv, QCA8K_MDIO_MASTER_CTRL, + QCA8K_MDIO_MASTER_BUSY)) +@@ -766,12 +786,18 @@ qca8k_setup(struct dsa_switch *ds) + QCA8K_GLOBAL_FW_CTRL0_CPU_PORT_EN); + + /* Enable MIB counters */ +- qca8k_mib_init(priv); ++ ret = qca8k_mib_init(priv); ++ if (ret) ++ dev_warn(priv->dev, "mib init failed"); + + /* Enable QCA header mode on the cpu port */ +- qca8k_write(priv, QCA8K_REG_PORT_HDR_CTRL(QCA8K_CPU_PORT), +- QCA8K_PORT_HDR_CTRL_ALL << QCA8K_PORT_HDR_CTRL_TX_S | +- QCA8K_PORT_HDR_CTRL_ALL << QCA8K_PORT_HDR_CTRL_RX_S); ++ ret = qca8k_write(priv, QCA8K_REG_PORT_HDR_CTRL(QCA8K_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 */ + for (i = 0; i < QCA8K_NUM_PORTS; i++) +@@ -783,11 +809,13 @@ qca8k_setup(struct dsa_switch *ds) + qca8k_port_set_status(priv, i, 0); + + /* Forward all unknown frames to CPU port for Linux processing */ +- 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); ++ 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); ++ if (ret) ++ return ret; + + /* Setup connection between CPU port & user ports */ + for (i = 0; i < QCA8K_NUM_PORTS; i++) { +@@ -815,16 +843,20 @@ qca8k_setup(struct dsa_switch *ds) + qca8k_rmw(priv, QCA8K_EGRESS_VLAN(i), + 0xfff << shift, + QCA8K_PORT_VID_DEF << shift); +- 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)); ++ 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; + } + } + + /* 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; +- qca8k_write(priv, QCA8K_MAX_FRAME_SIZE, 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"); + + /* Flush the FDB table */ + qca8k_fdb_flush(priv); +@@ -1140,8 +1172,8 @@ qca8k_set_mac_eee(struct dsa_switch *ds, + { + struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv; + u32 lpi_en = QCA8K_REG_EEE_CTRL_LPI_EN(port); +- int ret = 0; + u32 reg; ++ int ret; + + mutex_lock(&priv->reg_mutex); + reg = qca8k_read(priv, QCA8K_REG_EEE_CTRL); +@@ -1154,7 +1186,7 @@ qca8k_set_mac_eee(struct dsa_switch *ds, + reg |= lpi_en; + else + reg &= ~lpi_en; +- qca8k_write(priv, QCA8K_REG_EEE_CTRL, reg); ++ ret = qca8k_write(priv, QCA8K_REG_EEE_CTRL, reg); + + exit: + mutex_unlock(&priv->reg_mutex); +@@ -1284,9 +1316,7 @@ qca8k_port_change_mtu(struct dsa_switch + mtu = priv->port_mtu[i]; + + /* Include L2 header / FCS length */ +- qca8k_write(priv, QCA8K_MAX_FRAME_SIZE, mtu + ETH_HLEN + ETH_FCS_LEN); +- +- return 0; ++ return qca8k_write(priv, QCA8K_MAX_FRAME_SIZE, mtu + ETH_HLEN + ETH_FCS_LEN); + } + + static int diff --git a/target/linux/generic/backport-5.10/735-v5.14-07-net-dsa-qca8k-handle-error-with-qca8k_rmw-operation.patch b/target/linux/generic/backport-5.10/735-v5.14-07-net-dsa-qca8k-handle-error-with-qca8k_rmw-operation.patch new file mode 100644 index 000000000..506966f1a --- /dev/null +++ b/target/linux/generic/backport-5.10/735-v5.14-07-net-dsa-qca8k-handle-error-with-qca8k_rmw-operation.patch @@ -0,0 +1,226 @@ +From aaf421425cbdec4eb6fd75a29e65c2867b0b7bbd Mon Sep 17 00:00:00 2001 +From: Ansuel Smith +Date: Fri, 14 May 2021 22:59:57 +0200 +Subject: [PATCH] net: dsa: qca8k: handle error with qca8k_rmw operation + +qca8k_rmw can fail. Rework any user to handle error values and +correctly return. Change qca8k_rmw to return the error code or 0 instead +of the reg value. The reg returned by qca8k_rmw wasn't used anywhere, +so this doesn't cause any functional change. + +Signed-off-by: Ansuel Smith +Reviewed-by: Andrew Lunn +Signed-off-by: David S. Miller +--- + drivers/net/dsa/qca8k.c | 133 +++++++++++++++++++++++++--------------- + 1 file changed, 83 insertions(+), 50 deletions(-) + +--- a/drivers/net/dsa/qca8k.c ++++ b/drivers/net/dsa/qca8k.c +@@ -190,12 +190,13 @@ exit: + return ret; + } + +-static u32 +-qca8k_rmw(struct qca8k_priv *priv, u32 reg, u32 mask, u32 val) ++static int ++qca8k_rmw(struct qca8k_priv *priv, u32 reg, u32 mask, u32 write_val) + { + struct mii_bus *bus = priv->bus; + u16 r1, r2, page; +- u32 ret; ++ u32 val; ++ int ret; + + qca8k_split_addr(reg, &r1, &r2, &page); + +@@ -205,10 +206,15 @@ qca8k_rmw(struct qca8k_priv *priv, u32 r + if (ret < 0) + goto exit; + +- ret = qca8k_mii_read32(bus, 0x10 | r2, r1); +- ret &= ~mask; +- ret |= val; +- qca8k_mii_write32(bus, 0x10 | r2, r1, ret); ++ val = qca8k_mii_read32(bus, 0x10 | r2, r1); ++ if (val < 0) { ++ ret = val; ++ goto exit; ++ } ++ ++ val &= ~mask; ++ val |= write_val; ++ qca8k_mii_write32(bus, 0x10 | r2, r1, val); + + exit: + mutex_unlock(&bus->mdio_lock); +@@ -216,16 +222,16 @@ exit: + return ret; + } + +-static void ++static int + qca8k_reg_set(struct qca8k_priv *priv, u32 reg, u32 val) + { +- qca8k_rmw(priv, reg, 0, val); ++ return qca8k_rmw(priv, reg, 0, val); + } + +-static void ++static int + qca8k_reg_clear(struct qca8k_priv *priv, u32 reg, u32 val) + { +- qca8k_rmw(priv, reg, val, 0); ++ return qca8k_rmw(priv, reg, val, 0); + } + + static int +@@ -570,12 +576,19 @@ qca8k_mib_init(struct qca8k_priv *priv) + int ret; + + mutex_lock(&priv->reg_mutex); +- qca8k_reg_set(priv, QCA8K_REG_MIB, QCA8K_MIB_FLUSH | QCA8K_MIB_BUSY); ++ ret = qca8k_reg_set(priv, QCA8K_REG_MIB, QCA8K_MIB_FLUSH | QCA8K_MIB_BUSY); ++ if (ret) ++ goto exit; ++ + qca8k_busy_wait(priv, QCA8K_REG_MIB, QCA8K_MIB_BUSY); +- qca8k_reg_set(priv, QCA8K_REG_MIB, QCA8K_MIB_CPU_KEEP); ++ ++ ret = qca8k_reg_set(priv, 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; + } +@@ -747,9 +760,8 @@ qca8k_setup_mdio_bus(struct qca8k_priv * + * a dt-overlay and driver reload changed the configuration + */ + +- qca8k_reg_clear(priv, QCA8K_MDIO_MASTER_CTRL, +- QCA8K_MDIO_MASTER_EN); +- return 0; ++ return qca8k_reg_clear(priv, QCA8K_MDIO_MASTER_CTRL, ++ QCA8K_MDIO_MASTER_EN); + } + + priv->ops.phy_read = qca8k_phy_read; +@@ -782,8 +794,12 @@ qca8k_setup(struct dsa_switch *ds) + return ret; + + /* Enable CPU Port */ +- qca8k_reg_set(priv, QCA8K_REG_GLOBAL_FW_CTRL0, +- QCA8K_GLOBAL_FW_CTRL0_CPU_PORT_EN); ++ ret = qca8k_reg_set(priv, 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); +@@ -800,9 +816,12 @@ qca8k_setup(struct dsa_switch *ds) + } + + /* Disable forwarding by default on all ports */ +- for (i = 0; i < QCA8K_NUM_PORTS; i++) +- qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(i), +- QCA8K_PORT_LOOKUP_MEMBER, 0); ++ for (i = 0; i < QCA8K_NUM_PORTS; i++) { ++ 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++) +@@ -821,28 +840,37 @@ 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)) { +- qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(QCA8K_CPU_PORT), +- QCA8K_PORT_LOOKUP_MEMBER, dsa_user_ports(ds)); ++ ret = qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(QCA8K_CPU_PORT), ++ 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)) { + int shift = 16 * (i % 2); + +- qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(i), +- QCA8K_PORT_LOOKUP_MEMBER, +- BIT(QCA8K_CPU_PORT)); ++ ret = qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(i), ++ QCA8K_PORT_LOOKUP_MEMBER, ++ BIT(QCA8K_CPU_PORT)); ++ if (ret) ++ return ret; + + /* Enable ARP Auto-learning by default */ +- qca8k_reg_set(priv, QCA8K_PORT_LOOKUP_CTRL(i), +- QCA8K_PORT_LOOKUP_LEARN); ++ ret = qca8k_reg_set(priv, 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 + */ +- qca8k_rmw(priv, QCA8K_EGRESS_VLAN(i), +- 0xfff << shift, +- QCA8K_PORT_VID_DEF << shift); ++ ret = qca8k_rmw(priv, QCA8K_EGRESS_VLAN(i), ++ 0xfff << shift, ++ QCA8K_PORT_VID_DEF << shift); ++ 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)); +@@ -1234,7 +1262,7 @@ qca8k_port_bridge_join(struct dsa_switch + { + struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv; + int port_mask = BIT(QCA8K_CPU_PORT); +- int i; ++ int i, ret; + + for (i = 1; i < QCA8K_NUM_PORTS; i++) { + if (dsa_to_port(ds, i)->bridge_dev != br) +@@ -1242,17 +1270,20 @@ qca8k_port_bridge_join(struct dsa_switch + /* Add this port to the portvlan mask of the other ports + * in the bridge + */ +- qca8k_reg_set(priv, +- QCA8K_PORT_LOOKUP_CTRL(i), +- BIT(port)); ++ ret = qca8k_reg_set(priv, ++ 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 */ +- qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(port), +- QCA8K_PORT_LOOKUP_MEMBER, port_mask); ++ ret = qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(port), ++ QCA8K_PORT_LOOKUP_MEMBER, port_mask); + +- return 0; ++ return ret; + } + + static void diff --git a/target/linux/generic/backport-5.10/735-v5.14-08-net-dsa-qca8k-handle-error-from-qca8k_busy_wait.patch b/target/linux/generic/backport-5.10/735-v5.14-08-net-dsa-qca8k-handle-error-from-qca8k_busy_wait.patch new file mode 100644 index 000000000..360ce1d94 --- /dev/null +++ b/target/linux/generic/backport-5.10/735-v5.14-08-net-dsa-qca8k-handle-error-from-qca8k_busy_wait.patch @@ -0,0 +1,66 @@ +From b7c818d194927bdc60ed15db55bb8654496a36b7 Mon Sep 17 00:00:00 2001 +From: Ansuel Smith +Date: Fri, 14 May 2021 22:59:58 +0200 +Subject: [PATCH] net: dsa: qca8k: handle error from qca8k_busy_wait + +Propagate errors from qca8k_busy_wait instead of hardcoding return +value. + +Signed-off-by: Ansuel Smith +Reviewed-by: Andrew Lunn +Signed-off-by: David S. Miller +--- + drivers/net/dsa/qca8k.c | 21 +++++++++++++-------- + 1 file changed, 13 insertions(+), 8 deletions(-) + +--- a/drivers/net/dsa/qca8k.c ++++ b/drivers/net/dsa/qca8k.c +@@ -388,8 +388,9 @@ qca8k_fdb_access(struct qca8k_priv *priv + return ret; + + /* wait for completion */ +- if (qca8k_busy_wait(priv, QCA8K_REG_ATU_FUNC, QCA8K_ATU_FUNC_BUSY)) +- return -1; ++ 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) { +@@ -468,8 +469,9 @@ qca8k_vlan_access(struct qca8k_priv *pri + return ret; + + /* wait for completion */ +- if (qca8k_busy_wait(priv, QCA8K_REG_VTU_FUNC1, QCA8K_VTU_FUNC1_BUSY)) +- return -ETIMEDOUT; ++ 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) { +@@ -580,7 +582,9 @@ qca8k_mib_init(struct qca8k_priv *priv) + if (ret) + goto exit; + +- qca8k_busy_wait(priv, QCA8K_REG_MIB, QCA8K_MIB_BUSY); ++ ret = qca8k_busy_wait(priv, QCA8K_REG_MIB, QCA8K_MIB_BUSY); ++ if (ret) ++ goto exit; + + ret = qca8k_reg_set(priv, QCA8K_REG_MIB, QCA8K_MIB_CPU_KEEP); + if (ret) +@@ -670,9 +674,10 @@ qca8k_mdio_read(struct qca8k_priv *priv, + if (ret) + return ret; + +- if (qca8k_busy_wait(priv, QCA8K_MDIO_MASTER_CTRL, +- QCA8K_MDIO_MASTER_BUSY)) +- return -ETIMEDOUT; ++ ret = qca8k_busy_wait(priv, QCA8K_MDIO_MASTER_CTRL, ++ QCA8K_MDIO_MASTER_BUSY); ++ if (ret) ++ return ret; + + val = qca8k_read(priv, QCA8K_MDIO_MASTER_CTRL); + if (val < 0) diff --git a/target/linux/generic/backport-5.10/735-v5.14-09-net-dsa-qca8k-add-support-for-qca8327-switch.patch b/target/linux/generic/backport-5.10/735-v5.14-09-net-dsa-qca8k-add-support-for-qca8327-switch.patch new file mode 100644 index 000000000..72305850c --- /dev/null +++ b/target/linux/generic/backport-5.10/735-v5.14-09-net-dsa-qca8k-add-support-for-qca8327-switch.patch @@ -0,0 +1,96 @@ +From 6e82a457e06252b59102486767539cc9c2aba60b Mon Sep 17 00:00:00 2001 +From: Ansuel Smith +Date: Fri, 14 May 2021 22:59:59 +0200 +Subject: [PATCH] net: dsa: qca8k: add support for qca8327 switch + +qca8327 switch is a low tier version of the more recent qca8337. +It does share the same regs used by the qca8k driver and can be +supported with minimal change. + +Signed-off-by: Ansuel Smith +Reviewed-by: Andrew Lunn +Reviewed-by: Vladimir Oltean +Reviewed-by: Florian Fainelli +Signed-off-by: David S. Miller +--- + drivers/net/dsa/qca8k.c | 23 ++++++++++++++++++++--- + drivers/net/dsa/qca8k.h | 6 ++++++ + 2 files changed, 26 insertions(+), 3 deletions(-) + +--- a/drivers/net/dsa/qca8k.c ++++ b/drivers/net/dsa/qca8k.c +@@ -1533,6 +1533,7 @@ static const struct dsa_switch_ops qca8k + static int + qca8k_sw_probe(struct mdio_device *mdiodev) + { ++ const struct qca8k_match_data *data; + struct qca8k_priv *priv; + u32 id; + +@@ -1560,6 +1561,11 @@ qca8k_sw_probe(struct mdio_device *mdiod + gpiod_set_value_cansleep(priv->reset_gpio, 0); + } + ++ /* get the switches ID from the compatible */ ++ data = of_device_get_match_data(&mdiodev->dev); ++ if (!data) ++ return -ENODEV; ++ + /* read the switches ID register */ + id = qca8k_read(priv, QCA8K_REG_MASK_CTRL); + if (id < 0) +@@ -1567,8 +1573,10 @@ qca8k_sw_probe(struct mdio_device *mdiod + + id >>= QCA8K_MASK_CTRL_ID_S; + id &= QCA8K_MASK_CTRL_ID_M; +- if (id != QCA8K_ID_QCA8337) ++ if (id != data->id) { ++ dev_err(&mdiodev->dev, "Switch id detected %x but expected %x", id, data->id); + return -ENODEV; ++ } + + priv->ds = devm_kzalloc(&mdiodev->dev, sizeof(*priv->ds), GFP_KERNEL); + if (!priv->ds) +@@ -1634,9 +1642,18 @@ 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 = { ++ .id = QCA8K_ID_QCA8327, ++}; ++ ++static const struct qca8k_match_data qca833x = { ++ .id = QCA8K_ID_QCA8337, ++}; ++ + static const struct of_device_id qca8k_of_match[] = { +- { .compatible = "qca,qca8334" }, +- { .compatible = "qca,qca8337" }, ++ { .compatible = "qca,qca8327", .data = &qca832x }, ++ { .compatible = "qca,qca8334", .data = &qca833x }, ++ { .compatible = "qca,qca8337", .data = &qca833x }, + { /* sentinel */ }, + }; + +--- a/drivers/net/dsa/qca8k.h ++++ b/drivers/net/dsa/qca8k.h +@@ -15,6 +15,8 @@ + #define QCA8K_NUM_PORTS 7 + #define QCA8K_MAX_MTU 9000 + ++#define PHY_ID_QCA8327 0x004dd034 ++#define QCA8K_ID_QCA8327 0x12 + #define PHY_ID_QCA8337 0x004dd036 + #define QCA8K_ID_QCA8337 0x13 + +@@ -213,6 +215,10 @@ struct ar8xxx_port_status { + int enabled; + }; + ++struct qca8k_match_data { ++ u8 id; ++}; ++ + struct qca8k_priv { + struct regmap *regmap; + struct mii_bus *bus; diff --git a/target/linux/generic/backport-5.10/735-v5.14-10-devicetree-net-dsa-qca8k-Document-new-compatible-qca.patch b/target/linux/generic/backport-5.10/735-v5.14-10-devicetree-net-dsa-qca8k-Document-new-compatible-qca.patch new file mode 100644 index 000000000..3c4a14bd0 --- /dev/null +++ b/target/linux/generic/backport-5.10/735-v5.14-10-devicetree-net-dsa-qca8k-Document-new-compatible-qca.patch @@ -0,0 +1,26 @@ +From 227a9ffc1bc77037339530607fe129af3824620e Mon Sep 17 00:00:00 2001 +From: Ansuel Smith +Date: Fri, 14 May 2021 23:00:00 +0200 +Subject: [PATCH] devicetree: net: dsa: qca8k: Document new compatible qca8327 + +Add support for qca8327 in the compatible list. + +Signed-off-by: Ansuel Smith +Reviewed-by: Andrew Lunn +Acked-by: Rob Herring +Reviewed-by: Florian Fainelli +Signed-off-by: David S. Miller +--- + Documentation/devicetree/bindings/net/dsa/qca8k.txt | 1 + + 1 file changed, 1 insertion(+) + +--- a/Documentation/devicetree/bindings/net/dsa/qca8k.txt ++++ b/Documentation/devicetree/bindings/net/dsa/qca8k.txt +@@ -3,6 +3,7 @@ + Required properties: + + - compatible: should be one of: ++ "qca,qca8327" + "qca,qca8334" + "qca,qca8337" + diff --git a/target/linux/generic/backport-5.10/735-v5.14-11-net-dsa-qca8k-add-priority-tweak-to-qca8337-switch.patch b/target/linux/generic/backport-5.10/735-v5.14-11-net-dsa-qca8k-add-priority-tweak-to-qca8337-switch.patch new file mode 100644 index 000000000..cd3050ef7 --- /dev/null +++ b/target/linux/generic/backport-5.10/735-v5.14-11-net-dsa-qca8k-add-priority-tweak-to-qca8337-switch.patch @@ -0,0 +1,130 @@ +From 83a3ceb39b2495171aabe9446271b94c678354f3 Mon Sep 17 00:00:00 2001 +From: Ansuel Smith +Date: Fri, 14 May 2021 23:00:01 +0200 +Subject: [PATCH] net: dsa: qca8k: add priority tweak to qca8337 switch + +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. +The packet priority tweak is only needed for the qca8337 switch and +other qca8k switch are not affected. + +Signed-off-by: Ansuel Smith +Signed-off-by: David S. Miller +--- + drivers/net/dsa/qca8k.c | 47 +++++++++++++++++++++++++++++++++++++++++ + drivers/net/dsa/qca8k.h | 25 ++++++++++++++++++++++ + 2 files changed, 72 insertions(+) + +--- a/drivers/net/dsa/qca8k.c ++++ b/drivers/net/dsa/qca8k.c +@@ -779,6 +779,7 @@ qca8k_setup(struct dsa_switch *ds) + { + struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv; + int ret, i; ++ u32 mask; + + /* Make sure that port 0 is the cpu port */ + if (!dsa_is_cpu_port(ds, 0)) { +@@ -884,6 +885,51 @@ qca8k_setup(struct dsa_switch *ds) + } + } + ++ /* 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++) { ++ 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 | ++ QCA8K_PORT_HOL_CTRL1_EG_PRI_BUF_EN | ++ QCA8K_PORT_HOL_CTRL1_EG_PORT_BUF_EN | ++ QCA8K_PORT_HOL_CTRL1_WRED_EN, ++ mask); ++ } ++ } ++ + /* 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; +@@ -1578,6 +1624,7 @@ qca8k_sw_probe(struct mdio_device *mdiod + return -ENODEV; + } + ++ priv->switch_id = id; + priv->ds = devm_kzalloc(&mdiodev->dev, sizeof(*priv->ds), GFP_KERNEL); + if (!priv->ds) + return -ENOMEM; +--- a/drivers/net/dsa/qca8k.h ++++ b/drivers/net/dsa/qca8k.h +@@ -168,6 +168,30 @@ + #define QCA8K_PORT_LOOKUP_STATE GENMASK(18, 16) + #define QCA8K_PORT_LOOKUP_LEARN BIT(20) + ++#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_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_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_EGRESS_VLAN(x) (0x0c70 + (4 * (x / 2))) + +@@ -220,6 +244,7 @@ struct qca8k_match_data { + }; + + struct qca8k_priv { ++ u8 switch_id; + struct regmap *regmap; + struct mii_bus *bus; + struct ar8xxx_port_status port_sts[QCA8K_NUM_PORTS]; diff --git a/target/linux/generic/backport-5.10/735-v5.14-12-net-dsa-qca8k-limit-port5-delay-to-qca8337.patch b/target/linux/generic/backport-5.10/735-v5.14-12-net-dsa-qca8k-limit-port5-delay-to-qca8337.patch new file mode 100644 index 000000000..d25edbb1a --- /dev/null +++ b/target/linux/generic/backport-5.10/735-v5.14-12-net-dsa-qca8k-limit-port5-delay-to-qca8337.patch @@ -0,0 +1,31 @@ +From 5bf9ff3b9fb5ecb67a1a3517b26db3a00f2a2f11 Mon Sep 17 00:00:00 2001 +From: Ansuel Smith +Date: Fri, 14 May 2021 23:00:02 +0200 +Subject: [PATCH] net: dsa: qca8k: limit port5 delay to qca8337 + +Limit port5 rx delay to qca8337. This is taken from the legacy QSDK code +that limits the rx delay on port5 to only this particular switch version, +on other switch only the tx and rx delay for port0 are needed. + +Signed-off-by: Ansuel Smith +Reviewed-by: Andrew Lunn +Signed-off-by: David S. Miller +--- + drivers/net/dsa/qca8k.c | 6 ++++-- + 1 file changed, 4 insertions(+), 2 deletions(-) + +--- a/drivers/net/dsa/qca8k.c ++++ b/drivers/net/dsa/qca8k.c +@@ -1003,8 +1003,10 @@ qca8k_phylink_mac_config(struct dsa_swit + QCA8K_PORT_PAD_RGMII_EN | + QCA8K_PORT_PAD_RGMII_TX_DELAY(QCA8K_MAX_DELAY) | + QCA8K_PORT_PAD_RGMII_RX_DELAY(QCA8K_MAX_DELAY)); +- qca8k_write(priv, QCA8K_REG_PORT5_PAD_CTRL, +- 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); + break; + case PHY_INTERFACE_MODE_SGMII: + case PHY_INTERFACE_MODE_1000BASEX: diff --git a/target/linux/generic/backport-5.10/735-v5.14-13-net-dsa-qca8k-add-GLOBAL_FC-settings-needed-for-qca8.patch b/target/linux/generic/backport-5.10/735-v5.14-13-net-dsa-qca8k-add-GLOBAL_FC-settings-needed-for-qca8.patch new file mode 100644 index 000000000..2b393d242 --- /dev/null +++ b/target/linux/generic/backport-5.10/735-v5.14-13-net-dsa-qca8k-add-GLOBAL_FC-settings-needed-for-qca8.patch @@ -0,0 +1,48 @@ +From 0fc57e4b5e39461fc0a54aae0afe4241363a7267 Mon Sep 17 00:00:00 2001 +From: Ansuel Smith +Date: Fri, 14 May 2021 23:00:03 +0200 +Subject: [PATCH] net: dsa: qca8k: add GLOBAL_FC settings needed for qca8327 + +Switch qca8327 needs special settings for the GLOBAL_FC_THRES regs. + +Signed-off-by: Ansuel Smith +Signed-off-by: David S. Miller +--- + drivers/net/dsa/qca8k.c | 10 ++++++++++ + drivers/net/dsa/qca8k.h | 6 ++++++ + 2 files changed, 16 insertions(+) + +--- a/drivers/net/dsa/qca8k.c ++++ b/drivers/net/dsa/qca8k.c +@@ -930,6 +930,16 @@ qca8k_setup(struct dsa_switch *ds) + } + } + ++ /* 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_S | ++ QCA8K_GLOBAL_FC_GOL_XOFF_THRES_S, ++ mask); ++ } ++ + /* 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; +--- a/drivers/net/dsa/qca8k.h ++++ b/drivers/net/dsa/qca8k.h +@@ -168,6 +168,12 @@ + #define QCA8K_PORT_LOOKUP_STATE GENMASK(18, 16) + #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_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) diff --git a/target/linux/generic/backport-5.10/735-v5.14-14-net-dsa-qca8k-add-support-for-switch-rev.patch b/target/linux/generic/backport-5.10/735-v5.14-14-net-dsa-qca8k-add-support-for-switch-rev.patch new file mode 100644 index 000000000..ed9b8188d --- /dev/null +++ b/target/linux/generic/backport-5.10/735-v5.14-14-net-dsa-qca8k-add-support-for-switch-rev.patch @@ -0,0 +1,114 @@ +From 95ffeaf18b3bb90eeef52cbf7d79ccc9d0345ff5 Mon Sep 17 00:00:00 2001 +From: Ansuel Smith +Date: Fri, 14 May 2021 23:00:04 +0200 +Subject: [PATCH] net: dsa: qca8k: add support for switch rev + +qca8k internal phy driver require some special debug value to be set +based on the switch revision. Rework the switch id read function to +also read the chip revision. + +Signed-off-by: Ansuel Smith +Reviewed-by: Florian Fainelli +Signed-off-by: David S. Miller +--- + drivers/net/dsa/qca8k.c | 53 ++++++++++++++++++++++++++--------------- + drivers/net/dsa/qca8k.h | 7 ++++-- + 2 files changed, 39 insertions(+), 21 deletions(-) + +--- a/drivers/net/dsa/qca8k.c ++++ b/drivers/net/dsa/qca8k.c +@@ -1588,12 +1588,40 @@ static const struct dsa_switch_ops qca8k + .phylink_mac_link_up = qca8k_phylink_mac_link_up, + }; + ++static int qca8k_read_switch_id(struct qca8k_priv *priv) ++{ ++ const struct qca8k_match_data *data; ++ u32 val; ++ u8 id; ++ ++ /* get the switches ID from the compatible */ ++ data = of_device_get_match_data(priv->dev); ++ if (!data) ++ return -ENODEV; ++ ++ val = qca8k_read(priv, QCA8K_REG_MASK_CTRL); ++ if (val < 0) ++ return -ENODEV; ++ ++ id = QCA8K_MASK_CTRL_DEVICE_ID(val & QCA8K_MASK_CTRL_DEVICE_ID_MASK); ++ 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 = (val & QCA8K_MASK_CTRL_REV_ID_MASK); ++ ++ return 0; ++} ++ + static int + qca8k_sw_probe(struct mdio_device *mdiodev) + { +- const struct qca8k_match_data *data; + struct qca8k_priv *priv; +- u32 id; ++ int ret; + + /* allocate the private data struct so that we can probe the switches + * ID register +@@ -1619,24 +1647,11 @@ qca8k_sw_probe(struct mdio_device *mdiod + gpiod_set_value_cansleep(priv->reset_gpio, 0); + } + +- /* get the switches ID from the compatible */ +- data = of_device_get_match_data(&mdiodev->dev); +- if (!data) +- return -ENODEV; ++ /* Check the detected switch id */ ++ ret = qca8k_read_switch_id(priv); ++ if (ret) ++ return ret; + +- /* read the switches ID register */ +- id = qca8k_read(priv, QCA8K_REG_MASK_CTRL); +- if (id < 0) +- return id; +- +- id >>= QCA8K_MASK_CTRL_ID_S; +- id &= QCA8K_MASK_CTRL_ID_M; +- if (id != data->id) { +- dev_err(&mdiodev->dev, "Switch id detected %x but expected %x", id, data->id); +- return -ENODEV; +- } +- +- priv->switch_id = id; + priv->ds = devm_kzalloc(&mdiodev->dev, sizeof(*priv->ds), GFP_KERNEL); + if (!priv->ds) + return -ENOMEM; +--- a/drivers/net/dsa/qca8k.h ++++ b/drivers/net/dsa/qca8k.h +@@ -30,8 +30,10 @@ + + /* Global control registers */ + #define QCA8K_REG_MASK_CTRL 0x000 +-#define QCA8K_MASK_CTRL_ID_M 0xff +-#define QCA8K_MASK_CTRL_ID_S 8 ++#define QCA8K_MASK_CTRL_REV_ID_MASK GENMASK(7, 0) ++#define QCA8K_MASK_CTRL_REV_ID(x) ((x) >> 0) ++#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_REG_PORT5_PAD_CTRL 0x008 + #define QCA8K_REG_PORT6_PAD_CTRL 0x00c +@@ -251,6 +253,7 @@ struct qca8k_match_data { + + struct qca8k_priv { + u8 switch_id; ++ u8 switch_revision; + struct regmap *regmap; + struct mii_bus *bus; + struct ar8xxx_port_status port_sts[QCA8K_NUM_PORTS]; diff --git a/target/linux/generic/backport-5.10/735-v5.14-15-net-dsa-qca8k-add-ethernet-ports-fallback-to-setup_m.patch b/target/linux/generic/backport-5.10/735-v5.14-15-net-dsa-qca8k-add-ethernet-ports-fallback-to-setup_m.patch new file mode 100644 index 000000000..629cb324e --- /dev/null +++ b/target/linux/generic/backport-5.10/735-v5.14-15-net-dsa-qca8k-add-ethernet-ports-fallback-to-setup_m.patch @@ -0,0 +1,28 @@ +From 1ee0591a1093c2448642c33433483e9260275f7b Mon Sep 17 00:00:00 2001 +From: Ansuel Smith +Date: Fri, 14 May 2021 23:00:05 +0200 +Subject: [PATCH] net: dsa: qca8k: add ethernet-ports fallback to + setup_mdio_bus + +Dsa now also supports ethernet-ports. Add this new binding as a fallback +if the ports node can't be found. + +Signed-off-by: Ansuel Smith +Reviewed-by: Andrew Lunn +Signed-off-by: David S. Miller +--- + drivers/net/dsa/qca8k.c | 3 +++ + 1 file changed, 3 insertions(+) + +--- a/drivers/net/dsa/qca8k.c ++++ b/drivers/net/dsa/qca8k.c +@@ -719,6 +719,9 @@ qca8k_setup_mdio_bus(struct qca8k_priv * + + 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) { diff --git a/target/linux/generic/backport-5.10/735-v5.14-16-net-dsa-qca8k-make-rgmii-delay-configurable.patch b/target/linux/generic/backport-5.10/735-v5.14-16-net-dsa-qca8k-make-rgmii-delay-configurable.patch new file mode 100644 index 000000000..6dc2dc6e3 --- /dev/null +++ b/target/linux/generic/backport-5.10/735-v5.14-16-net-dsa-qca8k-make-rgmii-delay-configurable.patch @@ -0,0 +1,188 @@ +From e4b9977cee1583da38a6e9118078bb728aaccf7b Mon Sep 17 00:00:00 2001 +From: Ansuel Smith +Date: Fri, 14 May 2021 23:00:06 +0200 +Subject: [PATCH] net: dsa: qca8k: make rgmii delay configurable + +The legacy qsdk code used a different delay instead of the max value. +Qsdk use 1 ns for rx and 2 ns for tx. Make these values configurable +using the standard rx/tx-internal-delay-ps ethernet binding and apply +qsdk values by default. The connected gmac doesn't add any delay so no +additional delay is added to tx/rx. +On this switch the delay is actually in ns so value should be in the +1000 order. Any value converted from ps to ns by dividing it by 1000 +as the switch max value for delay is 3ns. + +Signed-off-by: Ansuel Smith +Signed-off-by: David S. Miller +--- + drivers/net/dsa/qca8k.c | 82 ++++++++++++++++++++++++++++++++++++++++- + drivers/net/dsa/qca8k.h | 11 +++--- + 2 files changed, 86 insertions(+), 7 deletions(-) + +--- a/drivers/net/dsa/qca8k.c ++++ b/drivers/net/dsa/qca8k.c +@@ -778,6 +778,68 @@ 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(struct dsa_switch *ds) + { + struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv; +@@ -802,6 +864,10 @@ qca8k_setup(struct dsa_switch *ds) + if (ret) + return ret; + ++ ret = qca8k_setup_of_rgmii_delay(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); +@@ -970,6 +1036,8 @@ qca8k_phylink_mac_config(struct dsa_swit + 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; + +@@ -985,6 +1053,8 @@ qca8k_phylink_mac_config(struct dsa_swit + 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; +@@ -1008,14 +1078,18 @@ qca8k_phylink_mac_config(struct dsa_swit + 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 + */ + qca8k_write(priv, reg, + QCA8K_PORT_PAD_RGMII_EN | +- QCA8K_PORT_PAD_RGMII_TX_DELAY(QCA8K_MAX_DELAY) | +- QCA8K_PORT_PAD_RGMII_RX_DELAY(QCA8K_MAX_DELAY)); ++ 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, +@@ -1073,6 +1147,8 @@ qca8k_phylink_validate(struct dsa_switch + 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; +@@ -1090,6 +1166,8 @@ qca8k_phylink_validate(struct dsa_switch + 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; +--- a/drivers/net/dsa/qca8k.h ++++ b/drivers/net/dsa/qca8k.h +@@ -38,12 +38,11 @@ + #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(x) \ +- ((0x8 + (x & 0x3)) << 22) +-#define QCA8K_PORT_PAD_RGMII_RX_DELAY(x) \ +- ((0x10 + (x & 0x3)) << 20) +-#define QCA8K_MAX_DELAY 3 ++#define QCA8K_PORT_PAD_RGMII_TX_DELAY(x) ((x) << 22) ++#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) ++#define QCA8K_MAX_DELAY 3 + #define QCA8K_PORT_PAD_SGMII_EN BIT(7) + #define QCA8K_REG_PWS 0x010 + #define QCA8K_PWS_SERDES_AEN_DIS BIT(7) +@@ -254,6 +253,8 @@ struct qca8k_match_data { + struct qca8k_priv { + u8 switch_id; + u8 switch_revision; ++ u8 rgmii_tx_delay; ++ u8 rgmii_rx_delay; + struct regmap *regmap; + struct mii_bus *bus; + struct ar8xxx_port_status port_sts[QCA8K_NUM_PORTS]; diff --git a/target/linux/generic/backport-5.10/735-v5.14-17-net-dsa-qca8k-clear-MASTER_EN-after-phy-read-write.patch b/target/linux/generic/backport-5.10/735-v5.14-17-net-dsa-qca8k-clear-MASTER_EN-after-phy-read-write.patch new file mode 100644 index 000000000..4593da032 --- /dev/null +++ b/target/linux/generic/backport-5.10/735-v5.14-17-net-dsa-qca8k-clear-MASTER_EN-after-phy-read-write.patch @@ -0,0 +1,50 @@ +From 63c33bbfeb6842a956a0eb12901e28eb335bdb18 Mon Sep 17 00:00:00 2001 +From: Ansuel Smith +Date: Fri, 14 May 2021 23:00:07 +0200 +Subject: [PATCH] net: dsa: qca8k: clear MASTER_EN after phy read/write + +Clear MDIO_MASTER_EN bit from MDIO_MASTER_CTRL after read/write +operation. The MDIO_MASTER_EN bit is not reset after read/write +operation and the next operation can be wrongly interpreted by the +switch as a mdio operation. This cause a production of wrong/garbage +data from the switch and underfined bheavior. (random port drop, +unplugged port flagged with link up, wrong port speed) +Also on driver remove the MASTER_CTRL can be left set and cause the +malfunction of any next driver using the mdio device. + +Signed-off-by: Ansuel Smith +Signed-off-by: David S. Miller +--- + drivers/net/dsa/qca8k.c | 14 ++++++++++++-- + 1 file changed, 12 insertions(+), 2 deletions(-) + +--- a/drivers/net/dsa/qca8k.c ++++ b/drivers/net/dsa/qca8k.c +@@ -649,8 +649,14 @@ qca8k_mdio_write(struct qca8k_priv *priv + if (ret) + return ret; + +- return qca8k_busy_wait(priv, QCA8K_MDIO_MASTER_CTRL, +- QCA8K_MDIO_MASTER_BUSY); ++ ret = qca8k_busy_wait(priv, QCA8K_MDIO_MASTER_CTRL, ++ QCA8K_MDIO_MASTER_BUSY); ++ ++ /* even if the busy_wait timeouts try to clear the MASTER_EN */ ++ qca8k_reg_clear(priv, QCA8K_MDIO_MASTER_CTRL, ++ QCA8K_MDIO_MASTER_EN); ++ ++ return ret; + } + + static int +@@ -685,6 +691,10 @@ qca8k_mdio_read(struct qca8k_priv *priv, + + val &= QCA8K_MDIO_MASTER_DATA_MASK; + ++ /* even if the busy_wait timeouts try to clear the MASTER_EN */ ++ qca8k_reg_clear(priv, QCA8K_MDIO_MASTER_CTRL, ++ QCA8K_MDIO_MASTER_EN); ++ + return val; + } + diff --git a/target/linux/generic/backport-5.10/735-v5.14-18-net-dsa-qca8k-dsa-qca8k-protect-MASTER-busy_wait-wit.patch b/target/linux/generic/backport-5.10/735-v5.14-18-net-dsa-qca8k-dsa-qca8k-protect-MASTER-busy_wait-wit.patch new file mode 100644 index 000000000..b6684d721 --- /dev/null +++ b/target/linux/generic/backport-5.10/735-v5.14-18-net-dsa-qca8k-dsa-qca8k-protect-MASTER-busy_wait-wit.patch @@ -0,0 +1,128 @@ +From 60df02b6ea4581d72eb7a3ab7204504a54059b72 Mon Sep 17 00:00:00 2001 +From: Ansuel Smith +Date: Fri, 14 May 2021 23:00:08 +0200 +Subject: [PATCH] net: dsa: qca8k: dsa: qca8k: protect MASTER busy_wait with + mdio mutex + +MDIO_MASTER operation have a dedicated busy wait that is not protected +by the mdio mutex. This can cause situation where the MASTER operation +is done and a normal operation is executed between the MASTER read/write +and the MASTER busy_wait. Rework the qca8k_mdio_read/write function to +address this issue by binding the lock for the whole MASTER operation +and not only the mdio read/write common operation. + +Signed-off-by: Ansuel Smith +Reviewed-by: Andrew Lunn +Signed-off-by: David S. Miller +--- + drivers/net/dsa/qca8k.c | 68 +++++++++++++++++++++++++++++++++-------- + 1 file changed, 55 insertions(+), 13 deletions(-) + +--- a/drivers/net/dsa/qca8k.c ++++ b/drivers/net/dsa/qca8k.c +@@ -628,8 +628,31 @@ qca8k_port_to_phy(int port) + } + + static int ++qca8k_mdio_busy_wait(struct qca8k_priv *priv, u32 reg, u32 mask) ++{ ++ u16 r1, r2, page; ++ u32 val; ++ int ret; ++ ++ qca8k_split_addr(reg, &r1, &r2, &page); ++ ++ ret = read_poll_timeout(qca8k_mii_read32, val, !(val & mask), 0, ++ QCA8K_BUSY_WAIT_TIMEOUT * USEC_PER_MSEC, false, ++ priv->bus, 0x10 | r2, r1); ++ ++ /* Check if qca8k_read has failed for a different reason ++ * before returnting -ETIMEDOUT ++ */ ++ if (ret < 0 && val < 0) ++ return val; ++ ++ return ret; ++} ++ ++static int + qca8k_mdio_write(struct qca8k_priv *priv, int port, u32 regnum, u16 data) + { ++ u16 r1, r2, page; + u32 phy, val; + int ret; + +@@ -645,12 +668,21 @@ qca8k_mdio_write(struct qca8k_priv *priv + QCA8K_MDIO_MASTER_REG_ADDR(regnum) | + QCA8K_MDIO_MASTER_DATA(data); + +- ret = qca8k_write(priv, QCA8K_MDIO_MASTER_CTRL, val); ++ qca8k_split_addr(QCA8K_MDIO_MASTER_CTRL, &r1, &r2, &page); ++ ++ mutex_lock_nested(&priv->bus->mdio_lock, MDIO_MUTEX_NESTED); ++ ++ ret = qca8k_set_page(priv->bus, page); + if (ret) +- return ret; ++ goto exit; ++ ++ qca8k_mii_write32(priv->bus, 0x10 | r2, r1, val); + +- ret = qca8k_busy_wait(priv, QCA8K_MDIO_MASTER_CTRL, +- QCA8K_MDIO_MASTER_BUSY); ++ ret = qca8k_mdio_busy_wait(priv, QCA8K_MDIO_MASTER_CTRL, ++ QCA8K_MDIO_MASTER_BUSY); ++ ++exit: ++ mutex_unlock(&priv->bus->mdio_lock); + + /* even if the busy_wait timeouts try to clear the MASTER_EN */ + qca8k_reg_clear(priv, QCA8K_MDIO_MASTER_CTRL, +@@ -662,6 +694,7 @@ qca8k_mdio_write(struct qca8k_priv *priv + static int + qca8k_mdio_read(struct qca8k_priv *priv, int port, u32 regnum) + { ++ u16 r1, r2, page; + u32 phy, val; + int ret; + +@@ -676,21 +709,30 @@ qca8k_mdio_read(struct qca8k_priv *priv, + QCA8K_MDIO_MASTER_READ | QCA8K_MDIO_MASTER_PHY_ADDR(phy) | + QCA8K_MDIO_MASTER_REG_ADDR(regnum); + +- ret = qca8k_write(priv, QCA8K_MDIO_MASTER_CTRL, val); +- if (ret) +- return ret; ++ qca8k_split_addr(QCA8K_MDIO_MASTER_CTRL, &r1, &r2, &page); ++ ++ mutex_lock_nested(&priv->bus->mdio_lock, MDIO_MUTEX_NESTED); + +- ret = qca8k_busy_wait(priv, QCA8K_MDIO_MASTER_CTRL, +- QCA8K_MDIO_MASTER_BUSY); ++ ret = qca8k_set_page(priv->bus, page); + if (ret) +- return ret; ++ goto exit; + +- val = qca8k_read(priv, QCA8K_MDIO_MASTER_CTRL); +- if (val < 0) +- return val; ++ qca8k_mii_write32(priv->bus, 0x10 | r2, r1, val); ++ ++ ret = qca8k_mdio_busy_wait(priv, QCA8K_MDIO_MASTER_CTRL, ++ QCA8K_MDIO_MASTER_BUSY); ++ if (ret) ++ goto exit; + ++ val = qca8k_mii_read32(priv->bus, 0x10 | r2, r1); + val &= QCA8K_MDIO_MASTER_DATA_MASK; + ++exit: ++ mutex_unlock(&priv->bus->mdio_lock); ++ ++ if (val >= 0) ++ val &= QCA8K_MDIO_MASTER_DATA_MASK; ++ + /* even if the busy_wait timeouts try to clear the MASTER_EN */ + qca8k_reg_clear(priv, QCA8K_MDIO_MASTER_CTRL, + QCA8K_MDIO_MASTER_EN); diff --git a/target/linux/generic/backport-5.10/735-v5.14-19-net-dsa-qca8k-enlarge-mdio-delay-and-timeout.patch b/target/linux/generic/backport-5.10/735-v5.14-19-net-dsa-qca8k-enlarge-mdio-delay-and-timeout.patch new file mode 100644 index 000000000..30eeed361 --- /dev/null +++ b/target/linux/generic/backport-5.10/735-v5.14-19-net-dsa-qca8k-enlarge-mdio-delay-and-timeout.patch @@ -0,0 +1,39 @@ +From 617960d72e93de0f3fa52407e2d39e8c43e73b0a Mon Sep 17 00:00:00 2001 +From: Ansuel Smith +Date: Fri, 14 May 2021 23:00:09 +0200 +Subject: [PATCH] net: dsa: qca8k: enlarge mdio delay and timeout + +The witch require some extra delay after setting page or the next +read/write can use still use the old page. Add a delay after the +set_page function to address this as it's done in QSDK legacy driver. +Some timeouts were notice with VLAN and phy function, enlarge the +mdio busy wait timeout to fix these problems. + +Signed-off-by: Ansuel Smith +Signed-off-by: David S. Miller +--- + drivers/net/dsa/qca8k.c | 1 + + drivers/net/dsa/qca8k.h | 2 +- + 2 files changed, 2 insertions(+), 1 deletion(-) + +--- a/drivers/net/dsa/qca8k.c ++++ b/drivers/net/dsa/qca8k.c +@@ -143,6 +143,7 @@ qca8k_set_page(struct mii_bus *bus, u16 + } + + qca8k_current_page = page; ++ usleep_range(1000, 2000); + return 0; + } + +--- a/drivers/net/dsa/qca8k.h ++++ b/drivers/net/dsa/qca8k.h +@@ -20,7 +20,7 @@ + #define PHY_ID_QCA8337 0x004dd036 + #define QCA8K_ID_QCA8337 0x13 + +-#define QCA8K_BUSY_WAIT_TIMEOUT 20 ++#define QCA8K_BUSY_WAIT_TIMEOUT 2000 + + #define QCA8K_NUM_FDB_RECORDS 2048 + diff --git a/target/linux/generic/backport-5.10/735-v5.14-20-net-dsa-qca8k-add-support-for-internal-phy-and-inter.patch b/target/linux/generic/backport-5.10/735-v5.14-20-net-dsa-qca8k-add-support-for-internal-phy-and-inter.patch new file mode 100644 index 000000000..88d3c1ef4 --- /dev/null +++ b/target/linux/generic/backport-5.10/735-v5.14-20-net-dsa-qca8k-add-support-for-internal-phy-and-inter.patch @@ -0,0 +1,267 @@ +From 759bafb8a3226326ca357613bc90acf738f80c32 Mon Sep 17 00:00:00 2001 +From: Ansuel Smith +Date: Fri, 14 May 2021 23:00:10 +0200 +Subject: [PATCH] net: dsa: qca8k: add support for internal phy and internal + mdio + +Add support to setup_mdio_bus for internal phy declaration. Introduce a +flag to use the legacy port phy mapping by default and use the direct +mapping if a mdio node is detected in the switch node. Register a +dedicated mdio internal mdio bus to address the different mapping +between port and phy if the mdio node is detected. + +Signed-off-by: Ansuel Smith +Signed-off-by: David S. Miller +--- + drivers/net/dsa/qca8k.c | 112 +++++++++++++++++++++++++++++----------- + drivers/net/dsa/qca8k.h | 1 + + 2 files changed, 83 insertions(+), 30 deletions(-) + +--- a/drivers/net/dsa/qca8k.c ++++ b/drivers/net/dsa/qca8k.c +@@ -11,6 +11,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -629,7 +630,7 @@ qca8k_port_to_phy(int port) + } + + static int +-qca8k_mdio_busy_wait(struct qca8k_priv *priv, u32 reg, u32 mask) ++qca8k_mdio_busy_wait(struct mii_bus *bus, u32 reg, u32 mask) + { + u16 r1, r2, page; + u32 val; +@@ -639,7 +640,7 @@ qca8k_mdio_busy_wait(struct qca8k_priv * + + ret = read_poll_timeout(qca8k_mii_read32, val, !(val & mask), 0, + QCA8K_BUSY_WAIT_TIMEOUT * USEC_PER_MSEC, false, +- priv->bus, 0x10 | r2, r1); ++ bus, 0x10 | r2, r1); + + /* Check if qca8k_read has failed for a different reason + * before returnting -ETIMEDOUT +@@ -651,19 +652,16 @@ qca8k_mdio_busy_wait(struct qca8k_priv * + } + + static int +-qca8k_mdio_write(struct qca8k_priv *priv, int port, u32 regnum, u16 data) ++qca8k_mdio_write(struct mii_bus *salve_bus, int phy, int regnum, u16 data) + { ++ struct qca8k_priv *priv = salve_bus->priv; + u16 r1, r2, page; +- u32 phy, val; ++ u32 val; + int ret; + + if (regnum >= QCA8K_MDIO_MASTER_MAX_REG) + return -EINVAL; + +- /* callee is responsible for not passing bad ports, +- * but we still would like to make spills impossible. +- */ +- phy = qca8k_port_to_phy(port) % PHY_MAX_ADDR; + 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) | +@@ -679,33 +677,29 @@ qca8k_mdio_write(struct qca8k_priv *priv + + qca8k_mii_write32(priv->bus, 0x10 | r2, r1, val); + +- ret = qca8k_mdio_busy_wait(priv, QCA8K_MDIO_MASTER_CTRL, ++ ret = qca8k_mdio_busy_wait(priv->bus, QCA8K_MDIO_MASTER_CTRL, + QCA8K_MDIO_MASTER_BUSY); + + exit: +- mutex_unlock(&priv->bus->mdio_lock); +- + /* even if the busy_wait timeouts try to clear the MASTER_EN */ +- qca8k_reg_clear(priv, QCA8K_MDIO_MASTER_CTRL, +- QCA8K_MDIO_MASTER_EN); ++ qca8k_mii_write32(priv->bus, 0x10 | r2, r1, 0); ++ ++ mutex_unlock(&priv->bus->mdio_lock); + + return ret; + } + + static int +-qca8k_mdio_read(struct qca8k_priv *priv, int port, u32 regnum) ++qca8k_mdio_read(struct mii_bus *salve_bus, int phy, int regnum) + { ++ struct qca8k_priv *priv = salve_bus->priv; + u16 r1, r2, page; +- u32 phy, val; ++ u32 val; + int ret; + + if (regnum >= QCA8K_MDIO_MASTER_MAX_REG) + return -EINVAL; + +- /* callee is responsible for not passing bad ports, +- * but we still would like to make spills impossible. +- */ +- phy = qca8k_port_to_phy(port) % PHY_MAX_ADDR; + 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); +@@ -720,24 +714,22 @@ qca8k_mdio_read(struct qca8k_priv *priv, + + qca8k_mii_write32(priv->bus, 0x10 | r2, r1, val); + +- ret = qca8k_mdio_busy_wait(priv, QCA8K_MDIO_MASTER_CTRL, ++ ret = qca8k_mdio_busy_wait(priv->bus, QCA8K_MDIO_MASTER_CTRL, + QCA8K_MDIO_MASTER_BUSY); + if (ret) + goto exit; + + val = qca8k_mii_read32(priv->bus, 0x10 | r2, r1); +- val &= QCA8K_MDIO_MASTER_DATA_MASK; + + exit: ++ /* even if the busy_wait timeouts try to clear the MASTER_EN */ ++ qca8k_mii_write32(priv->bus, 0x10 | r2, r1, 0); ++ + mutex_unlock(&priv->bus->mdio_lock); + + if (val >= 0) + val &= QCA8K_MDIO_MASTER_DATA_MASK; + +- /* even if the busy_wait timeouts try to clear the MASTER_EN */ +- qca8k_reg_clear(priv, QCA8K_MDIO_MASTER_CTRL, +- QCA8K_MDIO_MASTER_EN); +- + return val; + } + +@@ -746,7 +738,14 @@ qca8k_phy_write(struct dsa_switch *ds, i + { + struct qca8k_priv *priv = ds->priv; + +- return qca8k_mdio_write(priv, port, regnum, data); ++ /* 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; ++ ++ return qca8k_mdio_write(priv->bus, port, regnum, data); + } + + static int +@@ -755,7 +754,14 @@ qca8k_phy_read(struct dsa_switch *ds, in + struct qca8k_priv *priv = ds->priv; + int ret; + +- ret = qca8k_mdio_read(priv, port, regnum); ++ /* 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; ++ ++ ret = qca8k_mdio_read(priv->bus, port, regnum); + + if (ret < 0) + return 0xffff; +@@ -764,10 +770,37 @@ qca8k_phy_read(struct dsa_switch *ds, in + } + + static int ++qca8k_mdio_register(struct qca8k_priv *priv, struct device_node *mdio) ++{ ++ struct dsa_switch *ds = priv->ds; ++ 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_mdio_read; ++ bus->write = qca8k_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); ++} ++ ++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; ++ struct device_node *ports, *port, *mdio; ++ phy_interface_t mode; + int err; + + ports = of_get_child_by_name(priv->dev->of_node, "ports"); +@@ -788,7 +821,10 @@ qca8k_setup_mdio_bus(struct qca8k_priv * + if (!dsa_is_user_port(priv->ds, reg)) + continue; + +- if (of_property_read_bool(port, "phy-handle")) ++ 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); +@@ -825,8 +861,23 @@ 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; + } + +@@ -1212,7 +1263,8 @@ qca8k_phylink_validate(struct dsa_switch + case 5: + /* Internal PHY */ + if (state->interface != PHY_INTERFACE_MODE_NA && +- state->interface != PHY_INTERFACE_MODE_GMII) ++ state->interface != PHY_INTERFACE_MODE_GMII && ++ state->interface != PHY_INTERFACE_MODE_INTERNAL) + goto unsupported; + break; + case 6: /* 2nd CPU port / external PHY */ +--- a/drivers/net/dsa/qca8k.h ++++ b/drivers/net/dsa/qca8k.h +@@ -255,6 +255,7 @@ struct qca8k_priv { + u8 switch_revision; + u8 rgmii_tx_delay; + u8 rgmii_rx_delay; ++ bool legacy_phy_port_mapping; + struct regmap *regmap; + struct mii_bus *bus; + struct ar8xxx_port_status port_sts[QCA8K_NUM_PORTS]; diff --git a/target/linux/generic/backport-5.10/735-v5.14-21-devicetree-bindings-dsa-qca8k-Document-internal-mdio.patch b/target/linux/generic/backport-5.10/735-v5.14-21-devicetree-bindings-dsa-qca8k-Document-internal-mdio.patch new file mode 100644 index 000000000..6db01b4b4 --- /dev/null +++ b/target/linux/generic/backport-5.10/735-v5.14-21-devicetree-bindings-dsa-qca8k-Document-internal-mdio.patch @@ -0,0 +1,93 @@ +From 0c994a28e7518f098c84a3049cb2915780db873a Mon Sep 17 00:00:00 2001 +From: Ansuel Smith +Date: Fri, 14 May 2021 23:00:11 +0200 +Subject: [PATCH] devicetree: bindings: dsa: qca8k: Document internal mdio + definition + +Document new way of declare mapping of internal PHY to port. +The new implementation directly declare the PHY connected to the port +by adding a node in the switch node. The driver detect this and register +an internal mdiobus using the mapping defined in the mdio node. + +Signed-off-by: Ansuel Smith +Reviewed-by: Rob Herring +Signed-off-by: David S. Miller +--- + .../devicetree/bindings/net/dsa/qca8k.txt | 39 +++++++++++++++++++ + 1 file changed, 39 insertions(+) + +--- a/Documentation/devicetree/bindings/net/dsa/qca8k.txt ++++ b/Documentation/devicetree/bindings/net/dsa/qca8k.txt +@@ -21,6 +21,10 @@ described in dsa/dsa.txt. If the QCA8K s + 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. +@@ -150,26 +154,61 @@ for the internal master mdio-bus configu + 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>; + }; + }; + }; diff --git a/target/linux/generic/backport-5.10/735-v5.14-22-net-dsa-qca8k-improve-internal-mdio-read-write-bus-a.patch b/target/linux/generic/backport-5.10/735-v5.14-22-net-dsa-qca8k-improve-internal-mdio-read-write-bus-a.patch new file mode 100644 index 000000000..da8d5b346 --- /dev/null +++ b/target/linux/generic/backport-5.10/735-v5.14-22-net-dsa-qca8k-improve-internal-mdio-read-write-bus-a.patch @@ -0,0 +1,95 @@ +From b7ebac354d54f1657bb89b7a7ca149db50203e6a Mon Sep 17 00:00:00 2001 +From: Ansuel Smith +Date: Fri, 14 May 2021 23:00:12 +0200 +Subject: [PATCH] net: dsa: qca8k: improve internal mdio read/write bus access + +Improve the internal mdio read/write bus access by caching the value +without accessing it for every read/write. + +Signed-off-by: Ansuel Smith +Signed-off-by: David S. Miller +--- + drivers/net/dsa/qca8k.c | 28 +++++++++++++++------------- + 1 file changed, 15 insertions(+), 13 deletions(-) + +--- a/drivers/net/dsa/qca8k.c ++++ b/drivers/net/dsa/qca8k.c +@@ -655,6 +655,7 @@ static int + qca8k_mdio_write(struct mii_bus *salve_bus, int phy, int regnum, u16 data) + { + struct qca8k_priv *priv = salve_bus->priv; ++ struct mii_bus *bus = priv->bus; + u16 r1, r2, page; + u32 val; + int ret; +@@ -669,22 +670,22 @@ qca8k_mdio_write(struct mii_bus *salve_b + + qca8k_split_addr(QCA8K_MDIO_MASTER_CTRL, &r1, &r2, &page); + +- mutex_lock_nested(&priv->bus->mdio_lock, MDIO_MUTEX_NESTED); ++ mutex_lock_nested(&bus->mdio_lock, MDIO_MUTEX_NESTED); + +- ret = qca8k_set_page(priv->bus, page); ++ ret = qca8k_set_page(bus, page); + if (ret) + goto exit; + +- qca8k_mii_write32(priv->bus, 0x10 | r2, r1, val); ++ qca8k_mii_write32(bus, 0x10 | r2, r1, val); + +- ret = qca8k_mdio_busy_wait(priv->bus, QCA8K_MDIO_MASTER_CTRL, ++ 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->bus, 0x10 | r2, r1, 0); ++ qca8k_mii_write32(bus, 0x10 | r2, r1, 0); + +- mutex_unlock(&priv->bus->mdio_lock); ++ mutex_unlock(&bus->mdio_lock); + + return ret; + } +@@ -693,6 +694,7 @@ static int + qca8k_mdio_read(struct mii_bus *salve_bus, int phy, int regnum) + { + struct qca8k_priv *priv = salve_bus->priv; ++ struct mii_bus *bus = priv->bus; + u16 r1, r2, page; + u32 val; + int ret; +@@ -706,26 +708,26 @@ qca8k_mdio_read(struct mii_bus *salve_bu + + qca8k_split_addr(QCA8K_MDIO_MASTER_CTRL, &r1, &r2, &page); + +- mutex_lock_nested(&priv->bus->mdio_lock, MDIO_MUTEX_NESTED); ++ mutex_lock_nested(&bus->mdio_lock, MDIO_MUTEX_NESTED); + +- ret = qca8k_set_page(priv->bus, page); ++ ret = qca8k_set_page(bus, page); + if (ret) + goto exit; + +- qca8k_mii_write32(priv->bus, 0x10 | r2, r1, val); ++ qca8k_mii_write32(bus, 0x10 | r2, r1, val); + +- ret = qca8k_mdio_busy_wait(priv->bus, QCA8K_MDIO_MASTER_CTRL, ++ ret = qca8k_mdio_busy_wait(bus, QCA8K_MDIO_MASTER_CTRL, + QCA8K_MDIO_MASTER_BUSY); + if (ret) + goto exit; + +- val = qca8k_mii_read32(priv->bus, 0x10 | r2, r1); ++ val = qca8k_mii_read32(bus, 0x10 | r2, r1); + + exit: + /* even if the busy_wait timeouts try to clear the MASTER_EN */ +- qca8k_mii_write32(priv->bus, 0x10 | r2, r1, 0); ++ qca8k_mii_write32(bus, 0x10 | r2, r1, 0); + +- mutex_unlock(&priv->bus->mdio_lock); ++ mutex_unlock(&bus->mdio_lock); + + if (val >= 0) + val &= QCA8K_MDIO_MASTER_DATA_MASK; diff --git a/target/linux/generic/backport-5.10/735-v5.14-23-net-dsa-qca8k-pass-switch_revision-info-to-phy-dev_f.patch b/target/linux/generic/backport-5.10/735-v5.14-23-net-dsa-qca8k-pass-switch_revision-info-to-phy-dev_f.patch new file mode 100644 index 000000000..1179cf152 --- /dev/null +++ b/target/linux/generic/backport-5.10/735-v5.14-23-net-dsa-qca8k-pass-switch_revision-info-to-phy-dev_f.patch @@ -0,0 +1,48 @@ +From a46aec02bc06ac2c33f326339e4ef88c735dc30d Mon Sep 17 00:00:00 2001 +From: Ansuel Smith +Date: Fri, 14 May 2021 23:00:13 +0200 +Subject: [PATCH] net: dsa: qca8k: pass switch_revision info to phy dev_flags + +Define get_phy_flags to pass switch_Revision needed to tweak the +internal PHY with debug values based on the revision. + +Signed-off-by: Ansuel Smith +Reviewed-by: Florian Fainelli +Signed-off-by: David S. Miller +--- + drivers/net/dsa/qca8k.c | 17 +++++++++++++++++ + 1 file changed, 17 insertions(+) + +--- a/drivers/net/dsa/qca8k.c ++++ b/drivers/net/dsa/qca8k.c +@@ -1740,6 +1740,22 @@ qca8k_port_vlan_del(struct dsa_switch *d + 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) +@@ -1774,6 +1790,7 @@ static const struct dsa_switch_ops qca8k + .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, + }; + + static int qca8k_read_switch_id(struct qca8k_priv *priv) diff --git a/target/linux/generic/backport-5.10/735-v5.14-25-net-phy-add-support-for-qca8k-switch-internal-PHY-in.patch b/target/linux/generic/backport-5.10/735-v5.14-25-net-phy-add-support-for-qca8k-switch-internal-PHY-in.patch new file mode 100644 index 000000000..20325f564 --- /dev/null +++ b/target/linux/generic/backport-5.10/735-v5.14-25-net-phy-add-support-for-qca8k-switch-internal-PHY-in.patch @@ -0,0 +1,229 @@ +From 272833b9b3b3969be7a91839121d86662c8c4253 Mon Sep 17 00:00:00 2001 +From: Ansuel Smith +Date: Fri, 14 May 2021 23:00:15 +0200 +Subject: [PATCH] net: phy: add support for qca8k switch internal PHY in at803x + +Since the at803x share the same regs, it's assumed they are based on the +same implementation. Make it part of the at803x PHY driver to skip +having redudant code. +Add initial support for qca8k internal PHYs. The internal PHYs requires +special mmd and debug values to be set based on the switch revision +passwd using the dev_flags. Supports output of idle, receive and eee_wake +errors stats. +Some debug values sets can't be translated as the documentation lacks any +reference about them. + +Signed-off-by: Ansuel Smith +Signed-off-by: David S. Miller +--- + drivers/net/phy/Kconfig | 5 +- + drivers/net/phy/at803x.c | 132 ++++++++++++++++++++++++++++++++++++++- + 2 files changed, 134 insertions(+), 3 deletions(-) + +--- a/drivers/net/phy/Kconfig ++++ b/drivers/net/phy/Kconfig +@@ -235,10 +235,11 @@ config NXP_TJA11XX_PHY + Currently supports the NXP TJA1100 and TJA1101 PHY. + + config AT803X_PHY +- tristate "Qualcomm Atheros AR803X PHYs" ++ tristate "Qualcomm Atheros AR803X PHYs and QCA833x PHYs" + depends on REGULATOR + help +- Currently supports the AR8030, AR8031, AR8033 and AR8035 model ++ Currently supports the AR8030, AR8031, AR8033, AR8035 and internal ++ QCA8337(Internal qca8k PHY) model + + config QSEMI_PHY + tristate "Quality Semiconductor PHYs" +--- a/drivers/net/phy/at803x.c ++++ b/drivers/net/phy/at803x.c +@@ -92,10 +92,16 @@ + #define AT803X_DEBUG_REG_5 0x05 + #define AT803X_DEBUG_TX_CLK_DLY_EN BIT(8) + ++#define AT803X_DEBUG_REG_3C 0x3C ++ ++#define AT803X_DEBUG_REG_3D 0x3D ++ + #define AT803X_DEBUG_REG_1F 0x1F + #define AT803X_DEBUG_PLL_ON BIT(2) + #define AT803X_DEBUG_RGMII_1V8 BIT(3) + ++#define MDIO_AZ_DEBUG 0x800D ++ + /* AT803x supports either the XTAL input pad, an internal PLL or the + * DSP as clock reference for the clock output pad. The XTAL reference + * is only used for 25 MHz output, all other frequencies need the PLL. +@@ -142,10 +148,34 @@ + #define AT803X_PAGE_FIBER 0 + #define AT803X_PAGE_COPPER 1 + ++#define QCA8327_PHY_ID 0x004dd034 ++#define QCA8337_PHY_ID 0x004dd036 ++#define QCA8K_PHY_ID_MASK 0xffffffff ++ ++#define QCA8K_DEVFLAGS_REVISION_MASK GENMASK(2, 0) ++ + MODULE_DESCRIPTION("Qualcomm Atheros AR803x PHY driver"); + MODULE_AUTHOR("Matus Ujhelyi"); + MODULE_LICENSE("GPL"); + ++enum stat_access_type { ++ PHY, ++ MMD ++}; ++ ++struct at803x_hw_stat { ++ const char *string; ++ u8 reg; ++ u32 mask; ++ enum stat_access_type access_type; ++}; ++ ++static struct at803x_hw_stat at803x_hw_stats[] = { ++ { "phy_idle_errors", 0xa, GENMASK(7, 0), PHY}, ++ { "phy_receive_errors", 0x15, GENMASK(15, 0), PHY}, ++ { "eee_wake_errors", 0x16, GENMASK(15, 0), MMD}, ++}; ++ + struct at803x_priv { + int flags; + #define AT803X_KEEP_PLL_ENABLED BIT(0) /* don't turn off internal PLL */ +@@ -154,6 +184,7 @@ struct at803x_priv { + struct regulator_dev *vddio_rdev; + struct regulator_dev *vddh_rdev; + struct regulator *vddio; ++ u64 stats[ARRAY_SIZE(at803x_hw_stats)]; + }; + + struct at803x_context { +@@ -165,6 +196,17 @@ struct at803x_context { + u16 led_control; + }; + ++static int at803x_debug_reg_write(struct phy_device *phydev, u16 reg, u16 data) ++{ ++ int ret; ++ ++ ret = phy_write(phydev, AT803X_DEBUG_ADDR, reg); ++ if (ret < 0) ++ return ret; ++ ++ return phy_write(phydev, AT803X_DEBUG_DATA, data); ++} ++ + static int at803x_debug_reg_read(struct phy_device *phydev, u16 reg) + { + int ret; +@@ -327,6 +369,53 @@ static void at803x_get_wol(struct phy_de + wol->wolopts |= WAKE_MAGIC; + } + ++static int at803x_get_sset_count(struct phy_device *phydev) ++{ ++ return ARRAY_SIZE(at803x_hw_stats); ++} ++ ++static void at803x_get_strings(struct phy_device *phydev, u8 *data) ++{ ++ int i; ++ ++ for (i = 0; i < ARRAY_SIZE(at803x_hw_stats); i++) { ++ strscpy(data + i * ETH_GSTRING_LEN, ++ at803x_hw_stats[i].string, ETH_GSTRING_LEN); ++ } ++} ++ ++static u64 at803x_get_stat(struct phy_device *phydev, int i) ++{ ++ struct at803x_hw_stat stat = at803x_hw_stats[i]; ++ struct at803x_priv *priv = phydev->priv; ++ int val; ++ u64 ret; ++ ++ if (stat.access_type == MMD) ++ val = phy_read_mmd(phydev, MDIO_MMD_PCS, stat.reg); ++ else ++ val = phy_read(phydev, stat.reg); ++ ++ if (val < 0) { ++ ret = U64_MAX; ++ } else { ++ val = val & stat.mask; ++ priv->stats[i] += val; ++ ret = priv->stats[i]; ++ } ++ ++ return ret; ++} ++ ++static void at803x_get_stats(struct phy_device *phydev, ++ struct ethtool_stats *stats, u64 *data) ++{ ++ int i; ++ ++ for (i = 0; i < ARRAY_SIZE(at803x_hw_stats); i++) ++ data[i] = at803x_get_stat(phydev, i); ++} ++ + static int at803x_suspend(struct phy_device *phydev) + { + int value; +@@ -1102,6 +1191,34 @@ static int at803x_cable_test_start(struc + return 0; + } + ++static int qca83xx_config_init(struct phy_device *phydev) ++{ ++ u8 switch_revision; ++ ++ switch_revision = phydev->dev_flags & QCA8K_DEVFLAGS_REVISION_MASK; ++ ++ switch (switch_revision) { ++ case 1: ++ /* For 100M waveform */ ++ at803x_debug_reg_write(phydev, AT803X_DEBUG_REG_0, 0x02ea); ++ /* Turn on Gigabit clock */ ++ at803x_debug_reg_write(phydev, AT803X_DEBUG_REG_3D, 0x68a0); ++ break; ++ ++ case 2: ++ phy_write_mmd(phydev, MDIO_MMD_AN, MDIO_AN_EEE_ADV, 0x0); ++ 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_3C, 0x6000); ++ break; ++ } ++ ++ return 0; ++} ++ + static struct phy_driver at803x_driver[] = { + { + /* Qualcomm Atheros AR8035 */ +@@ -1198,7 +1315,20 @@ static struct phy_driver at803x_driver[] + .read_status = at803x_read_status, + .soft_reset = genphy_soft_reset, + .config_aneg = at803x_config_aneg, +-} }; ++}, { ++ /* QCA8337 */ ++ .phy_id = QCA8337_PHY_ID, ++ .phy_id_mask = QCA8K_PHY_ID_MASK, ++ .name = "QCA PHY 8337", ++ /* 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); + diff --git a/target/linux/generic/backport-5.10/736-v5.14-net-dsa-qca8k-fix-missing-unlock-on-error-in-qca8k-vlan.patch b/target/linux/generic/backport-5.10/736-v5.14-net-dsa-qca8k-fix-missing-unlock-on-error-in-qca8k-vlan.patch new file mode 100644 index 000000000..a68e3b182 --- /dev/null +++ b/target/linux/generic/backport-5.10/736-v5.14-net-dsa-qca8k-fix-missing-unlock-on-error-in-qca8k-vlan.patch @@ -0,0 +1,64 @@ +From 0d56e5c191b197e1d30a0a4c92628836dafced0f Mon Sep 17 00:00:00 2001 +From: Wei Yongjun +Date: Tue, 18 May 2021 11:24:13 +0000 +Subject: [PATCH] net: dsa: qca8k: fix missing unlock on error in + qca8k_vlan_(add|del) + +Add the missing unlock before return from function qca8k_vlan_add() +and qca8k_vlan_del() in the error handling case. + +Fixes: 028f5f8ef44f ("net: dsa: qca8k: handle error with qca8k_read operation") +Reported-by: Hulk Robot +Signed-off-by: Wei Yongjun +Reviewed-by: Vladimir Oltean +Signed-off-by: David S. Miller +--- + drivers/net/dsa/qca8k.c | 16 ++++++++++------ + 1 file changed, 10 insertions(+), 6 deletions(-) + +--- a/drivers/net/dsa/qca8k.c ++++ b/drivers/net/dsa/qca8k.c +@@ -506,8 +506,10 @@ qca8k_vlan_add(struct qca8k_priv *priv, + goto out; + + reg = qca8k_read(priv, QCA8K_REG_VTU_FUNC0); +- if (reg < 0) +- return reg; ++ if (reg < 0) { ++ ret = reg; ++ 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)); + if (untagged) +@@ -519,7 +521,7 @@ qca8k_vlan_add(struct qca8k_priv *priv, + + ret = qca8k_write(priv, QCA8K_REG_VTU_FUNC0, reg); + if (ret) +- return ret; ++ goto out; + ret = qca8k_vlan_access(priv, QCA8K_VLAN_LOAD, vid); + + out: +@@ -541,8 +543,10 @@ qca8k_vlan_del(struct qca8k_priv *priv, + goto out; + + reg = qca8k_read(priv, QCA8K_REG_VTU_FUNC0); +- if (reg < 0) +- return reg; ++ if (reg < 0) { ++ ret = reg; ++ 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); +@@ -564,7 +568,7 @@ qca8k_vlan_del(struct qca8k_priv *priv, + } else { + ret = qca8k_write(priv, QCA8K_REG_VTU_FUNC0, reg); + if (ret) +- return ret; ++ goto out; + ret = qca8k_vlan_access(priv, QCA8K_VLAN_LOAD, vid); + } + diff --git a/target/linux/generic/backport-5.10/737-v5.14-01-net-dsa-qca8k-check-return-value-of-read-functions-c.patch b/target/linux/generic/backport-5.10/737-v5.14-01-net-dsa-qca8k-check-return-value-of-read-functions-c.patch new file mode 100644 index 000000000..451b0e944 --- /dev/null +++ b/target/linux/generic/backport-5.10/737-v5.14-01-net-dsa-qca8k-check-return-value-of-read-functions-c.patch @@ -0,0 +1,348 @@ +From 7c9896e37807862e276064dd9331860f5d27affc Mon Sep 17 00:00:00 2001 +From: Yang Yingliang +Date: Sat, 29 May 2021 11:04:38 +0800 +Subject: [PATCH] net: dsa: qca8k: check return value of read functions + correctly + +Current return type of qca8k_mii_read32() and qca8k_read() are +unsigned, it can't be negative, so the return value check is +unuseful. For check the return value correctly, change return +type of the read functions and add a output parameter to store +the read value. + +Signed-off-by: Yang Yingliang +Signed-off-by: Jakub Kicinski +--- + drivers/net/dsa/qca8k.c | 130 +++++++++++++++++++--------------------- + 1 file changed, 60 insertions(+), 70 deletions(-) + +--- a/drivers/net/dsa/qca8k.c ++++ b/drivers/net/dsa/qca8k.c +@@ -89,26 +89,26 @@ qca8k_split_addr(u32 regaddr, u16 *r1, u + *page = regaddr & 0x3ff; + } + +-static u32 +-qca8k_mii_read32(struct mii_bus *bus, int phy_id, u32 regnum) ++static int ++qca8k_mii_read32(struct mii_bus *bus, int phy_id, u32 regnum, u32 *val) + { +- u32 val; + int ret; + + ret = bus->read(bus, phy_id, regnum); + if (ret >= 0) { +- val = ret; ++ *val = ret; + ret = bus->read(bus, phy_id, regnum + 1); +- val |= ret << 16; ++ *val |= ret << 16; + } + + if (ret < 0) { + dev_err_ratelimited(&bus->dev, + "failed to read qca8k 32bit register\n"); ++ *val = 0; + return ret; + } + +- return val; ++ return 0; + } + + static void +@@ -148,26 +148,26 @@ qca8k_set_page(struct mii_bus *bus, u16 + return 0; + } + +-static u32 +-qca8k_read(struct qca8k_priv *priv, u32 reg) ++static int ++qca8k_read(struct qca8k_priv *priv, u32 reg, u32 *val) + { + struct mii_bus *bus = priv->bus; + u16 r1, r2, page; +- u32 val; ++ int ret; + + qca8k_split_addr(reg, &r1, &r2, &page); + + mutex_lock_nested(&bus->mdio_lock, MDIO_MUTEX_NESTED); + +- val = qca8k_set_page(bus, page); +- if (val < 0) ++ ret = qca8k_set_page(bus, page); ++ if (ret < 0) + goto exit; + +- val = qca8k_mii_read32(bus, 0x10 | r2, r1); ++ ret = qca8k_mii_read32(bus, 0x10 | r2, r1, val); + + exit: + mutex_unlock(&bus->mdio_lock); +- return val; ++ return ret; + } + + static int +@@ -208,11 +208,9 @@ qca8k_rmw(struct qca8k_priv *priv, u32 r + if (ret < 0) + goto exit; + +- val = qca8k_mii_read32(bus, 0x10 | r2, r1); +- if (val < 0) { +- ret = val; ++ ret = qca8k_mii_read32(bus, 0x10 | r2, r1, &val); ++ if (ret < 0) + goto exit; +- } + + val &= ~mask; + val |= write_val; +@@ -240,15 +238,8 @@ static int + qca8k_regmap_read(void *ctx, uint32_t reg, uint32_t *val) + { + struct qca8k_priv *priv = (struct qca8k_priv *)ctx; +- int ret; +- +- ret = qca8k_read(priv, reg); +- if (ret < 0) +- return ret; +- +- *val = ret; + +- return 0; ++ return qca8k_read(priv, reg, val); + } + + static int +@@ -296,18 +287,18 @@ static struct regmap_config qca8k_regmap + static int + qca8k_busy_wait(struct qca8k_priv *priv, u32 reg, u32 mask) + { ++ int ret, ret1; + u32 val; +- int ret; + +- ret = read_poll_timeout(qca8k_read, val, !(val & mask), ++ ret = read_poll_timeout(qca8k_read, ret1, !(val & mask), + 0, QCA8K_BUSY_WAIT_TIMEOUT * USEC_PER_MSEC, false, +- priv, reg); ++ priv, reg, &val); + + /* Check if qca8k_read has failed for a different reason + * before returning -ETIMEDOUT + */ +- if (ret < 0 && val < 0) +- return val; ++ if (ret < 0 && ret1 < 0) ++ return ret1; + + return ret; + } +@@ -316,13 +307,13 @@ static int + qca8k_fdb_read(struct qca8k_priv *priv, struct qca8k_fdb *fdb) + { + u32 reg[4], val; +- int i; ++ int i, ret; + + /* load the ARL table into an array */ + for (i = 0; i < 4; i++) { +- val = qca8k_read(priv, QCA8K_REG_ATU_DATA0 + (i * 4)); +- if (val < 0) +- return val; ++ ret = qca8k_read(priv, QCA8K_REG_ATU_DATA0 + (i * 4), &val); ++ if (ret < 0) ++ return ret; + + reg[i] = val; + } +@@ -396,9 +387,9 @@ qca8k_fdb_access(struct qca8k_priv *priv + + /* Check for table full violation when adding an entry */ + if (cmd == QCA8K_FDB_LOAD) { +- reg = qca8k_read(priv, QCA8K_REG_ATU_FUNC); +- if (reg < 0) +- return reg; ++ ret = qca8k_read(priv, QCA8K_REG_ATU_FUNC, ®); ++ if (ret < 0) ++ return ret; + if (reg & QCA8K_ATU_FUNC_FULL) + return -1; + } +@@ -477,9 +468,9 @@ qca8k_vlan_access(struct qca8k_priv *pri + + /* Check for table full violation when adding an entry */ + if (cmd == QCA8K_VLAN_LOAD) { +- reg = qca8k_read(priv, QCA8K_REG_VTU_FUNC1); +- if (reg < 0) +- return reg; ++ ret = qca8k_read(priv, QCA8K_REG_VTU_FUNC1, ®); ++ if (ret < 0) ++ return ret; + if (reg & QCA8K_VTU_FUNC1_FULL) + return -ENOMEM; + } +@@ -505,11 +496,9 @@ qca8k_vlan_add(struct qca8k_priv *priv, + if (ret < 0) + goto out; + +- reg = qca8k_read(priv, QCA8K_REG_VTU_FUNC0); +- if (reg < 0) { +- ret = reg; ++ 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_MASK << QCA8K_VTU_FUNC0_EG_MODE_S(port)); + if (untagged) +@@ -542,11 +531,9 @@ qca8k_vlan_del(struct qca8k_priv *priv, + if (ret < 0) + goto out; + +- reg = qca8k_read(priv, QCA8K_REG_VTU_FUNC0); +- if (reg < 0) { +- ret = reg; ++ 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); +@@ -638,19 +625,19 @@ qca8k_mdio_busy_wait(struct mii_bus *bus + { + u16 r1, r2, page; + u32 val; +- int ret; ++ int ret, ret1; + + qca8k_split_addr(reg, &r1, &r2, &page); + +- ret = read_poll_timeout(qca8k_mii_read32, val, !(val & mask), 0, ++ ret = read_poll_timeout(qca8k_mii_read32, ret1, !(val & mask), 0, + QCA8K_BUSY_WAIT_TIMEOUT * USEC_PER_MSEC, false, +- bus, 0x10 | r2, r1); ++ bus, 0x10 | r2, r1, &val); + + /* Check if qca8k_read has failed for a different reason + * before returnting -ETIMEDOUT + */ +- if (ret < 0 && val < 0) +- return val; ++ if (ret < 0 && ret1 < 0) ++ return ret1; + + return ret; + } +@@ -725,7 +712,7 @@ qca8k_mdio_read(struct mii_bus *salve_bu + if (ret) + goto exit; + +- val = qca8k_mii_read32(bus, 0x10 | r2, r1); ++ ret = qca8k_mii_read32(bus, 0x10 | r2, r1, &val); + + exit: + /* even if the busy_wait timeouts try to clear the MASTER_EN */ +@@ -733,10 +720,10 @@ exit: + + mutex_unlock(&bus->mdio_lock); + +- if (val >= 0) +- val &= QCA8K_MDIO_MASTER_DATA_MASK; ++ if (ret >= 0) ++ ret = val & QCA8K_MDIO_MASTER_DATA_MASK; + +- return val; ++ return ret; + } + + static int +@@ -1211,7 +1198,7 @@ qca8k_phylink_mac_config(struct dsa_swit + qca8k_write(priv, reg, QCA8K_PORT_PAD_SGMII_EN); + + /* Enable/disable SerDes auto-negotiation as necessary */ +- val = qca8k_read(priv, QCA8K_REG_PWS); ++ qca8k_read(priv, QCA8K_REG_PWS, &val); + if (phylink_autoneg_inband(mode)) + val &= ~QCA8K_PWS_SERDES_AEN_DIS; + else +@@ -1219,7 +1206,7 @@ qca8k_phylink_mac_config(struct dsa_swit + qca8k_write(priv, QCA8K_REG_PWS, val); + + /* Configure the SGMII parameters */ +- val = qca8k_read(priv, QCA8K_REG_SGMII_CTRL); ++ qca8k_read(priv, QCA8K_REG_SGMII_CTRL, &val); + + val |= QCA8K_SGMII_EN_PLL | QCA8K_SGMII_EN_RX | + QCA8K_SGMII_EN_TX | QCA8K_SGMII_EN_SD; +@@ -1314,10 +1301,11 @@ qca8k_phylink_mac_link_state(struct dsa_ + { + struct qca8k_priv *priv = ds->priv; + u32 reg; ++ int ret; + +- reg = qca8k_read(priv, QCA8K_REG_PORT_STATUS(port)); +- if (reg < 0) +- return reg; ++ 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; +@@ -1419,19 +1407,20 @@ qca8k_get_ethtool_stats(struct dsa_switc + struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv; + const struct qca8k_mib_desc *mib; + u32 reg, i, val; +- u64 hi; ++ u64 hi = 0; ++ int ret; + + for (i = 0; i < ARRAY_SIZE(ar8327_mib); i++) { + mib = &ar8327_mib[i]; + reg = QCA8K_PORT_MIB_COUNTER(port) + mib->offset; + +- val = qca8k_read(priv, reg); +- if (val < 0) ++ ret = qca8k_read(priv, reg, &val); ++ if (ret < 0) + continue; + + if (mib->size == 2) { +- hi = qca8k_read(priv, reg + 4); +- if (hi < 0) ++ ret = qca8k_read(priv, reg + 4, (u32 *)&hi); ++ if (ret < 0) + continue; + } + +@@ -1459,7 +1448,7 @@ qca8k_set_mac_eee(struct dsa_switch *ds, + int ret; + + mutex_lock(&priv->reg_mutex); +- reg = qca8k_read(priv, QCA8K_REG_EEE_CTRL); ++ ret = qca8k_read(priv, QCA8K_REG_EEE_CTRL, ®); + if (reg < 0) { + ret = reg; + goto exit; +@@ -1802,14 +1791,15 @@ static int qca8k_read_switch_id(struct q + 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; + +- val = qca8k_read(priv, QCA8K_REG_MASK_CTRL); +- if (val < 0) ++ ret = qca8k_read(priv, QCA8K_REG_MASK_CTRL, &val); ++ if (ret < 0) + return -ENODEV; + + id = QCA8K_MASK_CTRL_DEVICE_ID(val & QCA8K_MASK_CTRL_DEVICE_ID_MASK); diff --git a/target/linux/generic/backport-5.10/737-v5.14-02-net-dsa-qca8k-add-missing-check-return-value-in-qca8.patch b/target/linux/generic/backport-5.10/737-v5.14-02-net-dsa-qca8k-add-missing-check-return-value-in-qca8.patch new file mode 100644 index 000000000..d20da5b85 --- /dev/null +++ b/target/linux/generic/backport-5.10/737-v5.14-02-net-dsa-qca8k-add-missing-check-return-value-in-qca8.patch @@ -0,0 +1,47 @@ +From 9fe99de01440d9ede74d447ac76e9c445d8daae9 Mon Sep 17 00:00:00 2001 +From: Yang Yingliang +Date: Sat, 29 May 2021 11:04:39 +0800 +Subject: [PATCH] net: dsa: qca8k: add missing check return value in + qca8k_phylink_mac_config() + +Now we can check qca8k_read() return value correctly, so if +it fails, we need return directly. + +Signed-off-by: Yang Yingliang +Signed-off-by: Jakub Kicinski +--- + drivers/net/dsa/qca8k.c | 9 +++++++-- + 1 file changed, 7 insertions(+), 2 deletions(-) + +--- a/drivers/net/dsa/qca8k.c ++++ b/drivers/net/dsa/qca8k.c +@@ -1128,6 +1128,7 @@ qca8k_phylink_mac_config(struct dsa_swit + { + struct qca8k_priv *priv = ds->priv; + u32 reg, val; ++ int ret; + + switch (port) { + case 0: /* 1st CPU port */ +@@ -1198,7 +1199,9 @@ qca8k_phylink_mac_config(struct dsa_swit + qca8k_write(priv, reg, QCA8K_PORT_PAD_SGMII_EN); + + /* Enable/disable SerDes auto-negotiation as necessary */ +- qca8k_read(priv, QCA8K_REG_PWS, &val); ++ ret = qca8k_read(priv, QCA8K_REG_PWS, &val); ++ if (ret) ++ return; + if (phylink_autoneg_inband(mode)) + val &= ~QCA8K_PWS_SERDES_AEN_DIS; + else +@@ -1206,7 +1209,9 @@ qca8k_phylink_mac_config(struct dsa_swit + qca8k_write(priv, QCA8K_REG_PWS, val); + + /* Configure the SGMII parameters */ +- qca8k_read(priv, QCA8K_REG_SGMII_CTRL, &val); ++ ret = qca8k_read(priv, QCA8K_REG_SGMII_CTRL, &val); ++ if (ret) ++ return; + + val |= QCA8K_SGMII_EN_PLL | QCA8K_SGMII_EN_RX | + QCA8K_SGMII_EN_TX | QCA8K_SGMII_EN_SD; diff --git a/target/linux/generic/backport-5.10/738-v5.14-01-net-dsa-qca8k-fix-an-endian-bug-in-qca8k-get-ethtool.patch b/target/linux/generic/backport-5.10/738-v5.14-01-net-dsa-qca8k-fix-an-endian-bug-in-qca8k-get-ethtool.patch new file mode 100644 index 000000000..aed97d054 --- /dev/null +++ b/target/linux/generic/backport-5.10/738-v5.14-01-net-dsa-qca8k-fix-an-endian-bug-in-qca8k-get-ethtool.patch @@ -0,0 +1,47 @@ +From aa3d020b22cb844ab7bdbb9e5d861a64666e2b74 Mon Sep 17 00:00:00 2001 +From: Dan Carpenter +Date: Wed, 9 Jun 2021 12:52:12 +0300 +Subject: [PATCH] net: dsa: qca8k: fix an endian bug in + qca8k_get_ethtool_stats() + +The "hi" variable is a u64 but the qca8k_read() writes to the top 32 +bits of it. That will work on little endian systems but it's a bit +subtle. It's cleaner to make declare "hi" as a u32. We will still need +to cast it when we shift it later on in the function but that's fine. + +Fixes: 7c9896e37807 ("net: dsa: qca8k: check return value of read functions correctly") +Signed-off-by: Dan Carpenter +Reviewed-by: Vladimir Oltean +Signed-off-by: David S. Miller +--- + drivers/net/dsa/qca8k.c | 6 +++--- + 1 file changed, 3 insertions(+), 3 deletions(-) + +--- a/drivers/net/dsa/qca8k.c ++++ b/drivers/net/dsa/qca8k.c +@@ -1412,7 +1412,7 @@ qca8k_get_ethtool_stats(struct dsa_switc + struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv; + const struct qca8k_mib_desc *mib; + u32 reg, i, val; +- u64 hi = 0; ++ u32 hi = 0; + int ret; + + for (i = 0; i < ARRAY_SIZE(ar8327_mib); i++) { +@@ -1424,14 +1424,14 @@ qca8k_get_ethtool_stats(struct dsa_switc + continue; + + if (mib->size == 2) { +- ret = qca8k_read(priv, reg + 4, (u32 *)&hi); ++ ret = qca8k_read(priv, reg + 4, &hi); + if (ret < 0) + continue; + } + + data[i] = val; + if (mib->size == 2) +- data[i] |= hi << 32; ++ data[i] |= (u64)hi << 32; + } + } + diff --git a/target/linux/generic/backport-5.10/738-v5.14-02-net-dsa-qca8k-check-the-correct-variable-in-qca8k-se.patch b/target/linux/generic/backport-5.10/738-v5.14-02-net-dsa-qca8k-check-the-correct-variable-in-qca8k-se.patch new file mode 100644 index 000000000..c58f79cd8 --- /dev/null +++ b/target/linux/generic/backport-5.10/738-v5.14-02-net-dsa-qca8k-check-the-correct-variable-in-qca8k-se.patch @@ -0,0 +1,31 @@ +From 3d0167f2a627528032821cdeb78b4eab0510460f Mon Sep 17 00:00:00 2001 +From: Dan Carpenter +Date: Wed, 9 Jun 2021 12:53:03 +0300 +Subject: [PATCH] net: dsa: qca8k: check the correct variable in + qca8k_set_mac_eee() + +This code check "reg" but "ret" was intended so the error handling will +never trigger. + +Fixes: 7c9896e37807 ("net: dsa: qca8k: check return value of read functions correctly") +Signed-off-by: Dan Carpenter +Reviewed-by: Vladimir Oltean +Signed-off-by: David S. Miller +--- + drivers/net/dsa/qca8k.c | 4 +--- + 1 file changed, 1 insertion(+), 3 deletions(-) + +--- a/drivers/net/dsa/qca8k.c ++++ b/drivers/net/dsa/qca8k.c +@@ -1454,10 +1454,8 @@ qca8k_set_mac_eee(struct dsa_switch *ds, + + mutex_lock(&priv->reg_mutex); + ret = qca8k_read(priv, QCA8K_REG_EEE_CTRL, ®); +- if (reg < 0) { +- ret = reg; ++ if (ret < 0) + goto exit; +- } + + if (eee->eee_enabled) + reg |= lpi_en; diff --git a/target/linux/generic/backport-5.10/739-v5.15-net-dsa-qca8k-fix-kernel-panic-with-legacy-mdio-mapping.patch b/target/linux/generic/backport-5.10/739-v5.15-net-dsa-qca8k-fix-kernel-panic-with-legacy-mdio-mapping.patch new file mode 100644 index 000000000..1e293d3a0 --- /dev/null +++ b/target/linux/generic/backport-5.10/739-v5.15-net-dsa-qca8k-fix-kernel-panic-with-legacy-mdio-mapping.patch @@ -0,0 +1,80 @@ +From ce062a0adbfe933b1932235fdfd874c4c91d1bb0 Mon Sep 17 00:00:00 2001 +From: Ansuel Smith +Date: Sat, 11 Sep 2021 17:50:09 +0200 +Subject: net: dsa: qca8k: fix kernel panic with legacy mdio mapping + +When the mdio legacy mapping is used the mii_bus priv registered by DSA +refer to the dsa switch struct instead of the qca8k_priv struct and +causes a kernel panic. Create dedicated function when the internal +dedicated mdio driver is used to properly handle the 2 different +implementation. + +Fixes: 759bafb8a322 ("net: dsa: qca8k: add support for internal phy and internal mdio") +Signed-off-by: Ansuel Smith +Signed-off-by: David S. Miller +--- + drivers/net/dsa/qca8k.c | 30 ++++++++++++++++++++++-------- + 1 file changed, 22 insertions(+), 8 deletions(-) + +--- a/drivers/net/dsa/qca8k.c ++++ b/drivers/net/dsa/qca8k.c +@@ -643,10 +643,8 @@ qca8k_mdio_busy_wait(struct mii_bus *bus + } + + static int +-qca8k_mdio_write(struct mii_bus *salve_bus, int phy, int regnum, u16 data) ++qca8k_mdio_write(struct mii_bus *bus, int phy, int regnum, u16 data) + { +- struct qca8k_priv *priv = salve_bus->priv; +- struct mii_bus *bus = priv->bus; + u16 r1, r2, page; + u32 val; + int ret; +@@ -682,10 +680,8 @@ exit: + } + + static int +-qca8k_mdio_read(struct mii_bus *salve_bus, int phy, int regnum) ++qca8k_mdio_read(struct mii_bus *bus, int phy, int regnum) + { +- struct qca8k_priv *priv = salve_bus->priv; +- struct mii_bus *bus = priv->bus; + u16 r1, r2, page; + u32 val; + int ret; +@@ -727,6 +723,24 @@ exit: + } + + 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; ++ ++ return qca8k_mdio_write(bus, 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; ++ ++ return qca8k_mdio_read(bus, phy, regnum); ++} ++ ++static int + qca8k_phy_write(struct dsa_switch *ds, int port, int regnum, u16 data) + { + struct qca8k_priv *priv = ds->priv; +@@ -775,8 +789,8 @@ qca8k_mdio_register(struct qca8k_priv *p + + bus->priv = (void *)priv; + bus->name = "qca8k slave mii"; +- bus->read = qca8k_mdio_read; +- bus->write = qca8k_mdio_write; ++ bus->read = qca8k_internal_mdio_read; ++ bus->write = qca8k_internal_mdio_write; + snprintf(bus->id, MII_BUS_ID_SIZE, "qca8k-%d", + ds->index); + diff --git a/target/linux/generic/backport-5.10/740-v5.13-0001-net-dsa-b53-Add-debug-prints-in-b53_vlan_enable.patch b/target/linux/generic/backport-5.10/740-v5.13-0001-net-dsa-b53-Add-debug-prints-in-b53_vlan_enable.patch new file mode 100644 index 000000000..91cf55b18 --- /dev/null +++ b/target/linux/generic/backport-5.10/740-v5.13-0001-net-dsa-b53-Add-debug-prints-in-b53_vlan_enable.patch @@ -0,0 +1,65 @@ +From ee47ed08d75e8f16b3cf882061ee19c2ea19dd6c Mon Sep 17 00:00:00 2001 +From: Florian Fainelli +Date: Wed, 10 Mar 2021 10:52:26 -0800 +Subject: [PATCH] net: dsa: b53: Add debug prints in b53_vlan_enable() + +Having dynamic debug prints in b53_vlan_enable() has been helpful to +uncover a recent but update the function to indicate the port being +configured (or -1 for initial setup) and include the global VLAN enabled +and VLAN filtering enable status. + +Signed-off-by: Florian Fainelli +Reviewed-by: Andrew Lunn +Signed-off-by: David S. Miller +--- + drivers/net/dsa/b53/b53_common.c | 11 +++++++---- + 1 file changed, 7 insertions(+), 4 deletions(-) + +--- a/drivers/net/dsa/b53/b53_common.c ++++ b/drivers/net/dsa/b53/b53_common.c +@@ -349,7 +349,7 @@ static void b53_set_forwarding(struct b5 + b53_write8(dev, B53_CTRL_PAGE, B53_IP_MULTICAST_CTRL, mgmt); + } + +-static void b53_enable_vlan(struct b53_device *dev, bool enable, ++static void b53_enable_vlan(struct b53_device *dev, int port, bool enable, + bool enable_filtering) + { + u8 mgmt, vc0, vc1, vc4 = 0, vc5; +@@ -431,6 +431,9 @@ static void b53_enable_vlan(struct b53_d + b53_write8(dev, B53_CTRL_PAGE, B53_SWITCH_MODE, mgmt); + + dev->vlan_enabled = enable; ++ ++ dev_dbg(dev->dev, "Port %d VLAN enabled: %d, filtering: %d\n", ++ port, enable, enable_filtering); + } + + static int b53_set_jumbo(struct b53_device *dev, bool enable, bool allow_10_100) +@@ -708,7 +711,7 @@ int b53_configure_vlan(struct dsa_switch + b53_do_vlan_op(dev, VTA_CMD_CLEAR); + } + +- b53_enable_vlan(dev, dev->vlan_enabled, ds->vlan_filtering); ++ b53_enable_vlan(dev, -1, dev->vlan_enabled, ds->vlan_filtering); + + b53_for_each_port(dev, i) + b53_write16(dev, B53_VLAN_PAGE, +@@ -1390,7 +1393,7 @@ int b53_vlan_filtering(struct dsa_switch + if (switchdev_trans_ph_prepare(trans)) + return 0; + +- b53_enable_vlan(dev, dev->vlan_enabled, vlan_filtering); ++ b53_enable_vlan(dev, port, dev->vlan_enabled, vlan_filtering); + + return 0; + } +@@ -1415,7 +1418,7 @@ int b53_vlan_prepare(struct dsa_switch * + if (vlan->vid_end >= dev->num_vlans) + return -ERANGE; + +- b53_enable_vlan(dev, true, ds->vlan_filtering); ++ b53_enable_vlan(dev, port, true, ds->vlan_filtering); + + return 0; + } diff --git a/target/linux/generic/backport-5.10/740-v5.13-0002-net-dsa-b53-spi-allow-device-tree-probing.patch b/target/linux/generic/backport-5.10/740-v5.13-0002-net-dsa-b53-spi-allow-device-tree-probing.patch new file mode 100644 index 000000000..56579b2d3 --- /dev/null +++ b/target/linux/generic/backport-5.10/740-v5.13-0002-net-dsa-b53-spi-allow-device-tree-probing.patch @@ -0,0 +1,41 @@ +From 6d16eadab6db0c1d61e59fee7ed1ecc2d10269be Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?=C3=81lvaro=20Fern=C3=A1ndez=20Rojas?= +Date: Mon, 15 Mar 2021 15:14:23 +0100 +Subject: [PATCH] net: dsa: b53: spi: allow device tree probing +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Add missing of_match_table to allow device tree probing. + +Signed-off-by: Álvaro Fernández Rojas +Signed-off-by: David S. Miller +--- + drivers/net/dsa/b53/b53_spi.c | 13 +++++++++++++ + 1 file changed, 13 insertions(+) + +--- a/drivers/net/dsa/b53/b53_spi.c ++++ b/drivers/net/dsa/b53/b53_spi.c +@@ -324,9 +324,22 @@ static int b53_spi_remove(struct spi_dev + return 0; + } + ++static const struct of_device_id b53_spi_of_match[] = { ++ { .compatible = "brcm,bcm5325" }, ++ { .compatible = "brcm,bcm5365" }, ++ { .compatible = "brcm,bcm5395" }, ++ { .compatible = "brcm,bcm5397" }, ++ { .compatible = "brcm,bcm5398" }, ++ { .compatible = "brcm,bcm53115" }, ++ { .compatible = "brcm,bcm53125" }, ++ { .compatible = "brcm,bcm53128" }, ++ { /* sentinel */ } ++}; ++ + static struct spi_driver b53_spi_driver = { + .driver = { + .name = "b53-switch", ++ .of_match_table = b53_spi_of_match, + }, + .probe = b53_spi_probe, + .remove = b53_spi_remove, diff --git a/target/linux/generic/backport-5.10/740-v5.13-0003-net-dsa-b53-relax-is63xx-condition.patch b/target/linux/generic/backport-5.10/740-v5.13-0003-net-dsa-b53-relax-is63xx-condition.patch new file mode 100644 index 000000000..99eced1b6 --- /dev/null +++ b/target/linux/generic/backport-5.10/740-v5.13-0003-net-dsa-b53-relax-is63xx-condition.patch @@ -0,0 +1,31 @@ +From ad426d7d966b525b73ed5a1842dd830312bbba71 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?=C3=81lvaro=20Fern=C3=A1ndez=20Rojas?= +Date: Wed, 17 Mar 2021 09:42:01 +0100 +Subject: [PATCH] net: dsa: b53: relax is63xx() condition +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +BCM63xx switches are present on bcm63xx and bmips devices. + +Signed-off-by: Álvaro Fernández Rojas +Acked-by: Florian Fainelli +Signed-off-by: David S. Miller +--- + drivers/net/dsa/b53/b53_priv.h | 4 ---- + 1 file changed, 4 deletions(-) + +--- a/drivers/net/dsa/b53/b53_priv.h ++++ b/drivers/net/dsa/b53/b53_priv.h +@@ -186,11 +186,7 @@ static inline int is531x5(struct b53_dev + + static inline int is63xx(struct b53_device *dev) + { +-#ifdef CONFIG_BCM63XX + return dev->chip_id == BCM63XX_DEVICE_ID; +-#else +- return 0; +-#endif + } + + static inline int is5301x(struct b53_device *dev) diff --git a/target/linux/generic/backport-5.10/740-v5.13-0004-net-dsa-tag_brcm-add-support-for-legacy-tags.patch b/target/linux/generic/backport-5.10/740-v5.13-0004-net-dsa-tag_brcm-add-support-for-legacy-tags.patch new file mode 100644 index 000000000..3b7d8f37c --- /dev/null +++ b/target/linux/generic/backport-5.10/740-v5.13-0004-net-dsa-tag_brcm-add-support-for-legacy-tags.patch @@ -0,0 +1,180 @@ +From 964dbf186eaa84d409c359ddf09c827a3fbe8228 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?=C3=81lvaro=20Fern=C3=A1ndez=20Rojas?= +Date: Wed, 17 Mar 2021 11:29:26 +0100 +Subject: [PATCH] net: dsa: tag_brcm: add support for legacy tags +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Add support for legacy Broadcom tags, which are similar to DSA_TAG_PROTO_BRCM. +These tags are used on BCM5325, BCM5365 and BCM63xx switches. + +Signed-off-by: Álvaro Fernández Rojas +Signed-off-by: David S. Miller +--- + include/net/dsa.h | 2 + + net/dsa/Kconfig | 7 +++ + net/dsa/tag_brcm.c | 107 +++++++++++++++++++++++++++++++++++++++++++-- + 3 files changed, 113 insertions(+), 3 deletions(-) + +--- a/include/net/dsa.h ++++ b/include/net/dsa.h +@@ -45,10 +45,12 @@ struct phylink_link_state; + #define DSA_TAG_PROTO_OCELOT_VALUE 15 + #define DSA_TAG_PROTO_AR9331_VALUE 16 + #define DSA_TAG_PROTO_RTL4_A_VALUE 17 ++#define DSA_TAG_PROTO_BRCM_LEGACY_VALUE 22 + + enum dsa_tag_protocol { + DSA_TAG_PROTO_NONE = DSA_TAG_PROTO_NONE_VALUE, + DSA_TAG_PROTO_BRCM = DSA_TAG_PROTO_BRCM_VALUE, ++ DSA_TAG_PROTO_BRCM_LEGACY = DSA_TAG_PROTO_BRCM_LEGACY_VALUE, + DSA_TAG_PROTO_BRCM_PREPEND = DSA_TAG_PROTO_BRCM_PREPEND_VALUE, + DSA_TAG_PROTO_DSA = DSA_TAG_PROTO_DSA_VALUE, + DSA_TAG_PROTO_EDSA = DSA_TAG_PROTO_EDSA_VALUE, +--- a/net/dsa/Kconfig ++++ b/net/dsa/Kconfig +@@ -47,6 +47,13 @@ config NET_DSA_TAG_BRCM + Say Y if you want to enable support for tagging frames for the + Broadcom switches which place the tag after the MAC source address. + ++config NET_DSA_TAG_BRCM_LEGACY ++ tristate "Tag driver for Broadcom legacy switches using in-frame headers" ++ select NET_DSA_TAG_BRCM_COMMON ++ help ++ Say Y if you want to enable support for tagging frames for the ++ Broadcom legacy switches which place the tag after the MAC source ++ address. + + config NET_DSA_TAG_BRCM_PREPEND + tristate "Tag driver for Broadcom switches using prepended headers" +--- a/net/dsa/tag_brcm.c ++++ b/net/dsa/tag_brcm.c +@@ -11,9 +11,26 @@ + + #include "dsa_priv.h" + +-/* This tag length is 4 bytes, older ones were 6 bytes, we do not +- * handle them +- */ ++/* Legacy Broadcom tag (6 bytes) */ ++#define BRCM_LEG_TAG_LEN 6 ++ ++/* Type fields */ ++/* 1st byte in the tag */ ++#define BRCM_LEG_TYPE_HI 0x88 ++/* 2nd byte in the tag */ ++#define BRCM_LEG_TYPE_LO 0x74 ++ ++/* Tag fields */ ++/* 3rd byte in the tag */ ++#define BRCM_LEG_UNICAST (0 << 5) ++#define BRCM_LEG_MULTICAST (1 << 5) ++#define BRCM_LEG_EGRESS (2 << 5) ++#define BRCM_LEG_INGRESS (3 << 5) ++ ++/* 6th byte in the tag */ ++#define BRCM_LEG_PORT_ID (0xf) ++ ++/* Newer Broadcom tag (4 bytes) */ + #define BRCM_TAG_LEN 4 + + /* Tag is constructed and desconstructed using byte by byte access +@@ -194,6 +211,87 @@ DSA_TAG_DRIVER(brcm_netdev_ops); + MODULE_ALIAS_DSA_TAG_DRIVER(DSA_TAG_PROTO_BRCM); + #endif + ++#if IS_ENABLED(CONFIG_NET_DSA_TAG_BRCM_LEGACY) ++static struct sk_buff *brcm_leg_tag_xmit(struct sk_buff *skb, ++ struct net_device *dev) ++{ ++ struct dsa_port *dp = dsa_slave_to_port(dev); ++ u8 *brcm_tag; ++ ++ /* The Ethernet switch we are interfaced with needs packets to be at ++ * least 64 bytes (including FCS) otherwise they will be discarded when ++ * they enter the switch port logic. When Broadcom tags are enabled, we ++ * need to make sure that packets are at least 70 bytes ++ * (including FCS and tag) because the length verification is done after ++ * the Broadcom tag is stripped off the ingress packet. ++ * ++ * Let dsa_slave_xmit() free the SKB ++ */ ++ if (__skb_put_padto(skb, ETH_ZLEN + BRCM_LEG_TAG_LEN, false)) ++ return NULL; ++ ++ skb_push(skb, BRCM_LEG_TAG_LEN); ++ ++ memmove(skb->data, skb->data + BRCM_LEG_TAG_LEN, 2 * ETH_ALEN); ++ ++ brcm_tag = skb->data + 2 * ETH_ALEN; ++ ++ /* Broadcom tag type */ ++ brcm_tag[0] = BRCM_LEG_TYPE_HI; ++ brcm_tag[1] = BRCM_LEG_TYPE_LO; ++ ++ /* Broadcom tag value */ ++ brcm_tag[2] = BRCM_LEG_EGRESS; ++ brcm_tag[3] = 0; ++ brcm_tag[4] = 0; ++ brcm_tag[5] = dp->index & BRCM_LEG_PORT_ID; ++ ++ return skb; ++} ++ ++static struct sk_buff *brcm_leg_tag_rcv(struct sk_buff *skb, ++ struct net_device *dev, ++ struct packet_type *pt) ++{ ++ int source_port; ++ u8 *brcm_tag; ++ ++ if (unlikely(!pskb_may_pull(skb, BRCM_LEG_PORT_ID))) ++ return NULL; ++ ++ brcm_tag = skb->data - 2; ++ ++ source_port = brcm_tag[5] & BRCM_LEG_PORT_ID; ++ ++ skb->dev = dsa_master_find_slave(dev, 0, source_port); ++ if (!skb->dev) ++ return NULL; ++ ++ /* Remove Broadcom tag and update checksum */ ++ skb_pull_rcsum(skb, BRCM_LEG_TAG_LEN); ++ ++ skb->offload_fwd_mark = 1; ++ ++ /* Move the Ethernet DA and SA */ ++ memmove(skb->data - ETH_HLEN, ++ skb->data - ETH_HLEN - BRCM_LEG_TAG_LEN, ++ 2 * ETH_ALEN); ++ ++ return skb; ++} ++ ++static const struct dsa_device_ops brcm_legacy_netdev_ops = { ++ .name = "brcm-legacy", ++ .proto = DSA_TAG_PROTO_BRCM_LEGACY, ++ .xmit = brcm_leg_tag_xmit, ++ .rcv = brcm_leg_tag_rcv, ++ .overhead = BRCM_LEG_TAG_LEN, ++}; ++ ++DSA_TAG_DRIVER(brcm_legacy_netdev_ops); ++MODULE_ALIAS_DSA_TAG_DRIVER(DSA_TAG_PROTO_BRCM_LEGACY); ++#endif /* CONFIG_NET_DSA_TAG_BRCM_LEGACY */ ++ + #if IS_ENABLED(CONFIG_NET_DSA_TAG_BRCM_PREPEND) + static struct sk_buff *brcm_tag_xmit_prepend(struct sk_buff *skb, + struct net_device *dev) +@@ -226,6 +324,9 @@ static struct dsa_tag_driver *dsa_tag_dr + #if IS_ENABLED(CONFIG_NET_DSA_TAG_BRCM) + &DSA_TAG_DRIVER_NAME(brcm_netdev_ops), + #endif ++#if IS_ENABLED(CONFIG_NET_DSA_TAG_BRCM_LEGACY) ++ &DSA_TAG_DRIVER_NAME(brcm_legacy_netdev_ops), ++#endif + #if IS_ENABLED(CONFIG_NET_DSA_TAG_BRCM_PREPEND) + &DSA_TAG_DRIVER_NAME(brcm_prepend_netdev_ops), + #endif diff --git a/target/linux/generic/backport-5.10/740-v5.13-0005-net-dsa-b53-support-legacy-tags.patch b/target/linux/generic/backport-5.10/740-v5.13-0005-net-dsa-b53-support-legacy-tags.patch new file mode 100644 index 000000000..838e78a05 --- /dev/null +++ b/target/linux/generic/backport-5.10/740-v5.13-0005-net-dsa-b53-support-legacy-tags.patch @@ -0,0 +1,53 @@ +From 46c5176c586c81306bf9e7024c13b95da775490f Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?=C3=81lvaro=20Fern=C3=A1ndez=20Rojas?= +Date: Wed, 17 Mar 2021 11:29:27 +0100 +Subject: [PATCH] net: dsa: b53: support legacy tags +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +These tags are used on BCM5325, BCM5365 and BCM63xx switches. + +Signed-off-by: Álvaro Fernández Rojas +Acked-by: Florian Fainelli +Signed-off-by: David S. Miller +--- + drivers/net/dsa/b53/Kconfig | 1 + + drivers/net/dsa/b53/b53_common.c | 12 +++++++----- + 2 files changed, 8 insertions(+), 5 deletions(-) + +--- a/drivers/net/dsa/b53/Kconfig ++++ b/drivers/net/dsa/b53/Kconfig +@@ -3,6 +3,7 @@ menuconfig B53 + tristate "Broadcom BCM53xx managed switch support" + depends on NET_DSA + select NET_DSA_TAG_BRCM ++ select NET_DSA_TAG_BRCM_LEGACY + select NET_DSA_TAG_BRCM_PREPEND + help + This driver adds support for Broadcom managed switch chips. It supports +--- a/drivers/net/dsa/b53/b53_common.c ++++ b/drivers/net/dsa/b53/b53_common.c +@@ -2024,15 +2024,17 @@ enum dsa_tag_protocol b53_get_tag_protoc + { + struct b53_device *dev = ds->priv; + +- /* Older models (5325, 5365) support a different tag format that we do +- * not support in net/dsa/tag_brcm.c yet. +- */ +- if (is5325(dev) || is5365(dev) || +- !b53_can_enable_brcm_tags(ds, port, mprot)) { ++ if (!b53_can_enable_brcm_tags(ds, port, mprot)) { + dev->tag_protocol = DSA_TAG_PROTO_NONE; + goto out; + } + ++ /* Older models require a different 6 byte tag */ ++ if (is5325(dev) || is5365(dev) || is63xx(dev)) { ++ dev->tag_protocol = DSA_TAG_PROTO_BRCM_LEGACY; ++ goto out; ++ } ++ + /* Broadcom BCM58xx chips have a flow accelerator on Port 8 + * which requires us to use the prepended Broadcom tag type + */ diff --git a/target/linux/generic/backport-5.10/740-v5.13-0006-net-dsa-b53-mmap-Add-device-tree-support.patch b/target/linux/generic/backport-5.10/740-v5.13-0006-net-dsa-b53-mmap-Add-device-tree-support.patch new file mode 100644 index 000000000..c1014b20b --- /dev/null +++ b/target/linux/generic/backport-5.10/740-v5.13-0006-net-dsa-b53-mmap-Add-device-tree-support.patch @@ -0,0 +1,92 @@ +From a5538a777b73b35750ed1ffff8c1ef539e861624 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?=C3=81lvaro=20Fern=C3=A1ndez=20Rojas?= +Date: Wed, 17 Mar 2021 10:23:17 +0100 +Subject: [PATCH] net: dsa: b53: mmap: Add device tree support +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Add device tree support to b53_mmap.c while keeping platform devices support. + +Signed-off-by: Álvaro Fernández Rojas +Signed-off-by: David S. Miller +--- + drivers/net/dsa/b53/b53_mmap.c | 55 ++++++++++++++++++++++++++++++++++ + 1 file changed, 55 insertions(+) + +--- a/drivers/net/dsa/b53/b53_mmap.c ++++ b/drivers/net/dsa/b53/b53_mmap.c +@@ -16,6 +16,7 @@ + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + ++#include + #include + #include + #include +@@ -228,11 +229,65 @@ static const struct b53_io_ops b53_mmap_ + .write64 = b53_mmap_write64, + }; + ++static int b53_mmap_probe_of(struct platform_device *pdev, ++ struct b53_platform_data **ppdata) ++{ ++ struct device_node *np = pdev->dev.of_node; ++ struct device_node *of_ports, *of_port; ++ struct device *dev = &pdev->dev; ++ struct b53_platform_data *pdata; ++ void __iomem *mem; ++ ++ mem = devm_platform_ioremap_resource(pdev, 0); ++ if (IS_ERR(mem)) ++ return PTR_ERR(mem); ++ ++ pdata = devm_kzalloc(dev, sizeof(struct b53_platform_data), ++ GFP_KERNEL); ++ if (!pdata) ++ return -ENOMEM; ++ ++ pdata->regs = mem; ++ pdata->chip_id = BCM63XX_DEVICE_ID; ++ pdata->big_endian = of_property_read_bool(np, "big-endian"); ++ ++ of_ports = of_get_child_by_name(np, "ports"); ++ if (!of_ports) { ++ dev_err(dev, "no ports child node found\n"); ++ return -EINVAL; ++ } ++ ++ for_each_available_child_of_node(of_ports, of_port) { ++ u32 reg; ++ ++ if (of_property_read_u32(of_port, "reg", ®)) ++ continue; ++ ++ if (reg < B53_CPU_PORT) ++ pdata->enabled_ports |= BIT(reg); ++ } ++ ++ of_node_put(of_ports); ++ *ppdata = pdata; ++ ++ return 0; ++} ++ + static int b53_mmap_probe(struct platform_device *pdev) + { ++ struct device_node *np = pdev->dev.of_node; + struct b53_platform_data *pdata = pdev->dev.platform_data; + struct b53_mmap_priv *priv; + struct b53_device *dev; ++ int ret; ++ ++ if (!pdata && np) { ++ ret = b53_mmap_probe_of(pdev, &pdata); ++ if (ret) { ++ dev_err(&pdev->dev, "OF probe error\n"); ++ return ret; ++ } ++ } + + if (!pdata) + return -EINVAL; diff --git a/target/linux/generic/backport-5.10/740-v5.13-0007-net-dsa-b53-spi-add-missing-MODULE_DEVICE_TABLE.patch b/target/linux/generic/backport-5.10/740-v5.13-0007-net-dsa-b53-spi-add-missing-MODULE_DEVICE_TABLE.patch new file mode 100644 index 000000000..ea3675573 --- /dev/null +++ b/target/linux/generic/backport-5.10/740-v5.13-0007-net-dsa-b53-spi-add-missing-MODULE_DEVICE_TABLE.patch @@ -0,0 +1,27 @@ +From 866f1577ba69bde2b9f36c300f603596c7d84a62 Mon Sep 17 00:00:00 2001 +From: Qinglang Miao +Date: Thu, 25 Mar 2021 17:19:54 +0800 +Subject: [PATCH] net: dsa: b53: spi: add missing MODULE_DEVICE_TABLE + +This patch adds missing MODULE_DEVICE_TABLE definition which generates +correct modalias for automatic loading of this driver when it is built +as an external module. + +Reported-by: Hulk Robot +Signed-off-by: Qinglang Miao +Acked-by: Florian Fainelli +Signed-off-by: David S. Miller +--- + drivers/net/dsa/b53/b53_spi.c | 1 + + 1 file changed, 1 insertion(+) + +--- a/drivers/net/dsa/b53/b53_spi.c ++++ b/drivers/net/dsa/b53/b53_spi.c +@@ -335,6 +335,7 @@ static const struct of_device_id b53_spi + { .compatible = "brcm,bcm53128" }, + { /* sentinel */ } + }; ++MODULE_DEVICE_TABLE(of, b53_spi_of_match); + + static struct spi_driver b53_spi_driver = { + .driver = { diff --git a/target/linux/generic/backport-5.10/741-v5.14-0001-net-dsa-b53-Do-not-force-CPU-to-be-always-tagged.patch b/target/linux/generic/backport-5.10/741-v5.14-0001-net-dsa-b53-Do-not-force-CPU-to-be-always-tagged.patch new file mode 100644 index 000000000..2a8def39b --- /dev/null +++ b/target/linux/generic/backport-5.10/741-v5.14-0001-net-dsa-b53-Do-not-force-CPU-to-be-always-tagged.patch @@ -0,0 +1,86 @@ +From 2c32a3d3c233b855943677609fe388f82b1f0975 Mon Sep 17 00:00:00 2001 +From: Florian Fainelli +Date: Tue, 8 Jun 2021 14:22:04 -0700 +Subject: [PATCH] net: dsa: b53: Do not force CPU to be always tagged + +Commit ca8931948344 ("net: dsa: b53: Keep CPU port as tagged in all +VLANs") forced the CPU port to be always tagged in any VLAN membership. +This was necessary back then because we did not support Broadcom tags +for all configurations so the only way to differentiate tagged and +untagged traffic while DSA_TAG_PROTO_NONE was used was to force the CPU +port into being always tagged. + +With most configurations enabling Broadcom tags, especially after +8fab459e69ab ("net: dsa: b53: Enable Broadcom tags for 531x5/539x +families") we do not need to apply this unconditional force tagging of +the CPU port in all VLANs. + +A helper function is introduced to faciliate the encapsulation of the +specific condition requiring the CPU port to be tagged in all VLANs and +the dsa_switch_ops::untag_bridge_pvid boolean is moved to when +dsa_switch_ops::setup is called when we have already determined the +tagging protocol we will be using. + +Reported-by: Matthew Hagan +Signed-off-by: Florian Fainelli +Reviewed-by: Vladimir Oltean +Tested-by: Matthew Hagan +Signed-off-by: David S. Miller +--- + drivers/net/dsa/b53/b53_common.c | 17 ++++++++++++++--- + 1 file changed, 14 insertions(+), 3 deletions(-) + +--- a/drivers/net/dsa/b53/b53_common.c ++++ b/drivers/net/dsa/b53/b53_common.c +@@ -1049,6 +1049,11 @@ static int b53_setup(struct dsa_switch * + unsigned int port; + int ret; + ++ /* Request bridge PVID untagged when DSA_TAG_PROTO_NONE is set ++ * which forces the CPU port to be tagged in all VLANs. ++ */ ++ ds->untag_bridge_pvid = dev->tag_protocol == DSA_TAG_PROTO_NONE; ++ + ret = b53_reset_switch(dev); + if (ret) { + dev_err(ds->dev, "failed to reset switch\n"); +@@ -1423,6 +1428,13 @@ int b53_vlan_prepare(struct dsa_switch * + return 0; + } + EXPORT_SYMBOL(b53_vlan_prepare); ++ ++static bool b53_vlan_port_needs_forced_tagged(struct dsa_switch *ds, int port) ++{ ++ struct b53_device *dev = ds->priv; ++ ++ return dev->tag_protocol == DSA_TAG_PROTO_NONE && dsa_is_cpu_port(ds, port); ++} + + void b53_vlan_add(struct dsa_switch *ds, int port, + const struct switchdev_obj_port_vlan *vlan) +@@ -1442,7 +1454,7 @@ void b53_vlan_add(struct dsa_switch *ds, + untagged = true; + + vl->members |= BIT(port); +- if (untagged && !dsa_is_cpu_port(ds, port)) ++ if (untagged && !b53_vlan_port_needs_forced_tagged(ds, port)) + vl->untag |= BIT(port); + else + vl->untag &= ~BIT(port); +@@ -1480,7 +1492,7 @@ int b53_vlan_del(struct dsa_switch *ds, + if (pvid == vid) + pvid = b53_default_pvid(dev); + +- if (untagged && !dsa_is_cpu_port(ds, port)) ++ if (untagged && !b53_vlan_port_needs_forced_tagged(ds, port)) + vl->untag &= ~(BIT(port)); + + b53_set_vlan_entry(dev, vid, vl); +@@ -2644,7 +2656,6 @@ struct b53_device *b53_switch_alloc(stru + dev->ops = ops; + ds->ops = &b53_switch_ops; + ds->configure_vlan_while_not_filtering = true; +- ds->untag_bridge_pvid = true; + dev->vlan_enabled = ds->configure_vlan_while_not_filtering; + /* Let DSA handle the case were multiple bridges span the same switch + * device and different VLAN awareness settings are requested, which diff --git a/target/linux/generic/backport-5.10/741-v5.14-0002-net-dsa-b53-remove-redundant-null-check-on-dev.patch b/target/linux/generic/backport-5.10/741-v5.14-0002-net-dsa-b53-remove-redundant-null-check-on-dev.patch new file mode 100644 index 000000000..ee3a71ffa --- /dev/null +++ b/target/linux/generic/backport-5.10/741-v5.14-0002-net-dsa-b53-remove-redundant-null-check-on-dev.patch @@ -0,0 +1,30 @@ +From 11b57faf951cd3a570e3d9e463fc7c41023bc8c6 Mon Sep 17 00:00:00 2001 +From: Colin Ian King +Date: Tue, 15 Jun 2021 10:05:16 +0100 +Subject: [PATCH] net: dsa: b53: remove redundant null check on dev + +The pointer dev can never be null, the null check is redundant +and can be removed. Cleans up a static analysis warning that +pointer priv is dereferencing dev before dev is being null +checked. + +Addresses-Coverity: ("Dereference before null check") +Signed-off-by: Colin Ian King +Acked-by: Florian Fainelli +Signed-off-by: David S. Miller +--- + drivers/net/dsa/b53/b53_srab.c | 3 +-- + 1 file changed, 1 insertion(+), 2 deletions(-) + +--- a/drivers/net/dsa/b53/b53_srab.c ++++ b/drivers/net/dsa/b53/b53_srab.c +@@ -632,8 +632,7 @@ static int b53_srab_remove(struct platfo + struct b53_srab_priv *priv = dev->priv; + + b53_srab_intr_set(priv, false); +- if (dev) +- b53_switch_remove(dev); ++ b53_switch_remove(dev); + + return 0; + } diff --git a/target/linux/generic/backport-5.10/741-v5.14-0003-net-dsa-b53-Create-default-VLAN-entry-explicitly.patch b/target/linux/generic/backport-5.10/741-v5.14-0003-net-dsa-b53-Create-default-VLAN-entry-explicitly.patch new file mode 100644 index 000000000..df891d68a --- /dev/null +++ b/target/linux/generic/backport-5.10/741-v5.14-0003-net-dsa-b53-Create-default-VLAN-entry-explicitly.patch @@ -0,0 +1,71 @@ +From 64a81b24487f0d2fba0f033029eec2abc7d82cee Mon Sep 17 00:00:00 2001 +From: Florian Fainelli +Date: Mon, 21 Jun 2021 15:10:55 -0700 +Subject: [PATCH] net: dsa: b53: Create default VLAN entry explicitly + +In case CONFIG_VLAN_8021Q is not set, there will be no call down to the +b53 driver to ensure that the default PVID VLAN entry will be configured +with the appropriate untagged attribute towards the CPU port. We were +implicitly relying on dsa_slave_vlan_rx_add_vid() to do that for us, +instead make it explicit. + +Reported-by: Vladimir Oltean +Signed-off-by: Florian Fainelli +Reviewed-by: Vladimir Oltean +Signed-off-by: David S. Miller +--- + drivers/net/dsa/b53/b53_common.c | 27 +++++++++++++++++++-------- + 1 file changed, 19 insertions(+), 8 deletions(-) + +--- a/drivers/net/dsa/b53/b53_common.c ++++ b/drivers/net/dsa/b53/b53_common.c +@@ -693,6 +693,13 @@ static u16 b53_default_pvid(struct b53_d + return 0; + } + ++static bool b53_vlan_port_needs_forced_tagged(struct dsa_switch *ds, int port) ++{ ++ struct b53_device *dev = ds->priv; ++ ++ return dev->tag_protocol == DSA_TAG_PROTO_NONE && dsa_is_cpu_port(ds, port); ++} ++ + int b53_configure_vlan(struct dsa_switch *ds) + { + struct b53_device *dev = ds->priv; +@@ -713,9 +720,20 @@ int b53_configure_vlan(struct dsa_switch + + b53_enable_vlan(dev, -1, dev->vlan_enabled, ds->vlan_filtering); + +- b53_for_each_port(dev, i) ++ /* Create an untagged VLAN entry for the default PVID in case ++ * CONFIG_VLAN_8021Q is disabled and there are no calls to ++ * dsa_slave_vlan_rx_add_vid() to create the default VLAN ++ * entry. Do this only when the tagging protocol is not ++ * DSA_TAG_PROTO_NONE ++ */ ++ b53_for_each_port(dev, i) { ++ v = &dev->vlans[def_vid]; ++ v->members |= BIT(i); ++ if (!b53_vlan_port_needs_forced_tagged(ds, i)) ++ v->untag = v->members; + b53_write16(dev, B53_VLAN_PAGE, + B53_VLAN_PORT_DEF_TAG(i), def_vid); ++ } + + /* Upon initial call we have not set-up any VLANs, but upon + * system resume, we need to restore all VLAN entries. +@@ -1429,13 +1447,6 @@ int b53_vlan_prepare(struct dsa_switch * + } + EXPORT_SYMBOL(b53_vlan_prepare); + +-static bool b53_vlan_port_needs_forced_tagged(struct dsa_switch *ds, int port) +-{ +- struct b53_device *dev = ds->priv; +- +- return dev->tag_protocol == DSA_TAG_PROTO_NONE && dsa_is_cpu_port(ds, port); +-} +- + void b53_vlan_add(struct dsa_switch *ds, int port, + const struct switchdev_obj_port_vlan *vlan) + { diff --git a/target/linux/generic/backport-5.10/742-v5.16-net-phy-at803x-add-support-for-qca-8327-internal-phy.patch b/target/linux/generic/backport-5.10/742-v5.16-net-phy-at803x-add-support-for-qca-8327-internal-phy.patch new file mode 100644 index 000000000..16aa0711a --- /dev/null +++ b/target/linux/generic/backport-5.10/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 +@@ -1328,6 +1328,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); +@@ -1338,6 +1351,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-5.10/743-v5.16-0001-net-dsa-b53-Include-all-ports-in-enabled_ports.patch b/target/linux/generic/backport-5.10/743-v5.16-0001-net-dsa-b53-Include-all-ports-in-enabled_ports.patch new file mode 100644 index 000000000..d80b5db71 --- /dev/null +++ b/target/linux/generic/backport-5.10/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 +@@ -2288,7 +2288,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, +@@ -2299,7 +2299,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, +@@ -2310,7 +2310,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, +@@ -2324,7 +2324,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, +@@ -2338,7 +2338,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, +@@ -2352,7 +2352,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, +@@ -2366,7 +2366,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, +@@ -2380,7 +2380,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, +@@ -2422,7 +2422,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, +@@ -2464,7 +2464,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, +@@ -2478,7 +2478,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, +@@ -2605,7 +2605,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-5.10/743-v5.16-0002-net-dsa-b53-Drop-BCM5301x-workaround-for-a-wrong-CPU.patch b/target/linux/generic/backport-5.10/743-v5.16-0002-net-dsa-b53-Drop-BCM5301x-workaround-for-a-wrong-CPU.patch new file mode 100644 index 000000000..4a4f8e940 --- /dev/null +++ b/target/linux/generic/backport-5.10/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 +@@ -1256,12 +1256,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-5.10/743-v5.16-0003-net-dsa-b53-Improve-flow-control-setup-on-BCM5301x.patch b/target/linux/generic/backport-5.10/743-v5.16-0003-net-dsa-b53-Improve-flow-control-setup-on-BCM5301x.patch new file mode 100644 index 000000000..3954ee4aa --- /dev/null +++ b/target/linux/generic/backport-5.10/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 +@@ -1187,7 +1187,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-5.10/743-v5.16-0004-net-dsa-b53-Drop-unused-cpu_port-field.patch b/target/linux/generic/backport-5.10/743-v5.16-0004-net-dsa-b53-Drop-unused-cpu_port-field.patch new file mode 100644 index 000000000..9e687b148 --- /dev/null +++ b/target/linux/generic/backport-5.10/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 +@@ -2286,7 +2286,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, + }, + { +@@ -2297,7 +2296,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, + }, + { +@@ -2308,7 +2306,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, +@@ -2322,7 +2319,6 @@ static const struct b53_chip_data b53_sw + .arl_bins = 4, + .arl_buckets = 1024, + .imp_port = 8, +- .cpu_port = B53_CPU_PORT, + .vta_regs = B53_VTA_REGS, + .duplex_reg = B53_DUPLEX_STAT_GE, + .jumbo_pm_reg = B53_JUMBO_PORT_MASK, +@@ -2336,7 +2332,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, +@@ -2350,7 +2345,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, +@@ -2365,7 +2359,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, +@@ -2378,7 +2371,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, +@@ -2392,7 +2384,6 @@ static const struct b53_chip_data b53_sw + .arl_bins = 4, + .arl_buckets = 1024, + .imp_port = 8, +- .cpu_port = B53_CPU_PORT, + .vta_regs = B53_VTA_REGS, + .duplex_reg = B53_DUPLEX_STAT_GE, + .jumbo_pm_reg = B53_JUMBO_PORT_MASK, +@@ -2406,7 +2397,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, +@@ -2420,7 +2410,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, +@@ -2434,7 +2423,6 @@ static const struct b53_chip_data b53_sw + .arl_bins = 4, + .arl_buckets = 1024, + .imp_port = 8, +- .cpu_port = B53_CPU_PORT_25, /* TODO: auto detect */ + .vta_regs = B53_VTA_REGS, + .duplex_reg = B53_DUPLEX_STAT_GE, + .jumbo_pm_reg = B53_JUMBO_PORT_MASK, +@@ -2448,7 +2436,6 @@ static const struct b53_chip_data b53_sw + .arl_bins = 4, + .arl_buckets = 1024, + .imp_port = 8, +- .cpu_port = B53_CPU_PORT_25, /* TODO: auto detect */ + .vta_regs = B53_VTA_REGS, + .duplex_reg = B53_DUPLEX_STAT_GE, + .jumbo_pm_reg = B53_JUMBO_PORT_MASK, +@@ -2462,7 +2449,6 @@ static const struct b53_chip_data b53_sw + .arl_bins = 4, + .arl_buckets = 1024, + .imp_port = 8, +- .cpu_port = B53_CPU_PORT_25, /* TODO: auto detect */ + .vta_regs = B53_VTA_REGS, + .duplex_reg = B53_DUPLEX_STAT_GE, + .jumbo_pm_reg = B53_JUMBO_PORT_MASK, +@@ -2476,7 +2462,6 @@ static const struct b53_chip_data b53_sw + .arl_bins = 4, + .arl_buckets = 1024, + .imp_port = 8, +- .cpu_port = B53_CPU_PORT_25, /* TODO: auto detect */ + .vta_regs = B53_VTA_REGS, + .duplex_reg = B53_DUPLEX_STAT_GE, + .jumbo_pm_reg = B53_JUMBO_PORT_MASK, +@@ -2490,7 +2475,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, +@@ -2504,7 +2488,6 @@ static const struct b53_chip_data b53_sw + .arl_bins = 4, + .arl_buckets = 1024, + .imp_port = 8, +- .cpu_port = B53_CPU_PORT, + .vta_regs = B53_VTA_REGS, + .duplex_reg = B53_DUPLEX_STAT_GE, + .jumbo_pm_reg = B53_JUMBO_PORT_MASK, +@@ -2518,7 +2501,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, +@@ -2532,7 +2514,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, +@@ -2558,7 +2539,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; +@@ -2590,13 +2570,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 +@@ -123,7 +123,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-5.10/744-v5.15-net-dsa-don-t-set-skb-offload_fwd_mark-when-not-offl.patch b/target/linux/generic/backport-5.10/744-v5.15-net-dsa-don-t-set-skb-offload_fwd_mark-when-not-offl.patch new file mode 100644 index 000000000..ab4fdf850 --- /dev/null +++ b/target/linux/generic/backport-5.10/744-v5.15-net-dsa-don-t-set-skb-offload_fwd_mark-when-not-offl.patch @@ -0,0 +1,138 @@ +From bea7907837c57a0aaac009931eb14efb056dafab Mon Sep 17 00:00:00 2001 +From: Vladimir Oltean +Date: Thu, 29 Jul 2021 17:56:00 +0300 +Subject: [PATCH] net: dsa: don't set skb->offload_fwd_mark when not offloading + the bridge + +DSA has gained the recent ability to deal gracefully with upper +interfaces it cannot offload, such as the bridge, bonding or team +drivers. When such uppers exist, the ports are still in standalone mode +as far as the hardware is concerned. + +But when we deliver packets to the software bridge in order for that to +do the forwarding, there is an unpleasant surprise in that the bridge +will refuse to forward them. This is because we unconditionally set +skb->offload_fwd_mark = true, meaning that the bridge thinks the frames +were already forwarded in hardware by us. + +Since dp->bridge_dev is populated only when there is hardware offload +for it, but not in the software fallback case, let's introduce a new +helper that can be called from the tagger data path which sets the +skb->offload_fwd_mark accordingly to zero when there is no hardware +offload for bridging. This lets the bridge forward packets back to other +interfaces of our switch, if needed. + +Signed-off-by: Vladimir Oltean +Reviewed-by: Tobias Waldekranz +Signed-off-by: David S. Miller +--- + net/dsa/dsa_priv.h | 14 ++++++++++++++ + net/dsa/tag_brcm.c | 4 ++-- + net/dsa/tag_dsa.c | 15 +++++++++++---- + net/dsa/tag_ksz.c | 2 +- + net/dsa/tag_lan9303.c | 3 ++- + net/dsa/tag_mtk.c | 2 +- + net/dsa/tag_ocelot.c | 2 +- + net/dsa/tag_rtl4_a.c | 2 +- + net/dsa/tag_sja1105.c | 20 ++++++++++++++------ + 9 files changed, 47 insertions(+), 17 deletions(-) + +--- a/net/dsa/dsa_priv.h ++++ b/net/dsa/dsa_priv.h +@@ -266,6 +266,20 @@ static inline struct sk_buff *dsa_untag_ + return skb; + } + ++/* If the ingress port offloads the bridge, we mark the frame as autonomously ++ * forwarded by hardware, so the software bridge doesn't forward in twice, back ++ * to us, because we already did. However, if we're in fallback mode and we do ++ * software bridging, we are not offloading it, therefore the dp->bridge_dev ++ * pointer is not populated, and flooding needs to be done by software (we are ++ * effectively operating in standalone ports mode). ++ */ ++static inline void dsa_default_offload_fwd_mark(struct sk_buff *skb) ++{ ++ struct dsa_port *dp = dsa_slave_to_port(skb->dev); ++ ++ skb->offload_fwd_mark = !!(dp->bridge_dev); ++} ++ + /* switch.c */ + int dsa_switch_register_notifier(struct dsa_switch *ds); + void dsa_switch_unregister_notifier(struct dsa_switch *ds); +--- a/net/dsa/tag_brcm.c ++++ b/net/dsa/tag_brcm.c +@@ -166,7 +166,7 @@ static struct sk_buff *brcm_tag_rcv_ll(s + /* Remove Broadcom tag and update checksum */ + skb_pull_rcsum(skb, BRCM_TAG_LEN); + +- skb->offload_fwd_mark = 1; ++ dsa_default_offload_fwd_mark(skb); + + return skb; + } +@@ -270,7 +270,7 @@ static struct sk_buff *brcm_leg_tag_rcv( + /* Remove Broadcom tag and update checksum */ + skb_pull_rcsum(skb, BRCM_LEG_TAG_LEN); + +- skb->offload_fwd_mark = 1; ++ dsa_default_offload_fwd_mark(skb); + + /* Move the Ethernet DA and SA */ + memmove(skb->data - ETH_HLEN, +--- a/net/dsa/tag_ksz.c ++++ b/net/dsa/tag_ksz.c +@@ -24,7 +24,7 @@ static struct sk_buff *ksz_common_rcv(st + + pskb_trim_rcsum(skb, skb->len - len); + +- skb->offload_fwd_mark = true; ++ dsa_default_offload_fwd_mark(skb); + + return skb; + } +--- a/net/dsa/tag_lan9303.c ++++ b/net/dsa/tag_lan9303.c +@@ -115,7 +115,8 @@ static struct sk_buff *lan9303_rcv(struc + skb_pull_rcsum(skb, 2 + 2); + memmove(skb->data - ETH_HLEN, skb->data - (ETH_HLEN + LAN9303_TAG_LEN), + 2 * ETH_ALEN); +- skb->offload_fwd_mark = !(lan9303_tag1 & LAN9303_TAG_RX_TRAPPED_TO_CPU); ++ if (!(lan9303_tag1 & LAN9303_TAG_RX_TRAPPED_TO_CPU)) ++ dsa_default_offload_fwd_mark(skb); + + return skb; + } +--- a/net/dsa/tag_mtk.c ++++ b/net/dsa/tag_mtk.c +@@ -104,7 +104,7 @@ static struct sk_buff *mtk_tag_rcv(struc + + /* Only unicast or broadcast frames are offloaded */ + if (likely(!is_multicast_skb)) +- skb->offload_fwd_mark = 1; ++ dsa_default_offload_fwd_mark(skb); + + return skb; + } +--- a/net/dsa/tag_ocelot.c ++++ b/net/dsa/tag_ocelot.c +@@ -225,7 +225,7 @@ static struct sk_buff *ocelot_rcv(struct + */ + return NULL; + +- skb->offload_fwd_mark = 1; ++ dsa_default_offload_fwd_mark(skb); + skb->priority = qos_class; + + /* Ocelot switches copy frames unmodified to the CPU. However, it is +--- a/net/dsa/tag_rtl4_a.c ++++ b/net/dsa/tag_rtl4_a.c +@@ -115,7 +115,7 @@ static struct sk_buff *rtl4a_tag_rcv(str + skb->data - ETH_HLEN - RTL4_A_HDR_LEN, + 2 * ETH_ALEN); + +- skb->offload_fwd_mark = 1; ++ dsa_default_offload_fwd_mark(skb); + + return skb; + } diff --git a/target/linux/generic/backport-5.10/745-v5.16-01-net-phy-at803x-add-support-for-qca-8327-A-variant.patch b/target/linux/generic/backport-5.10/745-v5.16-01-net-phy-at803x-add-support-for-qca-8327-A-variant.patch new file mode 100644 index 000000000..c82bf913a --- /dev/null +++ b/target/linux/generic/backport-5.10/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 +@@ -148,7 +148,8 @@ + #define AT803X_PAGE_FIBER 0 + #define AT803X_PAGE_COPPER 1 + +-#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 + +@@ -1329,10 +1330,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, +@@ -1352,7 +1366,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-5.10/745-v5.16-02-net-phy-at803x-add-resume-suspend-function-to-qca83x.patch b/target/linux/generic/backport-5.10/745-v5.16-02-net-phy-at803x-add-resume-suspend-function-to-qca83x.patch new file mode 100644 index 000000000..be24fd5cf --- /dev/null +++ b/target/linux/generic/backport-5.10/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 +@@ -1329,6 +1329,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, +@@ -1342,6 +1344,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, +@@ -1355,6 +1359,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-5.10/745-v5.16-03-net-phy-at803x-fix-spacing-and-improve-name-for-83xx.patch b/target/linux/generic/backport-5.10/745-v5.16-03-net-phy-at803x-fix-spacing-and-improve-name-for-83xx.patch new file mode 100644 index 000000000..23f574c76 --- /dev/null +++ b/target/linux/generic/backport-5.10/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 +@@ -1318,47 +1318,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-5.10/746-v5.16-01-net-phy-at803x-fix-resume-for-QCA8327-phy.patch b/target/linux/generic/backport-5.10/746-v5.16-01-net-phy-at803x-fix-resume-for-QCA8327-phy.patch new file mode 100644 index 000000000..5dfe27dd2 --- /dev/null +++ b/target/linux/generic/backport-5.10/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) +@@ -1220,6 +1225,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 */ +@@ -1329,8 +1386,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, +@@ -1344,8 +1401,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, +@@ -1359,8 +1416,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-5.10/746-v5.16-02-net-phy-at803x-add-DAC-amplitude-fix-for-8327-phy.patch b/target/linux/generic/backport-5.10/746-v5.16-02-net-phy-at803x-add-DAC-amplitude-fix-for-8327-phy.patch new file mode 100644 index 000000000..aeb43e2f6 --- /dev/null +++ b/target/linux/generic/backport-5.10/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 +@@ -1222,9 +1224,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; +@@ -1379,6 +1409,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, +@@ -1394,6 +1425,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, +@@ -1409,6 +1441,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-5.10/746-v5.16-03-net-phy-at803x-enable-prefer-master-for-83xx-interna.patch b/target/linux/generic/backport-5.10/746-v5.16-03-net-phy-at803x-enable-prefer-master-for-83xx-interna.patch new file mode 100644 index 000000000..2352fa0e4 --- /dev/null +++ b/target/linux/generic/backport-5.10/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 +@@ -1233,6 +1233,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-5.10/746-v5.16-04-net-phy-at803x-better-describe-debug-regs.patch b/target/linux/generic/backport-5.10/746-v5.16-04-net-phy-at803x-better-describe-debug-regs.patch new file mode 100644 index 000000000..9c28a893f --- /dev/null +++ b/target/linux/generic/backport-5.10/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 +@@ -274,25 +274,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); + } + +@@ -1208,9 +1208,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: +@@ -1218,8 +1218,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; + } +@@ -1230,7 +1230,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 */ +@@ -1248,12 +1248,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); + } + } +@@ -1300,7 +1300,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-5.10/747-v5.16-01-dsa-qca8k-add-mac-power-sel-support.patch b/target/linux/generic/backport-5.10/747-v5.16-01-dsa-qca8k-add-mac-power-sel-support.patch new file mode 100644 index 000000000..c8d424de3 --- /dev/null +++ b/target/linux/generic/backport-5.10/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-5.10/747-v5.16-02-dt-bindings-net-dsa-qca8k-Add-SGMII-clock-phase-prop.patch b/target/linux/generic/backport-5.10/747-v5.16-02-dt-bindings-net-dsa-qca8k-Add-SGMII-clock-phase-prop.patch new file mode 100644 index 000000000..bd768ec27 --- /dev/null +++ b/target/linux/generic/backport-5.10/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-5.10/747-v5.16-03-net-dsa-qca8k-add-support-for-sgmii-falling-edge.patch b/target/linux/generic/backport-5.10/747-v5.16-03-net-dsa-qca8k-add-support-for-sgmii-falling-edge.patch new file mode 100644 index 000000000..e464452d8 --- /dev/null +++ b/target/linux/generic/backport-5.10/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-5.10/747-v5.16-04-dt-bindings-net-dsa-qca8k-Document-support-for-CPU-p.patch b/target/linux/generic/backport-5.10/747-v5.16-04-dt-bindings-net-dsa-qca8k-Document-support-for-CPU-p.patch new file mode 100644 index 000000000..606ac0af3 --- /dev/null +++ b/target/linux/generic/backport-5.10/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-5.10/747-v5.16-05-net-dsa-qca8k-add-support-for-cpu-port-6.patch b/target/linux/generic/backport-5.10/747-v5.16-05-net-dsa-qca8k-add-support-for-cpu-port-6.patch new file mode 100644 index 000000000..320db8fa9 --- /dev/null +++ b/target/linux/generic/backport-5.10/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-5.10/747-v5.16-06-net-dsa-qca8k-rework-rgmii-delay-logic-and-scan-for-.patch b/target/linux/generic/backport-5.10/747-v5.16-06-net-dsa-qca8k-rework-rgmii-delay-logic-and-scan-for-.patch new file mode 100644 index 000000000..de201764f --- /dev/null +++ b/target/linux/generic/backport-5.10/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-5.10/747-v5.16-07-dt-bindings-net-dsa-qca8k-Document-qca-sgmii-enable-.patch b/target/linux/generic/backport-5.10/747-v5.16-07-dt-bindings-net-dsa-qca8k-Document-qca-sgmii-enable-.patch new file mode 100644 index 000000000..8abd264e7 --- /dev/null +++ b/target/linux/generic/backport-5.10/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-5.10/747-v5.16-08-net-dsa-qca8k-add-explicit-SGMII-PLL-enable.patch b/target/linux/generic/backport-5.10/747-v5.16-08-net-dsa-qca8k-add-explicit-SGMII-PLL-enable.patch new file mode 100644 index 000000000..2b5a84a1b --- /dev/null +++ b/target/linux/generic/backport-5.10/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-5.10/747-v5.16-09-dt-bindings-net-dsa-qca8k-Document-qca-led-open-drai.patch b/target/linux/generic/backport-5.10/747-v5.16-09-dt-bindings-net-dsa-qca8k-Document-qca-led-open-drai.patch new file mode 100644 index 000000000..38dc954e8 --- /dev/null +++ b/target/linux/generic/backport-5.10/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-5.10/747-v5.16-10-net-dsa-qca8k-add-support-for-pws-config-reg.patch b/target/linux/generic/backport-5.10/747-v5.16-10-net-dsa-qca8k-add-support-for-pws-config-reg.patch new file mode 100644 index 000000000..aa5d92a4f --- /dev/null +++ b/target/linux/generic/backport-5.10/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-5.10/747-v5.16-11-dt-bindings-net-dsa-qca8k-document-support-for-qca83.patch b/target/linux/generic/backport-5.10/747-v5.16-11-dt-bindings-net-dsa-qca8k-document-support-for-qca83.patch new file mode 100644 index 000000000..1bfb00c5b --- /dev/null +++ b/target/linux/generic/backport-5.10/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-5.10/747-v5.16-12-net-dsa-qca8k-add-support-for-QCA8328.patch b/target/linux/generic/backport-5.10/747-v5.16-12-net-dsa-qca8k-add-support-for-QCA8328.patch new file mode 100644 index 000000000..6e118f5a1 --- /dev/null +++ b/target/linux/generic/backport-5.10/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; + } +@@ -2098,7 +2105,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, + }; + +@@ -2107,7 +2119,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-5.10/747-v5.16-13-net-dsa-qca8k-set-internal-delay-also-for-sgmii.patch b/target/linux/generic/backport-5.10/747-v5.16-13-net-dsa-qca8k-set-internal-delay-also-for-sgmii.patch new file mode 100644 index 000000000..27f94dca0 --- /dev/null +++ b/target/linux/generic/backport-5.10/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-5.10/747-v5.16-14-net-dsa-qca8k-move-port-config-to-dedicated-struct.patch b/target/linux/generic/backport-5.10/747-v5.16-14-net-dsa-qca8k-move-port-config-to-dedicated-struct.patch new file mode 100644 index 000000000..b991798c8 --- /dev/null +++ b/target/linux/generic/backport-5.10/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-5.10/747-v5.16-15-dt-bindings-net-ipq8064-mdio-fix-warning-with-new-qc.patch b/target/linux/generic/backport-5.10/747-v5.16-15-dt-bindings-net-ipq8064-mdio-fix-warning-with-new-qc.patch new file mode 100644 index 000000000..f7cb51417 --- /dev/null +++ b/target/linux/generic/backport-5.10/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-5.10/747-v5.16-16-dt-bindings-net-dsa-qca8k-convert-to-YAML-schema.patch b/target/linux/generic/backport-5.10/747-v5.16-16-dt-bindings-net-dsa-qca8k-convert-to-YAML-schema.patch new file mode 100644 index 000000000..b9bce97dd --- /dev/null +++ b/target/linux/generic/backport-5.10/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-5.10/748-v5.16-net-dsa-qca8k-fix-delay-applied-to-wrong-cpu-in-parse-p.patch b/target/linux/generic/backport-5.10/748-v5.16-net-dsa-qca8k-fix-delay-applied-to-wrong-cpu-in-parse-p.patch new file mode 100644 index 000000000..a510cfdc1 --- /dev/null +++ b/target/linux/generic/backport-5.10/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-5.10/749-v5.16-net-dsa-qca8k-tidy-for-loop-in-setup-and-add-cpu-port-c.patch b/target/linux/generic/backport-5.10/749-v5.16-net-dsa-qca8k-tidy-for-loop-in-setup-and-add-cpu-port-c.patch new file mode 100644 index 000000000..71fa3022d --- /dev/null +++ b/target/linux/generic/backport-5.10/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-5.10/750-v5.16-net-dsa-qca8k-make-sure-pad0-mac06-exchange-is-disabled.patch b/target/linux/generic/backport-5.10/750-v5.16-net-dsa-qca8k-make-sure-pad0-mac06-exchange-is-disabled.patch new file mode 100644 index 000000000..4a61703c5 --- /dev/null +++ b/target/linux/generic/backport-5.10/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-5.10/751-v5.16-net-dsa-qca8k-fix-internal-delay-applied-to-the-wrong-PAD.patch b/target/linux/generic/backport-5.10/751-v5.16-net-dsa-qca8k-fix-internal-delay-applied-to-the-wrong-PAD.patch new file mode 100644 index 000000000..df9518d86 --- /dev/null +++ b/target/linux/generic/backport-5.10/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-5.10/752-v5.16-net-dsa-qca8k-fix-MTU-calculation.patch b/target/linux/generic/backport-5.10/752-v5.16-net-dsa-qca8k-fix-MTU-calculation.patch new file mode 100644 index 000000000..7348d93ec --- /dev/null +++ b/target/linux/generic/backport-5.10/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-5.10/753-net-next-net-dsa-qca8k-remove-redundant-check-in-parse_port_config.patch b/target/linux/generic/backport-5.10/753-net-next-net-dsa-qca8k-remove-redundant-check-in-parse_port_config.patch new file mode 100644 index 000000000..f477b1b92 --- /dev/null +++ b/target/linux/generic/backport-5.10/753-net-next-net-dsa-qca8k-remove-redundant-check-in-parse_port_config.patch @@ -0,0 +1,29 @@ +From b9133f3ef5a2659730cf47a74bd0a9259f1cf8ff Mon Sep 17 00:00:00 2001 +From: Ansuel Smith +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-5.10/754-net-next-net-dsa-qca8k-convert-to-GENMASK_FIELD_PREP_FIELD_GET.patch b/target/linux/generic/backport-5.10/754-net-next-net-dsa-qca8k-convert-to-GENMASK_FIELD_PREP_FIELD_GET.patch new file mode 100644 index 000000000..c1489fd9a --- /dev/null +++ b/target/linux/generic/backport-5.10/754-net-next-net-dsa-qca8k-convert-to-GENMASK_FIELD_PREP_FIELD_GET.patch @@ -0,0 +1,508 @@ +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); + } + +@@ -1918,11 +1913,11 @@ qca8k_port_vlan_filtering(struct dsa_swi + + if (vlan_filtering) { + 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 { + 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); + } + +@@ -1953,11 +1948,9 @@ qca8k_port_vlan_add(struct dsa_switch *d + dev_err(priv->dev, "Failed to add VLAN to port %d (%d)", port, ret); + + if (pvid) { +- int shift = 16 * (port % 2); +- + qca8k_rmw(priv, QCA8K_EGRESS_VLAN(port), +- 0xfff << shift, +- vlan->vid_end << shift); ++ QCA8K_EGREES_VLAN_PORT_MASK(port), ++ QCA8K_EGREES_VLAN_PORT(port, vlan->vid_end)); + qca8k_write(priv, QCA8K_REG_PORT_VLAN_CTRL0(port), + QCA8K_PORT_VLAN_CVID(vlan->vid_end) | + QCA8K_PORT_VLAN_SVID(vlan->vid_end)); +@@ -2050,7 +2043,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; +@@ -2059,7 +2052,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-5.10/755-net-next-net-dsa-qca8k-remove-extra-mutex_init-in-qca8k_setup.patch b/target/linux/generic/backport-5.10/755-net-next-net-dsa-qca8k-remove-extra-mutex_init-in-qca8k_setup.patch new file mode 100644 index 000000000..8c39b8ea2 --- /dev/null +++ b/target/linux/generic/backport-5.10/755-net-next-net-dsa-qca8k-remove-extra-mutex_init-in-qca8k_setup.patch @@ -0,0 +1,25 @@ +From 994c28b6f971fa5db8ae977daea37eee87d93d51 Mon Sep 17 00:00:00 2001 +From: Ansuel Smith +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-5.10/756-net-next-net-dsa-qca8k-move-regmap-init-in-probe-and-set-it.patch b/target/linux/generic/backport-5.10/756-net-next-net-dsa-qca8k-move-regmap-init-in-probe-and-set-it.patch new file mode 100644 index 000000000..9fcc74a7c --- /dev/null +++ b/target/linux/generic/backport-5.10/756-net-next-net-dsa-qca8k-move-regmap-init-in-probe-and-set-it.patch @@ -0,0 +1,46 @@ +From 36b8af12f424e7a7f60a935c60a0fd4aa0822378 Mon Sep 17 00:00:00 2001 +From: Ansuel Smith +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; +@@ -2085,6 +2079,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-5.10/757-net-next-net-dsa-qca8k-initial-conversion-to-regmap-heper.patch b/target/linux/generic/backport-5.10/757-net-next-net-dsa-qca8k-initial-conversion-to-regmap-heper.patch new file mode 100644 index 000000000..4ca9c8ba4 --- /dev/null +++ b/target/linux/generic/backport-5.10/757-net-next-net-dsa-qca8k-initial-conversion-to-regmap-heper.patch @@ -0,0 +1,249 @@ +From 8b5f3f29a81a71934d004e21a1292c1148b05926 Mon Sep 17 00:00:00 2001 +From: Ansuel Smith +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-5.10/758-net-next-net-dsa-qca8k-add-additional-MIB-counter-and-.patch b/target/linux/generic/backport-5.10/758-net-next-net-dsa-qca8k-add-additional-MIB-counter-and-.patch new file mode 100644 index 000000000..78bdf7f77 --- /dev/null +++ b/target/linux/generic/backport-5.10/758-net-next-net-dsa-qca8k-add-additional-MIB-counter-and-.patch @@ -0,0 +1,120 @@ +From c126f118b330ccf0db0dda4a4bd6c729865a205f Mon Sep 17 00:00:00 2001 +From: Ansuel Smith +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 +@@ -2146,14 +2160,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-5.10/759-net-next-net-dsa-qca8k-add-support-for-port-fast-aging.patch b/target/linux/generic/backport-5.10/759-net-next-net-dsa-qca8k-add-support-for-port-fast-aging.patch new file mode 100644 index 000000000..41efa89b5 --- /dev/null +++ b/target/linux/generic/backport-5.10/759-net-next-net-dsa-qca8k-add-support-for-port-fast-aging.patch @@ -0,0 +1,53 @@ +From 4592538bfb0d5d3c3c8a1d7071724d081412ac91 Mon Sep 17 00:00:00 2001 +From: Ansuel Smith +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) +@@ -2005,6 +2015,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-5.10/760-net-next-net-dsa-qca8k-add-set_ageing_time-support.patch b/target/linux/generic/backport-5.10/760-net-next-net-dsa-qca8k-add-set_ageing_time-support.patch new file mode 100644 index 000000000..f32e6ae93 --- /dev/null +++ b/target/linux/generic/backport-5.10/760-net-next-net-dsa-qca8k-add-set_ageing_time-support.patch @@ -0,0 +1,78 @@ +From 6a3bdc5209f45d2af83aa92433ab6e5cf2297aa4 Mon Sep 17 00:00:00 2001 +From: Ansuel Smith +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) + { +@@ -2006,6 +2030,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-5.10/761-net-next-net-dsa-qca8k-add-support-for-mdb_add-del.patch b/target/linux/generic/backport-5.10/761-net-next-net-dsa-qca8k-add-support-for-mdb_add-del.patch new file mode 100644 index 000000000..e0daa88c3 --- /dev/null +++ b/target/linux/generic/backport-5.10/761-net-next-net-dsa-qca8k-add-support-for-mdb_add-del.patch @@ -0,0 +1,142 @@ +From ba8f870dfa635113ce6e8095a5eb1835ecde2e9e Mon Sep 17 00:00:00 2001 +From: Ansuel Smith +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; +@@ -1929,6 +2004,28 @@ qca8k_port_fdb_dump(struct dsa_switch *d + return 0; + } + ++static void ++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; ++ ++ 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 switchdev_trans *trans) +@@ -2044,6 +2141,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_prepare = qca8k_port_vlan_prepare, + .port_vlan_add = qca8k_port_vlan_add, diff --git a/target/linux/generic/backport-5.10/762-v5.11-net-dsa-mt7530-support-setting-MTU.patch b/target/linux/generic/backport-5.10/762-v5.11-net-dsa-mt7530-support-setting-MTU.patch new file mode 100644 index 000000000..eb487cdfb --- /dev/null +++ b/target/linux/generic/backport-5.10/762-v5.11-net-dsa-mt7530-support-setting-MTU.patch @@ -0,0 +1,112 @@ +From 9470174e7581e75a8ebd78964997314dfc2e706c Mon Sep 17 00:00:00 2001 +From: DENG Qingfang +Date: Tue, 3 Nov 2020 13:06:18 +0800 +Subject: [PATCH] net: dsa: mt7530: support setting MTU + +MT7530/7531 has a global RX packet length register, which can be used +to set MTU. + +Supported packet length values are 1522 (1518 if untagged), 1536, +1552, and multiple of 1024 (from 2048 to 15360). + +Signed-off-by: DENG Qingfang +Link: https://lore.kernel.org/r/20201103050618.11419-1-dqfext@gmail.com +Signed-off-by: Jakub Kicinski +--- + drivers/net/dsa/mt7530.c | 49 ++++++++++++++++++++++++++++++++++++++++ + drivers/net/dsa/mt7530.h | 12 ++++++++++ + 2 files changed, 61 insertions(+) + +--- a/drivers/net/dsa/mt7530.c ++++ b/drivers/net/dsa/mt7530.c +@@ -1015,6 +1015,53 @@ mt7530_port_disable(struct dsa_switch *d + mutex_unlock(&priv->reg_mutex); + } + ++static int ++mt7530_port_change_mtu(struct dsa_switch *ds, int port, int new_mtu) ++{ ++ struct mt7530_priv *priv = ds->priv; ++ struct mii_bus *bus = priv->bus; ++ int length; ++ u32 val; ++ ++ /* When a new MTU is set, DSA always set the CPU port's MTU to the ++ * largest MTU of the slave ports. Because the switch only has a global ++ * RX length register, only allowing CPU port here is enough. ++ */ ++ if (!dsa_is_cpu_port(ds, port)) ++ return 0; ++ ++ mutex_lock_nested(&bus->mdio_lock, MDIO_MUTEX_NESTED); ++ ++ val = mt7530_mii_read(priv, MT7530_GMACCR); ++ val &= ~MAX_RX_PKT_LEN_MASK; ++ ++ /* RX length also includes Ethernet header, MTK tag, and FCS length */ ++ length = new_mtu + ETH_HLEN + MTK_HDR_LEN + ETH_FCS_LEN; ++ if (length <= 1522) { ++ val |= MAX_RX_PKT_LEN_1522; ++ } else if (length <= 1536) { ++ val |= MAX_RX_PKT_LEN_1536; ++ } else if (length <= 1552) { ++ val |= MAX_RX_PKT_LEN_1552; ++ } else { ++ val &= ~MAX_RX_JUMBO_MASK; ++ val |= MAX_RX_JUMBO(DIV_ROUND_UP(length, 1024)); ++ val |= MAX_RX_PKT_LEN_JUMBO; ++ } ++ ++ mt7530_mii_write(priv, MT7530_GMACCR, val); ++ ++ mutex_unlock(&bus->mdio_lock); ++ ++ return 0; ++} ++ ++static int ++mt7530_port_max_mtu(struct dsa_switch *ds, int port) ++{ ++ return MT7530_MAX_MTU; ++} ++ + static void + mt7530_stp_state_set(struct dsa_switch *ds, int port, u8 state) + { +@@ -2652,6 +2699,8 @@ static const struct dsa_switch_ops mt753 + .get_sset_count = mt7530_get_sset_count, + .port_enable = mt7530_port_enable, + .port_disable = mt7530_port_disable, ++ .port_change_mtu = mt7530_port_change_mtu, ++ .port_max_mtu = mt7530_port_max_mtu, + .port_stp_state_set = mt7530_stp_state_set, + .port_bridge_join = mt7530_port_bridge_join, + .port_bridge_leave = mt7530_port_bridge_leave, +--- a/drivers/net/dsa/mt7530.h ++++ b/drivers/net/dsa/mt7530.h +@@ -11,6 +11,9 @@ + #define MT7530_NUM_FDB_RECORDS 2048 + #define MT7530_ALL_MEMBERS 0xff + ++#define MTK_HDR_LEN 4 ++#define MT7530_MAX_MTU (15 * 1024 - ETH_HLEN - ETH_FCS_LEN - MTK_HDR_LEN) ++ + enum mt753x_id { + ID_MT7530 = 0, + ID_MT7621 = 1, +@@ -301,6 +304,15 @@ enum mt7530_vlan_port_attr { + #define MT7531_DBG_CNT(x) (0x3018 + (x) * 0x100) + #define MT7531_DIS_CLR BIT(31) + ++#define MT7530_GMACCR 0x30e0 ++#define MAX_RX_JUMBO(x) ((x) << 2) ++#define MAX_RX_JUMBO_MASK GENMASK(5, 2) ++#define MAX_RX_PKT_LEN_MASK GENMASK(1, 0) ++#define MAX_RX_PKT_LEN_1522 0x0 ++#define MAX_RX_PKT_LEN_1536 0x1 ++#define MAX_RX_PKT_LEN_1552 0x2 ++#define MAX_RX_PKT_LEN_JUMBO 0x3 ++ + /* Register for MIB */ + #define MT7530_PORT_MIB_COUNTER(x) (0x4000 + (x) * 0x100) + #define MT7530_MIB_CCR 0x4fe0 diff --git a/target/linux/generic/backport-5.10/763-v5.11-net-dsa-mt7530-enable-MTU-normalization.patch b/target/linux/generic/backport-5.10/763-v5.11-net-dsa-mt7530-enable-MTU-normalization.patch new file mode 100644 index 000000000..b0ad7b9f8 --- /dev/null +++ b/target/linux/generic/backport-5.10/763-v5.11-net-dsa-mt7530-enable-MTU-normalization.patch @@ -0,0 +1,36 @@ +From 771c8901568dd8776a260aa93db41be88a60389e Mon Sep 17 00:00:00 2001 +From: DENG Qingfang +Date: Fri, 11 Dec 2020 01:03:22 +0800 +Subject: [PATCH] net: dsa: mt7530: enable MTU normalization + +MT7530 has a global RX length register, so we are actually changing its +MRU. +Enable MTU normalization for this reason. + +Signed-off-by: DENG Qingfang +Acked-by: Landen Chao +Reviewed-by: Vladimir Oltean +Link: https://lore.kernel.org/r/20201210170322.3433-1-dqfext@gmail.com +Signed-off-by: Jakub Kicinski +--- + drivers/net/dsa/mt7530.c | 2 ++ + 1 file changed, 2 insertions(+) + +--- a/drivers/net/dsa/mt7530.c ++++ b/drivers/net/dsa/mt7530.c +@@ -1703,6 +1703,7 @@ mt7530_setup(struct dsa_switch *ds) + */ + dn = dsa_to_port(ds, MT7530_CPU_PORT)->master->dev.of_node->parent; + ds->configure_vlan_while_not_filtering = true; ++ ds->mtu_enforcement_ingress = true; + + if (priv->id == ID_MT7530) { + regulator_set_voltage(priv->core_pwr, 1000000, 1000000); +@@ -1947,6 +1948,7 @@ mt7531_setup(struct dsa_switch *ds) + } + + ds->configure_vlan_while_not_filtering = true; ++ ds->mtu_enforcement_ingress = true; + + /* Flush the FDB table */ + ret = mt7530_fdb_cmd(priv, MT7530_FDB_FLUSH, NULL); diff --git a/target/linux/generic/backport-5.10/764-v5.11-net-dsa-mt7530-support-setting-ageing-time.patch b/target/linux/generic/backport-5.10/764-v5.11-net-dsa-mt7530-support-setting-ageing-time.patch new file mode 100644 index 000000000..44fa40eab --- /dev/null +++ b/target/linux/generic/backport-5.10/764-v5.11-net-dsa-mt7530-support-setting-ageing-time.patch @@ -0,0 +1,99 @@ +From ea6d5c924e391872d402acac38461a5f8261e57f Mon Sep 17 00:00:00 2001 +From: DENG Qingfang +Date: Tue, 8 Dec 2020 15:00:28 +0800 +Subject: [PATCH] net: dsa: mt7530: support setting ageing time + +MT7530 has a global address age control register, so use it to set +ageing time. + +The applied timer is (AGE_CNT + 1) * (AGE_UNIT + 1) seconds + +Signed-off-by: DENG Qingfang +Reviewed-by: Andrew Lunn +Reviewed-by: Vladimir Oltean +Reviewed-by: Florian Fainelli +Signed-off-by: David S. Miller +--- + drivers/net/dsa/mt7530.c | 41 ++++++++++++++++++++++++++++++++++++++++ + drivers/net/dsa/mt7530.h | 13 +++++++++++++ + 2 files changed, 54 insertions(+) + +--- a/drivers/net/dsa/mt7530.c ++++ b/drivers/net/dsa/mt7530.c +@@ -870,6 +870,46 @@ mt7530_get_sset_count(struct dsa_switch + return ARRAY_SIZE(mt7530_mib); + } + ++static int ++mt7530_set_ageing_time(struct dsa_switch *ds, unsigned int msecs) ++{ ++ struct mt7530_priv *priv = ds->priv; ++ unsigned int secs = msecs / 1000; ++ unsigned int tmp_age_count; ++ unsigned int error = -1; ++ unsigned int age_count; ++ unsigned int age_unit; ++ ++ /* Applied timer is (AGE_CNT + 1) * (AGE_UNIT + 1) seconds */ ++ if (secs < 1 || secs > (AGE_CNT_MAX + 1) * (AGE_UNIT_MAX + 1)) ++ return -ERANGE; ++ ++ /* iterate through all possible age_count to find the closest pair */ ++ for (tmp_age_count = 0; tmp_age_count <= AGE_CNT_MAX; ++tmp_age_count) { ++ unsigned int tmp_age_unit = secs / (tmp_age_count + 1) - 1; ++ ++ if (tmp_age_unit <= AGE_UNIT_MAX) { ++ unsigned int tmp_error = secs - ++ (tmp_age_count + 1) * (tmp_age_unit + 1); ++ ++ /* found a closer pair */ ++ if (error > tmp_error) { ++ error = tmp_error; ++ age_count = tmp_age_count; ++ age_unit = tmp_age_unit; ++ } ++ ++ /* found the exact match, so break the loop */ ++ if (!error) ++ break; ++ } ++ } ++ ++ mt7530_write(priv, MT7530_AAC, AGE_CNT(age_count) | AGE_UNIT(age_unit)); ++ ++ return 0; ++} ++ + static void mt7530_setup_port5(struct dsa_switch *ds, phy_interface_t interface) + { + struct mt7530_priv *priv = ds->priv; +@@ -2699,6 +2739,7 @@ static const struct dsa_switch_ops mt753 + .phy_write = mt753x_phy_write, + .get_ethtool_stats = mt7530_get_ethtool_stats, + .get_sset_count = mt7530_get_sset_count, ++ .set_ageing_time = mt7530_set_ageing_time, + .port_enable = mt7530_port_enable, + .port_disable = mt7530_port_disable, + .port_change_mtu = mt7530_port_change_mtu, +--- a/drivers/net/dsa/mt7530.h ++++ b/drivers/net/dsa/mt7530.h +@@ -161,6 +161,19 @@ enum mt7530_vlan_egress_attr { + MT7530_VLAN_EGRESS_STACK = 3, + }; + ++/* Register for address age control */ ++#define MT7530_AAC 0xa0 ++/* Disable ageing */ ++#define AGE_DIS BIT(20) ++/* Age count */ ++#define AGE_CNT_MASK GENMASK(19, 12) ++#define AGE_CNT_MAX 0xff ++#define AGE_CNT(x) (AGE_CNT_MASK & ((x) << 12)) ++/* Age unit */ ++#define AGE_UNIT_MASK GENMASK(11, 0) ++#define AGE_UNIT_MAX 0xfff ++#define AGE_UNIT(x) (AGE_UNIT_MASK & (x)) ++ + /* Register for port STP state control */ + #define MT7530_SSP_P(x) (0x2000 + ((x) * 0x100)) + #define FID_PST(x) ((x) & 0x3) diff --git a/target/linux/generic/backport-5.10/770-v5.15-net-dsa-mt7530-support-MDB-operations.patch b/target/linux/generic/backport-5.10/770-v5.15-net-dsa-mt7530-support-MDB-operations.patch new file mode 100644 index 000000000..7143748ee --- /dev/null +++ b/target/linux/generic/backport-5.10/770-v5.15-net-dsa-mt7530-support-MDB-operations.patch @@ -0,0 +1,171 @@ +From 1f11a07a33bc26997c18b633d63f088bf75d11f2 Mon Sep 17 00:00:00 2001 +From: DENG Qingfang +Date: Tue, 24 Aug 2021 11:37:50 +0800 +Subject: [PATCH] net: dsa: mt7530: support MDB operations + +This is a partial backport of commit 5a30833b9a16f8d1aa15de06636f9317ca51f9df +("net: dsa: mt7530: support MDB and bridge flag operations") upstream. + +Signed-off-by: DENG Qingfang +--- + drivers/net/dsa/mt7530.c | 78 ++++++++++++++++++++++++++++++++++++++-- + net/dsa/tag_mtk.c | 14 +------- + 2 files changed, 76 insertions(+), 16 deletions(-) + +--- a/drivers/net/dsa/mt7530.c ++++ b/drivers/net/dsa/mt7530.c +@@ -998,9 +998,6 @@ mt753x_cpu_port_enable(struct dsa_switch + mt7530_write(priv, MT7530_PVC_P(port), + PORT_SPEC_TAG); + +- /* Unknown multicast frame forwarding to the cpu port */ +- mt7530_rmw(priv, MT7530_MFC, UNM_FFP_MASK, UNM_FFP(BIT(port))); +- + /* Set CPU port number */ + if (priv->id == ID_MT7621) + mt7530_rmw(priv, MT7530_MFC, CPU_MASK, CPU_EN | CPU_PORT(port)); +@@ -1131,6 +1128,20 @@ mt7530_stp_state_set(struct dsa_switch * + } + + static int ++mt7530_port_egress_floods(struct dsa_switch *ds, int port, ++ bool unicast, bool multicast) ++{ ++ struct mt7530_priv *priv = ds->priv; ++ ++ mt7530_rmw(priv, MT7530_MFC, ++ UNU_FFP(BIT(port)) | UNM_FFP(BIT(port)), ++ (unicast ? UNU_FFP(BIT(port)) : 0) | ++ (multicast ? UNM_FFP(BIT(port)) : 0)); ++ ++ return 0; ++} ++ ++static int + mt7530_port_bridge_join(struct dsa_switch *ds, int port, + struct net_device *bridge) + { +@@ -1331,6 +1342,63 @@ err: + } + + static int ++mt7530_port_mdb_prepare(struct dsa_switch *ds, int port, ++ const struct switchdev_obj_port_mdb *mdb) ++{ ++ return 0; ++} ++ ++static void ++mt7530_port_mdb_add(struct dsa_switch *ds, int port, ++ const struct switchdev_obj_port_mdb *mdb) ++{ ++ struct mt7530_priv *priv = ds->priv; ++ const u8 *addr = mdb->addr; ++ u16 vid = mdb->vid; ++ u8 port_mask = 0; ++ ++ mutex_lock(&priv->reg_mutex); ++ ++ mt7530_fdb_write(priv, vid, 0, addr, 0, STATIC_EMP); ++ if (!mt7530_fdb_cmd(priv, MT7530_FDB_READ, NULL)) ++ port_mask = (mt7530_read(priv, MT7530_ATRD) >> PORT_MAP) ++ & PORT_MAP_MASK; ++ ++ port_mask |= BIT(port); ++ mt7530_fdb_write(priv, vid, port_mask, addr, -1, STATIC_ENT); ++ mt7530_fdb_cmd(priv, MT7530_FDB_WRITE, NULL); ++ ++ mutex_unlock(&priv->reg_mutex); ++} ++ ++static int ++mt7530_port_mdb_del(struct dsa_switch *ds, int port, ++ const struct switchdev_obj_port_mdb *mdb) ++{ ++ struct mt7530_priv *priv = ds->priv; ++ const u8 *addr = mdb->addr; ++ u16 vid = mdb->vid; ++ u8 port_mask = 0; ++ int ret; ++ ++ mutex_lock(&priv->reg_mutex); ++ ++ mt7530_fdb_write(priv, vid, 0, addr, 0, STATIC_EMP); ++ if (!mt7530_fdb_cmd(priv, MT7530_FDB_READ, NULL)) ++ port_mask = (mt7530_read(priv, MT7530_ATRD) >> PORT_MAP) ++ & PORT_MAP_MASK; ++ ++ port_mask &= ~BIT(port); ++ mt7530_fdb_write(priv, vid, port_mask, addr, -1, ++ port_mask ? STATIC_ENT : STATIC_EMP); ++ ret = mt7530_fdb_cmd(priv, MT7530_FDB_WRITE, NULL); ++ ++ mutex_unlock(&priv->reg_mutex); ++ ++ return ret; ++} ++ ++static int + mt7530_vlan_cmd(struct mt7530_priv *priv, enum mt7530_vlan_cmd cmd, u16 vid) + { + struct mt7530_dummy_poll p; +@@ -2745,11 +2813,15 @@ static const struct dsa_switch_ops mt753 + .port_change_mtu = mt7530_port_change_mtu, + .port_max_mtu = mt7530_port_max_mtu, + .port_stp_state_set = mt7530_stp_state_set, ++ .port_egress_floods = mt7530_port_egress_floods, + .port_bridge_join = mt7530_port_bridge_join, + .port_bridge_leave = mt7530_port_bridge_leave, + .port_fdb_add = mt7530_port_fdb_add, + .port_fdb_del = mt7530_port_fdb_del, + .port_fdb_dump = mt7530_port_fdb_dump, ++ .port_mdb_prepare = mt7530_port_mdb_prepare, ++ .port_mdb_add = mt7530_port_mdb_add, ++ .port_mdb_del = mt7530_port_mdb_del, + .port_vlan_filtering = mt7530_port_vlan_filtering, + .port_vlan_prepare = mt7530_port_vlan_prepare, + .port_vlan_add = mt7530_port_vlan_add, +--- a/net/dsa/tag_mtk.c ++++ b/net/dsa/tag_mtk.c +@@ -24,9 +24,6 @@ static struct sk_buff *mtk_tag_xmit(stru + struct dsa_port *dp = dsa_slave_to_port(dev); + u8 xmit_tpid; + u8 *mtk_tag; +- unsigned char *dest = eth_hdr(skb)->h_dest; +- bool is_multicast_skb = is_multicast_ether_addr(dest) && +- !is_broadcast_ether_addr(dest); + + /* Build the special tag after the MAC Source Address. If VLAN header + * is present, it's required that VLAN header and special tag is +@@ -55,10 +52,6 @@ static struct sk_buff *mtk_tag_xmit(stru + mtk_tag[0] = xmit_tpid; + mtk_tag[1] = (1 << dp->index) & MTK_HDR_XMIT_DP_BIT_MASK; + +- /* Disable SA learning for multicast frames */ +- if (unlikely(is_multicast_skb)) +- mtk_tag[1] |= MTK_HDR_XMIT_SA_DIS; +- + /* Tag control information is kept for 802.1Q */ + if (xmit_tpid == MTK_HDR_XMIT_UNTAGGED) { + mtk_tag[2] = 0; +@@ -74,9 +67,6 @@ static struct sk_buff *mtk_tag_rcv(struc + u16 hdr; + int port; + __be16 *phdr; +- unsigned char *dest = eth_hdr(skb)->h_dest; +- bool is_multicast_skb = is_multicast_ether_addr(dest) && +- !is_broadcast_ether_addr(dest); + + if (unlikely(!pskb_may_pull(skb, MTK_HDR_LEN))) + return NULL; +@@ -102,9 +92,7 @@ static struct sk_buff *mtk_tag_rcv(struc + if (!skb->dev) + return NULL; + +- /* Only unicast or broadcast frames are offloaded */ +- if (likely(!is_multicast_skb)) +- dsa_default_offload_fwd_mark(skb); ++ dsa_default_offload_fwd_mark(skb); + + return skb; + } diff --git a/target/linux/generic/backport-5.10/771-v5.14-net-phy-add-MediaTek-Gigabit-Ethernet-PHY-driver.patch b/target/linux/generic/backport-5.10/771-v5.14-net-phy-add-MediaTek-Gigabit-Ethernet-PHY-driver.patch new file mode 100644 index 000000000..67e3ca91e --- /dev/null +++ b/target/linux/generic/backport-5.10/771-v5.14-net-phy-add-MediaTek-Gigabit-Ethernet-PHY-driver.patch @@ -0,0 +1,159 @@ +From e40d2cca01893c1941f5959b14bb0cd0d4f4d099 Mon Sep 17 00:00:00 2001 +From: DENG Qingfang +Date: Wed, 19 May 2021 11:31:59 +0800 +Subject: [PATCH] net: phy: add MediaTek Gigabit Ethernet PHY driver + +Add support for MediaTek Gigabit Ethernet PHYs found in MT7530 and +MT7531 switches. +The initialization procedure is from the vendor driver, but due to lack +of documentation, the function of some register values remains unknown. + +Signed-off-by: DENG Qingfang +Signed-off-by: David S. Miller +--- + drivers/net/phy/Kconfig | 5 ++ + drivers/net/phy/Makefile | 1 + + drivers/net/phy/mediatek-ge.c | 116 ++++++++++++++++++++++++++++++++++ + 3 files changed, 122 insertions(+) + create mode 100644 drivers/net/phy/mediatek-ge.c + +--- a/drivers/net/phy/Kconfig ++++ b/drivers/net/phy/Kconfig +@@ -201,6 +201,11 @@ config MARVELL_10G_PHY + help + Support for the Marvell Alaska MV88X3310 and compatible PHYs. + ++config MEDIATEK_GE_PHY ++ tristate "MediaTek PHYs" ++ help ++ Supports the MediaTek switch integrated PHYs. ++ + config MICREL_PHY + tristate "Micrel PHYs" + help +--- a/drivers/net/phy/Makefile ++++ b/drivers/net/phy/Makefile +@@ -63,6 +63,7 @@ obj-$(CONFIG_LSI_ET1011C_PHY) += et1011c + obj-$(CONFIG_LXT_PHY) += lxt.o + obj-$(CONFIG_MARVELL_10G_PHY) += marvell10g.o + obj-$(CONFIG_MARVELL_PHY) += marvell.o ++obj-$(CONFIG_MEDIATEK_GE_PHY) += mediatek-ge.o + obj-$(CONFIG_MESON_GXL_PHY) += meson-gxl.o + obj-$(CONFIG_MICREL_KS8995MA) += spi_ks8995.o + obj-$(CONFIG_MICREL_PHY) += micrel.o +--- /dev/null ++++ b/drivers/net/phy/mediatek-ge.c +@@ -0,0 +1,113 @@ ++// SPDX-License-Identifier: GPL-2.0+ ++#include ++#include ++#include ++ ++#define MTK_T10_TEST_CONTROL 0x145 ++#define MTK_PHY_TP_MASK GENMASK(4, 3) ++#define MTK_PHY_TP_AUTO 0 ++#define MTK_PHY_TP_MDI 2 ++#define MTK_PHY_TP_MDIX 3 ++ ++#define MTK_EXT_PAGE_ACCESS 0x1f ++#define MTK_PHY_PAGE_STANDARD 0x0000 ++#define MTK_PHY_PAGE_EXTENDED 0x0001 ++#define MTK_PHY_PAGE_EXTENDED_2 0x0002 ++#define MTK_PHY_PAGE_EXTENDED_3 0x0003 ++#define MTK_PHY_PAGE_EXTENDED_2A30 0x2a30 ++#define MTK_PHY_PAGE_EXTENDED_52B5 0x52b5 ++ ++static int mtk_gephy_read_page(struct phy_device *phydev) ++{ ++ return __phy_read(phydev, MTK_EXT_PAGE_ACCESS); ++} ++ ++static int mtk_gephy_write_page(struct phy_device *phydev, int page) ++{ ++ return __phy_write(phydev, MTK_EXT_PAGE_ACCESS, page); ++} ++ ++static void mtk_gephy_config_init(struct phy_device *phydev) ++{ ++ /* Disable EEE */ ++ phy_write_mmd(phydev, MDIO_MMD_AN, MDIO_AN_EEE_ADV, 0); ++ ++ /* Enable HW auto downshift */ ++ phy_modify_paged(phydev, MTK_PHY_PAGE_EXTENDED, 0x14, 0, BIT(4)); ++ ++ /* Increase SlvDPSready time */ ++ phy_select_page(phydev, MTK_PHY_PAGE_EXTENDED_52B5); ++ __phy_write(phydev, 0x10, 0xafae); ++ __phy_write(phydev, 0x12, 0x2f); ++ __phy_write(phydev, 0x10, 0x8fae); ++ phy_restore_page(phydev, MTK_PHY_PAGE_STANDARD, 0); ++ ++ /* Adjust 100_mse_threshold */ ++ phy_write_mmd(phydev, MDIO_MMD_VEND1, 0x123, 0xffff); ++ ++ /* Disable mcc */ ++ phy_write_mmd(phydev, MDIO_MMD_VEND1, 0xa6, 0x300); ++} ++ ++static int mt7530_phy_config_init(struct phy_device *phydev) ++{ ++ mtk_gephy_config_init(phydev); ++ ++ /* Increase post_update_timer */ ++ phy_write_paged(phydev, MTK_PHY_PAGE_EXTENDED_3, 0x11, 0x4b); ++ ++ return 0; ++} ++ ++static int mt7531_phy_config_init(struct phy_device *phydev) ++{ ++ mtk_gephy_config_init(phydev); ++ ++ /* PHY link down power saving enable */ ++ phy_set_bits(phydev, 0x17, BIT(4)); ++ phy_clear_bits_mmd(phydev, MDIO_MMD_VEND1, 0xc6, 0x300); ++ ++ /* Set TX Pair delay selection */ ++ phy_write_mmd(phydev, MDIO_MMD_VEND1, 0x13, 0x404); ++ phy_write_mmd(phydev, MDIO_MMD_VEND1, 0x14, 0x404); ++ ++ return 0; ++} ++ ++static struct phy_driver mtk_gephy_driver[] = { ++ { ++ PHY_ID_MATCH_EXACT(0x03a29412), ++ .name = "MediaTek MT7530 PHY", ++ .config_init = mt7530_phy_config_init, ++ /* Interrupts are handled by the switch, not the PHY ++ * itself. ++ */ ++ .config_intr = genphy_no_config_intr, ++ .ack_interrupt = genphy_no_ack_interrupt, ++ .suspend = genphy_suspend, ++ .resume = genphy_resume, ++ .read_page = mtk_gephy_read_page, ++ .write_page = mtk_gephy_write_page, ++ }, ++ { ++ PHY_ID_MATCH_EXACT(0x03a29441), ++ .name = "MediaTek MT7531 PHY", ++ .config_init = mt7531_phy_config_init, ++ /* Interrupts are handled by the switch, not the PHY ++ * itself. ++ */ ++ .config_intr = genphy_no_config_intr, ++ .ack_interrupt = genphy_no_ack_interrupt, ++ .suspend = genphy_suspend, ++ .resume = genphy_resume, ++ .read_page = mtk_gephy_read_page, ++ .write_page = mtk_gephy_write_page, ++ }, ++}; ++ ++module_phy_driver(mtk_gephy_driver); ++ ++static struct mdio_device_id __maybe_unused mtk_gephy_tbl[] = { ++ { PHY_ID_MATCH_VENDOR(0x03a29400) }, ++ { } ++}; diff --git a/target/linux/generic/backport-5.10/772-v5.14-net-dsa-mt7530-add-interrupt-support.patch b/target/linux/generic/backport-5.10/772-v5.14-net-dsa-mt7530-add-interrupt-support.patch new file mode 100644 index 000000000..1ee2e1cac --- /dev/null +++ b/target/linux/generic/backport-5.10/772-v5.14-net-dsa-mt7530-add-interrupt-support.patch @@ -0,0 +1,425 @@ +From ba751e28d44255744a30190faad0ca09b455c44d Mon Sep 17 00:00:00 2001 +From: DENG Qingfang +Date: Wed, 19 May 2021 11:32:00 +0800 +Subject: [PATCH] net: dsa: mt7530: add interrupt support + +Add support for MT7530 interrupt controller to handle internal PHYs. +In order to assign an IRQ number to each PHY, the registration of MDIO bus +is also done in this driver. + +Signed-off-by: DENG Qingfang +Reviewed-by: Andrew Lunn +Reviewed-by: Florian Fainelli +Reviewed-by: Vladimir Oltean +Signed-off-by: David S. Miller +--- + drivers/net/dsa/mt7530.c | 264 +++++++++++++++++++++++++++++++++++---- + drivers/net/dsa/mt7530.h | 20 ++- + 2 files changed, 256 insertions(+), 28 deletions(-) + +--- a/drivers/net/dsa/mt7530.c ++++ b/drivers/net/dsa/mt7530.c +@@ -10,6 +10,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -600,18 +601,14 @@ mt7530_mib_reset(struct dsa_switch *ds) + mt7530_write(priv, MT7530_MIB_CCR, CCR_MIB_ACTIVATE); + } + +-static int mt7530_phy_read(struct dsa_switch *ds, int port, int regnum) ++static int mt7530_phy_read(struct mt7530_priv *priv, int port, int regnum) + { +- struct mt7530_priv *priv = ds->priv; +- + return mdiobus_read_nested(priv->bus, port, regnum); + } + +-static int mt7530_phy_write(struct dsa_switch *ds, int port, int regnum, ++static int mt7530_phy_write(struct mt7530_priv *priv, int port, int regnum, + u16 val) + { +- struct mt7530_priv *priv = ds->priv; +- + return mdiobus_write_nested(priv->bus, port, regnum, val); + } + +@@ -789,9 +786,8 @@ out: + } + + static int +-mt7531_ind_phy_read(struct dsa_switch *ds, int port, int regnum) ++mt7531_ind_phy_read(struct mt7530_priv *priv, int port, int regnum) + { +- struct mt7530_priv *priv = ds->priv; + int devad; + int ret; + +@@ -807,10 +803,9 @@ mt7531_ind_phy_read(struct dsa_switch *d + } + + static int +-mt7531_ind_phy_write(struct dsa_switch *ds, int port, int regnum, ++mt7531_ind_phy_write(struct mt7530_priv *priv, int port, int regnum, + u16 data) + { +- struct mt7530_priv *priv = ds->priv; + int devad; + int ret; + +@@ -826,6 +821,22 @@ mt7531_ind_phy_write(struct dsa_switch * + return ret; + } + ++static int ++mt753x_phy_read(struct mii_bus *bus, int port, int regnum) ++{ ++ struct mt7530_priv *priv = bus->priv; ++ ++ return priv->info->phy_read(priv, port, regnum); ++} ++ ++static int ++mt753x_phy_write(struct mii_bus *bus, int port, int regnum, u16 val) ++{ ++ struct mt7530_priv *priv = bus->priv; ++ ++ return priv->info->phy_write(priv, port, regnum, val); ++} ++ + static void + mt7530_get_strings(struct dsa_switch *ds, int port, u32 stringset, + uint8_t *data) +@@ -1793,6 +1804,210 @@ mt7530_setup_gpio(struct mt7530_priv *pr + return devm_gpiochip_add_data(dev, gc, priv); + } + ++static irqreturn_t ++mt7530_irq_thread_fn(int irq, void *dev_id) ++{ ++ struct mt7530_priv *priv = dev_id; ++ bool handled = false; ++ u32 val; ++ int p; ++ ++ mutex_lock_nested(&priv->bus->mdio_lock, MDIO_MUTEX_NESTED); ++ val = mt7530_mii_read(priv, MT7530_SYS_INT_STS); ++ mt7530_mii_write(priv, MT7530_SYS_INT_STS, val); ++ mutex_unlock(&priv->bus->mdio_lock); ++ ++ for (p = 0; p < MT7530_NUM_PHYS; p++) { ++ if (BIT(p) & val) { ++ unsigned int irq; ++ ++ irq = irq_find_mapping(priv->irq_domain, p); ++ handle_nested_irq(irq); ++ handled = true; ++ } ++ } ++ ++ return IRQ_RETVAL(handled); ++} ++ ++static void ++mt7530_irq_mask(struct irq_data *d) ++{ ++ struct mt7530_priv *priv = irq_data_get_irq_chip_data(d); ++ ++ priv->irq_enable &= ~BIT(d->hwirq); ++} ++ ++static void ++mt7530_irq_unmask(struct irq_data *d) ++{ ++ struct mt7530_priv *priv = irq_data_get_irq_chip_data(d); ++ ++ priv->irq_enable |= BIT(d->hwirq); ++} ++ ++static void ++mt7530_irq_bus_lock(struct irq_data *d) ++{ ++ struct mt7530_priv *priv = irq_data_get_irq_chip_data(d); ++ ++ mutex_lock_nested(&priv->bus->mdio_lock, MDIO_MUTEX_NESTED); ++} ++ ++static void ++mt7530_irq_bus_sync_unlock(struct irq_data *d) ++{ ++ struct mt7530_priv *priv = irq_data_get_irq_chip_data(d); ++ ++ mt7530_mii_write(priv, MT7530_SYS_INT_EN, priv->irq_enable); ++ mutex_unlock(&priv->bus->mdio_lock); ++} ++ ++static struct irq_chip mt7530_irq_chip = { ++ .name = KBUILD_MODNAME, ++ .irq_mask = mt7530_irq_mask, ++ .irq_unmask = mt7530_irq_unmask, ++ .irq_bus_lock = mt7530_irq_bus_lock, ++ .irq_bus_sync_unlock = mt7530_irq_bus_sync_unlock, ++}; ++ ++static int ++mt7530_irq_map(struct irq_domain *domain, unsigned int irq, ++ irq_hw_number_t hwirq) ++{ ++ irq_set_chip_data(irq, domain->host_data); ++ irq_set_chip_and_handler(irq, &mt7530_irq_chip, handle_simple_irq); ++ irq_set_nested_thread(irq, true); ++ irq_set_noprobe(irq); ++ ++ return 0; ++} ++ ++static const struct irq_domain_ops mt7530_irq_domain_ops = { ++ .map = mt7530_irq_map, ++ .xlate = irq_domain_xlate_onecell, ++}; ++ ++static void ++mt7530_setup_mdio_irq(struct mt7530_priv *priv) ++{ ++ struct dsa_switch *ds = priv->ds; ++ int p; ++ ++ for (p = 0; p < MT7530_NUM_PHYS; p++) { ++ if (BIT(p) & ds->phys_mii_mask) { ++ unsigned int irq; ++ ++ irq = irq_create_mapping(priv->irq_domain, p); ++ ds->slave_mii_bus->irq[p] = irq; ++ } ++ } ++} ++ ++static int ++mt7530_setup_irq(struct mt7530_priv *priv) ++{ ++ struct device *dev = priv->dev; ++ struct device_node *np = dev->of_node; ++ int ret; ++ ++ if (!of_property_read_bool(np, "interrupt-controller")) { ++ dev_info(dev, "no interrupt support\n"); ++ return 0; ++ } ++ ++ priv->irq = of_irq_get(np, 0); ++ if (priv->irq <= 0) { ++ dev_err(dev, "failed to get parent IRQ: %d\n", priv->irq); ++ return priv->irq ? : -EINVAL; ++ } ++ ++ priv->irq_domain = irq_domain_add_linear(np, MT7530_NUM_PHYS, ++ &mt7530_irq_domain_ops, priv); ++ if (!priv->irq_domain) { ++ dev_err(dev, "failed to create IRQ domain\n"); ++ return -ENOMEM; ++ } ++ ++ /* This register must be set for MT7530 to properly fire interrupts */ ++ if (priv->id != ID_MT7531) ++ mt7530_set(priv, MT7530_TOP_SIG_CTRL, TOP_SIG_CTRL_NORMAL); ++ ++ ret = request_threaded_irq(priv->irq, NULL, mt7530_irq_thread_fn, ++ IRQF_ONESHOT, KBUILD_MODNAME, priv); ++ if (ret) { ++ irq_domain_remove(priv->irq_domain); ++ dev_err(dev, "failed to request IRQ: %d\n", ret); ++ return ret; ++ } ++ ++ return 0; ++} ++ ++static void ++mt7530_free_mdio_irq(struct mt7530_priv *priv) ++{ ++ int p; ++ ++ for (p = 0; p < MT7530_NUM_PHYS; p++) { ++ if (BIT(p) & priv->ds->phys_mii_mask) { ++ unsigned int irq; ++ ++ irq = irq_find_mapping(priv->irq_domain, p); ++ irq_dispose_mapping(irq); ++ } ++ } ++} ++ ++static void ++mt7530_free_irq_common(struct mt7530_priv *priv) ++{ ++ free_irq(priv->irq, priv); ++ irq_domain_remove(priv->irq_domain); ++} ++ ++static void ++mt7530_free_irq(struct mt7530_priv *priv) ++{ ++ mt7530_free_mdio_irq(priv); ++ mt7530_free_irq_common(priv); ++} ++ ++static int ++mt7530_setup_mdio(struct mt7530_priv *priv) ++{ ++ struct dsa_switch *ds = priv->ds; ++ struct device *dev = priv->dev; ++ struct mii_bus *bus; ++ static int idx; ++ int ret; ++ ++ bus = devm_mdiobus_alloc(dev); ++ if (!bus) ++ return -ENOMEM; ++ ++ ds->slave_mii_bus = bus; ++ bus->priv = priv; ++ bus->name = KBUILD_MODNAME "-mii"; ++ snprintf(bus->id, MII_BUS_ID_SIZE, KBUILD_MODNAME "-%d", idx++); ++ bus->read = mt753x_phy_read; ++ bus->write = mt753x_phy_write; ++ bus->parent = dev; ++ bus->phy_mask = ~ds->phys_mii_mask; ++ ++ if (priv->irq) ++ mt7530_setup_mdio_irq(priv); ++ ++ ret = devm_mdiobus_register(dev, bus); ++ if (ret) { ++ dev_err(dev, "failed to register MDIO bus: %d\n", ret); ++ if (priv->irq) ++ mt7530_free_mdio_irq(priv); ++ } ++ ++ return ret; ++} ++ + static int + mt7530_setup(struct dsa_switch *ds) + { +@@ -2749,24 +2964,20 @@ static int + mt753x_setup(struct dsa_switch *ds) + { + struct mt7530_priv *priv = ds->priv; ++ int ret = priv->info->sw_setup(ds); + +- return priv->info->sw_setup(ds); +-} +- +-static int +-mt753x_phy_read(struct dsa_switch *ds, int port, int regnum) +-{ +- struct mt7530_priv *priv = ds->priv; ++ if (ret) ++ return ret; + +- return priv->info->phy_read(ds, port, regnum); +-} ++ ret = mt7530_setup_irq(priv); ++ if (ret) ++ return ret; + +-static int +-mt753x_phy_write(struct dsa_switch *ds, int port, int regnum, u16 val) +-{ +- struct mt7530_priv *priv = ds->priv; ++ ret = mt7530_setup_mdio(priv); ++ if (ret && priv->irq) ++ mt7530_free_irq_common(priv); + +- return priv->info->phy_write(ds, port, regnum, val); ++ return ret; + } + + static int mt753x_get_mac_eee(struct dsa_switch *ds, int port, +@@ -2803,8 +3014,6 @@ static const struct dsa_switch_ops mt753 + .get_tag_protocol = mtk_get_tag_protocol, + .setup = mt753x_setup, + .get_strings = mt7530_get_strings, +- .phy_read = mt753x_phy_read, +- .phy_write = mt753x_phy_write, + .get_ethtool_stats = mt7530_get_ethtool_stats, + .get_sset_count = mt7530_get_sset_count, + .set_ageing_time = mt7530_set_ageing_time, +@@ -2987,6 +3196,9 @@ mt7530_remove(struct mdio_device *mdiode + dev_err(priv->dev, "Failed to disable io pwr: %d\n", + ret); + ++ if (priv->irq) ++ mt7530_free_irq(priv); ++ + dsa_unregister_switch(priv->ds); + mutex_destroy(&priv->reg_mutex); + } +--- a/drivers/net/dsa/mt7530.h ++++ b/drivers/net/dsa/mt7530.h +@@ -7,6 +7,7 @@ + #define __MT7530_H + + #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 +@@ -392,6 +393,12 @@ enum mt7531_sgmii_force_duplex { + #define SYS_CTRL_SW_RST BIT(1) + #define SYS_CTRL_REG_RST BIT(0) + ++/* Register for system interrupt */ ++#define MT7530_SYS_INT_EN 0x7008 ++ ++/* Register for system interrupt status */ ++#define MT7530_SYS_INT_STS 0x700c ++ + /* Register for PHY Indirect Access Control */ + #define MT7531_PHY_IAC 0x701C + #define MT7531_PHY_ACS_ST BIT(31) +@@ -713,6 +720,8 @@ static const char *p5_intf_modes(unsigne + } + } + ++struct mt7530_priv; ++ + /* 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 +@@ -737,8 +746,8 @@ struct mt753x_info { + enum mt753x_id id; + + int (*sw_setup)(struct dsa_switch *ds); +- int (*phy_read)(struct dsa_switch *ds, int port, int regnum); +- int (*phy_write)(struct dsa_switch *ds, int port, int regnum, u16 val); ++ int (*phy_read)(struct mt7530_priv *priv, int port, int regnum); ++ 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); + bool (*phy_mode_supported)(struct dsa_switch *ds, int port, +@@ -772,6 +781,10 @@ struct mt753x_info { + * registers + * @p6_interface Holding the current port 6 interface + * @p5_intf_sel: Holding the current port 5 interface select ++ * ++ * @irq: IRQ number of the switch ++ * @irq_domain: IRQ domain of the switch irq_chip ++ * @irq_enable: IRQ enable bits, synced to SYS_INT_EN + */ + struct mt7530_priv { + struct device *dev; +@@ -793,6 +806,9 @@ struct mt7530_priv { + struct mt7530_port ports[MT7530_NUM_PORTS]; + /* protect among processes for registers access*/ + struct mutex reg_mutex; ++ int irq; ++ struct irq_domain *irq_domain; ++ u32 irq_enable; + }; + + struct mt7530_hw_vlan_entry { diff --git a/target/linux/generic/backport-5.10/774-v5.15-1-igc-remove-_I_PHY_ID-checking.patch b/target/linux/generic/backport-5.10/774-v5.15-1-igc-remove-_I_PHY_ID-checking.patch new file mode 100644 index 000000000..d7df0685d --- /dev/null +++ b/target/linux/generic/backport-5.10/774-v5.15-1-igc-remove-_I_PHY_ID-checking.patch @@ -0,0 +1,73 @@ +From 7c496de538eebd8212dc2a3c9a468386b264d0d4 Mon Sep 17 00:00:00 2001 +From: Sasha Neftin +Date: Wed, 7 Jul 2021 08:14:40 +0300 +Subject: igc: Remove _I_PHY_ID checking + +i225 devices have only one PHY vendor. There is no point checking +_I_PHY_ID during the link establishment and auto-negotiation process. +This patch comes to clean up these pointless checkings. + +Signed-off-by: Sasha Neftin +Tested-by: Dvora Fuxbrumer +Signed-off-by: Tony Nguyen +--- + drivers/net/ethernet/intel/igc/igc_base.c | 10 +--------- + drivers/net/ethernet/intel/igc/igc_main.c | 3 +-- + drivers/net/ethernet/intel/igc/igc_phy.c | 6 ++---- + 3 files changed, 4 insertions(+), 15 deletions(-) + +(limited to 'drivers/net/ethernet/intel/igc') + +--- a/drivers/net/ethernet/intel/igc/igc_base.c ++++ b/drivers/net/ethernet/intel/igc/igc_base.c +@@ -187,15 +187,7 @@ static s32 igc_init_phy_params_base(stru + + igc_check_for_copper_link(hw); + +- /* Verify phy id and set remaining function pointers */ +- switch (phy->id) { +- case I225_I_PHY_ID: +- phy->type = igc_phy_i225; +- break; +- default: +- ret_val = -IGC_ERR_PHY; +- goto out; +- } ++ phy->type = igc_phy_i225; + + out: + return ret_val; +--- a/drivers/net/ethernet/intel/igc/igc_main.c ++++ b/drivers/net/ethernet/intel/igc/igc_main.c +@@ -4189,8 +4189,7 @@ bool igc_has_link(struct igc_adapter *ad + break; + } + +- if (hw->mac.type == igc_i225 && +- hw->phy.id == I225_I_PHY_ID) { ++ if (hw->mac.type == igc_i225) { + if (!netif_carrier_ok(adapter->netdev)) { + adapter->flags &= ~IGC_FLAG_NEED_LINK_UPDATE; + } else if (!(adapter->flags & IGC_FLAG_NEED_LINK_UPDATE)) { +--- a/drivers/net/ethernet/intel/igc/igc_phy.c ++++ b/drivers/net/ethernet/intel/igc/igc_phy.c +@@ -249,8 +249,7 @@ static s32 igc_phy_setup_autoneg(struct + return ret_val; + } + +- if ((phy->autoneg_mask & ADVERTISE_2500_FULL) && +- hw->phy.id == I225_I_PHY_ID) { ++ if (phy->autoneg_mask & ADVERTISE_2500_FULL) { + /* Read the MULTI GBT AN Control Register - reg 7.32 */ + ret_val = phy->ops.read_reg(hw, (STANDARD_AN_REG_MASK << + MMD_DEVADDR_SHIFT) | +@@ -390,8 +389,7 @@ static s32 igc_phy_setup_autoneg(struct + ret_val = phy->ops.write_reg(hw, PHY_1000T_CTRL, + mii_1000t_ctrl_reg); + +- if ((phy->autoneg_mask & ADVERTISE_2500_FULL) && +- hw->phy.id == I225_I_PHY_ID) ++ if (phy->autoneg_mask & ADVERTISE_2500_FULL) + ret_val = phy->ops.write_reg(hw, + (STANDARD_AN_REG_MASK << + MMD_DEVADDR_SHIFT) | diff --git a/target/linux/generic/backport-5.10/774-v5.15-2-igc-remove-phy-type-checking.patch b/target/linux/generic/backport-5.10/774-v5.15-2-igc-remove-phy-type-checking.patch new file mode 100644 index 000000000..ad4d1bb0d --- /dev/null +++ b/target/linux/generic/backport-5.10/774-v5.15-2-igc-remove-phy-type-checking.patch @@ -0,0 +1,43 @@ +From 47bca7de6a4fb8dcb564c7ca14d885c91ed19e03 Mon Sep 17 00:00:00 2001 +From: Sasha Neftin +Date: Sat, 10 Jul 2021 20:57:50 +0300 +Subject: igc: Remove phy->type checking + +i225 devices have only one phy->type: copper. There is no point checking +phy->type during the igc_has_link method from the watchdog that +invoked every 2 seconds. +This patch comes to clean up these pointless checkings. + +Signed-off-by: Sasha Neftin +Tested-by: Dvora Fuxbrumer +Signed-off-by: Tony Nguyen +--- + drivers/net/ethernet/intel/igc/igc_main.c | 15 ++++----------- + 1 file changed, 4 insertions(+), 11 deletions(-) + +(limited to 'drivers/net/ethernet/intel/igc') + +--- a/drivers/net/ethernet/intel/igc/igc_main.c ++++ b/drivers/net/ethernet/intel/igc/igc_main.c +@@ -4177,17 +4177,10 @@ bool igc_has_link(struct igc_adapter *ad + * false until the igc_check_for_link establishes link + * for copper adapters ONLY + */ +- switch (hw->phy.media_type) { +- case igc_media_type_copper: +- if (!hw->mac.get_link_status) +- return true; +- hw->mac.ops.check_for_link(hw); +- link_active = !hw->mac.get_link_status; +- break; +- default: +- case igc_media_type_unknown: +- break; +- } ++ if (!hw->mac.get_link_status) ++ return true; ++ hw->mac.ops.check_for_link(hw); ++ link_active = !hw->mac.get_link_status; + + if (hw->mac.type == igc_i225) { + if (!netif_carrier_ok(adapter->netdev)) { diff --git a/target/linux/generic/backport-5.10/774-v5.15-net-dsa-mv88e6xxx-keep-the-pvid-at-0-when-VLAN-unawa.patch b/target/linux/generic/backport-5.10/774-v5.15-net-dsa-mv88e6xxx-keep-the-pvid-at-0-when-VLAN-unawa.patch new file mode 100644 index 000000000..24bb5aa9c --- /dev/null +++ b/target/linux/generic/backport-5.10/774-v5.15-net-dsa-mv88e6xxx-keep-the-pvid-at-0-when-VLAN-unawa.patch @@ -0,0 +1,225 @@ +From 675992be6f7b603b8cfda4678f173e1021fc1ab6 Mon Sep 17 00:00:00 2001 +From: Vladimir Oltean +Date: Thu, 7 Oct 2021 19:47:10 +0300 +Subject: [PATCH] net: dsa: mv88e6xxx: keep the pvid at 0 when VLAN-unaware + +The VLAN support in mv88e6xxx has a loaded history. Commit 2ea7a679ca2a +("net: dsa: Don't add vlans when vlan filtering is disabled") noticed +some issues with VLAN and decided the best way to deal with them was to +make the DSA core ignore VLANs added by the bridge while VLAN awareness +is turned off. Those issues were never explained, just presented as +"at least one corner case". + +That approach had problems of its own, presented by +commit 54a0ed0df496 ("net: dsa: provide an option for drivers to always +receive bridge VLANs") for the DSA core, followed by +commit 1fb74191988f ("net: dsa: mv88e6xxx: fix vlan setup") which +applied ds->configure_vlan_while_not_filtering = true for mv88e6xxx in +particular. + +We still don't know what corner case Andrew saw when he wrote +commit 2ea7a679ca2a ("net: dsa: Don't add vlans when vlan filtering is +disabled"), but Tobias now reports that when we use TX forwarding +offload, pinging an external station from the bridge device is broken if +the front-facing DSA user port has flooding turned off. The full +description is in the link below, but for short, when a mv88e6xxx port +is under a VLAN-unaware bridge, it inherits that bridge's pvid. +So packets ingressing a user port will be classified to e.g. VID 1 +(assuming that value for the bridge_default_pvid), whereas when +tag_dsa.c xmits towards a user port, it always sends packets using a VID +of 0 if that port is standalone or under a VLAN-unaware bridge - or at +least it did so prior to commit d82f8ab0d874 ("net: dsa: tag_dsa: +offload the bridge forwarding process"). + +In any case, when there is a conversation between the CPU and a station +connected to a user port, the station's MAC address is learned in VID 1 +but the CPU tries to transmit through VID 0. The packets reach the +intended station, but via flooding and not by virtue of matching the +existing ATU entry. + +DSA has established (and enforced in other drivers: sja1105, felix, +mt7530) that a VLAN-unaware port should use a private pvid, and not +inherit the one from the bridge. The bridge's pvid should only be +inherited when that bridge is VLAN-aware, so all state transitions need +to be handled. On the other hand, all bridge VLANs should sit in the VTU +starting with the moment when the bridge offloads them via switchdev, +they are just not used. + +This solves the problem that Tobias sees because packets ingressing on +VLAN-unaware user ports now get classified to VID 0, which is also the +VID used by tag_dsa.c on xmit. + +Fixes: d82f8ab0d874 ("net: dsa: tag_dsa: offload the bridge forwarding process") +Link: https://patchwork.kernel.org/project/netdevbpf/patch/20211003222312.284175-2-vladimir.oltean@nxp.com/#24491503 +Reported-by: Tobias Waldekranz +Signed-off-by: Vladimir Oltean +Signed-off-by: Jakub Kicinski +--- + drivers/net/dsa/mv88e6xxx/chip.c | 56 +++++++++++++++++++++++++++++--- + drivers/net/dsa/mv88e6xxx/chip.h | 6 ++++ + drivers/net/dsa/mv88e6xxx/port.c | 21 ++++++++++++ + drivers/net/dsa/mv88e6xxx/port.h | 2 ++ + 4 files changed, 81 insertions(+), 4 deletions(-) + +--- a/drivers/net/dsa/mv88e6xxx/chip.c ++++ b/drivers/net/dsa/mv88e6xxx/chip.c +@@ -1586,6 +1586,26 @@ static int mv88e6xxx_port_check_hw_vlan( + return 0; + } + ++static int mv88e6xxx_port_commit_pvid(struct mv88e6xxx_chip *chip, int port) ++{ ++ struct dsa_port *dp = dsa_to_port(chip->ds, port); ++ struct mv88e6xxx_port *p = &chip->ports[port]; ++ bool drop_untagged = false; ++ u16 pvid = 0; ++ int err; ++ ++ if (dp->bridge_dev && br_vlan_enabled(dp->bridge_dev)) { ++ pvid = p->bridge_pvid.vid; ++ drop_untagged = !p->bridge_pvid.valid; ++ } ++ ++ err = mv88e6xxx_port_set_pvid(chip, port, pvid); ++ if (err) ++ return err; ++ ++ return mv88e6xxx_port_drop_untagged(chip, port, drop_untagged); ++} ++ + static int mv88e6xxx_port_vlan_filtering(struct dsa_switch *ds, int port, + bool vlan_filtering, + struct switchdev_trans *trans) +@@ -1599,7 +1619,16 @@ static int mv88e6xxx_port_vlan_filtering + return chip->info->max_vid ? 0 : -EOPNOTSUPP; + + mv88e6xxx_reg_lock(chip); ++ + err = mv88e6xxx_port_set_8021q_mode(chip, port, mode); ++ if (err) ++ goto unlock; ++ ++ err = mv88e6xxx_port_commit_pvid(chip, port); ++ if (err) ++ goto unlock; ++ ++unlock: + mv88e6xxx_reg_unlock(chip); + + return err; +@@ -1982,8 +2011,10 @@ static void mv88e6xxx_port_vlan_add(stru + struct mv88e6xxx_chip *chip = ds->priv; + bool untagged = vlan->flags & BRIDGE_VLAN_INFO_UNTAGGED; + bool pvid = vlan->flags & BRIDGE_VLAN_INFO_PVID; ++ struct mv88e6xxx_port *p = &chip->ports[port]; + bool warn; + u8 member; ++ int err; + u16 vid; + + if (!chip->info->max_vid) +@@ -2008,9 +2039,23 @@ static void mv88e6xxx_port_vlan_add(stru + dev_err(ds->dev, "p%d: failed to add VLAN %d%c\n", port, + vid, untagged ? 'u' : 't'); + +- if (pvid && mv88e6xxx_port_set_pvid(chip, port, vlan->vid_end)) +- dev_err(ds->dev, "p%d: failed to set PVID %d\n", port, +- vlan->vid_end); ++ if (pvid) { ++ p->bridge_pvid.vid = vlan->vid_end; ++ p->bridge_pvid.valid = true; ++ ++ err = mv88e6xxx_port_commit_pvid(chip, port); ++ if (err) ++ dev_err(ds->dev, "p%d: failed to set PVID %d", port, ++ vlan->vid_end); ++ } else if (vlan->vid_end && p->bridge_pvid.vid == vlan->vid_end) { ++ /* The old pvid was reinstalled as a non-pvid VLAN */ ++ p->bridge_pvid.valid = false; ++ ++ err = mv88e6xxx_port_commit_pvid(chip, port); ++ if (err) ++ dev_err(ds->dev, "p%d: failed to unset PVID %d", port, ++ vlan->vid_end); ++ } + + mv88e6xxx_reg_unlock(chip); + } +@@ -2061,6 +2106,7 @@ static int mv88e6xxx_port_vlan_del(struc + const struct switchdev_obj_port_vlan *vlan) + { + struct mv88e6xxx_chip *chip = ds->priv; ++ struct mv88e6xxx_port *p = &chip->ports[port]; + u16 pvid, vid; + int err = 0; + +@@ -2079,7 +2125,9 @@ static int mv88e6xxx_port_vlan_del(struc + goto unlock; + + if (vid == pvid) { +- err = mv88e6xxx_port_set_pvid(chip, port, 0); ++ p->bridge_pvid.valid = false; ++ ++ err = mv88e6xxx_port_commit_pvid(chip, port); + if (err) + goto unlock; + } +--- a/drivers/net/dsa/mv88e6xxx/chip.h ++++ b/drivers/net/dsa/mv88e6xxx/chip.h +@@ -224,9 +224,15 @@ struct mv88e6xxx_policy { + u16 vid; + }; + ++struct mv88e6xxx_vlan { ++ u16 vid; ++ bool valid; ++}; ++ + struct mv88e6xxx_port { + struct mv88e6xxx_chip *chip; + int port; ++ struct mv88e6xxx_vlan bridge_pvid; + u64 serdes_stats[2]; + u64 atu_member_violation; + u64 atu_miss_violation; +--- a/drivers/net/dsa/mv88e6xxx/port.c ++++ b/drivers/net/dsa/mv88e6xxx/port.c +@@ -1062,6 +1062,27 @@ int mv88e6xxx_port_set_8021q_mode(struct + return 0; + } + ++int mv88e6xxx_port_drop_untagged(struct mv88e6xxx_chip *chip, int port, ++ bool drop_untagged) ++{ ++ u16 old, new; ++ int err; ++ ++ err = mv88e6xxx_port_read(chip, port, MV88E6XXX_PORT_CTL2, &old); ++ if (err) ++ return err; ++ ++ if (drop_untagged) ++ new = old | MV88E6XXX_PORT_CTL2_DISCARD_UNTAGGED; ++ else ++ new = old & ~MV88E6XXX_PORT_CTL2_DISCARD_UNTAGGED; ++ ++ if (new == old) ++ return 0; ++ ++ return mv88e6xxx_port_write(chip, port, MV88E6XXX_PORT_CTL2, new); ++} ++ + int mv88e6xxx_port_set_map_da(struct mv88e6xxx_chip *chip, int port) + { + u16 reg; +--- a/drivers/net/dsa/mv88e6xxx/port.h ++++ b/drivers/net/dsa/mv88e6xxx/port.h +@@ -364,6 +364,8 @@ int mv88e6390x_port_set_cmode(struct mv8 + phy_interface_t mode); + int mv88e6185_port_get_cmode(struct mv88e6xxx_chip *chip, int port, u8 *cmode); + int mv88e6352_port_get_cmode(struct mv88e6xxx_chip *chip, int port, u8 *cmode); ++int mv88e6xxx_port_drop_untagged(struct mv88e6xxx_chip *chip, int port, ++ bool drop_untagged); + int mv88e6xxx_port_set_map_da(struct mv88e6xxx_chip *chip, int port); + int mv88e6095_port_set_upstream_port(struct mv88e6xxx_chip *chip, int port, + int upstream_port); diff --git a/target/linux/generic/backport-5.10/780-v5.11-net-usb-r8152-Provide-missing-documentation-for-some.patch b/target/linux/generic/backport-5.10/780-v5.11-net-usb-r8152-Provide-missing-documentation-for-some.patch new file mode 100644 index 000000000..e69144fcd --- /dev/null +++ b/target/linux/generic/backport-5.10/780-v5.11-net-usb-r8152-Provide-missing-documentation-for-some.patch @@ -0,0 +1,72 @@ +From 586f04ce6a391419ca3cc9cef6b6f38570cede88 Mon Sep 17 00:00:00 2001 +From: Lee Jones +Date: Mon, 2 Nov 2020 11:45:04 +0000 +Subject: [PATCH] net: usb: r8152: Provide missing documentation for + some struct members + +commit 34e653efb602e0651867fb5ab14369b555a61dcd upstream. + +Fixes the following W=1 kernel build warning(s): + + drivers/net/usb/r8152.c:934: warning: Function parameter or member 'blk_hdr' not described in 'fw_mac' + drivers/net/usb/r8152.c:934: warning: Function parameter or member 'reserved' not described in 'fw_mac' + drivers/net/usb/r8152.c:947: warning: Function parameter or member 'blk_hdr' not described in 'fw_phy_patch_key' + drivers/net/usb/r8152.c:947: warning: Function parameter or member 'reserved' not described in 'fw_phy_patch_key' + drivers/net/usb/r8152.c:986: warning: Function parameter or member 'blk_hdr' not described in 'fw_phy_nc' + drivers/net/usb/r8152.c:986: warning: Function parameter or member 'mode_pre' not described in 'fw_phy_nc' + drivers/net/usb/r8152.c:986: warning: Function parameter or member 'mode_post' not described in 'fw_phy_nc' + drivers/net/usb/r8152.c:986: warning: Function parameter or member 'reserved' not described in 'fw_phy_nc' + +Signed-off-by: Lee Jones +Acked-by: Hayes Wang +Link: https://lore.kernel.org/r/20201102114512.1062724-23-lee.jones@linaro.org +Signed-off-by: Jakub Kicinski +--- + drivers/net/usb/r8152.c | 6 ++++++ + 1 file changed, 6 insertions(+) + +--- a/drivers/net/usb/r8152.c ++++ b/drivers/net/usb/r8152.c +@@ -898,6 +898,7 @@ struct fw_header { + * struct fw_mac - a firmware block used by RTL_FW_PLA and RTL_FW_USB. + * The layout of the firmware block is: + * + + . ++ * @blk_hdr: firmware descriptor (type, length) + * @fw_offset: offset of the firmware binary data. The start address of + * the data would be the address of struct fw_mac + @fw_offset. + * @fw_reg: the register to load the firmware. Depends on chip. +@@ -911,6 +912,7 @@ struct fw_header { + * @bp_num: the break point number which needs to be set for this firmware. + * Depends on the firmware. + * @bp: break points. Depends on firmware. ++ * @reserved: reserved space (unused) + * @fw_ver_reg: the register to store the fw version. + * @fw_ver_data: the firmware version of the current type. + * @info: additional information for debugging, and is followed by the +@@ -936,8 +938,10 @@ struct fw_mac { + /** + * struct fw_phy_patch_key - a firmware block used by RTL_FW_PHY_START. + * This is used to set patch key when loading the firmware of PHY. ++ * @blk_hdr: firmware descriptor (type, length) + * @key_reg: the register to write the patch key. + * @key_data: patch key. ++ * @reserved: reserved space (unused) + */ + struct fw_phy_patch_key { + struct fw_block blk_hdr; +@@ -950,6 +954,7 @@ struct fw_phy_patch_key { + * struct fw_phy_nc - a firmware block used by RTL_FW_PHY_NC. + * The layout of the firmware block is: + * + + . ++ * @blk_hdr: firmware descriptor (type, length) + * @fw_offset: offset of the firmware binary data. The start address of + * the data would be the address of struct fw_phy_nc + @fw_offset. + * @fw_reg: the register to load the firmware. Depends on chip. +@@ -960,6 +965,7 @@ struct fw_phy_patch_key { + * @mode_reg: the regitster of switching the mode. + * @mod_pre: the mode needing to be set before loading the firmware. + * @mod_post: the mode to be set when finishing to load the firmware. ++ * @reserved: reserved space (unused) + * @bp_start: the start register of break points. Depends on chip. + * @bp_num: the break point number which needs to be set for this firmware. + * Depends on the firmware. diff --git a/target/linux/generic/backport-5.10/781-v5.11-net-usb-r8152-Fix-a-couple-of-spelling-errors-in-fw_.patch b/target/linux/generic/backport-5.10/781-v5.11-net-usb-r8152-Fix-a-couple-of-spelling-errors-in-fw_.patch new file mode 100644 index 000000000..96a9a3363 --- /dev/null +++ b/target/linux/generic/backport-5.10/781-v5.11-net-usb-r8152-Fix-a-couple-of-spelling-errors-in-fw_.patch @@ -0,0 +1,34 @@ +From 5fcfa846181de6676509696c4cd7b60a22e74077 Mon Sep 17 00:00:00 2001 +From: Lee Jones +Date: Mon, 2 Nov 2020 11:45:09 +0000 +Subject: [PATCH] net: usb: r8152: Fix a couple of spelling errors in + fw_phy_nc's docs + +commit 9f07814d01ad085b2d9f1d55b4ce532fb2c27110 upstream. + +Fixes the following W=1 kernel build warning(s): + + drivers/net/usb/r8152.c:992: warning: Function parameter or member 'mode_pre' not described in 'fw_phy_nc' + drivers/net/usb/r8152.c:992: warning: Function parameter or member 'mode_post' not described in 'fw_phy_nc' + +Signed-off-by: Lee Jones +Acked-by: Hayes Wang +Link: https://lore.kernel.org/r/20201102114512.1062724-28-lee.jones@linaro.org +Signed-off-by: Jakub Kicinski +--- + drivers/net/usb/r8152.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +--- a/drivers/net/usb/r8152.c ++++ b/drivers/net/usb/r8152.c +@@ -963,8 +963,8 @@ struct fw_phy_patch_key { + * @patch_en_addr: the register of enabling patch mode. Depends on chip. + * @patch_en_value: patch mode enabled mask. Depends on the firmware. + * @mode_reg: the regitster of switching the mode. +- * @mod_pre: the mode needing to be set before loading the firmware. +- * @mod_post: the mode to be set when finishing to load the firmware. ++ * @mode_pre: the mode needing to be set before loading the firmware. ++ * @mode_post: the mode to be set when finishing to load the firmware. + * @reserved: reserved space (unused) + * @bp_start: the start register of break points. Depends on chip. + * @bp_num: the break point number which needs to be set for this firmware. diff --git a/target/linux/generic/backport-5.10/782-v5.11-net-usb-r8153_ecm-support-ECM-mode-for-RTL8153.patch b/target/linux/generic/backport-5.10/782-v5.11-net-usb-r8153_ecm-support-ECM-mode-for-RTL8153.patch new file mode 100644 index 000000000..b3eb58831 --- /dev/null +++ b/target/linux/generic/backport-5.10/782-v5.11-net-usb-r8153_ecm-support-ECM-mode-for-RTL8153.patch @@ -0,0 +1,320 @@ +From 0ef50460f7f053bd2a911ec53e01bfda646a5574 Mon Sep 17 00:00:00 2001 +From: Hayes Wang +Date: Wed, 4 Nov 2020 10:19:22 +0800 +Subject: [PATCH] net/usb/r8153_ecm: support ECM mode for RTL8153 + +commit c1aedf015ebdd0232757a66e2daccf1246bd609c upstream. + +Support ECM mode based on cdc_ether with relative mii functions, +when CONFIG_USB_RTL8152 is not set, or the device is not supported +by r8152 driver. + +Both r8152 and r8153_ecm would check the return value of +rtl8152_get_version() in porbe(). If rtl8152_get_version() +return none zero value, the r8152 is used for the device +with vendor mode. Otherwise, the r8153_ecm is used for the +device with ECM mode. + +Signed-off-by: Hayes Wang +Link: https://lore.kernel.org/r/1394712342-15778-392-Taiwan-albertk@realtek.com +Signed-off-by: Jakub Kicinski +--- + drivers/net/usb/Makefile | 2 +- + drivers/net/usb/r8152.c | 30 +------ + drivers/net/usb/r8153_ecm.c | 162 ++++++++++++++++++++++++++++++++++++ + include/linux/usb/r8152.h | 37 ++++++++ + 4 files changed, 204 insertions(+), 27 deletions(-) + create mode 100644 drivers/net/usb/r8153_ecm.c + create mode 100644 include/linux/usb/r8152.h + +--- a/drivers/net/usb/Makefile ++++ b/drivers/net/usb/Makefile +@@ -13,7 +13,7 @@ obj-$(CONFIG_USB_LAN78XX) += lan78xx.o + obj-$(CONFIG_USB_NET_AX8817X) += asix.o + asix-y := asix_devices.o asix_common.o ax88172a.o + obj-$(CONFIG_USB_NET_AX88179_178A) += ax88179_178a.o +-obj-$(CONFIG_USB_NET_CDCETHER) += cdc_ether.o ++obj-$(CONFIG_USB_NET_CDCETHER) += cdc_ether.o r8153_ecm.o + obj-$(CONFIG_USB_NET_CDC_EEM) += cdc_eem.o + obj-$(CONFIG_USB_NET_DM9601) += dm9601.o + obj-$(CONFIG_USB_NET_SR9700) += sr9700.o +--- a/drivers/net/usb/r8152.c ++++ b/drivers/net/usb/r8152.c +@@ -26,6 +26,7 @@ + #include + #include + #include ++#include + + /* Information for net-next */ + #define NETNEXT_VERSION "11" +@@ -653,18 +654,6 @@ enum rtl_register_content { + + #define INTR_LINK 0x0004 + +-#define RTL8152_REQT_READ 0xc0 +-#define RTL8152_REQT_WRITE 0x40 +-#define RTL8152_REQ_GET_REGS 0x05 +-#define RTL8152_REQ_SET_REGS 0x05 +- +-#define BYTE_EN_DWORD 0xff +-#define BYTE_EN_WORD 0x33 +-#define BYTE_EN_BYTE 0x11 +-#define BYTE_EN_SIX_BYTES 0x3f +-#define BYTE_EN_START_MASK 0x0f +-#define BYTE_EN_END_MASK 0xf0 +- + #define RTL8153_MAX_PACKET 9216 /* 9K */ + #define RTL8153_MAX_MTU (RTL8153_MAX_PACKET - VLAN_ETH_HLEN - \ + ETH_FCS_LEN) +@@ -689,21 +678,9 @@ enum rtl8152_flags { + LENOVO_MACPASSTHRU, + }; + +-/* Define these values to match your device */ +-#define VENDOR_ID_REALTEK 0x0bda +-#define VENDOR_ID_MICROSOFT 0x045e +-#define VENDOR_ID_SAMSUNG 0x04e8 +-#define VENDOR_ID_LENOVO 0x17ef +-#define VENDOR_ID_LINKSYS 0x13b1 +-#define VENDOR_ID_NVIDIA 0x0955 +-#define VENDOR_ID_TPLINK 0x2357 +- + #define DEVICE_ID_THINKPAD_THUNDERBOLT3_DOCK_GEN2 0x3082 + #define DEVICE_ID_THINKPAD_USB_C_DOCK_GEN2 0xa387 + +-#define MCU_TYPE_PLA 0x0100 +-#define MCU_TYPE_USB 0x0000 +- + struct tally_counter { + __le64 tx_packets; + __le64 rx_packets; +@@ -6602,7 +6579,7 @@ static int rtl_fw_init(struct r8152 *tp) + return 0; + } + +-static u8 rtl_get_version(struct usb_interface *intf) ++u8 rtl8152_get_version(struct usb_interface *intf) + { + struct usb_device *udev = interface_to_usbdev(intf); + u32 ocp_data = 0; +@@ -6660,12 +6637,13 @@ static u8 rtl_get_version(struct usb_int + + return version; + } ++EXPORT_SYMBOL_GPL(rtl8152_get_version); + + static int rtl8152_probe(struct usb_interface *intf, + const struct usb_device_id *id) + { + struct usb_device *udev = interface_to_usbdev(intf); +- u8 version = rtl_get_version(intf); ++ u8 version = rtl8152_get_version(intf); + struct r8152 *tp; + struct net_device *netdev; + int ret; +--- /dev/null ++++ b/drivers/net/usb/r8153_ecm.c +@@ -0,0 +1,162 @@ ++// SPDX-License-Identifier: GPL-2.0-or-later ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#define OCP_BASE 0xe86c ++ ++static int pla_read_word(struct usbnet *dev, u16 index) ++{ ++ u16 byen = BYTE_EN_WORD; ++ u8 shift = index & 2; ++ __le32 tmp; ++ int ret; ++ ++ if (shift) ++ byen <<= shift; ++ ++ index &= ~3; ++ ++ ret = usbnet_read_cmd(dev, RTL8152_REQ_GET_REGS, RTL8152_REQT_READ, index, ++ MCU_TYPE_PLA | byen, &tmp, sizeof(tmp)); ++ if (ret < 0) ++ goto out; ++ ++ ret = __le32_to_cpu(tmp); ++ ret >>= (shift * 8); ++ ret &= 0xffff; ++ ++out: ++ return ret; ++} ++ ++static int pla_write_word(struct usbnet *dev, u16 index, u32 data) ++{ ++ u32 mask = 0xffff; ++ u16 byen = BYTE_EN_WORD; ++ u8 shift = index & 2; ++ __le32 tmp; ++ int ret; ++ ++ data &= mask; ++ ++ if (shift) { ++ byen <<= shift; ++ mask <<= (shift * 8); ++ data <<= (shift * 8); ++ } ++ ++ index &= ~3; ++ ++ ret = usbnet_read_cmd(dev, RTL8152_REQ_GET_REGS, RTL8152_REQT_READ, index, ++ MCU_TYPE_PLA | byen, &tmp, sizeof(tmp)); ++ ++ if (ret < 0) ++ goto out; ++ ++ data |= __le32_to_cpu(tmp) & ~mask; ++ tmp = __cpu_to_le32(data); ++ ++ ret = usbnet_write_cmd(dev, RTL8152_REQ_SET_REGS, RTL8152_REQT_WRITE, index, ++ MCU_TYPE_PLA | byen, &tmp, sizeof(tmp)); ++ ++out: ++ return ret; ++} ++ ++static int r8153_ecm_mdio_read(struct net_device *netdev, int phy_id, int reg) ++{ ++ struct usbnet *dev = netdev_priv(netdev); ++ int ret; ++ ++ ret = pla_write_word(dev, OCP_BASE, 0xa000); ++ if (ret < 0) ++ goto out; ++ ++ ret = pla_read_word(dev, 0xb400 + reg * 2); ++ ++out: ++ return ret; ++} ++ ++static void r8153_ecm_mdio_write(struct net_device *netdev, int phy_id, int reg, int val) ++{ ++ struct usbnet *dev = netdev_priv(netdev); ++ int ret; ++ ++ ret = pla_write_word(dev, OCP_BASE, 0xa000); ++ if (ret < 0) ++ return; ++ ++ ret = pla_write_word(dev, 0xb400 + reg * 2, val); ++} ++ ++static int r8153_bind(struct usbnet *dev, struct usb_interface *intf) ++{ ++ int status; ++ ++ status = usbnet_cdc_bind(dev, intf); ++ if (status < 0) ++ return status; ++ ++ dev->mii.dev = dev->net; ++ dev->mii.mdio_read = r8153_ecm_mdio_read; ++ dev->mii.mdio_write = r8153_ecm_mdio_write; ++ dev->mii.reg_num_mask = 0x1f; ++ dev->mii.supports_gmii = 1; ++ ++ return status; ++} ++ ++static const struct driver_info r8153_info = { ++ .description = "RTL8153 ECM Device", ++ .flags = FLAG_ETHER, ++ .bind = r8153_bind, ++ .unbind = usbnet_cdc_unbind, ++ .status = usbnet_cdc_status, ++ .manage_power = usbnet_manage_power, ++}; ++ ++static const struct usb_device_id products[] = { ++{ ++ USB_DEVICE_AND_INTERFACE_INFO(VENDOR_ID_REALTEK, 0x8153, USB_CLASS_COMM, ++ USB_CDC_SUBCLASS_ETHERNET, USB_CDC_PROTO_NONE), ++ .driver_info = (unsigned long)&r8153_info, ++}, ++ ++ { }, /* END */ ++}; ++MODULE_DEVICE_TABLE(usb, products); ++ ++static int rtl8153_ecm_probe(struct usb_interface *intf, ++ const struct usb_device_id *id) ++{ ++#if IS_REACHABLE(CONFIG_USB_RTL8152) ++ if (rtl8152_get_version(intf)) ++ return -ENODEV; ++#endif ++ ++ return usbnet_probe(intf, id); ++} ++ ++static struct usb_driver r8153_ecm_driver = { ++ .name = "r8153_ecm", ++ .id_table = products, ++ .probe = rtl8153_ecm_probe, ++ .disconnect = usbnet_disconnect, ++ .suspend = usbnet_suspend, ++ .resume = usbnet_resume, ++ .reset_resume = usbnet_resume, ++ .supports_autosuspend = 1, ++ .disable_hub_initiated_lpm = 1, ++}; ++ ++module_usb_driver(r8153_ecm_driver); ++ ++MODULE_AUTHOR("Hayes Wang"); ++MODULE_DESCRIPTION("Realtek USB ECM device"); ++MODULE_LICENSE("GPL"); +--- /dev/null ++++ b/include/linux/usb/r8152.h +@@ -0,0 +1,37 @@ ++/* SPDX-License-Identifier: GPL-2.0-only */ ++/* ++ * Copyright (c) 2020 Realtek Semiconductor Corp. All rights reserved. ++ */ ++ ++#ifndef __LINUX_R8152_H ++#define __LINUX_R8152_H ++ ++#define RTL8152_REQT_READ 0xc0 ++#define RTL8152_REQT_WRITE 0x40 ++#define RTL8152_REQ_GET_REGS 0x05 ++#define RTL8152_REQ_SET_REGS 0x05 ++ ++#define BYTE_EN_DWORD 0xff ++#define BYTE_EN_WORD 0x33 ++#define BYTE_EN_BYTE 0x11 ++#define BYTE_EN_SIX_BYTES 0x3f ++#define BYTE_EN_START_MASK 0x0f ++#define BYTE_EN_END_MASK 0xf0 ++ ++#define MCU_TYPE_PLA 0x0100 ++#define MCU_TYPE_USB 0x0000 ++ ++/* Define these values to match your device */ ++#define VENDOR_ID_REALTEK 0x0bda ++#define VENDOR_ID_MICROSOFT 0x045e ++#define VENDOR_ID_SAMSUNG 0x04e8 ++#define VENDOR_ID_LENOVO 0x17ef ++#define VENDOR_ID_LINKSYS 0x13b1 ++#define VENDOR_ID_NVIDIA 0x0955 ++#define VENDOR_ID_TPLINK 0x2357 ++ ++#if IS_REACHABLE(CONFIG_USB_RTL8152) ++extern u8 rtl8152_get_version(struct usb_interface *intf); ++#endif ++ ++#endif /* __LINUX_R8152_H */ diff --git a/target/linux/generic/backport-5.10/783-v5.12-net-usb-r8152-use-new-tasklet-API.patch b/target/linux/generic/backport-5.10/783-v5.12-net-usb-r8152-use-new-tasklet-API.patch new file mode 100644 index 000000000..eeb85d795 --- /dev/null +++ b/target/linux/generic/backport-5.10/783-v5.12-net-usb-r8152-use-new-tasklet-API.patch @@ -0,0 +1,41 @@ +From 90f1afc7f96c8f7cf19c82e5f4b39e61a63b053d Mon Sep 17 00:00:00 2001 +From: Emil Renner Berthing +Date: Sun, 31 Jan 2021 00:47:29 +0100 +Subject: [PATCH] net: usb: r8152: use new tasklet API + +commit f3163f1cb87141c7a41a15a5d4c98b353f807b04 upstream. + +This converts the driver to use the new tasklet API introduced in +commit 12cc923f1ccc ("tasklet: Introduce new initialization API") + +Signed-off-by: Emil Renner Berthing +Signed-off-by: Jakub Kicinski +--- + drivers/net/usb/r8152.c | 8 +++----- + 1 file changed, 3 insertions(+), 5 deletions(-) + +--- a/drivers/net/usb/r8152.c ++++ b/drivers/net/usb/r8152.c +@@ -2393,11 +2393,9 @@ static void tx_bottom(struct r8152 *tp) + } while (res == 0); + } + +-static void bottom_half(unsigned long data) ++static void bottom_half(struct tasklet_struct *t) + { +- struct r8152 *tp; +- +- tp = (struct r8152 *)data; ++ struct r8152 *tp = from_tasklet(tp, t, tx_tl); + + if (test_bit(RTL8152_UNPLUG, &tp->flags)) + return; +@@ -6695,7 +6693,7 @@ static int rtl8152_probe(struct usb_inte + mutex_init(&tp->control); + INIT_DELAYED_WORK(&tp->schedule, rtl_work_func_t); + INIT_DELAYED_WORK(&tp->hw_phy_work, rtl_hw_phy_work_func_t); +- tasklet_init(&tp->tx_tl, bottom_half, (unsigned long)tp); ++ tasklet_setup(&tp->tx_tl, bottom_half); + tasklet_disable(&tp->tx_tl); + + netdev->netdev_ops = &rtl8152_netdev_ops; diff --git a/target/linux/generic/backport-5.10/784-v5.12-r8152-replace-several-functions-about-phy-patch-requ.patch b/target/linux/generic/backport-5.10/784-v5.12-r8152-replace-several-functions-about-phy-patch-requ.patch new file mode 100644 index 000000000..faaa8275c --- /dev/null +++ b/target/linux/generic/backport-5.10/784-v5.12-r8152-replace-several-functions-about-phy-patch-requ.patch @@ -0,0 +1,198 @@ +From 86b98abf4f8c691c260c5113d6a2d32f5377caca Mon Sep 17 00:00:00 2001 +From: Hayes Wang +Date: Wed, 3 Feb 2021 17:14:28 +0800 +Subject: [PATCH] r8152: replace several functions about phy patch + request + +commit a08c0d309d8c078d22717d815cf9853f6f2c07bd upstream. + +Replace r8153_patch_request() with rtl_phy_patch_request(). +Replace r8153_pre_ram_code() with rtl_pre_ram_code(). +Replace r8153_post_ram_code() with rtl_post_ram_code(). +Add rtl_patch_key_set(). + +The new functions have an additional parameter. It is used to wait +the patch request command finished. When the PHY is resumed from +the state of power cut, the PHY is at a safe mode and the +OCP_PHY_PATCH_STAT wouldn't be updated. For this situation, it is +safe to set patch request command without waiting OCP_PHY_PATCH_STAT. + +Signed-off-by: Hayes Wang +Signed-off-by: Jakub Kicinski +--- + drivers/net/usb/r8152.c | 84 ++++++++++++++++++++++++----------------- + 1 file changed, 50 insertions(+), 34 deletions(-) + +--- a/drivers/net/usb/r8152.c ++++ b/drivers/net/usb/r8152.c +@@ -3443,59 +3443,76 @@ static void rtl_clear_bp(struct r8152 *t + ocp_write_word(tp, type, PLA_BP_BA, 0); + } + +-static int r8153_patch_request(struct r8152 *tp, bool request) ++static int rtl_phy_patch_request(struct r8152 *tp, bool request, bool wait) + { +- u16 data; ++ u16 data, check; + int i; + + data = ocp_reg_read(tp, OCP_PHY_PATCH_CMD); +- if (request) ++ if (request) { + data |= PATCH_REQUEST; +- else ++ check = 0; ++ } else { + data &= ~PATCH_REQUEST; ++ check = PATCH_READY; ++ } + ocp_reg_write(tp, OCP_PHY_PATCH_CMD, data); + +- for (i = 0; request && i < 5000; i++) { ++ for (i = 0; wait && i < 5000; i++) { ++ u32 ocp_data; ++ + usleep_range(1000, 2000); +- if (ocp_reg_read(tp, OCP_PHY_PATCH_STAT) & PATCH_READY) ++ ocp_data = ocp_reg_read(tp, OCP_PHY_PATCH_STAT); ++ if ((ocp_data & PATCH_READY) ^ check) + break; + } + +- if (request && !(ocp_reg_read(tp, OCP_PHY_PATCH_STAT) & PATCH_READY)) { +- netif_err(tp, drv, tp->netdev, "patch request fail\n"); +- r8153_patch_request(tp, false); ++ if (request && wait && ++ !(ocp_reg_read(tp, OCP_PHY_PATCH_STAT) & PATCH_READY)) { ++ dev_err(&tp->intf->dev, "PHY patch request fail\n"); ++ rtl_phy_patch_request(tp, false, false); + return -ETIME; + } else { + return 0; + } + } + +-static int r8153_pre_ram_code(struct r8152 *tp, u16 key_addr, u16 patch_key) ++static void rtl_patch_key_set(struct r8152 *tp, u16 key_addr, u16 patch_key) + { +- if (r8153_patch_request(tp, true)) { +- dev_err(&tp->intf->dev, "patch request fail\n"); +- return -ETIME; +- } ++ if (patch_key && key_addr) { ++ sram_write(tp, key_addr, patch_key); ++ sram_write(tp, SRAM_PHY_LOCK, PHY_PATCH_LOCK); ++ } else if (key_addr) { ++ u16 data; + +- sram_write(tp, key_addr, patch_key); +- sram_write(tp, SRAM_PHY_LOCK, PHY_PATCH_LOCK); ++ sram_write(tp, 0x0000, 0x0000); + +- return 0; ++ data = ocp_reg_read(tp, OCP_PHY_LOCK); ++ data &= ~PATCH_LOCK; ++ ocp_reg_write(tp, OCP_PHY_LOCK, data); ++ ++ sram_write(tp, key_addr, 0x0000); ++ } else { ++ WARN_ON_ONCE(1); ++ } + } + +-static int r8153_post_ram_code(struct r8152 *tp, u16 key_addr) ++static int ++rtl_pre_ram_code(struct r8152 *tp, u16 key_addr, u16 patch_key, bool wait) + { +- u16 data; ++ if (rtl_phy_patch_request(tp, true, wait)) ++ return -ETIME; + +- sram_write(tp, 0x0000, 0x0000); ++ rtl_patch_key_set(tp, key_addr, patch_key); + +- data = ocp_reg_read(tp, OCP_PHY_LOCK); +- data &= ~PATCH_LOCK; +- ocp_reg_write(tp, OCP_PHY_LOCK, data); ++ return 0; ++} + +- sram_write(tp, key_addr, 0x0000); ++static int rtl_post_ram_code(struct r8152 *tp, u16 key_addr, bool wait) ++{ ++ rtl_patch_key_set(tp, key_addr, 0); + +- r8153_patch_request(tp, false); ++ rtl_phy_patch_request(tp, false, wait); + + ocp_write_word(tp, MCU_TYPE_PLA, PLA_OCP_GPHY_BASE, tp->ocp_base); + +@@ -3980,7 +3997,7 @@ static void rtl8152_fw_mac_apply(struct + dev_dbg(&tp->intf->dev, "successfully applied %s\n", mac->info); + } + +-static void rtl8152_apply_firmware(struct r8152 *tp) ++static void rtl8152_apply_firmware(struct r8152 *tp, bool power_cut) + { + struct rtl_fw *rtl_fw = &tp->rtl_fw; + const struct firmware *fw; +@@ -4011,12 +4028,11 @@ static void rtl8152_apply_firmware(struc + case RTL_FW_PHY_START: + key = (struct fw_phy_patch_key *)block; + key_addr = __le16_to_cpu(key->key_reg); +- r8153_pre_ram_code(tp, key_addr, +- __le16_to_cpu(key->key_data)); ++ rtl_pre_ram_code(tp, key_addr, __le16_to_cpu(key->key_data), !power_cut); + break; + case RTL_FW_PHY_STOP: + WARN_ON(!key_addr); +- r8153_post_ram_code(tp, key_addr); ++ rtl_post_ram_code(tp, key_addr, !power_cut); + break; + case RTL_FW_PHY_NC: + rtl8152_fw_phy_nc_apply(tp, (struct fw_phy_nc *)block); +@@ -4221,7 +4237,7 @@ static void rtl8152_disable(struct r8152 + + static void r8152b_hw_phy_cfg(struct r8152 *tp) + { +- rtl8152_apply_firmware(tp); ++ rtl8152_apply_firmware(tp, false); + rtl_eee_enable(tp, tp->eee_en); + r8152_aldps_en(tp, true); + r8152b_enable_fc(tp); +@@ -4503,7 +4519,7 @@ static void r8153_hw_phy_cfg(struct r815 + /* disable EEE before updating the PHY parameters */ + rtl_eee_enable(tp, false); + +- rtl8152_apply_firmware(tp); ++ rtl8152_apply_firmware(tp, false); + + if (tp->version == RTL_VER_03) { + data = ocp_reg_read(tp, OCP_EEE_CFG); +@@ -4577,7 +4593,7 @@ static void r8153b_hw_phy_cfg(struct r81 + /* disable EEE before updating the PHY parameters */ + rtl_eee_enable(tp, false); + +- rtl8152_apply_firmware(tp); ++ rtl8152_apply_firmware(tp, false); + + r8153b_green_en(tp, test_bit(GREEN_ETHERNET, &tp->flags)); + +@@ -4618,7 +4634,7 @@ static void r8153b_hw_phy_cfg(struct r81 + ocp_write_word(tp, MCU_TYPE_PLA, PLA_PHY_PWR, ocp_data); + + /* Advnace EEE */ +- if (!r8153_patch_request(tp, true)) { ++ if (!rtl_phy_patch_request(tp, true, true)) { + data = ocp_reg_read(tp, OCP_POWER_CFG); + data |= EEE_CLKDIV_EN; + ocp_reg_write(tp, OCP_POWER_CFG, data); +@@ -4635,7 +4651,7 @@ static void r8153b_hw_phy_cfg(struct r81 + ocp_reg_write(tp, OCP_SYSCLK_CFG, clk_div_expo(5)); + tp->ups_info._250m_ckdiv = true; + +- r8153_patch_request(tp, false); ++ rtl_phy_patch_request(tp, false, true); + } + + if (tp->eee_en) diff --git a/target/linux/generic/backport-5.10/785-v5.12-r8152-adjust-the-flow-of-power-cut-for-RTL8153B.patch b/target/linux/generic/backport-5.10/785-v5.12-r8152-adjust-the-flow-of-power-cut-for-RTL8153B.patch new file mode 100644 index 000000000..9d12cb976 --- /dev/null +++ b/target/linux/generic/backport-5.10/785-v5.12-r8152-adjust-the-flow-of-power-cut-for-RTL8153B.patch @@ -0,0 +1,134 @@ +From 29a61d8564ad3439d03c7ec135016a4e70072af1 Mon Sep 17 00:00:00 2001 +From: Hayes Wang +Date: Wed, 3 Feb 2021 17:14:29 +0800 +Subject: [PATCH] r8152: adjust the flow of power cut for RTL8153B + +commit 80fd850b31f09263ad175b2f640d5c5c6f76ed41 upstream. + +For runtime resuming, the RTL8153B may be resumed from the state +of power cut, when enabling the feature of UPS. Then, the PHY +would be reset, so it is necessary to be initailized again. + +Besides, the USB_U1U2_TIMER also has to be set again, so I move +it from r8153b_init() to r8153b_hw_phy_cfg(). + +Signed-off-by: Hayes Wang +Signed-off-by: Jakub Kicinski +--- + drivers/net/usb/r8152.c | 68 ++++++++++++++++++++++++----------------- + 1 file changed, 40 insertions(+), 28 deletions(-) + +--- a/drivers/net/usb/r8152.c ++++ b/drivers/net/usb/r8152.c +@@ -1371,6 +1371,10 @@ void write_mii_word(struct net_device *n + static int + r8152_submit_rx(struct r8152 *tp, struct rx_agg *agg, gfp_t mem_flags); + ++static int ++rtl8152_set_speed(struct r8152 *tp, u8 autoneg, u32 speed, u8 duplex, ++ u32 advertising); ++ + static int rtl8152_set_mac_address(struct net_device *netdev, void *p) + { + struct r8152 *tp = netdev_priv(netdev); +@@ -3182,8 +3186,6 @@ static void r8153b_ups_en(struct r8152 * + ocp_data |= BIT(0); + ocp_write_byte(tp, MCU_TYPE_USB, 0xcfff, ocp_data); + } else { +- u16 data; +- + ocp_data &= ~(UPS_EN | USP_PREWAKE); + ocp_write_byte(tp, MCU_TYPE_USB, USB_POWER_CUT, ocp_data); + +@@ -3191,31 +3193,20 @@ static void r8153b_ups_en(struct r8152 * + ocp_data &= ~BIT(0); + ocp_write_byte(tp, MCU_TYPE_USB, 0xcfff, ocp_data); + +- ocp_data = ocp_read_word(tp, MCU_TYPE_USB, USB_MISC_0); +- ocp_data &= ~PCUT_STATUS; +- ocp_write_word(tp, MCU_TYPE_USB, USB_MISC_0, ocp_data); ++ if (ocp_read_word(tp, MCU_TYPE_USB, USB_MISC_0) & PCUT_STATUS) { ++ int i; + +- data = r8153_phy_status(tp, 0); ++ for (i = 0; i < 500; i++) { ++ if (ocp_read_word(tp, MCU_TYPE_PLA, PLA_BOOT_CTRL) & ++ AUTOLOAD_DONE) ++ break; ++ msleep(20); ++ } + +- switch (data) { +- case PHY_STAT_PWRDN: +- case PHY_STAT_EXT_INIT: +- r8153b_green_en(tp, +- test_bit(GREEN_ETHERNET, &tp->flags)); +- +- data = r8152_mdio_read(tp, MII_BMCR); +- data &= ~BMCR_PDOWN; +- data |= BMCR_RESET; +- r8152_mdio_write(tp, MII_BMCR, data); ++ tp->rtl_ops.hw_phy_cfg(tp); + +- data = r8153_phy_status(tp, PHY_STAT_LAN_ON); +- fallthrough; +- +- default: +- if (data != PHY_STAT_LAN_ON) +- netif_warn(tp, link, tp->netdev, +- "PHY not ready"); +- break; ++ rtl8152_set_speed(tp, tp->autoneg, tp->speed, ++ tp->duplex, tp->advertising); + } + } + } +@@ -4587,13 +4578,37 @@ static void r8153b_hw_phy_cfg(struct r81 + u32 ocp_data; + u16 data; + ++ ocp_data = ocp_read_word(tp, MCU_TYPE_USB, USB_MISC_0); ++ if (ocp_data & PCUT_STATUS) { ++ ocp_data &= ~PCUT_STATUS; ++ ocp_write_word(tp, MCU_TYPE_USB, USB_MISC_0, ocp_data); ++ } ++ + /* disable ALDPS before updating the PHY parameters */ + r8153_aldps_en(tp, false); + + /* disable EEE before updating the PHY parameters */ + rtl_eee_enable(tp, false); + +- rtl8152_apply_firmware(tp, false); ++ /* U1/U2/L1 idle timer. 500 us */ ++ ocp_write_word(tp, MCU_TYPE_USB, USB_U1U2_TIMER, 500); ++ ++ data = r8153_phy_status(tp, 0); ++ ++ switch (data) { ++ case PHY_STAT_PWRDN: ++ case PHY_STAT_EXT_INIT: ++ rtl8152_apply_firmware(tp, true); ++ ++ data = r8152_mdio_read(tp, MII_BMCR); ++ data &= ~BMCR_PDOWN; ++ r8152_mdio_write(tp, MII_BMCR, data); ++ break; ++ case PHY_STAT_LAN_ON: ++ default: ++ rtl8152_apply_firmware(tp, false); ++ break; ++ } + + r8153b_green_en(tp, test_bit(GREEN_ETHERNET, &tp->flags)); + +@@ -5522,9 +5537,6 @@ static void r8153b_init(struct r8152 *tp + /* MSC timer = 0xfff * 8ms = 32760 ms */ + ocp_write_word(tp, MCU_TYPE_USB, USB_MSC_TIMER, 0x0fff); + +- /* U1/U2/L1 idle timer. 500 us */ +- ocp_write_word(tp, MCU_TYPE_USB, USB_U1U2_TIMER, 500); +- + r8153b_power_cut_en(tp, false); + r8153b_ups_en(tp, false); + r8153_queue_wake(tp, false); diff --git a/target/linux/generic/backport-5.10/786-v5.12-r8152-enable-U1-U2-for-USB_SPEED_SUPER.patch b/target/linux/generic/backport-5.10/786-v5.12-r8152-enable-U1-U2-for-USB_SPEED_SUPER.patch new file mode 100644 index 000000000..f97deaa7d --- /dev/null +++ b/target/linux/generic/backport-5.10/786-v5.12-r8152-enable-U1-U2-for-USB_SPEED_SUPER.patch @@ -0,0 +1,47 @@ +From 69b4339c0b9f3edc6a8f681f05efaaf4add1bb0e Mon Sep 17 00:00:00 2001 +From: Hayes Wang +Date: Fri, 19 Feb 2021 17:04:40 +0800 +Subject: [PATCH] r8152: enable U1/U2 for USB_SPEED_SUPER + +commit 7a0ae61acde2cebd69665837170405eced86a6c7 upstream. + +U1/U2 shoued be enabled for USB 3.0 or later. The USB 2.0 doesn't +support it. + +Signed-off-by: Hayes Wang +Signed-off-by: Jakub Kicinski +--- + drivers/net/usb/r8152.c | 7 ++++--- + 1 file changed, 4 insertions(+), 3 deletions(-) + +--- a/drivers/net/usb/r8152.c ++++ b/drivers/net/usb/r8152.c +@@ -3335,7 +3335,7 @@ static void rtl8153b_runtime_enable(stru + r8153b_ups_en(tp, false); + r8153_queue_wake(tp, false); + rtl_runtime_suspend_enable(tp, false); +- if (tp->udev->speed != USB_SPEED_HIGH) ++ if (tp->udev->speed >= USB_SPEED_SUPER) + r8153b_u1u2en(tp, true); + } + } +@@ -5028,7 +5028,7 @@ static void rtl8153b_up(struct r8152 *tp + + r8153_aldps_en(tp, true); + +- if (tp->udev->speed != USB_SPEED_HIGH) ++ if (tp->udev->speed >= USB_SPEED_SUPER) + r8153b_u1u2en(tp, true); + } + +@@ -5550,8 +5550,9 @@ static void r8153b_init(struct r8152 *tp + ocp_data |= POLL_LINK_CHG; + ocp_write_word(tp, MCU_TYPE_PLA, PLA_EXTRA_STATUS, ocp_data); + +- if (tp->udev->speed != USB_SPEED_HIGH) ++ if (tp->udev->speed >= USB_SPEED_SUPER) + r8153b_u1u2en(tp, true); ++ + usb_enable_lpm(tp->udev); + + /* MAC clock speed down */ diff --git a/target/linux/generic/backport-5.10/787-v5.12-r8152-check-if-the-pointer-of-the-function-exists.patch b/target/linux/generic/backport-5.10/787-v5.12-r8152-check-if-the-pointer-of-the-function-exists.patch new file mode 100644 index 000000000..4f754bc69 --- /dev/null +++ b/target/linux/generic/backport-5.10/787-v5.12-r8152-check-if-the-pointer-of-the-function-exists.patch @@ -0,0 +1,51 @@ +From e78b75f5be204a0a235da995d01c778dc282bb42 Mon Sep 17 00:00:00 2001 +From: Hayes Wang +Date: Fri, 19 Feb 2021 17:04:41 +0800 +Subject: [PATCH] r8152: check if the pointer of the function exists + +commit c79515e47935c747282c6ed2ee5b2ef039756eeb upstream. + +Return error code if autosuspend_en, eee_get, or eee_set don't exist. + +Signed-off-by: Hayes Wang +Signed-off-by: Jakub Kicinski +--- + drivers/net/usb/r8152.c | 13 +++++++++++++ + 1 file changed, 13 insertions(+) + +--- a/drivers/net/usb/r8152.c ++++ b/drivers/net/usb/r8152.c +@@ -5735,6 +5735,9 @@ static int rtl8152_runtime_suspend(struc + struct net_device *netdev = tp->netdev; + int ret = 0; + ++ if (!tp->rtl_ops.autosuspend_en) ++ return -EBUSY; ++ + set_bit(SELECTIVE_SUSPEND, &tp->flags); + smp_mb__after_atomic(); + +@@ -6134,6 +6137,11 @@ rtl_ethtool_get_eee(struct net_device *n + struct r8152 *tp = netdev_priv(net); + int ret; + ++ if (!tp->rtl_ops.eee_get) { ++ ret = -EOPNOTSUPP; ++ goto out; ++ } ++ + ret = usb_autopm_get_interface(tp->intf); + if (ret < 0) + goto out; +@@ -6156,6 +6164,11 @@ rtl_ethtool_set_eee(struct net_device *n + struct r8152 *tp = netdev_priv(net); + int ret; + ++ if (!tp->rtl_ops.eee_set) { ++ ret = -EOPNOTSUPP; ++ goto out; ++ } ++ + ret = usb_autopm_get_interface(tp->intf); + if (ret < 0) + goto out; diff --git a/target/linux/generic/backport-5.10/788-v5.12-r8152-replace-netif_err-with-dev_err.patch b/target/linux/generic/backport-5.10/788-v5.12-r8152-replace-netif_err-with-dev_err.patch new file mode 100644 index 000000000..9136611e0 --- /dev/null +++ b/target/linux/generic/backport-5.10/788-v5.12-r8152-replace-netif_err-with-dev_err.patch @@ -0,0 +1,36 @@ +From 38e44c7926512cff0b2809dc329de2a8e769e523 Mon Sep 17 00:00:00 2001 +From: Hayes Wang +Date: Fri, 19 Feb 2021 17:04:42 +0800 +Subject: [PATCH] r8152: replace netif_err with dev_err + +commit 156c3207611262266f0eea589ac3f00c5657320e upstream. + +Some messages are before calling register_netdev(), so replace +netif_err() with dev_err(). + +Signed-off-by: Hayes Wang +Signed-off-by: Jakub Kicinski +--- + drivers/net/usb/r8152.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +--- a/drivers/net/usb/r8152.c ++++ b/drivers/net/usb/r8152.c +@@ -6571,7 +6571,7 @@ static int rtl_ops_init(struct r8152 *tp + + default: + ret = -ENODEV; +- netif_err(tp, probe, tp->netdev, "Unknown Device\n"); ++ dev_err(&tp->intf->dev, "Unknown Device\n"); + break; + } + +@@ -6828,7 +6828,7 @@ static int rtl8152_probe(struct usb_inte + + ret = register_netdev(netdev); + if (ret != 0) { +- netif_err(tp, probe, netdev, "couldn't register the device\n"); ++ dev_err(&intf->dev, "couldn't register the device\n"); + goto out1; + } + diff --git a/target/linux/generic/backport-5.10/789-v5.12-r8152-spilt-rtl_set_eee_plus-and-r8153b_green_en.patch b/target/linux/generic/backport-5.10/789-v5.12-r8152-spilt-rtl_set_eee_plus-and-r8153b_green_en.patch new file mode 100644 index 000000000..0467e06a3 --- /dev/null +++ b/target/linux/generic/backport-5.10/789-v5.12-r8152-spilt-rtl_set_eee_plus-and-r8153b_green_en.patch @@ -0,0 +1,89 @@ +From 260814de2d6cb958767785ffcb2e76915d1be32b Mon Sep 17 00:00:00 2001 +From: Hayes Wang +Date: Fri, 19 Feb 2021 17:04:43 +0800 +Subject: [PATCH] r8152: spilt rtl_set_eee_plus and r8153b_green_en + +commit 40fa7568ac230446d888b7ad402cff9e20fe3ad5 upstream. + +Add rtl_eee_plus_en() and rtl_green_en(). + +Signed-off-by: Hayes Wang +Signed-off-by: Jakub Kicinski +--- + drivers/net/usb/r8152.c | 43 ++++++++++++++++++++++++++--------------- + 1 file changed, 27 insertions(+), 16 deletions(-) + +--- a/drivers/net/usb/r8152.c ++++ b/drivers/net/usb/r8152.c +@@ -2632,21 +2632,24 @@ static inline u8 rtl8152_get_speed(struc + return ocp_read_byte(tp, MCU_TYPE_PLA, PLA_PHYSTATUS); + } + +-static void rtl_set_eee_plus(struct r8152 *tp) ++static void rtl_eee_plus_en(struct r8152 *tp, bool enable) + { + u32 ocp_data; +- u8 speed; + +- speed = rtl8152_get_speed(tp); +- if (speed & _10bps) { +- ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_EEEP_CR); ++ ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_EEEP_CR); ++ if (enable) + ocp_data |= EEEP_CR_EEEP_TX; +- ocp_write_word(tp, MCU_TYPE_PLA, PLA_EEEP_CR, ocp_data); +- } else { +- ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_EEEP_CR); ++ else + ocp_data &= ~EEEP_CR_EEEP_TX; +- ocp_write_word(tp, MCU_TYPE_PLA, PLA_EEEP_CR, ocp_data); +- } ++ ocp_write_word(tp, MCU_TYPE_PLA, PLA_EEEP_CR, ocp_data); ++} ++ ++static void rtl_set_eee_plus(struct r8152 *tp) ++{ ++ if (rtl8152_get_speed(tp) & _10bps) ++ rtl_eee_plus_en(tp, true); ++ else ++ rtl_eee_plus_en(tp, false); + } + + static void rxdy_gated_en(struct r8152 *tp, bool enable) +@@ -3127,10 +3130,22 @@ static void r8153b_ups_flags(struct r815 + ocp_write_dword(tp, MCU_TYPE_USB, USB_UPS_FLAGS, ups_flags); + } + +-static void r8153b_green_en(struct r8152 *tp, bool enable) ++static void rtl_green_en(struct r8152 *tp, bool enable) + { + u16 data; + ++ data = sram_read(tp, SRAM_GREEN_CFG); ++ if (enable) ++ data |= GREEN_ETH_EN; ++ else ++ data &= ~GREEN_ETH_EN; ++ sram_write(tp, SRAM_GREEN_CFG, data); ++ ++ tp->ups_info.green = enable; ++} ++ ++static void r8153b_green_en(struct r8152 *tp, bool enable) ++{ + if (enable) { + sram_write(tp, 0x8045, 0); /* 10M abiq&ldvbias */ + sram_write(tp, 0x804d, 0x1222); /* 100M short abiq&ldvbias */ +@@ -3141,11 +3156,7 @@ static void r8153b_green_en(struct r8152 + sram_write(tp, 0x805d, 0x2444); /* 1000M short abiq&ldvbias */ + } + +- data = sram_read(tp, SRAM_GREEN_CFG); +- data |= GREEN_ETH_EN; +- sram_write(tp, SRAM_GREEN_CFG, data); +- +- tp->ups_info.green = enable; ++ rtl_green_en(tp, true); + } + + static u16 r8153_phy_status(struct r8152 *tp, u16 desired) diff --git a/target/linux/generic/backport-5.10/790-v5.13-r8152-set-inter-fram-gap-time-depending-on-speed.patch b/target/linux/generic/backport-5.10/790-v5.13-r8152-set-inter-fram-gap-time-depending-on-speed.patch new file mode 100644 index 000000000..15ba2bf29 --- /dev/null +++ b/target/linux/generic/backport-5.10/790-v5.13-r8152-set-inter-fram-gap-time-depending-on-speed.patch @@ -0,0 +1,75 @@ +From f1bbbb260a8016373adf239c716d2da90e6ced0b Mon Sep 17 00:00:00 2001 +From: Hayes Wang +Date: Fri, 16 Apr 2021 16:04:32 +0800 +Subject: [PATCH] r8152: set inter fram gap time depending on speed + +commit 5133bcc7481528e36fff0a3b056601efb704fb32 upstream. + +Set the maximum inter frame gap time (144ns) for speed 10M/half and +100M/half. It improves the performance for those speeds. And, there +is no effect for the other speeds. + +For 10M/half and 100M/half, the fast inter frame gap time let the +device couldn't use the feature of the aggregation effectively, +because the transfer would be completed fastly. Therefore, use the +maximum value to improve the effect of the aggregation. However, you +may not feel the improvement for fast CPUs, because they compensate +for the effect of the aggregation. + +Signed-off-by: Hayes Wang +Signed-off-by: David S. Miller +--- + drivers/net/usb/r8152.c | 28 ++++++++++++++++++++++++++++ + 1 file changed, 28 insertions(+) + +--- a/drivers/net/usb/r8152.c ++++ b/drivers/net/usb/r8152.c +@@ -249,6 +249,9 @@ + + /* PLA_TCR1 */ + #define VERSION_MASK 0x7cf0 ++#define IFG_MASK (BIT(3) | BIT(9) | BIT(8)) ++#define IFG_144NS BIT(9) ++#define IFG_96NS (BIT(9) | BIT(8)) + + /* PLA_MTPS */ + #define MTPS_JUMBO (12 * 1024 / 64) +@@ -2747,6 +2750,29 @@ static int rtl_stop_rx(struct r8152 *tp) + return 0; + } + ++static void rtl_set_ifg(struct r8152 *tp, u16 speed) ++{ ++ u32 ocp_data; ++ ++ ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_TCR1); ++ ocp_data &= ~IFG_MASK; ++ if ((speed & (_10bps | _100bps)) && !(speed & FULL_DUP)) { ++ ocp_data |= IFG_144NS; ++ ocp_write_word(tp, MCU_TYPE_PLA, PLA_TCR1, ocp_data); ++ ++ ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_MAC_PWR_CTRL4); ++ ocp_data &= ~TX10MIDLE_EN; ++ ocp_write_word(tp, MCU_TYPE_PLA, PLA_MAC_PWR_CTRL4, ocp_data); ++ } else { ++ ocp_data |= IFG_96NS; ++ ocp_write_word(tp, MCU_TYPE_PLA, PLA_TCR1, ocp_data); ++ ++ ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_MAC_PWR_CTRL4); ++ ocp_data |= TX10MIDLE_EN; ++ ocp_write_word(tp, MCU_TYPE_PLA, PLA_MAC_PWR_CTRL4, ocp_data); ++ } ++} ++ + static inline void r8153b_rx_agg_chg_indicate(struct r8152 *tp) + { + ocp_write_byte(tp, MCU_TYPE_USB, USB_UPT_RXDMA_OWN, +@@ -2850,6 +2876,8 @@ static int rtl8153_enable(struct r8152 * + r8153_set_rx_early_timeout(tp); + r8153_set_rx_early_size(tp); + ++ rtl_set_ifg(tp, rtl8152_get_speed(tp)); ++ + if (tp->version == RTL_VER_09) { + u32 ocp_data; + diff --git a/target/linux/generic/backport-5.10/791-v5.13-r8152-adjust-rtl8152_check_firmware-function.patch b/target/linux/generic/backport-5.10/791-v5.13-r8152-adjust-rtl8152_check_firmware-function.patch new file mode 100644 index 000000000..f07c160c7 --- /dev/null +++ b/target/linux/generic/backport-5.10/791-v5.13-r8152-adjust-rtl8152_check_firmware-function.patch @@ -0,0 +1,152 @@ +From f10c9edf47d3fa240d965e151a48c670f5035b73 Mon Sep 17 00:00:00 2001 +From: Hayes Wang +Date: Fri, 16 Apr 2021 16:04:33 +0800 +Subject: [PATCH] r8152: adjust rtl8152_check_firmware function + +commit a8a7be178e81a3d4b6972cbeb0ccd091ca2f9f89 upstream. + +Use bits operations to record and check the firmware. + +Signed-off-by: Hayes Wang +Signed-off-by: David S. Miller +--- + drivers/net/usb/r8152.c | 51 +++++++++++++++++++++++------------------ + 1 file changed, 29 insertions(+), 22 deletions(-) + +--- a/drivers/net/usb/r8152.c ++++ b/drivers/net/usb/r8152.c +@@ -874,6 +874,14 @@ struct fw_header { + struct fw_block blocks[]; + } __packed; + ++enum rtl8152_fw_flags { ++ FW_FLAGS_USB = 0, ++ FW_FLAGS_PLA, ++ FW_FLAGS_START, ++ FW_FLAGS_STOP, ++ FW_FLAGS_NC, ++}; ++ + /** + * struct fw_mac - a firmware block used by RTL_FW_PLA and RTL_FW_USB. + * The layout of the firmware block is: +@@ -3800,10 +3808,7 @@ static long rtl8152_check_firmware(struc + { + const struct firmware *fw = rtl_fw->fw; + struct fw_header *fw_hdr = (struct fw_header *)fw->data; +- struct fw_mac *pla = NULL, *usb = NULL; +- struct fw_phy_patch_key *start = NULL; +- struct fw_phy_nc *phy_nc = NULL; +- struct fw_block *stop = NULL; ++ unsigned long fw_flags = 0; + long ret = -EFAULT; + int i; + +@@ -3832,50 +3837,52 @@ static long rtl8152_check_firmware(struc + goto fail; + goto fw_end; + case RTL_FW_PLA: +- if (pla) { ++ if (test_bit(FW_FLAGS_PLA, &fw_flags)) { + dev_err(&tp->intf->dev, + "multiple PLA firmware encountered"); + goto fail; + } + +- pla = (struct fw_mac *)block; +- if (!rtl8152_is_fw_mac_ok(tp, pla)) { ++ if (!rtl8152_is_fw_mac_ok(tp, (struct fw_mac *)block)) { + dev_err(&tp->intf->dev, + "check PLA firmware failed\n"); + goto fail; + } ++ __set_bit(FW_FLAGS_PLA, &fw_flags); + break; + case RTL_FW_USB: +- if (usb) { ++ if (test_bit(FW_FLAGS_USB, &fw_flags)) { + dev_err(&tp->intf->dev, + "multiple USB firmware encountered"); + goto fail; + } + +- usb = (struct fw_mac *)block; +- if (!rtl8152_is_fw_mac_ok(tp, usb)) { ++ if (!rtl8152_is_fw_mac_ok(tp, (struct fw_mac *)block)) { + dev_err(&tp->intf->dev, + "check USB firmware failed\n"); + goto fail; + } ++ __set_bit(FW_FLAGS_USB, &fw_flags); + break; + case RTL_FW_PHY_START: +- if (start || phy_nc || stop) { ++ if (test_bit(FW_FLAGS_START, &fw_flags) || ++ test_bit(FW_FLAGS_NC, &fw_flags) || ++ test_bit(FW_FLAGS_STOP, &fw_flags)) { + dev_err(&tp->intf->dev, + "check PHY_START fail\n"); + goto fail; + } + +- if (__le32_to_cpu(block->length) != sizeof(*start)) { ++ if (__le32_to_cpu(block->length) != sizeof(struct fw_phy_patch_key)) { + dev_err(&tp->intf->dev, + "Invalid length for PHY_START\n"); + goto fail; + } +- +- start = (struct fw_phy_patch_key *)block; ++ __set_bit(FW_FLAGS_START, &fw_flags); + break; + case RTL_FW_PHY_STOP: +- if (stop || !start) { ++ if (test_bit(FW_FLAGS_STOP, &fw_flags) || ++ !test_bit(FW_FLAGS_START, &fw_flags)) { + dev_err(&tp->intf->dev, + "Check PHY_STOP fail\n"); + goto fail; +@@ -3886,28 +3893,28 @@ static long rtl8152_check_firmware(struc + "Invalid length for PHY_STOP\n"); + goto fail; + } +- +- stop = block; ++ __set_bit(FW_FLAGS_STOP, &fw_flags); + break; + case RTL_FW_PHY_NC: +- if (!start || stop) { ++ if (!test_bit(FW_FLAGS_START, &fw_flags) || ++ test_bit(FW_FLAGS_STOP, &fw_flags)) { + dev_err(&tp->intf->dev, + "check PHY_NC fail\n"); + goto fail; + } + +- if (phy_nc) { ++ if (test_bit(FW_FLAGS_NC, &fw_flags)) { + dev_err(&tp->intf->dev, + "multiple PHY NC encountered\n"); + goto fail; + } + +- phy_nc = (struct fw_phy_nc *)block; +- if (!rtl8152_is_fw_phy_nc_ok(tp, phy_nc)) { ++ if (!rtl8152_is_fw_phy_nc_ok(tp, (struct fw_phy_nc *)block)) { + dev_err(&tp->intf->dev, + "check PHY NC firmware failed\n"); + goto fail; + } ++ __set_bit(FW_FLAGS_NC, &fw_flags); + + break; + default: +@@ -3921,7 +3928,7 @@ static long rtl8152_check_firmware(struc + } + + fw_end: +- if ((phy_nc || start) && !stop) { ++ if (test_bit(FW_FLAGS_START, &fw_flags) && !test_bit(FW_FLAGS_STOP, &fw_flags)) { + dev_err(&tp->intf->dev, "without PHY_STOP\n"); + goto fail; + } diff --git a/target/linux/generic/backport-5.10/792-v5.13-r8152-add-help-function-to-change-mtu.patch b/target/linux/generic/backport-5.10/792-v5.13-r8152-add-help-function-to-change-mtu.patch new file mode 100644 index 000000000..63da9adfd --- /dev/null +++ b/target/linux/generic/backport-5.10/792-v5.13-r8152-add-help-function-to-change-mtu.patch @@ -0,0 +1,157 @@ +From f010a7d51cbb42bdb956f0a28b8868b15d7a3816 Mon Sep 17 00:00:00 2001 +From: Hayes Wang +Date: Fri, 16 Apr 2021 16:04:34 +0800 +Subject: [PATCH] r8152: add help function to change mtu + +commit 67ce1a806f164e59a074fea8809725d3411eaa20 upstream. + +The different chips may have different requests when changing mtu. +Therefore, add a new help function of rtl_ops to change mtu. Besides, +reset the tx/rx after changing mtu. + +Additionally, add mtu_to_size() and size_to_mtu() macros to simplify +the code. + +Signed-off-by: Hayes Wang +Signed-off-by: David S. Miller +--- + drivers/net/usb/r8152.c | 53 ++++++++++++++++++++++++----------------- + 1 file changed, 31 insertions(+), 22 deletions(-) + +--- a/drivers/net/usb/r8152.c ++++ b/drivers/net/usb/r8152.c +@@ -657,15 +657,13 @@ enum rtl_register_content { + + #define INTR_LINK 0x0004 + +-#define RTL8153_MAX_PACKET 9216 /* 9K */ +-#define RTL8153_MAX_MTU (RTL8153_MAX_PACKET - VLAN_ETH_HLEN - \ +- ETH_FCS_LEN) + #define RTL8152_RMS (VLAN_ETH_FRAME_LEN + ETH_FCS_LEN) + #define RTL8153_RMS RTL8153_MAX_PACKET + #define RTL8152_TX_TIMEOUT (5 * HZ) + #define RTL8152_NAPI_WEIGHT 64 +-#define rx_reserved_size(x) ((x) + VLAN_ETH_HLEN + ETH_FCS_LEN + \ +- sizeof(struct rx_desc) + RX_ALIGN) ++#define mtu_to_size(m) ((m) + VLAN_ETH_HLEN + ETH_FCS_LEN) ++#define size_to_mtu(s) ((s) - VLAN_ETH_HLEN - ETH_FCS_LEN) ++#define rx_reserved_size(x) (mtu_to_size(x) + sizeof(struct rx_desc) + RX_ALIGN) + + /* rtl8152 flags */ + enum rtl8152_flags { +@@ -795,6 +793,7 @@ struct r8152 { + bool (*in_nway)(struct r8152 *tp); + void (*hw_phy_cfg)(struct r8152 *tp); + void (*autosuspend_en)(struct r8152 *tp, bool enable); ++ void (*change_mtu)(struct r8152 *tp); + } rtl_ops; + + struct ups_info { +@@ -1021,8 +1020,7 @@ enum tx_csum_stat { + static const int multicast_filter_limit = 32; + static unsigned int agg_buf_sz = 16384; + +-#define RTL_LIMITED_TSO_SIZE (agg_buf_sz - sizeof(struct tx_desc) - \ +- VLAN_ETH_HLEN - ETH_FCS_LEN) ++#define RTL_LIMITED_TSO_SIZE (size_to_mtu(agg_buf_sz) - sizeof(struct tx_desc)) + + static + int get_registers(struct r8152 *tp, u16 value, u16 index, u16 size, void *data) +@@ -2632,10 +2630,7 @@ static void rtl8152_nic_reset(struct r81 + + static void set_tx_qlen(struct r8152 *tp) + { +- struct net_device *netdev = tp->netdev; +- +- tp->tx_qlen = agg_buf_sz / (netdev->mtu + VLAN_ETH_HLEN + ETH_FCS_LEN + +- sizeof(struct tx_desc)); ++ tp->tx_qlen = agg_buf_sz / (mtu_to_size(tp->netdev->mtu) + sizeof(struct tx_desc)); + } + + static inline u8 rtl8152_get_speed(struct r8152 *tp) +@@ -4724,6 +4719,12 @@ static void r8153b_hw_phy_cfg(struct r81 + set_bit(PHY_RESET, &tp->flags); + } + ++static void rtl8153_change_mtu(struct r8152 *tp) ++{ ++ ocp_write_word(tp, MCU_TYPE_PLA, PLA_RMS, mtu_to_size(tp->netdev->mtu)); ++ ocp_write_byte(tp, MCU_TYPE_PLA, PLA_MTPS, MTPS_JUMBO); ++} ++ + static void r8153_first_init(struct r8152 *tp) + { + u32 ocp_data; +@@ -4756,9 +4757,7 @@ static void r8153_first_init(struct r815 + + rtl_rx_vlan_en(tp, tp->netdev->features & NETIF_F_HW_VLAN_CTAG_RX); + +- ocp_data = tp->netdev->mtu + VLAN_ETH_HLEN + ETH_FCS_LEN; +- ocp_write_word(tp, MCU_TYPE_PLA, PLA_RMS, ocp_data); +- ocp_write_byte(tp, MCU_TYPE_PLA, PLA_MTPS, MTPS_JUMBO); ++ rtl8153_change_mtu(tp); + + ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_TCR0); + ocp_data |= TCR0_AUTO_FIFO; +@@ -4793,8 +4792,7 @@ static void r8153_enter_oob(struct r8152 + + wait_oob_link_list_ready(tp); + +- ocp_data = tp->netdev->mtu + VLAN_ETH_HLEN + ETH_FCS_LEN; +- ocp_write_word(tp, MCU_TYPE_PLA, PLA_RMS, ocp_data); ++ ocp_write_word(tp, MCU_TYPE_PLA, PLA_RMS, mtu_to_size(tp->netdev->mtu)); + + switch (tp->version) { + case RTL_VER_03: +@@ -6495,12 +6493,21 @@ static int rtl8152_change_mtu(struct net + dev->mtu = new_mtu; + + if (netif_running(dev)) { +- u32 rms = new_mtu + VLAN_ETH_HLEN + ETH_FCS_LEN; +- +- ocp_write_word(tp, MCU_TYPE_PLA, PLA_RMS, rms); ++ if (tp->rtl_ops.change_mtu) ++ tp->rtl_ops.change_mtu(tp); + +- if (netif_carrier_ok(dev)) +- r8153_set_rx_early_size(tp); ++ if (netif_carrier_ok(dev)) { ++ netif_stop_queue(dev); ++ napi_disable(&tp->napi); ++ tasklet_disable(&tp->tx_tl); ++ tp->rtl_ops.disable(tp); ++ tp->rtl_ops.enable(tp); ++ rtl_start_rx(tp); ++ tasklet_enable(&tp->tx_tl); ++ napi_enable(&tp->napi); ++ rtl8152_set_rx_mode(dev); ++ netif_wake_queue(dev); ++ } + } + + mutex_unlock(&tp->control); +@@ -6589,6 +6596,7 @@ static int rtl_ops_init(struct r8152 *tp + ops->in_nway = rtl8153_in_nway; + ops->hw_phy_cfg = r8153_hw_phy_cfg; + ops->autosuspend_en = rtl8153_runtime_enable; ++ ops->change_mtu = rtl8153_change_mtu; + if (tp->udev->speed < USB_SPEED_SUPER) + tp->rx_buf_sz = 16 * 1024; + else +@@ -6610,6 +6618,7 @@ static int rtl_ops_init(struct r8152 *tp + ops->in_nway = rtl8153_in_nway; + ops->hw_phy_cfg = r8153b_hw_phy_cfg; + ops->autosuspend_en = rtl8153b_runtime_enable; ++ ops->change_mtu = rtl8153_change_mtu; + tp->rx_buf_sz = 32 * 1024; + tp->eee_en = true; + tp->eee_adv = MDIO_EEE_1000T | MDIO_EEE_100TX; +@@ -6830,7 +6839,7 @@ static int rtl8152_probe(struct usb_inte + netdev->max_mtu = ETH_DATA_LEN; + break; + default: +- netdev->max_mtu = RTL8153_MAX_MTU; ++ netdev->max_mtu = size_to_mtu(9 * 1024); + break; + } + diff --git a/target/linux/generic/backport-5.10/793-v5.13-r8152-support-new-chips.patch b/target/linux/generic/backport-5.10/793-v5.13-r8152-support-new-chips.patch new file mode 100644 index 000000000..c53315a0d --- /dev/null +++ b/target/linux/generic/backport-5.10/793-v5.13-r8152-support-new-chips.patch @@ -0,0 +1,2886 @@ +From e7439e7fd384f55f55837f7e4866e74d8dca3827 Mon Sep 17 00:00:00 2001 +From: Hayes Wang +Date: Fri, 16 Apr 2021 16:04:35 +0800 +Subject: [PATCH] r8152: support new chips + +commit 195aae321c829dd1945900d75561e6aa79cce208 upstream. + +Support RTL8153C, RTL8153D, RTL8156A, and RTL8156B. The RTL8156A +and RTL8156B are the 2.5G ethernet. + +Signed-off-by: Hayes Wang +Signed-off-by: David S. Miller +--- + drivers/net/usb/r8152.c | 2634 +++++++++++++++++++++++++++++++++++---- + 1 file changed, 2359 insertions(+), 275 deletions(-) + +--- a/drivers/net/usb/r8152.c ++++ b/drivers/net/usb/r8152.c +@@ -43,10 +43,14 @@ + + #define PLA_IDR 0xc000 + #define PLA_RCR 0xc010 ++#define PLA_RCR1 0xc012 + #define PLA_RMS 0xc016 + #define PLA_RXFIFO_CTRL0 0xc0a0 ++#define PLA_RXFIFO_FULL 0xc0a2 + #define PLA_RXFIFO_CTRL1 0xc0a4 ++#define PLA_RX_FIFO_FULL 0xc0a6 + #define PLA_RXFIFO_CTRL2 0xc0a8 ++#define PLA_RX_FIFO_EMPTY 0xc0aa + #define PLA_DMY_REG0 0xc0b0 + #define PLA_FMC 0xc0b4 + #define PLA_CFG_WOL 0xc0b6 +@@ -63,6 +67,8 @@ + #define PLA_MACDBG_PRE 0xd38c /* RTL_VER_04 only */ + #define PLA_MACDBG_POST 0xd38e /* RTL_VER_04 only */ + #define PLA_EXTRA_STATUS 0xd398 ++#define PLA_GPHY_CTRL 0xd3ae ++#define PLA_POL_GPIO_CTRL 0xdc6a + #define PLA_EFUSE_DATA 0xdd00 + #define PLA_EFUSE_CMD 0xdd02 + #define PLA_LEDSEL 0xdd90 +@@ -72,6 +78,8 @@ + #define PLA_LWAKE_CTRL_REG 0xe007 + #define PLA_GPHY_INTR_IMR 0xe022 + #define PLA_EEE_CR 0xe040 ++#define PLA_EEE_TXTWSYS 0xe04c ++#define PLA_EEE_TXTWSYS_2P5G 0xe058 + #define PLA_EEEP_CR 0xe080 + #define PLA_MAC_PWR_CTRL 0xe0c0 + #define PLA_MAC_PWR_CTRL2 0xe0ca +@@ -82,6 +90,7 @@ + #define PLA_TCR1 0xe612 + #define PLA_MTPS 0xe615 + #define PLA_TXFIFO_CTRL 0xe618 ++#define PLA_TXFIFO_FULL 0xe61a + #define PLA_RSTTALLY 0xe800 + #define PLA_CR 0xe813 + #define PLA_CRWECR 0xe81c +@@ -98,6 +107,7 @@ + #define PLA_SFF_STS_7 0xe8de + #define PLA_PHYSTATUS 0xe908 + #define PLA_CONFIG6 0xe90a /* CONFIG6 */ ++#define PLA_USB_CFG 0xe952 + #define PLA_BP_BA 0xfc26 + #define PLA_BP_0 0xfc28 + #define PLA_BP_1 0xfc2a +@@ -112,6 +122,7 @@ + #define USB_USB2PHY 0xb41e + #define USB_SSPHYLINK1 0xb426 + #define USB_SSPHYLINK2 0xb428 ++#define USB_L1_CTRL 0xb45e + #define USB_U2P3_CTRL 0xb460 + #define USB_CSR_DUMMY1 0xb464 + #define USB_CSR_DUMMY2 0xb466 +@@ -122,7 +133,12 @@ + #define USB_FW_FIX_EN0 0xcfca + #define USB_FW_FIX_EN1 0xcfcc + #define USB_LPM_CONFIG 0xcfd8 ++#define USB_ECM_OPTION 0xcfee + #define USB_CSTMR 0xcfef /* RTL8153A */ ++#define USB_MISC_2 0xcfff ++#define USB_ECM_OP 0xd26b ++#define USB_GPHY_CTRL 0xd284 ++#define USB_SPEED_OPTION 0xd32a + #define USB_FW_CTRL 0xd334 /* RTL8153B */ + #define USB_FC_TIMER 0xd340 + #define USB_USB_CTRL 0xd406 +@@ -136,16 +152,20 @@ + #define USB_RX_EXTRA_AGGR_TMR 0xd432 /* RTL8153B */ + #define USB_TX_DMA 0xd434 + #define USB_UPT_RXDMA_OWN 0xd437 ++#define USB_UPHY3_MDCMDIO 0xd480 + #define USB_TOLERANCE 0xd490 + #define USB_LPM_CTRL 0xd41a + #define USB_BMU_RESET 0xd4b0 ++#define USB_BMU_CONFIG 0xd4b4 + #define USB_U1U2_TIMER 0xd4da + #define USB_FW_TASK 0xd4e8 /* RTL8153B */ ++#define USB_RX_AGGR_NUM 0xd4ee + #define USB_UPS_CTRL 0xd800 + #define USB_POWER_CUT 0xd80a + #define USB_MISC_0 0xd81a + #define USB_MISC_1 0xd81f + #define USB_AFE_CTRL2 0xd824 ++#define USB_UPHY_XTAL 0xd826 + #define USB_UPS_CFG 0xd842 + #define USB_UPS_FLAGS 0xd848 + #define USB_WDT1_CTRL 0xe404 +@@ -188,6 +208,9 @@ + #define OCP_EEE_ABLE 0xa5c4 + #define OCP_EEE_ADV 0xa5d0 + #define OCP_EEE_LPABLE 0xa5d2 ++#define OCP_10GBT_CTRL 0xa5d4 ++#define OCP_10GBT_STAT 0xa5d6 ++#define OCP_EEE_ADV2 0xa6d4 + #define OCP_PHY_STATE 0xa708 /* nway state for 8153 */ + #define OCP_PHY_PATCH_STAT 0xb800 + #define OCP_PHY_PATCH_CMD 0xb820 +@@ -199,6 +222,7 @@ + /* SRAM Register */ + #define SRAM_GREEN_CFG 0x8011 + #define SRAM_LPF_CFG 0x8012 ++#define SRAM_GPHY_FW_VER 0x801e + #define SRAM_10M_AMP1 0x8080 + #define SRAM_10M_AMP2 0x8082 + #define SRAM_IMPEDANCE 0x8084 +@@ -210,11 +234,19 @@ + #define RCR_AM 0x00000004 + #define RCR_AB 0x00000008 + #define RCR_ACPT_ALL (RCR_AAP | RCR_APM | RCR_AM | RCR_AB) ++#define SLOT_EN BIT(11) ++ ++/* PLA_RCR1 */ ++#define OUTER_VLAN BIT(7) ++#define INNER_VLAN BIT(6) + + /* PLA_RXFIFO_CTRL0 */ + #define RXFIFO_THR1_NORMAL 0x00080002 + #define RXFIFO_THR1_OOB 0x01800003 + ++/* PLA_RXFIFO_FULL */ ++#define RXFIFO_FULL_MASK 0xfff ++ + /* PLA_RXFIFO_CTRL1 */ + #define RXFIFO_THR2_FULL 0x00000060 + #define RXFIFO_THR2_HIGH 0x00000038 +@@ -285,6 +317,7 @@ + #define MCU_BORW_EN 0x4000 + + /* PLA_CPCR */ ++#define FLOW_CTRL_EN BIT(0) + #define CPCR_RX_VLAN 0x0040 + + /* PLA_CFG_WOL */ +@@ -310,6 +343,10 @@ + /* PLA_CONFIG6 */ + #define LANWAKE_CLR_EN BIT(0) + ++/* PLA_USB_CFG */ ++#define EN_XG_LIP BIT(1) ++#define EN_G_LIP BIT(2) ++ + /* PLA_CONFIG5 */ + #define BWF_EN 0x0040 + #define MWF_EN 0x0020 +@@ -333,6 +370,7 @@ + /* PLA_MAC_PWR_CTRL2 */ + #define EEE_SPDWN_RATIO 0x8007 + #define MAC_CLK_SPDWN_EN BIT(15) ++#define EEE_SPDWN_RATIO_MASK 0xff + + /* PLA_MAC_PWR_CTRL3 */ + #define PLA_MCU_SPDWN_EN BIT(14) +@@ -345,6 +383,7 @@ + #define PWRSAVE_SPDWN_EN 0x1000 + #define RXDV_SPDWN_EN 0x0800 + #define TX10MIDLE_EN 0x0100 ++#define IDLE_SPDWN_EN BIT(6) + #define TP100_SPDWN_EN 0x0020 + #define TP500_SPDWN_EN 0x0010 + #define TP1000_SPDWN_EN 0x0008 +@@ -385,6 +424,13 @@ + #define LINK_CHANGE_FLAG BIT(8) + #define POLL_LINK_CHG BIT(0) + ++/* PLA_GPHY_CTRL */ ++#define GPHY_FLASH BIT(1) ++ ++/* PLA_POL_GPIO_CTRL */ ++#define DACK_DET_EN BIT(15) ++#define POL_GPHY_PATCH BIT(4) ++ + /* USB_USB2PHY */ + #define USB2PHY_SUSPEND 0x0001 + #define USB2PHY_L1 0x0002 +@@ -433,6 +479,9 @@ + #define BMU_RESET_EP_IN 0x01 + #define BMU_RESET_EP_OUT 0x02 + ++/* USB_BMU_CONFIG */ ++#define ACT_ODMA BIT(1) ++ + /* USB_UPT_RXDMA_OWN */ + #define OWN_UPDATE BIT(0) + #define OWN_CLEAR BIT(1) +@@ -440,27 +489,52 @@ + /* USB_FW_TASK */ + #define FC_PATCH_TASK BIT(1) + ++/* USB_RX_AGGR_NUM */ ++#define RX_AGGR_NUM_MASK 0x1ff ++ + /* USB_UPS_CTRL */ + #define POWER_CUT 0x0100 + + /* USB_PM_CTRL_STATUS */ + #define RESUME_INDICATE 0x0001 + ++/* USB_ECM_OPTION */ ++#define BYPASS_MAC_RESET BIT(5) ++ + /* USB_CSTMR */ + #define FORCE_SUPER BIT(0) + ++/* USB_MISC_2 */ ++#define UPS_FORCE_PWR_DOWN BIT(0) ++ ++/* USB_ECM_OP */ ++#define EN_ALL_SPEED BIT(0) ++ ++/* USB_GPHY_CTRL */ ++#define GPHY_PATCH_DONE BIT(2) ++#define BYPASS_FLASH BIT(5) ++#define BACKUP_RESTRORE BIT(6) ++ ++/* USB_SPEED_OPTION */ ++#define RG_PWRDN_EN BIT(8) ++#define ALL_SPEED_OFF BIT(9) ++ + /* USB_FW_CTRL */ + #define FLOW_CTRL_PATCH_OPT BIT(1) ++#define AUTO_SPEEDUP BIT(3) ++#define FLOW_CTRL_PATCH_2 BIT(8) + + /* USB_FC_TIMER */ + #define CTRL_TIMER_EN BIT(15) + + /* USB_USB_CTRL */ ++#define CDC_ECM_EN BIT(3) + #define RX_AGG_DISABLE 0x0010 + #define RX_ZERO_EN 0x0080 + + /* USB_U2P3_CTRL */ + #define U2P3_ENABLE 0x0001 ++#define RX_DETECT8 BIT(3) + + /* USB_POWER_CUT */ + #define PWR_EN 0x0001 +@@ -496,8 +570,12 @@ + #define SEN_VAL_NORMAL 0xa000 + #define SEL_RXIDLE 0x0100 + ++/* USB_UPHY_XTAL */ ++#define OOBS_POLLING BIT(8) ++ + /* USB_UPS_CFG */ + #define SAW_CNT_1MS_MASK 0x0fff ++#define MID_REVERSE BIT(5) /* RTL8156A */ + + /* USB_UPS_FLAGS */ + #define UPS_FLAGS_R_TUNE BIT(0) +@@ -505,6 +583,7 @@ + #define UPS_FLAGS_250M_CKDIV BIT(2) + #define UPS_FLAGS_EN_ALDPS BIT(3) + #define UPS_FLAGS_CTAP_SHORT_DIS BIT(4) ++#define UPS_FLAGS_SPEED_MASK (0xf << 16) + #define ups_flags_speed(x) ((x) << 16) + #define UPS_FLAGS_EN_EEE BIT(20) + #define UPS_FLAGS_EN_500M_EEE BIT(21) +@@ -525,6 +604,8 @@ enum spd_duplex { + FORCE_10M_FULL, + FORCE_100M_HALF, + FORCE_100M_FULL, ++ FORCE_1000M_FULL, ++ NWAY_2500M_FULL, + }; + + /* OCP_ALDPS_CONFIG */ +@@ -589,6 +670,9 @@ enum spd_duplex { + #define EN_10M_CLKDIV BIT(11) + #define EN_10M_BGOFF 0x0080 + ++/* OCP_10GBT_CTRL */ ++#define RTL_ADV2_5G_F_R BIT(5) /* Advertise 2.5GBASE-T fast-retrain */ ++ + /* OCP_PHY_STATE */ + #define TXDIS_STATE 0x01 + #define ABD_STATE 0x02 +@@ -608,7 +692,8 @@ enum spd_duplex { + #define EN_EMI_L 0x0040 + + /* OCP_SYSCLK_CFG */ +-#define clk_div_expo(x) (min(x, 5) << 8) ++#define sysclk_div_expo(x) (min(x, 5) << 8) ++#define clk_div_expo(x) (min(x, 5) << 4) + + /* SRAM_GREEN_CFG */ + #define GREEN_ETH_EN BIT(15) +@@ -639,6 +724,11 @@ enum spd_duplex { + #define BP4_SUPER_ONLY 0x1578 /* RTL_VER_04 only */ + + enum rtl_register_content { ++ _2500bps = BIT(10), ++ _1250bps = BIT(9), ++ _500bps = BIT(8), ++ _tx_flow = BIT(6), ++ _rx_flow = BIT(5), + _1000bps = 0x10, + _100bps = 0x08, + _10bps = 0x04, +@@ -646,6 +736,9 @@ enum rtl_register_content { + FULL_DUP = 0x01, + }; + ++#define is_speed_2500(_speed) (((_speed) & (_2500bps | LINK_STATUS)) == (_2500bps | LINK_STATUS)) ++#define is_flow_control(_speed) (((_speed) & (_tx_flow | _rx_flow)) == (_tx_flow | _rx_flow)) ++ + #define RTL8152_MAX_TX 4 + #define RTL8152_MAX_RX 10 + #define INTBUFSIZE 2 +@@ -660,7 +753,6 @@ enum rtl_register_content { + #define RTL8152_RMS (VLAN_ETH_FRAME_LEN + ETH_FCS_LEN) + #define RTL8153_RMS RTL8153_MAX_PACKET + #define RTL8152_TX_TIMEOUT (5 * HZ) +-#define RTL8152_NAPI_WEIGHT 64 + #define mtu_to_size(m) ((m) + VLAN_ETH_HLEN + ETH_FCS_LEN) + #define size_to_mtu(s) ((s) - VLAN_ETH_HLEN - ETH_FCS_LEN) + #define rx_reserved_size(x) (mtu_to_size(x) + sizeof(struct rx_desc) + RX_ALIGN) +@@ -797,6 +889,7 @@ struct r8152 { + } rtl_ops; + + struct ups_info { ++ u32 r_tune:1; + u32 _10m_ckdiv:1; + u32 _250m_ckdiv:1; + u32 aldps:1; +@@ -838,7 +931,9 @@ struct r8152 { + u32 rx_buf_sz; + u32 rx_copybreak; + u32 rx_pending; ++ u32 fc_pause_on, fc_pause_off; + ++ u32 support_2500full:1; + u16 ocp_base; + u16 speed; + u16 eee_adv; +@@ -998,6 +1093,15 @@ enum rtl_version { + RTL_VER_07, + RTL_VER_08, + RTL_VER_09, ++ ++ RTL_TEST_01, ++ RTL_VER_10, ++ RTL_VER_11, ++ RTL_VER_12, ++ RTL_VER_13, ++ RTL_VER_14, ++ RTL_VER_15, ++ + RTL_VER_MAX + }; + +@@ -1013,6 +1117,7 @@ enum tx_csum_stat { + #define RTL_ADVERTISED_100_FULL BIT(3) + #define RTL_ADVERTISED_1000_HALF BIT(4) + #define RTL_ADVERTISED_1000_FULL BIT(5) ++#define RTL_ADVERTISED_2500_FULL BIT(6) + + /* Maximum number of multicast addresses to filter (vs. Rx-all-multicast). + * The RTL chips use a 64 element hash table based on the Ethernet CRC. +@@ -2606,7 +2711,7 @@ static netdev_tx_t rtl8152_start_xmit(st + + static void r8152b_reset_packet_filter(struct r8152 *tp) + { +- u32 ocp_data; ++ u32 ocp_data; + + ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_FMC); + ocp_data &= ~FMC_FCR_MCU_EN; +@@ -2617,14 +2722,47 @@ static void r8152b_reset_packet_filter(s + + static void rtl8152_nic_reset(struct r8152 *tp) + { +- int i; ++ u32 ocp_data; ++ int i; + +- ocp_write_byte(tp, MCU_TYPE_PLA, PLA_CR, CR_RST); ++ switch (tp->version) { ++ case RTL_TEST_01: ++ case RTL_VER_10: ++ case RTL_VER_11: ++ ocp_data = ocp_read_byte(tp, MCU_TYPE_PLA, PLA_CR); ++ ocp_data &= ~CR_TE; ++ ocp_write_byte(tp, MCU_TYPE_PLA, PLA_CR, ocp_data); ++ ++ ocp_data = ocp_read_word(tp, MCU_TYPE_USB, USB_BMU_RESET); ++ ocp_data &= ~BMU_RESET_EP_IN; ++ ocp_write_word(tp, MCU_TYPE_USB, USB_BMU_RESET, ocp_data); ++ ++ ocp_data = ocp_read_word(tp, MCU_TYPE_USB, USB_USB_CTRL); ++ ocp_data |= CDC_ECM_EN; ++ ocp_write_word(tp, MCU_TYPE_USB, USB_USB_CTRL, ocp_data); ++ ++ ocp_data = ocp_read_byte(tp, MCU_TYPE_PLA, PLA_CR); ++ ocp_data &= ~CR_RE; ++ ocp_write_byte(tp, MCU_TYPE_PLA, PLA_CR, ocp_data); ++ ++ ocp_data = ocp_read_word(tp, MCU_TYPE_USB, USB_BMU_RESET); ++ ocp_data |= BMU_RESET_EP_IN; ++ ocp_write_word(tp, MCU_TYPE_USB, USB_BMU_RESET, ocp_data); ++ ++ ocp_data = ocp_read_word(tp, MCU_TYPE_USB, USB_USB_CTRL); ++ ocp_data &= ~CDC_ECM_EN; ++ ocp_write_word(tp, MCU_TYPE_USB, USB_USB_CTRL, ocp_data); ++ break; + +- for (i = 0; i < 1000; i++) { +- if (!(ocp_read_byte(tp, MCU_TYPE_PLA, PLA_CR) & CR_RST)) +- break; +- usleep_range(100, 400); ++ default: ++ ocp_write_byte(tp, MCU_TYPE_PLA, PLA_CR, CR_RST); ++ ++ for (i = 0; i < 1000; i++) { ++ if (!(ocp_read_byte(tp, MCU_TYPE_PLA, PLA_CR) & CR_RST)) ++ break; ++ usleep_range(100, 400); ++ } ++ break; + } + } + +@@ -2633,9 +2771,9 @@ static void set_tx_qlen(struct r8152 *tp + tp->tx_qlen = agg_buf_sz / (mtu_to_size(tp->netdev->mtu) + sizeof(struct tx_desc)); + } + +-static inline u8 rtl8152_get_speed(struct r8152 *tp) ++static inline u16 rtl8152_get_speed(struct r8152 *tp) + { +- return ocp_read_byte(tp, MCU_TYPE_PLA, PLA_PHYSTATUS); ++ return ocp_read_word(tp, MCU_TYPE_PLA, PLA_PHYSTATUS); + } + + static void rtl_eee_plus_en(struct r8152 *tp, bool enable) +@@ -2795,6 +2933,7 @@ static int rtl_enable(struct r8152 *tp) + switch (tp->version) { + case RTL_VER_08: + case RTL_VER_09: ++ case RTL_VER_14: + r8153b_rx_agg_chg_indicate(tp); + break; + default: +@@ -2832,6 +2971,7 @@ static void r8153_set_rx_early_timeout(s + + case RTL_VER_08: + case RTL_VER_09: ++ case RTL_VER_14: + /* The RTL8153B uses USB_RX_EXTRA_AGGR_TMR for rx timeout + * primarily. For USB_RX_EARLY_TIMEOUT, we fix it to 128ns. + */ +@@ -2841,6 +2981,18 @@ static void r8153_set_rx_early_timeout(s + ocp_data); + break; + ++ case RTL_VER_10: ++ case RTL_VER_11: ++ case RTL_VER_12: ++ case RTL_VER_13: ++ case RTL_VER_15: ++ ocp_write_word(tp, MCU_TYPE_USB, USB_RX_EARLY_TIMEOUT, ++ 640 / 8); ++ ocp_write_word(tp, MCU_TYPE_USB, USB_RX_EXTRA_AGGR_TMR, ++ ocp_data); ++ r8153b_rx_agg_chg_indicate(tp); ++ break; ++ + default: + break; + } +@@ -2860,8 +3012,19 @@ static void r8153_set_rx_early_size(stru + break; + case RTL_VER_08: + case RTL_VER_09: ++ case RTL_VER_14: ++ ocp_write_word(tp, MCU_TYPE_USB, USB_RX_EARLY_SIZE, ++ ocp_data / 8); ++ break; ++ case RTL_TEST_01: ++ case RTL_VER_10: ++ case RTL_VER_11: ++ case RTL_VER_12: ++ case RTL_VER_13: ++ case RTL_VER_15: + ocp_write_word(tp, MCU_TYPE_USB, USB_RX_EARLY_SIZE, + ocp_data / 8); ++ r8153b_rx_agg_chg_indicate(tp); + break; + default: + WARN_ON_ONCE(1); +@@ -2871,6 +3034,8 @@ static void r8153_set_rx_early_size(stru + + static int rtl8153_enable(struct r8152 *tp) + { ++ u32 ocp_data; ++ + if (test_bit(RTL8152_UNPLUG, &tp->flags)) + return -ENODEV; + +@@ -2881,15 +3046,18 @@ static int rtl8153_enable(struct r8152 * + + rtl_set_ifg(tp, rtl8152_get_speed(tp)); + +- if (tp->version == RTL_VER_09) { +- u32 ocp_data; +- ++ switch (tp->version) { ++ case RTL_VER_09: ++ case RTL_VER_14: + ocp_data = ocp_read_word(tp, MCU_TYPE_USB, USB_FW_TASK); + ocp_data &= ~FC_PATCH_TASK; + ocp_write_word(tp, MCU_TYPE_USB, USB_FW_TASK, ocp_data); + usleep_range(1000, 2000); + ocp_data |= FC_PATCH_TASK; + ocp_write_word(tp, MCU_TYPE_USB, USB_FW_TASK, ocp_data); ++ break; ++ default: ++ break; + } + + return rtl_enable(tp); +@@ -2954,12 +3122,40 @@ static void rtl_rx_vlan_en(struct r8152 + { + u32 ocp_data; + +- ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_CPCR); +- if (enable) +- ocp_data |= CPCR_RX_VLAN; +- else +- ocp_data &= ~CPCR_RX_VLAN; +- ocp_write_word(tp, MCU_TYPE_PLA, PLA_CPCR, ocp_data); ++ switch (tp->version) { ++ case RTL_VER_01: ++ case RTL_VER_02: ++ case RTL_VER_03: ++ case RTL_VER_04: ++ case RTL_VER_05: ++ case RTL_VER_06: ++ case RTL_VER_07: ++ case RTL_VER_08: ++ case RTL_VER_09: ++ case RTL_VER_14: ++ ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_CPCR); ++ if (enable) ++ ocp_data |= CPCR_RX_VLAN; ++ else ++ ocp_data &= ~CPCR_RX_VLAN; ++ ocp_write_word(tp, MCU_TYPE_PLA, PLA_CPCR, ocp_data); ++ break; ++ ++ case RTL_TEST_01: ++ case RTL_VER_10: ++ case RTL_VER_11: ++ case RTL_VER_12: ++ case RTL_VER_13: ++ case RTL_VER_15: ++ default: ++ ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_RCR1); ++ if (enable) ++ ocp_data |= OUTER_VLAN | INNER_VLAN; ++ else ++ ocp_data &= ~(OUTER_VLAN | INNER_VLAN); ++ ocp_write_word(tp, MCU_TYPE_PLA, PLA_RCR1, ocp_data); ++ break; ++ } + } + + static int rtl8152_set_features(struct net_device *dev, +@@ -3052,6 +3248,40 @@ static void __rtl_set_wol(struct r8152 * + device_set_wakeup_enable(&tp->udev->dev, false); + } + ++static void r8153_mac_clk_speed_down(struct r8152 *tp, bool enable) ++{ ++ u32 ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_MAC_PWR_CTRL2); ++ ++ /* MAC clock speed down */ ++ if (enable) ++ ocp_data |= MAC_CLK_SPDWN_EN; ++ else ++ ocp_data &= ~MAC_CLK_SPDWN_EN; ++ ++ ocp_write_word(tp, MCU_TYPE_PLA, PLA_MAC_PWR_CTRL2, ocp_data); ++} ++ ++static void r8156_mac_clk_spd(struct r8152 *tp, bool enable) ++{ ++ u32 ocp_data; ++ ++ /* MAC clock speed down */ ++ if (enable) { ++ /* aldps_spdwn_ratio, tp10_spdwn_ratio */ ++ ocp_write_word(tp, MCU_TYPE_PLA, PLA_MAC_PWR_CTRL, ++ 0x0403); ++ ++ ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_MAC_PWR_CTRL2); ++ ocp_data &= ~EEE_SPDWN_RATIO_MASK; ++ ocp_data |= MAC_CLK_SPDWN_EN | 0x03; /* eee_spdwn_ratio */ ++ ocp_write_word(tp, MCU_TYPE_PLA, PLA_MAC_PWR_CTRL2, ocp_data); ++ } else { ++ ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_MAC_PWR_CTRL2); ++ ocp_data &= ~MAC_CLK_SPDWN_EN; ++ ocp_write_word(tp, MCU_TYPE_PLA, PLA_MAC_PWR_CTRL2, ocp_data); ++ } ++} ++ + static void r8153_u1u2en(struct r8152 *tp, bool enable) + { + u8 u1u2[8]; +@@ -3111,6 +3341,9 @@ static void r8153b_ups_flags(struct r815 + if (tp->ups_info.eee_cmod_lv) + ups_flags |= UPS_FLAGS_EEE_CMOD_LV_EN; + ++ if (tp->ups_info.r_tune) ++ ups_flags |= UPS_FLAGS_R_TUNE; ++ + if (tp->ups_info._10m_ckdiv) + ups_flags |= UPS_FLAGS_EN_10M_CKDIV; + +@@ -3161,6 +3394,88 @@ static void r8153b_ups_flags(struct r815 + ocp_write_dword(tp, MCU_TYPE_USB, USB_UPS_FLAGS, ups_flags); + } + ++static void r8156_ups_flags(struct r8152 *tp) ++{ ++ u32 ups_flags = 0; ++ ++ if (tp->ups_info.green) ++ ups_flags |= UPS_FLAGS_EN_GREEN; ++ ++ if (tp->ups_info.aldps) ++ ups_flags |= UPS_FLAGS_EN_ALDPS; ++ ++ if (tp->ups_info.eee) ++ ups_flags |= UPS_FLAGS_EN_EEE; ++ ++ if (tp->ups_info.flow_control) ++ ups_flags |= UPS_FLAGS_EN_FLOW_CTR; ++ ++ if (tp->ups_info.eee_ckdiv) ++ ups_flags |= UPS_FLAGS_EN_EEE_CKDIV; ++ ++ if (tp->ups_info._10m_ckdiv) ++ ups_flags |= UPS_FLAGS_EN_10M_CKDIV; ++ ++ if (tp->ups_info.eee_plloff_100) ++ ups_flags |= UPS_FLAGS_EEE_PLLOFF_100; ++ ++ if (tp->ups_info.eee_plloff_giga) ++ ups_flags |= UPS_FLAGS_EEE_PLLOFF_GIGA; ++ ++ if (tp->ups_info._250m_ckdiv) ++ ups_flags |= UPS_FLAGS_250M_CKDIV; ++ ++ switch (tp->ups_info.speed_duplex) { ++ case FORCE_10M_HALF: ++ ups_flags |= ups_flags_speed(0); ++ break; ++ case FORCE_10M_FULL: ++ ups_flags |= ups_flags_speed(1); ++ break; ++ case FORCE_100M_HALF: ++ ups_flags |= ups_flags_speed(2); ++ break; ++ case FORCE_100M_FULL: ++ ups_flags |= ups_flags_speed(3); ++ break; ++ case NWAY_10M_HALF: ++ ups_flags |= ups_flags_speed(4); ++ break; ++ case NWAY_10M_FULL: ++ ups_flags |= ups_flags_speed(5); ++ break; ++ case NWAY_100M_HALF: ++ ups_flags |= ups_flags_speed(6); ++ break; ++ case NWAY_100M_FULL: ++ ups_flags |= ups_flags_speed(7); ++ break; ++ case NWAY_1000M_FULL: ++ ups_flags |= ups_flags_speed(8); ++ break; ++ case NWAY_2500M_FULL: ++ ups_flags |= ups_flags_speed(9); ++ break; ++ default: ++ break; ++ } ++ ++ switch (tp->ups_info.lite_mode) { ++ case 1: ++ ups_flags |= 0 << 5; ++ break; ++ case 2: ++ ups_flags |= 2 << 5; ++ break; ++ case 0: ++ default: ++ ups_flags |= 1 << 5; ++ break; ++ } ++ ++ ocp_write_dword(tp, MCU_TYPE_USB, USB_UPS_FLAGS, ups_flags); ++} ++ + static void rtl_green_en(struct r8152 *tp, bool enable) + { + u16 data; +@@ -3224,16 +3539,16 @@ static void r8153b_ups_en(struct r8152 * + ocp_data |= UPS_EN | USP_PREWAKE | PHASE2_EN; + ocp_write_byte(tp, MCU_TYPE_USB, USB_POWER_CUT, ocp_data); + +- ocp_data = ocp_read_byte(tp, MCU_TYPE_USB, 0xcfff); +- ocp_data |= BIT(0); +- ocp_write_byte(tp, MCU_TYPE_USB, 0xcfff, ocp_data); ++ ocp_data = ocp_read_byte(tp, MCU_TYPE_USB, USB_MISC_2); ++ ocp_data |= UPS_FORCE_PWR_DOWN; ++ ocp_write_byte(tp, MCU_TYPE_USB, USB_MISC_2, ocp_data); + } else { + ocp_data &= ~(UPS_EN | USP_PREWAKE); + ocp_write_byte(tp, MCU_TYPE_USB, USB_POWER_CUT, ocp_data); + +- ocp_data = ocp_read_byte(tp, MCU_TYPE_USB, 0xcfff); +- ocp_data &= ~BIT(0); +- ocp_write_byte(tp, MCU_TYPE_USB, 0xcfff, ocp_data); ++ ocp_data = ocp_read_byte(tp, MCU_TYPE_USB, USB_MISC_2); ++ ocp_data &= ~UPS_FORCE_PWR_DOWN; ++ ocp_write_byte(tp, MCU_TYPE_USB, USB_MISC_2, ocp_data); + + if (ocp_read_word(tp, MCU_TYPE_USB, USB_MISC_0) & PCUT_STATUS) { + int i; +@@ -3253,6 +3568,95 @@ static void r8153b_ups_en(struct r8152 * + } + } + ++static void r8153c_ups_en(struct r8152 *tp, bool enable) ++{ ++ u32 ocp_data = ocp_read_byte(tp, MCU_TYPE_USB, USB_POWER_CUT); ++ ++ if (enable) { ++ r8153b_ups_flags(tp); ++ ++ ocp_data |= UPS_EN | USP_PREWAKE | PHASE2_EN; ++ ocp_write_byte(tp, MCU_TYPE_USB, USB_POWER_CUT, ocp_data); ++ ++ ocp_data = ocp_read_byte(tp, MCU_TYPE_USB, USB_MISC_2); ++ ocp_data |= UPS_FORCE_PWR_DOWN; ++ ocp_data &= ~BIT(7); ++ ocp_write_byte(tp, MCU_TYPE_USB, USB_MISC_2, ocp_data); ++ } else { ++ ocp_data &= ~(UPS_EN | USP_PREWAKE); ++ ocp_write_byte(tp, MCU_TYPE_USB, USB_POWER_CUT, ocp_data); ++ ++ ocp_data = ocp_read_byte(tp, MCU_TYPE_USB, USB_MISC_2); ++ ocp_data &= ~UPS_FORCE_PWR_DOWN; ++ ocp_write_byte(tp, MCU_TYPE_USB, USB_MISC_2, ocp_data); ++ ++ if (ocp_read_word(tp, MCU_TYPE_USB, USB_MISC_0) & PCUT_STATUS) { ++ int i; ++ ++ for (i = 0; i < 500; i++) { ++ if (ocp_read_word(tp, MCU_TYPE_PLA, PLA_BOOT_CTRL) & ++ AUTOLOAD_DONE) ++ break; ++ msleep(20); ++ } ++ ++ tp->rtl_ops.hw_phy_cfg(tp); ++ ++ rtl8152_set_speed(tp, tp->autoneg, tp->speed, ++ tp->duplex, tp->advertising); ++ } ++ ++ ocp_write_byte(tp, MCU_TYPE_PLA, PLA_CRWECR, CRWECR_CONFIG); ++ ++ ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_CONFIG34); ++ ocp_data |= BIT(8); ++ ocp_write_word(tp, MCU_TYPE_PLA, PLA_CONFIG34, ocp_data); ++ ++ ocp_write_byte(tp, MCU_TYPE_PLA, PLA_CRWECR, CRWECR_NORAML); ++ } ++} ++ ++static void r8156_ups_en(struct r8152 *tp, bool enable) ++{ ++ u32 ocp_data = ocp_read_byte(tp, MCU_TYPE_USB, USB_POWER_CUT); ++ ++ if (enable) { ++ r8156_ups_flags(tp); ++ ++ ocp_data |= UPS_EN | USP_PREWAKE | PHASE2_EN; ++ ocp_write_byte(tp, MCU_TYPE_USB, USB_POWER_CUT, ocp_data); ++ ++ ocp_data = ocp_read_byte(tp, MCU_TYPE_USB, USB_MISC_2); ++ ocp_data |= UPS_FORCE_PWR_DOWN; ++ ocp_write_byte(tp, MCU_TYPE_USB, USB_MISC_2, ocp_data); ++ ++ switch (tp->version) { ++ case RTL_VER_13: ++ case RTL_VER_15: ++ ocp_data = ocp_read_word(tp, MCU_TYPE_USB, USB_UPHY_XTAL); ++ ocp_data &= ~OOBS_POLLING; ++ ocp_write_byte(tp, MCU_TYPE_USB, USB_UPHY_XTAL, ocp_data); ++ break; ++ default: ++ break; ++ } ++ } else { ++ ocp_data &= ~(UPS_EN | USP_PREWAKE); ++ ocp_write_byte(tp, MCU_TYPE_USB, USB_POWER_CUT, ocp_data); ++ ++ ocp_data = ocp_read_byte(tp, MCU_TYPE_USB, USB_MISC_2); ++ ocp_data &= ~UPS_FORCE_PWR_DOWN; ++ ocp_write_byte(tp, MCU_TYPE_USB, USB_MISC_2, ocp_data); ++ ++ if (ocp_read_word(tp, MCU_TYPE_USB, USB_MISC_0) & PCUT_STATUS) { ++ tp->rtl_ops.hw_phy_cfg(tp); ++ ++ rtl8152_set_speed(tp, tp->autoneg, tp->speed, ++ tp->duplex, tp->advertising); ++ } ++ } ++} ++ + static void r8153_power_cut_en(struct r8152 *tp, bool enable) + { + u32 ocp_data; +@@ -3382,6 +3786,38 @@ static void rtl8153b_runtime_enable(stru + } + } + ++static void rtl8153c_runtime_enable(struct r8152 *tp, bool enable) ++{ ++ if (enable) { ++ r8153_queue_wake(tp, true); ++ r8153b_u1u2en(tp, false); ++ r8153_u2p3en(tp, false); ++ rtl_runtime_suspend_enable(tp, true); ++ r8153c_ups_en(tp, true); ++ } else { ++ r8153c_ups_en(tp, false); ++ r8153_queue_wake(tp, false); ++ rtl_runtime_suspend_enable(tp, false); ++ r8153b_u1u2en(tp, true); ++ } ++} ++ ++static void rtl8156_runtime_enable(struct r8152 *tp, bool enable) ++{ ++ if (enable) { ++ r8153_queue_wake(tp, true); ++ r8153b_u1u2en(tp, false); ++ r8153_u2p3en(tp, false); ++ rtl_runtime_suspend_enable(tp, true); ++ } else { ++ r8153_queue_wake(tp, false); ++ rtl_runtime_suspend_enable(tp, false); ++ r8153_u2p3en(tp, true); ++ if (tp->udev->speed >= USB_SPEED_SUPER) ++ r8153b_u1u2en(tp, true); ++ } ++} ++ + static void r8153_teredo_off(struct r8152 *tp) + { + u32 ocp_data; +@@ -3402,14 +3838,19 @@ static void r8153_teredo_off(struct r815 + + case RTL_VER_08: + case RTL_VER_09: ++ case RTL_TEST_01: ++ case RTL_VER_10: ++ case RTL_VER_11: ++ case RTL_VER_12: ++ case RTL_VER_13: ++ case RTL_VER_14: ++ case RTL_VER_15: ++ default: + /* The bit 0 ~ 7 are relative with teredo settings. They are + * W1C (write 1 to clear), so set all 1 to disable it. + */ + ocp_write_byte(tp, MCU_TYPE_PLA, PLA_TEREDO_CFG, 0xff); + break; +- +- default: +- break; + } + + ocp_write_word(tp, MCU_TYPE_PLA, PLA_WDT6_CTRL, WDT6_SET_MODE); +@@ -3444,6 +3885,12 @@ static void rtl_clear_bp(struct r8152 *t + break; + case RTL_VER_08: + case RTL_VER_09: ++ case RTL_VER_10: ++ case RTL_VER_11: ++ case RTL_VER_12: ++ case RTL_VER_13: ++ case RTL_VER_14: ++ case RTL_VER_15: + default: + if (type == MCU_TYPE_USB) { + ocp_write_word(tp, MCU_TYPE_USB, USB_BP2_EN, 0); +@@ -3653,6 +4100,11 @@ static bool rtl8152_is_fw_mac_ok(struct + case RTL_VER_06: + case RTL_VER_08: + case RTL_VER_09: ++ case RTL_VER_11: ++ case RTL_VER_12: ++ case RTL_VER_13: ++ case RTL_VER_14: ++ case RTL_VER_15: + fw_reg = 0xf800; + bp_ba_addr = PLA_BP_BA; + bp_en_addr = PLA_BP_EN; +@@ -3676,6 +4128,11 @@ static bool rtl8152_is_fw_mac_ok(struct + break; + case RTL_VER_08: + case RTL_VER_09: ++ case RTL_VER_11: ++ case RTL_VER_12: ++ case RTL_VER_13: ++ case RTL_VER_14: ++ case RTL_VER_15: + fw_reg = 0xe600; + bp_ba_addr = USB_BP_BA; + bp_en_addr = USB_BP2_EN; +@@ -4215,6 +4672,22 @@ static void r8153_eee_en(struct r8152 *t + tp->ups_info.eee = enable; + } + ++static void r8156_eee_en(struct r8152 *tp, bool enable) ++{ ++ u16 config; ++ ++ r8153_eee_en(tp, enable); ++ ++ config = ocp_reg_read(tp, OCP_EEE_ADV2); ++ ++ if (enable) ++ config |= MDIO_EEE_2_5GT; ++ else ++ config &= ~MDIO_EEE_2_5GT; ++ ++ ocp_reg_write(tp, OCP_EEE_ADV2, config); ++} ++ + static void rtl_eee_enable(struct r8152 *tp, bool enable) + { + switch (tp->version) { +@@ -4236,6 +4709,7 @@ static void rtl_eee_enable(struct r8152 + case RTL_VER_06: + case RTL_VER_08: + case RTL_VER_09: ++ case RTL_VER_14: + if (enable) { + r8153_eee_en(tp, true); + ocp_reg_write(tp, OCP_EEE_ADV, tp->eee_adv); +@@ -4244,6 +4718,19 @@ static void rtl_eee_enable(struct r8152 + ocp_reg_write(tp, OCP_EEE_ADV, 0); + } + break; ++ case RTL_VER_10: ++ case RTL_VER_11: ++ case RTL_VER_12: ++ case RTL_VER_13: ++ case RTL_VER_15: ++ if (enable) { ++ r8156_eee_en(tp, true); ++ ocp_reg_write(tp, OCP_EEE_ADV, tp->eee_adv); ++ } else { ++ r8156_eee_en(tp, false); ++ ocp_reg_write(tp, OCP_EEE_ADV, 0); ++ } ++ break; + default: + break; + } +@@ -4290,6 +4777,20 @@ static void wait_oob_link_list_ready(str + } + } + ++static void r8156b_wait_loading_flash(struct r8152 *tp) ++{ ++ if ((ocp_read_word(tp, MCU_TYPE_PLA, PLA_GPHY_CTRL) & GPHY_FLASH) && ++ !(ocp_read_word(tp, MCU_TYPE_USB, USB_GPHY_CTRL) & BYPASS_FLASH)) { ++ int i; ++ ++ for (i = 0; i < 100; i++) { ++ if (ocp_read_word(tp, MCU_TYPE_USB, USB_GPHY_CTRL) & GPHY_PATCH_DONE) ++ break; ++ usleep_range(1000, 2000); ++ } ++ } ++} ++ + static void r8152b_exit_oob(struct r8152 *tp) + { + u32 ocp_data; +@@ -4340,7 +4841,7 @@ static void r8152b_exit_oob(struct r8152 + } + + /* TX share fifo free credit full threshold */ +- ocp_write_dword(tp, MCU_TYPE_PLA, PLA_TXFIFO_CTRL, TXFIFO_THR_NORMAL); ++ ocp_write_dword(tp, MCU_TYPE_PLA, PLA_TXFIFO_CTRL, TXFIFO_THR_NORMAL2); + + ocp_write_byte(tp, MCU_TYPE_USB, USB_TX_AGG, TX_AGG_MAX_THRESHOLD); + ocp_write_dword(tp, MCU_TYPE_USB, USB_RX_BUF_TH, RX_THR_HIGH); +@@ -4517,6 +5018,21 @@ static int r8153b_post_firmware_1(struct + return 0; + } + ++static int r8153c_post_firmware_1(struct r8152 *tp) ++{ ++ u32 ocp_data; ++ ++ ocp_data = ocp_read_word(tp, MCU_TYPE_USB, USB_FW_CTRL); ++ ocp_data |= FLOW_CTRL_PATCH_2; ++ ocp_write_word(tp, MCU_TYPE_USB, USB_FW_CTRL, ocp_data); ++ ++ ocp_data = ocp_read_word(tp, MCU_TYPE_USB, USB_FW_TASK); ++ ocp_data |= FC_PATCH_TASK; ++ ocp_write_word(tp, MCU_TYPE_USB, USB_FW_TASK, ocp_data); ++ ++ return 0; ++} ++ + static void r8153_aldps_en(struct r8152 *tp, bool enable) + { + u16 data; +@@ -4719,6 +5235,13 @@ static void r8153b_hw_phy_cfg(struct r81 + set_bit(PHY_RESET, &tp->flags); + } + ++static void r8153c_hw_phy_cfg(struct r8152 *tp) ++{ ++ r8153b_hw_phy_cfg(tp); ++ ++ tp->ups_info.r_tune = true; ++} ++ + static void rtl8153_change_mtu(struct r8152 *tp) + { + ocp_write_word(tp, MCU_TYPE_PLA, PLA_RMS, mtu_to_size(tp->netdev->mtu)); +@@ -4806,6 +5329,7 @@ static void r8153_enter_oob(struct r8152 + + case RTL_VER_08: + case RTL_VER_09: ++ case RTL_VER_14: + /* Clear teredo wake event. bit[15:8] is the teredo wakeup + * type. Set it to zero. bits[7:0] are the W1C bits about + * the events. Set them to all 1 to clear them. +@@ -4842,6 +5366,96 @@ static void rtl8153_disable(struct r8152 + r8153_aldps_en(tp, true); + } + ++static int rtl8156_enable(struct r8152 *tp) ++{ ++ u32 ocp_data; ++ u16 speed; ++ ++ if (test_bit(RTL8152_UNPLUG, &tp->flags)) ++ return -ENODEV; ++ ++ set_tx_qlen(tp); ++ rtl_set_eee_plus(tp); ++ r8153_set_rx_early_timeout(tp); ++ r8153_set_rx_early_size(tp); ++ ++ speed = rtl8152_get_speed(tp); ++ rtl_set_ifg(tp, speed); ++ ++ ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_MAC_PWR_CTRL4); ++ if (speed & _2500bps) ++ ocp_data &= ~IDLE_SPDWN_EN; ++ else ++ ocp_data |= IDLE_SPDWN_EN; ++ ocp_write_word(tp, MCU_TYPE_PLA, PLA_MAC_PWR_CTRL4, ocp_data); ++ ++ if (speed & _1000bps) ++ ocp_write_word(tp, MCU_TYPE_PLA, PLA_EEE_TXTWSYS, 0x11); ++ else if (speed & _500bps) ++ ocp_write_word(tp, MCU_TYPE_PLA, PLA_EEE_TXTWSYS, 0x3d); ++ ++ if (tp->udev->speed == USB_SPEED_HIGH) { ++ /* USB 0xb45e[3:0] l1_nyet_hird */ ++ ocp_data = ocp_read_word(tp, MCU_TYPE_USB, USB_L1_CTRL); ++ ocp_data &= ~0xf; ++ if (is_flow_control(speed)) ++ ocp_data |= 0xf; ++ else ++ ocp_data |= 0x1; ++ ocp_write_word(tp, MCU_TYPE_USB, USB_L1_CTRL, ocp_data); ++ } ++ ++ return rtl_enable(tp); ++} ++ ++static int rtl8156b_enable(struct r8152 *tp) ++{ ++ u32 ocp_data; ++ u16 speed; ++ ++ if (test_bit(RTL8152_UNPLUG, &tp->flags)) ++ return -ENODEV; ++ ++ set_tx_qlen(tp); ++ rtl_set_eee_plus(tp); ++ ++ ocp_data = ocp_read_word(tp, MCU_TYPE_USB, USB_RX_AGGR_NUM); ++ ocp_data &= ~RX_AGGR_NUM_MASK; ++ ocp_write_word(tp, MCU_TYPE_USB, USB_RX_AGGR_NUM, ocp_data); ++ ++ r8153_set_rx_early_timeout(tp); ++ r8153_set_rx_early_size(tp); ++ ++ speed = rtl8152_get_speed(tp); ++ rtl_set_ifg(tp, speed); ++ ++ ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_MAC_PWR_CTRL4); ++ if (speed & _2500bps) ++ ocp_data &= ~IDLE_SPDWN_EN; ++ else ++ ocp_data |= IDLE_SPDWN_EN; ++ ocp_write_word(tp, MCU_TYPE_PLA, PLA_MAC_PWR_CTRL4, ocp_data); ++ ++ if (tp->udev->speed == USB_SPEED_HIGH) { ++ ocp_data = ocp_read_word(tp, MCU_TYPE_USB, USB_L1_CTRL); ++ ocp_data &= ~0xf; ++ if (is_flow_control(speed)) ++ ocp_data |= 0xf; ++ else ++ ocp_data |= 0x1; ++ ocp_write_word(tp, MCU_TYPE_USB, USB_L1_CTRL, ocp_data); ++ } ++ ++ ocp_data = ocp_read_word(tp, MCU_TYPE_USB, USB_FW_TASK); ++ ocp_data &= ~FC_PATCH_TASK; ++ ocp_write_word(tp, MCU_TYPE_USB, USB_FW_TASK, ocp_data); ++ usleep_range(1000, 2000); ++ ocp_data |= FC_PATCH_TASK; ++ ocp_write_word(tp, MCU_TYPE_USB, USB_FW_TASK, ocp_data); ++ ++ return rtl_enable(tp); ++} ++ + static int rtl8152_set_speed(struct r8152 *tp, u8 autoneg, u32 speed, u8 duplex, + u32 advertising) + { +@@ -4890,58 +5504,73 @@ static int rtl8152_set_speed(struct r815 + + tp->mii.force_media = 1; + } else { +- u16 anar, tmp1; ++ u16 orig, new1; + u32 support; + + support = RTL_ADVERTISED_10_HALF | RTL_ADVERTISED_10_FULL | + RTL_ADVERTISED_100_HALF | RTL_ADVERTISED_100_FULL; + +- if (tp->mii.supports_gmii) ++ if (tp->mii.supports_gmii) { + support |= RTL_ADVERTISED_1000_FULL; + ++ if (tp->support_2500full) ++ support |= RTL_ADVERTISED_2500_FULL; ++ } ++ + if (!(advertising & support)) + return -EINVAL; + +- anar = r8152_mdio_read(tp, MII_ADVERTISE); +- tmp1 = anar & ~(ADVERTISE_10HALF | ADVERTISE_10FULL | ++ orig = r8152_mdio_read(tp, MII_ADVERTISE); ++ new1 = orig & ~(ADVERTISE_10HALF | ADVERTISE_10FULL | + ADVERTISE_100HALF | ADVERTISE_100FULL); + if (advertising & RTL_ADVERTISED_10_HALF) { +- tmp1 |= ADVERTISE_10HALF; ++ new1 |= ADVERTISE_10HALF; + tp->ups_info.speed_duplex = NWAY_10M_HALF; + } + if (advertising & RTL_ADVERTISED_10_FULL) { +- tmp1 |= ADVERTISE_10FULL; ++ new1 |= ADVERTISE_10FULL; + tp->ups_info.speed_duplex = NWAY_10M_FULL; + } + + if (advertising & RTL_ADVERTISED_100_HALF) { +- tmp1 |= ADVERTISE_100HALF; ++ new1 |= ADVERTISE_100HALF; + tp->ups_info.speed_duplex = NWAY_100M_HALF; + } + if (advertising & RTL_ADVERTISED_100_FULL) { +- tmp1 |= ADVERTISE_100FULL; ++ new1 |= ADVERTISE_100FULL; + tp->ups_info.speed_duplex = NWAY_100M_FULL; + } + +- if (anar != tmp1) { +- r8152_mdio_write(tp, MII_ADVERTISE, tmp1); +- tp->mii.advertising = tmp1; ++ if (orig != new1) { ++ r8152_mdio_write(tp, MII_ADVERTISE, new1); ++ tp->mii.advertising = new1; + } + + if (tp->mii.supports_gmii) { +- u16 gbcr; +- +- gbcr = r8152_mdio_read(tp, MII_CTRL1000); +- tmp1 = gbcr & ~(ADVERTISE_1000FULL | ++ orig = r8152_mdio_read(tp, MII_CTRL1000); ++ new1 = orig & ~(ADVERTISE_1000FULL | + ADVERTISE_1000HALF); + + if (advertising & RTL_ADVERTISED_1000_FULL) { +- tmp1 |= ADVERTISE_1000FULL; ++ new1 |= ADVERTISE_1000FULL; + tp->ups_info.speed_duplex = NWAY_1000M_FULL; + } + +- if (gbcr != tmp1) +- r8152_mdio_write(tp, MII_CTRL1000, tmp1); ++ if (orig != new1) ++ r8152_mdio_write(tp, MII_CTRL1000, new1); ++ } ++ ++ if (tp->support_2500full) { ++ orig = ocp_reg_read(tp, OCP_10GBT_CTRL); ++ new1 = orig & ~MDIO_AN_10GBT_CTRL_ADV2_5G; ++ ++ if (advertising & RTL_ADVERTISED_2500_FULL) { ++ new1 |= MDIO_AN_10GBT_CTRL_ADV2_5G; ++ tp->ups_info.speed_duplex = NWAY_2500M_FULL; ++ } ++ ++ if (orig != new1) ++ ocp_reg_write(tp, OCP_10GBT_CTRL, new1); + } + + bmcr = BMCR_ANENABLE | BMCR_ANRESTART; +@@ -5097,6 +5726,253 @@ static void rtl8153b_down(struct r8152 * + r8153_aldps_en(tp, true); + } + ++static void rtl8153c_change_mtu(struct r8152 *tp) ++{ ++ ocp_write_word(tp, MCU_TYPE_PLA, PLA_RMS, mtu_to_size(tp->netdev->mtu)); ++ ocp_write_byte(tp, MCU_TYPE_PLA, PLA_MTPS, 10 * 1024 / 64); ++ ++ ocp_write_word(tp, MCU_TYPE_PLA, PLA_TXFIFO_CTRL, 512 / 64); ++ ++ /* Adjust the tx fifo free credit full threshold, otherwise ++ * the fifo would be too small to send a jumbo frame packet. ++ */ ++ if (tp->netdev->mtu < 8000) ++ ocp_write_word(tp, MCU_TYPE_PLA, PLA_TXFIFO_FULL, 2048 / 8); ++ else ++ ocp_write_word(tp, MCU_TYPE_PLA, PLA_TXFIFO_FULL, 900 / 8); ++} ++ ++static void rtl8153c_up(struct r8152 *tp) ++{ ++ u32 ocp_data; ++ ++ if (test_bit(RTL8152_UNPLUG, &tp->flags)) ++ return; ++ ++ r8153b_u1u2en(tp, false); ++ r8153_u2p3en(tp, false); ++ r8153_aldps_en(tp, false); ++ ++ rxdy_gated_en(tp, true); ++ r8153_teredo_off(tp); ++ ++ ocp_data = ocp_read_dword(tp, MCU_TYPE_PLA, PLA_RCR); ++ ocp_data &= ~RCR_ACPT_ALL; ++ ocp_write_dword(tp, MCU_TYPE_PLA, PLA_RCR, ocp_data); ++ ++ rtl8152_nic_reset(tp); ++ rtl_reset_bmu(tp); ++ ++ ocp_data = ocp_read_byte(tp, MCU_TYPE_PLA, PLA_OOB_CTRL); ++ ocp_data &= ~NOW_IS_OOB; ++ ocp_write_byte(tp, MCU_TYPE_PLA, PLA_OOB_CTRL, ocp_data); ++ ++ ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_SFF_STS_7); ++ ocp_data &= ~MCU_BORW_EN; ++ ocp_write_word(tp, MCU_TYPE_PLA, PLA_SFF_STS_7, ocp_data); ++ ++ wait_oob_link_list_ready(tp); ++ ++ ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_SFF_STS_7); ++ ocp_data |= RE_INIT_LL; ++ ocp_write_word(tp, MCU_TYPE_PLA, PLA_SFF_STS_7, ocp_data); ++ ++ wait_oob_link_list_ready(tp); ++ ++ rtl_rx_vlan_en(tp, tp->netdev->features & NETIF_F_HW_VLAN_CTAG_RX); ++ ++ rtl8153c_change_mtu(tp); ++ ++ rtl8152_nic_reset(tp); ++ ++ /* rx share fifo credit full threshold */ ++ ocp_write_byte(tp, MCU_TYPE_PLA, PLA_RXFIFO_CTRL0, 0x02); ++ ocp_write_byte(tp, MCU_TYPE_PLA, PLA_RXFIFO_FULL, 0x08); ++ ocp_write_word(tp, MCU_TYPE_PLA, PLA_RXFIFO_CTRL1, RXFIFO_THR2_NORMAL); ++ ocp_write_word(tp, MCU_TYPE_PLA, PLA_RXFIFO_CTRL2, RXFIFO_THR3_NORMAL); ++ ++ ocp_write_dword(tp, MCU_TYPE_USB, USB_RX_BUF_TH, RX_THR_B); ++ ++ ocp_write_byte(tp, MCU_TYPE_PLA, PLA_CRWECR, CRWECR_CONFIG); ++ ++ ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_CONFIG34); ++ ocp_data |= BIT(8); ++ ocp_write_word(tp, MCU_TYPE_PLA, PLA_CONFIG34, ocp_data); ++ ++ ocp_write_byte(tp, MCU_TYPE_PLA, PLA_CRWECR, CRWECR_NORAML); ++ ++ ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_MAC_PWR_CTRL3); ++ ocp_data &= ~PLA_MCU_SPDWN_EN; ++ ocp_write_word(tp, MCU_TYPE_PLA, PLA_MAC_PWR_CTRL3, ocp_data); ++ ++ r8153_aldps_en(tp, true); ++ r8153b_u1u2en(tp, true); ++} ++ ++static inline u32 fc_pause_on_auto(struct r8152 *tp) ++{ ++ return (ALIGN(mtu_to_size(tp->netdev->mtu), 1024) + 6 * 1024); ++} ++ ++static inline u32 fc_pause_off_auto(struct r8152 *tp) ++{ ++ return (ALIGN(mtu_to_size(tp->netdev->mtu), 1024) + 14 * 1024); ++} ++ ++static void r8156_fc_parameter(struct r8152 *tp) ++{ ++ u32 pause_on = tp->fc_pause_on ? tp->fc_pause_on : fc_pause_on_auto(tp); ++ u32 pause_off = tp->fc_pause_off ? tp->fc_pause_off : fc_pause_off_auto(tp); ++ ++ switch (tp->version) { ++ case RTL_VER_10: ++ case RTL_VER_11: ++ ocp_write_word(tp, MCU_TYPE_PLA, PLA_RX_FIFO_FULL, pause_on / 8); ++ ocp_write_word(tp, MCU_TYPE_PLA, PLA_RX_FIFO_EMPTY, pause_off / 8); ++ break; ++ case RTL_VER_12: ++ case RTL_VER_13: ++ case RTL_VER_15: ++ ocp_write_word(tp, MCU_TYPE_PLA, PLA_RX_FIFO_FULL, pause_on / 16); ++ ocp_write_word(tp, MCU_TYPE_PLA, PLA_RX_FIFO_EMPTY, pause_off / 16); ++ break; ++ default: ++ break; ++ } ++} ++ ++static void rtl8156_change_mtu(struct r8152 *tp) ++{ ++ u32 rx_max_size = mtu_to_size(tp->netdev->mtu); ++ ++ ocp_write_word(tp, MCU_TYPE_PLA, PLA_RMS, rx_max_size); ++ ocp_write_byte(tp, MCU_TYPE_PLA, PLA_MTPS, MTPS_JUMBO); ++ r8156_fc_parameter(tp); ++ ++ /* TX share fifo free credit full threshold */ ++ ocp_write_word(tp, MCU_TYPE_PLA, PLA_TXFIFO_CTRL, 512 / 64); ++ ocp_write_word(tp, MCU_TYPE_PLA, PLA_TXFIFO_FULL, ++ ALIGN(rx_max_size + sizeof(struct tx_desc), 1024) / 16); ++} ++ ++static void rtl8156_up(struct r8152 *tp) ++{ ++ u32 ocp_data; ++ ++ if (test_bit(RTL8152_UNPLUG, &tp->flags)) ++ return; ++ ++ r8153b_u1u2en(tp, false); ++ r8153_u2p3en(tp, false); ++ r8153_aldps_en(tp, false); ++ ++ rxdy_gated_en(tp, true); ++ r8153_teredo_off(tp); ++ ++ ocp_data = ocp_read_dword(tp, MCU_TYPE_PLA, PLA_RCR); ++ ocp_data &= ~RCR_ACPT_ALL; ++ ocp_write_dword(tp, MCU_TYPE_PLA, PLA_RCR, ocp_data); ++ ++ rtl8152_nic_reset(tp); ++ rtl_reset_bmu(tp); ++ ++ ocp_data = ocp_read_byte(tp, MCU_TYPE_PLA, PLA_OOB_CTRL); ++ ocp_data &= ~NOW_IS_OOB; ++ ocp_write_byte(tp, MCU_TYPE_PLA, PLA_OOB_CTRL, ocp_data); ++ ++ ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_SFF_STS_7); ++ ocp_data &= ~MCU_BORW_EN; ++ ocp_write_word(tp, MCU_TYPE_PLA, PLA_SFF_STS_7, ocp_data); ++ ++ rtl_rx_vlan_en(tp, tp->netdev->features & NETIF_F_HW_VLAN_CTAG_RX); ++ ++ rtl8156_change_mtu(tp); ++ ++ switch (tp->version) { ++ case RTL_TEST_01: ++ case RTL_VER_10: ++ case RTL_VER_11: ++ ocp_data = ocp_read_word(tp, MCU_TYPE_USB, USB_BMU_CONFIG); ++ ocp_data |= ACT_ODMA; ++ ocp_write_word(tp, MCU_TYPE_USB, USB_BMU_CONFIG, ocp_data); ++ break; ++ default: ++ break; ++ } ++ ++ /* share FIFO settings */ ++ ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_RXFIFO_FULL); ++ ocp_data &= ~RXFIFO_FULL_MASK; ++ ocp_data |= 0x08; ++ ocp_write_word(tp, MCU_TYPE_PLA, PLA_RXFIFO_FULL, ocp_data); ++ ++ ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_MAC_PWR_CTRL3); ++ ocp_data &= ~PLA_MCU_SPDWN_EN; ++ ocp_write_word(tp, MCU_TYPE_PLA, PLA_MAC_PWR_CTRL3, ocp_data); ++ ++ ocp_data = ocp_read_word(tp, MCU_TYPE_USB, USB_SPEED_OPTION); ++ ocp_data &= ~(RG_PWRDN_EN | ALL_SPEED_OFF); ++ ocp_write_word(tp, MCU_TYPE_USB, USB_SPEED_OPTION, ocp_data); ++ ++ ocp_write_dword(tp, MCU_TYPE_USB, USB_RX_BUF_TH, 0x00600400); ++ ++ if (tp->saved_wolopts != __rtl_get_wol(tp)) { ++ netif_warn(tp, ifup, tp->netdev, "wol setting is changed\n"); ++ __rtl_set_wol(tp, tp->saved_wolopts); ++ } ++ ++ r8153_aldps_en(tp, true); ++ r8153_u2p3en(tp, true); ++ ++ if (tp->udev->speed >= USB_SPEED_SUPER) ++ r8153b_u1u2en(tp, true); ++} ++ ++static void rtl8156_down(struct r8152 *tp) ++{ ++ u32 ocp_data; ++ ++ if (test_bit(RTL8152_UNPLUG, &tp->flags)) { ++ rtl_drop_queued_tx(tp); ++ return; ++ } ++ ++ ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_MAC_PWR_CTRL3); ++ ocp_data |= PLA_MCU_SPDWN_EN; ++ ocp_write_word(tp, MCU_TYPE_PLA, PLA_MAC_PWR_CTRL3, ocp_data); ++ ++ r8153b_u1u2en(tp, false); ++ r8153_u2p3en(tp, false); ++ r8153b_power_cut_en(tp, false); ++ r8153_aldps_en(tp, false); ++ ++ ocp_data = ocp_read_byte(tp, MCU_TYPE_PLA, PLA_OOB_CTRL); ++ ocp_data &= ~NOW_IS_OOB; ++ ocp_write_byte(tp, MCU_TYPE_PLA, PLA_OOB_CTRL, ocp_data); ++ ++ rtl_disable(tp); ++ rtl_reset_bmu(tp); ++ ++ /* Clear teredo wake event. bit[15:8] is the teredo wakeup ++ * type. Set it to zero. bits[7:0] are the W1C bits about ++ * the events. Set them to all 1 to clear them. ++ */ ++ ocp_write_word(tp, MCU_TYPE_PLA, PLA_TEREDO_WAKE_BASE, 0x00ff); ++ ++ ocp_data = ocp_read_byte(tp, MCU_TYPE_PLA, PLA_OOB_CTRL); ++ ocp_data |= NOW_IS_OOB; ++ ocp_write_byte(tp, MCU_TYPE_PLA, PLA_OOB_CTRL, ocp_data); ++ ++ rtl_rx_vlan_en(tp, true); ++ rxdy_gated_en(tp, false); ++ ++ ocp_data = ocp_read_dword(tp, MCU_TYPE_PLA, PLA_RCR); ++ ocp_data |= RCR_APM | RCR_AM | RCR_AB; ++ ocp_write_dword(tp, MCU_TYPE_PLA, PLA_RCR, ocp_data); ++ ++ r8153_aldps_en(tp, true); ++} ++ + static bool rtl8152_in_nway(struct r8152 *tp) + { + u16 nway_state; +@@ -5127,7 +6003,7 @@ static void set_carrier(struct r8152 *tp + { + struct net_device *netdev = tp->netdev; + struct napi_struct *napi = &tp->napi; +- u8 speed; ++ u16 speed; + + speed = rtl8152_get_speed(tp); + +@@ -5140,7 +6016,7 @@ static void set_carrier(struct r8152 *tp + rtl_start_rx(tp); + clear_bit(RTL8152_SET_RX_MODE, &tp->flags); + _rtl8152_set_rx_mode(netdev); +- napi_enable(&tp->napi); ++ napi_enable(napi); + netif_wake_queue(netdev); + netif_info(tp, link, netdev, "carrier on\n"); + } else if (netif_queue_stopped(netdev) && +@@ -5502,14 +6378,9 @@ static void r8153_init(struct r8152 *tp) + + ocp_write_word(tp, MCU_TYPE_USB, USB_CONNECT_TIMER, 0x0001); + +- /* MAC clock speed down */ +- ocp_write_word(tp, MCU_TYPE_PLA, PLA_MAC_PWR_CTRL, 0); +- ocp_write_word(tp, MCU_TYPE_PLA, PLA_MAC_PWR_CTRL2, 0); +- ocp_write_word(tp, MCU_TYPE_PLA, PLA_MAC_PWR_CTRL3, 0); +- ocp_write_word(tp, MCU_TYPE_PLA, PLA_MAC_PWR_CTRL4, 0); +- + r8153_power_cut_en(tp, false); + rtl_runtime_suspend_enable(tp, false); ++ r8153_mac_clk_speed_down(tp, false); + r8153_u1u2en(tp, true); + usb_enable_lpm(tp->udev); + +@@ -5600,9 +6471,7 @@ static void r8153b_init(struct r8152 *tp + usb_enable_lpm(tp->udev); + + /* MAC clock speed down */ +- ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_MAC_PWR_CTRL2); +- ocp_data |= MAC_CLK_SPDWN_EN; +- ocp_write_word(tp, MCU_TYPE_PLA, PLA_MAC_PWR_CTRL2, ocp_data); ++ r8153_mac_clk_speed_down(tp, true); + + ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_MAC_PWR_CTRL3); + ocp_data &= ~PLA_MCU_SPDWN_EN; +@@ -5629,6 +6498,1069 @@ static void r8153b_init(struct r8152 *tp + tp->coalesce = 15000; /* 15 us */ + } + ++static void r8153c_init(struct r8152 *tp) ++{ ++ u32 ocp_data; ++ u16 data; ++ int i; ++ ++ if (test_bit(RTL8152_UNPLUG, &tp->flags)) ++ return; ++ ++ r8153b_u1u2en(tp, false); ++ ++ /* Disable spi_en */ ++ ocp_write_byte(tp, MCU_TYPE_PLA, PLA_CRWECR, CRWECR_CONFIG); ++ ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_CONFIG5); ++ ocp_data &= ~BIT(3); ++ ocp_write_word(tp, MCU_TYPE_PLA, PLA_CONFIG5, ocp_data); ++ ocp_data = ocp_read_word(tp, MCU_TYPE_USB, 0xcbf0); ++ ocp_data |= BIT(1); ++ ocp_write_word(tp, MCU_TYPE_USB, 0xcbf0, ocp_data); ++ ++ for (i = 0; i < 500; i++) { ++ if (ocp_read_word(tp, MCU_TYPE_PLA, PLA_BOOT_CTRL) & ++ AUTOLOAD_DONE) ++ break; ++ ++ msleep(20); ++ if (test_bit(RTL8152_UNPLUG, &tp->flags)) ++ return; ++ } ++ ++ data = r8153_phy_status(tp, 0); ++ ++ data = r8152_mdio_read(tp, MII_BMCR); ++ if (data & BMCR_PDOWN) { ++ data &= ~BMCR_PDOWN; ++ r8152_mdio_write(tp, MII_BMCR, data); ++ } ++ ++ data = r8153_phy_status(tp, PHY_STAT_LAN_ON); ++ ++ r8153_u2p3en(tp, false); ++ ++ /* MSC timer = 0xfff * 8ms = 32760 ms */ ++ ocp_write_word(tp, MCU_TYPE_USB, USB_MSC_TIMER, 0x0fff); ++ ++ r8153b_power_cut_en(tp, false); ++ r8153c_ups_en(tp, false); ++ r8153_queue_wake(tp, false); ++ rtl_runtime_suspend_enable(tp, false); ++ ++ ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_EXTRA_STATUS); ++ if (rtl8152_get_speed(tp) & LINK_STATUS) ++ ocp_data |= CUR_LINK_OK; ++ else ++ ocp_data &= ~CUR_LINK_OK; ++ ++ ocp_data |= POLL_LINK_CHG; ++ ocp_write_word(tp, MCU_TYPE_PLA, PLA_EXTRA_STATUS, ocp_data); ++ ++ r8153b_u1u2en(tp, true); ++ ++ usb_enable_lpm(tp->udev); ++ ++ /* MAC clock speed down */ ++ r8153_mac_clk_speed_down(tp, true); ++ ++ ocp_data = ocp_read_byte(tp, MCU_TYPE_USB, USB_MISC_2); ++ ocp_data &= ~BIT(7); ++ ocp_write_byte(tp, MCU_TYPE_USB, USB_MISC_2, ocp_data); ++ ++ set_bit(GREEN_ETHERNET, &tp->flags); ++ ++ /* rx aggregation */ ++ 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); ++ ++ rtl_tally_reset(tp); ++ ++ tp->coalesce = 15000; /* 15 us */ ++} ++ ++static void r8156_hw_phy_cfg(struct r8152 *tp) ++{ ++ u32 ocp_data; ++ u16 data; ++ ++ ocp_data = ocp_read_word(tp, MCU_TYPE_USB, USB_MISC_0); ++ if (ocp_data & PCUT_STATUS) { ++ ocp_data &= ~PCUT_STATUS; ++ ocp_write_word(tp, MCU_TYPE_USB, USB_MISC_0, ocp_data); ++ } ++ ++ data = r8153_phy_status(tp, 0); ++ switch (data) { ++ case PHY_STAT_EXT_INIT: ++ rtl8152_apply_firmware(tp, true); ++ ++ data = ocp_reg_read(tp, 0xa468); ++ data &= ~(BIT(3) | BIT(1)); ++ ocp_reg_write(tp, 0xa468, data); ++ break; ++ case PHY_STAT_LAN_ON: ++ case PHY_STAT_PWRDN: ++ default: ++ rtl8152_apply_firmware(tp, false); ++ break; ++ } ++ ++ /* disable ALDPS before updating the PHY parameters */ ++ r8153_aldps_en(tp, false); ++ ++ /* disable EEE before updating the PHY parameters */ ++ rtl_eee_enable(tp, false); ++ ++ data = r8153_phy_status(tp, PHY_STAT_LAN_ON); ++ WARN_ON_ONCE(data != PHY_STAT_LAN_ON); ++ ++ ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_PHY_PWR); ++ ocp_data |= PFM_PWM_SWITCH; ++ ocp_write_word(tp, MCU_TYPE_PLA, PLA_PHY_PWR, ocp_data); ++ ++ switch (tp->version) { ++ case RTL_VER_10: ++ data = ocp_reg_read(tp, 0xad40); ++ data &= ~0x3ff; ++ data |= BIT(7) | BIT(2); ++ ocp_reg_write(tp, 0xad40, data); ++ ++ data = ocp_reg_read(tp, 0xad4e); ++ data |= BIT(4); ++ ocp_reg_write(tp, 0xad4e, data); ++ data = ocp_reg_read(tp, 0xad16); ++ data &= ~0x3ff; ++ data |= 0x6; ++ ocp_reg_write(tp, 0xad16, data); ++ data = ocp_reg_read(tp, 0xad32); ++ data &= ~0x3f; ++ data |= 6; ++ ocp_reg_write(tp, 0xad32, data); ++ data = ocp_reg_read(tp, 0xac08); ++ data &= ~(BIT(12) | BIT(8)); ++ ocp_reg_write(tp, 0xac08, data); ++ data = ocp_reg_read(tp, 0xac8a); ++ data |= BIT(12) | BIT(13) | BIT(14); ++ data &= ~BIT(15); ++ ocp_reg_write(tp, 0xac8a, data); ++ data = ocp_reg_read(tp, 0xad18); ++ data |= BIT(10); ++ ocp_reg_write(tp, 0xad18, data); ++ data = ocp_reg_read(tp, 0xad1a); ++ data |= 0x3ff; ++ ocp_reg_write(tp, 0xad1a, data); ++ data = ocp_reg_read(tp, 0xad1c); ++ data |= 0x3ff; ++ ocp_reg_write(tp, 0xad1c, data); ++ ++ data = sram_read(tp, 0x80ea); ++ data &= ~0xff00; ++ data |= 0xc400; ++ sram_write(tp, 0x80ea, data); ++ data = sram_read(tp, 0x80eb); ++ data &= ~0x0700; ++ data |= 0x0300; ++ sram_write(tp, 0x80eb, data); ++ data = sram_read(tp, 0x80f8); ++ data &= ~0xff00; ++ data |= 0x1c00; ++ sram_write(tp, 0x80f8, data); ++ data = sram_read(tp, 0x80f1); ++ data &= ~0xff00; ++ data |= 0x3000; ++ sram_write(tp, 0x80f1, data); ++ ++ data = sram_read(tp, 0x80fe); ++ data &= ~0xff00; ++ data |= 0xa500; ++ sram_write(tp, 0x80fe, data); ++ data = sram_read(tp, 0x8102); ++ data &= ~0xff00; ++ data |= 0x5000; ++ sram_write(tp, 0x8102, data); ++ data = sram_read(tp, 0x8015); ++ data &= ~0xff00; ++ data |= 0x3300; ++ sram_write(tp, 0x8015, data); ++ data = sram_read(tp, 0x8100); ++ data &= ~0xff00; ++ data |= 0x7000; ++ sram_write(tp, 0x8100, data); ++ data = sram_read(tp, 0x8014); ++ data &= ~0xff00; ++ data |= 0xf000; ++ sram_write(tp, 0x8014, data); ++ data = sram_read(tp, 0x8016); ++ data &= ~0xff00; ++ data |= 0x6500; ++ sram_write(tp, 0x8016, data); ++ data = sram_read(tp, 0x80dc); ++ data &= ~0xff00; ++ data |= 0xed00; ++ sram_write(tp, 0x80dc, data); ++ data = sram_read(tp, 0x80df); ++ data |= BIT(8); ++ sram_write(tp, 0x80df, data); ++ data = sram_read(tp, 0x80e1); ++ data &= ~BIT(8); ++ sram_write(tp, 0x80e1, data); ++ ++ data = ocp_reg_read(tp, 0xbf06); ++ data &= ~0x003f; ++ data |= 0x0038; ++ ocp_reg_write(tp, 0xbf06, data); ++ ++ sram_write(tp, 0x819f, 0xddb6); ++ ++ ocp_reg_write(tp, 0xbc34, 0x5555); ++ data = ocp_reg_read(tp, 0xbf0a); ++ data &= ~0x0e00; ++ data |= 0x0a00; ++ ocp_reg_write(tp, 0xbf0a, data); ++ ++ data = ocp_reg_read(tp, 0xbd2c); ++ data &= ~BIT(13); ++ ocp_reg_write(tp, 0xbd2c, data); ++ break; ++ case RTL_VER_11: ++ data = ocp_reg_read(tp, 0xad16); ++ data |= 0x3ff; ++ ocp_reg_write(tp, 0xad16, data); ++ data = ocp_reg_read(tp, 0xad32); ++ data &= ~0x3f; ++ data |= 6; ++ ocp_reg_write(tp, 0xad32, data); ++ data = ocp_reg_read(tp, 0xac08); ++ data &= ~(BIT(12) | BIT(8)); ++ ocp_reg_write(tp, 0xac08, data); ++ data = ocp_reg_read(tp, 0xacc0); ++ data &= ~0x3; ++ data |= BIT(1); ++ ocp_reg_write(tp, 0xacc0, data); ++ data = ocp_reg_read(tp, 0xad40); ++ data &= ~0xe7; ++ data |= BIT(6) | BIT(2); ++ ocp_reg_write(tp, 0xad40, data); ++ data = ocp_reg_read(tp, 0xac14); ++ data &= ~BIT(7); ++ ocp_reg_write(tp, 0xac14, data); ++ data = ocp_reg_read(tp, 0xac80); ++ data &= ~(BIT(8) | BIT(9)); ++ ocp_reg_write(tp, 0xac80, data); ++ data = ocp_reg_read(tp, 0xac5e); ++ data &= ~0x7; ++ data |= BIT(1); ++ ocp_reg_write(tp, 0xac5e, data); ++ ocp_reg_write(tp, 0xad4c, 0x00a8); ++ ocp_reg_write(tp, 0xac5c, 0x01ff); ++ data = ocp_reg_read(tp, 0xac8a); ++ data &= ~0xf0; ++ data |= BIT(4) | BIT(5); ++ ocp_reg_write(tp, 0xac8a, data); ++ ocp_reg_write(tp, 0xb87c, 0x8157); ++ data = ocp_reg_read(tp, 0xb87e); ++ data &= ~0xff00; ++ data |= 0x0500; ++ ocp_reg_write(tp, 0xb87e, data); ++ ocp_reg_write(tp, 0xb87c, 0x8159); ++ data = ocp_reg_read(tp, 0xb87e); ++ data &= ~0xff00; ++ data |= 0x0700; ++ ocp_reg_write(tp, 0xb87e, data); ++ ++ /* AAGC */ ++ ocp_reg_write(tp, 0xb87c, 0x80a2); ++ ocp_reg_write(tp, 0xb87e, 0x0153); ++ ocp_reg_write(tp, 0xb87c, 0x809c); ++ ocp_reg_write(tp, 0xb87e, 0x0153); ++ ++ /* EEE parameter */ ++ ocp_write_word(tp, MCU_TYPE_PLA, PLA_EEE_TXTWSYS_2P5G, 0x0056); ++ ++ ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_USB_CFG); ++ ocp_data |= EN_XG_LIP | EN_G_LIP; ++ ocp_write_word(tp, MCU_TYPE_PLA, PLA_USB_CFG, ocp_data); ++ ++ sram_write(tp, 0x8257, 0x020f); /* XG PLL */ ++ sram_write(tp, 0x80ea, 0x7843); /* GIGA Master */ ++ ++ if (rtl_phy_patch_request(tp, true, true)) ++ return; ++ ++ /* Advance EEE */ ++ ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_MAC_PWR_CTRL4); ++ ocp_data |= EEE_SPDWN_EN; ++ ocp_write_word(tp, MCU_TYPE_PLA, PLA_MAC_PWR_CTRL4, ocp_data); ++ ++ data = ocp_reg_read(tp, OCP_DOWN_SPEED); ++ data &= ~(EN_EEE_100 | EN_EEE_1000); ++ data |= EN_10M_CLKDIV; ++ ocp_reg_write(tp, OCP_DOWN_SPEED, data); ++ tp->ups_info._10m_ckdiv = true; ++ tp->ups_info.eee_plloff_100 = false; ++ tp->ups_info.eee_plloff_giga = false; ++ ++ data = ocp_reg_read(tp, OCP_POWER_CFG); ++ data &= ~EEE_CLKDIV_EN; ++ ocp_reg_write(tp, OCP_POWER_CFG, data); ++ tp->ups_info.eee_ckdiv = false; ++ ++ ocp_reg_write(tp, OCP_SYSCLK_CFG, 0); ++ ocp_reg_write(tp, OCP_SYSCLK_CFG, sysclk_div_expo(5)); ++ tp->ups_info._250m_ckdiv = false; ++ ++ rtl_phy_patch_request(tp, false, true); ++ ++ /* enable ADC Ibias Cal */ ++ data = ocp_reg_read(tp, 0xd068); ++ data |= BIT(13); ++ ocp_reg_write(tp, 0xd068, data); ++ ++ /* enable Thermal Sensor */ ++ data = sram_read(tp, 0x81a2); ++ data &= ~BIT(8); ++ sram_write(tp, 0x81a2, data); ++ data = ocp_reg_read(tp, 0xb54c); ++ data &= ~0xff00; ++ data |= 0xdb00; ++ ocp_reg_write(tp, 0xb54c, data); ++ ++ /* Nway 2.5G Lite */ ++ data = ocp_reg_read(tp, 0xa454); ++ data &= ~BIT(0); ++ ocp_reg_write(tp, 0xa454, data); ++ ++ /* CS DSP solution */ ++ data = ocp_reg_read(tp, OCP_10GBT_CTRL); ++ data |= RTL_ADV2_5G_F_R; ++ ocp_reg_write(tp, OCP_10GBT_CTRL, data); ++ data = ocp_reg_read(tp, 0xad4e); ++ data &= ~BIT(4); ++ ocp_reg_write(tp, 0xad4e, data); ++ data = ocp_reg_read(tp, 0xa86a); ++ data &= ~BIT(0); ++ ocp_reg_write(tp, 0xa86a, data); ++ ++ /* MDI SWAP */ ++ if ((ocp_read_word(tp, MCU_TYPE_USB, USB_UPS_CFG) & MID_REVERSE) && ++ (ocp_reg_read(tp, 0xd068) & BIT(1))) { ++ u16 swap_a, swap_b; ++ ++ data = ocp_reg_read(tp, 0xd068); ++ data &= ~0x1f; ++ data |= 0x1; /* p0 */ ++ ocp_reg_write(tp, 0xd068, data); ++ swap_a = ocp_reg_read(tp, 0xd06a); ++ data &= ~0x18; ++ data |= 0x18; /* p3 */ ++ ocp_reg_write(tp, 0xd068, data); ++ swap_b = ocp_reg_read(tp, 0xd06a); ++ data &= ~0x18; /* p0 */ ++ ocp_reg_write(tp, 0xd068, data); ++ ocp_reg_write(tp, 0xd06a, ++ (swap_a & ~0x7ff) | (swap_b & 0x7ff)); ++ data |= 0x18; /* p3 */ ++ ocp_reg_write(tp, 0xd068, data); ++ ocp_reg_write(tp, 0xd06a, ++ (swap_b & ~0x7ff) | (swap_a & 0x7ff)); ++ data &= ~0x18; ++ data |= 0x08; /* p1 */ ++ ocp_reg_write(tp, 0xd068, data); ++ swap_a = ocp_reg_read(tp, 0xd06a); ++ data &= ~0x18; ++ data |= 0x10; /* p2 */ ++ ocp_reg_write(tp, 0xd068, data); ++ swap_b = ocp_reg_read(tp, 0xd06a); ++ data &= ~0x18; ++ data |= 0x08; /* p1 */ ++ ocp_reg_write(tp, 0xd068, data); ++ ocp_reg_write(tp, 0xd06a, ++ (swap_a & ~0x7ff) | (swap_b & 0x7ff)); ++ data &= ~0x18; ++ data |= 0x10; /* p2 */ ++ ocp_reg_write(tp, 0xd068, data); ++ ocp_reg_write(tp, 0xd06a, ++ (swap_b & ~0x7ff) | (swap_a & 0x7ff)); ++ swap_a = ocp_reg_read(tp, 0xbd5a); ++ swap_b = ocp_reg_read(tp, 0xbd5c); ++ ocp_reg_write(tp, 0xbd5a, (swap_a & ~0x1f1f) | ++ ((swap_b & 0x1f) << 8) | ++ ((swap_b >> 8) & 0x1f)); ++ ocp_reg_write(tp, 0xbd5c, (swap_b & ~0x1f1f) | ++ ((swap_a & 0x1f) << 8) | ++ ((swap_a >> 8) & 0x1f)); ++ swap_a = ocp_reg_read(tp, 0xbc18); ++ swap_b = ocp_reg_read(tp, 0xbc1a); ++ ocp_reg_write(tp, 0xbc18, (swap_a & ~0x1f1f) | ++ ((swap_b & 0x1f) << 8) | ++ ((swap_b >> 8) & 0x1f)); ++ ocp_reg_write(tp, 0xbc1a, (swap_b & ~0x1f1f) | ++ ((swap_a & 0x1f) << 8) | ++ ((swap_a >> 8) & 0x1f)); ++ } ++ break; ++ default: ++ break; ++ } ++ ++ rtl_green_en(tp, test_bit(GREEN_ETHERNET, &tp->flags)); ++ ++ data = ocp_reg_read(tp, 0xa428); ++ data &= ~BIT(9); ++ ocp_reg_write(tp, 0xa428, data); ++ data = ocp_reg_read(tp, 0xa5ea); ++ data &= ~BIT(0); ++ ocp_reg_write(tp, 0xa5ea, data); ++ tp->ups_info.lite_mode = 0; ++ ++ if (tp->eee_en) ++ rtl_eee_enable(tp, true); ++ ++ r8153_aldps_en(tp, true); ++ r8152b_enable_fc(tp); ++ r8153_u2p3en(tp, true); ++ ++ set_bit(PHY_RESET, &tp->flags); ++} ++ ++static void r8156b_hw_phy_cfg(struct r8152 *tp) ++{ ++ u32 ocp_data; ++ u16 data; ++ ++ switch (tp->version) { ++ case RTL_VER_12: ++ ocp_reg_write(tp, 0xbf86, 0x9000); ++ data = ocp_reg_read(tp, 0xc402); ++ data |= BIT(10); ++ ocp_reg_write(tp, 0xc402, data); ++ data &= ~BIT(10); ++ ocp_reg_write(tp, 0xc402, data); ++ ocp_reg_write(tp, 0xbd86, 0x1010); ++ ocp_reg_write(tp, 0xbd88, 0x1010); ++ data = ocp_reg_read(tp, 0xbd4e); ++ data &= ~(BIT(10) | BIT(11)); ++ data |= BIT(11); ++ ocp_reg_write(tp, 0xbd4e, data); ++ data = ocp_reg_read(tp, 0xbf46); ++ data &= ~0xf00; ++ data |= 0x700; ++ ocp_reg_write(tp, 0xbf46, data); ++ break; ++ case RTL_VER_13: ++ case RTL_VER_15: ++ r8156b_wait_loading_flash(tp); ++ break; ++ default: ++ break; ++ } ++ ++ ocp_data = ocp_read_word(tp, MCU_TYPE_USB, USB_MISC_0); ++ if (ocp_data & PCUT_STATUS) { ++ ocp_data &= ~PCUT_STATUS; ++ ocp_write_word(tp, MCU_TYPE_USB, USB_MISC_0, ocp_data); ++ } ++ ++ data = r8153_phy_status(tp, 0); ++ switch (data) { ++ case PHY_STAT_EXT_INIT: ++ rtl8152_apply_firmware(tp, true); ++ ++ data = ocp_reg_read(tp, 0xa466); ++ data &= ~BIT(0); ++ ocp_reg_write(tp, 0xa466, data); ++ ++ data = ocp_reg_read(tp, 0xa468); ++ data &= ~(BIT(3) | BIT(1)); ++ ocp_reg_write(tp, 0xa468, data); ++ break; ++ case PHY_STAT_LAN_ON: ++ case PHY_STAT_PWRDN: ++ default: ++ rtl8152_apply_firmware(tp, false); ++ break; ++ } ++ ++ data = r8152_mdio_read(tp, MII_BMCR); ++ if (data & BMCR_PDOWN) { ++ data &= ~BMCR_PDOWN; ++ r8152_mdio_write(tp, MII_BMCR, data); ++ } ++ ++ /* disable ALDPS before updating the PHY parameters */ ++ r8153_aldps_en(tp, false); ++ ++ /* disable EEE before updating the PHY parameters */ ++ rtl_eee_enable(tp, false); ++ ++ data = r8153_phy_status(tp, PHY_STAT_LAN_ON); ++ WARN_ON_ONCE(data != PHY_STAT_LAN_ON); ++ ++ ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_PHY_PWR); ++ ocp_data |= PFM_PWM_SWITCH; ++ ocp_write_word(tp, MCU_TYPE_PLA, PLA_PHY_PWR, ocp_data); ++ ++ switch (tp->version) { ++ case RTL_VER_12: ++ data = ocp_reg_read(tp, 0xbc08); ++ data |= BIT(3) | BIT(2); ++ ocp_reg_write(tp, 0xbc08, data); ++ ++ data = sram_read(tp, 0x8fff); ++ data &= ~0xff00; ++ data |= 0x0400; ++ sram_write(tp, 0x8fff, data); ++ ++ data = ocp_reg_read(tp, 0xacda); ++ data |= 0xff00; ++ ocp_reg_write(tp, 0xacda, data); ++ data = ocp_reg_read(tp, 0xacde); ++ data |= 0xf000; ++ ocp_reg_write(tp, 0xacde, data); ++ ocp_reg_write(tp, 0xac8c, 0x0ffc); ++ ocp_reg_write(tp, 0xac46, 0xb7b4); ++ ocp_reg_write(tp, 0xac50, 0x0fbc); ++ ocp_reg_write(tp, 0xac3c, 0x9240); ++ ocp_reg_write(tp, 0xac4e, 0x0db4); ++ ocp_reg_write(tp, 0xacc6, 0x0707); ++ ocp_reg_write(tp, 0xacc8, 0xa0d3); ++ ocp_reg_write(tp, 0xad08, 0x0007); ++ ++ ocp_reg_write(tp, 0xb87c, 0x8560); ++ ocp_reg_write(tp, 0xb87e, 0x19cc); ++ ocp_reg_write(tp, 0xb87c, 0x8562); ++ ocp_reg_write(tp, 0xb87e, 0x19cc); ++ ocp_reg_write(tp, 0xb87c, 0x8564); ++ ocp_reg_write(tp, 0xb87e, 0x19cc); ++ ocp_reg_write(tp, 0xb87c, 0x8566); ++ ocp_reg_write(tp, 0xb87e, 0x147d); ++ ocp_reg_write(tp, 0xb87c, 0x8568); ++ ocp_reg_write(tp, 0xb87e, 0x147d); ++ ocp_reg_write(tp, 0xb87c, 0x856a); ++ ocp_reg_write(tp, 0xb87e, 0x147d); ++ ocp_reg_write(tp, 0xb87c, 0x8ffe); ++ ocp_reg_write(tp, 0xb87e, 0x0907); ++ ocp_reg_write(tp, 0xb87c, 0x80d6); ++ ocp_reg_write(tp, 0xb87e, 0x2801); ++ ocp_reg_write(tp, 0xb87c, 0x80f2); ++ ocp_reg_write(tp, 0xb87e, 0x2801); ++ ocp_reg_write(tp, 0xb87c, 0x80f4); ++ ocp_reg_write(tp, 0xb87e, 0x6077); ++ ocp_reg_write(tp, 0xb506, 0x01e7); ++ ++ ocp_reg_write(tp, 0xb87c, 0x8013); ++ ocp_reg_write(tp, 0xb87e, 0x0700); ++ ocp_reg_write(tp, 0xb87c, 0x8fb9); ++ ocp_reg_write(tp, 0xb87e, 0x2801); ++ ocp_reg_write(tp, 0xb87c, 0x8fba); ++ ocp_reg_write(tp, 0xb87e, 0x0100); ++ ocp_reg_write(tp, 0xb87c, 0x8fbc); ++ ocp_reg_write(tp, 0xb87e, 0x1900); ++ ocp_reg_write(tp, 0xb87c, 0x8fbe); ++ ocp_reg_write(tp, 0xb87e, 0xe100); ++ ocp_reg_write(tp, 0xb87c, 0x8fc0); ++ ocp_reg_write(tp, 0xb87e, 0x0800); ++ ocp_reg_write(tp, 0xb87c, 0x8fc2); ++ ocp_reg_write(tp, 0xb87e, 0xe500); ++ ocp_reg_write(tp, 0xb87c, 0x8fc4); ++ ocp_reg_write(tp, 0xb87e, 0x0f00); ++ ocp_reg_write(tp, 0xb87c, 0x8fc6); ++ ocp_reg_write(tp, 0xb87e, 0xf100); ++ ocp_reg_write(tp, 0xb87c, 0x8fc8); ++ ocp_reg_write(tp, 0xb87e, 0x0400); ++ ocp_reg_write(tp, 0xb87c, 0x8fca); ++ ocp_reg_write(tp, 0xb87e, 0xf300); ++ ocp_reg_write(tp, 0xb87c, 0x8fcc); ++ ocp_reg_write(tp, 0xb87e, 0xfd00); ++ ocp_reg_write(tp, 0xb87c, 0x8fce); ++ ocp_reg_write(tp, 0xb87e, 0xff00); ++ ocp_reg_write(tp, 0xb87c, 0x8fd0); ++ ocp_reg_write(tp, 0xb87e, 0xfb00); ++ ocp_reg_write(tp, 0xb87c, 0x8fd2); ++ ocp_reg_write(tp, 0xb87e, 0x0100); ++ ocp_reg_write(tp, 0xb87c, 0x8fd4); ++ ocp_reg_write(tp, 0xb87e, 0xf400); ++ ocp_reg_write(tp, 0xb87c, 0x8fd6); ++ ocp_reg_write(tp, 0xb87e, 0xff00); ++ ocp_reg_write(tp, 0xb87c, 0x8fd8); ++ ocp_reg_write(tp, 0xb87e, 0xf600); ++ ++ ocp_data = ocp_read_byte(tp, MCU_TYPE_PLA, PLA_USB_CFG); ++ ocp_data |= EN_XG_LIP | EN_G_LIP; ++ ocp_write_byte(tp, MCU_TYPE_PLA, PLA_USB_CFG, ocp_data); ++ ocp_reg_write(tp, 0xb87c, 0x813d); ++ ocp_reg_write(tp, 0xb87e, 0x390e); ++ ocp_reg_write(tp, 0xb87c, 0x814f); ++ ocp_reg_write(tp, 0xb87e, 0x790e); ++ ocp_reg_write(tp, 0xb87c, 0x80b0); ++ ocp_reg_write(tp, 0xb87e, 0x0f31); ++ data = ocp_reg_read(tp, 0xbf4c); ++ data |= BIT(1); ++ ocp_reg_write(tp, 0xbf4c, data); ++ data = ocp_reg_read(tp, 0xbcca); ++ data |= BIT(9) | BIT(8); ++ ocp_reg_write(tp, 0xbcca, data); ++ ocp_reg_write(tp, 0xb87c, 0x8141); ++ ocp_reg_write(tp, 0xb87e, 0x320e); ++ ocp_reg_write(tp, 0xb87c, 0x8153); ++ ocp_reg_write(tp, 0xb87e, 0x720e); ++ ocp_reg_write(tp, 0xb87c, 0x8529); ++ ocp_reg_write(tp, 0xb87e, 0x050e); ++ data = ocp_reg_read(tp, OCP_EEE_CFG); ++ data &= ~CTAP_SHORT_EN; ++ ocp_reg_write(tp, OCP_EEE_CFG, data); ++ ++ sram_write(tp, 0x816c, 0xc4a0); ++ sram_write(tp, 0x8170, 0xc4a0); ++ sram_write(tp, 0x8174, 0x04a0); ++ sram_write(tp, 0x8178, 0x04a0); ++ sram_write(tp, 0x817c, 0x0719); ++ sram_write(tp, 0x8ff4, 0x0400); ++ sram_write(tp, 0x8ff1, 0x0404); ++ ++ ocp_reg_write(tp, 0xbf4a, 0x001b); ++ ocp_reg_write(tp, 0xb87c, 0x8033); ++ ocp_reg_write(tp, 0xb87e, 0x7c13); ++ ocp_reg_write(tp, 0xb87c, 0x8037); ++ ocp_reg_write(tp, 0xb87e, 0x7c13); ++ ocp_reg_write(tp, 0xb87c, 0x803b); ++ ocp_reg_write(tp, 0xb87e, 0xfc32); ++ ocp_reg_write(tp, 0xb87c, 0x803f); ++ ocp_reg_write(tp, 0xb87e, 0x7c13); ++ ocp_reg_write(tp, 0xb87c, 0x8043); ++ ocp_reg_write(tp, 0xb87e, 0x7c13); ++ ocp_reg_write(tp, 0xb87c, 0x8047); ++ ocp_reg_write(tp, 0xb87e, 0x7c13); ++ ++ ocp_reg_write(tp, 0xb87c, 0x8145); ++ ocp_reg_write(tp, 0xb87e, 0x370e); ++ ocp_reg_write(tp, 0xb87c, 0x8157); ++ ocp_reg_write(tp, 0xb87e, 0x770e); ++ ocp_reg_write(tp, 0xb87c, 0x8169); ++ ocp_reg_write(tp, 0xb87e, 0x0d0a); ++ ocp_reg_write(tp, 0xb87c, 0x817b); ++ ocp_reg_write(tp, 0xb87e, 0x1d0a); ++ ++ data = sram_read(tp, 0x8217); ++ data &= ~0xff00; ++ data |= 0x5000; ++ sram_write(tp, 0x8217, data); ++ data = sram_read(tp, 0x821a); ++ data &= ~0xff00; ++ data |= 0x5000; ++ sram_write(tp, 0x821a, data); ++ sram_write(tp, 0x80da, 0x0403); ++ data = sram_read(tp, 0x80dc); ++ data &= ~0xff00; ++ data |= 0x1000; ++ sram_write(tp, 0x80dc, data); ++ sram_write(tp, 0x80b3, 0x0384); ++ sram_write(tp, 0x80b7, 0x2007); ++ data = sram_read(tp, 0x80ba); ++ data &= ~0xff00; ++ data |= 0x6c00; ++ sram_write(tp, 0x80ba, data); ++ sram_write(tp, 0x80b5, 0xf009); ++ data = sram_read(tp, 0x80bd); ++ data &= ~0xff00; ++ data |= 0x9f00; ++ sram_write(tp, 0x80bd, data); ++ sram_write(tp, 0x80c7, 0xf083); ++ sram_write(tp, 0x80dd, 0x03f0); ++ data = sram_read(tp, 0x80df); ++ data &= ~0xff00; ++ data |= 0x1000; ++ sram_write(tp, 0x80df, data); ++ sram_write(tp, 0x80cb, 0x2007); ++ data = sram_read(tp, 0x80ce); ++ data &= ~0xff00; ++ data |= 0x6c00; ++ sram_write(tp, 0x80ce, data); ++ sram_write(tp, 0x80c9, 0x8009); ++ data = sram_read(tp, 0x80d1); ++ data &= ~0xff00; ++ data |= 0x8000; ++ sram_write(tp, 0x80d1, data); ++ sram_write(tp, 0x80a3, 0x200a); ++ sram_write(tp, 0x80a5, 0xf0ad); ++ sram_write(tp, 0x809f, 0x6073); ++ sram_write(tp, 0x80a1, 0x000b); ++ data = sram_read(tp, 0x80a9); ++ data &= ~0xff00; ++ data |= 0xc000; ++ sram_write(tp, 0x80a9, data); ++ ++ if (rtl_phy_patch_request(tp, true, true)) ++ return; ++ ++ data = ocp_reg_read(tp, 0xb896); ++ data &= ~BIT(0); ++ ocp_reg_write(tp, 0xb896, data); ++ data = ocp_reg_read(tp, 0xb892); ++ data &= ~0xff00; ++ ocp_reg_write(tp, 0xb892, data); ++ ocp_reg_write(tp, 0xb88e, 0xc23e); ++ ocp_reg_write(tp, 0xb890, 0x0000); ++ ocp_reg_write(tp, 0xb88e, 0xc240); ++ ocp_reg_write(tp, 0xb890, 0x0103); ++ ocp_reg_write(tp, 0xb88e, 0xc242); ++ ocp_reg_write(tp, 0xb890, 0x0507); ++ ocp_reg_write(tp, 0xb88e, 0xc244); ++ ocp_reg_write(tp, 0xb890, 0x090b); ++ ocp_reg_write(tp, 0xb88e, 0xc246); ++ ocp_reg_write(tp, 0xb890, 0x0c0e); ++ ocp_reg_write(tp, 0xb88e, 0xc248); ++ ocp_reg_write(tp, 0xb890, 0x1012); ++ ocp_reg_write(tp, 0xb88e, 0xc24a); ++ ocp_reg_write(tp, 0xb890, 0x1416); ++ data = ocp_reg_read(tp, 0xb896); ++ data |= BIT(0); ++ ocp_reg_write(tp, 0xb896, data); ++ ++ rtl_phy_patch_request(tp, false, true); ++ ++ data = ocp_reg_read(tp, 0xa86a); ++ data |= BIT(0); ++ ocp_reg_write(tp, 0xa86a, data); ++ data = ocp_reg_read(tp, 0xa6f0); ++ data |= BIT(0); ++ ocp_reg_write(tp, 0xa6f0, data); ++ ++ ocp_reg_write(tp, 0xbfa0, 0xd70d); ++ ocp_reg_write(tp, 0xbfa2, 0x4100); ++ ocp_reg_write(tp, 0xbfa4, 0xe868); ++ ocp_reg_write(tp, 0xbfa6, 0xdc59); ++ ocp_reg_write(tp, 0xb54c, 0x3c18); ++ data = ocp_reg_read(tp, 0xbfa4); ++ data &= ~BIT(5); ++ ocp_reg_write(tp, 0xbfa4, data); ++ data = sram_read(tp, 0x817d); ++ data |= BIT(12); ++ sram_write(tp, 0x817d, data); ++ break; ++ case RTL_VER_13: ++ /* 2.5G INRX */ ++ data = ocp_reg_read(tp, 0xac46); ++ data &= ~0x00f0; ++ data |= 0x0090; ++ ocp_reg_write(tp, 0xac46, data); ++ data = ocp_reg_read(tp, 0xad30); ++ data &= ~0x0003; ++ data |= 0x0001; ++ ocp_reg_write(tp, 0xad30, data); ++ fallthrough; ++ case RTL_VER_15: ++ /* EEE parameter */ ++ ocp_reg_write(tp, 0xb87c, 0x80f5); ++ ocp_reg_write(tp, 0xb87e, 0x760e); ++ ocp_reg_write(tp, 0xb87c, 0x8107); ++ ocp_reg_write(tp, 0xb87e, 0x360e); ++ ocp_reg_write(tp, 0xb87c, 0x8551); ++ data = ocp_reg_read(tp, 0xb87e); ++ data &= ~0xff00; ++ data |= 0x0800; ++ ocp_reg_write(tp, 0xb87e, data); ++ ++ /* ADC_PGA parameter */ ++ data = ocp_reg_read(tp, 0xbf00); ++ data &= ~0xe000; ++ data |= 0xa000; ++ ocp_reg_write(tp, 0xbf00, data); ++ data = ocp_reg_read(tp, 0xbf46); ++ data &= ~0x0f00; ++ data |= 0x0300; ++ ocp_reg_write(tp, 0xbf46, data); ++ ++ /* Green Table-PGA, 1G full viterbi */ ++ sram_write(tp, 0x8044, 0x2417); ++ sram_write(tp, 0x804a, 0x2417); ++ sram_write(tp, 0x8050, 0x2417); ++ sram_write(tp, 0x8056, 0x2417); ++ sram_write(tp, 0x805c, 0x2417); ++ sram_write(tp, 0x8062, 0x2417); ++ sram_write(tp, 0x8068, 0x2417); ++ sram_write(tp, 0x806e, 0x2417); ++ sram_write(tp, 0x8074, 0x2417); ++ sram_write(tp, 0x807a, 0x2417); ++ ++ /* XG PLL */ ++ data = ocp_reg_read(tp, 0xbf84); ++ data &= ~0xe000; ++ data |= 0xa000; ++ ocp_reg_write(tp, 0xbf84, data); ++ break; ++ default: ++ break; ++ } ++ ++ if (rtl_phy_patch_request(tp, true, true)) ++ return; ++ ++ ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_MAC_PWR_CTRL4); ++ ocp_data |= EEE_SPDWN_EN; ++ ocp_write_word(tp, MCU_TYPE_PLA, PLA_MAC_PWR_CTRL4, ocp_data); ++ ++ data = ocp_reg_read(tp, OCP_DOWN_SPEED); ++ data &= ~(EN_EEE_100 | EN_EEE_1000); ++ data |= EN_10M_CLKDIV; ++ ocp_reg_write(tp, OCP_DOWN_SPEED, data); ++ tp->ups_info._10m_ckdiv = true; ++ tp->ups_info.eee_plloff_100 = false; ++ tp->ups_info.eee_plloff_giga = false; ++ ++ data = ocp_reg_read(tp, OCP_POWER_CFG); ++ data &= ~EEE_CLKDIV_EN; ++ ocp_reg_write(tp, OCP_POWER_CFG, data); ++ tp->ups_info.eee_ckdiv = false; ++ ++ rtl_phy_patch_request(tp, false, true); ++ ++ rtl_green_en(tp, test_bit(GREEN_ETHERNET, &tp->flags)); ++ ++ data = ocp_reg_read(tp, 0xa428); ++ data &= ~BIT(9); ++ ocp_reg_write(tp, 0xa428, data); ++ data = ocp_reg_read(tp, 0xa5ea); ++ data &= ~BIT(0); ++ ocp_reg_write(tp, 0xa5ea, data); ++ tp->ups_info.lite_mode = 0; ++ ++ if (tp->eee_en) ++ rtl_eee_enable(tp, true); ++ ++ r8153_aldps_en(tp, true); ++ r8152b_enable_fc(tp); ++ r8153_u2p3en(tp, true); ++ ++ set_bit(PHY_RESET, &tp->flags); ++} ++ ++static void r8156_init(struct r8152 *tp) ++{ ++ u32 ocp_data; ++ u16 data; ++ int i; ++ ++ if (test_bit(RTL8152_UNPLUG, &tp->flags)) ++ return; ++ ++ ocp_data = ocp_read_byte(tp, MCU_TYPE_USB, USB_ECM_OP); ++ ocp_data &= ~EN_ALL_SPEED; ++ ocp_write_byte(tp, MCU_TYPE_USB, USB_ECM_OP, ocp_data); ++ ++ ocp_write_word(tp, MCU_TYPE_USB, USB_SPEED_OPTION, 0); ++ ++ ocp_data = ocp_read_word(tp, MCU_TYPE_USB, USB_ECM_OPTION); ++ ocp_data |= BYPASS_MAC_RESET; ++ ocp_write_word(tp, MCU_TYPE_USB, USB_ECM_OPTION, ocp_data); ++ ++ r8153b_u1u2en(tp, false); ++ ++ for (i = 0; i < 500; i++) { ++ if (ocp_read_word(tp, MCU_TYPE_PLA, PLA_BOOT_CTRL) & ++ AUTOLOAD_DONE) ++ break; ++ ++ msleep(20); ++ if (test_bit(RTL8152_UNPLUG, &tp->flags)) ++ return; ++ } ++ ++ data = r8153_phy_status(tp, 0); ++ if (data == PHY_STAT_EXT_INIT) { ++ data = ocp_reg_read(tp, 0xa468); ++ data &= ~(BIT(3) | BIT(1)); ++ ocp_reg_write(tp, 0xa468, data); ++ } ++ ++ data = r8152_mdio_read(tp, MII_BMCR); ++ if (data & BMCR_PDOWN) { ++ data &= ~BMCR_PDOWN; ++ r8152_mdio_write(tp, MII_BMCR, data); ++ } ++ ++ data = r8153_phy_status(tp, PHY_STAT_LAN_ON); ++ WARN_ON_ONCE(data != PHY_STAT_LAN_ON); ++ ++ r8153_u2p3en(tp, false); ++ ++ /* MSC timer = 0xfff * 8ms = 32760 ms */ ++ ocp_write_word(tp, MCU_TYPE_USB, USB_MSC_TIMER, 0x0fff); ++ ++ /* U1/U2/L1 idle timer. 500 us */ ++ ocp_write_word(tp, MCU_TYPE_USB, USB_U1U2_TIMER, 500); ++ ++ r8153b_power_cut_en(tp, false); ++ r8156_ups_en(tp, false); ++ r8153_queue_wake(tp, false); ++ rtl_runtime_suspend_enable(tp, false); ++ ++ if (tp->udev->speed >= USB_SPEED_SUPER) ++ r8153b_u1u2en(tp, true); ++ ++ usb_enable_lpm(tp->udev); ++ ++ r8156_mac_clk_spd(tp, true); ++ ++ ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_MAC_PWR_CTRL3); ++ ocp_data &= ~PLA_MCU_SPDWN_EN; ++ ocp_write_word(tp, MCU_TYPE_PLA, PLA_MAC_PWR_CTRL3, ocp_data); ++ ++ ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_EXTRA_STATUS); ++ if (rtl8152_get_speed(tp) & LINK_STATUS) ++ ocp_data |= CUR_LINK_OK; ++ else ++ ocp_data &= ~CUR_LINK_OK; ++ ocp_data |= POLL_LINK_CHG; ++ ocp_write_word(tp, MCU_TYPE_PLA, PLA_EXTRA_STATUS, ocp_data); ++ ++ set_bit(GREEN_ETHERNET, &tp->flags); ++ ++ /* rx aggregation */ ++ 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); ++ ++ ocp_data = ocp_read_byte(tp, MCU_TYPE_USB, USB_BMU_CONFIG); ++ ocp_data |= ACT_ODMA; ++ ocp_write_byte(tp, MCU_TYPE_USB, USB_BMU_CONFIG, ocp_data); ++ ++ rtl_tally_reset(tp); ++ ++ tp->coalesce = 15000; /* 15 us */ ++} ++ ++static void r8156b_init(struct r8152 *tp) ++{ ++ u32 ocp_data; ++ u16 data; ++ int i; ++ ++ if (test_bit(RTL8152_UNPLUG, &tp->flags)) ++ return; ++ ++ ocp_data = ocp_read_byte(tp, MCU_TYPE_USB, USB_ECM_OP); ++ ocp_data &= ~EN_ALL_SPEED; ++ ocp_write_byte(tp, MCU_TYPE_USB, USB_ECM_OP, ocp_data); ++ ++ ocp_write_word(tp, MCU_TYPE_USB, USB_SPEED_OPTION, 0); ++ ++ ocp_data = ocp_read_word(tp, MCU_TYPE_USB, USB_ECM_OPTION); ++ ocp_data |= BYPASS_MAC_RESET; ++ ocp_write_word(tp, MCU_TYPE_USB, USB_ECM_OPTION, ocp_data); ++ ++ ocp_data = ocp_read_word(tp, MCU_TYPE_USB, USB_U2P3_CTRL); ++ ocp_data |= RX_DETECT8; ++ ocp_write_word(tp, MCU_TYPE_USB, USB_U2P3_CTRL, ocp_data); ++ ++ r8153b_u1u2en(tp, false); ++ ++ switch (tp->version) { ++ case RTL_VER_13: ++ case RTL_VER_15: ++ r8156b_wait_loading_flash(tp); ++ break; ++ default: ++ break; ++ } ++ ++ for (i = 0; i < 500; i++) { ++ if (ocp_read_word(tp, MCU_TYPE_PLA, PLA_BOOT_CTRL) & ++ AUTOLOAD_DONE) ++ break; ++ ++ msleep(20); ++ if (test_bit(RTL8152_UNPLUG, &tp->flags)) ++ return; ++ } ++ ++ data = r8153_phy_status(tp, 0); ++ if (data == PHY_STAT_EXT_INIT) { ++ data = ocp_reg_read(tp, 0xa468); ++ data &= ~(BIT(3) | BIT(1)); ++ ocp_reg_write(tp, 0xa468, data); ++ ++ data = ocp_reg_read(tp, 0xa466); ++ data &= ~BIT(0); ++ ocp_reg_write(tp, 0xa466, data); ++ } ++ ++ data = r8152_mdio_read(tp, MII_BMCR); ++ if (data & BMCR_PDOWN) { ++ data &= ~BMCR_PDOWN; ++ r8152_mdio_write(tp, MII_BMCR, data); ++ } ++ ++ data = r8153_phy_status(tp, PHY_STAT_LAN_ON); ++ ++ r8153_u2p3en(tp, false); ++ ++ /* MSC timer = 0xfff * 8ms = 32760 ms */ ++ ocp_write_word(tp, MCU_TYPE_USB, USB_MSC_TIMER, 0x0fff); ++ ++ /* U1/U2/L1 idle timer. 500 us */ ++ ocp_write_word(tp, MCU_TYPE_USB, USB_U1U2_TIMER, 500); ++ ++ r8153b_power_cut_en(tp, false); ++ r8156_ups_en(tp, false); ++ r8153_queue_wake(tp, false); ++ rtl_runtime_suspend_enable(tp, false); ++ ++ if (tp->udev->speed >= USB_SPEED_SUPER) ++ r8153b_u1u2en(tp, true); ++ ++ usb_enable_lpm(tp->udev); ++ ++ ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_RCR); ++ ocp_data &= ~SLOT_EN; ++ ocp_write_word(tp, MCU_TYPE_PLA, PLA_RCR, ocp_data); ++ ++ ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_CPCR); ++ ocp_data |= FLOW_CTRL_EN; ++ ocp_write_word(tp, MCU_TYPE_PLA, PLA_CPCR, ocp_data); ++ ++ /* enable fc timer and set timer to 600 ms. */ ++ ocp_write_word(tp, MCU_TYPE_USB, USB_FC_TIMER, ++ CTRL_TIMER_EN | (600 / 8)); ++ ++ ocp_data = ocp_read_word(tp, MCU_TYPE_USB, USB_FW_CTRL); ++ if (!(ocp_read_word(tp, MCU_TYPE_PLA, PLA_POL_GPIO_CTRL) & DACK_DET_EN)) ++ ocp_data |= FLOW_CTRL_PATCH_2; ++ ocp_data &= ~AUTO_SPEEDUP; ++ ocp_write_word(tp, MCU_TYPE_USB, USB_FW_CTRL, ocp_data); ++ ++ ocp_data = ocp_read_word(tp, MCU_TYPE_USB, USB_FW_TASK); ++ ocp_data |= FC_PATCH_TASK; ++ ocp_write_word(tp, MCU_TYPE_USB, USB_FW_TASK, ocp_data); ++ ++ r8156_mac_clk_spd(tp, true); ++ ++ ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_MAC_PWR_CTRL3); ++ ocp_data &= ~PLA_MCU_SPDWN_EN; ++ ocp_write_word(tp, MCU_TYPE_PLA, PLA_MAC_PWR_CTRL3, ocp_data); ++ ++ ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_EXTRA_STATUS); ++ if (rtl8152_get_speed(tp) & LINK_STATUS) ++ ocp_data |= CUR_LINK_OK; ++ else ++ ocp_data &= ~CUR_LINK_OK; ++ ocp_data |= POLL_LINK_CHG; ++ ocp_write_word(tp, MCU_TYPE_PLA, PLA_EXTRA_STATUS, ocp_data); ++ ++ set_bit(GREEN_ETHERNET, &tp->flags); ++ ++ /* rx aggregation */ ++ 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); ++ ++ rtl_tally_reset(tp); ++ ++ tp->coalesce = 15000; /* 15 us */ ++} ++ + static int rtl8152_pre_reset(struct usb_interface *intf) + { + struct r8152 *tp = usb_get_intfdata(intf); +@@ -5992,6 +7924,22 @@ int rtl8152_get_link_ksettings(struct ne + + mii_ethtool_get_link_ksettings(&tp->mii, cmd); + ++ linkmode_mod_bit(ETHTOOL_LINK_MODE_2500baseT_Full_BIT, ++ cmd->link_modes.supported, tp->support_2500full); ++ ++ if (tp->support_2500full) { ++ linkmode_mod_bit(ETHTOOL_LINK_MODE_2500baseT_Full_BIT, ++ cmd->link_modes.advertising, ++ ocp_reg_read(tp, OCP_10GBT_CTRL) & MDIO_AN_10GBT_CTRL_ADV2_5G); ++ ++ linkmode_mod_bit(ETHTOOL_LINK_MODE_2500baseT_Full_BIT, ++ cmd->link_modes.lp_advertising, ++ ocp_reg_read(tp, OCP_10GBT_STAT) & MDIO_AN_10GBT_STAT_LP2_5G); ++ ++ if (is_speed_2500(rtl8152_get_speed(tp))) ++ cmd->base.speed = SPEED_2500; ++ } ++ + mutex_unlock(&tp->control); + + usb_autopm_put_interface(tp->intf); +@@ -6035,6 +7983,10 @@ static int rtl8152_set_link_ksettings(st + cmd->link_modes.advertising)) + advertising |= RTL_ADVERTISED_1000_FULL; + ++ if (test_bit(ETHTOOL_LINK_MODE_2500baseT_Full_BIT, ++ cmd->link_modes.advertising)) ++ advertising |= RTL_ADVERTISED_2500_FULL; ++ + mutex_lock(&tp->control); + + ret = rtl8152_set_speed(tp, cmd->base.autoneg, cmd->base.speed, +@@ -6624,6 +8576,67 @@ static int rtl_ops_init(struct r8152 *tp + tp->eee_adv = MDIO_EEE_1000T | MDIO_EEE_100TX; + break; + ++ case RTL_VER_11: ++ tp->eee_en = true; ++ tp->eee_adv = MDIO_EEE_1000T | MDIO_EEE_100TX; ++ fallthrough; ++ case RTL_VER_10: ++ ops->init = r8156_init; ++ ops->enable = rtl8156_enable; ++ ops->disable = rtl8153_disable; ++ ops->up = rtl8156_up; ++ ops->down = rtl8156_down; ++ ops->unload = rtl8153_unload; ++ ops->eee_get = r8153_get_eee; ++ ops->eee_set = r8152_set_eee; ++ ops->in_nway = rtl8153_in_nway; ++ ops->hw_phy_cfg = r8156_hw_phy_cfg; ++ ops->autosuspend_en = rtl8156_runtime_enable; ++ ops->change_mtu = rtl8156_change_mtu; ++ tp->rx_buf_sz = 48 * 1024; ++ tp->support_2500full = 1; ++ break; ++ ++ case RTL_VER_12: ++ case RTL_VER_13: ++ tp->support_2500full = 1; ++ fallthrough; ++ case RTL_VER_15: ++ tp->eee_en = true; ++ tp->eee_adv = MDIO_EEE_1000T | MDIO_EEE_100TX; ++ ops->init = r8156b_init; ++ ops->enable = rtl8156b_enable; ++ ops->disable = rtl8153_disable; ++ ops->up = rtl8156_up; ++ ops->down = rtl8156_down; ++ ops->unload = rtl8153_unload; ++ ops->eee_get = r8153_get_eee; ++ ops->eee_set = r8152_set_eee; ++ ops->in_nway = rtl8153_in_nway; ++ ops->hw_phy_cfg = r8156b_hw_phy_cfg; ++ ops->autosuspend_en = rtl8156_runtime_enable; ++ ops->change_mtu = rtl8156_change_mtu; ++ tp->rx_buf_sz = 48 * 1024; ++ break; ++ ++ case RTL_VER_14: ++ ops->init = r8153c_init; ++ ops->enable = rtl8153_enable; ++ ops->disable = rtl8153_disable; ++ ops->up = rtl8153c_up; ++ ops->down = rtl8153b_down; ++ ops->unload = rtl8153_unload; ++ ops->eee_get = r8153_get_eee; ++ ops->eee_set = r8152_set_eee; ++ ops->in_nway = rtl8153_in_nway; ++ ops->hw_phy_cfg = r8153c_hw_phy_cfg; ++ ops->autosuspend_en = rtl8153c_runtime_enable; ++ ops->change_mtu = rtl8153c_change_mtu; ++ tp->rx_buf_sz = 32 * 1024; ++ tp->eee_en = true; ++ tp->eee_adv = MDIO_EEE_1000T | MDIO_EEE_100TX; ++ break; ++ + default: + ret = -ENODEV; + dev_err(&tp->intf->dev, "Unknown Device\n"); +@@ -6637,11 +8650,13 @@ static int rtl_ops_init(struct r8152 *tp + #define FIRMWARE_8153A_3 "rtl_nic/rtl8153a-3.fw" + #define FIRMWARE_8153A_4 "rtl_nic/rtl8153a-4.fw" + #define FIRMWARE_8153B_2 "rtl_nic/rtl8153b-2.fw" ++#define FIRMWARE_8153C_1 "rtl_nic/rtl8153c-1.fw" + + MODULE_FIRMWARE(FIRMWARE_8153A_2); + MODULE_FIRMWARE(FIRMWARE_8153A_3); + MODULE_FIRMWARE(FIRMWARE_8153A_4); + MODULE_FIRMWARE(FIRMWARE_8153B_2); ++MODULE_FIRMWARE(FIRMWARE_8153C_1); + + static int rtl_fw_init(struct r8152 *tp) + { +@@ -6667,6 +8682,11 @@ static int rtl_fw_init(struct r8152 *tp) + rtl_fw->pre_fw = r8153b_pre_firmware_1; + rtl_fw->post_fw = r8153b_post_firmware_1; + break; ++ case RTL_VER_14: ++ rtl_fw->fw_name = FIRMWARE_8153C_1; ++ rtl_fw->pre_fw = r8153b_pre_firmware_1; ++ rtl_fw->post_fw = r8153c_post_firmware_1; ++ break; + default: + break; + } +@@ -6722,6 +8742,27 @@ u8 rtl8152_get_version(struct usb_interf + case 0x6010: + version = RTL_VER_09; + break; ++ case 0x7010: ++ version = RTL_TEST_01; ++ break; ++ case 0x7020: ++ version = RTL_VER_10; ++ break; ++ case 0x7030: ++ version = RTL_VER_11; ++ break; ++ case 0x7400: ++ version = RTL_VER_12; ++ break; ++ case 0x7410: ++ version = RTL_VER_13; ++ break; ++ case 0x6400: ++ version = RTL_VER_14; ++ break; ++ case 0x7420: ++ version = RTL_VER_15; ++ break; + default: + version = RTL_VER_UNKNOWN; + dev_info(&intf->dev, "Unknown version 0x%04x\n", ocp_data); +@@ -6834,12 +8875,29 @@ static int rtl8152_probe(struct usb_inte + /* MTU range: 68 - 1500 or 9194 */ + netdev->min_mtu = ETH_MIN_MTU; + switch (tp->version) { ++ case RTL_VER_03: ++ case RTL_VER_04: ++ case RTL_VER_05: ++ case RTL_VER_06: ++ case RTL_VER_08: ++ case RTL_VER_09: ++ case RTL_VER_14: ++ netdev->max_mtu = size_to_mtu(9 * 1024); ++ break; ++ case RTL_VER_10: ++ case RTL_VER_11: ++ netdev->max_mtu = size_to_mtu(15 * 1024); ++ break; ++ case RTL_VER_12: ++ case RTL_VER_13: ++ case RTL_VER_15: ++ netdev->max_mtu = size_to_mtu(16 * 1024); ++ break; + case RTL_VER_01: + case RTL_VER_02: +- netdev->max_mtu = ETH_DATA_LEN; +- break; ++ case RTL_VER_07: + default: +- netdev->max_mtu = size_to_mtu(9 * 1024); ++ netdev->max_mtu = ETH_DATA_LEN; + break; + } + +@@ -6855,7 +8913,13 @@ static int rtl8152_probe(struct usb_inte + tp->advertising = RTL_ADVERTISED_10_HALF | RTL_ADVERTISED_10_FULL | + RTL_ADVERTISED_100_HALF | RTL_ADVERTISED_100_FULL; + if (tp->mii.supports_gmii) { +- tp->speed = SPEED_1000; ++ if (tp->support_2500full && ++ tp->udev->speed >= USB_SPEED_SUPER) { ++ tp->speed = SPEED_2500; ++ tp->advertising |= RTL_ADVERTISED_2500_FULL; ++ } else { ++ tp->speed = SPEED_1000; ++ } + tp->advertising |= RTL_ADVERTISED_1000_FULL; + } + tp->duplex = DUPLEX_FULL; +@@ -6879,7 +8943,11 @@ static int rtl8152_probe(struct usb_inte + set_ethernet_addr(tp); + + usb_set_intfdata(intf, tp); +- netif_napi_add(netdev, &tp->napi, r8152_poll, RTL8152_NAPI_WEIGHT); ++ ++ if (tp->support_2500full) ++ netif_napi_add(netdev, &tp->napi, r8152_poll, 256); ++ else ++ netif_napi_add(netdev, &tp->napi, r8152_poll, 64); + + ret = register_netdev(netdev); + if (ret != 0) { +@@ -6915,7 +8983,8 @@ static void rtl8152_disconnect(struct us + unregister_netdev(tp->netdev); + tasklet_kill(&tp->tx_tl); + cancel_delayed_work_sync(&tp->hw_phy_work); +- tp->rtl_ops.unload(tp); ++ if (tp->rtl_ops.unload) ++ tp->rtl_ops.unload(tp); + rtl8152_release_firmware(tp); + free_netdev(tp->netdev); + } +@@ -6935,13 +9004,28 @@ static void rtl8152_disconnect(struct us + .idProduct = (prod), \ + .bInterfaceClass = USB_CLASS_COMM, \ + .bInterfaceSubClass = USB_CDC_SUBCLASS_ETHERNET, \ ++ .bInterfaceProtocol = USB_CDC_PROTO_NONE \ ++}, \ ++{ \ ++ .match_flags = USB_DEVICE_ID_MATCH_INT_INFO | \ ++ USB_DEVICE_ID_MATCH_DEVICE, \ ++ .idVendor = (vend), \ ++ .idProduct = (prod), \ ++ .bInterfaceClass = USB_CLASS_COMM, \ ++ .bInterfaceSubClass = USB_CDC_SUBCLASS_NCM, \ + .bInterfaceProtocol = USB_CDC_PROTO_NONE + + /* table of devices that work with this driver */ + static const struct usb_device_id rtl8152_table[] = { ++ /* Realtek */ + {REALTEK_USB_DEVICE(VENDOR_ID_REALTEK, 0x8050)}, ++ {REALTEK_USB_DEVICE(VENDOR_ID_REALTEK, 0x8053)}, + {REALTEK_USB_DEVICE(VENDOR_ID_REALTEK, 0x8152)}, + {REALTEK_USB_DEVICE(VENDOR_ID_REALTEK, 0x8153)}, ++ {REALTEK_USB_DEVICE(VENDOR_ID_REALTEK, 0x8155)}, ++ {REALTEK_USB_DEVICE(VENDOR_ID_REALTEK, 0x8156)}, ++ ++ /* Microsoft */ + {REALTEK_USB_DEVICE(VENDOR_ID_MICROSOFT, 0x07ab)}, + {REALTEK_USB_DEVICE(VENDOR_ID_MICROSOFT, 0x07c6)}, + {REALTEK_USB_DEVICE(VENDOR_ID_MICROSOFT, 0x0927)}, diff --git a/target/linux/generic/backport-5.10/794-v5.13-r8152-support-PHY-firmware-for-RTL8156-series.patch b/target/linux/generic/backport-5.10/794-v5.13-r8152-support-PHY-firmware-for-RTL8156-series.patch new file mode 100644 index 000000000..351f83e2e --- /dev/null +++ b/target/linux/generic/backport-5.10/794-v5.13-r8152-support-PHY-firmware-for-RTL8156-series.patch @@ -0,0 +1,691 @@ +From ca09589a72a0aa17389754fb75a5cd1a5d46818f Mon Sep 17 00:00:00 2001 +From: Hayes Wang +Date: Fri, 16 Apr 2021 16:04:36 +0800 +Subject: [PATCH] r8152: support PHY firmware for RTL8156 series + +commit 4a51b0e8a0143b0e83d51d9c58c6416c3818a9f2 upstream. + +Support new firmware type and method for RTL8156 series. + +Signed-off-by: Hayes Wang +Signed-off-by: David S. Miller +--- + drivers/net/usb/r8152.c | 563 +++++++++++++++++++++++++++++++++++++++- + 1 file changed, 561 insertions(+), 2 deletions(-) + +--- a/drivers/net/usb/r8152.c ++++ b/drivers/net/usb/r8152.c +@@ -974,8 +974,60 @@ enum rtl8152_fw_flags { + FW_FLAGS_START, + FW_FLAGS_STOP, + FW_FLAGS_NC, ++ FW_FLAGS_NC1, ++ FW_FLAGS_NC2, ++ FW_FLAGS_UC2, ++ FW_FLAGS_UC, ++ FW_FLAGS_SPEED_UP, ++ FW_FLAGS_VER, + }; + ++enum rtl8152_fw_fixup_cmd { ++ FW_FIXUP_AND = 0, ++ FW_FIXUP_OR, ++ FW_FIXUP_NOT, ++ FW_FIXUP_XOR, ++}; ++ ++struct fw_phy_set { ++ __le16 addr; ++ __le16 data; ++} __packed; ++ ++struct fw_phy_speed_up { ++ struct fw_block blk_hdr; ++ __le16 fw_offset; ++ __le16 version; ++ __le16 fw_reg; ++ __le16 reserved; ++ char info[]; ++} __packed; ++ ++struct fw_phy_ver { ++ struct fw_block blk_hdr; ++ struct fw_phy_set ver; ++ __le32 reserved; ++} __packed; ++ ++struct fw_phy_fixup { ++ struct fw_block blk_hdr; ++ struct fw_phy_set setting; ++ __le16 bit_cmd; ++ __le16 reserved; ++} __packed; ++ ++struct fw_phy_union { ++ struct fw_block blk_hdr; ++ __le16 fw_offset; ++ __le16 fw_reg; ++ struct fw_phy_set pre_set[2]; ++ struct fw_phy_set bp[8]; ++ struct fw_phy_set bp_en; ++ u8 pre_num; ++ u8 bp_num; ++ char info[]; ++} __packed; ++ + /** + * struct fw_mac - a firmware block used by RTL_FW_PLA and RTL_FW_USB. + * The layout of the firmware block is: +@@ -1080,6 +1132,15 @@ enum rtl_fw_type { + RTL_FW_PHY_START, + RTL_FW_PHY_STOP, + RTL_FW_PHY_NC, ++ RTL_FW_PHY_FIXUP, ++ RTL_FW_PHY_UNION_NC, ++ RTL_FW_PHY_UNION_NC1, ++ RTL_FW_PHY_UNION_NC2, ++ RTL_FW_PHY_UNION_UC2, ++ RTL_FW_PHY_UNION_UC, ++ RTL_FW_PHY_UNION_MISC, ++ RTL_FW_PHY_SPEED_UP, ++ RTL_FW_PHY_VER, + }; + + enum rtl_version { +@@ -3999,6 +4060,162 @@ static int rtl_post_ram_code(struct r815 + return 0; + } + ++static bool rtl8152_is_fw_phy_speed_up_ok(struct r8152 *tp, struct fw_phy_speed_up *phy) ++{ ++ u16 fw_offset; ++ u32 length; ++ bool rc = false; ++ ++ switch (tp->version) { ++ case RTL_VER_01: ++ case RTL_VER_02: ++ case RTL_VER_03: ++ case RTL_VER_04: ++ case RTL_VER_05: ++ case RTL_VER_06: ++ case RTL_VER_07: ++ case RTL_VER_08: ++ case RTL_VER_09: ++ case RTL_VER_10: ++ case RTL_VER_11: ++ case RTL_VER_12: ++ case RTL_VER_14: ++ goto out; ++ case RTL_VER_13: ++ case RTL_VER_15: ++ default: ++ break; ++ } ++ ++ fw_offset = __le16_to_cpu(phy->fw_offset); ++ length = __le32_to_cpu(phy->blk_hdr.length); ++ if (fw_offset < sizeof(*phy) || length <= fw_offset) { ++ dev_err(&tp->intf->dev, "invalid fw_offset\n"); ++ goto out; ++ } ++ ++ length -= fw_offset; ++ if (length & 3) { ++ dev_err(&tp->intf->dev, "invalid block length\n"); ++ goto out; ++ } ++ ++ if (__le16_to_cpu(phy->fw_reg) != 0x9A00) { ++ dev_err(&tp->intf->dev, "invalid register to load firmware\n"); ++ goto out; ++ } ++ ++ rc = true; ++out: ++ return rc; ++} ++ ++static bool rtl8152_is_fw_phy_ver_ok(struct r8152 *tp, struct fw_phy_ver *ver) ++{ ++ bool rc = false; ++ ++ switch (tp->version) { ++ case RTL_VER_10: ++ case RTL_VER_11: ++ case RTL_VER_12: ++ case RTL_VER_13: ++ case RTL_VER_15: ++ break; ++ default: ++ goto out; ++ } ++ ++ if (__le32_to_cpu(ver->blk_hdr.length) != sizeof(*ver)) { ++ dev_err(&tp->intf->dev, "invalid block length\n"); ++ goto out; ++ } ++ ++ if (__le16_to_cpu(ver->ver.addr) != SRAM_GPHY_FW_VER) { ++ dev_err(&tp->intf->dev, "invalid phy ver addr\n"); ++ goto out; ++ } ++ ++ rc = true; ++out: ++ return rc; ++} ++ ++static bool rtl8152_is_fw_phy_fixup_ok(struct r8152 *tp, struct fw_phy_fixup *fix) ++{ ++ bool rc = false; ++ ++ switch (tp->version) { ++ case RTL_VER_10: ++ case RTL_VER_11: ++ case RTL_VER_12: ++ case RTL_VER_13: ++ case RTL_VER_15: ++ break; ++ default: ++ goto out; ++ } ++ ++ if (__le32_to_cpu(fix->blk_hdr.length) != sizeof(*fix)) { ++ dev_err(&tp->intf->dev, "invalid block length\n"); ++ goto out; ++ } ++ ++ if (__le16_to_cpu(fix->setting.addr) != OCP_PHY_PATCH_CMD || ++ __le16_to_cpu(fix->setting.data) != BIT(7)) { ++ dev_err(&tp->intf->dev, "invalid phy fixup\n"); ++ goto out; ++ } ++ ++ rc = true; ++out: ++ return rc; ++} ++ ++static bool rtl8152_is_fw_phy_union_ok(struct r8152 *tp, struct fw_phy_union *phy) ++{ ++ u16 fw_offset; ++ u32 length; ++ bool rc = false; ++ ++ switch (tp->version) { ++ case RTL_VER_10: ++ case RTL_VER_11: ++ case RTL_VER_12: ++ case RTL_VER_13: ++ case RTL_VER_15: ++ break; ++ default: ++ goto out; ++ } ++ ++ fw_offset = __le16_to_cpu(phy->fw_offset); ++ length = __le32_to_cpu(phy->blk_hdr.length); ++ if (fw_offset < sizeof(*phy) || length <= fw_offset) { ++ dev_err(&tp->intf->dev, "invalid fw_offset\n"); ++ goto out; ++ } ++ ++ length -= fw_offset; ++ if (length & 1) { ++ dev_err(&tp->intf->dev, "invalid block length\n"); ++ goto out; ++ } ++ ++ if (phy->pre_num > 2) { ++ dev_err(&tp->intf->dev, "invalid pre_num %d\n", phy->pre_num); ++ goto out; ++ } ++ ++ if (phy->bp_num > 8) { ++ dev_err(&tp->intf->dev, "invalid bp_num %d\n", phy->bp_num); ++ goto out; ++ } ++ ++ rc = true; ++out: ++ return rc; ++} ++ + static bool rtl8152_is_fw_phy_nc_ok(struct r8152 *tp, struct fw_phy_nc *phy) + { + u32 length; +@@ -4319,6 +4536,10 @@ static long rtl8152_check_firmware(struc + case RTL_FW_PHY_START: + if (test_bit(FW_FLAGS_START, &fw_flags) || + test_bit(FW_FLAGS_NC, &fw_flags) || ++ test_bit(FW_FLAGS_NC1, &fw_flags) || ++ test_bit(FW_FLAGS_NC2, &fw_flags) || ++ test_bit(FW_FLAGS_UC2, &fw_flags) || ++ test_bit(FW_FLAGS_UC, &fw_flags) || + test_bit(FW_FLAGS_STOP, &fw_flags)) { + dev_err(&tp->intf->dev, + "check PHY_START fail\n"); +@@ -4367,7 +4588,153 @@ static long rtl8152_check_firmware(struc + goto fail; + } + __set_bit(FW_FLAGS_NC, &fw_flags); ++ break; ++ case RTL_FW_PHY_UNION_NC: ++ if (!test_bit(FW_FLAGS_START, &fw_flags) || ++ test_bit(FW_FLAGS_NC1, &fw_flags) || ++ test_bit(FW_FLAGS_NC2, &fw_flags) || ++ test_bit(FW_FLAGS_UC2, &fw_flags) || ++ test_bit(FW_FLAGS_UC, &fw_flags) || ++ test_bit(FW_FLAGS_STOP, &fw_flags)) { ++ dev_err(&tp->intf->dev, "PHY_UNION_NC out of order\n"); ++ goto fail; ++ } ++ ++ if (test_bit(FW_FLAGS_NC, &fw_flags)) { ++ dev_err(&tp->intf->dev, "multiple PHY_UNION_NC encountered\n"); ++ goto fail; ++ } + ++ if (!rtl8152_is_fw_phy_union_ok(tp, (struct fw_phy_union *)block)) { ++ dev_err(&tp->intf->dev, "check PHY_UNION_NC failed\n"); ++ goto fail; ++ } ++ __set_bit(FW_FLAGS_NC, &fw_flags); ++ break; ++ case RTL_FW_PHY_UNION_NC1: ++ if (!test_bit(FW_FLAGS_START, &fw_flags) || ++ test_bit(FW_FLAGS_NC2, &fw_flags) || ++ test_bit(FW_FLAGS_UC2, &fw_flags) || ++ test_bit(FW_FLAGS_UC, &fw_flags) || ++ test_bit(FW_FLAGS_STOP, &fw_flags)) { ++ dev_err(&tp->intf->dev, "PHY_UNION_NC1 out of order\n"); ++ goto fail; ++ } ++ ++ if (test_bit(FW_FLAGS_NC1, &fw_flags)) { ++ dev_err(&tp->intf->dev, "multiple PHY NC1 encountered\n"); ++ goto fail; ++ } ++ ++ if (!rtl8152_is_fw_phy_union_ok(tp, (struct fw_phy_union *)block)) { ++ dev_err(&tp->intf->dev, "check PHY_UNION_NC1 failed\n"); ++ goto fail; ++ } ++ __set_bit(FW_FLAGS_NC1, &fw_flags); ++ break; ++ case RTL_FW_PHY_UNION_NC2: ++ if (!test_bit(FW_FLAGS_START, &fw_flags) || ++ test_bit(FW_FLAGS_UC2, &fw_flags) || ++ test_bit(FW_FLAGS_UC, &fw_flags) || ++ test_bit(FW_FLAGS_STOP, &fw_flags)) { ++ dev_err(&tp->intf->dev, "PHY_UNION_NC2 out of order\n"); ++ goto fail; ++ } ++ ++ if (test_bit(FW_FLAGS_NC2, &fw_flags)) { ++ dev_err(&tp->intf->dev, "multiple PHY NC2 encountered\n"); ++ goto fail; ++ } ++ ++ if (!rtl8152_is_fw_phy_union_ok(tp, (struct fw_phy_union *)block)) { ++ dev_err(&tp->intf->dev, "check PHY_UNION_NC2 failed\n"); ++ goto fail; ++ } ++ __set_bit(FW_FLAGS_NC2, &fw_flags); ++ break; ++ case RTL_FW_PHY_UNION_UC2: ++ if (!test_bit(FW_FLAGS_START, &fw_flags) || ++ test_bit(FW_FLAGS_UC, &fw_flags) || ++ test_bit(FW_FLAGS_STOP, &fw_flags)) { ++ dev_err(&tp->intf->dev, "PHY_UNION_UC2 out of order\n"); ++ goto fail; ++ } ++ ++ if (test_bit(FW_FLAGS_UC2, &fw_flags)) { ++ dev_err(&tp->intf->dev, "multiple PHY UC2 encountered\n"); ++ goto fail; ++ } ++ ++ if (!rtl8152_is_fw_phy_union_ok(tp, (struct fw_phy_union *)block)) { ++ dev_err(&tp->intf->dev, "check PHY_UNION_UC2 failed\n"); ++ goto fail; ++ } ++ __set_bit(FW_FLAGS_UC2, &fw_flags); ++ break; ++ case RTL_FW_PHY_UNION_UC: ++ if (!test_bit(FW_FLAGS_START, &fw_flags) || ++ test_bit(FW_FLAGS_STOP, &fw_flags)) { ++ dev_err(&tp->intf->dev, "PHY_UNION_UC out of order\n"); ++ goto fail; ++ } ++ ++ if (test_bit(FW_FLAGS_UC, &fw_flags)) { ++ dev_err(&tp->intf->dev, "multiple PHY UC encountered\n"); ++ goto fail; ++ } ++ ++ if (!rtl8152_is_fw_phy_union_ok(tp, (struct fw_phy_union *)block)) { ++ dev_err(&tp->intf->dev, "check PHY_UNION_UC failed\n"); ++ goto fail; ++ } ++ __set_bit(FW_FLAGS_UC, &fw_flags); ++ break; ++ case RTL_FW_PHY_UNION_MISC: ++ if (!rtl8152_is_fw_phy_union_ok(tp, (struct fw_phy_union *)block)) { ++ dev_err(&tp->intf->dev, "check RTL_FW_PHY_UNION_MISC failed\n"); ++ goto fail; ++ } ++ break; ++ case RTL_FW_PHY_FIXUP: ++ if (!rtl8152_is_fw_phy_fixup_ok(tp, (struct fw_phy_fixup *)block)) { ++ dev_err(&tp->intf->dev, "check PHY fixup failed\n"); ++ goto fail; ++ } ++ break; ++ case RTL_FW_PHY_SPEED_UP: ++ if (test_bit(FW_FLAGS_SPEED_UP, &fw_flags)) { ++ dev_err(&tp->intf->dev, "multiple PHY firmware encountered"); ++ goto fail; ++ } ++ ++ if (!rtl8152_is_fw_phy_speed_up_ok(tp, (struct fw_phy_speed_up *)block)) { ++ dev_err(&tp->intf->dev, "check PHY speed up failed\n"); ++ goto fail; ++ } ++ __set_bit(FW_FLAGS_SPEED_UP, &fw_flags); ++ break; ++ case RTL_FW_PHY_VER: ++ if (test_bit(FW_FLAGS_START, &fw_flags) || ++ test_bit(FW_FLAGS_NC, &fw_flags) || ++ test_bit(FW_FLAGS_NC1, &fw_flags) || ++ test_bit(FW_FLAGS_NC2, &fw_flags) || ++ test_bit(FW_FLAGS_UC2, &fw_flags) || ++ test_bit(FW_FLAGS_UC, &fw_flags) || ++ test_bit(FW_FLAGS_STOP, &fw_flags)) { ++ dev_err(&tp->intf->dev, "Invalid order to set PHY version\n"); ++ goto fail; ++ } ++ ++ if (test_bit(FW_FLAGS_VER, &fw_flags)) { ++ dev_err(&tp->intf->dev, "multiple PHY version encountered"); ++ goto fail; ++ } ++ ++ if (!rtl8152_is_fw_phy_ver_ok(tp, (struct fw_phy_ver *)block)) { ++ dev_err(&tp->intf->dev, "check PHY version failed\n"); ++ goto fail; ++ } ++ __set_bit(FW_FLAGS_VER, &fw_flags); + break; + default: + dev_warn(&tp->intf->dev, "Unknown type %u is found\n", +@@ -4390,6 +4757,143 @@ fail: + return ret; + } + ++static void rtl_ram_code_speed_up(struct r8152 *tp, struct fw_phy_speed_up *phy, bool wait) ++{ ++ u32 len; ++ u8 *data; ++ ++ if (sram_read(tp, SRAM_GPHY_FW_VER) >= __le16_to_cpu(phy->version)) { ++ dev_dbg(&tp->intf->dev, "PHY firmware has been the newest\n"); ++ return; ++ } ++ ++ len = __le32_to_cpu(phy->blk_hdr.length); ++ len -= __le16_to_cpu(phy->fw_offset); ++ data = (u8 *)phy + __le16_to_cpu(phy->fw_offset); ++ ++ if (rtl_phy_patch_request(tp, true, wait)) ++ return; ++ ++ while (len) { ++ u32 ocp_data, size; ++ int i; ++ ++ if (len < 2048) ++ size = len; ++ else ++ size = 2048; ++ ++ ocp_data = ocp_read_word(tp, MCU_TYPE_USB, USB_GPHY_CTRL); ++ ocp_data |= GPHY_PATCH_DONE | BACKUP_RESTRORE; ++ ocp_write_word(tp, MCU_TYPE_USB, USB_GPHY_CTRL, ocp_data); ++ ++ generic_ocp_write(tp, __le16_to_cpu(phy->fw_reg), 0xff, size, data, MCU_TYPE_USB); ++ ++ data += size; ++ len -= size; ++ ++ ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_POL_GPIO_CTRL); ++ ocp_data |= POL_GPHY_PATCH; ++ ocp_write_word(tp, MCU_TYPE_PLA, PLA_POL_GPIO_CTRL, ocp_data); ++ ++ for (i = 0; i < 1000; i++) { ++ if (!(ocp_read_word(tp, MCU_TYPE_PLA, PLA_POL_GPIO_CTRL) & POL_GPHY_PATCH)) ++ break; ++ } ++ ++ if (i == 1000) { ++ dev_err(&tp->intf->dev, "ram code speedup mode timeout\n"); ++ return; ++ } ++ } ++ ++ ocp_write_word(tp, MCU_TYPE_PLA, PLA_OCP_GPHY_BASE, tp->ocp_base); ++ rtl_phy_patch_request(tp, false, wait); ++ ++ if (sram_read(tp, SRAM_GPHY_FW_VER) == __le16_to_cpu(phy->version)) ++ dev_dbg(&tp->intf->dev, "successfully applied %s\n", phy->info); ++ else ++ dev_err(&tp->intf->dev, "ram code speedup mode fail\n"); ++} ++ ++static int rtl8152_fw_phy_ver(struct r8152 *tp, struct fw_phy_ver *phy_ver) ++{ ++ u16 ver_addr, ver; ++ ++ ver_addr = __le16_to_cpu(phy_ver->ver.addr); ++ ver = __le16_to_cpu(phy_ver->ver.data); ++ ++ if (sram_read(tp, ver_addr) >= ver) { ++ dev_dbg(&tp->intf->dev, "PHY firmware has been the newest\n"); ++ return 0; ++ } ++ ++ sram_write(tp, ver_addr, ver); ++ ++ dev_dbg(&tp->intf->dev, "PHY firmware version %x\n", ver); ++ ++ return ver; ++} ++ ++static void rtl8152_fw_phy_fixup(struct r8152 *tp, struct fw_phy_fixup *fix) ++{ ++ u16 addr, data; ++ ++ addr = __le16_to_cpu(fix->setting.addr); ++ data = ocp_reg_read(tp, addr); ++ ++ switch (__le16_to_cpu(fix->bit_cmd)) { ++ case FW_FIXUP_AND: ++ data &= __le16_to_cpu(fix->setting.data); ++ break; ++ case FW_FIXUP_OR: ++ data |= __le16_to_cpu(fix->setting.data); ++ break; ++ case FW_FIXUP_NOT: ++ data &= ~__le16_to_cpu(fix->setting.data); ++ break; ++ case FW_FIXUP_XOR: ++ data ^= __le16_to_cpu(fix->setting.data); ++ break; ++ default: ++ return; ++ } ++ ++ ocp_reg_write(tp, addr, data); ++ ++ dev_dbg(&tp->intf->dev, "applied ocp %x %x\n", addr, data); ++} ++ ++static void rtl8152_fw_phy_union_apply(struct r8152 *tp, struct fw_phy_union *phy) ++{ ++ __le16 *data; ++ u32 length; ++ int i, num; ++ ++ num = phy->pre_num; ++ for (i = 0; i < num; i++) ++ sram_write(tp, __le16_to_cpu(phy->pre_set[i].addr), ++ __le16_to_cpu(phy->pre_set[i].data)); ++ ++ length = __le32_to_cpu(phy->blk_hdr.length); ++ length -= __le16_to_cpu(phy->fw_offset); ++ num = length / 2; ++ data = (__le16 *)((u8 *)phy + __le16_to_cpu(phy->fw_offset)); ++ ++ ocp_reg_write(tp, OCP_SRAM_ADDR, __le16_to_cpu(phy->fw_reg)); ++ for (i = 0; i < num; i++) ++ ocp_reg_write(tp, OCP_SRAM_DATA, __le16_to_cpu(data[i])); ++ ++ num = phy->bp_num; ++ for (i = 0; i < num; i++) ++ sram_write(tp, __le16_to_cpu(phy->bp[i].addr), __le16_to_cpu(phy->bp[i].data)); ++ ++ if (phy->bp_num && phy->bp_en.addr) ++ sram_write(tp, __le16_to_cpu(phy->bp_en.addr), __le16_to_cpu(phy->bp_en.data)); ++ ++ dev_dbg(&tp->intf->dev, "successfully applied %s\n", phy->info); ++} ++ + static void rtl8152_fw_phy_nc_apply(struct r8152 *tp, struct fw_phy_nc *phy) + { + u16 mode_reg, bp_index; +@@ -4443,6 +4947,12 @@ static void rtl8152_fw_mac_apply(struct + return; + } + ++ fw_ver_reg = __le16_to_cpu(mac->fw_ver_reg); ++ if (fw_ver_reg && ocp_read_byte(tp, MCU_TYPE_USB, fw_ver_reg) >= mac->fw_ver_data) { ++ dev_dbg(&tp->intf->dev, "%s firmware has been the newest\n", type ? "PLA" : "USB"); ++ return; ++ } ++ + rtl_clear_bp(tp, type); + + /* Enable backup/restore of MACDBG. This is required after clearing PLA +@@ -4478,7 +4988,6 @@ static void rtl8152_fw_mac_apply(struct + ocp_write_word(tp, type, bp_en_addr, + __le16_to_cpu(mac->bp_en_value)); + +- fw_ver_reg = __le16_to_cpu(mac->fw_ver_reg); + if (fw_ver_reg) + ocp_write_byte(tp, MCU_TYPE_USB, fw_ver_reg, + mac->fw_ver_data); +@@ -4493,7 +5002,7 @@ static void rtl8152_apply_firmware(struc + struct fw_header *fw_hdr; + struct fw_phy_patch_key *key; + u16 key_addr = 0; +- int i; ++ int i, patch_phy = 1; + + if (IS_ERR_OR_NULL(rtl_fw->fw)) + return; +@@ -4515,17 +5024,40 @@ static void rtl8152_apply_firmware(struc + rtl8152_fw_mac_apply(tp, (struct fw_mac *)block); + break; + case RTL_FW_PHY_START: ++ if (!patch_phy) ++ break; + key = (struct fw_phy_patch_key *)block; + key_addr = __le16_to_cpu(key->key_reg); + rtl_pre_ram_code(tp, key_addr, __le16_to_cpu(key->key_data), !power_cut); + break; + case RTL_FW_PHY_STOP: ++ if (!patch_phy) ++ break; + WARN_ON(!key_addr); + rtl_post_ram_code(tp, key_addr, !power_cut); + break; + case RTL_FW_PHY_NC: + rtl8152_fw_phy_nc_apply(tp, (struct fw_phy_nc *)block); + break; ++ case RTL_FW_PHY_VER: ++ patch_phy = rtl8152_fw_phy_ver(tp, (struct fw_phy_ver *)block); ++ break; ++ case RTL_FW_PHY_UNION_NC: ++ case RTL_FW_PHY_UNION_NC1: ++ case RTL_FW_PHY_UNION_NC2: ++ case RTL_FW_PHY_UNION_UC2: ++ case RTL_FW_PHY_UNION_UC: ++ case RTL_FW_PHY_UNION_MISC: ++ if (patch_phy) ++ rtl8152_fw_phy_union_apply(tp, (struct fw_phy_union *)block); ++ break; ++ case RTL_FW_PHY_FIXUP: ++ if (patch_phy) ++ rtl8152_fw_phy_fixup(tp, (struct fw_phy_fixup *)block); ++ break; ++ case RTL_FW_PHY_SPEED_UP: ++ rtl_ram_code_speed_up(tp, (struct fw_phy_speed_up *)block, !power_cut); ++ break; + default: + break; + } +@@ -5033,6 +5565,21 @@ static int r8153c_post_firmware_1(struct + return 0; + } + ++static int r8156a_post_firmware_1(struct r8152 *tp) ++{ ++ u32 ocp_data; ++ ++ ocp_data = ocp_read_word(tp, MCU_TYPE_USB, USB_FW_FIX_EN1); ++ ocp_data |= FW_IP_RESET_EN; ++ ocp_write_word(tp, MCU_TYPE_USB, USB_FW_FIX_EN1, ocp_data); ++ ++ /* Modify U3PHY parameter for compatibility issue */ ++ ocp_write_dword(tp, MCU_TYPE_USB, USB_UPHY3_MDCMDIO, 0x4026840e); ++ ocp_write_dword(tp, MCU_TYPE_USB, USB_UPHY3_MDCMDIO, 0x4001acc9); ++ ++ return 0; ++} ++ + static void r8153_aldps_en(struct r8152 *tp, bool enable) + { + u16 data; +@@ -8651,12 +9198,16 @@ static int rtl_ops_init(struct r8152 *tp + #define FIRMWARE_8153A_4 "rtl_nic/rtl8153a-4.fw" + #define FIRMWARE_8153B_2 "rtl_nic/rtl8153b-2.fw" + #define FIRMWARE_8153C_1 "rtl_nic/rtl8153c-1.fw" ++#define FIRMWARE_8156A_2 "rtl_nic/rtl8156a-2.fw" ++#define FIRMWARE_8156B_2 "rtl_nic/rtl8156b-2.fw" + + MODULE_FIRMWARE(FIRMWARE_8153A_2); + MODULE_FIRMWARE(FIRMWARE_8153A_3); + MODULE_FIRMWARE(FIRMWARE_8153A_4); + MODULE_FIRMWARE(FIRMWARE_8153B_2); + MODULE_FIRMWARE(FIRMWARE_8153C_1); ++MODULE_FIRMWARE(FIRMWARE_8156A_2); ++MODULE_FIRMWARE(FIRMWARE_8156B_2); + + static int rtl_fw_init(struct r8152 *tp) + { +@@ -8682,6 +9233,14 @@ static int rtl_fw_init(struct r8152 *tp) + rtl_fw->pre_fw = r8153b_pre_firmware_1; + rtl_fw->post_fw = r8153b_post_firmware_1; + break; ++ case RTL_VER_11: ++ rtl_fw->fw_name = FIRMWARE_8156A_2; ++ rtl_fw->post_fw = r8156a_post_firmware_1; ++ break; ++ case RTL_VER_13: ++ case RTL_VER_15: ++ rtl_fw->fw_name = FIRMWARE_8156B_2; ++ break; + case RTL_VER_14: + rtl_fw->fw_name = FIRMWARE_8153C_1; + rtl_fw->pre_fw = r8153b_pre_firmware_1; diff --git a/target/linux/generic/backport-5.10/795-v5.13-r8152-search-the-configuration-of-vendor-mode.patch b/target/linux/generic/backport-5.10/795-v5.13-r8152-search-the-configuration-of-vendor-mode.patch new file mode 100644 index 000000000..f5abb5d3c --- /dev/null +++ b/target/linux/generic/backport-5.10/795-v5.13-r8152-search-the-configuration-of-vendor-mode.patch @@ -0,0 +1,79 @@ +From 579f58dd2819910354753bc5489fc1588fe9cfe2 Mon Sep 17 00:00:00 2001 +From: Hayes Wang +Date: Fri, 16 Apr 2021 16:04:37 +0800 +Subject: [PATCH] r8152: search the configuration of vendor mode + +commit c2198943e33b100ed21dfb636c8fa6baef841e9d upstream. + +The vendor mode is not always at config #1, so it is necessary to +set the correct configuration number. + +Signed-off-by: Hayes Wang +Signed-off-by: David S. Miller +--- + drivers/net/usb/r8152.c | 39 +++++++++++++++++++++++++++++++++++---- + 1 file changed, 35 insertions(+), 4 deletions(-) + +--- a/drivers/net/usb/r8152.c ++++ b/drivers/net/usb/r8152.c +@@ -29,7 +29,7 @@ + #include + + /* Information for net-next */ +-#define NETNEXT_VERSION "11" ++#define NETNEXT_VERSION "12" + + /* Information for net */ + #define NET_VERSION "11" +@@ -8108,6 +8108,39 @@ static void r8156b_init(struct r8152 *tp + tp->coalesce = 15000; /* 15 us */ + } + ++static bool rtl_vendor_mode(struct usb_interface *intf) ++{ ++ struct usb_host_interface *alt = intf->cur_altsetting; ++ struct usb_device *udev; ++ struct usb_host_config *c; ++ int i, num_configs; ++ ++ if (alt->desc.bInterfaceClass == USB_CLASS_VENDOR_SPEC) ++ return true; ++ ++ /* The vendor mode is not always config #1, so to find it out. */ ++ udev = interface_to_usbdev(intf); ++ c = udev->config; ++ num_configs = udev->descriptor.bNumConfigurations; ++ for (i = 0; i < num_configs; (i++, c++)) { ++ struct usb_interface_descriptor *desc = NULL; ++ ++ if (c->desc.bNumInterfaces > 0) ++ desc = &c->intf_cache[0]->altsetting->desc; ++ else ++ continue; ++ ++ if (desc->bInterfaceClass == USB_CLASS_VENDOR_SPEC) { ++ usb_driver_set_configuration(udev, c->desc.bConfigurationValue); ++ break; ++ } ++ } ++ ++ WARN_ON_ONCE(i == num_configs); ++ ++ return false; ++} ++ + static int rtl8152_pre_reset(struct usb_interface *intf) + { + struct r8152 *tp = usb_get_intfdata(intf); +@@ -9346,10 +9379,8 @@ static int rtl8152_probe(struct usb_inte + if (version == RTL_VER_UNKNOWN) + return -ENODEV; + +- if (udev->actconfig->desc.bConfigurationValue != 1) { +- usb_driver_set_configuration(udev, 1); ++ if (!rtl_vendor_mode(intf)) + return -ENODEV; +- } + + if (intf->cur_altsetting->desc.bNumEndpoints < 3) + return -ENODEV; diff --git a/target/linux/generic/backport-5.10/820-v5.13-make-pci_host_common_probe-declare-its-reliance-on-msi-domains.patch b/target/linux/generic/backport-5.10/820-v5.13-make-pci_host_common_probe-declare-its-reliance-on-msi-domains.patch new file mode 100644 index 000000000..8ca2b78f7 --- /dev/null +++ b/target/linux/generic/backport-5.10/820-v5.13-make-pci_host_common_probe-declare-its-reliance-on-msi-domains.patch @@ -0,0 +1,34 @@ +From 9ec37efb87832b578d7972fc80b04d94f5d2bbe3 Mon Sep 17 00:00:00 2001 +From: Marc Zyngier +Date: Tue, 30 Mar 2021 16:11:42 +0100 +Subject: PCI/MSI: Make pci_host_common_probe() declare its reliance on MSI + domains + +The generic PCI host driver relies on MSI domains for MSIs to +be provided to its end-points. Make this dependency explicit. + +This cures the warnings occuring on arm/arm64 VMs when booted +with PCI virtio devices and no MSI controller (no GICv3 ITS, +for example). + +It is likely that other drivers will need to express the same +dependency. + +Link: https://lore.kernel.org/r/20210330151145.997953-12-maz@kernel.org +Signed-off-by: Marc Zyngier +Signed-off-by: Lorenzo Pieralisi +Acked-by: Bjorn Helgaas +--- + drivers/pci/controller/pci-host-common.c | 1 + + 1 file changed, 1 insertion(+) + +--- a/drivers/pci/controller/pci-host-common.c ++++ b/drivers/pci/controller/pci-host-common.c +@@ -77,6 +77,7 @@ int pci_host_common_probe(struct platfor + + bridge->sysdata = cfg; + bridge->ops = (struct pci_ops *)&ops->pci_ops; ++ bridge->msi_domain = true; + + platform_set_drvdata(pdev, bridge); + diff --git a/target/linux/generic/backport-5.10/821-v5.13-let-pci-host-bridges-declar-their-reliance-on-msi-domains.patch b/target/linux/generic/backport-5.10/821-v5.13-let-pci-host-bridges-declar-their-reliance-on-msi-domains.patch new file mode 100644 index 000000000..854dc34c4 --- /dev/null +++ b/target/linux/generic/backport-5.10/821-v5.13-let-pci-host-bridges-declar-their-reliance-on-msi-domains.patch @@ -0,0 +1,44 @@ +From 94e89b145371b68fa0ea294855adebcd03e0522e Mon Sep 17 00:00:00 2001 +From: Marc Zyngier +Date: Tue, 30 Mar 2021 16:11:41 +0100 +Subject: PCI/MSI: Let PCI host bridges declare their reliance on MSI domains + +There is a whole class of host bridges that cannot know whether +MSIs will be provided or not, as they rely on other blocks +to provide the MSI functionnality, using MSI domains. This is +the case for example on systems that use the ARM GIC architecture. + +Introduce a new attribute ('msi_domain') indicating that implicit +dependency, and use this property to set the NO_MSI flag when +no MSI domain is found at probe time. + +Link: https://lore.kernel.org/r/20210330151145.997953-11-maz@kernel.org +Signed-off-by: Marc Zyngier +Signed-off-by: Lorenzo Pieralisi +Acked-by: Bjorn Helgaas +--- + drivers/pci/probe.c | 2 ++ + include/linux/pci.h | 1 + + 2 files changed, 3 insertions(+) + +--- a/drivers/pci/probe.c ++++ b/drivers/pci/probe.c +@@ -925,6 +925,8 @@ static int pci_register_host_bridge(stru + device_enable_async_suspend(bus->bridge); + pci_set_bus_of_node(bus); + pci_set_bus_msi_domain(bus); ++ if (bridge->msi_domain && !dev_get_msi_domain(&bus->dev)) ++ bus->bus_flags |= PCI_BUS_FLAGS_NO_MSI; + + if (!parent) + set_dev_node(bus->bridge, pcibus_to_node(bus)); +--- a/include/linux/pci.h ++++ b/include/linux/pci.h +@@ -547,6 +547,7 @@ struct pci_host_bridge { + unsigned int native_dpc:1; /* OS may use PCIe DPC */ + unsigned int preserve_config:1; /* Preserve FW resource setup */ + unsigned int size_windows:1; /* Enable root bus sizing */ ++ unsigned int msi_domain:1; /* Bridge wants MSI domain */ + + /* Resource alignment requirements */ + resource_size_t (*align_resource)(struct pci_dev *dev, diff --git a/target/linux/generic/backport-5.10/822-v5.13-advertise-lack-of-built-in-msi-handling.patch b/target/linux/generic/backport-5.10/822-v5.13-advertise-lack-of-built-in-msi-handling.patch new file mode 100644 index 000000000..c11aedd81 --- /dev/null +++ b/target/linux/generic/backport-5.10/822-v5.13-advertise-lack-of-built-in-msi-handling.patch @@ -0,0 +1,59 @@ +From 645e9c38383d7fcde2784ee537fa18ec9bed54d9 Mon Sep 17 00:00:00 2001 +From: Thomas Gleixner +Date: Tue, 30 Mar 2021 16:11:43 +0100 +Subject: PCI: mediatek: Advertise lack of built-in MSI handling + +Some Mediatek host bridges cannot handle MSIs, which is sad. +This also results in an ugly warning at device probe time, +as the core PCI code wasn't told that MSIs were not available. + +Advertise this fact to the rest of the core PCI code by +using the 'msi_domain' attribute, which still opens the possibility +for another block to provide the MSI functionnality. + +[maz: commit message, switched over to msi_domain attribute] + +Link: https://lore.kernel.org/r/20210330151145.997953-13-maz@kernel.org +Reported-by: Frank Wunderlich +Signed-off-by: Thomas Gleixner +Signed-off-by: Marc Zyngier +Signed-off-by: Lorenzo Pieralisi +Acked-by: Bjorn Helgaas +--- + drivers/pci/controller/pcie-mediatek.c | 4 ++++ + 1 file changed, 4 insertions(+) + +--- a/drivers/pci/controller/pcie-mediatek.c ++++ b/drivers/pci/controller/pcie-mediatek.c +@@ -143,6 +143,7 @@ struct mtk_pcie_port; + * struct mtk_pcie_soc - differentiate between host generations + * @need_fix_class_id: whether this host's class ID needed to be fixed or not + * @need_fix_device_id: whether this host's device ID needed to be fixed or not ++ * @no_msi: Bridge has no MSI support, and relies on an external block + * @device_id: device ID which this host need to be fixed + * @ops: pointer to configuration access functions + * @startup: pointer to controller setting functions +@@ -151,6 +152,7 @@ struct mtk_pcie_port; + struct mtk_pcie_soc { + bool need_fix_class_id; + bool need_fix_device_id; ++ bool no_msi; + unsigned int device_id; + struct pci_ops *ops; + int (*startup)(struct mtk_pcie_port *port); +@@ -1087,6 +1089,7 @@ static int mtk_pcie_probe(struct platfor + + host->ops = pcie->soc->ops; + host->sysdata = pcie; ++ host->msi_domain = pcie->soc->no_msi; + + err = pci_host_probe(host); + if (err) +@@ -1176,6 +1179,7 @@ static const struct dev_pm_ops mtk_pcie_ + }; + + static const struct mtk_pcie_soc mtk_pcie_soc_v1 = { ++ .no_msi = true, + .ops = &mtk_pcie_ops, + .startup = mtk_pcie_startup_port, + }; diff --git a/target/linux/generic/backport-5.10/830-v5.14-leds-lp55xx-Initialize-enable-GPIO-direction-to-outp.patch b/target/linux/generic/backport-5.10/830-v5.14-leds-lp55xx-Initialize-enable-GPIO-direction-to-outp.patch new file mode 100644 index 000000000..75b994739 --- /dev/null +++ b/target/linux/generic/backport-5.10/830-v5.14-leds-lp55xx-Initialize-enable-GPIO-direction-to-outp.patch @@ -0,0 +1,28 @@ +From a5d3d1adc95f4ac5968b7a77ee95a3abbbb96f49 Mon Sep 17 00:00:00 2001 +From: Doug Zobel +Date: Mon, 10 May 2021 15:40:00 -0500 +Subject: [PATCH] leds: lp55xx: Initialize enable GPIO direction to output + +The "Convert to use GPIO descriptors" commit changed the +initialization of the enable GPIO from GPIOF_DIR_OUT to +GPIOD_ASIS. This breaks systems where the GPIO does not +default to output. Changing the enable initialization +to GPIOD_OUT_LOW. + +Signed-off-by: Doug Zobel +Signed-off-by: Pavel Machek +--- + drivers/leds/leds-lp55xx-common.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/drivers/leds/leds-lp55xx-common.c ++++ b/drivers/leds/leds-lp55xx-common.c +@@ -694,7 +694,7 @@ struct lp55xx_platform_data *lp55xx_of_p + of_property_read_u8(np, "clock-mode", &pdata->clock_mode); + + pdata->enable_gpiod = devm_gpiod_get_optional(dev, "enable", +- GPIOD_ASIS); ++ GPIOD_OUT_LOW); + if (IS_ERR(pdata->enable_gpiod)) + return ERR_CAST(pdata->enable_gpiod); + diff --git a/target/linux/generic/backport-5.10/850-v5.17-0001-PCI-pci-bridge-emul-Add-description-for-class_revisi.patch b/target/linux/generic/backport-5.10/850-v5.17-0001-PCI-pci-bridge-emul-Add-description-for-class_revisi.patch new file mode 100644 index 000000000..19a4be2a9 --- /dev/null +++ b/target/linux/generic/backport-5.10/850-v5.17-0001-PCI-pci-bridge-emul-Add-description-for-class_revisi.patch @@ -0,0 +1,44 @@ +From 9319230ac147067652b58fe849ffe0ceec098665 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Pali=20Roh=C3=A1r?= +Date: Tue, 30 Nov 2021 18:29:03 +0100 +Subject: [PATCH] PCI: pci-bridge-emul: Add description for class_revision + field +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +The current assignment to the class_revision member + + class_revision |= cpu_to_le32(PCI_CLASS_BRIDGE_PCI << 16); + +can make the reader think that class is at high 16 bits of the member and +revision at low 16 bits. + +In reality, class is at high 24 bits, but the class for PCI Bridge Normal +Decode is PCI_CLASS_BRIDGE_PCI << 8. + +Change the assignment and add a comment to make this clearer. + +Link: https://lore.kernel.org/r/20211130172913.9727-2-kabel@kernel.org +Signed-off-by: Pali Rohár +Signed-off-by: Marek Behún +Signed-off-by: Lorenzo Pieralisi +--- + drivers/pci/pci-bridge-emul.c | 6 +++++- + 1 file changed, 5 insertions(+), 1 deletion(-) + +--- a/drivers/pci/pci-bridge-emul.c ++++ b/drivers/pci/pci-bridge-emul.c +@@ -284,7 +284,11 @@ int pci_bridge_emul_init(struct pci_brid + { + BUILD_BUG_ON(sizeof(bridge->conf) != PCI_BRIDGE_CONF_END); + +- bridge->conf.class_revision |= cpu_to_le32(PCI_CLASS_BRIDGE_PCI << 16); ++ /* ++ * class_revision: Class is high 24 bits and revision is low 8 bit of this member, ++ * while class for PCI Bridge Normal Decode has the 24-bit value: PCI_CLASS_BRIDGE_PCI << 8 ++ */ ++ bridge->conf.class_revision |= cpu_to_le32((PCI_CLASS_BRIDGE_PCI << 8) << 8); + bridge->conf.header_type = PCI_HEADER_TYPE_BRIDGE; + bridge->conf.cache_line_size = 0x10; + bridge->conf.status = cpu_to_le16(PCI_STATUS_CAP_LIST); diff --git a/target/linux/generic/backport-5.10/850-v5.17-0002-PCI-pci-bridge-emul-Add-definitions-for-missing-capa.patch b/target/linux/generic/backport-5.10/850-v5.17-0002-PCI-pci-bridge-emul-Add-definitions-for-missing-capa.patch new file mode 100644 index 000000000..3dd82710e --- /dev/null +++ b/target/linux/generic/backport-5.10/850-v5.17-0002-PCI-pci-bridge-emul-Add-definitions-for-missing-capa.patch @@ -0,0 +1,73 @@ +From 8ea673a8b30b4a32516b8adabb15e2a68ff02ec8 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Pali=20Roh=C3=A1r?= +Date: Tue, 30 Nov 2021 18:29:04 +0100 +Subject: [PATCH] PCI: pci-bridge-emul: Add definitions for missing + capabilities registers +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +pci-bridge-emul driver already allocates buffer for capabilities up to the +PCI_EXP_SLTSTA2 register, but does not define bit access behavior for these +registers. Add these missing definitions. + +Link: https://lore.kernel.org/r/20211130172913.9727-3-kabel@kernel.org +Signed-off-by: Pali Rohár +Signed-off-by: Marek Behún +Signed-off-by: Lorenzo Pieralisi +--- + drivers/pci/pci-bridge-emul.c | 43 +++++++++++++++++++++++++++++++++++ + 1 file changed, 43 insertions(+) + +--- a/drivers/pci/pci-bridge-emul.c ++++ b/drivers/pci/pci-bridge-emul.c +@@ -270,6 +270,49 @@ struct pci_bridge_reg_behavior pcie_cap_ + .ro = GENMASK(15, 0) | PCI_EXP_RTSTA_PENDING, + .w1c = PCI_EXP_RTSTA_PME, + }, ++ ++ [PCI_EXP_DEVCAP2 / 4] = { ++ /* ++ * Device capabilities 2 register has reserved bits [30:27]. ++ * Also bits [26:24] are reserved for non-upstream ports. ++ */ ++ .ro = BIT(31) | GENMASK(23, 0), ++ }, ++ ++ [PCI_EXP_DEVCTL2 / 4] = { ++ /* ++ * Device control 2 register is RW. Bit 11 is reserved for ++ * non-upstream ports. ++ * ++ * Device status 2 register is reserved. ++ */ ++ .rw = GENMASK(15, 12) | GENMASK(10, 0), ++ }, ++ ++ [PCI_EXP_LNKCAP2 / 4] = { ++ /* Link capabilities 2 register has reserved bits [30:25] and 0. */ ++ .ro = BIT(31) | GENMASK(24, 1), ++ }, ++ ++ [PCI_EXP_LNKCTL2 / 4] = { ++ /* ++ * Link control 2 register is RW. ++ * ++ * Link status 2 register has bits 5, 15 W1C; ++ * bits 10, 11 reserved and others are RO. ++ */ ++ .rw = GENMASK(15, 0), ++ .w1c = (BIT(15) | BIT(5)) << 16, ++ .ro = (GENMASK(14, 12) | GENMASK(9, 6) | GENMASK(4, 0)) << 16, ++ }, ++ ++ [PCI_EXP_SLTCAP2 / 4] = { ++ /* Slot capabilities 2 register is reserved. */ ++ }, ++ ++ [PCI_EXP_SLTCTL2 / 4] = { ++ /* Both Slot control 2 and Slot status 2 registers are reserved. */ ++ }, + }; + + /* diff --git a/target/linux/generic/backport-5.10/850-v5.17-0003-PCI-aardvark-Add-support-for-DEVCAP2-DEVCTL2-LNKCAP2.patch b/target/linux/generic/backport-5.10/850-v5.17-0003-PCI-aardvark-Add-support-for-DEVCAP2-DEVCTL2-LNKCAP2.patch new file mode 100644 index 000000000..0d5066574 --- /dev/null +++ b/target/linux/generic/backport-5.10/850-v5.17-0003-PCI-aardvark-Add-support-for-DEVCAP2-DEVCTL2-LNKCAP2.patch @@ -0,0 +1,61 @@ +From 1d3e170344dff2cef8827db6c09909b78cbc11d7 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Pali=20Roh=C3=A1r?= +Date: Tue, 30 Nov 2021 18:29:05 +0100 +Subject: [PATCH] PCI: aardvark: Add support for DEVCAP2, DEVCTL2, LNKCAP2 and + LNKCTL2 registers on emulated bridge +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +PCI aardvark hardware supports access to DEVCAP2, DEVCTL2, LNKCAP2 and +LNKCTL2 configuration registers of PCIe core via PCIE_CORE_PCIEXP_CAP. +Export them via emulated software root bridge. + +Link: https://lore.kernel.org/r/20211130172913.9727-4-kabel@kernel.org +Signed-off-by: Pali Rohár +Signed-off-by: Marek Behún +Signed-off-by: Lorenzo Pieralisi +--- + drivers/pci/controller/pci-aardvark.c | 15 +++++++++++---- + 1 file changed, 11 insertions(+), 4 deletions(-) + +--- a/drivers/pci/controller/pci-aardvark.c ++++ b/drivers/pci/controller/pci-aardvark.c +@@ -883,8 +883,13 @@ advk_pci_bridge_emul_pcie_conf_read(stru + + case PCI_EXP_DEVCAP: + case PCI_EXP_DEVCTL: ++ case PCI_EXP_DEVCAP2: ++ case PCI_EXP_DEVCTL2: ++ case PCI_EXP_LNKCAP2: ++ case PCI_EXP_LNKCTL2: + *value = advk_readl(pcie, PCIE_CORE_PCIEXP_CAP + reg); + return PCI_BRIDGE_EMUL_HANDLED; ++ + default: + return PCI_BRIDGE_EMUL_NOT_HANDLED; + } +@@ -898,10 +903,6 @@ advk_pci_bridge_emul_pcie_conf_write(str + struct advk_pcie *pcie = bridge->data; + + switch (reg) { +- case PCI_EXP_DEVCTL: +- advk_writel(pcie, new, PCIE_CORE_PCIEXP_CAP + reg); +- break; +- + case PCI_EXP_LNKCTL: + advk_writel(pcie, new, PCIE_CORE_PCIEXP_CAP + reg); + if (new & PCI_EXP_LNKCTL_RL) +@@ -923,6 +924,12 @@ advk_pci_bridge_emul_pcie_conf_write(str + advk_writel(pcie, new, PCIE_ISR0_REG); + break; + ++ case PCI_EXP_DEVCTL: ++ case PCI_EXP_DEVCTL2: ++ case PCI_EXP_LNKCTL2: ++ advk_writel(pcie, new, PCIE_CORE_PCIEXP_CAP + reg); ++ break; ++ + default: + break; + } diff --git a/target/linux/generic/backport-5.10/850-v5.17-0004-PCI-aardvark-Clear-all-MSIs-at-setup.patch b/target/linux/generic/backport-5.10/850-v5.17-0004-PCI-aardvark-Clear-all-MSIs-at-setup.patch new file mode 100644 index 000000000..d617845e4 --- /dev/null +++ b/target/linux/generic/backport-5.10/850-v5.17-0004-PCI-aardvark-Clear-all-MSIs-at-setup.patch @@ -0,0 +1,59 @@ +From 7d8dc1f7cd007a7ce94c5b4c20d63a8b8d6d7751 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Pali=20Roh=C3=A1r?= +Date: Tue, 30 Nov 2021 18:29:06 +0100 +Subject: [PATCH] PCI: aardvark: Clear all MSIs at setup +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +We already clear all the other interrupts (ISR0, ISR1, HOST_CTRL_INT). + +Define a new macro PCIE_MSI_ALL_MASK and do the same clearing for MSIs, +to ensure that we don't start receiving spurious interrupts. + +Use this new mask in advk_pcie_handle_msi(); + +Link: https://lore.kernel.org/r/20211130172913.9727-5-kabel@kernel.org +Signed-off-by: Pali Rohár +Signed-off-by: Marek Behún +Signed-off-by: Lorenzo Pieralisi +--- + drivers/pci/controller/pci-aardvark.c | 6 ++++-- + 1 file changed, 4 insertions(+), 2 deletions(-) + +--- a/drivers/pci/controller/pci-aardvark.c ++++ b/drivers/pci/controller/pci-aardvark.c +@@ -114,6 +114,7 @@ + #define PCIE_MSI_ADDR_HIGH_REG (CONTROL_BASE_ADDR + 0x54) + #define PCIE_MSI_STATUS_REG (CONTROL_BASE_ADDR + 0x58) + #define PCIE_MSI_MASK_REG (CONTROL_BASE_ADDR + 0x5C) ++#define PCIE_MSI_ALL_MASK GENMASK(31, 0) + #define PCIE_MSI_PAYLOAD_REG (CONTROL_BASE_ADDR + 0x9C) + #define PCIE_MSI_DATA_MASK GENMASK(15, 0) + +@@ -577,6 +578,7 @@ static void advk_pcie_setup_hw(struct ad + advk_writel(pcie, reg, PCIE_CORE_CTRL2_REG); + + /* Clear all interrupts */ ++ advk_writel(pcie, PCIE_MSI_ALL_MASK, PCIE_MSI_STATUS_REG); + advk_writel(pcie, PCIE_ISR0_ALL_MASK, PCIE_ISR0_REG); + advk_writel(pcie, PCIE_ISR1_ALL_MASK, PCIE_ISR1_REG); + advk_writel(pcie, PCIE_IRQ_ALL_MASK, HOST_CTRL_INT_STATUS_REG); +@@ -589,7 +591,7 @@ static void advk_pcie_setup_hw(struct ad + advk_writel(pcie, PCIE_ISR1_ALL_MASK, PCIE_ISR1_MASK_REG); + + /* Unmask all MSIs */ +- advk_writel(pcie, 0, PCIE_MSI_MASK_REG); ++ advk_writel(pcie, ~(u32)PCIE_MSI_ALL_MASK, PCIE_MSI_MASK_REG); + + /* Enable summary interrupt for GIC SPI source */ + reg = PCIE_IRQ_ALL_MASK & (~PCIE_IRQ_ENABLE_INTS_MASK); +@@ -1401,7 +1403,7 @@ static void advk_pcie_handle_msi(struct + + msi_mask = advk_readl(pcie, PCIE_MSI_MASK_REG); + msi_val = advk_readl(pcie, PCIE_MSI_STATUS_REG); +- msi_status = msi_val & ~msi_mask; ++ msi_status = msi_val & ((~msi_mask) & PCIE_MSI_ALL_MASK); + + for (msi_idx = 0; msi_idx < MSI_IRQ_NUM; msi_idx++) { + if (!(BIT(msi_idx) & msi_status)) diff --git a/target/linux/generic/backport-5.10/850-v5.17-0005-PCI-aardvark-Comment-actions-in-driver-remove-method.patch b/target/linux/generic/backport-5.10/850-v5.17-0005-PCI-aardvark-Comment-actions-in-driver-remove-method.patch new file mode 100644 index 000000000..126106628 --- /dev/null +++ b/target/linux/generic/backport-5.10/850-v5.17-0005-PCI-aardvark-Comment-actions-in-driver-remove-method.patch @@ -0,0 +1,34 @@ +From a4ca7948e1d47275f8f3e5023243440c40561916 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Pali=20Roh=C3=A1r?= +Date: Tue, 30 Nov 2021 18:29:07 +0100 +Subject: [PATCH] PCI: aardvark: Comment actions in driver remove method +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Add two more comments into the advk_pcie_remove() method. + +Link: https://lore.kernel.org/r/20211130172913.9727-6-kabel@kernel.org +Signed-off-by: Pali Rohár +Signed-off-by: Marek Behún +Signed-off-by: Lorenzo Pieralisi +--- + drivers/pci/controller/pci-aardvark.c | 2 ++ + 1 file changed, 2 insertions(+) + +--- a/drivers/pci/controller/pci-aardvark.c ++++ b/drivers/pci/controller/pci-aardvark.c +@@ -1700,11 +1700,13 @@ static int advk_pcie_remove(struct platf + struct pci_host_bridge *bridge = pci_host_bridge_from_priv(pcie); + int i; + ++ /* Remove PCI bus with all devices */ + pci_lock_rescan_remove(); + pci_stop_root_bus(bridge->bus); + pci_remove_root_bus(bridge->bus); + pci_unlock_rescan_remove(); + ++ /* Remove IRQ domains */ + advk_pcie_remove_msi_irq_domain(pcie); + advk_pcie_remove_irq_domain(pcie); + diff --git a/target/linux/generic/backport-5.10/850-v5.17-0006-PCI-aardvark-Disable-bus-mastering-when-unbinding-dr.patch b/target/linux/generic/backport-5.10/850-v5.17-0006-PCI-aardvark-Disable-bus-mastering-when-unbinding-dr.patch new file mode 100644 index 000000000..cb50e08fd --- /dev/null +++ b/target/linux/generic/backport-5.10/850-v5.17-0006-PCI-aardvark-Disable-bus-mastering-when-unbinding-dr.patch @@ -0,0 +1,41 @@ +From a46f2f6dd4093438d9615dfbf5c0fea2a9835dba Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Pali=20Roh=C3=A1r?= +Date: Tue, 30 Nov 2021 18:29:08 +0100 +Subject: [PATCH] PCI: aardvark: Disable bus mastering when unbinding driver +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Ensure that after driver unbind PCIe cards are not able to forward +memory and I/O requests in the upstream direction. + +Link: https://lore.kernel.org/r/20211130172913.9727-7-kabel@kernel.org +Signed-off-by: Pali Rohár +Signed-off-by: Marek Behún +Signed-off-by: Lorenzo Pieralisi +--- + drivers/pci/controller/pci-aardvark.c | 6 ++++++ + 1 file changed, 6 insertions(+) + +--- a/drivers/pci/controller/pci-aardvark.c ++++ b/drivers/pci/controller/pci-aardvark.c +@@ -1698,6 +1698,7 @@ static int advk_pcie_remove(struct platf + { + struct advk_pcie *pcie = platform_get_drvdata(pdev); + struct pci_host_bridge *bridge = pci_host_bridge_from_priv(pcie); ++ u32 val; + int i; + + /* Remove PCI bus with all devices */ +@@ -1706,6 +1707,11 @@ static int advk_pcie_remove(struct platf + pci_remove_root_bus(bridge->bus); + pci_unlock_rescan_remove(); + ++ /* Disable Root Bridge I/O space, memory space and bus mastering */ ++ val = advk_readl(pcie, PCIE_CORE_CMD_STATUS_REG); ++ val &= ~(PCI_COMMAND_IO | PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER); ++ advk_writel(pcie, val, PCIE_CORE_CMD_STATUS_REG); ++ + /* Remove IRQ domains */ + advk_pcie_remove_msi_irq_domain(pcie); + advk_pcie_remove_irq_domain(pcie); diff --git a/target/linux/generic/backport-5.10/850-v5.17-0007-PCI-aardvark-Mask-all-interrupts-when-unbinding-driv.patch b/target/linux/generic/backport-5.10/850-v5.17-0007-PCI-aardvark-Mask-all-interrupts-when-unbinding-driv.patch new file mode 100644 index 000000000..ce8088b3b --- /dev/null +++ b/target/linux/generic/backport-5.10/850-v5.17-0007-PCI-aardvark-Mask-all-interrupts-when-unbinding-driv.patch @@ -0,0 +1,48 @@ +From 13bcdf07cb2ecff5d45d2c141df2539b15211448 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Pali=20Roh=C3=A1r?= +Date: Tue, 30 Nov 2021 18:29:09 +0100 +Subject: [PATCH] PCI: aardvark: Mask all interrupts when unbinding driver +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Ensure that no interrupt can be triggered after driver unbind. + +Link: https://lore.kernel.org/r/20211130172913.9727-8-kabel@kernel.org +Signed-off-by: Pali Rohár +Signed-off-by: Marek Behún +Signed-off-by: Lorenzo Pieralisi +--- + drivers/pci/controller/pci-aardvark.c | 21 +++++++++++++++++++++ + 1 file changed, 21 insertions(+) + +--- a/drivers/pci/controller/pci-aardvark.c ++++ b/drivers/pci/controller/pci-aardvark.c +@@ -1712,6 +1712,27 @@ static int advk_pcie_remove(struct platf + val &= ~(PCI_COMMAND_IO | PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER); + advk_writel(pcie, val, PCIE_CORE_CMD_STATUS_REG); + ++ /* Disable MSI */ ++ val = advk_readl(pcie, PCIE_CORE_CTRL2_REG); ++ val &= ~PCIE_CORE_CTRL2_MSI_ENABLE; ++ advk_writel(pcie, val, PCIE_CORE_CTRL2_REG); ++ ++ /* Clear MSI address */ ++ advk_writel(pcie, 0, PCIE_MSI_ADDR_LOW_REG); ++ advk_writel(pcie, 0, PCIE_MSI_ADDR_HIGH_REG); ++ ++ /* Mask all interrupts */ ++ advk_writel(pcie, PCIE_MSI_ALL_MASK, PCIE_MSI_MASK_REG); ++ advk_writel(pcie, PCIE_ISR0_ALL_MASK, PCIE_ISR0_MASK_REG); ++ advk_writel(pcie, PCIE_ISR1_ALL_MASK, PCIE_ISR1_MASK_REG); ++ advk_writel(pcie, PCIE_IRQ_ALL_MASK, HOST_CTRL_INT_MASK_REG); ++ ++ /* Clear all interrupts */ ++ advk_writel(pcie, PCIE_MSI_ALL_MASK, PCIE_MSI_STATUS_REG); ++ advk_writel(pcie, PCIE_ISR0_ALL_MASK, PCIE_ISR0_REG); ++ advk_writel(pcie, PCIE_ISR1_ALL_MASK, PCIE_ISR1_REG); ++ advk_writel(pcie, PCIE_IRQ_ALL_MASK, HOST_CTRL_INT_STATUS_REG); ++ + /* Remove IRQ domains */ + advk_pcie_remove_msi_irq_domain(pcie); + advk_pcie_remove_irq_domain(pcie); diff --git a/target/linux/generic/backport-5.10/850-v5.17-0008-PCI-aardvark-Fix-memory-leak-in-driver-unbind.patch b/target/linux/generic/backport-5.10/850-v5.17-0008-PCI-aardvark-Fix-memory-leak-in-driver-unbind.patch new file mode 100644 index 000000000..a460f770f --- /dev/null +++ b/target/linux/generic/backport-5.10/850-v5.17-0008-PCI-aardvark-Fix-memory-leak-in-driver-unbind.patch @@ -0,0 +1,33 @@ +From 2f040a17f5061457ae95035326d3159eddc1e5cc Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Pali=20Roh=C3=A1r?= +Date: Tue, 30 Nov 2021 18:29:10 +0100 +Subject: [PATCH] PCI: aardvark: Fix memory leak in driver unbind +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Free config space for emulated root bridge when unbinding driver to fix +memory leak. Do it after disabling and masking all interrupts, since +aardvark interrupt handler accesses config space of emulated root +bridge. + +Link: https://lore.kernel.org/r/20211130172913.9727-9-kabel@kernel.org +Signed-off-by: Pali Rohár +Signed-off-by: Marek Behún +Signed-off-by: Lorenzo Pieralisi +--- + drivers/pci/controller/pci-aardvark.c | 3 +++ + 1 file changed, 3 insertions(+) + +--- a/drivers/pci/controller/pci-aardvark.c ++++ b/drivers/pci/controller/pci-aardvark.c +@@ -1737,6 +1737,9 @@ static int advk_pcie_remove(struct platf + advk_pcie_remove_msi_irq_domain(pcie); + advk_pcie_remove_irq_domain(pcie); + ++ /* Free config space for emulated root bridge */ ++ pci_bridge_emul_cleanup(&pcie->bridge); ++ + /* Disable outbound address windows mapping */ + for (i = 0; i < OB_WIN_COUNT; i++) + advk_pcie_disable_ob_win(pcie, i); diff --git a/target/linux/generic/backport-5.10/850-v5.17-0009-PCI-aardvark-Assert-PERST-when-unbinding-driver.patch b/target/linux/generic/backport-5.10/850-v5.17-0009-PCI-aardvark-Assert-PERST-when-unbinding-driver.patch new file mode 100644 index 000000000..2dfd22f43 --- /dev/null +++ b/target/linux/generic/backport-5.10/850-v5.17-0009-PCI-aardvark-Assert-PERST-when-unbinding-driver.patch @@ -0,0 +1,33 @@ +From 1f54391be8ce0c981d312cb93acdc5608def576a Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Pali=20Roh=C3=A1r?= +Date: Tue, 30 Nov 2021 18:29:11 +0100 +Subject: [PATCH] PCI: aardvark: Assert PERST# when unbinding driver +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Put the PCIe card into reset by asserting PERST# signal when unbinding +driver. It doesn't make sense to leave the card working if it can't +communicate with the host. This should also save some power. + +Link: https://lore.kernel.org/r/20211130172913.9727-10-kabel@kernel.org +Signed-off-by: Pali Rohár +Signed-off-by: Marek Behún +Signed-off-by: Lorenzo Pieralisi +--- + drivers/pci/controller/pci-aardvark.c | 4 ++++ + 1 file changed, 4 insertions(+) + +--- a/drivers/pci/controller/pci-aardvark.c ++++ b/drivers/pci/controller/pci-aardvark.c +@@ -1740,6 +1740,10 @@ static int advk_pcie_remove(struct platf + /* Free config space for emulated root bridge */ + pci_bridge_emul_cleanup(&pcie->bridge); + ++ /* Assert PERST# signal which prepares PCIe card for power down */ ++ if (pcie->reset_gpio) ++ gpiod_set_value_cansleep(pcie->reset_gpio, 1); ++ + /* Disable outbound address windows mapping */ + for (i = 0; i < OB_WIN_COUNT; i++) + advk_pcie_disable_ob_win(pcie, i); diff --git a/target/linux/generic/backport-5.10/850-v5.17-0010-PCI-aardvark-Disable-link-training-when-unbinding-dr.patch b/target/linux/generic/backport-5.10/850-v5.17-0010-PCI-aardvark-Disable-link-training-when-unbinding-dr.patch new file mode 100644 index 000000000..e1ff7e857 --- /dev/null +++ b/target/linux/generic/backport-5.10/850-v5.17-0010-PCI-aardvark-Disable-link-training-when-unbinding-dr.patch @@ -0,0 +1,34 @@ +From 759dec2e3dfdbd261c41d2279f04f2351c971a49 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Pali=20Roh=C3=A1r?= +Date: Tue, 30 Nov 2021 18:29:12 +0100 +Subject: [PATCH] PCI: aardvark: Disable link training when unbinding driver +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Disable link training circuit in driver unbind sequence. We want to +leave link training in the same state as it was before the driver was +probed. + +Link: https://lore.kernel.org/r/20211130172913.9727-11-kabel@kernel.org +Signed-off-by: Pali Rohár +Signed-off-by: Marek Behún +Signed-off-by: Lorenzo Pieralisi +--- + drivers/pci/controller/pci-aardvark.c | 5 +++++ + 1 file changed, 5 insertions(+) + +--- a/drivers/pci/controller/pci-aardvark.c ++++ b/drivers/pci/controller/pci-aardvark.c +@@ -1744,6 +1744,11 @@ static int advk_pcie_remove(struct platf + if (pcie->reset_gpio) + gpiod_set_value_cansleep(pcie->reset_gpio, 1); + ++ /* Disable link training */ ++ val = advk_readl(pcie, PCIE_CORE_CTRL0_REG); ++ val &= ~LINK_TRAINING_EN; ++ advk_writel(pcie, val, PCIE_CORE_CTRL0_REG); ++ + /* Disable outbound address windows mapping */ + for (i = 0; i < OB_WIN_COUNT; i++) + advk_pcie_disable_ob_win(pcie, i); diff --git a/target/linux/generic/backport-5.10/850-v5.17-0011-PCI-aardvark-Disable-common-PHY-when-unbinding-drive.patch b/target/linux/generic/backport-5.10/850-v5.17-0011-PCI-aardvark-Disable-common-PHY-when-unbinding-drive.patch new file mode 100644 index 000000000..5a0840f3e --- /dev/null +++ b/target/linux/generic/backport-5.10/850-v5.17-0011-PCI-aardvark-Disable-common-PHY-when-unbinding-drive.patch @@ -0,0 +1,30 @@ +From fdbbe242c15a8f2cd0e3ad8a56cd0a447b771d0d Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Pali=20Roh=C3=A1r?= +Date: Tue, 30 Nov 2021 18:29:13 +0100 +Subject: [PATCH] PCI: aardvark: Disable common PHY when unbinding driver +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Disable the PCIe PHY when unbinding driver. This should save some power. + +Link: https://lore.kernel.org/r/20211130172913.9727-12-kabel@kernel.org +Signed-off-by: Pali Rohár +Signed-off-by: Marek Behún +Signed-off-by: Lorenzo Pieralisi +--- + drivers/pci/controller/pci-aardvark.c | 3 +++ + 1 file changed, 3 insertions(+) + +--- a/drivers/pci/controller/pci-aardvark.c ++++ b/drivers/pci/controller/pci-aardvark.c +@@ -1753,6 +1753,9 @@ static int advk_pcie_remove(struct platf + for (i = 0; i < OB_WIN_COUNT; i++) + advk_pcie_disable_ob_win(pcie, i); + ++ /* Disable phy */ ++ advk_pcie_disable_phy(pcie); ++ + return 0; + } + diff --git a/target/linux/generic/backport-5.10/851-v5.15-0001-phy-marvell-phy-mvebu-a3700-comphy-Rename-HS-SGMMI-t.patch b/target/linux/generic/backport-5.10/851-v5.15-0001-phy-marvell-phy-mvebu-a3700-comphy-Rename-HS-SGMMI-t.patch new file mode 100644 index 000000000..4f867724a --- /dev/null +++ b/target/linux/generic/backport-5.10/851-v5.15-0001-phy-marvell-phy-mvebu-a3700-comphy-Rename-HS-SGMMI-t.patch @@ -0,0 +1,67 @@ +From 40da06da15c1718b02072687bbfb2d08f5eb9399 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Pali=20Roh=C3=A1r?= +Date: Fri, 27 Aug 2021 11:27:52 +0200 +Subject: [PATCH] phy: marvell: phy-mvebu-a3700-comphy: Rename HS-SGMMI to + 2500Base-X +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Comphy phy mode 0x3 is incorrectly named. It is not SGMII but rather +2500Base-X mode which runs at 3.125 Gbps speed. + +Rename macro names and comments to 2500Base-X. + +Signed-off-by: Pali Rohár +Fixes: 9695375a3f4a ("phy: add A3700 COMPHY support") +Signed-off-by: David S. Miller +--- + drivers/phy/marvell/phy-mvebu-a3700-comphy.c | 10 +++++----- + 1 file changed, 5 insertions(+), 5 deletions(-) + +--- a/drivers/phy/marvell/phy-mvebu-a3700-comphy.c ++++ b/drivers/phy/marvell/phy-mvebu-a3700-comphy.c +@@ -29,7 +29,7 @@ + + #define COMPHY_FW_MODE_SATA 0x1 + #define COMPHY_FW_MODE_SGMII 0x2 +-#define COMPHY_FW_MODE_HS_SGMII 0x3 ++#define COMPHY_FW_MODE_2500BASEX 0x3 + #define COMPHY_FW_MODE_USB3H 0x4 + #define COMPHY_FW_MODE_USB3D 0x5 + #define COMPHY_FW_MODE_PCIE 0x6 +@@ -40,7 +40,7 @@ + + #define COMPHY_FW_SPEED_1_25G 0 /* SGMII 1G */ + #define COMPHY_FW_SPEED_2_5G 1 +-#define COMPHY_FW_SPEED_3_125G 2 /* SGMII 2.5G */ ++#define COMPHY_FW_SPEED_3_125G 2 /* 2500BASE-X */ + #define COMPHY_FW_SPEED_5G 3 + #define COMPHY_FW_SPEED_5_15625G 4 /* XFI 5G */ + #define COMPHY_FW_SPEED_6G 5 +@@ -84,14 +84,14 @@ static const struct mvebu_a3700_comphy_c + MVEBU_A3700_COMPHY_CONF_ETH(0, PHY_INTERFACE_MODE_SGMII, 1, + COMPHY_FW_MODE_SGMII), + MVEBU_A3700_COMPHY_CONF_ETH(0, PHY_INTERFACE_MODE_2500BASEX, 1, +- COMPHY_FW_MODE_HS_SGMII), ++ 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, + COMPHY_FW_MODE_SGMII), + MVEBU_A3700_COMPHY_CONF_ETH(1, PHY_INTERFACE_MODE_2500BASEX, 0, +- COMPHY_FW_MODE_HS_SGMII), ++ COMPHY_FW_MODE_2500BASEX), + /* lane 2 */ + MVEBU_A3700_COMPHY_CONF_GEN(2, PHY_MODE_SATA, 0, + COMPHY_FW_MODE_SATA), +@@ -205,7 +205,7 @@ static int mvebu_a3700_comphy_power_on(s + COMPHY_FW_SPEED_1_25G); + break; + case PHY_INTERFACE_MODE_2500BASEX: +- dev_dbg(lane->dev, "set lane %d to HS SGMII mode\n", ++ dev_dbg(lane->dev, "set lane %d to 2500BASEX mode\n", + lane->id); + fw_param = COMPHY_FW_NET(fw_mode, lane->port, + COMPHY_FW_SPEED_3_125G); diff --git a/target/linux/generic/backport-5.10/851-v5.15-0002-phy-marvell-phy-mvebu-a3700-comphy-Remove-unsupporte.patch b/target/linux/generic/backport-5.10/851-v5.15-0002-phy-marvell-phy-mvebu-a3700-comphy-Remove-unsupporte.patch new file mode 100644 index 000000000..99f56f1c5 --- /dev/null +++ b/target/linux/generic/backport-5.10/851-v5.15-0002-phy-marvell-phy-mvebu-a3700-comphy-Remove-unsupporte.patch @@ -0,0 +1,40 @@ +From e1dbe9ecf621b6f71f3d2df3e50731d583f3d27f Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Pali=20Roh=C3=A1r?= +Date: Fri, 27 Aug 2021 11:27:53 +0200 +Subject: [PATCH] phy: marvell: phy-mvebu-a3700-comphy: Remove unsupported + modes +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Armada 3700 does not support RXAUI, XFI and neither SFI. Remove unused +macros for these unsupported modes. + +Signed-off-by: Pali Rohár +Fixes: 9695375a3f4a ("phy: add A3700 COMPHY support") +Signed-off-by: David S. Miller +--- + drivers/phy/marvell/phy-mvebu-a3700-comphy.c | 6 ------ + 1 file changed, 6 deletions(-) + +--- a/drivers/phy/marvell/phy-mvebu-a3700-comphy.c ++++ b/drivers/phy/marvell/phy-mvebu-a3700-comphy.c +@@ -33,18 +33,12 @@ + #define COMPHY_FW_MODE_USB3H 0x4 + #define COMPHY_FW_MODE_USB3D 0x5 + #define COMPHY_FW_MODE_PCIE 0x6 +-#define COMPHY_FW_MODE_RXAUI 0x7 +-#define COMPHY_FW_MODE_XFI 0x8 +-#define COMPHY_FW_MODE_SFI 0x9 + #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_5_15625G 4 /* XFI 5G */ +-#define COMPHY_FW_SPEED_6G 5 +-#define COMPHY_FW_SPEED_10_3125G 6 /* XFI 10G */ + #define COMPHY_FW_SPEED_MAX 0x3F + + #define COMPHY_FW_MODE(mode) ((mode) << 12) diff --git a/target/linux/generic/backport-5.10/860-v5.17-MIPS-ath79-drop-_machine_restart-again.patch b/target/linux/generic/backport-5.10/860-v5.17-MIPS-ath79-drop-_machine_restart-again.patch new file mode 100644 index 000000000..971562a8f --- /dev/null +++ b/target/linux/generic/backport-5.10/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) +@@ -233,7 +224,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-5.10/870-hwmon-next-hwmon-lm70-Add-ti-tmp125-support.patch b/target/linux/generic/backport-5.10/870-hwmon-next-hwmon-lm70-Add-ti-tmp125-support.patch new file mode 100644 index 000000000..fabf17762 --- /dev/null +++ b/target/linux/generic/backport-5.10/870-hwmon-next-hwmon-lm70-Add-ti-tmp125-support.patch @@ -0,0 +1,71 @@ +From 31d8f414e1596ba54a4315418e4c0086fda9e428 Mon Sep 17 00:00:00 2001 +From: Christian Lamparter +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/files/drivers/mtd/nand/mtk_bmt.c b/target/linux/generic/files/drivers/mtd/nand/mtk_bmt.c new file mode 100644 index 000000000..bcff7d6ac --- /dev/null +++ b/target/linux/generic/files/drivers/mtd/nand/mtk_bmt.c @@ -0,0 +1,465 @@ +/* + * Copyright (c) 2017 MediaTek Inc. + * Author: Xiangsheng Hou + * Copyright (c) 2020-2022 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include "mtk_bmt.h" + +struct bmt_desc bmtd = {}; + +/* -------- Nand operations wrapper -------- */ +int bbt_nand_copy(u16 dest_blk, u16 src_blk, loff_t max_offset) +{ + int pages = bmtd.blk_size >> bmtd.pg_shift; + loff_t src = (loff_t)src_blk << bmtd.blk_shift; + loff_t dest = (loff_t)dest_blk << bmtd.blk_shift; + loff_t offset = 0; + uint8_t oob[64]; + int i, ret; + + for (i = 0; i < pages; i++) { + struct mtd_oob_ops rd_ops = { + .mode = MTD_OPS_PLACE_OOB, + .oobbuf = oob, + .ooblen = min_t(int, bmtd.mtd->oobsize / pages, sizeof(oob)), + .datbuf = bmtd.data_buf, + .len = bmtd.pg_size, + }; + struct mtd_oob_ops wr_ops = { + .mode = MTD_OPS_PLACE_OOB, + .oobbuf = oob, + .datbuf = bmtd.data_buf, + .len = bmtd.pg_size, + }; + + if (offset >= max_offset) + break; + + ret = bmtd._read_oob(bmtd.mtd, src + offset, &rd_ops); + if (ret < 0 && !mtd_is_bitflip(ret)) + return ret; + + if (!rd_ops.retlen) + break; + + ret = bmtd._write_oob(bmtd.mtd, dest + offset, &wr_ops); + if (ret < 0) + return ret; + + wr_ops.ooblen = rd_ops.oobretlen; + offset += rd_ops.retlen; + } + + return 0; +} + +/* -------- Bad Blocks Management -------- */ +bool mapping_block_in_range(int block, int *start, int *end) +{ + const __be32 *cur = bmtd.remap_range; + u32 addr = block << bmtd.blk_shift; + int i; + + if (!cur || !bmtd.remap_range_len) { + *start = 0; + *end = bmtd.total_blks; + return true; + } + + for (i = 0; i < bmtd.remap_range_len; i++, cur += 2) { + if (addr < be32_to_cpu(cur[0]) || addr >= be32_to_cpu(cur[1])) + continue; + + *start = be32_to_cpu(cur[0]); + *end = be32_to_cpu(cur[1]); + return true; + } + + return false; +} + +static bool +mtk_bmt_remap_block(u32 block, u32 mapped_block, int copy_len) +{ + int start, end; + + if (!mapping_block_in_range(block, &start, &end)) + return false; + + return bmtd.ops->remap_block(block, mapped_block, copy_len); +} + +static int +mtk_bmt_read(struct mtd_info *mtd, loff_t from, + struct mtd_oob_ops *ops) +{ + struct mtd_oob_ops cur_ops = *ops; + int retry_count = 0; + loff_t cur_from; + int ret = 0; + int max_bitflips = 0; + + ops->retlen = 0; + ops->oobretlen = 0; + + while (ops->retlen < ops->len || ops->oobretlen < ops->ooblen) { + int cur_ret; + + u32 offset = from & (bmtd.blk_size - 1); + u32 block = from >> bmtd.blk_shift; + int cur_block; + + cur_block = bmtd.ops->get_mapping_block(block); + if (cur_block < 0) + return -EIO; + + cur_from = ((loff_t)cur_block << bmtd.blk_shift) + offset; + + cur_ops.oobretlen = 0; + cur_ops.retlen = 0; + cur_ops.len = min_t(u32, mtd->erasesize - offset, + ops->len - ops->retlen); + cur_ret = bmtd._read_oob(mtd, cur_from, &cur_ops); + if (cur_ret < 0) + ret = cur_ret; + else + max_bitflips = max_t(int, max_bitflips, cur_ret); + if (cur_ret < 0 && !mtd_is_bitflip(cur_ret)) { + if (mtk_bmt_remap_block(block, cur_block, mtd->erasesize) && + retry_count++ < 10) + continue; + + goto out; + } + + if (mtd->bitflip_threshold && cur_ret >= mtd->bitflip_threshold) + mtk_bmt_remap_block(block, cur_block, mtd->erasesize); + + ops->retlen += cur_ops.retlen; + ops->oobretlen += cur_ops.oobretlen; + + cur_ops.ooboffs = 0; + cur_ops.datbuf += cur_ops.retlen; + cur_ops.oobbuf += cur_ops.oobretlen; + cur_ops.ooblen -= cur_ops.oobretlen; + + if (!cur_ops.len) + cur_ops.len = mtd->erasesize - offset; + + from += cur_ops.len; + retry_count = 0; + } + +out: + if (ret < 0) + return ret; + + return max_bitflips; +} + +static int +mtk_bmt_write(struct mtd_info *mtd, loff_t to, + struct mtd_oob_ops *ops) +{ + struct mtd_oob_ops cur_ops = *ops; + int retry_count = 0; + loff_t cur_to; + int ret; + + ops->retlen = 0; + ops->oobretlen = 0; + + while (ops->retlen < ops->len || ops->oobretlen < ops->ooblen) { + u32 offset = to & (bmtd.blk_size - 1); + u32 block = to >> bmtd.blk_shift; + int cur_block; + + cur_block = bmtd.ops->get_mapping_block(block); + if (cur_block < 0) + return -EIO; + + cur_to = ((loff_t)cur_block << bmtd.blk_shift) + offset; + + cur_ops.oobretlen = 0; + cur_ops.retlen = 0; + cur_ops.len = min_t(u32, bmtd.blk_size - offset, + ops->len - ops->retlen); + ret = bmtd._write_oob(mtd, cur_to, &cur_ops); + if (ret < 0) { + if (mtk_bmt_remap_block(block, cur_block, offset) && + retry_count++ < 10) + continue; + + return ret; + } + + ops->retlen += cur_ops.retlen; + ops->oobretlen += cur_ops.oobretlen; + + cur_ops.ooboffs = 0; + cur_ops.datbuf += cur_ops.retlen; + cur_ops.oobbuf += cur_ops.oobretlen; + cur_ops.ooblen -= cur_ops.oobretlen; + + if (!cur_ops.len) + cur_ops.len = mtd->erasesize - offset; + + to += cur_ops.len; + retry_count = 0; + } + + return 0; +} + +static int +mtk_bmt_mtd_erase(struct mtd_info *mtd, struct erase_info *instr) +{ + struct erase_info mapped_instr = { + .len = bmtd.blk_size, + }; + int retry_count = 0; + u64 start_addr, end_addr; + int ret; + u16 orig_block; + int block; + + start_addr = instr->addr & (~mtd->erasesize_mask); + end_addr = instr->addr + instr->len; + + while (start_addr < end_addr) { + orig_block = start_addr >> bmtd.blk_shift; + block = bmtd.ops->get_mapping_block(orig_block); + if (block < 0) + return -EIO; + mapped_instr.addr = (loff_t)block << bmtd.blk_shift; + ret = bmtd._erase(mtd, &mapped_instr); + if (ret) { + if (mtk_bmt_remap_block(orig_block, block, 0) && + retry_count++ < 10) + continue; + instr->fail_addr = start_addr; + break; + } + start_addr += mtd->erasesize; + retry_count = 0; + } + + return ret; +} +static int +mtk_bmt_block_isbad(struct mtd_info *mtd, loff_t ofs) +{ + int retry_count = 0; + u16 orig_block = ofs >> bmtd.blk_shift; + u16 block; + int ret; + +retry: + block = bmtd.ops->get_mapping_block(orig_block); + ret = bmtd._block_isbad(mtd, (loff_t)block << bmtd.blk_shift); + if (ret) { + if (mtk_bmt_remap_block(orig_block, block, bmtd.blk_size) && + retry_count++ < 10) + goto retry; + } + return ret; +} + +static int +mtk_bmt_block_markbad(struct mtd_info *mtd, loff_t ofs) +{ + u16 orig_block = ofs >> bmtd.blk_shift; + int block; + + block = bmtd.ops->get_mapping_block(orig_block); + if (block < 0) + return -EIO; + + mtk_bmt_remap_block(orig_block, block, bmtd.blk_size); + + return bmtd._block_markbad(mtd, (loff_t)block << bmtd.blk_shift); +} + +static void +mtk_bmt_replace_ops(struct mtd_info *mtd) +{ + bmtd._read_oob = mtd->_read_oob; + bmtd._write_oob = mtd->_write_oob; + bmtd._erase = mtd->_erase; + bmtd._block_isbad = mtd->_block_isbad; + bmtd._block_markbad = mtd->_block_markbad; + + mtd->_read_oob = mtk_bmt_read; + mtd->_write_oob = mtk_bmt_write; + mtd->_erase = mtk_bmt_mtd_erase; + mtd->_block_isbad = mtk_bmt_block_isbad; + mtd->_block_markbad = mtk_bmt_block_markbad; +} + +static int mtk_bmt_debug_repair(void *data, u64 val) +{ + int block = val >> bmtd.blk_shift; + int prev_block, new_block; + + prev_block = bmtd.ops->get_mapping_block(block); + if (prev_block < 0) + return -EIO; + + bmtd.ops->unmap_block(block); + new_block = bmtd.ops->get_mapping_block(block); + if (new_block < 0) + return -EIO; + + if (prev_block == new_block) + return 0; + + bbt_nand_erase(new_block); + bbt_nand_copy(new_block, prev_block, bmtd.blk_size); + + return 0; +} + +static int mtk_bmt_debug_mark_good(void *data, u64 val) +{ + bmtd.ops->unmap_block(val >> bmtd.blk_shift); + + return 0; +} + +static int mtk_bmt_debug_mark_bad(void *data, u64 val) +{ + u32 block = val >> bmtd.blk_shift; + int cur_block; + + cur_block = bmtd.ops->get_mapping_block(block); + if (cur_block < 0) + return -EIO; + + mtk_bmt_remap_block(block, cur_block, bmtd.blk_size); + + return 0; +} + +static int mtk_bmt_debug(void *data, u64 val) +{ + return bmtd.ops->debug(data, val); +} + + +DEFINE_DEBUGFS_ATTRIBUTE(fops_repair, NULL, mtk_bmt_debug_repair, "%llu\n"); +DEFINE_DEBUGFS_ATTRIBUTE(fops_mark_good, NULL, mtk_bmt_debug_mark_good, "%llu\n"); +DEFINE_DEBUGFS_ATTRIBUTE(fops_mark_bad, NULL, mtk_bmt_debug_mark_bad, "%llu\n"); +DEFINE_DEBUGFS_ATTRIBUTE(fops_debug, NULL, mtk_bmt_debug, "%llu\n"); + +static void +mtk_bmt_add_debugfs(void) +{ + struct dentry *dir; + + dir = bmtd.debugfs_dir = debugfs_create_dir("mtk-bmt", NULL); + if (!dir) + return; + + debugfs_create_file_unsafe("repair", S_IWUSR, dir, NULL, &fops_repair); + debugfs_create_file_unsafe("mark_good", S_IWUSR, dir, NULL, &fops_mark_good); + debugfs_create_file_unsafe("mark_bad", S_IWUSR, dir, NULL, &fops_mark_bad); + debugfs_create_file_unsafe("debug", S_IWUSR, dir, NULL, &fops_debug); +} + +void mtk_bmt_detach(struct mtd_info *mtd) +{ + if (bmtd.mtd != mtd) + return; + + if (bmtd.debugfs_dir) + debugfs_remove_recursive(bmtd.debugfs_dir); + bmtd.debugfs_dir = NULL; + + kfree(bmtd.bbt_buf); + kfree(bmtd.data_buf); + + mtd->_read_oob = bmtd._read_oob; + mtd->_write_oob = bmtd._write_oob; + mtd->_erase = bmtd._erase; + mtd->_block_isbad = bmtd._block_isbad; + mtd->_block_markbad = bmtd._block_markbad; + mtd->size = bmtd.total_blks << bmtd.blk_shift; + + memset(&bmtd, 0, sizeof(bmtd)); +} + + +int mtk_bmt_attach(struct mtd_info *mtd) +{ + struct device_node *np; + int ret = 0; + + if (bmtd.mtd) + return -ENOSPC; + + np = mtd_get_of_node(mtd); + if (!np) + return 0; + + if (of_property_read_bool(np, "mediatek,bmt-v2")) + bmtd.ops = &mtk_bmt_v2_ops; + else if (of_property_read_bool(np, "mediatek,nmbm")) + bmtd.ops = &mtk_bmt_nmbm_ops; + else if (of_property_read_bool(np, "mediatek,bbt")) + bmtd.ops = &mtk_bmt_bbt_ops; + else + return 0; + + bmtd.remap_range = of_get_property(np, "mediatek,bmt-remap-range", + &bmtd.remap_range_len); + bmtd.remap_range_len /= 8; + + bmtd.mtd = mtd; + mtk_bmt_replace_ops(mtd); + + bmtd.blk_size = mtd->erasesize; + bmtd.blk_shift = ffs(bmtd.blk_size) - 1; + bmtd.pg_size = mtd->writesize; + bmtd.pg_shift = ffs(bmtd.pg_size) - 1; + bmtd.total_blks = mtd->size >> bmtd.blk_shift; + + bmtd.data_buf = kzalloc(bmtd.pg_size + bmtd.mtd->oobsize, GFP_KERNEL); + if (!bmtd.data_buf) { + pr_info("nand: FATAL ERR: allocate buffer failed!\n"); + ret = -1; + goto error; + } + + memset(bmtd.data_buf, 0xff, bmtd.pg_size + bmtd.mtd->oobsize); + + ret = bmtd.ops->init(np); + if (ret) + goto error; + + mtk_bmt_add_debugfs(); + return 0; + +error: + mtk_bmt_detach(mtd); + return ret; +} + + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Xiangsheng Hou , Felix Fietkau "); +MODULE_DESCRIPTION("Bad Block mapping management v2 for MediaTek NAND Flash Driver"); + diff --git a/target/linux/generic/files/drivers/mtd/nand/mtk_bmt.h b/target/linux/generic/files/drivers/mtd/nand/mtk_bmt.h new file mode 100644 index 000000000..dff1f28c8 --- /dev/null +++ b/target/linux/generic/files/drivers/mtd/nand/mtk_bmt.h @@ -0,0 +1,131 @@ +#ifndef __MTK_BMT_PRIV_H +#define __MTK_BMT_PRIV_H + +#include +#include +#include +#include +#include +#include + +#define MAIN_SIGNATURE_OFFSET 0 +#define OOB_SIGNATURE_OFFSET 1 + +#define BBT_LOG(fmt, ...) pr_debug("[BBT][%s|%d] "fmt"\n", __func__, __LINE__, ##__VA_ARGS__) + +struct mtk_bmt_ops { + char *sig; + unsigned int sig_len; + int (*init)(struct device_node *np); + bool (*remap_block)(u16 block, u16 mapped_block, int copy_len); + void (*unmap_block)(u16 block); + int (*get_mapping_block)(int block); + int (*debug)(void *data, u64 val); +}; + +struct bbbt; +struct nmbm_instance; + +struct bmt_desc { + struct mtd_info *mtd; + unsigned char *bbt_buf; + unsigned char *data_buf; + + int (*_read_oob) (struct mtd_info *mtd, loff_t from, + struct mtd_oob_ops *ops); + int (*_write_oob) (struct mtd_info *mtd, loff_t to, + struct mtd_oob_ops *ops); + int (*_erase) (struct mtd_info *mtd, struct erase_info *instr); + int (*_block_isbad) (struct mtd_info *mtd, loff_t ofs); + int (*_block_markbad) (struct mtd_info *mtd, loff_t ofs); + + const struct mtk_bmt_ops *ops; + + union { + struct bbbt *bbt; + struct nmbm_instance *ni; + }; + + struct dentry *debugfs_dir; + + u32 table_size; + u32 pg_size; + u32 blk_size; + u16 pg_shift; + u16 blk_shift; + /* bbt logical address */ + u16 pool_lba; + /* bbt physical address */ + u16 pool_pba; + /* Maximum count of bad blocks that the vendor guaranteed */ + u16 bb_max; + /* Total blocks of the Nand Chip */ + u16 total_blks; + /* The block(n) BMT is located at (bmt_tbl[n]) */ + u16 bmt_blk_idx; + /* How many pages needs to store 'struct bbbt' */ + u32 bmt_pgs; + + const __be32 *remap_range; + int remap_range_len; + + /* to compensate for driver level remapping */ + u8 oob_offset; +}; + +extern struct bmt_desc bmtd; +extern const struct mtk_bmt_ops mtk_bmt_v2_ops; +extern const struct mtk_bmt_ops mtk_bmt_bbt_ops; +extern const struct mtk_bmt_ops mtk_bmt_nmbm_ops; + +static inline u32 blk_pg(u16 block) +{ + return (u32)(block << (bmtd.blk_shift - bmtd.pg_shift)); +} + +static inline int +bbt_nand_read(u32 page, unsigned char *dat, int dat_len, + unsigned char *fdm, int fdm_len) +{ + struct mtd_oob_ops ops = { + .mode = MTD_OPS_PLACE_OOB, + .ooboffs = bmtd.oob_offset, + .oobbuf = fdm, + .ooblen = fdm_len, + .datbuf = dat, + .len = dat_len, + }; + + return bmtd._read_oob(bmtd.mtd, page << bmtd.pg_shift, &ops); +} + +static inline int bbt_nand_erase(u16 block) +{ + struct mtd_info *mtd = bmtd.mtd; + struct erase_info instr = { + .addr = (loff_t)block << bmtd.blk_shift, + .len = bmtd.blk_size, + }; + + return bmtd._erase(mtd, &instr); +} + +static inline int write_bmt(u16 block, unsigned char *dat) +{ + struct mtd_oob_ops ops = { + .mode = MTD_OPS_PLACE_OOB, + .ooboffs = OOB_SIGNATURE_OFFSET + bmtd.oob_offset, + .oobbuf = bmtd.ops->sig, + .ooblen = bmtd.ops->sig_len, + .datbuf = dat, + .len = bmtd.bmt_pgs << bmtd.pg_shift, + }; + loff_t addr = (loff_t)block << bmtd.blk_shift; + + return bmtd._write_oob(bmtd.mtd, addr, &ops); +} + +int bbt_nand_copy(u16 dest_blk, u16 src_blk, loff_t max_offset); +bool mapping_block_in_range(int block, int *start, int *end); + +#endif diff --git a/target/linux/generic/files/drivers/mtd/nand/mtk_bmt_bbt.c b/target/linux/generic/files/drivers/mtd/nand/mtk_bmt_bbt.c new file mode 100644 index 000000000..519e1ed70 --- /dev/null +++ b/target/linux/generic/files/drivers/mtd/nand/mtk_bmt_bbt.c @@ -0,0 +1,203 @@ +/* + * Copyright (c) 2017 MediaTek Inc. + * Author: Xiangsheng Hou + * Copyright (c) 2020-2022 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include "mtk_bmt.h" + +static bool +bbt_block_is_bad(u16 block) +{ + u8 cur = bmtd.bbt_buf[block / 4]; + + return cur & (3 << ((block % 4) * 2)); +} + +static void +bbt_set_block_state(u16 block, bool bad) +{ + u8 mask = (3 << ((block % 4) * 2)); + + if (bad) + bmtd.bbt_buf[block / 4] |= mask; + else + bmtd.bbt_buf[block / 4] &= ~mask; + + bbt_nand_erase(bmtd.bmt_blk_idx); + write_bmt(bmtd.bmt_blk_idx, bmtd.bbt_buf); +} + +static int +get_mapping_block_index_bbt(int block) +{ + int start, end, ofs; + int bad_blocks = 0; + int i; + + if (!mapping_block_in_range(block, &start, &end)) + return block; + + start >>= bmtd.blk_shift; + end >>= bmtd.blk_shift; + /* skip bad blocks within the mapping range */ + ofs = block - start; + for (i = start; i < end; i++) { + if (bbt_block_is_bad(i)) + bad_blocks++; + else if (ofs) + ofs--; + else + break; + } + + if (i < end) + return i; + + /* when overflowing, remap remaining blocks to bad ones */ + for (i = end - 1; bad_blocks > 0; i--) { + if (!bbt_block_is_bad(i)) + continue; + + bad_blocks--; + if (bad_blocks <= ofs) + return i; + } + + return block; +} + +static bool remap_block_bbt(u16 block, u16 mapped_blk, int copy_len) +{ + int start, end; + u16 new_blk; + + if (!mapping_block_in_range(block, &start, &end)) + return false; + + bbt_set_block_state(mapped_blk, true); + + new_blk = get_mapping_block_index_bbt(block); + bbt_nand_erase(new_blk); + if (copy_len > 0) + bbt_nand_copy(new_blk, mapped_blk, copy_len); + + return true; +} + +static void +unmap_block_bbt(u16 block) +{ + bbt_set_block_state(block, false); +} + +static int +mtk_bmt_read_bbt(void) +{ + u8 oob_buf[8]; + int i; + + for (i = bmtd.total_blks - 1; i >= bmtd.total_blks - 5; i--) { + u32 page = i << (bmtd.blk_shift - bmtd.pg_shift); + + if (bbt_nand_read(page, bmtd.bbt_buf, bmtd.pg_size, + oob_buf, sizeof(oob_buf))) { + pr_info("read_bbt: could not read block %d\n", i); + continue; + } + + if (oob_buf[0] != 0xff) { + pr_info("read_bbt: bad block at %d\n", i); + continue; + } + + if (memcmp(&oob_buf[1], "mtknand", 7) != 0) { + pr_info("read_bbt: signature mismatch in block %d\n", i); + print_hex_dump(KERN_INFO, "", DUMP_PREFIX_OFFSET, 16, 1, oob_buf, 8, 1); + continue; + } + + pr_info("read_bbt: found bbt at block %d\n", i); + bmtd.bmt_blk_idx = i; + return 0; + } + + return -EIO; +} + + +static int +mtk_bmt_init_bbt(struct device_node *np) +{ + int buf_size = round_up(bmtd.total_blks >> 2, bmtd.blk_size); + int ret; + + bmtd.bbt_buf = kmalloc(buf_size, GFP_KERNEL); + if (!bmtd.bbt_buf) + return -ENOMEM; + + memset(bmtd.bbt_buf, 0xff, buf_size); + bmtd.mtd->size -= 4 * bmtd.mtd->erasesize; + + ret = mtk_bmt_read_bbt(); + if (ret) + return ret; + + bmtd.bmt_pgs = buf_size / bmtd.pg_size; + + return 0; +} + +static int mtk_bmt_debug_bbt(void *data, u64 val) +{ + char buf[5]; + int i, k; + + switch (val) { + case 0: + for (i = 0; i < bmtd.total_blks; i += 4) { + u8 cur = bmtd.bbt_buf[i / 4]; + + for (k = 0; k < 4; k++, cur >>= 2) + buf[k] = (cur & 3) ? 'B' : '.'; + + buf[4] = 0; + printk("[%06x] %s\n", i * bmtd.blk_size, buf); + } + break; + case 100: +#if 0 + for (i = bmtd.bmt_blk_idx; i < bmtd.total_blks - 1; i++) + bbt_nand_erase(bmtd.bmt_blk_idx); +#endif + + bmtd.bmt_blk_idx = bmtd.total_blks - 1; + bbt_nand_erase(bmtd.bmt_blk_idx); + write_bmt(bmtd.bmt_blk_idx, bmtd.bbt_buf); + break; + default: + break; + } + return 0; +} + +const struct mtk_bmt_ops mtk_bmt_bbt_ops = { + .sig = "mtknand", + .sig_len = 7, + .init = mtk_bmt_init_bbt, + .remap_block = remap_block_bbt, + .unmap_block = unmap_block_bbt, + .get_mapping_block = get_mapping_block_index_bbt, + .debug = mtk_bmt_debug_bbt, +}; diff --git a/target/linux/generic/files/drivers/mtd/nand/mtk_bmt_nmbm.c b/target/linux/generic/files/drivers/mtd/nand/mtk_bmt_nmbm.c new file mode 100644 index 000000000..a896e49ec --- /dev/null +++ b/target/linux/generic/files/drivers/mtd/nand/mtk_bmt_nmbm.c @@ -0,0 +1,2348 @@ +#include +#include +#include "mtk_bmt.h" + +#define nlog_err(ni, ...) printk(KERN_ERR __VA_ARGS__) +#define nlog_info(ni, ...) printk(KERN_INFO __VA_ARGS__) +#define nlog_debug(ni, ...) printk(KERN_INFO __VA_ARGS__) +#define nlog_warn(ni, ...) printk(KERN_WARNING __VA_ARGS__) + +#define NMBM_MAGIC_SIGNATURE 0x304d4d4e /* NMM0 */ +#define NMBM_MAGIC_INFO_TABLE 0x314d4d4e /* NMM1 */ + +#define NMBM_VERSION_MAJOR_S 0 +#define NMBM_VERSION_MAJOR_M 0xffff +#define NMBM_VERSION_MINOR_S 16 +#define NMBM_VERSION_MINOR_M 0xffff +#define NMBM_VERSION_MAKE(major, minor) (((major) & NMBM_VERSION_MAJOR_M) | \ + (((minor) & NMBM_VERSION_MINOR_M) << \ + NMBM_VERSION_MINOR_S)) +#define NMBM_VERSION_MAJOR_GET(ver) (((ver) >> NMBM_VERSION_MAJOR_S) & \ + NMBM_VERSION_MAJOR_M) +#define NMBM_VERSION_MINOR_GET(ver) (((ver) >> NMBM_VERSION_MINOR_S) & \ + NMBM_VERSION_MINOR_M) + +#define NMBM_BITMAP_UNIT_SIZE (sizeof(u32)) +#define NMBM_BITMAP_BITS_PER_BLOCK 2 +#define NMBM_BITMAP_BITS_PER_UNIT (8 * sizeof(u32)) +#define NMBM_BITMAP_BLOCKS_PER_UNIT (NMBM_BITMAP_BITS_PER_UNIT / \ + NMBM_BITMAP_BITS_PER_BLOCK) + +#define NMBM_SPARE_BLOCK_MULTI 1 +#define NMBM_SPARE_BLOCK_DIV 2 +#define NMBM_SPARE_BLOCK_MIN 2 + +#define NMBM_MGMT_DIV 16 +#define NMBM_MGMT_BLOCKS_MIN 32 + +#define NMBM_TRY_COUNT 3 + +#define BLOCK_ST_BAD 0 +#define BLOCK_ST_NEED_REMAP 2 +#define BLOCK_ST_GOOD 3 +#define BLOCK_ST_MASK 3 + +#define NMBM_VER_MAJOR 1 +#define NMBM_VER_MINOR 0 +#define NMBM_VER NMBM_VERSION_MAKE(NMBM_VER_MAJOR, \ + NMBM_VER_MINOR) + +struct nmbm_header { + u32 magic; + u32 version; + u32 size; + u32 checksum; +}; + +struct nmbm_signature { + struct nmbm_header header; + uint64_t nand_size; + u32 block_size; + u32 page_size; + u32 spare_size; + u32 mgmt_start_pb; + u8 max_try_count; + u8 padding[3]; +}; + +struct nmbm_info_table_header { + struct nmbm_header header; + u32 write_count; + u32 state_table_off; + u32 mapping_table_off; + u32 padding; +}; + +struct nmbm_instance { + u32 rawpage_size; + u32 rawblock_size; + u32 rawchip_size; + + struct nmbm_signature signature; + + u8 *info_table_cache; + u32 info_table_size; + u32 info_table_spare_blocks; + struct nmbm_info_table_header info_table; + + u32 *block_state; + u32 block_state_changed; + u32 state_table_size; + + int32_t *block_mapping; + u32 block_mapping_changed; + u32 mapping_table_size; + + u8 *page_cache; + + int protected; + + u32 block_count; + u32 data_block_count; + + u32 mgmt_start_ba; + u32 main_table_ba; + u32 backup_table_ba; + u32 mapping_blocks_ba; + u32 mapping_blocks_top_ba; + u32 signature_ba; + + u32 max_ratio; + u32 max_reserved_blocks; + bool empty_page_ecc_ok; + bool force_create; +}; + +static inline u32 nmbm_crc32(u32 crcval, const void *buf, size_t size) +{ + unsigned int chksz; + const unsigned char *p = buf; + + while (size) { + if (size > UINT_MAX) + chksz = UINT_MAX; + else + chksz = (uint)size; + + crcval = crc32_le(crcval, p, chksz); + size -= chksz; + p += chksz; + } + + return crcval; +} +/* + * nlog_table_creation - Print log of table creation event + * @ni: NMBM instance structure + * @main_table: whether the table is main info table + * @start_ba: start block address of the table + * @end_ba: block address after the end of the table + */ +static void nlog_table_creation(struct nmbm_instance *ni, bool main_table, + uint32_t start_ba, uint32_t end_ba) +{ + if (start_ba == end_ba - 1) + nlog_info(ni, "%s info table has been written to block %u\n", + main_table ? "Main" : "Backup", start_ba); + else + nlog_info(ni, "%s info table has been written to block %u-%u\n", + main_table ? "Main" : "Backup", start_ba, end_ba - 1); +} + +/* + * nlog_table_update - Print log of table update event + * @ni: NMBM instance structure + * @main_table: whether the table is main info table + * @start_ba: start block address of the table + * @end_ba: block address after the end of the table + */ +static void nlog_table_update(struct nmbm_instance *ni, bool main_table, + uint32_t start_ba, uint32_t end_ba) +{ + if (start_ba == end_ba - 1) + nlog_debug(ni, "%s info table has been updated in block %u\n", + main_table ? "Main" : "Backup", start_ba); + else + nlog_debug(ni, "%s info table has been updated in block %u-%u\n", + main_table ? "Main" : "Backup", start_ba, end_ba - 1); +} + +/* + * nlog_table_found - Print log of table found event + * @ni: NMBM instance structure + * @first_table: whether the table is first found info table + * @write_count: write count of the info table + * @start_ba: start block address of the table + * @end_ba: block address after the end of the table + */ +static void nlog_table_found(struct nmbm_instance *ni, bool first_table, + uint32_t write_count, uint32_t start_ba, + uint32_t end_ba) +{ + if (start_ba == end_ba - 1) + nlog_info(ni, "%s info table with writecount %u found in block %u\n", + first_table ? "First" : "Second", write_count, + start_ba); + else + nlog_info(ni, "%s info table with writecount %u found in block %u-%u\n", + first_table ? "First" : "Second", write_count, + start_ba, end_ba - 1); +} + +/*****************************************************************************/ +/* Address conversion functions */ +/*****************************************************************************/ + +/* + * ba2addr - Convert a block address to linear address + * @ni: NMBM instance structure + * @ba: Block address + */ +static uint64_t ba2addr(struct nmbm_instance *ni, uint32_t ba) +{ + return (uint64_t)ba << bmtd.blk_shift; +} +/* + * size2blk - Get minimum required blocks for storing specific size of data + * @ni: NMBM instance structure + * @size: size for storing + */ +static uint32_t size2blk(struct nmbm_instance *ni, uint64_t size) +{ + return (size + bmtd.blk_size - 1) >> bmtd.blk_shift; +} + +/*****************************************************************************/ +/* High level NAND chip APIs */ +/*****************************************************************************/ + +/* + * nmbm_read_phys_page - Read page with retry + * @ni: NMBM instance structure + * @addr: linear address where the data will be read from + * @data: the main data to be read + * @oob: the oob data to be read + * + * Read a page for at most NMBM_TRY_COUNT times. + * + * Return 0 for success, positive value for corrected bitflip count, + * -EBADMSG for ecc error, other negative values for other errors + */ +static int nmbm_read_phys_page(struct nmbm_instance *ni, uint64_t addr, + void *data, void *oob) +{ + int tries, ret; + + for (tries = 0; tries < NMBM_TRY_COUNT; tries++) { + struct mtd_oob_ops ops = { + .mode = MTD_OPS_PLACE_OOB, + .oobbuf = oob, + .datbuf = data, + }; + + if (data) + ops.len = bmtd.pg_size; + if (oob) + ops.ooblen = mtd_oobavail(bmtd.mtd, &ops); + + ret = bmtd._read_oob(bmtd.mtd, addr, &ops); + if (ret == -EUCLEAN) + return min_t(u32, bmtd.mtd->bitflip_threshold + 1, + bmtd.mtd->ecc_strength); + if (ret >= 0) + return 0; + } + + if (ret != -EBADMSG) + nlog_err(ni, "Page read failed at address 0x%08llx\n", addr); + + return ret; +} + +/* + * nmbm_write_phys_page - Write page with retry + * @ni: NMBM instance structure + * @addr: linear address where the data will be written to + * @data: the main data to be written + * @oob: the oob data to be written + * + * Write a page for at most NMBM_TRY_COUNT times. + */ +static bool nmbm_write_phys_page(struct nmbm_instance *ni, uint64_t addr, + const void *data, const void *oob) +{ + int tries, ret; + + for (tries = 0; tries < NMBM_TRY_COUNT; tries++) { + struct mtd_oob_ops ops = { + .mode = MTD_OPS_PLACE_OOB, + .oobbuf = (void *)oob, + .datbuf = (void *)data, + }; + + if (data) + ops.len = bmtd.pg_size; + if (oob) + ops.ooblen = mtd_oobavail(bmtd.mtd, &ops); + + ret = bmtd._write_oob(bmtd.mtd, addr, &ops); + if (!ret) + return true; + } + + nlog_err(ni, "Page write failed at address 0x%08llx\n", addr); + + return false; +} + +/* + * nmbm_erase_phys_block - Erase a block with retry + * @ni: NMBM instance structure + * @addr: Linear address + * + * Erase a block for at most NMBM_TRY_COUNT times. + */ +static bool nmbm_erase_phys_block(struct nmbm_instance *ni, uint64_t addr) +{ + int tries, ret; + + for (tries = 0; tries < NMBM_TRY_COUNT; tries++) { + struct erase_info ei = { + .addr = addr, + .len = bmtd.mtd->erasesize, + }; + + ret = bmtd._erase(bmtd.mtd, &ei); + if (!ret) + return true; + } + + nlog_err(ni, "Block erasure failed at address 0x%08llx\n", addr); + + return false; +} + +/* + * nmbm_check_bad_phys_block - Check whether a block is marked bad in OOB + * @ni: NMBM instance structure + * @ba: block address + */ +static bool nmbm_check_bad_phys_block(struct nmbm_instance *ni, uint32_t ba) +{ + uint64_t addr = ba2addr(ni, ba); + + return bmtd._block_isbad(bmtd.mtd, addr); +} + +/* + * nmbm_mark_phys_bad_block - Mark a block bad + * @ni: NMBM instance structure + * @addr: Linear address + */ +static int nmbm_mark_phys_bad_block(struct nmbm_instance *ni, uint32_t ba) +{ + uint64_t addr = ba2addr(ni, ba); + + nlog_info(ni, "Block %u [0x%08llx] will be marked bad\n", ba, addr); + + return bmtd._block_markbad(bmtd.mtd, addr); +} + +/*****************************************************************************/ +/* NMBM related functions */ +/*****************************************************************************/ + +/* + * nmbm_check_header - Check whether a NMBM structure is valid + * @data: pointer to a NMBM structure with a NMBM header at beginning + * @size: Size of the buffer pointed by @header + * + * The size of the NMBM structure may be larger than NMBM header, + * e.g. block mapping table and block state table. + */ +static bool nmbm_check_header(const void *data, uint32_t size) +{ + const struct nmbm_header *header = data; + struct nmbm_header nhdr; + uint32_t new_checksum; + + /* + * Make sure expected structure size is equal or smaller than + * buffer size. + */ + if (header->size > size) + return false; + + memcpy(&nhdr, data, sizeof(nhdr)); + + nhdr.checksum = 0; + new_checksum = nmbm_crc32(0, &nhdr, sizeof(nhdr)); + if (header->size > sizeof(nhdr)) + new_checksum = nmbm_crc32(new_checksum, + (const uint8_t *)data + sizeof(nhdr), + header->size - sizeof(nhdr)); + + if (header->checksum != new_checksum) + return false; + + return true; +} + +/* + * nmbm_update_checksum - Update checksum of a NMBM structure + * @header: pointer to a NMBM structure with a NMBM header at beginning + * + * The size of the NMBM structure must be specified by @header->size + */ +static void nmbm_update_checksum(struct nmbm_header *header) +{ + header->checksum = 0; + header->checksum = nmbm_crc32(0, header, header->size); +} + +/* + * nmbm_get_spare_block_count - Calculate number of blocks should be reserved + * @block_count: number of blocks of data + * + * Calculate number of blocks should be reserved for data + */ +static uint32_t nmbm_get_spare_block_count(uint32_t block_count) +{ + uint32_t val; + + val = (block_count + NMBM_SPARE_BLOCK_DIV / 2) / NMBM_SPARE_BLOCK_DIV; + val *= NMBM_SPARE_BLOCK_MULTI; + + if (val < NMBM_SPARE_BLOCK_MIN) + val = NMBM_SPARE_BLOCK_MIN; + + return val; +} + +/* + * nmbm_get_block_state_raw - Get state of a block from raw block state table + * @block_state: pointer to raw block state table (bitmap) + * @ba: block address + */ +static uint32_t nmbm_get_block_state_raw(u32 *block_state, + uint32_t ba) +{ + uint32_t unit, shift; + + unit = ba / NMBM_BITMAP_BLOCKS_PER_UNIT; + shift = (ba % NMBM_BITMAP_BLOCKS_PER_UNIT) * NMBM_BITMAP_BITS_PER_BLOCK; + + return (block_state[unit] >> shift) & BLOCK_ST_MASK; +} + +/* + * nmbm_get_block_state - Get state of a block from block state table + * @ni: NMBM instance structure + * @ba: block address + */ +static uint32_t nmbm_get_block_state(struct nmbm_instance *ni, uint32_t ba) +{ + return nmbm_get_block_state_raw(ni->block_state, ba); +} + +/* + * nmbm_set_block_state - Set state of a block to block state table + * @ni: NMBM instance structure + * @ba: block address + * @state: block state + * + * Set state of a block. If the block state changed, ni->block_state_changed + * will be increased. + */ +static bool nmbm_set_block_state(struct nmbm_instance *ni, uint32_t ba, + uint32_t state) +{ + uint32_t unit, shift, orig; + u32 uv; + + unit = ba / NMBM_BITMAP_BLOCKS_PER_UNIT; + shift = (ba % NMBM_BITMAP_BLOCKS_PER_UNIT) * NMBM_BITMAP_BITS_PER_BLOCK; + + orig = (ni->block_state[unit] >> shift) & BLOCK_ST_MASK; + state &= BLOCK_ST_MASK; + + uv = ni->block_state[unit] & (~(BLOCK_ST_MASK << shift)); + uv |= state << shift; + ni->block_state[unit] = uv; + + if (orig != state) { + ni->block_state_changed++; + return true; + } + + return false; +} + +/* + * nmbm_block_walk_asc - Skip specified number of good blocks, ascending addr. + * @ni: NMBM instance structure + * @ba: start physical block address + * @nba: return physical block address after walk + * @count: number of good blocks to be skipped + * @limit: highest block address allowed for walking + * + * Start from @ba, skipping any bad blocks, counting @count good blocks, and + * return the next good block address. + * + * If no enough good blocks counted while @limit reached, false will be returned. + * + * If @count == 0, nearest good block address will be returned. + * @limit is not counted in walking. + */ +static bool nmbm_block_walk_asc(struct nmbm_instance *ni, uint32_t ba, + uint32_t *nba, uint32_t count, + uint32_t limit) +{ + int32_t nblock = count; + + if (limit >= ni->block_count) + limit = ni->block_count - 1; + + while (ba < limit) { + if (nmbm_get_block_state(ni, ba) == BLOCK_ST_GOOD) + nblock--; + + if (nblock < 0) { + *nba = ba; + return true; + } + + ba++; + } + + return false; +} + +/* + * nmbm_block_walk_desc - Skip specified number of good blocks, descending addr + * @ni: NMBM instance structure + * @ba: start physical block address + * @nba: return physical block address after walk + * @count: number of good blocks to be skipped + * @limit: lowest block address allowed for walking + * + * Start from @ba, skipping any bad blocks, counting @count good blocks, and + * return the next good block address. + * + * If no enough good blocks counted while @limit reached, false will be returned. + * + * If @count == 0, nearest good block address will be returned. + * @limit is not counted in walking. + */ +static bool nmbm_block_walk_desc(struct nmbm_instance *ni, uint32_t ba, + uint32_t *nba, uint32_t count, uint32_t limit) +{ + int32_t nblock = count; + + if (limit >= ni->block_count) + limit = ni->block_count - 1; + + while (ba > limit) { + if (nmbm_get_block_state(ni, ba) == BLOCK_ST_GOOD) + nblock--; + + if (nblock < 0) { + *nba = ba; + return true; + } + + ba--; + } + + return false; +} + +/* + * nmbm_block_walk - Skip specified number of good blocks from curr. block addr + * @ni: NMBM instance structure + * @ascending: whether to walk ascending + * @ba: start physical block address + * @nba: return physical block address after walk + * @count: number of good blocks to be skipped + * @limit: highest/lowest block address allowed for walking + * + * Start from @ba, skipping any bad blocks, counting @count good blocks, and + * return the next good block address. + * + * If no enough good blocks counted while @limit reached, false will be returned. + * + * If @count == 0, nearest good block address will be returned. + * @limit can be set to negative if no limit required. + * @limit is not counted in walking. + */ +static bool nmbm_block_walk(struct nmbm_instance *ni, bool ascending, + uint32_t ba, uint32_t *nba, int32_t count, + int32_t limit) +{ + if (ascending) + return nmbm_block_walk_asc(ni, ba, nba, count, limit); + + return nmbm_block_walk_desc(ni, ba, nba, count, limit); +} + +/* + * nmbm_scan_badblocks - Scan and record all bad blocks + * @ni: NMBM instance structure + * + * Scan the entire lower NAND chip and record all bad blocks in to block state + * table. + */ +static void nmbm_scan_badblocks(struct nmbm_instance *ni) +{ + uint32_t ba; + + for (ba = 0; ba < ni->block_count; ba++) { + if (nmbm_check_bad_phys_block(ni, ba)) { + nmbm_set_block_state(ni, ba, BLOCK_ST_BAD); + nlog_info(ni, "Bad block %u [0x%08llx]\n", ba, + ba2addr(ni, ba)); + } + } +} + +/* + * nmbm_build_mapping_table - Build initial block mapping table + * @ni: NMBM instance structure + * + * The initial mapping table will be compatible with the stratage of + * factory production. + */ +static void nmbm_build_mapping_table(struct nmbm_instance *ni) +{ + uint32_t pb, lb; + + for (pb = 0, lb = 0; pb < ni->mgmt_start_ba; pb++) { + if (nmbm_get_block_state(ni, pb) == BLOCK_ST_BAD) + continue; + + /* Always map to the next good block */ + ni->block_mapping[lb++] = pb; + } + + ni->data_block_count = lb; + + /* Unusable/Management blocks */ + for (pb = lb; pb < ni->block_count; pb++) + ni->block_mapping[pb] = -1; +} + +/* + * nmbm_erase_block_and_check - Erase a block and check its usability + * @ni: NMBM instance structure + * @ba: block address to be erased + * + * Erase a block anc check its usability + * + * Return true if the block is usable, false if erasure failure or the block + * has too many bitflips. + */ +static bool nmbm_erase_block_and_check(struct nmbm_instance *ni, uint32_t ba) +{ + uint64_t addr, off; + bool success; + int ret; + + success = nmbm_erase_phys_block(ni, ba2addr(ni, ba)); + if (!success) + return false; + + if (!ni->empty_page_ecc_ok) + return true; + + /* Check every page to make sure there aren't too many bitflips */ + + addr = ba2addr(ni, ba); + + for (off = 0; off < bmtd.blk_size; off += bmtd.pg_size) { + ret = nmbm_read_phys_page(ni, addr + off, ni->page_cache, NULL); + if (ret == -EBADMSG) { + /* + * empty_page_ecc_ok means the empty page is + * still protected by ECC. So reading pages with ECC + * enabled and -EBADMSG means there are too many + * bitflips that can't be recovered, and the block + * containing the page should be marked bad. + */ + nlog_err(ni, + "Too many bitflips in empty page at 0x%llx\n", + addr + off); + return false; + } + } + + return true; +} + +/* + * nmbm_erase_range - Erase a range of blocks + * @ni: NMBM instance structure + * @ba: block address where the erasure will start + * @limit: top block address allowed for erasure + * + * Erase blocks within the specific range. Newly-found bad blocks will be + * marked. + * + * @limit is not counted into the allowed erasure address. + */ +static void nmbm_erase_range(struct nmbm_instance *ni, uint32_t ba, + uint32_t limit) +{ + bool success; + + while (ba < limit) { + if (nmbm_get_block_state(ni, ba) != BLOCK_ST_GOOD) + goto next_block; + + /* Insurance to detect unexpected bad block marked by user */ + if (nmbm_check_bad_phys_block(ni, ba)) { + nmbm_set_block_state(ni, ba, BLOCK_ST_BAD); + goto next_block; + } + + success = nmbm_erase_block_and_check(ni, ba); + if (success) + goto next_block; + + nmbm_mark_phys_bad_block(ni, ba); + nmbm_set_block_state(ni, ba, BLOCK_ST_BAD); + + next_block: + ba++; + } +} + +/* + * nmbm_write_repeated_data - Write critical data to a block with retry + * @ni: NMBM instance structure + * @ba: block address where the data will be written to + * @data: the data to be written + * @size: size of the data + * + * Write data to every page of the block. Success only if all pages within + * this block have been successfully written. + * + * Make sure data size is not bigger than one page. + * + * This function will write and verify every page for at most + * NMBM_TRY_COUNT times. + */ +static bool nmbm_write_repeated_data(struct nmbm_instance *ni, uint32_t ba, + const void *data, uint32_t size) +{ + uint64_t addr, off; + bool success; + int ret; + + if (size > bmtd.pg_size) + return false; + + addr = ba2addr(ni, ba); + + for (off = 0; off < bmtd.blk_size; off += bmtd.pg_size) { + /* Prepare page data. fill 0xff to unused region */ + memcpy(ni->page_cache, data, size); + memset(ni->page_cache + size, 0xff, ni->rawpage_size - size); + + success = nmbm_write_phys_page(ni, addr + off, ni->page_cache, NULL); + if (!success) + return false; + + /* Verify the data just written. ECC error indicates failure */ + ret = nmbm_read_phys_page(ni, addr + off, ni->page_cache, NULL); + if (ret < 0) + return false; + + if (memcmp(ni->page_cache, data, size)) + return false; + } + + return true; +} + +/* + * nmbm_write_signature - Write signature to NAND chip + * @ni: NMBM instance structure + * @limit: top block address allowed for writing + * @signature: the signature to be written + * @signature_ba: the actual block address where signature is written to + * + * Write signature within a specific range, from chip bottom to limit. + * At most one block will be written. + * + * @limit is not counted into the allowed write address. + */ +static bool nmbm_write_signature(struct nmbm_instance *ni, uint32_t limit, + const struct nmbm_signature *signature, + uint32_t *signature_ba) +{ + uint32_t ba = ni->block_count - 1; + bool success; + + while (ba > limit) { + if (nmbm_get_block_state(ni, ba) != BLOCK_ST_GOOD) + goto next_block; + + /* Insurance to detect unexpected bad block marked by user */ + if (nmbm_check_bad_phys_block(ni, ba)) { + nmbm_set_block_state(ni, ba, BLOCK_ST_BAD); + goto next_block; + } + + success = nmbm_erase_block_and_check(ni, ba); + if (!success) + goto skip_bad_block; + + success = nmbm_write_repeated_data(ni, ba, signature, + sizeof(*signature)); + if (success) { + *signature_ba = ba; + return true; + } + + skip_bad_block: + nmbm_mark_phys_bad_block(ni, ba); + nmbm_set_block_state(ni, ba, BLOCK_ST_BAD); + + next_block: + ba--; + }; + + return false; +} + +/* + * nmbn_read_data - Read data + * @ni: NMBM instance structure + * @addr: linear address where the data will be read from + * @data: the data to be read + * @size: the size of data + * + * Read data range. + * Every page will be tried for at most NMBM_TRY_COUNT times. + * + * Return 0 for success, positive value for corrected bitflip count, + * -EBADMSG for ecc error, other negative values for other errors + */ +static int nmbn_read_data(struct nmbm_instance *ni, uint64_t addr, void *data, + uint32_t size) +{ + uint64_t off = addr; + uint8_t *ptr = data; + uint32_t sizeremain = size, chunksize, leading; + int ret; + + while (sizeremain) { + leading = off & (bmtd.pg_size - 1); + chunksize = bmtd.pg_size - leading; + if (chunksize > sizeremain) + chunksize = sizeremain; + + if (chunksize == bmtd.pg_size) { + ret = nmbm_read_phys_page(ni, off - leading, ptr, NULL); + if (ret < 0) + return ret; + } else { + ret = nmbm_read_phys_page(ni, off - leading, + ni->page_cache, NULL); + if (ret < 0) + return ret; + + memcpy(ptr, ni->page_cache + leading, chunksize); + } + + off += chunksize; + ptr += chunksize; + sizeremain -= chunksize; + } + + return 0; +} + +/* + * nmbn_write_verify_data - Write data with validation + * @ni: NMBM instance structure + * @addr: linear address where the data will be written to + * @data: the data to be written + * @size: the size of data + * + * Write data and verify. + * Every page will be tried for at most NMBM_TRY_COUNT times. + */ +static bool nmbn_write_verify_data(struct nmbm_instance *ni, uint64_t addr, + const void *data, uint32_t size) +{ + uint64_t off = addr; + const uint8_t *ptr = data; + uint32_t sizeremain = size, chunksize, leading; + bool success; + int ret; + + while (sizeremain) { + leading = off & (bmtd.pg_size - 1); + chunksize = bmtd.pg_size - leading; + if (chunksize > sizeremain) + chunksize = sizeremain; + + /* Prepare page data. fill 0xff to unused region */ + memset(ni->page_cache, 0xff, ni->rawpage_size); + memcpy(ni->page_cache + leading, ptr, chunksize); + + success = nmbm_write_phys_page(ni, off - leading, + ni->page_cache, NULL); + if (!success) + return false; + + /* Verify the data just written. ECC error indicates failure */ + ret = nmbm_read_phys_page(ni, off - leading, ni->page_cache, NULL); + if (ret < 0) + return false; + + if (memcmp(ni->page_cache + leading, ptr, chunksize)) + return false; + + off += chunksize; + ptr += chunksize; + sizeremain -= chunksize; + } + + return true; +} + +/* + * nmbm_write_mgmt_range - Write management data into NAND within a range + * @ni: NMBM instance structure + * @addr: preferred start block address for writing + * @limit: highest block address allowed for writing + * @data: the data to be written + * @size: the size of data + * @actual_start_ba: actual start block address of data + * @actual_end_ba: block address after the end of data + * + * @limit is not counted into the allowed write address. + */ +static bool nmbm_write_mgmt_range(struct nmbm_instance *ni, uint32_t ba, + uint32_t limit, const void *data, + uint32_t size, uint32_t *actual_start_ba, + uint32_t *actual_end_ba) +{ + const uint8_t *ptr = data; + uint32_t sizeremain = size, chunksize; + bool success; + + while (sizeremain && ba < limit) { + chunksize = sizeremain; + if (chunksize > bmtd.blk_size) + chunksize = bmtd.blk_size; + + if (nmbm_get_block_state(ni, ba) != BLOCK_ST_GOOD) + goto next_block; + + /* Insurance to detect unexpected bad block marked by user */ + if (nmbm_check_bad_phys_block(ni, ba)) { + nmbm_set_block_state(ni, ba, BLOCK_ST_BAD); + goto next_block; + } + + success = nmbm_erase_block_and_check(ni, ba); + if (!success) + goto skip_bad_block; + + success = nmbn_write_verify_data(ni, ba2addr(ni, ba), ptr, + chunksize); + if (!success) + goto skip_bad_block; + + if (sizeremain == size) + *actual_start_ba = ba; + + ptr += chunksize; + sizeremain -= chunksize; + + goto next_block; + + skip_bad_block: + nmbm_mark_phys_bad_block(ni, ba); + nmbm_set_block_state(ni, ba, BLOCK_ST_BAD); + + next_block: + ba++; + } + + if (sizeremain) + return false; + + *actual_end_ba = ba; + + return true; +} + +/* + * nmbm_generate_info_table_cache - Generate info table cache data + * @ni: NMBM instance structure + * + * Generate info table cache data to be written into flash. + */ +static bool nmbm_generate_info_table_cache(struct nmbm_instance *ni) +{ + bool changed = false; + + memset(ni->info_table_cache, 0xff, ni->info_table_size); + + memcpy(ni->info_table_cache + ni->info_table.state_table_off, + ni->block_state, ni->state_table_size); + + memcpy(ni->info_table_cache + ni->info_table.mapping_table_off, + ni->block_mapping, ni->mapping_table_size); + + ni->info_table.header.magic = NMBM_MAGIC_INFO_TABLE; + ni->info_table.header.version = NMBM_VER; + ni->info_table.header.size = ni->info_table_size; + + if (ni->block_state_changed || ni->block_mapping_changed) { + ni->info_table.write_count++; + changed = true; + } + + memcpy(ni->info_table_cache, &ni->info_table, sizeof(ni->info_table)); + + nmbm_update_checksum((struct nmbm_header *)ni->info_table_cache); + + return changed; +} + +/* + * nmbm_write_info_table - Write info table into NAND within a range + * @ni: NMBM instance structure + * @ba: preferred start block address for writing + * @limit: highest block address allowed for writing + * @actual_start_ba: actual start block address of info table + * @actual_end_ba: block address after the end of info table + * + * @limit is counted into the allowed write address. + */ +static bool nmbm_write_info_table(struct nmbm_instance *ni, uint32_t ba, + uint32_t limit, uint32_t *actual_start_ba, + uint32_t *actual_end_ba) +{ + return nmbm_write_mgmt_range(ni, ba, limit, ni->info_table_cache, + ni->info_table_size, actual_start_ba, + actual_end_ba); +} + +/* + * nmbm_mark_tables_clean - Mark info table `clean' + * @ni: NMBM instance structure + */ +static void nmbm_mark_tables_clean(struct nmbm_instance *ni) +{ + ni->block_state_changed = 0; + ni->block_mapping_changed = 0; +} + +/* + * nmbm_try_reserve_blocks - Reserve blocks with compromisation + * @ni: NMBM instance structure + * @ba: start physical block address + * @nba: return physical block address after reservation + * @count: number of good blocks to be skipped + * @min_count: minimum number of good blocks to be skipped + * @limit: highest/lowest block address allowed for walking + * + * Reserve specific blocks. If failed, try to reserve as many as possible. + */ +static bool nmbm_try_reserve_blocks(struct nmbm_instance *ni, uint32_t ba, + uint32_t *nba, uint32_t count, + int32_t min_count, int32_t limit) +{ + int32_t nblocks = count; + bool success; + + while (nblocks >= min_count) { + success = nmbm_block_walk(ni, true, ba, nba, nblocks, limit); + if (success) + return true; + + nblocks--; + } + + return false; +} + +/* + * nmbm_rebuild_info_table - Build main & backup info table from scratch + * @ni: NMBM instance structure + * @allow_no_gap: allow no spare blocks between two tables + */ +static bool nmbm_rebuild_info_table(struct nmbm_instance *ni) +{ + uint32_t table_start_ba, table_end_ba, next_start_ba; + uint32_t main_table_end_ba; + bool success; + + /* Set initial value */ + ni->main_table_ba = 0; + ni->backup_table_ba = 0; + ni->mapping_blocks_ba = ni->mapping_blocks_top_ba; + + /* Write main table */ + success = nmbm_write_info_table(ni, ni->mgmt_start_ba, + ni->mapping_blocks_top_ba, + &table_start_ba, &table_end_ba); + if (!success) { + /* Failed to write main table, data will be lost */ + nlog_err(ni, "Unable to write at least one info table!\n"); + nlog_err(ni, "Please save your data before power off!\n"); + ni->protected = 1; + return false; + } + + /* Main info table is successfully written, record its offset */ + ni->main_table_ba = table_start_ba; + main_table_end_ba = table_end_ba; + + /* Adjust mapping_blocks_ba */ + ni->mapping_blocks_ba = table_end_ba; + + nmbm_mark_tables_clean(ni); + + nlog_table_creation(ni, true, table_start_ba, table_end_ba); + + /* Reserve spare blocks for main info table. */ + success = nmbm_try_reserve_blocks(ni, table_end_ba, + &next_start_ba, + ni->info_table_spare_blocks, 0, + ni->mapping_blocks_top_ba - + size2blk(ni, ni->info_table_size)); + if (!success) { + /* There is no spare block. */ + nlog_debug(ni, "No room for backup info table\n"); + return true; + } + + /* Write backup info table. */ + success = nmbm_write_info_table(ni, next_start_ba, + ni->mapping_blocks_top_ba, + &table_start_ba, &table_end_ba); + if (!success) { + /* There is no enough blocks for backup table. */ + nlog_debug(ni, "No room for backup info table\n"); + return true; + } + + /* Backup table is successfully written, record its offset */ + ni->backup_table_ba = table_start_ba; + + /* Adjust mapping_blocks_off */ + ni->mapping_blocks_ba = table_end_ba; + + /* Erase spare blocks of main table to clean possible interference data */ + nmbm_erase_range(ni, main_table_end_ba, ni->backup_table_ba); + + nlog_table_creation(ni, false, table_start_ba, table_end_ba); + + return true; +} + +/* + * nmbm_rescue_single_info_table - Rescue when there is only one info table + * @ni: NMBM instance structure + * + * This function is called when there is only one info table exists. + * This function may fail if we can't write new info table + */ +static bool nmbm_rescue_single_info_table(struct nmbm_instance *ni) +{ + uint32_t table_start_ba, table_end_ba, write_ba; + bool success; + + /* Try to write new info table in front of existing table */ + success = nmbm_write_info_table(ni, ni->mgmt_start_ba, + ni->main_table_ba, + &table_start_ba, + &table_end_ba); + if (success) { + /* + * New table becomes the main table, existing table becomes + * the backup table. + */ + ni->backup_table_ba = ni->main_table_ba; + ni->main_table_ba = table_start_ba; + + nmbm_mark_tables_clean(ni); + + /* Erase spare blocks of main table to clean possible interference data */ + nmbm_erase_range(ni, table_end_ba, ni->backup_table_ba); + + nlog_table_creation(ni, true, table_start_ba, table_end_ba); + + return true; + } + + /* Try to reserve spare blocks for existing table */ + success = nmbm_try_reserve_blocks(ni, ni->mapping_blocks_ba, &write_ba, + ni->info_table_spare_blocks, 0, + ni->mapping_blocks_top_ba - + size2blk(ni, ni->info_table_size)); + if (!success) { + nlog_warn(ni, "Failed to rescue single info table\n"); + return false; + } + + /* Try to write new info table next to the existing table */ + while (write_ba >= ni->mapping_blocks_ba) { + success = nmbm_write_info_table(ni, write_ba, + ni->mapping_blocks_top_ba, + &table_start_ba, + &table_end_ba); + if (success) + break; + + write_ba--; + } + + if (success) { + /* Erase spare blocks of main table to clean possible interference data */ + nmbm_erase_range(ni, ni->mapping_blocks_ba, table_start_ba); + + /* New table becomes the backup table */ + ni->backup_table_ba = table_start_ba; + ni->mapping_blocks_ba = table_end_ba; + + nmbm_mark_tables_clean(ni); + + nlog_table_creation(ni, false, table_start_ba, table_end_ba); + + return true; + } + + nlog_warn(ni, "Failed to rescue single info table\n"); + return false; +} + +/* + * nmbm_update_single_info_table - Update specific one info table + * @ni: NMBM instance structure + */ +static bool nmbm_update_single_info_table(struct nmbm_instance *ni, + bool update_main_table) +{ + uint32_t write_start_ba, write_limit, table_start_ba, table_end_ba; + bool success; + + /* Determine the write range */ + if (update_main_table) { + write_start_ba = ni->main_table_ba; + write_limit = ni->backup_table_ba; + } else { + write_start_ba = ni->backup_table_ba; + write_limit = ni->mapping_blocks_top_ba; + } + + success = nmbm_write_info_table(ni, write_start_ba, write_limit, + &table_start_ba, &table_end_ba); + if (success) { + if (update_main_table) { + ni->main_table_ba = table_start_ba; + } else { + ni->backup_table_ba = table_start_ba; + ni->mapping_blocks_ba = table_end_ba; + } + + nmbm_mark_tables_clean(ni); + + nlog_table_update(ni, update_main_table, table_start_ba, + table_end_ba); + + return true; + } + + if (update_main_table) { + /* + * If failed to update main table, make backup table the new + * main table, and call nmbm_rescue_single_info_table() + */ + nlog_warn(ni, "Unable to update %s info table\n", + update_main_table ? "Main" : "Backup"); + + ni->main_table_ba = ni->backup_table_ba; + ni->backup_table_ba = 0; + return nmbm_rescue_single_info_table(ni); + } + + /* Only one table left */ + ni->mapping_blocks_ba = ni->backup_table_ba; + ni->backup_table_ba = 0; + + return false; +} + +/* + * nmbm_rescue_main_info_table - Rescue when failed to write main info table + * @ni: NMBM instance structure + * + * This function is called when main info table failed to be written, and + * backup info table exists. + */ +static bool nmbm_rescue_main_info_table(struct nmbm_instance *ni) +{ + uint32_t tmp_table_start_ba, tmp_table_end_ba, main_table_start_ba; + uint32_t main_table_end_ba, write_ba; + uint32_t info_table_erasesize = size2blk(ni, ni->info_table_size); + bool success; + + /* Try to reserve spare blocks for existing backup info table */ + success = nmbm_try_reserve_blocks(ni, ni->mapping_blocks_ba, &write_ba, + ni->info_table_spare_blocks, 0, + ni->mapping_blocks_top_ba - + info_table_erasesize); + if (!success) { + /* There is no spare block. Backup info table becomes the main table. */ + nlog_err(ni, "No room for temporary info table\n"); + ni->main_table_ba = ni->backup_table_ba; + ni->backup_table_ba = 0; + return true; + } + + /* Try to write temporary info table into spare unmapped blocks */ + while (write_ba >= ni->mapping_blocks_ba) { + success = nmbm_write_info_table(ni, write_ba, + ni->mapping_blocks_top_ba, + &tmp_table_start_ba, + &tmp_table_end_ba); + if (success) + break; + + write_ba--; + } + + if (!success) { + /* Backup info table becomes the main table */ + nlog_err(ni, "Failed to update main info table\n"); + ni->main_table_ba = ni->backup_table_ba; + ni->backup_table_ba = 0; + return true; + } + + /* Adjust mapping_blocks_off */ + ni->mapping_blocks_ba = tmp_table_end_ba; + + /* + * Now write main info table at the beginning of management area. + * This operation will generally destroy the original backup info + * table. + */ + success = nmbm_write_info_table(ni, ni->mgmt_start_ba, + tmp_table_start_ba, + &main_table_start_ba, + &main_table_end_ba); + if (!success) { + /* Temporary info table becomes the main table */ + ni->main_table_ba = tmp_table_start_ba; + ni->backup_table_ba = 0; + + nmbm_mark_tables_clean(ni); + + nlog_err(ni, "Failed to update main info table\n"); + + return true; + } + + /* Main info table has been successfully written, record its offset */ + ni->main_table_ba = main_table_start_ba; + + nmbm_mark_tables_clean(ni); + + nlog_table_creation(ni, true, main_table_start_ba, main_table_end_ba); + + /* + * Temporary info table becomes the new backup info table if it's + * not overwritten. + */ + if (main_table_end_ba <= tmp_table_start_ba) { + ni->backup_table_ba = tmp_table_start_ba; + + nlog_table_creation(ni, false, tmp_table_start_ba, + tmp_table_end_ba); + + return true; + } + + /* Adjust mapping_blocks_off */ + ni->mapping_blocks_ba = main_table_end_ba; + + /* Try to reserve spare blocks for new main info table */ + success = nmbm_try_reserve_blocks(ni, main_table_end_ba, &write_ba, + ni->info_table_spare_blocks, 0, + ni->mapping_blocks_top_ba - + info_table_erasesize); + if (!success) { + /* There is no spare block. Only main table exists. */ + nlog_err(ni, "No room for backup info table\n"); + ni->backup_table_ba = 0; + return true; + } + + /* Write new backup info table. */ + while (write_ba >= main_table_end_ba) { + success = nmbm_write_info_table(ni, write_ba, + ni->mapping_blocks_top_ba, + &tmp_table_start_ba, + &tmp_table_end_ba); + if (success) + break; + + write_ba--; + } + + if (!success) { + nlog_err(ni, "No room for backup info table\n"); + ni->backup_table_ba = 0; + return true; + } + + /* Backup info table has been successfully written, record its offset */ + ni->backup_table_ba = tmp_table_start_ba; + + /* Adjust mapping_blocks_off */ + ni->mapping_blocks_ba = tmp_table_end_ba; + + /* Erase spare blocks of main table to clean possible interference data */ + nmbm_erase_range(ni, main_table_end_ba, ni->backup_table_ba); + + nlog_table_creation(ni, false, tmp_table_start_ba, tmp_table_end_ba); + + return true; +} + +/* + * nmbm_update_info_table_once - Update info table once + * @ni: NMBM instance structure + * @force: force update + * + * Update both main and backup info table. Return true if at least one info + * table has been successfully written. + * This function only try to update info table once regard less of the result. + */ +static bool nmbm_update_info_table_once(struct nmbm_instance *ni, bool force) +{ + uint32_t table_start_ba, table_end_ba; + uint32_t main_table_limit; + bool success; + + /* Do nothing if there is no change */ + if (!nmbm_generate_info_table_cache(ni) && !force) + return true; + + /* Check whether both two tables exist */ + if (!ni->backup_table_ba) { + main_table_limit = ni->mapping_blocks_top_ba; + goto write_main_table; + } + + /* + * Write backup info table in its current range. + * Note that limit is set to mapping_blocks_top_off to provide as many + * spare blocks as possible for the backup table. If at last + * unmapped blocks are used by backup table, mapping_blocks_off will + * be adjusted. + */ + success = nmbm_write_info_table(ni, ni->backup_table_ba, + ni->mapping_blocks_top_ba, + &table_start_ba, &table_end_ba); + if (!success) { + /* + * There is nothing to do if failed to write backup table. + * Write the main table now. + */ + nlog_err(ni, "No room for backup table\n"); + ni->mapping_blocks_ba = ni->backup_table_ba; + ni->backup_table_ba = 0; + main_table_limit = ni->mapping_blocks_top_ba; + goto write_main_table; + } + + /* Backup table is successfully written, record its offset */ + ni->backup_table_ba = table_start_ba; + + /* Adjust mapping_blocks_off */ + ni->mapping_blocks_ba = table_end_ba; + + nmbm_mark_tables_clean(ni); + + /* The normal limit of main table */ + main_table_limit = ni->backup_table_ba; + + nlog_table_update(ni, false, table_start_ba, table_end_ba); + +write_main_table: + if (!ni->main_table_ba) + goto rebuild_tables; + + /* Write main info table in its current range */ + success = nmbm_write_info_table(ni, ni->main_table_ba, + main_table_limit, &table_start_ba, + &table_end_ba); + if (!success) { + /* If failed to write main table, go rescue procedure */ + if (!ni->backup_table_ba) + goto rebuild_tables; + + return nmbm_rescue_main_info_table(ni); + } + + /* Main info table is successfully written, record its offset */ + ni->main_table_ba = table_start_ba; + + /* Adjust mapping_blocks_off */ + if (!ni->backup_table_ba) + ni->mapping_blocks_ba = table_end_ba; + + nmbm_mark_tables_clean(ni); + + nlog_table_update(ni, true, table_start_ba, table_end_ba); + + return true; + +rebuild_tables: + return nmbm_rebuild_info_table(ni); +} + +/* + * nmbm_update_info_table - Update info table + * @ni: NMBM instance structure + * + * Update both main and backup info table. Return true if at least one table + * has been successfully written. + * This function will try to update info table repeatedly until no new bad + * block found during updating. + */ +static bool nmbm_update_info_table(struct nmbm_instance *ni) +{ + bool success; + + if (ni->protected) + return true; + + while (ni->block_state_changed || ni->block_mapping_changed) { + success = nmbm_update_info_table_once(ni, false); + if (!success) { + nlog_err(ni, "Failed to update info table\n"); + return false; + } + } + + return true; +} + +/* + * nmbm_map_block - Map a bad block to a unused spare block + * @ni: NMBM instance structure + * @lb: logic block addr to map + */ +static bool nmbm_map_block(struct nmbm_instance *ni, uint32_t lb) +{ + uint32_t pb; + bool success; + + if (ni->mapping_blocks_ba == ni->mapping_blocks_top_ba) { + nlog_warn(ni, "No spare unmapped blocks.\n"); + return false; + } + + success = nmbm_block_walk(ni, false, ni->mapping_blocks_top_ba, &pb, 0, + ni->mapping_blocks_ba); + if (!success) { + nlog_warn(ni, "No spare unmapped blocks.\n"); + nmbm_update_info_table(ni); + ni->mapping_blocks_top_ba = ni->mapping_blocks_ba; + return false; + } + + ni->block_mapping[lb] = pb; + ni->mapping_blocks_top_ba--; + ni->block_mapping_changed++; + + nlog_info(ni, "Logic block %u mapped to physical block %u\n", lb, pb); + + return true; +} + +/* + * nmbm_create_info_table - Create info table(s) + * @ni: NMBM instance structure + * + * This function assumes that the chip has no existing info table(s) + */ +static bool nmbm_create_info_table(struct nmbm_instance *ni) +{ + uint32_t lb; + bool success; + + /* Set initial mapping_blocks_top_off */ + success = nmbm_block_walk(ni, false, ni->signature_ba, + &ni->mapping_blocks_top_ba, 1, + ni->mgmt_start_ba); + if (!success) { + nlog_err(ni, "No room for spare blocks\n"); + return false; + } + + /* Generate info table cache */ + nmbm_generate_info_table_cache(ni); + + /* Write info table */ + success = nmbm_rebuild_info_table(ni); + if (!success) { + nlog_err(ni, "Failed to build info tables\n"); + return false; + } + + /* Remap bad block(s) at end of data area */ + for (lb = ni->data_block_count; lb < ni->mgmt_start_ba; lb++) { + success = nmbm_map_block(ni, lb); + if (!success) + break; + + ni->data_block_count++; + } + + /* If state table and/or mapping table changed, update info table. */ + success = nmbm_update_info_table(ni); + if (!success) + return false; + + return true; +} + +/* + * nmbm_create_new - Create NMBM on a new chip + * @ni: NMBM instance structure + */ +static bool nmbm_create_new(struct nmbm_instance *ni) +{ + bool success; + + /* Determine the boundary of management blocks */ + ni->mgmt_start_ba = ni->block_count * (NMBM_MGMT_DIV - ni->max_ratio) / NMBM_MGMT_DIV; + + if (ni->max_reserved_blocks && ni->block_count - ni->mgmt_start_ba > ni->max_reserved_blocks) + ni->mgmt_start_ba = ni->block_count - ni->max_reserved_blocks; + + nlog_info(ni, "NMBM management region starts at block %u [0x%08llx]\n", + ni->mgmt_start_ba, ba2addr(ni, ni->mgmt_start_ba)); + + /* Fill block state table & mapping table */ + nmbm_scan_badblocks(ni); + nmbm_build_mapping_table(ni); + + /* Write signature */ + ni->signature.header.magic = NMBM_MAGIC_SIGNATURE; + ni->signature.header.version = NMBM_VER; + ni->signature.header.size = sizeof(ni->signature); + ni->signature.nand_size = bmtd.total_blks << bmtd.blk_shift; + ni->signature.block_size = bmtd.blk_size; + ni->signature.page_size = bmtd.pg_size; + ni->signature.spare_size = bmtd.mtd->oobsize; + ni->signature.mgmt_start_pb = ni->mgmt_start_ba; + ni->signature.max_try_count = NMBM_TRY_COUNT; + nmbm_update_checksum(&ni->signature.header); + + success = nmbm_write_signature(ni, ni->mgmt_start_ba, + &ni->signature, &ni->signature_ba); + if (!success) { + nlog_err(ni, "Failed to write signature to a proper offset\n"); + return false; + } + + nlog_info(ni, "Signature has been written to block %u [0x%08llx]\n", + ni->signature_ba, ba2addr(ni, ni->signature_ba)); + + /* Write info table(s) */ + success = nmbm_create_info_table(ni); + if (success) { + nlog_info(ni, "NMBM has been successfully created\n"); + return true; + } + + return false; +} + +/* + * nmbm_check_info_table_header - Check if a info table header is valid + * @ni: NMBM instance structure + * @data: pointer to the info table header + */ +static bool nmbm_check_info_table_header(struct nmbm_instance *ni, void *data) +{ + struct nmbm_info_table_header *ifthdr = data; + + if (ifthdr->header.magic != NMBM_MAGIC_INFO_TABLE) + return false; + + if (ifthdr->header.size != ni->info_table_size) + return false; + + if (ifthdr->mapping_table_off - ifthdr->state_table_off < ni->state_table_size) + return false; + + if (ni->info_table_size - ifthdr->mapping_table_off < ni->mapping_table_size) + return false; + + return true; +} + +/* + * nmbm_check_info_table - Check if a whole info table is valid + * @ni: NMBM instance structure + * @start_ba: start block address of this table + * @end_ba: end block address of this table + * @data: pointer to the info table header + * @mapping_blocks_top_ba: return the block address of top remapped block + */ +static bool nmbm_check_info_table(struct nmbm_instance *ni, uint32_t start_ba, + uint32_t end_ba, void *data, + uint32_t *mapping_blocks_top_ba) +{ + struct nmbm_info_table_header *ifthdr = data; + int32_t *block_mapping = (int32_t *)((uintptr_t)data + ifthdr->mapping_table_off); + u32 *block_state = (u32 *)((uintptr_t)data + ifthdr->state_table_off); + uint32_t minimum_mapping_pb = ni->signature_ba; + uint32_t ba; + + for (ba = 0; ba < ni->data_block_count; ba++) { + if ((block_mapping[ba] >= ni->data_block_count && block_mapping[ba] < end_ba) || + block_mapping[ba] == ni->signature_ba) + return false; + + if (block_mapping[ba] >= end_ba && block_mapping[ba] < minimum_mapping_pb) + minimum_mapping_pb = block_mapping[ba]; + } + + for (ba = start_ba; ba < end_ba; ba++) { + if (nmbm_get_block_state(ni, ba) != BLOCK_ST_GOOD) + continue; + + if (nmbm_get_block_state_raw(block_state, ba) != BLOCK_ST_GOOD) + return false; + } + + *mapping_blocks_top_ba = minimum_mapping_pb - 1; + + return true; +} + +/* + * nmbm_try_load_info_table - Try to load info table from a address + * @ni: NMBM instance structure + * @ba: start block address of the info table + * @eba: return the block address after end of the table + * @write_count: return the write count of this table + * @mapping_blocks_top_ba: return the block address of top remapped block + * @table_loaded: used to record whether ni->info_table has valid data + */ +static bool nmbm_try_load_info_table(struct nmbm_instance *ni, uint32_t ba, + uint32_t *eba, uint32_t *write_count, + uint32_t *mapping_blocks_top_ba, + bool table_loaded) +{ + struct nmbm_info_table_header *ifthdr = (void *)ni->info_table_cache; + uint8_t *off = ni->info_table_cache; + uint32_t limit = ba + size2blk(ni, ni->info_table_size); + uint32_t start_ba = 0, chunksize, sizeremain = ni->info_table_size; + bool success, checkhdr = true; + int ret; + + while (sizeremain && ba < limit) { + if (nmbm_get_block_state(ni, ba) != BLOCK_ST_GOOD) + goto next_block; + + if (nmbm_check_bad_phys_block(ni, ba)) { + nmbm_set_block_state(ni, ba, BLOCK_ST_BAD); + goto next_block; + } + + chunksize = sizeremain; + if (chunksize > bmtd.blk_size) + chunksize = bmtd.blk_size; + + /* Assume block with ECC error has no info table data */ + ret = nmbn_read_data(ni, ba2addr(ni, ba), off, chunksize); + if (ret < 0) + goto skip_bad_block; + else if (ret > 0) + return false; + + if (checkhdr) { + success = nmbm_check_info_table_header(ni, off); + if (!success) + return false; + + start_ba = ba; + checkhdr = false; + } + + off += chunksize; + sizeremain -= chunksize; + + goto next_block; + + skip_bad_block: + /* Only mark bad in memory */ + nmbm_set_block_state(ni, ba, BLOCK_ST_BAD); + + next_block: + ba++; + } + + if (sizeremain) + return false; + + success = nmbm_check_header(ni->info_table_cache, ni->info_table_size); + if (!success) + return false; + + *eba = ba; + *write_count = ifthdr->write_count; + + success = nmbm_check_info_table(ni, start_ba, ba, ni->info_table_cache, + mapping_blocks_top_ba); + if (!success) + return false; + + if (!table_loaded || ifthdr->write_count > ni->info_table.write_count) { + memcpy(&ni->info_table, ifthdr, sizeof(ni->info_table)); + memcpy(ni->block_state, + (uint8_t *)ifthdr + ifthdr->state_table_off, + ni->state_table_size); + memcpy(ni->block_mapping, + (uint8_t *)ifthdr + ifthdr->mapping_table_off, + ni->mapping_table_size); + ni->info_table.write_count = ifthdr->write_count; + } + + return true; +} + +/* + * nmbm_search_info_table - Search info table from specific address + * @ni: NMBM instance structure + * @ba: start block address to search + * @limit: highest block address allowed for searching + * @table_start_ba: return the start block address of this table + * @table_end_ba: return the block address after end of this table + * @write_count: return the write count of this table + * @mapping_blocks_top_ba: return the block address of top remapped block + * @table_loaded: used to record whether ni->info_table has valid data + */ +static bool nmbm_search_info_table(struct nmbm_instance *ni, uint32_t ba, + uint32_t limit, uint32_t *table_start_ba, + uint32_t *table_end_ba, + uint32_t *write_count, + uint32_t *mapping_blocks_top_ba, + bool table_loaded) +{ + bool success; + + while (ba < limit - size2blk(ni, ni->info_table_size)) { + success = nmbm_try_load_info_table(ni, ba, table_end_ba, + write_count, + mapping_blocks_top_ba, + table_loaded); + if (success) { + *table_start_ba = ba; + return true; + } + + ba++; + } + + return false; +} + +/* + * nmbm_load_info_table - Load info table(s) from a chip + * @ni: NMBM instance structure + * @ba: start block address to search info table + * @limit: highest block address allowed for searching + */ +static bool nmbm_load_info_table(struct nmbm_instance *ni, uint32_t ba, + uint32_t limit) +{ + uint32_t main_table_end_ba, backup_table_end_ba, table_end_ba; + uint32_t main_mapping_blocks_top_ba, backup_mapping_blocks_top_ba; + uint32_t main_table_write_count, backup_table_write_count; + uint32_t i; + bool success; + + /* Set initial value */ + ni->main_table_ba = 0; + ni->backup_table_ba = 0; + ni->info_table.write_count = 0; + ni->mapping_blocks_top_ba = ni->signature_ba - 1; + ni->data_block_count = ni->signature.mgmt_start_pb; + + /* Find first info table */ + success = nmbm_search_info_table(ni, ba, limit, &ni->main_table_ba, + &main_table_end_ba, &main_table_write_count, + &main_mapping_blocks_top_ba, false); + if (!success) { + nlog_warn(ni, "No valid info table found\n"); + return false; + } + + table_end_ba = main_table_end_ba; + + nlog_table_found(ni, true, main_table_write_count, ni->main_table_ba, + main_table_end_ba); + + /* Find second info table */ + success = nmbm_search_info_table(ni, main_table_end_ba, limit, + &ni->backup_table_ba, &backup_table_end_ba, + &backup_table_write_count, &backup_mapping_blocks_top_ba, true); + if (!success) { + nlog_warn(ni, "Second info table not found\n"); + } else { + table_end_ba = backup_table_end_ba; + + nlog_table_found(ni, false, backup_table_write_count, + ni->backup_table_ba, backup_table_end_ba); + } + + /* Pick mapping_blocks_top_ba */ + if (!ni->backup_table_ba) { + ni->mapping_blocks_top_ba= main_mapping_blocks_top_ba; + } else { + if (main_table_write_count >= backup_table_write_count) + ni->mapping_blocks_top_ba = main_mapping_blocks_top_ba; + else + ni->mapping_blocks_top_ba = backup_mapping_blocks_top_ba; + } + + /* Set final mapping_blocks_ba */ + ni->mapping_blocks_ba = table_end_ba; + + /* Set final data_block_count */ + for (i = ni->signature.mgmt_start_pb; i > 0; i--) { + if (ni->block_mapping[i - 1] >= 0) { + ni->data_block_count = i; + break; + } + } + + /* Regenerate the info table cache from the final selected info table */ + nmbm_generate_info_table_cache(ni); + + /* + * If only one table exists, try to write another table. + * If two tables have different write count, try to update info table + */ + if (!ni->backup_table_ba) { + success = nmbm_rescue_single_info_table(ni); + } else if (main_table_write_count != backup_table_write_count) { + /* Mark state & mapping tables changed */ + ni->block_state_changed = 1; + ni->block_mapping_changed = 1; + + success = nmbm_update_single_info_table(ni, + main_table_write_count < backup_table_write_count); + } else { + success = true; + } + + /* + * If there is no spare unmapped blocks, or still only one table + * exists, set the chip to read-only + */ + if (ni->mapping_blocks_ba == ni->mapping_blocks_top_ba) { + nlog_warn(ni, "No spare unmapped blocks. Device is now read-only\n"); + ni->protected = 1; + } else if (!success) { + nlog_warn(ni, "Only one info table found. Device is now read-only\n"); + ni->protected = 1; + } + + return true; +} + +/* + * nmbm_load_existing - Load NMBM from a new chip + * @ni: NMBM instance structure + */ +static bool nmbm_load_existing(struct nmbm_instance *ni) +{ + bool success; + + /* Calculate the boundary of management blocks */ + ni->mgmt_start_ba = ni->signature.mgmt_start_pb; + + nlog_debug(ni, "NMBM management region starts at block %u [0x%08llx]\n", + ni->mgmt_start_ba, ba2addr(ni, ni->mgmt_start_ba)); + + /* Look for info table(s) */ + success = nmbm_load_info_table(ni, ni->mgmt_start_ba, + ni->signature_ba); + if (success) { + nlog_info(ni, "NMBM has been successfully attached\n"); + return true; + } + + if (!ni->force_create) { + printk("not creating NMBM table\n"); + return false; + } + + /* Fill block state table & mapping table */ + nmbm_scan_badblocks(ni); + nmbm_build_mapping_table(ni); + + /* Write info table(s) */ + success = nmbm_create_info_table(ni); + if (success) { + nlog_info(ni, "NMBM has been successfully created\n"); + return true; + } + + return false; +} + +/* + * nmbm_find_signature - Find signature in the lower NAND chip + * @ni: NMBM instance structure + * @signature_ba: used for storing block address of the signature + * @signature_ba: return the actual block address of signature block + * + * Find a valid signature from a specific range in the lower NAND chip, + * from bottom (highest address) to top (lowest address) + * + * Return true if found. + */ +static bool nmbm_find_signature(struct nmbm_instance *ni, + struct nmbm_signature *signature, + uint32_t *signature_ba) +{ + struct nmbm_signature sig; + uint64_t off, addr; + uint32_t block_count, ba, limit; + bool success; + int ret; + + /* Calculate top and bottom block address */ + block_count = bmtd.total_blks; + ba = block_count; + limit = (block_count / NMBM_MGMT_DIV) * (NMBM_MGMT_DIV - ni->max_ratio); + if (ni->max_reserved_blocks && block_count - limit > ni->max_reserved_blocks) + limit = block_count - ni->max_reserved_blocks; + + while (ba >= limit) { + ba--; + addr = ba2addr(ni, ba); + + if (nmbm_check_bad_phys_block(ni, ba)) + continue; + + /* Check every page. + * As long as at leaset one page contains valid signature, + * the block is treated as a valid signature block. + */ + for (off = 0; off < bmtd.blk_size; + off += bmtd.pg_size) { + ret = nmbn_read_data(ni, addr + off, &sig, + sizeof(sig)); + if (ret) + continue; + + /* Check for header size and checksum */ + success = nmbm_check_header(&sig, sizeof(sig)); + if (!success) + continue; + + /* Check for header magic */ + if (sig.header.magic == NMBM_MAGIC_SIGNATURE) { + /* Found it */ + memcpy(signature, &sig, sizeof(sig)); + *signature_ba = ba; + return true; + } + } + }; + + return false; +} + +/* + * nmbm_calc_structure_size - Calculate the instance structure size + * @nld: NMBM lower device structure + */ +static size_t nmbm_calc_structure_size(void) +{ + uint32_t state_table_size, mapping_table_size, info_table_size; + uint32_t block_count; + + block_count = bmtd.total_blks; + + /* Calculate info table size */ + state_table_size = ((block_count + NMBM_BITMAP_BLOCKS_PER_UNIT - 1) / + NMBM_BITMAP_BLOCKS_PER_UNIT) * NMBM_BITMAP_UNIT_SIZE; + mapping_table_size = block_count * sizeof(int32_t); + + info_table_size = ALIGN(sizeof(struct nmbm_info_table_header), + bmtd.pg_size); + info_table_size += ALIGN(state_table_size, bmtd.pg_size); + info_table_size += ALIGN(mapping_table_size, bmtd.pg_size); + + return info_table_size + state_table_size + mapping_table_size + + sizeof(struct nmbm_instance); +} + +/* + * nmbm_init_structure - Initialize members of instance structure + * @ni: NMBM instance structure + */ +static void nmbm_init_structure(struct nmbm_instance *ni) +{ + uint32_t pages_per_block, blocks_per_chip; + uintptr_t ptr; + + pages_per_block = bmtd.blk_size / bmtd.pg_size; + blocks_per_chip = bmtd.total_blks; + + ni->rawpage_size = bmtd.pg_size + bmtd.mtd->oobsize; + ni->rawblock_size = pages_per_block * ni->rawpage_size; + ni->rawchip_size = blocks_per_chip * ni->rawblock_size; + + /* Calculate number of block this chip */ + ni->block_count = blocks_per_chip; + + /* Calculate info table size */ + ni->state_table_size = ((ni->block_count + NMBM_BITMAP_BLOCKS_PER_UNIT - 1) / + NMBM_BITMAP_BLOCKS_PER_UNIT) * NMBM_BITMAP_UNIT_SIZE; + ni->mapping_table_size = ni->block_count * sizeof(*ni->block_mapping); + + ni->info_table_size = ALIGN(sizeof(ni->info_table), + bmtd.pg_size); + ni->info_table.state_table_off = ni->info_table_size; + + ni->info_table_size += ALIGN(ni->state_table_size, + bmtd.pg_size); + ni->info_table.mapping_table_off = ni->info_table_size; + + ni->info_table_size += ALIGN(ni->mapping_table_size, + bmtd.pg_size); + + ni->info_table_spare_blocks = nmbm_get_spare_block_count( + size2blk(ni, ni->info_table_size)); + + /* Assign memory to members */ + ptr = (uintptr_t)ni + sizeof(*ni); + + ni->info_table_cache = (void *)ptr; + ptr += ni->info_table_size; + + ni->block_state = (void *)ptr; + ptr += ni->state_table_size; + + ni->block_mapping = (void *)ptr; + ptr += ni->mapping_table_size; + + ni->page_cache = bmtd.data_buf; + + /* Initialize block state table */ + ni->block_state_changed = 0; + memset(ni->block_state, 0xff, ni->state_table_size); + + /* Initialize block mapping table */ + ni->block_mapping_changed = 0; +} + +/* + * nmbm_attach - Attach to a lower device + * @ni: NMBM instance structure + */ +static int nmbm_attach(struct nmbm_instance *ni) +{ + bool success; + + if (!ni) + return -EINVAL; + + /* Initialize NMBM instance */ + nmbm_init_structure(ni); + + success = nmbm_find_signature(ni, &ni->signature, &ni->signature_ba); + if (!success) { + if (!ni->force_create) { + nlog_err(ni, "Signature not found\n"); + return -ENODEV; + } + + success = nmbm_create_new(ni); + if (!success) + return -ENODEV; + + return 0; + } + + nlog_info(ni, "Signature found at block %u [0x%08llx]\n", + ni->signature_ba, ba2addr(ni, ni->signature_ba)); + + if (ni->signature.header.version != NMBM_VER) { + nlog_err(ni, "NMBM version %u.%u is not supported\n", + NMBM_VERSION_MAJOR_GET(ni->signature.header.version), + NMBM_VERSION_MINOR_GET(ni->signature.header.version)); + return -EINVAL; + } + + if (ni->signature.nand_size != bmtd.total_blks << bmtd.blk_shift || + ni->signature.block_size != bmtd.blk_size || + ni->signature.page_size != bmtd.pg_size || + ni->signature.spare_size != bmtd.mtd->oobsize) { + nlog_err(ni, "NMBM configuration mismatch\n"); + return -EINVAL; + } + + success = nmbm_load_existing(ni); + if (!success) + return -ENODEV; + + return 0; +} + +static bool remap_block_nmbm(u16 block, u16 mapped_block, int copy_len) +{ + struct nmbm_instance *ni = bmtd.ni; + int new_block; + + if (block >= ni->data_block_count) + return false; + + nmbm_set_block_state(ni, mapped_block, BLOCK_ST_BAD); + if (!nmbm_map_block(ni, block)) + return false; + + new_block = ni->block_mapping[block]; + bbt_nand_erase(new_block); + if (copy_len > 0) + bbt_nand_copy(new_block, mapped_block, copy_len); + nmbm_update_info_table(ni); + + return true; +} + +static int get_mapping_block_index_nmbm(int block) +{ + struct nmbm_instance *ni = bmtd.ni; + + if (block >= ni->data_block_count) + return -1; + + return ni->block_mapping[block]; +} + +static int mtk_bmt_init_nmbm(struct device_node *np) +{ + struct nmbm_instance *ni; + int ret; + + ni = kzalloc(nmbm_calc_structure_size(), GFP_KERNEL); + if (!ni) + return -ENOMEM; + + bmtd.ni = ni; + + if (of_property_read_u32(np, "mediatek,bmt-max-ratio", &ni->max_ratio)) + ni->max_ratio = 1; + if (of_property_read_u32(np, "mediatek,bmt-max-reserved-blocks", + &ni->max_reserved_blocks)) + ni->max_reserved_blocks = 256; + if (of_property_read_bool(np, "mediatek,empty-page-ecc-protected")) + ni->empty_page_ecc_ok = true; + if (of_property_read_bool(np, "mediatek,bmt-force-create")) + ni->force_create = true; + + ret = nmbm_attach(ni); + if (ret) + goto out; + + bmtd.mtd->size = ni->data_block_count << bmtd.blk_shift; + + return 0; + +out: + kfree(ni); + bmtd.ni = NULL; + + return ret; +} + +static int mtk_bmt_debug_nmbm(void *data, u64 val) +{ + struct nmbm_instance *ni = bmtd.ni; + int i; + + switch (val) { + case 0: + for (i = 1; i < ni->data_block_count; i++) { + if (ni->block_mapping[i] < ni->mapping_blocks_ba) + continue; + + printk("remap [%x->%x]\n", i, ni->block_mapping[i]); + } + } + + return 0; +} + +static void unmap_block_nmbm(u16 block) +{ + struct nmbm_instance *ni = bmtd.ni; + int start, offset; + int new_block; + + if (block >= ni->data_block_count) + return; + + start = block; + offset = 0; + while (ni->block_mapping[start] >= ni->mapping_blocks_ba) { + start--; + offset++; + if (start < 0) + return; + } + + if (!offset) + return; + + new_block = ni->block_mapping[start] + offset; + nmbm_set_block_state(ni, new_block, BLOCK_ST_GOOD); + ni->block_mapping[block] = new_block; + ni->block_mapping_changed++; + + new_block = ni->signature_ba - 1; + for (block = 0; block < ni->data_block_count; block++) { + int cur = ni->block_mapping[block]; + + if (cur < ni->mapping_blocks_ba) + continue; + + if (cur <= new_block) + new_block = cur - 1; + } + + ni->mapping_blocks_top_ba = new_block; + + nmbm_update_info_table(ni); +} + +const struct mtk_bmt_ops mtk_bmt_nmbm_ops = { + .init = mtk_bmt_init_nmbm, + .remap_block = remap_block_nmbm, + .unmap_block = unmap_block_nmbm, + .get_mapping_block = get_mapping_block_index_nmbm, + .debug = mtk_bmt_debug_nmbm, +}; diff --git a/target/linux/generic/files/drivers/mtd/nand/mtk_bmt_v2.c b/target/linux/generic/files/drivers/mtd/nand/mtk_bmt_v2.c new file mode 100644 index 000000000..2770376e9 --- /dev/null +++ b/target/linux/generic/files/drivers/mtd/nand/mtk_bmt_v2.c @@ -0,0 +1,513 @@ +/* + * Copyright (c) 2017 MediaTek Inc. + * Author: Xiangsheng Hou + * Copyright (c) 2020-2022 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include "mtk_bmt.h" + +struct bbbt { + char signature[3]; + /* This version is used to distinguish the legacy and new algorithm */ +#define BBMT_VERSION 2 + unsigned char version; + /* Below 2 tables will be written in SLC */ + u16 bb_tbl[]; +}; + +struct bbmt { + u16 block; +#define NO_MAPPED 0 +#define NORMAL_MAPPED 1 +#define BMT_MAPPED 2 + u16 mapped; +}; + +/* Maximum 8k blocks */ +#define BBPOOL_RATIO 2 +#define BB_TABLE_MAX bmtd.table_size +#define BMT_TABLE_MAX (BB_TABLE_MAX * BBPOOL_RATIO / 100) +#define BMT_TBL_DEF_VAL 0x0 + +static inline struct bbmt *bmt_tbl(struct bbbt *bbbt) +{ + return (struct bbmt *)&bbbt->bb_tbl[bmtd.table_size]; +} + +static u16 find_valid_block(u16 block) +{ + u8 fdm[4]; + int ret; + int loop = 0; + +retry: + if (block >= bmtd.total_blks) + return 0; + + ret = bbt_nand_read(blk_pg(block), bmtd.data_buf, bmtd.pg_size, + fdm, sizeof(fdm)); + /* Read the 1st byte of FDM to judge whether it's a bad + * or not + */ + if (ret || fdm[0] != 0xff) { + pr_info("nand: found bad block 0x%x\n", block); + if (loop >= bmtd.bb_max) { + pr_info("nand: FATAL ERR: too many bad blocks!!\n"); + return 0; + } + + loop++; + block++; + goto retry; + } + + return block; +} + +/* Find out all bad blocks, and fill in the mapping table */ +static int scan_bad_blocks(struct bbbt *bbt) +{ + int i; + u16 block = 0; + + /* First time download, the block0 MUST NOT be a bad block, + * this is guaranteed by vendor + */ + bbt->bb_tbl[0] = 0; + + /* + * Construct the mapping table of Normal data area(non-PMT/BMTPOOL) + * G - Good block; B - Bad block + * --------------------------- + * physical |G|G|B|G|B|B|G|G|G|G|B|G|B| + * --------------------------- + * What bb_tbl[i] looks like: + * physical block(i): + * 0 1 2 3 4 5 6 7 8 9 a b c + * mapped block(bb_tbl[i]): + * 0 1 3 6 7 8 9 b ...... + * ATTENTION: + * If new bad block ocurred(n), search bmt_tbl to find + * a available block(x), and fill in the bb_tbl[n] = x; + */ + for (i = 1; i < bmtd.pool_lba; i++) { + bbt->bb_tbl[i] = find_valid_block(bbt->bb_tbl[i - 1] + 1); + BBT_LOG("bb_tbl[0x%x] = 0x%x", i, bbt->bb_tbl[i]); + if (bbt->bb_tbl[i] == 0) + return -1; + } + + /* Physical Block start Address of BMT pool */ + bmtd.pool_pba = bbt->bb_tbl[i - 1] + 1; + if (bmtd.pool_pba >= bmtd.total_blks - 2) { + pr_info("nand: FATAL ERR: Too many bad blocks!!\n"); + return -1; + } + + BBT_LOG("pool_pba=0x%x", bmtd.pool_pba); + i = 0; + block = bmtd.pool_pba; + /* + * The bmt table is used for runtime bad block mapping + * G - Good block; B - Bad block + * --------------------------- + * physical |G|G|B|G|B|B|G|G|G|G|B|G|B| + * --------------------------- + * block: 0 1 2 3 4 5 6 7 8 9 a b c + * What bmt_tbl[i] looks like in initial state: + * i: + * 0 1 2 3 4 5 6 7 + * bmt_tbl[i].block: + * 0 1 3 6 7 8 9 b + * bmt_tbl[i].mapped: + * N N N N N N N B + * N - Not mapped(Available) + * M - Mapped + * B - BMT + * ATTENTION: + * BMT always in the last valid block in pool + */ + while ((block = find_valid_block(block)) != 0) { + bmt_tbl(bbt)[i].block = block; + bmt_tbl(bbt)[i].mapped = NO_MAPPED; + BBT_LOG("bmt_tbl[%d].block = 0x%x", i, block); + block++; + i++; + } + + /* i - How many available blocks in pool, which is the length of bmt_tbl[] + * bmtd.bmt_blk_idx - bmt_tbl[bmtd.bmt_blk_idx].block => the BMT block + */ + bmtd.bmt_blk_idx = i - 1; + bmt_tbl(bbt)[bmtd.bmt_blk_idx].mapped = BMT_MAPPED; + + if (i < 1) { + pr_info("nand: FATAL ERR: no space to store BMT!!\n"); + return -1; + } + + pr_info("[BBT] %d available blocks in BMT pool\n", i); + + return 0; +} + +static bool is_valid_bmt(unsigned char *buf, unsigned char *fdm) +{ + struct bbbt *bbt = (struct bbbt *)buf; + u8 *sig = (u8*)bbt->signature + MAIN_SIGNATURE_OFFSET; + + + if (memcmp(bbt->signature + MAIN_SIGNATURE_OFFSET, "BMT", 3) == 0 && + memcmp(fdm + OOB_SIGNATURE_OFFSET, "bmt", 3) == 0) { + if (bbt->version == BBMT_VERSION) + return true; + } + BBT_LOG("[BBT] BMT Version not match,upgrage preloader and uboot please! sig=%02x%02x%02x, fdm=%02x%02x%02x", + sig[0], sig[1], sig[2], + fdm[1], fdm[2], fdm[3]); + return false; +} + +static u16 get_bmt_index(struct bbmt *bmt) +{ + int i = 0; + + while (bmt[i].block != BMT_TBL_DEF_VAL) { + if (bmt[i].mapped == BMT_MAPPED) + return i; + i++; + } + return 0; +} + +static int +read_bmt(u16 block, unsigned char *dat, unsigned char *fdm, int fdm_len) +{ + u32 len = bmtd.bmt_pgs << bmtd.pg_shift; + + return bbt_nand_read(blk_pg(block), dat, len, fdm, fdm_len); +} + +static struct bbbt *scan_bmt(u16 block) +{ + u8 fdm[4]; + + if (block < bmtd.pool_lba) + return NULL; + + if (read_bmt(block, bmtd.bbt_buf, fdm, sizeof(fdm))) + return scan_bmt(block - 1); + + if (is_valid_bmt(bmtd.bbt_buf, fdm)) { + bmtd.bmt_blk_idx = get_bmt_index(bmt_tbl((struct bbbt *)bmtd.bbt_buf)); + if (bmtd.bmt_blk_idx == 0) { + pr_info("[BBT] FATAL ERR: bmt block index is wrong!\n"); + return NULL; + } + pr_info("[BBT] BMT.v2 is found at 0x%x\n", block); + return (struct bbbt *)bmtd.bbt_buf; + } else + return scan_bmt(block - 1); +} + +/* Write the Burner Bad Block Table to Nand Flash + * n - write BMT to bmt_tbl[n] + */ +static u16 upload_bmt(struct bbbt *bbt, int n) +{ + u16 block; + +retry: + if (n < 0 || bmt_tbl(bbt)[n].mapped == NORMAL_MAPPED) { + pr_info("nand: FATAL ERR: no space to store BMT!\n"); + return (u16)-1; + } + + block = bmt_tbl(bbt)[n].block; + BBT_LOG("n = 0x%x, block = 0x%x", n, block); + if (bbt_nand_erase(block)) { + bmt_tbl(bbt)[n].block = 0; + /* erase failed, try the previous block: bmt_tbl[n - 1].block */ + n--; + goto retry; + } + + /* The signature offset is fixed set to 0, + * oob signature offset is fixed set to 1 + */ + memcpy(bbt->signature + MAIN_SIGNATURE_OFFSET, "BMT", 3); + bbt->version = BBMT_VERSION; + + if (write_bmt(block, (unsigned char *)bbt)) { + bmt_tbl(bbt)[n].block = 0; + + /* write failed, try the previous block in bmt_tbl[n - 1] */ + n--; + goto retry; + } + + /* Return the current index(n) of BMT pool (bmt_tbl[n]) */ + return n; +} + +static u16 find_valid_block_in_pool(struct bbbt *bbt) +{ + int i; + + if (bmtd.bmt_blk_idx == 0) + goto error; + + for (i = 0; i < bmtd.bmt_blk_idx; i++) { + if (bmt_tbl(bbt)[i].block != 0 && bmt_tbl(bbt)[i].mapped == NO_MAPPED) { + bmt_tbl(bbt)[i].mapped = NORMAL_MAPPED; + return bmt_tbl(bbt)[i].block; + } + } + +error: + pr_info("nand: FATAL ERR: BMT pool is run out!\n"); + return 0; +} + +/* We met a bad block, mark it as bad and map it to a valid block in pool, + * if it's a write failure, we need to write the data to mapped block + */ +static bool remap_block_v2(u16 block, u16 mapped_block, int copy_len) +{ + u16 new_block; + struct bbbt *bbt; + + bbt = bmtd.bbt; + new_block = find_valid_block_in_pool(bbt); + if (new_block == 0) + return false; + + /* Map new bad block to available block in pool */ + bbt->bb_tbl[block] = new_block; + + /* Erase new block */ + bbt_nand_erase(new_block); + if (copy_len > 0) + bbt_nand_copy(new_block, mapped_block, copy_len); + + bmtd.bmt_blk_idx = upload_bmt(bbt, bmtd.bmt_blk_idx); + + return true; +} + +static int get_mapping_block_index_v2(int block) +{ + int start, end; + + if (block >= bmtd.pool_lba) + return block; + + if (!mapping_block_in_range(block, &start, &end)) + return block; + + return bmtd.bbt->bb_tbl[block]; +} + +static void +unmap_block_v2(u16 block) +{ + bmtd.bbt->bb_tbl[block] = block; + bmtd.bmt_blk_idx = upload_bmt(bmtd.bbt, bmtd.bmt_blk_idx); +} + +static unsigned long * +mtk_bmt_get_mapping_mask(void) +{ + struct bbmt *bbmt = bmt_tbl(bmtd.bbt); + int main_blocks = bmtd.mtd->size >> bmtd.blk_shift; + unsigned long *used; + int i, k; + + used = kcalloc(sizeof(unsigned long), BIT_WORD(bmtd.bmt_blk_idx) + 1, GFP_KERNEL); + if (!used) + return NULL; + + for (i = 1; i < main_blocks; i++) { + if (bmtd.bbt->bb_tbl[i] == i) + continue; + + for (k = 0; k < bmtd.bmt_blk_idx; k++) { + if (bmtd.bbt->bb_tbl[i] != bbmt[k].block) + continue; + + set_bit(k, used); + break; + } + } + + return used; +} + +static int mtk_bmt_debug_v2(void *data, u64 val) +{ + struct bbmt *bbmt = bmt_tbl(bmtd.bbt); + struct mtd_info *mtd = bmtd.mtd; + unsigned long *used; + int main_blocks = mtd->size >> bmtd.blk_shift; + int n_remap = 0; + int i; + + used = mtk_bmt_get_mapping_mask(); + if (!used) + return -ENOMEM; + + switch (val) { + case 0: + for (i = 1; i < main_blocks; i++) { + if (bmtd.bbt->bb_tbl[i] == i) + continue; + + printk("remap [%x->%x]\n", i, bmtd.bbt->bb_tbl[i]); + n_remap++; + } + for (i = 0; i <= bmtd.bmt_blk_idx; i++) { + char c; + + switch (bbmt[i].mapped) { + case NO_MAPPED: + continue; + case NORMAL_MAPPED: + c = 'm'; + if (test_bit(i, used)) + c = 'M'; + break; + case BMT_MAPPED: + c = 'B'; + break; + default: + c = 'X'; + break; + } + printk("[%x:%c] = 0x%x\n", i, c, bbmt[i].block); + } + break; + case 100: + for (i = 0; i <= bmtd.bmt_blk_idx; i++) { + if (bbmt[i].mapped != NORMAL_MAPPED) + continue; + + if (test_bit(i, used)) + continue; + + n_remap++; + bbmt[i].mapped = NO_MAPPED; + printk("free block [%d:%x]\n", i, bbmt[i].block); + } + if (n_remap) + bmtd.bmt_blk_idx = upload_bmt(bmtd.bbt, bmtd.bmt_blk_idx); + break; + } + + kfree(used); + + return 0; +} + +static int mtk_bmt_init_v2(struct device_node *np) +{ + u32 bmt_pool_size, bmt_table_size; + u32 bufsz, block; + u16 pmt_block; + + if (of_property_read_u32(np, "mediatek,bmt-pool-size", + &bmt_pool_size) != 0) + bmt_pool_size = 80; + + if (of_property_read_u8(np, "mediatek,bmt-oob-offset", + &bmtd.oob_offset) != 0) + bmtd.oob_offset = 0; + + if (of_property_read_u32(np, "mediatek,bmt-table-size", + &bmt_table_size) != 0) + bmt_table_size = 0x2000U; + + bmtd.table_size = bmt_table_size; + + pmt_block = bmtd.total_blks - bmt_pool_size - 2; + + bmtd.mtd->size = pmt_block << bmtd.blk_shift; + + /* + * --------------------------------------- + * | PMT(2blks) | BMT POOL(totalblks * 2%) | + * --------------------------------------- + * ^ ^ + * | | + * pmt_block pmt_block + 2blocks(pool_lba) + * + * ATTETION!!!!!! + * The blocks ahead of the boundary block are stored in bb_tbl + * and blocks behind are stored in bmt_tbl + */ + + bmtd.pool_lba = (u16)(pmt_block + 2); + bmtd.bb_max = bmtd.total_blks * BBPOOL_RATIO / 100; + + bufsz = round_up(sizeof(struct bbbt) + + bmt_table_size * sizeof(struct bbmt), bmtd.pg_size); + bmtd.bmt_pgs = bufsz >> bmtd.pg_shift; + + bmtd.bbt_buf = kzalloc(bufsz, GFP_KERNEL); + if (!bmtd.bbt_buf) + return -ENOMEM; + + memset(bmtd.bbt_buf, 0xff, bufsz); + + /* Scanning start from the first page of the last block + * of whole flash + */ + bmtd.bbt = scan_bmt(bmtd.total_blks - 1); + if (!bmtd.bbt) { + /* BMT not found */ + if (bmtd.total_blks > BB_TABLE_MAX + BMT_TABLE_MAX) { + pr_info("nand: FATAL: Too many blocks, can not support!\n"); + return -1; + } + + bmtd.bbt = (struct bbbt *)bmtd.bbt_buf; + memset(bmt_tbl(bmtd.bbt), BMT_TBL_DEF_VAL, + bmtd.table_size * sizeof(struct bbmt)); + + if (scan_bad_blocks(bmtd.bbt)) + return -1; + + /* BMT always in the last valid block in pool */ + bmtd.bmt_blk_idx = upload_bmt(bmtd.bbt, bmtd.bmt_blk_idx); + block = bmt_tbl(bmtd.bbt)[bmtd.bmt_blk_idx].block; + pr_notice("[BBT] BMT.v2 is written into PBA:0x%x\n", block); + + if (bmtd.bmt_blk_idx == 0) + pr_info("nand: Warning: no available block in BMT pool!\n"); + else if (bmtd.bmt_blk_idx == (u16)-1) + return -1; + } + + return 0; +} + + +const struct mtk_bmt_ops mtk_bmt_v2_ops = { + .sig = "bmt", + .sig_len = 3, + .init = mtk_bmt_init_v2, + .remap_block = remap_block_v2, + .unmap_block = unmap_block_v2, + .get_mapping_block = get_mapping_block_index_v2, + .debug = mtk_bmt_debug_v2, +}; diff --git a/target/linux/generic/files/include/linux/mtd/mtk_bmt.h b/target/linux/generic/files/include/linux/mtd/mtk_bmt.h new file mode 100644 index 000000000..cbb6d04d8 --- /dev/null +++ b/target/linux/generic/files/include/linux/mtd/mtk_bmt.h @@ -0,0 +1,18 @@ +#ifndef __MTK_BMT_H +#define __MTK_BMT_H + +#ifdef CONFIG_MTD_NAND_MTK_BMT +int mtk_bmt_attach(struct mtd_info *mtd); +void mtk_bmt_detach(struct mtd_info *mtd); +#else +static inline int mtk_bmt_attach(struct mtd_info *mtd) +{ + return 0; +} + +static inline void mtk_bmt_detach(struct mtd_info *mtd) +{ +} +#endif + +#endif