From b3e60d1892cbbd9b69f6f87b40d2a2ddbd00e990 Mon Sep 17 00:00:00 2001 From: coolsnowwolf Date: Tue, 2 Jul 2024 15:24:05 +0800 Subject: [PATCH] generic: sync kernel 6.1 from upstream --- ....h-removed-REPEAT_BYTE-from-kernel.h.patch | 161 + ...per_-_bits-and-lower_-_bits-to-wordp.patch | 107 + ...nand-Support-write-protection-settin.patch | 36 + ...T_DEV-for-partitions-marked-as-rootf.patch | 2 +- ...mtd-spi-nor-add-generic-flash-driver.patch | 117 + ...licon-Labs-EM3581-device-compatible.patch} | 10 +- ...licon-Labs-SI3210-device-compatible.patch} | 10 +- ....3-mtd-ubi-wire-up-parent-MTD-device.patch | 33 + ...-mtd-ubi-block-wire-up-device-parent.patch | 49 + ...mtd_otp_nvmem_add-to-handle-EPROBE_.patch} | 0 ...-aquantia-move-to-separate-directory.patch | 2306 +++++++ ...antia-move-MMD_VEND-define-to-header.patch | 183 + ...y-aquantia-add-firmware-load-support.patch | 504 ++ ...edicated-C45-API-to-MDIO-bus-drivers.patch | 326 + ...-Introduce-PSGMII-PHY-interface-mode.patch | 105 + ...eplace-of_gpio.h-with-what-indeed-is.patch | 32 + ...upport-qca8081-genphy_c45_pma_read_a.patch | 70 + ...3x-merge-qca8081-slave-seed-function.patch | 73 + ...nable-qca8081-slave-seed-conditional.patch | 76 + ...-at803x-support-qca8081-1G-chip-type.patch | 48 + ...emove-qca8081-1G-fast-retrain-and-sl.patch | 98 + ...dd-qca8081-fifo-reset-on-the-link-ch.patch | 55 + ...ove-disable-WOL-to-specific-at8031-p.patch | 69 + ...aname-hw_stats-functions-to-qca83xx-.patch | 129 + ...ove-qca83xx-specific-check-in-dedica.patch | 155 + ...ove-specific-DT-option-for-at8031-to.patch | 94 + ...ove-specific-at8031-probe-mode-check.patch | 78 + ...ove-specific-at8031-config_init-to-d.patch | 86 + ...ove-specific-at8031-WOL-bits-to-dedi.patch | 92 + ...ove-specific-at8031-config_intr-to-d.patch | 78 + ...ake-at8031-related-DT-functions-name.patch | 78 + ...ove-at8031-functions-in-dedicated-se.patch | 297 + ...ove-at8035-specific-DT-parse-to-dedi.patch | 114 + ...rop-specific-PHY-ID-check-from-cable.patch | 219 + ...ove-specific-qca808x-config_aneg-to-.patch | 116 + ...ake-read-specific-status-function-mo.patch | 97 + ...at803x-remove-extra-space-after-cast.patch | 27 + ...x-replace-msleep-1-with-usleep_range.patch | 38 + ...etter-align-function-varibles-to-ope.patch | 152 + ...generalize-cdt-fault-length-function.patch | 62 + ...efactor-qca808x-cable-test-get-statu.patch | 118 + ...dd-support-for-cdt-cross-short-test-.patch | 182 + ...at803x-make-read_status-more-generic.patch | 62 + ...y-at803x-add-LED-support-for-qca808x.patch | 408 ++ ...03x-PHY-driver-to-dedicated-director.patch | 5598 +++++++++++++++++ ...ate-and-move-functions-to-shared-lib.patch | 243 + ...eatch-qca83xx-PHY-driver-from-at803x.patch | 638 ++ ...e-additional-functions-to-shared-lib.patch | 1014 +++ ...etach-qca808x-PHY-driver-from-at803x.patch | 1936 ++++++ ...s-add-driver-for-MediaTek-SGMII-PCS.patch} | 0 ...r-type-u8-in-phy_package_shared-stru.patch | 28 + ...HY-package-API-to-support-multiple-g.patch | 341 + ...ure-__phy_write-read_mmd-to-helper-a.patch | 116 + ...pport-for-PHY-package-MMD-read-write.patch | 196 + ....2-net-fman-memac-Add-serdes-support.patch | 103 + ...2-net-fman-memac-Use-lynx-pcs-driver.patch | 384 ++ ...-03-v6.2-net-dpaa-Convert-to-phylink.patch | 2451 ++++++++ ...ide-phylink_validate_mask_caps-helpe.patch | 93 + ...valid-state-argument-to-phylink_vali.patch | 39 + ...dd-phylink_get_link_timer_ns-helper.patch} | 6 +- ...cit-phylink_generic_validate-referen.patch | 250 + ..._bus_find_fwnode-take-a-const-fwnode.patch | 48 + ...-t-print-an_enabled-in-pcs_get_state.patch | 31 + ...e-Autoneg-bit-rather-than-an_enabled.patch | 32 + ...ort-validated-pause-and-autoneg-in-f.patch | 64 + ...2-v6.4-net-phylink-remove-an_enabled.patch | 177 + ...et-phylink-constify-fwnode-arguments.patch | 88 + ...-fwnode_get_phy_node-fwnode-argument.patch | 38 + ...ylink-fix-ksettings_set-ethtool-call.patch | 44 + ...-support-for-setting-signalling-rate.patch | 149 + ...hy-add-helpers-for-comparing-phy-IDs.patch | 147 + ...ire-supported_interfaces-to-be-fille.patch | 71 + ...ve-duplicated-linkmode-pause-resolut.patch | 64 + ...function-to-resolve-clause-73-negoti.patch | 76 + ...ide-phylink_pcs_config-and-phylink_p.patch | 100 + ...ually-fix-ksettings_set-ethtool-call.patch | 55 + ...net-phylink-add-PCS-negotiation-mode.patch | 324 + ...ert-phylink_mii_c22_pcs_config-to-ne.patch | 45 + ...-neg_mode-into-phylink_mii_c22_pcs_c.patch | 187 + ...xi-update-PCS-driver-to-use-neg_mode.patch | 101 + ...e-Autoneg-bit-rather-than-an_enabled.patch | 55 + ...s-fix-incorrect-number-of-interfaces.patch | 30 + ...808x-fix-logic-error-in-LED-brightne.patch | 36 + ...808x-default-to-LED-active-High-if-n.patch | 41 + ...ort-for-scanning-PHY-in-PHY-packages.patch | 211 + ...-add-devm-of_phy_package_join-helper.patch | 185 + ...move-more-function-to-shared-library.patch | 583 ++ ...whether-link-has-changed-in-c37_read.patch | 100 + ...m-add-support-for-QCA807x-PHY-Family.patch | 668 ++ ...e-common-qca808x-LED-define-to-share.patch | 179 + ...eneralize-some-qca808x-LED-functions.patch | 172 + ...07x-add-support-for-configurable-LED.patch | 326 + ...move-interface-mode-check-to-.config.patch | 51 + ...03x-fix-kernel-panic-with-at8031_pro.patch | 45 + ...-add-support-for-clock-frequency-pro.patch | 205 + ...-drop-wrong-endianness-conversion-fo.patch | 92 + ..._eth_soc-enable-flow-offloading-supp.patch | 2 +- ..._eth_soc-remove-cpu_relax-in-mtk_pen.patch | 4 +- ..._eth_soc-introduce-mtk_hw_reset-util.patch | 4 +- ..._eth_soc-introduce-mtk_hw_warm_reset.patch | 8 +- ..._eth_soc-align-reset-procedure-to-ve.patch | 10 +- ..._eth_soc-add-dma-checks-to-mtk_hw_re.patch | 8 +- ..._wed-add-reset-reset_complete-callba.patch | 4 +- ..._eth_soc-increase-tx-ring-side-for-Q.patch | 14 +- ..._eth_soc-avoid-port_mg-assignment-on.patch | 4 +- ..._eth_soc-implement-multi-queue-suppo.patch | 58 +- ..._eth_soc-fix-VLAN-rx-hardware-accele.patch | 10 +- ..._eth_soc-disable-hardware-DSA-untagg.patch | 4 +- ..._eth_soc-enable-special-tag-when-any.patch | 6 +- ..._eth_soc-fix-DSA-TX-tag-hwaccel-for-.patch | 4 +- ...t-mtk_eth_soc-add-support-for-MT7981.patch | 4 +- ..._eth_soc-set-MDIO-bus-clock-frequenc.patch | 4 +- ..._eth_soc-switch-to-external-PCS-driv.patch | 10 +- ..._eth_soc-ppe-add-support-for-flow-ac.patch | 10 +- ..._eth_soc-drop-generic-vlan-rx-offloa.patch | 14 +- ...net-phy-bcm54612e-add-suspend-resume.patch | 27 + ..._ppe-add-MTK_FOE_ENTRY_V-1-2-_SIZE-m.patch | 10 +- ..._eth_soc-remove-incorrect-PLL-config.patch | 4 +- ..._eth_soc-remove-mac_pcs_get_state-an.patch | 6 +- ..._eth_soc-add-version-in-mtk_soc_data.patch | 56 +- ..._eth_soc-rely-on-MTK_MAX_DEVS-and-re.patch | 30 +- ..._eth_soc-add-NETSYS_V3-version-suppo.patch | 22 +- ..._eth_soc-add-basic-support-for-MT798.patch | 10 +- ..._eth_soc-enable-page_pool-support-fo.patch | 2 +- ..._eth_soc-enable-nft-hw-flowtable_off.patch | 2 +- ..._eth_soc-support-per-flow-accounting.patch | 2 +- ...tk_eth_soc-add-reset-bits-for-MT7988.patch | 8 +- ..._eth_soc-add-support-for-in-SoC-SRAM.patch | 24 +- ..._eth_soc-support-36-bit-DMA-addressi.patch | 16 +- ...k_eth_soc-fix-uninitialized-variable.patch | 44 + ..._eth_soc-fix-pse_port-configuration-.patch | 33 + ...eth_soc-add-code-for-offloading-flo.patch} | 10 +- ...atek-mtk_ppe-prefer-newly-added-l2-.patch} | 2 +- ...eth_soc-improve-keeping-track-of-of.patch} | 18 +- ...atek-fix-ppe-flow-accounting-for-L2.patch} | 24 +- ..._wed-add-some-more-info-in-wed_txinf.patch | 45 + ..._wed-minor-change-in-wed_-tx-rx-info.patch | 47 + ..._eth_soc-rely-on-mtk_pse_port-defini.patch | 29 + ..._wed-check-update_wo_rx_stats-in-mtk.patch | 26 + ..._wed-do-not-assume-offload-callbacks.patch | 68 + ..._wed-introduce-versioning-utility-ro.patch | 232 + ..._wed-do-not-configure-rx-offload-if-.patch | 234 + ..._wed-rename-mtk_rxbm_desc-in-mtk_wed.patch | 52 + ..._wed-introduce-mtk_wed_buf-structure.patch | 87 + ..._wed-move-mem_region-array-out-of-mt.patch | 88 + ...-mtk_wed-make-memory-region-optional.patch | 71 + ...k_wed-add-mtk_wed_soc_data-structure.patch | 217 + ..._wed-introduce-WED-support-for-MT798.patch | 1280 ++++ ..._wed-refactor-mtk_wed_check_wfdma_rx.patch | 95 + ..._wed-introduce-partial-AMSDU-offload.patch | 465 ++ ..._wed-introduce-hw_rro-support-for-MT.patch | 483 ++ ..._wed-debugfs-move-wed_v2-specific-re.patch | 78 + ..._wed-debugfs-add-WED-3.0-debugfs-ent.patch | 432 ++ ...et-mtk_wed-add-wed-3.0-reset-support.patch | 587 ++ ...antia-add-AQR111-and-AQR111B0-PHY-ID.patch | 102 + ...9-net-phy-aquantia-add-AQR113-PHY-ID.patch | 60 + ...9-net-phy-aquantia-add-AQR813-PHY-ID.patch | 59 + ...et-dsa-introduce-dsa_phylink_to_port.patch | 99 + ...A-switch-drivers-to-provide-their-ow.patch | 117 + ...t-introduce-napi_is_scheduled-helper.patch | 96 + ...et-stmmac-improve-TX-timer-arm-logic.patch | 77 + ...c-move-TX-timer-arm-after-DMA-enable.patch | 96 + ...ac-increase-TX-coalesce-timer-to-5ms.patch | 38 + ...d-QCA8K_ATU_TABLE_SIZE-define-for-fd.patch | 63 + ...a8k-convert-to-regmap-read-write-API.patch | 261 + ...-enable-use_single_write-for-qca8xxx.patch | 78 + ...ke-learning-configurable-and-keep-of.patch | 146 + ...mit-user-ports-access-to-the-first-C.patch | 53 + ...ve-qca8xxx-hol-fixup-to-separate-fun.patch | 111 + ...e-dsa_for_each-macro-instead-of-for-.patch | 158 + ...x-regmap-bulk-read-write-methods-on-.patch | 61 + ...a-mt7530-remove-redundant-assignment.patch | 32 + ...-dsa-mt7530-use-external-PCS-driver.patch} | 71 +- ...ke-some-noise-if-register-read-fail.patch} | 4 +- ...-mt7530-refactor-SGMII-PCS-creation.patch} | 8 +- ...t7530-use-unlocked-regmap-accessors.patch} | 10 +- ...e-regmap-to-access-switch-register-.patch} | 14 +- ...ve-SGMII-PCS-creation-to-mt7530_pro.patch} | 10 +- ...-dsa-mt7530-introduce-mutex-helpers.patch} | 4 +- ...ve-p5_intf_modes-function-to-mt7530.patch} | 4 +- ...troduce-mt7530_probe_common-helper-.patch} | 10 +- ...troduce-mt7530_remove_common-helper.patch} | 8 +- ...7530-introduce-separate-MDIO-driver.patch} | 30 +- ...ip-locking-if-MDIO-bus-isn-t-presen.patch} | 4 +- ...troduce-driver-for-MT7988-built-in-.patch} | 12 +- ...dsa-mt7530-fix-support-for-MT7531BE.patch} | 6 +- ...30-update-PCS-driver-to-use-neg_mode.patch | 35 + ...onvert-to-platform-remove-callback-r.patch | 58 + ...3x-remove-mt753x_phylink_pcs_link_up.patch | 51 + ...eplace-deprecated-strncpy-with-ethto.patch | 40 + ...upport-OF-based-registration-of-swit.patch | 116 + ...-fix-10M-100M-speed-on-MT7988-switch.patch | 34 + ...lways-trap-frames-to-active-CPU-port.patch | 125 + ...se-p5_interface_select-as-data-type-.patch | 45 + ...tore-port-5-SGMII-capability-of-MT75.patch | 227 + ...mprove-comments-regarding-switch-por.patch | 133 + ...mprove-code-path-for-setting-up-port.patch | 95 + ...o-not-set-priv-p5_interface-on-mt753.patch | 42 + ...o-not-run-mt7530_setup_port5-if-port.patch | 62 + ...mpty-default-case-on-mt7530_setup_po.patch | 58 + ...7530-move-XTAL-check-to-mt7530_setup.patch | 53 + ...mt7530-simplify-mt7530_pad_clk_setup.patch | 146 + ...all-port-6-setup-from-mt7530_mac_con.patch | 97 + ...30-remove-pad_setup-function-pointer.patch | 148 + ...-correct-port-capabilities-of-MT7988.patch | 36 + ...o-not-clear-config-supported_interfa.patch | 38 + ...emove-.mac_port_config-for-MT7988-an.patch | 81 + ...et-interrupt-register-only-for-MT753.patch | 31 + ...o-not-use-SW_PHY_RST-to-reset-MT7531.patch | 41 + ...et-rid-of-useless-error-returns-on-p.patch | 217 + ...get-rid-of-priv-info-cpu_port_config.patch | 305 + ...-mt7530-get-rid-of-mt753x_mac_config.patch | 48 + ...ut-initialising-PCS-devices-code-bac.patch | 57 + ...ort-link-settings-ops-and-force-link.patch | 68 + ...-dsa-mt7530-simplify-link-operations.patch | 83 + ...0-provide-own-phylink-MAC-operations.patch | 135 + ...ix-port-mirroring-for-MT7988-SoC-swi.patch | 49 + ...dio-read-PHY-address-of-switch-from-.patch | 238 + ...-dsa-mt7530-simplify-core-operations.patch | 186 + ...isable-EEE-abilities-on-failure-on-M.patch | 88 + ...et-dsa-mt7530-refactor-MT7530_PMCR_P.patch | 200 + ...ename-p5_intf_sel-and-use-only-for-M.patch | 185 + ...ename-mt753x_bpdu_port_fw-enum-to-mt.patch | 169 + ...efactor-MT7530_MFC-and-MT7531_CFC-ad.patch | 201 + ...efactor-MT7530_HWTRAP-and-MT7530_MHW.patch | 257 + ...ove-MT753X_MTRAP-operations-for-MT75.patch | 117 + ...eturn-mt7530_setup_mdio-mt7531_setup.patch | 39 + ...efine-MAC-speed-capabilities-per-swi.patch | 75 + ...530-get-rid-of-function-sanity-check.patch | 33 + ...dsa-mt7530-refactor-MT7530_PMEEECR_P.patch | 71 + ...et-rid-of-mac_port_validate-member-o.patch | 46 + ...se-priv-ds-num_ports-instead-of-MT75.patch | 57 + ...o-not-pass-port-variable-to-mt7531_r.patch | 37 + ...xplain-exposing-MDIO-bus-of-MT7531AE.patch | 33 + ...0-do-not-set-MT7530_P5_DIS-when-PHY-.patch | 45 + ...etect-PHY-muxing-when-PHY-is-defined.patch | 45 + ...or-Motorcomm-yt8521-gigabit-ethernet.patch | 2 +- ...net-phy-add-Motorcomm-YT8531S-phy-id.patch | 2 +- ...or-Motorcomm-yt8531-gigabit-ethernet.patch | 2 +- ...k-add-pcs_enable-pcs_disable-methods.patch | 41 +- ...t-pcs-lynxi-implement-pcs_disable-op.patch | 2 +- ...ive-renaming-when-an-interface-is-up.patch | 136 + ...ix-risk-of-NULL-pointer-deallocation.patch | 30 + ...ansmit-URBs-without-trailing-padding.patch | 35 + ...03-usbnet-ipheth-add-CDC-NCM-support.patch | 326 + ...et-ipheth-update-Kconfig-description.patch | 36 + ...6xxx-fix-marvell-6350-switch-probing.patch | 89 + ...11h-Add-the-Airoha-EN8811H-PHY-drive.patch | 1140 ++++ ...phy-air_en8811h-fix-some-error-codes.patch | 47 + ...a8k-move-qca8k_port_to_phy-to-header.patch | 4 +- ...net-dsa-qca8k-add-LEDs-basic-support.patch | 6 +- ...5-net-phy-Add-a-binding-for-PHY-LEDs.patch | 12 +- ...ce-Call-into-the-PHY-driver-to-set-L.patch | 8 +- ...ce-Call-into-the-PHY-driver-to-set-L.patch | 6 +- ...d-support-for-additional-modes-for-n.patch | 64 + ...x-pass-directly-chip-structure-to-mv.patch | 64 + ...x-use-mv88e6xxx_phy_is_internal-in-m.patch | 31 + ...x-add-field-to-specify-internal-phys.patch | 69 + ...x-fix-88E6393X-family-internal-phys-.patch | 52 + ...x-pass-mv88e6xxx_chip-structure-to-p.patch | 110 + ...xx-enable-support-for-88E6361-switch.patch | 153 + ...ount-to-list.h-as-list_count_nodes-f.patch | 75 + ...Convert-to-devm_platform_ioremap_res.patch | 36 + ...-Use-devm_platform_get_and_ioremap_r.patch | 30 + ...p-Convert-to-devm_platform_ioremap_r.patch | 34 + ...fuse-Convert-to-devm_platform_iorema.patch | 36 + ...fuse-Use-devm_platform_get_and_iorem.patch | 31 + ...m-Use-devm_platform_get_and_ioremap_.patch | 30 + ....6-0007-nvmem-qfprom-do-some-cleanup.patch | 59 + ...se-devm_platform_get_and_ioremap_res.patch | 29 + ...nvmem-add-new-NXP-QorIQ-eFuse-driver.patch | 133 + ...-nvmem-Kconfig-Fix-typo-drive-driver.patch | 37 + ...m-Add-Qualcomm-secure-QFPROM-support.patch | 152 + ...-Replace-zero-length-array-with-DECL.patch | 30 + ...e-all-cells-before-adding-the-nvmem-.patch | 40 + ...n-NULL-when-no-nvmem-layout-is-found.patch | 35 + ...-Do-not-open-code-existing-functions.patch | 29 + ...tify-when-a-new-layout-is-registered.patch | 44 + ...ia-Use-sysfs_emit-instead-of-sprintf.patch | 29 + ...a-Make-set_brightness-more-efficient.patch | 207 + ...a-Support-HW-controlled-mode-via-pri.patch | 202 + ...a-Add-support-for-enabling-disabling.patch | 244 + ...a-Fix-brightness-setting-and-trigger.patch | 167 + ...mem-qfprom-Mark-core-clk-as-optional.patch | 37 + ...it-config-option-to-read-old-syntax-.patch | 330 + ...0003-nvmem-Use-device_get_match_data.patch | 77 + ...4-Revert-nvmem-add-new-config-option.patch | 77 + ...ect-fixed-layouts-to-grab-a-layout-d.patch | 45 + ..._nvram-store-a-copy-of-NVRAM-content.patch | 261 + ...-Add-FUNCTION-defines-for-per-band-W.patch | 34 + ...-Add-LED_FUNCTION_WAN_ONLINE-for-Int.patch | 35 + ...-device-Export-of_device_make_bus_id.patch | 140 + ...mem_layout_get_container-in-another-.patch | 95 + ...Create-a-header-for-internal-sharing.patch | 91 + ...03-nvmem-Simplify-the-add_cells-hook.patch | 79 + ...vmem-Move-and-rename-fixup_cell_info.patch | 169 + ...rk-layouts-to-become-regular-devices.patch | 763 +++ ...vmem-core-Expose-cells-through-sysfs.patch | 240 + ...support-for-STM32MP25-BSEC-to-contro.patch | 65 + ...factor-.add_cells-callback-arguments.patch | 94 + ...mem-drop-nvmem_layout_get_match_data.patch | 72 + ...nvmem-core-add-nvmem_dev_size-helper.patch | 53 + ...-use-nvmem_add_one_cell-nvmem-subsys.patch | 126 + ...-u-boot-env-use-nvmem-device-helpers.patch | 81 + ...vmem-u-boot-env-improve-coding-style.patch | 62 + ...fix-circular-LEDS_CLASS-dependencies.patch | 2 +- ...net-phy-Fix-reading-LED-reg-property.patch | 4 +- ...emove-LEDs-to-ensure-correct-orderin.patch | 8 +- ...ce-Call-into-the-PHY-driver-to-set-L.patch | 6 +- ...tc-rtc7301-Support-byte-addressed-IO.patch | 93 + ...t-phy-amd-Support-the-Altima-AMI101L.patch | 82 + ...ors-from-DT-bindings-to-led_co.patch.patch | 29 + ...igger-netdev-Extend-speeds-up-to-10G.patch | 111 + ...-support-for-PHY-LEDs-polarity-modes.patch | 98 + ...dev-fix-RTNL-handling-to-prevent-pot.patch | 170 + ....4-net-phy-hide-the-PHYLIB_LEDS-knob.patch | 43 + ...dev-Fix-kernel-panic-on-interface-re.patch | 55 + ...ix-order-of-pek-rise-and-fall-events.patch | 80 + ...x-Switch-to-the-sys-off-handler-API.patch} | 0 ...fd-axp20x-Fix-axp288-writable-ranges.patch | 28 + ...xp20x-Add-support-for-AXP15060-PMIC.patch} | 16 +- ...axp20x-Add-support-for-AXP313a-PMIC.patch} | 18 +- ...20x-Add-support-for-AXP313a-variant.patch} | 0 ...gulator-axp20x-Add-AXP15060-support.patch} | 0 ...5-mfd-axp20x-Add-support-for-AXP192.patch} | 14 +- ...v6.8-net-phy-add-possible-interfaces.patch | 60 + ...6.8-net-phylink-use-for_each_set_bit.patch | 46 + ...k-split-out-per-interface-validation.patch | 76 + ...k-pass-PHY-into-phylink_validate_one.patch | 47 + ...-pass-PHY-into-phylink_validate_mask.patch | 58 + ...t-out-PHY-validation-from-phylink_br.patch | 95 + ...the-PHY-s-possible_interfaces-if-pop.patch | 130 + ...808x-add-helper-for-checking-for-1G-.patch | 50 + ...-qca808x-fill-in-possible_interfaces.patch | 44 + target/linux/generic/config-6.1 | 20 +- .../files/drivers/mtd/mtdsplit/Kconfig | 13 +- .../files/drivers/mtd/mtdsplit/Makefile | 2 + .../drivers/mtd/mtdsplit/mtdsplit_bcm_wfi.c | 16 +- .../files/drivers/mtd/mtdsplit/mtdsplit_fit.c | 20 +- .../drivers/mtd/mtdsplit/mtdsplit_h3c_vfs.c | 170 + .../drivers/mtd/mtdsplit/mtdsplit_seil.c | 191 + .../generic/files/drivers/mtd/nand/mtk_bmt.h | 8 +- .../files/drivers/mtd/nand/mtk_bmt_v2.c | 55 +- .../drivers/mtd/parsers/routerbootpart.c | 2 +- .../generic/files/drivers/net/phy/ar8216.c | 21 +- .../generic/files/drivers/net/phy/ar8216.h | 26 +- .../generic/files/drivers/net/phy/ar8327.c | 2 +- .../files/drivers/net/phy/b53/b53_common.c | 24 +- .../files/drivers/net/phy/b53/b53_mdio.c | 38 +- .../files/drivers/net/phy/b53/b53_mmap.c | 4 +- .../files/drivers/net/phy/b53/b53_priv.h | 8 +- .../files/drivers/net/phy/b53/b53_spi.c | 4 +- .../files/drivers/net/phy/b53/b53_srab.c | 4 +- .../generic/files/drivers/net/phy/psb6970.c | 17 +- .../files/drivers/net/phy/rtl8366_smi.c | 39 +- .../files/drivers/net/phy/rtl8366_smi.h | 5 +- .../generic/files/drivers/net/phy/rtl8367.c | 7 +- .../generic/files/drivers/net/phy/rtl8367b.c | 11 +- .../generic/files/drivers/net/phy/swconfig.c | 3 + .../files/drivers/net/phy/swconfig_leds.c | 9 +- .../files/drivers/platform/mikrotik/Kconfig | 7 +- .../files/drivers/platform/mikrotik/Makefile | 1 + .../drivers/platform/mikrotik/rb_hardconfig.c | 37 +- .../drivers/platform/mikrotik/rb_hardconfig.h | 32 + .../drivers/platform/mikrotik/rb_nvmem.c | 230 + .../drivers/platform/mikrotik/rb_softconfig.c | 35 +- .../drivers/platform/mikrotik/routerboot.c | 47 +- .../drivers/platform/mikrotik/routerboot.h | 8 +- ...-abort-configuration-on-unset-symbol.patch | 41 + .../generic/hack-6.1/205-kconfig-exit.patch | 20 - .../hack-6.1/212-tools_portability.patch | 285 +- .../linux/generic/hack-6.1/251-kconfig.patch | 53 - ...rans-call-add-disks-after-mtd-device.patch | 2 +- ...upport-OpenWrt-s-MTD_ROOTFS_ROOT_DEV.patch | 2 +- .../700-swconfig_switch_drivers.patch | 4 +- ...-dsa-mv88e6xxx-disable-ATU-violation.patch | 2 +- .../hack-6.1/720-net-phy-add-aqr-phys.patch | 120 - .../721-net-add-packet-mangeling.patch | 14 +- ...hy-aquantia-enable-AQR112-and-AQR412.patch | 20 +- ...aquantia-fix-system-side-protocol-mi.patch | 8 +- ...y-aquantia-Add-AQR113-driver-support.patch | 43 - ...ntia-add-PHY_IDs-for-AQR112-variants.patch | 20 +- ...mtk-lynxi-workaround-2500BaseX-no-an.patch | 24 +- .../765-mxl-gpy-control-LED-reg-from-DT.patch | 10 +- .../781-usb-net-rndis-support-asr.patch | 13 + .../790-SFP-GE-T-ignore-TX_FAULT.patch | 2 +- .../800-GPIO-add-named-gpio-exports.patch | 38 +- .../hack-6.1/901-debloat_sock_diag.patch | 10 + .../generic/hack-6.1/902-debloat_proc.patch | 11 + ...m350.patch => 931-usb-net-for-fm350.patch} | 0 ...t-send-arp-replies-if-src-and-target.patch | 37 + .../pending-6.1/307-mips_highmem_offset.patch | 19 - ...el-fix-detect_memory_region-function.patch | 74 + ...er-NVMEM-devices-for-partitions-with.patch | 48 + ...support-for-minor-aligned-partitions.patch | 6 +- ...30-mtd-add-myloader-partition-parser.patch | 2 +- ...dings-mtd-add-basic-bindings-for-UBI.patch | 121 + ...ubi-volume-allow-UBI-volumes-to-prov.patch | 48 + ...e-notifier-to-create-ubiblock-from-p.patch | 225 + ...0-04-mtd-ubi-attach-from-device-tree.patch | 264 + ...e-pre-removal-notification-for-UBI-v.patch | 226 + ...6-mtd-ubi-populate-ubi-volume-fwnode.patch | 65 + ...provide-NVMEM-layer-over-UBI-volumes.patch | 246 + ...k-add-basic-bindings-for-block-devic.patch | 120 + ...-09-block-partitions-populate-fwnode.patch | 77 + ...ck-add-new-genhd-flag-GENHD_FL_NVMEM.patch | 29 + ...50-11-block-implement-NVMEM-provider.patch | 235 + ...-mmc-mmc-card-add-block-device-nodes.patch | 74 + ...0-13-mmc-core-set-card-fwnode_handle.patch | 23 + ...mmc-block-set-fwnode-of-disk-devices.patch | 39 + .../450-15-mmc-block-set-GENHD_FL_NVMEM.patch | 22 + .../479-mtd-spi-nor-add-xtx-xt25f128b.patch | 2 +- ...mtd-device-named-ubi-or-data-on-boot.patch | 17 +- ...to-create-ubiblock-device-for-rootfs.patch | 76 +- ...ROOT_DEV-to-ubiblock-rootfs-if-unset.patch | 4 +- .../494-mtd-ubi-add-EOF-marker-support.patch | 2 +- ...add-uImage.FIT-subimage-block-driver.patch | 7 +- .../530-jffs2_make_lzma_available.patch | 2 +- ...les-ignore-EOPNOTSUPP-on-flowtable-d.patch | 29 + ...net-mtk_eth_soc-enable-threaded-NAPI.patch | 2 +- ...detach-callback-to-struct-phy_driver.patch | 4 +- ...-qca8k-implement-lag_fdb_add-del-ops.patch | 86 + ...a8k-enable-flooding-to-both-CPU-port.patch | 37 + ...k-add-support-for-port_change_master.patch | 158 + ...enable-assisted-learning-on-CPU-port.patch | 57 + ...pq8074-add-clock-frequency-to-MDIO-n.patch | 25 + ...support-switching-between-SGMII-and-.patch | 16 + ...e-all-MACs-are-powered-down-before-r.patch | 28 - ...sable-SGMII-in-band-AN-for-2-5G-PHYs.patch | 1 + ...-phy-realtek-introduce-rtl822x_probe.patch | 24 +- ...tek-detect-early-version-of-RTL8221B.patch | 14 +- ...0211_ptr-even-with-no-CFG82111-suppo.patch | 2 +- ..._eth_soc-fix-remaining-throughput-re.patch | 4 +- ..._eth_soc-ppe-fix-L2-offloading-with-.patch | 2 +- ..._eth_soc-add-paths-and-SerDes-modes-.patch | 1294 +--- ..._eth_soc-set-coherent-mask-to-get-PP.patch | 46 + ...y-mediatek-xfi-tphy-add-new-bindings.patch | 136 + ...hy-add-driver-for-MediaTek-XFI-T-PHY.patch | 498 ++ ...lynxi-add-platform-driver-for-MT7988.patch | 371 ++ ...pcs-add-bindings-for-MediaTek-USXGMI.patch | 81 + ...-add-driver-for-MediaTek-USXGMII-PCS.patch | 547 ++ ...et-phy-motorcomm-Add-missing-include.patch | 22 + ...ealtek-support-interrupt-of-RTL8221B.patch | 16 +- ...11h-reset-netdev-rules-when-LED-is-s.patch | 45 + ...ional-threading-for-backlog-processi.patch | 22 +- ...equest-assisted-learning-on-CPU-port.patch | 2 +- ...gister-OF-node-for-internal-MDIO-bus.patch | 43 - ...env-align-endianness-of-crc32-values.patch | 6 +- ...-support-mac-base-fixed-layout-cells.patch | 124 + ...-nxp-imx7d-pico-add-cpu-supply-nodes.patch | 43 + ...-add-LED_FUNCTION_MOBILE-for-mobile-.patch | 37 + ...-add-LED_FUNCTION_SPEED_-for-link-sp.patch | 37 + .../041-block-fit-partition-parser.patch | 17 +- ..._wed-rename-mtk_wed_get_memory_regio.patch | 23 +- ..._wed-move-cpuboot-in-a-dedicated-dts.patch | 54 +- ...tk_wed-move-ilm-a-dedicated-dts-node.patch | 87 +- 456 files changed, 51781 insertions(+), 2226 deletions(-) create mode 100644 target/linux/generic/backport-6.1/301-v6.9-kernel.h-removed-REPEAT_BYTE-from-kernel.h.patch create mode 100644 target/linux/generic/backport-6.1/302-v6.9-kernel.h-Move-upper_-_bits-and-lower_-_bits-to-wordp.patch create mode 100644 target/linux/generic/backport-6.1/400-v6.9-mtd-rawnand-brcmnand-Support-write-protection-settin.patch create mode 100644 target/linux/generic/backport-6.1/410-v6.2-mtd-spi-nor-add-generic-flash-driver.patch rename target/linux/generic/{pending-6.1/110-v6.3-0001-spidev-Add-Silicon-Labs-EM3581-device-compatible.patch => backport-6.1/412-v6.3-01-spidev-Add-Silicon-Labs-EM3581-device-compatible.patch} (74%) rename target/linux/generic/{pending-6.1/110-v6.3-0002-spidev-Add-Silicon-Labs-SI3210-device-compatible.patch => backport-6.1/412-v6.3-02-spidev-Add-Silicon-Labs-SI3210-device-compatible.patch} (74%) create mode 100644 target/linux/generic/backport-6.1/424-v6.3-mtd-ubi-wire-up-parent-MTD-device.patch create mode 100644 target/linux/generic/backport-6.1/425-v6.3-mtd-ubi-block-wire-up-device-parent.patch rename target/linux/generic/backport-6.1/{424-v6.4-0004-mtd-core-prepare-mtd_otp_nvmem_add-to-handle-EPROBE_.patch => 426-v6.4-0004-mtd-core-prepare-mtd_otp_nvmem_add-to-handle-EPROBE_.patch} (100%) create mode 100644 target/linux/generic/backport-6.1/702-01-v6.7-net-phy-aquantia-move-to-separate-directory.patch create mode 100644 target/linux/generic/backport-6.1/702-02-v6.7-net-phy-aquantia-move-MMD_VEND-define-to-header.patch create mode 100644 target/linux/generic/backport-6.1/702-03-v6.7-net-phy-aquantia-add-firmware-load-support.patch create mode 100644 target/linux/generic/backport-6.1/703-v6.3-net-mdio-Add-dedicated-C45-API-to-MDIO-bus-drivers.patch create mode 100644 target/linux/generic/backport-6.1/704-v6.6-net-phy-Introduce-PSGMII-PHY-interface-mode.patch create mode 100644 target/linux/generic/backport-6.1/705-v6.4-net-phy-at803x-Replace-of_gpio.h-with-what-indeed-is.patch create mode 100644 target/linux/generic/backport-6.1/706-v6.6-01-net-phy-at803x-support-qca8081-genphy_c45_pma_read_a.patch create mode 100644 target/linux/generic/backport-6.1/706-v6.6-02-net-phy-at803x-merge-qca8081-slave-seed-function.patch create mode 100644 target/linux/generic/backport-6.1/706-v6.6-03-net-phy-at803x-enable-qca8081-slave-seed-conditional.patch create mode 100644 target/linux/generic/backport-6.1/706-v6.6-04-net-phy-at803x-support-qca8081-1G-chip-type.patch create mode 100644 target/linux/generic/backport-6.1/706-v6.6-05-net-phy-at803x-remove-qca8081-1G-fast-retrain-and-sl.patch create mode 100644 target/linux/generic/backport-6.1/706-v6.6-06-net-phy-at803x-add-qca8081-fifo-reset-on-the-link-ch.patch create mode 100644 target/linux/generic/backport-6.1/707-v6.8-02-net-phy-at803x-move-disable-WOL-to-specific-at8031-p.patch create mode 100644 target/linux/generic/backport-6.1/707-v6.8-03-net-phy-at803x-raname-hw_stats-functions-to-qca83xx-.patch create mode 100644 target/linux/generic/backport-6.1/707-v6.8-04-net-phy-at803x-move-qca83xx-specific-check-in-dedica.patch create mode 100644 target/linux/generic/backport-6.1/707-v6.8-05-net-phy-at803x-move-specific-DT-option-for-at8031-to.patch create mode 100644 target/linux/generic/backport-6.1/707-v6.8-06-net-phy-at803x-move-specific-at8031-probe-mode-check.patch create mode 100644 target/linux/generic/backport-6.1/707-v6.8-07-net-phy-at803x-move-specific-at8031-config_init-to-d.patch create mode 100644 target/linux/generic/backport-6.1/707-v6.8-08-net-phy-at803x-move-specific-at8031-WOL-bits-to-dedi.patch create mode 100644 target/linux/generic/backport-6.1/707-v6.8-09-net-phy-at803x-move-specific-at8031-config_intr-to-d.patch create mode 100644 target/linux/generic/backport-6.1/707-v6.8-10-net-phy-at803x-make-at8031-related-DT-functions-name.patch create mode 100644 target/linux/generic/backport-6.1/707-v6.8-11-net-phy-at803x-move-at8031-functions-in-dedicated-se.patch create mode 100644 target/linux/generic/backport-6.1/707-v6.8-12-net-phy-at803x-move-at8035-specific-DT-parse-to-dedi.patch create mode 100644 target/linux/generic/backport-6.1/707-v6.8-13-net-phy-at803x-drop-specific-PHY-ID-check-from-cable.patch create mode 100644 target/linux/generic/backport-6.1/708-v6.8-01-net-phy-at803x-move-specific-qca808x-config_aneg-to-.patch create mode 100644 target/linux/generic/backport-6.1/708-v6.8-02-net-phy-at803x-make-read-specific-status-function-mo.patch create mode 100644 target/linux/generic/backport-6.1/709-v6.8-01-net-phy-at803x-remove-extra-space-after-cast.patch create mode 100644 target/linux/generic/backport-6.1/709-v6.8-02-net-phy-at803x-replace-msleep-1-with-usleep_range.patch create mode 100644 target/linux/generic/backport-6.1/710-v6.8-net-phy-at803x-better-align-function-varibles-to-ope.patch create mode 100644 target/linux/generic/backport-6.1/711-v6.8-01-net-phy-at803x-generalize-cdt-fault-length-function.patch create mode 100644 target/linux/generic/backport-6.1/711-v6.8-02-net-phy-at803x-refactor-qca808x-cable-test-get-statu.patch create mode 100644 target/linux/generic/backport-6.1/711-v6.8-03-net-phy-at803x-add-support-for-cdt-cross-short-test-.patch create mode 100644 target/linux/generic/backport-6.1/711-v6.8-04-net-phy-at803x-make-read_status-more-generic.patch create mode 100644 target/linux/generic/backport-6.1/712-v6.9-net-phy-at803x-add-LED-support-for-qca808x.patch create mode 100644 target/linux/generic/backport-6.1/713-v6.9-01-net-phy-move-at803x-PHY-driver-to-dedicated-director.patch create mode 100644 target/linux/generic/backport-6.1/713-v6.9-02-net-phy-qcom-create-and-move-functions-to-shared-lib.patch create mode 100644 target/linux/generic/backport-6.1/713-v6.9-03-net-phy-qcom-deatch-qca83xx-PHY-driver-from-at803x.patch create mode 100644 target/linux/generic/backport-6.1/713-v6.9-04-net-phy-qcom-move-additional-functions-to-shared-lib.patch create mode 100644 target/linux/generic/backport-6.1/713-v6.9-05-net-phy-qcom-detach-qca808x-PHY-driver-from-at803x.patch rename target/linux/generic/backport-6.1/{723-v6.3-net-pcs-add-driver-for-MediaTek-SGMII-PCS.patch => 714-net-pcs-add-driver-for-MediaTek-SGMII-PCS.patch} (100%) create mode 100644 target/linux/generic/backport-6.1/714-v6.8-01-net-phy-make-addr-type-u8-in-phy_package_shared-stru.patch create mode 100644 target/linux/generic/backport-6.1/714-v6.8-02-net-phy-extend-PHY-package-API-to-support-multiple-g.patch create mode 100644 target/linux/generic/backport-6.1/714-v6.8-03-net-phy-restructure-__phy_write-read_mmd-to-helper-a.patch create mode 100644 target/linux/generic/backport-6.1/714-v6.8-04-net-phy-add-support-for-PHY-package-MMD-read-write.patch create mode 100644 target/linux/generic/backport-6.1/715-01-v6.2-net-fman-memac-Add-serdes-support.patch create mode 100644 target/linux/generic/backport-6.1/715-02-v6.2-net-fman-memac-Use-lynx-pcs-driver.patch create mode 100644 target/linux/generic/backport-6.1/715-03-v6.2-net-dpaa-Convert-to-phylink.patch create mode 100644 target/linux/generic/backport-6.1/715-04-v6.2-net-phylink-provide-phylink_validate_mask_caps-helpe.patch create mode 100644 target/linux/generic/backport-6.1/715-05-v6.2-phylink-require-valid-state-argument-to-phylink_vali.patch rename target/linux/generic/backport-6.1/{700-v6.2-net-phylink-add-phylink_get_link_timer_ns-helper.patch => 715-06-v6.2-net-phylink-add-phylink_get_link_timer_ns-helper.patch} (87%) create mode 100644 target/linux/generic/backport-6.1/715-07-v6.2-net-remove-explicit-phylink_generic_validate-referen.patch create mode 100644 target/linux/generic/backport-6.1/715-08-net-sfp-make-sfp_bus_find_fwnode-take-a-const-fwnode.patch create mode 100644 target/linux/generic/backport-6.1/715-09-v6.4-net-pcs-lynx-don-t-print-an_enabled-in-pcs_get_state.patch create mode 100644 target/linux/generic/backport-6.1/715-10-v6.4-net-dpaa2-mac-use-Autoneg-bit-rather-than-an_enabled.patch create mode 100644 target/linux/generic/backport-6.1/715-11-v6.4-net-phylink-support-validated-pause-and-autoneg-in-f.patch create mode 100644 target/linux/generic/backport-6.1/715-12-v6.4-net-phylink-remove-an_enabled.patch create mode 100644 target/linux/generic/backport-6.1/715-13-v6.5-net-phylink-constify-fwnode-arguments.patch create mode 100644 target/linux/generic/backport-6.1/715-14-v6.3-net-phy-constify-fwnode_get_phy_node-fwnode-argument.patch create mode 100644 target/linux/generic/backport-6.1/715-15-v6.4-net-phylink-fix-ksettings_set-ethtool-call.patch create mode 100644 target/linux/generic/backport-6.1/715-16-v6.5-net-sfp-add-support-for-setting-signalling-rate.patch create mode 100644 target/linux/generic/backport-6.1/715-17-v6.5-net-phy-add-helpers-for-comparing-phy-IDs.patch create mode 100644 target/linux/generic/backport-6.1/715-18-v6.5-net-phylink-require-supported_interfaces-to-be-fille.patch create mode 100644 target/linux/generic/backport-6.1/715-19-v6.5-net-phylink-remove-duplicated-linkmode-pause-resolut.patch create mode 100644 target/linux/generic/backport-6.1/715-20-v6.5-net-phylink-add-function-to-resolve-clause-73-negoti.patch create mode 100644 target/linux/generic/backport-6.1/715-21-v6.5-net-phylink-provide-phylink_pcs_config-and-phylink_p.patch create mode 100644 target/linux/generic/backport-6.1/715-23-v6.4-net-phylink-actually-fix-ksettings_set-ethtool-call.patch create mode 100644 target/linux/generic/backport-6.1/715-24-v6.5-net-phylink-add-PCS-negotiation-mode.patch create mode 100644 target/linux/generic/backport-6.1/715-25-v6.5-net-phylink-convert-phylink_mii_c22_pcs_config-to-ne.patch create mode 100644 target/linux/generic/backport-6.1/715-26-v6.5-net-phylink-pass-neg_mode-into-phylink_mii_c22_pcs_c.patch create mode 100644 target/linux/generic/backport-6.1/715-27-v6.5-net-pcs-lynxi-update-PCS-driver-to-use-neg_mode.patch create mode 100644 target/linux/generic/backport-6.1/715-28-v6.4-net-pcs-xpcs-use-Autoneg-bit-rather-than-an_enabled.patch create mode 100644 target/linux/generic/backport-6.1/715-29-v6.4-net-pcs-xpcs-fix-incorrect-number-of-interfaces.patch create mode 100644 target/linux/generic/backport-6.1/715-v6.9-01-net-phy-qcom-qca808x-fix-logic-error-in-LED-brightne.patch create mode 100644 target/linux/generic/backport-6.1/715-v6.9-02-net-phy-qcom-qca808x-default-to-LED-active-High-if-n.patch create mode 100644 target/linux/generic/backport-6.1/716-v6.9-02-net-phy-add-support-for-scanning-PHY-in-PHY-packages.patch create mode 100644 target/linux/generic/backport-6.1/716-v6.9-03-net-phy-add-devm-of_phy_package_join-helper.patch create mode 100644 target/linux/generic/backport-6.1/716-v6.9-04-net-phy-qcom-move-more-function-to-shared-library.patch create mode 100644 target/linux/generic/backport-6.1/716-v6.9-06-net-phy-provide-whether-link-has-changed-in-c37_read.patch create mode 100644 target/linux/generic/backport-6.1/716-v6.9-07-net-phy-qcom-add-support-for-QCA807x-PHY-Family.patch create mode 100644 target/linux/generic/backport-6.1/716-v6.9-08-net-phy-qcom-move-common-qca808x-LED-define-to-share.patch create mode 100644 target/linux/generic/backport-6.1/716-v6.9-09-net-phy-qcom-generalize-some-qca808x-LED-functions.patch create mode 100644 target/linux/generic/backport-6.1/716-v6.9-10-net-phy-qca807x-add-support-for-configurable-LED.patch create mode 100644 target/linux/generic/backport-6.1/717-v6.9-net-phy-qca807x-move-interface-mode-check-to-.config.patch create mode 100644 target/linux/generic/backport-6.1/718-v6.9-net-phy-qcom-at803x-fix-kernel-panic-with-at8031_pro.patch create mode 100644 target/linux/generic/backport-6.1/720-v6.9-net-mdio-ipq4019-add-support-for-clock-frequency-pro.patch create mode 100644 target/linux/generic/backport-6.1/721-v6.7-net-phy-aquantia-drop-wrong-endianness-conversion-fo.patch create mode 100644 target/linux/generic/backport-6.1/734-v6.8-net-phy-bcm54612e-add-suspend-resume.patch create mode 100644 target/linux/generic/backport-6.1/750-v6.5-20-net-ethernet-mtk_eth_soc-fix-uninitialized-variable.patch create mode 100644 target/linux/generic/backport-6.1/750-v6.5-21-net-ethernet-mtk_eth_soc-fix-pse_port-configuration-.patch rename target/linux/generic/{pending-6.1/736-01-net-ethernet-mtk_eth_soc-add-code-for-offloading-flo.patch => backport-6.1/751-01-v6.4-net-ethernet-mtk_eth_soc-add-code-for-offloading-flo.patch} (96%) rename target/linux/generic/{pending-6.1/736-02-net-ethernet-mediatek-mtk_ppe-prefer-newly-added-l2-.patch => backport-6.1/751-02-v6.4-net-ethernet-mediatek-mtk_ppe-prefer-newly-added-l2-.patch} (94%) rename target/linux/generic/{pending-6.1/736-03-net-ethernet-mtk_eth_soc-improve-keeping-track-of-of.patch => backport-6.1/751-03-v6.4-net-ethernet-mtk_eth_soc-improve-keeping-track-of-of.patch} (94%) rename target/linux/generic/{pending-6.1/736-04-net-ethernet-mediatek-fix-ppe-flow-accounting-for-L2.patch => backport-6.1/751-04-v6.4-net-ethernet-mediatek-fix-ppe-flow-accounting-for-L2.patch} (93%) create mode 100644 target/linux/generic/backport-6.1/752-01-v6.6-net-ethernet-mtk_wed-add-some-more-info-in-wed_txinf.patch create mode 100644 target/linux/generic/backport-6.1/752-02-v6.6-net-ethernet-mtk_wed-minor-change-in-wed_-tx-rx-info.patch create mode 100644 target/linux/generic/backport-6.1/752-03-v6.6-net-ethernet-mtk_eth_soc-rely-on-mtk_pse_port-defini.patch create mode 100644 target/linux/generic/backport-6.1/752-04-v6.6-net-ethernet-mtk_wed-check-update_wo_rx_stats-in-mtk.patch create mode 100644 target/linux/generic/backport-6.1/752-05-v6.7-net-ethernet-mtk_wed-do-not-assume-offload-callbacks.patch create mode 100644 target/linux/generic/backport-6.1/752-06-v6.7-net-ethernet-mtk_wed-introduce-versioning-utility-ro.patch create mode 100644 target/linux/generic/backport-6.1/752-07-v6.7-net-ethernet-mtk_wed-do-not-configure-rx-offload-if-.patch create mode 100644 target/linux/generic/backport-6.1/752-08-v6.7-net-ethernet-mtk_wed-rename-mtk_rxbm_desc-in-mtk_wed.patch create mode 100644 target/linux/generic/backport-6.1/752-09-v6.7-net-ethernet-mtk_wed-introduce-mtk_wed_buf-structure.patch create mode 100644 target/linux/generic/backport-6.1/752-10-v6.7-net-ethernet-mtk_wed-move-mem_region-array-out-of-mt.patch create mode 100644 target/linux/generic/backport-6.1/752-11-v6.7-net-ethernet-mtk_wed-make-memory-region-optional.patch create mode 100644 target/linux/generic/backport-6.1/752-13-v6.7-net-ethernet-mtk_wed-add-mtk_wed_soc_data-structure.patch create mode 100644 target/linux/generic/backport-6.1/752-14-v6.7-net-ethernet-mtk_wed-introduce-WED-support-for-MT798.patch create mode 100644 target/linux/generic/backport-6.1/752-15-v6.7-net-ethernet-mtk_wed-refactor-mtk_wed_check_wfdma_rx.patch create mode 100644 target/linux/generic/backport-6.1/752-16-v6.7-net-ethernet-mtk_wed-introduce-partial-AMSDU-offload.patch create mode 100644 target/linux/generic/backport-6.1/752-17-v6.7-net-ethernet-mtk_wed-introduce-hw_rro-support-for-MT.patch create mode 100644 target/linux/generic/backport-6.1/752-18-v6.7-net-ethernet-mtk_wed-debugfs-move-wed_v2-specific-re.patch create mode 100644 target/linux/generic/backport-6.1/752-19-v6.7-net-ethernet-mtk_wed-debugfs-add-WED-3.0-debugfs-ent.patch create mode 100644 target/linux/generic/backport-6.1/752-20-v6.7-net-ethernet-mtk_wed-add-wed-3.0-reset-support.patch create mode 100644 target/linux/generic/backport-6.1/760-v6.9-net-phy-aquantia-add-AQR111-and-AQR111B0-PHY-ID.patch create mode 100644 target/linux/generic/backport-6.1/761-v6.9-net-phy-aquantia-add-AQR113-PHY-ID.patch create mode 100644 target/linux/generic/backport-6.1/762-v6.9-net-phy-aquantia-add-AQR813-PHY-ID.patch create mode 100644 target/linux/generic/backport-6.1/765-v6.10-net-dsa-introduce-dsa_phylink_to_port.patch create mode 100644 target/linux/generic/backport-6.1/766-v6.10-net-dsa-allow-DSA-switch-drivers-to-provide-their-ow.patch create mode 100644 target/linux/generic/backport-6.1/770-net-introduce-napi_is_scheduled-helper.patch create mode 100644 target/linux/generic/backport-6.1/771-v6.7-01-net-stmmac-improve-TX-timer-arm-logic.patch create mode 100644 target/linux/generic/backport-6.1/771-v6.7-02-net-stmmac-move-TX-timer-arm-after-DMA-enable.patch create mode 100644 target/linux/generic/backport-6.1/771-v6.7-03-net-stmmac-increase-TX-coalesce-timer-to-5ms.patch create mode 100644 target/linux/generic/backport-6.1/778-v6.3-01-net-dsa-qca8k-add-QCA8K_ATU_TABLE_SIZE-define-for-fd.patch create mode 100644 target/linux/generic/backport-6.1/778-v6.3-02-net-dsa-qca8k-convert-to-regmap-read-write-API.patch create mode 100644 target/linux/generic/backport-6.1/779-v6.5-net-dsa-qca8k-enable-use_single_write-for-qca8xxx.patch create mode 100644 target/linux/generic/backport-6.1/780-v6.6-01-net-dsa-qca8k-make-learning-configurable-and-keep-of.patch create mode 100644 target/linux/generic/backport-6.1/780-v6.6-02-net-dsa-qca8k-limit-user-ports-access-to-the-first-C.patch create mode 100644 target/linux/generic/backport-6.1/780-v6.6-03-net-dsa-qca8k-move-qca8xxx-hol-fixup-to-separate-fun.patch create mode 100644 target/linux/generic/backport-6.1/780-v6.6-04-net-dsa-qca8k-use-dsa_for_each-macro-instead-of-for-.patch create mode 100644 target/linux/generic/backport-6.1/781-v6.6-01-net-dsa-qca8k-fix-regmap-bulk-read-write-methods-on-.patch create mode 100644 target/linux/generic/backport-6.1/790-01-v6.2-net-dsa-mt7530-remove-redundant-assignment.patch rename target/linux/generic/backport-6.1/{788-v6.3-net-dsa-mt7530-use-external-PCS-driver.patch => 790-02-v6.4-net-dsa-mt7530-use-external-PCS-driver.patch} (84%) rename target/linux/generic/backport-6.1/{790-v6.4-0001-net-dsa-mt7530-make-some-noise-if-register-read-fail.patch => 790-03-v6.4-net-dsa-mt7530-make-some-noise-if-register-read-fail.patch} (88%) rename target/linux/generic/backport-6.1/{790-v6.4-0002-net-dsa-mt7530-refactor-SGMII-PCS-creation.patch => 790-04-v6.4-net-dsa-mt7530-refactor-SGMII-PCS-creation.patch} (92%) rename target/linux/generic/backport-6.1/{790-v6.4-0003-net-dsa-mt7530-use-unlocked-regmap-accessors.patch => 790-05-v6.4-net-dsa-mt7530-use-unlocked-regmap-accessors.patch} (86%) rename target/linux/generic/backport-6.1/{790-v6.4-0004-net-dsa-mt7530-use-regmap-to-access-switch-register-.patch => 790-06-v6.4-net-dsa-mt7530-use-regmap-to-access-switch-register-.patch} (92%) rename target/linux/generic/backport-6.1/{790-v6.4-0005-net-dsa-mt7530-move-SGMII-PCS-creation-to-mt7530_pro.patch => 790-07-v6.4-net-dsa-mt7530-move-SGMII-PCS-creation-to-mt7530_pro.patch} (79%) rename target/linux/generic/backport-6.1/{790-v6.4-0006-net-dsa-mt7530-introduce-mutex-helpers.patch => 790-08-v6.4-net-dsa-mt7530-introduce-mutex-helpers.patch} (98%) rename target/linux/generic/backport-6.1/{790-v6.4-0007-net-dsa-mt7530-move-p5_intf_modes-function-to-mt7530.patch => 790-09-v6.4-net-dsa-mt7530-move-p5_intf_modes-function-to-mt7530.patch} (94%) rename target/linux/generic/backport-6.1/{790-v6.4-0008-net-dsa-mt7530-introduce-mt7530_probe_common-helper-.patch => 790-10-v6.4-net-dsa-mt7530-introduce-mt7530_probe_common-helper-.patch} (93%) rename target/linux/generic/backport-6.1/{790-v6.4-0009-net-dsa-mt7530-introduce-mt7530_remove_common-helper.patch => 790-11-v6.4-net-dsa-mt7530-introduce-mt7530_remove_common-helper.patch} (83%) rename target/linux/generic/backport-6.1/{790-v6.4-0010-net-dsa-mt7530-introduce-separate-MDIO-driver.patch => 790-12-v6.4-net-dsa-mt7530-introduce-separate-MDIO-driver.patch} (95%) rename target/linux/generic/backport-6.1/{790-v6.4-0011-net-dsa-mt7530-skip-locking-if-MDIO-bus-isn-t-presen.patch => 790-13-v6.4-net-dsa-mt7530-skip-locking-if-MDIO-bus-isn-t-presen.patch} (91%) rename target/linux/generic/backport-6.1/{790-v6.4-0012-net-dsa-mt7530-introduce-driver-for-MT7988-built-in-.patch => 790-14-v6.4-net-dsa-mt7530-introduce-driver-for-MT7988-built-in-.patch} (97%) rename target/linux/generic/backport-6.1/{790-v6.4-0013-net-dsa-mt7530-fix-support-for-MT7531BE.patch => 790-15-v6.4-net-dsa-mt7530-fix-support-for-MT7531BE.patch} (95%) create mode 100644 target/linux/generic/backport-6.1/790-17-v6.5-net-dsa-mt7530-update-PCS-driver-to-use-neg_mode.patch create mode 100644 target/linux/generic/backport-6.1/790-18-v6.7-net-dsa-mt7530-Convert-to-platform-remove-callback-r.patch create mode 100644 target/linux/generic/backport-6.1/790-19-v6.7-net-dsa-mt753x-remove-mt753x_phylink_pcs_link_up.patch create mode 100644 target/linux/generic/backport-6.1/790-20-v6.7-net-dsa-mt7530-replace-deprecated-strncpy-with-ethto.patch create mode 100644 target/linux/generic/backport-6.1/790-21-v6.9-net-dsa-mt7530-support-OF-based-registration-of-swit.patch create mode 100644 target/linux/generic/backport-6.1/790-22-v6.8-net-dsa-mt7530-fix-10M-100M-speed-on-MT7988-switch.patch create mode 100644 target/linux/generic/backport-6.1/790-23-v6.9-net-dsa-mt7530-always-trap-frames-to-active-CPU-port.patch create mode 100644 target/linux/generic/backport-6.1/790-24-v6.9-net-dsa-mt7530-use-p5_interface_select-as-data-type-.patch create mode 100644 target/linux/generic/backport-6.1/790-25-v6.9-net-dsa-mt7530-store-port-5-SGMII-capability-of-MT75.patch create mode 100644 target/linux/generic/backport-6.1/790-26-v6.9-net-dsa-mt7530-improve-comments-regarding-switch-por.patch create mode 100644 target/linux/generic/backport-6.1/790-27-v6.9-net-dsa-mt7530-improve-code-path-for-setting-up-port.patch create mode 100644 target/linux/generic/backport-6.1/790-28-v6.9-net-dsa-mt7530-do-not-set-priv-p5_interface-on-mt753.patch create mode 100644 target/linux/generic/backport-6.1/790-29-v6.9-net-dsa-mt7530-do-not-run-mt7530_setup_port5-if-port.patch create mode 100644 target/linux/generic/backport-6.1/790-30-v6.9-net-dsa-mt7530-empty-default-case-on-mt7530_setup_po.patch create mode 100644 target/linux/generic/backport-6.1/790-31-v6.9-net-dsa-mt7530-move-XTAL-check-to-mt7530_setup.patch create mode 100644 target/linux/generic/backport-6.1/790-32-v6.9-net-dsa-mt7530-simplify-mt7530_pad_clk_setup.patch create mode 100644 target/linux/generic/backport-6.1/790-33-v6.9-net-dsa-mt7530-call-port-6-setup-from-mt7530_mac_con.patch create mode 100644 target/linux/generic/backport-6.1/790-34-v6.9-net-dsa-mt7530-remove-pad_setup-function-pointer.patch create mode 100644 target/linux/generic/backport-6.1/790-35-v6.9-net-dsa-mt7530-correct-port-capabilities-of-MT7988.patch create mode 100644 target/linux/generic/backport-6.1/790-36-v6.9-net-dsa-mt7530-do-not-clear-config-supported_interfa.patch create mode 100644 target/linux/generic/backport-6.1/790-37-v6.9-net-dsa-mt7530-remove-.mac_port_config-for-MT7988-an.patch create mode 100644 target/linux/generic/backport-6.1/790-38-v6.9-net-dsa-mt7530-set-interrupt-register-only-for-MT753.patch create mode 100644 target/linux/generic/backport-6.1/790-39-v6.9-net-dsa-mt7530-do-not-use-SW_PHY_RST-to-reset-MT7531.patch create mode 100644 target/linux/generic/backport-6.1/790-40-v6.9-net-dsa-mt7530-get-rid-of-useless-error-returns-on-p.patch create mode 100644 target/linux/generic/backport-6.1/790-41-v6.9-net-dsa-mt7530-get-rid-of-priv-info-cpu_port_config.patch create mode 100644 target/linux/generic/backport-6.1/790-42-v6.9-net-dsa-mt7530-get-rid-of-mt753x_mac_config.patch create mode 100644 target/linux/generic/backport-6.1/790-43-v6.9-net-dsa-mt7530-put-initialising-PCS-devices-code-bac.patch create mode 100644 target/linux/generic/backport-6.1/790-44-v6.9-net-dsa-mt7530-sort-link-settings-ops-and-force-link.patch create mode 100644 target/linux/generic/backport-6.1/790-45-v6.9-net-dsa-mt7530-simplify-link-operations.patch create mode 100644 target/linux/generic/backport-6.1/790-49-v6.10-net-dsa-mt7530-provide-own-phylink-MAC-operations.patch create mode 100644 target/linux/generic/backport-6.1/790-51-v6.10-net-dsa-mt7530-fix-port-mirroring-for-MT7988-SoC-swi.patch create mode 100644 target/linux/generic/backport-6.1/790-52-v6.10-net-dsa-mt7530-mdio-read-PHY-address-of-switch-from-.patch create mode 100644 target/linux/generic/backport-6.1/790-53-v6.10-net-dsa-mt7530-simplify-core-operations.patch create mode 100644 target/linux/generic/backport-6.1/790-54-v6.10-net-dsa-mt7530-disable-EEE-abilities-on-failure-on-M.patch create mode 100644 target/linux/generic/backport-6.1/790-55-v6.10-net-dsa-mt7530-refactor-MT7530_PMCR_P.patch create mode 100644 target/linux/generic/backport-6.1/790-56-v6.10-net-dsa-mt7530-rename-p5_intf_sel-and-use-only-for-M.patch create mode 100644 target/linux/generic/backport-6.1/790-57-v6.10-net-dsa-mt7530-rename-mt753x_bpdu_port_fw-enum-to-mt.patch create mode 100644 target/linux/generic/backport-6.1/790-58-v6.10-net-dsa-mt7530-refactor-MT7530_MFC-and-MT7531_CFC-ad.patch create mode 100644 target/linux/generic/backport-6.1/790-59-v6.10-net-dsa-mt7530-refactor-MT7530_HWTRAP-and-MT7530_MHW.patch create mode 100644 target/linux/generic/backport-6.1/790-60-v6.10-net-dsa-mt7530-move-MT753X_MTRAP-operations-for-MT75.patch create mode 100644 target/linux/generic/backport-6.1/790-61-v6.10-net-dsa-mt7530-return-mt7530_setup_mdio-mt7531_setup.patch create mode 100644 target/linux/generic/backport-6.1/790-62-v6.10-net-dsa-mt7530-define-MAC-speed-capabilities-per-swi.patch create mode 100644 target/linux/generic/backport-6.1/790-63-v6.10-net-dsa-mt7530-get-rid-of-function-sanity-check.patch create mode 100644 target/linux/generic/backport-6.1/790-64-v6.10-net-dsa-mt7530-refactor-MT7530_PMEEECR_P.patch create mode 100644 target/linux/generic/backport-6.1/790-65-v6.10-net-dsa-mt7530-get-rid-of-mac_port_validate-member-o.patch create mode 100644 target/linux/generic/backport-6.1/790-66-v6.10-net-dsa-mt7530-use-priv-ds-num_ports-instead-of-MT75.patch create mode 100644 target/linux/generic/backport-6.1/790-67-v6.10-net-dsa-mt7530-do-not-pass-port-variable-to-mt7531_r.patch create mode 100644 target/linux/generic/backport-6.1/790-68-v6.10-net-dsa-mt7530-explain-exposing-MDIO-bus-of-MT7531AE.patch create mode 100644 target/linux/generic/backport-6.1/790-69-v6.10-net-dsa-mt7530-do-not-set-MT7530_P5_DIS-when-PHY-.patch create mode 100644 target/linux/generic/backport-6.1/790-70-v6.10-796-net-dsa-mt7530-detect-PHY-muxing-when-PHY-is-defined.patch create mode 100644 target/linux/generic/backport-6.1/794-v6.2-net-core-Allow-live-renaming-when-an-interface-is-up.patch create mode 100644 target/linux/generic/backport-6.1/796-v6.5-01-usbnet-ipheth-fix-risk-of-NULL-pointer-deallocation.patch create mode 100644 target/linux/generic/backport-6.1/796-v6.5-02-usbnet-ipheth-transmit-URBs-without-trailing-padding.patch create mode 100644 target/linux/generic/backport-6.1/796-v6.5-03-usbnet-ipheth-add-CDC-NCM-support.patch create mode 100644 target/linux/generic/backport-6.1/796-v6.5-04-usbnet-ipheth-update-Kconfig-description.patch create mode 100644 target/linux/generic/backport-6.1/797-6.7-net-dsa-mv88e6xxx-fix-marvell-6350-switch-probing.patch create mode 100644 target/linux/generic/backport-6.1/798-v6.10-net-phy-air_en8811h-Add-the-Airoha-EN8811H-PHY-drive.patch create mode 100644 target/linux/generic/backport-6.1/799-v6.10-net-phy-air_en8811h-fix-some-error-codes.patch create mode 100644 target/linux/generic/backport-6.1/806-v6.5-net-dsa-qca8k-add-support-for-additional-modes-for-n.patch create mode 100644 target/linux/generic/backport-6.1/807-v6.5-01-net-dsa-mv88e6xxx-pass-directly-chip-structure-to-mv.patch create mode 100644 target/linux/generic/backport-6.1/807-v6.5-02-net-dsa-mv88e6xxx-use-mv88e6xxx_phy_is_internal-in-m.patch create mode 100644 target/linux/generic/backport-6.1/807-v6.5-03-net-dsa-mv88e6xxx-add-field-to-specify-internal-phys.patch create mode 100644 target/linux/generic/backport-6.1/807-v6.5-04-net-dsa-mv88e6xxx-fix-88E6393X-family-internal-phys-.patch create mode 100644 target/linux/generic/backport-6.1/807-v6.5-05-net-dsa-mv88e6xxx-pass-mv88e6xxx_chip-structure-to-p.patch create mode 100644 target/linux/generic/backport-6.1/807-v6.5-06-net-dsa-mv88e6xxx-enable-support-for-88E6361-switch.patch create mode 100644 target/linux/generic/backport-6.1/810-v6.3-i915-Move-list_count-to-list.h-as-list_count_nodes-f.patch create mode 100644 target/linux/generic/backport-6.1/814-v6.6-0001-nvmem-sunxi_sid-Convert-to-devm_platform_ioremap_res.patch create mode 100644 target/linux/generic/backport-6.1/814-v6.6-0002-nvmem-brcm_nvram-Use-devm_platform_get_and_ioremap_r.patch create mode 100644 target/linux/generic/backport-6.1/814-v6.6-0003-nvmem-lpc18xx_otp-Convert-to-devm_platform_ioremap_r.patch create mode 100644 target/linux/generic/backport-6.1/814-v6.6-0004-nvmem-meson-mx-efuse-Convert-to-devm_platform_iorema.patch create mode 100644 target/linux/generic/backport-6.1/814-v6.6-0005-nvmem-rockchip-efuse-Use-devm_platform_get_and_iorem.patch create mode 100644 target/linux/generic/backport-6.1/814-v6.6-0006-nvmem-stm32-romem-Use-devm_platform_get_and_ioremap_.patch create mode 100644 target/linux/generic/backport-6.1/814-v6.6-0007-nvmem-qfprom-do-some-cleanup.patch create mode 100644 target/linux/generic/backport-6.1/814-v6.6-0008-nvmem-uniphier-Use-devm_platform_get_and_ioremap_res.patch create mode 100644 target/linux/generic/backport-6.1/814-v6.6-0009-nvmem-add-new-NXP-QorIQ-eFuse-driver.patch create mode 100644 target/linux/generic/backport-6.1/814-v6.6-0011-nvmem-Kconfig-Fix-typo-drive-driver.patch create mode 100644 target/linux/generic/backport-6.1/814-v6.6-0012-nvmem-sec-qfprom-Add-Qualcomm-secure-QFPROM-support.patch create mode 100644 target/linux/generic/backport-6.1/814-v6.6-0013-nvmem-u-boot-env-Replace-zero-length-array-with-DECL.patch create mode 100644 target/linux/generic/backport-6.1/814-v6.6-0014-nvmem-core-Create-all-cells-before-adding-the-nvmem-.patch create mode 100644 target/linux/generic/backport-6.1/814-v6.6-0015-nvmem-core-Return-NULL-when-no-nvmem-layout-is-found.patch create mode 100644 target/linux/generic/backport-6.1/814-v6.6-0016-nvmem-core-Do-not-open-code-existing-functions.patch create mode 100644 target/linux/generic/backport-6.1/814-v6.6-0017-nvmem-core-Notify-when-a-new-layout-is-registered.patch create mode 100644 target/linux/generic/backport-6.1/815-v6.6-1-leds-turris-omnia-Use-sysfs_emit-instead-of-sprintf.patch create mode 100644 target/linux/generic/backport-6.1/815-v6.7-2-leds-turris-omnia-Make-set_brightness-more-efficient.patch create mode 100644 target/linux/generic/backport-6.1/815-v6.7-3-leds-turris-omnia-Support-HW-controlled-mode-via-pri.patch create mode 100644 target/linux/generic/backport-6.1/815-v6.7-4-leds-turris-omnia-Add-support-for-enabling-disabling.patch create mode 100644 target/linux/generic/backport-6.1/815-v6.7-5-leds-turris-omnia-Fix-brightness-setting-and-trigger.patch create mode 100644 target/linux/generic/backport-6.1/816-v6.7-0001-nvmem-qfprom-Mark-core-clk-as-optional.patch create mode 100644 target/linux/generic/backport-6.1/816-v6.7-0002-nvmem-add-explicit-config-option-to-read-old-syntax-.patch create mode 100644 target/linux/generic/backport-6.1/816-v6.7-0003-nvmem-Use-device_get_match_data.patch create mode 100644 target/linux/generic/backport-6.1/816-v6.7-0004-Revert-nvmem-add-new-config-option.patch create mode 100644 target/linux/generic/backport-6.1/816-v6.7-0005-nvmem-Do-not-expect-fixed-layouts-to-grab-a-layout-d.patch create mode 100644 target/linux/generic/backport-6.1/816-v6.7-0006-nvmem-brcm_nvram-store-a-copy-of-NVRAM-content.patch create mode 100644 target/linux/generic/backport-6.1/817-v6.9-0001-dt-bindings-leds-Add-FUNCTION-defines-for-per-band-W.patch create mode 100644 target/linux/generic/backport-6.1/817-v6.9-0002-dt-bindings-leds-Add-LED_FUNCTION_WAN_ONLINE-for-Int.patch create mode 100644 target/linux/generic/backport-6.1/818-v6.8-of-device-Export-of_device_make_bus_id.patch create mode 100644 target/linux/generic/backport-6.1/819-v6.8-0001-nvmem-Move-of_nvmem_layout_get_container-in-another-.patch create mode 100644 target/linux/generic/backport-6.1/819-v6.8-0002-nvmem-Create-a-header-for-internal-sharing.patch create mode 100644 target/linux/generic/backport-6.1/819-v6.8-0003-nvmem-Simplify-the-add_cells-hook.patch create mode 100644 target/linux/generic/backport-6.1/819-v6.8-0004-nvmem-Move-and-rename-fixup_cell_info.patch create mode 100644 target/linux/generic/backport-6.1/819-v6.8-0005-nvmem-core-Rework-layouts-to-become-regular-devices.patch create mode 100644 target/linux/generic/backport-6.1/819-v6.8-0006-nvmem-core-Expose-cells-through-sysfs.patch create mode 100644 target/linux/generic/backport-6.1/819-v6.8-0007-nvmem-stm32-add-support-for-STM32MP25-BSEC-to-contro.patch create mode 100644 target/linux/generic/backport-6.1/819-v6.8-0008-nvmem-layouts-refactor-.add_cells-callback-arguments.patch create mode 100644 target/linux/generic/backport-6.1/819-v6.8-0009-nvmem-drop-nvmem_layout_get_match_data.patch create mode 100644 target/linux/generic/backport-6.1/819-v6.8-0010-nvmem-core-add-nvmem_dev_size-helper.patch create mode 100644 target/linux/generic/backport-6.1/819-v6.8-0011-nvmem-u-boot-env-use-nvmem_add_one_cell-nvmem-subsys.patch create mode 100644 target/linux/generic/backport-6.1/819-v6.8-0012-nvmem-u-boot-env-use-nvmem-device-helpers.patch create mode 100644 target/linux/generic/backport-6.1/819-v6.8-0013-nvmem-u-boot-env-improve-coding-style.patch create mode 100644 target/linux/generic/backport-6.1/831-v6.7-rtc-rtc7301-Support-byte-addressed-IO.patch create mode 100644 target/linux/generic/backport-6.1/832-v6.7-net-phy-amd-Support-the-Altima-AMI101L.patch create mode 100644 target/linux/generic/backport-6.1/833-v6.8-leds-core-Add-more-colors-from-DT-bindings-to-led_co.patch.patch create mode 100644 target/linux/generic/backport-6.1/834-v6.8-leds-trigger-netdev-Extend-speeds-up-to-10G.patch create mode 100644 target/linux/generic/backport-6.1/835-v6.9-net-phy-add-support-for-PHY-LEDs-polarity-modes.patch create mode 100644 target/linux/generic/backport-6.1/836-v6.7-leds-trigger-netdev-fix-RTNL-handling-to-prevent-pot.patch create mode 100644 target/linux/generic/backport-6.1/837-v6.4-net-phy-hide-the-PHYLIB_LEDS-knob.patch create mode 100644 target/linux/generic/backport-6.1/838-v6.9-leds-trigger-netdev-Fix-kernel-panic-on-interface-re.patch create mode 100644 target/linux/generic/backport-6.1/880-v6.3-mfd-axp20x-Fix-order-of-pek-rise-and-fall-events.patch rename target/linux/generic/backport-6.1/{880-v6.3-mfd-axp20x-Switch-to-the-sys-off-handler-API.patch => 881-v6.3-mfd-axp20x-Switch-to-the-sys-off-handler-API.patch} (100%) create mode 100644 target/linux/generic/backport-6.1/882-v6.3-mfd-axp20x-Fix-axp288-writable-ranges.patch rename target/linux/generic/backport-6.1/{881-v6.4-mfd-axp20x-Add-support-for-AXP15060-PMIC.patch => 883-v6.4-mfd-axp20x-Add-support-for-AXP15060-PMIC.patch} (95%) rename target/linux/generic/backport-6.1/{882-v6.5-mfd-axp20x-Add-support-for-AXP313a-PMIC.patch => 884-v6.5-mfd-axp20x-Add-support-for-AXP313a-PMIC.patch} (93%) rename target/linux/generic/backport-6.1/{883-v6.5-regulator-axp20x-Add-support-for-AXP313a-variant.patch => 885-v6.5-regulator-axp20x-Add-support-for-AXP313a-variant.patch} (100%) rename target/linux/generic/backport-6.1/{884-v6.5-regulator-axp20x-Add-AXP15060-support.patch => 886-v6.5-regulator-axp20x-Add-AXP15060-support.patch} (100%) rename target/linux/generic/backport-6.1/{885-v6.5-mfd-axp20x-Add-support-for-AXP192.patch => 887-v6.5-mfd-axp20x-Add-support-for-AXP192.patch} (96%) create mode 100644 target/linux/generic/backport-6.1/895-01-v6.8-net-phy-add-possible-interfaces.patch create mode 100644 target/linux/generic/backport-6.1/895-02-v6.8-net-phylink-use-for_each_set_bit.patch create mode 100644 target/linux/generic/backport-6.1/895-03-v6.8-net-phylink-split-out-per-interface-validation.patch create mode 100644 target/linux/generic/backport-6.1/895-04-v6.8-net-phylink-pass-PHY-into-phylink_validate_one.patch create mode 100644 target/linux/generic/backport-6.1/895-05-v6.8-net-phylink-pass-PHY-into-phylink_validate_mask.patch create mode 100644 target/linux/generic/backport-6.1/895-06-v6.8-net-phylink-split-out-PHY-validation-from-phylink_br.patch create mode 100644 target/linux/generic/backport-6.1/895-07-v6.8-net-phylink-use-the-PHY-s-possible_interfaces-if-pop.patch create mode 100644 target/linux/generic/backport-6.1/897-01-v6.9-net-phy-qcom-qca808x-add-helper-for-checking-for-1G-.patch create mode 100644 target/linux/generic/backport-6.1/897-02-v6.9-net-phy-qcom-qca808x-fill-in-possible_interfaces.patch create mode 100644 target/linux/generic/files/drivers/mtd/mtdsplit/mtdsplit_h3c_vfs.c create mode 100644 target/linux/generic/files/drivers/mtd/mtdsplit/mtdsplit_seil.c create mode 100644 target/linux/generic/files/drivers/platform/mikrotik/rb_hardconfig.h create mode 100644 target/linux/generic/files/drivers/platform/mikrotik/rb_nvmem.c create mode 100644 target/linux/generic/hack-6.1/205-kconfig-abort-configuration-on-unset-symbol.patch delete mode 100644 target/linux/generic/hack-6.1/205-kconfig-exit.patch delete mode 100644 target/linux/generic/hack-6.1/720-net-phy-add-aqr-phys.patch delete mode 100644 target/linux/generic/hack-6.1/724-net-phy-aquantia-Add-AQR113-driver-support.patch rename target/linux/generic/hack-6.1/{930-usb-net-for-fm350.patch => 931-usb-net-for-fm350.patch} (100%) create mode 100644 target/linux/generic/pending-6.1/151-net-bridge-do-not-send-arp-replies-if-src-and-target.patch delete mode 100644 target/linux/generic/pending-6.1/307-mips_highmem_offset.patch create mode 100644 target/linux/generic/pending-6.1/350-mips-kernel-fix-detect_memory_region-function.patch create mode 100644 target/linux/generic/pending-6.1/401-mtd-don-t-register-NVMEM-devices-for-partitions-with.patch create mode 100644 target/linux/generic/pending-6.1/450-01-dt-bindings-mtd-add-basic-bindings-for-UBI.patch create mode 100644 target/linux/generic/pending-6.1/450-02-dt-bindings-mtd-ubi-volume-allow-UBI-volumes-to-prov.patch create mode 100644 target/linux/generic/pending-6.1/450-03-mtd-ubi-block-use-notifier-to-create-ubiblock-from-p.patch create mode 100644 target/linux/generic/pending-6.1/450-04-mtd-ubi-attach-from-device-tree.patch create mode 100644 target/linux/generic/pending-6.1/450-05-mtd-ubi-introduce-pre-removal-notification-for-UBI-v.patch create mode 100644 target/linux/generic/pending-6.1/450-06-mtd-ubi-populate-ubi-volume-fwnode.patch create mode 100644 target/linux/generic/pending-6.1/450-07-mtd-ubi-provide-NVMEM-layer-over-UBI-volumes.patch create mode 100644 target/linux/generic/pending-6.1/450-08-dt-bindings-block-add-basic-bindings-for-block-devic.patch create mode 100644 target/linux/generic/pending-6.1/450-09-block-partitions-populate-fwnode.patch create mode 100644 target/linux/generic/pending-6.1/450-10-block-add-new-genhd-flag-GENHD_FL_NVMEM.patch create mode 100644 target/linux/generic/pending-6.1/450-11-block-implement-NVMEM-provider.patch create mode 100644 target/linux/generic/pending-6.1/450-12-dt-bindings-mmc-mmc-card-add-block-device-nodes.patch create mode 100644 target/linux/generic/pending-6.1/450-13-mmc-core-set-card-fwnode_handle.patch create mode 100644 target/linux/generic/pending-6.1/450-14-mmc-block-set-fwnode-of-disk-devices.patch create mode 100644 target/linux/generic/pending-6.1/450-15-mmc-block-set-GENHD_FL_NVMEM.patch create mode 100644 target/linux/generic/pending-6.1/701-netfilter-nf_tables-ignore-EOPNOTSUPP-on-flowtable-d.patch create mode 100644 target/linux/generic/pending-6.1/711-01-net-dsa-qca8k-implement-lag_fdb_add-del-ops.patch create mode 100644 target/linux/generic/pending-6.1/711-02-net-dsa-qca8k-enable-flooding-to-both-CPU-port.patch create mode 100644 target/linux/generic/pending-6.1/711-03-net-dsa-qca8k-add-support-for-port_change_master.patch create mode 100644 target/linux/generic/pending-6.1/712-net-dsa-qca8k-enable-assisted-learning-on-CPU-port.patch create mode 100644 target/linux/generic/pending-6.1/713-03-arm64-dts-qcom-ipq8074-add-clock-frequency-to-MDIO-n.patch delete mode 100644 target/linux/generic/pending-6.1/723-net-mt7531-ensure-all-MACs-are-powered-down-before-r.patch create mode 100644 target/linux/generic/pending-6.1/738-net-ethernet-mtk_eth_soc-set-coherent-mask-to-get-PP.patch create mode 100644 target/linux/generic/pending-6.1/739-01-dt-bindings-phy-mediatek-xfi-tphy-add-new-bindings.patch create mode 100644 target/linux/generic/pending-6.1/739-02-phy-add-driver-for-MediaTek-XFI-T-PHY.patch create mode 100644 target/linux/generic/pending-6.1/739-03-net-pcs-pcs-mtk-lynxi-add-platform-driver-for-MT7988.patch create mode 100644 target/linux/generic/pending-6.1/739-04-dt-bindings-net-pcs-add-bindings-for-MediaTek-USXGMI.patch create mode 100644 target/linux/generic/pending-6.1/739-05-net-pcs-add-driver-for-MediaTek-USXGMII-PCS.patch create mode 100644 target/linux/generic/pending-6.1/740-net-phy-motorcomm-Add-missing-include.patch create mode 100644 target/linux/generic/pending-6.1/742-net-phy-air_en8811h-reset-netdev-rules-when-LED-is-s.patch delete mode 100644 target/linux/generic/pending-6.1/795-mt7530-register-OF-node-for-internal-MDIO-bus.patch create mode 100644 target/linux/generic/pending-6.1/804-nvmem-core-support-mac-base-fixed-layout-cells.patch create mode 100644 target/linux/generic/pending-6.1/870-ARM-dts-nxp-imx7d-pico-add-cpu-supply-nodes.patch create mode 100644 target/linux/generic/pending-6.1/880-01-dt-bindings-leds-add-LED_FUNCTION_MOBILE-for-mobile-.patch create mode 100644 target/linux/generic/pending-6.1/880-02-dt-bindings-leds-add-LED_FUNCTION_SPEED_-for-link-sp.patch diff --git a/target/linux/generic/backport-6.1/301-v6.9-kernel.h-removed-REPEAT_BYTE-from-kernel.h.patch b/target/linux/generic/backport-6.1/301-v6.9-kernel.h-removed-REPEAT_BYTE-from-kernel.h.patch new file mode 100644 index 000000000..239adff1a --- /dev/null +++ b/target/linux/generic/backport-6.1/301-v6.9-kernel.h-removed-REPEAT_BYTE-from-kernel.h.patch @@ -0,0 +1,161 @@ +From 66a5c40f60f5d88ad8d47ba6a4ba05892853fa1f Mon Sep 17 00:00:00 2001 +From: Tanzir Hasan +Date: Tue, 26 Dec 2023 18:00:00 +0000 +Subject: [PATCH] kernel.h: removed REPEAT_BYTE from kernel.h + +This patch creates wordpart.h and includes it in asm/word-at-a-time.h +for all architectures. WORD_AT_A_TIME_CONSTANTS depends on kernel.h +because of REPEAT_BYTE. Moving this to another header and including it +where necessary allows us to not include the bloated kernel.h. Making +this implicit dependency on REPEAT_BYTE explicit allows for later +improvements in the lib/string.c inclusion list. + +Suggested-by: Al Viro +Suggested-by: Andy Shevchenko +Signed-off-by: Tanzir Hasan +Reviewed-by: Andy Shevchenko +Link: https://lore.kernel.org/r/20231226-libstringheader-v6-1-80aa08c7652c@google.com +Signed-off-by: Kees Cook +--- + arch/arm/include/asm/word-at-a-time.h | 3 ++- + arch/arm64/include/asm/word-at-a-time.h | 3 ++- + arch/powerpc/include/asm/word-at-a-time.h | 4 ++-- + arch/riscv/include/asm/word-at-a-time.h | 3 ++- + arch/s390/include/asm/word-at-a-time.h | 3 ++- + arch/sh/include/asm/word-at-a-time.h | 2 ++ + arch/x86/include/asm/word-at-a-time.h | 3 ++- + arch/x86/kvm/mmu/mmu.c | 1 + + fs/namei.c | 2 +- + include/asm-generic/word-at-a-time.h | 3 ++- + include/linux/kernel.h | 8 -------- + include/linux/wordpart.h | 13 +++++++++++++ + 12 files changed, 31 insertions(+), 17 deletions(-) + create mode 100644 include/linux/wordpart.h + +--- a/arch/arm/include/asm/word-at-a-time.h ++++ b/arch/arm/include/asm/word-at-a-time.h +@@ -8,7 +8,8 @@ + * Little-endian word-at-a-time zero byte handling. + * Heavily based on the x86 algorithm. + */ +-#include ++#include ++#include + + struct word_at_a_time { + const unsigned long one_bits, high_bits; +--- a/arch/arm64/include/asm/word-at-a-time.h ++++ b/arch/arm64/include/asm/word-at-a-time.h +@@ -9,7 +9,8 @@ + + #ifndef __AARCH64EB__ + +-#include ++#include ++#include + + struct word_at_a_time { + const unsigned long one_bits, high_bits; +--- a/arch/powerpc/include/asm/word-at-a-time.h ++++ b/arch/powerpc/include/asm/word-at-a-time.h +@@ -4,8 +4,8 @@ + /* + * Word-at-a-time interfaces for PowerPC. + */ +- +-#include ++#include ++#include + #include + #include + +--- a/arch/sh/include/asm/word-at-a-time.h ++++ b/arch/sh/include/asm/word-at-a-time.h +@@ -5,6 +5,8 @@ + #ifdef CONFIG_CPU_BIG_ENDIAN + # include + #else ++#include ++#include + /* + * Little-endian version cribbed from x86. + */ +--- a/arch/x86/include/asm/word-at-a-time.h ++++ b/arch/x86/include/asm/word-at-a-time.h +@@ -2,7 +2,8 @@ + #ifndef _ASM_WORD_AT_A_TIME_H + #define _ASM_WORD_AT_A_TIME_H + +-#include ++#include ++#include + + /* + * This is largely generic for little-endian machines, but the +--- a/arch/x86/kvm/mmu/mmu.c ++++ b/arch/x86/kvm/mmu/mmu.c +@@ -44,6 +44,7 @@ + #include + #include + #include ++#include + + #include + #include +--- a/fs/namei.c ++++ b/fs/namei.c +@@ -17,8 +17,8 @@ + + #include + #include +-#include + #include ++#include + #include + #include + #include +--- a/include/asm-generic/word-at-a-time.h ++++ b/include/asm-generic/word-at-a-time.h +@@ -2,7 +2,8 @@ + #ifndef _ASM_WORD_AT_A_TIME_H + #define _ASM_WORD_AT_A_TIME_H + +-#include ++#include ++#include + #include + + #ifdef __BIG_ENDIAN +--- a/include/linux/kernel.h ++++ b/include/linux/kernel.h +@@ -36,14 +36,6 @@ + + #define STACK_MAGIC 0xdeadbeef + +-/** +- * REPEAT_BYTE - repeat the value @x multiple times as an unsigned long value +- * @x: value to repeat +- * +- * NOTE: @x is not checked for > 0xff; larger values produce odd results. +- */ +-#define REPEAT_BYTE(x) ((~0ul / 0xff) * (x)) +- + /* generic data direction definitions */ + #define READ 0 + #define WRITE 1 +--- /dev/null ++++ b/include/linux/wordpart.h +@@ -0,0 +1,13 @@ ++/* SPDX-License-Identifier: GPL-2.0 */ ++ ++#ifndef _LINUX_WORDPART_H ++#define _LINUX_WORDPART_H ++/** ++ * REPEAT_BYTE - repeat the value @x multiple times as an unsigned long value ++ * @x: value to repeat ++ * ++ * NOTE: @x is not checked for > 0xff; larger values produce odd results. ++ */ ++#define REPEAT_BYTE(x) ((~0ul / 0xff) * (x)) ++ ++#endif // _LINUX_WORDPART_H diff --git a/target/linux/generic/backport-6.1/302-v6.9-kernel.h-Move-upper_-_bits-and-lower_-_bits-to-wordp.patch b/target/linux/generic/backport-6.1/302-v6.9-kernel.h-Move-upper_-_bits-and-lower_-_bits-to-wordp.patch new file mode 100644 index 000000000..9bbd51585 --- /dev/null +++ b/target/linux/generic/backport-6.1/302-v6.9-kernel.h-Move-upper_-_bits-and-lower_-_bits-to-wordp.patch @@ -0,0 +1,107 @@ +From adeb04362d74188c1e22ccb824b15a0a7b3de2f4 Mon Sep 17 00:00:00 2001 +From: Andy Shevchenko +Date: Wed, 14 Feb 2024 19:26:32 +0200 +Subject: [PATCH] kernel.h: Move upper_*_bits() and lower_*_bits() to + wordpart.h + +The wordpart.h header is collecting APIs related to the handling +parts of the word (usually in byte granularity). The upper_*_bits() +and lower_*_bits() are good candidates to be moved to there. + +This helps to clean up header dependency hell with regard to kernel.h +as the latter gathers completely unrelated stuff together and slows +down compilation (especially when it's included into other header). + +Signed-off-by: Andy Shevchenko +Link: https://lore.kernel.org/r/20240214172752.3605073-1-andriy.shevchenko@linux.intel.com +Reviewed-by: Randy Dunlap +Signed-off-by: Kees Cook +--- + include/linux/kernel.h | 30 ++---------------------------- + include/linux/wordpart.h | 29 +++++++++++++++++++++++++++++ + 2 files changed, 31 insertions(+), 28 deletions(-) + +--- a/include/linux/kernel.h ++++ b/include/linux/kernel.h +@@ -30,6 +30,8 @@ + #include + #include + #include ++#include ++ + #include + + #include +@@ -55,34 +57,6 @@ + } \ + ) + +-/** +- * upper_32_bits - return bits 32-63 of a number +- * @n: the number we're accessing +- * +- * A basic shift-right of a 64- or 32-bit quantity. Use this to suppress +- * the "right shift count >= width of type" warning when that quantity is +- * 32-bits. +- */ +-#define upper_32_bits(n) ((u32)(((n) >> 16) >> 16)) +- +-/** +- * lower_32_bits - return bits 0-31 of a number +- * @n: the number we're accessing +- */ +-#define lower_32_bits(n) ((u32)((n) & 0xffffffff)) +- +-/** +- * upper_16_bits - return bits 16-31 of a number +- * @n: the number we're accessing +- */ +-#define upper_16_bits(n) ((u16)((n) >> 16)) +- +-/** +- * lower_16_bits - return bits 0-15 of a number +- * @n: the number we're accessing +- */ +-#define lower_16_bits(n) ((u16)((n) & 0xffff)) +- + struct completion; + struct user; + +--- a/include/linux/wordpart.h ++++ b/include/linux/wordpart.h +@@ -2,6 +2,35 @@ + + #ifndef _LINUX_WORDPART_H + #define _LINUX_WORDPART_H ++ ++/** ++ * upper_32_bits - return bits 32-63 of a number ++ * @n: the number we're accessing ++ * ++ * A basic shift-right of a 64- or 32-bit quantity. Use this to suppress ++ * the "right shift count >= width of type" warning when that quantity is ++ * 32-bits. ++ */ ++#define upper_32_bits(n) ((u32)(((n) >> 16) >> 16)) ++ ++/** ++ * lower_32_bits - return bits 0-31 of a number ++ * @n: the number we're accessing ++ */ ++#define lower_32_bits(n) ((u32)((n) & 0xffffffff)) ++ ++/** ++ * upper_16_bits - return bits 16-31 of a number ++ * @n: the number we're accessing ++ */ ++#define upper_16_bits(n) ((u16)((n) >> 16)) ++ ++/** ++ * lower_16_bits - return bits 0-15 of a number ++ * @n: the number we're accessing ++ */ ++#define lower_16_bits(n) ((u16)((n) & 0xffff)) ++ + /** + * REPEAT_BYTE - repeat the value @x multiple times as an unsigned long value + * @x: value to repeat diff --git a/target/linux/generic/backport-6.1/400-v6.9-mtd-rawnand-brcmnand-Support-write-protection-settin.patch b/target/linux/generic/backport-6.1/400-v6.9-mtd-rawnand-brcmnand-Support-write-protection-settin.patch new file mode 100644 index 000000000..ba01bac8d --- /dev/null +++ b/target/linux/generic/backport-6.1/400-v6.9-mtd-rawnand-brcmnand-Support-write-protection-settin.patch @@ -0,0 +1,36 @@ +From 8e7daa85641c9559c113f6b217bdc923397de77c Mon Sep 17 00:00:00 2001 +From: William Zhang +Date: Thu, 22 Feb 2024 19:47:58 -0800 +Subject: [PATCH] mtd: rawnand: brcmnand: Support write protection setting from + dts + +The write protection feature is controlled by the module parameter wp_on +with default set to enabled. But not all the board use this feature +especially in BCMBCA broadband board. And module parameter is not +sufficient as different board can have different option. Add a device +tree property and allow this feature to be configured through the board +dts on per board basis. + +Signed-off-by: William Zhang +Reviewed-by: Florian Fainelli +Reviewed-by: Kamal Dasu +Reviewed-by: David Regan +Signed-off-by: Miquel Raynal +Link: https://lore.kernel.org/linux-mtd/20240223034758.13753-14-william.zhang@broadcom.com +--- + drivers/mtd/nand/raw/brcmnand/brcmnand.c | 4 ++++ + 1 file changed, 4 insertions(+) + +--- a/drivers/mtd/nand/raw/brcmnand/brcmnand.c ++++ b/drivers/mtd/nand/raw/brcmnand/brcmnand.c +@@ -3188,6 +3188,10 @@ int brcmnand_probe(struct platform_devic + /* Disable XOR addressing */ + brcmnand_rmw_reg(ctrl, BRCMNAND_CS_XOR, 0xff, 0, 0); + ++ /* Check if the board connects the WP pin */ ++ if (of_property_read_bool(dn, "brcm,wp-not-connected")) ++ wp_on = 0; ++ + if (ctrl->features & BRCMNAND_HAS_WP) { + /* Permanently disable write protection */ + if (wp_on == 2) diff --git a/target/linux/generic/backport-6.1/408-v6.2-mtd-core-set-ROOT_DEV-for-partitions-marked-as-rootf.patch b/target/linux/generic/backport-6.1/408-v6.2-mtd-core-set-ROOT_DEV-for-partitions-marked-as-rootf.patch index 4de4c7ebd..5a5e11c8f 100644 --- a/target/linux/generic/backport-6.1/408-v6.2-mtd-core-set-ROOT_DEV-for-partitions-marked-as-rootf.patch +++ b/target/linux/generic/backport-6.1/408-v6.2-mtd-core-set-ROOT_DEV-for-partitions-marked-as-rootf.patch @@ -10,7 +10,7 @@ This adds support for "linux,rootfs" binding that is used to mark flash partition containing rootfs. It's useful for devices using device tree that don't have bootloader passing root info in cmdline. -Signed-off-by: Rafa? Mi?ecki +Signed-off-by: RafaÅ‚ MiÅ‚ecki Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20221022211318.32009-2-zajec5@gmail.com --- diff --git a/target/linux/generic/backport-6.1/410-v6.2-mtd-spi-nor-add-generic-flash-driver.patch b/target/linux/generic/backport-6.1/410-v6.2-mtd-spi-nor-add-generic-flash-driver.patch new file mode 100644 index 000000000..4978dcedb --- /dev/null +++ b/target/linux/generic/backport-6.1/410-v6.2-mtd-spi-nor-add-generic-flash-driver.patch @@ -0,0 +1,117 @@ +From 773bbe10449731c9525457873e0c2342e5cf883b Mon Sep 17 00:00:00 2001 +From: Michael Walle +Date: Thu, 11 Aug 2022 00:06:53 +0200 +Subject: [PATCH] mtd: spi-nor: add generic flash driver + +Our SFDP parsing is everything we need to support all basic operations +of a flash device. If the flash isn't found in our in-kernel flash +database, gracefully fall back to a driver described solely by its SFDP +tables. + +Signed-off-by: Michael Walle +Signed-off-by: Tudor Ambarus +Tested-by: Tudor Ambarus +Reviewed-by: Takahiro Kuwano +Link: https://lore.kernel.org/r/20220810220654.1297699-7-michael@walle.cc +--- + drivers/mtd/spi-nor/core.c | 26 ++++++++++++++++++++++++-- + drivers/mtd/spi-nor/core.h | 1 + + drivers/mtd/spi-nor/sfdp.c | 27 +++++++++++++++++++++++++++ + 3 files changed, 52 insertions(+), 2 deletions(-) + +--- a/drivers/mtd/spi-nor/core.c ++++ b/drivers/mtd/spi-nor/core.c +@@ -1636,6 +1636,16 @@ static const struct spi_nor_manufacturer + &spi_nor_xmc, + }; + ++static const struct flash_info spi_nor_generic_flash = { ++ .name = "spi-nor-generic", ++ /* ++ * JESD216 rev A doesn't specify the page size, therefore we need a ++ * sane default. ++ */ ++ .page_size = 256, ++ .parse_sfdp = true, ++}; ++ + static const struct flash_info *spi_nor_match_id(struct spi_nor *nor, + const u8 *id) + { +@@ -1669,6 +1679,14 @@ static const struct flash_info *spi_nor_ + } + + info = spi_nor_match_id(nor, id); ++ ++ /* Fallback to a generic flash described only by its SFDP data. */ ++ if (!info) { ++ ret = spi_nor_check_sfdp_signature(nor); ++ if (!ret) ++ info = &spi_nor_generic_flash; ++ } ++ + if (!info) { + dev_err(nor->dev, "unrecognized JEDEC id bytes: %*ph\n", + SPI_NOR_MAX_ID_LEN, id); +@@ -2105,8 +2123,12 @@ static int spi_nor_select_pp(struct spi_ + * spi_nor_select_uniform_erase() - select optimum uniform erase type + * @map: the erase map of the SPI NOR + * @wanted_size: the erase type size to search for. Contains the value of +- * info->sector_size or of the "small sector" size in case +- * CONFIG_MTD_SPI_NOR_USE_4K_SECTORS is defined. ++ * info->sector_size, the "small sector" size in case ++ * CONFIG_MTD_SPI_NOR_USE_4K_SECTORS is defined or 0 if ++ * there is no information about the sector size. The ++ * latter is the case if the flash parameters are parsed ++ * solely by SFDP, then the largest supported erase type ++ * is selected. + * + * Once the optimum uniform sector erase command is found, disable all the + * other. +--- a/drivers/mtd/spi-nor/core.h ++++ b/drivers/mtd/spi-nor/core.h +@@ -708,6 +708,8 @@ int spi_nor_controller_ops_read_reg(stru + int spi_nor_controller_ops_write_reg(struct spi_nor *nor, u8 opcode, + const u8 *buf, size_t len); + ++int spi_nor_check_sfdp_signature(struct spi_nor *nor); ++ + static inline struct spi_nor *mtd_to_spi_nor(struct mtd_info *mtd) + { + return container_of(mtd, struct spi_nor, mtd); +--- a/drivers/mtd/spi-nor/sfdp.c ++++ b/drivers/mtd/spi-nor/sfdp.c +@@ -1250,6 +1250,33 @@ static void spi_nor_post_sfdp_fixups(str + } + + /** ++ * spi_nor_check_sfdp_signature() - check for a valid SFDP signature ++ * @nor: pointer to a 'struct spi_nor' ++ * ++ * Used to detect if the flash supports the RDSFDP command as well as the ++ * presence of a valid SFDP table. ++ * ++ * Return: 0 on success, -errno otherwise. ++ */ ++int spi_nor_check_sfdp_signature(struct spi_nor *nor) ++{ ++ u32 signature; ++ int err; ++ ++ /* Get the SFDP header. */ ++ err = spi_nor_read_sfdp_dma_unsafe(nor, 0, sizeof(signature), ++ &signature); ++ if (err < 0) ++ return err; ++ ++ /* Check the SFDP signature. */ ++ if (le32_to_cpu(signature) != SFDP_SIGNATURE) ++ return -EINVAL; ++ ++ return 0; ++} ++ ++/** + * spi_nor_parse_sfdp() - parse the Serial Flash Discoverable Parameters. + * @nor: pointer to a 'struct spi_nor' + * diff --git a/target/linux/generic/pending-6.1/110-v6.3-0001-spidev-Add-Silicon-Labs-EM3581-device-compatible.patch b/target/linux/generic/backport-6.1/412-v6.3-01-spidev-Add-Silicon-Labs-EM3581-device-compatible.patch similarity index 74% rename from target/linux/generic/pending-6.1/110-v6.3-0001-spidev-Add-Silicon-Labs-EM3581-device-compatible.patch rename to target/linux/generic/backport-6.1/412-v6.3-01-spidev-Add-Silicon-Labs-EM3581-device-compatible.patch index ebeeae2f8..cc05fa6b7 100644 --- a/target/linux/generic/pending-6.1/110-v6.3-0001-spidev-Add-Silicon-Labs-EM3581-device-compatible.patch +++ b/target/linux/generic/backport-6.1/412-v6.3-01-spidev-Add-Silicon-Labs-EM3581-device-compatible.patch @@ -1,13 +1,13 @@ -From f7982c726e02001afc19052fe48f642dfcbc00b2 Mon Sep 17 00:00:00 2001 +From c67d90e058550403a3e6f9b05bfcdcfa12b1815c Mon Sep 17 00:00:00 2001 From: Vincent Tremblay -Date: Mon, 26 Dec 2022 21:10:37 -0500 -Subject: [PATCH 1/2] spidev: Add Silicon Labs EM3581 device compatible +Date: Mon, 26 Dec 2022 21:35:48 -0500 +Subject: [PATCH] spidev: Add Silicon Labs EM3581 device compatible Add compatible string for Silicon Labs EM3581 device. -Note: This patch is adapted from a patch submitted to the for-next branch (v6.3). - Signed-off-by: Vincent Tremblay +Link: https://lore.kernel.org/r/20221227023550.569547-2-vincent@vtremblay.dev +Signed-off-by: Mark Brown --- drivers/spi/spidev.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/target/linux/generic/pending-6.1/110-v6.3-0002-spidev-Add-Silicon-Labs-SI3210-device-compatible.patch b/target/linux/generic/backport-6.1/412-v6.3-02-spidev-Add-Silicon-Labs-SI3210-device-compatible.patch similarity index 74% rename from target/linux/generic/pending-6.1/110-v6.3-0002-spidev-Add-Silicon-Labs-SI3210-device-compatible.patch rename to target/linux/generic/backport-6.1/412-v6.3-02-spidev-Add-Silicon-Labs-SI3210-device-compatible.patch index db5b5800f..59d025e08 100644 --- a/target/linux/generic/pending-6.1/110-v6.3-0002-spidev-Add-Silicon-Labs-SI3210-device-compatible.patch +++ b/target/linux/generic/backport-6.1/412-v6.3-02-spidev-Add-Silicon-Labs-SI3210-device-compatible.patch @@ -1,13 +1,13 @@ -From 536581825219e97fa2ae0c4de35605d2f6311416 Mon Sep 17 00:00:00 2001 +From 6c9d1fd52956c3148e847a214bae9102b1811de5 Mon Sep 17 00:00:00 2001 From: Vincent Tremblay -Date: Tue, 27 Dec 2022 09:00:58 -0500 -Subject: [PATCH 2/2] spidev: Add Silicon Labs SI3210 device compatible +Date: Tue, 27 Dec 2022 09:10:08 -0500 +Subject: [PATCH] spidev: Add Silicon Labs SI3210 device compatible Add compatible string for Silicon Labs SI3210 device. -Note: This patch is adapted from a patch submitted to the for-next branch (v6.3). - Signed-off-by: Vincent Tremblay +Link: https://lore.kernel.org/r/20221227141011.111410-2-vincent@vtremblay.dev +Signed-off-by: Mark Brown --- drivers/spi/spidev.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/target/linux/generic/backport-6.1/424-v6.3-mtd-ubi-wire-up-parent-MTD-device.patch b/target/linux/generic/backport-6.1/424-v6.3-mtd-ubi-wire-up-parent-MTD-device.patch new file mode 100644 index 000000000..657404196 --- /dev/null +++ b/target/linux/generic/backport-6.1/424-v6.3-mtd-ubi-wire-up-parent-MTD-device.patch @@ -0,0 +1,33 @@ +From 1ecf9e390452e73a362ea7fbde8f3f0db83de856 Mon Sep 17 00:00:00 2001 +From: Daniel Golle +Date: Thu, 22 Dec 2022 19:33:04 +0000 +Subject: [PATCH] mtd: ubi: wire-up parent MTD device + +Wire up the device parent pointer of UBI devices to their lower MTD +device, typically an MTD partition or whole-chip device. + +The most noticeable change is that in sysfs, previously ubi devices +would be could in /sys/devices/virtual/ubi while after this change they +would be correctly attached to their parent MTD device, e.g. + +/sys/devices/platform/1100d000.spi/spi_master/spi1/spi1.0/mtd/mtd2/ubi0. + +Locating UBI devices using /sys/class/ubi/ of course still works as +well. + +Signed-off-by: Daniel Golle +Signed-off-by: Richard Weinberger +--- + drivers/mtd/ubi/build.c | 1 + + 1 file changed, 1 insertion(+) + +--- a/drivers/mtd/ubi/build.c ++++ b/drivers/mtd/ubi/build.c +@@ -929,6 +929,7 @@ int ubi_attach_mtd_dev(struct mtd_info * + ubi->dev.release = dev_release; + ubi->dev.class = &ubi_class; + ubi->dev.groups = ubi_dev_groups; ++ ubi->dev.parent = &mtd->dev; + + ubi->mtd = mtd; + ubi->ubi_num = ubi_num; diff --git a/target/linux/generic/backport-6.1/425-v6.3-mtd-ubi-block-wire-up-device-parent.patch b/target/linux/generic/backport-6.1/425-v6.3-mtd-ubi-block-wire-up-device-parent.patch new file mode 100644 index 000000000..48bf98611 --- /dev/null +++ b/target/linux/generic/backport-6.1/425-v6.3-mtd-ubi-block-wire-up-device-parent.patch @@ -0,0 +1,49 @@ +From 05b8773ca33253ea562be145cf3145b05ef19f86 Mon Sep 17 00:00:00 2001 +From: Daniel Golle +Date: Thu, 22 Dec 2022 19:33:31 +0000 +Subject: [PATCH] mtd: ubi: block: wire-up device parent + +ubiblock devices were previously only identifyable by their name, but +not connected to their parent UBI volume device e.g. in sysfs. +Properly parent ubiblock device as descendant of a UBI volume device +to reflect device model hierachy. + +Signed-off-by: Daniel Golle +Signed-off-by: Richard Weinberger +--- + drivers/mtd/ubi/block.c | 2 +- + drivers/mtd/ubi/kapi.c | 1 + + include/linux/mtd/ubi.h | 1 + + 3 files changed, 3 insertions(+), 1 deletion(-) + +--- a/drivers/mtd/ubi/block.c ++++ b/drivers/mtd/ubi/block.c +@@ -452,7 +452,7 @@ int ubiblock_create(struct ubi_volume_in + list_add_tail(&dev->list, &ubiblock_devices); + + /* Must be the last step: anyone can call file ops from now on */ +- ret = add_disk(dev->gd); ++ ret = device_add_disk(vi->dev, dev->gd, NULL); + if (ret) + goto out_destroy_wq; + +--- a/drivers/mtd/ubi/kapi.c ++++ b/drivers/mtd/ubi/kapi.c +@@ -79,6 +79,7 @@ void ubi_do_get_volume_info(struct ubi_d + vi->name_len = vol->name_len; + vi->name = vol->name; + vi->cdev = vol->cdev.dev; ++ vi->dev = &vol->dev; + } + + /** +--- a/include/linux/mtd/ubi.h ++++ b/include/linux/mtd/ubi.h +@@ -110,6 +110,7 @@ struct ubi_volume_info { + int name_len; + const char *name; + dev_t cdev; ++ struct device *dev; + }; + + /** diff --git a/target/linux/generic/backport-6.1/424-v6.4-0004-mtd-core-prepare-mtd_otp_nvmem_add-to-handle-EPROBE_.patch b/target/linux/generic/backport-6.1/426-v6.4-0004-mtd-core-prepare-mtd_otp_nvmem_add-to-handle-EPROBE_.patch similarity index 100% rename from target/linux/generic/backport-6.1/424-v6.4-0004-mtd-core-prepare-mtd_otp_nvmem_add-to-handle-EPROBE_.patch rename to target/linux/generic/backport-6.1/426-v6.4-0004-mtd-core-prepare-mtd_otp_nvmem_add-to-handle-EPROBE_.patch diff --git a/target/linux/generic/backport-6.1/702-01-v6.7-net-phy-aquantia-move-to-separate-directory.patch b/target/linux/generic/backport-6.1/702-01-v6.7-net-phy-aquantia-move-to-separate-directory.patch new file mode 100644 index 000000000..0cad6c53d --- /dev/null +++ b/target/linux/generic/backport-6.1/702-01-v6.7-net-phy-aquantia-move-to-separate-directory.patch @@ -0,0 +1,2306 @@ +From d2213db3f49bce8e7a87c8de05b9a091f78f654e Mon Sep 17 00:00:00 2001 +From: Christian Marangi +Date: Tue, 14 Nov 2023 15:08:41 +0100 +Subject: [PATCH 1/3] net: phy: aquantia: move to separate directory + +Move aquantia PHY driver to separate driectory in preparation for +firmware loading support to keep things tidy. + +Signed-off-by: Christian Marangi +Reviewed-by: Andrew Lunn +Signed-off-by: David S. Miller +--- + drivers/net/phy/Kconfig | 5 +---- + drivers/net/phy/Makefile | 6 +----- + drivers/net/phy/aquantia/Kconfig | 5 +++++ + drivers/net/phy/aquantia/Makefile | 6 ++++++ + drivers/net/phy/{ => aquantia}/aquantia.h | 0 + drivers/net/phy/{ => aquantia}/aquantia_hwmon.c | 0 + drivers/net/phy/{ => aquantia}/aquantia_main.c | 0 + 7 files changed, 13 insertions(+), 9 deletions(-) + create mode 100644 drivers/net/phy/aquantia/Kconfig + create mode 100644 drivers/net/phy/aquantia/Makefile + rename drivers/net/phy/{ => aquantia}/aquantia.h (100%) + rename drivers/net/phy/{ => aquantia}/aquantia_hwmon.c (100%) + rename drivers/net/phy/{ => aquantia}/aquantia_main.c (100%) + +--- a/drivers/net/phy/Kconfig ++++ b/drivers/net/phy/Kconfig +@@ -90,10 +90,7 @@ config ADIN1100_PHY + Currently supports the: + - ADIN1100 - Robust,Industrial, Low Power 10BASE-T1L Ethernet PHY + +-config AQUANTIA_PHY +- tristate "Aquantia PHYs" +- help +- Currently supports the Aquantia AQ1202, AQ2104, AQR105, AQR405 ++source "drivers/net/phy/aquantia/Kconfig" + + config AX88796B_PHY + tristate "Asix PHYs" +--- a/drivers/net/phy/Makefile ++++ b/drivers/net/phy/Makefile +@@ -33,11 +33,7 @@ obj-y += $(sfp-obj-y) $(sfp-obj-m) + obj-$(CONFIG_ADIN_PHY) += adin.o + obj-$(CONFIG_ADIN1100_PHY) += adin1100.o + obj-$(CONFIG_AMD_PHY) += amd.o +-aquantia-objs += aquantia_main.o +-ifdef CONFIG_HWMON +-aquantia-objs += aquantia_hwmon.o +-endif +-obj-$(CONFIG_AQUANTIA_PHY) += aquantia.o ++obj-$(CONFIG_AQUANTIA_PHY) += aquantia/ + obj-$(CONFIG_AT803X_PHY) += at803x.o + obj-$(CONFIG_AX88796B_PHY) += ax88796b.o + obj-$(CONFIG_BCM54140_PHY) += bcm54140.o +--- /dev/null ++++ b/drivers/net/phy/aquantia/Kconfig +@@ -0,0 +1,5 @@ ++# SPDX-License-Identifier: GPL-2.0-only ++config AQUANTIA_PHY ++ tristate "Aquantia PHYs" ++ help ++ Currently supports the Aquantia AQ1202, AQ2104, AQR105, AQR405 +--- /dev/null ++++ b/drivers/net/phy/aquantia/Makefile +@@ -0,0 +1,6 @@ ++# SPDX-License-Identifier: GPL-2.0 ++aquantia-objs += aquantia_main.o ++ifdef CONFIG_HWMON ++aquantia-objs += aquantia_hwmon.o ++endif ++obj-$(CONFIG_AQUANTIA_PHY) += aquantia.o +--- a/drivers/net/phy/aquantia.h ++++ /dev/null +@@ -1,16 +0,0 @@ +-/* SPDX-License-Identifier: GPL-2.0 */ +-/* HWMON driver for Aquantia PHY +- * +- * Author: Nikita Yushchenko +- * Author: Andrew Lunn +- * Author: Heiner Kallweit +- */ +- +-#include +-#include +- +-#if IS_REACHABLE(CONFIG_HWMON) +-int aqr_hwmon_probe(struct phy_device *phydev); +-#else +-static inline int aqr_hwmon_probe(struct phy_device *phydev) { return 0; } +-#endif +--- /dev/null ++++ b/drivers/net/phy/aquantia/aquantia.h +@@ -0,0 +1,16 @@ ++/* SPDX-License-Identifier: GPL-2.0 */ ++/* HWMON driver for Aquantia PHY ++ * ++ * Author: Nikita Yushchenko ++ * Author: Andrew Lunn ++ * Author: Heiner Kallweit ++ */ ++ ++#include ++#include ++ ++#if IS_REACHABLE(CONFIG_HWMON) ++int aqr_hwmon_probe(struct phy_device *phydev); ++#else ++static inline int aqr_hwmon_probe(struct phy_device *phydev) { return 0; } ++#endif +--- /dev/null ++++ b/drivers/net/phy/aquantia/aquantia_hwmon.c +@@ -0,0 +1,250 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* HWMON driver for Aquantia PHY ++ * ++ * Author: Nikita Yushchenko ++ * Author: Andrew Lunn ++ * Author: Heiner Kallweit ++ */ ++ ++#include ++#include ++#include ++#include ++ ++#include "aquantia.h" ++ ++/* Vendor specific 1, MDIO_MMD_VEND2 */ ++#define VEND1_THERMAL_PROV_HIGH_TEMP_FAIL 0xc421 ++#define VEND1_THERMAL_PROV_LOW_TEMP_FAIL 0xc422 ++#define VEND1_THERMAL_PROV_HIGH_TEMP_WARN 0xc423 ++#define VEND1_THERMAL_PROV_LOW_TEMP_WARN 0xc424 ++#define VEND1_THERMAL_STAT1 0xc820 ++#define VEND1_THERMAL_STAT2 0xc821 ++#define VEND1_THERMAL_STAT2_VALID BIT(0) ++#define VEND1_GENERAL_STAT1 0xc830 ++#define VEND1_GENERAL_STAT1_HIGH_TEMP_FAIL BIT(14) ++#define VEND1_GENERAL_STAT1_LOW_TEMP_FAIL BIT(13) ++#define VEND1_GENERAL_STAT1_HIGH_TEMP_WARN BIT(12) ++#define VEND1_GENERAL_STAT1_LOW_TEMP_WARN BIT(11) ++ ++#if IS_REACHABLE(CONFIG_HWMON) ++ ++static umode_t aqr_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_min_alarm: ++ case hwmon_temp_max_alarm: ++ case hwmon_temp_lcrit_alarm: ++ case hwmon_temp_crit_alarm: ++ return 0444; ++ case hwmon_temp_min: ++ case hwmon_temp_max: ++ case hwmon_temp_lcrit: ++ case hwmon_temp_crit: ++ return 0644; ++ default: ++ return 0; ++ } ++} ++ ++static int aqr_hwmon_get(struct phy_device *phydev, int reg, long *value) ++{ ++ int temp = phy_read_mmd(phydev, MDIO_MMD_VEND1, reg); ++ ++ if (temp < 0) ++ return temp; ++ ++ /* 16 bit value is 2's complement with LSB = 1/256th degree Celsius */ ++ *value = (s16)temp * 1000 / 256; ++ ++ return 0; ++} ++ ++static int aqr_hwmon_set(struct phy_device *phydev, int reg, long value) ++{ ++ int temp; ++ ++ if (value >= 128000 || value < -128000) ++ return -ERANGE; ++ ++ temp = value * 256 / 1000; ++ ++ /* temp is in s16 range and we're interested in lower 16 bits only */ ++ return phy_write_mmd(phydev, MDIO_MMD_VEND1, reg, (u16)temp); ++} ++ ++static int aqr_hwmon_test_bit(struct phy_device *phydev, int reg, int bit) ++{ ++ int val = phy_read_mmd(phydev, MDIO_MMD_VEND1, reg); ++ ++ if (val < 0) ++ return val; ++ ++ return !!(val & bit); ++} ++ ++static int aqr_hwmon_status1(struct phy_device *phydev, int bit, long *value) ++{ ++ int val = aqr_hwmon_test_bit(phydev, VEND1_GENERAL_STAT1, bit); ++ ++ if (val < 0) ++ return val; ++ ++ *value = val; ++ ++ return 0; ++} ++ ++static int aqr_hwmon_read(struct device *dev, enum hwmon_sensor_types type, ++ u32 attr, int channel, long *value) ++{ ++ struct phy_device *phydev = dev_get_drvdata(dev); ++ int reg; ++ ++ if (type != hwmon_temp) ++ return -EOPNOTSUPP; ++ ++ switch (attr) { ++ case hwmon_temp_input: ++ reg = aqr_hwmon_test_bit(phydev, VEND1_THERMAL_STAT2, ++ VEND1_THERMAL_STAT2_VALID); ++ if (reg < 0) ++ return reg; ++ if (!reg) ++ return -EBUSY; ++ ++ return aqr_hwmon_get(phydev, VEND1_THERMAL_STAT1, value); ++ ++ case hwmon_temp_lcrit: ++ return aqr_hwmon_get(phydev, VEND1_THERMAL_PROV_LOW_TEMP_FAIL, ++ value); ++ case hwmon_temp_min: ++ return aqr_hwmon_get(phydev, VEND1_THERMAL_PROV_LOW_TEMP_WARN, ++ value); ++ case hwmon_temp_max: ++ return aqr_hwmon_get(phydev, VEND1_THERMAL_PROV_HIGH_TEMP_WARN, ++ value); ++ case hwmon_temp_crit: ++ return aqr_hwmon_get(phydev, VEND1_THERMAL_PROV_HIGH_TEMP_FAIL, ++ value); ++ case hwmon_temp_lcrit_alarm: ++ return aqr_hwmon_status1(phydev, ++ VEND1_GENERAL_STAT1_LOW_TEMP_FAIL, ++ value); ++ case hwmon_temp_min_alarm: ++ return aqr_hwmon_status1(phydev, ++ VEND1_GENERAL_STAT1_LOW_TEMP_WARN, ++ value); ++ case hwmon_temp_max_alarm: ++ return aqr_hwmon_status1(phydev, ++ VEND1_GENERAL_STAT1_HIGH_TEMP_WARN, ++ value); ++ case hwmon_temp_crit_alarm: ++ return aqr_hwmon_status1(phydev, ++ VEND1_GENERAL_STAT1_HIGH_TEMP_FAIL, ++ value); ++ default: ++ return -EOPNOTSUPP; ++ } ++} ++ ++static int aqr_hwmon_write(struct device *dev, enum hwmon_sensor_types type, ++ u32 attr, int channel, long value) ++{ ++ struct phy_device *phydev = dev_get_drvdata(dev); ++ ++ if (type != hwmon_temp) ++ return -EOPNOTSUPP; ++ ++ switch (attr) { ++ case hwmon_temp_lcrit: ++ return aqr_hwmon_set(phydev, VEND1_THERMAL_PROV_LOW_TEMP_FAIL, ++ value); ++ case hwmon_temp_min: ++ return aqr_hwmon_set(phydev, VEND1_THERMAL_PROV_LOW_TEMP_WARN, ++ value); ++ case hwmon_temp_max: ++ return aqr_hwmon_set(phydev, VEND1_THERMAL_PROV_HIGH_TEMP_WARN, ++ value); ++ case hwmon_temp_crit: ++ return aqr_hwmon_set(phydev, VEND1_THERMAL_PROV_HIGH_TEMP_FAIL, ++ value); ++ default: ++ return -EOPNOTSUPP; ++ } ++} ++ ++static const struct hwmon_ops aqr_hwmon_ops = { ++ .is_visible = aqr_hwmon_is_visible, ++ .read = aqr_hwmon_read, ++ .write = aqr_hwmon_write, ++}; ++ ++static u32 aqr_hwmon_chip_config[] = { ++ HWMON_C_REGISTER_TZ, ++ 0, ++}; ++ ++static const struct hwmon_channel_info aqr_hwmon_chip = { ++ .type = hwmon_chip, ++ .config = aqr_hwmon_chip_config, ++}; ++ ++static u32 aqr_hwmon_temp_config[] = { ++ HWMON_T_INPUT | ++ HWMON_T_MAX | HWMON_T_MIN | ++ HWMON_T_MAX_ALARM | HWMON_T_MIN_ALARM | ++ HWMON_T_CRIT | HWMON_T_LCRIT | ++ HWMON_T_CRIT_ALARM | HWMON_T_LCRIT_ALARM, ++ 0, ++}; ++ ++static const struct hwmon_channel_info aqr_hwmon_temp = { ++ .type = hwmon_temp, ++ .config = aqr_hwmon_temp_config, ++}; ++ ++static const struct hwmon_channel_info *aqr_hwmon_info[] = { ++ &aqr_hwmon_chip, ++ &aqr_hwmon_temp, ++ NULL, ++}; ++ ++static const struct hwmon_chip_info aqr_hwmon_chip_info = { ++ .ops = &aqr_hwmon_ops, ++ .info = aqr_hwmon_info, ++}; ++ ++int aqr_hwmon_probe(struct phy_device *phydev) ++{ ++ struct device *dev = &phydev->mdio.dev; ++ struct device *hwmon_dev; ++ char *hwmon_name; ++ int i, j; ++ ++ hwmon_name = devm_kstrdup(dev, dev_name(dev), GFP_KERNEL); ++ if (!hwmon_name) ++ return -ENOMEM; ++ ++ for (i = j = 0; hwmon_name[i]; i++) { ++ if (isalnum(hwmon_name[i])) { ++ if (i != j) ++ hwmon_name[j] = hwmon_name[i]; ++ j++; ++ } ++ } ++ hwmon_name[j] = '\0'; ++ ++ hwmon_dev = devm_hwmon_device_register_with_info(dev, hwmon_name, ++ phydev, &aqr_hwmon_chip_info, NULL); ++ ++ return PTR_ERR_OR_ZERO(hwmon_dev); ++} ++ ++#endif +--- /dev/null ++++ b/drivers/net/phy/aquantia/aquantia_main.c +@@ -0,0 +1,842 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Driver for Aquantia PHY ++ * ++ * Author: Shaohui Xie ++ * ++ * Copyright 2015 Freescale Semiconductor, Inc. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++ ++#include "aquantia.h" ++ ++#define PHY_ID_AQ1202 0x03a1b445 ++#define PHY_ID_AQ2104 0x03a1b460 ++#define PHY_ID_AQR105 0x03a1b4a2 ++#define PHY_ID_AQR106 0x03a1b4d0 ++#define PHY_ID_AQR107 0x03a1b4e0 ++#define PHY_ID_AQCS109 0x03a1b5c2 ++#define PHY_ID_AQR405 0x03a1b4b0 ++#define PHY_ID_AQR113C 0x31c31c12 ++ ++#define MDIO_PHYXS_VEND_IF_STATUS 0xe812 ++#define MDIO_PHYXS_VEND_IF_STATUS_TYPE_MASK GENMASK(7, 3) ++#define MDIO_PHYXS_VEND_IF_STATUS_TYPE_KR 0 ++#define MDIO_PHYXS_VEND_IF_STATUS_TYPE_KX 1 ++#define MDIO_PHYXS_VEND_IF_STATUS_TYPE_XFI 2 ++#define MDIO_PHYXS_VEND_IF_STATUS_TYPE_USXGMII 3 ++#define MDIO_PHYXS_VEND_IF_STATUS_TYPE_XAUI 4 ++#define MDIO_PHYXS_VEND_IF_STATUS_TYPE_SGMII 6 ++#define MDIO_PHYXS_VEND_IF_STATUS_TYPE_RXAUI 7 ++#define MDIO_PHYXS_VEND_IF_STATUS_TYPE_OCSGMII 10 ++ ++#define MDIO_AN_VEND_PROV 0xc400 ++#define MDIO_AN_VEND_PROV_1000BASET_FULL BIT(15) ++#define MDIO_AN_VEND_PROV_1000BASET_HALF BIT(14) ++#define MDIO_AN_VEND_PROV_5000BASET_FULL BIT(11) ++#define MDIO_AN_VEND_PROV_2500BASET_FULL BIT(10) ++#define MDIO_AN_VEND_PROV_DOWNSHIFT_EN BIT(4) ++#define MDIO_AN_VEND_PROV_DOWNSHIFT_MASK GENMASK(3, 0) ++#define MDIO_AN_VEND_PROV_DOWNSHIFT_DFLT 4 ++ ++#define MDIO_AN_TX_VEND_STATUS1 0xc800 ++#define MDIO_AN_TX_VEND_STATUS1_RATE_MASK GENMASK(3, 1) ++#define MDIO_AN_TX_VEND_STATUS1_10BASET 0 ++#define MDIO_AN_TX_VEND_STATUS1_100BASETX 1 ++#define MDIO_AN_TX_VEND_STATUS1_1000BASET 2 ++#define MDIO_AN_TX_VEND_STATUS1_10GBASET 3 ++#define MDIO_AN_TX_VEND_STATUS1_2500BASET 4 ++#define MDIO_AN_TX_VEND_STATUS1_5000BASET 5 ++#define MDIO_AN_TX_VEND_STATUS1_FULL_DUPLEX BIT(0) ++ ++#define MDIO_AN_TX_VEND_INT_STATUS1 0xcc00 ++#define MDIO_AN_TX_VEND_INT_STATUS1_DOWNSHIFT BIT(1) ++ ++#define MDIO_AN_TX_VEND_INT_STATUS2 0xcc01 ++#define MDIO_AN_TX_VEND_INT_STATUS2_MASK BIT(0) ++ ++#define MDIO_AN_TX_VEND_INT_MASK2 0xd401 ++#define MDIO_AN_TX_VEND_INT_MASK2_LINK BIT(0) ++ ++#define MDIO_AN_RX_LP_STAT1 0xe820 ++#define MDIO_AN_RX_LP_STAT1_1000BASET_FULL BIT(15) ++#define MDIO_AN_RX_LP_STAT1_1000BASET_HALF BIT(14) ++#define MDIO_AN_RX_LP_STAT1_SHORT_REACH BIT(13) ++#define MDIO_AN_RX_LP_STAT1_AQRATE_DOWNSHIFT BIT(12) ++#define MDIO_AN_RX_LP_STAT1_AQ_PHY BIT(2) ++ ++#define MDIO_AN_RX_LP_STAT4 0xe823 ++#define MDIO_AN_RX_LP_STAT4_FW_MAJOR GENMASK(15, 8) ++#define MDIO_AN_RX_LP_STAT4_FW_MINOR GENMASK(7, 0) ++ ++#define MDIO_AN_RX_VEND_STAT3 0xe832 ++#define MDIO_AN_RX_VEND_STAT3_AFR BIT(0) ++ ++/* MDIO_MMD_C22EXT */ ++#define MDIO_C22EXT_STAT_SGMII_RX_GOOD_FRAMES 0xd292 ++#define MDIO_C22EXT_STAT_SGMII_RX_BAD_FRAMES 0xd294 ++#define MDIO_C22EXT_STAT_SGMII_RX_FALSE_CARRIER 0xd297 ++#define MDIO_C22EXT_STAT_SGMII_TX_GOOD_FRAMES 0xd313 ++#define MDIO_C22EXT_STAT_SGMII_TX_BAD_FRAMES 0xd315 ++#define MDIO_C22EXT_STAT_SGMII_TX_FALSE_CARRIER 0xd317 ++#define MDIO_C22EXT_STAT_SGMII_TX_COLLISIONS 0xd318 ++#define MDIO_C22EXT_STAT_SGMII_TX_LINE_COLLISIONS 0xd319 ++#define MDIO_C22EXT_STAT_SGMII_TX_FRAME_ALIGN_ERR 0xd31a ++#define MDIO_C22EXT_STAT_SGMII_TX_RUNT_FRAMES 0xd31b ++ ++/* Vendor specific 1, MDIO_MMD_VEND1 */ ++#define VEND1_GLOBAL_FW_ID 0x0020 ++#define VEND1_GLOBAL_FW_ID_MAJOR GENMASK(15, 8) ++#define VEND1_GLOBAL_FW_ID_MINOR GENMASK(7, 0) ++ ++#define VEND1_GLOBAL_GEN_STAT2 0xc831 ++#define VEND1_GLOBAL_GEN_STAT2_OP_IN_PROG BIT(15) ++ ++/* The following registers all have similar layouts; first the registers... */ ++#define VEND1_GLOBAL_CFG_10M 0x0310 ++#define VEND1_GLOBAL_CFG_100M 0x031b ++#define VEND1_GLOBAL_CFG_1G 0x031c ++#define VEND1_GLOBAL_CFG_2_5G 0x031d ++#define VEND1_GLOBAL_CFG_5G 0x031e ++#define VEND1_GLOBAL_CFG_10G 0x031f ++/* ...and now the fields */ ++#define VEND1_GLOBAL_CFG_RATE_ADAPT GENMASK(8, 7) ++#define VEND1_GLOBAL_CFG_RATE_ADAPT_NONE 0 ++#define VEND1_GLOBAL_CFG_RATE_ADAPT_USX 1 ++#define VEND1_GLOBAL_CFG_RATE_ADAPT_PAUSE 2 ++ ++#define VEND1_GLOBAL_RSVD_STAT1 0xc885 ++#define VEND1_GLOBAL_RSVD_STAT1_FW_BUILD_ID GENMASK(7, 4) ++#define VEND1_GLOBAL_RSVD_STAT1_PROV_ID GENMASK(3, 0) ++ ++#define VEND1_GLOBAL_RSVD_STAT9 0xc88d ++#define VEND1_GLOBAL_RSVD_STAT9_MODE GENMASK(7, 0) ++#define VEND1_GLOBAL_RSVD_STAT9_1000BT2 0x23 ++ ++#define VEND1_GLOBAL_INT_STD_STATUS 0xfc00 ++#define VEND1_GLOBAL_INT_VEND_STATUS 0xfc01 ++ ++#define VEND1_GLOBAL_INT_STD_MASK 0xff00 ++#define VEND1_GLOBAL_INT_STD_MASK_PMA1 BIT(15) ++#define VEND1_GLOBAL_INT_STD_MASK_PMA2 BIT(14) ++#define VEND1_GLOBAL_INT_STD_MASK_PCS1 BIT(13) ++#define VEND1_GLOBAL_INT_STD_MASK_PCS2 BIT(12) ++#define VEND1_GLOBAL_INT_STD_MASK_PCS3 BIT(11) ++#define VEND1_GLOBAL_INT_STD_MASK_PHY_XS1 BIT(10) ++#define VEND1_GLOBAL_INT_STD_MASK_PHY_XS2 BIT(9) ++#define VEND1_GLOBAL_INT_STD_MASK_AN1 BIT(8) ++#define VEND1_GLOBAL_INT_STD_MASK_AN2 BIT(7) ++#define VEND1_GLOBAL_INT_STD_MASK_GBE BIT(6) ++#define VEND1_GLOBAL_INT_STD_MASK_ALL BIT(0) ++ ++#define VEND1_GLOBAL_INT_VEND_MASK 0xff01 ++#define VEND1_GLOBAL_INT_VEND_MASK_PMA BIT(15) ++#define VEND1_GLOBAL_INT_VEND_MASK_PCS BIT(14) ++#define VEND1_GLOBAL_INT_VEND_MASK_PHY_XS BIT(13) ++#define VEND1_GLOBAL_INT_VEND_MASK_AN BIT(12) ++#define VEND1_GLOBAL_INT_VEND_MASK_GBE BIT(11) ++#define VEND1_GLOBAL_INT_VEND_MASK_GLOBAL1 BIT(2) ++#define VEND1_GLOBAL_INT_VEND_MASK_GLOBAL2 BIT(1) ++#define VEND1_GLOBAL_INT_VEND_MASK_GLOBAL3 BIT(0) ++ ++/* Sleep and timeout for checking if the Processor-Intensive ++ * MDIO operation is finished ++ */ ++#define AQR107_OP_IN_PROG_SLEEP 1000 ++#define AQR107_OP_IN_PROG_TIMEOUT 100000 ++ ++struct aqr107_hw_stat { ++ const char *name; ++ int reg; ++ int size; ++}; ++ ++#define SGMII_STAT(n, r, s) { n, MDIO_C22EXT_STAT_SGMII_ ## r, s } ++static const struct aqr107_hw_stat aqr107_hw_stats[] = { ++ SGMII_STAT("sgmii_rx_good_frames", RX_GOOD_FRAMES, 26), ++ SGMII_STAT("sgmii_rx_bad_frames", RX_BAD_FRAMES, 26), ++ SGMII_STAT("sgmii_rx_false_carrier_events", RX_FALSE_CARRIER, 8), ++ SGMII_STAT("sgmii_tx_good_frames", TX_GOOD_FRAMES, 26), ++ SGMII_STAT("sgmii_tx_bad_frames", TX_BAD_FRAMES, 26), ++ SGMII_STAT("sgmii_tx_false_carrier_events", TX_FALSE_CARRIER, 8), ++ SGMII_STAT("sgmii_tx_collisions", TX_COLLISIONS, 8), ++ SGMII_STAT("sgmii_tx_line_collisions", TX_LINE_COLLISIONS, 8), ++ SGMII_STAT("sgmii_tx_frame_alignment_err", TX_FRAME_ALIGN_ERR, 16), ++ SGMII_STAT("sgmii_tx_runt_frames", TX_RUNT_FRAMES, 22), ++}; ++#define AQR107_SGMII_STAT_SZ ARRAY_SIZE(aqr107_hw_stats) ++ ++struct aqr107_priv { ++ u64 sgmii_stats[AQR107_SGMII_STAT_SZ]; ++}; ++ ++static int aqr107_get_sset_count(struct phy_device *phydev) ++{ ++ return AQR107_SGMII_STAT_SZ; ++} ++ ++static void aqr107_get_strings(struct phy_device *phydev, u8 *data) ++{ ++ int i; ++ ++ for (i = 0; i < AQR107_SGMII_STAT_SZ; i++) ++ strscpy(data + i * ETH_GSTRING_LEN, aqr107_hw_stats[i].name, ++ ETH_GSTRING_LEN); ++} ++ ++static u64 aqr107_get_stat(struct phy_device *phydev, int index) ++{ ++ const struct aqr107_hw_stat *stat = aqr107_hw_stats + index; ++ int len_l = min(stat->size, 16); ++ int len_h = stat->size - len_l; ++ u64 ret; ++ int val; ++ ++ val = phy_read_mmd(phydev, MDIO_MMD_C22EXT, stat->reg); ++ if (val < 0) ++ return U64_MAX; ++ ++ ret = val & GENMASK(len_l - 1, 0); ++ if (len_h) { ++ val = phy_read_mmd(phydev, MDIO_MMD_C22EXT, stat->reg + 1); ++ if (val < 0) ++ return U64_MAX; ++ ++ ret += (val & GENMASK(len_h - 1, 0)) << 16; ++ } ++ ++ return ret; ++} ++ ++static void aqr107_get_stats(struct phy_device *phydev, ++ struct ethtool_stats *stats, u64 *data) ++{ ++ struct aqr107_priv *priv = phydev->priv; ++ u64 val; ++ int i; ++ ++ for (i = 0; i < AQR107_SGMII_STAT_SZ; i++) { ++ val = aqr107_get_stat(phydev, i); ++ if (val == U64_MAX) ++ phydev_err(phydev, "Reading HW Statistics failed for %s\n", ++ aqr107_hw_stats[i].name); ++ else ++ priv->sgmii_stats[i] += val; ++ ++ data[i] = priv->sgmii_stats[i]; ++ } ++} ++ ++static int aqr_config_aneg(struct phy_device *phydev) ++{ ++ bool changed = false; ++ u16 reg; ++ int ret; ++ ++ if (phydev->autoneg == AUTONEG_DISABLE) ++ return genphy_c45_pma_setup_forced(phydev); ++ ++ ret = genphy_c45_an_config_aneg(phydev); ++ if (ret < 0) ++ return ret; ++ if (ret > 0) ++ changed = true; ++ ++ /* Clause 45 has no standardized support for 1000BaseT, therefore ++ * use vendor registers for this mode. ++ */ ++ reg = 0; ++ if (linkmode_test_bit(ETHTOOL_LINK_MODE_1000baseT_Full_BIT, ++ phydev->advertising)) ++ reg |= MDIO_AN_VEND_PROV_1000BASET_FULL; ++ ++ if (linkmode_test_bit(ETHTOOL_LINK_MODE_1000baseT_Half_BIT, ++ phydev->advertising)) ++ reg |= MDIO_AN_VEND_PROV_1000BASET_HALF; ++ ++ /* Handle the case when the 2.5G and 5G speeds are not advertised */ ++ if (linkmode_test_bit(ETHTOOL_LINK_MODE_2500baseT_Full_BIT, ++ phydev->advertising)) ++ reg |= MDIO_AN_VEND_PROV_2500BASET_FULL; ++ ++ if (linkmode_test_bit(ETHTOOL_LINK_MODE_5000baseT_Full_BIT, ++ phydev->advertising)) ++ reg |= MDIO_AN_VEND_PROV_5000BASET_FULL; ++ ++ ret = phy_modify_mmd_changed(phydev, MDIO_MMD_AN, MDIO_AN_VEND_PROV, ++ MDIO_AN_VEND_PROV_1000BASET_HALF | ++ MDIO_AN_VEND_PROV_1000BASET_FULL | ++ MDIO_AN_VEND_PROV_2500BASET_FULL | ++ MDIO_AN_VEND_PROV_5000BASET_FULL, reg); ++ if (ret < 0) ++ return ret; ++ if (ret > 0) ++ changed = true; ++ ++ return genphy_c45_check_and_restart_aneg(phydev, changed); ++} ++ ++static int aqr_config_intr(struct phy_device *phydev) ++{ ++ bool en = phydev->interrupts == PHY_INTERRUPT_ENABLED; ++ int err; ++ ++ if (en) { ++ /* Clear any pending interrupts before enabling them */ ++ err = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_TX_VEND_INT_STATUS2); ++ if (err < 0) ++ return err; ++ } ++ ++ err = phy_write_mmd(phydev, MDIO_MMD_AN, MDIO_AN_TX_VEND_INT_MASK2, ++ en ? MDIO_AN_TX_VEND_INT_MASK2_LINK : 0); ++ if (err < 0) ++ return err; ++ ++ err = phy_write_mmd(phydev, MDIO_MMD_VEND1, VEND1_GLOBAL_INT_STD_MASK, ++ en ? VEND1_GLOBAL_INT_STD_MASK_ALL : 0); ++ if (err < 0) ++ return err; ++ ++ err = phy_write_mmd(phydev, MDIO_MMD_VEND1, VEND1_GLOBAL_INT_VEND_MASK, ++ en ? VEND1_GLOBAL_INT_VEND_MASK_GLOBAL3 | ++ VEND1_GLOBAL_INT_VEND_MASK_AN : 0); ++ if (err < 0) ++ return err; ++ ++ if (!en) { ++ /* Clear any pending interrupts after we have disabled them */ ++ err = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_TX_VEND_INT_STATUS2); ++ if (err < 0) ++ return err; ++ } ++ ++ return 0; ++} ++ ++static irqreturn_t aqr_handle_interrupt(struct phy_device *phydev) ++{ ++ int irq_status; ++ ++ irq_status = phy_read_mmd(phydev, MDIO_MMD_AN, ++ MDIO_AN_TX_VEND_INT_STATUS2); ++ if (irq_status < 0) { ++ phy_error(phydev); ++ return IRQ_NONE; ++ } ++ ++ if (!(irq_status & MDIO_AN_TX_VEND_INT_STATUS2_MASK)) ++ return IRQ_NONE; ++ ++ phy_trigger_machine(phydev); ++ ++ return IRQ_HANDLED; ++} ++ ++static int aqr_read_status(struct phy_device *phydev) ++{ ++ int val; ++ ++ if (phydev->autoneg == AUTONEG_ENABLE) { ++ val = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_RX_LP_STAT1); ++ if (val < 0) ++ return val; ++ ++ linkmode_mod_bit(ETHTOOL_LINK_MODE_1000baseT_Full_BIT, ++ phydev->lp_advertising, ++ val & MDIO_AN_RX_LP_STAT1_1000BASET_FULL); ++ linkmode_mod_bit(ETHTOOL_LINK_MODE_1000baseT_Half_BIT, ++ phydev->lp_advertising, ++ val & MDIO_AN_RX_LP_STAT1_1000BASET_HALF); ++ } ++ ++ return genphy_c45_read_status(phydev); ++} ++ ++static int aqr107_read_rate(struct phy_device *phydev) ++{ ++ u32 config_reg; ++ int val; ++ ++ val = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_TX_VEND_STATUS1); ++ if (val < 0) ++ return val; ++ ++ if (val & MDIO_AN_TX_VEND_STATUS1_FULL_DUPLEX) ++ phydev->duplex = DUPLEX_FULL; ++ else ++ phydev->duplex = DUPLEX_HALF; ++ ++ switch (FIELD_GET(MDIO_AN_TX_VEND_STATUS1_RATE_MASK, val)) { ++ case MDIO_AN_TX_VEND_STATUS1_10BASET: ++ phydev->speed = SPEED_10; ++ config_reg = VEND1_GLOBAL_CFG_10M; ++ break; ++ case MDIO_AN_TX_VEND_STATUS1_100BASETX: ++ phydev->speed = SPEED_100; ++ config_reg = VEND1_GLOBAL_CFG_100M; ++ break; ++ case MDIO_AN_TX_VEND_STATUS1_1000BASET: ++ phydev->speed = SPEED_1000; ++ config_reg = VEND1_GLOBAL_CFG_1G; ++ break; ++ case MDIO_AN_TX_VEND_STATUS1_2500BASET: ++ phydev->speed = SPEED_2500; ++ config_reg = VEND1_GLOBAL_CFG_2_5G; ++ break; ++ case MDIO_AN_TX_VEND_STATUS1_5000BASET: ++ phydev->speed = SPEED_5000; ++ config_reg = VEND1_GLOBAL_CFG_5G; ++ break; ++ case MDIO_AN_TX_VEND_STATUS1_10GBASET: ++ phydev->speed = SPEED_10000; ++ config_reg = VEND1_GLOBAL_CFG_10G; ++ break; ++ default: ++ phydev->speed = SPEED_UNKNOWN; ++ return 0; ++ } ++ ++ val = phy_read_mmd(phydev, MDIO_MMD_VEND1, config_reg); ++ if (val < 0) ++ return val; ++ ++ if (FIELD_GET(VEND1_GLOBAL_CFG_RATE_ADAPT, val) == ++ VEND1_GLOBAL_CFG_RATE_ADAPT_PAUSE) ++ phydev->rate_matching = RATE_MATCH_PAUSE; ++ else ++ phydev->rate_matching = RATE_MATCH_NONE; ++ ++ return 0; ++} ++ ++static int aqr107_read_status(struct phy_device *phydev) ++{ ++ int val, ret; ++ ++ ret = aqr_read_status(phydev); ++ if (ret) ++ return ret; ++ ++ if (!phydev->link || phydev->autoneg == AUTONEG_DISABLE) ++ return 0; ++ ++ val = phy_read_mmd(phydev, MDIO_MMD_PHYXS, MDIO_PHYXS_VEND_IF_STATUS); ++ if (val < 0) ++ return val; ++ ++ switch (FIELD_GET(MDIO_PHYXS_VEND_IF_STATUS_TYPE_MASK, val)) { ++ case MDIO_PHYXS_VEND_IF_STATUS_TYPE_KR: ++ phydev->interface = PHY_INTERFACE_MODE_10GKR; ++ break; ++ case MDIO_PHYXS_VEND_IF_STATUS_TYPE_KX: ++ phydev->interface = PHY_INTERFACE_MODE_1000BASEKX; ++ break; ++ case MDIO_PHYXS_VEND_IF_STATUS_TYPE_XFI: ++ phydev->interface = PHY_INTERFACE_MODE_10GBASER; ++ break; ++ case MDIO_PHYXS_VEND_IF_STATUS_TYPE_USXGMII: ++ phydev->interface = PHY_INTERFACE_MODE_USXGMII; ++ break; ++ case MDIO_PHYXS_VEND_IF_STATUS_TYPE_XAUI: ++ phydev->interface = PHY_INTERFACE_MODE_XAUI; ++ break; ++ case MDIO_PHYXS_VEND_IF_STATUS_TYPE_SGMII: ++ phydev->interface = PHY_INTERFACE_MODE_SGMII; ++ break; ++ case MDIO_PHYXS_VEND_IF_STATUS_TYPE_RXAUI: ++ phydev->interface = PHY_INTERFACE_MODE_RXAUI; ++ break; ++ case MDIO_PHYXS_VEND_IF_STATUS_TYPE_OCSGMII: ++ phydev->interface = PHY_INTERFACE_MODE_2500BASEX; ++ break; ++ default: ++ phydev->interface = PHY_INTERFACE_MODE_NA; ++ break; ++ } ++ ++ /* Read possibly downshifted rate from vendor register */ ++ return aqr107_read_rate(phydev); ++} ++ ++static int aqr107_get_downshift(struct phy_device *phydev, u8 *data) ++{ ++ int val, cnt, enable; ++ ++ val = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_VEND_PROV); ++ if (val < 0) ++ return val; ++ ++ enable = FIELD_GET(MDIO_AN_VEND_PROV_DOWNSHIFT_EN, val); ++ cnt = FIELD_GET(MDIO_AN_VEND_PROV_DOWNSHIFT_MASK, val); ++ ++ *data = enable && cnt ? cnt : DOWNSHIFT_DEV_DISABLE; ++ ++ return 0; ++} ++ ++static int aqr107_set_downshift(struct phy_device *phydev, u8 cnt) ++{ ++ int val = 0; ++ ++ if (!FIELD_FIT(MDIO_AN_VEND_PROV_DOWNSHIFT_MASK, cnt)) ++ return -E2BIG; ++ ++ if (cnt != DOWNSHIFT_DEV_DISABLE) { ++ val = MDIO_AN_VEND_PROV_DOWNSHIFT_EN; ++ val |= FIELD_PREP(MDIO_AN_VEND_PROV_DOWNSHIFT_MASK, cnt); ++ } ++ ++ return phy_modify_mmd(phydev, MDIO_MMD_AN, MDIO_AN_VEND_PROV, ++ MDIO_AN_VEND_PROV_DOWNSHIFT_EN | ++ MDIO_AN_VEND_PROV_DOWNSHIFT_MASK, val); ++} ++ ++static int aqr107_get_tunable(struct phy_device *phydev, ++ struct ethtool_tunable *tuna, void *data) ++{ ++ switch (tuna->id) { ++ case ETHTOOL_PHY_DOWNSHIFT: ++ return aqr107_get_downshift(phydev, data); ++ default: ++ return -EOPNOTSUPP; ++ } ++} ++ ++static int aqr107_set_tunable(struct phy_device *phydev, ++ struct ethtool_tunable *tuna, const void *data) ++{ ++ switch (tuna->id) { ++ case ETHTOOL_PHY_DOWNSHIFT: ++ return aqr107_set_downshift(phydev, *(const u8 *)data); ++ default: ++ return -EOPNOTSUPP; ++ } ++} ++ ++/* If we configure settings whilst firmware is still initializing the chip, ++ * then these settings may be overwritten. Therefore make sure chip ++ * initialization has completed. Use presence of the firmware ID as ++ * indicator for initialization having completed. ++ * The chip also provides a "reset completed" bit, but it's cleared after ++ * read. Therefore function would time out if called again. ++ */ ++static int aqr107_wait_reset_complete(struct phy_device *phydev) ++{ ++ int val; ++ ++ return phy_read_mmd_poll_timeout(phydev, MDIO_MMD_VEND1, ++ VEND1_GLOBAL_FW_ID, val, val != 0, ++ 20000, 2000000, false); ++} ++ ++static void aqr107_chip_info(struct phy_device *phydev) ++{ ++ u8 fw_major, fw_minor, build_id, prov_id; ++ int val; ++ ++ val = phy_read_mmd(phydev, MDIO_MMD_VEND1, VEND1_GLOBAL_FW_ID); ++ if (val < 0) ++ return; ++ ++ fw_major = FIELD_GET(VEND1_GLOBAL_FW_ID_MAJOR, val); ++ fw_minor = FIELD_GET(VEND1_GLOBAL_FW_ID_MINOR, val); ++ ++ val = phy_read_mmd(phydev, MDIO_MMD_VEND1, VEND1_GLOBAL_RSVD_STAT1); ++ if (val < 0) ++ return; ++ ++ build_id = FIELD_GET(VEND1_GLOBAL_RSVD_STAT1_FW_BUILD_ID, val); ++ prov_id = FIELD_GET(VEND1_GLOBAL_RSVD_STAT1_PROV_ID, val); ++ ++ phydev_dbg(phydev, "FW %u.%u, Build %u, Provisioning %u\n", ++ fw_major, fw_minor, build_id, prov_id); ++} ++ ++static int aqr107_config_init(struct phy_device *phydev) ++{ ++ int ret; ++ ++ /* Check that the PHY interface type is compatible */ ++ if (phydev->interface != PHY_INTERFACE_MODE_SGMII && ++ phydev->interface != PHY_INTERFACE_MODE_1000BASEKX && ++ phydev->interface != PHY_INTERFACE_MODE_2500BASEX && ++ phydev->interface != PHY_INTERFACE_MODE_XGMII && ++ phydev->interface != PHY_INTERFACE_MODE_USXGMII && ++ phydev->interface != PHY_INTERFACE_MODE_10GKR && ++ phydev->interface != PHY_INTERFACE_MODE_10GBASER && ++ phydev->interface != PHY_INTERFACE_MODE_XAUI && ++ phydev->interface != PHY_INTERFACE_MODE_RXAUI) ++ return -ENODEV; ++ ++ WARN(phydev->interface == PHY_INTERFACE_MODE_XGMII, ++ "Your devicetree is out of date, please update it. The AQR107 family doesn't support XGMII, maybe you mean USXGMII.\n"); ++ ++ ret = aqr107_wait_reset_complete(phydev); ++ if (!ret) ++ aqr107_chip_info(phydev); ++ ++ return aqr107_set_downshift(phydev, MDIO_AN_VEND_PROV_DOWNSHIFT_DFLT); ++} ++ ++static int aqcs109_config_init(struct phy_device *phydev) ++{ ++ int ret; ++ ++ /* Check that the PHY interface type is compatible */ ++ if (phydev->interface != PHY_INTERFACE_MODE_SGMII && ++ phydev->interface != PHY_INTERFACE_MODE_2500BASEX) ++ return -ENODEV; ++ ++ ret = aqr107_wait_reset_complete(phydev); ++ if (!ret) ++ aqr107_chip_info(phydev); ++ ++ /* AQCS109 belongs to a chip family partially supporting 10G and 5G. ++ * PMA speed ability bits are the same for all members of the family, ++ * AQCS109 however supports speeds up to 2.5G only. ++ */ ++ phy_set_max_speed(phydev, SPEED_2500); ++ ++ return aqr107_set_downshift(phydev, MDIO_AN_VEND_PROV_DOWNSHIFT_DFLT); ++} ++ ++static void aqr107_link_change_notify(struct phy_device *phydev) ++{ ++ u8 fw_major, fw_minor; ++ bool downshift, short_reach, afr; ++ int mode, val; ++ ++ if (phydev->state != PHY_RUNNING || phydev->autoneg == AUTONEG_DISABLE) ++ return; ++ ++ val = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_RX_LP_STAT1); ++ /* call failed or link partner is no Aquantia PHY */ ++ if (val < 0 || !(val & MDIO_AN_RX_LP_STAT1_AQ_PHY)) ++ return; ++ ++ short_reach = val & MDIO_AN_RX_LP_STAT1_SHORT_REACH; ++ downshift = val & MDIO_AN_RX_LP_STAT1_AQRATE_DOWNSHIFT; ++ ++ val = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_RX_LP_STAT4); ++ if (val < 0) ++ return; ++ ++ fw_major = FIELD_GET(MDIO_AN_RX_LP_STAT4_FW_MAJOR, val); ++ fw_minor = FIELD_GET(MDIO_AN_RX_LP_STAT4_FW_MINOR, val); ++ ++ val = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_RX_VEND_STAT3); ++ if (val < 0) ++ return; ++ ++ afr = val & MDIO_AN_RX_VEND_STAT3_AFR; ++ ++ phydev_dbg(phydev, "Link partner is Aquantia PHY, FW %u.%u%s%s%s\n", ++ fw_major, fw_minor, ++ short_reach ? ", short reach mode" : "", ++ downshift ? ", fast-retrain downshift advertised" : "", ++ afr ? ", fast reframe advertised" : ""); ++ ++ val = phy_read_mmd(phydev, MDIO_MMD_VEND1, VEND1_GLOBAL_RSVD_STAT9); ++ if (val < 0) ++ return; ++ ++ mode = FIELD_GET(VEND1_GLOBAL_RSVD_STAT9_MODE, val); ++ if (mode == VEND1_GLOBAL_RSVD_STAT9_1000BT2) ++ phydev_info(phydev, "Aquantia 1000Base-T2 mode active\n"); ++} ++ ++static int aqr107_wait_processor_intensive_op(struct phy_device *phydev) ++{ ++ int val, err; ++ ++ /* The datasheet notes to wait at least 1ms after issuing a ++ * processor intensive operation before checking. ++ * We cannot use the 'sleep_before_read' parameter of read_poll_timeout ++ * because that just determines the maximum time slept, not the minimum. ++ */ ++ usleep_range(1000, 5000); ++ ++ err = phy_read_mmd_poll_timeout(phydev, MDIO_MMD_VEND1, ++ VEND1_GLOBAL_GEN_STAT2, val, ++ !(val & VEND1_GLOBAL_GEN_STAT2_OP_IN_PROG), ++ AQR107_OP_IN_PROG_SLEEP, ++ AQR107_OP_IN_PROG_TIMEOUT, false); ++ if (err) { ++ phydev_err(phydev, "timeout: processor-intensive MDIO operation\n"); ++ return err; ++ } ++ ++ return 0; ++} ++ ++static int aqr107_get_rate_matching(struct phy_device *phydev, ++ phy_interface_t iface) ++{ ++ if (iface == PHY_INTERFACE_MODE_10GBASER || ++ iface == PHY_INTERFACE_MODE_2500BASEX || ++ iface == PHY_INTERFACE_MODE_NA) ++ return RATE_MATCH_PAUSE; ++ return RATE_MATCH_NONE; ++} ++ ++static int aqr107_suspend(struct phy_device *phydev) ++{ ++ int err; ++ ++ err = phy_set_bits_mmd(phydev, MDIO_MMD_VEND1, MDIO_CTRL1, ++ MDIO_CTRL1_LPOWER); ++ if (err) ++ return err; ++ ++ return aqr107_wait_processor_intensive_op(phydev); ++} ++ ++static int aqr107_resume(struct phy_device *phydev) ++{ ++ int err; ++ ++ err = phy_clear_bits_mmd(phydev, MDIO_MMD_VEND1, MDIO_CTRL1, ++ MDIO_CTRL1_LPOWER); ++ if (err) ++ return err; ++ ++ return aqr107_wait_processor_intensive_op(phydev); ++} ++ ++static int aqr107_probe(struct phy_device *phydev) ++{ ++ phydev->priv = devm_kzalloc(&phydev->mdio.dev, ++ sizeof(struct aqr107_priv), GFP_KERNEL); ++ if (!phydev->priv) ++ return -ENOMEM; ++ ++ return aqr_hwmon_probe(phydev); ++} ++ ++static struct phy_driver aqr_driver[] = { ++{ ++ PHY_ID_MATCH_MODEL(PHY_ID_AQ1202), ++ .name = "Aquantia AQ1202", ++ .config_aneg = aqr_config_aneg, ++ .config_intr = aqr_config_intr, ++ .handle_interrupt = aqr_handle_interrupt, ++ .read_status = aqr_read_status, ++}, ++{ ++ PHY_ID_MATCH_MODEL(PHY_ID_AQ2104), ++ .name = "Aquantia AQ2104", ++ .config_aneg = aqr_config_aneg, ++ .config_intr = aqr_config_intr, ++ .handle_interrupt = aqr_handle_interrupt, ++ .read_status = aqr_read_status, ++}, ++{ ++ PHY_ID_MATCH_MODEL(PHY_ID_AQR105), ++ .name = "Aquantia AQR105", ++ .config_aneg = aqr_config_aneg, ++ .config_intr = aqr_config_intr, ++ .handle_interrupt = aqr_handle_interrupt, ++ .read_status = aqr_read_status, ++ .suspend = aqr107_suspend, ++ .resume = aqr107_resume, ++}, ++{ ++ PHY_ID_MATCH_MODEL(PHY_ID_AQR106), ++ .name = "Aquantia AQR106", ++ .config_aneg = aqr_config_aneg, ++ .config_intr = aqr_config_intr, ++ .handle_interrupt = aqr_handle_interrupt, ++ .read_status = aqr_read_status, ++}, ++{ ++ PHY_ID_MATCH_MODEL(PHY_ID_AQR107), ++ .name = "Aquantia AQR107", ++ .probe = aqr107_probe, ++ .get_rate_matching = aqr107_get_rate_matching, ++ .config_init = aqr107_config_init, ++ .config_aneg = aqr_config_aneg, ++ .config_intr = aqr_config_intr, ++ .handle_interrupt = aqr_handle_interrupt, ++ .read_status = aqr107_read_status, ++ .get_tunable = aqr107_get_tunable, ++ .set_tunable = aqr107_set_tunable, ++ .suspend = aqr107_suspend, ++ .resume = aqr107_resume, ++ .get_sset_count = aqr107_get_sset_count, ++ .get_strings = aqr107_get_strings, ++ .get_stats = aqr107_get_stats, ++ .link_change_notify = aqr107_link_change_notify, ++}, ++{ ++ PHY_ID_MATCH_MODEL(PHY_ID_AQCS109), ++ .name = "Aquantia AQCS109", ++ .probe = aqr107_probe, ++ .get_rate_matching = aqr107_get_rate_matching, ++ .config_init = aqcs109_config_init, ++ .config_aneg = aqr_config_aneg, ++ .config_intr = aqr_config_intr, ++ .handle_interrupt = aqr_handle_interrupt, ++ .read_status = aqr107_read_status, ++ .get_tunable = aqr107_get_tunable, ++ .set_tunable = aqr107_set_tunable, ++ .suspend = aqr107_suspend, ++ .resume = aqr107_resume, ++ .get_sset_count = aqr107_get_sset_count, ++ .get_strings = aqr107_get_strings, ++ .get_stats = aqr107_get_stats, ++ .link_change_notify = aqr107_link_change_notify, ++}, ++{ ++ PHY_ID_MATCH_MODEL(PHY_ID_AQR405), ++ .name = "Aquantia AQR405", ++ .config_aneg = aqr_config_aneg, ++ .config_intr = aqr_config_intr, ++ .handle_interrupt = aqr_handle_interrupt, ++ .read_status = aqr_read_status, ++}, ++{ ++ PHY_ID_MATCH_MODEL(PHY_ID_AQR113C), ++ .name = "Aquantia AQR113C", ++ .probe = aqr107_probe, ++ .get_rate_matching = aqr107_get_rate_matching, ++ .config_init = aqr107_config_init, ++ .config_aneg = aqr_config_aneg, ++ .config_intr = aqr_config_intr, ++ .handle_interrupt = aqr_handle_interrupt, ++ .read_status = aqr107_read_status, ++ .get_tunable = aqr107_get_tunable, ++ .set_tunable = aqr107_set_tunable, ++ .suspend = aqr107_suspend, ++ .resume = aqr107_resume, ++ .get_sset_count = aqr107_get_sset_count, ++ .get_strings = aqr107_get_strings, ++ .get_stats = aqr107_get_stats, ++ .link_change_notify = aqr107_link_change_notify, ++}, ++}; ++ ++module_phy_driver(aqr_driver); ++ ++static struct mdio_device_id __maybe_unused aqr_tbl[] = { ++ { PHY_ID_MATCH_MODEL(PHY_ID_AQ1202) }, ++ { PHY_ID_MATCH_MODEL(PHY_ID_AQ2104) }, ++ { PHY_ID_MATCH_MODEL(PHY_ID_AQR105) }, ++ { PHY_ID_MATCH_MODEL(PHY_ID_AQR106) }, ++ { PHY_ID_MATCH_MODEL(PHY_ID_AQR107) }, ++ { PHY_ID_MATCH_MODEL(PHY_ID_AQCS109) }, ++ { PHY_ID_MATCH_MODEL(PHY_ID_AQR405) }, ++ { PHY_ID_MATCH_MODEL(PHY_ID_AQR113C) }, ++ { } ++}; ++ ++MODULE_DEVICE_TABLE(mdio, aqr_tbl); ++ ++MODULE_DESCRIPTION("Aquantia PHY driver"); ++MODULE_AUTHOR("Shaohui Xie "); ++MODULE_LICENSE("GPL v2"); +--- a/drivers/net/phy/aquantia_hwmon.c ++++ /dev/null +@@ -1,250 +0,0 @@ +-// SPDX-License-Identifier: GPL-2.0 +-/* HWMON driver for Aquantia PHY +- * +- * Author: Nikita Yushchenko +- * Author: Andrew Lunn +- * Author: Heiner Kallweit +- */ +- +-#include +-#include +-#include +-#include +- +-#include "aquantia.h" +- +-/* Vendor specific 1, MDIO_MMD_VEND2 */ +-#define VEND1_THERMAL_PROV_HIGH_TEMP_FAIL 0xc421 +-#define VEND1_THERMAL_PROV_LOW_TEMP_FAIL 0xc422 +-#define VEND1_THERMAL_PROV_HIGH_TEMP_WARN 0xc423 +-#define VEND1_THERMAL_PROV_LOW_TEMP_WARN 0xc424 +-#define VEND1_THERMAL_STAT1 0xc820 +-#define VEND1_THERMAL_STAT2 0xc821 +-#define VEND1_THERMAL_STAT2_VALID BIT(0) +-#define VEND1_GENERAL_STAT1 0xc830 +-#define VEND1_GENERAL_STAT1_HIGH_TEMP_FAIL BIT(14) +-#define VEND1_GENERAL_STAT1_LOW_TEMP_FAIL BIT(13) +-#define VEND1_GENERAL_STAT1_HIGH_TEMP_WARN BIT(12) +-#define VEND1_GENERAL_STAT1_LOW_TEMP_WARN BIT(11) +- +-#if IS_REACHABLE(CONFIG_HWMON) +- +-static umode_t aqr_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_min_alarm: +- case hwmon_temp_max_alarm: +- case hwmon_temp_lcrit_alarm: +- case hwmon_temp_crit_alarm: +- return 0444; +- case hwmon_temp_min: +- case hwmon_temp_max: +- case hwmon_temp_lcrit: +- case hwmon_temp_crit: +- return 0644; +- default: +- return 0; +- } +-} +- +-static int aqr_hwmon_get(struct phy_device *phydev, int reg, long *value) +-{ +- int temp = phy_read_mmd(phydev, MDIO_MMD_VEND1, reg); +- +- if (temp < 0) +- return temp; +- +- /* 16 bit value is 2's complement with LSB = 1/256th degree Celsius */ +- *value = (s16)temp * 1000 / 256; +- +- return 0; +-} +- +-static int aqr_hwmon_set(struct phy_device *phydev, int reg, long value) +-{ +- int temp; +- +- if (value >= 128000 || value < -128000) +- return -ERANGE; +- +- temp = value * 256 / 1000; +- +- /* temp is in s16 range and we're interested in lower 16 bits only */ +- return phy_write_mmd(phydev, MDIO_MMD_VEND1, reg, (u16)temp); +-} +- +-static int aqr_hwmon_test_bit(struct phy_device *phydev, int reg, int bit) +-{ +- int val = phy_read_mmd(phydev, MDIO_MMD_VEND1, reg); +- +- if (val < 0) +- return val; +- +- return !!(val & bit); +-} +- +-static int aqr_hwmon_status1(struct phy_device *phydev, int bit, long *value) +-{ +- int val = aqr_hwmon_test_bit(phydev, VEND1_GENERAL_STAT1, bit); +- +- if (val < 0) +- return val; +- +- *value = val; +- +- return 0; +-} +- +-static int aqr_hwmon_read(struct device *dev, enum hwmon_sensor_types type, +- u32 attr, int channel, long *value) +-{ +- struct phy_device *phydev = dev_get_drvdata(dev); +- int reg; +- +- if (type != hwmon_temp) +- return -EOPNOTSUPP; +- +- switch (attr) { +- case hwmon_temp_input: +- reg = aqr_hwmon_test_bit(phydev, VEND1_THERMAL_STAT2, +- VEND1_THERMAL_STAT2_VALID); +- if (reg < 0) +- return reg; +- if (!reg) +- return -EBUSY; +- +- return aqr_hwmon_get(phydev, VEND1_THERMAL_STAT1, value); +- +- case hwmon_temp_lcrit: +- return aqr_hwmon_get(phydev, VEND1_THERMAL_PROV_LOW_TEMP_FAIL, +- value); +- case hwmon_temp_min: +- return aqr_hwmon_get(phydev, VEND1_THERMAL_PROV_LOW_TEMP_WARN, +- value); +- case hwmon_temp_max: +- return aqr_hwmon_get(phydev, VEND1_THERMAL_PROV_HIGH_TEMP_WARN, +- value); +- case hwmon_temp_crit: +- return aqr_hwmon_get(phydev, VEND1_THERMAL_PROV_HIGH_TEMP_FAIL, +- value); +- case hwmon_temp_lcrit_alarm: +- return aqr_hwmon_status1(phydev, +- VEND1_GENERAL_STAT1_LOW_TEMP_FAIL, +- value); +- case hwmon_temp_min_alarm: +- return aqr_hwmon_status1(phydev, +- VEND1_GENERAL_STAT1_LOW_TEMP_WARN, +- value); +- case hwmon_temp_max_alarm: +- return aqr_hwmon_status1(phydev, +- VEND1_GENERAL_STAT1_HIGH_TEMP_WARN, +- value); +- case hwmon_temp_crit_alarm: +- return aqr_hwmon_status1(phydev, +- VEND1_GENERAL_STAT1_HIGH_TEMP_FAIL, +- value); +- default: +- return -EOPNOTSUPP; +- } +-} +- +-static int aqr_hwmon_write(struct device *dev, enum hwmon_sensor_types type, +- u32 attr, int channel, long value) +-{ +- struct phy_device *phydev = dev_get_drvdata(dev); +- +- if (type != hwmon_temp) +- return -EOPNOTSUPP; +- +- switch (attr) { +- case hwmon_temp_lcrit: +- return aqr_hwmon_set(phydev, VEND1_THERMAL_PROV_LOW_TEMP_FAIL, +- value); +- case hwmon_temp_min: +- return aqr_hwmon_set(phydev, VEND1_THERMAL_PROV_LOW_TEMP_WARN, +- value); +- case hwmon_temp_max: +- return aqr_hwmon_set(phydev, VEND1_THERMAL_PROV_HIGH_TEMP_WARN, +- value); +- case hwmon_temp_crit: +- return aqr_hwmon_set(phydev, VEND1_THERMAL_PROV_HIGH_TEMP_FAIL, +- value); +- default: +- return -EOPNOTSUPP; +- } +-} +- +-static const struct hwmon_ops aqr_hwmon_ops = { +- .is_visible = aqr_hwmon_is_visible, +- .read = aqr_hwmon_read, +- .write = aqr_hwmon_write, +-}; +- +-static u32 aqr_hwmon_chip_config[] = { +- HWMON_C_REGISTER_TZ, +- 0, +-}; +- +-static const struct hwmon_channel_info aqr_hwmon_chip = { +- .type = hwmon_chip, +- .config = aqr_hwmon_chip_config, +-}; +- +-static u32 aqr_hwmon_temp_config[] = { +- HWMON_T_INPUT | +- HWMON_T_MAX | HWMON_T_MIN | +- HWMON_T_MAX_ALARM | HWMON_T_MIN_ALARM | +- HWMON_T_CRIT | HWMON_T_LCRIT | +- HWMON_T_CRIT_ALARM | HWMON_T_LCRIT_ALARM, +- 0, +-}; +- +-static const struct hwmon_channel_info aqr_hwmon_temp = { +- .type = hwmon_temp, +- .config = aqr_hwmon_temp_config, +-}; +- +-static const struct hwmon_channel_info *aqr_hwmon_info[] = { +- &aqr_hwmon_chip, +- &aqr_hwmon_temp, +- NULL, +-}; +- +-static const struct hwmon_chip_info aqr_hwmon_chip_info = { +- .ops = &aqr_hwmon_ops, +- .info = aqr_hwmon_info, +-}; +- +-int aqr_hwmon_probe(struct phy_device *phydev) +-{ +- struct device *dev = &phydev->mdio.dev; +- struct device *hwmon_dev; +- char *hwmon_name; +- int i, j; +- +- hwmon_name = devm_kstrdup(dev, dev_name(dev), GFP_KERNEL); +- if (!hwmon_name) +- return -ENOMEM; +- +- for (i = j = 0; hwmon_name[i]; i++) { +- if (isalnum(hwmon_name[i])) { +- if (i != j) +- hwmon_name[j] = hwmon_name[i]; +- j++; +- } +- } +- hwmon_name[j] = '\0'; +- +- hwmon_dev = devm_hwmon_device_register_with_info(dev, hwmon_name, +- phydev, &aqr_hwmon_chip_info, NULL); +- +- return PTR_ERR_OR_ZERO(hwmon_dev); +-} +- +-#endif +--- a/drivers/net/phy/aquantia_main.c ++++ /dev/null +@@ -1,842 +0,0 @@ +-// SPDX-License-Identifier: GPL-2.0 +-/* +- * Driver for Aquantia PHY +- * +- * Author: Shaohui Xie +- * +- * Copyright 2015 Freescale Semiconductor, Inc. +- */ +- +-#include +-#include +-#include +-#include +-#include +- +-#include "aquantia.h" +- +-#define PHY_ID_AQ1202 0x03a1b445 +-#define PHY_ID_AQ2104 0x03a1b460 +-#define PHY_ID_AQR105 0x03a1b4a2 +-#define PHY_ID_AQR106 0x03a1b4d0 +-#define PHY_ID_AQR107 0x03a1b4e0 +-#define PHY_ID_AQCS109 0x03a1b5c2 +-#define PHY_ID_AQR405 0x03a1b4b0 +-#define PHY_ID_AQR113C 0x31c31c12 +- +-#define MDIO_PHYXS_VEND_IF_STATUS 0xe812 +-#define MDIO_PHYXS_VEND_IF_STATUS_TYPE_MASK GENMASK(7, 3) +-#define MDIO_PHYXS_VEND_IF_STATUS_TYPE_KR 0 +-#define MDIO_PHYXS_VEND_IF_STATUS_TYPE_KX 1 +-#define MDIO_PHYXS_VEND_IF_STATUS_TYPE_XFI 2 +-#define MDIO_PHYXS_VEND_IF_STATUS_TYPE_USXGMII 3 +-#define MDIO_PHYXS_VEND_IF_STATUS_TYPE_XAUI 4 +-#define MDIO_PHYXS_VEND_IF_STATUS_TYPE_SGMII 6 +-#define MDIO_PHYXS_VEND_IF_STATUS_TYPE_RXAUI 7 +-#define MDIO_PHYXS_VEND_IF_STATUS_TYPE_OCSGMII 10 +- +-#define MDIO_AN_VEND_PROV 0xc400 +-#define MDIO_AN_VEND_PROV_1000BASET_FULL BIT(15) +-#define MDIO_AN_VEND_PROV_1000BASET_HALF BIT(14) +-#define MDIO_AN_VEND_PROV_5000BASET_FULL BIT(11) +-#define MDIO_AN_VEND_PROV_2500BASET_FULL BIT(10) +-#define MDIO_AN_VEND_PROV_DOWNSHIFT_EN BIT(4) +-#define MDIO_AN_VEND_PROV_DOWNSHIFT_MASK GENMASK(3, 0) +-#define MDIO_AN_VEND_PROV_DOWNSHIFT_DFLT 4 +- +-#define MDIO_AN_TX_VEND_STATUS1 0xc800 +-#define MDIO_AN_TX_VEND_STATUS1_RATE_MASK GENMASK(3, 1) +-#define MDIO_AN_TX_VEND_STATUS1_10BASET 0 +-#define MDIO_AN_TX_VEND_STATUS1_100BASETX 1 +-#define MDIO_AN_TX_VEND_STATUS1_1000BASET 2 +-#define MDIO_AN_TX_VEND_STATUS1_10GBASET 3 +-#define MDIO_AN_TX_VEND_STATUS1_2500BASET 4 +-#define MDIO_AN_TX_VEND_STATUS1_5000BASET 5 +-#define MDIO_AN_TX_VEND_STATUS1_FULL_DUPLEX BIT(0) +- +-#define MDIO_AN_TX_VEND_INT_STATUS1 0xcc00 +-#define MDIO_AN_TX_VEND_INT_STATUS1_DOWNSHIFT BIT(1) +- +-#define MDIO_AN_TX_VEND_INT_STATUS2 0xcc01 +-#define MDIO_AN_TX_VEND_INT_STATUS2_MASK BIT(0) +- +-#define MDIO_AN_TX_VEND_INT_MASK2 0xd401 +-#define MDIO_AN_TX_VEND_INT_MASK2_LINK BIT(0) +- +-#define MDIO_AN_RX_LP_STAT1 0xe820 +-#define MDIO_AN_RX_LP_STAT1_1000BASET_FULL BIT(15) +-#define MDIO_AN_RX_LP_STAT1_1000BASET_HALF BIT(14) +-#define MDIO_AN_RX_LP_STAT1_SHORT_REACH BIT(13) +-#define MDIO_AN_RX_LP_STAT1_AQRATE_DOWNSHIFT BIT(12) +-#define MDIO_AN_RX_LP_STAT1_AQ_PHY BIT(2) +- +-#define MDIO_AN_RX_LP_STAT4 0xe823 +-#define MDIO_AN_RX_LP_STAT4_FW_MAJOR GENMASK(15, 8) +-#define MDIO_AN_RX_LP_STAT4_FW_MINOR GENMASK(7, 0) +- +-#define MDIO_AN_RX_VEND_STAT3 0xe832 +-#define MDIO_AN_RX_VEND_STAT3_AFR BIT(0) +- +-/* MDIO_MMD_C22EXT */ +-#define MDIO_C22EXT_STAT_SGMII_RX_GOOD_FRAMES 0xd292 +-#define MDIO_C22EXT_STAT_SGMII_RX_BAD_FRAMES 0xd294 +-#define MDIO_C22EXT_STAT_SGMII_RX_FALSE_CARRIER 0xd297 +-#define MDIO_C22EXT_STAT_SGMII_TX_GOOD_FRAMES 0xd313 +-#define MDIO_C22EXT_STAT_SGMII_TX_BAD_FRAMES 0xd315 +-#define MDIO_C22EXT_STAT_SGMII_TX_FALSE_CARRIER 0xd317 +-#define MDIO_C22EXT_STAT_SGMII_TX_COLLISIONS 0xd318 +-#define MDIO_C22EXT_STAT_SGMII_TX_LINE_COLLISIONS 0xd319 +-#define MDIO_C22EXT_STAT_SGMII_TX_FRAME_ALIGN_ERR 0xd31a +-#define MDIO_C22EXT_STAT_SGMII_TX_RUNT_FRAMES 0xd31b +- +-/* Vendor specific 1, MDIO_MMD_VEND1 */ +-#define VEND1_GLOBAL_FW_ID 0x0020 +-#define VEND1_GLOBAL_FW_ID_MAJOR GENMASK(15, 8) +-#define VEND1_GLOBAL_FW_ID_MINOR GENMASK(7, 0) +- +-#define VEND1_GLOBAL_GEN_STAT2 0xc831 +-#define VEND1_GLOBAL_GEN_STAT2_OP_IN_PROG BIT(15) +- +-/* The following registers all have similar layouts; first the registers... */ +-#define VEND1_GLOBAL_CFG_10M 0x0310 +-#define VEND1_GLOBAL_CFG_100M 0x031b +-#define VEND1_GLOBAL_CFG_1G 0x031c +-#define VEND1_GLOBAL_CFG_2_5G 0x031d +-#define VEND1_GLOBAL_CFG_5G 0x031e +-#define VEND1_GLOBAL_CFG_10G 0x031f +-/* ...and now the fields */ +-#define VEND1_GLOBAL_CFG_RATE_ADAPT GENMASK(8, 7) +-#define VEND1_GLOBAL_CFG_RATE_ADAPT_NONE 0 +-#define VEND1_GLOBAL_CFG_RATE_ADAPT_USX 1 +-#define VEND1_GLOBAL_CFG_RATE_ADAPT_PAUSE 2 +- +-#define VEND1_GLOBAL_RSVD_STAT1 0xc885 +-#define VEND1_GLOBAL_RSVD_STAT1_FW_BUILD_ID GENMASK(7, 4) +-#define VEND1_GLOBAL_RSVD_STAT1_PROV_ID GENMASK(3, 0) +- +-#define VEND1_GLOBAL_RSVD_STAT9 0xc88d +-#define VEND1_GLOBAL_RSVD_STAT9_MODE GENMASK(7, 0) +-#define VEND1_GLOBAL_RSVD_STAT9_1000BT2 0x23 +- +-#define VEND1_GLOBAL_INT_STD_STATUS 0xfc00 +-#define VEND1_GLOBAL_INT_VEND_STATUS 0xfc01 +- +-#define VEND1_GLOBAL_INT_STD_MASK 0xff00 +-#define VEND1_GLOBAL_INT_STD_MASK_PMA1 BIT(15) +-#define VEND1_GLOBAL_INT_STD_MASK_PMA2 BIT(14) +-#define VEND1_GLOBAL_INT_STD_MASK_PCS1 BIT(13) +-#define VEND1_GLOBAL_INT_STD_MASK_PCS2 BIT(12) +-#define VEND1_GLOBAL_INT_STD_MASK_PCS3 BIT(11) +-#define VEND1_GLOBAL_INT_STD_MASK_PHY_XS1 BIT(10) +-#define VEND1_GLOBAL_INT_STD_MASK_PHY_XS2 BIT(9) +-#define VEND1_GLOBAL_INT_STD_MASK_AN1 BIT(8) +-#define VEND1_GLOBAL_INT_STD_MASK_AN2 BIT(7) +-#define VEND1_GLOBAL_INT_STD_MASK_GBE BIT(6) +-#define VEND1_GLOBAL_INT_STD_MASK_ALL BIT(0) +- +-#define VEND1_GLOBAL_INT_VEND_MASK 0xff01 +-#define VEND1_GLOBAL_INT_VEND_MASK_PMA BIT(15) +-#define VEND1_GLOBAL_INT_VEND_MASK_PCS BIT(14) +-#define VEND1_GLOBAL_INT_VEND_MASK_PHY_XS BIT(13) +-#define VEND1_GLOBAL_INT_VEND_MASK_AN BIT(12) +-#define VEND1_GLOBAL_INT_VEND_MASK_GBE BIT(11) +-#define VEND1_GLOBAL_INT_VEND_MASK_GLOBAL1 BIT(2) +-#define VEND1_GLOBAL_INT_VEND_MASK_GLOBAL2 BIT(1) +-#define VEND1_GLOBAL_INT_VEND_MASK_GLOBAL3 BIT(0) +- +-/* Sleep and timeout for checking if the Processor-Intensive +- * MDIO operation is finished +- */ +-#define AQR107_OP_IN_PROG_SLEEP 1000 +-#define AQR107_OP_IN_PROG_TIMEOUT 100000 +- +-struct aqr107_hw_stat { +- const char *name; +- int reg; +- int size; +-}; +- +-#define SGMII_STAT(n, r, s) { n, MDIO_C22EXT_STAT_SGMII_ ## r, s } +-static const struct aqr107_hw_stat aqr107_hw_stats[] = { +- SGMII_STAT("sgmii_rx_good_frames", RX_GOOD_FRAMES, 26), +- SGMII_STAT("sgmii_rx_bad_frames", RX_BAD_FRAMES, 26), +- SGMII_STAT("sgmii_rx_false_carrier_events", RX_FALSE_CARRIER, 8), +- SGMII_STAT("sgmii_tx_good_frames", TX_GOOD_FRAMES, 26), +- SGMII_STAT("sgmii_tx_bad_frames", TX_BAD_FRAMES, 26), +- SGMII_STAT("sgmii_tx_false_carrier_events", TX_FALSE_CARRIER, 8), +- SGMII_STAT("sgmii_tx_collisions", TX_COLLISIONS, 8), +- SGMII_STAT("sgmii_tx_line_collisions", TX_LINE_COLLISIONS, 8), +- SGMII_STAT("sgmii_tx_frame_alignment_err", TX_FRAME_ALIGN_ERR, 16), +- SGMII_STAT("sgmii_tx_runt_frames", TX_RUNT_FRAMES, 22), +-}; +-#define AQR107_SGMII_STAT_SZ ARRAY_SIZE(aqr107_hw_stats) +- +-struct aqr107_priv { +- u64 sgmii_stats[AQR107_SGMII_STAT_SZ]; +-}; +- +-static int aqr107_get_sset_count(struct phy_device *phydev) +-{ +- return AQR107_SGMII_STAT_SZ; +-} +- +-static void aqr107_get_strings(struct phy_device *phydev, u8 *data) +-{ +- int i; +- +- for (i = 0; i < AQR107_SGMII_STAT_SZ; i++) +- strscpy(data + i * ETH_GSTRING_LEN, aqr107_hw_stats[i].name, +- ETH_GSTRING_LEN); +-} +- +-static u64 aqr107_get_stat(struct phy_device *phydev, int index) +-{ +- const struct aqr107_hw_stat *stat = aqr107_hw_stats + index; +- int len_l = min(stat->size, 16); +- int len_h = stat->size - len_l; +- u64 ret; +- int val; +- +- val = phy_read_mmd(phydev, MDIO_MMD_C22EXT, stat->reg); +- if (val < 0) +- return U64_MAX; +- +- ret = val & GENMASK(len_l - 1, 0); +- if (len_h) { +- val = phy_read_mmd(phydev, MDIO_MMD_C22EXT, stat->reg + 1); +- if (val < 0) +- return U64_MAX; +- +- ret += (val & GENMASK(len_h - 1, 0)) << 16; +- } +- +- return ret; +-} +- +-static void aqr107_get_stats(struct phy_device *phydev, +- struct ethtool_stats *stats, u64 *data) +-{ +- struct aqr107_priv *priv = phydev->priv; +- u64 val; +- int i; +- +- for (i = 0; i < AQR107_SGMII_STAT_SZ; i++) { +- val = aqr107_get_stat(phydev, i); +- if (val == U64_MAX) +- phydev_err(phydev, "Reading HW Statistics failed for %s\n", +- aqr107_hw_stats[i].name); +- else +- priv->sgmii_stats[i] += val; +- +- data[i] = priv->sgmii_stats[i]; +- } +-} +- +-static int aqr_config_aneg(struct phy_device *phydev) +-{ +- bool changed = false; +- u16 reg; +- int ret; +- +- if (phydev->autoneg == AUTONEG_DISABLE) +- return genphy_c45_pma_setup_forced(phydev); +- +- ret = genphy_c45_an_config_aneg(phydev); +- if (ret < 0) +- return ret; +- if (ret > 0) +- changed = true; +- +- /* Clause 45 has no standardized support for 1000BaseT, therefore +- * use vendor registers for this mode. +- */ +- reg = 0; +- if (linkmode_test_bit(ETHTOOL_LINK_MODE_1000baseT_Full_BIT, +- phydev->advertising)) +- reg |= MDIO_AN_VEND_PROV_1000BASET_FULL; +- +- if (linkmode_test_bit(ETHTOOL_LINK_MODE_1000baseT_Half_BIT, +- phydev->advertising)) +- reg |= MDIO_AN_VEND_PROV_1000BASET_HALF; +- +- /* Handle the case when the 2.5G and 5G speeds are not advertised */ +- if (linkmode_test_bit(ETHTOOL_LINK_MODE_2500baseT_Full_BIT, +- phydev->advertising)) +- reg |= MDIO_AN_VEND_PROV_2500BASET_FULL; +- +- if (linkmode_test_bit(ETHTOOL_LINK_MODE_5000baseT_Full_BIT, +- phydev->advertising)) +- reg |= MDIO_AN_VEND_PROV_5000BASET_FULL; +- +- ret = phy_modify_mmd_changed(phydev, MDIO_MMD_AN, MDIO_AN_VEND_PROV, +- MDIO_AN_VEND_PROV_1000BASET_HALF | +- MDIO_AN_VEND_PROV_1000BASET_FULL | +- MDIO_AN_VEND_PROV_2500BASET_FULL | +- MDIO_AN_VEND_PROV_5000BASET_FULL, reg); +- if (ret < 0) +- return ret; +- if (ret > 0) +- changed = true; +- +- return genphy_c45_check_and_restart_aneg(phydev, changed); +-} +- +-static int aqr_config_intr(struct phy_device *phydev) +-{ +- bool en = phydev->interrupts == PHY_INTERRUPT_ENABLED; +- int err; +- +- if (en) { +- /* Clear any pending interrupts before enabling them */ +- err = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_TX_VEND_INT_STATUS2); +- if (err < 0) +- return err; +- } +- +- err = phy_write_mmd(phydev, MDIO_MMD_AN, MDIO_AN_TX_VEND_INT_MASK2, +- en ? MDIO_AN_TX_VEND_INT_MASK2_LINK : 0); +- if (err < 0) +- return err; +- +- err = phy_write_mmd(phydev, MDIO_MMD_VEND1, VEND1_GLOBAL_INT_STD_MASK, +- en ? VEND1_GLOBAL_INT_STD_MASK_ALL : 0); +- if (err < 0) +- return err; +- +- err = phy_write_mmd(phydev, MDIO_MMD_VEND1, VEND1_GLOBAL_INT_VEND_MASK, +- en ? VEND1_GLOBAL_INT_VEND_MASK_GLOBAL3 | +- VEND1_GLOBAL_INT_VEND_MASK_AN : 0); +- if (err < 0) +- return err; +- +- if (!en) { +- /* Clear any pending interrupts after we have disabled them */ +- err = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_TX_VEND_INT_STATUS2); +- if (err < 0) +- return err; +- } +- +- return 0; +-} +- +-static irqreturn_t aqr_handle_interrupt(struct phy_device *phydev) +-{ +- int irq_status; +- +- irq_status = phy_read_mmd(phydev, MDIO_MMD_AN, +- MDIO_AN_TX_VEND_INT_STATUS2); +- if (irq_status < 0) { +- phy_error(phydev); +- return IRQ_NONE; +- } +- +- if (!(irq_status & MDIO_AN_TX_VEND_INT_STATUS2_MASK)) +- return IRQ_NONE; +- +- phy_trigger_machine(phydev); +- +- return IRQ_HANDLED; +-} +- +-static int aqr_read_status(struct phy_device *phydev) +-{ +- int val; +- +- if (phydev->autoneg == AUTONEG_ENABLE) { +- val = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_RX_LP_STAT1); +- if (val < 0) +- return val; +- +- linkmode_mod_bit(ETHTOOL_LINK_MODE_1000baseT_Full_BIT, +- phydev->lp_advertising, +- val & MDIO_AN_RX_LP_STAT1_1000BASET_FULL); +- linkmode_mod_bit(ETHTOOL_LINK_MODE_1000baseT_Half_BIT, +- phydev->lp_advertising, +- val & MDIO_AN_RX_LP_STAT1_1000BASET_HALF); +- } +- +- return genphy_c45_read_status(phydev); +-} +- +-static int aqr107_read_rate(struct phy_device *phydev) +-{ +- u32 config_reg; +- int val; +- +- val = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_TX_VEND_STATUS1); +- if (val < 0) +- return val; +- +- if (val & MDIO_AN_TX_VEND_STATUS1_FULL_DUPLEX) +- phydev->duplex = DUPLEX_FULL; +- else +- phydev->duplex = DUPLEX_HALF; +- +- switch (FIELD_GET(MDIO_AN_TX_VEND_STATUS1_RATE_MASK, val)) { +- case MDIO_AN_TX_VEND_STATUS1_10BASET: +- phydev->speed = SPEED_10; +- config_reg = VEND1_GLOBAL_CFG_10M; +- break; +- case MDIO_AN_TX_VEND_STATUS1_100BASETX: +- phydev->speed = SPEED_100; +- config_reg = VEND1_GLOBAL_CFG_100M; +- break; +- case MDIO_AN_TX_VEND_STATUS1_1000BASET: +- phydev->speed = SPEED_1000; +- config_reg = VEND1_GLOBAL_CFG_1G; +- break; +- case MDIO_AN_TX_VEND_STATUS1_2500BASET: +- phydev->speed = SPEED_2500; +- config_reg = VEND1_GLOBAL_CFG_2_5G; +- break; +- case MDIO_AN_TX_VEND_STATUS1_5000BASET: +- phydev->speed = SPEED_5000; +- config_reg = VEND1_GLOBAL_CFG_5G; +- break; +- case MDIO_AN_TX_VEND_STATUS1_10GBASET: +- phydev->speed = SPEED_10000; +- config_reg = VEND1_GLOBAL_CFG_10G; +- break; +- default: +- phydev->speed = SPEED_UNKNOWN; +- return 0; +- } +- +- val = phy_read_mmd(phydev, MDIO_MMD_VEND1, config_reg); +- if (val < 0) +- return val; +- +- if (FIELD_GET(VEND1_GLOBAL_CFG_RATE_ADAPT, val) == +- VEND1_GLOBAL_CFG_RATE_ADAPT_PAUSE) +- phydev->rate_matching = RATE_MATCH_PAUSE; +- else +- phydev->rate_matching = RATE_MATCH_NONE; +- +- return 0; +-} +- +-static int aqr107_read_status(struct phy_device *phydev) +-{ +- int val, ret; +- +- ret = aqr_read_status(phydev); +- if (ret) +- return ret; +- +- if (!phydev->link || phydev->autoneg == AUTONEG_DISABLE) +- return 0; +- +- val = phy_read_mmd(phydev, MDIO_MMD_PHYXS, MDIO_PHYXS_VEND_IF_STATUS); +- if (val < 0) +- return val; +- +- switch (FIELD_GET(MDIO_PHYXS_VEND_IF_STATUS_TYPE_MASK, val)) { +- case MDIO_PHYXS_VEND_IF_STATUS_TYPE_KR: +- phydev->interface = PHY_INTERFACE_MODE_10GKR; +- break; +- case MDIO_PHYXS_VEND_IF_STATUS_TYPE_KX: +- phydev->interface = PHY_INTERFACE_MODE_1000BASEKX; +- break; +- case MDIO_PHYXS_VEND_IF_STATUS_TYPE_XFI: +- phydev->interface = PHY_INTERFACE_MODE_10GBASER; +- break; +- case MDIO_PHYXS_VEND_IF_STATUS_TYPE_USXGMII: +- phydev->interface = PHY_INTERFACE_MODE_USXGMII; +- break; +- case MDIO_PHYXS_VEND_IF_STATUS_TYPE_XAUI: +- phydev->interface = PHY_INTERFACE_MODE_XAUI; +- break; +- case MDIO_PHYXS_VEND_IF_STATUS_TYPE_SGMII: +- phydev->interface = PHY_INTERFACE_MODE_SGMII; +- break; +- case MDIO_PHYXS_VEND_IF_STATUS_TYPE_RXAUI: +- phydev->interface = PHY_INTERFACE_MODE_RXAUI; +- break; +- case MDIO_PHYXS_VEND_IF_STATUS_TYPE_OCSGMII: +- phydev->interface = PHY_INTERFACE_MODE_2500BASEX; +- break; +- default: +- phydev->interface = PHY_INTERFACE_MODE_NA; +- break; +- } +- +- /* Read possibly downshifted rate from vendor register */ +- return aqr107_read_rate(phydev); +-} +- +-static int aqr107_get_downshift(struct phy_device *phydev, u8 *data) +-{ +- int val, cnt, enable; +- +- val = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_VEND_PROV); +- if (val < 0) +- return val; +- +- enable = FIELD_GET(MDIO_AN_VEND_PROV_DOWNSHIFT_EN, val); +- cnt = FIELD_GET(MDIO_AN_VEND_PROV_DOWNSHIFT_MASK, val); +- +- *data = enable && cnt ? cnt : DOWNSHIFT_DEV_DISABLE; +- +- return 0; +-} +- +-static int aqr107_set_downshift(struct phy_device *phydev, u8 cnt) +-{ +- int val = 0; +- +- if (!FIELD_FIT(MDIO_AN_VEND_PROV_DOWNSHIFT_MASK, cnt)) +- return -E2BIG; +- +- if (cnt != DOWNSHIFT_DEV_DISABLE) { +- val = MDIO_AN_VEND_PROV_DOWNSHIFT_EN; +- val |= FIELD_PREP(MDIO_AN_VEND_PROV_DOWNSHIFT_MASK, cnt); +- } +- +- return phy_modify_mmd(phydev, MDIO_MMD_AN, MDIO_AN_VEND_PROV, +- MDIO_AN_VEND_PROV_DOWNSHIFT_EN | +- MDIO_AN_VEND_PROV_DOWNSHIFT_MASK, val); +-} +- +-static int aqr107_get_tunable(struct phy_device *phydev, +- struct ethtool_tunable *tuna, void *data) +-{ +- switch (tuna->id) { +- case ETHTOOL_PHY_DOWNSHIFT: +- return aqr107_get_downshift(phydev, data); +- default: +- return -EOPNOTSUPP; +- } +-} +- +-static int aqr107_set_tunable(struct phy_device *phydev, +- struct ethtool_tunable *tuna, const void *data) +-{ +- switch (tuna->id) { +- case ETHTOOL_PHY_DOWNSHIFT: +- return aqr107_set_downshift(phydev, *(const u8 *)data); +- default: +- return -EOPNOTSUPP; +- } +-} +- +-/* If we configure settings whilst firmware is still initializing the chip, +- * then these settings may be overwritten. Therefore make sure chip +- * initialization has completed. Use presence of the firmware ID as +- * indicator for initialization having completed. +- * The chip also provides a "reset completed" bit, but it's cleared after +- * read. Therefore function would time out if called again. +- */ +-static int aqr107_wait_reset_complete(struct phy_device *phydev) +-{ +- int val; +- +- return phy_read_mmd_poll_timeout(phydev, MDIO_MMD_VEND1, +- VEND1_GLOBAL_FW_ID, val, val != 0, +- 20000, 2000000, false); +-} +- +-static void aqr107_chip_info(struct phy_device *phydev) +-{ +- u8 fw_major, fw_minor, build_id, prov_id; +- int val; +- +- val = phy_read_mmd(phydev, MDIO_MMD_VEND1, VEND1_GLOBAL_FW_ID); +- if (val < 0) +- return; +- +- fw_major = FIELD_GET(VEND1_GLOBAL_FW_ID_MAJOR, val); +- fw_minor = FIELD_GET(VEND1_GLOBAL_FW_ID_MINOR, val); +- +- val = phy_read_mmd(phydev, MDIO_MMD_VEND1, VEND1_GLOBAL_RSVD_STAT1); +- if (val < 0) +- return; +- +- build_id = FIELD_GET(VEND1_GLOBAL_RSVD_STAT1_FW_BUILD_ID, val); +- prov_id = FIELD_GET(VEND1_GLOBAL_RSVD_STAT1_PROV_ID, val); +- +- phydev_dbg(phydev, "FW %u.%u, Build %u, Provisioning %u\n", +- fw_major, fw_minor, build_id, prov_id); +-} +- +-static int aqr107_config_init(struct phy_device *phydev) +-{ +- int ret; +- +- /* Check that the PHY interface type is compatible */ +- if (phydev->interface != PHY_INTERFACE_MODE_SGMII && +- phydev->interface != PHY_INTERFACE_MODE_1000BASEKX && +- phydev->interface != PHY_INTERFACE_MODE_2500BASEX && +- phydev->interface != PHY_INTERFACE_MODE_XGMII && +- phydev->interface != PHY_INTERFACE_MODE_USXGMII && +- phydev->interface != PHY_INTERFACE_MODE_10GKR && +- phydev->interface != PHY_INTERFACE_MODE_10GBASER && +- phydev->interface != PHY_INTERFACE_MODE_XAUI && +- phydev->interface != PHY_INTERFACE_MODE_RXAUI) +- return -ENODEV; +- +- WARN(phydev->interface == PHY_INTERFACE_MODE_XGMII, +- "Your devicetree is out of date, please update it. The AQR107 family doesn't support XGMII, maybe you mean USXGMII.\n"); +- +- ret = aqr107_wait_reset_complete(phydev); +- if (!ret) +- aqr107_chip_info(phydev); +- +- return aqr107_set_downshift(phydev, MDIO_AN_VEND_PROV_DOWNSHIFT_DFLT); +-} +- +-static int aqcs109_config_init(struct phy_device *phydev) +-{ +- int ret; +- +- /* Check that the PHY interface type is compatible */ +- if (phydev->interface != PHY_INTERFACE_MODE_SGMII && +- phydev->interface != PHY_INTERFACE_MODE_2500BASEX) +- return -ENODEV; +- +- ret = aqr107_wait_reset_complete(phydev); +- if (!ret) +- aqr107_chip_info(phydev); +- +- /* AQCS109 belongs to a chip family partially supporting 10G and 5G. +- * PMA speed ability bits are the same for all members of the family, +- * AQCS109 however supports speeds up to 2.5G only. +- */ +- phy_set_max_speed(phydev, SPEED_2500); +- +- return aqr107_set_downshift(phydev, MDIO_AN_VEND_PROV_DOWNSHIFT_DFLT); +-} +- +-static void aqr107_link_change_notify(struct phy_device *phydev) +-{ +- u8 fw_major, fw_minor; +- bool downshift, short_reach, afr; +- int mode, val; +- +- if (phydev->state != PHY_RUNNING || phydev->autoneg == AUTONEG_DISABLE) +- return; +- +- val = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_RX_LP_STAT1); +- /* call failed or link partner is no Aquantia PHY */ +- if (val < 0 || !(val & MDIO_AN_RX_LP_STAT1_AQ_PHY)) +- return; +- +- short_reach = val & MDIO_AN_RX_LP_STAT1_SHORT_REACH; +- downshift = val & MDIO_AN_RX_LP_STAT1_AQRATE_DOWNSHIFT; +- +- val = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_RX_LP_STAT4); +- if (val < 0) +- return; +- +- fw_major = FIELD_GET(MDIO_AN_RX_LP_STAT4_FW_MAJOR, val); +- fw_minor = FIELD_GET(MDIO_AN_RX_LP_STAT4_FW_MINOR, val); +- +- val = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_RX_VEND_STAT3); +- if (val < 0) +- return; +- +- afr = val & MDIO_AN_RX_VEND_STAT3_AFR; +- +- phydev_dbg(phydev, "Link partner is Aquantia PHY, FW %u.%u%s%s%s\n", +- fw_major, fw_minor, +- short_reach ? ", short reach mode" : "", +- downshift ? ", fast-retrain downshift advertised" : "", +- afr ? ", fast reframe advertised" : ""); +- +- val = phy_read_mmd(phydev, MDIO_MMD_VEND1, VEND1_GLOBAL_RSVD_STAT9); +- if (val < 0) +- return; +- +- mode = FIELD_GET(VEND1_GLOBAL_RSVD_STAT9_MODE, val); +- if (mode == VEND1_GLOBAL_RSVD_STAT9_1000BT2) +- phydev_info(phydev, "Aquantia 1000Base-T2 mode active\n"); +-} +- +-static int aqr107_wait_processor_intensive_op(struct phy_device *phydev) +-{ +- int val, err; +- +- /* The datasheet notes to wait at least 1ms after issuing a +- * processor intensive operation before checking. +- * We cannot use the 'sleep_before_read' parameter of read_poll_timeout +- * because that just determines the maximum time slept, not the minimum. +- */ +- usleep_range(1000, 5000); +- +- err = phy_read_mmd_poll_timeout(phydev, MDIO_MMD_VEND1, +- VEND1_GLOBAL_GEN_STAT2, val, +- !(val & VEND1_GLOBAL_GEN_STAT2_OP_IN_PROG), +- AQR107_OP_IN_PROG_SLEEP, +- AQR107_OP_IN_PROG_TIMEOUT, false); +- if (err) { +- phydev_err(phydev, "timeout: processor-intensive MDIO operation\n"); +- return err; +- } +- +- return 0; +-} +- +-static int aqr107_get_rate_matching(struct phy_device *phydev, +- phy_interface_t iface) +-{ +- if (iface == PHY_INTERFACE_MODE_10GBASER || +- iface == PHY_INTERFACE_MODE_2500BASEX || +- iface == PHY_INTERFACE_MODE_NA) +- return RATE_MATCH_PAUSE; +- return RATE_MATCH_NONE; +-} +- +-static int aqr107_suspend(struct phy_device *phydev) +-{ +- int err; +- +- err = phy_set_bits_mmd(phydev, MDIO_MMD_VEND1, MDIO_CTRL1, +- MDIO_CTRL1_LPOWER); +- if (err) +- return err; +- +- return aqr107_wait_processor_intensive_op(phydev); +-} +- +-static int aqr107_resume(struct phy_device *phydev) +-{ +- int err; +- +- err = phy_clear_bits_mmd(phydev, MDIO_MMD_VEND1, MDIO_CTRL1, +- MDIO_CTRL1_LPOWER); +- if (err) +- return err; +- +- return aqr107_wait_processor_intensive_op(phydev); +-} +- +-static int aqr107_probe(struct phy_device *phydev) +-{ +- phydev->priv = devm_kzalloc(&phydev->mdio.dev, +- sizeof(struct aqr107_priv), GFP_KERNEL); +- if (!phydev->priv) +- return -ENOMEM; +- +- return aqr_hwmon_probe(phydev); +-} +- +-static struct phy_driver aqr_driver[] = { +-{ +- PHY_ID_MATCH_MODEL(PHY_ID_AQ1202), +- .name = "Aquantia AQ1202", +- .config_aneg = aqr_config_aneg, +- .config_intr = aqr_config_intr, +- .handle_interrupt = aqr_handle_interrupt, +- .read_status = aqr_read_status, +-}, +-{ +- PHY_ID_MATCH_MODEL(PHY_ID_AQ2104), +- .name = "Aquantia AQ2104", +- .config_aneg = aqr_config_aneg, +- .config_intr = aqr_config_intr, +- .handle_interrupt = aqr_handle_interrupt, +- .read_status = aqr_read_status, +-}, +-{ +- PHY_ID_MATCH_MODEL(PHY_ID_AQR105), +- .name = "Aquantia AQR105", +- .config_aneg = aqr_config_aneg, +- .config_intr = aqr_config_intr, +- .handle_interrupt = aqr_handle_interrupt, +- .read_status = aqr_read_status, +- .suspend = aqr107_suspend, +- .resume = aqr107_resume, +-}, +-{ +- PHY_ID_MATCH_MODEL(PHY_ID_AQR106), +- .name = "Aquantia AQR106", +- .config_aneg = aqr_config_aneg, +- .config_intr = aqr_config_intr, +- .handle_interrupt = aqr_handle_interrupt, +- .read_status = aqr_read_status, +-}, +-{ +- PHY_ID_MATCH_MODEL(PHY_ID_AQR107), +- .name = "Aquantia AQR107", +- .probe = aqr107_probe, +- .get_rate_matching = aqr107_get_rate_matching, +- .config_init = aqr107_config_init, +- .config_aneg = aqr_config_aneg, +- .config_intr = aqr_config_intr, +- .handle_interrupt = aqr_handle_interrupt, +- .read_status = aqr107_read_status, +- .get_tunable = aqr107_get_tunable, +- .set_tunable = aqr107_set_tunable, +- .suspend = aqr107_suspend, +- .resume = aqr107_resume, +- .get_sset_count = aqr107_get_sset_count, +- .get_strings = aqr107_get_strings, +- .get_stats = aqr107_get_stats, +- .link_change_notify = aqr107_link_change_notify, +-}, +-{ +- PHY_ID_MATCH_MODEL(PHY_ID_AQCS109), +- .name = "Aquantia AQCS109", +- .probe = aqr107_probe, +- .get_rate_matching = aqr107_get_rate_matching, +- .config_init = aqcs109_config_init, +- .config_aneg = aqr_config_aneg, +- .config_intr = aqr_config_intr, +- .handle_interrupt = aqr_handle_interrupt, +- .read_status = aqr107_read_status, +- .get_tunable = aqr107_get_tunable, +- .set_tunable = aqr107_set_tunable, +- .suspend = aqr107_suspend, +- .resume = aqr107_resume, +- .get_sset_count = aqr107_get_sset_count, +- .get_strings = aqr107_get_strings, +- .get_stats = aqr107_get_stats, +- .link_change_notify = aqr107_link_change_notify, +-}, +-{ +- PHY_ID_MATCH_MODEL(PHY_ID_AQR405), +- .name = "Aquantia AQR405", +- .config_aneg = aqr_config_aneg, +- .config_intr = aqr_config_intr, +- .handle_interrupt = aqr_handle_interrupt, +- .read_status = aqr_read_status, +-}, +-{ +- PHY_ID_MATCH_MODEL(PHY_ID_AQR113C), +- .name = "Aquantia AQR113C", +- .probe = aqr107_probe, +- .get_rate_matching = aqr107_get_rate_matching, +- .config_init = aqr107_config_init, +- .config_aneg = aqr_config_aneg, +- .config_intr = aqr_config_intr, +- .handle_interrupt = aqr_handle_interrupt, +- .read_status = aqr107_read_status, +- .get_tunable = aqr107_get_tunable, +- .set_tunable = aqr107_set_tunable, +- .suspend = aqr107_suspend, +- .resume = aqr107_resume, +- .get_sset_count = aqr107_get_sset_count, +- .get_strings = aqr107_get_strings, +- .get_stats = aqr107_get_stats, +- .link_change_notify = aqr107_link_change_notify, +-}, +-}; +- +-module_phy_driver(aqr_driver); +- +-static struct mdio_device_id __maybe_unused aqr_tbl[] = { +- { PHY_ID_MATCH_MODEL(PHY_ID_AQ1202) }, +- { PHY_ID_MATCH_MODEL(PHY_ID_AQ2104) }, +- { PHY_ID_MATCH_MODEL(PHY_ID_AQR105) }, +- { PHY_ID_MATCH_MODEL(PHY_ID_AQR106) }, +- { PHY_ID_MATCH_MODEL(PHY_ID_AQR107) }, +- { PHY_ID_MATCH_MODEL(PHY_ID_AQCS109) }, +- { PHY_ID_MATCH_MODEL(PHY_ID_AQR405) }, +- { PHY_ID_MATCH_MODEL(PHY_ID_AQR113C) }, +- { } +-}; +- +-MODULE_DEVICE_TABLE(mdio, aqr_tbl); +- +-MODULE_DESCRIPTION("Aquantia PHY driver"); +-MODULE_AUTHOR("Shaohui Xie "); +-MODULE_LICENSE("GPL v2"); diff --git a/target/linux/generic/backport-6.1/702-02-v6.7-net-phy-aquantia-move-MMD_VEND-define-to-header.patch b/target/linux/generic/backport-6.1/702-02-v6.7-net-phy-aquantia-move-MMD_VEND-define-to-header.patch new file mode 100644 index 000000000..2b9452272 --- /dev/null +++ b/target/linux/generic/backport-6.1/702-02-v6.7-net-phy-aquantia-move-MMD_VEND-define-to-header.patch @@ -0,0 +1,183 @@ +From e1fbfa4a995d42e02e22b0dff2f8b4fdee1504b3 Mon Sep 17 00:00:00 2001 +From: Christian Marangi +Date: Tue, 14 Nov 2023 15:08:42 +0100 +Subject: [PATCH 2/3] net: phy: aquantia: move MMD_VEND define to header + +Move MMD_VEND define to header to clean things up and in preparation for +firmware loading support that require some define placed in +aquantia_main. + +Signed-off-by: Christian Marangi +Reviewed-by: Andrew Lunn +Signed-off-by: David S. Miller +--- + drivers/net/phy/aquantia/aquantia.h | 69 +++++++++++++++++++++++ + drivers/net/phy/aquantia/aquantia_hwmon.c | 14 ----- + drivers/net/phy/aquantia/aquantia_main.c | 55 ------------------ + 3 files changed, 69 insertions(+), 69 deletions(-) + +--- a/drivers/net/phy/aquantia/aquantia.h ++++ b/drivers/net/phy/aquantia/aquantia.h +@@ -9,6 +9,75 @@ + #include + #include + ++/* Vendor specific 1, MDIO_MMD_VEND1 */ ++#define VEND1_GLOBAL_FW_ID 0x0020 ++#define VEND1_GLOBAL_FW_ID_MAJOR GENMASK(15, 8) ++#define VEND1_GLOBAL_FW_ID_MINOR GENMASK(7, 0) ++ ++/* The following registers all have similar layouts; first the registers... */ ++#define VEND1_GLOBAL_CFG_10M 0x0310 ++#define VEND1_GLOBAL_CFG_100M 0x031b ++#define VEND1_GLOBAL_CFG_1G 0x031c ++#define VEND1_GLOBAL_CFG_2_5G 0x031d ++#define VEND1_GLOBAL_CFG_5G 0x031e ++#define VEND1_GLOBAL_CFG_10G 0x031f ++/* ...and now the fields */ ++#define VEND1_GLOBAL_CFG_RATE_ADAPT GENMASK(8, 7) ++#define VEND1_GLOBAL_CFG_RATE_ADAPT_NONE 0 ++#define VEND1_GLOBAL_CFG_RATE_ADAPT_USX 1 ++#define VEND1_GLOBAL_CFG_RATE_ADAPT_PAUSE 2 ++ ++/* Vendor specific 1, MDIO_MMD_VEND2 */ ++#define VEND1_THERMAL_PROV_HIGH_TEMP_FAIL 0xc421 ++#define VEND1_THERMAL_PROV_LOW_TEMP_FAIL 0xc422 ++#define VEND1_THERMAL_PROV_HIGH_TEMP_WARN 0xc423 ++#define VEND1_THERMAL_PROV_LOW_TEMP_WARN 0xc424 ++#define VEND1_THERMAL_STAT1 0xc820 ++#define VEND1_THERMAL_STAT2 0xc821 ++#define VEND1_THERMAL_STAT2_VALID BIT(0) ++#define VEND1_GENERAL_STAT1 0xc830 ++#define VEND1_GENERAL_STAT1_HIGH_TEMP_FAIL BIT(14) ++#define VEND1_GENERAL_STAT1_LOW_TEMP_FAIL BIT(13) ++#define VEND1_GENERAL_STAT1_HIGH_TEMP_WARN BIT(12) ++#define VEND1_GENERAL_STAT1_LOW_TEMP_WARN BIT(11) ++ ++#define VEND1_GLOBAL_GEN_STAT2 0xc831 ++#define VEND1_GLOBAL_GEN_STAT2_OP_IN_PROG BIT(15) ++ ++#define VEND1_GLOBAL_RSVD_STAT1 0xc885 ++#define VEND1_GLOBAL_RSVD_STAT1_FW_BUILD_ID GENMASK(7, 4) ++#define VEND1_GLOBAL_RSVD_STAT1_PROV_ID GENMASK(3, 0) ++ ++#define VEND1_GLOBAL_RSVD_STAT9 0xc88d ++#define VEND1_GLOBAL_RSVD_STAT9_MODE GENMASK(7, 0) ++#define VEND1_GLOBAL_RSVD_STAT9_1000BT2 0x23 ++ ++#define VEND1_GLOBAL_INT_STD_STATUS 0xfc00 ++#define VEND1_GLOBAL_INT_VEND_STATUS 0xfc01 ++ ++#define VEND1_GLOBAL_INT_STD_MASK 0xff00 ++#define VEND1_GLOBAL_INT_STD_MASK_PMA1 BIT(15) ++#define VEND1_GLOBAL_INT_STD_MASK_PMA2 BIT(14) ++#define VEND1_GLOBAL_INT_STD_MASK_PCS1 BIT(13) ++#define VEND1_GLOBAL_INT_STD_MASK_PCS2 BIT(12) ++#define VEND1_GLOBAL_INT_STD_MASK_PCS3 BIT(11) ++#define VEND1_GLOBAL_INT_STD_MASK_PHY_XS1 BIT(10) ++#define VEND1_GLOBAL_INT_STD_MASK_PHY_XS2 BIT(9) ++#define VEND1_GLOBAL_INT_STD_MASK_AN1 BIT(8) ++#define VEND1_GLOBAL_INT_STD_MASK_AN2 BIT(7) ++#define VEND1_GLOBAL_INT_STD_MASK_GBE BIT(6) ++#define VEND1_GLOBAL_INT_STD_MASK_ALL BIT(0) ++ ++#define VEND1_GLOBAL_INT_VEND_MASK 0xff01 ++#define VEND1_GLOBAL_INT_VEND_MASK_PMA BIT(15) ++#define VEND1_GLOBAL_INT_VEND_MASK_PCS BIT(14) ++#define VEND1_GLOBAL_INT_VEND_MASK_PHY_XS BIT(13) ++#define VEND1_GLOBAL_INT_VEND_MASK_AN BIT(12) ++#define VEND1_GLOBAL_INT_VEND_MASK_GBE BIT(11) ++#define VEND1_GLOBAL_INT_VEND_MASK_GLOBAL1 BIT(2) ++#define VEND1_GLOBAL_INT_VEND_MASK_GLOBAL2 BIT(1) ++#define VEND1_GLOBAL_INT_VEND_MASK_GLOBAL3 BIT(0) ++ + #if IS_REACHABLE(CONFIG_HWMON) + int aqr_hwmon_probe(struct phy_device *phydev); + #else +--- a/drivers/net/phy/aquantia/aquantia_hwmon.c ++++ b/drivers/net/phy/aquantia/aquantia_hwmon.c +@@ -13,20 +13,6 @@ + + #include "aquantia.h" + +-/* Vendor specific 1, MDIO_MMD_VEND2 */ +-#define VEND1_THERMAL_PROV_HIGH_TEMP_FAIL 0xc421 +-#define VEND1_THERMAL_PROV_LOW_TEMP_FAIL 0xc422 +-#define VEND1_THERMAL_PROV_HIGH_TEMP_WARN 0xc423 +-#define VEND1_THERMAL_PROV_LOW_TEMP_WARN 0xc424 +-#define VEND1_THERMAL_STAT1 0xc820 +-#define VEND1_THERMAL_STAT2 0xc821 +-#define VEND1_THERMAL_STAT2_VALID BIT(0) +-#define VEND1_GENERAL_STAT1 0xc830 +-#define VEND1_GENERAL_STAT1_HIGH_TEMP_FAIL BIT(14) +-#define VEND1_GENERAL_STAT1_LOW_TEMP_FAIL BIT(13) +-#define VEND1_GENERAL_STAT1_HIGH_TEMP_WARN BIT(12) +-#define VEND1_GENERAL_STAT1_LOW_TEMP_WARN BIT(11) +- + #if IS_REACHABLE(CONFIG_HWMON) + + static umode_t aqr_hwmon_is_visible(const void *data, +--- a/drivers/net/phy/aquantia/aquantia_main.c ++++ b/drivers/net/phy/aquantia/aquantia_main.c +@@ -89,61 +89,6 @@ + #define MDIO_C22EXT_STAT_SGMII_TX_FRAME_ALIGN_ERR 0xd31a + #define MDIO_C22EXT_STAT_SGMII_TX_RUNT_FRAMES 0xd31b + +-/* Vendor specific 1, MDIO_MMD_VEND1 */ +-#define VEND1_GLOBAL_FW_ID 0x0020 +-#define VEND1_GLOBAL_FW_ID_MAJOR GENMASK(15, 8) +-#define VEND1_GLOBAL_FW_ID_MINOR GENMASK(7, 0) +- +-#define VEND1_GLOBAL_GEN_STAT2 0xc831 +-#define VEND1_GLOBAL_GEN_STAT2_OP_IN_PROG BIT(15) +- +-/* The following registers all have similar layouts; first the registers... */ +-#define VEND1_GLOBAL_CFG_10M 0x0310 +-#define VEND1_GLOBAL_CFG_100M 0x031b +-#define VEND1_GLOBAL_CFG_1G 0x031c +-#define VEND1_GLOBAL_CFG_2_5G 0x031d +-#define VEND1_GLOBAL_CFG_5G 0x031e +-#define VEND1_GLOBAL_CFG_10G 0x031f +-/* ...and now the fields */ +-#define VEND1_GLOBAL_CFG_RATE_ADAPT GENMASK(8, 7) +-#define VEND1_GLOBAL_CFG_RATE_ADAPT_NONE 0 +-#define VEND1_GLOBAL_CFG_RATE_ADAPT_USX 1 +-#define VEND1_GLOBAL_CFG_RATE_ADAPT_PAUSE 2 +- +-#define VEND1_GLOBAL_RSVD_STAT1 0xc885 +-#define VEND1_GLOBAL_RSVD_STAT1_FW_BUILD_ID GENMASK(7, 4) +-#define VEND1_GLOBAL_RSVD_STAT1_PROV_ID GENMASK(3, 0) +- +-#define VEND1_GLOBAL_RSVD_STAT9 0xc88d +-#define VEND1_GLOBAL_RSVD_STAT9_MODE GENMASK(7, 0) +-#define VEND1_GLOBAL_RSVD_STAT9_1000BT2 0x23 +- +-#define VEND1_GLOBAL_INT_STD_STATUS 0xfc00 +-#define VEND1_GLOBAL_INT_VEND_STATUS 0xfc01 +- +-#define VEND1_GLOBAL_INT_STD_MASK 0xff00 +-#define VEND1_GLOBAL_INT_STD_MASK_PMA1 BIT(15) +-#define VEND1_GLOBAL_INT_STD_MASK_PMA2 BIT(14) +-#define VEND1_GLOBAL_INT_STD_MASK_PCS1 BIT(13) +-#define VEND1_GLOBAL_INT_STD_MASK_PCS2 BIT(12) +-#define VEND1_GLOBAL_INT_STD_MASK_PCS3 BIT(11) +-#define VEND1_GLOBAL_INT_STD_MASK_PHY_XS1 BIT(10) +-#define VEND1_GLOBAL_INT_STD_MASK_PHY_XS2 BIT(9) +-#define VEND1_GLOBAL_INT_STD_MASK_AN1 BIT(8) +-#define VEND1_GLOBAL_INT_STD_MASK_AN2 BIT(7) +-#define VEND1_GLOBAL_INT_STD_MASK_GBE BIT(6) +-#define VEND1_GLOBAL_INT_STD_MASK_ALL BIT(0) +- +-#define VEND1_GLOBAL_INT_VEND_MASK 0xff01 +-#define VEND1_GLOBAL_INT_VEND_MASK_PMA BIT(15) +-#define VEND1_GLOBAL_INT_VEND_MASK_PCS BIT(14) +-#define VEND1_GLOBAL_INT_VEND_MASK_PHY_XS BIT(13) +-#define VEND1_GLOBAL_INT_VEND_MASK_AN BIT(12) +-#define VEND1_GLOBAL_INT_VEND_MASK_GBE BIT(11) +-#define VEND1_GLOBAL_INT_VEND_MASK_GLOBAL1 BIT(2) +-#define VEND1_GLOBAL_INT_VEND_MASK_GLOBAL2 BIT(1) +-#define VEND1_GLOBAL_INT_VEND_MASK_GLOBAL3 BIT(0) +- + /* Sleep and timeout for checking if the Processor-Intensive + * MDIO operation is finished + */ diff --git a/target/linux/generic/backport-6.1/702-03-v6.7-net-phy-aquantia-add-firmware-load-support.patch b/target/linux/generic/backport-6.1/702-03-v6.7-net-phy-aquantia-add-firmware-load-support.patch new file mode 100644 index 000000000..aa52b3baa --- /dev/null +++ b/target/linux/generic/backport-6.1/702-03-v6.7-net-phy-aquantia-add-firmware-load-support.patch @@ -0,0 +1,504 @@ +From e93984ebc1c82bd34f7a1b3391efaceee0a8ae96 Mon Sep 17 00:00:00 2001 +From: Robert Marko +Date: Tue, 14 Nov 2023 15:08:43 +0100 +Subject: [PATCH 3/3] net: phy: aquantia: add firmware load support + +Aquantia PHY-s require firmware to be loaded before they start operating. +It can be automatically loaded in case when there is a SPI-NOR connected +to Aquantia PHY-s or can be loaded from the host via MDIO. + +This patch adds support for loading the firmware via MDIO as in most cases +there is no SPI-NOR being used to save on cost. +Firmware loading code itself is ported from mainline U-boot with cleanups. + +The firmware has mixed values both in big and little endian. +PHY core itself is big-endian but it expects values to be in little-endian. +The firmware is little-endian but CRC-16 value for it is stored at the end +of firmware in big-endian. + +It seems the PHY does the conversion internally from firmware that is +little-endian to the PHY that is big-endian on using the mailbox +but mailbox returns a big-endian CRC-16 to verify the written data +integrity. + +Co-developed-by: Christian Marangi +Signed-off-by: Robert Marko +Signed-off-by: Christian Marangi +Reviewed-by: Andrew Lunn +Signed-off-by: David S. Miller +--- + drivers/net/phy/aquantia/Kconfig | 1 + + drivers/net/phy/aquantia/Makefile | 2 +- + drivers/net/phy/aquantia/aquantia.h | 32 ++ + drivers/net/phy/aquantia/aquantia_firmware.c | 370 +++++++++++++++++++ + drivers/net/phy/aquantia/aquantia_main.c | 6 + + 5 files changed, 410 insertions(+), 1 deletion(-) + create mode 100644 drivers/net/phy/aquantia/aquantia_firmware.c + +--- a/drivers/net/phy/aquantia/Kconfig ++++ b/drivers/net/phy/aquantia/Kconfig +@@ -1,5 +1,6 @@ + # SPDX-License-Identifier: GPL-2.0-only + config AQUANTIA_PHY + tristate "Aquantia PHYs" ++ select CRC_CCITT + help + Currently supports the Aquantia AQ1202, AQ2104, AQR105, AQR405 +--- a/drivers/net/phy/aquantia/Makefile ++++ b/drivers/net/phy/aquantia/Makefile +@@ -1,5 +1,5 @@ + # SPDX-License-Identifier: GPL-2.0 +-aquantia-objs += aquantia_main.o ++aquantia-objs += aquantia_main.o aquantia_firmware.o + ifdef CONFIG_HWMON + aquantia-objs += aquantia_hwmon.o + endif +--- a/drivers/net/phy/aquantia/aquantia.h ++++ b/drivers/net/phy/aquantia/aquantia.h +@@ -10,10 +10,35 @@ + #include + + /* Vendor specific 1, MDIO_MMD_VEND1 */ ++#define VEND1_GLOBAL_SC 0x0 ++#define VEND1_GLOBAL_SC_SOFT_RESET BIT(15) ++#define VEND1_GLOBAL_SC_LOW_POWER BIT(11) ++ + #define VEND1_GLOBAL_FW_ID 0x0020 + #define VEND1_GLOBAL_FW_ID_MAJOR GENMASK(15, 8) + #define VEND1_GLOBAL_FW_ID_MINOR GENMASK(7, 0) + ++#define VEND1_GLOBAL_MAILBOX_INTERFACE1 0x0200 ++#define VEND1_GLOBAL_MAILBOX_INTERFACE1_EXECUTE BIT(15) ++#define VEND1_GLOBAL_MAILBOX_INTERFACE1_WRITE BIT(14) ++#define VEND1_GLOBAL_MAILBOX_INTERFACE1_CRC_RESET BIT(12) ++#define VEND1_GLOBAL_MAILBOX_INTERFACE1_BUSY BIT(8) ++ ++#define VEND1_GLOBAL_MAILBOX_INTERFACE2 0x0201 ++#define VEND1_GLOBAL_MAILBOX_INTERFACE3 0x0202 ++#define VEND1_GLOBAL_MAILBOX_INTERFACE3_MSW_ADDR_MASK GENMASK(15, 0) ++#define VEND1_GLOBAL_MAILBOX_INTERFACE3_MSW_ADDR(x) FIELD_PREP(VEND1_GLOBAL_MAILBOX_INTERFACE3_MSW_ADDR_MASK, (u16)((x) >> 16)) ++#define VEND1_GLOBAL_MAILBOX_INTERFACE4 0x0203 ++#define VEND1_GLOBAL_MAILBOX_INTERFACE4_LSW_ADDR_MASK GENMASK(15, 2) ++#define VEND1_GLOBAL_MAILBOX_INTERFACE4_LSW_ADDR(x) FIELD_PREP(VEND1_GLOBAL_MAILBOX_INTERFACE4_LSW_ADDR_MASK, (u16)(x)) ++ ++#define VEND1_GLOBAL_MAILBOX_INTERFACE5 0x0204 ++#define VEND1_GLOBAL_MAILBOX_INTERFACE5_MSW_DATA_MASK GENMASK(15, 0) ++#define VEND1_GLOBAL_MAILBOX_INTERFACE5_MSW_DATA(x) FIELD_PREP(VEND1_GLOBAL_MAILBOX_INTERFACE5_MSW_DATA_MASK, (u16)((x) >> 16)) ++#define VEND1_GLOBAL_MAILBOX_INTERFACE6 0x0205 ++#define VEND1_GLOBAL_MAILBOX_INTERFACE6_LSW_DATA_MASK GENMASK(15, 0) ++#define VEND1_GLOBAL_MAILBOX_INTERFACE6_LSW_DATA(x) FIELD_PREP(VEND1_GLOBAL_MAILBOX_INTERFACE6_LSW_DATA_MASK, (u16)(x)) ++ + /* The following registers all have similar layouts; first the registers... */ + #define VEND1_GLOBAL_CFG_10M 0x0310 + #define VEND1_GLOBAL_CFG_100M 0x031b +@@ -28,6 +53,11 @@ + #define VEND1_GLOBAL_CFG_RATE_ADAPT_PAUSE 2 + + /* Vendor specific 1, MDIO_MMD_VEND2 */ ++#define VEND1_GLOBAL_CONTROL2 0xc001 ++#define VEND1_GLOBAL_CONTROL2_UP_RUN_STALL_RST BIT(15) ++#define VEND1_GLOBAL_CONTROL2_UP_RUN_STALL_OVD BIT(6) ++#define VEND1_GLOBAL_CONTROL2_UP_RUN_STALL BIT(0) ++ + #define VEND1_THERMAL_PROV_HIGH_TEMP_FAIL 0xc421 + #define VEND1_THERMAL_PROV_LOW_TEMP_FAIL 0xc422 + #define VEND1_THERMAL_PROV_HIGH_TEMP_WARN 0xc423 +@@ -83,3 +113,5 @@ int aqr_hwmon_probe(struct phy_device *p + #else + static inline int aqr_hwmon_probe(struct phy_device *phydev) { return 0; } + #endif ++ ++int aqr_firmware_load(struct phy_device *phydev); +--- /dev/null ++++ b/drivers/net/phy/aquantia/aquantia_firmware.c +@@ -0,0 +1,370 @@ ++// SPDX-License-Identifier: GPL-2.0 ++ ++#include ++#include ++#include ++#include ++#include ++ ++#include ++ ++#include "aquantia.h" ++ ++#define UP_RESET_SLEEP 100 ++ ++/* addresses of memory segments in the phy */ ++#define DRAM_BASE_ADDR 0x3FFE0000 ++#define IRAM_BASE_ADDR 0x40000000 ++ ++/* firmware image format constants */ ++#define VERSION_STRING_SIZE 0x40 ++#define VERSION_STRING_OFFSET 0x0200 ++/* primary offset is written at an offset from the start of the fw blob */ ++#define PRIMARY_OFFSET_OFFSET 0x8 ++/* primary offset needs to be then added to a base offset */ ++#define PRIMARY_OFFSET_SHIFT 12 ++#define PRIMARY_OFFSET(x) ((x) << PRIMARY_OFFSET_SHIFT) ++#define HEADER_OFFSET 0x300 ++ ++struct aqr_fw_header { ++ u32 padding; ++ u8 iram_offset[3]; ++ u8 iram_size[3]; ++ u8 dram_offset[3]; ++ u8 dram_size[3]; ++} __packed; ++ ++enum aqr_fw_src { ++ AQR_FW_SRC_NVMEM = 0, ++ AQR_FW_SRC_FS, ++}; ++ ++static const char * const aqr_fw_src_string[] = { ++ [AQR_FW_SRC_NVMEM] = "NVMEM", ++ [AQR_FW_SRC_FS] = "FS", ++}; ++ ++/* AQR firmware doesn't have fixed offsets for iram and dram section ++ * but instead provide an header with the offset to use on reading ++ * and parsing the firmware. ++ * ++ * AQR firmware can't be trusted and each offset is validated to be ++ * not negative and be in the size of the firmware itself. ++ */ ++static bool aqr_fw_validate_get(size_t size, size_t offset, size_t get_size) ++{ ++ return offset + get_size <= size; ++} ++ ++static int aqr_fw_get_be16(const u8 *data, size_t offset, size_t size, u16 *value) ++{ ++ if (!aqr_fw_validate_get(size, offset, sizeof(u16))) ++ return -EINVAL; ++ ++ *value = get_unaligned_be16(data + offset); ++ ++ return 0; ++} ++ ++static int aqr_fw_get_le16(const u8 *data, size_t offset, size_t size, u16 *value) ++{ ++ if (!aqr_fw_validate_get(size, offset, sizeof(u16))) ++ return -EINVAL; ++ ++ *value = get_unaligned_le16(data + offset); ++ ++ return 0; ++} ++ ++static int aqr_fw_get_le24(const u8 *data, size_t offset, size_t size, u32 *value) ++{ ++ if (!aqr_fw_validate_get(size, offset, sizeof(u8) * 3)) ++ return -EINVAL; ++ ++ *value = get_unaligned_le24(data + offset); ++ ++ return 0; ++} ++ ++/* load data into the phy's memory */ ++static int aqr_fw_load_memory(struct phy_device *phydev, u32 addr, ++ const u8 *data, size_t len) ++{ ++ u16 crc = 0, up_crc; ++ size_t pos; ++ ++ /* PHY expect addr in LE */ ++ addr = (__force u32)cpu_to_le32(addr); ++ ++ phy_write_mmd(phydev, MDIO_MMD_VEND1, ++ VEND1_GLOBAL_MAILBOX_INTERFACE1, ++ VEND1_GLOBAL_MAILBOX_INTERFACE1_CRC_RESET); ++ phy_write_mmd(phydev, MDIO_MMD_VEND1, ++ VEND1_GLOBAL_MAILBOX_INTERFACE3, ++ VEND1_GLOBAL_MAILBOX_INTERFACE3_MSW_ADDR(addr)); ++ phy_write_mmd(phydev, MDIO_MMD_VEND1, ++ VEND1_GLOBAL_MAILBOX_INTERFACE4, ++ VEND1_GLOBAL_MAILBOX_INTERFACE4_LSW_ADDR(addr)); ++ ++ /* We assume and enforce the size to be word aligned. ++ * If a firmware that is not word aligned is found, please report upstream. ++ */ ++ for (pos = 0; pos < len; pos += sizeof(u32)) { ++ u32 word; ++ ++ /* FW data is always stored in little-endian */ ++ word = get_unaligned((const u32 *)(data + pos)); ++ ++ phy_write_mmd(phydev, MDIO_MMD_VEND1, VEND1_GLOBAL_MAILBOX_INTERFACE5, ++ VEND1_GLOBAL_MAILBOX_INTERFACE5_MSW_DATA(word)); ++ phy_write_mmd(phydev, MDIO_MMD_VEND1, VEND1_GLOBAL_MAILBOX_INTERFACE6, ++ VEND1_GLOBAL_MAILBOX_INTERFACE6_LSW_DATA(word)); ++ ++ phy_write_mmd(phydev, MDIO_MMD_VEND1, VEND1_GLOBAL_MAILBOX_INTERFACE1, ++ VEND1_GLOBAL_MAILBOX_INTERFACE1_EXECUTE | ++ VEND1_GLOBAL_MAILBOX_INTERFACE1_WRITE); ++ ++ /* calculate CRC as we load data to the mailbox. ++ * We convert word to big-endian as PHY is BE and mailbox will ++ * return a BE CRC. ++ */ ++ word = (__force u32)cpu_to_be32(word); ++ crc = crc_ccitt_false(crc, (u8 *)&word, sizeof(word)); ++ } ++ ++ up_crc = phy_read_mmd(phydev, MDIO_MMD_VEND1, VEND1_GLOBAL_MAILBOX_INTERFACE2); ++ if (crc != up_crc) { ++ phydev_err(phydev, "CRC mismatch: calculated 0x%04x PHY 0x%04x\n", ++ crc, up_crc); ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++ ++static int aqr_fw_boot(struct phy_device *phydev, const u8 *data, size_t size, ++ enum aqr_fw_src fw_src) ++{ ++ u16 calculated_crc, read_crc, read_primary_offset; ++ u32 iram_offset = 0, iram_size = 0; ++ u32 dram_offset = 0, dram_size = 0; ++ char version[VERSION_STRING_SIZE]; ++ u32 primary_offset = 0; ++ int ret; ++ ++ /* extract saved CRC at the end of the fw ++ * CRC is saved in big-endian as PHY is BE ++ */ ++ ret = aqr_fw_get_be16(data, size - sizeof(u16), size, &read_crc); ++ if (ret) { ++ phydev_err(phydev, "bad firmware CRC in firmware\n"); ++ return ret; ++ } ++ calculated_crc = crc_ccitt_false(0, data, size - sizeof(u16)); ++ if (read_crc != calculated_crc) { ++ phydev_err(phydev, "bad firmware CRC: file 0x%04x calculated 0x%04x\n", ++ read_crc, calculated_crc); ++ return -EINVAL; ++ } ++ ++ /* Get the primary offset to extract DRAM and IRAM sections. */ ++ ret = aqr_fw_get_le16(data, PRIMARY_OFFSET_OFFSET, size, &read_primary_offset); ++ if (ret) { ++ phydev_err(phydev, "bad primary offset in firmware\n"); ++ return ret; ++ } ++ primary_offset = PRIMARY_OFFSET(read_primary_offset); ++ ++ /* Find the DRAM and IRAM sections within the firmware file. ++ * Make sure the fw_header is correctly in the firmware. ++ */ ++ if (!aqr_fw_validate_get(size, primary_offset + HEADER_OFFSET, ++ sizeof(struct aqr_fw_header))) { ++ phydev_err(phydev, "bad fw_header in firmware\n"); ++ return -EINVAL; ++ } ++ ++ /* offset are in LE and values needs to be converted to cpu endian */ ++ ret = aqr_fw_get_le24(data, primary_offset + HEADER_OFFSET + ++ offsetof(struct aqr_fw_header, iram_offset), ++ size, &iram_offset); ++ if (ret) { ++ phydev_err(phydev, "bad iram offset in firmware\n"); ++ return ret; ++ } ++ ret = aqr_fw_get_le24(data, primary_offset + HEADER_OFFSET + ++ offsetof(struct aqr_fw_header, iram_size), ++ size, &iram_size); ++ if (ret) { ++ phydev_err(phydev, "invalid iram size in firmware\n"); ++ return ret; ++ } ++ ret = aqr_fw_get_le24(data, primary_offset + HEADER_OFFSET + ++ offsetof(struct aqr_fw_header, dram_offset), ++ size, &dram_offset); ++ if (ret) { ++ phydev_err(phydev, "bad dram offset in firmware\n"); ++ return ret; ++ } ++ ret = aqr_fw_get_le24(data, primary_offset + HEADER_OFFSET + ++ offsetof(struct aqr_fw_header, dram_size), ++ size, &dram_size); ++ if (ret) { ++ phydev_err(phydev, "invalid dram size in firmware\n"); ++ return ret; ++ } ++ ++ /* Increment the offset with the primary offset. ++ * Validate iram/dram offset and size. ++ */ ++ iram_offset += primary_offset; ++ if (iram_size % sizeof(u32)) { ++ phydev_err(phydev, "iram size if not aligned to word size. Please report this upstream!\n"); ++ return -EINVAL; ++ } ++ if (!aqr_fw_validate_get(size, iram_offset, iram_size)) { ++ phydev_err(phydev, "invalid iram offset for iram size\n"); ++ return -EINVAL; ++ } ++ ++ dram_offset += primary_offset; ++ if (dram_size % sizeof(u32)) { ++ phydev_err(phydev, "dram size if not aligned to word size. Please report this upstream!\n"); ++ return -EINVAL; ++ } ++ if (!aqr_fw_validate_get(size, dram_offset, dram_size)) { ++ phydev_err(phydev, "invalid iram offset for iram size\n"); ++ return -EINVAL; ++ } ++ ++ phydev_dbg(phydev, "primary %d IRAM offset=%d size=%d DRAM offset=%d size=%d\n", ++ primary_offset, iram_offset, iram_size, dram_offset, dram_size); ++ ++ if (!aqr_fw_validate_get(size, dram_offset + VERSION_STRING_OFFSET, ++ VERSION_STRING_SIZE)) { ++ phydev_err(phydev, "invalid version in firmware\n"); ++ return -EINVAL; ++ } ++ strscpy(version, (char *)data + dram_offset + VERSION_STRING_OFFSET, ++ VERSION_STRING_SIZE); ++ if (version[0] == '\0') { ++ phydev_err(phydev, "invalid version in firmware\n"); ++ return -EINVAL; ++ } ++ phydev_info(phydev, "loading firmware version '%s' from '%s'\n", version, ++ aqr_fw_src_string[fw_src]); ++ ++ /* stall the microcprocessor */ ++ phy_write_mmd(phydev, MDIO_MMD_VEND1, VEND1_GLOBAL_CONTROL2, ++ VEND1_GLOBAL_CONTROL2_UP_RUN_STALL | VEND1_GLOBAL_CONTROL2_UP_RUN_STALL_OVD); ++ ++ phydev_dbg(phydev, "loading DRAM 0x%08x from offset=%d size=%d\n", ++ DRAM_BASE_ADDR, dram_offset, dram_size); ++ ret = aqr_fw_load_memory(phydev, DRAM_BASE_ADDR, data + dram_offset, ++ dram_size); ++ if (ret) ++ return ret; ++ ++ phydev_dbg(phydev, "loading IRAM 0x%08x from offset=%d size=%d\n", ++ IRAM_BASE_ADDR, iram_offset, iram_size); ++ ret = aqr_fw_load_memory(phydev, IRAM_BASE_ADDR, data + iram_offset, ++ iram_size); ++ if (ret) ++ return ret; ++ ++ /* make sure soft reset and low power mode are clear */ ++ phy_clear_bits_mmd(phydev, MDIO_MMD_VEND1, VEND1_GLOBAL_SC, ++ VEND1_GLOBAL_SC_SOFT_RESET | VEND1_GLOBAL_SC_LOW_POWER); ++ ++ /* Release the microprocessor. UP_RESET must be held for 100 usec. */ ++ phy_write_mmd(phydev, MDIO_MMD_VEND1, VEND1_GLOBAL_CONTROL2, ++ VEND1_GLOBAL_CONTROL2_UP_RUN_STALL | ++ VEND1_GLOBAL_CONTROL2_UP_RUN_STALL_OVD | ++ VEND1_GLOBAL_CONTROL2_UP_RUN_STALL_RST); ++ usleep_range(UP_RESET_SLEEP, UP_RESET_SLEEP * 2); ++ ++ phy_write_mmd(phydev, MDIO_MMD_VEND1, VEND1_GLOBAL_CONTROL2, ++ VEND1_GLOBAL_CONTROL2_UP_RUN_STALL_OVD); ++ ++ return 0; ++} ++ ++static int aqr_firmware_load_nvmem(struct phy_device *phydev) ++{ ++ struct nvmem_cell *cell; ++ size_t size; ++ u8 *buf; ++ int ret; ++ ++ cell = nvmem_cell_get(&phydev->mdio.dev, "firmware"); ++ if (IS_ERR(cell)) ++ return PTR_ERR(cell); ++ ++ buf = nvmem_cell_read(cell, &size); ++ if (IS_ERR(buf)) { ++ ret = PTR_ERR(buf); ++ goto exit; ++ } ++ ++ ret = aqr_fw_boot(phydev, buf, size, AQR_FW_SRC_NVMEM); ++ if (ret) ++ phydev_err(phydev, "firmware loading failed: %d\n", ret); ++ ++ kfree(buf); ++exit: ++ nvmem_cell_put(cell); ++ ++ return ret; ++} ++ ++static int aqr_firmware_load_fs(struct phy_device *phydev) ++{ ++ struct device *dev = &phydev->mdio.dev; ++ const struct firmware *fw; ++ const char *fw_name; ++ int ret; ++ ++ ret = of_property_read_string(dev->of_node, "firmware-name", ++ &fw_name); ++ if (ret) ++ return ret; ++ ++ ret = request_firmware(&fw, fw_name, dev); ++ if (ret) { ++ phydev_err(phydev, "failed to find FW file %s (%d)\n", ++ fw_name, ret); ++ return ret; ++ } ++ ++ ret = aqr_fw_boot(phydev, fw->data, fw->size, AQR_FW_SRC_FS); ++ if (ret) ++ phydev_err(phydev, "firmware loading failed: %d\n", ret); ++ ++ release_firmware(fw); ++ ++ return ret; ++} ++ ++int aqr_firmware_load(struct phy_device *phydev) ++{ ++ int ret; ++ ++ /* Check if the firmware is not already loaded by pooling ++ * the current version returned by the PHY. If 0 is returned, ++ * no firmware is loaded. ++ */ ++ ret = phy_read_mmd(phydev, MDIO_MMD_VEND1, VEND1_GLOBAL_FW_ID); ++ if (ret > 0) ++ goto exit; ++ ++ ret = aqr_firmware_load_nvmem(phydev); ++ if (!ret) ++ goto exit; ++ ++ ret = aqr_firmware_load_fs(phydev); ++ if (ret) ++ return ret; ++ ++exit: ++ return 0; ++} +--- a/drivers/net/phy/aquantia/aquantia_main.c ++++ b/drivers/net/phy/aquantia/aquantia_main.c +@@ -656,11 +656,17 @@ static int aqr107_resume(struct phy_devi + + static int aqr107_probe(struct phy_device *phydev) + { ++ int ret; ++ + phydev->priv = devm_kzalloc(&phydev->mdio.dev, + sizeof(struct aqr107_priv), GFP_KERNEL); + if (!phydev->priv) + return -ENOMEM; + ++ ret = aqr_firmware_load(phydev); ++ if (ret) ++ return ret; ++ + return aqr_hwmon_probe(phydev); + } + diff --git a/target/linux/generic/backport-6.1/703-v6.3-net-mdio-Add-dedicated-C45-API-to-MDIO-bus-drivers.patch b/target/linux/generic/backport-6.1/703-v6.3-net-mdio-Add-dedicated-C45-API-to-MDIO-bus-drivers.patch new file mode 100644 index 000000000..ff186742b --- /dev/null +++ b/target/linux/generic/backport-6.1/703-v6.3-net-mdio-Add-dedicated-C45-API-to-MDIO-bus-drivers.patch @@ -0,0 +1,326 @@ +From 4e4aafcddbbfcdd6eed5780e190fcbfac8b4685a Mon Sep 17 00:00:00 2001 +From: Andrew Lunn +Date: Mon, 9 Jan 2023 16:30:41 +0100 +Subject: [PATCH] net: mdio: Add dedicated C45 API to MDIO bus drivers + +Currently C22 and C45 transactions are mixed over a combined API calls +which make use of a special bit in the reg address to indicate if a +C45 transaction should be performed. This makes it impossible to know +if the bus driver actually supports C45. Additionally, many C22 only +drivers don't return -EOPNOTSUPP when asked to perform a C45 +transaction, they mistaking perform a C22 transaction. + +This is the first step to cleanly separate C22 from C45. To maintain +backwards compatibility until all drivers which are capable of +performing C45 are converted to this new API, the helper functions +will fall back to the older API if the new API is not +supported. Eventually this fallback will be removed. + +Signed-off-by: Andrew Lunn +Signed-off-by: Michael Walle +Signed-off-by: Jakub Kicinski +--- + drivers/net/phy/mdio_bus.c | 189 +++++++++++++++++++++++++++++++++++++ + include/linux/mdio.h | 39 ++++---- + include/linux/phy.h | 5 + + 3 files changed, 214 insertions(+), 19 deletions(-) + +--- a/drivers/net/phy/mdio_bus.c ++++ b/drivers/net/phy/mdio_bus.c +@@ -832,6 +832,100 @@ int __mdiobus_modify_changed(struct mii_ + EXPORT_SYMBOL_GPL(__mdiobus_modify_changed); + + /** ++ * __mdiobus_c45_read - Unlocked version of the mdiobus_c45_read function ++ * @bus: the mii_bus struct ++ * @addr: the phy address ++ * @devad: device address to read ++ * @regnum: register number to read ++ * ++ * Read a MDIO bus register. Caller must hold the mdio bus lock. ++ * ++ * NOTE: MUST NOT be called from interrupt context. ++ */ ++int __mdiobus_c45_read(struct mii_bus *bus, int addr, int devad, u32 regnum) ++{ ++ int retval; ++ ++ lockdep_assert_held_once(&bus->mdio_lock); ++ ++ if (bus->read_c45) ++ retval = bus->read_c45(bus, addr, devad, regnum); ++ else ++ retval = bus->read(bus, addr, mdiobus_c45_addr(devad, regnum)); ++ ++ trace_mdio_access(bus, 1, addr, regnum, retval, retval); ++ mdiobus_stats_acct(&bus->stats[addr], true, retval); ++ ++ return retval; ++} ++EXPORT_SYMBOL(__mdiobus_c45_read); ++ ++/** ++ * __mdiobus_c45_write - Unlocked version of the mdiobus_write function ++ * @bus: the mii_bus struct ++ * @addr: the phy address ++ * @devad: device address to read ++ * @regnum: register number to write ++ * @val: value to write to @regnum ++ * ++ * Write a MDIO bus register. Caller must hold the mdio bus lock. ++ * ++ * NOTE: MUST NOT be called from interrupt context. ++ */ ++int __mdiobus_c45_write(struct mii_bus *bus, int addr, int devad, u32 regnum, ++ u16 val) ++{ ++ int err; ++ ++ lockdep_assert_held_once(&bus->mdio_lock); ++ ++ if (bus->write_c45) ++ err = bus->write_c45(bus, addr, devad, regnum, val); ++ else ++ err = bus->write(bus, addr, mdiobus_c45_addr(devad, regnum), ++ val); ++ ++ trace_mdio_access(bus, 0, addr, regnum, val, err); ++ mdiobus_stats_acct(&bus->stats[addr], false, err); ++ ++ return err; ++} ++EXPORT_SYMBOL(__mdiobus_c45_write); ++ ++/** ++ * __mdiobus_c45_modify_changed - Unlocked version of the mdiobus_modify function ++ * @bus: the mii_bus struct ++ * @addr: the phy address ++ * @devad: device address to read ++ * @regnum: register number to modify ++ * @mask: bit mask of bits to clear ++ * @set: bit mask of bits to set ++ * ++ * Read, modify, and if any change, write the register value back to the ++ * device. Any error returns a negative number. ++ * ++ * NOTE: MUST NOT be called from interrupt context. ++ */ ++static int __mdiobus_c45_modify_changed(struct mii_bus *bus, int addr, ++ int devad, u32 regnum, u16 mask, ++ u16 set) ++{ ++ int new, ret; ++ ++ ret = __mdiobus_c45_read(bus, addr, devad, regnum); ++ if (ret < 0) ++ return ret; ++ ++ new = (ret & ~mask) | set; ++ if (new == ret) ++ return 0; ++ ++ ret = __mdiobus_c45_write(bus, addr, devad, regnum, new); ++ ++ return ret < 0 ? ret : 1; ++} ++ ++/** + * mdiobus_read_nested - Nested version of the mdiobus_read function + * @bus: the mii_bus struct + * @addr: the phy address +@@ -879,6 +973,29 @@ int mdiobus_read(struct mii_bus *bus, in + EXPORT_SYMBOL(mdiobus_read); + + /** ++ * mdiobus_c45_read - Convenience function for reading a given MII mgmt register ++ * @bus: the mii_bus struct ++ * @addr: the phy address ++ * @devad: device address to read ++ * @regnum: register number to read ++ * ++ * NOTE: MUST NOT be called from interrupt context, ++ * because the bus read/write functions may wait for an interrupt ++ * to conclude the operation. ++ */ ++int mdiobus_c45_read(struct mii_bus *bus, int addr, int devad, u32 regnum) ++{ ++ int retval; ++ ++ mutex_lock(&bus->mdio_lock); ++ retval = __mdiobus_c45_read(bus, addr, devad, regnum); ++ mutex_unlock(&bus->mdio_lock); ++ ++ return retval; ++} ++EXPORT_SYMBOL(mdiobus_c45_read); ++ ++/** + * mdiobus_write_nested - Nested version of the mdiobus_write function + * @bus: the mii_bus struct + * @addr: the phy address +@@ -928,6 +1045,31 @@ int mdiobus_write(struct mii_bus *bus, i + EXPORT_SYMBOL(mdiobus_write); + + /** ++ * mdiobus_c45_write - Convenience function for writing a given MII mgmt register ++ * @bus: the mii_bus struct ++ * @addr: the phy address ++ * @devad: device address to read ++ * @regnum: register number to write ++ * @val: value to write to @regnum ++ * ++ * NOTE: MUST NOT be called from interrupt context, ++ * because the bus read/write functions may wait for an interrupt ++ * to conclude the operation. ++ */ ++int mdiobus_c45_write(struct mii_bus *bus, int addr, int devad, u32 regnum, ++ u16 val) ++{ ++ int err; ++ ++ mutex_lock(&bus->mdio_lock); ++ err = __mdiobus_c45_write(bus, addr, devad, regnum, val); ++ mutex_unlock(&bus->mdio_lock); ++ ++ return err; ++} ++EXPORT_SYMBOL(mdiobus_c45_write); ++ ++/** + * mdiobus_modify - Convenience function for modifying a given mdio device + * register + * @bus: the mii_bus struct +@@ -949,6 +1091,30 @@ int mdiobus_modify(struct mii_bus *bus, + EXPORT_SYMBOL_GPL(mdiobus_modify); + + /** ++ * mdiobus_c45_modify - Convenience function for modifying a given mdio device ++ * register ++ * @bus: the mii_bus struct ++ * @addr: the phy address ++ * @devad: device address to read ++ * @regnum: register number to write ++ * @mask: bit mask of bits to clear ++ * @set: bit mask of bits to set ++ */ ++int mdiobus_c45_modify(struct mii_bus *bus, int addr, int devad, u32 regnum, ++ u16 mask, u16 set) ++{ ++ int err; ++ ++ mutex_lock(&bus->mdio_lock); ++ err = __mdiobus_c45_modify_changed(bus, addr, devad, regnum, ++ mask, set); ++ mutex_unlock(&bus->mdio_lock); ++ ++ return err < 0 ? err : 0; ++} ++EXPORT_SYMBOL_GPL(mdiobus_c45_modify); ++ ++/** + * mdiobus_modify_changed - Convenience function for modifying a given mdio + * device register and returning if it changed + * @bus: the mii_bus struct +@@ -971,6 +1137,29 @@ int mdiobus_modify_changed(struct mii_bu + EXPORT_SYMBOL_GPL(mdiobus_modify_changed); + + /** ++ * mdiobus_c45_modify_changed - Convenience function for modifying a given mdio ++ * device register and returning if it changed ++ * @bus: the mii_bus struct ++ * @addr: the phy address ++ * @devad: device address to read ++ * @regnum: register number to write ++ * @mask: bit mask of bits to clear ++ * @set: bit mask of bits to set ++ */ ++int mdiobus_c45_modify_changed(struct mii_bus *bus, int devad, int addr, ++ u32 regnum, u16 mask, u16 set) ++{ ++ int err; ++ ++ mutex_lock(&bus->mdio_lock); ++ err = __mdiobus_c45_modify_changed(bus, addr, devad, regnum, mask, set); ++ mutex_unlock(&bus->mdio_lock); ++ ++ return err; ++} ++EXPORT_SYMBOL_GPL(mdiobus_c45_modify_changed); ++ ++/** + * mdio_bus_match - determine if given MDIO driver supports the given + * MDIO device + * @dev: target MDIO device +--- a/include/linux/mdio.h ++++ b/include/linux/mdio.h +@@ -423,6 +423,17 @@ int mdiobus_modify(struct mii_bus *bus, + u16 set); + int mdiobus_modify_changed(struct mii_bus *bus, int addr, u32 regnum, + u16 mask, u16 set); ++int __mdiobus_c45_read(struct mii_bus *bus, int addr, int devad, u32 regnum); ++int mdiobus_c45_read(struct mii_bus *bus, int addr, int devad, u32 regnum); ++int __mdiobus_c45_write(struct mii_bus *bus, int addr, int devad, u32 regnum, ++ u16 val); ++int mdiobus_c45_write(struct mii_bus *bus, int addr, int devad, u32 regnum, ++ u16 val); ++int mdiobus_c45_modify(struct mii_bus *bus, int addr, int devad, u32 regnum, ++ u16 mask, u16 set); ++ ++int mdiobus_c45_modify_changed(struct mii_bus *bus, int addr, int devad, ++ u32 regnum, u16 mask, u16 set); + + static inline int mdiodev_read(struct mdio_device *mdiodev, u32 regnum) + { +@@ -463,29 +474,19 @@ static inline u16 mdiobus_c45_devad(u32 + return FIELD_GET(MII_DEVADDR_C45_MASK, regnum); + } + +-static inline int __mdiobus_c45_read(struct mii_bus *bus, int prtad, int devad, +- u16 regnum) +-{ +- return __mdiobus_read(bus, prtad, mdiobus_c45_addr(devad, regnum)); +-} +- +-static inline int __mdiobus_c45_write(struct mii_bus *bus, int prtad, int devad, +- u16 regnum, u16 val) +-{ +- return __mdiobus_write(bus, prtad, mdiobus_c45_addr(devad, regnum), +- val); +-} +- +-static inline int mdiobus_c45_read(struct mii_bus *bus, int prtad, int devad, +- u16 regnum) ++static inline int mdiodev_c45_modify(struct mdio_device *mdiodev, int devad, ++ u32 regnum, u16 mask, u16 set) + { +- return mdiobus_read(bus, prtad, mdiobus_c45_addr(devad, regnum)); ++ return mdiobus_c45_modify(mdiodev->bus, mdiodev->addr, devad, regnum, ++ mask, set); + } + +-static inline int mdiobus_c45_write(struct mii_bus *bus, int prtad, int devad, +- u16 regnum, u16 val) ++static inline int mdiodev_c45_modify_changed(struct mdio_device *mdiodev, ++ int devad, u32 regnum, u16 mask, ++ u16 set) + { +- return mdiobus_write(bus, prtad, mdiobus_c45_addr(devad, regnum), val); ++ return mdiobus_c45_modify_changed(mdiodev->bus, mdiodev->addr, devad, ++ regnum, mask, set); + } + + int mdiobus_register_device(struct mdio_device *mdiodev); +--- a/include/linux/phy.h ++++ b/include/linux/phy.h +@@ -364,6 +364,11 @@ struct mii_bus { + int (*read)(struct mii_bus *bus, int addr, int regnum); + /** @write: Perform a write transfer on the bus */ + int (*write)(struct mii_bus *bus, int addr, int regnum, u16 val); ++ /** @read_c45: Perform a C45 read transfer on the bus */ ++ int (*read_c45)(struct mii_bus *bus, int addr, int devnum, int regnum); ++ /** @write_c45: Perform a C45 write transfer on the bus */ ++ int (*write_c45)(struct mii_bus *bus, int addr, int devnum, ++ int regnum, u16 val); + /** @reset: Perform a reset of the bus */ + int (*reset)(struct mii_bus *bus); + diff --git a/target/linux/generic/backport-6.1/704-v6.6-net-phy-Introduce-PSGMII-PHY-interface-mode.patch b/target/linux/generic/backport-6.1/704-v6.6-net-phy-Introduce-PSGMII-PHY-interface-mode.patch new file mode 100644 index 000000000..80210e6da --- /dev/null +++ b/target/linux/generic/backport-6.1/704-v6.6-net-phy-Introduce-PSGMII-PHY-interface-mode.patch @@ -0,0 +1,105 @@ +From 9a0e95e34e9c0a713ddfd48c3a88a20d2bdfd514 Mon Sep 17 00:00:00 2001 +From: Gabor Juhos +Date: Fri, 11 Aug 2023 13:10:07 +0200 +Subject: [PATCH] net: phy: Introduce PSGMII PHY interface mode + +The PSGMII interface is similar to QSGMII. The main difference +is that the PSGMII interface combines five SGMII lines into a +single link while in QSGMII only four lines are combined. + +Similarly to the QSGMII, this interface mode might also needs +special handling within the MAC driver. + +It is commonly used by Qualcomm with their QCA807x PHY series and +modern WiSoC-s. + +Add definitions for the PHY layer to allow to express this type +of connection between the MAC and PHY. + +Signed-off-by: Gabor Juhos +Signed-off-by: Robert Marko +Signed-off-by: David S. Miller +--- + Documentation/networking/phy.rst | 4 ++++ + drivers/net/phy/phy-core.c | 2 ++ + drivers/net/phy/phylink.c | 3 +++ + include/linux/phy.h | 4 ++++ + 4 files changed, 13 insertions(+) + +--- a/Documentation/networking/phy.rst ++++ b/Documentation/networking/phy.rst +@@ -323,6 +323,10 @@ Some of the interface modes are describe + contrast with the 1000BASE-X phy mode used for Clause 38 and 39 PMDs, this + interface mode has different autonegotiation and only supports full duplex. + ++``PHY_INTERFACE_MODE_PSGMII`` ++ This is the Penta SGMII mode, it is similar to QSGMII but it combines 5 ++ SGMII lines into a single link compared to 4 on QSGMII. ++ + Pause frames / flow control + =========================== + +--- a/drivers/net/phy/phy-core.c ++++ b/drivers/net/phy/phy-core.c +@@ -140,6 +140,8 @@ int phy_interface_num_ports(phy_interfac + case PHY_INTERFACE_MODE_QSGMII: + case PHY_INTERFACE_MODE_QUSGMII: + return 4; ++ case PHY_INTERFACE_MODE_PSGMII: ++ return 5; + case PHY_INTERFACE_MODE_MAX: + WARN_ONCE(1, "PHY_INTERFACE_MODE_MAX isn't a valid interface mode"); + return 0; +--- a/drivers/net/phy/phylink.c ++++ b/drivers/net/phy/phylink.c +@@ -187,6 +187,7 @@ static int phylink_interface_max_speed(p + case PHY_INTERFACE_MODE_RGMII_RXID: + case PHY_INTERFACE_MODE_RGMII_ID: + case PHY_INTERFACE_MODE_RGMII: ++ case PHY_INTERFACE_MODE_PSGMII: + case PHY_INTERFACE_MODE_QSGMII: + case PHY_INTERFACE_MODE_QUSGMII: + case PHY_INTERFACE_MODE_SGMII: +@@ -448,6 +449,7 @@ unsigned long phylink_get_capabilities(p + case PHY_INTERFACE_MODE_RGMII_RXID: + case PHY_INTERFACE_MODE_RGMII_ID: + case PHY_INTERFACE_MODE_RGMII: ++ case PHY_INTERFACE_MODE_PSGMII: + case PHY_INTERFACE_MODE_QSGMII: + case PHY_INTERFACE_MODE_QUSGMII: + case PHY_INTERFACE_MODE_SGMII: +@@ -814,6 +816,7 @@ static int phylink_parse_mode(struct phy + + switch (pl->link_config.interface) { + case PHY_INTERFACE_MODE_SGMII: ++ case PHY_INTERFACE_MODE_PSGMII: + case PHY_INTERFACE_MODE_QSGMII: + case PHY_INTERFACE_MODE_QUSGMII: + case PHY_INTERFACE_MODE_RGMII: +--- a/include/linux/phy.h ++++ b/include/linux/phy.h +@@ -103,6 +103,7 @@ extern const int phy_10gbit_features_arr + * @PHY_INTERFACE_MODE_XGMII: 10 gigabit media-independent interface + * @PHY_INTERFACE_MODE_XLGMII:40 gigabit media-independent interface + * @PHY_INTERFACE_MODE_MOCA: Multimedia over Coax ++ * @PHY_INTERFACE_MODE_PSGMII: Penta SGMII + * @PHY_INTERFACE_MODE_QSGMII: Quad SGMII + * @PHY_INTERFACE_MODE_TRGMII: Turbo RGMII + * @PHY_INTERFACE_MODE_100BASEX: 100 BaseX +@@ -140,6 +141,7 @@ typedef enum { + PHY_INTERFACE_MODE_XGMII, + PHY_INTERFACE_MODE_XLGMII, + PHY_INTERFACE_MODE_MOCA, ++ PHY_INTERFACE_MODE_PSGMII, + PHY_INTERFACE_MODE_QSGMII, + PHY_INTERFACE_MODE_TRGMII, + PHY_INTERFACE_MODE_100BASEX, +@@ -247,6 +249,8 @@ static inline const char *phy_modes(phy_ + return "xlgmii"; + case PHY_INTERFACE_MODE_MOCA: + return "moca"; ++ case PHY_INTERFACE_MODE_PSGMII: ++ return "psgmii"; + case PHY_INTERFACE_MODE_QSGMII: + return "qsgmii"; + case PHY_INTERFACE_MODE_TRGMII: diff --git a/target/linux/generic/backport-6.1/705-v6.4-net-phy-at803x-Replace-of_gpio.h-with-what-indeed-is.patch b/target/linux/generic/backport-6.1/705-v6.4-net-phy-at803x-Replace-of_gpio.h-with-what-indeed-is.patch new file mode 100644 index 000000000..7cacb71b1 --- /dev/null +++ b/target/linux/generic/backport-6.1/705-v6.4-net-phy-at803x-Replace-of_gpio.h-with-what-indeed-is.patch @@ -0,0 +1,32 @@ +From a593a2fcfdfb92cfd0ffc54bc81b07e6bfaaaf46 Mon Sep 17 00:00:00 2001 +From: Andy Shevchenko +Date: Thu, 16 Mar 2023 14:08:26 +0200 +Subject: [PATCH] net: phy: at803x: Replace of_gpio.h with what indeed is used + +of_gpio.h in this driver is solely used as a proxy to other headers. +This is incorrect usage of the of_gpio.h. Replace it .h with what +indeed is used in the code. + +Signed-off-by: Andy Shevchenko +Reviewed-by: Andrew Lunn +Signed-off-by: David S. Miller +--- + drivers/net/phy/at803x.c | 3 +-- + 1 file changed, 1 insertion(+), 2 deletions(-) + +--- a/drivers/net/phy/at803x.c ++++ b/drivers/net/phy/at803x.c +@@ -13,12 +13,11 @@ + #include + #include + #include +-#include + #include +-#include + #include + #include + #include ++#include + #include + #include + #include diff --git a/target/linux/generic/backport-6.1/706-v6.6-01-net-phy-at803x-support-qca8081-genphy_c45_pma_read_a.patch b/target/linux/generic/backport-6.1/706-v6.6-01-net-phy-at803x-support-qca8081-genphy_c45_pma_read_a.patch new file mode 100644 index 000000000..dab492896 --- /dev/null +++ b/target/linux/generic/backport-6.1/706-v6.6-01-net-phy-at803x-support-qca8081-genphy_c45_pma_read_a.patch @@ -0,0 +1,70 @@ +From 8b8bc13d89a7d23d14b0e041c73f037c9db997b1 Mon Sep 17 00:00:00 2001 +From: Luo Jie +Date: Sun, 16 Jul 2023 16:49:19 +0800 +Subject: [PATCH 1/6] net: phy: at803x: support qca8081 + genphy_c45_pma_read_abilities + +qca8081 PHY supports to use genphy_c45_pma_read_abilities for +getting the PHY features supported except for the autoneg ability + +but autoneg ability exists in MDIO_STAT1 instead of MMD7.1, add it +manually after calling genphy_c45_pma_read_abilities. + +Signed-off-by: Luo Jie +Reviewed-by: Russell King (Oracle) +Signed-off-by: David S. Miller +--- + drivers/net/phy/at803x.c | 28 ++++++++++++++++++---------- + 1 file changed, 18 insertions(+), 10 deletions(-) + +--- a/drivers/net/phy/at803x.c ++++ b/drivers/net/phy/at803x.c +@@ -902,15 +902,6 @@ static int at803x_get_features(struct ph + if (err) + return err; + +- if (phydev->drv->phy_id == QCA8081_PHY_ID) { +- err = phy_read_mmd(phydev, MDIO_MMD_PMAPMD, MDIO_PMA_NG_EXTABLE); +- if (err < 0) +- return err; +- +- linkmode_mod_bit(ETHTOOL_LINK_MODE_2500baseT_Full_BIT, phydev->supported, +- err & MDIO_PMA_NG_EXTABLE_2_5GBT); +- } +- + if (phydev->drv->phy_id != ATH8031_PHY_ID) + return 0; + +@@ -1996,6 +1987,23 @@ static int qca808x_cable_test_get_status + return 0; + } + ++static int qca808x_get_features(struct phy_device *phydev) ++{ ++ int ret; ++ ++ ret = genphy_c45_pma_read_abilities(phydev); ++ if (ret) ++ return ret; ++ ++ /* The autoneg ability is not existed in bit3 of MMD7.1, ++ * but it is supported by qca808x PHY, so we add it here ++ * manually. ++ */ ++ linkmode_set_bit(ETHTOOL_LINK_MODE_Autoneg_BIT, phydev->supported); ++ ++ return 0; ++} ++ + static struct phy_driver at803x_driver[] = { + { + /* Qualcomm Atheros AR8035 */ +@@ -2163,7 +2171,7 @@ static struct phy_driver at803x_driver[] + .set_tunable = at803x_set_tunable, + .set_wol = at803x_set_wol, + .get_wol = at803x_get_wol, +- .get_features = at803x_get_features, ++ .get_features = qca808x_get_features, + .config_aneg = at803x_config_aneg, + .suspend = genphy_suspend, + .resume = genphy_resume, diff --git a/target/linux/generic/backport-6.1/706-v6.6-02-net-phy-at803x-merge-qca8081-slave-seed-function.patch b/target/linux/generic/backport-6.1/706-v6.6-02-net-phy-at803x-merge-qca8081-slave-seed-function.patch new file mode 100644 index 000000000..28874068c --- /dev/null +++ b/target/linux/generic/backport-6.1/706-v6.6-02-net-phy-at803x-merge-qca8081-slave-seed-function.patch @@ -0,0 +1,73 @@ +From f3db55ae860a82e1224a909072783ef850e5d228 Mon Sep 17 00:00:00 2001 +From: Luo Jie +Date: Sun, 16 Jul 2023 16:49:20 +0800 +Subject: [PATCH 2/6] net: phy: at803x: merge qca8081 slave seed function + +merge the seed enablement and seed value configuration into +one function, since the random seed value is needed to be +configured when the seed is enabled. + +Signed-off-by: Luo Jie +Reviewed-by: Russell King (Oracle) +Signed-off-by: David S. Miller +--- + drivers/net/phy/at803x.c | 29 +++++++++-------------------- + 1 file changed, 9 insertions(+), 20 deletions(-) + +--- a/drivers/net/phy/at803x.c ++++ b/drivers/net/phy/at803x.c +@@ -1730,24 +1730,19 @@ static int qca808x_phy_fast_retrain_conf + return 0; + } + +-static int qca808x_phy_ms_random_seed_set(struct phy_device *phydev) +-{ +- u16 seed_value = prandom_u32_max(QCA808X_MASTER_SLAVE_SEED_RANGE); +- +- return at803x_debug_reg_mask(phydev, QCA808X_PHY_DEBUG_LOCAL_SEED, +- QCA808X_MASTER_SLAVE_SEED_CFG, +- FIELD_PREP(QCA808X_MASTER_SLAVE_SEED_CFG, seed_value)); +-} +- + static int qca808x_phy_ms_seed_enable(struct phy_device *phydev, bool enable) + { +- u16 seed_enable = 0; ++ u16 seed_value; + +- if (enable) +- seed_enable = QCA808X_MASTER_SLAVE_SEED_ENABLE; ++ if (!enable) ++ return at803x_debug_reg_mask(phydev, QCA808X_PHY_DEBUG_LOCAL_SEED, ++ QCA808X_MASTER_SLAVE_SEED_ENABLE, 0); + ++ seed_value = prandom_u32_max(QCA808X_MASTER_SLAVE_SEED_RANGE); + return at803x_debug_reg_mask(phydev, QCA808X_PHY_DEBUG_LOCAL_SEED, +- QCA808X_MASTER_SLAVE_SEED_ENABLE, seed_enable); ++ QCA808X_MASTER_SLAVE_SEED_CFG | QCA808X_MASTER_SLAVE_SEED_ENABLE, ++ FIELD_PREP(QCA808X_MASTER_SLAVE_SEED_CFG, seed_value) | ++ QCA808X_MASTER_SLAVE_SEED_ENABLE); + } + + static int qca808x_config_init(struct phy_device *phydev) +@@ -1771,12 +1766,7 @@ static int qca808x_config_init(struct ph + if (ret) + return ret; + +- /* Configure lower ramdom seed to make phy linked as slave mode */ +- ret = qca808x_phy_ms_random_seed_set(phydev); +- if (ret) +- return ret; +- +- /* Enable seed */ ++ /* Enable seed and configure lower ramdom seed to make phy linked as slave mode */ + ret = qca808x_phy_ms_seed_enable(phydev, true); + if (ret) + return ret; +@@ -1821,7 +1811,6 @@ static int qca808x_read_status(struct ph + if (phydev->master_slave_state == MASTER_SLAVE_STATE_ERR) { + qca808x_phy_ms_seed_enable(phydev, false); + } else { +- qca808x_phy_ms_random_seed_set(phydev); + qca808x_phy_ms_seed_enable(phydev, true); + } + } diff --git a/target/linux/generic/backport-6.1/706-v6.6-03-net-phy-at803x-enable-qca8081-slave-seed-conditional.patch b/target/linux/generic/backport-6.1/706-v6.6-03-net-phy-at803x-enable-qca8081-slave-seed-conditional.patch new file mode 100644 index 000000000..b91d2cab3 --- /dev/null +++ b/target/linux/generic/backport-6.1/706-v6.6-03-net-phy-at803x-enable-qca8081-slave-seed-conditional.patch @@ -0,0 +1,76 @@ +From 7cc3209558002d95c0d45a1276ba4f5f741eec42 Mon Sep 17 00:00:00 2001 +From: Luo Jie +Date: Sun, 16 Jul 2023 16:49:21 +0800 +Subject: [PATCH 3/6] net: phy: at803x: enable qca8081 slave seed conditionally + +qca8081 is the single port PHY, the slave prefer mode is used +by default. + +if the phy master perfer mode is configured, the slave seed +configuration should not be enabled, since the slave seed +enablement is for making PHY linked as slave mode easily. + +disable slave seed if the master mode is preferred. + +Signed-off-by: Luo Jie +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 +@@ -1745,6 +1745,12 @@ static int qca808x_phy_ms_seed_enable(st + QCA808X_MASTER_SLAVE_SEED_ENABLE); + } + ++static bool qca808x_is_prefer_master(struct phy_device *phydev) ++{ ++ return (phydev->master_slave_get == MASTER_SLAVE_CFG_MASTER_FORCE) || ++ (phydev->master_slave_get == MASTER_SLAVE_CFG_MASTER_PREFERRED); ++} ++ + static int qca808x_config_init(struct phy_device *phydev) + { + int ret; +@@ -1766,11 +1772,17 @@ static int qca808x_config_init(struct ph + if (ret) + return ret; + +- /* Enable seed and configure lower ramdom seed to make phy linked as slave mode */ +- ret = qca808x_phy_ms_seed_enable(phydev, true); +- if (ret) ++ ret = genphy_read_master_slave(phydev); ++ if (ret < 0) + return ret; + ++ if (!qca808x_is_prefer_master(phydev)) { ++ /* Enable seed and configure lower ramdom seed to make phy linked as slave mode */ ++ ret = qca808x_phy_ms_seed_enable(phydev, true); ++ if (ret) ++ return ret; ++ } ++ + /* Configure adc threshold as 100mv for the link 10M */ + return at803x_debug_reg_mask(phydev, QCA808X_PHY_DEBUG_ADC_THRESHOLD, + QCA808X_ADC_THRESHOLD_MASK, QCA808X_ADC_THRESHOLD_100MV); +@@ -1802,13 +1814,16 @@ static int qca808x_read_status(struct ph + phydev->interface = PHY_INTERFACE_MODE_SGMII; + } else { + /* generate seed as a lower random value to make PHY linked as SLAVE easily, +- * except for master/slave configuration fault detected. ++ * except for master/slave configuration fault detected or the master mode ++ * preferred. ++ * + * the reason for not putting this code into the function link_change_notify is + * the corner case where the link partner is also the qca8081 PHY and the seed + * value is configured as the same value, the link can't be up and no link change + * occurs. + */ +- if (phydev->master_slave_state == MASTER_SLAVE_STATE_ERR) { ++ if (phydev->master_slave_state == MASTER_SLAVE_STATE_ERR || ++ qca808x_is_prefer_master(phydev)) { + qca808x_phy_ms_seed_enable(phydev, false); + } else { + qca808x_phy_ms_seed_enable(phydev, true); diff --git a/target/linux/generic/backport-6.1/706-v6.6-04-net-phy-at803x-support-qca8081-1G-chip-type.patch b/target/linux/generic/backport-6.1/706-v6.6-04-net-phy-at803x-support-qca8081-1G-chip-type.patch new file mode 100644 index 000000000..b8db89d85 --- /dev/null +++ b/target/linux/generic/backport-6.1/706-v6.6-04-net-phy-at803x-support-qca8081-1G-chip-type.patch @@ -0,0 +1,48 @@ +From fea7cfb83d1a2782e39cd101dd44ed2548539de5 Mon Sep 17 00:00:00 2001 +From: Luo Jie +Date: Sun, 16 Jul 2023 16:49:22 +0800 +Subject: [PATCH 4/6] net: phy: at803x: support qca8081 1G chip type + +The qca8081 1G chip version does not support 2.5 capability, which +is distinguished from qca8081 2.5G chip according to the bit0 of +register mmd7.0x901d, the 1G version chip also has the same PHY ID +as the normal qca8081 2.5G chip. + +Signed-off-by: Luo Jie +Reviewed-by: Russell King (Oracle) +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 +@@ -272,6 +272,10 @@ + #define QCA808X_CDT_STATUS_STAT_OPEN 2 + #define QCA808X_CDT_STATUS_STAT_SHORT 3 + ++/* QCA808X 1G chip type */ ++#define QCA808X_PHY_MMD7_CHIP_TYPE 0x901d ++#define QCA808X_PHY_CHIP_TYPE_1G BIT(0) ++ + MODULE_DESCRIPTION("Qualcomm Atheros AR803x and QCA808X PHY driver"); + MODULE_AUTHOR("Matus Ujhelyi"); + MODULE_LICENSE("GPL"); +@@ -2005,6 +2009,17 @@ static int qca808x_get_features(struct p + */ + linkmode_set_bit(ETHTOOL_LINK_MODE_Autoneg_BIT, phydev->supported); + ++ /* As for the qca8081 1G version chip, the 2500baseT ability is also ++ * existed in the bit0 of MMD1.21, we need to remove it manually if ++ * it is the qca8081 1G chip according to the bit0 of MMD7.0x901d. ++ */ ++ ret = phy_read_mmd(phydev, MDIO_MMD_AN, QCA808X_PHY_MMD7_CHIP_TYPE); ++ if (ret < 0) ++ return ret; ++ ++ if (QCA808X_PHY_CHIP_TYPE_1G & ret) ++ linkmode_clear_bit(ETHTOOL_LINK_MODE_2500baseT_Full_BIT, phydev->supported); ++ + return 0; + } + diff --git a/target/linux/generic/backport-6.1/706-v6.6-05-net-phy-at803x-remove-qca8081-1G-fast-retrain-and-sl.patch b/target/linux/generic/backport-6.1/706-v6.6-05-net-phy-at803x-remove-qca8081-1G-fast-retrain-and-sl.patch new file mode 100644 index 000000000..b54a86e5f --- /dev/null +++ b/target/linux/generic/backport-6.1/706-v6.6-05-net-phy-at803x-remove-qca8081-1G-fast-retrain-and-sl.patch @@ -0,0 +1,98 @@ +From df9401ff3e6eeaa42bfb06761967f1b71f5afce7 Mon Sep 17 00:00:00 2001 +From: Luo Jie +Date: Sun, 16 Jul 2023 16:49:23 +0800 +Subject: [PATCH 5/6] net: phy: at803x: remove qca8081 1G fast retrain and + slave seed config + +The fast retrain and slave seed configs are only applicable when the 2.5G +ability is supported. + +Signed-off-by: Luo Jie +Reviewed-by: Andrew Lunn +Signed-off-by: David S. Miller +--- + drivers/net/phy/at803x.c | 50 +++++++++++++++++++++++++--------------- + 1 file changed, 32 insertions(+), 18 deletions(-) + +--- a/drivers/net/phy/at803x.c ++++ b/drivers/net/phy/at803x.c +@@ -1755,6 +1755,11 @@ static bool qca808x_is_prefer_master(str + (phydev->master_slave_get == MASTER_SLAVE_CFG_MASTER_PREFERRED); + } + ++static bool qca808x_has_fast_retrain_or_slave_seed(struct phy_device *phydev) ++{ ++ return linkmode_test_bit(ETHTOOL_LINK_MODE_2500baseT_Full_BIT, phydev->supported); ++} ++ + static int qca808x_config_init(struct phy_device *phydev) + { + int ret; +@@ -1771,20 +1776,24 @@ static int qca808x_config_init(struct ph + if (ret) + return ret; + +- /* Config the fast retrain for the link 2500M */ +- ret = qca808x_phy_fast_retrain_config(phydev); +- if (ret) +- return ret; +- +- ret = genphy_read_master_slave(phydev); +- if (ret < 0) +- return ret; +- +- if (!qca808x_is_prefer_master(phydev)) { +- /* Enable seed and configure lower ramdom seed to make phy linked as slave mode */ +- ret = qca808x_phy_ms_seed_enable(phydev, true); ++ if (qca808x_has_fast_retrain_or_slave_seed(phydev)) { ++ /* Config the fast retrain for the link 2500M */ ++ ret = qca808x_phy_fast_retrain_config(phydev); + if (ret) + return ret; ++ ++ ret = genphy_read_master_slave(phydev); ++ if (ret < 0) ++ return ret; ++ ++ if (!qca808x_is_prefer_master(phydev)) { ++ /* Enable seed and configure lower ramdom seed to make phy ++ * linked as slave mode. ++ */ ++ ret = qca808x_phy_ms_seed_enable(phydev, true); ++ if (ret) ++ return ret; ++ } + } + + /* Configure adc threshold as 100mv for the link 10M */ +@@ -1826,11 +1835,13 @@ static int qca808x_read_status(struct ph + * value is configured as the same value, the link can't be up and no link change + * occurs. + */ +- if (phydev->master_slave_state == MASTER_SLAVE_STATE_ERR || +- qca808x_is_prefer_master(phydev)) { +- qca808x_phy_ms_seed_enable(phydev, false); +- } else { +- qca808x_phy_ms_seed_enable(phydev, true); ++ if (qca808x_has_fast_retrain_or_slave_seed(phydev)) { ++ if (phydev->master_slave_state == MASTER_SLAVE_STATE_ERR || ++ qca808x_is_prefer_master(phydev)) { ++ qca808x_phy_ms_seed_enable(phydev, false); ++ } else { ++ qca808x_phy_ms_seed_enable(phydev, true); ++ } + } + } + +@@ -1845,7 +1856,10 @@ static int qca808x_soft_reset(struct phy + if (ret < 0) + return ret; + +- return qca808x_phy_ms_seed_enable(phydev, true); ++ if (qca808x_has_fast_retrain_or_slave_seed(phydev)) ++ ret = qca808x_phy_ms_seed_enable(phydev, true); ++ ++ return ret; + } + + static bool qca808x_cdt_fault_length_valid(int cdt_code) diff --git a/target/linux/generic/backport-6.1/706-v6.6-06-net-phy-at803x-add-qca8081-fifo-reset-on-the-link-ch.patch b/target/linux/generic/backport-6.1/706-v6.6-06-net-phy-at803x-add-qca8081-fifo-reset-on-the-link-ch.patch new file mode 100644 index 000000000..e382cf787 --- /dev/null +++ b/target/linux/generic/backport-6.1/706-v6.6-06-net-phy-at803x-add-qca8081-fifo-reset-on-the-link-ch.patch @@ -0,0 +1,55 @@ +From 723970affdd8766fa0d91cd34bf2ffc861538b5f Mon Sep 17 00:00:00 2001 +From: Luo Jie +Date: Sun, 16 Jul 2023 16:49:24 +0800 +Subject: [PATCH 6/6] net: phy: at803x: add qca8081 fifo reset on the link + changed + +The qca8081 sgmii fifo needs to be reset on link down and +released on the link up in case of any abnormal issue +such as the packet blocked on the PHY. + +Signed-off-by: Luo Jie +Reviewed-by: Andrew Lunn +Reviewed-by: Russell King (Oracle) +Signed-off-by: David S. Miller +--- + drivers/net/phy/at803x.c | 14 ++++++++++++++ + 1 file changed, 14 insertions(+) + +--- a/drivers/net/phy/at803x.c ++++ b/drivers/net/phy/at803x.c +@@ -276,6 +276,9 @@ + #define QCA808X_PHY_MMD7_CHIP_TYPE 0x901d + #define QCA808X_PHY_CHIP_TYPE_1G BIT(0) + ++#define QCA8081_PHY_SERDES_MMD1_FIFO_CTRL 0x9072 ++#define QCA8081_PHY_FIFO_RSTN BIT(11) ++ + MODULE_DESCRIPTION("Qualcomm Atheros AR803x and QCA808X PHY driver"); + MODULE_AUTHOR("Matus Ujhelyi"); + MODULE_LICENSE("GPL"); +@@ -2037,6 +2040,16 @@ static int qca808x_get_features(struct p + return 0; + } + ++static void qca808x_link_change_notify(struct phy_device *phydev) ++{ ++ /* Assert interface sgmii fifo on link down, deassert it on link up, ++ * the interface device address is always phy address added by 1. ++ */ ++ mdiobus_c45_modify_changed(phydev->mdio.bus, phydev->mdio.addr + 1, ++ MDIO_MMD_PMAPMD, QCA8081_PHY_SERDES_MMD1_FIFO_CTRL, ++ QCA8081_PHY_FIFO_RSTN, phydev->link ? QCA8081_PHY_FIFO_RSTN : 0); ++} ++ + static struct phy_driver at803x_driver[] = { + { + /* Qualcomm Atheros AR8035 */ +@@ -2213,6 +2226,7 @@ static struct phy_driver at803x_driver[] + .soft_reset = qca808x_soft_reset, + .cable_test_start = qca808x_cable_test_start, + .cable_test_get_status = qca808x_cable_test_get_status, ++ .link_change_notify = qca808x_link_change_notify, + }, }; + + module_phy_driver(at803x_driver); diff --git a/target/linux/generic/backport-6.1/707-v6.8-02-net-phy-at803x-move-disable-WOL-to-specific-at8031-p.patch b/target/linux/generic/backport-6.1/707-v6.8-02-net-phy-at803x-move-disable-WOL-to-specific-at8031-p.patch new file mode 100644 index 000000000..eb9172b1c --- /dev/null +++ b/target/linux/generic/backport-6.1/707-v6.8-02-net-phy-at803x-move-disable-WOL-to-specific-at8031-p.patch @@ -0,0 +1,69 @@ +From 6a3b8c573b5a152a6aa7a0b54c5e18b84c6ba6f5 Mon Sep 17 00:00:00 2001 +From: Christian Marangi +Date: Fri, 8 Dec 2023 15:51:49 +0100 +Subject: [PATCH 02/13] net: phy: at803x: move disable WOL to specific at8031 + probe + +Move the WOL disable call to specific at8031 probe to make at803x_probe +more generic and drop extra check for PHY ID. + +Keep the same previous behaviour by first calling at803x_probe and then +disabling WOL. + +Signed-off-by: Christian Marangi +Reviewed-by: Andrew Lunn +Signed-off-by: David S. Miller +--- + drivers/net/phy/at803x.c | 27 +++++++++++++++++---------- + 1 file changed, 17 insertions(+), 10 deletions(-) + +--- a/drivers/net/phy/at803x.c ++++ b/drivers/net/phy/at803x.c +@@ -886,15 +886,6 @@ static int at803x_probe(struct phy_devic + priv->is_fiber = true; + break; + } +- +- /* Disable WoL in 1588 register which is enabled +- * by default +- */ +- ret = phy_modify_mmd(phydev, MDIO_MMD_PCS, +- AT803X_PHY_MMD3_WOL_CTRL, +- AT803X_WOL_EN, 0); +- if (ret) +- return ret; + } + + return 0; +@@ -1591,6 +1582,22 @@ static int at803x_cable_test_start(struc + return 0; + } + ++static int at8031_probe(struct phy_device *phydev) ++{ ++ int ret; ++ ++ ret = at803x_probe(phydev); ++ if (ret) ++ return ret; ++ ++ /* Disable WoL in 1588 register which is enabled ++ * by default ++ */ ++ return phy_modify_mmd(phydev, MDIO_MMD_PCS, ++ AT803X_PHY_MMD3_WOL_CTRL, ++ AT803X_WOL_EN, 0); ++} ++ + static int qca83xx_config_init(struct phy_device *phydev) + { + u8 switch_revision; +@@ -2092,7 +2099,7 @@ static struct phy_driver at803x_driver[] + PHY_ID_MATCH_EXACT(ATH8031_PHY_ID), + .name = "Qualcomm Atheros AR8031/AR8033", + .flags = PHY_POLL_CABLE_TEST, +- .probe = at803x_probe, ++ .probe = at8031_probe, + .config_init = at803x_config_init, + .config_aneg = at803x_config_aneg, + .soft_reset = genphy_soft_reset, diff --git a/target/linux/generic/backport-6.1/707-v6.8-03-net-phy-at803x-raname-hw_stats-functions-to-qca83xx-.patch b/target/linux/generic/backport-6.1/707-v6.8-03-net-phy-at803x-raname-hw_stats-functions-to-qca83xx-.patch new file mode 100644 index 000000000..5a9d1764f --- /dev/null +++ b/target/linux/generic/backport-6.1/707-v6.8-03-net-phy-at803x-raname-hw_stats-functions-to-qca83xx-.patch @@ -0,0 +1,129 @@ +From 07b1ad83b9ed6db1735ba10baf67b7a565ac0cef Mon Sep 17 00:00:00 2001 +From: Christian Marangi +Date: Fri, 8 Dec 2023 15:51:50 +0100 +Subject: [PATCH 03/13] net: phy: at803x: raname hw_stats functions to qca83xx + specific name + +The function and the struct related to hw_stats were specific to qca83xx +PHY but were called following the convention in the driver of calling +everything with at803x prefix. + +To better organize the code, rename these function a more specific name +to better describe that they are specific to 83xx PHY family. + +Signed-off-by: Christian Marangi +Reviewed-by: Andrew Lunn +Signed-off-by: David S. Miller +--- + drivers/net/phy/at803x.c | 44 ++++++++++++++++++++-------------------- + 1 file changed, 22 insertions(+), 22 deletions(-) + +--- a/drivers/net/phy/at803x.c ++++ b/drivers/net/phy/at803x.c +@@ -295,7 +295,7 @@ struct at803x_hw_stat { + enum stat_access_type access_type; + }; + +-static struct at803x_hw_stat at803x_hw_stats[] = { ++static struct at803x_hw_stat qca83xx_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}, +@@ -311,7 +311,7 @@ struct at803x_priv { + bool is_1000basex; + struct regulator_dev *vddio_rdev; + struct regulator_dev *vddh_rdev; +- u64 stats[ARRAY_SIZE(at803x_hw_stats)]; ++ u64 stats[ARRAY_SIZE(qca83xx_hw_stats)]; + }; + + struct at803x_context { +@@ -529,24 +529,24 @@ static void at803x_get_wol(struct phy_de + wol->wolopts |= WAKE_MAGIC; + } + +-static int at803x_get_sset_count(struct phy_device *phydev) ++static int qca83xx_get_sset_count(struct phy_device *phydev) + { +- return ARRAY_SIZE(at803x_hw_stats); ++ return ARRAY_SIZE(qca83xx_hw_stats); + } + +-static void at803x_get_strings(struct phy_device *phydev, u8 *data) ++static void qca83xx_get_strings(struct phy_device *phydev, u8 *data) + { + int i; + +- for (i = 0; i < ARRAY_SIZE(at803x_hw_stats); i++) { ++ for (i = 0; i < ARRAY_SIZE(qca83xx_hw_stats); i++) { + strscpy(data + i * ETH_GSTRING_LEN, +- at803x_hw_stats[i].string, ETH_GSTRING_LEN); ++ qca83xx_hw_stats[i].string, ETH_GSTRING_LEN); + } + } + +-static u64 at803x_get_stat(struct phy_device *phydev, int i) ++static u64 qca83xx_get_stat(struct phy_device *phydev, int i) + { +- struct at803x_hw_stat stat = at803x_hw_stats[i]; ++ struct at803x_hw_stat stat = qca83xx_hw_stats[i]; + struct at803x_priv *priv = phydev->priv; + int val; + u64 ret; +@@ -567,13 +567,13 @@ static u64 at803x_get_stat(struct phy_de + return ret; + } + +-static void at803x_get_stats(struct phy_device *phydev, +- struct ethtool_stats *stats, u64 *data) ++static void qca83xx_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); ++ for (i = 0; i < ARRAY_SIZE(qca83xx_hw_stats); i++) ++ data[i] = qca83xx_get_stat(phydev, i); + } + + static int at803x_suspend(struct phy_device *phydev) +@@ -2175,9 +2175,9 @@ static struct phy_driver at803x_driver[] + .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, ++ .get_sset_count = qca83xx_get_sset_count, ++ .get_strings = qca83xx_get_strings, ++ .get_stats = qca83xx_get_stats, + .suspend = qca83xx_suspend, + .resume = qca83xx_resume, + }, { +@@ -2191,9 +2191,9 @@ static struct phy_driver at803x_driver[] + .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, ++ .get_sset_count = qca83xx_get_sset_count, ++ .get_strings = qca83xx_get_strings, ++ .get_stats = qca83xx_get_stats, + .suspend = qca83xx_suspend, + .resume = qca83xx_resume, + }, { +@@ -2207,9 +2207,9 @@ static struct phy_driver at803x_driver[] + .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, ++ .get_sset_count = qca83xx_get_sset_count, ++ .get_strings = qca83xx_get_strings, ++ .get_stats = qca83xx_get_stats, + .suspend = qca83xx_suspend, + .resume = qca83xx_resume, + }, { diff --git a/target/linux/generic/backport-6.1/707-v6.8-04-net-phy-at803x-move-qca83xx-specific-check-in-dedica.patch b/target/linux/generic/backport-6.1/707-v6.8-04-net-phy-at803x-move-qca83xx-specific-check-in-dedica.patch new file mode 100644 index 000000000..f09142c5e --- /dev/null +++ b/target/linux/generic/backport-6.1/707-v6.8-04-net-phy-at803x-move-qca83xx-specific-check-in-dedica.patch @@ -0,0 +1,155 @@ +From d43cff3f82336c0bd965ea552232d9f4ddac71a6 Mon Sep 17 00:00:00 2001 +From: Christian Marangi +Date: Fri, 8 Dec 2023 15:51:51 +0100 +Subject: [PATCH 04/13] net: phy: at803x: move qca83xx specific check in + dedicated functions + +Rework qca83xx specific check to dedicated function to tidy things up +and drop useless phy_id check. + +Also drop an useless link_change_notify for QCA8337 as it did nothing an +returned early. + +Signed-off-by: Christian Marangi +Reviewed-by: Andrew Lunn +Signed-off-by: David S. Miller +--- + drivers/net/phy/at803x.c | 68 ++++++++++++++++++++++------------------ + 1 file changed, 37 insertions(+), 31 deletions(-) + +--- a/drivers/net/phy/at803x.c ++++ b/drivers/net/phy/at803x.c +@@ -1623,27 +1623,26 @@ static int qca83xx_config_init(struct ph + break; + } + ++ /* Following original QCA sourcecode set port to prefer master */ ++ phy_set_bits(phydev, MII_CTRL1000, CTL1000_PREFER_MASTER); ++ ++ return 0; ++} ++ ++static int qca8327_config_init(struct phy_device *phydev) ++{ + /* 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_ANALOG_TEST_CTRL, +- QCA8327_DEBUG_MANU_CTRL_EN, 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 */ +- phy_set_bits(phydev, MII_CTRL1000, CTL1000_PREFER_MASTER); +- +- return 0; ++ return qca83xx_config_init(phydev); + } + + 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) +@@ -1686,19 +1685,6 @@ static int qca83xx_resume(struct phy_dev + + 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_GREEN, + AT803X_DEBUG_GATE_CLK_IN1000, 0); + +@@ -1709,6 +1695,27 @@ static int qca83xx_suspend(struct phy_de + return 0; + } + ++static int qca8337_suspend(struct phy_device *phydev) ++{ ++ /* Only QCA8337 support actual suspend. */ ++ genphy_suspend(phydev); ++ ++ return qca83xx_suspend(phydev); ++} ++ ++static int qca8327_suspend(struct phy_device *phydev) ++{ ++ u16 mask = 0; ++ ++ /* QCA8327 cause port unreliability when phy suspend ++ * is set. ++ */ ++ mask |= ~(BMCR_SPEED1000 | BMCR_FULLDPLX); ++ phy_modify(phydev, MII_BMCR, mask, 0); ++ ++ return qca83xx_suspend(phydev); ++} ++ + static int qca808x_phy_fast_retrain_config(struct phy_device *phydev) + { + int ret; +@@ -2170,7 +2177,6 @@ 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, +@@ -2178,7 +2184,7 @@ static struct phy_driver at803x_driver[] + .get_sset_count = qca83xx_get_sset_count, + .get_strings = qca83xx_get_strings, + .get_stats = qca83xx_get_stats, +- .suspend = qca83xx_suspend, ++ .suspend = qca8337_suspend, + .resume = qca83xx_resume, + }, { + /* QCA8327-A from switch QCA8327-AL1A */ +@@ -2189,12 +2195,12 @@ static struct phy_driver at803x_driver[] + .link_change_notify = qca83xx_link_change_notify, + .probe = at803x_probe, + .flags = PHY_IS_INTERNAL, +- .config_init = qca83xx_config_init, ++ .config_init = qca8327_config_init, + .soft_reset = genphy_soft_reset, + .get_sset_count = qca83xx_get_sset_count, + .get_strings = qca83xx_get_strings, + .get_stats = qca83xx_get_stats, +- .suspend = qca83xx_suspend, ++ .suspend = qca8327_suspend, + .resume = qca83xx_resume, + }, { + /* QCA8327-B from switch QCA8327-BL1A */ +@@ -2205,12 +2211,12 @@ static struct phy_driver at803x_driver[] + .link_change_notify = qca83xx_link_change_notify, + .probe = at803x_probe, + .flags = PHY_IS_INTERNAL, +- .config_init = qca83xx_config_init, ++ .config_init = qca8327_config_init, + .soft_reset = genphy_soft_reset, + .get_sset_count = qca83xx_get_sset_count, + .get_strings = qca83xx_get_strings, + .get_stats = qca83xx_get_stats, +- .suspend = qca83xx_suspend, ++ .suspend = qca8327_suspend, + .resume = qca83xx_resume, + }, { + /* Qualcomm QCA8081 */ diff --git a/target/linux/generic/backport-6.1/707-v6.8-05-net-phy-at803x-move-specific-DT-option-for-at8031-to.patch b/target/linux/generic/backport-6.1/707-v6.8-05-net-phy-at803x-move-specific-DT-option-for-at8031-to.patch new file mode 100644 index 000000000..a5cc67a8c --- /dev/null +++ b/target/linux/generic/backport-6.1/707-v6.8-05-net-phy-at803x-move-specific-DT-option-for-at8031-to.patch @@ -0,0 +1,94 @@ +From 900eef75cc5018e149c52fe305c9c3fe424c52a7 Mon Sep 17 00:00:00 2001 +From: Christian Marangi +Date: Fri, 8 Dec 2023 15:51:52 +0100 +Subject: [PATCH 05/13] net: phy: at803x: move specific DT option for at8031 to + specific probe + +Move specific DT options for at8031 to specific probe to tidy things up +and make at803x_parse_dt more generic. + +Signed-off-by: Christian Marangi +Reviewed-by: Andrew Lunn +Signed-off-by: David S. Miller +--- + drivers/net/phy/at803x.c | 55 ++++++++++++++++++++++------------------ + 1 file changed, 31 insertions(+), 24 deletions(-) + +--- a/drivers/net/phy/at803x.c ++++ b/drivers/net/phy/at803x.c +@@ -825,30 +825,6 @@ static int at803x_parse_dt(struct phy_de + } + } + +- /* Only supported on AR8031/AR8033, the AR8030/AR8035 use strapping +- * options. +- */ +- if (phydev->drv->phy_id == ATH8031_PHY_ID) { +- if (of_property_read_bool(node, "qca,keep-pll-enabled")) +- priv->flags |= AT803X_KEEP_PLL_ENABLED; +- +- ret = at8031_register_regulators(phydev); +- if (ret < 0) +- return ret; +- +- ret = devm_regulator_get_enable_optional(&phydev->mdio.dev, +- "vddio"); +- if (ret) { +- phydev_err(phydev, "failed to get VDDIO regulator\n"); +- return ret; +- } +- +- /* Only AR8031/8033 support 1000Base-X for SFP modules */ +- ret = phy_sfp_probe(phydev, &at803x_sfp_ops); +- if (ret < 0) +- return ret; +- } +- + return 0; + } + +@@ -1582,6 +1558,30 @@ static int at803x_cable_test_start(struc + return 0; + } + ++static int at8031_parse_dt(struct phy_device *phydev) ++{ ++ struct device_node *node = phydev->mdio.dev.of_node; ++ struct at803x_priv *priv = phydev->priv; ++ int ret; ++ ++ if (of_property_read_bool(node, "qca,keep-pll-enabled")) ++ priv->flags |= AT803X_KEEP_PLL_ENABLED; ++ ++ ret = at8031_register_regulators(phydev); ++ if (ret < 0) ++ return ret; ++ ++ ret = devm_regulator_get_enable_optional(&phydev->mdio.dev, ++ "vddio"); ++ if (ret) { ++ phydev_err(phydev, "failed to get VDDIO regulator\n"); ++ return ret; ++ } ++ ++ /* Only AR8031/8033 support 1000Base-X for SFP modules */ ++ return phy_sfp_probe(phydev, &at803x_sfp_ops); ++} ++ + static int at8031_probe(struct phy_device *phydev) + { + int ret; +@@ -1590,6 +1590,13 @@ static int at8031_probe(struct phy_devic + if (ret) + return ret; + ++ /* Only supported on AR8031/AR8033, the AR8030/AR8035 use strapping ++ * options. ++ */ ++ ret = at8031_parse_dt(phydev); ++ if (ret) ++ return ret; ++ + /* Disable WoL in 1588 register which is enabled + * by default + */ diff --git a/target/linux/generic/backport-6.1/707-v6.8-06-net-phy-at803x-move-specific-at8031-probe-mode-check.patch b/target/linux/generic/backport-6.1/707-v6.8-06-net-phy-at803x-move-specific-at8031-probe-mode-check.patch new file mode 100644 index 000000000..9e10e000e --- /dev/null +++ b/target/linux/generic/backport-6.1/707-v6.8-06-net-phy-at803x-move-specific-at8031-probe-mode-check.patch @@ -0,0 +1,78 @@ +From 25d2ba94005fac18fe68878cddff59a67e115554 Mon Sep 17 00:00:00 2001 +From: Christian Marangi +Date: Fri, 8 Dec 2023 15:51:53 +0100 +Subject: [PATCH 06/13] net: phy: at803x: move specific at8031 probe mode check + to dedicated probe + +Move specific at8031 probe mode check to dedicated probe to make +at803x_probe more generic and keep code tidy. + +Signed-off-by: Christian Marangi +Reviewed-by: Andrew Lunn +Signed-off-by: David S. Miller +--- + drivers/net/phy/at803x.c | 39 +++++++++++++++++++-------------------- + 1 file changed, 19 insertions(+), 20 deletions(-) + +--- a/drivers/net/phy/at803x.c ++++ b/drivers/net/phy/at803x.c +@@ -844,26 +844,6 @@ static int at803x_probe(struct phy_devic + if (ret) + return ret; + +- if (phydev->drv->phy_id == ATH8031_PHY_ID) { +- int ccr = phy_read(phydev, AT803X_REG_CHIP_CONFIG); +- int mode_cfg; +- +- if (ccr < 0) +- return ccr; +- mode_cfg = ccr & AT803X_MODE_CFG_MASK; +- +- switch (mode_cfg) { +- case AT803X_MODE_CFG_BX1000_RGMII_50OHM: +- case AT803X_MODE_CFG_BX1000_RGMII_75OHM: +- priv->is_1000basex = true; +- fallthrough; +- case AT803X_MODE_CFG_FX100_RGMII_50OHM: +- case AT803X_MODE_CFG_FX100_RGMII_75OHM: +- priv->is_fiber = true; +- break; +- } +- } +- + return 0; + } + +@@ -1584,6 +1564,9 @@ static int at8031_parse_dt(struct phy_de + + static int at8031_probe(struct phy_device *phydev) + { ++ struct at803x_priv *priv = phydev->priv; ++ int mode_cfg; ++ int ccr; + int ret; + + ret = at803x_probe(phydev); +@@ -1597,6 +1580,22 @@ static int at8031_probe(struct phy_devic + if (ret) + return ret; + ++ ccr = phy_read(phydev, AT803X_REG_CHIP_CONFIG); ++ if (ccr < 0) ++ return ccr; ++ mode_cfg = ccr & AT803X_MODE_CFG_MASK; ++ ++ switch (mode_cfg) { ++ case AT803X_MODE_CFG_BX1000_RGMII_50OHM: ++ case AT803X_MODE_CFG_BX1000_RGMII_75OHM: ++ priv->is_1000basex = true; ++ fallthrough; ++ case AT803X_MODE_CFG_FX100_RGMII_50OHM: ++ case AT803X_MODE_CFG_FX100_RGMII_75OHM: ++ priv->is_fiber = true; ++ break; ++ } ++ + /* Disable WoL in 1588 register which is enabled + * by default + */ diff --git a/target/linux/generic/backport-6.1/707-v6.8-07-net-phy-at803x-move-specific-at8031-config_init-to-d.patch b/target/linux/generic/backport-6.1/707-v6.8-07-net-phy-at803x-move-specific-at8031-config_init-to-d.patch new file mode 100644 index 000000000..c9ef6df82 --- /dev/null +++ b/target/linux/generic/backport-6.1/707-v6.8-07-net-phy-at803x-move-specific-at8031-config_init-to-d.patch @@ -0,0 +1,86 @@ +From 3ae3bc426eaf57ca8f53d75777d9a5ef779bc7b7 Mon Sep 17 00:00:00 2001 +From: Christian Marangi +Date: Fri, 8 Dec 2023 15:51:54 +0100 +Subject: [PATCH 07/13] net: phy: at803x: move specific at8031 config_init to + dedicated function + +Move specific at8031 config_init to dedicated function to make +at803x_config_init more generic and tidy things up. + +Signed-off-by: Christian Marangi +Reviewed-by: Andrew Lunn +Signed-off-by: David S. Miller +--- + drivers/net/phy/at803x.c | 45 ++++++++++++++++++++++------------------ + 1 file changed, 25 insertions(+), 20 deletions(-) + +--- a/drivers/net/phy/at803x.c ++++ b/drivers/net/phy/at803x.c +@@ -951,27 +951,8 @@ static int at803x_hibernation_mode_confi + + static int at803x_config_init(struct phy_device *phydev) + { +- struct at803x_priv *priv = phydev->priv; + int ret; + +- if (phydev->drv->phy_id == ATH8031_PHY_ID) { +- /* Some bootloaders leave the fiber page selected. +- * Switch to the appropriate page (fiber or copper), as otherwise we +- * read the PHY capabilities from the wrong page. +- */ +- phy_lock_mdio_bus(phydev); +- ret = at803x_write_page(phydev, +- priv->is_fiber ? AT803X_PAGE_FIBER : +- AT803X_PAGE_COPPER); +- phy_unlock_mdio_bus(phydev); +- if (ret) +- return ret; +- +- ret = at8031_pll_config(phydev); +- if (ret < 0) +- return ret; +- } +- + /* The RX and TX delay default is: + * after HW reset: RX delay enabled and TX delay disabled + * after SW reset: RX delay enabled, while TX delay retains the +@@ -1604,6 +1585,30 @@ static int at8031_probe(struct phy_devic + AT803X_WOL_EN, 0); + } + ++static int at8031_config_init(struct phy_device *phydev) ++{ ++ struct at803x_priv *priv = phydev->priv; ++ int ret; ++ ++ /* Some bootloaders leave the fiber page selected. ++ * Switch to the appropriate page (fiber or copper), as otherwise we ++ * read the PHY capabilities from the wrong page. ++ */ ++ phy_lock_mdio_bus(phydev); ++ ret = at803x_write_page(phydev, ++ priv->is_fiber ? AT803X_PAGE_FIBER : ++ AT803X_PAGE_COPPER); ++ phy_unlock_mdio_bus(phydev); ++ if (ret) ++ return ret; ++ ++ ret = at8031_pll_config(phydev); ++ if (ret < 0) ++ return ret; ++ ++ return at803x_config_init(phydev); ++} ++ + static int qca83xx_config_init(struct phy_device *phydev) + { + u8 switch_revision; +@@ -2113,7 +2118,7 @@ static struct phy_driver at803x_driver[] + .name = "Qualcomm Atheros AR8031/AR8033", + .flags = PHY_POLL_CABLE_TEST, + .probe = at8031_probe, +- .config_init = at803x_config_init, ++ .config_init = at8031_config_init, + .config_aneg = at803x_config_aneg, + .soft_reset = genphy_soft_reset, + .set_wol = at803x_set_wol, diff --git a/target/linux/generic/backport-6.1/707-v6.8-08-net-phy-at803x-move-specific-at8031-WOL-bits-to-dedi.patch b/target/linux/generic/backport-6.1/707-v6.8-08-net-phy-at803x-move-specific-at8031-WOL-bits-to-dedi.patch new file mode 100644 index 000000000..6746a4d43 --- /dev/null +++ b/target/linux/generic/backport-6.1/707-v6.8-08-net-phy-at803x-move-specific-at8031-WOL-bits-to-dedi.patch @@ -0,0 +1,92 @@ +From 27b89c9dc1b0393090d68d651b82f30ad2696baa Mon Sep 17 00:00:00 2001 +From: Christian Marangi +Date: Fri, 8 Dec 2023 15:51:55 +0100 +Subject: [PATCH 08/13] net: phy: at803x: move specific at8031 WOL bits to + dedicated function + +Move specific at8031 WOL enable/disable to dedicated function to make +at803x_set_wol more generic. + +This is needed in preparation for PHY driver split as qca8081 share the +same function to toggle WOL settings. + +In this new implementation WOL module in at8031 is enabled after the +generic interrupt is setup. This should not cause any problem as the +WOL_INT has a separate implementation and only relay on MAC bits. + +Signed-off-by: Christian Marangi +Signed-off-by: David S. Miller +--- + drivers/net/phy/at803x.c | 42 ++++++++++++++++++++++++---------------- + 1 file changed, 25 insertions(+), 17 deletions(-) + +--- a/drivers/net/phy/at803x.c ++++ b/drivers/net/phy/at803x.c +@@ -466,27 +466,11 @@ static int at803x_set_wol(struct phy_dev + phy_write_mmd(phydev, MDIO_MMD_PCS, offsets[i], + mac[(i * 2) + 1] | (mac[(i * 2)] << 8)); + +- /* Enable WOL function for 1588 */ +- if (phydev->drv->phy_id == ATH8031_PHY_ID) { +- ret = phy_modify_mmd(phydev, MDIO_MMD_PCS, +- AT803X_PHY_MMD3_WOL_CTRL, +- 0, AT803X_WOL_EN); +- if (ret) +- return ret; +- } + /* Enable WOL interrupt */ + ret = phy_modify(phydev, AT803X_INTR_ENABLE, 0, AT803X_INTR_ENABLE_WOL); + if (ret) + return ret; + } else { +- /* Disable WoL function for 1588 */ +- if (phydev->drv->phy_id == ATH8031_PHY_ID) { +- ret = phy_modify_mmd(phydev, MDIO_MMD_PCS, +- AT803X_PHY_MMD3_WOL_CTRL, +- AT803X_WOL_EN, 0); +- if (ret) +- return ret; +- } + /* Disable WOL interrupt */ + ret = phy_modify(phydev, AT803X_INTR_ENABLE, AT803X_INTR_ENABLE_WOL, 0); + if (ret) +@@ -1609,6 +1593,30 @@ static int at8031_config_init(struct phy + return at803x_config_init(phydev); + } + ++static int at8031_set_wol(struct phy_device *phydev, ++ struct ethtool_wolinfo *wol) ++{ ++ int ret; ++ ++ /* First setup MAC address and enable WOL interrupt */ ++ ret = at803x_set_wol(phydev, wol); ++ if (ret) ++ return ret; ++ ++ if (wol->wolopts & WAKE_MAGIC) ++ /* Enable WOL function for 1588 */ ++ ret = phy_modify_mmd(phydev, MDIO_MMD_PCS, ++ AT803X_PHY_MMD3_WOL_CTRL, ++ 0, AT803X_WOL_EN); ++ else ++ /* Disable WoL function for 1588 */ ++ ret = phy_modify_mmd(phydev, MDIO_MMD_PCS, ++ AT803X_PHY_MMD3_WOL_CTRL, ++ AT803X_WOL_EN, 0); ++ ++ return ret; ++} ++ + static int qca83xx_config_init(struct phy_device *phydev) + { + u8 switch_revision; +@@ -2121,7 +2129,7 @@ static struct phy_driver at803x_driver[] + .config_init = at8031_config_init, + .config_aneg = at803x_config_aneg, + .soft_reset = genphy_soft_reset, +- .set_wol = at803x_set_wol, ++ .set_wol = at8031_set_wol, + .get_wol = at803x_get_wol, + .suspend = at803x_suspend, + .resume = at803x_resume, diff --git a/target/linux/generic/backport-6.1/707-v6.8-09-net-phy-at803x-move-specific-at8031-config_intr-to-d.patch b/target/linux/generic/backport-6.1/707-v6.8-09-net-phy-at803x-move-specific-at8031-config_intr-to-d.patch new file mode 100644 index 000000000..0d284c3d2 --- /dev/null +++ b/target/linux/generic/backport-6.1/707-v6.8-09-net-phy-at803x-move-specific-at8031-config_intr-to-d.patch @@ -0,0 +1,78 @@ +From 30dd62191d3dd97c08f7f9dc9ce77ffab457e4fb Mon Sep 17 00:00:00 2001 +From: Christian Marangi +Date: Fri, 8 Dec 2023 15:51:56 +0100 +Subject: [PATCH 09/13] net: phy: at803x: move specific at8031 config_intr to + dedicated function + +Move specific at8031 config_intr bits to dedicated function to make +at803x_config_initr more generic. + +This is needed in preparation for PHY driver split as qca8081 share the +same function to setup interrupts. + +Signed-off-by: Christian Marangi +Signed-off-by: David S. Miller +--- + drivers/net/phy/at803x.c | 30 ++++++++++++++++++++++++------ + 1 file changed, 24 insertions(+), 6 deletions(-) + +--- a/drivers/net/phy/at803x.c ++++ b/drivers/net/phy/at803x.c +@@ -990,7 +990,6 @@ static int at803x_ack_interrupt(struct p + + static int at803x_config_intr(struct phy_device *phydev) + { +- struct at803x_priv *priv = phydev->priv; + int err; + int value; + +@@ -1007,10 +1006,6 @@ static int at803x_config_intr(struct phy + value |= AT803X_INTR_ENABLE_DUPLEX_CHANGED; + value |= AT803X_INTR_ENABLE_LINK_FAIL; + value |= AT803X_INTR_ENABLE_LINK_SUCCESS; +- if (priv->is_fiber) { +- value |= AT803X_INTR_ENABLE_LINK_FAIL_BX; +- value |= AT803X_INTR_ENABLE_LINK_SUCCESS_BX; +- } + + err = phy_write(phydev, AT803X_INTR_ENABLE, value); + } else { +@@ -1617,6 +1612,29 @@ static int at8031_set_wol(struct phy_dev + return ret; + } + ++static int at8031_config_intr(struct phy_device *phydev) ++{ ++ struct at803x_priv *priv = phydev->priv; ++ int err, value = 0; ++ ++ if (phydev->interrupts == PHY_INTERRUPT_ENABLED && ++ priv->is_fiber) { ++ /* Clear any pending interrupts */ ++ err = at803x_ack_interrupt(phydev); ++ if (err) ++ return err; ++ ++ value |= AT803X_INTR_ENABLE_LINK_FAIL_BX; ++ value |= AT803X_INTR_ENABLE_LINK_SUCCESS_BX; ++ ++ err = phy_set_bits(phydev, AT803X_INTR_ENABLE, value); ++ if (err) ++ return err; ++ } ++ ++ return at803x_config_intr(phydev); ++} ++ + static int qca83xx_config_init(struct phy_device *phydev) + { + u8 switch_revision; +@@ -2137,7 +2155,7 @@ static struct phy_driver at803x_driver[] + .write_page = at803x_write_page, + .get_features = at803x_get_features, + .read_status = at803x_read_status, +- .config_intr = at803x_config_intr, ++ .config_intr = at8031_config_intr, + .handle_interrupt = at803x_handle_interrupt, + .get_tunable = at803x_get_tunable, + .set_tunable = at803x_set_tunable, diff --git a/target/linux/generic/backport-6.1/707-v6.8-10-net-phy-at803x-make-at8031-related-DT-functions-name.patch b/target/linux/generic/backport-6.1/707-v6.8-10-net-phy-at803x-make-at8031-related-DT-functions-name.patch new file mode 100644 index 000000000..210949ea8 --- /dev/null +++ b/target/linux/generic/backport-6.1/707-v6.8-10-net-phy-at803x-make-at8031-related-DT-functions-name.patch @@ -0,0 +1,78 @@ +From a5ab9d8e7ae0da8328ac1637a9755311508dc8ab Mon Sep 17 00:00:00 2001 +From: Christian Marangi +Date: Fri, 8 Dec 2023 15:51:57 +0100 +Subject: [PATCH 10/13] net: phy: at803x: make at8031 related DT functions name + more specific + +Rename at8031 related DT function name to a more specific name +referencing they are only related to at8031 and not to the generic +at803x PHY family. + +Signed-off-by: Christian Marangi +Signed-off-by: David S. Miller +--- + drivers/net/phy/at803x.c | 16 ++++++++-------- + 1 file changed, 8 insertions(+), 8 deletions(-) + +--- a/drivers/net/phy/at803x.c ++++ b/drivers/net/phy/at803x.c +@@ -583,7 +583,7 @@ static int at803x_resume(struct phy_devi + return phy_modify(phydev, MII_BMCR, BMCR_PDOWN | BMCR_ISOLATE, 0); + } + +-static int at803x_rgmii_reg_set_voltage_sel(struct regulator_dev *rdev, ++static int at8031_rgmii_reg_set_voltage_sel(struct regulator_dev *rdev, + unsigned int selector) + { + struct phy_device *phydev = rdev_get_drvdata(rdev); +@@ -596,7 +596,7 @@ static int at803x_rgmii_reg_set_voltage_ + AT803X_DEBUG_RGMII_1V8, 0); + } + +-static int at803x_rgmii_reg_get_voltage_sel(struct regulator_dev *rdev) ++static int at8031_rgmii_reg_get_voltage_sel(struct regulator_dev *rdev) + { + struct phy_device *phydev = rdev_get_drvdata(rdev); + int val; +@@ -610,8 +610,8 @@ static int at803x_rgmii_reg_get_voltage_ + + static const struct regulator_ops vddio_regulator_ops = { + .list_voltage = regulator_list_voltage_table, +- .set_voltage_sel = at803x_rgmii_reg_set_voltage_sel, +- .get_voltage_sel = at803x_rgmii_reg_get_voltage_sel, ++ .set_voltage_sel = at8031_rgmii_reg_set_voltage_sel, ++ .get_voltage_sel = at8031_rgmii_reg_get_voltage_sel, + }; + + static const unsigned int vddio_voltage_table[] = { +@@ -666,7 +666,7 @@ static int at8031_register_regulators(st + return 0; + } + +-static int at803x_sfp_insert(void *upstream, const struct sfp_eeprom_id *id) ++static int at8031_sfp_insert(void *upstream, const struct sfp_eeprom_id *id) + { + struct phy_device *phydev = upstream; + __ETHTOOL_DECLARE_LINK_MODE_MASK(phy_support); +@@ -710,10 +710,10 @@ static int at803x_sfp_insert(void *upstr + return 0; + } + +-static const struct sfp_upstream_ops at803x_sfp_ops = { ++static const struct sfp_upstream_ops at8031_sfp_ops = { + .attach = phy_sfp_attach, + .detach = phy_sfp_detach, +- .module_insert = at803x_sfp_insert, ++ .module_insert = at8031_sfp_insert, + }; + + static int at803x_parse_dt(struct phy_device *phydev) +@@ -1519,7 +1519,7 @@ static int at8031_parse_dt(struct phy_de + } + + /* Only AR8031/8033 support 1000Base-X for SFP modules */ +- return phy_sfp_probe(phydev, &at803x_sfp_ops); ++ return phy_sfp_probe(phydev, &at8031_sfp_ops); + } + + static int at8031_probe(struct phy_device *phydev) diff --git a/target/linux/generic/backport-6.1/707-v6.8-11-net-phy-at803x-move-at8031-functions-in-dedicated-se.patch b/target/linux/generic/backport-6.1/707-v6.8-11-net-phy-at803x-move-at8031-functions-in-dedicated-se.patch new file mode 100644 index 000000000..5856b5975 --- /dev/null +++ b/target/linux/generic/backport-6.1/707-v6.8-11-net-phy-at803x-move-at8031-functions-in-dedicated-se.patch @@ -0,0 +1,297 @@ +From f932a6dc8bae0dae9645b5b1b4c65aed8a8acb2a Mon Sep 17 00:00:00 2001 +From: Christian Marangi +Date: Fri, 8 Dec 2023 15:51:58 +0100 +Subject: [PATCH 11/13] net: phy: at803x: move at8031 functions in dedicated + section + +Move at8031 functions in dedicated section with dedicated at8031 +parse_dt and probe. + +Signed-off-by: Christian Marangi +Signed-off-by: David S. Miller +--- + drivers/net/phy/at803x.c | 266 +++++++++++++++++++-------------------- + 1 file changed, 133 insertions(+), 133 deletions(-) + +--- a/drivers/net/phy/at803x.c ++++ b/drivers/net/phy/at803x.c +@@ -583,139 +583,6 @@ static int at803x_resume(struct phy_devi + return phy_modify(phydev, MII_BMCR, BMCR_PDOWN | BMCR_ISOLATE, 0); + } + +-static int at8031_rgmii_reg_set_voltage_sel(struct regulator_dev *rdev, +- unsigned int selector) +-{ +- struct phy_device *phydev = rdev_get_drvdata(rdev); +- +- if (selector) +- return at803x_debug_reg_mask(phydev, AT803X_DEBUG_REG_1F, +- 0, AT803X_DEBUG_RGMII_1V8); +- else +- return at803x_debug_reg_mask(phydev, AT803X_DEBUG_REG_1F, +- AT803X_DEBUG_RGMII_1V8, 0); +-} +- +-static int at8031_rgmii_reg_get_voltage_sel(struct regulator_dev *rdev) +-{ +- struct phy_device *phydev = rdev_get_drvdata(rdev); +- int val; +- +- val = at803x_debug_reg_read(phydev, AT803X_DEBUG_REG_1F); +- if (val < 0) +- return val; +- +- return (val & AT803X_DEBUG_RGMII_1V8) ? 1 : 0; +-} +- +-static const struct regulator_ops vddio_regulator_ops = { +- .list_voltage = regulator_list_voltage_table, +- .set_voltage_sel = at8031_rgmii_reg_set_voltage_sel, +- .get_voltage_sel = at8031_rgmii_reg_get_voltage_sel, +-}; +- +-static const unsigned int vddio_voltage_table[] = { +- 1500000, +- 1800000, +-}; +- +-static const struct regulator_desc vddio_desc = { +- .name = "vddio", +- .of_match = of_match_ptr("vddio-regulator"), +- .n_voltages = ARRAY_SIZE(vddio_voltage_table), +- .volt_table = vddio_voltage_table, +- .ops = &vddio_regulator_ops, +- .type = REGULATOR_VOLTAGE, +- .owner = THIS_MODULE, +-}; +- +-static const struct regulator_ops vddh_regulator_ops = { +-}; +- +-static const struct regulator_desc vddh_desc = { +- .name = "vddh", +- .of_match = of_match_ptr("vddh-regulator"), +- .n_voltages = 1, +- .fixed_uV = 2500000, +- .ops = &vddh_regulator_ops, +- .type = REGULATOR_VOLTAGE, +- .owner = THIS_MODULE, +-}; +- +-static int at8031_register_regulators(struct phy_device *phydev) +-{ +- struct at803x_priv *priv = phydev->priv; +- struct device *dev = &phydev->mdio.dev; +- struct regulator_config config = { }; +- +- config.dev = dev; +- config.driver_data = phydev; +- +- priv->vddio_rdev = devm_regulator_register(dev, &vddio_desc, &config); +- if (IS_ERR(priv->vddio_rdev)) { +- phydev_err(phydev, "failed to register VDDIO regulator\n"); +- return PTR_ERR(priv->vddio_rdev); +- } +- +- priv->vddh_rdev = devm_regulator_register(dev, &vddh_desc, &config); +- if (IS_ERR(priv->vddh_rdev)) { +- phydev_err(phydev, "failed to register VDDH regulator\n"); +- return PTR_ERR(priv->vddh_rdev); +- } +- +- return 0; +-} +- +-static int at8031_sfp_insert(void *upstream, const struct sfp_eeprom_id *id) +-{ +- struct phy_device *phydev = upstream; +- __ETHTOOL_DECLARE_LINK_MODE_MASK(phy_support); +- __ETHTOOL_DECLARE_LINK_MODE_MASK(sfp_support); +- DECLARE_PHY_INTERFACE_MASK(interfaces); +- phy_interface_t iface; +- +- linkmode_zero(phy_support); +- phylink_set(phy_support, 1000baseX_Full); +- phylink_set(phy_support, 1000baseT_Full); +- phylink_set(phy_support, Autoneg); +- phylink_set(phy_support, Pause); +- phylink_set(phy_support, Asym_Pause); +- +- linkmode_zero(sfp_support); +- sfp_parse_support(phydev->sfp_bus, id, sfp_support, interfaces); +- /* Some modules support 10G modes as well as others we support. +- * Mask out non-supported modes so the correct interface is picked. +- */ +- linkmode_and(sfp_support, phy_support, sfp_support); +- +- if (linkmode_empty(sfp_support)) { +- dev_err(&phydev->mdio.dev, "incompatible SFP module inserted\n"); +- return -EINVAL; +- } +- +- iface = sfp_select_interface(phydev->sfp_bus, sfp_support); +- +- /* Only 1000Base-X is supported by AR8031/8033 as the downstream SerDes +- * interface for use with SFP modules. +- * However, some copper modules detected as having a preferred SGMII +- * interface do default to and function in 1000Base-X mode, so just +- * print a warning and allow such modules, as they may have some chance +- * of working. +- */ +- if (iface == PHY_INTERFACE_MODE_SGMII) +- dev_warn(&phydev->mdio.dev, "module may not function if 1000Base-X not supported\n"); +- else if (iface != PHY_INTERFACE_MODE_1000BASEX) +- return -EINVAL; +- +- return 0; +-} +- +-static const struct sfp_upstream_ops at8031_sfp_ops = { +- .attach = phy_sfp_attach, +- .detach = phy_sfp_detach, +- .module_insert = at8031_sfp_insert, +-}; +- + static int at803x_parse_dt(struct phy_device *phydev) + { + struct device_node *node = phydev->mdio.dev.of_node; +@@ -1498,6 +1365,139 @@ static int at803x_cable_test_start(struc + return 0; + } + ++static int at8031_rgmii_reg_set_voltage_sel(struct regulator_dev *rdev, ++ unsigned int selector) ++{ ++ struct phy_device *phydev = rdev_get_drvdata(rdev); ++ ++ if (selector) ++ return at803x_debug_reg_mask(phydev, AT803X_DEBUG_REG_1F, ++ 0, AT803X_DEBUG_RGMII_1V8); ++ else ++ return at803x_debug_reg_mask(phydev, AT803X_DEBUG_REG_1F, ++ AT803X_DEBUG_RGMII_1V8, 0); ++} ++ ++static int at8031_rgmii_reg_get_voltage_sel(struct regulator_dev *rdev) ++{ ++ struct phy_device *phydev = rdev_get_drvdata(rdev); ++ int val; ++ ++ val = at803x_debug_reg_read(phydev, AT803X_DEBUG_REG_1F); ++ if (val < 0) ++ return val; ++ ++ return (val & AT803X_DEBUG_RGMII_1V8) ? 1 : 0; ++} ++ ++static const struct regulator_ops vddio_regulator_ops = { ++ .list_voltage = regulator_list_voltage_table, ++ .set_voltage_sel = at8031_rgmii_reg_set_voltage_sel, ++ .get_voltage_sel = at8031_rgmii_reg_get_voltage_sel, ++}; ++ ++static const unsigned int vddio_voltage_table[] = { ++ 1500000, ++ 1800000, ++}; ++ ++static const struct regulator_desc vddio_desc = { ++ .name = "vddio", ++ .of_match = of_match_ptr("vddio-regulator"), ++ .n_voltages = ARRAY_SIZE(vddio_voltage_table), ++ .volt_table = vddio_voltage_table, ++ .ops = &vddio_regulator_ops, ++ .type = REGULATOR_VOLTAGE, ++ .owner = THIS_MODULE, ++}; ++ ++static const struct regulator_ops vddh_regulator_ops = { ++}; ++ ++static const struct regulator_desc vddh_desc = { ++ .name = "vddh", ++ .of_match = of_match_ptr("vddh-regulator"), ++ .n_voltages = 1, ++ .fixed_uV = 2500000, ++ .ops = &vddh_regulator_ops, ++ .type = REGULATOR_VOLTAGE, ++ .owner = THIS_MODULE, ++}; ++ ++static int at8031_register_regulators(struct phy_device *phydev) ++{ ++ struct at803x_priv *priv = phydev->priv; ++ struct device *dev = &phydev->mdio.dev; ++ struct regulator_config config = { }; ++ ++ config.dev = dev; ++ config.driver_data = phydev; ++ ++ priv->vddio_rdev = devm_regulator_register(dev, &vddio_desc, &config); ++ if (IS_ERR(priv->vddio_rdev)) { ++ phydev_err(phydev, "failed to register VDDIO regulator\n"); ++ return PTR_ERR(priv->vddio_rdev); ++ } ++ ++ priv->vddh_rdev = devm_regulator_register(dev, &vddh_desc, &config); ++ if (IS_ERR(priv->vddh_rdev)) { ++ phydev_err(phydev, "failed to register VDDH regulator\n"); ++ return PTR_ERR(priv->vddh_rdev); ++ } ++ ++ return 0; ++} ++ ++static int at8031_sfp_insert(void *upstream, const struct sfp_eeprom_id *id) ++{ ++ struct phy_device *phydev = upstream; ++ __ETHTOOL_DECLARE_LINK_MODE_MASK(phy_support); ++ __ETHTOOL_DECLARE_LINK_MODE_MASK(sfp_support); ++ DECLARE_PHY_INTERFACE_MASK(interfaces); ++ phy_interface_t iface; ++ ++ linkmode_zero(phy_support); ++ phylink_set(phy_support, 1000baseX_Full); ++ phylink_set(phy_support, 1000baseT_Full); ++ phylink_set(phy_support, Autoneg); ++ phylink_set(phy_support, Pause); ++ phylink_set(phy_support, Asym_Pause); ++ ++ linkmode_zero(sfp_support); ++ sfp_parse_support(phydev->sfp_bus, id, sfp_support, interfaces); ++ /* Some modules support 10G modes as well as others we support. ++ * Mask out non-supported modes so the correct interface is picked. ++ */ ++ linkmode_and(sfp_support, phy_support, sfp_support); ++ ++ if (linkmode_empty(sfp_support)) { ++ dev_err(&phydev->mdio.dev, "incompatible SFP module inserted\n"); ++ return -EINVAL; ++ } ++ ++ iface = sfp_select_interface(phydev->sfp_bus, sfp_support); ++ ++ /* Only 1000Base-X is supported by AR8031/8033 as the downstream SerDes ++ * interface for use with SFP modules. ++ * However, some copper modules detected as having a preferred SGMII ++ * interface do default to and function in 1000Base-X mode, so just ++ * print a warning and allow such modules, as they may have some chance ++ * of working. ++ */ ++ if (iface == PHY_INTERFACE_MODE_SGMII) ++ dev_warn(&phydev->mdio.dev, "module may not function if 1000Base-X not supported\n"); ++ else if (iface != PHY_INTERFACE_MODE_1000BASEX) ++ return -EINVAL; ++ ++ return 0; ++} ++ ++static const struct sfp_upstream_ops at8031_sfp_ops = { ++ .attach = phy_sfp_attach, ++ .detach = phy_sfp_detach, ++ .module_insert = at8031_sfp_insert, ++}; ++ + static int at8031_parse_dt(struct phy_device *phydev) + { + struct device_node *node = phydev->mdio.dev.of_node; diff --git a/target/linux/generic/backport-6.1/707-v6.8-12-net-phy-at803x-move-at8035-specific-DT-parse-to-dedi.patch b/target/linux/generic/backport-6.1/707-v6.8-12-net-phy-at803x-move-at8035-specific-DT-parse-to-dedi.patch new file mode 100644 index 000000000..0a822c6d3 --- /dev/null +++ b/target/linux/generic/backport-6.1/707-v6.8-12-net-phy-at803x-move-at8035-specific-DT-parse-to-dedi.patch @@ -0,0 +1,114 @@ +From 21a2802a8365cfa82cc02187c1f95136d85592ad Mon Sep 17 00:00:00 2001 +From: Christian Marangi +Date: Fri, 8 Dec 2023 15:51:59 +0100 +Subject: [PATCH 12/13] net: phy: at803x: move at8035 specific DT parse to + dedicated probe + +Move at8035 specific DT parse for clock out frequency to dedicated probe +to make at803x probe function more generic. + +This is to tidy code and no behaviour change are intended. + +Detection logic is changed, we check if the clk 25m mask is set and if +it's not zero, we assume the qca,clk-out-frequency property is set. + +The property is checked in the generic at803x_parse_dt called by +at803x_probe. + +Signed-off-by: Christian Marangi +Reviewed-by: Andrew Lunn +Signed-off-by: David S. Miller +--- + drivers/net/phy/at803x.c | 60 +++++++++++++++++++++++++++------------- + 1 file changed, 41 insertions(+), 19 deletions(-) + +--- a/drivers/net/phy/at803x.c ++++ b/drivers/net/phy/at803x.c +@@ -638,23 +638,6 @@ static int at803x_parse_dt(struct phy_de + + priv->clk_25m_reg |= FIELD_PREP(AT803X_CLK_OUT_MASK, sel); + priv->clk_25m_mask |= AT803X_CLK_OUT_MASK; +- +- /* Fixup for the AR8030/AR8035. This chip has another mask and +- * doesn't support the DSP reference. Eg. the lowest bit of the +- * mask. The upper two bits select the same frequencies. Mask +- * the lowest bit here. +- * +- * Warning: +- * There was no datasheet for the AR8030 available so this is +- * just a guess. But the AR8035 is listed as pin compatible +- * to the AR8030 so there might be a good chance it works on +- * the AR8030 too. +- */ +- if (phydev->drv->phy_id == ATH8030_PHY_ID || +- phydev->drv->phy_id == ATH8035_PHY_ID) { +- priv->clk_25m_reg &= AT8035_CLK_OUT_MASK; +- priv->clk_25m_mask &= AT8035_CLK_OUT_MASK; +- } + } + + ret = of_property_read_u32(node, "qca,clk-out-strength", &strength); +@@ -1635,6 +1618,45 @@ static int at8031_config_intr(struct phy + return at803x_config_intr(phydev); + } + ++static int at8035_parse_dt(struct phy_device *phydev) ++{ ++ struct at803x_priv *priv = phydev->priv; ++ ++ /* Mask is set by the generic at803x_parse_dt ++ * if property is set. Assume property is set ++ * with the mask not zero. ++ */ ++ if (priv->clk_25m_mask) { ++ /* Fixup for the AR8030/AR8035. This chip has another mask and ++ * doesn't support the DSP reference. Eg. the lowest bit of the ++ * mask. The upper two bits select the same frequencies. Mask ++ * the lowest bit here. ++ * ++ * Warning: ++ * There was no datasheet for the AR8030 available so this is ++ * just a guess. But the AR8035 is listed as pin compatible ++ * to the AR8030 so there might be a good chance it works on ++ * the AR8030 too. ++ */ ++ priv->clk_25m_reg &= AT8035_CLK_OUT_MASK; ++ priv->clk_25m_mask &= AT8035_CLK_OUT_MASK; ++ } ++ ++ return 0; ++} ++ ++/* AR8030 and AR8035 shared the same special mask for clk_25m */ ++static int at8035_probe(struct phy_device *phydev) ++{ ++ int ret; ++ ++ ret = at803x_probe(phydev); ++ if (ret) ++ return ret; ++ ++ return at8035_parse_dt(phydev); ++} ++ + static int qca83xx_config_init(struct phy_device *phydev) + { + u8 switch_revision; +@@ -2107,7 +2129,7 @@ static struct phy_driver at803x_driver[] + PHY_ID_MATCH_EXACT(ATH8035_PHY_ID), + .name = "Qualcomm Atheros AR8035", + .flags = PHY_POLL_CABLE_TEST, +- .probe = at803x_probe, ++ .probe = at8035_probe, + .config_aneg = at803x_config_aneg, + .config_init = at803x_config_init, + .soft_reset = genphy_soft_reset, +@@ -2128,7 +2150,7 @@ static struct phy_driver at803x_driver[] + .phy_id = ATH8030_PHY_ID, + .name = "Qualcomm Atheros AR8030", + .phy_id_mask = AT8030_PHY_ID_MASK, +- .probe = at803x_probe, ++ .probe = at8035_probe, + .config_init = at803x_config_init, + .link_change_notify = at803x_link_change_notify, + .set_wol = at803x_set_wol, diff --git a/target/linux/generic/backport-6.1/707-v6.8-13-net-phy-at803x-drop-specific-PHY-ID-check-from-cable.patch b/target/linux/generic/backport-6.1/707-v6.8-13-net-phy-at803x-drop-specific-PHY-ID-check-from-cable.patch new file mode 100644 index 000000000..e1ba6ec2d --- /dev/null +++ b/target/linux/generic/backport-6.1/707-v6.8-13-net-phy-at803x-drop-specific-PHY-ID-check-from-cable.patch @@ -0,0 +1,219 @@ +From ef9df47b449e32e06501a11272809be49019bdb6 Mon Sep 17 00:00:00 2001 +From: Christian Marangi +Date: Fri, 8 Dec 2023 15:52:00 +0100 +Subject: [PATCH 13/13] net: phy: at803x: drop specific PHY ID check from cable + test functions + +Drop specific PHY ID check for cable test functions for at803x. This is +done to make functions more generic. While at it better describe what +the functions does by using more symbolic function names. + +PHYs that requires to set additional reg are moved to specific function +calling the more generic one. + +cdt_start and cdt_wait_for_completion are changed to take an additional +arg to pass specific values specific to the PHY. + +Signed-off-by: Christian Marangi +Signed-off-by: David S. Miller +--- + drivers/net/phy/at803x.c | 95 +++++++++++++++++++++------------------- + 1 file changed, 50 insertions(+), 45 deletions(-) + +--- a/drivers/net/phy/at803x.c ++++ b/drivers/net/phy/at803x.c +@@ -1222,31 +1222,16 @@ static int at803x_cdt_fault_length(u16 s + return (dt * 824) / 10; + } + +-static int at803x_cdt_start(struct phy_device *phydev, int pair) ++static int at803x_cdt_start(struct phy_device *phydev, ++ u32 cdt_start) + { +- u16 cdt; +- +- /* qca8081 takes the different bit 15 to enable CDT test */ +- if (phydev->drv->phy_id == QCA8081_PHY_ID) +- cdt = QCA808X_CDT_ENABLE_TEST | +- QCA808X_CDT_LENGTH_UNIT | +- QCA808X_CDT_INTER_CHECK_DIS; +- else +- cdt = FIELD_PREP(AT803X_CDT_MDI_PAIR_MASK, pair) | +- AT803X_CDT_ENABLE_TEST; +- +- return phy_write(phydev, AT803X_CDT, cdt); ++ return phy_write(phydev, AT803X_CDT, cdt_start); + } + +-static int at803x_cdt_wait_for_completion(struct phy_device *phydev) ++static int at803x_cdt_wait_for_completion(struct phy_device *phydev, ++ u32 cdt_en) + { + int val, ret; +- u16 cdt_en; +- +- if (phydev->drv->phy_id == QCA8081_PHY_ID) +- cdt_en = QCA808X_CDT_ENABLE_TEST; +- else +- cdt_en = AT803X_CDT_ENABLE_TEST; + + /* One test run takes about 25ms */ + ret = phy_read_poll_timeout(phydev, AT803X_CDT, val, +@@ -1266,11 +1251,13 @@ static int at803x_cable_test_one_pair(st + }; + int ret, val; + +- ret = at803x_cdt_start(phydev, pair); ++ val = FIELD_PREP(AT803X_CDT_MDI_PAIR_MASK, pair) | ++ AT803X_CDT_ENABLE_TEST; ++ ret = at803x_cdt_start(phydev, val); + if (ret) + return ret; + +- ret = at803x_cdt_wait_for_completion(phydev); ++ ret = at803x_cdt_wait_for_completion(phydev, AT803X_CDT_ENABLE_TEST); + if (ret) + return ret; + +@@ -1292,19 +1279,11 @@ static int at803x_cable_test_one_pair(st + } + + static int at803x_cable_test_get_status(struct phy_device *phydev, +- bool *finished) ++ bool *finished, unsigned long pair_mask) + { +- unsigned long pair_mask; + int retries = 20; + int pair, ret; + +- if (phydev->phy_id == ATH9331_PHY_ID || +- phydev->phy_id == ATH8032_PHY_ID || +- phydev->phy_id == QCA9561_PHY_ID) +- pair_mask = 0x3; +- else +- pair_mask = 0xf; +- + *finished = false; + + /* According to the datasheet the CDT can be performed when +@@ -1331,7 +1310,7 @@ static int at803x_cable_test_get_status( + return 0; + } + +-static int at803x_cable_test_start(struct phy_device *phydev) ++static void at803x_cable_test_autoneg(struct phy_device *phydev) + { + /* Enable auto-negotiation, but advertise no capabilities, no link + * will be established. A restart of the auto-negotiation is not +@@ -1339,11 +1318,11 @@ static int at803x_cable_test_start(struc + */ + phy_write(phydev, MII_BMCR, BMCR_ANENABLE); + phy_write(phydev, MII_ADVERTISE, ADVERTISE_CSMA); +- if (phydev->phy_id != ATH9331_PHY_ID && +- phydev->phy_id != ATH8032_PHY_ID && +- phydev->phy_id != QCA9561_PHY_ID) +- phy_write(phydev, MII_CTRL1000, 0); ++} + ++static int at803x_cable_test_start(struct phy_device *phydev) ++{ ++ at803x_cable_test_autoneg(phydev); + /* we do all the (time consuming) work later */ + return 0; + } +@@ -1618,6 +1597,29 @@ static int at8031_config_intr(struct phy + return at803x_config_intr(phydev); + } + ++/* AR8031 and AR8035 share the same cable test get status reg */ ++static int at8031_cable_test_get_status(struct phy_device *phydev, ++ bool *finished) ++{ ++ return at803x_cable_test_get_status(phydev, finished, 0xf); ++} ++ ++/* AR8031 and AR8035 share the same cable test start logic */ ++static int at8031_cable_test_start(struct phy_device *phydev) ++{ ++ at803x_cable_test_autoneg(phydev); ++ phy_write(phydev, MII_CTRL1000, 0); ++ /* we do all the (time consuming) work later */ ++ return 0; ++} ++ ++/* AR8032, AR9331 and QCA9561 share the same cable test get status reg */ ++static int at8032_cable_test_get_status(struct phy_device *phydev, ++ bool *finished) ++{ ++ return at803x_cable_test_get_status(phydev, finished, 0x3); ++} ++ + static int at8035_parse_dt(struct phy_device *phydev) + { + struct at803x_priv *priv = phydev->priv; +@@ -2041,11 +2043,14 @@ static int qca808x_cable_test_get_status + + *finished = false; + +- ret = at803x_cdt_start(phydev, 0); ++ val = QCA808X_CDT_ENABLE_TEST | ++ QCA808X_CDT_LENGTH_UNIT | ++ QCA808X_CDT_INTER_CHECK_DIS; ++ ret = at803x_cdt_start(phydev, val); + if (ret) + return ret; + +- ret = at803x_cdt_wait_for_completion(phydev); ++ ret = at803x_cdt_wait_for_completion(phydev, QCA808X_CDT_ENABLE_TEST); + if (ret) + return ret; + +@@ -2143,8 +2148,8 @@ static struct phy_driver at803x_driver[] + .handle_interrupt = at803x_handle_interrupt, + .get_tunable = at803x_get_tunable, + .set_tunable = at803x_set_tunable, +- .cable_test_start = at803x_cable_test_start, +- .cable_test_get_status = at803x_cable_test_get_status, ++ .cable_test_start = at8031_cable_test_start, ++ .cable_test_get_status = at8031_cable_test_get_status, + }, { + /* Qualcomm Atheros AR8030 */ + .phy_id = ATH8030_PHY_ID, +@@ -2181,8 +2186,8 @@ static struct phy_driver at803x_driver[] + .handle_interrupt = at803x_handle_interrupt, + .get_tunable = at803x_get_tunable, + .set_tunable = at803x_set_tunable, +- .cable_test_start = at803x_cable_test_start, +- .cable_test_get_status = at803x_cable_test_get_status, ++ .cable_test_start = at8031_cable_test_start, ++ .cable_test_get_status = at8031_cable_test_get_status, + }, { + /* Qualcomm Atheros AR8032 */ + PHY_ID_MATCH_EXACT(ATH8032_PHY_ID), +@@ -2197,7 +2202,7 @@ static struct phy_driver at803x_driver[] + .config_intr = at803x_config_intr, + .handle_interrupt = at803x_handle_interrupt, + .cable_test_start = at803x_cable_test_start, +- .cable_test_get_status = at803x_cable_test_get_status, ++ .cable_test_get_status = at8032_cable_test_get_status, + }, { + /* ATHEROS AR9331 */ + PHY_ID_MATCH_EXACT(ATH9331_PHY_ID), +@@ -2210,7 +2215,7 @@ static struct phy_driver at803x_driver[] + .config_intr = at803x_config_intr, + .handle_interrupt = at803x_handle_interrupt, + .cable_test_start = at803x_cable_test_start, +- .cable_test_get_status = at803x_cable_test_get_status, ++ .cable_test_get_status = at8032_cable_test_get_status, + .read_status = at803x_read_status, + .soft_reset = genphy_soft_reset, + .config_aneg = at803x_config_aneg, +@@ -2226,7 +2231,7 @@ static struct phy_driver at803x_driver[] + .config_intr = at803x_config_intr, + .handle_interrupt = at803x_handle_interrupt, + .cable_test_start = at803x_cable_test_start, +- .cable_test_get_status = at803x_cable_test_get_status, ++ .cable_test_get_status = at8032_cable_test_get_status, + .read_status = at803x_read_status, + .soft_reset = genphy_soft_reset, + .config_aneg = at803x_config_aneg, diff --git a/target/linux/generic/backport-6.1/708-v6.8-01-net-phy-at803x-move-specific-qca808x-config_aneg-to-.patch b/target/linux/generic/backport-6.1/708-v6.8-01-net-phy-at803x-move-specific-qca808x-config_aneg-to-.patch new file mode 100644 index 000000000..0bb3673c0 --- /dev/null +++ b/target/linux/generic/backport-6.1/708-v6.8-01-net-phy-at803x-move-specific-qca808x-config_aneg-to-.patch @@ -0,0 +1,116 @@ +From 8e732f1c6f2dc5e18f766d0f1b11df9db2dd044a Mon Sep 17 00:00:00 2001 +From: Christian Marangi +Date: Thu, 14 Dec 2023 01:44:31 +0100 +Subject: [PATCH 1/2] net: phy: at803x: move specific qca808x config_aneg to + dedicated function + +Move specific qca808x config_aneg to dedicated function to permit easier +split of qca808x portion from at803x driver. + +Signed-off-by: Christian Marangi +Signed-off-by: David S. Miller +--- + drivers/net/phy/at803x.c | 66 ++++++++++++++++++++++++---------------- + 1 file changed, 40 insertions(+), 26 deletions(-) + +--- a/drivers/net/phy/at803x.c ++++ b/drivers/net/phy/at803x.c +@@ -1045,9 +1045,8 @@ static int at803x_config_mdix(struct phy + FIELD_PREP(AT803X_SFC_MDI_CROSSOVER_MODE_M, val)); + } + +-static int at803x_config_aneg(struct phy_device *phydev) ++static int at803x_prepare_config_aneg(struct phy_device *phydev) + { +- struct at803x_priv *priv = phydev->priv; + int ret; + + ret = at803x_config_mdix(phydev, phydev->mdix_ctrl); +@@ -1064,33 +1063,22 @@ static int at803x_config_aneg(struct phy + return ret; + } + +- if (priv->is_1000basex) +- return genphy_c37_config_aneg(phydev); +- +- /* Do not restart auto-negotiation by setting ret to 0 defautly, +- * when calling __genphy_config_aneg later. +- */ +- ret = 0; +- +- if (phydev->drv->phy_id == QCA8081_PHY_ID) { +- int phy_ctrl = 0; ++ return 0; ++} + +- /* The reg MII_BMCR also needs to be configured for force mode, the +- * genphy_config_aneg is also needed. +- */ +- if (phydev->autoneg == AUTONEG_DISABLE) +- genphy_c45_pma_setup_forced(phydev); ++static int at803x_config_aneg(struct phy_device *phydev) ++{ ++ struct at803x_priv *priv = phydev->priv; ++ int ret; + +- if (linkmode_test_bit(ETHTOOL_LINK_MODE_2500baseT_Full_BIT, phydev->advertising)) +- phy_ctrl = MDIO_AN_10GBT_CTRL_ADV2_5G; ++ ret = at803x_prepare_config_aneg(phydev); ++ if (ret) ++ return ret; + +- ret = phy_modify_mmd_changed(phydev, MDIO_MMD_AN, MDIO_AN_10GBT_CTRL, +- MDIO_AN_10GBT_CTRL_ADV2_5G, phy_ctrl); +- if (ret < 0) +- return ret; +- } ++ if (priv->is_1000basex) ++ return genphy_c37_config_aneg(phydev); + +- return __genphy_config_aneg(phydev, ret); ++ return genphy_config_aneg(phydev); + } + + static int at803x_get_downshift(struct phy_device *phydev, u8 *d) +@@ -2118,6 +2106,32 @@ static int qca808x_get_features(struct p + return 0; + } + ++static int qca808x_config_aneg(struct phy_device *phydev) ++{ ++ int phy_ctrl = 0; ++ int ret; ++ ++ ret = at803x_prepare_config_aneg(phydev); ++ if (ret) ++ return ret; ++ ++ /* The reg MII_BMCR also needs to be configured for force mode, the ++ * genphy_config_aneg is also needed. ++ */ ++ if (phydev->autoneg == AUTONEG_DISABLE) ++ genphy_c45_pma_setup_forced(phydev); ++ ++ if (linkmode_test_bit(ETHTOOL_LINK_MODE_2500baseT_Full_BIT, phydev->advertising)) ++ phy_ctrl = MDIO_AN_10GBT_CTRL_ADV2_5G; ++ ++ ret = phy_modify_mmd_changed(phydev, MDIO_MMD_AN, MDIO_AN_10GBT_CTRL, ++ MDIO_AN_10GBT_CTRL_ADV2_5G, phy_ctrl); ++ if (ret < 0) ++ return ret; ++ ++ return __genphy_config_aneg(phydev, ret); ++} ++ + static void qca808x_link_change_notify(struct phy_device *phydev) + { + /* Assert interface sgmii fifo on link down, deassert it on link up, +@@ -2295,7 +2309,7 @@ static struct phy_driver at803x_driver[] + .set_wol = at803x_set_wol, + .get_wol = at803x_get_wol, + .get_features = qca808x_get_features, +- .config_aneg = at803x_config_aneg, ++ .config_aneg = qca808x_config_aneg, + .suspend = genphy_suspend, + .resume = genphy_resume, + .read_status = qca808x_read_status, diff --git a/target/linux/generic/backport-6.1/708-v6.8-02-net-phy-at803x-make-read-specific-status-function-mo.patch b/target/linux/generic/backport-6.1/708-v6.8-02-net-phy-at803x-make-read-specific-status-function-mo.patch new file mode 100644 index 000000000..54ca3bdfc --- /dev/null +++ b/target/linux/generic/backport-6.1/708-v6.8-02-net-phy-at803x-make-read-specific-status-function-mo.patch @@ -0,0 +1,97 @@ +From 38eb804e8458ba181a03a0498ce4bf84eebd1931 Mon Sep 17 00:00:00 2001 +From: Christian Marangi +Date: Thu, 14 Dec 2023 01:44:32 +0100 +Subject: [PATCH 2/2] net: phy: at803x: make read specific status function more + generic + +Rework read specific status function to be more generic. The function +apply different speed mask based on the PHY ID. Make it more generic by +adding an additional arg to pass the specific speed (ss) mask and use +the provided mask to parse the speed value. + +This is needed to permit an easier deatch of qca808x code from the +at803x driver. + +Signed-off-by: Christian Marangi +Signed-off-by: David S. Miller +--- + drivers/net/phy/at803x.c | 26 ++++++++++++++++++-------- + 1 file changed, 18 insertions(+), 8 deletions(-) + +--- a/drivers/net/phy/at803x.c ++++ b/drivers/net/phy/at803x.c +@@ -301,6 +301,11 @@ static struct at803x_hw_stat qca83xx_hw_ + { "eee_wake_errors", 0x16, GENMASK(15, 0), MMD}, + }; + ++struct at803x_ss_mask { ++ u16 speed_mask; ++ u8 speed_shift; ++}; ++ + struct at803x_priv { + int flags; + u16 clk_25m_reg; +@@ -921,7 +926,8 @@ static void at803x_link_change_notify(st + } + } + +-static int at803x_read_specific_status(struct phy_device *phydev) ++static int at803x_read_specific_status(struct phy_device *phydev, ++ struct at803x_ss_mask ss_mask) + { + int ss; + +@@ -940,11 +946,8 @@ static int at803x_read_specific_status(s + if (sfc < 0) + return sfc; + +- /* qca8081 takes the different bits for speed value from at803x */ +- if (phydev->drv->phy_id == QCA8081_PHY_ID) +- speed = FIELD_GET(QCA808X_SS_SPEED_MASK, ss); +- else +- speed = FIELD_GET(AT803X_SS_SPEED_MASK, ss); ++ speed = ss & ss_mask.speed_mask; ++ speed >>= ss_mask.speed_shift; + + switch (speed) { + case AT803X_SS_SPEED_10: +@@ -989,6 +992,7 @@ static int at803x_read_specific_status(s + static int at803x_read_status(struct phy_device *phydev) + { + struct at803x_priv *priv = phydev->priv; ++ struct at803x_ss_mask ss_mask = { 0 }; + int err, old_link = phydev->link; + + if (priv->is_1000basex) +@@ -1012,7 +1016,9 @@ static int at803x_read_status(struct phy + if (err < 0) + return err; + +- err = at803x_read_specific_status(phydev); ++ ss_mask.speed_mask = AT803X_SS_SPEED_MASK; ++ ss_mask.speed_shift = __bf_shf(AT803X_SS_SPEED_MASK); ++ err = at803x_read_specific_status(phydev, ss_mask); + if (err < 0) + return err; + +@@ -1869,6 +1875,7 @@ static int qca808x_config_init(struct ph + + static int qca808x_read_status(struct phy_device *phydev) + { ++ struct at803x_ss_mask ss_mask = { 0 }; + int ret; + + ret = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_10GBT_STAT); +@@ -1882,7 +1889,10 @@ static int qca808x_read_status(struct ph + if (ret) + return ret; + +- ret = at803x_read_specific_status(phydev); ++ /* qca8081 takes the different bits for speed value from at803x */ ++ ss_mask.speed_mask = QCA808X_SS_SPEED_MASK; ++ ss_mask.speed_shift = __bf_shf(QCA808X_SS_SPEED_MASK); ++ ret = at803x_read_specific_status(phydev, ss_mask); + if (ret < 0) + return ret; + diff --git a/target/linux/generic/backport-6.1/709-v6.8-01-net-phy-at803x-remove-extra-space-after-cast.patch b/target/linux/generic/backport-6.1/709-v6.8-01-net-phy-at803x-remove-extra-space-after-cast.patch new file mode 100644 index 000000000..8097a33e4 --- /dev/null +++ b/target/linux/generic/backport-6.1/709-v6.8-01-net-phy-at803x-remove-extra-space-after-cast.patch @@ -0,0 +1,27 @@ +From fc9d7264ddc32eaa647d6bfcdc25cdf9f786fde0 Mon Sep 17 00:00:00 2001 +From: Christian Marangi +Date: Mon, 18 Dec 2023 00:27:39 +0100 +Subject: [PATCH 1/2] net: phy: at803x: remove extra space after cast + +Remove extra space after cast as reported by checkpatch to keep code +clean. + +Signed-off-by: Christian Marangi +Reviewed-by: Andrew Lunn +Link: https://lore.kernel.org/r/20231217232739.27065-1-ansuelsmth@gmail.com +Signed-off-by: Paolo Abeni +--- + drivers/net/phy/at803x.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/drivers/net/phy/at803x.c ++++ b/drivers/net/phy/at803x.c +@@ -462,7 +462,7 @@ static int at803x_set_wol(struct phy_dev + if (!ndev) + return -ENODEV; + +- mac = (const u8 *) ndev->dev_addr; ++ mac = (const u8 *)ndev->dev_addr; + + if (!is_valid_ether_addr(mac)) + return -EINVAL; diff --git a/target/linux/generic/backport-6.1/709-v6.8-02-net-phy-at803x-replace-msleep-1-with-usleep_range.patch b/target/linux/generic/backport-6.1/709-v6.8-02-net-phy-at803x-replace-msleep-1-with-usleep_range.patch new file mode 100644 index 000000000..b8d459108 --- /dev/null +++ b/target/linux/generic/backport-6.1/709-v6.8-02-net-phy-at803x-replace-msleep-1-with-usleep_range.patch @@ -0,0 +1,38 @@ +From 3ab5720881a924fb6405d9e6a3b09f1026467c47 Mon Sep 17 00:00:00 2001 +From: Christian Marangi +Date: Mon, 18 Dec 2023 00:25:08 +0100 +Subject: [PATCH 2/2] net: phy: at803x: replace msleep(1) with usleep_range + +Replace msleep(1) with usleep_range as suggested by timers-howto guide. + +Signed-off-by: Christian Marangi +Reviewed-by: Andrew Lunn +Link: https://lore.kernel.org/r/20231217232508.26470-1-ansuelsmth@gmail.com +Signed-off-by: Paolo Abeni +--- + drivers/net/phy/at803x.c | 6 +++--- + 1 file changed, 3 insertions(+), 3 deletions(-) + +--- a/drivers/net/phy/at803x.c ++++ b/drivers/net/phy/at803x.c +@@ -916,9 +916,9 @@ static void at803x_link_change_notify(st + at803x_context_save(phydev, &context); + + phy_device_reset(phydev, 1); +- msleep(1); ++ usleep_range(1000, 2000); + phy_device_reset(phydev, 0); +- msleep(1); ++ usleep_range(1000, 2000); + + at803x_context_restore(phydev, &context); + +@@ -1733,7 +1733,7 @@ static int qca83xx_resume(struct phy_dev + if (ret) + return ret; + +- msleep(1); ++ usleep_range(1000, 2000); + + return 0; + } diff --git a/target/linux/generic/backport-6.1/710-v6.8-net-phy-at803x-better-align-function-varibles-to-ope.patch b/target/linux/generic/backport-6.1/710-v6.8-net-phy-at803x-better-align-function-varibles-to-ope.patch new file mode 100644 index 000000000..776b7bb43 --- /dev/null +++ b/target/linux/generic/backport-6.1/710-v6.8-net-phy-at803x-better-align-function-varibles-to-ope.patch @@ -0,0 +1,152 @@ +From 7961ef1fa10ec35ad6923fb5751877116e4b035b Mon Sep 17 00:00:00 2001 +From: Christian Marangi +Date: Tue, 19 Dec 2023 21:21:24 +0100 +Subject: [PATCH] net: phy: at803x: better align function varibles to open + parenthesis + +Better align function variables to open parenthesis as suggested by +checkpatch script for qca808x function to make code cleaner. + +For cable_test_get_status function some additional rework was needed to +handle too long functions. + +Signed-off-by: Christian Marangi +Reviewed-by: Andrew Lunn +Signed-off-by: David S. Miller +--- + drivers/net/phy/at803x.c | 67 ++++++++++++++++++++++------------------ + 1 file changed, 37 insertions(+), 30 deletions(-) + +--- a/drivers/net/phy/at803x.c ++++ b/drivers/net/phy/at803x.c +@@ -1781,27 +1781,27 @@ static int qca808x_phy_fast_retrain_conf + return ret; + + phy_write_mmd(phydev, MDIO_MMD_AN, QCA808X_PHY_MMD7_TOP_OPTION1, +- QCA808X_TOP_OPTION1_DATA); ++ QCA808X_TOP_OPTION1_DATA); + phy_write_mmd(phydev, MDIO_MMD_PMAPMD, QCA808X_PHY_MMD1_MSE_THRESHOLD_20DB, +- QCA808X_MSE_THRESHOLD_20DB_VALUE); ++ QCA808X_MSE_THRESHOLD_20DB_VALUE); + phy_write_mmd(phydev, MDIO_MMD_PMAPMD, QCA808X_PHY_MMD1_MSE_THRESHOLD_17DB, +- QCA808X_MSE_THRESHOLD_17DB_VALUE); ++ QCA808X_MSE_THRESHOLD_17DB_VALUE); + phy_write_mmd(phydev, MDIO_MMD_PMAPMD, QCA808X_PHY_MMD1_MSE_THRESHOLD_27DB, +- QCA808X_MSE_THRESHOLD_27DB_VALUE); ++ QCA808X_MSE_THRESHOLD_27DB_VALUE); + phy_write_mmd(phydev, MDIO_MMD_PMAPMD, QCA808X_PHY_MMD1_MSE_THRESHOLD_28DB, +- QCA808X_MSE_THRESHOLD_28DB_VALUE); ++ QCA808X_MSE_THRESHOLD_28DB_VALUE); + phy_write_mmd(phydev, MDIO_MMD_PCS, QCA808X_PHY_MMD3_DEBUG_1, +- QCA808X_MMD3_DEBUG_1_VALUE); ++ QCA808X_MMD3_DEBUG_1_VALUE); + phy_write_mmd(phydev, MDIO_MMD_PCS, QCA808X_PHY_MMD3_DEBUG_4, +- QCA808X_MMD3_DEBUG_4_VALUE); ++ QCA808X_MMD3_DEBUG_4_VALUE); + phy_write_mmd(phydev, MDIO_MMD_PCS, QCA808X_PHY_MMD3_DEBUG_5, +- QCA808X_MMD3_DEBUG_5_VALUE); ++ QCA808X_MMD3_DEBUG_5_VALUE); + phy_write_mmd(phydev, MDIO_MMD_PCS, QCA808X_PHY_MMD3_DEBUG_3, +- QCA808X_MMD3_DEBUG_3_VALUE); ++ QCA808X_MMD3_DEBUG_3_VALUE); + phy_write_mmd(phydev, MDIO_MMD_PCS, QCA808X_PHY_MMD3_DEBUG_6, +- QCA808X_MMD3_DEBUG_6_VALUE); ++ QCA808X_MMD3_DEBUG_6_VALUE); + phy_write_mmd(phydev, MDIO_MMD_PCS, QCA808X_PHY_MMD3_DEBUG_2, +- QCA808X_MMD3_DEBUG_2_VALUE); ++ QCA808X_MMD3_DEBUG_2_VALUE); + + return 0; + } +@@ -1838,13 +1838,14 @@ static int qca808x_config_init(struct ph + + /* Active adc&vga on 802.3az for the link 1000M and 100M */ + ret = phy_modify_mmd(phydev, MDIO_MMD_PCS, QCA808X_PHY_MMD3_ADDR_CLD_CTRL7, +- QCA808X_8023AZ_AFE_CTRL_MASK, QCA808X_8023AZ_AFE_EN); ++ QCA808X_8023AZ_AFE_CTRL_MASK, QCA808X_8023AZ_AFE_EN); + if (ret) + return ret; + + /* Adjust the threshold on 802.3az for the link 1000M */ + ret = phy_write_mmd(phydev, MDIO_MMD_PCS, +- QCA808X_PHY_MMD3_AZ_TRAINING_CTRL, QCA808X_MMD3_AZ_TRAINING_VAL); ++ QCA808X_PHY_MMD3_AZ_TRAINING_CTRL, ++ QCA808X_MMD3_AZ_TRAINING_VAL); + if (ret) + return ret; + +@@ -1870,7 +1871,8 @@ static int qca808x_config_init(struct ph + + /* Configure adc threshold as 100mv for the link 10M */ + return at803x_debug_reg_mask(phydev, QCA808X_PHY_DEBUG_ADC_THRESHOLD, +- QCA808X_ADC_THRESHOLD_MASK, QCA808X_ADC_THRESHOLD_100MV); ++ QCA808X_ADC_THRESHOLD_MASK, ++ QCA808X_ADC_THRESHOLD_100MV); + } + + static int qca808x_read_status(struct phy_device *phydev) +@@ -1883,7 +1885,7 @@ static int qca808x_read_status(struct ph + return ret; + + linkmode_mod_bit(ETHTOOL_LINK_MODE_2500baseT_Full_BIT, phydev->lp_advertising, +- ret & MDIO_AN_10GBT_STAT_LP2_5G); ++ ret & MDIO_AN_10GBT_STAT_LP2_5G); + + ret = genphy_read_status(phydev); + if (ret) +@@ -1913,7 +1915,7 @@ static int qca808x_read_status(struct ph + */ + if (qca808x_has_fast_retrain_or_slave_seed(phydev)) { + if (phydev->master_slave_state == MASTER_SLAVE_STATE_ERR || +- qca808x_is_prefer_master(phydev)) { ++ qca808x_is_prefer_master(phydev)) { + qca808x_phy_ms_seed_enable(phydev, false); + } else { + qca808x_phy_ms_seed_enable(phydev, true); +@@ -2070,18 +2072,22 @@ static int qca808x_cable_test_get_status + ethnl_cable_test_result(phydev, ETHTOOL_A_CABLE_PAIR_D, + qca808x_cable_test_result_trans(pair_d)); + +- if (qca808x_cdt_fault_length_valid(pair_a)) +- ethnl_cable_test_fault_length(phydev, ETHTOOL_A_CABLE_PAIR_A, +- qca808x_cdt_fault_length(phydev, ETHTOOL_A_CABLE_PAIR_A)); +- if (qca808x_cdt_fault_length_valid(pair_b)) +- ethnl_cable_test_fault_length(phydev, ETHTOOL_A_CABLE_PAIR_B, +- qca808x_cdt_fault_length(phydev, ETHTOOL_A_CABLE_PAIR_B)); +- if (qca808x_cdt_fault_length_valid(pair_c)) +- ethnl_cable_test_fault_length(phydev, ETHTOOL_A_CABLE_PAIR_C, +- qca808x_cdt_fault_length(phydev, ETHTOOL_A_CABLE_PAIR_C)); +- if (qca808x_cdt_fault_length_valid(pair_d)) +- ethnl_cable_test_fault_length(phydev, ETHTOOL_A_CABLE_PAIR_D, +- qca808x_cdt_fault_length(phydev, ETHTOOL_A_CABLE_PAIR_D)); ++ if (qca808x_cdt_fault_length_valid(pair_a)) { ++ val = qca808x_cdt_fault_length(phydev, ETHTOOL_A_CABLE_PAIR_A); ++ ethnl_cable_test_fault_length(phydev, ETHTOOL_A_CABLE_PAIR_A, val); ++ } ++ if (qca808x_cdt_fault_length_valid(pair_b)) { ++ val = qca808x_cdt_fault_length(phydev, ETHTOOL_A_CABLE_PAIR_B); ++ ethnl_cable_test_fault_length(phydev, ETHTOOL_A_CABLE_PAIR_B, val); ++ } ++ if (qca808x_cdt_fault_length_valid(pair_c)) { ++ val = qca808x_cdt_fault_length(phydev, ETHTOOL_A_CABLE_PAIR_C); ++ ethnl_cable_test_fault_length(phydev, ETHTOOL_A_CABLE_PAIR_C, val); ++ } ++ if (qca808x_cdt_fault_length_valid(pair_d)) { ++ val = qca808x_cdt_fault_length(phydev, ETHTOOL_A_CABLE_PAIR_D); ++ ethnl_cable_test_fault_length(phydev, ETHTOOL_A_CABLE_PAIR_D, val); ++ } + + *finished = true; + +@@ -2148,8 +2154,9 @@ static void qca808x_link_change_notify(s + * the interface device address is always phy address added by 1. + */ + mdiobus_c45_modify_changed(phydev->mdio.bus, phydev->mdio.addr + 1, +- MDIO_MMD_PMAPMD, QCA8081_PHY_SERDES_MMD1_FIFO_CTRL, +- QCA8081_PHY_FIFO_RSTN, phydev->link ? QCA8081_PHY_FIFO_RSTN : 0); ++ MDIO_MMD_PMAPMD, QCA8081_PHY_SERDES_MMD1_FIFO_CTRL, ++ QCA8081_PHY_FIFO_RSTN, ++ phydev->link ? QCA8081_PHY_FIFO_RSTN : 0); + } + + static struct phy_driver at803x_driver[] = { diff --git a/target/linux/generic/backport-6.1/711-v6.8-01-net-phy-at803x-generalize-cdt-fault-length-function.patch b/target/linux/generic/backport-6.1/711-v6.8-01-net-phy-at803x-generalize-cdt-fault-length-function.patch new file mode 100644 index 000000000..d7196da46 --- /dev/null +++ b/target/linux/generic/backport-6.1/711-v6.8-01-net-phy-at803x-generalize-cdt-fault-length-function.patch @@ -0,0 +1,62 @@ +From 22eb276098da820d9440fad22901f9b74ed4d659 Mon Sep 17 00:00:00 2001 +From: Christian Marangi +Date: Thu, 4 Jan 2024 22:30:38 +0100 +Subject: [PATCH 1/4] net: phy: at803x: generalize cdt fault length function + +Generalize cable test fault length function since they all base on the +same magic values (already reverse engineered to understand the meaning +of it) to have consistenct values on every PHY. + +Signed-off-by: Christian Marangi +Reviewed-by: Simon Horman +Signed-off-by: David S. Miller +--- + drivers/net/phy/at803x.c | 13 ++++++------- + 1 file changed, 6 insertions(+), 7 deletions(-) + +--- a/drivers/net/phy/at803x.c ++++ b/drivers/net/phy/at803x.c +@@ -1192,10 +1192,8 @@ static bool at803x_cdt_fault_length_vali + return false; + } + +-static int at803x_cdt_fault_length(u16 status) ++static int at803x_cdt_fault_length(int dt) + { +- int dt; +- + /* According to the datasheet the distance to the fault is + * DELTA_TIME * 0.824 meters. + * +@@ -1211,8 +1209,6 @@ static int at803x_cdt_fault_length(u16 s + * With a VF of 0.69 we get the factor 0.824 mentioned in the + * datasheet. + */ +- dt = FIELD_GET(AT803X_CDT_STATUS_DELTA_TIME_MASK, status); +- + return (dt * 824) / 10; + } + +@@ -1265,9 +1261,11 @@ static int at803x_cable_test_one_pair(st + ethnl_cable_test_result(phydev, ethtool_pair[pair], + at803x_cable_test_result_trans(val)); + +- if (at803x_cdt_fault_length_valid(val)) ++ if (at803x_cdt_fault_length_valid(val)) { ++ val = FIELD_GET(AT803X_CDT_STATUS_DELTA_TIME_MASK, val); + ethnl_cable_test_fault_length(phydev, ethtool_pair[pair], + at803x_cdt_fault_length(val)); ++ } + + return 1; + } +@@ -1992,7 +1990,8 @@ static int qca808x_cdt_fault_length(stru + if (val < 0) + return val; + +- return (FIELD_GET(QCA808X_CDT_DIAG_LENGTH, val) * 824) / 10; ++ val = FIELD_GET(QCA808X_CDT_DIAG_LENGTH, val); ++ return at803x_cdt_fault_length(val); + } + + static int qca808x_cable_test_start(struct phy_device *phydev) diff --git a/target/linux/generic/backport-6.1/711-v6.8-02-net-phy-at803x-refactor-qca808x-cable-test-get-statu.patch b/target/linux/generic/backport-6.1/711-v6.8-02-net-phy-at803x-refactor-qca808x-cable-test-get-statu.patch new file mode 100644 index 000000000..5d38fe7e9 --- /dev/null +++ b/target/linux/generic/backport-6.1/711-v6.8-02-net-phy-at803x-refactor-qca808x-cable-test-get-statu.patch @@ -0,0 +1,118 @@ +From e0e9ada1df6133513249861c1d91c1dbefd9383b Mon Sep 17 00:00:00 2001 +From: Christian Marangi +Date: Thu, 4 Jan 2024 22:30:39 +0100 +Subject: [PATCH 2/4] net: phy: at803x: refactor qca808x cable test get status + function + +Refactor qca808x cable test get status function to remove code +duplication and clean things up. + +The same logic is applied to each pair hence it can be generalized and +moved to a common function. + +Signed-off-by: Christian Marangi +Reviewed-by: Simon Horman +Signed-off-by: David S. Miller +--- + drivers/net/phy/at803x.c | 80 ++++++++++++++++++++++++---------------- + 1 file changed, 49 insertions(+), 31 deletions(-) + +--- a/drivers/net/phy/at803x.c ++++ b/drivers/net/phy/at803x.c +@@ -2035,10 +2035,43 @@ static int qca808x_cable_test_start(stru + return 0; + } + ++static int qca808x_cable_test_get_pair_status(struct phy_device *phydev, u8 pair, ++ u16 status) ++{ ++ u16 pair_code; ++ int length; ++ ++ switch (pair) { ++ case ETHTOOL_A_CABLE_PAIR_A: ++ pair_code = FIELD_GET(QCA808X_CDT_CODE_PAIR_A, status); ++ break; ++ case ETHTOOL_A_CABLE_PAIR_B: ++ pair_code = FIELD_GET(QCA808X_CDT_CODE_PAIR_B, status); ++ break; ++ case ETHTOOL_A_CABLE_PAIR_C: ++ pair_code = FIELD_GET(QCA808X_CDT_CODE_PAIR_C, status); ++ break; ++ case ETHTOOL_A_CABLE_PAIR_D: ++ pair_code = FIELD_GET(QCA808X_CDT_CODE_PAIR_D, status); ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ ethnl_cable_test_result(phydev, pair, ++ qca808x_cable_test_result_trans(pair_code)); ++ ++ if (qca808x_cdt_fault_length_valid(pair_code)) { ++ length = qca808x_cdt_fault_length(phydev, pair); ++ ethnl_cable_test_fault_length(phydev, pair, length); ++ } ++ ++ return 0; ++} ++ + static int qca808x_cable_test_get_status(struct phy_device *phydev, bool *finished) + { + int ret, val; +- int pair_a, pair_b, pair_c, pair_d; + + *finished = false; + +@@ -2057,36 +2090,21 @@ static int qca808x_cable_test_get_status + if (val < 0) + return val; + +- pair_a = FIELD_GET(QCA808X_CDT_CODE_PAIR_A, val); +- pair_b = FIELD_GET(QCA808X_CDT_CODE_PAIR_B, val); +- pair_c = FIELD_GET(QCA808X_CDT_CODE_PAIR_C, val); +- pair_d = FIELD_GET(QCA808X_CDT_CODE_PAIR_D, val); +- +- ethnl_cable_test_result(phydev, ETHTOOL_A_CABLE_PAIR_A, +- qca808x_cable_test_result_trans(pair_a)); +- ethnl_cable_test_result(phydev, ETHTOOL_A_CABLE_PAIR_B, +- qca808x_cable_test_result_trans(pair_b)); +- ethnl_cable_test_result(phydev, ETHTOOL_A_CABLE_PAIR_C, +- qca808x_cable_test_result_trans(pair_c)); +- ethnl_cable_test_result(phydev, ETHTOOL_A_CABLE_PAIR_D, +- qca808x_cable_test_result_trans(pair_d)); +- +- if (qca808x_cdt_fault_length_valid(pair_a)) { +- val = qca808x_cdt_fault_length(phydev, ETHTOOL_A_CABLE_PAIR_A); +- ethnl_cable_test_fault_length(phydev, ETHTOOL_A_CABLE_PAIR_A, val); +- } +- if (qca808x_cdt_fault_length_valid(pair_b)) { +- val = qca808x_cdt_fault_length(phydev, ETHTOOL_A_CABLE_PAIR_B); +- ethnl_cable_test_fault_length(phydev, ETHTOOL_A_CABLE_PAIR_B, val); +- } +- if (qca808x_cdt_fault_length_valid(pair_c)) { +- val = qca808x_cdt_fault_length(phydev, ETHTOOL_A_CABLE_PAIR_C); +- ethnl_cable_test_fault_length(phydev, ETHTOOL_A_CABLE_PAIR_C, val); +- } +- if (qca808x_cdt_fault_length_valid(pair_d)) { +- val = qca808x_cdt_fault_length(phydev, ETHTOOL_A_CABLE_PAIR_D); +- ethnl_cable_test_fault_length(phydev, ETHTOOL_A_CABLE_PAIR_D, val); +- } ++ ret = qca808x_cable_test_get_pair_status(phydev, ETHTOOL_A_CABLE_PAIR_A, val); ++ if (ret) ++ return ret; ++ ++ ret = qca808x_cable_test_get_pair_status(phydev, ETHTOOL_A_CABLE_PAIR_B, val); ++ if (ret) ++ return ret; ++ ++ ret = qca808x_cable_test_get_pair_status(phydev, ETHTOOL_A_CABLE_PAIR_C, val); ++ if (ret) ++ return ret; ++ ++ ret = qca808x_cable_test_get_pair_status(phydev, ETHTOOL_A_CABLE_PAIR_D, val); ++ if (ret) ++ return ret; + + *finished = true; + diff --git a/target/linux/generic/backport-6.1/711-v6.8-03-net-phy-at803x-add-support-for-cdt-cross-short-test-.patch b/target/linux/generic/backport-6.1/711-v6.8-03-net-phy-at803x-add-support-for-cdt-cross-short-test-.patch new file mode 100644 index 000000000..d5793860a --- /dev/null +++ b/target/linux/generic/backport-6.1/711-v6.8-03-net-phy-at803x-add-support-for-cdt-cross-short-test-.patch @@ -0,0 +1,182 @@ +From ea73e5ea442ee2aade67b1fb1233ccb3cbea2ceb Mon Sep 17 00:00:00 2001 +From: Christian Marangi +Date: Thu, 4 Jan 2024 22:30:40 +0100 +Subject: [PATCH 3/4] net: phy: at803x: add support for cdt cross short test + for qca808x + +QCA808x PHY Family supports Cable Diagnostic Test also for Cross Pair +Short. + +Add all the define to make enable and support these additional tests. + +Cross Short test was previously disabled by default, this is now changed +and enabled by default. In this mode, the mask changed a bit and length +is shifted based on the fault condition. + +Signed-off-by: Christian Marangi +Reviewed-by: Simon Horman +Signed-off-by: David S. Miller +--- + drivers/net/phy/at803x.c | 86 ++++++++++++++++++++++++++++++++-------- + 1 file changed, 69 insertions(+), 17 deletions(-) + +--- a/drivers/net/phy/at803x.c ++++ b/drivers/net/phy/at803x.c +@@ -254,6 +254,7 @@ + + #define QCA808X_CDT_ENABLE_TEST BIT(15) + #define QCA808X_CDT_INTER_CHECK_DIS BIT(13) ++#define QCA808X_CDT_STATUS BIT(11) + #define QCA808X_CDT_LENGTH_UNIT BIT(10) + + #define QCA808X_MMD3_CDT_STATUS 0x8064 +@@ -261,16 +262,44 @@ + #define QCA808X_MMD3_CDT_DIAG_PAIR_B 0x8066 + #define QCA808X_MMD3_CDT_DIAG_PAIR_C 0x8067 + #define QCA808X_MMD3_CDT_DIAG_PAIR_D 0x8068 +-#define QCA808X_CDT_DIAG_LENGTH GENMASK(7, 0) ++#define QCA808X_CDT_DIAG_LENGTH_SAME_SHORT GENMASK(15, 8) ++#define QCA808X_CDT_DIAG_LENGTH_CROSS_SHORT GENMASK(7, 0) + + #define QCA808X_CDT_CODE_PAIR_A GENMASK(15, 12) + #define QCA808X_CDT_CODE_PAIR_B GENMASK(11, 8) + #define QCA808X_CDT_CODE_PAIR_C GENMASK(7, 4) + #define QCA808X_CDT_CODE_PAIR_D GENMASK(3, 0) +-#define QCA808X_CDT_STATUS_STAT_FAIL 0 +-#define QCA808X_CDT_STATUS_STAT_NORMAL 1 +-#define QCA808X_CDT_STATUS_STAT_OPEN 2 +-#define QCA808X_CDT_STATUS_STAT_SHORT 3 ++ ++#define QCA808X_CDT_STATUS_STAT_TYPE GENMASK(1, 0) ++#define QCA808X_CDT_STATUS_STAT_FAIL FIELD_PREP_CONST(QCA808X_CDT_STATUS_STAT_TYPE, 0) ++#define QCA808X_CDT_STATUS_STAT_NORMAL FIELD_PREP_CONST(QCA808X_CDT_STATUS_STAT_TYPE, 1) ++#define QCA808X_CDT_STATUS_STAT_SAME_OPEN FIELD_PREP_CONST(QCA808X_CDT_STATUS_STAT_TYPE, 2) ++#define QCA808X_CDT_STATUS_STAT_SAME_SHORT FIELD_PREP_CONST(QCA808X_CDT_STATUS_STAT_TYPE, 3) ++ ++#define QCA808X_CDT_STATUS_STAT_MDI GENMASK(3, 2) ++#define QCA808X_CDT_STATUS_STAT_MDI1 FIELD_PREP_CONST(QCA808X_CDT_STATUS_STAT_MDI, 1) ++#define QCA808X_CDT_STATUS_STAT_MDI2 FIELD_PREP_CONST(QCA808X_CDT_STATUS_STAT_MDI, 2) ++#define QCA808X_CDT_STATUS_STAT_MDI3 FIELD_PREP_CONST(QCA808X_CDT_STATUS_STAT_MDI, 3) ++ ++/* NORMAL are MDI with type set to 0 */ ++#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_NORMAL QCA808X_CDT_STATUS_STAT_MDI1 ++#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_OPEN (QCA808X_CDT_STATUS_STAT_SAME_OPEN |\ ++ QCA808X_CDT_STATUS_STAT_MDI1) ++#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_SHORT (QCA808X_CDT_STATUS_STAT_SAME_SHORT |\ ++ QCA808X_CDT_STATUS_STAT_MDI1) ++#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_NORMAL QCA808X_CDT_STATUS_STAT_MDI2 ++#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_OPEN (QCA808X_CDT_STATUS_STAT_SAME_OPEN |\ ++ QCA808X_CDT_STATUS_STAT_MDI2) ++#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_SHORT (QCA808X_CDT_STATUS_STAT_SAME_SHORT |\ ++ QCA808X_CDT_STATUS_STAT_MDI2) ++#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_NORMAL QCA808X_CDT_STATUS_STAT_MDI3 ++#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_OPEN (QCA808X_CDT_STATUS_STAT_SAME_OPEN |\ ++ QCA808X_CDT_STATUS_STAT_MDI3) ++#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_SHORT (QCA808X_CDT_STATUS_STAT_SAME_SHORT |\ ++ QCA808X_CDT_STATUS_STAT_MDI3) ++ ++/* Added for reference of existence but should be handled by wait_for_completion already */ ++#define QCA808X_CDT_STATUS_STAT_BUSY (BIT(1) | BIT(3)) + + /* QCA808X 1G chip type */ + #define QCA808X_PHY_MMD7_CHIP_TYPE 0x901d +@@ -1941,8 +1970,17 @@ static int qca808x_soft_reset(struct phy + static bool qca808x_cdt_fault_length_valid(int cdt_code) + { + switch (cdt_code) { +- case QCA808X_CDT_STATUS_STAT_SHORT: +- case QCA808X_CDT_STATUS_STAT_OPEN: ++ case QCA808X_CDT_STATUS_STAT_SAME_SHORT: ++ case QCA808X_CDT_STATUS_STAT_SAME_OPEN: ++ case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_NORMAL: ++ case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_OPEN: ++ case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_SHORT: ++ case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_NORMAL: ++ case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_OPEN: ++ case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_SHORT: ++ case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_NORMAL: ++ case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_OPEN: ++ case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_SHORT: + return true; + default: + return false; +@@ -1954,17 +1992,28 @@ static int qca808x_cable_test_result_tra + switch (cdt_code) { + case QCA808X_CDT_STATUS_STAT_NORMAL: + return ETHTOOL_A_CABLE_RESULT_CODE_OK; +- case QCA808X_CDT_STATUS_STAT_SHORT: ++ case QCA808X_CDT_STATUS_STAT_SAME_SHORT: + return ETHTOOL_A_CABLE_RESULT_CODE_SAME_SHORT; +- case QCA808X_CDT_STATUS_STAT_OPEN: ++ case QCA808X_CDT_STATUS_STAT_SAME_OPEN: + return ETHTOOL_A_CABLE_RESULT_CODE_OPEN; ++ case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_NORMAL: ++ case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_OPEN: ++ case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_SHORT: ++ case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_NORMAL: ++ case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_OPEN: ++ case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_SHORT: ++ case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_NORMAL: ++ case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_OPEN: ++ case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_SHORT: ++ return ETHTOOL_A_CABLE_RESULT_CODE_CROSS_SHORT; + case QCA808X_CDT_STATUS_STAT_FAIL: + default: + return ETHTOOL_A_CABLE_RESULT_CODE_UNSPEC; + } + } + +-static int qca808x_cdt_fault_length(struct phy_device *phydev, int pair) ++static int qca808x_cdt_fault_length(struct phy_device *phydev, int pair, ++ int result) + { + int val; + u32 cdt_length_reg = 0; +@@ -1990,7 +2039,11 @@ static int qca808x_cdt_fault_length(stru + if (val < 0) + return val; + +- val = FIELD_GET(QCA808X_CDT_DIAG_LENGTH, val); ++ if (result == ETHTOOL_A_CABLE_RESULT_CODE_SAME_SHORT) ++ val = FIELD_GET(QCA808X_CDT_DIAG_LENGTH_SAME_SHORT, val); ++ else ++ val = FIELD_GET(QCA808X_CDT_DIAG_LENGTH_CROSS_SHORT, val); ++ + return at803x_cdt_fault_length(val); + } + +@@ -2038,8 +2091,8 @@ static int qca808x_cable_test_start(stru + static int qca808x_cable_test_get_pair_status(struct phy_device *phydev, u8 pair, + u16 status) + { ++ int length, result; + u16 pair_code; +- int length; + + switch (pair) { + case ETHTOOL_A_CABLE_PAIR_A: +@@ -2058,11 +2111,11 @@ static int qca808x_cable_test_get_pair_s + return -EINVAL; + } + +- ethnl_cable_test_result(phydev, pair, +- qca808x_cable_test_result_trans(pair_code)); ++ result = qca808x_cable_test_result_trans(pair_code); ++ ethnl_cable_test_result(phydev, pair, result); + + if (qca808x_cdt_fault_length_valid(pair_code)) { +- length = qca808x_cdt_fault_length(phydev, pair); ++ length = qca808x_cdt_fault_length(phydev, pair, result); + ethnl_cable_test_fault_length(phydev, pair, length); + } + +@@ -2076,8 +2129,7 @@ static int qca808x_cable_test_get_status + *finished = false; + + val = QCA808X_CDT_ENABLE_TEST | +- QCA808X_CDT_LENGTH_UNIT | +- QCA808X_CDT_INTER_CHECK_DIS; ++ QCA808X_CDT_LENGTH_UNIT; + ret = at803x_cdt_start(phydev, val); + if (ret) + return ret; diff --git a/target/linux/generic/backport-6.1/711-v6.8-04-net-phy-at803x-make-read_status-more-generic.patch b/target/linux/generic/backport-6.1/711-v6.8-04-net-phy-at803x-make-read_status-more-generic.patch new file mode 100644 index 000000000..c146e502f --- /dev/null +++ b/target/linux/generic/backport-6.1/711-v6.8-04-net-phy-at803x-make-read_status-more-generic.patch @@ -0,0 +1,62 @@ +From c34d9452d4e5d98a655d7b625e85466320885416 Mon Sep 17 00:00:00 2001 +From: Christian Marangi +Date: Thu, 4 Jan 2024 22:30:41 +0100 +Subject: [PATCH 4/4] net: phy: at803x: make read_status more generic + +Make read_status more generic in preparation on moving it to shared +library as other PHY Family Driver will have the exact same +implementation. + +The only specific part was a check for AR8031/33 if 1000basex was used. +The check is moved to a dedicated function specific for those PHYs. + +Signed-off-by: Christian Marangi +Reviewed-by: Simon Horman +Signed-off-by: David S. Miller +--- + drivers/net/phy/at803x.c | 17 ++++++++++++----- + 1 file changed, 12 insertions(+), 5 deletions(-) + +--- a/drivers/net/phy/at803x.c ++++ b/drivers/net/phy/at803x.c +@@ -1020,13 +1020,9 @@ static int at803x_read_specific_status(s + + static int at803x_read_status(struct phy_device *phydev) + { +- struct at803x_priv *priv = phydev->priv; + struct at803x_ss_mask ss_mask = { 0 }; + int err, old_link = phydev->link; + +- if (priv->is_1000basex) +- return genphy_c37_read_status(phydev); +- + /* Update the link, but return if there was an error */ + err = genphy_update_link(phydev); + if (err) +@@ -1618,6 +1614,17 @@ static int at8031_config_intr(struct phy + return at803x_config_intr(phydev); + } + ++/* AR8031 and AR8033 share the same read status logic */ ++static int at8031_read_status(struct phy_device *phydev) ++{ ++ struct at803x_priv *priv = phydev->priv; ++ ++ if (priv->is_1000basex) ++ return genphy_c37_read_status(phydev); ++ ++ return at803x_read_status(phydev); ++} ++ + /* AR8031 and AR8035 share the same cable test get status reg */ + static int at8031_cable_test_get_status(struct phy_device *phydev, + bool *finished) +@@ -2281,7 +2288,7 @@ static struct phy_driver at803x_driver[] + .read_page = at803x_read_page, + .write_page = at803x_write_page, + .get_features = at803x_get_features, +- .read_status = at803x_read_status, ++ .read_status = at8031_read_status, + .config_intr = at8031_config_intr, + .handle_interrupt = at803x_handle_interrupt, + .get_tunable = at803x_get_tunable, diff --git a/target/linux/generic/backport-6.1/712-v6.9-net-phy-at803x-add-LED-support-for-qca808x.patch b/target/linux/generic/backport-6.1/712-v6.9-net-phy-at803x-add-LED-support-for-qca808x.patch new file mode 100644 index 000000000..36675e758 --- /dev/null +++ b/target/linux/generic/backport-6.1/712-v6.9-net-phy-at803x-add-LED-support-for-qca808x.patch @@ -0,0 +1,408 @@ +From 7196062b64ee470b91015f3d2e82d225948258ea Mon Sep 17 00:00:00 2001 +From: Christian Marangi +Date: Thu, 25 Jan 2024 21:37:01 +0100 +Subject: [PATCH 5/5] net: phy: at803x: add LED support for qca808x + +Add LED support for QCA8081 PHY. + +Documentation for this LEDs PHY is very scarce even with NDA access +to Documentation for OEMs. Only the blink pattern are documented and are +very confusing most of the time. No documentation is present about +forcing the LED on/off or to always blink. + +Those settings were reversed by poking the regs and trying to find the +correct bits to trigger these modes. Some bits mode are not clear and +maybe the documentation option are not 100% correct. For the sake of LED +support the reversed option are enough to add support for current LED +APIs. + +Supported HW control modes are: +- tx +- rx +- link_10 +- link_100 +- link_1000 +- link_2500 +- half_duplex +- full_duplex + +Also add support for LED polarity set to set LED polarity to active +high or low. QSDK sets this value to high by default but PHY reset value +doesn't have this enabled by default. + +QSDK also sets 2 additional bits but their usage is not clear, info about +this is added in the header. It was verified that for correct function +of the LED if active high is needed, only BIT 6 is needed. + +Signed-off-by: Christian Marangi +Reviewed-by: Andrew Lunn +Link: https://lore.kernel.org/r/20240125203702.4552-6-ansuelsmth@gmail.com +Signed-off-by: Jakub Kicinski +--- + drivers/net/phy/at803x.c | 327 +++++++++++++++++++++++++++++++++++++++ + 1 file changed, 327 insertions(+) + +--- a/drivers/net/phy/at803x.c ++++ b/drivers/net/phy/at803x.c +@@ -301,6 +301,87 @@ + /* Added for reference of existence but should be handled by wait_for_completion already */ + #define QCA808X_CDT_STATUS_STAT_BUSY (BIT(1) | BIT(3)) + ++#define QCA808X_MMD7_LED_GLOBAL 0x8073 ++#define QCA808X_LED_BLINK_1 GENMASK(11, 6) ++#define QCA808X_LED_BLINK_2 GENMASK(5, 0) ++/* Values are the same for both BLINK_1 and BLINK_2 */ ++#define QCA808X_LED_BLINK_FREQ_MASK GENMASK(5, 3) ++#define QCA808X_LED_BLINK_FREQ_2HZ FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x0) ++#define QCA808X_LED_BLINK_FREQ_4HZ FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x1) ++#define QCA808X_LED_BLINK_FREQ_8HZ FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x2) ++#define QCA808X_LED_BLINK_FREQ_16HZ FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x3) ++#define QCA808X_LED_BLINK_FREQ_32HZ FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x4) ++#define QCA808X_LED_BLINK_FREQ_64HZ FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x5) ++#define QCA808X_LED_BLINK_FREQ_128HZ FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x6) ++#define QCA808X_LED_BLINK_FREQ_256HZ FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x7) ++#define QCA808X_LED_BLINK_DUTY_MASK GENMASK(2, 0) ++#define QCA808X_LED_BLINK_DUTY_50_50 FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x0) ++#define QCA808X_LED_BLINK_DUTY_75_25 FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x1) ++#define QCA808X_LED_BLINK_DUTY_25_75 FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x2) ++#define QCA808X_LED_BLINK_DUTY_33_67 FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x3) ++#define QCA808X_LED_BLINK_DUTY_67_33 FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x4) ++#define QCA808X_LED_BLINK_DUTY_17_83 FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x5) ++#define QCA808X_LED_BLINK_DUTY_83_17 FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x6) ++#define QCA808X_LED_BLINK_DUTY_8_92 FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x7) ++ ++#define QCA808X_MMD7_LED2_CTRL 0x8074 ++#define QCA808X_MMD7_LED2_FORCE_CTRL 0x8075 ++#define QCA808X_MMD7_LED1_CTRL 0x8076 ++#define QCA808X_MMD7_LED1_FORCE_CTRL 0x8077 ++#define QCA808X_MMD7_LED0_CTRL 0x8078 ++#define QCA808X_MMD7_LED_CTRL(x) (0x8078 - ((x) * 2)) ++ ++/* LED hw control pattern is the same for every LED */ ++#define QCA808X_LED_PATTERN_MASK GENMASK(15, 0) ++#define QCA808X_LED_SPEED2500_ON BIT(15) ++#define QCA808X_LED_SPEED2500_BLINK BIT(14) ++/* Follow blink trigger even if duplex or speed condition doesn't match */ ++#define QCA808X_LED_BLINK_CHECK_BYPASS BIT(13) ++#define QCA808X_LED_FULL_DUPLEX_ON BIT(12) ++#define QCA808X_LED_HALF_DUPLEX_ON BIT(11) ++#define QCA808X_LED_TX_BLINK BIT(10) ++#define QCA808X_LED_RX_BLINK BIT(9) ++#define QCA808X_LED_TX_ON_10MS BIT(8) ++#define QCA808X_LED_RX_ON_10MS BIT(7) ++#define QCA808X_LED_SPEED1000_ON BIT(6) ++#define QCA808X_LED_SPEED100_ON BIT(5) ++#define QCA808X_LED_SPEED10_ON BIT(4) ++#define QCA808X_LED_COLLISION_BLINK BIT(3) ++#define QCA808X_LED_SPEED1000_BLINK BIT(2) ++#define QCA808X_LED_SPEED100_BLINK BIT(1) ++#define QCA808X_LED_SPEED10_BLINK BIT(0) ++ ++#define QCA808X_MMD7_LED0_FORCE_CTRL 0x8079 ++#define QCA808X_MMD7_LED_FORCE_CTRL(x) (0x8079 - ((x) * 2)) ++ ++/* LED force ctrl is the same for every LED ++ * No documentation exist for this, not even internal one ++ * with NDA as QCOM gives only info about configuring ++ * hw control pattern rules and doesn't indicate any way ++ * to force the LED to specific mode. ++ * These define comes from reverse and testing and maybe ++ * lack of some info or some info are not entirely correct. ++ * For the basic LED control and hw control these finding ++ * are enough to support LED control in all the required APIs. ++ * ++ * On doing some comparison with implementation with qca807x, ++ * it was found that it's 1:1 equal to it and confirms all the ++ * reverse done. It was also found further specification with the ++ * force mode and the blink modes. ++ */ ++#define QCA808X_LED_FORCE_EN BIT(15) ++#define QCA808X_LED_FORCE_MODE_MASK GENMASK(14, 13) ++#define QCA808X_LED_FORCE_BLINK_1 FIELD_PREP(QCA808X_LED_FORCE_MODE_MASK, 0x3) ++#define QCA808X_LED_FORCE_BLINK_2 FIELD_PREP(QCA808X_LED_FORCE_MODE_MASK, 0x2) ++#define QCA808X_LED_FORCE_ON FIELD_PREP(QCA808X_LED_FORCE_MODE_MASK, 0x1) ++#define QCA808X_LED_FORCE_OFF FIELD_PREP(QCA808X_LED_FORCE_MODE_MASK, 0x0) ++ ++#define QCA808X_MMD7_LED_POLARITY_CTRL 0x901a ++/* QSDK sets by default 0x46 to this reg that sets BIT 6 for ++ * LED to active high. It's not clear what BIT 3 and BIT 4 does. ++ */ ++#define QCA808X_LED_ACTIVE_HIGH BIT(6) ++ + /* QCA808X 1G chip type */ + #define QCA808X_PHY_MMD7_CHIP_TYPE 0x901d + #define QCA808X_PHY_CHIP_TYPE_1G BIT(0) +@@ -346,6 +427,7 @@ struct at803x_priv { + struct regulator_dev *vddio_rdev; + struct regulator_dev *vddh_rdev; + u64 stats[ARRAY_SIZE(qca83xx_hw_stats)]; ++ int led_polarity_mode; + }; + + struct at803x_context { +@@ -706,6 +788,9 @@ static int at803x_probe(struct phy_devic + if (!priv) + return -ENOMEM; + ++ /* Init LED polarity mode to -1 */ ++ priv->led_polarity_mode = -1; ++ + phydev->priv = priv; + + ret = at803x_parse_dt(phydev); +@@ -2235,6 +2320,242 @@ static void qca808x_link_change_notify(s + phydev->link ? QCA8081_PHY_FIFO_RSTN : 0); + } + ++static int qca808x_led_parse_netdev(struct phy_device *phydev, unsigned long rules, ++ u16 *offload_trigger) ++{ ++ /* Parsing specific to netdev trigger */ ++ if (test_bit(TRIGGER_NETDEV_TX, &rules)) ++ *offload_trigger |= QCA808X_LED_TX_BLINK; ++ if (test_bit(TRIGGER_NETDEV_RX, &rules)) ++ *offload_trigger |= QCA808X_LED_RX_BLINK; ++ if (test_bit(TRIGGER_NETDEV_LINK_10, &rules)) ++ *offload_trigger |= QCA808X_LED_SPEED10_ON; ++ if (test_bit(TRIGGER_NETDEV_LINK_100, &rules)) ++ *offload_trigger |= QCA808X_LED_SPEED100_ON; ++ if (test_bit(TRIGGER_NETDEV_LINK_1000, &rules)) ++ *offload_trigger |= QCA808X_LED_SPEED1000_ON; ++ if (test_bit(TRIGGER_NETDEV_LINK_2500, &rules)) ++ *offload_trigger |= QCA808X_LED_SPEED2500_ON; ++ if (test_bit(TRIGGER_NETDEV_HALF_DUPLEX, &rules)) ++ *offload_trigger |= QCA808X_LED_HALF_DUPLEX_ON; ++ if (test_bit(TRIGGER_NETDEV_FULL_DUPLEX, &rules)) ++ *offload_trigger |= QCA808X_LED_FULL_DUPLEX_ON; ++ ++ if (rules && !*offload_trigger) ++ return -EOPNOTSUPP; ++ ++ /* Enable BLINK_CHECK_BYPASS by default to make the LED ++ * blink even with duplex or speed mode not enabled. ++ */ ++ *offload_trigger |= QCA808X_LED_BLINK_CHECK_BYPASS; ++ ++ return 0; ++} ++ ++static int qca808x_led_hw_control_enable(struct phy_device *phydev, u8 index) ++{ ++ u16 reg; ++ ++ if (index > 2) ++ return -EINVAL; ++ ++ reg = QCA808X_MMD7_LED_FORCE_CTRL(index); ++ ++ return phy_clear_bits_mmd(phydev, MDIO_MMD_AN, reg, ++ QCA808X_LED_FORCE_EN); ++} ++ ++static int qca808x_led_hw_is_supported(struct phy_device *phydev, u8 index, ++ unsigned long rules) ++{ ++ u16 offload_trigger = 0; ++ ++ if (index > 2) ++ return -EINVAL; ++ ++ return qca808x_led_parse_netdev(phydev, rules, &offload_trigger); ++} ++ ++static int qca808x_led_hw_control_set(struct phy_device *phydev, u8 index, ++ unsigned long rules) ++{ ++ u16 reg, offload_trigger = 0; ++ int ret; ++ ++ if (index > 2) ++ return -EINVAL; ++ ++ reg = QCA808X_MMD7_LED_CTRL(index); ++ ++ ret = qca808x_led_parse_netdev(phydev, rules, &offload_trigger); ++ if (ret) ++ return ret; ++ ++ ret = qca808x_led_hw_control_enable(phydev, index); ++ if (ret) ++ return ret; ++ ++ return phy_modify_mmd(phydev, MDIO_MMD_AN, reg, ++ QCA808X_LED_PATTERN_MASK, ++ offload_trigger); ++} ++ ++static bool qca808x_led_hw_control_status(struct phy_device *phydev, u8 index) ++{ ++ u16 reg; ++ int val; ++ ++ if (index > 2) ++ return false; ++ ++ reg = QCA808X_MMD7_LED_FORCE_CTRL(index); ++ ++ val = phy_read_mmd(phydev, MDIO_MMD_AN, reg); ++ ++ return !(val & QCA808X_LED_FORCE_EN); ++} ++ ++static int qca808x_led_hw_control_get(struct phy_device *phydev, u8 index, ++ unsigned long *rules) ++{ ++ u16 reg; ++ int val; ++ ++ if (index > 2) ++ return -EINVAL; ++ ++ /* Check if we have hw control enabled */ ++ if (qca808x_led_hw_control_status(phydev, index)) ++ return -EINVAL; ++ ++ reg = QCA808X_MMD7_LED_CTRL(index); ++ ++ val = phy_read_mmd(phydev, MDIO_MMD_AN, reg); ++ if (val & QCA808X_LED_TX_BLINK) ++ set_bit(TRIGGER_NETDEV_TX, rules); ++ if (val & QCA808X_LED_RX_BLINK) ++ set_bit(TRIGGER_NETDEV_RX, rules); ++ if (val & QCA808X_LED_SPEED10_ON) ++ set_bit(TRIGGER_NETDEV_LINK_10, rules); ++ if (val & QCA808X_LED_SPEED100_ON) ++ set_bit(TRIGGER_NETDEV_LINK_100, rules); ++ if (val & QCA808X_LED_SPEED1000_ON) ++ set_bit(TRIGGER_NETDEV_LINK_1000, rules); ++ if (val & QCA808X_LED_SPEED2500_ON) ++ set_bit(TRIGGER_NETDEV_LINK_2500, rules); ++ if (val & QCA808X_LED_HALF_DUPLEX_ON) ++ set_bit(TRIGGER_NETDEV_HALF_DUPLEX, rules); ++ if (val & QCA808X_LED_FULL_DUPLEX_ON) ++ set_bit(TRIGGER_NETDEV_FULL_DUPLEX, rules); ++ ++ return 0; ++} ++ ++static int qca808x_led_hw_control_reset(struct phy_device *phydev, u8 index) ++{ ++ u16 reg; ++ ++ if (index > 2) ++ return -EINVAL; ++ ++ reg = QCA808X_MMD7_LED_CTRL(index); ++ ++ return phy_clear_bits_mmd(phydev, MDIO_MMD_AN, reg, ++ QCA808X_LED_PATTERN_MASK); ++} ++ ++static int qca808x_led_brightness_set(struct phy_device *phydev, ++ u8 index, enum led_brightness value) ++{ ++ u16 reg; ++ int ret; ++ ++ if (index > 2) ++ return -EINVAL; ++ ++ if (!value) { ++ ret = qca808x_led_hw_control_reset(phydev, index); ++ if (ret) ++ return ret; ++ } ++ ++ reg = QCA808X_MMD7_LED_FORCE_CTRL(index); ++ ++ return phy_modify_mmd(phydev, MDIO_MMD_AN, reg, ++ QCA808X_LED_FORCE_EN | QCA808X_LED_FORCE_MODE_MASK, ++ QCA808X_LED_FORCE_EN | value ? QCA808X_LED_FORCE_ON : ++ QCA808X_LED_FORCE_OFF); ++} ++ ++static int qca808x_led_blink_set(struct phy_device *phydev, u8 index, ++ unsigned long *delay_on, ++ unsigned long *delay_off) ++{ ++ int ret; ++ u16 reg; ++ ++ if (index > 2) ++ return -EINVAL; ++ ++ reg = QCA808X_MMD7_LED_FORCE_CTRL(index); ++ ++ /* Set blink to 50% off, 50% on at 4Hz by default */ ++ ret = phy_modify_mmd(phydev, MDIO_MMD_AN, QCA808X_MMD7_LED_GLOBAL, ++ QCA808X_LED_BLINK_FREQ_MASK | QCA808X_LED_BLINK_DUTY_MASK, ++ QCA808X_LED_BLINK_FREQ_4HZ | QCA808X_LED_BLINK_DUTY_50_50); ++ if (ret) ++ return ret; ++ ++ /* We use BLINK_1 for normal blinking */ ++ ret = phy_modify_mmd(phydev, MDIO_MMD_AN, reg, ++ QCA808X_LED_FORCE_EN | QCA808X_LED_FORCE_MODE_MASK, ++ QCA808X_LED_FORCE_EN | QCA808X_LED_FORCE_BLINK_1); ++ if (ret) ++ return ret; ++ ++ /* We set blink to 4Hz, aka 250ms */ ++ *delay_on = 250 / 2; ++ *delay_off = 250 / 2; ++ ++ return 0; ++} ++ ++static int qca808x_led_polarity_set(struct phy_device *phydev, int index, ++ unsigned long modes) ++{ ++ struct at803x_priv *priv = phydev->priv; ++ bool active_low = false; ++ u32 mode; ++ ++ for_each_set_bit(mode, &modes, __PHY_LED_MODES_NUM) { ++ switch (mode) { ++ case PHY_LED_ACTIVE_LOW: ++ active_low = true; ++ break; ++ default: ++ return -EINVAL; ++ } ++ } ++ ++ /* PHY polarity is global and can't be set per LED. ++ * To detect this, check if last requested polarity mode ++ * match the new one. ++ */ ++ if (priv->led_polarity_mode >= 0 && ++ priv->led_polarity_mode != active_low) { ++ phydev_err(phydev, "PHY polarity is global. Mismatched polarity on different LED\n"); ++ return -EINVAL; ++ } ++ ++ /* Save the last PHY polarity mode */ ++ priv->led_polarity_mode = active_low; ++ ++ return phy_modify_mmd(phydev, MDIO_MMD_AN, ++ QCA808X_MMD7_LED_POLARITY_CTRL, ++ QCA808X_LED_ACTIVE_HIGH, ++ active_low ? 0 : QCA808X_LED_ACTIVE_HIGH); ++} ++ + static struct phy_driver at803x_driver[] = { + { + /* Qualcomm Atheros AR8035 */ +@@ -2411,6 +2732,12 @@ static struct phy_driver at803x_driver[] + .cable_test_start = qca808x_cable_test_start, + .cable_test_get_status = qca808x_cable_test_get_status, + .link_change_notify = qca808x_link_change_notify, ++ .led_brightness_set = qca808x_led_brightness_set, ++ .led_blink_set = qca808x_led_blink_set, ++ .led_hw_is_supported = qca808x_led_hw_is_supported, ++ .led_hw_control_set = qca808x_led_hw_control_set, ++ .led_hw_control_get = qca808x_led_hw_control_get, ++ .led_polarity_set = qca808x_led_polarity_set, + }, }; + + module_phy_driver(at803x_driver); diff --git a/target/linux/generic/backport-6.1/713-v6.9-01-net-phy-move-at803x-PHY-driver-to-dedicated-director.patch b/target/linux/generic/backport-6.1/713-v6.9-01-net-phy-move-at803x-PHY-driver-to-dedicated-director.patch new file mode 100644 index 000000000..8c9babea7 --- /dev/null +++ b/target/linux/generic/backport-6.1/713-v6.9-01-net-phy-move-at803x-PHY-driver-to-dedicated-director.patch @@ -0,0 +1,5598 @@ +From 9e56ff53b4115875667760445b028357848b4748 Mon Sep 17 00:00:00 2001 +From: Christian Marangi +Date: Mon, 29 Jan 2024 15:15:19 +0100 +Subject: [PATCH 1/5] net: phy: move at803x PHY driver to dedicated directory + +In preparation for addition of other Qcom PHY and to tidy things up, +move the at803x PHY driver to dedicated directory. + +The same order in the Kconfig selection is saved. + +Signed-off-by: Christian Marangi +Reviewed-by: Andrew Lunn +Link: https://lore.kernel.org/r/20240129141600.2592-2-ansuelsmth@gmail.com +Signed-off-by: Jakub Kicinski +--- + drivers/net/phy/Kconfig | 7 +------ + drivers/net/phy/Makefile | 2 +- + drivers/net/phy/qcom/Kconfig | 7 +++++++ + drivers/net/phy/qcom/Makefile | 2 ++ + drivers/net/phy/{ => qcom}/at803x.c | 0 + 5 files changed, 11 insertions(+), 7 deletions(-) + create mode 100644 drivers/net/phy/qcom/Kconfig + create mode 100644 drivers/net/phy/qcom/Makefile + rename drivers/net/phy/{ => qcom}/at803x.c (100%) + +--- a/drivers/net/phy/Kconfig ++++ b/drivers/net/phy/Kconfig +@@ -277,12 +277,7 @@ config NXP_TJA11XX_PHY + help + Currently supports the NXP TJA1100 and TJA1101 PHY. + +-config AT803X_PHY +- tristate "Qualcomm Atheros AR803X PHYs and QCA833x PHYs" +- depends on REGULATOR +- help +- Currently supports the AR8030, AR8031, AR8033, AR8035 and internal +- QCA8337(Internal qca8k PHY) model ++source "drivers/net/phy/qcom/Kconfig" + + config QSEMI_PHY + tristate "Quality Semiconductor PHYs" +--- a/drivers/net/phy/Makefile ++++ b/drivers/net/phy/Makefile +@@ -34,7 +34,6 @@ obj-$(CONFIG_ADIN_PHY) += adin.o + obj-$(CONFIG_ADIN1100_PHY) += adin1100.o + obj-$(CONFIG_AMD_PHY) += amd.o + obj-$(CONFIG_AQUANTIA_PHY) += aquantia/ +-obj-$(CONFIG_AT803X_PHY) += at803x.o + obj-$(CONFIG_AX88796B_PHY) += ax88796b.o + obj-$(CONFIG_BCM54140_PHY) += bcm54140.o + obj-$(CONFIG_BCM63XX_PHY) += bcm63xx.o +@@ -75,6 +74,7 @@ obj-$(CONFIG_MOTORCOMM_PHY) += motorcomm + obj-$(CONFIG_NATIONAL_PHY) += national.o + obj-$(CONFIG_NXP_C45_TJA11XX_PHY) += nxp-c45-tja11xx.o + obj-$(CONFIG_NXP_TJA11XX_PHY) += nxp-tja11xx.o ++obj-y += qcom/ + obj-$(CONFIG_QSEMI_PHY) += qsemi.o + obj-$(CONFIG_REALTEK_PHY) += realtek.o + obj-$(CONFIG_RENESAS_PHY) += uPD60620.o +--- /dev/null ++++ b/drivers/net/phy/qcom/Kconfig +@@ -0,0 +1,7 @@ ++# SPDX-License-Identifier: GPL-2.0-only ++config AT803X_PHY ++ tristate "Qualcomm Atheros AR803X PHYs and QCA833x PHYs" ++ depends on REGULATOR ++ help ++ Currently supports the AR8030, AR8031, AR8033, AR8035 and internal ++ QCA8337(Internal qca8k PHY) model +--- /dev/null ++++ b/drivers/net/phy/qcom/Makefile +@@ -0,0 +1,2 @@ ++# SPDX-License-Identifier: GPL-2.0 ++obj-$(CONFIG_AT803X_PHY) += at803x.o +--- a/drivers/net/phy/at803x.c ++++ /dev/null +@@ -1,2759 +0,0 @@ +-// SPDX-License-Identifier: GPL-2.0+ +-/* +- * drivers/net/phy/at803x.c +- * +- * Driver for Qualcomm Atheros AR803x PHY +- * +- * Author: Matus Ujhelyi +- */ +- +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +- +-#define AT803X_SPECIFIC_FUNCTION_CONTROL 0x10 +-#define AT803X_SFC_ASSERT_CRS BIT(11) +-#define AT803X_SFC_FORCE_LINK BIT(10) +-#define AT803X_SFC_MDI_CROSSOVER_MODE_M GENMASK(6, 5) +-#define AT803X_SFC_AUTOMATIC_CROSSOVER 0x3 +-#define AT803X_SFC_MANUAL_MDIX 0x1 +-#define AT803X_SFC_MANUAL_MDI 0x0 +-#define AT803X_SFC_SQE_TEST BIT(2) +-#define AT803X_SFC_POLARITY_REVERSAL BIT(1) +-#define AT803X_SFC_DISABLE_JABBER BIT(0) +- +-#define AT803X_SPECIFIC_STATUS 0x11 +-#define AT803X_SS_SPEED_MASK GENMASK(15, 14) +-#define AT803X_SS_SPEED_1000 2 +-#define AT803X_SS_SPEED_100 1 +-#define AT803X_SS_SPEED_10 0 +-#define AT803X_SS_DUPLEX BIT(13) +-#define AT803X_SS_SPEED_DUPLEX_RESOLVED BIT(11) +-#define AT803X_SS_MDIX BIT(6) +- +-#define QCA808X_SS_SPEED_MASK GENMASK(9, 7) +-#define QCA808X_SS_SPEED_2500 4 +- +-#define AT803X_INTR_ENABLE 0x12 +-#define AT803X_INTR_ENABLE_AUTONEG_ERR BIT(15) +-#define AT803X_INTR_ENABLE_SPEED_CHANGED BIT(14) +-#define AT803X_INTR_ENABLE_DUPLEX_CHANGED BIT(13) +-#define AT803X_INTR_ENABLE_PAGE_RECEIVED BIT(12) +-#define AT803X_INTR_ENABLE_LINK_FAIL BIT(11) +-#define AT803X_INTR_ENABLE_LINK_SUCCESS BIT(10) +-#define AT803X_INTR_ENABLE_LINK_FAIL_BX BIT(8) +-#define AT803X_INTR_ENABLE_LINK_SUCCESS_BX BIT(7) +-#define AT803X_INTR_ENABLE_WIRESPEED_DOWNGRADE BIT(5) +-#define AT803X_INTR_ENABLE_POLARITY_CHANGED BIT(1) +-#define AT803X_INTR_ENABLE_WOL BIT(0) +- +-#define AT803X_INTR_STATUS 0x13 +- +-#define AT803X_SMART_SPEED 0x14 +-#define AT803X_SMART_SPEED_ENABLE BIT(5) +-#define AT803X_SMART_SPEED_RETRY_LIMIT_MASK GENMASK(4, 2) +-#define AT803X_SMART_SPEED_BYPASS_TIMER BIT(1) +-#define AT803X_CDT 0x16 +-#define AT803X_CDT_MDI_PAIR_MASK GENMASK(9, 8) +-#define AT803X_CDT_ENABLE_TEST BIT(0) +-#define AT803X_CDT_STATUS 0x1c +-#define AT803X_CDT_STATUS_STAT_NORMAL 0 +-#define AT803X_CDT_STATUS_STAT_SHORT 1 +-#define AT803X_CDT_STATUS_STAT_OPEN 2 +-#define AT803X_CDT_STATUS_STAT_FAIL 3 +-#define AT803X_CDT_STATUS_STAT_MASK GENMASK(9, 8) +-#define AT803X_CDT_STATUS_DELTA_TIME_MASK GENMASK(7, 0) +-#define AT803X_LED_CONTROL 0x18 +- +-#define AT803X_PHY_MMD3_WOL_CTRL 0x8012 +-#define AT803X_WOL_EN BIT(5) +-#define AT803X_LOC_MAC_ADDR_0_15_OFFSET 0x804C +-#define AT803X_LOC_MAC_ADDR_16_31_OFFSET 0x804B +-#define AT803X_LOC_MAC_ADDR_32_47_OFFSET 0x804A +-#define AT803X_REG_CHIP_CONFIG 0x1f +-#define AT803X_BT_BX_REG_SEL 0x8000 +- +-#define AT803X_DEBUG_ADDR 0x1D +-#define AT803X_DEBUG_DATA 0x1E +- +-#define AT803X_MODE_CFG_MASK 0x0F +-#define AT803X_MODE_CFG_BASET_RGMII 0x00 +-#define AT803X_MODE_CFG_BASET_SGMII 0x01 +-#define AT803X_MODE_CFG_BX1000_RGMII_50OHM 0x02 +-#define AT803X_MODE_CFG_BX1000_RGMII_75OHM 0x03 +-#define AT803X_MODE_CFG_BX1000_CONV_50OHM 0x04 +-#define AT803X_MODE_CFG_BX1000_CONV_75OHM 0x05 +-#define AT803X_MODE_CFG_FX100_RGMII_50OHM 0x06 +-#define AT803X_MODE_CFG_FX100_CONV_50OHM 0x07 +-#define AT803X_MODE_CFG_RGMII_AUTO_MDET 0x0B +-#define AT803X_MODE_CFG_FX100_RGMII_75OHM 0x0E +-#define AT803X_MODE_CFG_FX100_CONV_75OHM 0x0F +- +-#define AT803X_PSSR 0x11 /*PHY-Specific Status Register*/ +-#define AT803X_PSSR_MR_AN_COMPLETE 0x0200 +- +-#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_SYSTEM_CTRL_MODE 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_HIB_CTRL_PS_HIB_EN BIT(15) +- +-#define AT803X_DEBUG_REG_3C 0x3C +- +-#define AT803X_DEBUG_REG_GREEN 0x3D +-#define AT803X_DEBUG_GATE_CLK_IN1000 BIT(6) +- +-#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. +- * The DSP as a clock reference is used in synchronous ethernet +- * applications. +- * +- * By default the PLL is only enabled if there is a link. Otherwise +- * the PHY will go into low power state and disabled the PLL. You can +- * set the PLL_ON bit (see debug register 0x1f) to keep the PLL always +- * enabled. +- */ +-#define AT803X_MMD7_CLK25M 0x8016 +-#define AT803X_CLK_OUT_MASK GENMASK(4, 2) +-#define AT803X_CLK_OUT_25MHZ_XTAL 0 +-#define AT803X_CLK_OUT_25MHZ_DSP 1 +-#define AT803X_CLK_OUT_50MHZ_PLL 2 +-#define AT803X_CLK_OUT_50MHZ_DSP 3 +-#define AT803X_CLK_OUT_62_5MHZ_PLL 4 +-#define AT803X_CLK_OUT_62_5MHZ_DSP 5 +-#define AT803X_CLK_OUT_125MHZ_PLL 6 +-#define AT803X_CLK_OUT_125MHZ_DSP 7 +- +-/* The AR8035 has another mask which is compatible with the AR8031/AR8033 mask +- * but doesn't support choosing between XTAL/PLL and DSP. +- */ +-#define AT8035_CLK_OUT_MASK GENMASK(4, 3) +- +-#define AT803X_CLK_OUT_STRENGTH_MASK GENMASK(8, 7) +-#define AT803X_CLK_OUT_STRENGTH_FULL 0 +-#define AT803X_CLK_OUT_STRENGTH_HALF 1 +-#define AT803X_CLK_OUT_STRENGTH_QUARTER 2 +- +-#define AT803X_DEFAULT_DOWNSHIFT 5 +-#define AT803X_MIN_DOWNSHIFT 2 +-#define AT803X_MAX_DOWNSHIFT 9 +- +-#define AT803X_MMD3_SMARTEEE_CTL1 0x805b +-#define AT803X_MMD3_SMARTEEE_CTL2 0x805c +-#define AT803X_MMD3_SMARTEEE_CTL3 0x805d +-#define AT803X_MMD3_SMARTEEE_CTL3_LPI_EN BIT(8) +- +-#define ATH9331_PHY_ID 0x004dd041 +-#define ATH8030_PHY_ID 0x004dd076 +-#define ATH8031_PHY_ID 0x004dd074 +-#define ATH8032_PHY_ID 0x004dd023 +-#define ATH8035_PHY_ID 0x004dd072 +-#define AT8030_PHY_ID_MASK 0xffffffef +- +-#define QCA8081_PHY_ID 0x004dd101 +- +-#define QCA8327_A_PHY_ID 0x004dd033 +-#define QCA8327_B_PHY_ID 0x004dd034 +-#define QCA8337_PHY_ID 0x004dd036 +-#define QCA9561_PHY_ID 0x004dd042 +-#define QCA8K_PHY_ID_MASK 0xffffffff +- +-#define QCA8K_DEVFLAGS_REVISION_MASK GENMASK(2, 0) +- +-#define AT803X_PAGE_FIBER 0 +-#define AT803X_PAGE_COPPER 1 +- +-/* don't turn off internal PLL */ +-#define AT803X_KEEP_PLL_ENABLED BIT(0) +-#define AT803X_DISABLE_SMARTEEE BIT(1) +- +-/* disable hibernation mode */ +-#define AT803X_DISABLE_HIBERNATION_MODE BIT(2) +- +-/* ADC threshold */ +-#define QCA808X_PHY_DEBUG_ADC_THRESHOLD 0x2c80 +-#define QCA808X_ADC_THRESHOLD_MASK GENMASK(7, 0) +-#define QCA808X_ADC_THRESHOLD_80MV 0 +-#define QCA808X_ADC_THRESHOLD_100MV 0xf0 +-#define QCA808X_ADC_THRESHOLD_200MV 0x0f +-#define QCA808X_ADC_THRESHOLD_300MV 0xff +- +-/* CLD control */ +-#define QCA808X_PHY_MMD3_ADDR_CLD_CTRL7 0x8007 +-#define QCA808X_8023AZ_AFE_CTRL_MASK GENMASK(8, 4) +-#define QCA808X_8023AZ_AFE_EN 0x90 +- +-/* AZ control */ +-#define QCA808X_PHY_MMD3_AZ_TRAINING_CTRL 0x8008 +-#define QCA808X_MMD3_AZ_TRAINING_VAL 0x1c32 +- +-#define QCA808X_PHY_MMD1_MSE_THRESHOLD_20DB 0x8014 +-#define QCA808X_MSE_THRESHOLD_20DB_VALUE 0x529 +- +-#define QCA808X_PHY_MMD1_MSE_THRESHOLD_17DB 0x800E +-#define QCA808X_MSE_THRESHOLD_17DB_VALUE 0x341 +- +-#define QCA808X_PHY_MMD1_MSE_THRESHOLD_27DB 0x801E +-#define QCA808X_MSE_THRESHOLD_27DB_VALUE 0x419 +- +-#define QCA808X_PHY_MMD1_MSE_THRESHOLD_28DB 0x8020 +-#define QCA808X_MSE_THRESHOLD_28DB_VALUE 0x341 +- +-#define QCA808X_PHY_MMD7_TOP_OPTION1 0x901c +-#define QCA808X_TOP_OPTION1_DATA 0x0 +- +-#define QCA808X_PHY_MMD3_DEBUG_1 0xa100 +-#define QCA808X_MMD3_DEBUG_1_VALUE 0x9203 +-#define QCA808X_PHY_MMD3_DEBUG_2 0xa101 +-#define QCA808X_MMD3_DEBUG_2_VALUE 0x48ad +-#define QCA808X_PHY_MMD3_DEBUG_3 0xa103 +-#define QCA808X_MMD3_DEBUG_3_VALUE 0x1698 +-#define QCA808X_PHY_MMD3_DEBUG_4 0xa105 +-#define QCA808X_MMD3_DEBUG_4_VALUE 0x8001 +-#define QCA808X_PHY_MMD3_DEBUG_5 0xa106 +-#define QCA808X_MMD3_DEBUG_5_VALUE 0x1111 +-#define QCA808X_PHY_MMD3_DEBUG_6 0xa011 +-#define QCA808X_MMD3_DEBUG_6_VALUE 0x5f85 +- +-/* master/slave seed config */ +-#define QCA808X_PHY_DEBUG_LOCAL_SEED 9 +-#define QCA808X_MASTER_SLAVE_SEED_ENABLE BIT(1) +-#define QCA808X_MASTER_SLAVE_SEED_CFG GENMASK(12, 2) +-#define QCA808X_MASTER_SLAVE_SEED_RANGE 0x32 +- +-/* Hibernation yields lower power consumpiton in contrast with normal operation mode. +- * when the copper cable is unplugged, the PHY enters into hibernation mode in about 10s. +- */ +-#define QCA808X_DBG_AN_TEST 0xb +-#define QCA808X_HIBERNATION_EN BIT(15) +- +-#define QCA808X_CDT_ENABLE_TEST BIT(15) +-#define QCA808X_CDT_INTER_CHECK_DIS BIT(13) +-#define QCA808X_CDT_STATUS BIT(11) +-#define QCA808X_CDT_LENGTH_UNIT BIT(10) +- +-#define QCA808X_MMD3_CDT_STATUS 0x8064 +-#define QCA808X_MMD3_CDT_DIAG_PAIR_A 0x8065 +-#define QCA808X_MMD3_CDT_DIAG_PAIR_B 0x8066 +-#define QCA808X_MMD3_CDT_DIAG_PAIR_C 0x8067 +-#define QCA808X_MMD3_CDT_DIAG_PAIR_D 0x8068 +-#define QCA808X_CDT_DIAG_LENGTH_SAME_SHORT GENMASK(15, 8) +-#define QCA808X_CDT_DIAG_LENGTH_CROSS_SHORT GENMASK(7, 0) +- +-#define QCA808X_CDT_CODE_PAIR_A GENMASK(15, 12) +-#define QCA808X_CDT_CODE_PAIR_B GENMASK(11, 8) +-#define QCA808X_CDT_CODE_PAIR_C GENMASK(7, 4) +-#define QCA808X_CDT_CODE_PAIR_D GENMASK(3, 0) +- +-#define QCA808X_CDT_STATUS_STAT_TYPE GENMASK(1, 0) +-#define QCA808X_CDT_STATUS_STAT_FAIL FIELD_PREP_CONST(QCA808X_CDT_STATUS_STAT_TYPE, 0) +-#define QCA808X_CDT_STATUS_STAT_NORMAL FIELD_PREP_CONST(QCA808X_CDT_STATUS_STAT_TYPE, 1) +-#define QCA808X_CDT_STATUS_STAT_SAME_OPEN FIELD_PREP_CONST(QCA808X_CDT_STATUS_STAT_TYPE, 2) +-#define QCA808X_CDT_STATUS_STAT_SAME_SHORT FIELD_PREP_CONST(QCA808X_CDT_STATUS_STAT_TYPE, 3) +- +-#define QCA808X_CDT_STATUS_STAT_MDI GENMASK(3, 2) +-#define QCA808X_CDT_STATUS_STAT_MDI1 FIELD_PREP_CONST(QCA808X_CDT_STATUS_STAT_MDI, 1) +-#define QCA808X_CDT_STATUS_STAT_MDI2 FIELD_PREP_CONST(QCA808X_CDT_STATUS_STAT_MDI, 2) +-#define QCA808X_CDT_STATUS_STAT_MDI3 FIELD_PREP_CONST(QCA808X_CDT_STATUS_STAT_MDI, 3) +- +-/* NORMAL are MDI with type set to 0 */ +-#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_NORMAL QCA808X_CDT_STATUS_STAT_MDI1 +-#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_OPEN (QCA808X_CDT_STATUS_STAT_SAME_OPEN |\ +- QCA808X_CDT_STATUS_STAT_MDI1) +-#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_SHORT (QCA808X_CDT_STATUS_STAT_SAME_SHORT |\ +- QCA808X_CDT_STATUS_STAT_MDI1) +-#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_NORMAL QCA808X_CDT_STATUS_STAT_MDI2 +-#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_OPEN (QCA808X_CDT_STATUS_STAT_SAME_OPEN |\ +- QCA808X_CDT_STATUS_STAT_MDI2) +-#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_SHORT (QCA808X_CDT_STATUS_STAT_SAME_SHORT |\ +- QCA808X_CDT_STATUS_STAT_MDI2) +-#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_NORMAL QCA808X_CDT_STATUS_STAT_MDI3 +-#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_OPEN (QCA808X_CDT_STATUS_STAT_SAME_OPEN |\ +- QCA808X_CDT_STATUS_STAT_MDI3) +-#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_SHORT (QCA808X_CDT_STATUS_STAT_SAME_SHORT |\ +- QCA808X_CDT_STATUS_STAT_MDI3) +- +-/* Added for reference of existence but should be handled by wait_for_completion already */ +-#define QCA808X_CDT_STATUS_STAT_BUSY (BIT(1) | BIT(3)) +- +-#define QCA808X_MMD7_LED_GLOBAL 0x8073 +-#define QCA808X_LED_BLINK_1 GENMASK(11, 6) +-#define QCA808X_LED_BLINK_2 GENMASK(5, 0) +-/* Values are the same for both BLINK_1 and BLINK_2 */ +-#define QCA808X_LED_BLINK_FREQ_MASK GENMASK(5, 3) +-#define QCA808X_LED_BLINK_FREQ_2HZ FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x0) +-#define QCA808X_LED_BLINK_FREQ_4HZ FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x1) +-#define QCA808X_LED_BLINK_FREQ_8HZ FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x2) +-#define QCA808X_LED_BLINK_FREQ_16HZ FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x3) +-#define QCA808X_LED_BLINK_FREQ_32HZ FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x4) +-#define QCA808X_LED_BLINK_FREQ_64HZ FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x5) +-#define QCA808X_LED_BLINK_FREQ_128HZ FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x6) +-#define QCA808X_LED_BLINK_FREQ_256HZ FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x7) +-#define QCA808X_LED_BLINK_DUTY_MASK GENMASK(2, 0) +-#define QCA808X_LED_BLINK_DUTY_50_50 FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x0) +-#define QCA808X_LED_BLINK_DUTY_75_25 FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x1) +-#define QCA808X_LED_BLINK_DUTY_25_75 FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x2) +-#define QCA808X_LED_BLINK_DUTY_33_67 FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x3) +-#define QCA808X_LED_BLINK_DUTY_67_33 FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x4) +-#define QCA808X_LED_BLINK_DUTY_17_83 FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x5) +-#define QCA808X_LED_BLINK_DUTY_83_17 FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x6) +-#define QCA808X_LED_BLINK_DUTY_8_92 FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x7) +- +-#define QCA808X_MMD7_LED2_CTRL 0x8074 +-#define QCA808X_MMD7_LED2_FORCE_CTRL 0x8075 +-#define QCA808X_MMD7_LED1_CTRL 0x8076 +-#define QCA808X_MMD7_LED1_FORCE_CTRL 0x8077 +-#define QCA808X_MMD7_LED0_CTRL 0x8078 +-#define QCA808X_MMD7_LED_CTRL(x) (0x8078 - ((x) * 2)) +- +-/* LED hw control pattern is the same for every LED */ +-#define QCA808X_LED_PATTERN_MASK GENMASK(15, 0) +-#define QCA808X_LED_SPEED2500_ON BIT(15) +-#define QCA808X_LED_SPEED2500_BLINK BIT(14) +-/* Follow blink trigger even if duplex or speed condition doesn't match */ +-#define QCA808X_LED_BLINK_CHECK_BYPASS BIT(13) +-#define QCA808X_LED_FULL_DUPLEX_ON BIT(12) +-#define QCA808X_LED_HALF_DUPLEX_ON BIT(11) +-#define QCA808X_LED_TX_BLINK BIT(10) +-#define QCA808X_LED_RX_BLINK BIT(9) +-#define QCA808X_LED_TX_ON_10MS BIT(8) +-#define QCA808X_LED_RX_ON_10MS BIT(7) +-#define QCA808X_LED_SPEED1000_ON BIT(6) +-#define QCA808X_LED_SPEED100_ON BIT(5) +-#define QCA808X_LED_SPEED10_ON BIT(4) +-#define QCA808X_LED_COLLISION_BLINK BIT(3) +-#define QCA808X_LED_SPEED1000_BLINK BIT(2) +-#define QCA808X_LED_SPEED100_BLINK BIT(1) +-#define QCA808X_LED_SPEED10_BLINK BIT(0) +- +-#define QCA808X_MMD7_LED0_FORCE_CTRL 0x8079 +-#define QCA808X_MMD7_LED_FORCE_CTRL(x) (0x8079 - ((x) * 2)) +- +-/* LED force ctrl is the same for every LED +- * No documentation exist for this, not even internal one +- * with NDA as QCOM gives only info about configuring +- * hw control pattern rules and doesn't indicate any way +- * to force the LED to specific mode. +- * These define comes from reverse and testing and maybe +- * lack of some info or some info are not entirely correct. +- * For the basic LED control and hw control these finding +- * are enough to support LED control in all the required APIs. +- * +- * On doing some comparison with implementation with qca807x, +- * it was found that it's 1:1 equal to it and confirms all the +- * reverse done. It was also found further specification with the +- * force mode and the blink modes. +- */ +-#define QCA808X_LED_FORCE_EN BIT(15) +-#define QCA808X_LED_FORCE_MODE_MASK GENMASK(14, 13) +-#define QCA808X_LED_FORCE_BLINK_1 FIELD_PREP(QCA808X_LED_FORCE_MODE_MASK, 0x3) +-#define QCA808X_LED_FORCE_BLINK_2 FIELD_PREP(QCA808X_LED_FORCE_MODE_MASK, 0x2) +-#define QCA808X_LED_FORCE_ON FIELD_PREP(QCA808X_LED_FORCE_MODE_MASK, 0x1) +-#define QCA808X_LED_FORCE_OFF FIELD_PREP(QCA808X_LED_FORCE_MODE_MASK, 0x0) +- +-#define QCA808X_MMD7_LED_POLARITY_CTRL 0x901a +-/* QSDK sets by default 0x46 to this reg that sets BIT 6 for +- * LED to active high. It's not clear what BIT 3 and BIT 4 does. +- */ +-#define QCA808X_LED_ACTIVE_HIGH BIT(6) +- +-/* QCA808X 1G chip type */ +-#define QCA808X_PHY_MMD7_CHIP_TYPE 0x901d +-#define QCA808X_PHY_CHIP_TYPE_1G BIT(0) +- +-#define QCA8081_PHY_SERDES_MMD1_FIFO_CTRL 0x9072 +-#define QCA8081_PHY_FIFO_RSTN BIT(11) +- +-MODULE_DESCRIPTION("Qualcomm Atheros AR803x and QCA808X 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 qca83xx_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_ss_mask { +- u16 speed_mask; +- u8 speed_shift; +-}; +- +-struct at803x_priv { +- int flags; +- u16 clk_25m_reg; +- u16 clk_25m_mask; +- u8 smarteee_lpi_tw_1g; +- u8 smarteee_lpi_tw_100m; +- bool is_fiber; +- bool is_1000basex; +- struct regulator_dev *vddio_rdev; +- struct regulator_dev *vddh_rdev; +- u64 stats[ARRAY_SIZE(qca83xx_hw_stats)]; +- int led_polarity_mode; +-}; +- +-struct at803x_context { +- u16 bmcr; +- u16 advertise; +- u16 control1000; +- u16 int_enable; +- u16 smart_speed; +- 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; +- +- ret = phy_write(phydev, AT803X_DEBUG_ADDR, reg); +- if (ret < 0) +- return ret; +- +- return phy_read(phydev, AT803X_DEBUG_DATA); +-} +- +-static int at803x_debug_reg_mask(struct phy_device *phydev, u16 reg, +- u16 clear, u16 set) +-{ +- u16 val; +- int ret; +- +- ret = at803x_debug_reg_read(phydev, reg); +- if (ret < 0) +- return ret; +- +- val = ret & 0xffff; +- val &= ~clear; +- val |= set; +- +- return phy_write(phydev, AT803X_DEBUG_DATA, val); +-} +- +-static int at803x_write_page(struct phy_device *phydev, int page) +-{ +- int mask; +- int set; +- +- if (page == AT803X_PAGE_COPPER) { +- set = AT803X_BT_BX_REG_SEL; +- mask = 0; +- } else { +- set = 0; +- mask = AT803X_BT_BX_REG_SEL; +- } +- +- return __phy_modify(phydev, AT803X_REG_CHIP_CONFIG, mask, set); +-} +- +-static int at803x_read_page(struct phy_device *phydev) +-{ +- int ccr = __phy_read(phydev, AT803X_REG_CHIP_CONFIG); +- +- if (ccr < 0) +- return ccr; +- +- if (ccr & AT803X_BT_BX_REG_SEL) +- return AT803X_PAGE_COPPER; +- +- return AT803X_PAGE_FIBER; +-} +- +-static int at803x_enable_rx_delay(struct phy_device *phydev) +-{ +- 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_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_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_SYSTEM_CTRL_MODE, +- AT803X_DEBUG_TX_CLK_DLY_EN, 0); +-} +- +-/* save relevant PHY registers to private copy */ +-static void at803x_context_save(struct phy_device *phydev, +- struct at803x_context *context) +-{ +- context->bmcr = phy_read(phydev, MII_BMCR); +- context->advertise = phy_read(phydev, MII_ADVERTISE); +- context->control1000 = phy_read(phydev, MII_CTRL1000); +- context->int_enable = phy_read(phydev, AT803X_INTR_ENABLE); +- context->smart_speed = phy_read(phydev, AT803X_SMART_SPEED); +- context->led_control = phy_read(phydev, AT803X_LED_CONTROL); +-} +- +-/* restore relevant PHY registers from private copy */ +-static void at803x_context_restore(struct phy_device *phydev, +- const struct at803x_context *context) +-{ +- phy_write(phydev, MII_BMCR, context->bmcr); +- phy_write(phydev, MII_ADVERTISE, context->advertise); +- phy_write(phydev, MII_CTRL1000, context->control1000); +- phy_write(phydev, AT803X_INTR_ENABLE, context->int_enable); +- phy_write(phydev, AT803X_SMART_SPEED, context->smart_speed); +- phy_write(phydev, AT803X_LED_CONTROL, context->led_control); +-} +- +-static int at803x_set_wol(struct phy_device *phydev, +- struct ethtool_wolinfo *wol) +-{ +- int ret, irq_enabled; +- +- if (wol->wolopts & WAKE_MAGIC) { +- struct net_device *ndev = phydev->attached_dev; +- const u8 *mac; +- unsigned int i; +- static const unsigned int offsets[] = { +- AT803X_LOC_MAC_ADDR_32_47_OFFSET, +- AT803X_LOC_MAC_ADDR_16_31_OFFSET, +- AT803X_LOC_MAC_ADDR_0_15_OFFSET, +- }; +- +- if (!ndev) +- return -ENODEV; +- +- mac = (const u8 *)ndev->dev_addr; +- +- if (!is_valid_ether_addr(mac)) +- return -EINVAL; +- +- for (i = 0; i < 3; i++) +- phy_write_mmd(phydev, MDIO_MMD_PCS, offsets[i], +- mac[(i * 2) + 1] | (mac[(i * 2)] << 8)); +- +- /* Enable WOL interrupt */ +- ret = phy_modify(phydev, AT803X_INTR_ENABLE, 0, AT803X_INTR_ENABLE_WOL); +- if (ret) +- return ret; +- } else { +- /* Disable WOL interrupt */ +- ret = phy_modify(phydev, AT803X_INTR_ENABLE, AT803X_INTR_ENABLE_WOL, 0); +- if (ret) +- return ret; +- } +- +- /* Clear WOL status */ +- ret = phy_read(phydev, AT803X_INTR_STATUS); +- if (ret < 0) +- return ret; +- +- /* Check if there are other interrupts except for WOL triggered when PHY is +- * in interrupt mode, only the interrupts enabled by AT803X_INTR_ENABLE can +- * be passed up to the interrupt PIN. +- */ +- irq_enabled = phy_read(phydev, AT803X_INTR_ENABLE); +- if (irq_enabled < 0) +- return irq_enabled; +- +- irq_enabled &= ~AT803X_INTR_ENABLE_WOL; +- if (ret & irq_enabled && !phy_polling_mode(phydev)) +- phy_trigger_machine(phydev); +- +- return 0; +-} +- +-static void at803x_get_wol(struct phy_device *phydev, +- struct ethtool_wolinfo *wol) +-{ +- int value; +- +- wol->supported = WAKE_MAGIC; +- wol->wolopts = 0; +- +- value = phy_read(phydev, AT803X_INTR_ENABLE); +- if (value < 0) +- return; +- +- if (value & AT803X_INTR_ENABLE_WOL) +- wol->wolopts |= WAKE_MAGIC; +-} +- +-static int qca83xx_get_sset_count(struct phy_device *phydev) +-{ +- return ARRAY_SIZE(qca83xx_hw_stats); +-} +- +-static void qca83xx_get_strings(struct phy_device *phydev, u8 *data) +-{ +- int i; +- +- for (i = 0; i < ARRAY_SIZE(qca83xx_hw_stats); i++) { +- strscpy(data + i * ETH_GSTRING_LEN, +- qca83xx_hw_stats[i].string, ETH_GSTRING_LEN); +- } +-} +- +-static u64 qca83xx_get_stat(struct phy_device *phydev, int i) +-{ +- struct at803x_hw_stat stat = qca83xx_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 qca83xx_get_stats(struct phy_device *phydev, +- struct ethtool_stats *stats, u64 *data) +-{ +- int i; +- +- for (i = 0; i < ARRAY_SIZE(qca83xx_hw_stats); i++) +- data[i] = qca83xx_get_stat(phydev, i); +-} +- +-static int at803x_suspend(struct phy_device *phydev) +-{ +- int value; +- int wol_enabled; +- +- value = phy_read(phydev, AT803X_INTR_ENABLE); +- wol_enabled = value & AT803X_INTR_ENABLE_WOL; +- +- if (wol_enabled) +- value = BMCR_ISOLATE; +- else +- value = BMCR_PDOWN; +- +- phy_modify(phydev, MII_BMCR, 0, value); +- +- return 0; +-} +- +-static int at803x_resume(struct phy_device *phydev) +-{ +- return phy_modify(phydev, MII_BMCR, BMCR_PDOWN | BMCR_ISOLATE, 0); +-} +- +-static int at803x_parse_dt(struct phy_device *phydev) +-{ +- struct device_node *node = phydev->mdio.dev.of_node; +- struct at803x_priv *priv = phydev->priv; +- u32 freq, strength, tw; +- unsigned int sel; +- int ret; +- +- if (!IS_ENABLED(CONFIG_OF_MDIO)) +- return 0; +- +- if (of_property_read_bool(node, "qca,disable-smarteee")) +- priv->flags |= AT803X_DISABLE_SMARTEEE; +- +- if (of_property_read_bool(node, "qca,disable-hibernation-mode")) +- priv->flags |= AT803X_DISABLE_HIBERNATION_MODE; +- +- if (!of_property_read_u32(node, "qca,smarteee-tw-us-1g", &tw)) { +- if (!tw || tw > 255) { +- phydev_err(phydev, "invalid qca,smarteee-tw-us-1g\n"); +- return -EINVAL; +- } +- priv->smarteee_lpi_tw_1g = tw; +- } +- +- if (!of_property_read_u32(node, "qca,smarteee-tw-us-100m", &tw)) { +- if (!tw || tw > 255) { +- phydev_err(phydev, "invalid qca,smarteee-tw-us-100m\n"); +- return -EINVAL; +- } +- priv->smarteee_lpi_tw_100m = tw; +- } +- +- ret = of_property_read_u32(node, "qca,clk-out-frequency", &freq); +- if (!ret) { +- switch (freq) { +- case 25000000: +- sel = AT803X_CLK_OUT_25MHZ_XTAL; +- break; +- case 50000000: +- sel = AT803X_CLK_OUT_50MHZ_PLL; +- break; +- case 62500000: +- sel = AT803X_CLK_OUT_62_5MHZ_PLL; +- break; +- case 125000000: +- sel = AT803X_CLK_OUT_125MHZ_PLL; +- break; +- default: +- phydev_err(phydev, "invalid qca,clk-out-frequency\n"); +- return -EINVAL; +- } +- +- priv->clk_25m_reg |= FIELD_PREP(AT803X_CLK_OUT_MASK, sel); +- priv->clk_25m_mask |= AT803X_CLK_OUT_MASK; +- } +- +- ret = of_property_read_u32(node, "qca,clk-out-strength", &strength); +- if (!ret) { +- priv->clk_25m_mask |= AT803X_CLK_OUT_STRENGTH_MASK; +- switch (strength) { +- case AR803X_STRENGTH_FULL: +- priv->clk_25m_reg |= AT803X_CLK_OUT_STRENGTH_FULL; +- break; +- case AR803X_STRENGTH_HALF: +- priv->clk_25m_reg |= AT803X_CLK_OUT_STRENGTH_HALF; +- break; +- case AR803X_STRENGTH_QUARTER: +- priv->clk_25m_reg |= AT803X_CLK_OUT_STRENGTH_QUARTER; +- break; +- default: +- phydev_err(phydev, "invalid qca,clk-out-strength\n"); +- return -EINVAL; +- } +- } +- +- return 0; +-} +- +-static int at803x_probe(struct phy_device *phydev) +-{ +- struct device *dev = &phydev->mdio.dev; +- struct at803x_priv *priv; +- int ret; +- +- priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); +- if (!priv) +- return -ENOMEM; +- +- /* Init LED polarity mode to -1 */ +- priv->led_polarity_mode = -1; +- +- phydev->priv = priv; +- +- ret = at803x_parse_dt(phydev); +- if (ret) +- return ret; +- +- return 0; +-} +- +-static int at803x_get_features(struct phy_device *phydev) +-{ +- struct at803x_priv *priv = phydev->priv; +- int err; +- +- err = genphy_read_abilities(phydev); +- if (err) +- return err; +- +- if (phydev->drv->phy_id != ATH8031_PHY_ID) +- return 0; +- +- /* AR8031/AR8033 have different status registers +- * for copper and fiber operation. However, the +- * extended status register is the same for both +- * operation modes. +- * +- * As a result of that, ESTATUS_1000_XFULL is set +- * to 1 even when operating in copper TP mode. +- * +- * Remove this mode from the supported link modes +- * when not operating in 1000BaseX mode. +- */ +- if (!priv->is_1000basex) +- linkmode_clear_bit(ETHTOOL_LINK_MODE_1000baseX_Full_BIT, +- phydev->supported); +- +- return 0; +-} +- +-static int at803x_smarteee_config(struct phy_device *phydev) +-{ +- struct at803x_priv *priv = phydev->priv; +- u16 mask = 0, val = 0; +- int ret; +- +- if (priv->flags & AT803X_DISABLE_SMARTEEE) +- return phy_modify_mmd(phydev, MDIO_MMD_PCS, +- AT803X_MMD3_SMARTEEE_CTL3, +- AT803X_MMD3_SMARTEEE_CTL3_LPI_EN, 0); +- +- if (priv->smarteee_lpi_tw_1g) { +- mask |= 0xff00; +- val |= priv->smarteee_lpi_tw_1g << 8; +- } +- if (priv->smarteee_lpi_tw_100m) { +- mask |= 0x00ff; +- val |= priv->smarteee_lpi_tw_100m; +- } +- if (!mask) +- return 0; +- +- ret = phy_modify_mmd(phydev, MDIO_MMD_PCS, AT803X_MMD3_SMARTEEE_CTL1, +- mask, val); +- if (ret) +- return ret; +- +- return phy_modify_mmd(phydev, MDIO_MMD_PCS, AT803X_MMD3_SMARTEEE_CTL3, +- AT803X_MMD3_SMARTEEE_CTL3_LPI_EN, +- AT803X_MMD3_SMARTEEE_CTL3_LPI_EN); +-} +- +-static int at803x_clk_out_config(struct phy_device *phydev) +-{ +- struct at803x_priv *priv = phydev->priv; +- +- if (!priv->clk_25m_mask) +- return 0; +- +- return phy_modify_mmd(phydev, MDIO_MMD_AN, AT803X_MMD7_CLK25M, +- priv->clk_25m_mask, priv->clk_25m_reg); +-} +- +-static int at8031_pll_config(struct phy_device *phydev) +-{ +- struct at803x_priv *priv = phydev->priv; +- +- /* The default after hardware reset is PLL OFF. After a soft reset, the +- * values are retained. +- */ +- if (priv->flags & AT803X_KEEP_PLL_ENABLED) +- return at803x_debug_reg_mask(phydev, AT803X_DEBUG_REG_1F, +- 0, AT803X_DEBUG_PLL_ON); +- else +- return at803x_debug_reg_mask(phydev, AT803X_DEBUG_REG_1F, +- AT803X_DEBUG_PLL_ON, 0); +-} +- +-static int at803x_hibernation_mode_config(struct phy_device *phydev) +-{ +- struct at803x_priv *priv = phydev->priv; +- +- /* The default after hardware reset is hibernation mode enabled. After +- * software reset, the value is retained. +- */ +- if (!(priv->flags & AT803X_DISABLE_HIBERNATION_MODE)) +- return 0; +- +- return at803x_debug_reg_mask(phydev, AT803X_DEBUG_REG_HIB_CTRL, +- AT803X_DEBUG_HIB_CTRL_PS_HIB_EN, 0); +-} +- +-static int at803x_config_init(struct phy_device *phydev) +-{ +- int ret; +- +- /* The RX and TX delay default is: +- * after HW reset: RX delay enabled and TX delay disabled +- * after SW reset: RX delay enabled, while TX delay retains the +- * value before reset. +- */ +- if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID || +- phydev->interface == PHY_INTERFACE_MODE_RGMII_RXID) +- ret = at803x_enable_rx_delay(phydev); +- else +- ret = at803x_disable_rx_delay(phydev); +- if (ret < 0) +- return ret; +- +- if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID || +- phydev->interface == PHY_INTERFACE_MODE_RGMII_TXID) +- ret = at803x_enable_tx_delay(phydev); +- else +- ret = at803x_disable_tx_delay(phydev); +- if (ret < 0) +- return ret; +- +- ret = at803x_smarteee_config(phydev); +- if (ret < 0) +- return ret; +- +- ret = at803x_clk_out_config(phydev); +- if (ret < 0) +- return ret; +- +- ret = at803x_hibernation_mode_config(phydev); +- if (ret < 0) +- return ret; +- +- /* Ar803x extended next page bit is enabled by default. Cisco +- * multigig switches read this bit and attempt to negotiate 10Gbps +- * rates even if the next page bit is disabled. This is incorrect +- * behaviour but we still need to accommodate it. XNP is only needed +- * for 10Gbps support, so disable XNP. +- */ +- return phy_modify(phydev, MII_ADVERTISE, MDIO_AN_CTRL1_XNP, 0); +-} +- +-static int at803x_ack_interrupt(struct phy_device *phydev) +-{ +- int err; +- +- err = phy_read(phydev, AT803X_INTR_STATUS); +- +- return (err < 0) ? err : 0; +-} +- +-static int at803x_config_intr(struct phy_device *phydev) +-{ +- int err; +- int value; +- +- value = phy_read(phydev, AT803X_INTR_ENABLE); +- +- if (phydev->interrupts == PHY_INTERRUPT_ENABLED) { +- /* Clear any pending interrupts */ +- err = at803x_ack_interrupt(phydev); +- if (err) +- return err; +- +- value |= AT803X_INTR_ENABLE_AUTONEG_ERR; +- value |= AT803X_INTR_ENABLE_SPEED_CHANGED; +- value |= AT803X_INTR_ENABLE_DUPLEX_CHANGED; +- value |= AT803X_INTR_ENABLE_LINK_FAIL; +- value |= AT803X_INTR_ENABLE_LINK_SUCCESS; +- +- err = phy_write(phydev, AT803X_INTR_ENABLE, value); +- } else { +- err = phy_write(phydev, AT803X_INTR_ENABLE, 0); +- if (err) +- return err; +- +- /* Clear any pending interrupts */ +- err = at803x_ack_interrupt(phydev); +- } +- +- return err; +-} +- +-static irqreturn_t at803x_handle_interrupt(struct phy_device *phydev) +-{ +- int irq_status, int_enabled; +- +- irq_status = phy_read(phydev, AT803X_INTR_STATUS); +- if (irq_status < 0) { +- phy_error(phydev); +- return IRQ_NONE; +- } +- +- /* Read the current enabled interrupts */ +- int_enabled = phy_read(phydev, AT803X_INTR_ENABLE); +- if (int_enabled < 0) { +- phy_error(phydev); +- return IRQ_NONE; +- } +- +- /* See if this was one of our enabled interrupts */ +- if (!(irq_status & int_enabled)) +- return IRQ_NONE; +- +- phy_trigger_machine(phydev); +- +- return IRQ_HANDLED; +-} +- +-static void at803x_link_change_notify(struct phy_device *phydev) +-{ +- /* +- * Conduct a hardware reset for AT8030 every time a link loss is +- * signalled. This is necessary to circumvent a hardware bug that +- * occurs when the cable is unplugged while TX packets are pending +- * in the FIFO. In such cases, the FIFO enters an error mode it +- * cannot recover from by software. +- */ +- if (phydev->state == PHY_NOLINK && phydev->mdio.reset_gpio) { +- struct at803x_context context; +- +- at803x_context_save(phydev, &context); +- +- phy_device_reset(phydev, 1); +- usleep_range(1000, 2000); +- phy_device_reset(phydev, 0); +- usleep_range(1000, 2000); +- +- at803x_context_restore(phydev, &context); +- +- phydev_dbg(phydev, "%s(): phy was reset\n", __func__); +- } +-} +- +-static int at803x_read_specific_status(struct phy_device *phydev, +- struct at803x_ss_mask ss_mask) +-{ +- int ss; +- +- /* Read the AT8035 PHY-Specific Status register, which indicates the +- * speed and duplex that the PHY is actually using, irrespective of +- * whether we are in autoneg mode or not. +- */ +- ss = phy_read(phydev, AT803X_SPECIFIC_STATUS); +- if (ss < 0) +- return ss; +- +- if (ss & AT803X_SS_SPEED_DUPLEX_RESOLVED) { +- int sfc, speed; +- +- sfc = phy_read(phydev, AT803X_SPECIFIC_FUNCTION_CONTROL); +- if (sfc < 0) +- return sfc; +- +- speed = ss & ss_mask.speed_mask; +- speed >>= ss_mask.speed_shift; +- +- switch (speed) { +- case AT803X_SS_SPEED_10: +- phydev->speed = SPEED_10; +- break; +- case AT803X_SS_SPEED_100: +- phydev->speed = SPEED_100; +- break; +- case AT803X_SS_SPEED_1000: +- phydev->speed = SPEED_1000; +- break; +- case QCA808X_SS_SPEED_2500: +- phydev->speed = SPEED_2500; +- break; +- } +- if (ss & AT803X_SS_DUPLEX) +- phydev->duplex = DUPLEX_FULL; +- else +- phydev->duplex = DUPLEX_HALF; +- +- if (ss & AT803X_SS_MDIX) +- phydev->mdix = ETH_TP_MDI_X; +- else +- phydev->mdix = ETH_TP_MDI; +- +- switch (FIELD_GET(AT803X_SFC_MDI_CROSSOVER_MODE_M, sfc)) { +- case AT803X_SFC_MANUAL_MDI: +- phydev->mdix_ctrl = ETH_TP_MDI; +- break; +- case AT803X_SFC_MANUAL_MDIX: +- phydev->mdix_ctrl = ETH_TP_MDI_X; +- break; +- case AT803X_SFC_AUTOMATIC_CROSSOVER: +- phydev->mdix_ctrl = ETH_TP_MDI_AUTO; +- break; +- } +- } +- +- return 0; +-} +- +-static int at803x_read_status(struct phy_device *phydev) +-{ +- struct at803x_ss_mask ss_mask = { 0 }; +- int err, old_link = phydev->link; +- +- /* Update the link, but return if there was an error */ +- err = genphy_update_link(phydev); +- if (err) +- return err; +- +- /* why bother the PHY if nothing can have changed */ +- if (phydev->autoneg == AUTONEG_ENABLE && old_link && phydev->link) +- return 0; +- +- phydev->speed = SPEED_UNKNOWN; +- phydev->duplex = DUPLEX_UNKNOWN; +- phydev->pause = 0; +- phydev->asym_pause = 0; +- +- err = genphy_read_lpa(phydev); +- if (err < 0) +- return err; +- +- ss_mask.speed_mask = AT803X_SS_SPEED_MASK; +- ss_mask.speed_shift = __bf_shf(AT803X_SS_SPEED_MASK); +- err = at803x_read_specific_status(phydev, ss_mask); +- if (err < 0) +- return err; +- +- if (phydev->autoneg == AUTONEG_ENABLE && phydev->autoneg_complete) +- phy_resolve_aneg_pause(phydev); +- +- return 0; +-} +- +-static int at803x_config_mdix(struct phy_device *phydev, u8 ctrl) +-{ +- u16 val; +- +- switch (ctrl) { +- case ETH_TP_MDI: +- val = AT803X_SFC_MANUAL_MDI; +- break; +- case ETH_TP_MDI_X: +- val = AT803X_SFC_MANUAL_MDIX; +- break; +- case ETH_TP_MDI_AUTO: +- val = AT803X_SFC_AUTOMATIC_CROSSOVER; +- break; +- default: +- return 0; +- } +- +- return phy_modify_changed(phydev, AT803X_SPECIFIC_FUNCTION_CONTROL, +- AT803X_SFC_MDI_CROSSOVER_MODE_M, +- FIELD_PREP(AT803X_SFC_MDI_CROSSOVER_MODE_M, val)); +-} +- +-static int at803x_prepare_config_aneg(struct phy_device *phydev) +-{ +- int ret; +- +- ret = at803x_config_mdix(phydev, phydev->mdix_ctrl); +- if (ret < 0) +- return ret; +- +- /* Changes of the midx bits are disruptive to the normal operation; +- * therefore any changes to these registers must be followed by a +- * software reset to take effect. +- */ +- if (ret == 1) { +- ret = genphy_soft_reset(phydev); +- if (ret < 0) +- return ret; +- } +- +- return 0; +-} +- +-static int at803x_config_aneg(struct phy_device *phydev) +-{ +- struct at803x_priv *priv = phydev->priv; +- int ret; +- +- ret = at803x_prepare_config_aneg(phydev); +- if (ret) +- return ret; +- +- if (priv->is_1000basex) +- return genphy_c37_config_aneg(phydev); +- +- return genphy_config_aneg(phydev); +-} +- +-static int at803x_get_downshift(struct phy_device *phydev, u8 *d) +-{ +- int val; +- +- val = phy_read(phydev, AT803X_SMART_SPEED); +- if (val < 0) +- return val; +- +- if (val & AT803X_SMART_SPEED_ENABLE) +- *d = FIELD_GET(AT803X_SMART_SPEED_RETRY_LIMIT_MASK, val) + 2; +- else +- *d = DOWNSHIFT_DEV_DISABLE; +- +- return 0; +-} +- +-static int at803x_set_downshift(struct phy_device *phydev, u8 cnt) +-{ +- u16 mask, set; +- int ret; +- +- switch (cnt) { +- case DOWNSHIFT_DEV_DEFAULT_COUNT: +- cnt = AT803X_DEFAULT_DOWNSHIFT; +- fallthrough; +- case AT803X_MIN_DOWNSHIFT ... AT803X_MAX_DOWNSHIFT: +- set = AT803X_SMART_SPEED_ENABLE | +- AT803X_SMART_SPEED_BYPASS_TIMER | +- FIELD_PREP(AT803X_SMART_SPEED_RETRY_LIMIT_MASK, cnt - 2); +- mask = AT803X_SMART_SPEED_RETRY_LIMIT_MASK; +- break; +- case DOWNSHIFT_DEV_DISABLE: +- set = 0; +- mask = AT803X_SMART_SPEED_ENABLE | +- AT803X_SMART_SPEED_BYPASS_TIMER; +- break; +- default: +- return -EINVAL; +- } +- +- ret = phy_modify_changed(phydev, AT803X_SMART_SPEED, mask, set); +- +- /* After changing the smart speed settings, we need to perform a +- * software reset, use phy_init_hw() to make sure we set the +- * reapply any values which might got lost during software reset. +- */ +- if (ret == 1) +- ret = phy_init_hw(phydev); +- +- return ret; +-} +- +-static int at803x_get_tunable(struct phy_device *phydev, +- struct ethtool_tunable *tuna, void *data) +-{ +- switch (tuna->id) { +- case ETHTOOL_PHY_DOWNSHIFT: +- return at803x_get_downshift(phydev, data); +- default: +- return -EOPNOTSUPP; +- } +-} +- +-static int at803x_set_tunable(struct phy_device *phydev, +- struct ethtool_tunable *tuna, const void *data) +-{ +- switch (tuna->id) { +- case ETHTOOL_PHY_DOWNSHIFT: +- return at803x_set_downshift(phydev, *(const u8 *)data); +- default: +- return -EOPNOTSUPP; +- } +-} +- +-static int at803x_cable_test_result_trans(u16 status) +-{ +- switch (FIELD_GET(AT803X_CDT_STATUS_STAT_MASK, status)) { +- case AT803X_CDT_STATUS_STAT_NORMAL: +- return ETHTOOL_A_CABLE_RESULT_CODE_OK; +- case AT803X_CDT_STATUS_STAT_SHORT: +- return ETHTOOL_A_CABLE_RESULT_CODE_SAME_SHORT; +- case AT803X_CDT_STATUS_STAT_OPEN: +- return ETHTOOL_A_CABLE_RESULT_CODE_OPEN; +- case AT803X_CDT_STATUS_STAT_FAIL: +- default: +- return ETHTOOL_A_CABLE_RESULT_CODE_UNSPEC; +- } +-} +- +-static bool at803x_cdt_test_failed(u16 status) +-{ +- return FIELD_GET(AT803X_CDT_STATUS_STAT_MASK, status) == +- AT803X_CDT_STATUS_STAT_FAIL; +-} +- +-static bool at803x_cdt_fault_length_valid(u16 status) +-{ +- switch (FIELD_GET(AT803X_CDT_STATUS_STAT_MASK, status)) { +- case AT803X_CDT_STATUS_STAT_OPEN: +- case AT803X_CDT_STATUS_STAT_SHORT: +- return true; +- } +- return false; +-} +- +-static int at803x_cdt_fault_length(int dt) +-{ +- /* According to the datasheet the distance to the fault is +- * DELTA_TIME * 0.824 meters. +- * +- * The author suspect the correct formula is: +- * +- * fault_distance = DELTA_TIME * (c * VF) / 125MHz / 2 +- * +- * where c is the speed of light, VF is the velocity factor of +- * the twisted pair cable, 125MHz the counter frequency and +- * we need to divide by 2 because the hardware will measure the +- * round trip time to the fault and back to the PHY. +- * +- * With a VF of 0.69 we get the factor 0.824 mentioned in the +- * datasheet. +- */ +- return (dt * 824) / 10; +-} +- +-static int at803x_cdt_start(struct phy_device *phydev, +- u32 cdt_start) +-{ +- return phy_write(phydev, AT803X_CDT, cdt_start); +-} +- +-static int at803x_cdt_wait_for_completion(struct phy_device *phydev, +- u32 cdt_en) +-{ +- int val, ret; +- +- /* One test run takes about 25ms */ +- ret = phy_read_poll_timeout(phydev, AT803X_CDT, val, +- !(val & cdt_en), +- 30000, 100000, true); +- +- return ret < 0 ? ret : 0; +-} +- +-static int at803x_cable_test_one_pair(struct phy_device *phydev, int pair) +-{ +- static const int ethtool_pair[] = { +- ETHTOOL_A_CABLE_PAIR_A, +- ETHTOOL_A_CABLE_PAIR_B, +- ETHTOOL_A_CABLE_PAIR_C, +- ETHTOOL_A_CABLE_PAIR_D, +- }; +- int ret, val; +- +- val = FIELD_PREP(AT803X_CDT_MDI_PAIR_MASK, pair) | +- AT803X_CDT_ENABLE_TEST; +- ret = at803x_cdt_start(phydev, val); +- if (ret) +- return ret; +- +- ret = at803x_cdt_wait_for_completion(phydev, AT803X_CDT_ENABLE_TEST); +- if (ret) +- return ret; +- +- val = phy_read(phydev, AT803X_CDT_STATUS); +- if (val < 0) +- return val; +- +- if (at803x_cdt_test_failed(val)) +- return 0; +- +- ethnl_cable_test_result(phydev, ethtool_pair[pair], +- at803x_cable_test_result_trans(val)); +- +- if (at803x_cdt_fault_length_valid(val)) { +- val = FIELD_GET(AT803X_CDT_STATUS_DELTA_TIME_MASK, val); +- ethnl_cable_test_fault_length(phydev, ethtool_pair[pair], +- at803x_cdt_fault_length(val)); +- } +- +- return 1; +-} +- +-static int at803x_cable_test_get_status(struct phy_device *phydev, +- bool *finished, unsigned long pair_mask) +-{ +- int retries = 20; +- int pair, ret; +- +- *finished = false; +- +- /* According to the datasheet the CDT can be performed when +- * there is no link partner or when the link partner is +- * auto-negotiating. Starting the test will restart the AN +- * automatically. It seems that doing this repeatedly we will +- * get a slot where our link partner won't disturb our +- * measurement. +- */ +- while (pair_mask && retries--) { +- for_each_set_bit(pair, &pair_mask, 4) { +- ret = at803x_cable_test_one_pair(phydev, pair); +- if (ret < 0) +- return ret; +- if (ret) +- clear_bit(pair, &pair_mask); +- } +- if (pair_mask) +- msleep(250); +- } +- +- *finished = true; +- +- return 0; +-} +- +-static void at803x_cable_test_autoneg(struct phy_device *phydev) +-{ +- /* Enable auto-negotiation, but advertise no capabilities, no link +- * will be established. A restart of the auto-negotiation is not +- * required, because the cable test will automatically break the link. +- */ +- phy_write(phydev, MII_BMCR, BMCR_ANENABLE); +- phy_write(phydev, MII_ADVERTISE, ADVERTISE_CSMA); +-} +- +-static int at803x_cable_test_start(struct phy_device *phydev) +-{ +- at803x_cable_test_autoneg(phydev); +- /* we do all the (time consuming) work later */ +- return 0; +-} +- +-static int at8031_rgmii_reg_set_voltage_sel(struct regulator_dev *rdev, +- unsigned int selector) +-{ +- struct phy_device *phydev = rdev_get_drvdata(rdev); +- +- if (selector) +- return at803x_debug_reg_mask(phydev, AT803X_DEBUG_REG_1F, +- 0, AT803X_DEBUG_RGMII_1V8); +- else +- return at803x_debug_reg_mask(phydev, AT803X_DEBUG_REG_1F, +- AT803X_DEBUG_RGMII_1V8, 0); +-} +- +-static int at8031_rgmii_reg_get_voltage_sel(struct regulator_dev *rdev) +-{ +- struct phy_device *phydev = rdev_get_drvdata(rdev); +- int val; +- +- val = at803x_debug_reg_read(phydev, AT803X_DEBUG_REG_1F); +- if (val < 0) +- return val; +- +- return (val & AT803X_DEBUG_RGMII_1V8) ? 1 : 0; +-} +- +-static const struct regulator_ops vddio_regulator_ops = { +- .list_voltage = regulator_list_voltage_table, +- .set_voltage_sel = at8031_rgmii_reg_set_voltage_sel, +- .get_voltage_sel = at8031_rgmii_reg_get_voltage_sel, +-}; +- +-static const unsigned int vddio_voltage_table[] = { +- 1500000, +- 1800000, +-}; +- +-static const struct regulator_desc vddio_desc = { +- .name = "vddio", +- .of_match = of_match_ptr("vddio-regulator"), +- .n_voltages = ARRAY_SIZE(vddio_voltage_table), +- .volt_table = vddio_voltage_table, +- .ops = &vddio_regulator_ops, +- .type = REGULATOR_VOLTAGE, +- .owner = THIS_MODULE, +-}; +- +-static const struct regulator_ops vddh_regulator_ops = { +-}; +- +-static const struct regulator_desc vddh_desc = { +- .name = "vddh", +- .of_match = of_match_ptr("vddh-regulator"), +- .n_voltages = 1, +- .fixed_uV = 2500000, +- .ops = &vddh_regulator_ops, +- .type = REGULATOR_VOLTAGE, +- .owner = THIS_MODULE, +-}; +- +-static int at8031_register_regulators(struct phy_device *phydev) +-{ +- struct at803x_priv *priv = phydev->priv; +- struct device *dev = &phydev->mdio.dev; +- struct regulator_config config = { }; +- +- config.dev = dev; +- config.driver_data = phydev; +- +- priv->vddio_rdev = devm_regulator_register(dev, &vddio_desc, &config); +- if (IS_ERR(priv->vddio_rdev)) { +- phydev_err(phydev, "failed to register VDDIO regulator\n"); +- return PTR_ERR(priv->vddio_rdev); +- } +- +- priv->vddh_rdev = devm_regulator_register(dev, &vddh_desc, &config); +- if (IS_ERR(priv->vddh_rdev)) { +- phydev_err(phydev, "failed to register VDDH regulator\n"); +- return PTR_ERR(priv->vddh_rdev); +- } +- +- return 0; +-} +- +-static int at8031_sfp_insert(void *upstream, const struct sfp_eeprom_id *id) +-{ +- struct phy_device *phydev = upstream; +- __ETHTOOL_DECLARE_LINK_MODE_MASK(phy_support); +- __ETHTOOL_DECLARE_LINK_MODE_MASK(sfp_support); +- DECLARE_PHY_INTERFACE_MASK(interfaces); +- phy_interface_t iface; +- +- linkmode_zero(phy_support); +- phylink_set(phy_support, 1000baseX_Full); +- phylink_set(phy_support, 1000baseT_Full); +- phylink_set(phy_support, Autoneg); +- phylink_set(phy_support, Pause); +- phylink_set(phy_support, Asym_Pause); +- +- linkmode_zero(sfp_support); +- sfp_parse_support(phydev->sfp_bus, id, sfp_support, interfaces); +- /* Some modules support 10G modes as well as others we support. +- * Mask out non-supported modes so the correct interface is picked. +- */ +- linkmode_and(sfp_support, phy_support, sfp_support); +- +- if (linkmode_empty(sfp_support)) { +- dev_err(&phydev->mdio.dev, "incompatible SFP module inserted\n"); +- return -EINVAL; +- } +- +- iface = sfp_select_interface(phydev->sfp_bus, sfp_support); +- +- /* Only 1000Base-X is supported by AR8031/8033 as the downstream SerDes +- * interface for use with SFP modules. +- * However, some copper modules detected as having a preferred SGMII +- * interface do default to and function in 1000Base-X mode, so just +- * print a warning and allow such modules, as they may have some chance +- * of working. +- */ +- if (iface == PHY_INTERFACE_MODE_SGMII) +- dev_warn(&phydev->mdio.dev, "module may not function if 1000Base-X not supported\n"); +- else if (iface != PHY_INTERFACE_MODE_1000BASEX) +- return -EINVAL; +- +- return 0; +-} +- +-static const struct sfp_upstream_ops at8031_sfp_ops = { +- .attach = phy_sfp_attach, +- .detach = phy_sfp_detach, +- .module_insert = at8031_sfp_insert, +-}; +- +-static int at8031_parse_dt(struct phy_device *phydev) +-{ +- struct device_node *node = phydev->mdio.dev.of_node; +- struct at803x_priv *priv = phydev->priv; +- int ret; +- +- if (of_property_read_bool(node, "qca,keep-pll-enabled")) +- priv->flags |= AT803X_KEEP_PLL_ENABLED; +- +- ret = at8031_register_regulators(phydev); +- if (ret < 0) +- return ret; +- +- ret = devm_regulator_get_enable_optional(&phydev->mdio.dev, +- "vddio"); +- if (ret) { +- phydev_err(phydev, "failed to get VDDIO regulator\n"); +- return ret; +- } +- +- /* Only AR8031/8033 support 1000Base-X for SFP modules */ +- return phy_sfp_probe(phydev, &at8031_sfp_ops); +-} +- +-static int at8031_probe(struct phy_device *phydev) +-{ +- struct at803x_priv *priv = phydev->priv; +- int mode_cfg; +- int ccr; +- int ret; +- +- ret = at803x_probe(phydev); +- if (ret) +- return ret; +- +- /* Only supported on AR8031/AR8033, the AR8030/AR8035 use strapping +- * options. +- */ +- ret = at8031_parse_dt(phydev); +- if (ret) +- return ret; +- +- ccr = phy_read(phydev, AT803X_REG_CHIP_CONFIG); +- if (ccr < 0) +- return ccr; +- mode_cfg = ccr & AT803X_MODE_CFG_MASK; +- +- switch (mode_cfg) { +- case AT803X_MODE_CFG_BX1000_RGMII_50OHM: +- case AT803X_MODE_CFG_BX1000_RGMII_75OHM: +- priv->is_1000basex = true; +- fallthrough; +- case AT803X_MODE_CFG_FX100_RGMII_50OHM: +- case AT803X_MODE_CFG_FX100_RGMII_75OHM: +- priv->is_fiber = true; +- break; +- } +- +- /* Disable WoL in 1588 register which is enabled +- * by default +- */ +- return phy_modify_mmd(phydev, MDIO_MMD_PCS, +- AT803X_PHY_MMD3_WOL_CTRL, +- AT803X_WOL_EN, 0); +-} +- +-static int at8031_config_init(struct phy_device *phydev) +-{ +- struct at803x_priv *priv = phydev->priv; +- int ret; +- +- /* Some bootloaders leave the fiber page selected. +- * Switch to the appropriate page (fiber or copper), as otherwise we +- * read the PHY capabilities from the wrong page. +- */ +- phy_lock_mdio_bus(phydev); +- ret = at803x_write_page(phydev, +- priv->is_fiber ? AT803X_PAGE_FIBER : +- AT803X_PAGE_COPPER); +- phy_unlock_mdio_bus(phydev); +- if (ret) +- return ret; +- +- ret = at8031_pll_config(phydev); +- if (ret < 0) +- return ret; +- +- return at803x_config_init(phydev); +-} +- +-static int at8031_set_wol(struct phy_device *phydev, +- struct ethtool_wolinfo *wol) +-{ +- int ret; +- +- /* First setup MAC address and enable WOL interrupt */ +- ret = at803x_set_wol(phydev, wol); +- if (ret) +- return ret; +- +- if (wol->wolopts & WAKE_MAGIC) +- /* Enable WOL function for 1588 */ +- ret = phy_modify_mmd(phydev, MDIO_MMD_PCS, +- AT803X_PHY_MMD3_WOL_CTRL, +- 0, AT803X_WOL_EN); +- else +- /* Disable WoL function for 1588 */ +- ret = phy_modify_mmd(phydev, MDIO_MMD_PCS, +- AT803X_PHY_MMD3_WOL_CTRL, +- AT803X_WOL_EN, 0); +- +- return ret; +-} +- +-static int at8031_config_intr(struct phy_device *phydev) +-{ +- struct at803x_priv *priv = phydev->priv; +- int err, value = 0; +- +- if (phydev->interrupts == PHY_INTERRUPT_ENABLED && +- priv->is_fiber) { +- /* Clear any pending interrupts */ +- err = at803x_ack_interrupt(phydev); +- if (err) +- return err; +- +- value |= AT803X_INTR_ENABLE_LINK_FAIL_BX; +- value |= AT803X_INTR_ENABLE_LINK_SUCCESS_BX; +- +- err = phy_set_bits(phydev, AT803X_INTR_ENABLE, value); +- if (err) +- return err; +- } +- +- return at803x_config_intr(phydev); +-} +- +-/* AR8031 and AR8033 share the same read status logic */ +-static int at8031_read_status(struct phy_device *phydev) +-{ +- struct at803x_priv *priv = phydev->priv; +- +- if (priv->is_1000basex) +- return genphy_c37_read_status(phydev); +- +- return at803x_read_status(phydev); +-} +- +-/* AR8031 and AR8035 share the same cable test get status reg */ +-static int at8031_cable_test_get_status(struct phy_device *phydev, +- bool *finished) +-{ +- return at803x_cable_test_get_status(phydev, finished, 0xf); +-} +- +-/* AR8031 and AR8035 share the same cable test start logic */ +-static int at8031_cable_test_start(struct phy_device *phydev) +-{ +- at803x_cable_test_autoneg(phydev); +- phy_write(phydev, MII_CTRL1000, 0); +- /* we do all the (time consuming) work later */ +- return 0; +-} +- +-/* AR8032, AR9331 and QCA9561 share the same cable test get status reg */ +-static int at8032_cable_test_get_status(struct phy_device *phydev, +- bool *finished) +-{ +- return at803x_cable_test_get_status(phydev, finished, 0x3); +-} +- +-static int at8035_parse_dt(struct phy_device *phydev) +-{ +- struct at803x_priv *priv = phydev->priv; +- +- /* Mask is set by the generic at803x_parse_dt +- * if property is set. Assume property is set +- * with the mask not zero. +- */ +- if (priv->clk_25m_mask) { +- /* Fixup for the AR8030/AR8035. This chip has another mask and +- * doesn't support the DSP reference. Eg. the lowest bit of the +- * mask. The upper two bits select the same frequencies. Mask +- * the lowest bit here. +- * +- * Warning: +- * There was no datasheet for the AR8030 available so this is +- * just a guess. But the AR8035 is listed as pin compatible +- * to the AR8030 so there might be a good chance it works on +- * the AR8030 too. +- */ +- priv->clk_25m_reg &= AT8035_CLK_OUT_MASK; +- priv->clk_25m_mask &= AT8035_CLK_OUT_MASK; +- } +- +- return 0; +-} +- +-/* AR8030 and AR8035 shared the same special mask for clk_25m */ +-static int at8035_probe(struct phy_device *phydev) +-{ +- int ret; +- +- ret = at803x_probe(phydev); +- if (ret) +- return ret; +- +- return at8035_parse_dt(phydev); +-} +- +-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_ANALOG_TEST_CTRL, 0x02ea); +- /* Turn on Gigabit clock */ +- at803x_debug_reg_write(phydev, AT803X_DEBUG_REG_GREEN, 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_GREEN, 0x6860); +- at803x_debug_reg_write(phydev, AT803X_DEBUG_SYSTEM_CTRL_MODE, 0x2c46); +- at803x_debug_reg_write(phydev, AT803X_DEBUG_REG_3C, 0x6000); +- break; +- } +- +- /* Following original QCA sourcecode set port to prefer master */ +- phy_set_bits(phydev, MII_CTRL1000, CTL1000_PREFER_MASTER); +- +- return 0; +-} +- +-static int qca8327_config_init(struct phy_device *phydev) +-{ +- /* QCA8327 require DAC amplitude adjustment for 100m set to +6%. +- * Disable on init and enable only with 100m speed following +- * qca original source code. +- */ +- at803x_debug_reg_mask(phydev, AT803X_DEBUG_ANALOG_TEST_CTRL, +- QCA8327_DEBUG_MANU_CTRL_EN, 0); +- +- return qca83xx_config_init(phydev); +-} +- +-static void qca83xx_link_change_notify(struct phy_device *phydev) +-{ +- /* 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_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_ANALOG_TEST_CTRL, +- QCA8327_DEBUG_MANU_CTRL_EN, 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; +- +- usleep_range(1000, 2000); +- +- return 0; +-} +- +-static int qca83xx_suspend(struct phy_device *phydev) +-{ +- 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, +- AT803X_DEBUG_HIB_CTRL_EN_ANY_CHANGE | +- AT803X_DEBUG_HIB_CTRL_SEL_RST_80U, 0); +- +- return 0; +-} +- +-static int qca8337_suspend(struct phy_device *phydev) +-{ +- /* Only QCA8337 support actual suspend. */ +- genphy_suspend(phydev); +- +- return qca83xx_suspend(phydev); +-} +- +-static int qca8327_suspend(struct phy_device *phydev) +-{ +- u16 mask = 0; +- +- /* QCA8327 cause port unreliability when phy suspend +- * is set. +- */ +- mask |= ~(BMCR_SPEED1000 | BMCR_FULLDPLX); +- phy_modify(phydev, MII_BMCR, mask, 0); +- +- return qca83xx_suspend(phydev); +-} +- +-static int qca808x_phy_fast_retrain_config(struct phy_device *phydev) +-{ +- int ret; +- +- /* Enable fast retrain */ +- ret = genphy_c45_fast_retrain(phydev, true); +- if (ret) +- return ret; +- +- phy_write_mmd(phydev, MDIO_MMD_AN, QCA808X_PHY_MMD7_TOP_OPTION1, +- QCA808X_TOP_OPTION1_DATA); +- phy_write_mmd(phydev, MDIO_MMD_PMAPMD, QCA808X_PHY_MMD1_MSE_THRESHOLD_20DB, +- QCA808X_MSE_THRESHOLD_20DB_VALUE); +- phy_write_mmd(phydev, MDIO_MMD_PMAPMD, QCA808X_PHY_MMD1_MSE_THRESHOLD_17DB, +- QCA808X_MSE_THRESHOLD_17DB_VALUE); +- phy_write_mmd(phydev, MDIO_MMD_PMAPMD, QCA808X_PHY_MMD1_MSE_THRESHOLD_27DB, +- QCA808X_MSE_THRESHOLD_27DB_VALUE); +- phy_write_mmd(phydev, MDIO_MMD_PMAPMD, QCA808X_PHY_MMD1_MSE_THRESHOLD_28DB, +- QCA808X_MSE_THRESHOLD_28DB_VALUE); +- phy_write_mmd(phydev, MDIO_MMD_PCS, QCA808X_PHY_MMD3_DEBUG_1, +- QCA808X_MMD3_DEBUG_1_VALUE); +- phy_write_mmd(phydev, MDIO_MMD_PCS, QCA808X_PHY_MMD3_DEBUG_4, +- QCA808X_MMD3_DEBUG_4_VALUE); +- phy_write_mmd(phydev, MDIO_MMD_PCS, QCA808X_PHY_MMD3_DEBUG_5, +- QCA808X_MMD3_DEBUG_5_VALUE); +- phy_write_mmd(phydev, MDIO_MMD_PCS, QCA808X_PHY_MMD3_DEBUG_3, +- QCA808X_MMD3_DEBUG_3_VALUE); +- phy_write_mmd(phydev, MDIO_MMD_PCS, QCA808X_PHY_MMD3_DEBUG_6, +- QCA808X_MMD3_DEBUG_6_VALUE); +- phy_write_mmd(phydev, MDIO_MMD_PCS, QCA808X_PHY_MMD3_DEBUG_2, +- QCA808X_MMD3_DEBUG_2_VALUE); +- +- return 0; +-} +- +-static int qca808x_phy_ms_seed_enable(struct phy_device *phydev, bool enable) +-{ +- u16 seed_value; +- +- if (!enable) +- return at803x_debug_reg_mask(phydev, QCA808X_PHY_DEBUG_LOCAL_SEED, +- QCA808X_MASTER_SLAVE_SEED_ENABLE, 0); +- +- seed_value = prandom_u32_max(QCA808X_MASTER_SLAVE_SEED_RANGE); +- return at803x_debug_reg_mask(phydev, QCA808X_PHY_DEBUG_LOCAL_SEED, +- QCA808X_MASTER_SLAVE_SEED_CFG | QCA808X_MASTER_SLAVE_SEED_ENABLE, +- FIELD_PREP(QCA808X_MASTER_SLAVE_SEED_CFG, seed_value) | +- QCA808X_MASTER_SLAVE_SEED_ENABLE); +-} +- +-static bool qca808x_is_prefer_master(struct phy_device *phydev) +-{ +- return (phydev->master_slave_get == MASTER_SLAVE_CFG_MASTER_FORCE) || +- (phydev->master_slave_get == MASTER_SLAVE_CFG_MASTER_PREFERRED); +-} +- +-static bool qca808x_has_fast_retrain_or_slave_seed(struct phy_device *phydev) +-{ +- return linkmode_test_bit(ETHTOOL_LINK_MODE_2500baseT_Full_BIT, phydev->supported); +-} +- +-static int qca808x_config_init(struct phy_device *phydev) +-{ +- int ret; +- +- /* Active adc&vga on 802.3az for the link 1000M and 100M */ +- ret = phy_modify_mmd(phydev, MDIO_MMD_PCS, QCA808X_PHY_MMD3_ADDR_CLD_CTRL7, +- QCA808X_8023AZ_AFE_CTRL_MASK, QCA808X_8023AZ_AFE_EN); +- if (ret) +- return ret; +- +- /* Adjust the threshold on 802.3az for the link 1000M */ +- ret = phy_write_mmd(phydev, MDIO_MMD_PCS, +- QCA808X_PHY_MMD3_AZ_TRAINING_CTRL, +- QCA808X_MMD3_AZ_TRAINING_VAL); +- if (ret) +- return ret; +- +- if (qca808x_has_fast_retrain_or_slave_seed(phydev)) { +- /* Config the fast retrain for the link 2500M */ +- ret = qca808x_phy_fast_retrain_config(phydev); +- if (ret) +- return ret; +- +- ret = genphy_read_master_slave(phydev); +- if (ret < 0) +- return ret; +- +- if (!qca808x_is_prefer_master(phydev)) { +- /* Enable seed and configure lower ramdom seed to make phy +- * linked as slave mode. +- */ +- ret = qca808x_phy_ms_seed_enable(phydev, true); +- if (ret) +- return ret; +- } +- } +- +- /* Configure adc threshold as 100mv for the link 10M */ +- return at803x_debug_reg_mask(phydev, QCA808X_PHY_DEBUG_ADC_THRESHOLD, +- QCA808X_ADC_THRESHOLD_MASK, +- QCA808X_ADC_THRESHOLD_100MV); +-} +- +-static int qca808x_read_status(struct phy_device *phydev) +-{ +- struct at803x_ss_mask ss_mask = { 0 }; +- int ret; +- +- ret = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_10GBT_STAT); +- if (ret < 0) +- return ret; +- +- linkmode_mod_bit(ETHTOOL_LINK_MODE_2500baseT_Full_BIT, phydev->lp_advertising, +- ret & MDIO_AN_10GBT_STAT_LP2_5G); +- +- ret = genphy_read_status(phydev); +- if (ret) +- return ret; +- +- /* qca8081 takes the different bits for speed value from at803x */ +- ss_mask.speed_mask = QCA808X_SS_SPEED_MASK; +- ss_mask.speed_shift = __bf_shf(QCA808X_SS_SPEED_MASK); +- ret = at803x_read_specific_status(phydev, ss_mask); +- if (ret < 0) +- return ret; +- +- if (phydev->link) { +- if (phydev->speed == SPEED_2500) +- phydev->interface = PHY_INTERFACE_MODE_2500BASEX; +- else +- phydev->interface = PHY_INTERFACE_MODE_SGMII; +- } else { +- /* generate seed as a lower random value to make PHY linked as SLAVE easily, +- * except for master/slave configuration fault detected or the master mode +- * preferred. +- * +- * the reason for not putting this code into the function link_change_notify is +- * the corner case where the link partner is also the qca8081 PHY and the seed +- * value is configured as the same value, the link can't be up and no link change +- * occurs. +- */ +- if (qca808x_has_fast_retrain_or_slave_seed(phydev)) { +- if (phydev->master_slave_state == MASTER_SLAVE_STATE_ERR || +- qca808x_is_prefer_master(phydev)) { +- qca808x_phy_ms_seed_enable(phydev, false); +- } else { +- qca808x_phy_ms_seed_enable(phydev, true); +- } +- } +- } +- +- return 0; +-} +- +-static int qca808x_soft_reset(struct phy_device *phydev) +-{ +- int ret; +- +- ret = genphy_soft_reset(phydev); +- if (ret < 0) +- return ret; +- +- if (qca808x_has_fast_retrain_or_slave_seed(phydev)) +- ret = qca808x_phy_ms_seed_enable(phydev, true); +- +- return ret; +-} +- +-static bool qca808x_cdt_fault_length_valid(int cdt_code) +-{ +- switch (cdt_code) { +- case QCA808X_CDT_STATUS_STAT_SAME_SHORT: +- case QCA808X_CDT_STATUS_STAT_SAME_OPEN: +- case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_NORMAL: +- case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_OPEN: +- case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_SHORT: +- case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_NORMAL: +- case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_OPEN: +- case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_SHORT: +- case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_NORMAL: +- case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_OPEN: +- case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_SHORT: +- return true; +- default: +- return false; +- } +-} +- +-static int qca808x_cable_test_result_trans(int cdt_code) +-{ +- switch (cdt_code) { +- case QCA808X_CDT_STATUS_STAT_NORMAL: +- return ETHTOOL_A_CABLE_RESULT_CODE_OK; +- case QCA808X_CDT_STATUS_STAT_SAME_SHORT: +- return ETHTOOL_A_CABLE_RESULT_CODE_SAME_SHORT; +- case QCA808X_CDT_STATUS_STAT_SAME_OPEN: +- return ETHTOOL_A_CABLE_RESULT_CODE_OPEN; +- case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_NORMAL: +- case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_OPEN: +- case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_SHORT: +- case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_NORMAL: +- case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_OPEN: +- case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_SHORT: +- case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_NORMAL: +- case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_OPEN: +- case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_SHORT: +- return ETHTOOL_A_CABLE_RESULT_CODE_CROSS_SHORT; +- case QCA808X_CDT_STATUS_STAT_FAIL: +- default: +- return ETHTOOL_A_CABLE_RESULT_CODE_UNSPEC; +- } +-} +- +-static int qca808x_cdt_fault_length(struct phy_device *phydev, int pair, +- int result) +-{ +- int val; +- u32 cdt_length_reg = 0; +- +- switch (pair) { +- case ETHTOOL_A_CABLE_PAIR_A: +- cdt_length_reg = QCA808X_MMD3_CDT_DIAG_PAIR_A; +- break; +- case ETHTOOL_A_CABLE_PAIR_B: +- cdt_length_reg = QCA808X_MMD3_CDT_DIAG_PAIR_B; +- break; +- case ETHTOOL_A_CABLE_PAIR_C: +- cdt_length_reg = QCA808X_MMD3_CDT_DIAG_PAIR_C; +- break; +- case ETHTOOL_A_CABLE_PAIR_D: +- cdt_length_reg = QCA808X_MMD3_CDT_DIAG_PAIR_D; +- break; +- default: +- return -EINVAL; +- } +- +- val = phy_read_mmd(phydev, MDIO_MMD_PCS, cdt_length_reg); +- if (val < 0) +- return val; +- +- if (result == ETHTOOL_A_CABLE_RESULT_CODE_SAME_SHORT) +- val = FIELD_GET(QCA808X_CDT_DIAG_LENGTH_SAME_SHORT, val); +- else +- val = FIELD_GET(QCA808X_CDT_DIAG_LENGTH_CROSS_SHORT, val); +- +- return at803x_cdt_fault_length(val); +-} +- +-static int qca808x_cable_test_start(struct phy_device *phydev) +-{ +- int ret; +- +- /* perform CDT with the following configs: +- * 1. disable hibernation. +- * 2. force PHY working in MDI mode. +- * 3. for PHY working in 1000BaseT. +- * 4. configure the threshold. +- */ +- +- ret = at803x_debug_reg_mask(phydev, QCA808X_DBG_AN_TEST, QCA808X_HIBERNATION_EN, 0); +- if (ret < 0) +- return ret; +- +- ret = at803x_config_mdix(phydev, ETH_TP_MDI); +- if (ret < 0) +- return ret; +- +- /* Force 1000base-T needs to configure PMA/PMD and MII_BMCR */ +- phydev->duplex = DUPLEX_FULL; +- phydev->speed = SPEED_1000; +- ret = genphy_c45_pma_setup_forced(phydev); +- if (ret < 0) +- return ret; +- +- ret = genphy_setup_forced(phydev); +- if (ret < 0) +- return ret; +- +- /* configure the thresholds for open, short, pair ok test */ +- phy_write_mmd(phydev, MDIO_MMD_PCS, 0x8074, 0xc040); +- phy_write_mmd(phydev, MDIO_MMD_PCS, 0x8076, 0xc040); +- phy_write_mmd(phydev, MDIO_MMD_PCS, 0x8077, 0xa060); +- phy_write_mmd(phydev, MDIO_MMD_PCS, 0x8078, 0xc050); +- phy_write_mmd(phydev, MDIO_MMD_PCS, 0x807a, 0xc060); +- phy_write_mmd(phydev, MDIO_MMD_PCS, 0x807e, 0xb060); +- +- return 0; +-} +- +-static int qca808x_cable_test_get_pair_status(struct phy_device *phydev, u8 pair, +- u16 status) +-{ +- int length, result; +- u16 pair_code; +- +- switch (pair) { +- case ETHTOOL_A_CABLE_PAIR_A: +- pair_code = FIELD_GET(QCA808X_CDT_CODE_PAIR_A, status); +- break; +- case ETHTOOL_A_CABLE_PAIR_B: +- pair_code = FIELD_GET(QCA808X_CDT_CODE_PAIR_B, status); +- break; +- case ETHTOOL_A_CABLE_PAIR_C: +- pair_code = FIELD_GET(QCA808X_CDT_CODE_PAIR_C, status); +- break; +- case ETHTOOL_A_CABLE_PAIR_D: +- pair_code = FIELD_GET(QCA808X_CDT_CODE_PAIR_D, status); +- break; +- default: +- return -EINVAL; +- } +- +- result = qca808x_cable_test_result_trans(pair_code); +- ethnl_cable_test_result(phydev, pair, result); +- +- if (qca808x_cdt_fault_length_valid(pair_code)) { +- length = qca808x_cdt_fault_length(phydev, pair, result); +- ethnl_cable_test_fault_length(phydev, pair, length); +- } +- +- return 0; +-} +- +-static int qca808x_cable_test_get_status(struct phy_device *phydev, bool *finished) +-{ +- int ret, val; +- +- *finished = false; +- +- val = QCA808X_CDT_ENABLE_TEST | +- QCA808X_CDT_LENGTH_UNIT; +- ret = at803x_cdt_start(phydev, val); +- if (ret) +- return ret; +- +- ret = at803x_cdt_wait_for_completion(phydev, QCA808X_CDT_ENABLE_TEST); +- if (ret) +- return ret; +- +- val = phy_read_mmd(phydev, MDIO_MMD_PCS, QCA808X_MMD3_CDT_STATUS); +- if (val < 0) +- return val; +- +- ret = qca808x_cable_test_get_pair_status(phydev, ETHTOOL_A_CABLE_PAIR_A, val); +- if (ret) +- return ret; +- +- ret = qca808x_cable_test_get_pair_status(phydev, ETHTOOL_A_CABLE_PAIR_B, val); +- if (ret) +- return ret; +- +- ret = qca808x_cable_test_get_pair_status(phydev, ETHTOOL_A_CABLE_PAIR_C, val); +- if (ret) +- return ret; +- +- ret = qca808x_cable_test_get_pair_status(phydev, ETHTOOL_A_CABLE_PAIR_D, val); +- if (ret) +- return ret; +- +- *finished = true; +- +- return 0; +-} +- +-static int qca808x_get_features(struct phy_device *phydev) +-{ +- int ret; +- +- ret = genphy_c45_pma_read_abilities(phydev); +- if (ret) +- return ret; +- +- /* The autoneg ability is not existed in bit3 of MMD7.1, +- * but it is supported by qca808x PHY, so we add it here +- * manually. +- */ +- linkmode_set_bit(ETHTOOL_LINK_MODE_Autoneg_BIT, phydev->supported); +- +- /* As for the qca8081 1G version chip, the 2500baseT ability is also +- * existed in the bit0 of MMD1.21, we need to remove it manually if +- * it is the qca8081 1G chip according to the bit0 of MMD7.0x901d. +- */ +- ret = phy_read_mmd(phydev, MDIO_MMD_AN, QCA808X_PHY_MMD7_CHIP_TYPE); +- if (ret < 0) +- return ret; +- +- if (QCA808X_PHY_CHIP_TYPE_1G & ret) +- linkmode_clear_bit(ETHTOOL_LINK_MODE_2500baseT_Full_BIT, phydev->supported); +- +- return 0; +-} +- +-static int qca808x_config_aneg(struct phy_device *phydev) +-{ +- int phy_ctrl = 0; +- int ret; +- +- ret = at803x_prepare_config_aneg(phydev); +- if (ret) +- return ret; +- +- /* The reg MII_BMCR also needs to be configured for force mode, the +- * genphy_config_aneg is also needed. +- */ +- if (phydev->autoneg == AUTONEG_DISABLE) +- genphy_c45_pma_setup_forced(phydev); +- +- if (linkmode_test_bit(ETHTOOL_LINK_MODE_2500baseT_Full_BIT, phydev->advertising)) +- phy_ctrl = MDIO_AN_10GBT_CTRL_ADV2_5G; +- +- ret = phy_modify_mmd_changed(phydev, MDIO_MMD_AN, MDIO_AN_10GBT_CTRL, +- MDIO_AN_10GBT_CTRL_ADV2_5G, phy_ctrl); +- if (ret < 0) +- return ret; +- +- return __genphy_config_aneg(phydev, ret); +-} +- +-static void qca808x_link_change_notify(struct phy_device *phydev) +-{ +- /* Assert interface sgmii fifo on link down, deassert it on link up, +- * the interface device address is always phy address added by 1. +- */ +- mdiobus_c45_modify_changed(phydev->mdio.bus, phydev->mdio.addr + 1, +- MDIO_MMD_PMAPMD, QCA8081_PHY_SERDES_MMD1_FIFO_CTRL, +- QCA8081_PHY_FIFO_RSTN, +- phydev->link ? QCA8081_PHY_FIFO_RSTN : 0); +-} +- +-static int qca808x_led_parse_netdev(struct phy_device *phydev, unsigned long rules, +- u16 *offload_trigger) +-{ +- /* Parsing specific to netdev trigger */ +- if (test_bit(TRIGGER_NETDEV_TX, &rules)) +- *offload_trigger |= QCA808X_LED_TX_BLINK; +- if (test_bit(TRIGGER_NETDEV_RX, &rules)) +- *offload_trigger |= QCA808X_LED_RX_BLINK; +- if (test_bit(TRIGGER_NETDEV_LINK_10, &rules)) +- *offload_trigger |= QCA808X_LED_SPEED10_ON; +- if (test_bit(TRIGGER_NETDEV_LINK_100, &rules)) +- *offload_trigger |= QCA808X_LED_SPEED100_ON; +- if (test_bit(TRIGGER_NETDEV_LINK_1000, &rules)) +- *offload_trigger |= QCA808X_LED_SPEED1000_ON; +- if (test_bit(TRIGGER_NETDEV_LINK_2500, &rules)) +- *offload_trigger |= QCA808X_LED_SPEED2500_ON; +- if (test_bit(TRIGGER_NETDEV_HALF_DUPLEX, &rules)) +- *offload_trigger |= QCA808X_LED_HALF_DUPLEX_ON; +- if (test_bit(TRIGGER_NETDEV_FULL_DUPLEX, &rules)) +- *offload_trigger |= QCA808X_LED_FULL_DUPLEX_ON; +- +- if (rules && !*offload_trigger) +- return -EOPNOTSUPP; +- +- /* Enable BLINK_CHECK_BYPASS by default to make the LED +- * blink even with duplex or speed mode not enabled. +- */ +- *offload_trigger |= QCA808X_LED_BLINK_CHECK_BYPASS; +- +- return 0; +-} +- +-static int qca808x_led_hw_control_enable(struct phy_device *phydev, u8 index) +-{ +- u16 reg; +- +- if (index > 2) +- return -EINVAL; +- +- reg = QCA808X_MMD7_LED_FORCE_CTRL(index); +- +- return phy_clear_bits_mmd(phydev, MDIO_MMD_AN, reg, +- QCA808X_LED_FORCE_EN); +-} +- +-static int qca808x_led_hw_is_supported(struct phy_device *phydev, u8 index, +- unsigned long rules) +-{ +- u16 offload_trigger = 0; +- +- if (index > 2) +- return -EINVAL; +- +- return qca808x_led_parse_netdev(phydev, rules, &offload_trigger); +-} +- +-static int qca808x_led_hw_control_set(struct phy_device *phydev, u8 index, +- unsigned long rules) +-{ +- u16 reg, offload_trigger = 0; +- int ret; +- +- if (index > 2) +- return -EINVAL; +- +- reg = QCA808X_MMD7_LED_CTRL(index); +- +- ret = qca808x_led_parse_netdev(phydev, rules, &offload_trigger); +- if (ret) +- return ret; +- +- ret = qca808x_led_hw_control_enable(phydev, index); +- if (ret) +- return ret; +- +- return phy_modify_mmd(phydev, MDIO_MMD_AN, reg, +- QCA808X_LED_PATTERN_MASK, +- offload_trigger); +-} +- +-static bool qca808x_led_hw_control_status(struct phy_device *phydev, u8 index) +-{ +- u16 reg; +- int val; +- +- if (index > 2) +- return false; +- +- reg = QCA808X_MMD7_LED_FORCE_CTRL(index); +- +- val = phy_read_mmd(phydev, MDIO_MMD_AN, reg); +- +- return !(val & QCA808X_LED_FORCE_EN); +-} +- +-static int qca808x_led_hw_control_get(struct phy_device *phydev, u8 index, +- unsigned long *rules) +-{ +- u16 reg; +- int val; +- +- if (index > 2) +- return -EINVAL; +- +- /* Check if we have hw control enabled */ +- if (qca808x_led_hw_control_status(phydev, index)) +- return -EINVAL; +- +- reg = QCA808X_MMD7_LED_CTRL(index); +- +- val = phy_read_mmd(phydev, MDIO_MMD_AN, reg); +- if (val & QCA808X_LED_TX_BLINK) +- set_bit(TRIGGER_NETDEV_TX, rules); +- if (val & QCA808X_LED_RX_BLINK) +- set_bit(TRIGGER_NETDEV_RX, rules); +- if (val & QCA808X_LED_SPEED10_ON) +- set_bit(TRIGGER_NETDEV_LINK_10, rules); +- if (val & QCA808X_LED_SPEED100_ON) +- set_bit(TRIGGER_NETDEV_LINK_100, rules); +- if (val & QCA808X_LED_SPEED1000_ON) +- set_bit(TRIGGER_NETDEV_LINK_1000, rules); +- if (val & QCA808X_LED_SPEED2500_ON) +- set_bit(TRIGGER_NETDEV_LINK_2500, rules); +- if (val & QCA808X_LED_HALF_DUPLEX_ON) +- set_bit(TRIGGER_NETDEV_HALF_DUPLEX, rules); +- if (val & QCA808X_LED_FULL_DUPLEX_ON) +- set_bit(TRIGGER_NETDEV_FULL_DUPLEX, rules); +- +- return 0; +-} +- +-static int qca808x_led_hw_control_reset(struct phy_device *phydev, u8 index) +-{ +- u16 reg; +- +- if (index > 2) +- return -EINVAL; +- +- reg = QCA808X_MMD7_LED_CTRL(index); +- +- return phy_clear_bits_mmd(phydev, MDIO_MMD_AN, reg, +- QCA808X_LED_PATTERN_MASK); +-} +- +-static int qca808x_led_brightness_set(struct phy_device *phydev, +- u8 index, enum led_brightness value) +-{ +- u16 reg; +- int ret; +- +- if (index > 2) +- return -EINVAL; +- +- if (!value) { +- ret = qca808x_led_hw_control_reset(phydev, index); +- if (ret) +- return ret; +- } +- +- reg = QCA808X_MMD7_LED_FORCE_CTRL(index); +- +- return phy_modify_mmd(phydev, MDIO_MMD_AN, reg, +- QCA808X_LED_FORCE_EN | QCA808X_LED_FORCE_MODE_MASK, +- QCA808X_LED_FORCE_EN | value ? QCA808X_LED_FORCE_ON : +- QCA808X_LED_FORCE_OFF); +-} +- +-static int qca808x_led_blink_set(struct phy_device *phydev, u8 index, +- unsigned long *delay_on, +- unsigned long *delay_off) +-{ +- int ret; +- u16 reg; +- +- if (index > 2) +- return -EINVAL; +- +- reg = QCA808X_MMD7_LED_FORCE_CTRL(index); +- +- /* Set blink to 50% off, 50% on at 4Hz by default */ +- ret = phy_modify_mmd(phydev, MDIO_MMD_AN, QCA808X_MMD7_LED_GLOBAL, +- QCA808X_LED_BLINK_FREQ_MASK | QCA808X_LED_BLINK_DUTY_MASK, +- QCA808X_LED_BLINK_FREQ_4HZ | QCA808X_LED_BLINK_DUTY_50_50); +- if (ret) +- return ret; +- +- /* We use BLINK_1 for normal blinking */ +- ret = phy_modify_mmd(phydev, MDIO_MMD_AN, reg, +- QCA808X_LED_FORCE_EN | QCA808X_LED_FORCE_MODE_MASK, +- QCA808X_LED_FORCE_EN | QCA808X_LED_FORCE_BLINK_1); +- if (ret) +- return ret; +- +- /* We set blink to 4Hz, aka 250ms */ +- *delay_on = 250 / 2; +- *delay_off = 250 / 2; +- +- return 0; +-} +- +-static int qca808x_led_polarity_set(struct phy_device *phydev, int index, +- unsigned long modes) +-{ +- struct at803x_priv *priv = phydev->priv; +- bool active_low = false; +- u32 mode; +- +- for_each_set_bit(mode, &modes, __PHY_LED_MODES_NUM) { +- switch (mode) { +- case PHY_LED_ACTIVE_LOW: +- active_low = true; +- break; +- default: +- return -EINVAL; +- } +- } +- +- /* PHY polarity is global and can't be set per LED. +- * To detect this, check if last requested polarity mode +- * match the new one. +- */ +- if (priv->led_polarity_mode >= 0 && +- priv->led_polarity_mode != active_low) { +- phydev_err(phydev, "PHY polarity is global. Mismatched polarity on different LED\n"); +- return -EINVAL; +- } +- +- /* Save the last PHY polarity mode */ +- priv->led_polarity_mode = active_low; +- +- return phy_modify_mmd(phydev, MDIO_MMD_AN, +- QCA808X_MMD7_LED_POLARITY_CTRL, +- QCA808X_LED_ACTIVE_HIGH, +- active_low ? 0 : QCA808X_LED_ACTIVE_HIGH); +-} +- +-static struct phy_driver at803x_driver[] = { +-{ +- /* Qualcomm Atheros AR8035 */ +- PHY_ID_MATCH_EXACT(ATH8035_PHY_ID), +- .name = "Qualcomm Atheros AR8035", +- .flags = PHY_POLL_CABLE_TEST, +- .probe = at8035_probe, +- .config_aneg = at803x_config_aneg, +- .config_init = at803x_config_init, +- .soft_reset = genphy_soft_reset, +- .set_wol = at803x_set_wol, +- .get_wol = at803x_get_wol, +- .suspend = at803x_suspend, +- .resume = at803x_resume, +- /* PHY_GBIT_FEATURES */ +- .read_status = at803x_read_status, +- .config_intr = at803x_config_intr, +- .handle_interrupt = at803x_handle_interrupt, +- .get_tunable = at803x_get_tunable, +- .set_tunable = at803x_set_tunable, +- .cable_test_start = at8031_cable_test_start, +- .cable_test_get_status = at8031_cable_test_get_status, +-}, { +- /* Qualcomm Atheros AR8030 */ +- .phy_id = ATH8030_PHY_ID, +- .name = "Qualcomm Atheros AR8030", +- .phy_id_mask = AT8030_PHY_ID_MASK, +- .probe = at8035_probe, +- .config_init = at803x_config_init, +- .link_change_notify = at803x_link_change_notify, +- .set_wol = at803x_set_wol, +- .get_wol = at803x_get_wol, +- .suspend = at803x_suspend, +- .resume = at803x_resume, +- /* PHY_BASIC_FEATURES */ +- .config_intr = at803x_config_intr, +- .handle_interrupt = at803x_handle_interrupt, +-}, { +- /* Qualcomm Atheros AR8031/AR8033 */ +- PHY_ID_MATCH_EXACT(ATH8031_PHY_ID), +- .name = "Qualcomm Atheros AR8031/AR8033", +- .flags = PHY_POLL_CABLE_TEST, +- .probe = at8031_probe, +- .config_init = at8031_config_init, +- .config_aneg = at803x_config_aneg, +- .soft_reset = genphy_soft_reset, +- .set_wol = at8031_set_wol, +- .get_wol = at803x_get_wol, +- .suspend = at803x_suspend, +- .resume = at803x_resume, +- .read_page = at803x_read_page, +- .write_page = at803x_write_page, +- .get_features = at803x_get_features, +- .read_status = at8031_read_status, +- .config_intr = at8031_config_intr, +- .handle_interrupt = at803x_handle_interrupt, +- .get_tunable = at803x_get_tunable, +- .set_tunable = at803x_set_tunable, +- .cable_test_start = at8031_cable_test_start, +- .cable_test_get_status = at8031_cable_test_get_status, +-}, { +- /* Qualcomm Atheros AR8032 */ +- PHY_ID_MATCH_EXACT(ATH8032_PHY_ID), +- .name = "Qualcomm Atheros AR8032", +- .probe = at803x_probe, +- .flags = PHY_POLL_CABLE_TEST, +- .config_init = at803x_config_init, +- .link_change_notify = at803x_link_change_notify, +- .suspend = at803x_suspend, +- .resume = at803x_resume, +- /* PHY_BASIC_FEATURES */ +- .config_intr = at803x_config_intr, +- .handle_interrupt = at803x_handle_interrupt, +- .cable_test_start = at803x_cable_test_start, +- .cable_test_get_status = at8032_cable_test_get_status, +-}, { +- /* ATHEROS AR9331 */ +- PHY_ID_MATCH_EXACT(ATH9331_PHY_ID), +- .name = "Qualcomm Atheros AR9331 built-in PHY", +- .probe = at803x_probe, +- .suspend = at803x_suspend, +- .resume = at803x_resume, +- .flags = PHY_POLL_CABLE_TEST, +- /* PHY_BASIC_FEATURES */ +- .config_intr = at803x_config_intr, +- .handle_interrupt = at803x_handle_interrupt, +- .cable_test_start = at803x_cable_test_start, +- .cable_test_get_status = at8032_cable_test_get_status, +- .read_status = at803x_read_status, +- .soft_reset = genphy_soft_reset, +- .config_aneg = at803x_config_aneg, +-}, { +- /* Qualcomm Atheros QCA9561 */ +- PHY_ID_MATCH_EXACT(QCA9561_PHY_ID), +- .name = "Qualcomm Atheros QCA9561 built-in PHY", +- .probe = at803x_probe, +- .suspend = at803x_suspend, +- .resume = at803x_resume, +- .flags = PHY_POLL_CABLE_TEST, +- /* PHY_BASIC_FEATURES */ +- .config_intr = at803x_config_intr, +- .handle_interrupt = at803x_handle_interrupt, +- .cable_test_start = at803x_cable_test_start, +- .cable_test_get_status = at8032_cable_test_get_status, +- .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 = "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 = qca83xx_get_sset_count, +- .get_strings = qca83xx_get_strings, +- .get_stats = qca83xx_get_stats, +- .suspend = qca8337_suspend, +- .resume = qca83xx_resume, +-}, { +- /* QCA8327-A from switch QCA8327-AL1A */ +- .phy_id = QCA8327_A_PHY_ID, +- .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 = qca8327_config_init, +- .soft_reset = genphy_soft_reset, +- .get_sset_count = qca83xx_get_sset_count, +- .get_strings = qca83xx_get_strings, +- .get_stats = qca83xx_get_stats, +- .suspend = qca8327_suspend, +- .resume = qca83xx_resume, +-}, { +- /* QCA8327-B from switch QCA8327-BL1A */ +- .phy_id = QCA8327_B_PHY_ID, +- .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 = qca8327_config_init, +- .soft_reset = genphy_soft_reset, +- .get_sset_count = qca83xx_get_sset_count, +- .get_strings = qca83xx_get_strings, +- .get_stats = qca83xx_get_stats, +- .suspend = qca8327_suspend, +- .resume = qca83xx_resume, +-}, { +- /* Qualcomm QCA8081 */ +- PHY_ID_MATCH_EXACT(QCA8081_PHY_ID), +- .name = "Qualcomm QCA8081", +- .flags = PHY_POLL_CABLE_TEST, +- .probe = at803x_probe, +- .config_intr = at803x_config_intr, +- .handle_interrupt = at803x_handle_interrupt, +- .get_tunable = at803x_get_tunable, +- .set_tunable = at803x_set_tunable, +- .set_wol = at803x_set_wol, +- .get_wol = at803x_get_wol, +- .get_features = qca808x_get_features, +- .config_aneg = qca808x_config_aneg, +- .suspend = genphy_suspend, +- .resume = genphy_resume, +- .read_status = qca808x_read_status, +- .config_init = qca808x_config_init, +- .soft_reset = qca808x_soft_reset, +- .cable_test_start = qca808x_cable_test_start, +- .cable_test_get_status = qca808x_cable_test_get_status, +- .link_change_notify = qca808x_link_change_notify, +- .led_brightness_set = qca808x_led_brightness_set, +- .led_blink_set = qca808x_led_blink_set, +- .led_hw_is_supported = qca808x_led_hw_is_supported, +- .led_hw_control_set = qca808x_led_hw_control_set, +- .led_hw_control_get = qca808x_led_hw_control_get, +- .led_polarity_set = qca808x_led_polarity_set, +-}, }; +- +-module_phy_driver(at803x_driver); +- +-static struct mdio_device_id __maybe_unused atheros_tbl[] = { +- { ATH8030_PHY_ID, AT8030_PHY_ID_MASK }, +- { PHY_ID_MATCH_EXACT(ATH8031_PHY_ID) }, +- { 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_A_PHY_ID) }, +- { PHY_ID_MATCH_EXACT(QCA8327_B_PHY_ID) }, +- { PHY_ID_MATCH_EXACT(QCA9561_PHY_ID) }, +- { PHY_ID_MATCH_EXACT(QCA8081_PHY_ID) }, +- { } +-}; +- +-MODULE_DEVICE_TABLE(mdio, atheros_tbl); +--- /dev/null ++++ b/drivers/net/phy/qcom/at803x.c +@@ -0,0 +1,2759 @@ ++// SPDX-License-Identifier: GPL-2.0+ ++/* ++ * drivers/net/phy/at803x.c ++ * ++ * Driver for Qualcomm Atheros AR803x PHY ++ * ++ * Author: Matus Ujhelyi ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#define AT803X_SPECIFIC_FUNCTION_CONTROL 0x10 ++#define AT803X_SFC_ASSERT_CRS BIT(11) ++#define AT803X_SFC_FORCE_LINK BIT(10) ++#define AT803X_SFC_MDI_CROSSOVER_MODE_M GENMASK(6, 5) ++#define AT803X_SFC_AUTOMATIC_CROSSOVER 0x3 ++#define AT803X_SFC_MANUAL_MDIX 0x1 ++#define AT803X_SFC_MANUAL_MDI 0x0 ++#define AT803X_SFC_SQE_TEST BIT(2) ++#define AT803X_SFC_POLARITY_REVERSAL BIT(1) ++#define AT803X_SFC_DISABLE_JABBER BIT(0) ++ ++#define AT803X_SPECIFIC_STATUS 0x11 ++#define AT803X_SS_SPEED_MASK GENMASK(15, 14) ++#define AT803X_SS_SPEED_1000 2 ++#define AT803X_SS_SPEED_100 1 ++#define AT803X_SS_SPEED_10 0 ++#define AT803X_SS_DUPLEX BIT(13) ++#define AT803X_SS_SPEED_DUPLEX_RESOLVED BIT(11) ++#define AT803X_SS_MDIX BIT(6) ++ ++#define QCA808X_SS_SPEED_MASK GENMASK(9, 7) ++#define QCA808X_SS_SPEED_2500 4 ++ ++#define AT803X_INTR_ENABLE 0x12 ++#define AT803X_INTR_ENABLE_AUTONEG_ERR BIT(15) ++#define AT803X_INTR_ENABLE_SPEED_CHANGED BIT(14) ++#define AT803X_INTR_ENABLE_DUPLEX_CHANGED BIT(13) ++#define AT803X_INTR_ENABLE_PAGE_RECEIVED BIT(12) ++#define AT803X_INTR_ENABLE_LINK_FAIL BIT(11) ++#define AT803X_INTR_ENABLE_LINK_SUCCESS BIT(10) ++#define AT803X_INTR_ENABLE_LINK_FAIL_BX BIT(8) ++#define AT803X_INTR_ENABLE_LINK_SUCCESS_BX BIT(7) ++#define AT803X_INTR_ENABLE_WIRESPEED_DOWNGRADE BIT(5) ++#define AT803X_INTR_ENABLE_POLARITY_CHANGED BIT(1) ++#define AT803X_INTR_ENABLE_WOL BIT(0) ++ ++#define AT803X_INTR_STATUS 0x13 ++ ++#define AT803X_SMART_SPEED 0x14 ++#define AT803X_SMART_SPEED_ENABLE BIT(5) ++#define AT803X_SMART_SPEED_RETRY_LIMIT_MASK GENMASK(4, 2) ++#define AT803X_SMART_SPEED_BYPASS_TIMER BIT(1) ++#define AT803X_CDT 0x16 ++#define AT803X_CDT_MDI_PAIR_MASK GENMASK(9, 8) ++#define AT803X_CDT_ENABLE_TEST BIT(0) ++#define AT803X_CDT_STATUS 0x1c ++#define AT803X_CDT_STATUS_STAT_NORMAL 0 ++#define AT803X_CDT_STATUS_STAT_SHORT 1 ++#define AT803X_CDT_STATUS_STAT_OPEN 2 ++#define AT803X_CDT_STATUS_STAT_FAIL 3 ++#define AT803X_CDT_STATUS_STAT_MASK GENMASK(9, 8) ++#define AT803X_CDT_STATUS_DELTA_TIME_MASK GENMASK(7, 0) ++#define AT803X_LED_CONTROL 0x18 ++ ++#define AT803X_PHY_MMD3_WOL_CTRL 0x8012 ++#define AT803X_WOL_EN BIT(5) ++#define AT803X_LOC_MAC_ADDR_0_15_OFFSET 0x804C ++#define AT803X_LOC_MAC_ADDR_16_31_OFFSET 0x804B ++#define AT803X_LOC_MAC_ADDR_32_47_OFFSET 0x804A ++#define AT803X_REG_CHIP_CONFIG 0x1f ++#define AT803X_BT_BX_REG_SEL 0x8000 ++ ++#define AT803X_DEBUG_ADDR 0x1D ++#define AT803X_DEBUG_DATA 0x1E ++ ++#define AT803X_MODE_CFG_MASK 0x0F ++#define AT803X_MODE_CFG_BASET_RGMII 0x00 ++#define AT803X_MODE_CFG_BASET_SGMII 0x01 ++#define AT803X_MODE_CFG_BX1000_RGMII_50OHM 0x02 ++#define AT803X_MODE_CFG_BX1000_RGMII_75OHM 0x03 ++#define AT803X_MODE_CFG_BX1000_CONV_50OHM 0x04 ++#define AT803X_MODE_CFG_BX1000_CONV_75OHM 0x05 ++#define AT803X_MODE_CFG_FX100_RGMII_50OHM 0x06 ++#define AT803X_MODE_CFG_FX100_CONV_50OHM 0x07 ++#define AT803X_MODE_CFG_RGMII_AUTO_MDET 0x0B ++#define AT803X_MODE_CFG_FX100_RGMII_75OHM 0x0E ++#define AT803X_MODE_CFG_FX100_CONV_75OHM 0x0F ++ ++#define AT803X_PSSR 0x11 /*PHY-Specific Status Register*/ ++#define AT803X_PSSR_MR_AN_COMPLETE 0x0200 ++ ++#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_SYSTEM_CTRL_MODE 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_HIB_CTRL_PS_HIB_EN BIT(15) ++ ++#define AT803X_DEBUG_REG_3C 0x3C ++ ++#define AT803X_DEBUG_REG_GREEN 0x3D ++#define AT803X_DEBUG_GATE_CLK_IN1000 BIT(6) ++ ++#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. ++ * The DSP as a clock reference is used in synchronous ethernet ++ * applications. ++ * ++ * By default the PLL is only enabled if there is a link. Otherwise ++ * the PHY will go into low power state and disabled the PLL. You can ++ * set the PLL_ON bit (see debug register 0x1f) to keep the PLL always ++ * enabled. ++ */ ++#define AT803X_MMD7_CLK25M 0x8016 ++#define AT803X_CLK_OUT_MASK GENMASK(4, 2) ++#define AT803X_CLK_OUT_25MHZ_XTAL 0 ++#define AT803X_CLK_OUT_25MHZ_DSP 1 ++#define AT803X_CLK_OUT_50MHZ_PLL 2 ++#define AT803X_CLK_OUT_50MHZ_DSP 3 ++#define AT803X_CLK_OUT_62_5MHZ_PLL 4 ++#define AT803X_CLK_OUT_62_5MHZ_DSP 5 ++#define AT803X_CLK_OUT_125MHZ_PLL 6 ++#define AT803X_CLK_OUT_125MHZ_DSP 7 ++ ++/* The AR8035 has another mask which is compatible with the AR8031/AR8033 mask ++ * but doesn't support choosing between XTAL/PLL and DSP. ++ */ ++#define AT8035_CLK_OUT_MASK GENMASK(4, 3) ++ ++#define AT803X_CLK_OUT_STRENGTH_MASK GENMASK(8, 7) ++#define AT803X_CLK_OUT_STRENGTH_FULL 0 ++#define AT803X_CLK_OUT_STRENGTH_HALF 1 ++#define AT803X_CLK_OUT_STRENGTH_QUARTER 2 ++ ++#define AT803X_DEFAULT_DOWNSHIFT 5 ++#define AT803X_MIN_DOWNSHIFT 2 ++#define AT803X_MAX_DOWNSHIFT 9 ++ ++#define AT803X_MMD3_SMARTEEE_CTL1 0x805b ++#define AT803X_MMD3_SMARTEEE_CTL2 0x805c ++#define AT803X_MMD3_SMARTEEE_CTL3 0x805d ++#define AT803X_MMD3_SMARTEEE_CTL3_LPI_EN BIT(8) ++ ++#define ATH9331_PHY_ID 0x004dd041 ++#define ATH8030_PHY_ID 0x004dd076 ++#define ATH8031_PHY_ID 0x004dd074 ++#define ATH8032_PHY_ID 0x004dd023 ++#define ATH8035_PHY_ID 0x004dd072 ++#define AT8030_PHY_ID_MASK 0xffffffef ++ ++#define QCA8081_PHY_ID 0x004dd101 ++ ++#define QCA8327_A_PHY_ID 0x004dd033 ++#define QCA8327_B_PHY_ID 0x004dd034 ++#define QCA8337_PHY_ID 0x004dd036 ++#define QCA9561_PHY_ID 0x004dd042 ++#define QCA8K_PHY_ID_MASK 0xffffffff ++ ++#define QCA8K_DEVFLAGS_REVISION_MASK GENMASK(2, 0) ++ ++#define AT803X_PAGE_FIBER 0 ++#define AT803X_PAGE_COPPER 1 ++ ++/* don't turn off internal PLL */ ++#define AT803X_KEEP_PLL_ENABLED BIT(0) ++#define AT803X_DISABLE_SMARTEEE BIT(1) ++ ++/* disable hibernation mode */ ++#define AT803X_DISABLE_HIBERNATION_MODE BIT(2) ++ ++/* ADC threshold */ ++#define QCA808X_PHY_DEBUG_ADC_THRESHOLD 0x2c80 ++#define QCA808X_ADC_THRESHOLD_MASK GENMASK(7, 0) ++#define QCA808X_ADC_THRESHOLD_80MV 0 ++#define QCA808X_ADC_THRESHOLD_100MV 0xf0 ++#define QCA808X_ADC_THRESHOLD_200MV 0x0f ++#define QCA808X_ADC_THRESHOLD_300MV 0xff ++ ++/* CLD control */ ++#define QCA808X_PHY_MMD3_ADDR_CLD_CTRL7 0x8007 ++#define QCA808X_8023AZ_AFE_CTRL_MASK GENMASK(8, 4) ++#define QCA808X_8023AZ_AFE_EN 0x90 ++ ++/* AZ control */ ++#define QCA808X_PHY_MMD3_AZ_TRAINING_CTRL 0x8008 ++#define QCA808X_MMD3_AZ_TRAINING_VAL 0x1c32 ++ ++#define QCA808X_PHY_MMD1_MSE_THRESHOLD_20DB 0x8014 ++#define QCA808X_MSE_THRESHOLD_20DB_VALUE 0x529 ++ ++#define QCA808X_PHY_MMD1_MSE_THRESHOLD_17DB 0x800E ++#define QCA808X_MSE_THRESHOLD_17DB_VALUE 0x341 ++ ++#define QCA808X_PHY_MMD1_MSE_THRESHOLD_27DB 0x801E ++#define QCA808X_MSE_THRESHOLD_27DB_VALUE 0x419 ++ ++#define QCA808X_PHY_MMD1_MSE_THRESHOLD_28DB 0x8020 ++#define QCA808X_MSE_THRESHOLD_28DB_VALUE 0x341 ++ ++#define QCA808X_PHY_MMD7_TOP_OPTION1 0x901c ++#define QCA808X_TOP_OPTION1_DATA 0x0 ++ ++#define QCA808X_PHY_MMD3_DEBUG_1 0xa100 ++#define QCA808X_MMD3_DEBUG_1_VALUE 0x9203 ++#define QCA808X_PHY_MMD3_DEBUG_2 0xa101 ++#define QCA808X_MMD3_DEBUG_2_VALUE 0x48ad ++#define QCA808X_PHY_MMD3_DEBUG_3 0xa103 ++#define QCA808X_MMD3_DEBUG_3_VALUE 0x1698 ++#define QCA808X_PHY_MMD3_DEBUG_4 0xa105 ++#define QCA808X_MMD3_DEBUG_4_VALUE 0x8001 ++#define QCA808X_PHY_MMD3_DEBUG_5 0xa106 ++#define QCA808X_MMD3_DEBUG_5_VALUE 0x1111 ++#define QCA808X_PHY_MMD3_DEBUG_6 0xa011 ++#define QCA808X_MMD3_DEBUG_6_VALUE 0x5f85 ++ ++/* master/slave seed config */ ++#define QCA808X_PHY_DEBUG_LOCAL_SEED 9 ++#define QCA808X_MASTER_SLAVE_SEED_ENABLE BIT(1) ++#define QCA808X_MASTER_SLAVE_SEED_CFG GENMASK(12, 2) ++#define QCA808X_MASTER_SLAVE_SEED_RANGE 0x32 ++ ++/* Hibernation yields lower power consumpiton in contrast with normal operation mode. ++ * when the copper cable is unplugged, the PHY enters into hibernation mode in about 10s. ++ */ ++#define QCA808X_DBG_AN_TEST 0xb ++#define QCA808X_HIBERNATION_EN BIT(15) ++ ++#define QCA808X_CDT_ENABLE_TEST BIT(15) ++#define QCA808X_CDT_INTER_CHECK_DIS BIT(13) ++#define QCA808X_CDT_STATUS BIT(11) ++#define QCA808X_CDT_LENGTH_UNIT BIT(10) ++ ++#define QCA808X_MMD3_CDT_STATUS 0x8064 ++#define QCA808X_MMD3_CDT_DIAG_PAIR_A 0x8065 ++#define QCA808X_MMD3_CDT_DIAG_PAIR_B 0x8066 ++#define QCA808X_MMD3_CDT_DIAG_PAIR_C 0x8067 ++#define QCA808X_MMD3_CDT_DIAG_PAIR_D 0x8068 ++#define QCA808X_CDT_DIAG_LENGTH_SAME_SHORT GENMASK(15, 8) ++#define QCA808X_CDT_DIAG_LENGTH_CROSS_SHORT GENMASK(7, 0) ++ ++#define QCA808X_CDT_CODE_PAIR_A GENMASK(15, 12) ++#define QCA808X_CDT_CODE_PAIR_B GENMASK(11, 8) ++#define QCA808X_CDT_CODE_PAIR_C GENMASK(7, 4) ++#define QCA808X_CDT_CODE_PAIR_D GENMASK(3, 0) ++ ++#define QCA808X_CDT_STATUS_STAT_TYPE GENMASK(1, 0) ++#define QCA808X_CDT_STATUS_STAT_FAIL FIELD_PREP_CONST(QCA808X_CDT_STATUS_STAT_TYPE, 0) ++#define QCA808X_CDT_STATUS_STAT_NORMAL FIELD_PREP_CONST(QCA808X_CDT_STATUS_STAT_TYPE, 1) ++#define QCA808X_CDT_STATUS_STAT_SAME_OPEN FIELD_PREP_CONST(QCA808X_CDT_STATUS_STAT_TYPE, 2) ++#define QCA808X_CDT_STATUS_STAT_SAME_SHORT FIELD_PREP_CONST(QCA808X_CDT_STATUS_STAT_TYPE, 3) ++ ++#define QCA808X_CDT_STATUS_STAT_MDI GENMASK(3, 2) ++#define QCA808X_CDT_STATUS_STAT_MDI1 FIELD_PREP_CONST(QCA808X_CDT_STATUS_STAT_MDI, 1) ++#define QCA808X_CDT_STATUS_STAT_MDI2 FIELD_PREP_CONST(QCA808X_CDT_STATUS_STAT_MDI, 2) ++#define QCA808X_CDT_STATUS_STAT_MDI3 FIELD_PREP_CONST(QCA808X_CDT_STATUS_STAT_MDI, 3) ++ ++/* NORMAL are MDI with type set to 0 */ ++#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_NORMAL QCA808X_CDT_STATUS_STAT_MDI1 ++#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_OPEN (QCA808X_CDT_STATUS_STAT_SAME_OPEN |\ ++ QCA808X_CDT_STATUS_STAT_MDI1) ++#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_SHORT (QCA808X_CDT_STATUS_STAT_SAME_SHORT |\ ++ QCA808X_CDT_STATUS_STAT_MDI1) ++#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_NORMAL QCA808X_CDT_STATUS_STAT_MDI2 ++#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_OPEN (QCA808X_CDT_STATUS_STAT_SAME_OPEN |\ ++ QCA808X_CDT_STATUS_STAT_MDI2) ++#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_SHORT (QCA808X_CDT_STATUS_STAT_SAME_SHORT |\ ++ QCA808X_CDT_STATUS_STAT_MDI2) ++#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_NORMAL QCA808X_CDT_STATUS_STAT_MDI3 ++#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_OPEN (QCA808X_CDT_STATUS_STAT_SAME_OPEN |\ ++ QCA808X_CDT_STATUS_STAT_MDI3) ++#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_SHORT (QCA808X_CDT_STATUS_STAT_SAME_SHORT |\ ++ QCA808X_CDT_STATUS_STAT_MDI3) ++ ++/* Added for reference of existence but should be handled by wait_for_completion already */ ++#define QCA808X_CDT_STATUS_STAT_BUSY (BIT(1) | BIT(3)) ++ ++#define QCA808X_MMD7_LED_GLOBAL 0x8073 ++#define QCA808X_LED_BLINK_1 GENMASK(11, 6) ++#define QCA808X_LED_BLINK_2 GENMASK(5, 0) ++/* Values are the same for both BLINK_1 and BLINK_2 */ ++#define QCA808X_LED_BLINK_FREQ_MASK GENMASK(5, 3) ++#define QCA808X_LED_BLINK_FREQ_2HZ FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x0) ++#define QCA808X_LED_BLINK_FREQ_4HZ FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x1) ++#define QCA808X_LED_BLINK_FREQ_8HZ FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x2) ++#define QCA808X_LED_BLINK_FREQ_16HZ FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x3) ++#define QCA808X_LED_BLINK_FREQ_32HZ FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x4) ++#define QCA808X_LED_BLINK_FREQ_64HZ FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x5) ++#define QCA808X_LED_BLINK_FREQ_128HZ FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x6) ++#define QCA808X_LED_BLINK_FREQ_256HZ FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x7) ++#define QCA808X_LED_BLINK_DUTY_MASK GENMASK(2, 0) ++#define QCA808X_LED_BLINK_DUTY_50_50 FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x0) ++#define QCA808X_LED_BLINK_DUTY_75_25 FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x1) ++#define QCA808X_LED_BLINK_DUTY_25_75 FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x2) ++#define QCA808X_LED_BLINK_DUTY_33_67 FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x3) ++#define QCA808X_LED_BLINK_DUTY_67_33 FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x4) ++#define QCA808X_LED_BLINK_DUTY_17_83 FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x5) ++#define QCA808X_LED_BLINK_DUTY_83_17 FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x6) ++#define QCA808X_LED_BLINK_DUTY_8_92 FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x7) ++ ++#define QCA808X_MMD7_LED2_CTRL 0x8074 ++#define QCA808X_MMD7_LED2_FORCE_CTRL 0x8075 ++#define QCA808X_MMD7_LED1_CTRL 0x8076 ++#define QCA808X_MMD7_LED1_FORCE_CTRL 0x8077 ++#define QCA808X_MMD7_LED0_CTRL 0x8078 ++#define QCA808X_MMD7_LED_CTRL(x) (0x8078 - ((x) * 2)) ++ ++/* LED hw control pattern is the same for every LED */ ++#define QCA808X_LED_PATTERN_MASK GENMASK(15, 0) ++#define QCA808X_LED_SPEED2500_ON BIT(15) ++#define QCA808X_LED_SPEED2500_BLINK BIT(14) ++/* Follow blink trigger even if duplex or speed condition doesn't match */ ++#define QCA808X_LED_BLINK_CHECK_BYPASS BIT(13) ++#define QCA808X_LED_FULL_DUPLEX_ON BIT(12) ++#define QCA808X_LED_HALF_DUPLEX_ON BIT(11) ++#define QCA808X_LED_TX_BLINK BIT(10) ++#define QCA808X_LED_RX_BLINK BIT(9) ++#define QCA808X_LED_TX_ON_10MS BIT(8) ++#define QCA808X_LED_RX_ON_10MS BIT(7) ++#define QCA808X_LED_SPEED1000_ON BIT(6) ++#define QCA808X_LED_SPEED100_ON BIT(5) ++#define QCA808X_LED_SPEED10_ON BIT(4) ++#define QCA808X_LED_COLLISION_BLINK BIT(3) ++#define QCA808X_LED_SPEED1000_BLINK BIT(2) ++#define QCA808X_LED_SPEED100_BLINK BIT(1) ++#define QCA808X_LED_SPEED10_BLINK BIT(0) ++ ++#define QCA808X_MMD7_LED0_FORCE_CTRL 0x8079 ++#define QCA808X_MMD7_LED_FORCE_CTRL(x) (0x8079 - ((x) * 2)) ++ ++/* LED force ctrl is the same for every LED ++ * No documentation exist for this, not even internal one ++ * with NDA as QCOM gives only info about configuring ++ * hw control pattern rules and doesn't indicate any way ++ * to force the LED to specific mode. ++ * These define comes from reverse and testing and maybe ++ * lack of some info or some info are not entirely correct. ++ * For the basic LED control and hw control these finding ++ * are enough to support LED control in all the required APIs. ++ * ++ * On doing some comparison with implementation with qca807x, ++ * it was found that it's 1:1 equal to it and confirms all the ++ * reverse done. It was also found further specification with the ++ * force mode and the blink modes. ++ */ ++#define QCA808X_LED_FORCE_EN BIT(15) ++#define QCA808X_LED_FORCE_MODE_MASK GENMASK(14, 13) ++#define QCA808X_LED_FORCE_BLINK_1 FIELD_PREP(QCA808X_LED_FORCE_MODE_MASK, 0x3) ++#define QCA808X_LED_FORCE_BLINK_2 FIELD_PREP(QCA808X_LED_FORCE_MODE_MASK, 0x2) ++#define QCA808X_LED_FORCE_ON FIELD_PREP(QCA808X_LED_FORCE_MODE_MASK, 0x1) ++#define QCA808X_LED_FORCE_OFF FIELD_PREP(QCA808X_LED_FORCE_MODE_MASK, 0x0) ++ ++#define QCA808X_MMD7_LED_POLARITY_CTRL 0x901a ++/* QSDK sets by default 0x46 to this reg that sets BIT 6 for ++ * LED to active high. It's not clear what BIT 3 and BIT 4 does. ++ */ ++#define QCA808X_LED_ACTIVE_HIGH BIT(6) ++ ++/* QCA808X 1G chip type */ ++#define QCA808X_PHY_MMD7_CHIP_TYPE 0x901d ++#define QCA808X_PHY_CHIP_TYPE_1G BIT(0) ++ ++#define QCA8081_PHY_SERDES_MMD1_FIFO_CTRL 0x9072 ++#define QCA8081_PHY_FIFO_RSTN BIT(11) ++ ++MODULE_DESCRIPTION("Qualcomm Atheros AR803x and QCA808X 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 qca83xx_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_ss_mask { ++ u16 speed_mask; ++ u8 speed_shift; ++}; ++ ++struct at803x_priv { ++ int flags; ++ u16 clk_25m_reg; ++ u16 clk_25m_mask; ++ u8 smarteee_lpi_tw_1g; ++ u8 smarteee_lpi_tw_100m; ++ bool is_fiber; ++ bool is_1000basex; ++ struct regulator_dev *vddio_rdev; ++ struct regulator_dev *vddh_rdev; ++ u64 stats[ARRAY_SIZE(qca83xx_hw_stats)]; ++ int led_polarity_mode; ++}; ++ ++struct at803x_context { ++ u16 bmcr; ++ u16 advertise; ++ u16 control1000; ++ u16 int_enable; ++ u16 smart_speed; ++ 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; ++ ++ ret = phy_write(phydev, AT803X_DEBUG_ADDR, reg); ++ if (ret < 0) ++ return ret; ++ ++ return phy_read(phydev, AT803X_DEBUG_DATA); ++} ++ ++static int at803x_debug_reg_mask(struct phy_device *phydev, u16 reg, ++ u16 clear, u16 set) ++{ ++ u16 val; ++ int ret; ++ ++ ret = at803x_debug_reg_read(phydev, reg); ++ if (ret < 0) ++ return ret; ++ ++ val = ret & 0xffff; ++ val &= ~clear; ++ val |= set; ++ ++ return phy_write(phydev, AT803X_DEBUG_DATA, val); ++} ++ ++static int at803x_write_page(struct phy_device *phydev, int page) ++{ ++ int mask; ++ int set; ++ ++ if (page == AT803X_PAGE_COPPER) { ++ set = AT803X_BT_BX_REG_SEL; ++ mask = 0; ++ } else { ++ set = 0; ++ mask = AT803X_BT_BX_REG_SEL; ++ } ++ ++ return __phy_modify(phydev, AT803X_REG_CHIP_CONFIG, mask, set); ++} ++ ++static int at803x_read_page(struct phy_device *phydev) ++{ ++ int ccr = __phy_read(phydev, AT803X_REG_CHIP_CONFIG); ++ ++ if (ccr < 0) ++ return ccr; ++ ++ if (ccr & AT803X_BT_BX_REG_SEL) ++ return AT803X_PAGE_COPPER; ++ ++ return AT803X_PAGE_FIBER; ++} ++ ++static int at803x_enable_rx_delay(struct phy_device *phydev) ++{ ++ 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_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_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_SYSTEM_CTRL_MODE, ++ AT803X_DEBUG_TX_CLK_DLY_EN, 0); ++} ++ ++/* save relevant PHY registers to private copy */ ++static void at803x_context_save(struct phy_device *phydev, ++ struct at803x_context *context) ++{ ++ context->bmcr = phy_read(phydev, MII_BMCR); ++ context->advertise = phy_read(phydev, MII_ADVERTISE); ++ context->control1000 = phy_read(phydev, MII_CTRL1000); ++ context->int_enable = phy_read(phydev, AT803X_INTR_ENABLE); ++ context->smart_speed = phy_read(phydev, AT803X_SMART_SPEED); ++ context->led_control = phy_read(phydev, AT803X_LED_CONTROL); ++} ++ ++/* restore relevant PHY registers from private copy */ ++static void at803x_context_restore(struct phy_device *phydev, ++ const struct at803x_context *context) ++{ ++ phy_write(phydev, MII_BMCR, context->bmcr); ++ phy_write(phydev, MII_ADVERTISE, context->advertise); ++ phy_write(phydev, MII_CTRL1000, context->control1000); ++ phy_write(phydev, AT803X_INTR_ENABLE, context->int_enable); ++ phy_write(phydev, AT803X_SMART_SPEED, context->smart_speed); ++ phy_write(phydev, AT803X_LED_CONTROL, context->led_control); ++} ++ ++static int at803x_set_wol(struct phy_device *phydev, ++ struct ethtool_wolinfo *wol) ++{ ++ int ret, irq_enabled; ++ ++ if (wol->wolopts & WAKE_MAGIC) { ++ struct net_device *ndev = phydev->attached_dev; ++ const u8 *mac; ++ unsigned int i; ++ static const unsigned int offsets[] = { ++ AT803X_LOC_MAC_ADDR_32_47_OFFSET, ++ AT803X_LOC_MAC_ADDR_16_31_OFFSET, ++ AT803X_LOC_MAC_ADDR_0_15_OFFSET, ++ }; ++ ++ if (!ndev) ++ return -ENODEV; ++ ++ mac = (const u8 *)ndev->dev_addr; ++ ++ if (!is_valid_ether_addr(mac)) ++ return -EINVAL; ++ ++ for (i = 0; i < 3; i++) ++ phy_write_mmd(phydev, MDIO_MMD_PCS, offsets[i], ++ mac[(i * 2) + 1] | (mac[(i * 2)] << 8)); ++ ++ /* Enable WOL interrupt */ ++ ret = phy_modify(phydev, AT803X_INTR_ENABLE, 0, AT803X_INTR_ENABLE_WOL); ++ if (ret) ++ return ret; ++ } else { ++ /* Disable WOL interrupt */ ++ ret = phy_modify(phydev, AT803X_INTR_ENABLE, AT803X_INTR_ENABLE_WOL, 0); ++ if (ret) ++ return ret; ++ } ++ ++ /* Clear WOL status */ ++ ret = phy_read(phydev, AT803X_INTR_STATUS); ++ if (ret < 0) ++ return ret; ++ ++ /* Check if there are other interrupts except for WOL triggered when PHY is ++ * in interrupt mode, only the interrupts enabled by AT803X_INTR_ENABLE can ++ * be passed up to the interrupt PIN. ++ */ ++ irq_enabled = phy_read(phydev, AT803X_INTR_ENABLE); ++ if (irq_enabled < 0) ++ return irq_enabled; ++ ++ irq_enabled &= ~AT803X_INTR_ENABLE_WOL; ++ if (ret & irq_enabled && !phy_polling_mode(phydev)) ++ phy_trigger_machine(phydev); ++ ++ return 0; ++} ++ ++static void at803x_get_wol(struct phy_device *phydev, ++ struct ethtool_wolinfo *wol) ++{ ++ int value; ++ ++ wol->supported = WAKE_MAGIC; ++ wol->wolopts = 0; ++ ++ value = phy_read(phydev, AT803X_INTR_ENABLE); ++ if (value < 0) ++ return; ++ ++ if (value & AT803X_INTR_ENABLE_WOL) ++ wol->wolopts |= WAKE_MAGIC; ++} ++ ++static int qca83xx_get_sset_count(struct phy_device *phydev) ++{ ++ return ARRAY_SIZE(qca83xx_hw_stats); ++} ++ ++static void qca83xx_get_strings(struct phy_device *phydev, u8 *data) ++{ ++ int i; ++ ++ for (i = 0; i < ARRAY_SIZE(qca83xx_hw_stats); i++) { ++ strscpy(data + i * ETH_GSTRING_LEN, ++ qca83xx_hw_stats[i].string, ETH_GSTRING_LEN); ++ } ++} ++ ++static u64 qca83xx_get_stat(struct phy_device *phydev, int i) ++{ ++ struct at803x_hw_stat stat = qca83xx_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 qca83xx_get_stats(struct phy_device *phydev, ++ struct ethtool_stats *stats, u64 *data) ++{ ++ int i; ++ ++ for (i = 0; i < ARRAY_SIZE(qca83xx_hw_stats); i++) ++ data[i] = qca83xx_get_stat(phydev, i); ++} ++ ++static int at803x_suspend(struct phy_device *phydev) ++{ ++ int value; ++ int wol_enabled; ++ ++ value = phy_read(phydev, AT803X_INTR_ENABLE); ++ wol_enabled = value & AT803X_INTR_ENABLE_WOL; ++ ++ if (wol_enabled) ++ value = BMCR_ISOLATE; ++ else ++ value = BMCR_PDOWN; ++ ++ phy_modify(phydev, MII_BMCR, 0, value); ++ ++ return 0; ++} ++ ++static int at803x_resume(struct phy_device *phydev) ++{ ++ return phy_modify(phydev, MII_BMCR, BMCR_PDOWN | BMCR_ISOLATE, 0); ++} ++ ++static int at803x_parse_dt(struct phy_device *phydev) ++{ ++ struct device_node *node = phydev->mdio.dev.of_node; ++ struct at803x_priv *priv = phydev->priv; ++ u32 freq, strength, tw; ++ unsigned int sel; ++ int ret; ++ ++ if (!IS_ENABLED(CONFIG_OF_MDIO)) ++ return 0; ++ ++ if (of_property_read_bool(node, "qca,disable-smarteee")) ++ priv->flags |= AT803X_DISABLE_SMARTEEE; ++ ++ if (of_property_read_bool(node, "qca,disable-hibernation-mode")) ++ priv->flags |= AT803X_DISABLE_HIBERNATION_MODE; ++ ++ if (!of_property_read_u32(node, "qca,smarteee-tw-us-1g", &tw)) { ++ if (!tw || tw > 255) { ++ phydev_err(phydev, "invalid qca,smarteee-tw-us-1g\n"); ++ return -EINVAL; ++ } ++ priv->smarteee_lpi_tw_1g = tw; ++ } ++ ++ if (!of_property_read_u32(node, "qca,smarteee-tw-us-100m", &tw)) { ++ if (!tw || tw > 255) { ++ phydev_err(phydev, "invalid qca,smarteee-tw-us-100m\n"); ++ return -EINVAL; ++ } ++ priv->smarteee_lpi_tw_100m = tw; ++ } ++ ++ ret = of_property_read_u32(node, "qca,clk-out-frequency", &freq); ++ if (!ret) { ++ switch (freq) { ++ case 25000000: ++ sel = AT803X_CLK_OUT_25MHZ_XTAL; ++ break; ++ case 50000000: ++ sel = AT803X_CLK_OUT_50MHZ_PLL; ++ break; ++ case 62500000: ++ sel = AT803X_CLK_OUT_62_5MHZ_PLL; ++ break; ++ case 125000000: ++ sel = AT803X_CLK_OUT_125MHZ_PLL; ++ break; ++ default: ++ phydev_err(phydev, "invalid qca,clk-out-frequency\n"); ++ return -EINVAL; ++ } ++ ++ priv->clk_25m_reg |= FIELD_PREP(AT803X_CLK_OUT_MASK, sel); ++ priv->clk_25m_mask |= AT803X_CLK_OUT_MASK; ++ } ++ ++ ret = of_property_read_u32(node, "qca,clk-out-strength", &strength); ++ if (!ret) { ++ priv->clk_25m_mask |= AT803X_CLK_OUT_STRENGTH_MASK; ++ switch (strength) { ++ case AR803X_STRENGTH_FULL: ++ priv->clk_25m_reg |= AT803X_CLK_OUT_STRENGTH_FULL; ++ break; ++ case AR803X_STRENGTH_HALF: ++ priv->clk_25m_reg |= AT803X_CLK_OUT_STRENGTH_HALF; ++ break; ++ case AR803X_STRENGTH_QUARTER: ++ priv->clk_25m_reg |= AT803X_CLK_OUT_STRENGTH_QUARTER; ++ break; ++ default: ++ phydev_err(phydev, "invalid qca,clk-out-strength\n"); ++ return -EINVAL; ++ } ++ } ++ ++ return 0; ++} ++ ++static int at803x_probe(struct phy_device *phydev) ++{ ++ struct device *dev = &phydev->mdio.dev; ++ struct at803x_priv *priv; ++ int ret; ++ ++ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); ++ if (!priv) ++ return -ENOMEM; ++ ++ /* Init LED polarity mode to -1 */ ++ priv->led_polarity_mode = -1; ++ ++ phydev->priv = priv; ++ ++ ret = at803x_parse_dt(phydev); ++ if (ret) ++ return ret; ++ ++ return 0; ++} ++ ++static int at803x_get_features(struct phy_device *phydev) ++{ ++ struct at803x_priv *priv = phydev->priv; ++ int err; ++ ++ err = genphy_read_abilities(phydev); ++ if (err) ++ return err; ++ ++ if (phydev->drv->phy_id != ATH8031_PHY_ID) ++ return 0; ++ ++ /* AR8031/AR8033 have different status registers ++ * for copper and fiber operation. However, the ++ * extended status register is the same for both ++ * operation modes. ++ * ++ * As a result of that, ESTATUS_1000_XFULL is set ++ * to 1 even when operating in copper TP mode. ++ * ++ * Remove this mode from the supported link modes ++ * when not operating in 1000BaseX mode. ++ */ ++ if (!priv->is_1000basex) ++ linkmode_clear_bit(ETHTOOL_LINK_MODE_1000baseX_Full_BIT, ++ phydev->supported); ++ ++ return 0; ++} ++ ++static int at803x_smarteee_config(struct phy_device *phydev) ++{ ++ struct at803x_priv *priv = phydev->priv; ++ u16 mask = 0, val = 0; ++ int ret; ++ ++ if (priv->flags & AT803X_DISABLE_SMARTEEE) ++ return phy_modify_mmd(phydev, MDIO_MMD_PCS, ++ AT803X_MMD3_SMARTEEE_CTL3, ++ AT803X_MMD3_SMARTEEE_CTL3_LPI_EN, 0); ++ ++ if (priv->smarteee_lpi_tw_1g) { ++ mask |= 0xff00; ++ val |= priv->smarteee_lpi_tw_1g << 8; ++ } ++ if (priv->smarteee_lpi_tw_100m) { ++ mask |= 0x00ff; ++ val |= priv->smarteee_lpi_tw_100m; ++ } ++ if (!mask) ++ return 0; ++ ++ ret = phy_modify_mmd(phydev, MDIO_MMD_PCS, AT803X_MMD3_SMARTEEE_CTL1, ++ mask, val); ++ if (ret) ++ return ret; ++ ++ return phy_modify_mmd(phydev, MDIO_MMD_PCS, AT803X_MMD3_SMARTEEE_CTL3, ++ AT803X_MMD3_SMARTEEE_CTL3_LPI_EN, ++ AT803X_MMD3_SMARTEEE_CTL3_LPI_EN); ++} ++ ++static int at803x_clk_out_config(struct phy_device *phydev) ++{ ++ struct at803x_priv *priv = phydev->priv; ++ ++ if (!priv->clk_25m_mask) ++ return 0; ++ ++ return phy_modify_mmd(phydev, MDIO_MMD_AN, AT803X_MMD7_CLK25M, ++ priv->clk_25m_mask, priv->clk_25m_reg); ++} ++ ++static int at8031_pll_config(struct phy_device *phydev) ++{ ++ struct at803x_priv *priv = phydev->priv; ++ ++ /* The default after hardware reset is PLL OFF. After a soft reset, the ++ * values are retained. ++ */ ++ if (priv->flags & AT803X_KEEP_PLL_ENABLED) ++ return at803x_debug_reg_mask(phydev, AT803X_DEBUG_REG_1F, ++ 0, AT803X_DEBUG_PLL_ON); ++ else ++ return at803x_debug_reg_mask(phydev, AT803X_DEBUG_REG_1F, ++ AT803X_DEBUG_PLL_ON, 0); ++} ++ ++static int at803x_hibernation_mode_config(struct phy_device *phydev) ++{ ++ struct at803x_priv *priv = phydev->priv; ++ ++ /* The default after hardware reset is hibernation mode enabled. After ++ * software reset, the value is retained. ++ */ ++ if (!(priv->flags & AT803X_DISABLE_HIBERNATION_MODE)) ++ return 0; ++ ++ return at803x_debug_reg_mask(phydev, AT803X_DEBUG_REG_HIB_CTRL, ++ AT803X_DEBUG_HIB_CTRL_PS_HIB_EN, 0); ++} ++ ++static int at803x_config_init(struct phy_device *phydev) ++{ ++ int ret; ++ ++ /* The RX and TX delay default is: ++ * after HW reset: RX delay enabled and TX delay disabled ++ * after SW reset: RX delay enabled, while TX delay retains the ++ * value before reset. ++ */ ++ if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID || ++ phydev->interface == PHY_INTERFACE_MODE_RGMII_RXID) ++ ret = at803x_enable_rx_delay(phydev); ++ else ++ ret = at803x_disable_rx_delay(phydev); ++ if (ret < 0) ++ return ret; ++ ++ if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID || ++ phydev->interface == PHY_INTERFACE_MODE_RGMII_TXID) ++ ret = at803x_enable_tx_delay(phydev); ++ else ++ ret = at803x_disable_tx_delay(phydev); ++ if (ret < 0) ++ return ret; ++ ++ ret = at803x_smarteee_config(phydev); ++ if (ret < 0) ++ return ret; ++ ++ ret = at803x_clk_out_config(phydev); ++ if (ret < 0) ++ return ret; ++ ++ ret = at803x_hibernation_mode_config(phydev); ++ if (ret < 0) ++ return ret; ++ ++ /* Ar803x extended next page bit is enabled by default. Cisco ++ * multigig switches read this bit and attempt to negotiate 10Gbps ++ * rates even if the next page bit is disabled. This is incorrect ++ * behaviour but we still need to accommodate it. XNP is only needed ++ * for 10Gbps support, so disable XNP. ++ */ ++ return phy_modify(phydev, MII_ADVERTISE, MDIO_AN_CTRL1_XNP, 0); ++} ++ ++static int at803x_ack_interrupt(struct phy_device *phydev) ++{ ++ int err; ++ ++ err = phy_read(phydev, AT803X_INTR_STATUS); ++ ++ return (err < 0) ? err : 0; ++} ++ ++static int at803x_config_intr(struct phy_device *phydev) ++{ ++ int err; ++ int value; ++ ++ value = phy_read(phydev, AT803X_INTR_ENABLE); ++ ++ if (phydev->interrupts == PHY_INTERRUPT_ENABLED) { ++ /* Clear any pending interrupts */ ++ err = at803x_ack_interrupt(phydev); ++ if (err) ++ return err; ++ ++ value |= AT803X_INTR_ENABLE_AUTONEG_ERR; ++ value |= AT803X_INTR_ENABLE_SPEED_CHANGED; ++ value |= AT803X_INTR_ENABLE_DUPLEX_CHANGED; ++ value |= AT803X_INTR_ENABLE_LINK_FAIL; ++ value |= AT803X_INTR_ENABLE_LINK_SUCCESS; ++ ++ err = phy_write(phydev, AT803X_INTR_ENABLE, value); ++ } else { ++ err = phy_write(phydev, AT803X_INTR_ENABLE, 0); ++ if (err) ++ return err; ++ ++ /* Clear any pending interrupts */ ++ err = at803x_ack_interrupt(phydev); ++ } ++ ++ return err; ++} ++ ++static irqreturn_t at803x_handle_interrupt(struct phy_device *phydev) ++{ ++ int irq_status, int_enabled; ++ ++ irq_status = phy_read(phydev, AT803X_INTR_STATUS); ++ if (irq_status < 0) { ++ phy_error(phydev); ++ return IRQ_NONE; ++ } ++ ++ /* Read the current enabled interrupts */ ++ int_enabled = phy_read(phydev, AT803X_INTR_ENABLE); ++ if (int_enabled < 0) { ++ phy_error(phydev); ++ return IRQ_NONE; ++ } ++ ++ /* See if this was one of our enabled interrupts */ ++ if (!(irq_status & int_enabled)) ++ return IRQ_NONE; ++ ++ phy_trigger_machine(phydev); ++ ++ return IRQ_HANDLED; ++} ++ ++static void at803x_link_change_notify(struct phy_device *phydev) ++{ ++ /* ++ * Conduct a hardware reset for AT8030 every time a link loss is ++ * signalled. This is necessary to circumvent a hardware bug that ++ * occurs when the cable is unplugged while TX packets are pending ++ * in the FIFO. In such cases, the FIFO enters an error mode it ++ * cannot recover from by software. ++ */ ++ if (phydev->state == PHY_NOLINK && phydev->mdio.reset_gpio) { ++ struct at803x_context context; ++ ++ at803x_context_save(phydev, &context); ++ ++ phy_device_reset(phydev, 1); ++ usleep_range(1000, 2000); ++ phy_device_reset(phydev, 0); ++ usleep_range(1000, 2000); ++ ++ at803x_context_restore(phydev, &context); ++ ++ phydev_dbg(phydev, "%s(): phy was reset\n", __func__); ++ } ++} ++ ++static int at803x_read_specific_status(struct phy_device *phydev, ++ struct at803x_ss_mask ss_mask) ++{ ++ int ss; ++ ++ /* Read the AT8035 PHY-Specific Status register, which indicates the ++ * speed and duplex that the PHY is actually using, irrespective of ++ * whether we are in autoneg mode or not. ++ */ ++ ss = phy_read(phydev, AT803X_SPECIFIC_STATUS); ++ if (ss < 0) ++ return ss; ++ ++ if (ss & AT803X_SS_SPEED_DUPLEX_RESOLVED) { ++ int sfc, speed; ++ ++ sfc = phy_read(phydev, AT803X_SPECIFIC_FUNCTION_CONTROL); ++ if (sfc < 0) ++ return sfc; ++ ++ speed = ss & ss_mask.speed_mask; ++ speed >>= ss_mask.speed_shift; ++ ++ switch (speed) { ++ case AT803X_SS_SPEED_10: ++ phydev->speed = SPEED_10; ++ break; ++ case AT803X_SS_SPEED_100: ++ phydev->speed = SPEED_100; ++ break; ++ case AT803X_SS_SPEED_1000: ++ phydev->speed = SPEED_1000; ++ break; ++ case QCA808X_SS_SPEED_2500: ++ phydev->speed = SPEED_2500; ++ break; ++ } ++ if (ss & AT803X_SS_DUPLEX) ++ phydev->duplex = DUPLEX_FULL; ++ else ++ phydev->duplex = DUPLEX_HALF; ++ ++ if (ss & AT803X_SS_MDIX) ++ phydev->mdix = ETH_TP_MDI_X; ++ else ++ phydev->mdix = ETH_TP_MDI; ++ ++ switch (FIELD_GET(AT803X_SFC_MDI_CROSSOVER_MODE_M, sfc)) { ++ case AT803X_SFC_MANUAL_MDI: ++ phydev->mdix_ctrl = ETH_TP_MDI; ++ break; ++ case AT803X_SFC_MANUAL_MDIX: ++ phydev->mdix_ctrl = ETH_TP_MDI_X; ++ break; ++ case AT803X_SFC_AUTOMATIC_CROSSOVER: ++ phydev->mdix_ctrl = ETH_TP_MDI_AUTO; ++ break; ++ } ++ } ++ ++ return 0; ++} ++ ++static int at803x_read_status(struct phy_device *phydev) ++{ ++ struct at803x_ss_mask ss_mask = { 0 }; ++ int err, old_link = phydev->link; ++ ++ /* Update the link, but return if there was an error */ ++ err = genphy_update_link(phydev); ++ if (err) ++ return err; ++ ++ /* why bother the PHY if nothing can have changed */ ++ if (phydev->autoneg == AUTONEG_ENABLE && old_link && phydev->link) ++ return 0; ++ ++ phydev->speed = SPEED_UNKNOWN; ++ phydev->duplex = DUPLEX_UNKNOWN; ++ phydev->pause = 0; ++ phydev->asym_pause = 0; ++ ++ err = genphy_read_lpa(phydev); ++ if (err < 0) ++ return err; ++ ++ ss_mask.speed_mask = AT803X_SS_SPEED_MASK; ++ ss_mask.speed_shift = __bf_shf(AT803X_SS_SPEED_MASK); ++ err = at803x_read_specific_status(phydev, ss_mask); ++ if (err < 0) ++ return err; ++ ++ if (phydev->autoneg == AUTONEG_ENABLE && phydev->autoneg_complete) ++ phy_resolve_aneg_pause(phydev); ++ ++ return 0; ++} ++ ++static int at803x_config_mdix(struct phy_device *phydev, u8 ctrl) ++{ ++ u16 val; ++ ++ switch (ctrl) { ++ case ETH_TP_MDI: ++ val = AT803X_SFC_MANUAL_MDI; ++ break; ++ case ETH_TP_MDI_X: ++ val = AT803X_SFC_MANUAL_MDIX; ++ break; ++ case ETH_TP_MDI_AUTO: ++ val = AT803X_SFC_AUTOMATIC_CROSSOVER; ++ break; ++ default: ++ return 0; ++ } ++ ++ return phy_modify_changed(phydev, AT803X_SPECIFIC_FUNCTION_CONTROL, ++ AT803X_SFC_MDI_CROSSOVER_MODE_M, ++ FIELD_PREP(AT803X_SFC_MDI_CROSSOVER_MODE_M, val)); ++} ++ ++static int at803x_prepare_config_aneg(struct phy_device *phydev) ++{ ++ int ret; ++ ++ ret = at803x_config_mdix(phydev, phydev->mdix_ctrl); ++ if (ret < 0) ++ return ret; ++ ++ /* Changes of the midx bits are disruptive to the normal operation; ++ * therefore any changes to these registers must be followed by a ++ * software reset to take effect. ++ */ ++ if (ret == 1) { ++ ret = genphy_soft_reset(phydev); ++ if (ret < 0) ++ return ret; ++ } ++ ++ return 0; ++} ++ ++static int at803x_config_aneg(struct phy_device *phydev) ++{ ++ struct at803x_priv *priv = phydev->priv; ++ int ret; ++ ++ ret = at803x_prepare_config_aneg(phydev); ++ if (ret) ++ return ret; ++ ++ if (priv->is_1000basex) ++ return genphy_c37_config_aneg(phydev); ++ ++ return genphy_config_aneg(phydev); ++} ++ ++static int at803x_get_downshift(struct phy_device *phydev, u8 *d) ++{ ++ int val; ++ ++ val = phy_read(phydev, AT803X_SMART_SPEED); ++ if (val < 0) ++ return val; ++ ++ if (val & AT803X_SMART_SPEED_ENABLE) ++ *d = FIELD_GET(AT803X_SMART_SPEED_RETRY_LIMIT_MASK, val) + 2; ++ else ++ *d = DOWNSHIFT_DEV_DISABLE; ++ ++ return 0; ++} ++ ++static int at803x_set_downshift(struct phy_device *phydev, u8 cnt) ++{ ++ u16 mask, set; ++ int ret; ++ ++ switch (cnt) { ++ case DOWNSHIFT_DEV_DEFAULT_COUNT: ++ cnt = AT803X_DEFAULT_DOWNSHIFT; ++ fallthrough; ++ case AT803X_MIN_DOWNSHIFT ... AT803X_MAX_DOWNSHIFT: ++ set = AT803X_SMART_SPEED_ENABLE | ++ AT803X_SMART_SPEED_BYPASS_TIMER | ++ FIELD_PREP(AT803X_SMART_SPEED_RETRY_LIMIT_MASK, cnt - 2); ++ mask = AT803X_SMART_SPEED_RETRY_LIMIT_MASK; ++ break; ++ case DOWNSHIFT_DEV_DISABLE: ++ set = 0; ++ mask = AT803X_SMART_SPEED_ENABLE | ++ AT803X_SMART_SPEED_BYPASS_TIMER; ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ ret = phy_modify_changed(phydev, AT803X_SMART_SPEED, mask, set); ++ ++ /* After changing the smart speed settings, we need to perform a ++ * software reset, use phy_init_hw() to make sure we set the ++ * reapply any values which might got lost during software reset. ++ */ ++ if (ret == 1) ++ ret = phy_init_hw(phydev); ++ ++ return ret; ++} ++ ++static int at803x_get_tunable(struct phy_device *phydev, ++ struct ethtool_tunable *tuna, void *data) ++{ ++ switch (tuna->id) { ++ case ETHTOOL_PHY_DOWNSHIFT: ++ return at803x_get_downshift(phydev, data); ++ default: ++ return -EOPNOTSUPP; ++ } ++} ++ ++static int at803x_set_tunable(struct phy_device *phydev, ++ struct ethtool_tunable *tuna, const void *data) ++{ ++ switch (tuna->id) { ++ case ETHTOOL_PHY_DOWNSHIFT: ++ return at803x_set_downshift(phydev, *(const u8 *)data); ++ default: ++ return -EOPNOTSUPP; ++ } ++} ++ ++static int at803x_cable_test_result_trans(u16 status) ++{ ++ switch (FIELD_GET(AT803X_CDT_STATUS_STAT_MASK, status)) { ++ case AT803X_CDT_STATUS_STAT_NORMAL: ++ return ETHTOOL_A_CABLE_RESULT_CODE_OK; ++ case AT803X_CDT_STATUS_STAT_SHORT: ++ return ETHTOOL_A_CABLE_RESULT_CODE_SAME_SHORT; ++ case AT803X_CDT_STATUS_STAT_OPEN: ++ return ETHTOOL_A_CABLE_RESULT_CODE_OPEN; ++ case AT803X_CDT_STATUS_STAT_FAIL: ++ default: ++ return ETHTOOL_A_CABLE_RESULT_CODE_UNSPEC; ++ } ++} ++ ++static bool at803x_cdt_test_failed(u16 status) ++{ ++ return FIELD_GET(AT803X_CDT_STATUS_STAT_MASK, status) == ++ AT803X_CDT_STATUS_STAT_FAIL; ++} ++ ++static bool at803x_cdt_fault_length_valid(u16 status) ++{ ++ switch (FIELD_GET(AT803X_CDT_STATUS_STAT_MASK, status)) { ++ case AT803X_CDT_STATUS_STAT_OPEN: ++ case AT803X_CDT_STATUS_STAT_SHORT: ++ return true; ++ } ++ return false; ++} ++ ++static int at803x_cdt_fault_length(int dt) ++{ ++ /* According to the datasheet the distance to the fault is ++ * DELTA_TIME * 0.824 meters. ++ * ++ * The author suspect the correct formula is: ++ * ++ * fault_distance = DELTA_TIME * (c * VF) / 125MHz / 2 ++ * ++ * where c is the speed of light, VF is the velocity factor of ++ * the twisted pair cable, 125MHz the counter frequency and ++ * we need to divide by 2 because the hardware will measure the ++ * round trip time to the fault and back to the PHY. ++ * ++ * With a VF of 0.69 we get the factor 0.824 mentioned in the ++ * datasheet. ++ */ ++ return (dt * 824) / 10; ++} ++ ++static int at803x_cdt_start(struct phy_device *phydev, ++ u32 cdt_start) ++{ ++ return phy_write(phydev, AT803X_CDT, cdt_start); ++} ++ ++static int at803x_cdt_wait_for_completion(struct phy_device *phydev, ++ u32 cdt_en) ++{ ++ int val, ret; ++ ++ /* One test run takes about 25ms */ ++ ret = phy_read_poll_timeout(phydev, AT803X_CDT, val, ++ !(val & cdt_en), ++ 30000, 100000, true); ++ ++ return ret < 0 ? ret : 0; ++} ++ ++static int at803x_cable_test_one_pair(struct phy_device *phydev, int pair) ++{ ++ static const int ethtool_pair[] = { ++ ETHTOOL_A_CABLE_PAIR_A, ++ ETHTOOL_A_CABLE_PAIR_B, ++ ETHTOOL_A_CABLE_PAIR_C, ++ ETHTOOL_A_CABLE_PAIR_D, ++ }; ++ int ret, val; ++ ++ val = FIELD_PREP(AT803X_CDT_MDI_PAIR_MASK, pair) | ++ AT803X_CDT_ENABLE_TEST; ++ ret = at803x_cdt_start(phydev, val); ++ if (ret) ++ return ret; ++ ++ ret = at803x_cdt_wait_for_completion(phydev, AT803X_CDT_ENABLE_TEST); ++ if (ret) ++ return ret; ++ ++ val = phy_read(phydev, AT803X_CDT_STATUS); ++ if (val < 0) ++ return val; ++ ++ if (at803x_cdt_test_failed(val)) ++ return 0; ++ ++ ethnl_cable_test_result(phydev, ethtool_pair[pair], ++ at803x_cable_test_result_trans(val)); ++ ++ if (at803x_cdt_fault_length_valid(val)) { ++ val = FIELD_GET(AT803X_CDT_STATUS_DELTA_TIME_MASK, val); ++ ethnl_cable_test_fault_length(phydev, ethtool_pair[pair], ++ at803x_cdt_fault_length(val)); ++ } ++ ++ return 1; ++} ++ ++static int at803x_cable_test_get_status(struct phy_device *phydev, ++ bool *finished, unsigned long pair_mask) ++{ ++ int retries = 20; ++ int pair, ret; ++ ++ *finished = false; ++ ++ /* According to the datasheet the CDT can be performed when ++ * there is no link partner or when the link partner is ++ * auto-negotiating. Starting the test will restart the AN ++ * automatically. It seems that doing this repeatedly we will ++ * get a slot where our link partner won't disturb our ++ * measurement. ++ */ ++ while (pair_mask && retries--) { ++ for_each_set_bit(pair, &pair_mask, 4) { ++ ret = at803x_cable_test_one_pair(phydev, pair); ++ if (ret < 0) ++ return ret; ++ if (ret) ++ clear_bit(pair, &pair_mask); ++ } ++ if (pair_mask) ++ msleep(250); ++ } ++ ++ *finished = true; ++ ++ return 0; ++} ++ ++static void at803x_cable_test_autoneg(struct phy_device *phydev) ++{ ++ /* Enable auto-negotiation, but advertise no capabilities, no link ++ * will be established. A restart of the auto-negotiation is not ++ * required, because the cable test will automatically break the link. ++ */ ++ phy_write(phydev, MII_BMCR, BMCR_ANENABLE); ++ phy_write(phydev, MII_ADVERTISE, ADVERTISE_CSMA); ++} ++ ++static int at803x_cable_test_start(struct phy_device *phydev) ++{ ++ at803x_cable_test_autoneg(phydev); ++ /* we do all the (time consuming) work later */ ++ return 0; ++} ++ ++static int at8031_rgmii_reg_set_voltage_sel(struct regulator_dev *rdev, ++ unsigned int selector) ++{ ++ struct phy_device *phydev = rdev_get_drvdata(rdev); ++ ++ if (selector) ++ return at803x_debug_reg_mask(phydev, AT803X_DEBUG_REG_1F, ++ 0, AT803X_DEBUG_RGMII_1V8); ++ else ++ return at803x_debug_reg_mask(phydev, AT803X_DEBUG_REG_1F, ++ AT803X_DEBUG_RGMII_1V8, 0); ++} ++ ++static int at8031_rgmii_reg_get_voltage_sel(struct regulator_dev *rdev) ++{ ++ struct phy_device *phydev = rdev_get_drvdata(rdev); ++ int val; ++ ++ val = at803x_debug_reg_read(phydev, AT803X_DEBUG_REG_1F); ++ if (val < 0) ++ return val; ++ ++ return (val & AT803X_DEBUG_RGMII_1V8) ? 1 : 0; ++} ++ ++static const struct regulator_ops vddio_regulator_ops = { ++ .list_voltage = regulator_list_voltage_table, ++ .set_voltage_sel = at8031_rgmii_reg_set_voltage_sel, ++ .get_voltage_sel = at8031_rgmii_reg_get_voltage_sel, ++}; ++ ++static const unsigned int vddio_voltage_table[] = { ++ 1500000, ++ 1800000, ++}; ++ ++static const struct regulator_desc vddio_desc = { ++ .name = "vddio", ++ .of_match = of_match_ptr("vddio-regulator"), ++ .n_voltages = ARRAY_SIZE(vddio_voltage_table), ++ .volt_table = vddio_voltage_table, ++ .ops = &vddio_regulator_ops, ++ .type = REGULATOR_VOLTAGE, ++ .owner = THIS_MODULE, ++}; ++ ++static const struct regulator_ops vddh_regulator_ops = { ++}; ++ ++static const struct regulator_desc vddh_desc = { ++ .name = "vddh", ++ .of_match = of_match_ptr("vddh-regulator"), ++ .n_voltages = 1, ++ .fixed_uV = 2500000, ++ .ops = &vddh_regulator_ops, ++ .type = REGULATOR_VOLTAGE, ++ .owner = THIS_MODULE, ++}; ++ ++static int at8031_register_regulators(struct phy_device *phydev) ++{ ++ struct at803x_priv *priv = phydev->priv; ++ struct device *dev = &phydev->mdio.dev; ++ struct regulator_config config = { }; ++ ++ config.dev = dev; ++ config.driver_data = phydev; ++ ++ priv->vddio_rdev = devm_regulator_register(dev, &vddio_desc, &config); ++ if (IS_ERR(priv->vddio_rdev)) { ++ phydev_err(phydev, "failed to register VDDIO regulator\n"); ++ return PTR_ERR(priv->vddio_rdev); ++ } ++ ++ priv->vddh_rdev = devm_regulator_register(dev, &vddh_desc, &config); ++ if (IS_ERR(priv->vddh_rdev)) { ++ phydev_err(phydev, "failed to register VDDH regulator\n"); ++ return PTR_ERR(priv->vddh_rdev); ++ } ++ ++ return 0; ++} ++ ++static int at8031_sfp_insert(void *upstream, const struct sfp_eeprom_id *id) ++{ ++ struct phy_device *phydev = upstream; ++ __ETHTOOL_DECLARE_LINK_MODE_MASK(phy_support); ++ __ETHTOOL_DECLARE_LINK_MODE_MASK(sfp_support); ++ DECLARE_PHY_INTERFACE_MASK(interfaces); ++ phy_interface_t iface; ++ ++ linkmode_zero(phy_support); ++ phylink_set(phy_support, 1000baseX_Full); ++ phylink_set(phy_support, 1000baseT_Full); ++ phylink_set(phy_support, Autoneg); ++ phylink_set(phy_support, Pause); ++ phylink_set(phy_support, Asym_Pause); ++ ++ linkmode_zero(sfp_support); ++ sfp_parse_support(phydev->sfp_bus, id, sfp_support, interfaces); ++ /* Some modules support 10G modes as well as others we support. ++ * Mask out non-supported modes so the correct interface is picked. ++ */ ++ linkmode_and(sfp_support, phy_support, sfp_support); ++ ++ if (linkmode_empty(sfp_support)) { ++ dev_err(&phydev->mdio.dev, "incompatible SFP module inserted\n"); ++ return -EINVAL; ++ } ++ ++ iface = sfp_select_interface(phydev->sfp_bus, sfp_support); ++ ++ /* Only 1000Base-X is supported by AR8031/8033 as the downstream SerDes ++ * interface for use with SFP modules. ++ * However, some copper modules detected as having a preferred SGMII ++ * interface do default to and function in 1000Base-X mode, so just ++ * print a warning and allow such modules, as they may have some chance ++ * of working. ++ */ ++ if (iface == PHY_INTERFACE_MODE_SGMII) ++ dev_warn(&phydev->mdio.dev, "module may not function if 1000Base-X not supported\n"); ++ else if (iface != PHY_INTERFACE_MODE_1000BASEX) ++ return -EINVAL; ++ ++ return 0; ++} ++ ++static const struct sfp_upstream_ops at8031_sfp_ops = { ++ .attach = phy_sfp_attach, ++ .detach = phy_sfp_detach, ++ .module_insert = at8031_sfp_insert, ++}; ++ ++static int at8031_parse_dt(struct phy_device *phydev) ++{ ++ struct device_node *node = phydev->mdio.dev.of_node; ++ struct at803x_priv *priv = phydev->priv; ++ int ret; ++ ++ if (of_property_read_bool(node, "qca,keep-pll-enabled")) ++ priv->flags |= AT803X_KEEP_PLL_ENABLED; ++ ++ ret = at8031_register_regulators(phydev); ++ if (ret < 0) ++ return ret; ++ ++ ret = devm_regulator_get_enable_optional(&phydev->mdio.dev, ++ "vddio"); ++ if (ret) { ++ phydev_err(phydev, "failed to get VDDIO regulator\n"); ++ return ret; ++ } ++ ++ /* Only AR8031/8033 support 1000Base-X for SFP modules */ ++ return phy_sfp_probe(phydev, &at8031_sfp_ops); ++} ++ ++static int at8031_probe(struct phy_device *phydev) ++{ ++ struct at803x_priv *priv = phydev->priv; ++ int mode_cfg; ++ int ccr; ++ int ret; ++ ++ ret = at803x_probe(phydev); ++ if (ret) ++ return ret; ++ ++ /* Only supported on AR8031/AR8033, the AR8030/AR8035 use strapping ++ * options. ++ */ ++ ret = at8031_parse_dt(phydev); ++ if (ret) ++ return ret; ++ ++ ccr = phy_read(phydev, AT803X_REG_CHIP_CONFIG); ++ if (ccr < 0) ++ return ccr; ++ mode_cfg = ccr & AT803X_MODE_CFG_MASK; ++ ++ switch (mode_cfg) { ++ case AT803X_MODE_CFG_BX1000_RGMII_50OHM: ++ case AT803X_MODE_CFG_BX1000_RGMII_75OHM: ++ priv->is_1000basex = true; ++ fallthrough; ++ case AT803X_MODE_CFG_FX100_RGMII_50OHM: ++ case AT803X_MODE_CFG_FX100_RGMII_75OHM: ++ priv->is_fiber = true; ++ break; ++ } ++ ++ /* Disable WoL in 1588 register which is enabled ++ * by default ++ */ ++ return phy_modify_mmd(phydev, MDIO_MMD_PCS, ++ AT803X_PHY_MMD3_WOL_CTRL, ++ AT803X_WOL_EN, 0); ++} ++ ++static int at8031_config_init(struct phy_device *phydev) ++{ ++ struct at803x_priv *priv = phydev->priv; ++ int ret; ++ ++ /* Some bootloaders leave the fiber page selected. ++ * Switch to the appropriate page (fiber or copper), as otherwise we ++ * read the PHY capabilities from the wrong page. ++ */ ++ phy_lock_mdio_bus(phydev); ++ ret = at803x_write_page(phydev, ++ priv->is_fiber ? AT803X_PAGE_FIBER : ++ AT803X_PAGE_COPPER); ++ phy_unlock_mdio_bus(phydev); ++ if (ret) ++ return ret; ++ ++ ret = at8031_pll_config(phydev); ++ if (ret < 0) ++ return ret; ++ ++ return at803x_config_init(phydev); ++} ++ ++static int at8031_set_wol(struct phy_device *phydev, ++ struct ethtool_wolinfo *wol) ++{ ++ int ret; ++ ++ /* First setup MAC address and enable WOL interrupt */ ++ ret = at803x_set_wol(phydev, wol); ++ if (ret) ++ return ret; ++ ++ if (wol->wolopts & WAKE_MAGIC) ++ /* Enable WOL function for 1588 */ ++ ret = phy_modify_mmd(phydev, MDIO_MMD_PCS, ++ AT803X_PHY_MMD3_WOL_CTRL, ++ 0, AT803X_WOL_EN); ++ else ++ /* Disable WoL function for 1588 */ ++ ret = phy_modify_mmd(phydev, MDIO_MMD_PCS, ++ AT803X_PHY_MMD3_WOL_CTRL, ++ AT803X_WOL_EN, 0); ++ ++ return ret; ++} ++ ++static int at8031_config_intr(struct phy_device *phydev) ++{ ++ struct at803x_priv *priv = phydev->priv; ++ int err, value = 0; ++ ++ if (phydev->interrupts == PHY_INTERRUPT_ENABLED && ++ priv->is_fiber) { ++ /* Clear any pending interrupts */ ++ err = at803x_ack_interrupt(phydev); ++ if (err) ++ return err; ++ ++ value |= AT803X_INTR_ENABLE_LINK_FAIL_BX; ++ value |= AT803X_INTR_ENABLE_LINK_SUCCESS_BX; ++ ++ err = phy_set_bits(phydev, AT803X_INTR_ENABLE, value); ++ if (err) ++ return err; ++ } ++ ++ return at803x_config_intr(phydev); ++} ++ ++/* AR8031 and AR8033 share the same read status logic */ ++static int at8031_read_status(struct phy_device *phydev) ++{ ++ struct at803x_priv *priv = phydev->priv; ++ ++ if (priv->is_1000basex) ++ return genphy_c37_read_status(phydev); ++ ++ return at803x_read_status(phydev); ++} ++ ++/* AR8031 and AR8035 share the same cable test get status reg */ ++static int at8031_cable_test_get_status(struct phy_device *phydev, ++ bool *finished) ++{ ++ return at803x_cable_test_get_status(phydev, finished, 0xf); ++} ++ ++/* AR8031 and AR8035 share the same cable test start logic */ ++static int at8031_cable_test_start(struct phy_device *phydev) ++{ ++ at803x_cable_test_autoneg(phydev); ++ phy_write(phydev, MII_CTRL1000, 0); ++ /* we do all the (time consuming) work later */ ++ return 0; ++} ++ ++/* AR8032, AR9331 and QCA9561 share the same cable test get status reg */ ++static int at8032_cable_test_get_status(struct phy_device *phydev, ++ bool *finished) ++{ ++ return at803x_cable_test_get_status(phydev, finished, 0x3); ++} ++ ++static int at8035_parse_dt(struct phy_device *phydev) ++{ ++ struct at803x_priv *priv = phydev->priv; ++ ++ /* Mask is set by the generic at803x_parse_dt ++ * if property is set. Assume property is set ++ * with the mask not zero. ++ */ ++ if (priv->clk_25m_mask) { ++ /* Fixup for the AR8030/AR8035. This chip has another mask and ++ * doesn't support the DSP reference. Eg. the lowest bit of the ++ * mask. The upper two bits select the same frequencies. Mask ++ * the lowest bit here. ++ * ++ * Warning: ++ * There was no datasheet for the AR8030 available so this is ++ * just a guess. But the AR8035 is listed as pin compatible ++ * to the AR8030 so there might be a good chance it works on ++ * the AR8030 too. ++ */ ++ priv->clk_25m_reg &= AT8035_CLK_OUT_MASK; ++ priv->clk_25m_mask &= AT8035_CLK_OUT_MASK; ++ } ++ ++ return 0; ++} ++ ++/* AR8030 and AR8035 shared the same special mask for clk_25m */ ++static int at8035_probe(struct phy_device *phydev) ++{ ++ int ret; ++ ++ ret = at803x_probe(phydev); ++ if (ret) ++ return ret; ++ ++ return at8035_parse_dt(phydev); ++} ++ ++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_ANALOG_TEST_CTRL, 0x02ea); ++ /* Turn on Gigabit clock */ ++ at803x_debug_reg_write(phydev, AT803X_DEBUG_REG_GREEN, 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_GREEN, 0x6860); ++ at803x_debug_reg_write(phydev, AT803X_DEBUG_SYSTEM_CTRL_MODE, 0x2c46); ++ at803x_debug_reg_write(phydev, AT803X_DEBUG_REG_3C, 0x6000); ++ break; ++ } ++ ++ /* Following original QCA sourcecode set port to prefer master */ ++ phy_set_bits(phydev, MII_CTRL1000, CTL1000_PREFER_MASTER); ++ ++ return 0; ++} ++ ++static int qca8327_config_init(struct phy_device *phydev) ++{ ++ /* QCA8327 require DAC amplitude adjustment for 100m set to +6%. ++ * Disable on init and enable only with 100m speed following ++ * qca original source code. ++ */ ++ at803x_debug_reg_mask(phydev, AT803X_DEBUG_ANALOG_TEST_CTRL, ++ QCA8327_DEBUG_MANU_CTRL_EN, 0); ++ ++ return qca83xx_config_init(phydev); ++} ++ ++static void qca83xx_link_change_notify(struct phy_device *phydev) ++{ ++ /* 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_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_ANALOG_TEST_CTRL, ++ QCA8327_DEBUG_MANU_CTRL_EN, 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; ++ ++ usleep_range(1000, 2000); ++ ++ return 0; ++} ++ ++static int qca83xx_suspend(struct phy_device *phydev) ++{ ++ 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, ++ AT803X_DEBUG_HIB_CTRL_EN_ANY_CHANGE | ++ AT803X_DEBUG_HIB_CTRL_SEL_RST_80U, 0); ++ ++ return 0; ++} ++ ++static int qca8337_suspend(struct phy_device *phydev) ++{ ++ /* Only QCA8337 support actual suspend. */ ++ genphy_suspend(phydev); ++ ++ return qca83xx_suspend(phydev); ++} ++ ++static int qca8327_suspend(struct phy_device *phydev) ++{ ++ u16 mask = 0; ++ ++ /* QCA8327 cause port unreliability when phy suspend ++ * is set. ++ */ ++ mask |= ~(BMCR_SPEED1000 | BMCR_FULLDPLX); ++ phy_modify(phydev, MII_BMCR, mask, 0); ++ ++ return qca83xx_suspend(phydev); ++} ++ ++static int qca808x_phy_fast_retrain_config(struct phy_device *phydev) ++{ ++ int ret; ++ ++ /* Enable fast retrain */ ++ ret = genphy_c45_fast_retrain(phydev, true); ++ if (ret) ++ return ret; ++ ++ phy_write_mmd(phydev, MDIO_MMD_AN, QCA808X_PHY_MMD7_TOP_OPTION1, ++ QCA808X_TOP_OPTION1_DATA); ++ phy_write_mmd(phydev, MDIO_MMD_PMAPMD, QCA808X_PHY_MMD1_MSE_THRESHOLD_20DB, ++ QCA808X_MSE_THRESHOLD_20DB_VALUE); ++ phy_write_mmd(phydev, MDIO_MMD_PMAPMD, QCA808X_PHY_MMD1_MSE_THRESHOLD_17DB, ++ QCA808X_MSE_THRESHOLD_17DB_VALUE); ++ phy_write_mmd(phydev, MDIO_MMD_PMAPMD, QCA808X_PHY_MMD1_MSE_THRESHOLD_27DB, ++ QCA808X_MSE_THRESHOLD_27DB_VALUE); ++ phy_write_mmd(phydev, MDIO_MMD_PMAPMD, QCA808X_PHY_MMD1_MSE_THRESHOLD_28DB, ++ QCA808X_MSE_THRESHOLD_28DB_VALUE); ++ phy_write_mmd(phydev, MDIO_MMD_PCS, QCA808X_PHY_MMD3_DEBUG_1, ++ QCA808X_MMD3_DEBUG_1_VALUE); ++ phy_write_mmd(phydev, MDIO_MMD_PCS, QCA808X_PHY_MMD3_DEBUG_4, ++ QCA808X_MMD3_DEBUG_4_VALUE); ++ phy_write_mmd(phydev, MDIO_MMD_PCS, QCA808X_PHY_MMD3_DEBUG_5, ++ QCA808X_MMD3_DEBUG_5_VALUE); ++ phy_write_mmd(phydev, MDIO_MMD_PCS, QCA808X_PHY_MMD3_DEBUG_3, ++ QCA808X_MMD3_DEBUG_3_VALUE); ++ phy_write_mmd(phydev, MDIO_MMD_PCS, QCA808X_PHY_MMD3_DEBUG_6, ++ QCA808X_MMD3_DEBUG_6_VALUE); ++ phy_write_mmd(phydev, MDIO_MMD_PCS, QCA808X_PHY_MMD3_DEBUG_2, ++ QCA808X_MMD3_DEBUG_2_VALUE); ++ ++ return 0; ++} ++ ++static int qca808x_phy_ms_seed_enable(struct phy_device *phydev, bool enable) ++{ ++ u16 seed_value; ++ ++ if (!enable) ++ return at803x_debug_reg_mask(phydev, QCA808X_PHY_DEBUG_LOCAL_SEED, ++ QCA808X_MASTER_SLAVE_SEED_ENABLE, 0); ++ ++ seed_value = prandom_u32_max(QCA808X_MASTER_SLAVE_SEED_RANGE); ++ return at803x_debug_reg_mask(phydev, QCA808X_PHY_DEBUG_LOCAL_SEED, ++ QCA808X_MASTER_SLAVE_SEED_CFG | QCA808X_MASTER_SLAVE_SEED_ENABLE, ++ FIELD_PREP(QCA808X_MASTER_SLAVE_SEED_CFG, seed_value) | ++ QCA808X_MASTER_SLAVE_SEED_ENABLE); ++} ++ ++static bool qca808x_is_prefer_master(struct phy_device *phydev) ++{ ++ return (phydev->master_slave_get == MASTER_SLAVE_CFG_MASTER_FORCE) || ++ (phydev->master_slave_get == MASTER_SLAVE_CFG_MASTER_PREFERRED); ++} ++ ++static bool qca808x_has_fast_retrain_or_slave_seed(struct phy_device *phydev) ++{ ++ return linkmode_test_bit(ETHTOOL_LINK_MODE_2500baseT_Full_BIT, phydev->supported); ++} ++ ++static int qca808x_config_init(struct phy_device *phydev) ++{ ++ int ret; ++ ++ /* Active adc&vga on 802.3az for the link 1000M and 100M */ ++ ret = phy_modify_mmd(phydev, MDIO_MMD_PCS, QCA808X_PHY_MMD3_ADDR_CLD_CTRL7, ++ QCA808X_8023AZ_AFE_CTRL_MASK, QCA808X_8023AZ_AFE_EN); ++ if (ret) ++ return ret; ++ ++ /* Adjust the threshold on 802.3az for the link 1000M */ ++ ret = phy_write_mmd(phydev, MDIO_MMD_PCS, ++ QCA808X_PHY_MMD3_AZ_TRAINING_CTRL, ++ QCA808X_MMD3_AZ_TRAINING_VAL); ++ if (ret) ++ return ret; ++ ++ if (qca808x_has_fast_retrain_or_slave_seed(phydev)) { ++ /* Config the fast retrain for the link 2500M */ ++ ret = qca808x_phy_fast_retrain_config(phydev); ++ if (ret) ++ return ret; ++ ++ ret = genphy_read_master_slave(phydev); ++ if (ret < 0) ++ return ret; ++ ++ if (!qca808x_is_prefer_master(phydev)) { ++ /* Enable seed and configure lower ramdom seed to make phy ++ * linked as slave mode. ++ */ ++ ret = qca808x_phy_ms_seed_enable(phydev, true); ++ if (ret) ++ return ret; ++ } ++ } ++ ++ /* Configure adc threshold as 100mv for the link 10M */ ++ return at803x_debug_reg_mask(phydev, QCA808X_PHY_DEBUG_ADC_THRESHOLD, ++ QCA808X_ADC_THRESHOLD_MASK, ++ QCA808X_ADC_THRESHOLD_100MV); ++} ++ ++static int qca808x_read_status(struct phy_device *phydev) ++{ ++ struct at803x_ss_mask ss_mask = { 0 }; ++ int ret; ++ ++ ret = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_10GBT_STAT); ++ if (ret < 0) ++ return ret; ++ ++ linkmode_mod_bit(ETHTOOL_LINK_MODE_2500baseT_Full_BIT, phydev->lp_advertising, ++ ret & MDIO_AN_10GBT_STAT_LP2_5G); ++ ++ ret = genphy_read_status(phydev); ++ if (ret) ++ return ret; ++ ++ /* qca8081 takes the different bits for speed value from at803x */ ++ ss_mask.speed_mask = QCA808X_SS_SPEED_MASK; ++ ss_mask.speed_shift = __bf_shf(QCA808X_SS_SPEED_MASK); ++ ret = at803x_read_specific_status(phydev, ss_mask); ++ if (ret < 0) ++ return ret; ++ ++ if (phydev->link) { ++ if (phydev->speed == SPEED_2500) ++ phydev->interface = PHY_INTERFACE_MODE_2500BASEX; ++ else ++ phydev->interface = PHY_INTERFACE_MODE_SGMII; ++ } else { ++ /* generate seed as a lower random value to make PHY linked as SLAVE easily, ++ * except for master/slave configuration fault detected or the master mode ++ * preferred. ++ * ++ * the reason for not putting this code into the function link_change_notify is ++ * the corner case where the link partner is also the qca8081 PHY and the seed ++ * value is configured as the same value, the link can't be up and no link change ++ * occurs. ++ */ ++ if (qca808x_has_fast_retrain_or_slave_seed(phydev)) { ++ if (phydev->master_slave_state == MASTER_SLAVE_STATE_ERR || ++ qca808x_is_prefer_master(phydev)) { ++ qca808x_phy_ms_seed_enable(phydev, false); ++ } else { ++ qca808x_phy_ms_seed_enable(phydev, true); ++ } ++ } ++ } ++ ++ return 0; ++} ++ ++static int qca808x_soft_reset(struct phy_device *phydev) ++{ ++ int ret; ++ ++ ret = genphy_soft_reset(phydev); ++ if (ret < 0) ++ return ret; ++ ++ if (qca808x_has_fast_retrain_or_slave_seed(phydev)) ++ ret = qca808x_phy_ms_seed_enable(phydev, true); ++ ++ return ret; ++} ++ ++static bool qca808x_cdt_fault_length_valid(int cdt_code) ++{ ++ switch (cdt_code) { ++ case QCA808X_CDT_STATUS_STAT_SAME_SHORT: ++ case QCA808X_CDT_STATUS_STAT_SAME_OPEN: ++ case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_NORMAL: ++ case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_OPEN: ++ case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_SHORT: ++ case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_NORMAL: ++ case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_OPEN: ++ case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_SHORT: ++ case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_NORMAL: ++ case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_OPEN: ++ case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_SHORT: ++ return true; ++ default: ++ return false; ++ } ++} ++ ++static int qca808x_cable_test_result_trans(int cdt_code) ++{ ++ switch (cdt_code) { ++ case QCA808X_CDT_STATUS_STAT_NORMAL: ++ return ETHTOOL_A_CABLE_RESULT_CODE_OK; ++ case QCA808X_CDT_STATUS_STAT_SAME_SHORT: ++ return ETHTOOL_A_CABLE_RESULT_CODE_SAME_SHORT; ++ case QCA808X_CDT_STATUS_STAT_SAME_OPEN: ++ return ETHTOOL_A_CABLE_RESULT_CODE_OPEN; ++ case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_NORMAL: ++ case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_OPEN: ++ case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_SHORT: ++ case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_NORMAL: ++ case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_OPEN: ++ case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_SHORT: ++ case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_NORMAL: ++ case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_OPEN: ++ case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_SHORT: ++ return ETHTOOL_A_CABLE_RESULT_CODE_CROSS_SHORT; ++ case QCA808X_CDT_STATUS_STAT_FAIL: ++ default: ++ return ETHTOOL_A_CABLE_RESULT_CODE_UNSPEC; ++ } ++} ++ ++static int qca808x_cdt_fault_length(struct phy_device *phydev, int pair, ++ int result) ++{ ++ int val; ++ u32 cdt_length_reg = 0; ++ ++ switch (pair) { ++ case ETHTOOL_A_CABLE_PAIR_A: ++ cdt_length_reg = QCA808X_MMD3_CDT_DIAG_PAIR_A; ++ break; ++ case ETHTOOL_A_CABLE_PAIR_B: ++ cdt_length_reg = QCA808X_MMD3_CDT_DIAG_PAIR_B; ++ break; ++ case ETHTOOL_A_CABLE_PAIR_C: ++ cdt_length_reg = QCA808X_MMD3_CDT_DIAG_PAIR_C; ++ break; ++ case ETHTOOL_A_CABLE_PAIR_D: ++ cdt_length_reg = QCA808X_MMD3_CDT_DIAG_PAIR_D; ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ val = phy_read_mmd(phydev, MDIO_MMD_PCS, cdt_length_reg); ++ if (val < 0) ++ return val; ++ ++ if (result == ETHTOOL_A_CABLE_RESULT_CODE_SAME_SHORT) ++ val = FIELD_GET(QCA808X_CDT_DIAG_LENGTH_SAME_SHORT, val); ++ else ++ val = FIELD_GET(QCA808X_CDT_DIAG_LENGTH_CROSS_SHORT, val); ++ ++ return at803x_cdt_fault_length(val); ++} ++ ++static int qca808x_cable_test_start(struct phy_device *phydev) ++{ ++ int ret; ++ ++ /* perform CDT with the following configs: ++ * 1. disable hibernation. ++ * 2. force PHY working in MDI mode. ++ * 3. for PHY working in 1000BaseT. ++ * 4. configure the threshold. ++ */ ++ ++ ret = at803x_debug_reg_mask(phydev, QCA808X_DBG_AN_TEST, QCA808X_HIBERNATION_EN, 0); ++ if (ret < 0) ++ return ret; ++ ++ ret = at803x_config_mdix(phydev, ETH_TP_MDI); ++ if (ret < 0) ++ return ret; ++ ++ /* Force 1000base-T needs to configure PMA/PMD and MII_BMCR */ ++ phydev->duplex = DUPLEX_FULL; ++ phydev->speed = SPEED_1000; ++ ret = genphy_c45_pma_setup_forced(phydev); ++ if (ret < 0) ++ return ret; ++ ++ ret = genphy_setup_forced(phydev); ++ if (ret < 0) ++ return ret; ++ ++ /* configure the thresholds for open, short, pair ok test */ ++ phy_write_mmd(phydev, MDIO_MMD_PCS, 0x8074, 0xc040); ++ phy_write_mmd(phydev, MDIO_MMD_PCS, 0x8076, 0xc040); ++ phy_write_mmd(phydev, MDIO_MMD_PCS, 0x8077, 0xa060); ++ phy_write_mmd(phydev, MDIO_MMD_PCS, 0x8078, 0xc050); ++ phy_write_mmd(phydev, MDIO_MMD_PCS, 0x807a, 0xc060); ++ phy_write_mmd(phydev, MDIO_MMD_PCS, 0x807e, 0xb060); ++ ++ return 0; ++} ++ ++static int qca808x_cable_test_get_pair_status(struct phy_device *phydev, u8 pair, ++ u16 status) ++{ ++ int length, result; ++ u16 pair_code; ++ ++ switch (pair) { ++ case ETHTOOL_A_CABLE_PAIR_A: ++ pair_code = FIELD_GET(QCA808X_CDT_CODE_PAIR_A, status); ++ break; ++ case ETHTOOL_A_CABLE_PAIR_B: ++ pair_code = FIELD_GET(QCA808X_CDT_CODE_PAIR_B, status); ++ break; ++ case ETHTOOL_A_CABLE_PAIR_C: ++ pair_code = FIELD_GET(QCA808X_CDT_CODE_PAIR_C, status); ++ break; ++ case ETHTOOL_A_CABLE_PAIR_D: ++ pair_code = FIELD_GET(QCA808X_CDT_CODE_PAIR_D, status); ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ result = qca808x_cable_test_result_trans(pair_code); ++ ethnl_cable_test_result(phydev, pair, result); ++ ++ if (qca808x_cdt_fault_length_valid(pair_code)) { ++ length = qca808x_cdt_fault_length(phydev, pair, result); ++ ethnl_cable_test_fault_length(phydev, pair, length); ++ } ++ ++ return 0; ++} ++ ++static int qca808x_cable_test_get_status(struct phy_device *phydev, bool *finished) ++{ ++ int ret, val; ++ ++ *finished = false; ++ ++ val = QCA808X_CDT_ENABLE_TEST | ++ QCA808X_CDT_LENGTH_UNIT; ++ ret = at803x_cdt_start(phydev, val); ++ if (ret) ++ return ret; ++ ++ ret = at803x_cdt_wait_for_completion(phydev, QCA808X_CDT_ENABLE_TEST); ++ if (ret) ++ return ret; ++ ++ val = phy_read_mmd(phydev, MDIO_MMD_PCS, QCA808X_MMD3_CDT_STATUS); ++ if (val < 0) ++ return val; ++ ++ ret = qca808x_cable_test_get_pair_status(phydev, ETHTOOL_A_CABLE_PAIR_A, val); ++ if (ret) ++ return ret; ++ ++ ret = qca808x_cable_test_get_pair_status(phydev, ETHTOOL_A_CABLE_PAIR_B, val); ++ if (ret) ++ return ret; ++ ++ ret = qca808x_cable_test_get_pair_status(phydev, ETHTOOL_A_CABLE_PAIR_C, val); ++ if (ret) ++ return ret; ++ ++ ret = qca808x_cable_test_get_pair_status(phydev, ETHTOOL_A_CABLE_PAIR_D, val); ++ if (ret) ++ return ret; ++ ++ *finished = true; ++ ++ return 0; ++} ++ ++static int qca808x_get_features(struct phy_device *phydev) ++{ ++ int ret; ++ ++ ret = genphy_c45_pma_read_abilities(phydev); ++ if (ret) ++ return ret; ++ ++ /* The autoneg ability is not existed in bit3 of MMD7.1, ++ * but it is supported by qca808x PHY, so we add it here ++ * manually. ++ */ ++ linkmode_set_bit(ETHTOOL_LINK_MODE_Autoneg_BIT, phydev->supported); ++ ++ /* As for the qca8081 1G version chip, the 2500baseT ability is also ++ * existed in the bit0 of MMD1.21, we need to remove it manually if ++ * it is the qca8081 1G chip according to the bit0 of MMD7.0x901d. ++ */ ++ ret = phy_read_mmd(phydev, MDIO_MMD_AN, QCA808X_PHY_MMD7_CHIP_TYPE); ++ if (ret < 0) ++ return ret; ++ ++ if (QCA808X_PHY_CHIP_TYPE_1G & ret) ++ linkmode_clear_bit(ETHTOOL_LINK_MODE_2500baseT_Full_BIT, phydev->supported); ++ ++ return 0; ++} ++ ++static int qca808x_config_aneg(struct phy_device *phydev) ++{ ++ int phy_ctrl = 0; ++ int ret; ++ ++ ret = at803x_prepare_config_aneg(phydev); ++ if (ret) ++ return ret; ++ ++ /* The reg MII_BMCR also needs to be configured for force mode, the ++ * genphy_config_aneg is also needed. ++ */ ++ if (phydev->autoneg == AUTONEG_DISABLE) ++ genphy_c45_pma_setup_forced(phydev); ++ ++ if (linkmode_test_bit(ETHTOOL_LINK_MODE_2500baseT_Full_BIT, phydev->advertising)) ++ phy_ctrl = MDIO_AN_10GBT_CTRL_ADV2_5G; ++ ++ ret = phy_modify_mmd_changed(phydev, MDIO_MMD_AN, MDIO_AN_10GBT_CTRL, ++ MDIO_AN_10GBT_CTRL_ADV2_5G, phy_ctrl); ++ if (ret < 0) ++ return ret; ++ ++ return __genphy_config_aneg(phydev, ret); ++} ++ ++static void qca808x_link_change_notify(struct phy_device *phydev) ++{ ++ /* Assert interface sgmii fifo on link down, deassert it on link up, ++ * the interface device address is always phy address added by 1. ++ */ ++ mdiobus_c45_modify_changed(phydev->mdio.bus, phydev->mdio.addr + 1, ++ MDIO_MMD_PMAPMD, QCA8081_PHY_SERDES_MMD1_FIFO_CTRL, ++ QCA8081_PHY_FIFO_RSTN, ++ phydev->link ? QCA8081_PHY_FIFO_RSTN : 0); ++} ++ ++static int qca808x_led_parse_netdev(struct phy_device *phydev, unsigned long rules, ++ u16 *offload_trigger) ++{ ++ /* Parsing specific to netdev trigger */ ++ if (test_bit(TRIGGER_NETDEV_TX, &rules)) ++ *offload_trigger |= QCA808X_LED_TX_BLINK; ++ if (test_bit(TRIGGER_NETDEV_RX, &rules)) ++ *offload_trigger |= QCA808X_LED_RX_BLINK; ++ if (test_bit(TRIGGER_NETDEV_LINK_10, &rules)) ++ *offload_trigger |= QCA808X_LED_SPEED10_ON; ++ if (test_bit(TRIGGER_NETDEV_LINK_100, &rules)) ++ *offload_trigger |= QCA808X_LED_SPEED100_ON; ++ if (test_bit(TRIGGER_NETDEV_LINK_1000, &rules)) ++ *offload_trigger |= QCA808X_LED_SPEED1000_ON; ++ if (test_bit(TRIGGER_NETDEV_LINK_2500, &rules)) ++ *offload_trigger |= QCA808X_LED_SPEED2500_ON; ++ if (test_bit(TRIGGER_NETDEV_HALF_DUPLEX, &rules)) ++ *offload_trigger |= QCA808X_LED_HALF_DUPLEX_ON; ++ if (test_bit(TRIGGER_NETDEV_FULL_DUPLEX, &rules)) ++ *offload_trigger |= QCA808X_LED_FULL_DUPLEX_ON; ++ ++ if (rules && !*offload_trigger) ++ return -EOPNOTSUPP; ++ ++ /* Enable BLINK_CHECK_BYPASS by default to make the LED ++ * blink even with duplex or speed mode not enabled. ++ */ ++ *offload_trigger |= QCA808X_LED_BLINK_CHECK_BYPASS; ++ ++ return 0; ++} ++ ++static int qca808x_led_hw_control_enable(struct phy_device *phydev, u8 index) ++{ ++ u16 reg; ++ ++ if (index > 2) ++ return -EINVAL; ++ ++ reg = QCA808X_MMD7_LED_FORCE_CTRL(index); ++ ++ return phy_clear_bits_mmd(phydev, MDIO_MMD_AN, reg, ++ QCA808X_LED_FORCE_EN); ++} ++ ++static int qca808x_led_hw_is_supported(struct phy_device *phydev, u8 index, ++ unsigned long rules) ++{ ++ u16 offload_trigger = 0; ++ ++ if (index > 2) ++ return -EINVAL; ++ ++ return qca808x_led_parse_netdev(phydev, rules, &offload_trigger); ++} ++ ++static int qca808x_led_hw_control_set(struct phy_device *phydev, u8 index, ++ unsigned long rules) ++{ ++ u16 reg, offload_trigger = 0; ++ int ret; ++ ++ if (index > 2) ++ return -EINVAL; ++ ++ reg = QCA808X_MMD7_LED_CTRL(index); ++ ++ ret = qca808x_led_parse_netdev(phydev, rules, &offload_trigger); ++ if (ret) ++ return ret; ++ ++ ret = qca808x_led_hw_control_enable(phydev, index); ++ if (ret) ++ return ret; ++ ++ return phy_modify_mmd(phydev, MDIO_MMD_AN, reg, ++ QCA808X_LED_PATTERN_MASK, ++ offload_trigger); ++} ++ ++static bool qca808x_led_hw_control_status(struct phy_device *phydev, u8 index) ++{ ++ u16 reg; ++ int val; ++ ++ if (index > 2) ++ return false; ++ ++ reg = QCA808X_MMD7_LED_FORCE_CTRL(index); ++ ++ val = phy_read_mmd(phydev, MDIO_MMD_AN, reg); ++ ++ return !(val & QCA808X_LED_FORCE_EN); ++} ++ ++static int qca808x_led_hw_control_get(struct phy_device *phydev, u8 index, ++ unsigned long *rules) ++{ ++ u16 reg; ++ int val; ++ ++ if (index > 2) ++ return -EINVAL; ++ ++ /* Check if we have hw control enabled */ ++ if (qca808x_led_hw_control_status(phydev, index)) ++ return -EINVAL; ++ ++ reg = QCA808X_MMD7_LED_CTRL(index); ++ ++ val = phy_read_mmd(phydev, MDIO_MMD_AN, reg); ++ if (val & QCA808X_LED_TX_BLINK) ++ set_bit(TRIGGER_NETDEV_TX, rules); ++ if (val & QCA808X_LED_RX_BLINK) ++ set_bit(TRIGGER_NETDEV_RX, rules); ++ if (val & QCA808X_LED_SPEED10_ON) ++ set_bit(TRIGGER_NETDEV_LINK_10, rules); ++ if (val & QCA808X_LED_SPEED100_ON) ++ set_bit(TRIGGER_NETDEV_LINK_100, rules); ++ if (val & QCA808X_LED_SPEED1000_ON) ++ set_bit(TRIGGER_NETDEV_LINK_1000, rules); ++ if (val & QCA808X_LED_SPEED2500_ON) ++ set_bit(TRIGGER_NETDEV_LINK_2500, rules); ++ if (val & QCA808X_LED_HALF_DUPLEX_ON) ++ set_bit(TRIGGER_NETDEV_HALF_DUPLEX, rules); ++ if (val & QCA808X_LED_FULL_DUPLEX_ON) ++ set_bit(TRIGGER_NETDEV_FULL_DUPLEX, rules); ++ ++ return 0; ++} ++ ++static int qca808x_led_hw_control_reset(struct phy_device *phydev, u8 index) ++{ ++ u16 reg; ++ ++ if (index > 2) ++ return -EINVAL; ++ ++ reg = QCA808X_MMD7_LED_CTRL(index); ++ ++ return phy_clear_bits_mmd(phydev, MDIO_MMD_AN, reg, ++ QCA808X_LED_PATTERN_MASK); ++} ++ ++static int qca808x_led_brightness_set(struct phy_device *phydev, ++ u8 index, enum led_brightness value) ++{ ++ u16 reg; ++ int ret; ++ ++ if (index > 2) ++ return -EINVAL; ++ ++ if (!value) { ++ ret = qca808x_led_hw_control_reset(phydev, index); ++ if (ret) ++ return ret; ++ } ++ ++ reg = QCA808X_MMD7_LED_FORCE_CTRL(index); ++ ++ return phy_modify_mmd(phydev, MDIO_MMD_AN, reg, ++ QCA808X_LED_FORCE_EN | QCA808X_LED_FORCE_MODE_MASK, ++ QCA808X_LED_FORCE_EN | value ? QCA808X_LED_FORCE_ON : ++ QCA808X_LED_FORCE_OFF); ++} ++ ++static int qca808x_led_blink_set(struct phy_device *phydev, u8 index, ++ unsigned long *delay_on, ++ unsigned long *delay_off) ++{ ++ int ret; ++ u16 reg; ++ ++ if (index > 2) ++ return -EINVAL; ++ ++ reg = QCA808X_MMD7_LED_FORCE_CTRL(index); ++ ++ /* Set blink to 50% off, 50% on at 4Hz by default */ ++ ret = phy_modify_mmd(phydev, MDIO_MMD_AN, QCA808X_MMD7_LED_GLOBAL, ++ QCA808X_LED_BLINK_FREQ_MASK | QCA808X_LED_BLINK_DUTY_MASK, ++ QCA808X_LED_BLINK_FREQ_4HZ | QCA808X_LED_BLINK_DUTY_50_50); ++ if (ret) ++ return ret; ++ ++ /* We use BLINK_1 for normal blinking */ ++ ret = phy_modify_mmd(phydev, MDIO_MMD_AN, reg, ++ QCA808X_LED_FORCE_EN | QCA808X_LED_FORCE_MODE_MASK, ++ QCA808X_LED_FORCE_EN | QCA808X_LED_FORCE_BLINK_1); ++ if (ret) ++ return ret; ++ ++ /* We set blink to 4Hz, aka 250ms */ ++ *delay_on = 250 / 2; ++ *delay_off = 250 / 2; ++ ++ return 0; ++} ++ ++static int qca808x_led_polarity_set(struct phy_device *phydev, int index, ++ unsigned long modes) ++{ ++ struct at803x_priv *priv = phydev->priv; ++ bool active_low = false; ++ u32 mode; ++ ++ for_each_set_bit(mode, &modes, __PHY_LED_MODES_NUM) { ++ switch (mode) { ++ case PHY_LED_ACTIVE_LOW: ++ active_low = true; ++ break; ++ default: ++ return -EINVAL; ++ } ++ } ++ ++ /* PHY polarity is global and can't be set per LED. ++ * To detect this, check if last requested polarity mode ++ * match the new one. ++ */ ++ if (priv->led_polarity_mode >= 0 && ++ priv->led_polarity_mode != active_low) { ++ phydev_err(phydev, "PHY polarity is global. Mismatched polarity on different LED\n"); ++ return -EINVAL; ++ } ++ ++ /* Save the last PHY polarity mode */ ++ priv->led_polarity_mode = active_low; ++ ++ return phy_modify_mmd(phydev, MDIO_MMD_AN, ++ QCA808X_MMD7_LED_POLARITY_CTRL, ++ QCA808X_LED_ACTIVE_HIGH, ++ active_low ? 0 : QCA808X_LED_ACTIVE_HIGH); ++} ++ ++static struct phy_driver at803x_driver[] = { ++{ ++ /* Qualcomm Atheros AR8035 */ ++ PHY_ID_MATCH_EXACT(ATH8035_PHY_ID), ++ .name = "Qualcomm Atheros AR8035", ++ .flags = PHY_POLL_CABLE_TEST, ++ .probe = at8035_probe, ++ .config_aneg = at803x_config_aneg, ++ .config_init = at803x_config_init, ++ .soft_reset = genphy_soft_reset, ++ .set_wol = at803x_set_wol, ++ .get_wol = at803x_get_wol, ++ .suspend = at803x_suspend, ++ .resume = at803x_resume, ++ /* PHY_GBIT_FEATURES */ ++ .read_status = at803x_read_status, ++ .config_intr = at803x_config_intr, ++ .handle_interrupt = at803x_handle_interrupt, ++ .get_tunable = at803x_get_tunable, ++ .set_tunable = at803x_set_tunable, ++ .cable_test_start = at8031_cable_test_start, ++ .cable_test_get_status = at8031_cable_test_get_status, ++}, { ++ /* Qualcomm Atheros AR8030 */ ++ .phy_id = ATH8030_PHY_ID, ++ .name = "Qualcomm Atheros AR8030", ++ .phy_id_mask = AT8030_PHY_ID_MASK, ++ .probe = at8035_probe, ++ .config_init = at803x_config_init, ++ .link_change_notify = at803x_link_change_notify, ++ .set_wol = at803x_set_wol, ++ .get_wol = at803x_get_wol, ++ .suspend = at803x_suspend, ++ .resume = at803x_resume, ++ /* PHY_BASIC_FEATURES */ ++ .config_intr = at803x_config_intr, ++ .handle_interrupt = at803x_handle_interrupt, ++}, { ++ /* Qualcomm Atheros AR8031/AR8033 */ ++ PHY_ID_MATCH_EXACT(ATH8031_PHY_ID), ++ .name = "Qualcomm Atheros AR8031/AR8033", ++ .flags = PHY_POLL_CABLE_TEST, ++ .probe = at8031_probe, ++ .config_init = at8031_config_init, ++ .config_aneg = at803x_config_aneg, ++ .soft_reset = genphy_soft_reset, ++ .set_wol = at8031_set_wol, ++ .get_wol = at803x_get_wol, ++ .suspend = at803x_suspend, ++ .resume = at803x_resume, ++ .read_page = at803x_read_page, ++ .write_page = at803x_write_page, ++ .get_features = at803x_get_features, ++ .read_status = at8031_read_status, ++ .config_intr = at8031_config_intr, ++ .handle_interrupt = at803x_handle_interrupt, ++ .get_tunable = at803x_get_tunable, ++ .set_tunable = at803x_set_tunable, ++ .cable_test_start = at8031_cable_test_start, ++ .cable_test_get_status = at8031_cable_test_get_status, ++}, { ++ /* Qualcomm Atheros AR8032 */ ++ PHY_ID_MATCH_EXACT(ATH8032_PHY_ID), ++ .name = "Qualcomm Atheros AR8032", ++ .probe = at803x_probe, ++ .flags = PHY_POLL_CABLE_TEST, ++ .config_init = at803x_config_init, ++ .link_change_notify = at803x_link_change_notify, ++ .suspend = at803x_suspend, ++ .resume = at803x_resume, ++ /* PHY_BASIC_FEATURES */ ++ .config_intr = at803x_config_intr, ++ .handle_interrupt = at803x_handle_interrupt, ++ .cable_test_start = at803x_cable_test_start, ++ .cable_test_get_status = at8032_cable_test_get_status, ++}, { ++ /* ATHEROS AR9331 */ ++ PHY_ID_MATCH_EXACT(ATH9331_PHY_ID), ++ .name = "Qualcomm Atheros AR9331 built-in PHY", ++ .probe = at803x_probe, ++ .suspend = at803x_suspend, ++ .resume = at803x_resume, ++ .flags = PHY_POLL_CABLE_TEST, ++ /* PHY_BASIC_FEATURES */ ++ .config_intr = at803x_config_intr, ++ .handle_interrupt = at803x_handle_interrupt, ++ .cable_test_start = at803x_cable_test_start, ++ .cable_test_get_status = at8032_cable_test_get_status, ++ .read_status = at803x_read_status, ++ .soft_reset = genphy_soft_reset, ++ .config_aneg = at803x_config_aneg, ++}, { ++ /* Qualcomm Atheros QCA9561 */ ++ PHY_ID_MATCH_EXACT(QCA9561_PHY_ID), ++ .name = "Qualcomm Atheros QCA9561 built-in PHY", ++ .probe = at803x_probe, ++ .suspend = at803x_suspend, ++ .resume = at803x_resume, ++ .flags = PHY_POLL_CABLE_TEST, ++ /* PHY_BASIC_FEATURES */ ++ .config_intr = at803x_config_intr, ++ .handle_interrupt = at803x_handle_interrupt, ++ .cable_test_start = at803x_cable_test_start, ++ .cable_test_get_status = at8032_cable_test_get_status, ++ .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 = "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 = qca83xx_get_sset_count, ++ .get_strings = qca83xx_get_strings, ++ .get_stats = qca83xx_get_stats, ++ .suspend = qca8337_suspend, ++ .resume = qca83xx_resume, ++}, { ++ /* QCA8327-A from switch QCA8327-AL1A */ ++ .phy_id = QCA8327_A_PHY_ID, ++ .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 = qca8327_config_init, ++ .soft_reset = genphy_soft_reset, ++ .get_sset_count = qca83xx_get_sset_count, ++ .get_strings = qca83xx_get_strings, ++ .get_stats = qca83xx_get_stats, ++ .suspend = qca8327_suspend, ++ .resume = qca83xx_resume, ++}, { ++ /* QCA8327-B from switch QCA8327-BL1A */ ++ .phy_id = QCA8327_B_PHY_ID, ++ .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 = qca8327_config_init, ++ .soft_reset = genphy_soft_reset, ++ .get_sset_count = qca83xx_get_sset_count, ++ .get_strings = qca83xx_get_strings, ++ .get_stats = qca83xx_get_stats, ++ .suspend = qca8327_suspend, ++ .resume = qca83xx_resume, ++}, { ++ /* Qualcomm QCA8081 */ ++ PHY_ID_MATCH_EXACT(QCA8081_PHY_ID), ++ .name = "Qualcomm QCA8081", ++ .flags = PHY_POLL_CABLE_TEST, ++ .probe = at803x_probe, ++ .config_intr = at803x_config_intr, ++ .handle_interrupt = at803x_handle_interrupt, ++ .get_tunable = at803x_get_tunable, ++ .set_tunable = at803x_set_tunable, ++ .set_wol = at803x_set_wol, ++ .get_wol = at803x_get_wol, ++ .get_features = qca808x_get_features, ++ .config_aneg = qca808x_config_aneg, ++ .suspend = genphy_suspend, ++ .resume = genphy_resume, ++ .read_status = qca808x_read_status, ++ .config_init = qca808x_config_init, ++ .soft_reset = qca808x_soft_reset, ++ .cable_test_start = qca808x_cable_test_start, ++ .cable_test_get_status = qca808x_cable_test_get_status, ++ .link_change_notify = qca808x_link_change_notify, ++ .led_brightness_set = qca808x_led_brightness_set, ++ .led_blink_set = qca808x_led_blink_set, ++ .led_hw_is_supported = qca808x_led_hw_is_supported, ++ .led_hw_control_set = qca808x_led_hw_control_set, ++ .led_hw_control_get = qca808x_led_hw_control_get, ++ .led_polarity_set = qca808x_led_polarity_set, ++}, }; ++ ++module_phy_driver(at803x_driver); ++ ++static struct mdio_device_id __maybe_unused atheros_tbl[] = { ++ { ATH8030_PHY_ID, AT8030_PHY_ID_MASK }, ++ { PHY_ID_MATCH_EXACT(ATH8031_PHY_ID) }, ++ { 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_A_PHY_ID) }, ++ { PHY_ID_MATCH_EXACT(QCA8327_B_PHY_ID) }, ++ { PHY_ID_MATCH_EXACT(QCA9561_PHY_ID) }, ++ { PHY_ID_MATCH_EXACT(QCA8081_PHY_ID) }, ++ { } ++}; ++ ++MODULE_DEVICE_TABLE(mdio, atheros_tbl); diff --git a/target/linux/generic/backport-6.1/713-v6.9-02-net-phy-qcom-create-and-move-functions-to-shared-lib.patch b/target/linux/generic/backport-6.1/713-v6.9-02-net-phy-qcom-create-and-move-functions-to-shared-lib.patch new file mode 100644 index 000000000..7d0e1f4a2 --- /dev/null +++ b/target/linux/generic/backport-6.1/713-v6.9-02-net-phy-qcom-create-and-move-functions-to-shared-lib.patch @@ -0,0 +1,243 @@ +From 6fb760972c49490b03f3db2ad64cf30bdd28c54a Mon Sep 17 00:00:00 2001 +From: Christian Marangi +Date: Mon, 29 Jan 2024 15:15:20 +0100 +Subject: [PATCH 2/5] net: phy: qcom: create and move functions to shared + library + +Create and move functions to shared library in preparation for qca83xx +PHY Family to be detached from at803x driver. + +Only the shared defines are moved to the shared qcom.h header. + +Signed-off-by: Christian Marangi +Reviewed-by: Andrew Lunn +Link: https://lore.kernel.org/r/20240129141600.2592-3-ansuelsmth@gmail.com +Signed-off-by: Jakub Kicinski +--- + drivers/net/phy/qcom/Kconfig | 4 ++ + drivers/net/phy/qcom/Makefile | 1 + + drivers/net/phy/qcom/at803x.c | 69 +---------------------------- + drivers/net/phy/qcom/qcom-phy-lib.c | 53 ++++++++++++++++++++++ + drivers/net/phy/qcom/qcom.h | 34 ++++++++++++++ + 5 files changed, 94 insertions(+), 67 deletions(-) + create mode 100644 drivers/net/phy/qcom/qcom-phy-lib.c + create mode 100644 drivers/net/phy/qcom/qcom.h + +--- a/drivers/net/phy/qcom/Kconfig ++++ b/drivers/net/phy/qcom/Kconfig +@@ -1,6 +1,10 @@ + # SPDX-License-Identifier: GPL-2.0-only ++config QCOM_NET_PHYLIB ++ tristate ++ + config AT803X_PHY + tristate "Qualcomm Atheros AR803X PHYs and QCA833x PHYs" ++ select QCOM_NET_PHYLIB + depends on REGULATOR + help + Currently supports the AR8030, AR8031, AR8033, AR8035 and internal +--- a/drivers/net/phy/qcom/Makefile ++++ b/drivers/net/phy/qcom/Makefile +@@ -1,2 +1,3 @@ + # SPDX-License-Identifier: GPL-2.0 ++obj-$(CONFIG_QCOM_NET_PHYLIB) += qcom-phy-lib.o + obj-$(CONFIG_AT803X_PHY) += at803x.o +--- a/drivers/net/phy/qcom/at803x.c ++++ b/drivers/net/phy/qcom/at803x.c +@@ -22,6 +22,8 @@ + #include + #include + ++#include "qcom.h" ++ + #define AT803X_SPECIFIC_FUNCTION_CONTROL 0x10 + #define AT803X_SFC_ASSERT_CRS BIT(11) + #define AT803X_SFC_FORCE_LINK BIT(10) +@@ -84,9 +86,6 @@ + #define AT803X_REG_CHIP_CONFIG 0x1f + #define AT803X_BT_BX_REG_SEL 0x8000 + +-#define AT803X_DEBUG_ADDR 0x1D +-#define AT803X_DEBUG_DATA 0x1E +- + #define AT803X_MODE_CFG_MASK 0x0F + #define AT803X_MODE_CFG_BASET_RGMII 0x00 + #define AT803X_MODE_CFG_BASET_SGMII 0x01 +@@ -103,19 +102,6 @@ + #define AT803X_PSSR 0x11 /*PHY-Specific Status Register*/ + #define AT803X_PSSR_MR_AN_COMPLETE 0x0200 + +-#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_SYSTEM_CTRL_MODE 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_HIB_CTRL_PS_HIB_EN BIT(15) +- + #define AT803X_DEBUG_REG_3C 0x3C + + #define AT803X_DEBUG_REG_GREEN 0x3D +@@ -393,18 +379,6 @@ MODULE_DESCRIPTION("Qualcomm Atheros AR8 + 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 qca83xx_hw_stats[] = { + { "phy_idle_errors", 0xa, GENMASK(7, 0), PHY}, + { "phy_receive_errors", 0x15, GENMASK(15, 0), PHY}, +@@ -439,45 +413,6 @@ 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; +- +- ret = phy_write(phydev, AT803X_DEBUG_ADDR, reg); +- if (ret < 0) +- return ret; +- +- return phy_read(phydev, AT803X_DEBUG_DATA); +-} +- +-static int at803x_debug_reg_mask(struct phy_device *phydev, u16 reg, +- u16 clear, u16 set) +-{ +- u16 val; +- int ret; +- +- ret = at803x_debug_reg_read(phydev, reg); +- if (ret < 0) +- return ret; +- +- val = ret & 0xffff; +- val &= ~clear; +- val |= set; +- +- return phy_write(phydev, AT803X_DEBUG_DATA, val); +-} +- + static int at803x_write_page(struct phy_device *phydev, int page) + { + int mask; +--- /dev/null ++++ b/drivers/net/phy/qcom/qcom-phy-lib.c +@@ -0,0 +1,53 @@ ++// SPDX-License-Identifier: GPL-2.0 ++ ++#include ++#include ++ ++#include "qcom.h" ++ ++MODULE_DESCRIPTION("Qualcomm PHY driver Common Functions"); ++MODULE_AUTHOR("Matus Ujhelyi"); ++MODULE_AUTHOR("Christian Marangi "); ++MODULE_LICENSE("GPL"); ++ ++int at803x_debug_reg_read(struct phy_device *phydev, u16 reg) ++{ ++ int ret; ++ ++ ret = phy_write(phydev, AT803X_DEBUG_ADDR, reg); ++ if (ret < 0) ++ return ret; ++ ++ return phy_read(phydev, AT803X_DEBUG_DATA); ++} ++EXPORT_SYMBOL_GPL(at803x_debug_reg_read); ++ ++int at803x_debug_reg_mask(struct phy_device *phydev, u16 reg, ++ u16 clear, u16 set) ++{ ++ u16 val; ++ int ret; ++ ++ ret = at803x_debug_reg_read(phydev, reg); ++ if (ret < 0) ++ return ret; ++ ++ val = ret & 0xffff; ++ val &= ~clear; ++ val |= set; ++ ++ return phy_write(phydev, AT803X_DEBUG_DATA, val); ++} ++EXPORT_SYMBOL_GPL(at803x_debug_reg_mask); ++ ++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); ++} ++EXPORT_SYMBOL_GPL(at803x_debug_reg_write); +--- /dev/null ++++ b/drivers/net/phy/qcom/qcom.h +@@ -0,0 +1,34 @@ ++/* SPDX-License-Identifier: GPL-2.0 */ ++ ++#define AT803X_DEBUG_ADDR 0x1D ++#define AT803X_DEBUG_DATA 0x1E ++ ++#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_SYSTEM_CTRL_MODE 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_HIB_CTRL_PS_HIB_EN BIT(15) ++ ++enum stat_access_type { ++ PHY, ++ MMD ++}; ++ ++struct at803x_hw_stat { ++ const char *string; ++ u8 reg; ++ u32 mask; ++ enum stat_access_type access_type; ++}; ++ ++int at803x_debug_reg_read(struct phy_device *phydev, u16 reg); ++int at803x_debug_reg_mask(struct phy_device *phydev, u16 reg, ++ u16 clear, u16 set); ++int at803x_debug_reg_write(struct phy_device *phydev, u16 reg, u16 data); diff --git a/target/linux/generic/backport-6.1/713-v6.9-03-net-phy-qcom-deatch-qca83xx-PHY-driver-from-at803x.patch b/target/linux/generic/backport-6.1/713-v6.9-03-net-phy-qcom-deatch-qca83xx-PHY-driver-from-at803x.patch new file mode 100644 index 000000000..6ac09dcb9 --- /dev/null +++ b/target/linux/generic/backport-6.1/713-v6.9-03-net-phy-qcom-deatch-qca83xx-PHY-driver-from-at803x.patch @@ -0,0 +1,638 @@ +From 2e45d404d99d43bb7127b74b5dea8818df64996c Mon Sep 17 00:00:00 2001 +From: Christian Marangi +Date: Mon, 29 Jan 2024 15:15:21 +0100 +Subject: [PATCH 3/5] net: phy: qcom: deatch qca83xx PHY driver from at803x + +Deatch qca83xx PHY driver from at803x. + +The QCA83xx PHYs implement specific function and doesn't use generic +at803x so it can be detached from the driver and moved to a dedicated +one. + +Probe function and priv struct is reimplemented to allocate and use +only the qca83xx specific data. Unused data from at803x PHY driver +are dropped from at803x priv struct. + +This is to make slimmer PHY drivers instead of including lots of bloat +that would never be used in specific SoC. + +A new Kconfig flag QCA83XX_PHY is introduced to compile the new +introduced PHY driver. + +As the Kconfig name starts with Qualcomm the same order is kept. + +Signed-off-by: Christian Marangi +Reviewed-by: Andrew Lunn +Link: https://lore.kernel.org/r/20240129141600.2592-4-ansuelsmth@gmail.com +Signed-off-by: Jakub Kicinski +--- + drivers/net/phy/qcom/Kconfig | 11 +- + drivers/net/phy/qcom/Makefile | 1 + + drivers/net/phy/qcom/at803x.c | 235 ---------------------------- + drivers/net/phy/qcom/qca83xx.c | 275 +++++++++++++++++++++++++++++++++ + 4 files changed, 284 insertions(+), 238 deletions(-) + create mode 100644 drivers/net/phy/qcom/qca83xx.c + +--- a/drivers/net/phy/qcom/Kconfig ++++ b/drivers/net/phy/qcom/Kconfig +@@ -3,9 +3,14 @@ config QCOM_NET_PHYLIB + tristate + + config AT803X_PHY +- tristate "Qualcomm Atheros AR803X PHYs and QCA833x PHYs" ++ tristate "Qualcomm Atheros AR803X PHYs" + select QCOM_NET_PHYLIB + depends on REGULATOR + help +- Currently supports the AR8030, AR8031, AR8033, AR8035 and internal +- QCA8337(Internal qca8k PHY) model ++ Currently supports the AR8030, AR8031, AR8033, AR8035 model ++ ++config QCA83XX_PHY ++ tristate "Qualcomm Atheros QCA833x PHYs" ++ select QCOM_NET_PHYLIB ++ help ++ Currently supports the internal QCA8337(Internal qca8k PHY) model +--- a/drivers/net/phy/qcom/Makefile ++++ b/drivers/net/phy/qcom/Makefile +@@ -1,3 +1,4 @@ + # SPDX-License-Identifier: GPL-2.0 + obj-$(CONFIG_QCOM_NET_PHYLIB) += qcom-phy-lib.o + obj-$(CONFIG_AT803X_PHY) += at803x.o ++obj-$(CONFIG_QCA83XX_PHY) += qca83xx.o +--- a/drivers/net/phy/qcom/at803x.c ++++ b/drivers/net/phy/qcom/at803x.c +@@ -102,17 +102,10 @@ + #define AT803X_PSSR 0x11 /*PHY-Specific Status Register*/ + #define AT803X_PSSR_MR_AN_COMPLETE 0x0200 + +-#define AT803X_DEBUG_REG_3C 0x3C +- +-#define AT803X_DEBUG_REG_GREEN 0x3D +-#define AT803X_DEBUG_GATE_CLK_IN1000 BIT(6) +- + #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. +@@ -163,13 +156,7 @@ + + #define QCA8081_PHY_ID 0x004dd101 + +-#define QCA8327_A_PHY_ID 0x004dd033 +-#define QCA8327_B_PHY_ID 0x004dd034 +-#define QCA8337_PHY_ID 0x004dd036 + #define QCA9561_PHY_ID 0x004dd042 +-#define QCA8K_PHY_ID_MASK 0xffffffff +- +-#define QCA8K_DEVFLAGS_REVISION_MASK GENMASK(2, 0) + + #define AT803X_PAGE_FIBER 0 + #define AT803X_PAGE_COPPER 1 +@@ -379,12 +366,6 @@ MODULE_DESCRIPTION("Qualcomm Atheros AR8 + MODULE_AUTHOR("Matus Ujhelyi"); + MODULE_LICENSE("GPL"); + +-static struct at803x_hw_stat qca83xx_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_ss_mask { + u16 speed_mask; + u8 speed_shift; +@@ -400,7 +381,6 @@ struct at803x_priv { + bool is_1000basex; + struct regulator_dev *vddio_rdev; + struct regulator_dev *vddh_rdev; +- u64 stats[ARRAY_SIZE(qca83xx_hw_stats)]; + int led_polarity_mode; + }; + +@@ -564,53 +544,6 @@ static void at803x_get_wol(struct phy_de + wol->wolopts |= WAKE_MAGIC; + } + +-static int qca83xx_get_sset_count(struct phy_device *phydev) +-{ +- return ARRAY_SIZE(qca83xx_hw_stats); +-} +- +-static void qca83xx_get_strings(struct phy_device *phydev, u8 *data) +-{ +- int i; +- +- for (i = 0; i < ARRAY_SIZE(qca83xx_hw_stats); i++) { +- strscpy(data + i * ETH_GSTRING_LEN, +- qca83xx_hw_stats[i].string, ETH_GSTRING_LEN); +- } +-} +- +-static u64 qca83xx_get_stat(struct phy_device *phydev, int i) +-{ +- struct at803x_hw_stat stat = qca83xx_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 qca83xx_get_stats(struct phy_device *phydev, +- struct ethtool_stats *stats, u64 *data) +-{ +- int i; +- +- for (i = 0; i < ARRAY_SIZE(qca83xx_hw_stats); i++) +- data[i] = qca83xx_get_stat(phydev, i); +-} +- + static int at803x_suspend(struct phy_device *phydev) + { + int value; +@@ -1707,124 +1640,6 @@ static int at8035_probe(struct phy_devic + return at8035_parse_dt(phydev); + } + +-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_ANALOG_TEST_CTRL, 0x02ea); +- /* Turn on Gigabit clock */ +- at803x_debug_reg_write(phydev, AT803X_DEBUG_REG_GREEN, 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_GREEN, 0x6860); +- at803x_debug_reg_write(phydev, AT803X_DEBUG_SYSTEM_CTRL_MODE, 0x2c46); +- at803x_debug_reg_write(phydev, AT803X_DEBUG_REG_3C, 0x6000); +- break; +- } +- +- /* Following original QCA sourcecode set port to prefer master */ +- phy_set_bits(phydev, MII_CTRL1000, CTL1000_PREFER_MASTER); +- +- return 0; +-} +- +-static int qca8327_config_init(struct phy_device *phydev) +-{ +- /* QCA8327 require DAC amplitude adjustment for 100m set to +6%. +- * Disable on init and enable only with 100m speed following +- * qca original source code. +- */ +- at803x_debug_reg_mask(phydev, AT803X_DEBUG_ANALOG_TEST_CTRL, +- QCA8327_DEBUG_MANU_CTRL_EN, 0); +- +- return qca83xx_config_init(phydev); +-} +- +-static void qca83xx_link_change_notify(struct phy_device *phydev) +-{ +- /* 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_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_ANALOG_TEST_CTRL, +- QCA8327_DEBUG_MANU_CTRL_EN, 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; +- +- usleep_range(1000, 2000); +- +- return 0; +-} +- +-static int qca83xx_suspend(struct phy_device *phydev) +-{ +- 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, +- AT803X_DEBUG_HIB_CTRL_EN_ANY_CHANGE | +- AT803X_DEBUG_HIB_CTRL_SEL_RST_80U, 0); +- +- return 0; +-} +- +-static int qca8337_suspend(struct phy_device *phydev) +-{ +- /* Only QCA8337 support actual suspend. */ +- genphy_suspend(phydev); +- +- return qca83xx_suspend(phydev); +-} +- +-static int qca8327_suspend(struct phy_device *phydev) +-{ +- u16 mask = 0; +- +- /* QCA8327 cause port unreliability when phy suspend +- * is set. +- */ +- mask |= ~(BMCR_SPEED1000 | BMCR_FULLDPLX); +- phy_modify(phydev, MII_BMCR, mask, 0); +- +- return qca83xx_suspend(phydev); +-} +- + static int qca808x_phy_fast_retrain_config(struct phy_device *phydev) + { + int ret; +@@ -2599,53 +2414,6 @@ static struct phy_driver at803x_driver[] + .soft_reset = genphy_soft_reset, + .config_aneg = at803x_config_aneg, + }, { +- /* QCA8337 */ +- .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 = qca83xx_get_sset_count, +- .get_strings = qca83xx_get_strings, +- .get_stats = qca83xx_get_stats, +- .suspend = qca8337_suspend, +- .resume = qca83xx_resume, +-}, { +- /* QCA8327-A from switch QCA8327-AL1A */ +- .phy_id = QCA8327_A_PHY_ID, +- .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 = qca8327_config_init, +- .soft_reset = genphy_soft_reset, +- .get_sset_count = qca83xx_get_sset_count, +- .get_strings = qca83xx_get_strings, +- .get_stats = qca83xx_get_stats, +- .suspend = qca8327_suspend, +- .resume = qca83xx_resume, +-}, { +- /* QCA8327-B from switch QCA8327-BL1A */ +- .phy_id = QCA8327_B_PHY_ID, +- .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 = qca8327_config_init, +- .soft_reset = genphy_soft_reset, +- .get_sset_count = qca83xx_get_sset_count, +- .get_strings = qca83xx_get_strings, +- .get_stats = qca83xx_get_stats, +- .suspend = qca8327_suspend, +- .resume = qca83xx_resume, +-}, { + /* Qualcomm QCA8081 */ + PHY_ID_MATCH_EXACT(QCA8081_PHY_ID), + .name = "Qualcomm QCA8081", +@@ -2683,9 +2451,6 @@ 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_A_PHY_ID) }, +- { PHY_ID_MATCH_EXACT(QCA8327_B_PHY_ID) }, + { PHY_ID_MATCH_EXACT(QCA9561_PHY_ID) }, + { PHY_ID_MATCH_EXACT(QCA8081_PHY_ID) }, + { } +--- /dev/null ++++ b/drivers/net/phy/qcom/qca83xx.c +@@ -0,0 +1,275 @@ ++// SPDX-License-Identifier: GPL-2.0+ ++ ++#include ++#include ++ ++#include "qcom.h" ++ ++#define AT803X_DEBUG_REG_3C 0x3C ++ ++#define AT803X_DEBUG_REG_GREEN 0x3D ++#define AT803X_DEBUG_GATE_CLK_IN1000 BIT(6) ++ ++#define MDIO_AZ_DEBUG 0x800D ++ ++#define QCA8327_A_PHY_ID 0x004dd033 ++#define QCA8327_B_PHY_ID 0x004dd034 ++#define QCA8337_PHY_ID 0x004dd036 ++#define QCA8K_PHY_ID_MASK 0xffffffff ++ ++#define QCA8K_DEVFLAGS_REVISION_MASK GENMASK(2, 0) ++ ++static struct at803x_hw_stat qca83xx_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 qca83xx_priv { ++ u64 stats[ARRAY_SIZE(qca83xx_hw_stats)]; ++}; ++ ++MODULE_DESCRIPTION("Qualcomm Atheros QCA83XX PHY driver"); ++MODULE_AUTHOR("Matus Ujhelyi"); ++MODULE_AUTHOR("Christian Marangi "); ++MODULE_LICENSE("GPL"); ++ ++static int qca83xx_get_sset_count(struct phy_device *phydev) ++{ ++ return ARRAY_SIZE(qca83xx_hw_stats); ++} ++ ++static void qca83xx_get_strings(struct phy_device *phydev, u8 *data) ++{ ++ int i; ++ ++ for (i = 0; i < ARRAY_SIZE(qca83xx_hw_stats); i++) { ++ strscpy(data + i * ETH_GSTRING_LEN, ++ qca83xx_hw_stats[i].string, ETH_GSTRING_LEN); ++ } ++} ++ ++static u64 qca83xx_get_stat(struct phy_device *phydev, int i) ++{ ++ struct at803x_hw_stat stat = qca83xx_hw_stats[i]; ++ struct qca83xx_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 qca83xx_get_stats(struct phy_device *phydev, ++ struct ethtool_stats *stats, u64 *data) ++{ ++ int i; ++ ++ for (i = 0; i < ARRAY_SIZE(qca83xx_hw_stats); i++) ++ data[i] = qca83xx_get_stat(phydev, i); ++} ++ ++static int qca83xx_probe(struct phy_device *phydev) ++{ ++ struct device *dev = &phydev->mdio.dev; ++ struct qca83xx_priv *priv; ++ ++ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); ++ if (!priv) ++ return -ENOMEM; ++ ++ phydev->priv = priv; ++ ++ 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_ANALOG_TEST_CTRL, 0x02ea); ++ /* Turn on Gigabit clock */ ++ at803x_debug_reg_write(phydev, AT803X_DEBUG_REG_GREEN, 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_GREEN, 0x6860); ++ at803x_debug_reg_write(phydev, AT803X_DEBUG_SYSTEM_CTRL_MODE, 0x2c46); ++ at803x_debug_reg_write(phydev, AT803X_DEBUG_REG_3C, 0x6000); ++ break; ++ } ++ ++ /* Following original QCA sourcecode set port to prefer master */ ++ phy_set_bits(phydev, MII_CTRL1000, CTL1000_PREFER_MASTER); ++ ++ return 0; ++} ++ ++static int qca8327_config_init(struct phy_device *phydev) ++{ ++ /* QCA8327 require DAC amplitude adjustment for 100m set to +6%. ++ * Disable on init and enable only with 100m speed following ++ * qca original source code. ++ */ ++ at803x_debug_reg_mask(phydev, AT803X_DEBUG_ANALOG_TEST_CTRL, ++ QCA8327_DEBUG_MANU_CTRL_EN, 0); ++ ++ return qca83xx_config_init(phydev); ++} ++ ++static void qca83xx_link_change_notify(struct phy_device *phydev) ++{ ++ /* 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_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_ANALOG_TEST_CTRL, ++ QCA8327_DEBUG_MANU_CTRL_EN, 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; ++ ++ usleep_range(1000, 2000); ++ ++ return 0; ++} ++ ++static int qca83xx_suspend(struct phy_device *phydev) ++{ ++ 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, ++ AT803X_DEBUG_HIB_CTRL_EN_ANY_CHANGE | ++ AT803X_DEBUG_HIB_CTRL_SEL_RST_80U, 0); ++ ++ return 0; ++} ++ ++static int qca8337_suspend(struct phy_device *phydev) ++{ ++ /* Only QCA8337 support actual suspend. */ ++ genphy_suspend(phydev); ++ ++ return qca83xx_suspend(phydev); ++} ++ ++static int qca8327_suspend(struct phy_device *phydev) ++{ ++ u16 mask = 0; ++ ++ /* QCA8327 cause port unreliability when phy suspend ++ * is set. ++ */ ++ mask |= ~(BMCR_SPEED1000 | BMCR_FULLDPLX); ++ phy_modify(phydev, MII_BMCR, mask, 0); ++ ++ return qca83xx_suspend(phydev); ++} ++ ++static struct phy_driver qca83xx_driver[] = { ++{ ++ /* QCA8337 */ ++ .phy_id = QCA8337_PHY_ID, ++ .phy_id_mask = QCA8K_PHY_ID_MASK, ++ .name = "Qualcomm Atheros 8337 internal PHY", ++ /* PHY_GBIT_FEATURES */ ++ .probe = qca83xx_probe, ++ .flags = PHY_IS_INTERNAL, ++ .config_init = qca83xx_config_init, ++ .soft_reset = genphy_soft_reset, ++ .get_sset_count = qca83xx_get_sset_count, ++ .get_strings = qca83xx_get_strings, ++ .get_stats = qca83xx_get_stats, ++ .suspend = qca8337_suspend, ++ .resume = qca83xx_resume, ++}, { ++ /* QCA8327-A from switch QCA8327-AL1A */ ++ .phy_id = QCA8327_A_PHY_ID, ++ .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 = qca83xx_probe, ++ .flags = PHY_IS_INTERNAL, ++ .config_init = qca8327_config_init, ++ .soft_reset = genphy_soft_reset, ++ .get_sset_count = qca83xx_get_sset_count, ++ .get_strings = qca83xx_get_strings, ++ .get_stats = qca83xx_get_stats, ++ .suspend = qca8327_suspend, ++ .resume = qca83xx_resume, ++}, { ++ /* QCA8327-B from switch QCA8327-BL1A */ ++ .phy_id = QCA8327_B_PHY_ID, ++ .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 = qca83xx_probe, ++ .flags = PHY_IS_INTERNAL, ++ .config_init = qca8327_config_init, ++ .soft_reset = genphy_soft_reset, ++ .get_sset_count = qca83xx_get_sset_count, ++ .get_strings = qca83xx_get_strings, ++ .get_stats = qca83xx_get_stats, ++ .suspend = qca8327_suspend, ++ .resume = qca83xx_resume, ++}, }; ++ ++module_phy_driver(qca83xx_driver); ++ ++static struct mdio_device_id __maybe_unused qca83xx_tbl[] = { ++ { PHY_ID_MATCH_EXACT(QCA8337_PHY_ID) }, ++ { PHY_ID_MATCH_EXACT(QCA8327_A_PHY_ID) }, ++ { PHY_ID_MATCH_EXACT(QCA8327_B_PHY_ID) }, ++ { } ++}; ++ ++MODULE_DEVICE_TABLE(mdio, qca83xx_tbl); diff --git a/target/linux/generic/backport-6.1/713-v6.9-04-net-phy-qcom-move-additional-functions-to-shared-lib.patch b/target/linux/generic/backport-6.1/713-v6.9-04-net-phy-qcom-move-additional-functions-to-shared-lib.patch new file mode 100644 index 000000000..9c43ad13b --- /dev/null +++ b/target/linux/generic/backport-6.1/713-v6.9-04-net-phy-qcom-move-additional-functions-to-shared-lib.patch @@ -0,0 +1,1014 @@ +From 249d2b80e4db0e38503ed0ec2af6c7401bc099b9 Mon Sep 17 00:00:00 2001 +From: Christian Marangi +Date: Mon, 29 Jan 2024 15:15:22 +0100 +Subject: [PATCH 4/5] net: phy: qcom: move additional functions to shared + library + +Move additional functions to shared library in preparation for qca808x +PHY Family to be detached from at803x driver. + +Only the shared defines are moved to the shared qcom.h header. + +Signed-off-by: Christian Marangi +Reviewed-by: Andrew Lunn +Link: https://lore.kernel.org/r/20240129141600.2592-5-ansuelsmth@gmail.com +Signed-off-by: Jakub Kicinski +--- + drivers/net/phy/qcom/at803x.c | 426 +--------------------------- + drivers/net/phy/qcom/qcom-phy-lib.c | 376 ++++++++++++++++++++++++ + drivers/net/phy/qcom/qcom.h | 86 ++++++ + 3 files changed, 463 insertions(+), 425 deletions(-) + +--- a/drivers/net/phy/qcom/at803x.c ++++ b/drivers/net/phy/qcom/at803x.c +@@ -24,65 +24,11 @@ + + #include "qcom.h" + +-#define AT803X_SPECIFIC_FUNCTION_CONTROL 0x10 +-#define AT803X_SFC_ASSERT_CRS BIT(11) +-#define AT803X_SFC_FORCE_LINK BIT(10) +-#define AT803X_SFC_MDI_CROSSOVER_MODE_M GENMASK(6, 5) +-#define AT803X_SFC_AUTOMATIC_CROSSOVER 0x3 +-#define AT803X_SFC_MANUAL_MDIX 0x1 +-#define AT803X_SFC_MANUAL_MDI 0x0 +-#define AT803X_SFC_SQE_TEST BIT(2) +-#define AT803X_SFC_POLARITY_REVERSAL BIT(1) +-#define AT803X_SFC_DISABLE_JABBER BIT(0) +- +-#define AT803X_SPECIFIC_STATUS 0x11 +-#define AT803X_SS_SPEED_MASK GENMASK(15, 14) +-#define AT803X_SS_SPEED_1000 2 +-#define AT803X_SS_SPEED_100 1 +-#define AT803X_SS_SPEED_10 0 +-#define AT803X_SS_DUPLEX BIT(13) +-#define AT803X_SS_SPEED_DUPLEX_RESOLVED BIT(11) +-#define AT803X_SS_MDIX BIT(6) +- +-#define QCA808X_SS_SPEED_MASK GENMASK(9, 7) +-#define QCA808X_SS_SPEED_2500 4 +- +-#define AT803X_INTR_ENABLE 0x12 +-#define AT803X_INTR_ENABLE_AUTONEG_ERR BIT(15) +-#define AT803X_INTR_ENABLE_SPEED_CHANGED BIT(14) +-#define AT803X_INTR_ENABLE_DUPLEX_CHANGED BIT(13) +-#define AT803X_INTR_ENABLE_PAGE_RECEIVED BIT(12) +-#define AT803X_INTR_ENABLE_LINK_FAIL BIT(11) +-#define AT803X_INTR_ENABLE_LINK_SUCCESS BIT(10) +-#define AT803X_INTR_ENABLE_LINK_FAIL_BX BIT(8) +-#define AT803X_INTR_ENABLE_LINK_SUCCESS_BX BIT(7) +-#define AT803X_INTR_ENABLE_WIRESPEED_DOWNGRADE BIT(5) +-#define AT803X_INTR_ENABLE_POLARITY_CHANGED BIT(1) +-#define AT803X_INTR_ENABLE_WOL BIT(0) +- +-#define AT803X_INTR_STATUS 0x13 +- +-#define AT803X_SMART_SPEED 0x14 +-#define AT803X_SMART_SPEED_ENABLE BIT(5) +-#define AT803X_SMART_SPEED_RETRY_LIMIT_MASK GENMASK(4, 2) +-#define AT803X_SMART_SPEED_BYPASS_TIMER BIT(1) +-#define AT803X_CDT 0x16 +-#define AT803X_CDT_MDI_PAIR_MASK GENMASK(9, 8) +-#define AT803X_CDT_ENABLE_TEST BIT(0) +-#define AT803X_CDT_STATUS 0x1c +-#define AT803X_CDT_STATUS_STAT_NORMAL 0 +-#define AT803X_CDT_STATUS_STAT_SHORT 1 +-#define AT803X_CDT_STATUS_STAT_OPEN 2 +-#define AT803X_CDT_STATUS_STAT_FAIL 3 +-#define AT803X_CDT_STATUS_STAT_MASK GENMASK(9, 8) +-#define AT803X_CDT_STATUS_DELTA_TIME_MASK GENMASK(7, 0) + #define AT803X_LED_CONTROL 0x18 + + #define AT803X_PHY_MMD3_WOL_CTRL 0x8012 + #define AT803X_WOL_EN BIT(5) +-#define AT803X_LOC_MAC_ADDR_0_15_OFFSET 0x804C +-#define AT803X_LOC_MAC_ADDR_16_31_OFFSET 0x804B +-#define AT803X_LOC_MAC_ADDR_32_47_OFFSET 0x804A ++ + #define AT803X_REG_CHIP_CONFIG 0x1f + #define AT803X_BT_BX_REG_SEL 0x8000 + +@@ -138,10 +84,6 @@ + #define AT803X_CLK_OUT_STRENGTH_HALF 1 + #define AT803X_CLK_OUT_STRENGTH_QUARTER 2 + +-#define AT803X_DEFAULT_DOWNSHIFT 5 +-#define AT803X_MIN_DOWNSHIFT 2 +-#define AT803X_MAX_DOWNSHIFT 9 +- + #define AT803X_MMD3_SMARTEEE_CTL1 0x805b + #define AT803X_MMD3_SMARTEEE_CTL2 0x805c + #define AT803X_MMD3_SMARTEEE_CTL3 0x805d +@@ -366,11 +308,6 @@ MODULE_DESCRIPTION("Qualcomm Atheros AR8 + MODULE_AUTHOR("Matus Ujhelyi"); + MODULE_LICENSE("GPL"); + +-struct at803x_ss_mask { +- u16 speed_mask; +- u8 speed_shift; +-}; +- + struct at803x_priv { + int flags; + u16 clk_25m_reg; +@@ -470,80 +407,6 @@ static void at803x_context_restore(struc + phy_write(phydev, AT803X_LED_CONTROL, context->led_control); + } + +-static int at803x_set_wol(struct phy_device *phydev, +- struct ethtool_wolinfo *wol) +-{ +- int ret, irq_enabled; +- +- if (wol->wolopts & WAKE_MAGIC) { +- struct net_device *ndev = phydev->attached_dev; +- const u8 *mac; +- unsigned int i; +- static const unsigned int offsets[] = { +- AT803X_LOC_MAC_ADDR_32_47_OFFSET, +- AT803X_LOC_MAC_ADDR_16_31_OFFSET, +- AT803X_LOC_MAC_ADDR_0_15_OFFSET, +- }; +- +- if (!ndev) +- return -ENODEV; +- +- mac = (const u8 *)ndev->dev_addr; +- +- if (!is_valid_ether_addr(mac)) +- return -EINVAL; +- +- for (i = 0; i < 3; i++) +- phy_write_mmd(phydev, MDIO_MMD_PCS, offsets[i], +- mac[(i * 2) + 1] | (mac[(i * 2)] << 8)); +- +- /* Enable WOL interrupt */ +- ret = phy_modify(phydev, AT803X_INTR_ENABLE, 0, AT803X_INTR_ENABLE_WOL); +- if (ret) +- return ret; +- } else { +- /* Disable WOL interrupt */ +- ret = phy_modify(phydev, AT803X_INTR_ENABLE, AT803X_INTR_ENABLE_WOL, 0); +- if (ret) +- return ret; +- } +- +- /* Clear WOL status */ +- ret = phy_read(phydev, AT803X_INTR_STATUS); +- if (ret < 0) +- return ret; +- +- /* Check if there are other interrupts except for WOL triggered when PHY is +- * in interrupt mode, only the interrupts enabled by AT803X_INTR_ENABLE can +- * be passed up to the interrupt PIN. +- */ +- irq_enabled = phy_read(phydev, AT803X_INTR_ENABLE); +- if (irq_enabled < 0) +- return irq_enabled; +- +- irq_enabled &= ~AT803X_INTR_ENABLE_WOL; +- if (ret & irq_enabled && !phy_polling_mode(phydev)) +- phy_trigger_machine(phydev); +- +- return 0; +-} +- +-static void at803x_get_wol(struct phy_device *phydev, +- struct ethtool_wolinfo *wol) +-{ +- int value; +- +- wol->supported = WAKE_MAGIC; +- wol->wolopts = 0; +- +- value = phy_read(phydev, AT803X_INTR_ENABLE); +- if (value < 0) +- return; +- +- if (value & AT803X_INTR_ENABLE_WOL) +- wol->wolopts |= WAKE_MAGIC; +-} +- + static int at803x_suspend(struct phy_device *phydev) + { + int value; +@@ -816,73 +679,6 @@ static int at803x_config_init(struct phy + return phy_modify(phydev, MII_ADVERTISE, MDIO_AN_CTRL1_XNP, 0); + } + +-static int at803x_ack_interrupt(struct phy_device *phydev) +-{ +- int err; +- +- err = phy_read(phydev, AT803X_INTR_STATUS); +- +- return (err < 0) ? err : 0; +-} +- +-static int at803x_config_intr(struct phy_device *phydev) +-{ +- int err; +- int value; +- +- value = phy_read(phydev, AT803X_INTR_ENABLE); +- +- if (phydev->interrupts == PHY_INTERRUPT_ENABLED) { +- /* Clear any pending interrupts */ +- err = at803x_ack_interrupt(phydev); +- if (err) +- return err; +- +- value |= AT803X_INTR_ENABLE_AUTONEG_ERR; +- value |= AT803X_INTR_ENABLE_SPEED_CHANGED; +- value |= AT803X_INTR_ENABLE_DUPLEX_CHANGED; +- value |= AT803X_INTR_ENABLE_LINK_FAIL; +- value |= AT803X_INTR_ENABLE_LINK_SUCCESS; +- +- err = phy_write(phydev, AT803X_INTR_ENABLE, value); +- } else { +- err = phy_write(phydev, AT803X_INTR_ENABLE, 0); +- if (err) +- return err; +- +- /* Clear any pending interrupts */ +- err = at803x_ack_interrupt(phydev); +- } +- +- return err; +-} +- +-static irqreturn_t at803x_handle_interrupt(struct phy_device *phydev) +-{ +- int irq_status, int_enabled; +- +- irq_status = phy_read(phydev, AT803X_INTR_STATUS); +- if (irq_status < 0) { +- phy_error(phydev); +- return IRQ_NONE; +- } +- +- /* Read the current enabled interrupts */ +- int_enabled = phy_read(phydev, AT803X_INTR_ENABLE); +- if (int_enabled < 0) { +- phy_error(phydev); +- return IRQ_NONE; +- } +- +- /* See if this was one of our enabled interrupts */ +- if (!(irq_status & int_enabled)) +- return IRQ_NONE; +- +- phy_trigger_machine(phydev); +- +- return IRQ_HANDLED; +-} +- + static void at803x_link_change_notify(struct phy_device *phydev) + { + /* +@@ -908,69 +704,6 @@ static void at803x_link_change_notify(st + } + } + +-static int at803x_read_specific_status(struct phy_device *phydev, +- struct at803x_ss_mask ss_mask) +-{ +- int ss; +- +- /* Read the AT8035 PHY-Specific Status register, which indicates the +- * speed and duplex that the PHY is actually using, irrespective of +- * whether we are in autoneg mode or not. +- */ +- ss = phy_read(phydev, AT803X_SPECIFIC_STATUS); +- if (ss < 0) +- return ss; +- +- if (ss & AT803X_SS_SPEED_DUPLEX_RESOLVED) { +- int sfc, speed; +- +- sfc = phy_read(phydev, AT803X_SPECIFIC_FUNCTION_CONTROL); +- if (sfc < 0) +- return sfc; +- +- speed = ss & ss_mask.speed_mask; +- speed >>= ss_mask.speed_shift; +- +- switch (speed) { +- case AT803X_SS_SPEED_10: +- phydev->speed = SPEED_10; +- break; +- case AT803X_SS_SPEED_100: +- phydev->speed = SPEED_100; +- break; +- case AT803X_SS_SPEED_1000: +- phydev->speed = SPEED_1000; +- break; +- case QCA808X_SS_SPEED_2500: +- phydev->speed = SPEED_2500; +- break; +- } +- if (ss & AT803X_SS_DUPLEX) +- phydev->duplex = DUPLEX_FULL; +- else +- phydev->duplex = DUPLEX_HALF; +- +- if (ss & AT803X_SS_MDIX) +- phydev->mdix = ETH_TP_MDI_X; +- else +- phydev->mdix = ETH_TP_MDI; +- +- switch (FIELD_GET(AT803X_SFC_MDI_CROSSOVER_MODE_M, sfc)) { +- case AT803X_SFC_MANUAL_MDI: +- phydev->mdix_ctrl = ETH_TP_MDI; +- break; +- case AT803X_SFC_MANUAL_MDIX: +- phydev->mdix_ctrl = ETH_TP_MDI_X; +- break; +- case AT803X_SFC_AUTOMATIC_CROSSOVER: +- phydev->mdix_ctrl = ETH_TP_MDI_AUTO; +- break; +- } +- } +- +- return 0; +-} +- + static int at803x_read_status(struct phy_device *phydev) + { + struct at803x_ss_mask ss_mask = { 0 }; +@@ -1006,50 +739,6 @@ static int at803x_read_status(struct phy + return 0; + } + +-static int at803x_config_mdix(struct phy_device *phydev, u8 ctrl) +-{ +- u16 val; +- +- switch (ctrl) { +- case ETH_TP_MDI: +- val = AT803X_SFC_MANUAL_MDI; +- break; +- case ETH_TP_MDI_X: +- val = AT803X_SFC_MANUAL_MDIX; +- break; +- case ETH_TP_MDI_AUTO: +- val = AT803X_SFC_AUTOMATIC_CROSSOVER; +- break; +- default: +- return 0; +- } +- +- return phy_modify_changed(phydev, AT803X_SPECIFIC_FUNCTION_CONTROL, +- AT803X_SFC_MDI_CROSSOVER_MODE_M, +- FIELD_PREP(AT803X_SFC_MDI_CROSSOVER_MODE_M, val)); +-} +- +-static int at803x_prepare_config_aneg(struct phy_device *phydev) +-{ +- int ret; +- +- ret = at803x_config_mdix(phydev, phydev->mdix_ctrl); +- if (ret < 0) +- return ret; +- +- /* Changes of the midx bits are disruptive to the normal operation; +- * therefore any changes to these registers must be followed by a +- * software reset to take effect. +- */ +- if (ret == 1) { +- ret = genphy_soft_reset(phydev); +- if (ret < 0) +- return ret; +- } +- +- return 0; +-} +- + static int at803x_config_aneg(struct phy_device *phydev) + { + struct at803x_priv *priv = phydev->priv; +@@ -1065,80 +754,6 @@ static int at803x_config_aneg(struct phy + return genphy_config_aneg(phydev); + } + +-static int at803x_get_downshift(struct phy_device *phydev, u8 *d) +-{ +- int val; +- +- val = phy_read(phydev, AT803X_SMART_SPEED); +- if (val < 0) +- return val; +- +- if (val & AT803X_SMART_SPEED_ENABLE) +- *d = FIELD_GET(AT803X_SMART_SPEED_RETRY_LIMIT_MASK, val) + 2; +- else +- *d = DOWNSHIFT_DEV_DISABLE; +- +- return 0; +-} +- +-static int at803x_set_downshift(struct phy_device *phydev, u8 cnt) +-{ +- u16 mask, set; +- int ret; +- +- switch (cnt) { +- case DOWNSHIFT_DEV_DEFAULT_COUNT: +- cnt = AT803X_DEFAULT_DOWNSHIFT; +- fallthrough; +- case AT803X_MIN_DOWNSHIFT ... AT803X_MAX_DOWNSHIFT: +- set = AT803X_SMART_SPEED_ENABLE | +- AT803X_SMART_SPEED_BYPASS_TIMER | +- FIELD_PREP(AT803X_SMART_SPEED_RETRY_LIMIT_MASK, cnt - 2); +- mask = AT803X_SMART_SPEED_RETRY_LIMIT_MASK; +- break; +- case DOWNSHIFT_DEV_DISABLE: +- set = 0; +- mask = AT803X_SMART_SPEED_ENABLE | +- AT803X_SMART_SPEED_BYPASS_TIMER; +- break; +- default: +- return -EINVAL; +- } +- +- ret = phy_modify_changed(phydev, AT803X_SMART_SPEED, mask, set); +- +- /* After changing the smart speed settings, we need to perform a +- * software reset, use phy_init_hw() to make sure we set the +- * reapply any values which might got lost during software reset. +- */ +- if (ret == 1) +- ret = phy_init_hw(phydev); +- +- return ret; +-} +- +-static int at803x_get_tunable(struct phy_device *phydev, +- struct ethtool_tunable *tuna, void *data) +-{ +- switch (tuna->id) { +- case ETHTOOL_PHY_DOWNSHIFT: +- return at803x_get_downshift(phydev, data); +- default: +- return -EOPNOTSUPP; +- } +-} +- +-static int at803x_set_tunable(struct phy_device *phydev, +- struct ethtool_tunable *tuna, const void *data) +-{ +- switch (tuna->id) { +- case ETHTOOL_PHY_DOWNSHIFT: +- return at803x_set_downshift(phydev, *(const u8 *)data); +- default: +- return -EOPNOTSUPP; +- } +-} +- + static int at803x_cable_test_result_trans(u16 status) + { + switch (FIELD_GET(AT803X_CDT_STATUS_STAT_MASK, status)) { +@@ -1170,45 +785,6 @@ static bool at803x_cdt_fault_length_vali + return false; + } + +-static int at803x_cdt_fault_length(int dt) +-{ +- /* According to the datasheet the distance to the fault is +- * DELTA_TIME * 0.824 meters. +- * +- * The author suspect the correct formula is: +- * +- * fault_distance = DELTA_TIME * (c * VF) / 125MHz / 2 +- * +- * where c is the speed of light, VF is the velocity factor of +- * the twisted pair cable, 125MHz the counter frequency and +- * we need to divide by 2 because the hardware will measure the +- * round trip time to the fault and back to the PHY. +- * +- * With a VF of 0.69 we get the factor 0.824 mentioned in the +- * datasheet. +- */ +- return (dt * 824) / 10; +-} +- +-static int at803x_cdt_start(struct phy_device *phydev, +- u32 cdt_start) +-{ +- return phy_write(phydev, AT803X_CDT, cdt_start); +-} +- +-static int at803x_cdt_wait_for_completion(struct phy_device *phydev, +- u32 cdt_en) +-{ +- int val, ret; +- +- /* One test run takes about 25ms */ +- ret = phy_read_poll_timeout(phydev, AT803X_CDT, val, +- !(val & cdt_en), +- 30000, 100000, true); +- +- return ret < 0 ? ret : 0; +-} +- + static int at803x_cable_test_one_pair(struct phy_device *phydev, int pair) + { + static const int ethtool_pair[] = { +--- a/drivers/net/phy/qcom/qcom-phy-lib.c ++++ b/drivers/net/phy/qcom/qcom-phy-lib.c +@@ -3,6 +3,9 @@ + #include + #include + ++#include ++#include ++ + #include "qcom.h" + + MODULE_DESCRIPTION("Qualcomm PHY driver Common Functions"); +@@ -51,3 +54,376 @@ int at803x_debug_reg_write(struct phy_de + return phy_write(phydev, AT803X_DEBUG_DATA, data); + } + EXPORT_SYMBOL_GPL(at803x_debug_reg_write); ++ ++int at803x_set_wol(struct phy_device *phydev, ++ struct ethtool_wolinfo *wol) ++{ ++ int ret, irq_enabled; ++ ++ if (wol->wolopts & WAKE_MAGIC) { ++ struct net_device *ndev = phydev->attached_dev; ++ const u8 *mac; ++ unsigned int i; ++ static const unsigned int offsets[] = { ++ AT803X_LOC_MAC_ADDR_32_47_OFFSET, ++ AT803X_LOC_MAC_ADDR_16_31_OFFSET, ++ AT803X_LOC_MAC_ADDR_0_15_OFFSET, ++ }; ++ ++ if (!ndev) ++ return -ENODEV; ++ ++ mac = (const u8 *)ndev->dev_addr; ++ ++ if (!is_valid_ether_addr(mac)) ++ return -EINVAL; ++ ++ for (i = 0; i < 3; i++) ++ phy_write_mmd(phydev, MDIO_MMD_PCS, offsets[i], ++ mac[(i * 2) + 1] | (mac[(i * 2)] << 8)); ++ ++ /* Enable WOL interrupt */ ++ ret = phy_modify(phydev, AT803X_INTR_ENABLE, 0, AT803X_INTR_ENABLE_WOL); ++ if (ret) ++ return ret; ++ } else { ++ /* Disable WOL interrupt */ ++ ret = phy_modify(phydev, AT803X_INTR_ENABLE, AT803X_INTR_ENABLE_WOL, 0); ++ if (ret) ++ return ret; ++ } ++ ++ /* Clear WOL status */ ++ ret = phy_read(phydev, AT803X_INTR_STATUS); ++ if (ret < 0) ++ return ret; ++ ++ /* Check if there are other interrupts except for WOL triggered when PHY is ++ * in interrupt mode, only the interrupts enabled by AT803X_INTR_ENABLE can ++ * be passed up to the interrupt PIN. ++ */ ++ irq_enabled = phy_read(phydev, AT803X_INTR_ENABLE); ++ if (irq_enabled < 0) ++ return irq_enabled; ++ ++ irq_enabled &= ~AT803X_INTR_ENABLE_WOL; ++ if (ret & irq_enabled && !phy_polling_mode(phydev)) ++ phy_trigger_machine(phydev); ++ ++ return 0; ++} ++EXPORT_SYMBOL_GPL(at803x_set_wol); ++ ++void at803x_get_wol(struct phy_device *phydev, ++ struct ethtool_wolinfo *wol) ++{ ++ int value; ++ ++ wol->supported = WAKE_MAGIC; ++ wol->wolopts = 0; ++ ++ value = phy_read(phydev, AT803X_INTR_ENABLE); ++ if (value < 0) ++ return; ++ ++ if (value & AT803X_INTR_ENABLE_WOL) ++ wol->wolopts |= WAKE_MAGIC; ++} ++EXPORT_SYMBOL_GPL(at803x_get_wol); ++ ++int at803x_ack_interrupt(struct phy_device *phydev) ++{ ++ int err; ++ ++ err = phy_read(phydev, AT803X_INTR_STATUS); ++ ++ return (err < 0) ? err : 0; ++} ++EXPORT_SYMBOL_GPL(at803x_ack_interrupt); ++ ++int at803x_config_intr(struct phy_device *phydev) ++{ ++ int err; ++ int value; ++ ++ value = phy_read(phydev, AT803X_INTR_ENABLE); ++ ++ if (phydev->interrupts == PHY_INTERRUPT_ENABLED) { ++ /* Clear any pending interrupts */ ++ err = at803x_ack_interrupt(phydev); ++ if (err) ++ return err; ++ ++ value |= AT803X_INTR_ENABLE_AUTONEG_ERR; ++ value |= AT803X_INTR_ENABLE_SPEED_CHANGED; ++ value |= AT803X_INTR_ENABLE_DUPLEX_CHANGED; ++ value |= AT803X_INTR_ENABLE_LINK_FAIL; ++ value |= AT803X_INTR_ENABLE_LINK_SUCCESS; ++ ++ err = phy_write(phydev, AT803X_INTR_ENABLE, value); ++ } else { ++ err = phy_write(phydev, AT803X_INTR_ENABLE, 0); ++ if (err) ++ return err; ++ ++ /* Clear any pending interrupts */ ++ err = at803x_ack_interrupt(phydev); ++ } ++ ++ return err; ++} ++EXPORT_SYMBOL_GPL(at803x_config_intr); ++ ++irqreturn_t at803x_handle_interrupt(struct phy_device *phydev) ++{ ++ int irq_status, int_enabled; ++ ++ irq_status = phy_read(phydev, AT803X_INTR_STATUS); ++ if (irq_status < 0) { ++ phy_error(phydev); ++ return IRQ_NONE; ++ } ++ ++ /* Read the current enabled interrupts */ ++ int_enabled = phy_read(phydev, AT803X_INTR_ENABLE); ++ if (int_enabled < 0) { ++ phy_error(phydev); ++ return IRQ_NONE; ++ } ++ ++ /* See if this was one of our enabled interrupts */ ++ if (!(irq_status & int_enabled)) ++ return IRQ_NONE; ++ ++ phy_trigger_machine(phydev); ++ ++ return IRQ_HANDLED; ++} ++EXPORT_SYMBOL_GPL(at803x_handle_interrupt); ++ ++int at803x_read_specific_status(struct phy_device *phydev, ++ struct at803x_ss_mask ss_mask) ++{ ++ int ss; ++ ++ /* Read the AT8035 PHY-Specific Status register, which indicates the ++ * speed and duplex that the PHY is actually using, irrespective of ++ * whether we are in autoneg mode or not. ++ */ ++ ss = phy_read(phydev, AT803X_SPECIFIC_STATUS); ++ if (ss < 0) ++ return ss; ++ ++ if (ss & AT803X_SS_SPEED_DUPLEX_RESOLVED) { ++ int sfc, speed; ++ ++ sfc = phy_read(phydev, AT803X_SPECIFIC_FUNCTION_CONTROL); ++ if (sfc < 0) ++ return sfc; ++ ++ speed = ss & ss_mask.speed_mask; ++ speed >>= ss_mask.speed_shift; ++ ++ switch (speed) { ++ case AT803X_SS_SPEED_10: ++ phydev->speed = SPEED_10; ++ break; ++ case AT803X_SS_SPEED_100: ++ phydev->speed = SPEED_100; ++ break; ++ case AT803X_SS_SPEED_1000: ++ phydev->speed = SPEED_1000; ++ break; ++ case QCA808X_SS_SPEED_2500: ++ phydev->speed = SPEED_2500; ++ break; ++ } ++ if (ss & AT803X_SS_DUPLEX) ++ phydev->duplex = DUPLEX_FULL; ++ else ++ phydev->duplex = DUPLEX_HALF; ++ ++ if (ss & AT803X_SS_MDIX) ++ phydev->mdix = ETH_TP_MDI_X; ++ else ++ phydev->mdix = ETH_TP_MDI; ++ ++ switch (FIELD_GET(AT803X_SFC_MDI_CROSSOVER_MODE_M, sfc)) { ++ case AT803X_SFC_MANUAL_MDI: ++ phydev->mdix_ctrl = ETH_TP_MDI; ++ break; ++ case AT803X_SFC_MANUAL_MDIX: ++ phydev->mdix_ctrl = ETH_TP_MDI_X; ++ break; ++ case AT803X_SFC_AUTOMATIC_CROSSOVER: ++ phydev->mdix_ctrl = ETH_TP_MDI_AUTO; ++ break; ++ } ++ } ++ ++ return 0; ++} ++EXPORT_SYMBOL_GPL(at803x_read_specific_status); ++ ++int at803x_config_mdix(struct phy_device *phydev, u8 ctrl) ++{ ++ u16 val; ++ ++ switch (ctrl) { ++ case ETH_TP_MDI: ++ val = AT803X_SFC_MANUAL_MDI; ++ break; ++ case ETH_TP_MDI_X: ++ val = AT803X_SFC_MANUAL_MDIX; ++ break; ++ case ETH_TP_MDI_AUTO: ++ val = AT803X_SFC_AUTOMATIC_CROSSOVER; ++ break; ++ default: ++ return 0; ++ } ++ ++ return phy_modify_changed(phydev, AT803X_SPECIFIC_FUNCTION_CONTROL, ++ AT803X_SFC_MDI_CROSSOVER_MODE_M, ++ FIELD_PREP(AT803X_SFC_MDI_CROSSOVER_MODE_M, val)); ++} ++EXPORT_SYMBOL_GPL(at803x_config_mdix); ++ ++int at803x_prepare_config_aneg(struct phy_device *phydev) ++{ ++ int ret; ++ ++ ret = at803x_config_mdix(phydev, phydev->mdix_ctrl); ++ if (ret < 0) ++ return ret; ++ ++ /* Changes of the midx bits are disruptive to the normal operation; ++ * therefore any changes to these registers must be followed by a ++ * software reset to take effect. ++ */ ++ if (ret == 1) { ++ ret = genphy_soft_reset(phydev); ++ if (ret < 0) ++ return ret; ++ } ++ ++ return 0; ++} ++EXPORT_SYMBOL_GPL(at803x_prepare_config_aneg); ++ ++static int at803x_get_downshift(struct phy_device *phydev, u8 *d) ++{ ++ int val; ++ ++ val = phy_read(phydev, AT803X_SMART_SPEED); ++ if (val < 0) ++ return val; ++ ++ if (val & AT803X_SMART_SPEED_ENABLE) ++ *d = FIELD_GET(AT803X_SMART_SPEED_RETRY_LIMIT_MASK, val) + 2; ++ else ++ *d = DOWNSHIFT_DEV_DISABLE; ++ ++ return 0; ++} ++ ++static int at803x_set_downshift(struct phy_device *phydev, u8 cnt) ++{ ++ u16 mask, set; ++ int ret; ++ ++ switch (cnt) { ++ case DOWNSHIFT_DEV_DEFAULT_COUNT: ++ cnt = AT803X_DEFAULT_DOWNSHIFT; ++ fallthrough; ++ case AT803X_MIN_DOWNSHIFT ... AT803X_MAX_DOWNSHIFT: ++ set = AT803X_SMART_SPEED_ENABLE | ++ AT803X_SMART_SPEED_BYPASS_TIMER | ++ FIELD_PREP(AT803X_SMART_SPEED_RETRY_LIMIT_MASK, cnt - 2); ++ mask = AT803X_SMART_SPEED_RETRY_LIMIT_MASK; ++ break; ++ case DOWNSHIFT_DEV_DISABLE: ++ set = 0; ++ mask = AT803X_SMART_SPEED_ENABLE | ++ AT803X_SMART_SPEED_BYPASS_TIMER; ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ ret = phy_modify_changed(phydev, AT803X_SMART_SPEED, mask, set); ++ ++ /* After changing the smart speed settings, we need to perform a ++ * software reset, use phy_init_hw() to make sure we set the ++ * reapply any values which might got lost during software reset. ++ */ ++ if (ret == 1) ++ ret = phy_init_hw(phydev); ++ ++ return ret; ++} ++ ++int at803x_get_tunable(struct phy_device *phydev, ++ struct ethtool_tunable *tuna, void *data) ++{ ++ switch (tuna->id) { ++ case ETHTOOL_PHY_DOWNSHIFT: ++ return at803x_get_downshift(phydev, data); ++ default: ++ return -EOPNOTSUPP; ++ } ++} ++EXPORT_SYMBOL_GPL(at803x_get_tunable); ++ ++int at803x_set_tunable(struct phy_device *phydev, ++ struct ethtool_tunable *tuna, const void *data) ++{ ++ switch (tuna->id) { ++ case ETHTOOL_PHY_DOWNSHIFT: ++ return at803x_set_downshift(phydev, *(const u8 *)data); ++ default: ++ return -EOPNOTSUPP; ++ } ++} ++EXPORT_SYMBOL_GPL(at803x_set_tunable); ++ ++int at803x_cdt_fault_length(int dt) ++{ ++ /* According to the datasheet the distance to the fault is ++ * DELTA_TIME * 0.824 meters. ++ * ++ * The author suspect the correct formula is: ++ * ++ * fault_distance = DELTA_TIME * (c * VF) / 125MHz / 2 ++ * ++ * where c is the speed of light, VF is the velocity factor of ++ * the twisted pair cable, 125MHz the counter frequency and ++ * we need to divide by 2 because the hardware will measure the ++ * round trip time to the fault and back to the PHY. ++ * ++ * With a VF of 0.69 we get the factor 0.824 mentioned in the ++ * datasheet. ++ */ ++ return (dt * 824) / 10; ++} ++EXPORT_SYMBOL_GPL(at803x_cdt_fault_length); ++ ++int at803x_cdt_start(struct phy_device *phydev, u32 cdt_start) ++{ ++ return phy_write(phydev, AT803X_CDT, cdt_start); ++} ++EXPORT_SYMBOL_GPL(at803x_cdt_start); ++ ++int at803x_cdt_wait_for_completion(struct phy_device *phydev, ++ u32 cdt_en) ++{ ++ int val, ret; ++ ++ /* One test run takes about 25ms */ ++ ret = phy_read_poll_timeout(phydev, AT803X_CDT, val, ++ !(val & cdt_en), ++ 30000, 100000, true); ++ ++ return ret < 0 ? ret : 0; ++} ++EXPORT_SYMBOL_GPL(at803x_cdt_wait_for_completion); +--- a/drivers/net/phy/qcom/qcom.h ++++ b/drivers/net/phy/qcom/qcom.h +@@ -1,5 +1,63 @@ + /* SPDX-License-Identifier: GPL-2.0 */ + ++#define AT803X_SPECIFIC_FUNCTION_CONTROL 0x10 ++#define AT803X_SFC_ASSERT_CRS BIT(11) ++#define AT803X_SFC_FORCE_LINK BIT(10) ++#define AT803X_SFC_MDI_CROSSOVER_MODE_M GENMASK(6, 5) ++#define AT803X_SFC_AUTOMATIC_CROSSOVER 0x3 ++#define AT803X_SFC_MANUAL_MDIX 0x1 ++#define AT803X_SFC_MANUAL_MDI 0x0 ++#define AT803X_SFC_SQE_TEST BIT(2) ++#define AT803X_SFC_POLARITY_REVERSAL BIT(1) ++#define AT803X_SFC_DISABLE_JABBER BIT(0) ++ ++#define AT803X_SPECIFIC_STATUS 0x11 ++#define AT803X_SS_SPEED_MASK GENMASK(15, 14) ++#define AT803X_SS_SPEED_1000 2 ++#define AT803X_SS_SPEED_100 1 ++#define AT803X_SS_SPEED_10 0 ++#define AT803X_SS_DUPLEX BIT(13) ++#define AT803X_SS_SPEED_DUPLEX_RESOLVED BIT(11) ++#define AT803X_SS_MDIX BIT(6) ++ ++#define QCA808X_SS_SPEED_MASK GENMASK(9, 7) ++#define QCA808X_SS_SPEED_2500 4 ++ ++#define AT803X_INTR_ENABLE 0x12 ++#define AT803X_INTR_ENABLE_AUTONEG_ERR BIT(15) ++#define AT803X_INTR_ENABLE_SPEED_CHANGED BIT(14) ++#define AT803X_INTR_ENABLE_DUPLEX_CHANGED BIT(13) ++#define AT803X_INTR_ENABLE_PAGE_RECEIVED BIT(12) ++#define AT803X_INTR_ENABLE_LINK_FAIL BIT(11) ++#define AT803X_INTR_ENABLE_LINK_SUCCESS BIT(10) ++#define AT803X_INTR_ENABLE_LINK_FAIL_BX BIT(8) ++#define AT803X_INTR_ENABLE_LINK_SUCCESS_BX BIT(7) ++#define AT803X_INTR_ENABLE_WIRESPEED_DOWNGRADE BIT(5) ++#define AT803X_INTR_ENABLE_POLARITY_CHANGED BIT(1) ++#define AT803X_INTR_ENABLE_WOL BIT(0) ++ ++#define AT803X_INTR_STATUS 0x13 ++ ++#define AT803X_SMART_SPEED 0x14 ++#define AT803X_SMART_SPEED_ENABLE BIT(5) ++#define AT803X_SMART_SPEED_RETRY_LIMIT_MASK GENMASK(4, 2) ++#define AT803X_SMART_SPEED_BYPASS_TIMER BIT(1) ++ ++#define AT803X_CDT 0x16 ++#define AT803X_CDT_MDI_PAIR_MASK GENMASK(9, 8) ++#define AT803X_CDT_ENABLE_TEST BIT(0) ++#define AT803X_CDT_STATUS 0x1c ++#define AT803X_CDT_STATUS_STAT_NORMAL 0 ++#define AT803X_CDT_STATUS_STAT_SHORT 1 ++#define AT803X_CDT_STATUS_STAT_OPEN 2 ++#define AT803X_CDT_STATUS_STAT_FAIL 3 ++#define AT803X_CDT_STATUS_STAT_MASK GENMASK(9, 8) ++#define AT803X_CDT_STATUS_DELTA_TIME_MASK GENMASK(7, 0) ++ ++#define AT803X_LOC_MAC_ADDR_0_15_OFFSET 0x804C ++#define AT803X_LOC_MAC_ADDR_16_31_OFFSET 0x804B ++#define AT803X_LOC_MAC_ADDR_32_47_OFFSET 0x804A ++ + #define AT803X_DEBUG_ADDR 0x1D + #define AT803X_DEBUG_DATA 0x1E + +@@ -16,6 +74,10 @@ + #define AT803X_DEBUG_HIB_CTRL_EN_ANY_CHANGE BIT(13) + #define AT803X_DEBUG_HIB_CTRL_PS_HIB_EN BIT(15) + ++#define AT803X_DEFAULT_DOWNSHIFT 5 ++#define AT803X_MIN_DOWNSHIFT 2 ++#define AT803X_MAX_DOWNSHIFT 9 ++ + enum stat_access_type { + PHY, + MMD +@@ -28,7 +90,31 @@ struct at803x_hw_stat { + enum stat_access_type access_type; + }; + ++struct at803x_ss_mask { ++ u16 speed_mask; ++ u8 speed_shift; ++}; ++ + int at803x_debug_reg_read(struct phy_device *phydev, u16 reg); + int at803x_debug_reg_mask(struct phy_device *phydev, u16 reg, + u16 clear, u16 set); + int at803x_debug_reg_write(struct phy_device *phydev, u16 reg, u16 data); ++int at803x_set_wol(struct phy_device *phydev, ++ struct ethtool_wolinfo *wol); ++void at803x_get_wol(struct phy_device *phydev, ++ struct ethtool_wolinfo *wol); ++int at803x_ack_interrupt(struct phy_device *phydev); ++int at803x_config_intr(struct phy_device *phydev); ++irqreturn_t at803x_handle_interrupt(struct phy_device *phydev); ++int at803x_read_specific_status(struct phy_device *phydev, ++ struct at803x_ss_mask ss_mask); ++int at803x_config_mdix(struct phy_device *phydev, u8 ctrl); ++int at803x_prepare_config_aneg(struct phy_device *phydev); ++int at803x_get_tunable(struct phy_device *phydev, ++ struct ethtool_tunable *tuna, void *data); ++int at803x_set_tunable(struct phy_device *phydev, ++ struct ethtool_tunable *tuna, const void *data); ++int at803x_cdt_fault_length(int dt); ++int at803x_cdt_start(struct phy_device *phydev, u32 cdt_start); ++int at803x_cdt_wait_for_completion(struct phy_device *phydev, ++ u32 cdt_en); diff --git a/target/linux/generic/backport-6.1/713-v6.9-05-net-phy-qcom-detach-qca808x-PHY-driver-from-at803x.patch b/target/linux/generic/backport-6.1/713-v6.9-05-net-phy-qcom-detach-qca808x-PHY-driver-from-at803x.patch new file mode 100644 index 000000000..597dcea4c --- /dev/null +++ b/target/linux/generic/backport-6.1/713-v6.9-05-net-phy-qcom-detach-qca808x-PHY-driver-from-at803x.patch @@ -0,0 +1,1936 @@ +From c89414adf2ec7cd9e7080c419aa5847f1db1009c Mon Sep 17 00:00:00 2001 +From: Christian Marangi +Date: Mon, 29 Jan 2024 15:15:23 +0100 +Subject: [PATCH 5/5] net: phy: qcom: detach qca808x PHY driver from at803x + +Almost all the QCA8081 PHY driver OPs are specific and only some of them +use the generic at803x. + +To make the at803x code slimmer, move all the specific qca808x regs and +functions to a dedicated PHY driver. + +Probe function and priv struct is reworked to allocate and use only the +qca808x specific data. Unused data from at803x PHY driver are dropped +from at803x priv struct. + +Also a new Kconfig is introduced QCA808X_PHY, to compile the newly +introduced PHY driver for QCA8081 PHY. + +As the Kconfig name starts with Qualcomm the same order is kept. + +Signed-off-by: Christian Marangi +Reviewed-by: Andrew Lunn +Link: https://lore.kernel.org/r/20240129141600.2592-6-ansuelsmth@gmail.com +Signed-off-by: Jakub Kicinski +--- + drivers/net/phy/qcom/Kconfig | 6 + + drivers/net/phy/qcom/Makefile | 1 + + drivers/net/phy/qcom/at803x.c | 897 +------------------------------ + drivers/net/phy/qcom/qca808x.c | 934 +++++++++++++++++++++++++++++++++ + 4 files changed, 942 insertions(+), 896 deletions(-) + create mode 100644 drivers/net/phy/qcom/qca808x.c + +--- a/drivers/net/phy/qcom/Kconfig ++++ b/drivers/net/phy/qcom/Kconfig +@@ -14,3 +14,9 @@ config QCA83XX_PHY + select QCOM_NET_PHYLIB + help + Currently supports the internal QCA8337(Internal qca8k PHY) model ++ ++config QCA808X_PHY ++ tristate "Qualcomm QCA808x PHYs" ++ select QCOM_NET_PHYLIB ++ help ++ Currently supports the QCA8081 model +--- a/drivers/net/phy/qcom/Makefile ++++ b/drivers/net/phy/qcom/Makefile +@@ -2,3 +2,4 @@ + obj-$(CONFIG_QCOM_NET_PHYLIB) += qcom-phy-lib.o + obj-$(CONFIG_AT803X_PHY) += at803x.o + obj-$(CONFIG_QCA83XX_PHY) += qca83xx.o ++obj-$(CONFIG_QCA808X_PHY) += qca808x.o +--- a/drivers/net/phy/qcom/at803x.c ++++ b/drivers/net/phy/qcom/at803x.c +@@ -96,8 +96,6 @@ + #define ATH8035_PHY_ID 0x004dd072 + #define AT8030_PHY_ID_MASK 0xffffffef + +-#define QCA8081_PHY_ID 0x004dd101 +- + #define QCA9561_PHY_ID 0x004dd042 + + #define AT803X_PAGE_FIBER 0 +@@ -110,201 +108,7 @@ + /* disable hibernation mode */ + #define AT803X_DISABLE_HIBERNATION_MODE BIT(2) + +-/* ADC threshold */ +-#define QCA808X_PHY_DEBUG_ADC_THRESHOLD 0x2c80 +-#define QCA808X_ADC_THRESHOLD_MASK GENMASK(7, 0) +-#define QCA808X_ADC_THRESHOLD_80MV 0 +-#define QCA808X_ADC_THRESHOLD_100MV 0xf0 +-#define QCA808X_ADC_THRESHOLD_200MV 0x0f +-#define QCA808X_ADC_THRESHOLD_300MV 0xff +- +-/* CLD control */ +-#define QCA808X_PHY_MMD3_ADDR_CLD_CTRL7 0x8007 +-#define QCA808X_8023AZ_AFE_CTRL_MASK GENMASK(8, 4) +-#define QCA808X_8023AZ_AFE_EN 0x90 +- +-/* AZ control */ +-#define QCA808X_PHY_MMD3_AZ_TRAINING_CTRL 0x8008 +-#define QCA808X_MMD3_AZ_TRAINING_VAL 0x1c32 +- +-#define QCA808X_PHY_MMD1_MSE_THRESHOLD_20DB 0x8014 +-#define QCA808X_MSE_THRESHOLD_20DB_VALUE 0x529 +- +-#define QCA808X_PHY_MMD1_MSE_THRESHOLD_17DB 0x800E +-#define QCA808X_MSE_THRESHOLD_17DB_VALUE 0x341 +- +-#define QCA808X_PHY_MMD1_MSE_THRESHOLD_27DB 0x801E +-#define QCA808X_MSE_THRESHOLD_27DB_VALUE 0x419 +- +-#define QCA808X_PHY_MMD1_MSE_THRESHOLD_28DB 0x8020 +-#define QCA808X_MSE_THRESHOLD_28DB_VALUE 0x341 +- +-#define QCA808X_PHY_MMD7_TOP_OPTION1 0x901c +-#define QCA808X_TOP_OPTION1_DATA 0x0 +- +-#define QCA808X_PHY_MMD3_DEBUG_1 0xa100 +-#define QCA808X_MMD3_DEBUG_1_VALUE 0x9203 +-#define QCA808X_PHY_MMD3_DEBUG_2 0xa101 +-#define QCA808X_MMD3_DEBUG_2_VALUE 0x48ad +-#define QCA808X_PHY_MMD3_DEBUG_3 0xa103 +-#define QCA808X_MMD3_DEBUG_3_VALUE 0x1698 +-#define QCA808X_PHY_MMD3_DEBUG_4 0xa105 +-#define QCA808X_MMD3_DEBUG_4_VALUE 0x8001 +-#define QCA808X_PHY_MMD3_DEBUG_5 0xa106 +-#define QCA808X_MMD3_DEBUG_5_VALUE 0x1111 +-#define QCA808X_PHY_MMD3_DEBUG_6 0xa011 +-#define QCA808X_MMD3_DEBUG_6_VALUE 0x5f85 +- +-/* master/slave seed config */ +-#define QCA808X_PHY_DEBUG_LOCAL_SEED 9 +-#define QCA808X_MASTER_SLAVE_SEED_ENABLE BIT(1) +-#define QCA808X_MASTER_SLAVE_SEED_CFG GENMASK(12, 2) +-#define QCA808X_MASTER_SLAVE_SEED_RANGE 0x32 +- +-/* Hibernation yields lower power consumpiton in contrast with normal operation mode. +- * when the copper cable is unplugged, the PHY enters into hibernation mode in about 10s. +- */ +-#define QCA808X_DBG_AN_TEST 0xb +-#define QCA808X_HIBERNATION_EN BIT(15) +- +-#define QCA808X_CDT_ENABLE_TEST BIT(15) +-#define QCA808X_CDT_INTER_CHECK_DIS BIT(13) +-#define QCA808X_CDT_STATUS BIT(11) +-#define QCA808X_CDT_LENGTH_UNIT BIT(10) +- +-#define QCA808X_MMD3_CDT_STATUS 0x8064 +-#define QCA808X_MMD3_CDT_DIAG_PAIR_A 0x8065 +-#define QCA808X_MMD3_CDT_DIAG_PAIR_B 0x8066 +-#define QCA808X_MMD3_CDT_DIAG_PAIR_C 0x8067 +-#define QCA808X_MMD3_CDT_DIAG_PAIR_D 0x8068 +-#define QCA808X_CDT_DIAG_LENGTH_SAME_SHORT GENMASK(15, 8) +-#define QCA808X_CDT_DIAG_LENGTH_CROSS_SHORT GENMASK(7, 0) +- +-#define QCA808X_CDT_CODE_PAIR_A GENMASK(15, 12) +-#define QCA808X_CDT_CODE_PAIR_B GENMASK(11, 8) +-#define QCA808X_CDT_CODE_PAIR_C GENMASK(7, 4) +-#define QCA808X_CDT_CODE_PAIR_D GENMASK(3, 0) +- +-#define QCA808X_CDT_STATUS_STAT_TYPE GENMASK(1, 0) +-#define QCA808X_CDT_STATUS_STAT_FAIL FIELD_PREP_CONST(QCA808X_CDT_STATUS_STAT_TYPE, 0) +-#define QCA808X_CDT_STATUS_STAT_NORMAL FIELD_PREP_CONST(QCA808X_CDT_STATUS_STAT_TYPE, 1) +-#define QCA808X_CDT_STATUS_STAT_SAME_OPEN FIELD_PREP_CONST(QCA808X_CDT_STATUS_STAT_TYPE, 2) +-#define QCA808X_CDT_STATUS_STAT_SAME_SHORT FIELD_PREP_CONST(QCA808X_CDT_STATUS_STAT_TYPE, 3) +- +-#define QCA808X_CDT_STATUS_STAT_MDI GENMASK(3, 2) +-#define QCA808X_CDT_STATUS_STAT_MDI1 FIELD_PREP_CONST(QCA808X_CDT_STATUS_STAT_MDI, 1) +-#define QCA808X_CDT_STATUS_STAT_MDI2 FIELD_PREP_CONST(QCA808X_CDT_STATUS_STAT_MDI, 2) +-#define QCA808X_CDT_STATUS_STAT_MDI3 FIELD_PREP_CONST(QCA808X_CDT_STATUS_STAT_MDI, 3) +- +-/* NORMAL are MDI with type set to 0 */ +-#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_NORMAL QCA808X_CDT_STATUS_STAT_MDI1 +-#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_OPEN (QCA808X_CDT_STATUS_STAT_SAME_OPEN |\ +- QCA808X_CDT_STATUS_STAT_MDI1) +-#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_SHORT (QCA808X_CDT_STATUS_STAT_SAME_SHORT |\ +- QCA808X_CDT_STATUS_STAT_MDI1) +-#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_NORMAL QCA808X_CDT_STATUS_STAT_MDI2 +-#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_OPEN (QCA808X_CDT_STATUS_STAT_SAME_OPEN |\ +- QCA808X_CDT_STATUS_STAT_MDI2) +-#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_SHORT (QCA808X_CDT_STATUS_STAT_SAME_SHORT |\ +- QCA808X_CDT_STATUS_STAT_MDI2) +-#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_NORMAL QCA808X_CDT_STATUS_STAT_MDI3 +-#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_OPEN (QCA808X_CDT_STATUS_STAT_SAME_OPEN |\ +- QCA808X_CDT_STATUS_STAT_MDI3) +-#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_SHORT (QCA808X_CDT_STATUS_STAT_SAME_SHORT |\ +- QCA808X_CDT_STATUS_STAT_MDI3) +- +-/* Added for reference of existence but should be handled by wait_for_completion already */ +-#define QCA808X_CDT_STATUS_STAT_BUSY (BIT(1) | BIT(3)) +- +-#define QCA808X_MMD7_LED_GLOBAL 0x8073 +-#define QCA808X_LED_BLINK_1 GENMASK(11, 6) +-#define QCA808X_LED_BLINK_2 GENMASK(5, 0) +-/* Values are the same for both BLINK_1 and BLINK_2 */ +-#define QCA808X_LED_BLINK_FREQ_MASK GENMASK(5, 3) +-#define QCA808X_LED_BLINK_FREQ_2HZ FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x0) +-#define QCA808X_LED_BLINK_FREQ_4HZ FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x1) +-#define QCA808X_LED_BLINK_FREQ_8HZ FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x2) +-#define QCA808X_LED_BLINK_FREQ_16HZ FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x3) +-#define QCA808X_LED_BLINK_FREQ_32HZ FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x4) +-#define QCA808X_LED_BLINK_FREQ_64HZ FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x5) +-#define QCA808X_LED_BLINK_FREQ_128HZ FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x6) +-#define QCA808X_LED_BLINK_FREQ_256HZ FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x7) +-#define QCA808X_LED_BLINK_DUTY_MASK GENMASK(2, 0) +-#define QCA808X_LED_BLINK_DUTY_50_50 FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x0) +-#define QCA808X_LED_BLINK_DUTY_75_25 FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x1) +-#define QCA808X_LED_BLINK_DUTY_25_75 FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x2) +-#define QCA808X_LED_BLINK_DUTY_33_67 FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x3) +-#define QCA808X_LED_BLINK_DUTY_67_33 FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x4) +-#define QCA808X_LED_BLINK_DUTY_17_83 FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x5) +-#define QCA808X_LED_BLINK_DUTY_83_17 FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x6) +-#define QCA808X_LED_BLINK_DUTY_8_92 FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x7) +- +-#define QCA808X_MMD7_LED2_CTRL 0x8074 +-#define QCA808X_MMD7_LED2_FORCE_CTRL 0x8075 +-#define QCA808X_MMD7_LED1_CTRL 0x8076 +-#define QCA808X_MMD7_LED1_FORCE_CTRL 0x8077 +-#define QCA808X_MMD7_LED0_CTRL 0x8078 +-#define QCA808X_MMD7_LED_CTRL(x) (0x8078 - ((x) * 2)) +- +-/* LED hw control pattern is the same for every LED */ +-#define QCA808X_LED_PATTERN_MASK GENMASK(15, 0) +-#define QCA808X_LED_SPEED2500_ON BIT(15) +-#define QCA808X_LED_SPEED2500_BLINK BIT(14) +-/* Follow blink trigger even if duplex or speed condition doesn't match */ +-#define QCA808X_LED_BLINK_CHECK_BYPASS BIT(13) +-#define QCA808X_LED_FULL_DUPLEX_ON BIT(12) +-#define QCA808X_LED_HALF_DUPLEX_ON BIT(11) +-#define QCA808X_LED_TX_BLINK BIT(10) +-#define QCA808X_LED_RX_BLINK BIT(9) +-#define QCA808X_LED_TX_ON_10MS BIT(8) +-#define QCA808X_LED_RX_ON_10MS BIT(7) +-#define QCA808X_LED_SPEED1000_ON BIT(6) +-#define QCA808X_LED_SPEED100_ON BIT(5) +-#define QCA808X_LED_SPEED10_ON BIT(4) +-#define QCA808X_LED_COLLISION_BLINK BIT(3) +-#define QCA808X_LED_SPEED1000_BLINK BIT(2) +-#define QCA808X_LED_SPEED100_BLINK BIT(1) +-#define QCA808X_LED_SPEED10_BLINK BIT(0) +- +-#define QCA808X_MMD7_LED0_FORCE_CTRL 0x8079 +-#define QCA808X_MMD7_LED_FORCE_CTRL(x) (0x8079 - ((x) * 2)) +- +-/* LED force ctrl is the same for every LED +- * No documentation exist for this, not even internal one +- * with NDA as QCOM gives only info about configuring +- * hw control pattern rules and doesn't indicate any way +- * to force the LED to specific mode. +- * These define comes from reverse and testing and maybe +- * lack of some info or some info are not entirely correct. +- * For the basic LED control and hw control these finding +- * are enough to support LED control in all the required APIs. +- * +- * On doing some comparison with implementation with qca807x, +- * it was found that it's 1:1 equal to it and confirms all the +- * reverse done. It was also found further specification with the +- * force mode and the blink modes. +- */ +-#define QCA808X_LED_FORCE_EN BIT(15) +-#define QCA808X_LED_FORCE_MODE_MASK GENMASK(14, 13) +-#define QCA808X_LED_FORCE_BLINK_1 FIELD_PREP(QCA808X_LED_FORCE_MODE_MASK, 0x3) +-#define QCA808X_LED_FORCE_BLINK_2 FIELD_PREP(QCA808X_LED_FORCE_MODE_MASK, 0x2) +-#define QCA808X_LED_FORCE_ON FIELD_PREP(QCA808X_LED_FORCE_MODE_MASK, 0x1) +-#define QCA808X_LED_FORCE_OFF FIELD_PREP(QCA808X_LED_FORCE_MODE_MASK, 0x0) +- +-#define QCA808X_MMD7_LED_POLARITY_CTRL 0x901a +-/* QSDK sets by default 0x46 to this reg that sets BIT 6 for +- * LED to active high. It's not clear what BIT 3 and BIT 4 does. +- */ +-#define QCA808X_LED_ACTIVE_HIGH BIT(6) +- +-/* QCA808X 1G chip type */ +-#define QCA808X_PHY_MMD7_CHIP_TYPE 0x901d +-#define QCA808X_PHY_CHIP_TYPE_1G BIT(0) +- +-#define QCA8081_PHY_SERDES_MMD1_FIFO_CTRL 0x9072 +-#define QCA8081_PHY_FIFO_RSTN BIT(11) +- +-MODULE_DESCRIPTION("Qualcomm Atheros AR803x and QCA808X PHY driver"); ++MODULE_DESCRIPTION("Qualcomm Atheros AR803x PHY driver"); + MODULE_AUTHOR("Matus Ujhelyi"); + MODULE_LICENSE("GPL"); + +@@ -318,7 +122,6 @@ struct at803x_priv { + bool is_1000basex; + struct regulator_dev *vddio_rdev; + struct regulator_dev *vddh_rdev; +- int led_polarity_mode; + }; + + struct at803x_context { +@@ -519,9 +322,6 @@ static int at803x_probe(struct phy_devic + if (!priv) + return -ENOMEM; + +- /* Init LED polarity mode to -1 */ +- priv->led_polarity_mode = -1; +- + phydev->priv = priv; + + ret = at803x_parse_dt(phydev); +@@ -1216,672 +1016,6 @@ static int at8035_probe(struct phy_devic + return at8035_parse_dt(phydev); + } + +-static int qca808x_phy_fast_retrain_config(struct phy_device *phydev) +-{ +- int ret; +- +- /* Enable fast retrain */ +- ret = genphy_c45_fast_retrain(phydev, true); +- if (ret) +- return ret; +- +- phy_write_mmd(phydev, MDIO_MMD_AN, QCA808X_PHY_MMD7_TOP_OPTION1, +- QCA808X_TOP_OPTION1_DATA); +- phy_write_mmd(phydev, MDIO_MMD_PMAPMD, QCA808X_PHY_MMD1_MSE_THRESHOLD_20DB, +- QCA808X_MSE_THRESHOLD_20DB_VALUE); +- phy_write_mmd(phydev, MDIO_MMD_PMAPMD, QCA808X_PHY_MMD1_MSE_THRESHOLD_17DB, +- QCA808X_MSE_THRESHOLD_17DB_VALUE); +- phy_write_mmd(phydev, MDIO_MMD_PMAPMD, QCA808X_PHY_MMD1_MSE_THRESHOLD_27DB, +- QCA808X_MSE_THRESHOLD_27DB_VALUE); +- phy_write_mmd(phydev, MDIO_MMD_PMAPMD, QCA808X_PHY_MMD1_MSE_THRESHOLD_28DB, +- QCA808X_MSE_THRESHOLD_28DB_VALUE); +- phy_write_mmd(phydev, MDIO_MMD_PCS, QCA808X_PHY_MMD3_DEBUG_1, +- QCA808X_MMD3_DEBUG_1_VALUE); +- phy_write_mmd(phydev, MDIO_MMD_PCS, QCA808X_PHY_MMD3_DEBUG_4, +- QCA808X_MMD3_DEBUG_4_VALUE); +- phy_write_mmd(phydev, MDIO_MMD_PCS, QCA808X_PHY_MMD3_DEBUG_5, +- QCA808X_MMD3_DEBUG_5_VALUE); +- phy_write_mmd(phydev, MDIO_MMD_PCS, QCA808X_PHY_MMD3_DEBUG_3, +- QCA808X_MMD3_DEBUG_3_VALUE); +- phy_write_mmd(phydev, MDIO_MMD_PCS, QCA808X_PHY_MMD3_DEBUG_6, +- QCA808X_MMD3_DEBUG_6_VALUE); +- phy_write_mmd(phydev, MDIO_MMD_PCS, QCA808X_PHY_MMD3_DEBUG_2, +- QCA808X_MMD3_DEBUG_2_VALUE); +- +- return 0; +-} +- +-static int qca808x_phy_ms_seed_enable(struct phy_device *phydev, bool enable) +-{ +- u16 seed_value; +- +- if (!enable) +- return at803x_debug_reg_mask(phydev, QCA808X_PHY_DEBUG_LOCAL_SEED, +- QCA808X_MASTER_SLAVE_SEED_ENABLE, 0); +- +- seed_value = prandom_u32_max(QCA808X_MASTER_SLAVE_SEED_RANGE); +- return at803x_debug_reg_mask(phydev, QCA808X_PHY_DEBUG_LOCAL_SEED, +- QCA808X_MASTER_SLAVE_SEED_CFG | QCA808X_MASTER_SLAVE_SEED_ENABLE, +- FIELD_PREP(QCA808X_MASTER_SLAVE_SEED_CFG, seed_value) | +- QCA808X_MASTER_SLAVE_SEED_ENABLE); +-} +- +-static bool qca808x_is_prefer_master(struct phy_device *phydev) +-{ +- return (phydev->master_slave_get == MASTER_SLAVE_CFG_MASTER_FORCE) || +- (phydev->master_slave_get == MASTER_SLAVE_CFG_MASTER_PREFERRED); +-} +- +-static bool qca808x_has_fast_retrain_or_slave_seed(struct phy_device *phydev) +-{ +- return linkmode_test_bit(ETHTOOL_LINK_MODE_2500baseT_Full_BIT, phydev->supported); +-} +- +-static int qca808x_config_init(struct phy_device *phydev) +-{ +- int ret; +- +- /* Active adc&vga on 802.3az for the link 1000M and 100M */ +- ret = phy_modify_mmd(phydev, MDIO_MMD_PCS, QCA808X_PHY_MMD3_ADDR_CLD_CTRL7, +- QCA808X_8023AZ_AFE_CTRL_MASK, QCA808X_8023AZ_AFE_EN); +- if (ret) +- return ret; +- +- /* Adjust the threshold on 802.3az for the link 1000M */ +- ret = phy_write_mmd(phydev, MDIO_MMD_PCS, +- QCA808X_PHY_MMD3_AZ_TRAINING_CTRL, +- QCA808X_MMD3_AZ_TRAINING_VAL); +- if (ret) +- return ret; +- +- if (qca808x_has_fast_retrain_or_slave_seed(phydev)) { +- /* Config the fast retrain for the link 2500M */ +- ret = qca808x_phy_fast_retrain_config(phydev); +- if (ret) +- return ret; +- +- ret = genphy_read_master_slave(phydev); +- if (ret < 0) +- return ret; +- +- if (!qca808x_is_prefer_master(phydev)) { +- /* Enable seed and configure lower ramdom seed to make phy +- * linked as slave mode. +- */ +- ret = qca808x_phy_ms_seed_enable(phydev, true); +- if (ret) +- return ret; +- } +- } +- +- /* Configure adc threshold as 100mv for the link 10M */ +- return at803x_debug_reg_mask(phydev, QCA808X_PHY_DEBUG_ADC_THRESHOLD, +- QCA808X_ADC_THRESHOLD_MASK, +- QCA808X_ADC_THRESHOLD_100MV); +-} +- +-static int qca808x_read_status(struct phy_device *phydev) +-{ +- struct at803x_ss_mask ss_mask = { 0 }; +- int ret; +- +- ret = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_10GBT_STAT); +- if (ret < 0) +- return ret; +- +- linkmode_mod_bit(ETHTOOL_LINK_MODE_2500baseT_Full_BIT, phydev->lp_advertising, +- ret & MDIO_AN_10GBT_STAT_LP2_5G); +- +- ret = genphy_read_status(phydev); +- if (ret) +- return ret; +- +- /* qca8081 takes the different bits for speed value from at803x */ +- ss_mask.speed_mask = QCA808X_SS_SPEED_MASK; +- ss_mask.speed_shift = __bf_shf(QCA808X_SS_SPEED_MASK); +- ret = at803x_read_specific_status(phydev, ss_mask); +- if (ret < 0) +- return ret; +- +- if (phydev->link) { +- if (phydev->speed == SPEED_2500) +- phydev->interface = PHY_INTERFACE_MODE_2500BASEX; +- else +- phydev->interface = PHY_INTERFACE_MODE_SGMII; +- } else { +- /* generate seed as a lower random value to make PHY linked as SLAVE easily, +- * except for master/slave configuration fault detected or the master mode +- * preferred. +- * +- * the reason for not putting this code into the function link_change_notify is +- * the corner case where the link partner is also the qca8081 PHY and the seed +- * value is configured as the same value, the link can't be up and no link change +- * occurs. +- */ +- if (qca808x_has_fast_retrain_or_slave_seed(phydev)) { +- if (phydev->master_slave_state == MASTER_SLAVE_STATE_ERR || +- qca808x_is_prefer_master(phydev)) { +- qca808x_phy_ms_seed_enable(phydev, false); +- } else { +- qca808x_phy_ms_seed_enable(phydev, true); +- } +- } +- } +- +- return 0; +-} +- +-static int qca808x_soft_reset(struct phy_device *phydev) +-{ +- int ret; +- +- ret = genphy_soft_reset(phydev); +- if (ret < 0) +- return ret; +- +- if (qca808x_has_fast_retrain_or_slave_seed(phydev)) +- ret = qca808x_phy_ms_seed_enable(phydev, true); +- +- return ret; +-} +- +-static bool qca808x_cdt_fault_length_valid(int cdt_code) +-{ +- switch (cdt_code) { +- case QCA808X_CDT_STATUS_STAT_SAME_SHORT: +- case QCA808X_CDT_STATUS_STAT_SAME_OPEN: +- case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_NORMAL: +- case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_OPEN: +- case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_SHORT: +- case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_NORMAL: +- case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_OPEN: +- case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_SHORT: +- case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_NORMAL: +- case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_OPEN: +- case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_SHORT: +- return true; +- default: +- return false; +- } +-} +- +-static int qca808x_cable_test_result_trans(int cdt_code) +-{ +- switch (cdt_code) { +- case QCA808X_CDT_STATUS_STAT_NORMAL: +- return ETHTOOL_A_CABLE_RESULT_CODE_OK; +- case QCA808X_CDT_STATUS_STAT_SAME_SHORT: +- return ETHTOOL_A_CABLE_RESULT_CODE_SAME_SHORT; +- case QCA808X_CDT_STATUS_STAT_SAME_OPEN: +- return ETHTOOL_A_CABLE_RESULT_CODE_OPEN; +- case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_NORMAL: +- case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_OPEN: +- case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_SHORT: +- case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_NORMAL: +- case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_OPEN: +- case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_SHORT: +- case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_NORMAL: +- case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_OPEN: +- case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_SHORT: +- return ETHTOOL_A_CABLE_RESULT_CODE_CROSS_SHORT; +- case QCA808X_CDT_STATUS_STAT_FAIL: +- default: +- return ETHTOOL_A_CABLE_RESULT_CODE_UNSPEC; +- } +-} +- +-static int qca808x_cdt_fault_length(struct phy_device *phydev, int pair, +- int result) +-{ +- int val; +- u32 cdt_length_reg = 0; +- +- switch (pair) { +- case ETHTOOL_A_CABLE_PAIR_A: +- cdt_length_reg = QCA808X_MMD3_CDT_DIAG_PAIR_A; +- break; +- case ETHTOOL_A_CABLE_PAIR_B: +- cdt_length_reg = QCA808X_MMD3_CDT_DIAG_PAIR_B; +- break; +- case ETHTOOL_A_CABLE_PAIR_C: +- cdt_length_reg = QCA808X_MMD3_CDT_DIAG_PAIR_C; +- break; +- case ETHTOOL_A_CABLE_PAIR_D: +- cdt_length_reg = QCA808X_MMD3_CDT_DIAG_PAIR_D; +- break; +- default: +- return -EINVAL; +- } +- +- val = phy_read_mmd(phydev, MDIO_MMD_PCS, cdt_length_reg); +- if (val < 0) +- return val; +- +- if (result == ETHTOOL_A_CABLE_RESULT_CODE_SAME_SHORT) +- val = FIELD_GET(QCA808X_CDT_DIAG_LENGTH_SAME_SHORT, val); +- else +- val = FIELD_GET(QCA808X_CDT_DIAG_LENGTH_CROSS_SHORT, val); +- +- return at803x_cdt_fault_length(val); +-} +- +-static int qca808x_cable_test_start(struct phy_device *phydev) +-{ +- int ret; +- +- /* perform CDT with the following configs: +- * 1. disable hibernation. +- * 2. force PHY working in MDI mode. +- * 3. for PHY working in 1000BaseT. +- * 4. configure the threshold. +- */ +- +- ret = at803x_debug_reg_mask(phydev, QCA808X_DBG_AN_TEST, QCA808X_HIBERNATION_EN, 0); +- if (ret < 0) +- return ret; +- +- ret = at803x_config_mdix(phydev, ETH_TP_MDI); +- if (ret < 0) +- return ret; +- +- /* Force 1000base-T needs to configure PMA/PMD and MII_BMCR */ +- phydev->duplex = DUPLEX_FULL; +- phydev->speed = SPEED_1000; +- ret = genphy_c45_pma_setup_forced(phydev); +- if (ret < 0) +- return ret; +- +- ret = genphy_setup_forced(phydev); +- if (ret < 0) +- return ret; +- +- /* configure the thresholds for open, short, pair ok test */ +- phy_write_mmd(phydev, MDIO_MMD_PCS, 0x8074, 0xc040); +- phy_write_mmd(phydev, MDIO_MMD_PCS, 0x8076, 0xc040); +- phy_write_mmd(phydev, MDIO_MMD_PCS, 0x8077, 0xa060); +- phy_write_mmd(phydev, MDIO_MMD_PCS, 0x8078, 0xc050); +- phy_write_mmd(phydev, MDIO_MMD_PCS, 0x807a, 0xc060); +- phy_write_mmd(phydev, MDIO_MMD_PCS, 0x807e, 0xb060); +- +- return 0; +-} +- +-static int qca808x_cable_test_get_pair_status(struct phy_device *phydev, u8 pair, +- u16 status) +-{ +- int length, result; +- u16 pair_code; +- +- switch (pair) { +- case ETHTOOL_A_CABLE_PAIR_A: +- pair_code = FIELD_GET(QCA808X_CDT_CODE_PAIR_A, status); +- break; +- case ETHTOOL_A_CABLE_PAIR_B: +- pair_code = FIELD_GET(QCA808X_CDT_CODE_PAIR_B, status); +- break; +- case ETHTOOL_A_CABLE_PAIR_C: +- pair_code = FIELD_GET(QCA808X_CDT_CODE_PAIR_C, status); +- break; +- case ETHTOOL_A_CABLE_PAIR_D: +- pair_code = FIELD_GET(QCA808X_CDT_CODE_PAIR_D, status); +- break; +- default: +- return -EINVAL; +- } +- +- result = qca808x_cable_test_result_trans(pair_code); +- ethnl_cable_test_result(phydev, pair, result); +- +- if (qca808x_cdt_fault_length_valid(pair_code)) { +- length = qca808x_cdt_fault_length(phydev, pair, result); +- ethnl_cable_test_fault_length(phydev, pair, length); +- } +- +- return 0; +-} +- +-static int qca808x_cable_test_get_status(struct phy_device *phydev, bool *finished) +-{ +- int ret, val; +- +- *finished = false; +- +- val = QCA808X_CDT_ENABLE_TEST | +- QCA808X_CDT_LENGTH_UNIT; +- ret = at803x_cdt_start(phydev, val); +- if (ret) +- return ret; +- +- ret = at803x_cdt_wait_for_completion(phydev, QCA808X_CDT_ENABLE_TEST); +- if (ret) +- return ret; +- +- val = phy_read_mmd(phydev, MDIO_MMD_PCS, QCA808X_MMD3_CDT_STATUS); +- if (val < 0) +- return val; +- +- ret = qca808x_cable_test_get_pair_status(phydev, ETHTOOL_A_CABLE_PAIR_A, val); +- if (ret) +- return ret; +- +- ret = qca808x_cable_test_get_pair_status(phydev, ETHTOOL_A_CABLE_PAIR_B, val); +- if (ret) +- return ret; +- +- ret = qca808x_cable_test_get_pair_status(phydev, ETHTOOL_A_CABLE_PAIR_C, val); +- if (ret) +- return ret; +- +- ret = qca808x_cable_test_get_pair_status(phydev, ETHTOOL_A_CABLE_PAIR_D, val); +- if (ret) +- return ret; +- +- *finished = true; +- +- return 0; +-} +- +-static int qca808x_get_features(struct phy_device *phydev) +-{ +- int ret; +- +- ret = genphy_c45_pma_read_abilities(phydev); +- if (ret) +- return ret; +- +- /* The autoneg ability is not existed in bit3 of MMD7.1, +- * but it is supported by qca808x PHY, so we add it here +- * manually. +- */ +- linkmode_set_bit(ETHTOOL_LINK_MODE_Autoneg_BIT, phydev->supported); +- +- /* As for the qca8081 1G version chip, the 2500baseT ability is also +- * existed in the bit0 of MMD1.21, we need to remove it manually if +- * it is the qca8081 1G chip according to the bit0 of MMD7.0x901d. +- */ +- ret = phy_read_mmd(phydev, MDIO_MMD_AN, QCA808X_PHY_MMD7_CHIP_TYPE); +- if (ret < 0) +- return ret; +- +- if (QCA808X_PHY_CHIP_TYPE_1G & ret) +- linkmode_clear_bit(ETHTOOL_LINK_MODE_2500baseT_Full_BIT, phydev->supported); +- +- return 0; +-} +- +-static int qca808x_config_aneg(struct phy_device *phydev) +-{ +- int phy_ctrl = 0; +- int ret; +- +- ret = at803x_prepare_config_aneg(phydev); +- if (ret) +- return ret; +- +- /* The reg MII_BMCR also needs to be configured for force mode, the +- * genphy_config_aneg is also needed. +- */ +- if (phydev->autoneg == AUTONEG_DISABLE) +- genphy_c45_pma_setup_forced(phydev); +- +- if (linkmode_test_bit(ETHTOOL_LINK_MODE_2500baseT_Full_BIT, phydev->advertising)) +- phy_ctrl = MDIO_AN_10GBT_CTRL_ADV2_5G; +- +- ret = phy_modify_mmd_changed(phydev, MDIO_MMD_AN, MDIO_AN_10GBT_CTRL, +- MDIO_AN_10GBT_CTRL_ADV2_5G, phy_ctrl); +- if (ret < 0) +- return ret; +- +- return __genphy_config_aneg(phydev, ret); +-} +- +-static void qca808x_link_change_notify(struct phy_device *phydev) +-{ +- /* Assert interface sgmii fifo on link down, deassert it on link up, +- * the interface device address is always phy address added by 1. +- */ +- mdiobus_c45_modify_changed(phydev->mdio.bus, phydev->mdio.addr + 1, +- MDIO_MMD_PMAPMD, QCA8081_PHY_SERDES_MMD1_FIFO_CTRL, +- QCA8081_PHY_FIFO_RSTN, +- phydev->link ? QCA8081_PHY_FIFO_RSTN : 0); +-} +- +-static int qca808x_led_parse_netdev(struct phy_device *phydev, unsigned long rules, +- u16 *offload_trigger) +-{ +- /* Parsing specific to netdev trigger */ +- if (test_bit(TRIGGER_NETDEV_TX, &rules)) +- *offload_trigger |= QCA808X_LED_TX_BLINK; +- if (test_bit(TRIGGER_NETDEV_RX, &rules)) +- *offload_trigger |= QCA808X_LED_RX_BLINK; +- if (test_bit(TRIGGER_NETDEV_LINK_10, &rules)) +- *offload_trigger |= QCA808X_LED_SPEED10_ON; +- if (test_bit(TRIGGER_NETDEV_LINK_100, &rules)) +- *offload_trigger |= QCA808X_LED_SPEED100_ON; +- if (test_bit(TRIGGER_NETDEV_LINK_1000, &rules)) +- *offload_trigger |= QCA808X_LED_SPEED1000_ON; +- if (test_bit(TRIGGER_NETDEV_LINK_2500, &rules)) +- *offload_trigger |= QCA808X_LED_SPEED2500_ON; +- if (test_bit(TRIGGER_NETDEV_HALF_DUPLEX, &rules)) +- *offload_trigger |= QCA808X_LED_HALF_DUPLEX_ON; +- if (test_bit(TRIGGER_NETDEV_FULL_DUPLEX, &rules)) +- *offload_trigger |= QCA808X_LED_FULL_DUPLEX_ON; +- +- if (rules && !*offload_trigger) +- return -EOPNOTSUPP; +- +- /* Enable BLINK_CHECK_BYPASS by default to make the LED +- * blink even with duplex or speed mode not enabled. +- */ +- *offload_trigger |= QCA808X_LED_BLINK_CHECK_BYPASS; +- +- return 0; +-} +- +-static int qca808x_led_hw_control_enable(struct phy_device *phydev, u8 index) +-{ +- u16 reg; +- +- if (index > 2) +- return -EINVAL; +- +- reg = QCA808X_MMD7_LED_FORCE_CTRL(index); +- +- return phy_clear_bits_mmd(phydev, MDIO_MMD_AN, reg, +- QCA808X_LED_FORCE_EN); +-} +- +-static int qca808x_led_hw_is_supported(struct phy_device *phydev, u8 index, +- unsigned long rules) +-{ +- u16 offload_trigger = 0; +- +- if (index > 2) +- return -EINVAL; +- +- return qca808x_led_parse_netdev(phydev, rules, &offload_trigger); +-} +- +-static int qca808x_led_hw_control_set(struct phy_device *phydev, u8 index, +- unsigned long rules) +-{ +- u16 reg, offload_trigger = 0; +- int ret; +- +- if (index > 2) +- return -EINVAL; +- +- reg = QCA808X_MMD7_LED_CTRL(index); +- +- ret = qca808x_led_parse_netdev(phydev, rules, &offload_trigger); +- if (ret) +- return ret; +- +- ret = qca808x_led_hw_control_enable(phydev, index); +- if (ret) +- return ret; +- +- return phy_modify_mmd(phydev, MDIO_MMD_AN, reg, +- QCA808X_LED_PATTERN_MASK, +- offload_trigger); +-} +- +-static bool qca808x_led_hw_control_status(struct phy_device *phydev, u8 index) +-{ +- u16 reg; +- int val; +- +- if (index > 2) +- return false; +- +- reg = QCA808X_MMD7_LED_FORCE_CTRL(index); +- +- val = phy_read_mmd(phydev, MDIO_MMD_AN, reg); +- +- return !(val & QCA808X_LED_FORCE_EN); +-} +- +-static int qca808x_led_hw_control_get(struct phy_device *phydev, u8 index, +- unsigned long *rules) +-{ +- u16 reg; +- int val; +- +- if (index > 2) +- return -EINVAL; +- +- /* Check if we have hw control enabled */ +- if (qca808x_led_hw_control_status(phydev, index)) +- return -EINVAL; +- +- reg = QCA808X_MMD7_LED_CTRL(index); +- +- val = phy_read_mmd(phydev, MDIO_MMD_AN, reg); +- if (val & QCA808X_LED_TX_BLINK) +- set_bit(TRIGGER_NETDEV_TX, rules); +- if (val & QCA808X_LED_RX_BLINK) +- set_bit(TRIGGER_NETDEV_RX, rules); +- if (val & QCA808X_LED_SPEED10_ON) +- set_bit(TRIGGER_NETDEV_LINK_10, rules); +- if (val & QCA808X_LED_SPEED100_ON) +- set_bit(TRIGGER_NETDEV_LINK_100, rules); +- if (val & QCA808X_LED_SPEED1000_ON) +- set_bit(TRIGGER_NETDEV_LINK_1000, rules); +- if (val & QCA808X_LED_SPEED2500_ON) +- set_bit(TRIGGER_NETDEV_LINK_2500, rules); +- if (val & QCA808X_LED_HALF_DUPLEX_ON) +- set_bit(TRIGGER_NETDEV_HALF_DUPLEX, rules); +- if (val & QCA808X_LED_FULL_DUPLEX_ON) +- set_bit(TRIGGER_NETDEV_FULL_DUPLEX, rules); +- +- return 0; +-} +- +-static int qca808x_led_hw_control_reset(struct phy_device *phydev, u8 index) +-{ +- u16 reg; +- +- if (index > 2) +- return -EINVAL; +- +- reg = QCA808X_MMD7_LED_CTRL(index); +- +- return phy_clear_bits_mmd(phydev, MDIO_MMD_AN, reg, +- QCA808X_LED_PATTERN_MASK); +-} +- +-static int qca808x_led_brightness_set(struct phy_device *phydev, +- u8 index, enum led_brightness value) +-{ +- u16 reg; +- int ret; +- +- if (index > 2) +- return -EINVAL; +- +- if (!value) { +- ret = qca808x_led_hw_control_reset(phydev, index); +- if (ret) +- return ret; +- } +- +- reg = QCA808X_MMD7_LED_FORCE_CTRL(index); +- +- return phy_modify_mmd(phydev, MDIO_MMD_AN, reg, +- QCA808X_LED_FORCE_EN | QCA808X_LED_FORCE_MODE_MASK, +- QCA808X_LED_FORCE_EN | value ? QCA808X_LED_FORCE_ON : +- QCA808X_LED_FORCE_OFF); +-} +- +-static int qca808x_led_blink_set(struct phy_device *phydev, u8 index, +- unsigned long *delay_on, +- unsigned long *delay_off) +-{ +- int ret; +- u16 reg; +- +- if (index > 2) +- return -EINVAL; +- +- reg = QCA808X_MMD7_LED_FORCE_CTRL(index); +- +- /* Set blink to 50% off, 50% on at 4Hz by default */ +- ret = phy_modify_mmd(phydev, MDIO_MMD_AN, QCA808X_MMD7_LED_GLOBAL, +- QCA808X_LED_BLINK_FREQ_MASK | QCA808X_LED_BLINK_DUTY_MASK, +- QCA808X_LED_BLINK_FREQ_4HZ | QCA808X_LED_BLINK_DUTY_50_50); +- if (ret) +- return ret; +- +- /* We use BLINK_1 for normal blinking */ +- ret = phy_modify_mmd(phydev, MDIO_MMD_AN, reg, +- QCA808X_LED_FORCE_EN | QCA808X_LED_FORCE_MODE_MASK, +- QCA808X_LED_FORCE_EN | QCA808X_LED_FORCE_BLINK_1); +- if (ret) +- return ret; +- +- /* We set blink to 4Hz, aka 250ms */ +- *delay_on = 250 / 2; +- *delay_off = 250 / 2; +- +- return 0; +-} +- +-static int qca808x_led_polarity_set(struct phy_device *phydev, int index, +- unsigned long modes) +-{ +- struct at803x_priv *priv = phydev->priv; +- bool active_low = false; +- u32 mode; +- +- for_each_set_bit(mode, &modes, __PHY_LED_MODES_NUM) { +- switch (mode) { +- case PHY_LED_ACTIVE_LOW: +- active_low = true; +- break; +- default: +- return -EINVAL; +- } +- } +- +- /* PHY polarity is global and can't be set per LED. +- * To detect this, check if last requested polarity mode +- * match the new one. +- */ +- if (priv->led_polarity_mode >= 0 && +- priv->led_polarity_mode != active_low) { +- phydev_err(phydev, "PHY polarity is global. Mismatched polarity on different LED\n"); +- return -EINVAL; +- } +- +- /* Save the last PHY polarity mode */ +- priv->led_polarity_mode = active_low; +- +- return phy_modify_mmd(phydev, MDIO_MMD_AN, +- QCA808X_MMD7_LED_POLARITY_CTRL, +- QCA808X_LED_ACTIVE_HIGH, +- active_low ? 0 : QCA808X_LED_ACTIVE_HIGH); +-} +- + static struct phy_driver at803x_driver[] = { + { + /* Qualcomm Atheros AR8035 */ +@@ -1989,34 +1123,6 @@ static struct phy_driver at803x_driver[] + .read_status = at803x_read_status, + .soft_reset = genphy_soft_reset, + .config_aneg = at803x_config_aneg, +-}, { +- /* Qualcomm QCA8081 */ +- PHY_ID_MATCH_EXACT(QCA8081_PHY_ID), +- .name = "Qualcomm QCA8081", +- .flags = PHY_POLL_CABLE_TEST, +- .probe = at803x_probe, +- .config_intr = at803x_config_intr, +- .handle_interrupt = at803x_handle_interrupt, +- .get_tunable = at803x_get_tunable, +- .set_tunable = at803x_set_tunable, +- .set_wol = at803x_set_wol, +- .get_wol = at803x_get_wol, +- .get_features = qca808x_get_features, +- .config_aneg = qca808x_config_aneg, +- .suspend = genphy_suspend, +- .resume = genphy_resume, +- .read_status = qca808x_read_status, +- .config_init = qca808x_config_init, +- .soft_reset = qca808x_soft_reset, +- .cable_test_start = qca808x_cable_test_start, +- .cable_test_get_status = qca808x_cable_test_get_status, +- .link_change_notify = qca808x_link_change_notify, +- .led_brightness_set = qca808x_led_brightness_set, +- .led_blink_set = qca808x_led_blink_set, +- .led_hw_is_supported = qca808x_led_hw_is_supported, +- .led_hw_control_set = qca808x_led_hw_control_set, +- .led_hw_control_get = qca808x_led_hw_control_get, +- .led_polarity_set = qca808x_led_polarity_set, + }, }; + + module_phy_driver(at803x_driver); +@@ -2028,7 +1134,6 @@ 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(QCA9561_PHY_ID) }, +- { PHY_ID_MATCH_EXACT(QCA8081_PHY_ID) }, + { } + }; + +--- /dev/null ++++ b/drivers/net/phy/qcom/qca808x.c +@@ -0,0 +1,934 @@ ++// SPDX-License-Identifier: GPL-2.0+ ++ ++#include ++#include ++#include ++ ++#include "qcom.h" ++ ++/* ADC threshold */ ++#define QCA808X_PHY_DEBUG_ADC_THRESHOLD 0x2c80 ++#define QCA808X_ADC_THRESHOLD_MASK GENMASK(7, 0) ++#define QCA808X_ADC_THRESHOLD_80MV 0 ++#define QCA808X_ADC_THRESHOLD_100MV 0xf0 ++#define QCA808X_ADC_THRESHOLD_200MV 0x0f ++#define QCA808X_ADC_THRESHOLD_300MV 0xff ++ ++/* CLD control */ ++#define QCA808X_PHY_MMD3_ADDR_CLD_CTRL7 0x8007 ++#define QCA808X_8023AZ_AFE_CTRL_MASK GENMASK(8, 4) ++#define QCA808X_8023AZ_AFE_EN 0x90 ++ ++/* AZ control */ ++#define QCA808X_PHY_MMD3_AZ_TRAINING_CTRL 0x8008 ++#define QCA808X_MMD3_AZ_TRAINING_VAL 0x1c32 ++ ++#define QCA808X_PHY_MMD1_MSE_THRESHOLD_20DB 0x8014 ++#define QCA808X_MSE_THRESHOLD_20DB_VALUE 0x529 ++ ++#define QCA808X_PHY_MMD1_MSE_THRESHOLD_17DB 0x800E ++#define QCA808X_MSE_THRESHOLD_17DB_VALUE 0x341 ++ ++#define QCA808X_PHY_MMD1_MSE_THRESHOLD_27DB 0x801E ++#define QCA808X_MSE_THRESHOLD_27DB_VALUE 0x419 ++ ++#define QCA808X_PHY_MMD1_MSE_THRESHOLD_28DB 0x8020 ++#define QCA808X_MSE_THRESHOLD_28DB_VALUE 0x341 ++ ++#define QCA808X_PHY_MMD7_TOP_OPTION1 0x901c ++#define QCA808X_TOP_OPTION1_DATA 0x0 ++ ++#define QCA808X_PHY_MMD3_DEBUG_1 0xa100 ++#define QCA808X_MMD3_DEBUG_1_VALUE 0x9203 ++#define QCA808X_PHY_MMD3_DEBUG_2 0xa101 ++#define QCA808X_MMD3_DEBUG_2_VALUE 0x48ad ++#define QCA808X_PHY_MMD3_DEBUG_3 0xa103 ++#define QCA808X_MMD3_DEBUG_3_VALUE 0x1698 ++#define QCA808X_PHY_MMD3_DEBUG_4 0xa105 ++#define QCA808X_MMD3_DEBUG_4_VALUE 0x8001 ++#define QCA808X_PHY_MMD3_DEBUG_5 0xa106 ++#define QCA808X_MMD3_DEBUG_5_VALUE 0x1111 ++#define QCA808X_PHY_MMD3_DEBUG_6 0xa011 ++#define QCA808X_MMD3_DEBUG_6_VALUE 0x5f85 ++ ++/* master/slave seed config */ ++#define QCA808X_PHY_DEBUG_LOCAL_SEED 9 ++#define QCA808X_MASTER_SLAVE_SEED_ENABLE BIT(1) ++#define QCA808X_MASTER_SLAVE_SEED_CFG GENMASK(12, 2) ++#define QCA808X_MASTER_SLAVE_SEED_RANGE 0x32 ++ ++/* Hibernation yields lower power consumpiton in contrast with normal operation mode. ++ * when the copper cable is unplugged, the PHY enters into hibernation mode in about 10s. ++ */ ++#define QCA808X_DBG_AN_TEST 0xb ++#define QCA808X_HIBERNATION_EN BIT(15) ++ ++#define QCA808X_CDT_ENABLE_TEST BIT(15) ++#define QCA808X_CDT_INTER_CHECK_DIS BIT(13) ++#define QCA808X_CDT_STATUS BIT(11) ++#define QCA808X_CDT_LENGTH_UNIT BIT(10) ++ ++#define QCA808X_MMD3_CDT_STATUS 0x8064 ++#define QCA808X_MMD3_CDT_DIAG_PAIR_A 0x8065 ++#define QCA808X_MMD3_CDT_DIAG_PAIR_B 0x8066 ++#define QCA808X_MMD3_CDT_DIAG_PAIR_C 0x8067 ++#define QCA808X_MMD3_CDT_DIAG_PAIR_D 0x8068 ++#define QCA808X_CDT_DIAG_LENGTH_SAME_SHORT GENMASK(15, 8) ++#define QCA808X_CDT_DIAG_LENGTH_CROSS_SHORT GENMASK(7, 0) ++ ++#define QCA808X_CDT_CODE_PAIR_A GENMASK(15, 12) ++#define QCA808X_CDT_CODE_PAIR_B GENMASK(11, 8) ++#define QCA808X_CDT_CODE_PAIR_C GENMASK(7, 4) ++#define QCA808X_CDT_CODE_PAIR_D GENMASK(3, 0) ++ ++#define QCA808X_CDT_STATUS_STAT_TYPE GENMASK(1, 0) ++#define QCA808X_CDT_STATUS_STAT_FAIL FIELD_PREP_CONST(QCA808X_CDT_STATUS_STAT_TYPE, 0) ++#define QCA808X_CDT_STATUS_STAT_NORMAL FIELD_PREP_CONST(QCA808X_CDT_STATUS_STAT_TYPE, 1) ++#define QCA808X_CDT_STATUS_STAT_SAME_OPEN FIELD_PREP_CONST(QCA808X_CDT_STATUS_STAT_TYPE, 2) ++#define QCA808X_CDT_STATUS_STAT_SAME_SHORT FIELD_PREP_CONST(QCA808X_CDT_STATUS_STAT_TYPE, 3) ++ ++#define QCA808X_CDT_STATUS_STAT_MDI GENMASK(3, 2) ++#define QCA808X_CDT_STATUS_STAT_MDI1 FIELD_PREP_CONST(QCA808X_CDT_STATUS_STAT_MDI, 1) ++#define QCA808X_CDT_STATUS_STAT_MDI2 FIELD_PREP_CONST(QCA808X_CDT_STATUS_STAT_MDI, 2) ++#define QCA808X_CDT_STATUS_STAT_MDI3 FIELD_PREP_CONST(QCA808X_CDT_STATUS_STAT_MDI, 3) ++ ++/* NORMAL are MDI with type set to 0 */ ++#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_NORMAL QCA808X_CDT_STATUS_STAT_MDI1 ++#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_OPEN (QCA808X_CDT_STATUS_STAT_SAME_OPEN |\ ++ QCA808X_CDT_STATUS_STAT_MDI1) ++#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_SHORT (QCA808X_CDT_STATUS_STAT_SAME_SHORT |\ ++ QCA808X_CDT_STATUS_STAT_MDI1) ++#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_NORMAL QCA808X_CDT_STATUS_STAT_MDI2 ++#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_OPEN (QCA808X_CDT_STATUS_STAT_SAME_OPEN |\ ++ QCA808X_CDT_STATUS_STAT_MDI2) ++#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_SHORT (QCA808X_CDT_STATUS_STAT_SAME_SHORT |\ ++ QCA808X_CDT_STATUS_STAT_MDI2) ++#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_NORMAL QCA808X_CDT_STATUS_STAT_MDI3 ++#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_OPEN (QCA808X_CDT_STATUS_STAT_SAME_OPEN |\ ++ QCA808X_CDT_STATUS_STAT_MDI3) ++#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_SHORT (QCA808X_CDT_STATUS_STAT_SAME_SHORT |\ ++ QCA808X_CDT_STATUS_STAT_MDI3) ++ ++/* Added for reference of existence but should be handled by wait_for_completion already */ ++#define QCA808X_CDT_STATUS_STAT_BUSY (BIT(1) | BIT(3)) ++ ++#define QCA808X_MMD7_LED_GLOBAL 0x8073 ++#define QCA808X_LED_BLINK_1 GENMASK(11, 6) ++#define QCA808X_LED_BLINK_2 GENMASK(5, 0) ++/* Values are the same for both BLINK_1 and BLINK_2 */ ++#define QCA808X_LED_BLINK_FREQ_MASK GENMASK(5, 3) ++#define QCA808X_LED_BLINK_FREQ_2HZ FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x0) ++#define QCA808X_LED_BLINK_FREQ_4HZ FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x1) ++#define QCA808X_LED_BLINK_FREQ_8HZ FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x2) ++#define QCA808X_LED_BLINK_FREQ_16HZ FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x3) ++#define QCA808X_LED_BLINK_FREQ_32HZ FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x4) ++#define QCA808X_LED_BLINK_FREQ_64HZ FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x5) ++#define QCA808X_LED_BLINK_FREQ_128HZ FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x6) ++#define QCA808X_LED_BLINK_FREQ_256HZ FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x7) ++#define QCA808X_LED_BLINK_DUTY_MASK GENMASK(2, 0) ++#define QCA808X_LED_BLINK_DUTY_50_50 FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x0) ++#define QCA808X_LED_BLINK_DUTY_75_25 FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x1) ++#define QCA808X_LED_BLINK_DUTY_25_75 FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x2) ++#define QCA808X_LED_BLINK_DUTY_33_67 FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x3) ++#define QCA808X_LED_BLINK_DUTY_67_33 FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x4) ++#define QCA808X_LED_BLINK_DUTY_17_83 FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x5) ++#define QCA808X_LED_BLINK_DUTY_83_17 FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x6) ++#define QCA808X_LED_BLINK_DUTY_8_92 FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x7) ++ ++#define QCA808X_MMD7_LED2_CTRL 0x8074 ++#define QCA808X_MMD7_LED2_FORCE_CTRL 0x8075 ++#define QCA808X_MMD7_LED1_CTRL 0x8076 ++#define QCA808X_MMD7_LED1_FORCE_CTRL 0x8077 ++#define QCA808X_MMD7_LED0_CTRL 0x8078 ++#define QCA808X_MMD7_LED_CTRL(x) (0x8078 - ((x) * 2)) ++ ++/* LED hw control pattern is the same for every LED */ ++#define QCA808X_LED_PATTERN_MASK GENMASK(15, 0) ++#define QCA808X_LED_SPEED2500_ON BIT(15) ++#define QCA808X_LED_SPEED2500_BLINK BIT(14) ++/* Follow blink trigger even if duplex or speed condition doesn't match */ ++#define QCA808X_LED_BLINK_CHECK_BYPASS BIT(13) ++#define QCA808X_LED_FULL_DUPLEX_ON BIT(12) ++#define QCA808X_LED_HALF_DUPLEX_ON BIT(11) ++#define QCA808X_LED_TX_BLINK BIT(10) ++#define QCA808X_LED_RX_BLINK BIT(9) ++#define QCA808X_LED_TX_ON_10MS BIT(8) ++#define QCA808X_LED_RX_ON_10MS BIT(7) ++#define QCA808X_LED_SPEED1000_ON BIT(6) ++#define QCA808X_LED_SPEED100_ON BIT(5) ++#define QCA808X_LED_SPEED10_ON BIT(4) ++#define QCA808X_LED_COLLISION_BLINK BIT(3) ++#define QCA808X_LED_SPEED1000_BLINK BIT(2) ++#define QCA808X_LED_SPEED100_BLINK BIT(1) ++#define QCA808X_LED_SPEED10_BLINK BIT(0) ++ ++#define QCA808X_MMD7_LED0_FORCE_CTRL 0x8079 ++#define QCA808X_MMD7_LED_FORCE_CTRL(x) (0x8079 - ((x) * 2)) ++ ++/* LED force ctrl is the same for every LED ++ * No documentation exist for this, not even internal one ++ * with NDA as QCOM gives only info about configuring ++ * hw control pattern rules and doesn't indicate any way ++ * to force the LED to specific mode. ++ * These define comes from reverse and testing and maybe ++ * lack of some info or some info are not entirely correct. ++ * For the basic LED control and hw control these finding ++ * are enough to support LED control in all the required APIs. ++ * ++ * On doing some comparison with implementation with qca807x, ++ * it was found that it's 1:1 equal to it and confirms all the ++ * reverse done. It was also found further specification with the ++ * force mode and the blink modes. ++ */ ++#define QCA808X_LED_FORCE_EN BIT(15) ++#define QCA808X_LED_FORCE_MODE_MASK GENMASK(14, 13) ++#define QCA808X_LED_FORCE_BLINK_1 FIELD_PREP(QCA808X_LED_FORCE_MODE_MASK, 0x3) ++#define QCA808X_LED_FORCE_BLINK_2 FIELD_PREP(QCA808X_LED_FORCE_MODE_MASK, 0x2) ++#define QCA808X_LED_FORCE_ON FIELD_PREP(QCA808X_LED_FORCE_MODE_MASK, 0x1) ++#define QCA808X_LED_FORCE_OFF FIELD_PREP(QCA808X_LED_FORCE_MODE_MASK, 0x0) ++ ++#define QCA808X_MMD7_LED_POLARITY_CTRL 0x901a ++/* QSDK sets by default 0x46 to this reg that sets BIT 6 for ++ * LED to active high. It's not clear what BIT 3 and BIT 4 does. ++ */ ++#define QCA808X_LED_ACTIVE_HIGH BIT(6) ++ ++/* QCA808X 1G chip type */ ++#define QCA808X_PHY_MMD7_CHIP_TYPE 0x901d ++#define QCA808X_PHY_CHIP_TYPE_1G BIT(0) ++ ++#define QCA8081_PHY_SERDES_MMD1_FIFO_CTRL 0x9072 ++#define QCA8081_PHY_FIFO_RSTN BIT(11) ++ ++#define QCA8081_PHY_ID 0x004dd101 ++ ++MODULE_DESCRIPTION("Qualcomm Atheros QCA808X PHY driver"); ++MODULE_AUTHOR("Matus Ujhelyi"); ++MODULE_LICENSE("GPL"); ++ ++struct qca808x_priv { ++ int led_polarity_mode; ++}; ++ ++static int qca808x_phy_fast_retrain_config(struct phy_device *phydev) ++{ ++ int ret; ++ ++ /* Enable fast retrain */ ++ ret = genphy_c45_fast_retrain(phydev, true); ++ if (ret) ++ return ret; ++ ++ phy_write_mmd(phydev, MDIO_MMD_AN, QCA808X_PHY_MMD7_TOP_OPTION1, ++ QCA808X_TOP_OPTION1_DATA); ++ phy_write_mmd(phydev, MDIO_MMD_PMAPMD, QCA808X_PHY_MMD1_MSE_THRESHOLD_20DB, ++ QCA808X_MSE_THRESHOLD_20DB_VALUE); ++ phy_write_mmd(phydev, MDIO_MMD_PMAPMD, QCA808X_PHY_MMD1_MSE_THRESHOLD_17DB, ++ QCA808X_MSE_THRESHOLD_17DB_VALUE); ++ phy_write_mmd(phydev, MDIO_MMD_PMAPMD, QCA808X_PHY_MMD1_MSE_THRESHOLD_27DB, ++ QCA808X_MSE_THRESHOLD_27DB_VALUE); ++ phy_write_mmd(phydev, MDIO_MMD_PMAPMD, QCA808X_PHY_MMD1_MSE_THRESHOLD_28DB, ++ QCA808X_MSE_THRESHOLD_28DB_VALUE); ++ phy_write_mmd(phydev, MDIO_MMD_PCS, QCA808X_PHY_MMD3_DEBUG_1, ++ QCA808X_MMD3_DEBUG_1_VALUE); ++ phy_write_mmd(phydev, MDIO_MMD_PCS, QCA808X_PHY_MMD3_DEBUG_4, ++ QCA808X_MMD3_DEBUG_4_VALUE); ++ phy_write_mmd(phydev, MDIO_MMD_PCS, QCA808X_PHY_MMD3_DEBUG_5, ++ QCA808X_MMD3_DEBUG_5_VALUE); ++ phy_write_mmd(phydev, MDIO_MMD_PCS, QCA808X_PHY_MMD3_DEBUG_3, ++ QCA808X_MMD3_DEBUG_3_VALUE); ++ phy_write_mmd(phydev, MDIO_MMD_PCS, QCA808X_PHY_MMD3_DEBUG_6, ++ QCA808X_MMD3_DEBUG_6_VALUE); ++ phy_write_mmd(phydev, MDIO_MMD_PCS, QCA808X_PHY_MMD3_DEBUG_2, ++ QCA808X_MMD3_DEBUG_2_VALUE); ++ ++ return 0; ++} ++ ++static int qca808x_phy_ms_seed_enable(struct phy_device *phydev, bool enable) ++{ ++ u16 seed_value; ++ ++ if (!enable) ++ return at803x_debug_reg_mask(phydev, QCA808X_PHY_DEBUG_LOCAL_SEED, ++ QCA808X_MASTER_SLAVE_SEED_ENABLE, 0); ++ ++ seed_value = prandom_u32_max(QCA808X_MASTER_SLAVE_SEED_RANGE); ++ return at803x_debug_reg_mask(phydev, QCA808X_PHY_DEBUG_LOCAL_SEED, ++ QCA808X_MASTER_SLAVE_SEED_CFG | QCA808X_MASTER_SLAVE_SEED_ENABLE, ++ FIELD_PREP(QCA808X_MASTER_SLAVE_SEED_CFG, seed_value) | ++ QCA808X_MASTER_SLAVE_SEED_ENABLE); ++} ++ ++static bool qca808x_is_prefer_master(struct phy_device *phydev) ++{ ++ return (phydev->master_slave_get == MASTER_SLAVE_CFG_MASTER_FORCE) || ++ (phydev->master_slave_get == MASTER_SLAVE_CFG_MASTER_PREFERRED); ++} ++ ++static bool qca808x_has_fast_retrain_or_slave_seed(struct phy_device *phydev) ++{ ++ return linkmode_test_bit(ETHTOOL_LINK_MODE_2500baseT_Full_BIT, phydev->supported); ++} ++ ++static int qca808x_probe(struct phy_device *phydev) ++{ ++ struct device *dev = &phydev->mdio.dev; ++ struct qca808x_priv *priv; ++ ++ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); ++ if (!priv) ++ return -ENOMEM; ++ ++ /* Init LED polarity mode to -1 */ ++ priv->led_polarity_mode = -1; ++ ++ phydev->priv = priv; ++ ++ return 0; ++} ++ ++static int qca808x_config_init(struct phy_device *phydev) ++{ ++ int ret; ++ ++ /* Active adc&vga on 802.3az for the link 1000M and 100M */ ++ ret = phy_modify_mmd(phydev, MDIO_MMD_PCS, QCA808X_PHY_MMD3_ADDR_CLD_CTRL7, ++ QCA808X_8023AZ_AFE_CTRL_MASK, QCA808X_8023AZ_AFE_EN); ++ if (ret) ++ return ret; ++ ++ /* Adjust the threshold on 802.3az for the link 1000M */ ++ ret = phy_write_mmd(phydev, MDIO_MMD_PCS, ++ QCA808X_PHY_MMD3_AZ_TRAINING_CTRL, ++ QCA808X_MMD3_AZ_TRAINING_VAL); ++ if (ret) ++ return ret; ++ ++ if (qca808x_has_fast_retrain_or_slave_seed(phydev)) { ++ /* Config the fast retrain for the link 2500M */ ++ ret = qca808x_phy_fast_retrain_config(phydev); ++ if (ret) ++ return ret; ++ ++ ret = genphy_read_master_slave(phydev); ++ if (ret < 0) ++ return ret; ++ ++ if (!qca808x_is_prefer_master(phydev)) { ++ /* Enable seed and configure lower ramdom seed to make phy ++ * linked as slave mode. ++ */ ++ ret = qca808x_phy_ms_seed_enable(phydev, true); ++ if (ret) ++ return ret; ++ } ++ } ++ ++ /* Configure adc threshold as 100mv for the link 10M */ ++ return at803x_debug_reg_mask(phydev, QCA808X_PHY_DEBUG_ADC_THRESHOLD, ++ QCA808X_ADC_THRESHOLD_MASK, ++ QCA808X_ADC_THRESHOLD_100MV); ++} ++ ++static int qca808x_read_status(struct phy_device *phydev) ++{ ++ struct at803x_ss_mask ss_mask = { 0 }; ++ int ret; ++ ++ ret = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_10GBT_STAT); ++ if (ret < 0) ++ return ret; ++ ++ linkmode_mod_bit(ETHTOOL_LINK_MODE_2500baseT_Full_BIT, phydev->lp_advertising, ++ ret & MDIO_AN_10GBT_STAT_LP2_5G); ++ ++ ret = genphy_read_status(phydev); ++ if (ret) ++ return ret; ++ ++ /* qca8081 takes the different bits for speed value from at803x */ ++ ss_mask.speed_mask = QCA808X_SS_SPEED_MASK; ++ ss_mask.speed_shift = __bf_shf(QCA808X_SS_SPEED_MASK); ++ ret = at803x_read_specific_status(phydev, ss_mask); ++ if (ret < 0) ++ return ret; ++ ++ if (phydev->link) { ++ if (phydev->speed == SPEED_2500) ++ phydev->interface = PHY_INTERFACE_MODE_2500BASEX; ++ else ++ phydev->interface = PHY_INTERFACE_MODE_SGMII; ++ } else { ++ /* generate seed as a lower random value to make PHY linked as SLAVE easily, ++ * except for master/slave configuration fault detected or the master mode ++ * preferred. ++ * ++ * the reason for not putting this code into the function link_change_notify is ++ * the corner case where the link partner is also the qca8081 PHY and the seed ++ * value is configured as the same value, the link can't be up and no link change ++ * occurs. ++ */ ++ if (qca808x_has_fast_retrain_or_slave_seed(phydev)) { ++ if (phydev->master_slave_state == MASTER_SLAVE_STATE_ERR || ++ qca808x_is_prefer_master(phydev)) { ++ qca808x_phy_ms_seed_enable(phydev, false); ++ } else { ++ qca808x_phy_ms_seed_enable(phydev, true); ++ } ++ } ++ } ++ ++ return 0; ++} ++ ++static int qca808x_soft_reset(struct phy_device *phydev) ++{ ++ int ret; ++ ++ ret = genphy_soft_reset(phydev); ++ if (ret < 0) ++ return ret; ++ ++ if (qca808x_has_fast_retrain_or_slave_seed(phydev)) ++ ret = qca808x_phy_ms_seed_enable(phydev, true); ++ ++ return ret; ++} ++ ++static bool qca808x_cdt_fault_length_valid(int cdt_code) ++{ ++ switch (cdt_code) { ++ case QCA808X_CDT_STATUS_STAT_SAME_SHORT: ++ case QCA808X_CDT_STATUS_STAT_SAME_OPEN: ++ case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_NORMAL: ++ case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_OPEN: ++ case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_SHORT: ++ case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_NORMAL: ++ case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_OPEN: ++ case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_SHORT: ++ case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_NORMAL: ++ case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_OPEN: ++ case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_SHORT: ++ return true; ++ default: ++ return false; ++ } ++} ++ ++static int qca808x_cable_test_result_trans(int cdt_code) ++{ ++ switch (cdt_code) { ++ case QCA808X_CDT_STATUS_STAT_NORMAL: ++ return ETHTOOL_A_CABLE_RESULT_CODE_OK; ++ case QCA808X_CDT_STATUS_STAT_SAME_SHORT: ++ return ETHTOOL_A_CABLE_RESULT_CODE_SAME_SHORT; ++ case QCA808X_CDT_STATUS_STAT_SAME_OPEN: ++ return ETHTOOL_A_CABLE_RESULT_CODE_OPEN; ++ case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_NORMAL: ++ case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_OPEN: ++ case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_SHORT: ++ case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_NORMAL: ++ case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_OPEN: ++ case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_SHORT: ++ case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_NORMAL: ++ case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_OPEN: ++ case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_SHORT: ++ return ETHTOOL_A_CABLE_RESULT_CODE_CROSS_SHORT; ++ case QCA808X_CDT_STATUS_STAT_FAIL: ++ default: ++ return ETHTOOL_A_CABLE_RESULT_CODE_UNSPEC; ++ } ++} ++ ++static int qca808x_cdt_fault_length(struct phy_device *phydev, int pair, ++ int result) ++{ ++ int val; ++ u32 cdt_length_reg = 0; ++ ++ switch (pair) { ++ case ETHTOOL_A_CABLE_PAIR_A: ++ cdt_length_reg = QCA808X_MMD3_CDT_DIAG_PAIR_A; ++ break; ++ case ETHTOOL_A_CABLE_PAIR_B: ++ cdt_length_reg = QCA808X_MMD3_CDT_DIAG_PAIR_B; ++ break; ++ case ETHTOOL_A_CABLE_PAIR_C: ++ cdt_length_reg = QCA808X_MMD3_CDT_DIAG_PAIR_C; ++ break; ++ case ETHTOOL_A_CABLE_PAIR_D: ++ cdt_length_reg = QCA808X_MMD3_CDT_DIAG_PAIR_D; ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ val = phy_read_mmd(phydev, MDIO_MMD_PCS, cdt_length_reg); ++ if (val < 0) ++ return val; ++ ++ if (result == ETHTOOL_A_CABLE_RESULT_CODE_SAME_SHORT) ++ val = FIELD_GET(QCA808X_CDT_DIAG_LENGTH_SAME_SHORT, val); ++ else ++ val = FIELD_GET(QCA808X_CDT_DIAG_LENGTH_CROSS_SHORT, val); ++ ++ return at803x_cdt_fault_length(val); ++} ++ ++static int qca808x_cable_test_start(struct phy_device *phydev) ++{ ++ int ret; ++ ++ /* perform CDT with the following configs: ++ * 1. disable hibernation. ++ * 2. force PHY working in MDI mode. ++ * 3. for PHY working in 1000BaseT. ++ * 4. configure the threshold. ++ */ ++ ++ ret = at803x_debug_reg_mask(phydev, QCA808X_DBG_AN_TEST, QCA808X_HIBERNATION_EN, 0); ++ if (ret < 0) ++ return ret; ++ ++ ret = at803x_config_mdix(phydev, ETH_TP_MDI); ++ if (ret < 0) ++ return ret; ++ ++ /* Force 1000base-T needs to configure PMA/PMD and MII_BMCR */ ++ phydev->duplex = DUPLEX_FULL; ++ phydev->speed = SPEED_1000; ++ ret = genphy_c45_pma_setup_forced(phydev); ++ if (ret < 0) ++ return ret; ++ ++ ret = genphy_setup_forced(phydev); ++ if (ret < 0) ++ return ret; ++ ++ /* configure the thresholds for open, short, pair ok test */ ++ phy_write_mmd(phydev, MDIO_MMD_PCS, 0x8074, 0xc040); ++ phy_write_mmd(phydev, MDIO_MMD_PCS, 0x8076, 0xc040); ++ phy_write_mmd(phydev, MDIO_MMD_PCS, 0x8077, 0xa060); ++ phy_write_mmd(phydev, MDIO_MMD_PCS, 0x8078, 0xc050); ++ phy_write_mmd(phydev, MDIO_MMD_PCS, 0x807a, 0xc060); ++ phy_write_mmd(phydev, MDIO_MMD_PCS, 0x807e, 0xb060); ++ ++ return 0; ++} ++ ++static int qca808x_cable_test_get_pair_status(struct phy_device *phydev, u8 pair, ++ u16 status) ++{ ++ int length, result; ++ u16 pair_code; ++ ++ switch (pair) { ++ case ETHTOOL_A_CABLE_PAIR_A: ++ pair_code = FIELD_GET(QCA808X_CDT_CODE_PAIR_A, status); ++ break; ++ case ETHTOOL_A_CABLE_PAIR_B: ++ pair_code = FIELD_GET(QCA808X_CDT_CODE_PAIR_B, status); ++ break; ++ case ETHTOOL_A_CABLE_PAIR_C: ++ pair_code = FIELD_GET(QCA808X_CDT_CODE_PAIR_C, status); ++ break; ++ case ETHTOOL_A_CABLE_PAIR_D: ++ pair_code = FIELD_GET(QCA808X_CDT_CODE_PAIR_D, status); ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ result = qca808x_cable_test_result_trans(pair_code); ++ ethnl_cable_test_result(phydev, pair, result); ++ ++ if (qca808x_cdt_fault_length_valid(pair_code)) { ++ length = qca808x_cdt_fault_length(phydev, pair, result); ++ ethnl_cable_test_fault_length(phydev, pair, length); ++ } ++ ++ return 0; ++} ++ ++static int qca808x_cable_test_get_status(struct phy_device *phydev, bool *finished) ++{ ++ int ret, val; ++ ++ *finished = false; ++ ++ val = QCA808X_CDT_ENABLE_TEST | ++ QCA808X_CDT_LENGTH_UNIT; ++ ret = at803x_cdt_start(phydev, val); ++ if (ret) ++ return ret; ++ ++ ret = at803x_cdt_wait_for_completion(phydev, QCA808X_CDT_ENABLE_TEST); ++ if (ret) ++ return ret; ++ ++ val = phy_read_mmd(phydev, MDIO_MMD_PCS, QCA808X_MMD3_CDT_STATUS); ++ if (val < 0) ++ return val; ++ ++ ret = qca808x_cable_test_get_pair_status(phydev, ETHTOOL_A_CABLE_PAIR_A, val); ++ if (ret) ++ return ret; ++ ++ ret = qca808x_cable_test_get_pair_status(phydev, ETHTOOL_A_CABLE_PAIR_B, val); ++ if (ret) ++ return ret; ++ ++ ret = qca808x_cable_test_get_pair_status(phydev, ETHTOOL_A_CABLE_PAIR_C, val); ++ if (ret) ++ return ret; ++ ++ ret = qca808x_cable_test_get_pair_status(phydev, ETHTOOL_A_CABLE_PAIR_D, val); ++ if (ret) ++ return ret; ++ ++ *finished = true; ++ ++ return 0; ++} ++ ++static int qca808x_get_features(struct phy_device *phydev) ++{ ++ int ret; ++ ++ ret = genphy_c45_pma_read_abilities(phydev); ++ if (ret) ++ return ret; ++ ++ /* The autoneg ability is not existed in bit3 of MMD7.1, ++ * but it is supported by qca808x PHY, so we add it here ++ * manually. ++ */ ++ linkmode_set_bit(ETHTOOL_LINK_MODE_Autoneg_BIT, phydev->supported); ++ ++ /* As for the qca8081 1G version chip, the 2500baseT ability is also ++ * existed in the bit0 of MMD1.21, we need to remove it manually if ++ * it is the qca8081 1G chip according to the bit0 of MMD7.0x901d. ++ */ ++ ret = phy_read_mmd(phydev, MDIO_MMD_AN, QCA808X_PHY_MMD7_CHIP_TYPE); ++ if (ret < 0) ++ return ret; ++ ++ if (QCA808X_PHY_CHIP_TYPE_1G & ret) ++ linkmode_clear_bit(ETHTOOL_LINK_MODE_2500baseT_Full_BIT, phydev->supported); ++ ++ return 0; ++} ++ ++static int qca808x_config_aneg(struct phy_device *phydev) ++{ ++ int phy_ctrl = 0; ++ int ret; ++ ++ ret = at803x_prepare_config_aneg(phydev); ++ if (ret) ++ return ret; ++ ++ /* The reg MII_BMCR also needs to be configured for force mode, the ++ * genphy_config_aneg is also needed. ++ */ ++ if (phydev->autoneg == AUTONEG_DISABLE) ++ genphy_c45_pma_setup_forced(phydev); ++ ++ if (linkmode_test_bit(ETHTOOL_LINK_MODE_2500baseT_Full_BIT, phydev->advertising)) ++ phy_ctrl = MDIO_AN_10GBT_CTRL_ADV2_5G; ++ ++ ret = phy_modify_mmd_changed(phydev, MDIO_MMD_AN, MDIO_AN_10GBT_CTRL, ++ MDIO_AN_10GBT_CTRL_ADV2_5G, phy_ctrl); ++ if (ret < 0) ++ return ret; ++ ++ return __genphy_config_aneg(phydev, ret); ++} ++ ++static void qca808x_link_change_notify(struct phy_device *phydev) ++{ ++ /* Assert interface sgmii fifo on link down, deassert it on link up, ++ * the interface device address is always phy address added by 1. ++ */ ++ mdiobus_c45_modify_changed(phydev->mdio.bus, phydev->mdio.addr + 1, ++ MDIO_MMD_PMAPMD, QCA8081_PHY_SERDES_MMD1_FIFO_CTRL, ++ QCA8081_PHY_FIFO_RSTN, ++ phydev->link ? QCA8081_PHY_FIFO_RSTN : 0); ++} ++ ++static int qca808x_led_parse_netdev(struct phy_device *phydev, unsigned long rules, ++ u16 *offload_trigger) ++{ ++ /* Parsing specific to netdev trigger */ ++ if (test_bit(TRIGGER_NETDEV_TX, &rules)) ++ *offload_trigger |= QCA808X_LED_TX_BLINK; ++ if (test_bit(TRIGGER_NETDEV_RX, &rules)) ++ *offload_trigger |= QCA808X_LED_RX_BLINK; ++ if (test_bit(TRIGGER_NETDEV_LINK_10, &rules)) ++ *offload_trigger |= QCA808X_LED_SPEED10_ON; ++ if (test_bit(TRIGGER_NETDEV_LINK_100, &rules)) ++ *offload_trigger |= QCA808X_LED_SPEED100_ON; ++ if (test_bit(TRIGGER_NETDEV_LINK_1000, &rules)) ++ *offload_trigger |= QCA808X_LED_SPEED1000_ON; ++ if (test_bit(TRIGGER_NETDEV_LINK_2500, &rules)) ++ *offload_trigger |= QCA808X_LED_SPEED2500_ON; ++ if (test_bit(TRIGGER_NETDEV_HALF_DUPLEX, &rules)) ++ *offload_trigger |= QCA808X_LED_HALF_DUPLEX_ON; ++ if (test_bit(TRIGGER_NETDEV_FULL_DUPLEX, &rules)) ++ *offload_trigger |= QCA808X_LED_FULL_DUPLEX_ON; ++ ++ if (rules && !*offload_trigger) ++ return -EOPNOTSUPP; ++ ++ /* Enable BLINK_CHECK_BYPASS by default to make the LED ++ * blink even with duplex or speed mode not enabled. ++ */ ++ *offload_trigger |= QCA808X_LED_BLINK_CHECK_BYPASS; ++ ++ return 0; ++} ++ ++static int qca808x_led_hw_control_enable(struct phy_device *phydev, u8 index) ++{ ++ u16 reg; ++ ++ if (index > 2) ++ return -EINVAL; ++ ++ reg = QCA808X_MMD7_LED_FORCE_CTRL(index); ++ ++ return phy_clear_bits_mmd(phydev, MDIO_MMD_AN, reg, ++ QCA808X_LED_FORCE_EN); ++} ++ ++static int qca808x_led_hw_is_supported(struct phy_device *phydev, u8 index, ++ unsigned long rules) ++{ ++ u16 offload_trigger = 0; ++ ++ if (index > 2) ++ return -EINVAL; ++ ++ return qca808x_led_parse_netdev(phydev, rules, &offload_trigger); ++} ++ ++static int qca808x_led_hw_control_set(struct phy_device *phydev, u8 index, ++ unsigned long rules) ++{ ++ u16 reg, offload_trigger = 0; ++ int ret; ++ ++ if (index > 2) ++ return -EINVAL; ++ ++ reg = QCA808X_MMD7_LED_CTRL(index); ++ ++ ret = qca808x_led_parse_netdev(phydev, rules, &offload_trigger); ++ if (ret) ++ return ret; ++ ++ ret = qca808x_led_hw_control_enable(phydev, index); ++ if (ret) ++ return ret; ++ ++ return phy_modify_mmd(phydev, MDIO_MMD_AN, reg, ++ QCA808X_LED_PATTERN_MASK, ++ offload_trigger); ++} ++ ++static bool qca808x_led_hw_control_status(struct phy_device *phydev, u8 index) ++{ ++ u16 reg; ++ int val; ++ ++ if (index > 2) ++ return false; ++ ++ reg = QCA808X_MMD7_LED_FORCE_CTRL(index); ++ ++ val = phy_read_mmd(phydev, MDIO_MMD_AN, reg); ++ ++ return !(val & QCA808X_LED_FORCE_EN); ++} ++ ++static int qca808x_led_hw_control_get(struct phy_device *phydev, u8 index, ++ unsigned long *rules) ++{ ++ u16 reg; ++ int val; ++ ++ if (index > 2) ++ return -EINVAL; ++ ++ /* Check if we have hw control enabled */ ++ if (qca808x_led_hw_control_status(phydev, index)) ++ return -EINVAL; ++ ++ reg = QCA808X_MMD7_LED_CTRL(index); ++ ++ val = phy_read_mmd(phydev, MDIO_MMD_AN, reg); ++ if (val & QCA808X_LED_TX_BLINK) ++ set_bit(TRIGGER_NETDEV_TX, rules); ++ if (val & QCA808X_LED_RX_BLINK) ++ set_bit(TRIGGER_NETDEV_RX, rules); ++ if (val & QCA808X_LED_SPEED10_ON) ++ set_bit(TRIGGER_NETDEV_LINK_10, rules); ++ if (val & QCA808X_LED_SPEED100_ON) ++ set_bit(TRIGGER_NETDEV_LINK_100, rules); ++ if (val & QCA808X_LED_SPEED1000_ON) ++ set_bit(TRIGGER_NETDEV_LINK_1000, rules); ++ if (val & QCA808X_LED_SPEED2500_ON) ++ set_bit(TRIGGER_NETDEV_LINK_2500, rules); ++ if (val & QCA808X_LED_HALF_DUPLEX_ON) ++ set_bit(TRIGGER_NETDEV_HALF_DUPLEX, rules); ++ if (val & QCA808X_LED_FULL_DUPLEX_ON) ++ set_bit(TRIGGER_NETDEV_FULL_DUPLEX, rules); ++ ++ return 0; ++} ++ ++static int qca808x_led_hw_control_reset(struct phy_device *phydev, u8 index) ++{ ++ u16 reg; ++ ++ if (index > 2) ++ return -EINVAL; ++ ++ reg = QCA808X_MMD7_LED_CTRL(index); ++ ++ return phy_clear_bits_mmd(phydev, MDIO_MMD_AN, reg, ++ QCA808X_LED_PATTERN_MASK); ++} ++ ++static int qca808x_led_brightness_set(struct phy_device *phydev, ++ u8 index, enum led_brightness value) ++{ ++ u16 reg; ++ int ret; ++ ++ if (index > 2) ++ return -EINVAL; ++ ++ if (!value) { ++ ret = qca808x_led_hw_control_reset(phydev, index); ++ if (ret) ++ return ret; ++ } ++ ++ reg = QCA808X_MMD7_LED_FORCE_CTRL(index); ++ ++ return phy_modify_mmd(phydev, MDIO_MMD_AN, reg, ++ QCA808X_LED_FORCE_EN | QCA808X_LED_FORCE_MODE_MASK, ++ QCA808X_LED_FORCE_EN | value ? QCA808X_LED_FORCE_ON : ++ QCA808X_LED_FORCE_OFF); ++} ++ ++static int qca808x_led_blink_set(struct phy_device *phydev, u8 index, ++ unsigned long *delay_on, ++ unsigned long *delay_off) ++{ ++ int ret; ++ u16 reg; ++ ++ if (index > 2) ++ return -EINVAL; ++ ++ reg = QCA808X_MMD7_LED_FORCE_CTRL(index); ++ ++ /* Set blink to 50% off, 50% on at 4Hz by default */ ++ ret = phy_modify_mmd(phydev, MDIO_MMD_AN, QCA808X_MMD7_LED_GLOBAL, ++ QCA808X_LED_BLINK_FREQ_MASK | QCA808X_LED_BLINK_DUTY_MASK, ++ QCA808X_LED_BLINK_FREQ_4HZ | QCA808X_LED_BLINK_DUTY_50_50); ++ if (ret) ++ return ret; ++ ++ /* We use BLINK_1 for normal blinking */ ++ ret = phy_modify_mmd(phydev, MDIO_MMD_AN, reg, ++ QCA808X_LED_FORCE_EN | QCA808X_LED_FORCE_MODE_MASK, ++ QCA808X_LED_FORCE_EN | QCA808X_LED_FORCE_BLINK_1); ++ if (ret) ++ return ret; ++ ++ /* We set blink to 4Hz, aka 250ms */ ++ *delay_on = 250 / 2; ++ *delay_off = 250 / 2; ++ ++ return 0; ++} ++ ++static int qca808x_led_polarity_set(struct phy_device *phydev, int index, ++ unsigned long modes) ++{ ++ struct qca808x_priv *priv = phydev->priv; ++ bool active_low = false; ++ u32 mode; ++ ++ for_each_set_bit(mode, &modes, __PHY_LED_MODES_NUM) { ++ switch (mode) { ++ case PHY_LED_ACTIVE_LOW: ++ active_low = true; ++ break; ++ default: ++ return -EINVAL; ++ } ++ } ++ ++ /* PHY polarity is global and can't be set per LED. ++ * To detect this, check if last requested polarity mode ++ * match the new one. ++ */ ++ if (priv->led_polarity_mode >= 0 && ++ priv->led_polarity_mode != active_low) { ++ phydev_err(phydev, "PHY polarity is global. Mismatched polarity on different LED\n"); ++ return -EINVAL; ++ } ++ ++ /* Save the last PHY polarity mode */ ++ priv->led_polarity_mode = active_low; ++ ++ return phy_modify_mmd(phydev, MDIO_MMD_AN, ++ QCA808X_MMD7_LED_POLARITY_CTRL, ++ QCA808X_LED_ACTIVE_HIGH, ++ active_low ? 0 : QCA808X_LED_ACTIVE_HIGH); ++} ++ ++static struct phy_driver qca808x_driver[] = { ++{ ++ /* Qualcomm QCA8081 */ ++ PHY_ID_MATCH_EXACT(QCA8081_PHY_ID), ++ .name = "Qualcomm QCA8081", ++ .flags = PHY_POLL_CABLE_TEST, ++ .probe = qca808x_probe, ++ .config_intr = at803x_config_intr, ++ .handle_interrupt = at803x_handle_interrupt, ++ .get_tunable = at803x_get_tunable, ++ .set_tunable = at803x_set_tunable, ++ .set_wol = at803x_set_wol, ++ .get_wol = at803x_get_wol, ++ .get_features = qca808x_get_features, ++ .config_aneg = qca808x_config_aneg, ++ .suspend = genphy_suspend, ++ .resume = genphy_resume, ++ .read_status = qca808x_read_status, ++ .config_init = qca808x_config_init, ++ .soft_reset = qca808x_soft_reset, ++ .cable_test_start = qca808x_cable_test_start, ++ .cable_test_get_status = qca808x_cable_test_get_status, ++ .link_change_notify = qca808x_link_change_notify, ++ .led_brightness_set = qca808x_led_brightness_set, ++ .led_blink_set = qca808x_led_blink_set, ++ .led_hw_is_supported = qca808x_led_hw_is_supported, ++ .led_hw_control_set = qca808x_led_hw_control_set, ++ .led_hw_control_get = qca808x_led_hw_control_get, ++ .led_polarity_set = qca808x_led_polarity_set, ++}, }; ++ ++module_phy_driver(qca808x_driver); ++ ++static struct mdio_device_id __maybe_unused qca808x_tbl[] = { ++ { PHY_ID_MATCH_EXACT(QCA8081_PHY_ID) }, ++ { } ++}; ++ ++MODULE_DEVICE_TABLE(mdio, qca808x_tbl); diff --git a/target/linux/generic/backport-6.1/723-v6.3-net-pcs-add-driver-for-MediaTek-SGMII-PCS.patch b/target/linux/generic/backport-6.1/714-net-pcs-add-driver-for-MediaTek-SGMII-PCS.patch similarity index 100% rename from target/linux/generic/backport-6.1/723-v6.3-net-pcs-add-driver-for-MediaTek-SGMII-PCS.patch rename to target/linux/generic/backport-6.1/714-net-pcs-add-driver-for-MediaTek-SGMII-PCS.patch diff --git a/target/linux/generic/backport-6.1/714-v6.8-01-net-phy-make-addr-type-u8-in-phy_package_shared-stru.patch b/target/linux/generic/backport-6.1/714-v6.8-01-net-phy-make-addr-type-u8-in-phy_package_shared-stru.patch new file mode 100644 index 000000000..6decc3430 --- /dev/null +++ b/target/linux/generic/backport-6.1/714-v6.8-01-net-phy-make-addr-type-u8-in-phy_package_shared-stru.patch @@ -0,0 +1,28 @@ +From ebb30ccbbdbd6fae5177b676da4f4ac92bb4f635 Mon Sep 17 00:00:00 2001 +From: Christian Marangi +Date: Fri, 15 Dec 2023 14:15:31 +0100 +Subject: [PATCH 1/4] net: phy: make addr type u8 in phy_package_shared struct + +Switch addr type in phy_package_shared struct to u8. + +The value is already checked to be non negative and to be less than +PHY_MAX_ADDR, hence u8 is better suited than using int. + +Signed-off-by: Christian Marangi +Reviewed-by: Russell King (Oracle) +Signed-off-by: David S. Miller +--- + include/linux/phy.h | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/include/linux/phy.h ++++ b/include/linux/phy.h +@@ -330,7 +330,7 @@ struct mdio_bus_stats { + * phy_package_leave(). + */ + struct phy_package_shared { +- int addr; ++ u8 addr; + refcount_t refcnt; + unsigned long flags; + size_t priv_size; diff --git a/target/linux/generic/backport-6.1/714-v6.8-02-net-phy-extend-PHY-package-API-to-support-multiple-g.patch b/target/linux/generic/backport-6.1/714-v6.8-02-net-phy-extend-PHY-package-API-to-support-multiple-g.patch new file mode 100644 index 000000000..0cf01df3d --- /dev/null +++ b/target/linux/generic/backport-6.1/714-v6.8-02-net-phy-extend-PHY-package-API-to-support-multiple-g.patch @@ -0,0 +1,341 @@ +From 9eea577eb1155fe4a183bc5e7bf269b0b2e7a6ba Mon Sep 17 00:00:00 2001 +From: Christian Marangi +Date: Fri, 15 Dec 2023 14:15:32 +0100 +Subject: [PATCH 2/4] net: phy: extend PHY package API to support multiple + global address + +Current API for PHY package are limited to single address to configure +global settings for the PHY package. + +It was found that some PHY package (for example the qca807x, a PHY +package that is shipped with a bundle of 5 PHY) requires multiple PHY +address to configure global settings. An example scenario is a PHY that +have a dedicated PHY for PSGMII/serdes calibrarion and have a specific +PHY in the package where the global PHY mode is set and affects every +other PHY in the package. + +Change the API in the following way: +- Change phy_package_join() to take the base addr of the PHY package + instead of the global PHY addr. +- Make __/phy_package_write/read() require an additional arg that + select what global PHY address to use by passing the offset from the + base addr passed on phy_package_join(). + +Each user of this API is updated to follow this new implementation +following a pattern where an enum is defined to declare the offset of the +addr. + +We also drop the check if shared is defined as any user of the +phy_package_read/write is expected to use phy_package_join first. Misuse +of this will correctly trigger a kernel panic for NULL pointer +exception. + +Signed-off-by: Christian Marangi +Signed-off-by: David S. Miller +--- + drivers/net/phy/bcm54140.c | 16 ++++++-- + drivers/net/phy/mscc/mscc.h | 5 +++ + drivers/net/phy/mscc/mscc_main.c | 4 +- + drivers/net/phy/phy_device.c | 35 +++++++++-------- + include/linux/phy.h | 64 +++++++++++++++++++++----------- + 5 files changed, 80 insertions(+), 44 deletions(-) + +--- a/drivers/net/phy/bcm54140.c ++++ b/drivers/net/phy/bcm54140.c +@@ -128,6 +128,10 @@ + #define BCM54140_DEFAULT_DOWNSHIFT 5 + #define BCM54140_MAX_DOWNSHIFT 9 + ++enum bcm54140_global_phy { ++ BCM54140_BASE_ADDR = 0, ++}; ++ + struct bcm54140_priv { + int port; + int base_addr; +@@ -429,11 +433,13 @@ static int bcm54140_base_read_rdb(struct + int ret; + + phy_lock_mdio_bus(phydev); +- ret = __phy_package_write(phydev, MII_BCM54XX_RDB_ADDR, rdb); ++ ret = __phy_package_write(phydev, BCM54140_BASE_ADDR, ++ MII_BCM54XX_RDB_ADDR, rdb); + if (ret < 0) + goto out; + +- ret = __phy_package_read(phydev, MII_BCM54XX_RDB_DATA); ++ ret = __phy_package_read(phydev, BCM54140_BASE_ADDR, ++ MII_BCM54XX_RDB_DATA); + + out: + phy_unlock_mdio_bus(phydev); +@@ -446,11 +452,13 @@ static int bcm54140_base_write_rdb(struc + int ret; + + phy_lock_mdio_bus(phydev); +- ret = __phy_package_write(phydev, MII_BCM54XX_RDB_ADDR, rdb); ++ ret = __phy_package_write(phydev, BCM54140_BASE_ADDR, ++ MII_BCM54XX_RDB_ADDR, rdb); + if (ret < 0) + goto out; + +- ret = __phy_package_write(phydev, MII_BCM54XX_RDB_DATA, val); ++ ret = __phy_package_write(phydev, BCM54140_BASE_ADDR, ++ MII_BCM54XX_RDB_DATA, val); + + out: + phy_unlock_mdio_bus(phydev); +--- a/drivers/net/phy/mscc/mscc.h ++++ b/drivers/net/phy/mscc/mscc.h +@@ -414,6 +414,11 @@ struct vsc8531_private { + * gpio_lock: used for PHC operations. Common for all PHYs as the load/save GPIO + * is shared. + */ ++ ++enum vsc85xx_global_phy { ++ VSC88XX_BASE_ADDR = 0, ++}; ++ + struct vsc85xx_shared_private { + struct mutex gpio_lock; + }; +--- a/drivers/net/phy/mscc/mscc_main.c ++++ b/drivers/net/phy/mscc/mscc_main.c +@@ -700,7 +700,7 @@ int phy_base_write(struct phy_device *ph + dump_stack(); + } + +- return __phy_package_write(phydev, regnum, val); ++ return __phy_package_write(phydev, VSC88XX_BASE_ADDR, regnum, val); + } + + /* phydev->bus->mdio_lock should be locked when using this function */ +@@ -711,7 +711,7 @@ int phy_base_read(struct phy_device *phy + dump_stack(); + } + +- return __phy_package_read(phydev, regnum); ++ return __phy_package_read(phydev, VSC88XX_BASE_ADDR, regnum); + } + + u32 vsc85xx_csr_read(struct phy_device *phydev, +--- a/drivers/net/phy/phy_device.c ++++ b/drivers/net/phy/phy_device.c +@@ -1602,20 +1602,22 @@ EXPORT_SYMBOL_GPL(phy_driver_is_genphy_1 + /** + * phy_package_join - join a common PHY group + * @phydev: target phy_device struct +- * @addr: cookie and PHY address for global register access ++ * @base_addr: cookie and base PHY address of PHY package for offset ++ * calculation of global register access + * @priv_size: if non-zero allocate this amount of bytes for private data + * + * This joins a PHY group and provides a shared storage for all phydevs in + * this group. This is intended to be used for packages which contain + * more than one PHY, for example a quad PHY transceiver. + * +- * The addr parameter serves as a cookie which has to have the same value +- * for all members of one group and as a PHY address to access generic +- * registers of a PHY package. Usually, one of the PHY addresses of the +- * different PHYs in the package provides access to these global registers. ++ * The base_addr parameter serves as cookie which has to have the same values ++ * for all members of one group and as the base PHY address of the PHY package ++ * for offset calculation to access generic registers of a PHY package. ++ * Usually, one of the PHY addresses of the different PHYs in the package ++ * provides access to these global registers. + * The address which is given here, will be used in the phy_package_read() +- * and phy_package_write() convenience functions. If your PHY doesn't have +- * global registers you can just pick any of the PHY addresses. ++ * and phy_package_write() convenience functions as base and added to the ++ * passed offset in those functions. + * + * This will set the shared pointer of the phydev to the shared storage. + * If this is the first call for a this cookie the shared storage will be +@@ -1625,17 +1627,17 @@ EXPORT_SYMBOL_GPL(phy_driver_is_genphy_1 + * Returns < 1 on error, 0 on success. Esp. calling phy_package_join() + * with the same cookie but a different priv_size is an error. + */ +-int phy_package_join(struct phy_device *phydev, int addr, size_t priv_size) ++int phy_package_join(struct phy_device *phydev, int base_addr, size_t priv_size) + { + struct mii_bus *bus = phydev->mdio.bus; + struct phy_package_shared *shared; + int ret; + +- if (addr < 0 || addr >= PHY_MAX_ADDR) ++ if (base_addr < 0 || base_addr >= PHY_MAX_ADDR) + return -EINVAL; + + mutex_lock(&bus->shared_lock); +- shared = bus->shared[addr]; ++ shared = bus->shared[base_addr]; + if (!shared) { + ret = -ENOMEM; + shared = kzalloc(sizeof(*shared), GFP_KERNEL); +@@ -1647,9 +1649,9 @@ int phy_package_join(struct phy_device * + goto err_free; + shared->priv_size = priv_size; + } +- shared->addr = addr; ++ shared->base_addr = base_addr; + refcount_set(&shared->refcnt, 1); +- bus->shared[addr] = shared; ++ bus->shared[base_addr] = shared; + } else { + ret = -EINVAL; + if (priv_size && priv_size != shared->priv_size) +@@ -1687,7 +1689,7 @@ void phy_package_leave(struct phy_device + return; + + if (refcount_dec_and_mutex_lock(&shared->refcnt, &bus->shared_lock)) { +- bus->shared[shared->addr] = NULL; ++ bus->shared[shared->base_addr] = NULL; + mutex_unlock(&bus->shared_lock); + kfree(shared->priv); + kfree(shared); +@@ -1706,7 +1708,8 @@ static void devm_phy_package_leave(struc + * devm_phy_package_join - resource managed phy_package_join() + * @dev: device that is registering this PHY package + * @phydev: target phy_device struct +- * @addr: cookie and PHY address for global register access ++ * @base_addr: cookie and base PHY address of PHY package for offset ++ * calculation of global register access + * @priv_size: if non-zero allocate this amount of bytes for private data + * + * Managed phy_package_join(). Shared storage fetched by this function, +@@ -1714,7 +1717,7 @@ static void devm_phy_package_leave(struc + * phy_package_join() for more information. + */ + int devm_phy_package_join(struct device *dev, struct phy_device *phydev, +- int addr, size_t priv_size) ++ int base_addr, size_t priv_size) + { + struct phy_device **ptr; + int ret; +@@ -1724,7 +1727,7 @@ int devm_phy_package_join(struct device + if (!ptr) + return -ENOMEM; + +- ret = phy_package_join(phydev, addr, priv_size); ++ ret = phy_package_join(phydev, base_addr, priv_size); + + if (!ret) { + *ptr = phydev; +--- a/include/linux/phy.h ++++ b/include/linux/phy.h +@@ -319,7 +319,8 @@ struct mdio_bus_stats { + + /** + * struct phy_package_shared - Shared information in PHY packages +- * @addr: Common PHY address used to combine PHYs in one package ++ * @base_addr: Base PHY address of PHY package used to combine PHYs ++ * in one package and for offset calculation of phy_package_read/write + * @refcnt: Number of PHYs connected to this shared data + * @flags: Initialization of PHY package + * @priv_size: Size of the shared private data @priv +@@ -330,7 +331,7 @@ struct mdio_bus_stats { + * phy_package_leave(). + */ + struct phy_package_shared { +- u8 addr; ++ u8 base_addr; + refcount_t refcnt; + unsigned long flags; + size_t priv_size; +@@ -1763,10 +1764,10 @@ int phy_ethtool_get_link_ksettings(struc + int phy_ethtool_set_link_ksettings(struct net_device *ndev, + const struct ethtool_link_ksettings *cmd); + int phy_ethtool_nway_reset(struct net_device *ndev); +-int phy_package_join(struct phy_device *phydev, int addr, size_t priv_size); ++int phy_package_join(struct phy_device *phydev, int base_addr, size_t priv_size); + void phy_package_leave(struct phy_device *phydev); + int devm_phy_package_join(struct device *dev, struct phy_device *phydev, +- int addr, size_t priv_size); ++ int base_addr, size_t priv_size); + + #if IS_ENABLED(CONFIG_PHYLIB) + int __init mdio_bus_init(void); +@@ -1778,46 +1779,65 @@ int phy_ethtool_get_sset_count(struct ph + int phy_ethtool_get_stats(struct phy_device *phydev, + struct ethtool_stats *stats, u64 *data); + +-static inline int phy_package_read(struct phy_device *phydev, u32 regnum) ++static inline int phy_package_address(struct phy_device *phydev, ++ unsigned int addr_offset) + { + struct phy_package_shared *shared = phydev->shared; ++ u8 base_addr = shared->base_addr; + +- if (!shared) ++ if (addr_offset >= PHY_MAX_ADDR - base_addr) + return -EIO; + +- return mdiobus_read(phydev->mdio.bus, shared->addr, regnum); ++ /* we know that addr will be in the range 0..31 and thus the ++ * implicit cast to a signed int is not a problem. ++ */ ++ return base_addr + addr_offset; + } + +-static inline int __phy_package_read(struct phy_device *phydev, u32 regnum) ++static inline int phy_package_read(struct phy_device *phydev, ++ unsigned int addr_offset, u32 regnum) + { +- struct phy_package_shared *shared = phydev->shared; ++ int addr = phy_package_address(phydev, addr_offset); + +- if (!shared) +- return -EIO; ++ if (addr < 0) ++ return addr; ++ ++ return mdiobus_read(phydev->mdio.bus, addr, regnum); ++} ++ ++static inline int __phy_package_read(struct phy_device *phydev, ++ unsigned int addr_offset, u32 regnum) ++{ ++ int addr = phy_package_address(phydev, addr_offset); ++ ++ if (addr < 0) ++ return addr; + +- return __mdiobus_read(phydev->mdio.bus, shared->addr, regnum); ++ return __mdiobus_read(phydev->mdio.bus, addr, regnum); + } + + static inline int phy_package_write(struct phy_device *phydev, +- u32 regnum, u16 val) ++ unsigned int addr_offset, u32 regnum, ++ u16 val) + { +- struct phy_package_shared *shared = phydev->shared; ++ int addr = phy_package_address(phydev, addr_offset); + +- if (!shared) +- return -EIO; ++ if (addr < 0) ++ return addr; + +- return mdiobus_write(phydev->mdio.bus, shared->addr, regnum, val); ++ return mdiobus_write(phydev->mdio.bus, addr, regnum, val); + } + + static inline int __phy_package_write(struct phy_device *phydev, +- u32 regnum, u16 val) ++ unsigned int addr_offset, u32 regnum, ++ u16 val) + { +- struct phy_package_shared *shared = phydev->shared; ++ int addr = phy_package_address(phydev, addr_offset); + +- if (!shared) +- return -EIO; ++ if (addr < 0) ++ return addr; + +- return __mdiobus_write(phydev->mdio.bus, shared->addr, regnum, val); ++ return __mdiobus_write(phydev->mdio.bus, addr, regnum, val); + } + + static inline bool __phy_package_set_once(struct phy_device *phydev, diff --git a/target/linux/generic/backport-6.1/714-v6.8-03-net-phy-restructure-__phy_write-read_mmd-to-helper-a.patch b/target/linux/generic/backport-6.1/714-v6.8-03-net-phy-restructure-__phy_write-read_mmd-to-helper-a.patch new file mode 100644 index 000000000..4a17d4645 --- /dev/null +++ b/target/linux/generic/backport-6.1/714-v6.8-03-net-phy-restructure-__phy_write-read_mmd-to-helper-a.patch @@ -0,0 +1,116 @@ +From 028672bd1d73cf65249a420c1de75e8d2acd2f6a Mon Sep 17 00:00:00 2001 +From: Christian Marangi +Date: Fri, 15 Dec 2023 14:15:33 +0100 +Subject: [PATCH 3/4] net: phy: restructure __phy_write/read_mmd to helper and + phydev user + +Restructure phy_write_mmd and phy_read_mmd to implement generic helper +for direct mdiobus access for mmd and use these helper for phydev user. + +This is needed in preparation of PHY package API that requires generic +access to the mdiobus and are deatched from phydev struct but instead +access them based on PHY package base_addr and offsets. + +Signed-off-by: Christian Marangi +Reviewed-by: Russell King (Oracle) +Signed-off-by: David S. Miller +--- + drivers/net/phy/phy-core.c | 64 ++++++++++++++++++-------------------- + 1 file changed, 30 insertions(+), 34 deletions(-) + +--- a/drivers/net/phy/phy-core.c ++++ b/drivers/net/phy/phy-core.c +@@ -528,6 +528,28 @@ static void mmd_phy_indirect(struct mii_ + devad | MII_MMD_CTRL_NOINCR); + } + ++static int mmd_phy_read(struct mii_bus *bus, int phy_addr, bool is_c45, ++ int devad, u32 regnum) ++{ ++ if (is_c45) ++ return __mdiobus_c45_read(bus, phy_addr, devad, regnum); ++ ++ mmd_phy_indirect(bus, phy_addr, devad, regnum); ++ /* Read the content of the MMD's selected register */ ++ return __mdiobus_read(bus, phy_addr, MII_MMD_DATA); ++} ++ ++static int mmd_phy_write(struct mii_bus *bus, int phy_addr, bool is_c45, ++ int devad, u32 regnum, u16 val) ++{ ++ if (is_c45) ++ return __mdiobus_c45_write(bus, phy_addr, devad, regnum, val); ++ ++ mmd_phy_indirect(bus, phy_addr, devad, regnum); ++ /* Write the data into MMD's selected register */ ++ return __mdiobus_write(bus, phy_addr, MII_MMD_DATA, val); ++} ++ + /** + * __phy_read_mmd - Convenience function for reading a register + * from an MMD on a given PHY. +@@ -539,26 +561,14 @@ static void mmd_phy_indirect(struct mii_ + */ + int __phy_read_mmd(struct phy_device *phydev, int devad, u32 regnum) + { +- int val; +- + if (regnum > (u16)~0 || devad > 32) + return -EINVAL; + +- if (phydev->drv && phydev->drv->read_mmd) { +- val = phydev->drv->read_mmd(phydev, devad, regnum); +- } else if (phydev->is_c45) { +- val = __mdiobus_c45_read(phydev->mdio.bus, phydev->mdio.addr, +- devad, regnum); +- } else { +- struct mii_bus *bus = phydev->mdio.bus; +- int phy_addr = phydev->mdio.addr; +- +- mmd_phy_indirect(bus, phy_addr, devad, regnum); +- +- /* Read the content of the MMD's selected register */ +- val = __mdiobus_read(bus, phy_addr, MII_MMD_DATA); +- } +- return val; ++ if (phydev->drv && phydev->drv->read_mmd) ++ return phydev->drv->read_mmd(phydev, devad, regnum); ++ ++ return mmd_phy_read(phydev->mdio.bus, phydev->mdio.addr, ++ phydev->is_c45, devad, regnum); + } + EXPORT_SYMBOL(__phy_read_mmd); + +@@ -595,28 +605,14 @@ EXPORT_SYMBOL(phy_read_mmd); + */ + int __phy_write_mmd(struct phy_device *phydev, int devad, u32 regnum, u16 val) + { +- int ret; +- + if (regnum > (u16)~0 || devad > 32) + return -EINVAL; + +- if (phydev->drv && phydev->drv->write_mmd) { +- ret = phydev->drv->write_mmd(phydev, devad, regnum, val); +- } else if (phydev->is_c45) { +- ret = __mdiobus_c45_write(phydev->mdio.bus, phydev->mdio.addr, +- devad, regnum, val); +- } else { +- struct mii_bus *bus = phydev->mdio.bus; +- int phy_addr = phydev->mdio.addr; ++ if (phydev->drv && phydev->drv->write_mmd) ++ return phydev->drv->write_mmd(phydev, devad, regnum, val); + +- mmd_phy_indirect(bus, phy_addr, devad, regnum); +- +- /* Write the data into MMD's selected register */ +- __mdiobus_write(bus, phy_addr, MII_MMD_DATA, val); +- +- ret = 0; +- } +- return ret; ++ return mmd_phy_write(phydev->mdio.bus, phydev->mdio.addr, ++ phydev->is_c45, devad, regnum, val); + } + EXPORT_SYMBOL(__phy_write_mmd); + diff --git a/target/linux/generic/backport-6.1/714-v6.8-04-net-phy-add-support-for-PHY-package-MMD-read-write.patch b/target/linux/generic/backport-6.1/714-v6.8-04-net-phy-add-support-for-PHY-package-MMD-read-write.patch new file mode 100644 index 000000000..a628a3792 --- /dev/null +++ b/target/linux/generic/backport-6.1/714-v6.8-04-net-phy-add-support-for-PHY-package-MMD-read-write.patch @@ -0,0 +1,196 @@ +From d63710fc0f1a501fd75a7025e3070a96ffa1645f Mon Sep 17 00:00:00 2001 +From: Christian Marangi +Date: Fri, 15 Dec 2023 14:15:34 +0100 +Subject: [PATCH 4/4] net: phy: add support for PHY package MMD read/write + +Some PHY in PHY package may require to read/write MMD regs to correctly +configure the PHY package. + +Add support for these additional required function in both lock and no +lock variant. + +It's assumed that the entire PHY package is either C22 or C45. We use +C22 or C45 way of writing/reading to mmd regs based on the passed phydev +whether it's C22 or C45. + +Signed-off-by: Christian Marangi +Signed-off-by: David S. Miller +--- + drivers/net/phy/phy-core.c | 140 +++++++++++++++++++++++++++++++++++++ + include/linux/phy.h | 16 +++++ + 2 files changed, 156 insertions(+) + +--- a/drivers/net/phy/phy-core.c ++++ b/drivers/net/phy/phy-core.c +@@ -639,6 +639,146 @@ int phy_write_mmd(struct phy_device *phy + EXPORT_SYMBOL(phy_write_mmd); + + /** ++ * __phy_package_read_mmd - read MMD reg relative to PHY package base addr ++ * @phydev: The phy_device struct ++ * @addr_offset: The offset to be added to PHY package base_addr ++ * @devad: The MMD to read from ++ * @regnum: The register on the MMD to read ++ * ++ * Convenience helper for reading a register of an MMD on a given PHY ++ * using the PHY package base address. The base address is added to ++ * the addr_offset value. ++ * ++ * Same calling rules as for __phy_read(); ++ * ++ * NOTE: It's assumed that the entire PHY package is either C22 or C45. ++ */ ++int __phy_package_read_mmd(struct phy_device *phydev, ++ unsigned int addr_offset, int devad, ++ u32 regnum) ++{ ++ int addr = phy_package_address(phydev, addr_offset); ++ ++ if (addr < 0) ++ return addr; ++ ++ if (regnum > (u16)~0 || devad > 32) ++ return -EINVAL; ++ ++ return mmd_phy_read(phydev->mdio.bus, addr, phydev->is_c45, devad, ++ regnum); ++} ++EXPORT_SYMBOL(__phy_package_read_mmd); ++ ++/** ++ * phy_package_read_mmd - read MMD reg relative to PHY package base addr ++ * @phydev: The phy_device struct ++ * @addr_offset: The offset to be added to PHY package base_addr ++ * @devad: The MMD to read from ++ * @regnum: The register on the MMD to read ++ * ++ * Convenience helper for reading a register of an MMD on a given PHY ++ * using the PHY package base address. The base address is added to ++ * the addr_offset value. ++ * ++ * Same calling rules as for phy_read(); ++ * ++ * NOTE: It's assumed that the entire PHY package is either C22 or C45. ++ */ ++int phy_package_read_mmd(struct phy_device *phydev, ++ unsigned int addr_offset, int devad, ++ u32 regnum) ++{ ++ int addr = phy_package_address(phydev, addr_offset); ++ int val; ++ ++ if (addr < 0) ++ return addr; ++ ++ if (regnum > (u16)~0 || devad > 32) ++ return -EINVAL; ++ ++ phy_lock_mdio_bus(phydev); ++ val = mmd_phy_read(phydev->mdio.bus, addr, phydev->is_c45, devad, ++ regnum); ++ phy_unlock_mdio_bus(phydev); ++ ++ return val; ++} ++EXPORT_SYMBOL(phy_package_read_mmd); ++ ++/** ++ * __phy_package_write_mmd - write MMD reg relative to PHY package base addr ++ * @phydev: The phy_device struct ++ * @addr_offset: The offset to be added to PHY package base_addr ++ * @devad: The MMD to write to ++ * @regnum: The register on the MMD to write ++ * @val: value to write to @regnum ++ * ++ * Convenience helper for writing a register of an MMD on a given PHY ++ * using the PHY package base address. The base address is added to ++ * the addr_offset value. ++ * ++ * Same calling rules as for __phy_write(); ++ * ++ * NOTE: It's assumed that the entire PHY package is either C22 or C45. ++ */ ++int __phy_package_write_mmd(struct phy_device *phydev, ++ unsigned int addr_offset, int devad, ++ u32 regnum, u16 val) ++{ ++ int addr = phy_package_address(phydev, addr_offset); ++ ++ if (addr < 0) ++ return addr; ++ ++ if (regnum > (u16)~0 || devad > 32) ++ return -EINVAL; ++ ++ return mmd_phy_write(phydev->mdio.bus, addr, phydev->is_c45, devad, ++ regnum, val); ++} ++EXPORT_SYMBOL(__phy_package_write_mmd); ++ ++/** ++ * phy_package_write_mmd - write MMD reg relative to PHY package base addr ++ * @phydev: The phy_device struct ++ * @addr_offset: The offset to be added to PHY package base_addr ++ * @devad: The MMD to write to ++ * @regnum: The register on the MMD to write ++ * @val: value to write to @regnum ++ * ++ * Convenience helper for writing a register of an MMD on a given PHY ++ * using the PHY package base address. The base address is added to ++ * the addr_offset value. ++ * ++ * Same calling rules as for phy_write(); ++ * ++ * NOTE: It's assumed that the entire PHY package is either C22 or C45. ++ */ ++int phy_package_write_mmd(struct phy_device *phydev, ++ unsigned int addr_offset, int devad, ++ u32 regnum, u16 val) ++{ ++ int addr = phy_package_address(phydev, addr_offset); ++ int ret; ++ ++ if (addr < 0) ++ return addr; ++ ++ if (regnum > (u16)~0 || devad > 32) ++ return -EINVAL; ++ ++ phy_lock_mdio_bus(phydev); ++ ret = mmd_phy_write(phydev->mdio.bus, addr, phydev->is_c45, devad, ++ regnum, val); ++ phy_unlock_mdio_bus(phydev); ++ ++ return ret; ++} ++EXPORT_SYMBOL(phy_package_write_mmd); ++ ++/** + * phy_modify_changed - Function for modifying a PHY register + * @phydev: the phy_device struct + * @regnum: register number to modify +--- a/include/linux/phy.h ++++ b/include/linux/phy.h +@@ -1840,6 +1840,22 @@ static inline int __phy_package_write(st + return __mdiobus_write(phydev->mdio.bus, addr, regnum, val); + } + ++int __phy_package_read_mmd(struct phy_device *phydev, ++ unsigned int addr_offset, int devad, ++ u32 regnum); ++ ++int phy_package_read_mmd(struct phy_device *phydev, ++ unsigned int addr_offset, int devad, ++ u32 regnum); ++ ++int __phy_package_write_mmd(struct phy_device *phydev, ++ unsigned int addr_offset, int devad, ++ u32 regnum, u16 val); ++ ++int phy_package_write_mmd(struct phy_device *phydev, ++ unsigned int addr_offset, int devad, ++ u32 regnum, u16 val); ++ + static inline bool __phy_package_set_once(struct phy_device *phydev, + unsigned int b) + { diff --git a/target/linux/generic/backport-6.1/715-01-v6.2-net-fman-memac-Add-serdes-support.patch b/target/linux/generic/backport-6.1/715-01-v6.2-net-fman-memac-Add-serdes-support.patch new file mode 100644 index 000000000..2886123f2 --- /dev/null +++ b/target/linux/generic/backport-6.1/715-01-v6.2-net-fman-memac-Add-serdes-support.patch @@ -0,0 +1,103 @@ +From affa013f494486079c3c5ad2d00cebc41a3d7445 Mon Sep 17 00:00:00 2001 +From: Sean Anderson +Date: Mon, 17 Oct 2022 16:22:36 -0400 +Subject: [PATCH 01/21] net: fman: memac: Add serdes support + +This adds support for using a serdes which has to be configured. This is +primarly in preparation for phylink conversion, which will then change the +serdes mode dynamically. + +Signed-off-by: Sean Anderson +Signed-off-by: David S. Miller +--- + .../net/ethernet/freescale/fman/fman_memac.c | 49 ++++++++++++++++++- + 1 file changed, 47 insertions(+), 2 deletions(-) + +--- a/drivers/net/ethernet/freescale/fman/fman_memac.c ++++ b/drivers/net/ethernet/freescale/fman/fman_memac.c +@@ -13,6 +13,7 @@ + #include + #include + #include ++#include + #include + + /* PCS registers */ +@@ -324,6 +325,7 @@ struct fman_mac { + void *fm; + struct fman_rev_info fm_rev_info; + bool basex_if; ++ struct phy *serdes; + struct phy_device *pcsphy; + bool allmulti_enabled; + }; +@@ -1203,17 +1205,56 @@ int memac_initialization(struct mac_devi + } + } + ++ memac->serdes = devm_of_phy_get(mac_dev->dev, mac_node, "serdes"); ++ err = PTR_ERR(memac->serdes); ++ if (err == -ENODEV || err == -ENOSYS) { ++ dev_dbg(mac_dev->dev, "could not get (optional) serdes\n"); ++ memac->serdes = NULL; ++ } else if (IS_ERR(memac->serdes)) { ++ dev_err_probe(mac_dev->dev, err, "could not get serdes\n"); ++ goto _return_fm_mac_free; ++ } else { ++ err = phy_init(memac->serdes); ++ if (err) { ++ dev_err_probe(mac_dev->dev, err, ++ "could not initialize serdes\n"); ++ goto _return_fm_mac_free; ++ } ++ ++ err = phy_power_on(memac->serdes); ++ if (err) { ++ dev_err_probe(mac_dev->dev, err, ++ "could not power on serdes\n"); ++ goto _return_phy_exit; ++ } ++ ++ if (memac->phy_if == PHY_INTERFACE_MODE_SGMII || ++ memac->phy_if == PHY_INTERFACE_MODE_1000BASEX || ++ memac->phy_if == PHY_INTERFACE_MODE_2500BASEX || ++ memac->phy_if == PHY_INTERFACE_MODE_QSGMII || ++ memac->phy_if == PHY_INTERFACE_MODE_XGMII) { ++ err = phy_set_mode_ext(memac->serdes, PHY_MODE_ETHERNET, ++ memac->phy_if); ++ if (err) { ++ dev_err_probe(mac_dev->dev, err, ++ "could not set serdes mode to %s\n", ++ phy_modes(memac->phy_if)); ++ goto _return_phy_power_off; ++ } ++ } ++ } ++ + if (!mac_dev->phy_node && of_phy_is_fixed_link(mac_node)) { + struct phy_device *phy; + + err = of_phy_register_fixed_link(mac_node); + if (err) +- goto _return_fm_mac_free; ++ goto _return_phy_power_off; + + fixed_link = kzalloc(sizeof(*fixed_link), GFP_KERNEL); + if (!fixed_link) { + err = -ENOMEM; +- goto _return_fm_mac_free; ++ goto _return_phy_power_off; + } + + mac_dev->phy_node = of_node_get(mac_node); +@@ -1242,6 +1283,10 @@ int memac_initialization(struct mac_devi + + goto _return; + ++_return_phy_power_off: ++ phy_power_off(memac->serdes); ++_return_phy_exit: ++ phy_exit(memac->serdes); + _return_fixed_link_free: + kfree(fixed_link); + _return_fm_mac_free: diff --git a/target/linux/generic/backport-6.1/715-02-v6.2-net-fman-memac-Use-lynx-pcs-driver.patch b/target/linux/generic/backport-6.1/715-02-v6.2-net-fman-memac-Use-lynx-pcs-driver.patch new file mode 100644 index 000000000..873debc08 --- /dev/null +++ b/target/linux/generic/backport-6.1/715-02-v6.2-net-fman-memac-Use-lynx-pcs-driver.patch @@ -0,0 +1,384 @@ +From fe60e7154d3a35af975c5e6570d6ec31aab9a731 Mon Sep 17 00:00:00 2001 +From: Sean Anderson +Date: Mon, 17 Oct 2022 16:22:37 -0400 +Subject: [PATCH 02/21] net: fman: memac: Use lynx pcs driver + +Although not stated in the datasheet, as far as I can tell PCS for mEMACs +is a "Lynx." By reusing the existing driver, we can remove the PCS +management code from the memac driver. This requires calling some PCS +functions manually which phylink would usually do for us, but we will let +it do that soon. + +One problem is that we don't actually have a PCS for QSGMII. We pretend +that each mEMAC's MDIO bus has four QSGMII PCSs, but this is not the case. +Only the "base" mEMAC's MDIO bus has the four QSGMII PCSs. This is not an +issue yet, because we never get the PCS state. However, it will be once the +conversion to phylink is complete, since the links will appear to never +come up. To get around this, we allow specifying multiple PCSs in pcsphy. +This breaks backwards compatibility with old device trees, but only for +QSGMII. IMO this is the only reasonable way to figure out what the actual +QSGMII PCS is. + +Additionally, we now also support a separate XFI PCS. This can allow the +SerDes driver to set different addresses for the SGMII and XFI PCSs so they +can be accessed at the same time. + +Signed-off-by: Sean Anderson +Signed-off-by: David S. Miller +--- + drivers/net/ethernet/freescale/fman/Kconfig | 3 + + .../net/ethernet/freescale/fman/fman_memac.c | 258 +++++++----------- + 2 files changed, 105 insertions(+), 156 deletions(-) + +--- a/drivers/net/ethernet/freescale/fman/Kconfig ++++ b/drivers/net/ethernet/freescale/fman/Kconfig +@@ -4,6 +4,9 @@ config FSL_FMAN + depends on FSL_SOC || ARCH_LAYERSCAPE || COMPILE_TEST + select GENERIC_ALLOCATOR + select PHYLIB ++ select PHYLINK ++ select PCS ++ select PCS_LYNX + select CRC32 + default n + help +--- a/drivers/net/ethernet/freescale/fman/fman_memac.c ++++ b/drivers/net/ethernet/freescale/fman/fman_memac.c +@@ -11,43 +11,12 @@ + + #include + #include ++#include + #include + #include + #include + #include + +-/* PCS registers */ +-#define MDIO_SGMII_CR 0x00 +-#define MDIO_SGMII_DEV_ABIL_SGMII 0x04 +-#define MDIO_SGMII_LINK_TMR_L 0x12 +-#define MDIO_SGMII_LINK_TMR_H 0x13 +-#define MDIO_SGMII_IF_MODE 0x14 +- +-/* SGMII Control defines */ +-#define SGMII_CR_AN_EN 0x1000 +-#define SGMII_CR_RESTART_AN 0x0200 +-#define SGMII_CR_FD 0x0100 +-#define SGMII_CR_SPEED_SEL1_1G 0x0040 +-#define SGMII_CR_DEF_VAL (SGMII_CR_AN_EN | SGMII_CR_FD | \ +- SGMII_CR_SPEED_SEL1_1G) +- +-/* SGMII Device Ability for SGMII defines */ +-#define MDIO_SGMII_DEV_ABIL_SGMII_MODE 0x4001 +-#define MDIO_SGMII_DEV_ABIL_BASEX_MODE 0x01A0 +- +-/* Link timer define */ +-#define LINK_TMR_L 0xa120 +-#define LINK_TMR_H 0x0007 +-#define LINK_TMR_L_BASEX 0xaf08 +-#define LINK_TMR_H_BASEX 0x002f +- +-/* SGMII IF Mode defines */ +-#define IF_MODE_USE_SGMII_AN 0x0002 +-#define IF_MODE_SGMII_EN 0x0001 +-#define IF_MODE_SGMII_SPEED_100M 0x0004 +-#define IF_MODE_SGMII_SPEED_1G 0x0008 +-#define IF_MODE_SGMII_DUPLEX_HALF 0x0010 +- + /* Num of additional exact match MAC adr regs */ + #define MEMAC_NUM_OF_PADDRS 7 + +@@ -326,7 +295,9 @@ struct fman_mac { + struct fman_rev_info fm_rev_info; + bool basex_if; + struct phy *serdes; +- struct phy_device *pcsphy; ++ struct phylink_pcs *sgmii_pcs; ++ struct phylink_pcs *qsgmii_pcs; ++ struct phylink_pcs *xfi_pcs; + bool allmulti_enabled; + }; + +@@ -487,91 +458,22 @@ static u32 get_mac_addr_hash_code(u64 et + return xor_val; + } + +-static void setup_sgmii_internal_phy(struct fman_mac *memac, +- struct fixed_phy_status *fixed_link) +-{ +- u16 tmp_reg16; +- +- if (WARN_ON(!memac->pcsphy)) +- return; +- +- /* SGMII mode */ +- tmp_reg16 = IF_MODE_SGMII_EN; +- if (!fixed_link) +- /* AN enable */ +- tmp_reg16 |= IF_MODE_USE_SGMII_AN; +- else { +- switch (fixed_link->speed) { +- case 10: +- /* For 10M: IF_MODE[SPEED_10M] = 0 */ +- break; +- case 100: +- tmp_reg16 |= IF_MODE_SGMII_SPEED_100M; +- break; +- case 1000: +- default: +- tmp_reg16 |= IF_MODE_SGMII_SPEED_1G; +- break; +- } +- if (!fixed_link->duplex) +- tmp_reg16 |= IF_MODE_SGMII_DUPLEX_HALF; +- } +- phy_write(memac->pcsphy, MDIO_SGMII_IF_MODE, tmp_reg16); +- +- /* Device ability according to SGMII specification */ +- tmp_reg16 = MDIO_SGMII_DEV_ABIL_SGMII_MODE; +- phy_write(memac->pcsphy, MDIO_SGMII_DEV_ABIL_SGMII, tmp_reg16); +- +- /* Adjust link timer for SGMII - +- * According to Cisco SGMII specification the timer should be 1.6 ms. +- * The link_timer register is configured in units of the clock. +- * - When running as 1G SGMII, Serdes clock is 125 MHz, so +- * unit = 1 / (125*10^6 Hz) = 8 ns. +- * 1.6 ms in units of 8 ns = 1.6ms / 8ns = 2*10^5 = 0x30d40 +- * - When running as 2.5G SGMII, Serdes clock is 312.5 MHz, so +- * unit = 1 / (312.5*10^6 Hz) = 3.2 ns. +- * 1.6 ms in units of 3.2 ns = 1.6ms / 3.2ns = 5*10^5 = 0x7a120. +- * Since link_timer value of 1G SGMII will be too short for 2.5 SGMII, +- * we always set up here a value of 2.5 SGMII. +- */ +- phy_write(memac->pcsphy, MDIO_SGMII_LINK_TMR_H, LINK_TMR_H); +- phy_write(memac->pcsphy, MDIO_SGMII_LINK_TMR_L, LINK_TMR_L); +- +- if (!fixed_link) +- /* Restart AN */ +- tmp_reg16 = SGMII_CR_DEF_VAL | SGMII_CR_RESTART_AN; ++static void setup_sgmii_internal(struct fman_mac *memac, ++ struct phylink_pcs *pcs, ++ struct fixed_phy_status *fixed_link) ++{ ++ __ETHTOOL_DECLARE_LINK_MODE_MASK(advertising); ++ phy_interface_t iface = memac->basex_if ? PHY_INTERFACE_MODE_1000BASEX : ++ PHY_INTERFACE_MODE_SGMII; ++ unsigned int mode = fixed_link ? MLO_AN_FIXED : MLO_AN_INBAND; ++ ++ linkmode_set_pause(advertising, true, true); ++ pcs->ops->pcs_config(pcs, mode, iface, advertising, true); ++ if (fixed_link) ++ pcs->ops->pcs_link_up(pcs, mode, iface, fixed_link->speed, ++ fixed_link->duplex); + else +- /* AN disabled */ +- tmp_reg16 = SGMII_CR_DEF_VAL & ~SGMII_CR_AN_EN; +- phy_write(memac->pcsphy, 0x0, tmp_reg16); +-} +- +-static void setup_sgmii_internal_phy_base_x(struct fman_mac *memac) +-{ +- u16 tmp_reg16; +- +- /* AN Device capability */ +- tmp_reg16 = MDIO_SGMII_DEV_ABIL_BASEX_MODE; +- phy_write(memac->pcsphy, MDIO_SGMII_DEV_ABIL_SGMII, tmp_reg16); +- +- /* Adjust link timer for SGMII - +- * For Serdes 1000BaseX auto-negotiation the timer should be 10 ms. +- * The link_timer register is configured in units of the clock. +- * - When running as 1G SGMII, Serdes clock is 125 MHz, so +- * unit = 1 / (125*10^6 Hz) = 8 ns. +- * 10 ms in units of 8 ns = 10ms / 8ns = 1250000 = 0x1312d0 +- * - When running as 2.5G SGMII, Serdes clock is 312.5 MHz, so +- * unit = 1 / (312.5*10^6 Hz) = 3.2 ns. +- * 10 ms in units of 3.2 ns = 10ms / 3.2ns = 3125000 = 0x2faf08. +- * Since link_timer value of 1G SGMII will be too short for 2.5 SGMII, +- * we always set up here a value of 2.5 SGMII. +- */ +- phy_write(memac->pcsphy, MDIO_SGMII_LINK_TMR_H, LINK_TMR_H_BASEX); +- phy_write(memac->pcsphy, MDIO_SGMII_LINK_TMR_L, LINK_TMR_L_BASEX); +- +- /* Restart AN */ +- tmp_reg16 = SGMII_CR_DEF_VAL | SGMII_CR_RESTART_AN; +- phy_write(memac->pcsphy, 0x0, tmp_reg16); ++ pcs->ops->pcs_an_restart(pcs); + } + + static int check_init_parameters(struct fman_mac *memac) +@@ -983,7 +885,6 @@ static int memac_set_exception(struct fm + static int memac_init(struct fman_mac *memac) + { + struct memac_cfg *memac_drv_param; +- u8 i; + enet_addr_t eth_addr; + bool slow_10g_if = false; + struct fixed_phy_status *fixed_link = NULL; +@@ -1036,32 +937,10 @@ static int memac_init(struct fman_mac *m + iowrite32be(reg32, &memac->regs->command_config); + } + +- if (memac->phy_if == PHY_INTERFACE_MODE_SGMII) { +- /* Configure internal SGMII PHY */ +- if (memac->basex_if) +- setup_sgmii_internal_phy_base_x(memac); +- else +- setup_sgmii_internal_phy(memac, fixed_link); +- } else if (memac->phy_if == PHY_INTERFACE_MODE_QSGMII) { +- /* Configure 4 internal SGMII PHYs */ +- for (i = 0; i < 4; i++) { +- u8 qsmgii_phy_addr, phy_addr; +- /* QSGMII PHY address occupies 3 upper bits of 5-bit +- * phy_address; the lower 2 bits are used to extend +- * register address space and access each one of 4 +- * ports inside QSGMII. +- */ +- phy_addr = memac->pcsphy->mdio.addr; +- qsmgii_phy_addr = (u8)((phy_addr << 2) | i); +- memac->pcsphy->mdio.addr = qsmgii_phy_addr; +- if (memac->basex_if) +- setup_sgmii_internal_phy_base_x(memac); +- else +- setup_sgmii_internal_phy(memac, fixed_link); +- +- memac->pcsphy->mdio.addr = phy_addr; +- } +- } ++ if (memac->phy_if == PHY_INTERFACE_MODE_SGMII) ++ setup_sgmii_internal(memac, memac->sgmii_pcs, fixed_link); ++ else if (memac->phy_if == PHY_INTERFACE_MODE_QSGMII) ++ setup_sgmii_internal(memac, memac->qsgmii_pcs, fixed_link); + + /* Max Frame Length */ + err = fman_set_mac_max_frame(memac->fm, memac->mac_id, +@@ -1097,12 +976,25 @@ static int memac_init(struct fman_mac *m + return 0; + } + ++static void pcs_put(struct phylink_pcs *pcs) ++{ ++ struct mdio_device *mdiodev; ++ ++ if (IS_ERR_OR_NULL(pcs)) ++ return; ++ ++ mdiodev = lynx_get_mdio_device(pcs); ++ lynx_pcs_destroy(pcs); ++ mdio_device_free(mdiodev); ++} ++ + static int memac_free(struct fman_mac *memac) + { + free_init_resources(memac); + +- if (memac->pcsphy) +- put_device(&memac->pcsphy->mdio.dev); ++ pcs_put(memac->sgmii_pcs); ++ pcs_put(memac->qsgmii_pcs); ++ pcs_put(memac->xfi_pcs); + + kfree(memac->memac_drv_param); + kfree(memac); +@@ -1153,12 +1045,31 @@ static struct fman_mac *memac_config(str + return memac; + } + ++static struct phylink_pcs *memac_pcs_create(struct device_node *mac_node, ++ int index) ++{ ++ struct device_node *node; ++ struct mdio_device *mdiodev = NULL; ++ struct phylink_pcs *pcs; ++ ++ node = of_parse_phandle(mac_node, "pcsphy-handle", index); ++ if (node && of_device_is_available(node)) ++ mdiodev = of_mdio_find_device(node); ++ of_node_put(node); ++ ++ if (!mdiodev) ++ return ERR_PTR(-EPROBE_DEFER); ++ ++ pcs = lynx_pcs_create(mdiodev); ++ return pcs; ++} ++ + int memac_initialization(struct mac_device *mac_dev, + struct device_node *mac_node, + struct fman_mac_params *params) + { + int err; +- struct device_node *phy_node; ++ struct phylink_pcs *pcs; + struct fixed_phy_status *fixed_link; + struct fman_mac *memac; + +@@ -1188,23 +1099,58 @@ int memac_initialization(struct mac_devi + memac = mac_dev->fman_mac; + memac->memac_drv_param->max_frame_length = fman_get_max_frm(); + memac->memac_drv_param->reset_on_init = true; +- if (memac->phy_if == PHY_INTERFACE_MODE_SGMII || +- memac->phy_if == PHY_INTERFACE_MODE_QSGMII) { +- phy_node = of_parse_phandle(mac_node, "pcsphy-handle", 0); +- if (!phy_node) { +- pr_err("PCS PHY node is not available\n"); +- err = -EINVAL; ++ ++ err = of_property_match_string(mac_node, "pcs-handle-names", "xfi"); ++ if (err >= 0) { ++ memac->xfi_pcs = memac_pcs_create(mac_node, err); ++ if (IS_ERR(memac->xfi_pcs)) { ++ err = PTR_ERR(memac->xfi_pcs); ++ dev_err_probe(mac_dev->dev, err, "missing xfi pcs\n"); + goto _return_fm_mac_free; + } ++ } else if (err != -EINVAL && err != -ENODATA) { ++ goto _return_fm_mac_free; ++ } + +- memac->pcsphy = of_phy_find_device(phy_node); +- if (!memac->pcsphy) { +- pr_err("of_phy_find_device (PCS PHY) failed\n"); +- err = -EINVAL; ++ err = of_property_match_string(mac_node, "pcs-handle-names", "qsgmii"); ++ if (err >= 0) { ++ memac->qsgmii_pcs = memac_pcs_create(mac_node, err); ++ if (IS_ERR(memac->qsgmii_pcs)) { ++ err = PTR_ERR(memac->qsgmii_pcs); ++ dev_err_probe(mac_dev->dev, err, ++ "missing qsgmii pcs\n"); + goto _return_fm_mac_free; + } ++ } else if (err != -EINVAL && err != -ENODATA) { ++ goto _return_fm_mac_free; ++ } ++ ++ /* For compatibility, if pcs-handle-names is missing, we assume this ++ * phy is the first one in pcsphy-handle ++ */ ++ err = of_property_match_string(mac_node, "pcs-handle-names", "sgmii"); ++ if (err == -EINVAL || err == -ENODATA) ++ pcs = memac_pcs_create(mac_node, 0); ++ else if (err < 0) ++ goto _return_fm_mac_free; ++ else ++ pcs = memac_pcs_create(mac_node, err); ++ ++ if (!pcs) { ++ dev_err(mac_dev->dev, "missing pcs\n"); ++ err = -ENOENT; ++ goto _return_fm_mac_free; + } + ++ /* If err is set here, it means that pcs-handle-names was missing above ++ * (and therefore that xfi_pcs cannot be set). If we are defaulting to ++ * XGMII, assume this is for XFI. Otherwise, assume it is for SGMII. ++ */ ++ if (err && mac_dev->phy_if == PHY_INTERFACE_MODE_XGMII) ++ memac->xfi_pcs = pcs; ++ else ++ memac->sgmii_pcs = pcs; ++ + memac->serdes = devm_of_phy_get(mac_dev->dev, mac_node, "serdes"); + err = PTR_ERR(memac->serdes); + if (err == -ENODEV || err == -ENOSYS) { diff --git a/target/linux/generic/backport-6.1/715-03-v6.2-net-dpaa-Convert-to-phylink.patch b/target/linux/generic/backport-6.1/715-03-v6.2-net-dpaa-Convert-to-phylink.patch new file mode 100644 index 000000000..63b651bb2 --- /dev/null +++ b/target/linux/generic/backport-6.1/715-03-v6.2-net-dpaa-Convert-to-phylink.patch @@ -0,0 +1,2451 @@ +From 38e50fc3d43882a43115b4f1ca3eb88255163c5b Mon Sep 17 00:00:00 2001 +From: Sean Anderson +Date: Mon, 17 Oct 2022 16:22:38 -0400 +Subject: [PATCH 03/21] net: dpaa: Convert to phylink + +This converts DPAA to phylink. All macs are converted. This should work +with no device tree modifications (including those made in this series), +except for QSGMII (as noted previously). + +The mEMAC configuration is one of the tricker areas. I have tried to +capture all the restrictions across the various models. Most of the time, +we assume that if the serdes supports a mode or the phy-interface-mode +specifies it, then we support it. The only place we can't do this is +(RG)MII, since there's no serdes. In that case, we rely on a (new) +devicetree property. There are also several cases where half-duplex is +broken. Unfortunately, only a single compatible is used for the MAC, so we +have to use the board compatible instead. + +The 10GEC conversion is very straightforward, since it only supports XAUI. +There is generally nothing to configure. + +The dTSEC conversion is broadly similar to mEMAC, but is simpler because we +don't support configuring the SerDes (though this can be easily added) and +we don't have multiple PCSs. From what I can tell, there's nothing +different in the driver or documentation between SGMII and 1000BASE-X +except for the advertising. Similarly, I couldn't find anything about +2500BASE-X. In both cases, I treat them like SGMII. These modes aren't used +by any in-tree boards. Similarly, despite being mentioned in the driver, I +couldn't find any documented SoCs which supported QSGMII. I have left it +unimplemented for now. + +Signed-off-by: Sean Anderson +Signed-off-by: David S. Miller +--- + drivers/net/ethernet/freescale/dpaa/Kconfig | 4 +- + .../net/ethernet/freescale/dpaa/dpaa_eth.c | 89 +-- + .../ethernet/freescale/dpaa/dpaa_ethtool.c | 90 +-- + drivers/net/ethernet/freescale/fman/Kconfig | 1 - + .../net/ethernet/freescale/fman/fman_dtsec.c | 458 +++++++-------- + .../net/ethernet/freescale/fman/fman_mac.h | 10 - + .../net/ethernet/freescale/fman/fman_memac.c | 547 +++++++++--------- + .../net/ethernet/freescale/fman/fman_tgec.c | 131 ++--- + drivers/net/ethernet/freescale/fman/mac.c | 168 +----- + drivers/net/ethernet/freescale/fman/mac.h | 23 +- + 10 files changed, 612 insertions(+), 909 deletions(-) + +--- a/drivers/net/ethernet/freescale/dpaa/Kconfig ++++ b/drivers/net/ethernet/freescale/dpaa/Kconfig +@@ -2,8 +2,8 @@ + menuconfig FSL_DPAA_ETH + tristate "DPAA Ethernet" + depends on FSL_DPAA && FSL_FMAN +- select PHYLIB +- select FIXED_PHY ++ select PHYLINK ++ select PCS_LYNX + help + Data Path Acceleration Architecture Ethernet driver, + supporting the Freescale QorIQ chips. +--- a/drivers/net/ethernet/freescale/dpaa/dpaa_eth.c ++++ b/drivers/net/ethernet/freescale/dpaa/dpaa_eth.c +@@ -264,8 +264,19 @@ static int dpaa_netdev_init(struct net_d + net_dev->needed_headroom = priv->tx_headroom; + net_dev->watchdog_timeo = msecs_to_jiffies(tx_timeout); + +- mac_dev->net_dev = net_dev; ++ /* The rest of the config is filled in by the mac device already */ ++ mac_dev->phylink_config.dev = &net_dev->dev; ++ mac_dev->phylink_config.type = PHYLINK_NETDEV; + mac_dev->update_speed = dpaa_eth_cgr_set_speed; ++ mac_dev->phylink = phylink_create(&mac_dev->phylink_config, ++ dev_fwnode(mac_dev->dev), ++ mac_dev->phy_if, ++ mac_dev->phylink_ops); ++ if (IS_ERR(mac_dev->phylink)) { ++ err = PTR_ERR(mac_dev->phylink); ++ dev_err_probe(dev, err, "Could not create phylink\n"); ++ return err; ++ } + + /* start without the RUNNING flag, phylib controls it later */ + netif_carrier_off(net_dev); +@@ -273,6 +284,7 @@ static int dpaa_netdev_init(struct net_d + err = register_netdev(net_dev); + if (err < 0) { + dev_err(dev, "register_netdev() = %d\n", err); ++ phylink_destroy(mac_dev->phylink); + return err; + } + +@@ -295,8 +307,7 @@ static int dpaa_stop(struct net_device * + */ + msleep(200); + +- if (mac_dev->phy_dev) +- phy_stop(mac_dev->phy_dev); ++ phylink_stop(mac_dev->phylink); + mac_dev->disable(mac_dev->fman_mac); + + for (i = 0; i < ARRAY_SIZE(mac_dev->port); i++) { +@@ -305,8 +316,7 @@ static int dpaa_stop(struct net_device * + err = error; + } + +- if (net_dev->phydev) +- phy_disconnect(net_dev->phydev); ++ phylink_disconnect_phy(mac_dev->phylink); + net_dev->phydev = NULL; + + msleep(200); +@@ -834,10 +844,10 @@ static int dpaa_eth_cgr_init(struct dpaa + + /* Set different thresholds based on the configured MAC speed. + * This may turn suboptimal if the MAC is reconfigured at another +- * speed, so MACs must call dpaa_eth_cgr_set_speed in their adjust_link ++ * speed, so MACs must call dpaa_eth_cgr_set_speed in their link_up + * callback. + */ +- if (priv->mac_dev->if_support & SUPPORTED_10000baseT_Full) ++ if (priv->mac_dev->phylink_config.mac_capabilities & MAC_10000FD) + cs_th = DPAA_CS_THRESHOLD_10G; + else + cs_th = DPAA_CS_THRESHOLD_1G; +@@ -866,7 +876,7 @@ out_error: + + static void dpaa_eth_cgr_set_speed(struct mac_device *mac_dev, int speed) + { +- struct net_device *net_dev = mac_dev->net_dev; ++ struct net_device *net_dev = to_net_dev(mac_dev->phylink_config.dev); + struct dpaa_priv *priv = netdev_priv(net_dev); + struct qm_mcc_initcgr opts = { }; + u32 cs_th; +@@ -2905,58 +2915,6 @@ static void dpaa_eth_napi_disable(struct + } + } + +-static void dpaa_adjust_link(struct net_device *net_dev) +-{ +- struct mac_device *mac_dev; +- struct dpaa_priv *priv; +- +- priv = netdev_priv(net_dev); +- mac_dev = priv->mac_dev; +- mac_dev->adjust_link(mac_dev); +-} +- +-/* The Aquantia PHYs are capable of performing rate adaptation */ +-#define PHY_VEND_AQUANTIA 0x03a1b400 +-#define PHY_VEND_AQUANTIA2 0x31c31c00 +- +-static int dpaa_phy_init(struct net_device *net_dev) +-{ +- __ETHTOOL_DECLARE_LINK_MODE_MASK(mask) = { 0, }; +- struct mac_device *mac_dev; +- struct phy_device *phy_dev; +- struct dpaa_priv *priv; +- u32 phy_vendor; +- +- priv = netdev_priv(net_dev); +- mac_dev = priv->mac_dev; +- +- phy_dev = of_phy_connect(net_dev, mac_dev->phy_node, +- &dpaa_adjust_link, 0, +- mac_dev->phy_if); +- if (!phy_dev) { +- netif_err(priv, ifup, net_dev, "init_phy() failed\n"); +- return -ENODEV; +- } +- +- phy_vendor = phy_dev->drv->phy_id & GENMASK(31, 10); +- /* Unless the PHY is capable of rate adaptation */ +- if (mac_dev->phy_if != PHY_INTERFACE_MODE_XGMII || +- (phy_vendor != PHY_VEND_AQUANTIA && +- phy_vendor != PHY_VEND_AQUANTIA2)) { +- /* remove any features not supported by the controller */ +- ethtool_convert_legacy_u32_to_link_mode(mask, +- mac_dev->if_support); +- linkmode_and(phy_dev->supported, phy_dev->supported, mask); +- } +- +- phy_support_asym_pause(phy_dev); +- +- mac_dev->phy_dev = phy_dev; +- net_dev->phydev = phy_dev; +- +- return 0; +-} +- + static int dpaa_open(struct net_device *net_dev) + { + struct mac_device *mac_dev; +@@ -2967,7 +2925,8 @@ static int dpaa_open(struct net_device * + mac_dev = priv->mac_dev; + dpaa_eth_napi_enable(priv); + +- err = dpaa_phy_init(net_dev); ++ err = phylink_of_phy_connect(mac_dev->phylink, ++ mac_dev->dev->of_node, 0); + if (err) + goto phy_init_failed; + +@@ -2982,7 +2941,7 @@ static int dpaa_open(struct net_device * + netif_err(priv, ifup, net_dev, "mac_dev->enable() = %d\n", err); + goto mac_start_failed; + } +- phy_start(priv->mac_dev->phy_dev); ++ phylink_start(mac_dev->phylink); + + netif_tx_start_all_queues(net_dev); + +@@ -2991,6 +2950,7 @@ static int dpaa_open(struct net_device * + mac_start_failed: + for (i = 0; i < ARRAY_SIZE(mac_dev->port); i++) + fman_port_disable(mac_dev->port[i]); ++ phylink_disconnect_phy(mac_dev->phylink); + + phy_init_failed: + dpaa_eth_napi_disable(priv); +@@ -3146,10 +3106,12 @@ static int dpaa_ts_ioctl(struct net_devi + static int dpaa_ioctl(struct net_device *net_dev, struct ifreq *rq, int cmd) + { + int ret = -EINVAL; ++ struct dpaa_priv *priv = netdev_priv(net_dev); + + if (cmd == SIOCGMIIREG) { + if (net_dev->phydev) +- return phy_mii_ioctl(net_dev->phydev, rq, cmd); ++ return phylink_mii_ioctl(priv->mac_dev->phylink, rq, ++ cmd); + } + + if (cmd == SIOCSHWTSTAMP) +@@ -3552,6 +3514,7 @@ static int dpaa_remove(struct platform_d + + dev_set_drvdata(dev, NULL); + unregister_netdev(net_dev); ++ phylink_destroy(priv->mac_dev->phylink); + + err = dpaa_fq_free(dev, &priv->dpaa_fq_list); + +--- a/drivers/net/ethernet/freescale/dpaa/dpaa_ethtool.c ++++ b/drivers/net/ethernet/freescale/dpaa/dpaa_ethtool.c +@@ -54,27 +54,19 @@ static char dpaa_stats_global[][ETH_GSTR + static int dpaa_get_link_ksettings(struct net_device *net_dev, + struct ethtool_link_ksettings *cmd) + { +- if (!net_dev->phydev) +- return 0; ++ struct dpaa_priv *priv = netdev_priv(net_dev); ++ struct mac_device *mac_dev = priv->mac_dev; + +- phy_ethtool_ksettings_get(net_dev->phydev, cmd); +- +- return 0; ++ return phylink_ethtool_ksettings_get(mac_dev->phylink, cmd); + } + + static int dpaa_set_link_ksettings(struct net_device *net_dev, + const struct ethtool_link_ksettings *cmd) + { +- int err; +- +- if (!net_dev->phydev) +- return -ENODEV; ++ struct dpaa_priv *priv = netdev_priv(net_dev); ++ struct mac_device *mac_dev = priv->mac_dev; + +- err = phy_ethtool_ksettings_set(net_dev->phydev, cmd); +- if (err < 0) +- netdev_err(net_dev, "phy_ethtool_ksettings_set() = %d\n", err); +- +- return err; ++ return phylink_ethtool_ksettings_set(mac_dev->phylink, cmd); + } + + static void dpaa_get_drvinfo(struct net_device *net_dev, +@@ -99,80 +91,28 @@ static void dpaa_set_msglevel(struct net + + static int dpaa_nway_reset(struct net_device *net_dev) + { +- int err; +- +- if (!net_dev->phydev) +- return -ENODEV; ++ struct dpaa_priv *priv = netdev_priv(net_dev); ++ struct mac_device *mac_dev = priv->mac_dev; + +- err = 0; +- if (net_dev->phydev->autoneg) { +- err = phy_start_aneg(net_dev->phydev); +- if (err < 0) +- netdev_err(net_dev, "phy_start_aneg() = %d\n", +- err); +- } +- +- return err; ++ return phylink_ethtool_nway_reset(mac_dev->phylink); + } + + static void dpaa_get_pauseparam(struct net_device *net_dev, + struct ethtool_pauseparam *epause) + { +- struct mac_device *mac_dev; +- struct dpaa_priv *priv; +- +- priv = netdev_priv(net_dev); +- mac_dev = priv->mac_dev; +- +- if (!net_dev->phydev) +- return; ++ struct dpaa_priv *priv = netdev_priv(net_dev); ++ struct mac_device *mac_dev = priv->mac_dev; + +- epause->autoneg = mac_dev->autoneg_pause; +- epause->rx_pause = mac_dev->rx_pause_active; +- epause->tx_pause = mac_dev->tx_pause_active; ++ phylink_ethtool_get_pauseparam(mac_dev->phylink, epause); + } + + static int dpaa_set_pauseparam(struct net_device *net_dev, + struct ethtool_pauseparam *epause) + { +- struct mac_device *mac_dev; +- struct phy_device *phydev; +- bool rx_pause, tx_pause; +- struct dpaa_priv *priv; +- int err; +- +- priv = netdev_priv(net_dev); +- mac_dev = priv->mac_dev; +- +- phydev = net_dev->phydev; +- if (!phydev) { +- netdev_err(net_dev, "phy device not initialized\n"); +- return -ENODEV; +- } +- +- if (!phy_validate_pause(phydev, epause)) +- return -EINVAL; +- +- /* The MAC should know how to handle PAUSE frame autonegotiation before +- * adjust_link is triggered by a forced renegotiation of sym/asym PAUSE +- * settings. +- */ +- mac_dev->autoneg_pause = !!epause->autoneg; +- mac_dev->rx_pause_req = !!epause->rx_pause; +- mac_dev->tx_pause_req = !!epause->tx_pause; +- +- /* Determine the sym/asym advertised PAUSE capabilities from the desired +- * rx/tx pause settings. +- */ +- +- phy_set_asym_pause(phydev, epause->rx_pause, epause->tx_pause); +- +- fman_get_pause_cfg(mac_dev, &rx_pause, &tx_pause); +- err = fman_set_mac_active_pause(mac_dev, rx_pause, tx_pause); +- if (err < 0) +- netdev_err(net_dev, "set_mac_active_pause() = %d\n", err); ++ struct dpaa_priv *priv = netdev_priv(net_dev); ++ struct mac_device *mac_dev = priv->mac_dev; + +- return err; ++ return phylink_ethtool_set_pauseparam(mac_dev->phylink, epause); + } + + static int dpaa_get_sset_count(struct net_device *net_dev, int type) +--- a/drivers/net/ethernet/freescale/fman/Kconfig ++++ b/drivers/net/ethernet/freescale/fman/Kconfig +@@ -3,7 +3,6 @@ config FSL_FMAN + tristate "FMan support" + depends on FSL_SOC || ARCH_LAYERSCAPE || COMPILE_TEST + select GENERIC_ALLOCATOR +- select PHYLIB + select PHYLINK + select PCS + select PCS_LYNX +--- a/drivers/net/ethernet/freescale/fman/fman_dtsec.c ++++ b/drivers/net/ethernet/freescale/fman/fman_dtsec.c +@@ -17,6 +17,7 @@ + #include + #include + #include ++#include + + /* TBI register addresses */ + #define MII_TBICON 0x11 +@@ -29,9 +30,6 @@ + #define TBICON_CLK_SELECT 0x0020 /* Clock select */ + #define TBICON_MI_MODE 0x0010 /* GMII mode (TBI if not set) */ + +-#define TBIANA_SGMII 0x4001 +-#define TBIANA_1000X 0x01a0 +- + /* Interrupt Mask Register (IMASK) */ + #define DTSEC_IMASK_BREN 0x80000000 + #define DTSEC_IMASK_RXCEN 0x40000000 +@@ -92,9 +90,10 @@ + + #define DTSEC_ECNTRL_GMIIM 0x00000040 + #define DTSEC_ECNTRL_TBIM 0x00000020 +-#define DTSEC_ECNTRL_SGMIIM 0x00000002 + #define DTSEC_ECNTRL_RPM 0x00000010 + #define DTSEC_ECNTRL_R100M 0x00000008 ++#define DTSEC_ECNTRL_RMM 0x00000004 ++#define DTSEC_ECNTRL_SGMIIM 0x00000002 + #define DTSEC_ECNTRL_QSGMIIM 0x00000001 + + #define TCTRL_TTSE 0x00000040 +@@ -318,7 +317,8 @@ struct fman_mac { + void *fm; + struct fman_rev_info fm_rev_info; + bool basex_if; +- struct phy_device *tbiphy; ++ struct mdio_device *tbidev; ++ struct phylink_pcs pcs; + }; + + static void set_dflts(struct dtsec_cfg *cfg) +@@ -356,56 +356,14 @@ static int init(struct dtsec_regs __iome + phy_interface_t iface, u16 iface_speed, u64 addr, + u32 exception_mask, u8 tbi_addr) + { +- bool is_rgmii, is_sgmii, is_qsgmii; + enet_addr_t eth_addr; +- u32 tmp; ++ u32 tmp = 0; + int i; + + /* Soft reset */ + iowrite32be(MACCFG1_SOFT_RESET, ®s->maccfg1); + iowrite32be(0, ®s->maccfg1); + +- /* dtsec_id2 */ +- tmp = ioread32be(®s->tsec_id2); +- +- /* check RGMII support */ +- if (iface == PHY_INTERFACE_MODE_RGMII || +- iface == PHY_INTERFACE_MODE_RGMII_ID || +- iface == PHY_INTERFACE_MODE_RGMII_RXID || +- iface == PHY_INTERFACE_MODE_RGMII_TXID || +- iface == PHY_INTERFACE_MODE_RMII) +- if (tmp & DTSEC_ID2_INT_REDUCED_OFF) +- return -EINVAL; +- +- if (iface == PHY_INTERFACE_MODE_SGMII || +- iface == PHY_INTERFACE_MODE_MII) +- if (tmp & DTSEC_ID2_INT_REDUCED_OFF) +- return -EINVAL; +- +- is_rgmii = iface == PHY_INTERFACE_MODE_RGMII || +- iface == PHY_INTERFACE_MODE_RGMII_ID || +- iface == PHY_INTERFACE_MODE_RGMII_RXID || +- iface == PHY_INTERFACE_MODE_RGMII_TXID; +- is_sgmii = iface == PHY_INTERFACE_MODE_SGMII; +- is_qsgmii = iface == PHY_INTERFACE_MODE_QSGMII; +- +- tmp = 0; +- if (is_rgmii || iface == PHY_INTERFACE_MODE_GMII) +- tmp |= DTSEC_ECNTRL_GMIIM; +- if (is_sgmii) +- tmp |= (DTSEC_ECNTRL_SGMIIM | DTSEC_ECNTRL_TBIM); +- if (is_qsgmii) +- tmp |= (DTSEC_ECNTRL_SGMIIM | DTSEC_ECNTRL_TBIM | +- DTSEC_ECNTRL_QSGMIIM); +- if (is_rgmii) +- tmp |= DTSEC_ECNTRL_RPM; +- if (iface_speed == SPEED_100) +- tmp |= DTSEC_ECNTRL_R100M; +- +- iowrite32be(tmp, ®s->ecntrl); +- +- tmp = 0; +- + if (cfg->tx_pause_time) + tmp |= cfg->tx_pause_time; + if (cfg->tx_pause_time_extd) +@@ -446,17 +404,10 @@ static int init(struct dtsec_regs __iome + + tmp = 0; + +- if (iface_speed < SPEED_1000) +- tmp |= MACCFG2_NIBBLE_MODE; +- else if (iface_speed == SPEED_1000) +- tmp |= MACCFG2_BYTE_MODE; +- + tmp |= (cfg->preamble_len << MACCFG2_PREAMBLE_LENGTH_SHIFT) & + MACCFG2_PREAMBLE_LENGTH_MASK; + if (cfg->tx_pad_crc) + tmp |= MACCFG2_PAD_CRC_EN; +- /* Full Duplex */ +- tmp |= MACCFG2_FULL_DUPLEX; + iowrite32be(tmp, ®s->maccfg2); + + tmp = (((cfg->non_back_to_back_ipg1 << +@@ -525,10 +476,6 @@ static void set_bucket(struct dtsec_regs + + static int check_init_parameters(struct fman_mac *dtsec) + { +- if (dtsec->max_speed >= SPEED_10000) { +- pr_err("1G MAC driver supports 1G or lower speeds\n"); +- return -EINVAL; +- } + if ((dtsec->dtsec_drv_param)->rx_prepend > + MAX_PACKET_ALIGNMENT) { + pr_err("packetAlignmentPadding can't be > than %d\n", +@@ -630,22 +577,10 @@ static int get_exception_flag(enum fman_ + return bit_mask; + } + +-static bool is_init_done(struct dtsec_cfg *dtsec_drv_params) +-{ +- /* Checks if dTSEC driver parameters were initialized */ +- if (!dtsec_drv_params) +- return true; +- +- return false; +-} +- + static u16 dtsec_get_max_frame_length(struct fman_mac *dtsec) + { + struct dtsec_regs __iomem *regs = dtsec->regs; + +- if (is_init_done(dtsec->dtsec_drv_param)) +- return 0; +- + return (u16)ioread32be(®s->maxfrm); + } + +@@ -682,6 +617,7 @@ static void dtsec_isr(void *handle) + dtsec->exception_cb(dtsec->dev_id, FM_MAC_EX_1G_COL_RET_LMT); + if (event & DTSEC_IMASK_XFUNEN) { + /* FM_TX_LOCKUP_ERRATA_DTSEC6 Errata workaround */ ++ /* FIXME: This races with the rest of the driver! */ + if (dtsec->fm_rev_info.major == 2) { + u32 tpkt1, tmp_reg1, tpkt2, tmp_reg2, i; + /* a. Write 0x00E0_0C00 to DTSEC_ID +@@ -814,6 +750,43 @@ static void free_init_resources(struct f + dtsec->unicast_addr_hash = NULL; + } + ++static struct fman_mac *pcs_to_dtsec(struct phylink_pcs *pcs) ++{ ++ return container_of(pcs, struct fman_mac, pcs); ++} ++ ++static void dtsec_pcs_get_state(struct phylink_pcs *pcs, ++ struct phylink_link_state *state) ++{ ++ struct fman_mac *dtsec = pcs_to_dtsec(pcs); ++ ++ phylink_mii_c22_pcs_get_state(dtsec->tbidev, state); ++} ++ ++static int dtsec_pcs_config(struct phylink_pcs *pcs, unsigned int mode, ++ phy_interface_t interface, ++ const unsigned long *advertising, ++ bool permit_pause_to_mac) ++{ ++ struct fman_mac *dtsec = pcs_to_dtsec(pcs); ++ ++ return phylink_mii_c22_pcs_config(dtsec->tbidev, mode, interface, ++ advertising); ++} ++ ++static void dtsec_pcs_an_restart(struct phylink_pcs *pcs) ++{ ++ struct fman_mac *dtsec = pcs_to_dtsec(pcs); ++ ++ phylink_mii_c22_pcs_an_restart(dtsec->tbidev); ++} ++ ++static const struct phylink_pcs_ops dtsec_pcs_ops = { ++ .pcs_get_state = dtsec_pcs_get_state, ++ .pcs_config = dtsec_pcs_config, ++ .pcs_an_restart = dtsec_pcs_an_restart, ++}; ++ + static void graceful_start(struct fman_mac *dtsec) + { + struct dtsec_regs __iomem *regs = dtsec->regs; +@@ -854,36 +827,11 @@ static void graceful_stop(struct fman_ma + + static int dtsec_enable(struct fman_mac *dtsec) + { +- struct dtsec_regs __iomem *regs = dtsec->regs; +- u32 tmp; +- +- if (!is_init_done(dtsec->dtsec_drv_param)) +- return -EINVAL; +- +- /* Enable */ +- tmp = ioread32be(®s->maccfg1); +- tmp |= MACCFG1_RX_EN | MACCFG1_TX_EN; +- iowrite32be(tmp, ®s->maccfg1); +- +- /* Graceful start - clear the graceful Rx/Tx stop bit */ +- graceful_start(dtsec); +- + return 0; + } + + static void dtsec_disable(struct fman_mac *dtsec) + { +- struct dtsec_regs __iomem *regs = dtsec->regs; +- u32 tmp; +- +- WARN_ON_ONCE(!is_init_done(dtsec->dtsec_drv_param)); +- +- /* Graceful stop - Assert the graceful Rx/Tx stop bit */ +- graceful_stop(dtsec); +- +- tmp = ioread32be(®s->maccfg1); +- tmp &= ~(MACCFG1_RX_EN | MACCFG1_TX_EN); +- iowrite32be(tmp, ®s->maccfg1); + } + + static int dtsec_set_tx_pause_frames(struct fman_mac *dtsec, +@@ -894,11 +842,6 @@ static int dtsec_set_tx_pause_frames(str + struct dtsec_regs __iomem *regs = dtsec->regs; + u32 ptv = 0; + +- if (!is_init_done(dtsec->dtsec_drv_param)) +- return -EINVAL; +- +- graceful_stop(dtsec); +- + if (pause_time) { + /* FM_BAD_TX_TS_IN_B_2_B_ERRATA_DTSEC_A003 Errata workaround */ + if (dtsec->fm_rev_info.major == 2 && pause_time <= 320) { +@@ -919,8 +862,6 @@ static int dtsec_set_tx_pause_frames(str + iowrite32be(ioread32be(®s->maccfg1) & ~MACCFG1_TX_FLOW, + ®s->maccfg1); + +- graceful_start(dtsec); +- + return 0; + } + +@@ -929,11 +870,6 @@ static int dtsec_accept_rx_pause_frames( + struct dtsec_regs __iomem *regs = dtsec->regs; + u32 tmp; + +- if (!is_init_done(dtsec->dtsec_drv_param)) +- return -EINVAL; +- +- graceful_stop(dtsec); +- + tmp = ioread32be(®s->maccfg1); + if (en) + tmp |= MACCFG1_RX_FLOW; +@@ -941,17 +877,125 @@ static int dtsec_accept_rx_pause_frames( + tmp &= ~MACCFG1_RX_FLOW; + iowrite32be(tmp, ®s->maccfg1); + ++ return 0; ++} ++ ++static struct phylink_pcs *dtsec_select_pcs(struct phylink_config *config, ++ phy_interface_t iface) ++{ ++ struct fman_mac *dtsec = fman_config_to_mac(config)->fman_mac; ++ ++ switch (iface) { ++ case PHY_INTERFACE_MODE_SGMII: ++ case PHY_INTERFACE_MODE_1000BASEX: ++ case PHY_INTERFACE_MODE_2500BASEX: ++ return &dtsec->pcs; ++ default: ++ return NULL; ++ } ++} ++ ++static void dtsec_mac_config(struct phylink_config *config, unsigned int mode, ++ const struct phylink_link_state *state) ++{ ++ struct mac_device *mac_dev = fman_config_to_mac(config); ++ struct dtsec_regs __iomem *regs = mac_dev->fman_mac->regs; ++ u32 tmp; ++ ++ switch (state->interface) { ++ case PHY_INTERFACE_MODE_RMII: ++ tmp = DTSEC_ECNTRL_RMM; ++ break; ++ case PHY_INTERFACE_MODE_RGMII: ++ case PHY_INTERFACE_MODE_RGMII_ID: ++ case PHY_INTERFACE_MODE_RGMII_RXID: ++ case PHY_INTERFACE_MODE_RGMII_TXID: ++ tmp = DTSEC_ECNTRL_GMIIM | DTSEC_ECNTRL_RPM; ++ break; ++ case PHY_INTERFACE_MODE_SGMII: ++ case PHY_INTERFACE_MODE_1000BASEX: ++ case PHY_INTERFACE_MODE_2500BASEX: ++ tmp = DTSEC_ECNTRL_TBIM | DTSEC_ECNTRL_SGMIIM; ++ break; ++ default: ++ dev_warn(mac_dev->dev, "cannot configure dTSEC for %s\n", ++ phy_modes(state->interface)); ++ return; ++ } ++ ++ iowrite32be(tmp, ®s->ecntrl); ++} ++ ++static void dtsec_link_up(struct phylink_config *config, struct phy_device *phy, ++ unsigned int mode, phy_interface_t interface, ++ int speed, int duplex, bool tx_pause, bool rx_pause) ++{ ++ struct mac_device *mac_dev = fman_config_to_mac(config); ++ struct fman_mac *dtsec = mac_dev->fman_mac; ++ struct dtsec_regs __iomem *regs = dtsec->regs; ++ u16 pause_time = tx_pause ? FSL_FM_PAUSE_TIME_ENABLE : ++ FSL_FM_PAUSE_TIME_DISABLE; ++ u32 tmp; ++ ++ dtsec_set_tx_pause_frames(dtsec, 0, pause_time, 0); ++ dtsec_accept_rx_pause_frames(dtsec, rx_pause); ++ ++ tmp = ioread32be(®s->ecntrl); ++ if (speed == SPEED_100) ++ tmp |= DTSEC_ECNTRL_R100M; ++ else ++ tmp &= ~DTSEC_ECNTRL_R100M; ++ iowrite32be(tmp, ®s->ecntrl); ++ ++ tmp = ioread32be(®s->maccfg2); ++ tmp &= ~(MACCFG2_NIBBLE_MODE | MACCFG2_BYTE_MODE | MACCFG2_FULL_DUPLEX); ++ if (speed >= SPEED_1000) ++ tmp |= MACCFG2_BYTE_MODE; ++ else ++ tmp |= MACCFG2_NIBBLE_MODE; ++ ++ if (duplex == DUPLEX_FULL) ++ tmp |= MACCFG2_FULL_DUPLEX; ++ ++ iowrite32be(tmp, ®s->maccfg2); ++ ++ mac_dev->update_speed(mac_dev, speed); ++ ++ /* Enable */ ++ tmp = ioread32be(®s->maccfg1); ++ tmp |= MACCFG1_RX_EN | MACCFG1_TX_EN; ++ iowrite32be(tmp, ®s->maccfg1); ++ ++ /* Graceful start - clear the graceful Rx/Tx stop bit */ + graceful_start(dtsec); ++} + +- return 0; ++static void dtsec_link_down(struct phylink_config *config, unsigned int mode, ++ phy_interface_t interface) ++{ ++ struct fman_mac *dtsec = fman_config_to_mac(config)->fman_mac; ++ struct dtsec_regs __iomem *regs = dtsec->regs; ++ u32 tmp; ++ ++ /* Graceful stop - Assert the graceful Rx/Tx stop bit */ ++ graceful_stop(dtsec); ++ ++ tmp = ioread32be(®s->maccfg1); ++ tmp &= ~(MACCFG1_RX_EN | MACCFG1_TX_EN); ++ iowrite32be(tmp, ®s->maccfg1); + } + ++static const struct phylink_mac_ops dtsec_mac_ops = { ++ .validate = phylink_generic_validate, ++ .mac_select_pcs = dtsec_select_pcs, ++ .mac_config = dtsec_mac_config, ++ .mac_link_up = dtsec_link_up, ++ .mac_link_down = dtsec_link_down, ++}; ++ + static int dtsec_modify_mac_address(struct fman_mac *dtsec, + const enet_addr_t *enet_addr) + { +- if (!is_init_done(dtsec->dtsec_drv_param)) +- return -EINVAL; +- + graceful_stop(dtsec); + + /* Initialize MAC Station Address registers (1 & 2) +@@ -975,9 +1019,6 @@ static int dtsec_add_hash_mac_address(st + u32 crc = 0xFFFFFFFF; + bool mcast, ghtx; + +- if (!is_init_done(dtsec->dtsec_drv_param)) +- return -EINVAL; +- + addr = ENET_ADDR_TO_UINT64(*eth_addr); + + ghtx = (bool)((ioread32be(®s->rctrl) & RCTRL_GHTX) ? true : false); +@@ -1037,9 +1078,6 @@ static int dtsec_set_allmulti(struct fma + u32 tmp; + struct dtsec_regs __iomem *regs = dtsec->regs; + +- if (!is_init_done(dtsec->dtsec_drv_param)) +- return -EINVAL; +- + tmp = ioread32be(®s->rctrl); + if (enable) + tmp |= RCTRL_MPROM; +@@ -1056,9 +1094,6 @@ static int dtsec_set_tstamp(struct fman_ + struct dtsec_regs __iomem *regs = dtsec->regs; + u32 rctrl, tctrl; + +- if (!is_init_done(dtsec->dtsec_drv_param)) +- return -EINVAL; +- + rctrl = ioread32be(®s->rctrl); + tctrl = ioread32be(®s->tctrl); + +@@ -1087,9 +1122,6 @@ static int dtsec_del_hash_mac_address(st + u32 crc = 0xFFFFFFFF; + bool mcast, ghtx; + +- if (!is_init_done(dtsec->dtsec_drv_param)) +- return -EINVAL; +- + addr = ENET_ADDR_TO_UINT64(*eth_addr); + + ghtx = (bool)((ioread32be(®s->rctrl) & RCTRL_GHTX) ? true : false); +@@ -1153,9 +1185,6 @@ static int dtsec_set_promiscuous(struct + struct dtsec_regs __iomem *regs = dtsec->regs; + u32 tmp; + +- if (!is_init_done(dtsec->dtsec_drv_param)) +- return -EINVAL; +- + /* Set unicast promiscuous */ + tmp = ioread32be(®s->rctrl); + if (new_val) +@@ -1177,90 +1206,12 @@ static int dtsec_set_promiscuous(struct + return 0; + } + +-static int dtsec_adjust_link(struct fman_mac *dtsec, u16 speed) +-{ +- struct dtsec_regs __iomem *regs = dtsec->regs; +- u32 tmp; +- +- if (!is_init_done(dtsec->dtsec_drv_param)) +- return -EINVAL; +- +- graceful_stop(dtsec); +- +- tmp = ioread32be(®s->maccfg2); +- +- /* Full Duplex */ +- tmp |= MACCFG2_FULL_DUPLEX; +- +- tmp &= ~(MACCFG2_NIBBLE_MODE | MACCFG2_BYTE_MODE); +- if (speed < SPEED_1000) +- tmp |= MACCFG2_NIBBLE_MODE; +- else if (speed == SPEED_1000) +- tmp |= MACCFG2_BYTE_MODE; +- iowrite32be(tmp, ®s->maccfg2); +- +- tmp = ioread32be(®s->ecntrl); +- if (speed == SPEED_100) +- tmp |= DTSEC_ECNTRL_R100M; +- else +- tmp &= ~DTSEC_ECNTRL_R100M; +- iowrite32be(tmp, ®s->ecntrl); +- +- graceful_start(dtsec); +- +- return 0; +-} +- +-static int dtsec_restart_autoneg(struct fman_mac *dtsec) +-{ +- u16 tmp_reg16; +- +- if (!is_init_done(dtsec->dtsec_drv_param)) +- return -EINVAL; +- +- tmp_reg16 = phy_read(dtsec->tbiphy, MII_BMCR); +- +- tmp_reg16 &= ~(BMCR_SPEED100 | BMCR_SPEED1000); +- tmp_reg16 |= (BMCR_ANENABLE | BMCR_ANRESTART | +- BMCR_FULLDPLX | BMCR_SPEED1000); +- +- phy_write(dtsec->tbiphy, MII_BMCR, tmp_reg16); +- +- return 0; +-} +- +-static void adjust_link_dtsec(struct mac_device *mac_dev) +-{ +- struct phy_device *phy_dev = mac_dev->phy_dev; +- struct fman_mac *fman_mac; +- bool rx_pause, tx_pause; +- int err; +- +- fman_mac = mac_dev->fman_mac; +- if (!phy_dev->link) { +- dtsec_restart_autoneg(fman_mac); +- +- return; +- } +- +- dtsec_adjust_link(fman_mac, phy_dev->speed); +- mac_dev->update_speed(mac_dev, phy_dev->speed); +- fman_get_pause_cfg(mac_dev, &rx_pause, &tx_pause); +- err = fman_set_mac_active_pause(mac_dev, rx_pause, tx_pause); +- if (err < 0) +- dev_err(mac_dev->dev, "fman_set_mac_active_pause() = %d\n", +- err); +-} +- + static int dtsec_set_exception(struct fman_mac *dtsec, + enum fman_mac_exceptions exception, bool enable) + { + struct dtsec_regs __iomem *regs = dtsec->regs; + u32 bit_mask = 0; + +- if (!is_init_done(dtsec->dtsec_drv_param)) +- return -EINVAL; +- + if (exception != FM_MAC_EX_1G_1588_TS_RX_ERR) { + bit_mask = get_exception_flag(exception); + if (bit_mask) { +@@ -1310,12 +1261,9 @@ static int dtsec_init(struct fman_mac *d + { + struct dtsec_regs __iomem *regs = dtsec->regs; + struct dtsec_cfg *dtsec_drv_param; +- u16 max_frm_ln; ++ u16 max_frm_ln, tbicon; + int err; + +- if (is_init_done(dtsec->dtsec_drv_param)) +- return -EINVAL; +- + if (DEFAULT_RESET_ON_INIT && + (fman_reset_mac(dtsec->fm, dtsec->mac_id) != 0)) { + pr_err("Can't reset MAC!\n"); +@@ -1330,38 +1278,19 @@ static int dtsec_init(struct fman_mac *d + + err = init(dtsec->regs, dtsec_drv_param, dtsec->phy_if, + dtsec->max_speed, dtsec->addr, dtsec->exceptions, +- dtsec->tbiphy->mdio.addr); ++ dtsec->tbidev->addr); + if (err) { + free_init_resources(dtsec); + pr_err("DTSEC version doesn't support this i/f mode\n"); + return err; + } + +- if (dtsec->phy_if == PHY_INTERFACE_MODE_SGMII) { +- u16 tmp_reg16; +- +- /* Configure the TBI PHY Control Register */ +- tmp_reg16 = TBICON_CLK_SELECT | TBICON_SOFT_RESET; +- phy_write(dtsec->tbiphy, MII_TBICON, tmp_reg16); +- +- tmp_reg16 = TBICON_CLK_SELECT; +- phy_write(dtsec->tbiphy, MII_TBICON, tmp_reg16); +- +- tmp_reg16 = (BMCR_RESET | BMCR_ANENABLE | +- BMCR_FULLDPLX | BMCR_SPEED1000); +- phy_write(dtsec->tbiphy, MII_BMCR, tmp_reg16); +- +- if (dtsec->basex_if) +- tmp_reg16 = TBIANA_1000X; +- else +- tmp_reg16 = TBIANA_SGMII; +- phy_write(dtsec->tbiphy, MII_ADVERTISE, tmp_reg16); ++ /* Configure the TBI PHY Control Register */ ++ tbicon = TBICON_CLK_SELECT | TBICON_SOFT_RESET; ++ mdiodev_write(dtsec->tbidev, MII_TBICON, tbicon); + +- tmp_reg16 = (BMCR_ANENABLE | BMCR_ANRESTART | +- BMCR_FULLDPLX | BMCR_SPEED1000); +- +- phy_write(dtsec->tbiphy, MII_BMCR, tmp_reg16); +- } ++ tbicon = TBICON_CLK_SELECT; ++ mdiodev_write(dtsec->tbidev, MII_TBICON, tbicon); + + /* Max Frame Length */ + max_frm_ln = (u16)ioread32be(®s->maxfrm); +@@ -1406,6 +1335,8 @@ static int dtsec_free(struct fman_mac *d + + kfree(dtsec->dtsec_drv_param); + dtsec->dtsec_drv_param = NULL; ++ if (!IS_ERR_OR_NULL(dtsec->tbidev)) ++ put_device(&dtsec->tbidev->dev); + kfree(dtsec); + + return 0; +@@ -1434,7 +1365,6 @@ static struct fman_mac *dtsec_config(str + + dtsec->regs = mac_dev->vaddr; + dtsec->addr = ENET_ADDR_TO_UINT64(mac_dev->addr); +- dtsec->max_speed = params->max_speed; + dtsec->phy_if = mac_dev->phy_if; + dtsec->mac_id = params->mac_id; + dtsec->exceptions = (DTSEC_IMASK_BREN | +@@ -1457,7 +1387,6 @@ static struct fman_mac *dtsec_config(str + dtsec->en_tsu_err_exception = dtsec->dtsec_drv_param->ptp_exception_en; + + dtsec->fm = params->fm; +- dtsec->basex_if = params->basex_if; + + /* Save FMan revision */ + fman_get_revision(dtsec->fm, &dtsec->fm_rev_info); +@@ -1476,18 +1405,18 @@ int dtsec_initialization(struct mac_devi + int err; + struct fman_mac *dtsec; + struct device_node *phy_node; ++ unsigned long capabilities; ++ unsigned long *supported; + ++ mac_dev->phylink_ops = &dtsec_mac_ops; + mac_dev->set_promisc = dtsec_set_promiscuous; + mac_dev->change_addr = dtsec_modify_mac_address; + mac_dev->add_hash_mac_addr = dtsec_add_hash_mac_address; + mac_dev->remove_hash_mac_addr = dtsec_del_hash_mac_address; +- mac_dev->set_tx_pause = dtsec_set_tx_pause_frames; +- mac_dev->set_rx_pause = dtsec_accept_rx_pause_frames; + mac_dev->set_exception = dtsec_set_exception; + mac_dev->set_allmulti = dtsec_set_allmulti; + mac_dev->set_tstamp = dtsec_set_tstamp; + mac_dev->set_multi = fman_set_multi; +- mac_dev->adjust_link = adjust_link_dtsec; + mac_dev->enable = dtsec_enable; + mac_dev->disable = dtsec_disable; + +@@ -1502,19 +1431,56 @@ int dtsec_initialization(struct mac_devi + dtsec->dtsec_drv_param->tx_pad_crc = true; + + phy_node = of_parse_phandle(mac_node, "tbi-handle", 0); +- if (!phy_node) { +- pr_err("TBI PHY node is not available\n"); ++ if (!phy_node || of_device_is_available(phy_node)) { ++ of_node_put(phy_node); + err = -EINVAL; ++ dev_err_probe(mac_dev->dev, err, ++ "TBI PCS node is not available\n"); + goto _return_fm_mac_free; + } + +- dtsec->tbiphy = of_phy_find_device(phy_node); +- if (!dtsec->tbiphy) { +- pr_err("of_phy_find_device (TBI PHY) failed\n"); +- err = -EINVAL; ++ dtsec->tbidev = of_mdio_find_device(phy_node); ++ of_node_put(phy_node); ++ if (!dtsec->tbidev) { ++ err = -EPROBE_DEFER; ++ dev_err_probe(mac_dev->dev, err, ++ "could not find mdiodev for PCS\n"); + goto _return_fm_mac_free; + } +- put_device(&dtsec->tbiphy->mdio.dev); ++ dtsec->pcs.ops = &dtsec_pcs_ops; ++ dtsec->pcs.poll = true; ++ ++ supported = mac_dev->phylink_config.supported_interfaces; ++ ++ /* FIXME: Can we use DTSEC_ID2_INT_FULL_OFF to determine if these are ++ * supported? If not, we can determine support via the phy if SerDes ++ * support is added. ++ */ ++ if (mac_dev->phy_if == PHY_INTERFACE_MODE_SGMII || ++ mac_dev->phy_if == PHY_INTERFACE_MODE_1000BASEX) { ++ __set_bit(PHY_INTERFACE_MODE_SGMII, supported); ++ __set_bit(PHY_INTERFACE_MODE_1000BASEX, supported); ++ } else if (mac_dev->phy_if == PHY_INTERFACE_MODE_2500BASEX) { ++ __set_bit(PHY_INTERFACE_MODE_2500BASEX, supported); ++ } ++ ++ if (!(ioread32be(&dtsec->regs->tsec_id2) & DTSEC_ID2_INT_REDUCED_OFF)) { ++ phy_interface_set_rgmii(supported); ++ ++ /* DTSEC_ID2_INT_REDUCED_OFF indicates that the dTSEC supports ++ * RMII and RGMII. However, the only SoCs which support RMII ++ * are the P1017 and P1023. Avoid advertising this mode on ++ * other SoCs. This is a bit of a moot point, since there's no ++ * in-tree support for ethernet on these platforms... ++ */ ++ if (of_machine_is_compatible("fsl,P1023") || ++ of_machine_is_compatible("fsl,P1023RDB")) ++ __set_bit(PHY_INTERFACE_MODE_RMII, supported); ++ } ++ ++ capabilities = MAC_SYM_PAUSE | MAC_ASYM_PAUSE; ++ capabilities |= MAC_10 | MAC_100 | MAC_1000FD | MAC_2500FD; ++ mac_dev->phylink_config.mac_capabilities = capabilities; + + err = dtsec_init(dtsec); + if (err < 0) +--- a/drivers/net/ethernet/freescale/fman/fman_mac.h ++++ b/drivers/net/ethernet/freescale/fman/fman_mac.h +@@ -170,20 +170,10 @@ struct fman_mac_params { + * 0 - FM_MAX_NUM_OF_10G_MACS + */ + u8 mac_id; +- /* Note that the speed should indicate the maximum rate that +- * this MAC should support rather than the actual speed; +- */ +- u16 max_speed; + /* A handle to the FM object this port related to */ + void *fm; + fman_mac_exception_cb *event_cb; /* MDIO Events Callback Routine */ + fman_mac_exception_cb *exception_cb;/* Exception Callback Routine */ +- /* SGMII/QSGII interface with 1000BaseX auto-negotiation between MAC +- * and phy or backplane; Note: 1000BaseX auto-negotiation relates only +- * to interface between MAC and phy/backplane, SGMII phy can still +- * synchronize with far-end phy at 10Mbps, 100Mbps or 1000Mbps +- */ +- bool basex_if; + }; + + struct eth_hash_t { +--- a/drivers/net/ethernet/freescale/fman/fman_memac.c ++++ b/drivers/net/ethernet/freescale/fman/fman_memac.c +@@ -278,9 +278,6 @@ struct fman_mac { + struct memac_regs __iomem *regs; + /* MAC address of device */ + u64 addr; +- /* Ethernet physical interface */ +- phy_interface_t phy_if; +- u16 max_speed; + struct mac_device *dev_id; /* device cookie used by the exception cbs */ + fman_mac_exception_cb *exception_cb; + fman_mac_exception_cb *event_cb; +@@ -293,12 +290,12 @@ struct fman_mac { + struct memac_cfg *memac_drv_param; + void *fm; + struct fman_rev_info fm_rev_info; +- bool basex_if; + struct phy *serdes; + struct phylink_pcs *sgmii_pcs; + struct phylink_pcs *qsgmii_pcs; + struct phylink_pcs *xfi_pcs; + bool allmulti_enabled; ++ bool rgmii_no_half_duplex; + }; + + static void add_addr_in_paddr(struct memac_regs __iomem *regs, const u8 *adr, +@@ -356,7 +353,6 @@ static void set_exception(struct memac_r + } + + static int init(struct memac_regs __iomem *regs, struct memac_cfg *cfg, +- phy_interface_t phy_if, u16 speed, bool slow_10g_if, + u32 exceptions) + { + u32 tmp; +@@ -384,41 +380,6 @@ static int init(struct memac_regs __iome + iowrite32be((u32)cfg->pause_quanta, ®s->pause_quanta[0]); + iowrite32be((u32)0, ®s->pause_thresh[0]); + +- /* IF_MODE */ +- tmp = 0; +- switch (phy_if) { +- case PHY_INTERFACE_MODE_XGMII: +- tmp |= IF_MODE_10G; +- break; +- case PHY_INTERFACE_MODE_MII: +- tmp |= IF_MODE_MII; +- break; +- default: +- tmp |= IF_MODE_GMII; +- if (phy_if == PHY_INTERFACE_MODE_RGMII || +- phy_if == PHY_INTERFACE_MODE_RGMII_ID || +- phy_if == PHY_INTERFACE_MODE_RGMII_RXID || +- phy_if == PHY_INTERFACE_MODE_RGMII_TXID) +- tmp |= IF_MODE_RGMII | IF_MODE_RGMII_AUTO; +- } +- iowrite32be(tmp, ®s->if_mode); +- +- /* TX_FIFO_SECTIONS */ +- tmp = 0; +- if (phy_if == PHY_INTERFACE_MODE_XGMII) { +- if (slow_10g_if) { +- tmp |= (TX_FIFO_SECTIONS_TX_AVAIL_SLOW_10G | +- TX_FIFO_SECTIONS_TX_EMPTY_DEFAULT_10G); +- } else { +- tmp |= (TX_FIFO_SECTIONS_TX_AVAIL_10G | +- TX_FIFO_SECTIONS_TX_EMPTY_DEFAULT_10G); +- } +- } else { +- tmp |= (TX_FIFO_SECTIONS_TX_AVAIL_1G | +- TX_FIFO_SECTIONS_TX_EMPTY_DEFAULT_1G); +- } +- iowrite32be(tmp, ®s->tx_fifo_sections); +- + /* clear all pending events and set-up interrupts */ + iowrite32be(0xffffffff, ®s->ievent); + set_exception(regs, exceptions, true); +@@ -458,24 +419,6 @@ static u32 get_mac_addr_hash_code(u64 et + return xor_val; + } + +-static void setup_sgmii_internal(struct fman_mac *memac, +- struct phylink_pcs *pcs, +- struct fixed_phy_status *fixed_link) +-{ +- __ETHTOOL_DECLARE_LINK_MODE_MASK(advertising); +- phy_interface_t iface = memac->basex_if ? PHY_INTERFACE_MODE_1000BASEX : +- PHY_INTERFACE_MODE_SGMII; +- unsigned int mode = fixed_link ? MLO_AN_FIXED : MLO_AN_INBAND; +- +- linkmode_set_pause(advertising, true, true); +- pcs->ops->pcs_config(pcs, mode, iface, advertising, true); +- if (fixed_link) +- pcs->ops->pcs_link_up(pcs, mode, iface, fixed_link->speed, +- fixed_link->duplex); +- else +- pcs->ops->pcs_an_restart(pcs); +-} +- + static int check_init_parameters(struct fman_mac *memac) + { + if (!memac->exception_cb) { +@@ -581,41 +524,31 @@ static void free_init_resources(struct f + memac->unicast_addr_hash = NULL; + } + +-static bool is_init_done(struct memac_cfg *memac_drv_params) +-{ +- /* Checks if mEMAC driver parameters were initialized */ +- if (!memac_drv_params) +- return true; +- +- return false; +-} +- + static int memac_enable(struct fman_mac *memac) + { +- struct memac_regs __iomem *regs = memac->regs; +- u32 tmp; ++ int ret; + +- if (!is_init_done(memac->memac_drv_param)) +- return -EINVAL; ++ ret = phy_init(memac->serdes); ++ if (ret) { ++ dev_err(memac->dev_id->dev, ++ "could not initialize serdes: %pe\n", ERR_PTR(ret)); ++ return ret; ++ } + +- tmp = ioread32be(®s->command_config); +- tmp |= CMD_CFG_RX_EN | CMD_CFG_TX_EN; +- iowrite32be(tmp, ®s->command_config); ++ ret = phy_power_on(memac->serdes); ++ if (ret) { ++ dev_err(memac->dev_id->dev, ++ "could not power on serdes: %pe\n", ERR_PTR(ret)); ++ phy_exit(memac->serdes); ++ } + +- return 0; ++ return ret; + } + + static void memac_disable(struct fman_mac *memac) +- + { +- struct memac_regs __iomem *regs = memac->regs; +- u32 tmp; +- +- WARN_ON_ONCE(!is_init_done(memac->memac_drv_param)); +- +- tmp = ioread32be(®s->command_config); +- tmp &= ~(CMD_CFG_RX_EN | CMD_CFG_TX_EN); +- iowrite32be(tmp, ®s->command_config); ++ phy_power_off(memac->serdes); ++ phy_exit(memac->serdes); + } + + static int memac_set_promiscuous(struct fman_mac *memac, bool new_val) +@@ -623,9 +556,6 @@ static int memac_set_promiscuous(struct + struct memac_regs __iomem *regs = memac->regs; + u32 tmp; + +- if (!is_init_done(memac->memac_drv_param)) +- return -EINVAL; +- + tmp = ioread32be(®s->command_config); + if (new_val) + tmp |= CMD_CFG_PROMIS_EN; +@@ -637,73 +567,12 @@ static int memac_set_promiscuous(struct + return 0; + } + +-static int memac_adjust_link(struct fman_mac *memac, u16 speed) +-{ +- struct memac_regs __iomem *regs = memac->regs; +- u32 tmp; +- +- if (!is_init_done(memac->memac_drv_param)) +- return -EINVAL; +- +- tmp = ioread32be(®s->if_mode); +- +- /* Set full duplex */ +- tmp &= ~IF_MODE_HD; +- +- if (phy_interface_mode_is_rgmii(memac->phy_if)) { +- /* Configure RGMII in manual mode */ +- tmp &= ~IF_MODE_RGMII_AUTO; +- tmp &= ~IF_MODE_RGMII_SP_MASK; +- /* Full duplex */ +- tmp |= IF_MODE_RGMII_FD; +- +- switch (speed) { +- case SPEED_1000: +- tmp |= IF_MODE_RGMII_1000; +- break; +- case SPEED_100: +- tmp |= IF_MODE_RGMII_100; +- break; +- case SPEED_10: +- tmp |= IF_MODE_RGMII_10; +- break; +- default: +- break; +- } +- } +- +- iowrite32be(tmp, ®s->if_mode); +- +- return 0; +-} +- +-static void adjust_link_memac(struct mac_device *mac_dev) +-{ +- struct phy_device *phy_dev = mac_dev->phy_dev; +- struct fman_mac *fman_mac; +- bool rx_pause, tx_pause; +- int err; +- +- fman_mac = mac_dev->fman_mac; +- memac_adjust_link(fman_mac, phy_dev->speed); +- mac_dev->update_speed(mac_dev, phy_dev->speed); +- +- fman_get_pause_cfg(mac_dev, &rx_pause, &tx_pause); +- err = fman_set_mac_active_pause(mac_dev, rx_pause, tx_pause); +- if (err < 0) +- dev_err(mac_dev->dev, "fman_set_mac_active_pause() = %d\n", +- err); +-} +- + static int memac_set_tx_pause_frames(struct fman_mac *memac, u8 priority, + u16 pause_time, u16 thresh_time) + { + struct memac_regs __iomem *regs = memac->regs; + u32 tmp; + +- if (!is_init_done(memac->memac_drv_param)) +- return -EINVAL; +- + tmp = ioread32be(®s->tx_fifo_sections); + + GET_TX_EMPTY_DEFAULT_VALUE(tmp); +@@ -738,9 +607,6 @@ static int memac_accept_rx_pause_frames( + struct memac_regs __iomem *regs = memac->regs; + u32 tmp; + +- if (!is_init_done(memac->memac_drv_param)) +- return -EINVAL; +- + tmp = ioread32be(®s->command_config); + if (en) + tmp &= ~CMD_CFG_PAUSE_IGNORE; +@@ -752,12 +618,175 @@ static int memac_accept_rx_pause_frames( + return 0; + } + ++static void memac_validate(struct phylink_config *config, ++ unsigned long *supported, ++ struct phylink_link_state *state) ++{ ++ struct fman_mac *memac = fman_config_to_mac(config)->fman_mac; ++ unsigned long caps = config->mac_capabilities; ++ ++ if (phy_interface_mode_is_rgmii(state->interface) && ++ memac->rgmii_no_half_duplex) ++ caps &= ~(MAC_10HD | MAC_100HD); ++ ++ phylink_validate_mask_caps(supported, state, caps); ++} ++ ++/** ++ * memac_if_mode() - Convert an interface mode into an IF_MODE config ++ * @interface: A phy interface mode ++ * ++ * Return: A configuration word, suitable for programming into the lower bits ++ * of %IF_MODE. ++ */ ++static u32 memac_if_mode(phy_interface_t interface) ++{ ++ switch (interface) { ++ case PHY_INTERFACE_MODE_MII: ++ return IF_MODE_MII; ++ case PHY_INTERFACE_MODE_RGMII: ++ case PHY_INTERFACE_MODE_RGMII_ID: ++ case PHY_INTERFACE_MODE_RGMII_RXID: ++ case PHY_INTERFACE_MODE_RGMII_TXID: ++ return IF_MODE_GMII | IF_MODE_RGMII; ++ case PHY_INTERFACE_MODE_SGMII: ++ case PHY_INTERFACE_MODE_1000BASEX: ++ case PHY_INTERFACE_MODE_QSGMII: ++ return IF_MODE_GMII; ++ case PHY_INTERFACE_MODE_10GBASER: ++ return IF_MODE_10G; ++ default: ++ WARN_ON_ONCE(1); ++ return 0; ++ } ++} ++ ++static struct phylink_pcs *memac_select_pcs(struct phylink_config *config, ++ phy_interface_t iface) ++{ ++ struct fman_mac *memac = fman_config_to_mac(config)->fman_mac; ++ ++ switch (iface) { ++ case PHY_INTERFACE_MODE_SGMII: ++ case PHY_INTERFACE_MODE_1000BASEX: ++ return memac->sgmii_pcs; ++ case PHY_INTERFACE_MODE_QSGMII: ++ return memac->qsgmii_pcs; ++ case PHY_INTERFACE_MODE_10GBASER: ++ return memac->xfi_pcs; ++ default: ++ return NULL; ++ } ++} ++ ++static int memac_prepare(struct phylink_config *config, unsigned int mode, ++ phy_interface_t iface) ++{ ++ struct fman_mac *memac = fman_config_to_mac(config)->fman_mac; ++ ++ switch (iface) { ++ case PHY_INTERFACE_MODE_SGMII: ++ case PHY_INTERFACE_MODE_1000BASEX: ++ case PHY_INTERFACE_MODE_QSGMII: ++ case PHY_INTERFACE_MODE_10GBASER: ++ return phy_set_mode_ext(memac->serdes, PHY_MODE_ETHERNET, ++ iface); ++ default: ++ return 0; ++ } ++} ++ ++static void memac_mac_config(struct phylink_config *config, unsigned int mode, ++ const struct phylink_link_state *state) ++{ ++ struct mac_device *mac_dev = fman_config_to_mac(config); ++ struct memac_regs __iomem *regs = mac_dev->fman_mac->regs; ++ u32 tmp = ioread32be(®s->if_mode); ++ ++ tmp &= ~(IF_MODE_MASK | IF_MODE_RGMII); ++ tmp |= memac_if_mode(state->interface); ++ if (phylink_autoneg_inband(mode)) ++ tmp |= IF_MODE_RGMII_AUTO; ++ iowrite32be(tmp, ®s->if_mode); ++} ++ ++static void memac_link_up(struct phylink_config *config, struct phy_device *phy, ++ unsigned int mode, phy_interface_t interface, ++ int speed, int duplex, bool tx_pause, bool rx_pause) ++{ ++ struct mac_device *mac_dev = fman_config_to_mac(config); ++ struct fman_mac *memac = mac_dev->fman_mac; ++ struct memac_regs __iomem *regs = memac->regs; ++ u32 tmp = memac_if_mode(interface); ++ u16 pause_time = tx_pause ? FSL_FM_PAUSE_TIME_ENABLE : ++ FSL_FM_PAUSE_TIME_DISABLE; ++ ++ memac_set_tx_pause_frames(memac, 0, pause_time, 0); ++ memac_accept_rx_pause_frames(memac, rx_pause); ++ ++ if (duplex == DUPLEX_HALF) ++ tmp |= IF_MODE_HD; ++ ++ switch (speed) { ++ case SPEED_1000: ++ tmp |= IF_MODE_RGMII_1000; ++ break; ++ case SPEED_100: ++ tmp |= IF_MODE_RGMII_100; ++ break; ++ case SPEED_10: ++ tmp |= IF_MODE_RGMII_10; ++ break; ++ } ++ iowrite32be(tmp, ®s->if_mode); ++ ++ /* TODO: EEE? */ ++ ++ if (speed == SPEED_10000) { ++ if (memac->fm_rev_info.major == 6 && ++ memac->fm_rev_info.minor == 4) ++ tmp = TX_FIFO_SECTIONS_TX_AVAIL_SLOW_10G; ++ else ++ tmp = TX_FIFO_SECTIONS_TX_AVAIL_10G; ++ tmp |= TX_FIFO_SECTIONS_TX_EMPTY_DEFAULT_10G; ++ } else { ++ tmp = TX_FIFO_SECTIONS_TX_AVAIL_1G | ++ TX_FIFO_SECTIONS_TX_EMPTY_DEFAULT_1G; ++ } ++ iowrite32be(tmp, ®s->tx_fifo_sections); ++ ++ mac_dev->update_speed(mac_dev, speed); ++ ++ tmp = ioread32be(®s->command_config); ++ tmp |= CMD_CFG_RX_EN | CMD_CFG_TX_EN; ++ iowrite32be(tmp, ®s->command_config); ++} ++ ++static void memac_link_down(struct phylink_config *config, unsigned int mode, ++ phy_interface_t interface) ++{ ++ struct fman_mac *memac = fman_config_to_mac(config)->fman_mac; ++ struct memac_regs __iomem *regs = memac->regs; ++ u32 tmp; ++ ++ /* TODO: graceful */ ++ tmp = ioread32be(®s->command_config); ++ tmp &= ~(CMD_CFG_RX_EN | CMD_CFG_TX_EN); ++ iowrite32be(tmp, ®s->command_config); ++} ++ ++static const struct phylink_mac_ops memac_mac_ops = { ++ .validate = memac_validate, ++ .mac_select_pcs = memac_select_pcs, ++ .mac_prepare = memac_prepare, ++ .mac_config = memac_mac_config, ++ .mac_link_up = memac_link_up, ++ .mac_link_down = memac_link_down, ++}; ++ + static int memac_modify_mac_address(struct fman_mac *memac, + const enet_addr_t *enet_addr) + { +- if (!is_init_done(memac->memac_drv_param)) +- return -EINVAL; +- + add_addr_in_paddr(memac->regs, (const u8 *)(*enet_addr), 0); + + return 0; +@@ -771,9 +800,6 @@ static int memac_add_hash_mac_address(st + u32 hash; + u64 addr; + +- if (!is_init_done(memac->memac_drv_param)) +- return -EINVAL; +- + addr = ENET_ADDR_TO_UINT64(*eth_addr); + + if (!(addr & GROUP_ADDRESS)) { +@@ -802,9 +828,6 @@ static int memac_set_allmulti(struct fma + u32 entry; + struct memac_regs __iomem *regs = memac->regs; + +- if (!is_init_done(memac->memac_drv_param)) +- return -EINVAL; +- + if (enable) { + for (entry = 0; entry < HASH_TABLE_SIZE; entry++) + iowrite32be(entry | HASH_CTRL_MCAST_EN, +@@ -834,9 +857,6 @@ static int memac_del_hash_mac_address(st + u32 hash; + u64 addr; + +- if (!is_init_done(memac->memac_drv_param)) +- return -EINVAL; +- + addr = ENET_ADDR_TO_UINT64(*eth_addr); + + hash = get_mac_addr_hash_code(addr) & HASH_CTRL_ADDR_MASK; +@@ -864,9 +884,6 @@ static int memac_set_exception(struct fm + { + u32 bit_mask = 0; + +- if (!is_init_done(memac->memac_drv_param)) +- return -EINVAL; +- + bit_mask = get_exception_flag(exception); + if (bit_mask) { + if (enable) +@@ -886,23 +903,15 @@ static int memac_init(struct fman_mac *m + { + struct memac_cfg *memac_drv_param; + enet_addr_t eth_addr; +- bool slow_10g_if = false; +- struct fixed_phy_status *fixed_link = NULL; + int err; + u32 reg32 = 0; + +- if (is_init_done(memac->memac_drv_param)) +- return -EINVAL; +- + err = check_init_parameters(memac); + if (err) + return err; + + memac_drv_param = memac->memac_drv_param; + +- if (memac->fm_rev_info.major == 6 && memac->fm_rev_info.minor == 4) +- slow_10g_if = true; +- + /* First, reset the MAC if desired. */ + if (memac_drv_param->reset_on_init) { + err = reset(memac->regs); +@@ -918,10 +927,7 @@ static int memac_init(struct fman_mac *m + add_addr_in_paddr(memac->regs, (const u8 *)eth_addr, 0); + } + +- fixed_link = memac_drv_param->fixed_link; +- +- init(memac->regs, memac->memac_drv_param, memac->phy_if, +- memac->max_speed, slow_10g_if, memac->exceptions); ++ init(memac->regs, memac->memac_drv_param, memac->exceptions); + + /* FM_RX_FIFO_CORRUPT_ERRATA_10GMAC_A006320 errata workaround + * Exists only in FMan 6.0 and 6.3. +@@ -937,11 +943,6 @@ static int memac_init(struct fman_mac *m + iowrite32be(reg32, &memac->regs->command_config); + } + +- if (memac->phy_if == PHY_INTERFACE_MODE_SGMII) +- setup_sgmii_internal(memac, memac->sgmii_pcs, fixed_link); +- else if (memac->phy_if == PHY_INTERFACE_MODE_QSGMII) +- setup_sgmii_internal(memac, memac->qsgmii_pcs, fixed_link); +- + /* Max Frame Length */ + err = fman_set_mac_max_frame(memac->fm, memac->mac_id, + memac_drv_param->max_frame_length); +@@ -970,9 +971,6 @@ static int memac_init(struct fman_mac *m + fman_register_intr(memac->fm, FMAN_MOD_MAC, memac->mac_id, + FMAN_INTR_TYPE_NORMAL, memac_exception, memac); + +- kfree(memac_drv_param); +- memac->memac_drv_param = NULL; +- + return 0; + } + +@@ -995,7 +993,6 @@ static int memac_free(struct fman_mac *m + pcs_put(memac->sgmii_pcs); + pcs_put(memac->qsgmii_pcs); + pcs_put(memac->xfi_pcs); +- + kfree(memac->memac_drv_param); + kfree(memac); + +@@ -1028,8 +1025,6 @@ static struct fman_mac *memac_config(str + memac->addr = ENET_ADDR_TO_UINT64(mac_dev->addr); + + memac->regs = mac_dev->vaddr; +- memac->max_speed = params->max_speed; +- memac->phy_if = mac_dev->phy_if; + memac->mac_id = params->mac_id; + memac->exceptions = (MEMAC_IMASK_TSECC_ER | MEMAC_IMASK_TECC_ER | + MEMAC_IMASK_RECC_ER | MEMAC_IMASK_MGI); +@@ -1037,7 +1032,6 @@ static struct fman_mac *memac_config(str + memac->event_cb = params->event_cb; + memac->dev_id = mac_dev; + memac->fm = params->fm; +- memac->basex_if = params->basex_if; + + /* Save FMan revision */ + fman_get_revision(memac->fm, &memac->fm_rev_info); +@@ -1064,37 +1058,44 @@ static struct phylink_pcs *memac_pcs_cre + return pcs; + } + ++static bool memac_supports(struct mac_device *mac_dev, phy_interface_t iface) ++{ ++ /* If there's no serdes device, assume that it's been configured for ++ * whatever the default interface mode is. ++ */ ++ if (!mac_dev->fman_mac->serdes) ++ return mac_dev->phy_if == iface; ++ /* Otherwise, ask the serdes */ ++ return !phy_validate(mac_dev->fman_mac->serdes, PHY_MODE_ETHERNET, ++ iface, NULL); ++} ++ + int memac_initialization(struct mac_device *mac_dev, + struct device_node *mac_node, + struct fman_mac_params *params) + { + int err; ++ struct device_node *fixed; + struct phylink_pcs *pcs; +- struct fixed_phy_status *fixed_link; + struct fman_mac *memac; ++ unsigned long capabilities; ++ unsigned long *supported; + ++ mac_dev->phylink_ops = &memac_mac_ops; + mac_dev->set_promisc = memac_set_promiscuous; + mac_dev->change_addr = memac_modify_mac_address; + mac_dev->add_hash_mac_addr = memac_add_hash_mac_address; + mac_dev->remove_hash_mac_addr = memac_del_hash_mac_address; +- mac_dev->set_tx_pause = memac_set_tx_pause_frames; +- mac_dev->set_rx_pause = memac_accept_rx_pause_frames; + mac_dev->set_exception = memac_set_exception; + mac_dev->set_allmulti = memac_set_allmulti; + mac_dev->set_tstamp = memac_set_tstamp; + mac_dev->set_multi = fman_set_multi; +- mac_dev->adjust_link = adjust_link_memac; + mac_dev->enable = memac_enable; + mac_dev->disable = memac_disable; + +- if (params->max_speed == SPEED_10000) +- mac_dev->phy_if = PHY_INTERFACE_MODE_XGMII; +- + mac_dev->fman_mac = memac_config(mac_dev, params); +- if (!mac_dev->fman_mac) { +- err = -EINVAL; +- goto _return; +- } ++ if (!mac_dev->fman_mac) ++ return -EINVAL; + + memac = mac_dev->fman_mac; + memac->memac_drv_param->max_frame_length = fman_get_max_frm(); +@@ -1136,9 +1137,9 @@ int memac_initialization(struct mac_devi + else + pcs = memac_pcs_create(mac_node, err); + +- if (!pcs) { +- dev_err(mac_dev->dev, "missing pcs\n"); +- err = -ENOENT; ++ if (IS_ERR(pcs)) { ++ err = PTR_ERR(pcs); ++ dev_err_probe(mac_dev->dev, err, "missing pcs\n"); + goto _return_fm_mac_free; + } + +@@ -1159,84 +1160,100 @@ int memac_initialization(struct mac_devi + } else if (IS_ERR(memac->serdes)) { + dev_err_probe(mac_dev->dev, err, "could not get serdes\n"); + goto _return_fm_mac_free; +- } else { +- err = phy_init(memac->serdes); +- if (err) { +- dev_err_probe(mac_dev->dev, err, +- "could not initialize serdes\n"); +- goto _return_fm_mac_free; +- } +- +- err = phy_power_on(memac->serdes); +- if (err) { +- dev_err_probe(mac_dev->dev, err, +- "could not power on serdes\n"); +- goto _return_phy_exit; +- } +- +- if (memac->phy_if == PHY_INTERFACE_MODE_SGMII || +- memac->phy_if == PHY_INTERFACE_MODE_1000BASEX || +- memac->phy_if == PHY_INTERFACE_MODE_2500BASEX || +- memac->phy_if == PHY_INTERFACE_MODE_QSGMII || +- memac->phy_if == PHY_INTERFACE_MODE_XGMII) { +- err = phy_set_mode_ext(memac->serdes, PHY_MODE_ETHERNET, +- memac->phy_if); +- if (err) { +- dev_err_probe(mac_dev->dev, err, +- "could not set serdes mode to %s\n", +- phy_modes(memac->phy_if)); +- goto _return_phy_power_off; +- } +- } + } + +- if (!mac_dev->phy_node && of_phy_is_fixed_link(mac_node)) { +- struct phy_device *phy; +- +- err = of_phy_register_fixed_link(mac_node); +- if (err) +- goto _return_phy_power_off; +- +- fixed_link = kzalloc(sizeof(*fixed_link), GFP_KERNEL); +- if (!fixed_link) { +- err = -ENOMEM; +- goto _return_phy_power_off; +- } ++ /* The internal connection to the serdes is XGMII, but this isn't ++ * really correct for the phy mode (which is the external connection). ++ * However, this is how all older device trees say that they want ++ * 10GBASE-R (aka XFI), so just convert it for them. ++ */ ++ if (mac_dev->phy_if == PHY_INTERFACE_MODE_XGMII) ++ mac_dev->phy_if = PHY_INTERFACE_MODE_10GBASER; + +- mac_dev->phy_node = of_node_get(mac_node); +- phy = of_phy_find_device(mac_dev->phy_node); +- if (!phy) { +- err = -EINVAL; +- of_node_put(mac_dev->phy_node); +- goto _return_fixed_link_free; +- } ++ /* TODO: The following interface modes are supported by (some) hardware ++ * but not by this driver: ++ * - 1000BASE-KX ++ * - 10GBASE-KR ++ * - XAUI/HiGig ++ */ ++ supported = mac_dev->phylink_config.supported_interfaces; + +- fixed_link->link = phy->link; +- fixed_link->speed = phy->speed; +- fixed_link->duplex = phy->duplex; +- fixed_link->pause = phy->pause; +- fixed_link->asym_pause = phy->asym_pause; ++ /* Note that half duplex is only supported on 10/100M interfaces. */ + +- put_device(&phy->mdio.dev); +- memac->memac_drv_param->fixed_link = fixed_link; ++ if (memac->sgmii_pcs && ++ (memac_supports(mac_dev, PHY_INTERFACE_MODE_SGMII) || ++ memac_supports(mac_dev, PHY_INTERFACE_MODE_1000BASEX))) { ++ __set_bit(PHY_INTERFACE_MODE_SGMII, supported); ++ __set_bit(PHY_INTERFACE_MODE_1000BASEX, supported); ++ } ++ ++ if (memac->sgmii_pcs && ++ memac_supports(mac_dev, PHY_INTERFACE_MODE_2500BASEX)) ++ __set_bit(PHY_INTERFACE_MODE_2500BASEX, supported); ++ ++ if (memac->qsgmii_pcs && ++ memac_supports(mac_dev, PHY_INTERFACE_MODE_QSGMII)) ++ __set_bit(PHY_INTERFACE_MODE_QSGMII, supported); ++ else if (mac_dev->phy_if == PHY_INTERFACE_MODE_QSGMII) ++ dev_warn(mac_dev->dev, "no QSGMII pcs specified\n"); ++ ++ if (memac->xfi_pcs && ++ memac_supports(mac_dev, PHY_INTERFACE_MODE_10GBASER)) { ++ __set_bit(PHY_INTERFACE_MODE_10GBASER, supported); ++ } else { ++ /* From what I can tell, no 10g macs support RGMII. */ ++ phy_interface_set_rgmii(supported); ++ __set_bit(PHY_INTERFACE_MODE_MII, supported); + } + ++ capabilities = MAC_SYM_PAUSE | MAC_ASYM_PAUSE | MAC_10 | MAC_100; ++ capabilities |= MAC_1000FD | MAC_2500FD | MAC_10000FD; ++ ++ /* These SoCs don't support half duplex at all; there's no different ++ * FMan version or compatible, so we just have to check the machine ++ * compatible instead ++ */ ++ if (of_machine_is_compatible("fsl,ls1043a") || ++ of_machine_is_compatible("fsl,ls1046a") || ++ of_machine_is_compatible("fsl,B4QDS")) ++ capabilities &= ~(MAC_10HD | MAC_100HD); ++ ++ mac_dev->phylink_config.mac_capabilities = capabilities; ++ ++ /* The T2080 and T4240 don't support half duplex RGMII. There is no ++ * other way to identify these SoCs, so just use the machine ++ * compatible. ++ */ ++ if (of_machine_is_compatible("fsl,T2080QDS") || ++ of_machine_is_compatible("fsl,T2080RDB") || ++ of_machine_is_compatible("fsl,T2081QDS") || ++ of_machine_is_compatible("fsl,T4240QDS") || ++ of_machine_is_compatible("fsl,T4240RDB")) ++ memac->rgmii_no_half_duplex = true; ++ ++ /* Most boards should use MLO_AN_INBAND, but existing boards don't have ++ * a managed property. Default to MLO_AN_INBAND if nothing else is ++ * specified. We need to be careful and not enable this if we have a ++ * fixed link or if we are using MII or RGMII, since those ++ * configurations modes don't use in-band autonegotiation. ++ */ ++ fixed = of_get_child_by_name(mac_node, "fixed-link"); ++ if (!fixed && !of_property_read_bool(mac_node, "fixed-link") && ++ !of_property_read_bool(mac_node, "managed") && ++ mac_dev->phy_if != PHY_INTERFACE_MODE_MII && ++ !phy_interface_mode_is_rgmii(mac_dev->phy_if)) ++ mac_dev->phylink_config.ovr_an_inband = true; ++ of_node_put(fixed); ++ + err = memac_init(mac_dev->fman_mac); + if (err < 0) +- goto _return_fixed_link_free; ++ goto _return_fm_mac_free; + + dev_info(mac_dev->dev, "FMan MEMAC\n"); + +- goto _return; ++ return 0; + +-_return_phy_power_off: +- phy_power_off(memac->serdes); +-_return_phy_exit: +- phy_exit(memac->serdes); +-_return_fixed_link_free: +- kfree(fixed_link); + _return_fm_mac_free: + memac_free(mac_dev->fman_mac); +-_return: + return err; + } +--- a/drivers/net/ethernet/freescale/fman/fman_tgec.c ++++ b/drivers/net/ethernet/freescale/fman/fman_tgec.c +@@ -13,6 +13,7 @@ + #include + #include + #include ++#include + + /* Transmit Inter-Packet Gap Length Register (TX_IPG_LENGTH) */ + #define TGEC_TX_IPG_LENGTH_MASK 0x000003ff +@@ -243,10 +244,6 @@ static int init(struct tgec_regs __iomem + + static int check_init_parameters(struct fman_mac *tgec) + { +- if (tgec->max_speed < SPEED_10000) { +- pr_err("10G MAC driver only support 10G speed\n"); +- return -EINVAL; +- } + if (!tgec->exception_cb) { + pr_err("uninitialized exception_cb\n"); + return -EINVAL; +@@ -384,40 +381,13 @@ static void free_init_resources(struct f + tgec->unicast_addr_hash = NULL; + } + +-static bool is_init_done(struct tgec_cfg *cfg) +-{ +- /* Checks if tGEC driver parameters were initialized */ +- if (!cfg) +- return true; +- +- return false; +-} +- + static int tgec_enable(struct fman_mac *tgec) + { +- struct tgec_regs __iomem *regs = tgec->regs; +- u32 tmp; +- +- if (!is_init_done(tgec->cfg)) +- return -EINVAL; +- +- tmp = ioread32be(®s->command_config); +- tmp |= CMD_CFG_RX_EN | CMD_CFG_TX_EN; +- iowrite32be(tmp, ®s->command_config); +- + return 0; + } + + static void tgec_disable(struct fman_mac *tgec) + { +- struct tgec_regs __iomem *regs = tgec->regs; +- u32 tmp; +- +- WARN_ON_ONCE(!is_init_done(tgec->cfg)); +- +- tmp = ioread32be(®s->command_config); +- tmp &= ~(CMD_CFG_RX_EN | CMD_CFG_TX_EN); +- iowrite32be(tmp, ®s->command_config); + } + + static int tgec_set_promiscuous(struct fman_mac *tgec, bool new_val) +@@ -425,9 +395,6 @@ static int tgec_set_promiscuous(struct f + struct tgec_regs __iomem *regs = tgec->regs; + u32 tmp; + +- if (!is_init_done(tgec->cfg)) +- return -EINVAL; +- + tmp = ioread32be(®s->command_config); + if (new_val) + tmp |= CMD_CFG_PROMIS_EN; +@@ -444,9 +411,6 @@ static int tgec_set_tx_pause_frames(stru + { + struct tgec_regs __iomem *regs = tgec->regs; + +- if (!is_init_done(tgec->cfg)) +- return -EINVAL; +- + iowrite32be((u32)pause_time, ®s->pause_quant); + + return 0; +@@ -457,9 +421,6 @@ static int tgec_accept_rx_pause_frames(s + struct tgec_regs __iomem *regs = tgec->regs; + u32 tmp; + +- if (!is_init_done(tgec->cfg)) +- return -EINVAL; +- + tmp = ioread32be(®s->command_config); + if (!en) + tmp |= CMD_CFG_PAUSE_IGNORE; +@@ -470,12 +431,53 @@ static int tgec_accept_rx_pause_frames(s + return 0; + } + ++static void tgec_mac_config(struct phylink_config *config, unsigned int mode, ++ const struct phylink_link_state *state) ++{ ++} ++ ++static void tgec_link_up(struct phylink_config *config, struct phy_device *phy, ++ unsigned int mode, phy_interface_t interface, ++ int speed, int duplex, bool tx_pause, bool rx_pause) ++{ ++ struct mac_device *mac_dev = fman_config_to_mac(config); ++ struct fman_mac *tgec = mac_dev->fman_mac; ++ struct tgec_regs __iomem *regs = tgec->regs; ++ u16 pause_time = tx_pause ? FSL_FM_PAUSE_TIME_ENABLE : ++ FSL_FM_PAUSE_TIME_DISABLE; ++ u32 tmp; ++ ++ tgec_set_tx_pause_frames(tgec, 0, pause_time, 0); ++ tgec_accept_rx_pause_frames(tgec, rx_pause); ++ mac_dev->update_speed(mac_dev, speed); ++ ++ tmp = ioread32be(®s->command_config); ++ tmp |= CMD_CFG_RX_EN | CMD_CFG_TX_EN; ++ iowrite32be(tmp, ®s->command_config); ++} ++ ++static void tgec_link_down(struct phylink_config *config, unsigned int mode, ++ phy_interface_t interface) ++{ ++ struct fman_mac *tgec = fman_config_to_mac(config)->fman_mac; ++ struct tgec_regs __iomem *regs = tgec->regs; ++ u32 tmp; ++ ++ tmp = ioread32be(®s->command_config); ++ tmp &= ~(CMD_CFG_RX_EN | CMD_CFG_TX_EN); ++ iowrite32be(tmp, ®s->command_config); ++} ++ ++static const struct phylink_mac_ops tgec_mac_ops = { ++ .validate = phylink_generic_validate, ++ .mac_config = tgec_mac_config, ++ .mac_link_up = tgec_link_up, ++ .mac_link_down = tgec_link_down, ++}; ++ + static int tgec_modify_mac_address(struct fman_mac *tgec, + const enet_addr_t *p_enet_addr) + { +- if (!is_init_done(tgec->cfg)) +- return -EINVAL; +- + tgec->addr = ENET_ADDR_TO_UINT64(*p_enet_addr); + set_mac_address(tgec->regs, (const u8 *)(*p_enet_addr)); + +@@ -490,9 +492,6 @@ static int tgec_add_hash_mac_address(str + u32 crc = 0xFFFFFFFF, hash; + u64 addr; + +- if (!is_init_done(tgec->cfg)) +- return -EINVAL; +- + addr = ENET_ADDR_TO_UINT64(*eth_addr); + + if (!(addr & GROUP_ADDRESS)) { +@@ -525,9 +524,6 @@ static int tgec_set_allmulti(struct fman + u32 entry; + struct tgec_regs __iomem *regs = tgec->regs; + +- if (!is_init_done(tgec->cfg)) +- return -EINVAL; +- + if (enable) { + for (entry = 0; entry < TGEC_HASH_TABLE_SIZE; entry++) + iowrite32be(entry | TGEC_HASH_MCAST_EN, +@@ -548,9 +544,6 @@ static int tgec_set_tstamp(struct fman_m + struct tgec_regs __iomem *regs = tgec->regs; + u32 tmp; + +- if (!is_init_done(tgec->cfg)) +- return -EINVAL; +- + tmp = ioread32be(®s->command_config); + + if (enable) +@@ -572,9 +565,6 @@ static int tgec_del_hash_mac_address(str + u32 crc = 0xFFFFFFFF, hash; + u64 addr; + +- if (!is_init_done(tgec->cfg)) +- return -EINVAL; +- + addr = ((*(u64 *)eth_addr) >> 16); + + /* CRC calculation */ +@@ -601,22 +591,12 @@ static int tgec_del_hash_mac_address(str + return 0; + } + +-static void tgec_adjust_link(struct mac_device *mac_dev) +-{ +- struct phy_device *phy_dev = mac_dev->phy_dev; +- +- mac_dev->update_speed(mac_dev, phy_dev->speed); +-} +- + static int tgec_set_exception(struct fman_mac *tgec, + enum fman_mac_exceptions exception, bool enable) + { + struct tgec_regs __iomem *regs = tgec->regs; + u32 bit_mask = 0; + +- if (!is_init_done(tgec->cfg)) +- return -EINVAL; +- + bit_mask = get_exception_flag(exception); + if (bit_mask) { + if (enable) +@@ -641,9 +621,6 @@ static int tgec_init(struct fman_mac *tg + enet_addr_t eth_addr; + int err; + +- if (is_init_done(tgec->cfg)) +- return -EINVAL; +- + if (DEFAULT_RESET_ON_INIT && + (fman_reset_mac(tgec->fm, tgec->mac_id) != 0)) { + pr_err("Can't reset MAC!\n"); +@@ -753,7 +730,6 @@ static struct fman_mac *tgec_config(stru + + tgec->regs = mac_dev->vaddr; + tgec->addr = ENET_ADDR_TO_UINT64(mac_dev->addr); +- tgec->max_speed = params->max_speed; + tgec->mac_id = params->mac_id; + tgec->exceptions = (TGEC_IMASK_MDIO_SCAN_EVENT | + TGEC_IMASK_REM_FAULT | +@@ -788,17 +764,15 @@ int tgec_initialization(struct mac_devic + int err; + struct fman_mac *tgec; + ++ mac_dev->phylink_ops = &tgec_mac_ops; + mac_dev->set_promisc = tgec_set_promiscuous; + mac_dev->change_addr = tgec_modify_mac_address; + mac_dev->add_hash_mac_addr = tgec_add_hash_mac_address; + mac_dev->remove_hash_mac_addr = tgec_del_hash_mac_address; +- mac_dev->set_tx_pause = tgec_set_tx_pause_frames; +- mac_dev->set_rx_pause = tgec_accept_rx_pause_frames; + mac_dev->set_exception = tgec_set_exception; + mac_dev->set_allmulti = tgec_set_allmulti; + mac_dev->set_tstamp = tgec_set_tstamp; + mac_dev->set_multi = fman_set_multi; +- mac_dev->adjust_link = tgec_adjust_link; + mac_dev->enable = tgec_enable; + mac_dev->disable = tgec_disable; + +@@ -808,6 +782,19 @@ int tgec_initialization(struct mac_devic + goto _return; + } + ++ /* The internal connection to the serdes is XGMII, but this isn't ++ * really correct for the phy mode (which is the external connection). ++ * However, this is how all older device trees say that they want ++ * XAUI, so just convert it for them. ++ */ ++ if (mac_dev->phy_if == PHY_INTERFACE_MODE_XGMII) ++ mac_dev->phy_if = PHY_INTERFACE_MODE_XAUI; ++ ++ __set_bit(PHY_INTERFACE_MODE_XAUI, ++ mac_dev->phylink_config.supported_interfaces); ++ mac_dev->phylink_config.mac_capabilities = ++ MAC_SYM_PAUSE | MAC_ASYM_PAUSE | MAC_10000FD; ++ + tgec = mac_dev->fman_mac; + tgec->cfg->max_frame_length = fman_get_max_frm(); + err = tgec_init(tgec); +--- a/drivers/net/ethernet/freescale/fman/mac.c ++++ b/drivers/net/ethernet/freescale/fman/mac.c +@@ -15,6 +15,7 @@ + #include + #include + #include ++#include + #include + #include + +@@ -93,130 +94,8 @@ int fman_set_multi(struct net_device *ne + return 0; + } + +-/** +- * fman_set_mac_active_pause +- * @mac_dev: A pointer to the MAC device +- * @rx: Pause frame setting for RX +- * @tx: Pause frame setting for TX +- * +- * Set the MAC RX/TX PAUSE frames settings +- * +- * Avoid redundant calls to FMD, if the MAC driver already contains the desired +- * active PAUSE settings. Otherwise, the new active settings should be reflected +- * in FMan. +- * +- * Return: 0 on success; Error code otherwise. +- */ +-int fman_set_mac_active_pause(struct mac_device *mac_dev, bool rx, bool tx) +-{ +- struct fman_mac *fman_mac = mac_dev->fman_mac; +- int err = 0; +- +- if (rx != mac_dev->rx_pause_active) { +- err = mac_dev->set_rx_pause(fman_mac, rx); +- if (likely(err == 0)) +- mac_dev->rx_pause_active = rx; +- } +- +- if (tx != mac_dev->tx_pause_active) { +- u16 pause_time = (tx ? FSL_FM_PAUSE_TIME_ENABLE : +- FSL_FM_PAUSE_TIME_DISABLE); +- +- err = mac_dev->set_tx_pause(fman_mac, 0, pause_time, 0); +- +- if (likely(err == 0)) +- mac_dev->tx_pause_active = tx; +- } +- +- return err; +-} +-EXPORT_SYMBOL(fman_set_mac_active_pause); +- +-/** +- * fman_get_pause_cfg +- * @mac_dev: A pointer to the MAC device +- * @rx_pause: Return value for RX setting +- * @tx_pause: Return value for TX setting +- * +- * Determine the MAC RX/TX PAUSE frames settings based on PHY +- * autonegotiation or values set by eththool. +- * +- * Return: Pointer to FMan device. +- */ +-void fman_get_pause_cfg(struct mac_device *mac_dev, bool *rx_pause, +- bool *tx_pause) +-{ +- struct phy_device *phy_dev = mac_dev->phy_dev; +- u16 lcl_adv, rmt_adv; +- u8 flowctrl; +- +- *rx_pause = *tx_pause = false; +- +- if (!phy_dev->duplex) +- return; +- +- /* If PAUSE autonegotiation is disabled, the TX/RX PAUSE settings +- * are those set by ethtool. +- */ +- if (!mac_dev->autoneg_pause) { +- *rx_pause = mac_dev->rx_pause_req; +- *tx_pause = mac_dev->tx_pause_req; +- return; +- } +- +- /* Else if PAUSE autonegotiation is enabled, the TX/RX PAUSE +- * settings depend on the result of the link negotiation. +- */ +- +- /* get local capabilities */ +- lcl_adv = linkmode_adv_to_lcl_adv_t(phy_dev->advertising); +- +- /* get link partner capabilities */ +- rmt_adv = 0; +- if (phy_dev->pause) +- rmt_adv |= LPA_PAUSE_CAP; +- if (phy_dev->asym_pause) +- rmt_adv |= LPA_PAUSE_ASYM; +- +- /* Calculate TX/RX settings based on local and peer advertised +- * symmetric/asymmetric PAUSE capabilities. +- */ +- flowctrl = mii_resolve_flowctrl_fdx(lcl_adv, rmt_adv); +- if (flowctrl & FLOW_CTRL_RX) +- *rx_pause = true; +- if (flowctrl & FLOW_CTRL_TX) +- *tx_pause = true; +-} +-EXPORT_SYMBOL(fman_get_pause_cfg); +- +-#define DTSEC_SUPPORTED \ +- (SUPPORTED_10baseT_Half \ +- | SUPPORTED_10baseT_Full \ +- | SUPPORTED_100baseT_Half \ +- | SUPPORTED_100baseT_Full \ +- | SUPPORTED_Autoneg \ +- | SUPPORTED_Pause \ +- | SUPPORTED_Asym_Pause \ +- | SUPPORTED_FIBRE \ +- | SUPPORTED_MII) +- + static DEFINE_MUTEX(eth_lock); + +-static const u16 phy2speed[] = { +- [PHY_INTERFACE_MODE_MII] = SPEED_100, +- [PHY_INTERFACE_MODE_GMII] = SPEED_1000, +- [PHY_INTERFACE_MODE_SGMII] = SPEED_1000, +- [PHY_INTERFACE_MODE_TBI] = SPEED_1000, +- [PHY_INTERFACE_MODE_RMII] = SPEED_100, +- [PHY_INTERFACE_MODE_RGMII] = SPEED_1000, +- [PHY_INTERFACE_MODE_RGMII_ID] = SPEED_1000, +- [PHY_INTERFACE_MODE_RGMII_RXID] = SPEED_1000, +- [PHY_INTERFACE_MODE_RGMII_TXID] = SPEED_1000, +- [PHY_INTERFACE_MODE_RTBI] = SPEED_1000, +- [PHY_INTERFACE_MODE_QSGMII] = SPEED_1000, +- [PHY_INTERFACE_MODE_XGMII] = SPEED_10000 +-}; +- + static struct platform_device *dpaa_eth_add_device(int fman_id, + struct mac_device *mac_dev) + { +@@ -263,8 +142,8 @@ no_mem: + } + + static const struct of_device_id mac_match[] = { +- { .compatible = "fsl,fman-dtsec", .data = dtsec_initialization }, +- { .compatible = "fsl,fman-xgec", .data = tgec_initialization }, ++ { .compatible = "fsl,fman-dtsec", .data = dtsec_initialization }, ++ { .compatible = "fsl,fman-xgec", .data = tgec_initialization }, + { .compatible = "fsl,fman-memac", .data = memac_initialization }, + {} + }; +@@ -295,6 +174,7 @@ static int mac_probe(struct platform_dev + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; ++ platform_set_drvdata(_of_dev, mac_dev); + + /* Save private information */ + mac_dev->priv = priv; +@@ -424,57 +304,21 @@ static int mac_probe(struct platform_dev + } + mac_dev->phy_if = phy_if; + +- priv->speed = phy2speed[mac_dev->phy_if]; +- params.max_speed = priv->speed; +- mac_dev->if_support = DTSEC_SUPPORTED; +- /* We don't support half-duplex in SGMII mode */ +- if (mac_dev->phy_if == PHY_INTERFACE_MODE_SGMII) +- mac_dev->if_support &= ~(SUPPORTED_10baseT_Half | +- SUPPORTED_100baseT_Half); +- +- /* Gigabit support (no half-duplex) */ +- if (params.max_speed == 1000) +- mac_dev->if_support |= SUPPORTED_1000baseT_Full; +- +- /* The 10G interface only supports one mode */ +- if (mac_dev->phy_if == PHY_INTERFACE_MODE_XGMII) +- mac_dev->if_support = SUPPORTED_10000baseT_Full; +- +- /* Get the rest of the PHY information */ +- mac_dev->phy_node = of_parse_phandle(mac_node, "phy-handle", 0); +- +- params.basex_if = false; + params.mac_id = priv->cell_index; + params.fm = (void *)priv->fman; + params.exception_cb = mac_exception; + params.event_cb = mac_exception; + + err = init(mac_dev, mac_node, ¶ms); +- if (err < 0) { +- dev_err(dev, "mac_dev->init() = %d\n", err); +- of_node_put(mac_dev->phy_node); +- return err; +- } +- +- /* pause frame autonegotiation enabled */ +- mac_dev->autoneg_pause = true; +- +- /* By intializing the values to false, force FMD to enable PAUSE frames +- * on RX and TX +- */ +- mac_dev->rx_pause_req = true; +- mac_dev->tx_pause_req = true; +- mac_dev->rx_pause_active = false; +- mac_dev->tx_pause_active = false; +- err = fman_set_mac_active_pause(mac_dev, true, true); + if (err < 0) +- dev_err(dev, "fman_set_mac_active_pause() = %d\n", err); ++ return err; + + if (!is_zero_ether_addr(mac_dev->addr)) + dev_info(dev, "FMan MAC address: %pM\n", mac_dev->addr); + + priv->eth_dev = dpaa_eth_add_device(fman_id, mac_dev); + if (IS_ERR(priv->eth_dev)) { ++ err = PTR_ERR(priv->eth_dev); + dev_err(dev, "failed to add Ethernet platform device for MAC %d\n", + priv->cell_index); + priv->eth_dev = NULL; +--- a/drivers/net/ethernet/freescale/fman/mac.h ++++ b/drivers/net/ethernet/freescale/fman/mac.h +@@ -9,6 +9,7 @@ + #include + #include + #include ++#include + #include + + #include "fman_port.h" +@@ -24,32 +25,22 @@ struct mac_device { + struct resource *res; + u8 addr[ETH_ALEN]; + struct fman_port *port[2]; +- u32 if_support; +- struct phy_device *phy_dev; ++ struct phylink *phylink; ++ struct phylink_config phylink_config; + phy_interface_t phy_if; +- struct device_node *phy_node; +- struct net_device *net_dev; + +- bool autoneg_pause; +- bool rx_pause_req; +- bool tx_pause_req; +- bool rx_pause_active; +- bool tx_pause_active; + bool promisc; + bool allmulti; + ++ const struct phylink_mac_ops *phylink_ops; + int (*enable)(struct fman_mac *mac_dev); + void (*disable)(struct fman_mac *mac_dev); +- void (*adjust_link)(struct mac_device *mac_dev); + int (*set_promisc)(struct fman_mac *mac_dev, bool enable); + int (*change_addr)(struct fman_mac *mac_dev, const enet_addr_t *enet_addr); + int (*set_allmulti)(struct fman_mac *mac_dev, bool enable); + int (*set_tstamp)(struct fman_mac *mac_dev, bool enable); + int (*set_multi)(struct net_device *net_dev, + struct mac_device *mac_dev); +- int (*set_rx_pause)(struct fman_mac *mac_dev, bool en); +- int (*set_tx_pause)(struct fman_mac *mac_dev, u8 priority, +- u16 pause_time, u16 thresh_time); + int (*set_exception)(struct fman_mac *mac_dev, + enum fman_mac_exceptions exception, bool enable); + int (*add_hash_mac_addr)(struct fman_mac *mac_dev, +@@ -63,6 +54,12 @@ struct mac_device { + struct mac_priv_s *priv; + }; + ++static inline struct mac_device ++*fman_config_to_mac(struct phylink_config *config) ++{ ++ return container_of(config, struct mac_device, phylink_config); ++} ++ + struct dpaa_eth_data { + struct mac_device *mac_dev; + int mac_hw_id; diff --git a/target/linux/generic/backport-6.1/715-04-v6.2-net-phylink-provide-phylink_validate_mask_caps-helpe.patch b/target/linux/generic/backport-6.1/715-04-v6.2-net-phylink-provide-phylink_validate_mask_caps-helpe.patch new file mode 100644 index 000000000..06c348b1c --- /dev/null +++ b/target/linux/generic/backport-6.1/715-04-v6.2-net-phylink-provide-phylink_validate_mask_caps-helpe.patch @@ -0,0 +1,93 @@ +From bf4de031052fe7c5309e8956c342d4e5ce79038e Mon Sep 17 00:00:00 2001 +From: "Russell King (Oracle)" +Date: Mon, 17 Oct 2022 16:22:35 -0400 +Subject: [PATCH 04/21] net: phylink: provide phylink_validate_mask_caps() + helper + +Provide a helper that restricts the link modes according to the +phylink capabilities. + +Signed-off-by: Russell King (Oracle) +[rebased on net-next/master and added documentation] +Signed-off-by: Sean Anderson +Signed-off-by: David S. Miller +--- + drivers/net/phy/phylink.c | 41 +++++++++++++++++++++++++++------------ + include/linux/phylink.h | 3 +++ + 2 files changed, 32 insertions(+), 12 deletions(-) + +--- a/drivers/net/phy/phylink.c ++++ b/drivers/net/phy/phylink.c +@@ -564,31 +564,48 @@ unsigned long phylink_get_capabilities(p + EXPORT_SYMBOL_GPL(phylink_get_capabilities); + + /** +- * phylink_generic_validate() - generic validate() callback implementation +- * @config: a pointer to a &struct phylink_config. ++ * phylink_validate_mask_caps() - Restrict link modes based on caps + * @supported: ethtool bitmask for supported link modes. +- * @state: a pointer to a &struct phylink_link_state. ++ * @state: an (optional) pointer to a &struct phylink_link_state. ++ * @mac_capabilities: bitmask of MAC capabilities + * +- * Generic implementation of the validate() callback that MAC drivers can +- * use when they pass the range of supported interfaces and MAC capabilities. +- * This makes use of phylink_get_linkmodes(). ++ * Calculate the supported link modes based on @mac_capabilities, and restrict ++ * @supported and @state based on that. Use this function if your capabiliies ++ * aren't constant, such as if they vary depending on the interface. + */ +-void phylink_generic_validate(struct phylink_config *config, +- unsigned long *supported, +- struct phylink_link_state *state) ++void phylink_validate_mask_caps(unsigned long *supported, ++ struct phylink_link_state *state, ++ unsigned long mac_capabilities) + { + __ETHTOOL_DECLARE_LINK_MODE_MASK(mask) = { 0, }; + unsigned long caps; + + phylink_set_port_modes(mask); + phylink_set(mask, Autoneg); +- caps = phylink_get_capabilities(state->interface, +- config->mac_capabilities, ++ caps = phylink_get_capabilities(state->interface, mac_capabilities, + state->rate_matching); + phylink_caps_to_linkmodes(mask, caps); + + linkmode_and(supported, supported, mask); +- linkmode_and(state->advertising, state->advertising, mask); ++ if (state) ++ linkmode_and(state->advertising, state->advertising, mask); ++} ++EXPORT_SYMBOL_GPL(phylink_validate_mask_caps); ++ ++/** ++ * phylink_generic_validate() - generic validate() callback implementation ++ * @config: a pointer to a &struct phylink_config. ++ * @supported: ethtool bitmask for supported link modes. ++ * @state: a pointer to a &struct phylink_link_state. ++ * ++ * Generic implementation of the validate() callback that MAC drivers can ++ * use when they pass the range of supported interfaces and MAC capabilities. ++ */ ++void phylink_generic_validate(struct phylink_config *config, ++ unsigned long *supported, ++ struct phylink_link_state *state) ++{ ++ phylink_validate_mask_caps(supported, state, config->mac_capabilities); + } + EXPORT_SYMBOL_GPL(phylink_generic_validate); + +--- a/include/linux/phylink.h ++++ b/include/linux/phylink.h +@@ -558,6 +558,9 @@ void phylink_caps_to_linkmodes(unsigned + unsigned long phylink_get_capabilities(phy_interface_t interface, + unsigned long mac_capabilities, + int rate_matching); ++void phylink_validate_mask_caps(unsigned long *supported, ++ struct phylink_link_state *state, ++ unsigned long caps); + void phylink_generic_validate(struct phylink_config *config, + unsigned long *supported, + struct phylink_link_state *state); diff --git a/target/linux/generic/backport-6.1/715-05-v6.2-phylink-require-valid-state-argument-to-phylink_vali.patch b/target/linux/generic/backport-6.1/715-05-v6.2-phylink-require-valid-state-argument-to-phylink_vali.patch new file mode 100644 index 000000000..e3a1dda68 --- /dev/null +++ b/target/linux/generic/backport-6.1/715-05-v6.2-phylink-require-valid-state-argument-to-phylink_vali.patch @@ -0,0 +1,39 @@ +From 2bf7e4a68c42eed909f3c29582e1fb85cb157e35 Mon Sep 17 00:00:00 2001 +From: Jakub Kicinski +Date: Tue, 25 Oct 2022 11:51:26 -0700 +Subject: [PATCH 05/21] phylink: require valid state argument to + phylink_validate_mask_caps() + +state is deferenced earlier in the function, the NULL check +is pointless. Since we don't have any crash reports presumably +it's safe to assume state is not NULL. + +Fixes: f392a1846489 ("net: phylink: provide phylink_validate_mask_caps() helper") +Reviewed-by: Sean Anderson +Link: https://lore.kernel.org/r/20221025185126.1720553-1-kuba@kernel.org +Signed-off-by: Jakub Kicinski +--- + drivers/net/phy/phylink.c | 5 ++--- + 1 file changed, 2 insertions(+), 3 deletions(-) + +--- a/drivers/net/phy/phylink.c ++++ b/drivers/net/phy/phylink.c +@@ -566,7 +566,7 @@ EXPORT_SYMBOL_GPL(phylink_get_capabiliti + /** + * phylink_validate_mask_caps() - Restrict link modes based on caps + * @supported: ethtool bitmask for supported link modes. +- * @state: an (optional) pointer to a &struct phylink_link_state. ++ * @state: pointer to a &struct phylink_link_state. + * @mac_capabilities: bitmask of MAC capabilities + * + * Calculate the supported link modes based on @mac_capabilities, and restrict +@@ -587,8 +587,7 @@ void phylink_validate_mask_caps(unsigned + phylink_caps_to_linkmodes(mask, caps); + + linkmode_and(supported, supported, mask); +- if (state) +- linkmode_and(state->advertising, state->advertising, mask); ++ linkmode_and(state->advertising, state->advertising, mask); + } + EXPORT_SYMBOL_GPL(phylink_validate_mask_caps); + diff --git a/target/linux/generic/backport-6.1/700-v6.2-net-phylink-add-phylink_get_link_timer_ns-helper.patch b/target/linux/generic/backport-6.1/715-06-v6.2-net-phylink-add-phylink_get_link_timer_ns-helper.patch similarity index 87% rename from target/linux/generic/backport-6.1/700-v6.2-net-phylink-add-phylink_get_link_timer_ns-helper.patch rename to target/linux/generic/backport-6.1/715-06-v6.2-net-phylink-add-phylink_get_link_timer_ns-helper.patch index 81c14a055..c217ed87b 100644 --- a/target/linux/generic/backport-6.1/700-v6.2-net-phylink-add-phylink_get_link_timer_ns-helper.patch +++ b/target/linux/generic/backport-6.1/715-06-v6.2-net-phylink-add-phylink_get_link_timer_ns-helper.patch @@ -1,7 +1,7 @@ -From 9c5a170677c3c8facc83e931a57f4c99c0511ae0 Mon Sep 17 00:00:00 2001 +From f8fc363bf0c023e4736a0328174b4a24b44ab23a Mon Sep 17 00:00:00 2001 From: "Russell King (Oracle)" Date: Thu, 27 Oct 2022 14:10:37 +0100 -Subject: [PATCH] net: phylink: add phylink_get_link_timer_ns() helper +Subject: [PATCH 06/21] net: phylink: add phylink_get_link_timer_ns() helper Add a helper to convert the PHY interface mode to the required link timer setting as stated by the appropriate standard. Inappropriate @@ -15,7 +15,7 @@ Signed-off-by: Jakub Kicinski --- a/include/linux/phylink.h +++ b/include/linux/phylink.h -@@ -614,6 +614,30 @@ int phylink_speed_up(struct phylink *pl) +@@ -617,6 +617,30 @@ int phylink_speed_up(struct phylink *pl) void phylink_set_port_modes(unsigned long *bits); diff --git a/target/linux/generic/backport-6.1/715-07-v6.2-net-remove-explicit-phylink_generic_validate-referen.patch b/target/linux/generic/backport-6.1/715-07-v6.2-net-remove-explicit-phylink_generic_validate-referen.patch new file mode 100644 index 000000000..31fde5f18 --- /dev/null +++ b/target/linux/generic/backport-6.1/715-07-v6.2-net-remove-explicit-phylink_generic_validate-referen.patch @@ -0,0 +1,250 @@ +From b45b773a96b0e9e8d51e5d005485f4e376d6ce9a Mon Sep 17 00:00:00 2001 +From: "Russell King (Oracle)" +Date: Fri, 4 Nov 2022 17:13:01 +0000 +Subject: [PATCH 07/21] net: remove explicit phylink_generic_validate() + references + +Virtually all conventional network drivers are now converted to use +phylink_generic_validate() - only DSA drivers and fman_memac remain, +so lets remove the necessity for network drivers to explicitly set +this member, and default to phylink_generic_validate() when unset. +This is possible as .validate must currently be set. + +Any remaining instances that have not been addressed by this patch can +be fixed up later. + +Signed-off-by: Russell King (Oracle) +Reviewed-by: Vladimir Oltean +Link: https://lore.kernel.org/r/E1or0FZ-001tRa-DI@rmk-PC.armlinux.org.uk +Signed-off-by: Jakub Kicinski +--- + drivers/net/ethernet/altera/altera_tse_main.c | 1 - + drivers/net/ethernet/atheros/ag71xx.c | 1 - + drivers/net/ethernet/cadence/macb_main.c | 1 - + drivers/net/ethernet/freescale/dpaa2/dpaa2-mac.c | 1 - + drivers/net/ethernet/freescale/enetc/enetc_pf.c | 1 - + drivers/net/ethernet/freescale/fman/fman_dtsec.c | 1 - + drivers/net/ethernet/freescale/fman/fman_tgec.c | 1 - + drivers/net/ethernet/marvell/mvneta.c | 1 - + drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c | 1 - + drivers/net/ethernet/marvell/prestera/prestera_main.c | 1 - + drivers/net/ethernet/mediatek/mtk_eth_soc.c | 1 - + drivers/net/ethernet/microchip/lan966x/lan966x_phylink.c | 1 - + drivers/net/ethernet/microchip/sparx5/sparx5_phylink.c | 1 - + drivers/net/ethernet/mscc/ocelot_net.c | 1 - + drivers/net/ethernet/stmicro/stmmac/stmmac_main.c | 1 - + drivers/net/ethernet/ti/am65-cpsw-nuss.c | 1 - + drivers/net/ethernet/xilinx/xilinx_axienet_main.c | 1 - + drivers/net/phy/phylink.c | 5 ++++- + drivers/net/usb/asix_devices.c | 1 - + include/linux/phylink.h | 5 +++++ + 20 files changed, 9 insertions(+), 19 deletions(-) + +--- a/drivers/net/ethernet/altera/altera_tse_main.c ++++ b/drivers/net/ethernet/altera/altera_tse_main.c +@@ -1096,7 +1096,6 @@ static struct phylink_pcs *alt_tse_selec + } + + static const struct phylink_mac_ops alt_tse_phylink_ops = { +- .validate = phylink_generic_validate, + .mac_an_restart = alt_tse_mac_an_restart, + .mac_config = alt_tse_mac_config, + .mac_link_down = alt_tse_mac_link_down, +--- a/drivers/net/ethernet/atheros/ag71xx.c ++++ b/drivers/net/ethernet/atheros/ag71xx.c +@@ -1086,7 +1086,6 @@ static void ag71xx_mac_link_up(struct ph + } + + static const struct phylink_mac_ops ag71xx_phylink_mac_ops = { +- .validate = phylink_generic_validate, + .mac_config = ag71xx_mac_config, + .mac_link_down = ag71xx_mac_link_down, + .mac_link_up = ag71xx_mac_link_up, +--- a/drivers/net/ethernet/cadence/macb_main.c ++++ b/drivers/net/ethernet/cadence/macb_main.c +@@ -752,7 +752,6 @@ static struct phylink_pcs *macb_mac_sele + } + + static const struct phylink_mac_ops macb_phylink_ops = { +- .validate = phylink_generic_validate, + .mac_select_pcs = macb_mac_select_pcs, + .mac_config = macb_mac_config, + .mac_link_down = macb_mac_link_down, +--- a/drivers/net/ethernet/freescale/dpaa2/dpaa2-mac.c ++++ b/drivers/net/ethernet/freescale/dpaa2/dpaa2-mac.c +@@ -235,7 +235,6 @@ static void dpaa2_mac_link_down(struct p + } + + static const struct phylink_mac_ops dpaa2_mac_phylink_ops = { +- .validate = phylink_generic_validate, + .mac_select_pcs = dpaa2_mac_select_pcs, + .mac_config = dpaa2_mac_config, + .mac_link_up = dpaa2_mac_link_up, +--- a/drivers/net/ethernet/freescale/enetc/enetc_pf.c ++++ b/drivers/net/ethernet/freescale/enetc/enetc_pf.c +@@ -1111,7 +1111,6 @@ static void enetc_pl_mac_link_down(struc + } + + static const struct phylink_mac_ops enetc_mac_phylink_ops = { +- .validate = phylink_generic_validate, + .mac_select_pcs = enetc_pl_mac_select_pcs, + .mac_config = enetc_pl_mac_config, + .mac_link_up = enetc_pl_mac_link_up, +--- a/drivers/net/ethernet/freescale/fman/fman_dtsec.c ++++ b/drivers/net/ethernet/freescale/fman/fman_dtsec.c +@@ -986,7 +986,6 @@ static void dtsec_link_down(struct phyli + } + + static const struct phylink_mac_ops dtsec_mac_ops = { +- .validate = phylink_generic_validate, + .mac_select_pcs = dtsec_select_pcs, + .mac_config = dtsec_mac_config, + .mac_link_up = dtsec_link_up, +--- a/drivers/net/ethernet/freescale/fman/fman_tgec.c ++++ b/drivers/net/ethernet/freescale/fman/fman_tgec.c +@@ -469,7 +469,6 @@ static void tgec_link_down(struct phylin + } + + static const struct phylink_mac_ops tgec_mac_ops = { +- .validate = phylink_generic_validate, + .mac_config = tgec_mac_config, + .mac_link_up = tgec_link_up, + .mac_link_down = tgec_link_down, +--- a/drivers/net/ethernet/marvell/mvneta.c ++++ b/drivers/net/ethernet/marvell/mvneta.c +@@ -4228,7 +4228,6 @@ static void mvneta_mac_link_up(struct ph + } + + static const struct phylink_mac_ops mvneta_phylink_ops = { +- .validate = phylink_generic_validate, + .mac_select_pcs = mvneta_mac_select_pcs, + .mac_prepare = mvneta_mac_prepare, + .mac_config = mvneta_mac_config, +--- a/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c ++++ b/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c +@@ -6633,7 +6633,6 @@ static void mvpp2_mac_link_down(struct p + } + + static const struct phylink_mac_ops mvpp2_phylink_ops = { +- .validate = phylink_generic_validate, + .mac_select_pcs = mvpp2_select_pcs, + .mac_prepare = mvpp2_mac_prepare, + .mac_config = mvpp2_mac_config, +--- a/drivers/net/ethernet/marvell/prestera/prestera_main.c ++++ b/drivers/net/ethernet/marvell/prestera/prestera_main.c +@@ -360,7 +360,6 @@ static void prestera_pcs_an_restart(stru + } + + static const struct phylink_mac_ops prestera_mac_ops = { +- .validate = phylink_generic_validate, + .mac_select_pcs = prestera_mac_select_pcs, + .mac_config = prestera_mac_config, + .mac_link_down = prestera_mac_link_down, +--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c ++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c +@@ -653,7 +653,6 @@ static void mtk_mac_link_up(struct phyli + } + + static const struct phylink_mac_ops mtk_phylink_ops = { +- .validate = phylink_generic_validate, + .mac_select_pcs = mtk_mac_select_pcs, + .mac_pcs_get_state = mtk_mac_pcs_get_state, + .mac_config = mtk_mac_config, +--- a/drivers/net/ethernet/microchip/lan966x/lan966x_phylink.c ++++ b/drivers/net/ethernet/microchip/lan966x/lan966x_phylink.c +@@ -125,7 +125,6 @@ static void lan966x_pcs_aneg_restart(str + } + + const struct phylink_mac_ops lan966x_phylink_mac_ops = { +- .validate = phylink_generic_validate, + .mac_select_pcs = lan966x_phylink_mac_select, + .mac_config = lan966x_phylink_mac_config, + .mac_prepare = lan966x_phylink_mac_prepare, +--- a/drivers/net/ethernet/microchip/sparx5/sparx5_phylink.c ++++ b/drivers/net/ethernet/microchip/sparx5/sparx5_phylink.c +@@ -138,7 +138,6 @@ const struct phylink_pcs_ops sparx5_phyl + }; + + const struct phylink_mac_ops sparx5_phylink_mac_ops = { +- .validate = phylink_generic_validate, + .mac_select_pcs = sparx5_phylink_mac_select_pcs, + .mac_config = sparx5_phylink_mac_config, + .mac_link_down = sparx5_phylink_mac_link_down, +--- a/drivers/net/ethernet/mscc/ocelot_net.c ++++ b/drivers/net/ethernet/mscc/ocelot_net.c +@@ -1737,7 +1737,6 @@ static void vsc7514_phylink_mac_link_up( + } + + static const struct phylink_mac_ops ocelot_phylink_ops = { +- .validate = phylink_generic_validate, + .mac_config = vsc7514_phylink_mac_config, + .mac_link_down = vsc7514_phylink_mac_link_down, + .mac_link_up = vsc7514_phylink_mac_link_up, +--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c ++++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +@@ -1090,7 +1090,6 @@ static void stmmac_mac_link_up(struct ph + } + + static const struct phylink_mac_ops stmmac_phylink_mac_ops = { +- .validate = phylink_generic_validate, + .mac_select_pcs = stmmac_mac_select_pcs, + .mac_config = stmmac_mac_config, + .mac_link_down = stmmac_mac_link_down, +--- a/drivers/net/ethernet/ti/am65-cpsw-nuss.c ++++ b/drivers/net/ethernet/ti/am65-cpsw-nuss.c +@@ -1493,7 +1493,6 @@ static void am65_cpsw_nuss_mac_link_up(s + } + + static const struct phylink_mac_ops am65_cpsw_phylink_mac_ops = { +- .validate = phylink_generic_validate, + .mac_config = am65_cpsw_nuss_mac_config, + .mac_link_down = am65_cpsw_nuss_mac_link_down, + .mac_link_up = am65_cpsw_nuss_mac_link_up, +--- a/drivers/net/ethernet/xilinx/xilinx_axienet_main.c ++++ b/drivers/net/ethernet/xilinx/xilinx_axienet_main.c +@@ -1736,7 +1736,6 @@ static void axienet_mac_link_up(struct p + } + + static const struct phylink_mac_ops axienet_phylink_ops = { +- .validate = phylink_generic_validate, + .mac_select_pcs = axienet_mac_select_pcs, + .mac_config = axienet_mac_config, + .mac_link_down = axienet_mac_link_down, +--- a/drivers/net/phy/phylink.c ++++ b/drivers/net/phy/phylink.c +@@ -651,7 +651,10 @@ static int phylink_validate_mac_and_pcs( + } + + /* Then validate the link parameters with the MAC */ +- pl->mac_ops->validate(pl->config, supported, state); ++ if (pl->mac_ops->validate) ++ pl->mac_ops->validate(pl->config, supported, state); ++ else ++ phylink_generic_validate(pl->config, supported, state); + + return phylink_is_empty_linkmode(supported) ? -EINVAL : 0; + } +--- a/drivers/net/usb/asix_devices.c ++++ b/drivers/net/usb/asix_devices.c +@@ -787,7 +787,6 @@ static void ax88772_mac_link_up(struct p + } + + static const struct phylink_mac_ops ax88772_phylink_mac_ops = { +- .validate = phylink_generic_validate, + .mac_config = ax88772_mac_config, + .mac_link_down = ax88772_mac_link_down, + .mac_link_up = ax88772_mac_link_up, +--- a/include/linux/phylink.h ++++ b/include/linux/phylink.h +@@ -207,6 +207,11 @@ struct phylink_mac_ops { + * + * If the @state->interface mode is not supported, then the @supported + * mask must be cleared. ++ * ++ * This member is optional; if not set, the generic validator will be ++ * used making use of @config->mac_capabilities and ++ * @config->supported_interfaces to determine which link modes are ++ * supported. + */ + void validate(struct phylink_config *config, unsigned long *supported, + struct phylink_link_state *state); diff --git a/target/linux/generic/backport-6.1/715-08-net-sfp-make-sfp_bus_find_fwnode-take-a-const-fwnode.patch b/target/linux/generic/backport-6.1/715-08-net-sfp-make-sfp_bus_find_fwnode-take-a-const-fwnode.patch new file mode 100644 index 000000000..37d82b2cd --- /dev/null +++ b/target/linux/generic/backport-6.1/715-08-net-sfp-make-sfp_bus_find_fwnode-take-a-const-fwnode.patch @@ -0,0 +1,48 @@ +From a90ac762d345890b40d88a1385a34a2449c2d75e Mon Sep 17 00:00:00 2001 +From: "Russell King (Oracle)" +Date: Fri, 24 Mar 2023 09:23:42 +0000 +Subject: [PATCH] net: sfp: make sfp_bus_find_fwnode() take a const fwnode + +sfp_bus_find_fwnode() does not write to the fwnode, so let's make it +const. + +Signed-off-by: Russell King (Oracle) +Reviewed-by: Simon Horman +Signed-off-by: David S. Miller +--- + drivers/net/phy/sfp-bus.c | 2 +- + include/linux/sfp.h | 5 +++-- + 2 files changed, 4 insertions(+), 3 deletions(-) + +--- a/drivers/net/phy/sfp-bus.c ++++ b/drivers/net/phy/sfp-bus.c +@@ -603,7 +603,7 @@ static void sfp_upstream_clear(struct sf + * - %-ENOMEM if we failed to allocate the bus. + * - an error from the upstream's connect_phy() method. + */ +-struct sfp_bus *sfp_bus_find_fwnode(struct fwnode_handle *fwnode) ++struct sfp_bus *sfp_bus_find_fwnode(const struct fwnode_handle *fwnode) + { + struct fwnode_reference_args ref; + struct sfp_bus *bus; +--- a/include/linux/sfp.h ++++ b/include/linux/sfp.h +@@ -548,7 +548,7 @@ int sfp_get_module_eeprom_by_page(struct + void sfp_upstream_start(struct sfp_bus *bus); + void sfp_upstream_stop(struct sfp_bus *bus); + void sfp_bus_put(struct sfp_bus *bus); +-struct sfp_bus *sfp_bus_find_fwnode(struct fwnode_handle *fwnode); ++struct sfp_bus *sfp_bus_find_fwnode(const struct fwnode_handle *fwnode); + int sfp_bus_add_upstream(struct sfp_bus *bus, void *upstream, + const struct sfp_upstream_ops *ops); + void sfp_bus_del_upstream(struct sfp_bus *bus); +@@ -610,7 +610,8 @@ static inline void sfp_bus_put(struct sf + { + } + +-static inline struct sfp_bus *sfp_bus_find_fwnode(struct fwnode_handle *fwnode) ++static inline struct sfp_bus * ++sfp_bus_find_fwnode(const struct fwnode_handle *fwnode) + { + return NULL; + } diff --git a/target/linux/generic/backport-6.1/715-09-v6.4-net-pcs-lynx-don-t-print-an_enabled-in-pcs_get_state.patch b/target/linux/generic/backport-6.1/715-09-v6.4-net-pcs-lynx-don-t-print-an_enabled-in-pcs_get_state.patch new file mode 100644 index 000000000..290cb8d16 --- /dev/null +++ b/target/linux/generic/backport-6.1/715-09-v6.4-net-pcs-lynx-don-t-print-an_enabled-in-pcs_get_state.patch @@ -0,0 +1,31 @@ +From ecec0ebbc6381a5a375f1cf10c4858f24e91e2ef Mon Sep 17 00:00:00 2001 +From: "Russell King (Oracle)" +Date: Wed, 15 Mar 2023 14:46:49 +0000 +Subject: [PATCH] net: pcs: lynx: don't print an_enabled in pcs_get_state() + +an_enabled will be going away, and in any case, pcs_get_state() should +not be updating this member. Remove the print. + +Signed-off-by: Russell King (Oracle) +Reviewed-by: Steen Hegelund +Signed-off-by: David S. Miller +--- + drivers/net/pcs/pcs-lynx.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +--- a/drivers/net/pcs/pcs-lynx.c ++++ b/drivers/net/pcs/pcs-lynx.c +@@ -115,11 +115,11 @@ static void lynx_pcs_get_state(struct ph + } + + dev_dbg(&lynx->mdio->dev, +- "mode=%s/%s/%s link=%u an_enabled=%u an_complete=%u\n", ++ "mode=%s/%s/%s link=%u an_complete=%u\n", + phy_modes(state->interface), + phy_speed_to_str(state->speed), + phy_duplex_to_str(state->duplex), +- state->link, state->an_enabled, state->an_complete); ++ state->link, state->an_complete); + } + + static int lynx_pcs_config_giga(struct mdio_device *pcs, unsigned int mode, diff --git a/target/linux/generic/backport-6.1/715-10-v6.4-net-dpaa2-mac-use-Autoneg-bit-rather-than-an_enabled.patch b/target/linux/generic/backport-6.1/715-10-v6.4-net-dpaa2-mac-use-Autoneg-bit-rather-than-an_enabled.patch new file mode 100644 index 000000000..38ea26547 --- /dev/null +++ b/target/linux/generic/backport-6.1/715-10-v6.4-net-dpaa2-mac-use-Autoneg-bit-rather-than-an_enabled.patch @@ -0,0 +1,32 @@ +From 99d0f3a1095f4c938b1665025c29411edafe8a01 Mon Sep 17 00:00:00 2001 +From: "Russell King (Oracle)" +Date: Tue, 21 Mar 2023 15:58:44 +0000 +Subject: [PATCH] net: dpaa2-mac: use Autoneg bit rather than an_enabled + +The Autoneg bit in the advertising bitmap and state->an_enabled are +always identical. Thus, we will be removing state->an_enabled. + +Use the Autoneg bit in the advertising bitmap to indicate whether +autonegotiation should be used, rather than using the an_enabled +member which will be going away. This means we use the same condition +as phylink_mii_c22_pcs_config(). + +Signed-off-by: Russell King (Oracle) +Reviewed-by: Simon Horman +Signed-off-by: Jakub Kicinski +--- + drivers/net/ethernet/freescale/dpaa2/dpaa2-mac.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +--- a/drivers/net/ethernet/freescale/dpaa2/dpaa2-mac.c ++++ b/drivers/net/ethernet/freescale/dpaa2/dpaa2-mac.c +@@ -158,7 +158,8 @@ static void dpaa2_mac_config(struct phyl + struct dpmac_link_state *dpmac_state = &mac->state; + int err; + +- if (state->an_enabled) ++ if (linkmode_test_bit(ETHTOOL_LINK_MODE_Autoneg_BIT, ++ state->advertising)) + dpmac_state->options |= DPMAC_LINK_OPT_AUTONEG; + else + dpmac_state->options &= ~DPMAC_LINK_OPT_AUTONEG; diff --git a/target/linux/generic/backport-6.1/715-11-v6.4-net-phylink-support-validated-pause-and-autoneg-in-f.patch b/target/linux/generic/backport-6.1/715-11-v6.4-net-phylink-support-validated-pause-and-autoneg-in-f.patch new file mode 100644 index 000000000..cb9c411cf --- /dev/null +++ b/target/linux/generic/backport-6.1/715-11-v6.4-net-phylink-support-validated-pause-and-autoneg-in-f.patch @@ -0,0 +1,64 @@ +From 471c40bde606ec0b1ce8c616f7998739c7a783a6 Mon Sep 17 00:00:00 2001 +From: Ivan Bornyakov +Date: Fri, 10 Feb 2023 18:46:27 +0300 +Subject: [PATCH 10/21] net: phylink: support validated pause and autoneg in + fixed-link + +In fixed-link setup phylink_parse_fixedlink() unconditionally sets +Pause, Asym_Pause and Autoneg bits to "supported" bitmap, while MAC may +not support these. + +This leads to ethtool reporting: + + > Supported pause frame use: Symmetric Receive-only + > Supports auto-negotiation: Yes + +regardless of what is actually supported. + +Instead of unconditionally set Pause, Asym_Pause and Autoneg it is +sensible to set them according to validated "supported" bitmap, i.e. the +result of phylink_validate(). + +Signed-off-by: Ivan Bornyakov +Signed-off-by: David S. Miller +--- + drivers/net/phy/phylink.c | 17 ++++++++++++++--- + 1 file changed, 14 insertions(+), 3 deletions(-) + +--- a/drivers/net/phy/phylink.c ++++ b/drivers/net/phy/phylink.c +@@ -709,6 +709,7 @@ static int phylink_parse_fixedlink(struc + struct fwnode_handle *fwnode) + { + struct fwnode_handle *fixed_node; ++ bool pause, asym_pause, autoneg; + const struct phy_setting *s; + struct gpio_desc *desc; + u32 speed; +@@ -781,13 +782,23 @@ static int phylink_parse_fixedlink(struc + linkmode_copy(pl->link_config.advertising, pl->supported); + phylink_validate(pl, pl->supported, &pl->link_config); + ++ pause = phylink_test(pl->supported, Pause); ++ asym_pause = phylink_test(pl->supported, Asym_Pause); ++ autoneg = phylink_test(pl->supported, Autoneg); + s = phy_lookup_setting(pl->link_config.speed, pl->link_config.duplex, + pl->supported, true); + linkmode_zero(pl->supported); + phylink_set(pl->supported, MII); +- phylink_set(pl->supported, Pause); +- phylink_set(pl->supported, Asym_Pause); +- phylink_set(pl->supported, Autoneg); ++ ++ if (pause) ++ phylink_set(pl->supported, Pause); ++ ++ if (asym_pause) ++ phylink_set(pl->supported, Asym_Pause); ++ ++ if (autoneg) ++ phylink_set(pl->supported, Autoneg); ++ + if (s) { + __set_bit(s->bit, pl->supported); + __set_bit(s->bit, pl->link_config.lp_advertising); diff --git a/target/linux/generic/backport-6.1/715-12-v6.4-net-phylink-remove-an_enabled.patch b/target/linux/generic/backport-6.1/715-12-v6.4-net-phylink-remove-an_enabled.patch new file mode 100644 index 000000000..03b4f9d0c --- /dev/null +++ b/target/linux/generic/backport-6.1/715-12-v6.4-net-phylink-remove-an_enabled.patch @@ -0,0 +1,177 @@ +From 7211ffd70941933a7825a56cf480f07ee81c321c Mon Sep 17 00:00:00 2001 +From: "Russell King (Oracle)" +Date: Tue, 21 Mar 2023 15:58:54 +0000 +Subject: [PATCH 11/21] net: phylink: remove an_enabled + +The Autoneg bit in the advertising bitmap and state->an_enabled are +always identical. state->an_enabled is now no longer used by any +drivers, so lets kill this duplication. + +Signed-off-by: Russell King (Oracle) +Reviewed-by: Simon Horman +Signed-off-by: Jakub Kicinski +--- + drivers/net/phy/phylink.c | 37 +++++++++++++++++-------------------- + include/linux/phylink.h | 2 -- + 2 files changed, 17 insertions(+), 22 deletions(-) + +--- a/drivers/net/phy/phylink.c ++++ b/drivers/net/phy/phylink.c +@@ -841,7 +841,6 @@ static int phylink_parse_mode(struct phy + phylink_set(pl->supported, Autoneg); + phylink_set(pl->supported, Asym_Pause); + phylink_set(pl->supported, Pause); +- pl->link_config.an_enabled = true; + pl->cfg_link_an_mode = MLO_AN_INBAND; + + switch (pl->link_config.interface) { +@@ -944,9 +943,6 @@ static int phylink_parse_mode(struct phy + "failed to validate link configuration for in-band status\n"); + return -EINVAL; + } +- +- /* Check if MAC/PCS also supports Autoneg. */ +- pl->link_config.an_enabled = phylink_test(pl->supported, Autoneg); + } + + return 0; +@@ -956,7 +952,8 @@ static void phylink_apply_manual_flow(st + struct phylink_link_state *state) + { + /* If autoneg is disabled, pause AN is also disabled */ +- if (!state->an_enabled) ++ if (!linkmode_test_bit(ETHTOOL_LINK_MODE_Autoneg_BIT, ++ state->advertising)) + state->pause &= ~MLO_PAUSE_AN; + + /* Manual configuration of pause modes */ +@@ -996,21 +993,22 @@ static void phylink_mac_config(struct ph + const struct phylink_link_state *state) + { + phylink_dbg(pl, +- "%s: mode=%s/%s/%s/%s/%s adv=%*pb pause=%02x link=%u an=%u\n", ++ "%s: mode=%s/%s/%s/%s/%s adv=%*pb pause=%02x link=%u\n", + __func__, phylink_an_mode_str(pl->cur_link_an_mode), + phy_modes(state->interface), + phy_speed_to_str(state->speed), + phy_duplex_to_str(state->duplex), + phy_rate_matching_to_str(state->rate_matching), + __ETHTOOL_LINK_MODE_MASK_NBITS, state->advertising, +- state->pause, state->link, state->an_enabled); ++ state->pause, state->link); + + pl->mac_ops->mac_config(pl->config, pl->cur_link_an_mode, state); + } + + static void phylink_mac_pcs_an_restart(struct phylink *pl) + { +- if (pl->link_config.an_enabled && ++ if (linkmode_test_bit(ETHTOOL_LINK_MODE_Autoneg_BIT, ++ pl->link_config.advertising) && + phy_interface_mode_is_8023z(pl->link_config.interface) && + phylink_autoneg_inband(pl->cur_link_an_mode)) { + if (pl->pcs) +@@ -1137,9 +1135,9 @@ static void phylink_mac_pcs_get_state(st + linkmode_copy(state->advertising, pl->link_config.advertising); + linkmode_zero(state->lp_advertising); + state->interface = pl->link_config.interface; +- state->an_enabled = pl->link_config.an_enabled; + state->rate_matching = pl->link_config.rate_matching; +- if (state->an_enabled) { ++ if (linkmode_test_bit(ETHTOOL_LINK_MODE_Autoneg_BIT, ++ state->advertising)) { + state->speed = SPEED_UNKNOWN; + state->duplex = DUPLEX_UNKNOWN; + state->pause = MLO_PAUSE_NONE; +@@ -1531,7 +1529,6 @@ struct phylink *phylink_create(struct ph + pl->link_config.pause = MLO_PAUSE_AN; + pl->link_config.speed = SPEED_UNKNOWN; + pl->link_config.duplex = DUPLEX_UNKNOWN; +- pl->link_config.an_enabled = true; + pl->mac_ops = mac_ops; + __set_bit(PHYLINK_DISABLE_STOPPED, &pl->phylink_disable_state); + timer_setup(&pl->link_poll, phylink_fixed_poll, 0); +@@ -2155,8 +2152,9 @@ static void phylink_get_ksettings(const + kset->base.speed = state->speed; + kset->base.duplex = state->duplex; + } +- kset->base.autoneg = state->an_enabled ? AUTONEG_ENABLE : +- AUTONEG_DISABLE; ++ kset->base.autoneg = linkmode_test_bit(ETHTOOL_LINK_MODE_Autoneg_BIT, ++ state->advertising) ? ++ AUTONEG_ENABLE : AUTONEG_DISABLE; + } + + /** +@@ -2303,9 +2301,8 @@ int phylink_ethtool_ksettings_set(struct + /* We have ruled out the case with a PHY attached, and the + * fixed-link cases. All that is left are in-band links. + */ +- config.an_enabled = kset->base.autoneg == AUTONEG_ENABLE; + linkmode_mod_bit(ETHTOOL_LINK_MODE_Autoneg_BIT, config.advertising, +- config.an_enabled); ++ kset->base.autoneg == AUTONEG_ENABLE); + + /* If this link is with an SFP, ensure that changes to advertised modes + * also cause the associated interface to be selected such that the +@@ -2339,13 +2336,14 @@ int phylink_ethtool_ksettings_set(struct + } + + /* If autonegotiation is enabled, we must have an advertisement */ +- if (config.an_enabled && phylink_is_empty_linkmode(config.advertising)) ++ if (linkmode_test_bit(ETHTOOL_LINK_MODE_Autoneg_BIT, ++ config.advertising) && ++ phylink_is_empty_linkmode(config.advertising)) + return -EINVAL; + + mutex_lock(&pl->state_mutex); + pl->link_config.speed = config.speed; + pl->link_config.duplex = config.duplex; +- pl->link_config.an_enabled = config.an_enabled; + + if (pl->link_config.interface != config.interface) { + /* The interface changed, e.g. 1000base-X <-> 2500base-X */ +@@ -2951,7 +2949,6 @@ static int phylink_sfp_config_phy(struct + config.speed = SPEED_UNKNOWN; + config.duplex = DUPLEX_UNKNOWN; + config.pause = MLO_PAUSE_AN; +- config.an_enabled = pl->link_config.an_enabled; + + /* Ignore errors if we're expecting a PHY to attach later */ + ret = phylink_validate(pl, support, &config); +@@ -3020,7 +3017,6 @@ static int phylink_sfp_config_optical(st + config.speed = SPEED_UNKNOWN; + config.duplex = DUPLEX_UNKNOWN; + config.pause = MLO_PAUSE_AN; +- config.an_enabled = true; + + /* For all the interfaces that are supported, reduce the sfp_support + * mask to only those link modes that can be supported. +@@ -3354,7 +3350,8 @@ void phylink_mii_c22_pcs_decode_state(st + /* If there is no link or autonegotiation is disabled, the LP advertisement + * data is not meaningful, so don't go any further. + */ +- if (!state->link || !state->an_enabled) ++ if (!state->link || !linkmode_test_bit(ETHTOOL_LINK_MODE_Autoneg_BIT, ++ state->advertising)) + return; + + switch (state->interface) { +--- a/include/linux/phylink.h ++++ b/include/linux/phylink.h +@@ -93,7 +93,6 @@ static inline bool phylink_autoneg_inban + * the medium link mode (@speed and @duplex) and the speed/duplex of the phy + * interface mode (@interface) are different. + * @link: true if the link is up. +- * @an_enabled: true if autonegotiation is enabled/desired. + * @an_complete: true if autonegotiation has completed. + */ + struct phylink_link_state { +@@ -105,7 +104,6 @@ struct phylink_link_state { + int pause; + int rate_matching; + unsigned int link:1; +- unsigned int an_enabled:1; + unsigned int an_complete:1; + }; + diff --git a/target/linux/generic/backport-6.1/715-13-v6.5-net-phylink-constify-fwnode-arguments.patch b/target/linux/generic/backport-6.1/715-13-v6.5-net-phylink-constify-fwnode-arguments.patch new file mode 100644 index 000000000..c06a367b8 --- /dev/null +++ b/target/linux/generic/backport-6.1/715-13-v6.5-net-phylink-constify-fwnode-arguments.patch @@ -0,0 +1,88 @@ +From a3555d1f5c208f0a63eafee77381f68d304a0512 Mon Sep 17 00:00:00 2001 +From: "Russell King (Oracle)" +Date: Fri, 12 May 2023 17:58:37 +0100 +Subject: [PATCH 12/21] net: phylink: constify fwnode arguments + +Both phylink_create() and phylink_fwnode_phy_connect() do not modify +the fwnode argument that they are passed, so lets constify these. + +Reviewed-by: Simon Horman +Signed-off-by: Russell King (Oracle) +Signed-off-by: David S. Miller +--- + drivers/net/phy/phylink.c | 11 ++++++----- + include/linux/phylink.h | 9 +++++---- + 2 files changed, 11 insertions(+), 9 deletions(-) + +--- a/drivers/net/phy/phylink.c ++++ b/drivers/net/phy/phylink.c +@@ -706,7 +706,7 @@ static int phylink_validate(struct phyli + } + + static int phylink_parse_fixedlink(struct phylink *pl, +- struct fwnode_handle *fwnode) ++ const struct fwnode_handle *fwnode) + { + struct fwnode_handle *fixed_node; + bool pause, asym_pause, autoneg; +@@ -817,7 +817,8 @@ static int phylink_parse_fixedlink(struc + return 0; + } + +-static int phylink_parse_mode(struct phylink *pl, struct fwnode_handle *fwnode) ++static int phylink_parse_mode(struct phylink *pl, ++ const struct fwnode_handle *fwnode) + { + struct fwnode_handle *dn; + const char *managed; +@@ -1440,7 +1441,7 @@ static void phylink_fixed_poll(struct ti + static const struct sfp_upstream_ops sfp_phylink_ops; + + static int phylink_register_sfp(struct phylink *pl, +- struct fwnode_handle *fwnode) ++ const struct fwnode_handle *fwnode) + { + struct sfp_bus *bus; + int ret; +@@ -1479,7 +1480,7 @@ static int phylink_register_sfp(struct p + * must use IS_ERR() to check for errors from this function. + */ + struct phylink *phylink_create(struct phylink_config *config, +- struct fwnode_handle *fwnode, ++ const struct fwnode_handle *fwnode, + phy_interface_t iface, + const struct phylink_mac_ops *mac_ops) + { +@@ -1809,7 +1810,7 @@ EXPORT_SYMBOL_GPL(phylink_of_phy_connect + * Returns 0 on success or a negative errno. + */ + int phylink_fwnode_phy_connect(struct phylink *pl, +- struct fwnode_handle *fwnode, ++ const struct fwnode_handle *fwnode, + u32 flags) + { + struct fwnode_handle *phy_fwnode; +--- a/include/linux/phylink.h ++++ b/include/linux/phylink.h +@@ -568,16 +568,17 @@ void phylink_generic_validate(struct phy + unsigned long *supported, + struct phylink_link_state *state); + +-struct phylink *phylink_create(struct phylink_config *, struct fwnode_handle *, +- phy_interface_t iface, +- const struct phylink_mac_ops *mac_ops); ++struct phylink *phylink_create(struct phylink_config *, ++ const struct fwnode_handle *, ++ phy_interface_t, ++ const struct phylink_mac_ops *); + void phylink_destroy(struct phylink *); + bool phylink_expects_phy(struct phylink *pl); + + int phylink_connect_phy(struct phylink *, struct phy_device *); + int phylink_of_phy_connect(struct phylink *, struct device_node *, u32 flags); + int phylink_fwnode_phy_connect(struct phylink *pl, +- struct fwnode_handle *fwnode, ++ const struct fwnode_handle *fwnode, + u32 flags); + void phylink_disconnect_phy(struct phylink *); + diff --git a/target/linux/generic/backport-6.1/715-14-v6.3-net-phy-constify-fwnode_get_phy_node-fwnode-argument.patch b/target/linux/generic/backport-6.1/715-14-v6.3-net-phy-constify-fwnode_get_phy_node-fwnode-argument.patch new file mode 100644 index 000000000..2649634dc --- /dev/null +++ b/target/linux/generic/backport-6.1/715-14-v6.3-net-phy-constify-fwnode_get_phy_node-fwnode-argument.patch @@ -0,0 +1,38 @@ +From 4a0faa02d419a6728abef0f1d8a32d8c35ef95e6 Mon Sep 17 00:00:00 2001 +From: "Russell King (Oracle)" +Date: Fri, 24 Mar 2023 09:23:53 +0000 +Subject: [PATCH] net: phy: constify fwnode_get_phy_node() fwnode argument + +fwnode_get_phy_node() does not motify the fwnode structure, so make +the argument const, + +Signed-off-by: Russell King (Oracle) +Reviewed-by: Simon Horman +Signed-off-by: David S. Miller +--- + drivers/net/phy/phy_device.c | 2 +- + include/linux/phy.h | 2 +- + 2 files changed, 2 insertions(+), 2 deletions(-) + +--- a/drivers/net/phy/phy_device.c ++++ b/drivers/net/phy/phy_device.c +@@ -3003,7 +3003,7 @@ EXPORT_SYMBOL_GPL(device_phy_find_device + * and "phy-device" are not supported in ACPI. DT supports all the three + * named references to the phy node. + */ +-struct fwnode_handle *fwnode_get_phy_node(struct fwnode_handle *fwnode) ++struct fwnode_handle *fwnode_get_phy_node(const struct fwnode_handle *fwnode) + { + struct fwnode_handle *phy_node; + +--- a/include/linux/phy.h ++++ b/include/linux/phy.h +@@ -1473,7 +1473,7 @@ int fwnode_get_phy_id(struct fwnode_hand + struct mdio_device *fwnode_mdio_find_device(struct fwnode_handle *fwnode); + struct phy_device *fwnode_phy_find_device(struct fwnode_handle *phy_fwnode); + struct phy_device *device_phy_find_device(struct device *dev); +-struct fwnode_handle *fwnode_get_phy_node(struct fwnode_handle *fwnode); ++struct fwnode_handle *fwnode_get_phy_node(const struct fwnode_handle *fwnode); + struct phy_device *get_phy_device(struct mii_bus *bus, int addr, bool is_c45); + int phy_device_register(struct phy_device *phy); + void phy_device_free(struct phy_device *phydev); diff --git a/target/linux/generic/backport-6.1/715-15-v6.4-net-phylink-fix-ksettings_set-ethtool-call.patch b/target/linux/generic/backport-6.1/715-15-v6.4-net-phylink-fix-ksettings_set-ethtool-call.patch new file mode 100644 index 000000000..5eba18b02 --- /dev/null +++ b/target/linux/generic/backport-6.1/715-15-v6.4-net-phylink-fix-ksettings_set-ethtool-call.patch @@ -0,0 +1,44 @@ +From cc73de0411f7d3cdd157564a78f7a39058420ff8 Mon Sep 17 00:00:00 2001 +From: "Russell King (Oracle)" +Date: Sat, 13 May 2023 22:03:45 +0100 +Subject: [PATCH 13/21] net: phylink: fix ksettings_set() ethtool call + +While testing a Fiberstore SFP-10G-T module (which uses 10GBASE-R with +rate adaption) in a Clearfog platform (which can't do that) it was +found that the PHYs advertisement was not limited according to the +hosts capabilities when using ethtool to change it. + +Fix this by ensuring that we mask the advertisement with the computed +support mask as the very first thing we do. + +Fixes: cbc1bb1e4689 ("net: phylink: simplify phy case for ksettings_set method") +Signed-off-by: Russell King (Oracle) +Signed-off-by: David S. Miller +--- + drivers/net/phy/phylink.c | 8 ++++---- + 1 file changed, 4 insertions(+), 4 deletions(-) + +--- a/drivers/net/phy/phylink.c ++++ b/drivers/net/phy/phylink.c +@@ -2226,6 +2226,10 @@ int phylink_ethtool_ksettings_set(struct + + ASSERT_RTNL(); + ++ /* Mask out unsupported advertisements */ ++ linkmode_and(config.advertising, kset->link_modes.advertising, ++ pl->supported); ++ + if (pl->phydev) { + /* We can rely on phylib for this update; we also do not need + * to update the pl->link_config settings: +@@ -2250,10 +2254,6 @@ int phylink_ethtool_ksettings_set(struct + + config = pl->link_config; + +- /* Mask out unsupported advertisements */ +- linkmode_and(config.advertising, kset->link_modes.advertising, +- pl->supported); +- + /* FIXME: should we reject autoneg if phy/mac does not support it? */ + switch (kset->base.autoneg) { + case AUTONEG_DISABLE: diff --git a/target/linux/generic/backport-6.1/715-16-v6.5-net-sfp-add-support-for-setting-signalling-rate.patch b/target/linux/generic/backport-6.1/715-16-v6.5-net-sfp-add-support-for-setting-signalling-rate.patch new file mode 100644 index 000000000..f9670539f --- /dev/null +++ b/target/linux/generic/backport-6.1/715-16-v6.5-net-sfp-add-support-for-setting-signalling-rate.patch @@ -0,0 +1,149 @@ +From 0100d1c5789018ba77bf2f4fab3bd91ecece7b3b Mon Sep 17 00:00:00 2001 +From: "Russell King (Oracle)" +Date: Wed, 17 May 2023 11:38:12 +0100 +Subject: [PATCH 14/21] net: sfp: add support for setting signalling rate + +Add support to the SFP layer to allow phylink to set the signalling +rate for a SFP module. The rate given will be in units of kilo-baud +(1000 baud). + +Reviewed-by: Simon Horman +Signed-off-by: Russell King (Oracle) +Signed-off-by: Jakub Kicinski +--- + drivers/net/phy/phylink.c | 24 ++++++++++++++++++++++++ + drivers/net/phy/sfp-bus.c | 20 ++++++++++++++++++++ + drivers/net/phy/sfp.c | 5 +++++ + drivers/net/phy/sfp.h | 1 + + include/linux/sfp.h | 6 ++++++ + 5 files changed, 56 insertions(+) + +--- a/drivers/net/phy/phylink.c ++++ b/drivers/net/phy/phylink.c +@@ -156,6 +156,23 @@ static const char *phylink_an_mode_str(u + return mode < ARRAY_SIZE(modestr) ? modestr[mode] : "unknown"; + } + ++static unsigned int phylink_interface_signal_rate(phy_interface_t interface) ++{ ++ switch (interface) { ++ case PHY_INTERFACE_MODE_SGMII: ++ case PHY_INTERFACE_MODE_1000BASEX: /* 1.25Mbd */ ++ return 1250; ++ case PHY_INTERFACE_MODE_2500BASEX: /* 3.125Mbd */ ++ return 3125; ++ case PHY_INTERFACE_MODE_5GBASER: /* 5.15625Mbd */ ++ return 5156; ++ case PHY_INTERFACE_MODE_10GBASER: /* 10.3125Mbd */ ++ return 10313; ++ default: ++ return 0; ++ } ++} ++ + /** + * phylink_interface_max_speed() - get the maximum speed of a phy interface + * @interface: phy interface mode defined by &typedef phy_interface_t +@@ -1024,6 +1041,7 @@ static void phylink_major_config(struct + { + struct phylink_pcs *pcs = NULL; + bool pcs_changed = false; ++ unsigned int rate_kbd; + int err; + + phylink_dbg(pl, "major config %s\n", phy_modes(state->interface)); +@@ -1083,6 +1101,12 @@ static void phylink_major_config(struct + ERR_PTR(err)); + } + ++ if (pl->sfp_bus) { ++ rate_kbd = phylink_interface_signal_rate(state->interface); ++ if (rate_kbd) ++ sfp_upstream_set_signal_rate(pl->sfp_bus, rate_kbd); ++ } ++ + phylink_pcs_poll_start(pl); + } + +--- a/drivers/net/phy/sfp-bus.c ++++ b/drivers/net/phy/sfp-bus.c +@@ -586,6 +586,26 @@ static void sfp_upstream_clear(struct sf + } + + /** ++ * sfp_upstream_set_signal_rate() - set data signalling rate ++ * @bus: a pointer to the &struct sfp_bus structure for the sfp module ++ * @rate_kbd: signalling rate in units of 1000 baud ++ * ++ * Configure the rate select settings on the SFP module for the signalling ++ * rate (not the same as the data rate). ++ * ++ * Locks that may be held: ++ * Phylink's state_mutex ++ * rtnl lock ++ * SFP's sm_mutex ++ */ ++void sfp_upstream_set_signal_rate(struct sfp_bus *bus, unsigned int rate_kbd) ++{ ++ if (bus->registered) ++ bus->socket_ops->set_signal_rate(bus->sfp, rate_kbd); ++} ++EXPORT_SYMBOL_GPL(sfp_upstream_set_signal_rate); ++ ++/** + * sfp_bus_find_fwnode() - parse and locate the SFP bus from fwnode + * @fwnode: firmware node for the parent device (MAC or PHY) + * +--- a/drivers/net/phy/sfp.c ++++ b/drivers/net/phy/sfp.c +@@ -2473,6 +2473,10 @@ static void sfp_stop(struct sfp *sfp) + sfp_sm_event(sfp, SFP_E_DEV_DOWN); + } + ++static void sfp_set_signal_rate(struct sfp *sfp, unsigned int rate_kbd) ++{ ++} ++ + static int sfp_module_info(struct sfp *sfp, struct ethtool_modinfo *modinfo) + { + /* locking... and check module is present */ +@@ -2551,6 +2555,7 @@ static const struct sfp_socket_ops sfp_m + .detach = sfp_detach, + .start = sfp_start, + .stop = sfp_stop, ++ .set_signal_rate = sfp_set_signal_rate, + .module_info = sfp_module_info, + .module_eeprom = sfp_module_eeprom, + .module_eeprom_by_page = sfp_module_eeprom_by_page, +--- a/drivers/net/phy/sfp.h ++++ b/drivers/net/phy/sfp.h +@@ -19,6 +19,7 @@ struct sfp_socket_ops { + void (*detach)(struct sfp *sfp); + void (*start)(struct sfp *sfp); + void (*stop)(struct sfp *sfp); ++ void (*set_signal_rate)(struct sfp *sfp, unsigned int rate_kbd); + int (*module_info)(struct sfp *sfp, struct ethtool_modinfo *modinfo); + int (*module_eeprom)(struct sfp *sfp, struct ethtool_eeprom *ee, + u8 *data); +--- a/include/linux/sfp.h ++++ b/include/linux/sfp.h +@@ -547,6 +547,7 @@ int sfp_get_module_eeprom_by_page(struct + struct netlink_ext_ack *extack); + void sfp_upstream_start(struct sfp_bus *bus); + void sfp_upstream_stop(struct sfp_bus *bus); ++void sfp_upstream_set_signal_rate(struct sfp_bus *bus, unsigned int rate_kbd); + void sfp_bus_put(struct sfp_bus *bus); + struct sfp_bus *sfp_bus_find_fwnode(const struct fwnode_handle *fwnode); + int sfp_bus_add_upstream(struct sfp_bus *bus, void *upstream, +@@ -606,6 +607,11 @@ static inline void sfp_upstream_stop(str + { + } + ++static inline void sfp_upstream_set_signal_rate(struct sfp_bus *bus, ++ unsigned int rate_kbd) ++{ ++} ++ + static inline void sfp_bus_put(struct sfp_bus *bus) + { + } diff --git a/target/linux/generic/backport-6.1/715-17-v6.5-net-phy-add-helpers-for-comparing-phy-IDs.patch b/target/linux/generic/backport-6.1/715-17-v6.5-net-phy-add-helpers-for-comparing-phy-IDs.patch new file mode 100644 index 000000000..8a694c86d --- /dev/null +++ b/target/linux/generic/backport-6.1/715-17-v6.5-net-phy-add-helpers-for-comparing-phy-IDs.patch @@ -0,0 +1,147 @@ +From b84acdb07222a701bfc6403b374249c86f97d18d Mon Sep 17 00:00:00 2001 +From: Russell King +Date: Fri, 19 May 2023 14:03:59 +0100 +Subject: [PATCH 15/21] net: phy: add helpers for comparing phy IDs + +There are several places which open code comparing PHY IDs. Provide a +couple of helpers to assist with this, using a slightly simpler test +than the original: + +- phy_id_compare() compares two arbitary PHY IDs and a mask of the + significant bits in the ID. +- phydev_id_compare() compares the bound phydev with the specified + PHY ID, using the bound driver's mask. + +Signed-off-by: Russell King +Reviewed-by: Simon Horman +Reviewed-by: Andrew Lunn +Signed-off-by: David S. Miller +--- + drivers/net/phy/micrel.c | 6 +++--- + drivers/net/phy/phy_device.c | 16 +++++++--------- + drivers/net/phy/phylink.c | 4 ++-- + include/linux/phy.h | 28 ++++++++++++++++++++++++++++ + 4 files changed, 40 insertions(+), 14 deletions(-) + +--- a/drivers/net/phy/micrel.c ++++ b/drivers/net/phy/micrel.c +@@ -620,7 +620,7 @@ static int ksz8051_ksz8795_match_phy_dev + { + int ret; + +- if ((phydev->phy_id & MICREL_PHY_ID_MASK) != PHY_ID_KSZ8051) ++ if (!phy_id_compare(phydev->phy_id, PHY_ID_KSZ8051, MICREL_PHY_ID_MASK)) + return 0; + + ret = phy_read(phydev, MII_BMSR); +@@ -1455,7 +1455,7 @@ static int ksz9x31_cable_test_fault_leng + * + * distance to fault = (VCT_DATA - 22) * 4 / cable propagation velocity + */ +- if ((phydev->phy_id & MICREL_PHY_ID_MASK) == PHY_ID_KSZ9131) ++ if (phydev_id_compare(phydev, PHY_ID_KSZ9131)) + dt = clamp(dt - 22, 0, 255); + + return (dt * 400) / 10; +@@ -1887,7 +1887,7 @@ static __always_inline int ksz886x_cable + */ + dt = FIELD_GET(data_mask, status); + +- if ((phydev->phy_id & MICREL_PHY_ID_MASK) == PHY_ID_LAN8814) ++ if (phydev_id_compare(phydev, PHY_ID_LAN8814)) + return ((dt - 22) * 800) / 10; + else + return (dt * 400) / 10; +--- a/drivers/net/phy/phy_device.c ++++ b/drivers/net/phy/phy_device.c +@@ -422,8 +422,7 @@ int phy_unregister_fixup(const char *bus + fixup = list_entry(pos, struct phy_fixup, list); + + if ((!strcmp(fixup->bus_id, bus_id)) && +- ((fixup->phy_uid & phy_uid_mask) == +- (phy_uid & phy_uid_mask))) { ++ phy_id_compare(fixup->phy_uid, phy_uid, phy_uid_mask)) { + list_del(&fixup->list); + kfree(fixup); + ret = 0; +@@ -459,8 +458,8 @@ static int phy_needs_fixup(struct phy_de + if (strcmp(fixup->bus_id, PHY_ANY_ID) != 0) + return 0; + +- if ((fixup->phy_uid & fixup->phy_uid_mask) != +- (phydev->phy_id & fixup->phy_uid_mask)) ++ if (!phy_id_compare(phydev->phy_id, fixup->phy_uid, ++ fixup->phy_uid_mask)) + if (fixup->phy_uid != PHY_ANY_UID) + return 0; + +@@ -507,15 +506,14 @@ static int phy_bus_match(struct device * + if (phydev->c45_ids.device_ids[i] == 0xffffffff) + continue; + +- if ((phydrv->phy_id & phydrv->phy_id_mask) == +- (phydev->c45_ids.device_ids[i] & +- phydrv->phy_id_mask)) ++ if (phy_id_compare(phydev->c45_ids.device_ids[i], ++ phydrv->phy_id, phydrv->phy_id_mask)) + return 1; + } + return 0; + } else { +- return (phydrv->phy_id & phydrv->phy_id_mask) == +- (phydev->phy_id & phydrv->phy_id_mask); ++ return phy_id_compare(phydev->phy_id, phydrv->phy_id, ++ phydrv->phy_id_mask); + } + } + +--- a/drivers/net/phy/phylink.c ++++ b/drivers/net/phy/phylink.c +@@ -3151,8 +3151,8 @@ static void phylink_sfp_link_up(void *up + */ + static bool phylink_phy_no_inband(struct phy_device *phy) + { +- return phy->is_c45 && +- (phy->c45_ids.device_ids[1] & 0xfffffff0) == 0xae025150; ++ return phy->is_c45 && phy_id_compare(phy->c45_ids.device_ids[1], ++ 0xae025150, 0xfffffff0); + } + + static int phylink_sfp_connect_phy(void *upstream, struct phy_device *phy) +--- a/include/linux/phy.h ++++ b/include/linux/phy.h +@@ -993,6 +993,34 @@ struct phy_driver { + #define PHY_ID_MATCH_MODEL(id) .phy_id = (id), .phy_id_mask = GENMASK(31, 4) + #define PHY_ID_MATCH_VENDOR(id) .phy_id = (id), .phy_id_mask = GENMASK(31, 10) + ++/** ++ * phy_id_compare - compare @id1 with @id2 taking account of @mask ++ * @id1: first PHY ID ++ * @id2: second PHY ID ++ * @mask: the PHY ID mask, set bits are significant in matching ++ * ++ * Return true if the bits from @id1 and @id2 specified by @mask match. ++ * This uses an equivalent test to (@id & @mask) == (@phy_id & @mask). ++ */ ++static inline bool phy_id_compare(u32 id1, u32 id2, u32 mask) ++{ ++ return !((id1 ^ id2) & mask); ++} ++ ++/** ++ * phydev_id_compare - compare @id with the PHY's Clause 22 ID ++ * @phydev: the PHY device ++ * @id: the PHY ID to be matched ++ * ++ * Compare the @phydev clause 22 ID with the provided @id and return true or ++ * false depending whether it matches, using the bound driver mask. The ++ * @phydev must be bound to a driver. ++ */ ++static inline bool phydev_id_compare(struct phy_device *phydev, u32 id) ++{ ++ return phy_id_compare(id, phydev->phy_id, phydev->drv->phy_id_mask); ++} ++ + /* A Structure for boards to register fixups with the PHY Lib */ + struct phy_fixup { + struct list_head list; diff --git a/target/linux/generic/backport-6.1/715-18-v6.5-net-phylink-require-supported_interfaces-to-be-fille.patch b/target/linux/generic/backport-6.1/715-18-v6.5-net-phylink-require-supported_interfaces-to-be-fille.patch new file mode 100644 index 000000000..5970355a6 --- /dev/null +++ b/target/linux/generic/backport-6.1/715-18-v6.5-net-phylink-require-supported_interfaces-to-be-fille.patch @@ -0,0 +1,71 @@ +From 441e1e44301fc5762a06737f8ec04bf1ce3fb039 Mon Sep 17 00:00:00 2001 +From: "Russell King (Oracle)" +Date: Sat, 20 May 2023 11:41:42 +0100 +Subject: [PATCH 16/21] net: phylink: require supported_interfaces to be filled + +We have been requiring the supported_interfaces bitmap to be filled in +by MAC drivers that have a mac_select_pcs() method. Now that all MAC +drivers fill in the supported_interfaces bitmap, it is time to enforce +this. We have already required supported_interfaces to be set in order +for optical SFPs to be configured in commit f81fa96d8a6c ("net: phylink: +use phy_interface_t bitmaps for optical modules"). + +Refuse phylink creation if supported_interfaces is empty, and remove +code to deal with cases where this mask is empty. + +Signed-off-by: Russell King (Oracle) +Reviewed-by: Andrew Lunn +Link: https://lore.kernel.org/r/E1q0K1u-006EIP-ET@rmk-PC.armlinux.org.uk +Signed-off-by: Jakub Kicinski +--- + drivers/net/phy/phylink.c | 26 +++++++++++--------------- + 1 file changed, 11 insertions(+), 15 deletions(-) + +--- a/drivers/net/phy/phylink.c ++++ b/drivers/net/phy/phylink.c +@@ -710,14 +710,11 @@ static int phylink_validate(struct phyli + { + const unsigned long *interfaces = pl->config->supported_interfaces; + +- if (!phy_interface_empty(interfaces)) { +- if (state->interface == PHY_INTERFACE_MODE_NA) +- return phylink_validate_mask(pl, supported, state, +- interfaces); ++ if (state->interface == PHY_INTERFACE_MODE_NA) ++ return phylink_validate_mask(pl, supported, state, interfaces); + +- if (!test_bit(state->interface, interfaces)) +- return -EINVAL; +- } ++ if (!test_bit(state->interface, interfaces)) ++ return -EINVAL; + + return phylink_validate_mac_and_pcs(pl, supported, state); + } +@@ -1512,19 +1509,18 @@ struct phylink *phylink_create(struct ph + struct phylink *pl; + int ret; + +- if (mac_ops->mac_select_pcs && +- mac_ops->mac_select_pcs(config, PHY_INTERFACE_MODE_NA) != +- ERR_PTR(-EOPNOTSUPP)) +- using_mac_select_pcs = true; +- + /* Validate the supplied configuration */ +- if (using_mac_select_pcs && +- phy_interface_empty(config->supported_interfaces)) { ++ if (phy_interface_empty(config->supported_interfaces)) { + dev_err(config->dev, +- "phylink: error: empty supported_interfaces but mac_select_pcs() method present\n"); ++ "phylink: error: empty supported_interfaces\n"); + return ERR_PTR(-EINVAL); + } + ++ if (mac_ops->mac_select_pcs && ++ mac_ops->mac_select_pcs(config, PHY_INTERFACE_MODE_NA) != ++ ERR_PTR(-EOPNOTSUPP)) ++ using_mac_select_pcs = true; ++ + pl = kzalloc(sizeof(*pl), GFP_KERNEL); + if (!pl) + return ERR_PTR(-ENOMEM); diff --git a/target/linux/generic/backport-6.1/715-19-v6.5-net-phylink-remove-duplicated-linkmode-pause-resolut.patch b/target/linux/generic/backport-6.1/715-19-v6.5-net-phylink-remove-duplicated-linkmode-pause-resolut.patch new file mode 100644 index 000000000..3a26b4b60 --- /dev/null +++ b/target/linux/generic/backport-6.1/715-19-v6.5-net-phylink-remove-duplicated-linkmode-pause-resolut.patch @@ -0,0 +1,64 @@ +From 4b624a39f2ab523ca6a6ad9448fab1deb7b101e2 Mon Sep 17 00:00:00 2001 +From: "Russell King (Oracle)" +Date: Tue, 23 May 2023 11:15:53 +0100 +Subject: [PATCH 17/21] net: phylink: remove duplicated linkmode pause + resolution + +Phylink had two chunks of code virtually the same for resolving the +negotiated pause modes. Factor this down to one function. + +Reviewed-by: Andrew Lunn +Signed-off-by: Russell King (Oracle) +Signed-off-by: Jakub Kicinski +--- + drivers/net/phy/phylink.c | 15 ++++----------- + 1 file changed, 4 insertions(+), 11 deletions(-) + +--- a/drivers/net/phy/phylink.c ++++ b/drivers/net/phy/phylink.c +@@ -976,11 +976,10 @@ static void phylink_apply_manual_flow(st + state->pause = pl->link_config.pause; + } + +-static void phylink_resolve_flow(struct phylink_link_state *state) ++static void phylink_resolve_an_pause(struct phylink_link_state *state) + { + bool tx_pause, rx_pause; + +- state->pause = MLO_PAUSE_NONE; + if (state->duplex == DUPLEX_FULL) { + linkmode_resolve_pause(state->advertising, + state->lp_advertising, +@@ -1192,7 +1191,8 @@ static void phylink_get_fixed_state(stru + else if (pl->link_gpio) + state->link = !!gpiod_get_value_cansleep(pl->link_gpio); + +- phylink_resolve_flow(state); ++ state->pause = MLO_PAUSE_NONE; ++ phylink_resolve_an_pause(state); + } + + static void phylink_mac_initial_config(struct phylink *pl, bool force_restart) +@@ -3215,7 +3215,6 @@ static const struct sfp_upstream_ops sfp + static void phylink_decode_c37_word(struct phylink_link_state *state, + uint16_t config_reg, int speed) + { +- bool tx_pause, rx_pause; + int fd_bit; + + if (speed == SPEED_2500) +@@ -3234,13 +3233,7 @@ static void phylink_decode_c37_word(stru + state->link = false; + } + +- linkmode_resolve_pause(state->advertising, state->lp_advertising, +- &tx_pause, &rx_pause); +- +- if (tx_pause) +- state->pause |= MLO_PAUSE_TX; +- if (rx_pause) +- state->pause |= MLO_PAUSE_RX; ++ phylink_resolve_an_pause(state); + } + + static void phylink_decode_sgmii_word(struct phylink_link_state *state, diff --git a/target/linux/generic/backport-6.1/715-20-v6.5-net-phylink-add-function-to-resolve-clause-73-negoti.patch b/target/linux/generic/backport-6.1/715-20-v6.5-net-phylink-add-function-to-resolve-clause-73-negoti.patch new file mode 100644 index 000000000..2b2634f80 --- /dev/null +++ b/target/linux/generic/backport-6.1/715-20-v6.5-net-phylink-add-function-to-resolve-clause-73-negoti.patch @@ -0,0 +1,76 @@ +From aa8b6bd2b1f235b262bd27f317a0516f196c2c6a Mon Sep 17 00:00:00 2001 +From: "Russell King (Oracle)" +Date: Tue, 23 May 2023 11:15:58 +0100 +Subject: [PATCH 18/21] net: phylink: add function to resolve clause 73 + negotiation + +Add a function to resolve clause 73 negotiation according to the +priority resolution function described in clause 73.3.6. + +Signed-off-by: Russell King (Oracle) +Reviewed-by: Andrew Lunn +Signed-off-by: Jakub Kicinski +--- + drivers/net/phy/phylink.c | 39 +++++++++++++++++++++++++++++++++++++++ + include/linux/phylink.h | 2 ++ + 2 files changed, 41 insertions(+) + +--- a/drivers/net/phy/phylink.c ++++ b/drivers/net/phy/phylink.c +@@ -3212,6 +3212,45 @@ static const struct sfp_upstream_ops sfp + + /* Helpers for MAC drivers */ + ++static struct { ++ int bit; ++ int speed; ++} phylink_c73_priority_resolution[] = { ++ { ETHTOOL_LINK_MODE_100000baseCR4_Full_BIT, SPEED_100000 }, ++ { ETHTOOL_LINK_MODE_100000baseKR4_Full_BIT, SPEED_100000 }, ++ /* 100GBASE-KP4 and 100GBASE-CR10 not supported */ ++ { ETHTOOL_LINK_MODE_40000baseCR4_Full_BIT, SPEED_40000 }, ++ { ETHTOOL_LINK_MODE_40000baseKR4_Full_BIT, SPEED_40000 }, ++ { ETHTOOL_LINK_MODE_10000baseKR_Full_BIT, SPEED_10000 }, ++ { ETHTOOL_LINK_MODE_10000baseKX4_Full_BIT, SPEED_10000 }, ++ /* 5GBASE-KR not supported */ ++ { ETHTOOL_LINK_MODE_2500baseX_Full_BIT, SPEED_2500 }, ++ { ETHTOOL_LINK_MODE_1000baseKX_Full_BIT, SPEED_1000 }, ++}; ++ ++void phylink_resolve_c73(struct phylink_link_state *state) ++{ ++ int i; ++ ++ for (i = 0; i < ARRAY_SIZE(phylink_c73_priority_resolution); i++) { ++ int bit = phylink_c73_priority_resolution[i].bit; ++ if (linkmode_test_bit(bit, state->advertising) && ++ linkmode_test_bit(bit, state->lp_advertising)) ++ break; ++ } ++ ++ if (i < ARRAY_SIZE(phylink_c73_priority_resolution)) { ++ state->speed = phylink_c73_priority_resolution[i].speed; ++ state->duplex = DUPLEX_FULL; ++ } else { ++ /* negotiation failure */ ++ state->link = false; ++ } ++ ++ phylink_resolve_an_pause(state); ++} ++EXPORT_SYMBOL_GPL(phylink_resolve_c73); ++ + static void phylink_decode_c37_word(struct phylink_link_state *state, + uint16_t config_reg, int speed) + { +--- a/include/linux/phylink.h ++++ b/include/linux/phylink.h +@@ -656,6 +656,8 @@ int phylink_mii_c22_pcs_config(struct md + const unsigned long *advertising); + void phylink_mii_c22_pcs_an_restart(struct mdio_device *pcs); + ++void phylink_resolve_c73(struct phylink_link_state *state); ++ + void phylink_mii_c45_pcs_get_state(struct mdio_device *pcs, + struct phylink_link_state *state); + diff --git a/target/linux/generic/backport-6.1/715-21-v6.5-net-phylink-provide-phylink_pcs_config-and-phylink_p.patch b/target/linux/generic/backport-6.1/715-21-v6.5-net-phylink-provide-phylink_pcs_config-and-phylink_p.patch new file mode 100644 index 000000000..eea99a5d7 --- /dev/null +++ b/target/linux/generic/backport-6.1/715-21-v6.5-net-phylink-provide-phylink_pcs_config-and-phylink_p.patch @@ -0,0 +1,100 @@ +From 796d709363135a6bd6a8ccc07b509c939e5b855f Mon Sep 17 00:00:00 2001 +From: "Russell King (Oracle)" +Date: Tue, 23 May 2023 16:31:50 +0100 +Subject: [PATCH 19/21] net: phylink: provide phylink_pcs_config() and + phylink_pcs_link_up() + +Add two helper functions for calling PCS methods. phylink_pcs_config() +allows us to handle PCS configuration specifics in one location, rather +than the two call sites. phylink_pcs_link_up() gives us consistency. + +Signed-off-by: Russell King (Oracle) +Link: https://lore.kernel.org/r/E1q1TzK-007Exd-Rs@rmk-PC.armlinux.org.uk +Signed-off-by: Jakub Kicinski +--- + drivers/net/phy/phylink.c | 53 ++++++++++++++++++++++++--------------- + 1 file changed, 33 insertions(+), 20 deletions(-) + +--- a/drivers/net/phy/phylink.c ++++ b/drivers/net/phy/phylink.c +@@ -991,6 +991,25 @@ static void phylink_resolve_an_pause(str + } + } + ++static int phylink_pcs_config(struct phylink_pcs *pcs, unsigned int mode, ++ const struct phylink_link_state *state, ++ bool permit_pause_to_mac) ++{ ++ if (!pcs) ++ return 0; ++ ++ return pcs->ops->pcs_config(pcs, mode, state->interface, ++ state->advertising, permit_pause_to_mac); ++} ++ ++static void phylink_pcs_link_up(struct phylink_pcs *pcs, unsigned int mode, ++ phy_interface_t interface, int speed, ++ int duplex) ++{ ++ if (pcs && pcs->ops->pcs_link_up) ++ pcs->ops->pcs_link_up(pcs, mode, interface, speed, duplex); ++} ++ + static void phylink_pcs_poll_stop(struct phylink *pl) + { + if (pl->cfg_link_an_mode == MLO_AN_INBAND) +@@ -1074,18 +1093,15 @@ static void phylink_major_config(struct + + phylink_mac_config(pl, state); + +- if (pl->pcs) { +- err = pl->pcs->ops->pcs_config(pl->pcs, pl->cur_link_an_mode, +- state->interface, +- state->advertising, +- !!(pl->link_config.pause & +- MLO_PAUSE_AN)); +- if (err < 0) +- phylink_err(pl, "pcs_config failed: %pe\n", +- ERR_PTR(err)); +- if (err > 0) +- restart = true; +- } ++ err = phylink_pcs_config(pl->pcs, pl->cur_link_an_mode, state, ++ !!(pl->link_config.pause & ++ MLO_PAUSE_AN)); ++ if (err < 0) ++ phylink_err(pl, "pcs_config failed: %pe\n", ++ ERR_PTR(err)); ++ else if (err > 0) ++ restart = true; ++ + if (restart) + phylink_mac_pcs_an_restart(pl); + +@@ -1136,11 +1152,9 @@ static int phylink_change_inband_advert( + * restart negotiation if the pcs_config() helper indicates that + * the programmed advertisement has changed. + */ +- ret = pl->pcs->ops->pcs_config(pl->pcs, pl->cur_link_an_mode, +- pl->link_config.interface, +- pl->link_config.advertising, +- !!(pl->link_config.pause & +- MLO_PAUSE_AN)); ++ ret = phylink_pcs_config(pl->pcs, pl->cur_link_an_mode, ++ &pl->link_config, ++ !!(pl->link_config.pause & MLO_PAUSE_AN)); + if (ret < 0) + return ret; + +@@ -1272,9 +1286,8 @@ static void phylink_link_up(struct phyli + + pl->cur_interface = link_state.interface; + +- if (pl->pcs && pl->pcs->ops->pcs_link_up) +- pl->pcs->ops->pcs_link_up(pl->pcs, pl->cur_link_an_mode, +- pl->cur_interface, speed, duplex); ++ phylink_pcs_link_up(pl->pcs, pl->cur_link_an_mode, pl->cur_interface, ++ speed, duplex); + + pl->mac_ops->mac_link_up(pl->config, pl->phydev, pl->cur_link_an_mode, + pl->cur_interface, speed, duplex, diff --git a/target/linux/generic/backport-6.1/715-23-v6.4-net-phylink-actually-fix-ksettings_set-ethtool-call.patch b/target/linux/generic/backport-6.1/715-23-v6.4-net-phylink-actually-fix-ksettings_set-ethtool-call.patch new file mode 100644 index 000000000..2f7f7a573 --- /dev/null +++ b/target/linux/generic/backport-6.1/715-23-v6.4-net-phylink-actually-fix-ksettings_set-ethtool-call.patch @@ -0,0 +1,55 @@ +From 11933aa76865621d8e82553c8f3bc07796a5aaa2 Mon Sep 17 00:00:00 2001 +From: "Russell King (Oracle)" +Date: Thu, 1 Jun 2023 10:12:06 +0100 +Subject: [PATCH 20/21] net: phylink: actually fix ksettings_set() ethtool call + +Raju Lakkaraju reported that the below commit caused a regression +with Lan743x drivers and a 2.5G SFP. Sadly, this is because the commit +was utterly wrong. Let's fix this properly by not moving the +linkmode_and(), but instead copying the link ksettings and then +modifying the advertising mask before passing the modified link +ksettings to phylib. + +Fixes: df0acdc59b09 ("net: phylink: fix ksettings_set() ethtool call") +Signed-off-by: Russell King (Oracle) +Link: https://lore.kernel.org/r/E1q4eLm-00Ayxk-GZ@rmk-PC.armlinux.org.uk +Signed-off-by: Jakub Kicinski +--- + drivers/net/phy/phylink.c | 15 ++++++++++----- + 1 file changed, 10 insertions(+), 5 deletions(-) + +--- a/drivers/net/phy/phylink.c ++++ b/drivers/net/phy/phylink.c +@@ -2259,11 +2259,13 @@ int phylink_ethtool_ksettings_set(struct + + ASSERT_RTNL(); + +- /* Mask out unsupported advertisements */ +- linkmode_and(config.advertising, kset->link_modes.advertising, +- pl->supported); +- + if (pl->phydev) { ++ struct ethtool_link_ksettings phy_kset = *kset; ++ ++ linkmode_and(phy_kset.link_modes.advertising, ++ phy_kset.link_modes.advertising, ++ pl->supported); ++ + /* We can rely on phylib for this update; we also do not need + * to update the pl->link_config settings: + * - the configuration returned via ksettings_get() will come +@@ -2282,10 +2284,13 @@ int phylink_ethtool_ksettings_set(struct + * the presence of a PHY, this should not be changed as that + * should be determined from the media side advertisement. + */ +- return phy_ethtool_ksettings_set(pl->phydev, kset); ++ return phy_ethtool_ksettings_set(pl->phydev, &phy_kset); + } + + config = pl->link_config; ++ /* Mask out unsupported advertisements */ ++ linkmode_and(config.advertising, kset->link_modes.advertising, ++ pl->supported); + + /* FIXME: should we reject autoneg if phy/mac does not support it? */ + switch (kset->base.autoneg) { diff --git a/target/linux/generic/backport-6.1/715-24-v6.5-net-phylink-add-PCS-negotiation-mode.patch b/target/linux/generic/backport-6.1/715-24-v6.5-net-phylink-add-PCS-negotiation-mode.patch new file mode 100644 index 000000000..e1a8539aa --- /dev/null +++ b/target/linux/generic/backport-6.1/715-24-v6.5-net-phylink-add-PCS-negotiation-mode.patch @@ -0,0 +1,324 @@ +From 79b07c3e9c4a2272927be8848c26b372516e1958 Mon Sep 17 00:00:00 2001 +From: "Russell King (Oracle)" +Date: Fri, 16 Jun 2023 13:06:22 +0100 +Subject: [PATCH 21/21] net: phylink: add PCS negotiation mode + +PCS have to work out whether they should enable PCS negotiation by +looking at the "mode" and "interface" arguments, and the Autoneg bit +in the advertising mask. + +This leads to some complex logic, so lets pull that out into phylink +and instead pass a "neg_mode" argument to the PCS configuration and +link up methods, instead of the "mode" argument. + +In order to transition drivers, add a "neg_mode" flag to the phylink +PCS structure to PCS can indicate whether they want to be passed the +neg_mode or the old mode argument. + +Signed-off-by: Russell King (Oracle) +Link: https://lore.kernel.org/r/E1qA8De-00EaFA-Ht@rmk-PC.armlinux.org.uk +Signed-off-by: Jakub Kicinski +--- + drivers/net/phy/phylink.c | 45 +++++++++++++---- + include/linux/phylink.h | 104 +++++++++++++++++++++++++++++++++++--- + 2 files changed, 132 insertions(+), 17 deletions(-) + +--- a/drivers/net/phy/phylink.c ++++ b/drivers/net/phy/phylink.c +@@ -71,6 +71,7 @@ struct phylink { + struct mutex state_mutex; + struct phylink_link_state phy_state; + struct work_struct resolve; ++ unsigned int pcs_neg_mode; + + bool mac_link_dropped; + bool using_mac_select_pcs; +@@ -991,23 +992,23 @@ static void phylink_resolve_an_pause(str + } + } + +-static int phylink_pcs_config(struct phylink_pcs *pcs, unsigned int mode, ++static int phylink_pcs_config(struct phylink_pcs *pcs, unsigned int neg_mode, + const struct phylink_link_state *state, + bool permit_pause_to_mac) + { + if (!pcs) + return 0; + +- return pcs->ops->pcs_config(pcs, mode, state->interface, ++ return pcs->ops->pcs_config(pcs, neg_mode, state->interface, + state->advertising, permit_pause_to_mac); + } + +-static void phylink_pcs_link_up(struct phylink_pcs *pcs, unsigned int mode, ++static void phylink_pcs_link_up(struct phylink_pcs *pcs, unsigned int neg_mode, + phy_interface_t interface, int speed, + int duplex) + { + if (pcs && pcs->ops->pcs_link_up) +- pcs->ops->pcs_link_up(pcs, mode, interface, speed, duplex); ++ pcs->ops->pcs_link_up(pcs, neg_mode, interface, speed, duplex); + } + + static void phylink_pcs_poll_stop(struct phylink *pl) +@@ -1057,10 +1058,15 @@ static void phylink_major_config(struct + struct phylink_pcs *pcs = NULL; + bool pcs_changed = false; + unsigned int rate_kbd; ++ unsigned int neg_mode; + int err; + + phylink_dbg(pl, "major config %s\n", phy_modes(state->interface)); + ++ pl->pcs_neg_mode = phylink_pcs_neg_mode(pl->cur_link_an_mode, ++ state->interface, ++ state->advertising); ++ + if (pl->using_mac_select_pcs) { + pcs = pl->mac_ops->mac_select_pcs(pl->config, state->interface); + if (IS_ERR(pcs)) { +@@ -1093,9 +1099,12 @@ static void phylink_major_config(struct + + phylink_mac_config(pl, state); + +- err = phylink_pcs_config(pl->pcs, pl->cur_link_an_mode, state, +- !!(pl->link_config.pause & +- MLO_PAUSE_AN)); ++ neg_mode = pl->cur_link_an_mode; ++ if (pl->pcs && pl->pcs->neg_mode) ++ neg_mode = pl->pcs_neg_mode; ++ ++ err = phylink_pcs_config(pl->pcs, neg_mode, state, ++ !!(pl->link_config.pause & MLO_PAUSE_AN)); + if (err < 0) + phylink_err(pl, "pcs_config failed: %pe\n", + ERR_PTR(err)); +@@ -1130,6 +1139,7 @@ static void phylink_major_config(struct + */ + static int phylink_change_inband_advert(struct phylink *pl) + { ++ unsigned int neg_mode; + int ret; + + if (test_bit(PHYLINK_DISABLE_STOPPED, &pl->phylink_disable_state)) +@@ -1148,12 +1158,20 @@ static int phylink_change_inband_advert( + __ETHTOOL_LINK_MODE_MASK_NBITS, pl->link_config.advertising, + pl->link_config.pause); + ++ /* Recompute the PCS neg mode */ ++ pl->pcs_neg_mode = phylink_pcs_neg_mode(pl->cur_link_an_mode, ++ pl->link_config.interface, ++ pl->link_config.advertising); ++ ++ neg_mode = pl->cur_link_an_mode; ++ if (pl->pcs->neg_mode) ++ neg_mode = pl->pcs_neg_mode; ++ + /* Modern PCS-based method; update the advert at the PCS, and + * restart negotiation if the pcs_config() helper indicates that + * the programmed advertisement has changed. + */ +- ret = phylink_pcs_config(pl->pcs, pl->cur_link_an_mode, +- &pl->link_config, ++ ret = phylink_pcs_config(pl->pcs, neg_mode, &pl->link_config, + !!(pl->link_config.pause & MLO_PAUSE_AN)); + if (ret < 0) + return ret; +@@ -1256,6 +1274,7 @@ static void phylink_link_up(struct phyli + struct phylink_link_state link_state) + { + struct net_device *ndev = pl->netdev; ++ unsigned int neg_mode; + int speed, duplex; + bool rx_pause; + +@@ -1286,8 +1305,12 @@ static void phylink_link_up(struct phyli + + pl->cur_interface = link_state.interface; + +- phylink_pcs_link_up(pl->pcs, pl->cur_link_an_mode, pl->cur_interface, +- speed, duplex); ++ neg_mode = pl->cur_link_an_mode; ++ if (pl->pcs && pl->pcs->neg_mode) ++ neg_mode = pl->pcs_neg_mode; ++ ++ phylink_pcs_link_up(pl->pcs, neg_mode, pl->cur_interface, speed, ++ duplex); + + pl->mac_ops->mac_link_up(pl->config, pl->phydev, pl->cur_link_an_mode, + pl->cur_interface, speed, duplex, +--- a/include/linux/phylink.h ++++ b/include/linux/phylink.h +@@ -21,6 +21,24 @@ enum { + MLO_AN_FIXED, /* Fixed-link mode */ + MLO_AN_INBAND, /* In-band protocol */ + ++ /* PCS "negotiation" mode. ++ * PHYLINK_PCS_NEG_NONE - protocol has no inband capability ++ * PHYLINK_PCS_NEG_OUTBAND - some out of band or fixed link setting ++ * PHYLINK_PCS_NEG_INBAND_DISABLED - inband mode disabled, e.g. ++ * 1000base-X with autoneg off ++ * PHYLINK_PCS_NEG_INBAND_ENABLED - inband mode enabled ++ * Additionally, this can be tested using bitmasks: ++ * PHYLINK_PCS_NEG_INBAND - inband mode selected ++ * PHYLINK_PCS_NEG_ENABLED - negotiation mode enabled ++ */ ++ PHYLINK_PCS_NEG_NONE = 0, ++ PHYLINK_PCS_NEG_ENABLED = BIT(4), ++ PHYLINK_PCS_NEG_OUTBAND = BIT(5), ++ PHYLINK_PCS_NEG_INBAND = BIT(6), ++ PHYLINK_PCS_NEG_INBAND_DISABLED = PHYLINK_PCS_NEG_INBAND, ++ PHYLINK_PCS_NEG_INBAND_ENABLED = PHYLINK_PCS_NEG_INBAND | ++ PHYLINK_PCS_NEG_ENABLED, ++ + /* MAC_SYM_PAUSE and MAC_ASYM_PAUSE are used when configuring our + * autonegotiation advertisement. They correspond to the PAUSE and + * ASM_DIR bits defined by 802.3, respectively. +@@ -80,6 +98,70 @@ static inline bool phylink_autoneg_inban + } + + /** ++ * phylink_pcs_neg_mode() - helper to determine PCS inband mode ++ * @mode: one of %MLO_AN_FIXED, %MLO_AN_PHY, %MLO_AN_INBAND. ++ * @interface: interface mode to be used ++ * @advertising: adertisement ethtool link mode mask ++ * ++ * Determines the negotiation mode to be used by the PCS, and returns ++ * one of: ++ * %PHYLINK_PCS_NEG_NONE: interface mode does not support inband ++ * %PHYLINK_PCS_NEG_OUTBAND: an out of band mode (e.g. reading the PHY) ++ * will be used. ++ * %PHYLINK_PCS_NEG_INBAND_DISABLED: inband mode selected but autoneg disabled ++ * %PHYLINK_PCS_NEG_INBAND_ENABLED: inband mode selected and autoneg enabled ++ * ++ * Note: this is for cases where the PCS itself is involved in negotiation ++ * (e.g. Clause 37, SGMII and similar) not Clause 73. ++ */ ++static inline unsigned int phylink_pcs_neg_mode(unsigned int mode, ++ phy_interface_t interface, ++ const unsigned long *advertising) ++{ ++ unsigned int neg_mode; ++ ++ switch (interface) { ++ case PHY_INTERFACE_MODE_SGMII: ++ case PHY_INTERFACE_MODE_QSGMII: ++ case PHY_INTERFACE_MODE_QUSGMII: ++ case PHY_INTERFACE_MODE_USXGMII: ++ /* These protocols are designed for use with a PHY which ++ * communicates its negotiation result back to the MAC via ++ * inband communication. Note: there exist PHYs that run ++ * with SGMII but do not send the inband data. ++ */ ++ if (!phylink_autoneg_inband(mode)) ++ neg_mode = PHYLINK_PCS_NEG_OUTBAND; ++ else ++ neg_mode = PHYLINK_PCS_NEG_INBAND_ENABLED; ++ break; ++ ++ case PHY_INTERFACE_MODE_1000BASEX: ++ case PHY_INTERFACE_MODE_2500BASEX: ++ /* 1000base-X is designed for use media-side for Fibre ++ * connections, and thus the Autoneg bit needs to be ++ * taken into account. We also do this for 2500base-X ++ * as well, but drivers may not support this, so may ++ * need to override this. ++ */ ++ if (!phylink_autoneg_inband(mode)) ++ neg_mode = PHYLINK_PCS_NEG_OUTBAND; ++ else if (linkmode_test_bit(ETHTOOL_LINK_MODE_Autoneg_BIT, ++ advertising)) ++ neg_mode = PHYLINK_PCS_NEG_INBAND_ENABLED; ++ else ++ neg_mode = PHYLINK_PCS_NEG_INBAND_DISABLED; ++ break; ++ ++ default: ++ neg_mode = PHYLINK_PCS_NEG_NONE; ++ break; ++ } ++ ++ return neg_mode; ++} ++ ++/** + * struct phylink_link_state - link state structure + * @advertising: ethtool bitmask containing advertised link modes + * @lp_advertising: ethtool bitmask containing link partner advertised link +@@ -436,6 +518,7 @@ struct phylink_pcs_ops; + /** + * struct phylink_pcs - PHYLINK PCS instance + * @ops: a pointer to the &struct phylink_pcs_ops structure ++ * @neg_mode: provide PCS neg mode via "mode" argument + * @poll: poll the PCS for link changes + * + * This structure is designed to be embedded within the PCS private data, +@@ -443,6 +526,7 @@ struct phylink_pcs_ops; + */ + struct phylink_pcs { + const struct phylink_pcs_ops *ops; ++ bool neg_mode; + bool poll; + }; + +@@ -460,12 +544,12 @@ struct phylink_pcs_ops { + const struct phylink_link_state *state); + void (*pcs_get_state)(struct phylink_pcs *pcs, + struct phylink_link_state *state); +- int (*pcs_config)(struct phylink_pcs *pcs, unsigned int mode, ++ int (*pcs_config)(struct phylink_pcs *pcs, unsigned int neg_mode, + phy_interface_t interface, + const unsigned long *advertising, + bool permit_pause_to_mac); + void (*pcs_an_restart)(struct phylink_pcs *pcs); +- void (*pcs_link_up)(struct phylink_pcs *pcs, unsigned int mode, ++ void (*pcs_link_up)(struct phylink_pcs *pcs, unsigned int neg_mode, + phy_interface_t interface, int speed, int duplex); + }; + +@@ -508,7 +592,7 @@ void pcs_get_state(struct phylink_pcs *p + /** + * pcs_config() - Configure the PCS mode and advertisement + * @pcs: a pointer to a &struct phylink_pcs. +- * @mode: one of %MLO_AN_FIXED, %MLO_AN_PHY, %MLO_AN_INBAND. ++ * @neg_mode: link negotiation mode (see below) + * @interface: interface mode to be used + * @advertising: adertisement ethtool link mode mask + * @permit_pause_to_mac: permit forwarding pause resolution to MAC +@@ -526,8 +610,12 @@ void pcs_get_state(struct phylink_pcs *p + * For 1000BASE-X, the advertisement should be programmed into the PCS. + * + * For most 10GBASE-R, there is no advertisement. ++ * ++ * The %neg_mode argument should be tested via the phylink_mode_*() family of ++ * functions, or for PCS that set pcs->neg_mode true, should be tested ++ * against the %PHYLINK_PCS_NEG_* definitions. + */ +-int pcs_config(struct phylink_pcs *pcs, unsigned int mode, ++int pcs_config(struct phylink_pcs *pcs, unsigned int neg_mode, + phy_interface_t interface, const unsigned long *advertising, + bool permit_pause_to_mac); + +@@ -543,7 +631,7 @@ void pcs_an_restart(struct phylink_pcs * + /** + * pcs_link_up() - program the PCS for the resolved link configuration + * @pcs: a pointer to a &struct phylink_pcs. +- * @mode: link autonegotiation mode ++ * @neg_mode: link negotiation mode (see below) + * @interface: link &typedef phy_interface_t mode + * @speed: link speed + * @duplex: link duplex +@@ -552,8 +640,12 @@ void pcs_an_restart(struct phylink_pcs * + * the resolved link parameters. For example, a PCS operating in SGMII + * mode without in-band AN needs to be manually configured for the link + * and duplex setting. Otherwise, this should be a no-op. ++ * ++ * The %mode argument should be tested via the phylink_mode_*() family of ++ * functions, or for PCS that set pcs->neg_mode true, should be tested ++ * against the %PHYLINK_PCS_NEG_* definitions. + */ +-void pcs_link_up(struct phylink_pcs *pcs, unsigned int mode, ++void pcs_link_up(struct phylink_pcs *pcs, unsigned int neg_mode, + phy_interface_t interface, int speed, int duplex); + #endif + diff --git a/target/linux/generic/backport-6.1/715-25-v6.5-net-phylink-convert-phylink_mii_c22_pcs_config-to-ne.patch b/target/linux/generic/backport-6.1/715-25-v6.5-net-phylink-convert-phylink_mii_c22_pcs_config-to-ne.patch new file mode 100644 index 000000000..473e9d583 --- /dev/null +++ b/target/linux/generic/backport-6.1/715-25-v6.5-net-phylink-convert-phylink_mii_c22_pcs_config-to-ne.patch @@ -0,0 +1,45 @@ +From cdb08aa0473730315dbc088d5394e59622314034 Mon Sep 17 00:00:00 2001 +From: "Russell King (Oracle)" +Date: Fri, 16 Jun 2023 13:06:27 +0100 +Subject: [PATCH 1/2] net: phylink: convert phylink_mii_c22_pcs_config() to + neg_mode + +Use phylink_pcs_neg_mode() for phylink_mii_c22_pcs_config(). This +results in no functional change. + +Signed-off-by: Russell King (Oracle) +Link: https://lore.kernel.org/r/E1qA8Dj-00EaFG-Mt@rmk-PC.armlinux.org.uk +Signed-off-by: Jakub Kicinski +--- + drivers/net/phy/phylink.c | 9 ++++----- + 1 file changed, 4 insertions(+), 5 deletions(-) + +--- a/drivers/net/phy/phylink.c ++++ b/drivers/net/phy/phylink.c +@@ -3558,6 +3558,7 @@ int phylink_mii_c22_pcs_config(struct md + phy_interface_t interface, + const unsigned long *advertising) + { ++ unsigned int neg_mode; + bool changed = 0; + u16 bmcr; + int ret, adv; +@@ -3571,15 +3572,13 @@ int phylink_mii_c22_pcs_config(struct md + changed = ret; + } + +- /* Ensure ISOLATE bit is disabled */ +- if (mode == MLO_AN_INBAND && +- (interface == PHY_INTERFACE_MODE_SGMII || +- interface == PHY_INTERFACE_MODE_QSGMII || +- linkmode_test_bit(ETHTOOL_LINK_MODE_Autoneg_BIT, advertising))) ++ neg_mode = phylink_pcs_neg_mode(mode, interface, advertising); ++ if (neg_mode == PHYLINK_PCS_NEG_INBAND_ENABLED) + bmcr = BMCR_ANENABLE; + else + bmcr = 0; + ++ /* Configure the inband state. Ensure ISOLATE bit is disabled */ + ret = mdiodev_modify(pcs, MII_BMCR, BMCR_ANENABLE | BMCR_ISOLATE, bmcr); + if (ret < 0) + return ret; diff --git a/target/linux/generic/backport-6.1/715-26-v6.5-net-phylink-pass-neg_mode-into-phylink_mii_c22_pcs_c.patch b/target/linux/generic/backport-6.1/715-26-v6.5-net-phylink-pass-neg_mode-into-phylink_mii_c22_pcs_c.patch new file mode 100644 index 000000000..5572850e9 --- /dev/null +++ b/target/linux/generic/backport-6.1/715-26-v6.5-net-phylink-pass-neg_mode-into-phylink_mii_c22_pcs_c.patch @@ -0,0 +1,187 @@ +From febf2aaf05641f3258cc30e072aff65cffc7c82c Mon Sep 17 00:00:00 2001 +From: "Russell King (Oracle)" +Date: Fri, 16 Jun 2023 13:06:32 +0100 +Subject: [PATCH 2/2] net: phylink: pass neg_mode into + phylink_mii_c22_pcs_config() + +Convert fman_dtsec, xilinx_axienet and pcs-lynx to pass the neg_mode +into phylink_mii_c22_pcs_config(). Where appropriate, drivers are +updated to have neg_mode passed into their pcs_config() and +pcs_link_up() functions. For other drivers, we just hoist the call +to phylink_pcs_neg_mode() to their pcs_config() method out of +phylink_mii_c22_pcs_config(). + +Signed-off-by: Russell King (Oracle) +Link: https://lore.kernel.org/r/E1qA8Do-00EaFM-Ra@rmk-PC.armlinux.org.uk +Signed-off-by: Jakub Kicinski +--- + .../net/ethernet/freescale/fman/fman_dtsec.c | 7 ++++--- + .../net/ethernet/xilinx/xilinx_axienet_main.c | 6 ++++-- + drivers/net/pcs/pcs-lynx.c | 18 ++++++++++++------ + drivers/net/phy/phylink.c | 9 ++++----- + include/linux/phylink.h | 5 +++-- + 5 files changed, 27 insertions(+), 18 deletions(-) + +--- a/drivers/net/ethernet/freescale/fman/fman_dtsec.c ++++ b/drivers/net/ethernet/freescale/fman/fman_dtsec.c +@@ -763,15 +763,15 @@ static void dtsec_pcs_get_state(struct p + phylink_mii_c22_pcs_get_state(dtsec->tbidev, state); + } + +-static int dtsec_pcs_config(struct phylink_pcs *pcs, unsigned int mode, ++static int dtsec_pcs_config(struct phylink_pcs *pcs, unsigned int neg_mode, + phy_interface_t interface, + const unsigned long *advertising, + bool permit_pause_to_mac) + { + struct fman_mac *dtsec = pcs_to_dtsec(pcs); + +- return phylink_mii_c22_pcs_config(dtsec->tbidev, mode, interface, +- advertising); ++ return phylink_mii_c22_pcs_config(dtsec->tbidev, interface, ++ advertising, neg_mode); + } + + static void dtsec_pcs_an_restart(struct phylink_pcs *pcs) +@@ -1447,6 +1447,7 @@ int dtsec_initialization(struct mac_devi + goto _return_fm_mac_free; + } + dtsec->pcs.ops = &dtsec_pcs_ops; ++ dtsec->pcs.neg_mode = true; + dtsec->pcs.poll = true; + + supported = mac_dev->phylink_config.supported_interfaces; +--- a/drivers/net/ethernet/xilinx/xilinx_axienet_main.c ++++ b/drivers/net/ethernet/xilinx/xilinx_axienet_main.c +@@ -1631,7 +1631,7 @@ static void axienet_pcs_an_restart(struc + phylink_mii_c22_pcs_an_restart(pcs_phy); + } + +-static int axienet_pcs_config(struct phylink_pcs *pcs, unsigned int mode, ++static int axienet_pcs_config(struct phylink_pcs *pcs, unsigned int neg_mode, + phy_interface_t interface, + const unsigned long *advertising, + bool permit_pause_to_mac) +@@ -1653,7 +1653,8 @@ static int axienet_pcs_config(struct phy + } + } + +- ret = phylink_mii_c22_pcs_config(pcs_phy, mode, interface, advertising); ++ ret = phylink_mii_c22_pcs_config(pcs_phy, interface, advertising, ++ neg_mode); + if (ret < 0) + netdev_warn(ndev, "Failed to configure PCS: %d\n", ret); + +@@ -2129,6 +2130,7 @@ static int axienet_probe(struct platform + } + of_node_put(np); + lp->pcs.ops = &axienet_pcs_ops; ++ lp->pcs.neg_mode = true; + lp->pcs.poll = true; + } + +--- a/drivers/net/pcs/pcs-lynx.c ++++ b/drivers/net/pcs/pcs-lynx.c +@@ -122,9 +122,10 @@ static void lynx_pcs_get_state(struct ph + state->link, state->an_complete); + } + +-static int lynx_pcs_config_giga(struct mdio_device *pcs, unsigned int mode, ++static int lynx_pcs_config_giga(struct mdio_device *pcs, + phy_interface_t interface, +- const unsigned long *advertising) ++ const unsigned long *advertising, ++ unsigned int neg_mode) + { + u32 link_timer; + u16 if_mode; +@@ -137,8 +138,9 @@ static int lynx_pcs_config_giga(struct m + + if_mode = 0; + } else { ++ /* SGMII and QSGMII */ + if_mode = IF_MODE_SGMII_EN; +- if (mode == MLO_AN_INBAND) { ++ if (neg_mode == PHYLINK_PCS_NEG_INBAND_ENABLED) { + if_mode |= IF_MODE_USE_SGMII_AN; + + /* Adjust link timer for SGMII */ +@@ -154,7 +156,8 @@ static int lynx_pcs_config_giga(struct m + if (err) + return err; + +- return phylink_mii_c22_pcs_config(pcs, mode, interface, advertising); ++ return phylink_mii_c22_pcs_config(pcs, interface, advertising, ++ neg_mode); + } + + static int lynx_pcs_config_usxgmii(struct mdio_device *pcs, unsigned int mode, +@@ -181,13 +184,16 @@ static int lynx_pcs_config(struct phylin + bool permit) + { + struct lynx_pcs *lynx = phylink_pcs_to_lynx(pcs); ++ unsigned int neg_mode; ++ ++ neg_mode = phylink_pcs_neg_mode(mode, ifmode, advertising); + + switch (ifmode) { + case PHY_INTERFACE_MODE_1000BASEX: + case PHY_INTERFACE_MODE_SGMII: + case PHY_INTERFACE_MODE_QSGMII: +- return lynx_pcs_config_giga(lynx->mdio, mode, ifmode, +- advertising); ++ return lynx_pcs_config_giga(lynx->mdio, ifmode, advertising, ++ neg_mode); + case PHY_INTERFACE_MODE_2500BASEX: + if (phylink_autoneg_inband(mode)) { + dev_err(&lynx->mdio->dev, +--- a/drivers/net/phy/phylink.c ++++ b/drivers/net/phy/phylink.c +@@ -3545,20 +3545,20 @@ EXPORT_SYMBOL_GPL(phylink_mii_c22_pcs_en + /** + * phylink_mii_c22_pcs_config() - configure clause 22 PCS + * @pcs: a pointer to a &struct mdio_device. +- * @mode: link autonegotiation mode + * @interface: the PHY interface mode being configured + * @advertising: the ethtool advertisement mask ++ * @neg_mode: PCS negotiation mode + * + * Configure a Clause 22 PCS PHY with the appropriate negotiation + * parameters for the @mode, @interface and @advertising parameters. + * Returns negative error number on failure, zero if the advertisement + * has not changed, or positive if there is a change. + */ +-int phylink_mii_c22_pcs_config(struct mdio_device *pcs, unsigned int mode, ++int phylink_mii_c22_pcs_config(struct mdio_device *pcs, + phy_interface_t interface, +- const unsigned long *advertising) ++ const unsigned long *advertising, ++ unsigned int neg_mode) + { +- unsigned int neg_mode; + bool changed = 0; + u16 bmcr; + int ret, adv; +@@ -3572,7 +3572,6 @@ int phylink_mii_c22_pcs_config(struct md + changed = ret; + } + +- neg_mode = phylink_pcs_neg_mode(mode, interface, advertising); + if (neg_mode == PHYLINK_PCS_NEG_INBAND_ENABLED) + bmcr = BMCR_ANENABLE; + else +--- a/include/linux/phylink.h ++++ b/include/linux/phylink.h +@@ -743,9 +743,10 @@ void phylink_mii_c22_pcs_get_state(struc + struct phylink_link_state *state); + int phylink_mii_c22_pcs_encode_advertisement(phy_interface_t interface, + const unsigned long *advertising); +-int phylink_mii_c22_pcs_config(struct mdio_device *pcs, unsigned int mode, ++int phylink_mii_c22_pcs_config(struct mdio_device *pcs, + phy_interface_t interface, +- const unsigned long *advertising); ++ const unsigned long *advertising, ++ unsigned int neg_mode); + void phylink_mii_c22_pcs_an_restart(struct mdio_device *pcs); + + void phylink_resolve_c73(struct phylink_link_state *state); diff --git a/target/linux/generic/backport-6.1/715-27-v6.5-net-pcs-lynxi-update-PCS-driver-to-use-neg_mode.patch b/target/linux/generic/backport-6.1/715-27-v6.5-net-pcs-lynxi-update-PCS-driver-to-use-neg_mode.patch new file mode 100644 index 000000000..5e0128766 --- /dev/null +++ b/target/linux/generic/backport-6.1/715-27-v6.5-net-pcs-lynxi-update-PCS-driver-to-use-neg_mode.patch @@ -0,0 +1,101 @@ +From 3b2de56a146f34e3f70a84cc3a1897064e445d16 Mon Sep 17 00:00:00 2001 +From: "Russell King (Oracle)" +Date: Fri, 16 Jun 2023 13:06:43 +0100 +Subject: [PATCH] net: pcs: lynxi: update PCS driver to use neg_mode + +Update the Lynxi PCS driver to use neg_mode rather than the mode +argument. This ensures that the link_up() method will always program +the speed and duplex when negotiation is disabled. + +Signed-off-by: Russell King (Oracle) +Link: https://lore.kernel.org/r/E1qA8Dz-00EaFY-5A@rmk-PC.armlinux.org.uk +Signed-off-by: Jakub Kicinski +--- + drivers/net/pcs/pcs-mtk-lynxi.c | 39 ++++++++++++++------------------- + 1 file changed, 16 insertions(+), 23 deletions(-) + +--- a/drivers/net/pcs/pcs-mtk-lynxi.c ++++ b/drivers/net/pcs/pcs-mtk-lynxi.c +@@ -102,13 +102,13 @@ static void mtk_pcs_lynxi_get_state(stru + FIELD_GET(SGMII_LPA, adv)); + } + +-static int mtk_pcs_lynxi_config(struct phylink_pcs *pcs, unsigned int mode, ++static int mtk_pcs_lynxi_config(struct phylink_pcs *pcs, unsigned int neg_mode, + phy_interface_t interface, + const unsigned long *advertising, + bool permit_pause_to_mac) + { + struct mtk_pcs_lynxi *mpcs = pcs_to_mtk_pcs_lynxi(pcs); +- bool mode_changed = false, changed, use_an; ++ bool mode_changed = false, changed; + unsigned int rgc3, sgm_mode, bmcr; + int advertise, link_timer; + +@@ -121,30 +121,22 @@ static int mtk_pcs_lynxi_config(struct p + * we assume that fixes it's speed at bitrate = line rate (in + * other words, 1000Mbps or 2500Mbps). + */ +- if (interface == PHY_INTERFACE_MODE_SGMII) { ++ if (interface == PHY_INTERFACE_MODE_SGMII) + sgm_mode = SGMII_IF_MODE_SGMII; +- if (phylink_autoneg_inband(mode)) { +- sgm_mode |= SGMII_REMOTE_FAULT_DIS | +- SGMII_SPEED_DUPLEX_AN; +- use_an = true; +- } else { +- use_an = false; +- } +- } else if (phylink_autoneg_inband(mode)) { +- /* 1000base-X or 2500base-X autoneg */ +- sgm_mode = SGMII_REMOTE_FAULT_DIS; +- use_an = linkmode_test_bit(ETHTOOL_LINK_MODE_Autoneg_BIT, +- advertising); +- } else { ++ else + /* 1000base-X or 2500base-X without autoneg */ + sgm_mode = 0; +- use_an = false; +- } + +- if (use_an) ++ if (neg_mode & PHYLINK_PCS_NEG_INBAND) ++ sgm_mode |= SGMII_REMOTE_FAULT_DIS; ++ ++ if (neg_mode == PHYLINK_PCS_NEG_INBAND_ENABLED) { ++ if (interface == PHY_INTERFACE_MODE_SGMII) ++ sgm_mode |= SGMII_SPEED_DUPLEX_AN; + bmcr = BMCR_ANENABLE; +- else ++ } else { + bmcr = 0; ++ } + + if (mpcs->interface != interface) { + link_timer = phylink_get_link_timer_ns(interface); +@@ -216,14 +208,15 @@ static void mtk_pcs_lynxi_restart_an(str + regmap_set_bits(mpcs->regmap, SGMSYS_PCS_CONTROL_1, BMCR_ANRESTART); + } + +-static void mtk_pcs_lynxi_link_up(struct phylink_pcs *pcs, unsigned int mode, ++static void mtk_pcs_lynxi_link_up(struct phylink_pcs *pcs, ++ unsigned int neg_mode, + phy_interface_t interface, int speed, + int duplex) + { + struct mtk_pcs_lynxi *mpcs = pcs_to_mtk_pcs_lynxi(pcs); + unsigned int sgm_mode; + +- if (!phylink_autoneg_inband(mode)) { ++ if (neg_mode != PHYLINK_PCS_NEG_INBAND_ENABLED) { + /* Force the speed and duplex setting */ + if (speed == SPEED_10) + sgm_mode = SGMII_SPEED_10; +@@ -286,6 +279,7 @@ struct phylink_pcs *mtk_pcs_lynxi_create + mpcs->regmap = regmap; + mpcs->flags = flags; + mpcs->pcs.ops = &mtk_pcs_lynxi_ops; ++ mpcs->pcs.neg_mode = true; + mpcs->pcs.poll = true; + mpcs->interface = PHY_INTERFACE_MODE_NA; + diff --git a/target/linux/generic/backport-6.1/715-28-v6.4-net-pcs-xpcs-use-Autoneg-bit-rather-than-an_enabled.patch b/target/linux/generic/backport-6.1/715-28-v6.4-net-pcs-xpcs-use-Autoneg-bit-rather-than-an_enabled.patch new file mode 100644 index 000000000..3dd22d291 --- /dev/null +++ b/target/linux/generic/backport-6.1/715-28-v6.4-net-pcs-xpcs-use-Autoneg-bit-rather-than-an_enabled.patch @@ -0,0 +1,55 @@ +From 459fd2f11204c962e3153020f4f56748e0e10afb Mon Sep 17 00:00:00 2001 +From: "Russell King (Oracle)" +Date: Tue, 21 Mar 2023 15:58:49 +0000 +Subject: [PATCH] net: pcs: xpcs: use Autoneg bit rather than an_enabled + +The Autoneg bit in the advertising bitmap and state->an_enabled are +always identical. Thus, we will be removing state->an_enabled. + +Use the Autoneg bit in the advertising bitmap to indicate whether +autonegotiation should be used, rather than using the an_enabled +member which will be going away. + +Signed-off-by: Russell King (Oracle) +Reviewed-by: Simon Horman +Signed-off-by: Jakub Kicinski +--- + drivers/net/pcs/pcs-xpcs.c | 10 +++++++--- + 1 file changed, 7 insertions(+), 3 deletions(-) + +--- a/drivers/net/pcs/pcs-xpcs.c ++++ b/drivers/net/pcs/pcs-xpcs.c +@@ -931,6 +931,7 @@ static int xpcs_get_state_c73(struct dw_ + struct phylink_link_state *state, + const struct xpcs_compat *compat) + { ++ bool an_enabled; + int ret; + + /* Link needs to be read first ... */ +@@ -948,11 +949,13 @@ static int xpcs_get_state_c73(struct dw_ + return xpcs_do_config(xpcs, state->interface, MLO_AN_INBAND, NULL); + } + +- if (state->an_enabled && xpcs_aneg_done_c73(xpcs, state, compat)) { ++ an_enabled = linkmode_test_bit(ETHTOOL_LINK_MODE_Autoneg_BIT, ++ state->advertising); ++ if (an_enabled && xpcs_aneg_done_c73(xpcs, state, compat)) { + state->an_complete = true; + xpcs_read_lpa_c73(xpcs, state); + xpcs_resolve_lpa_c73(xpcs, state); +- } else if (state->an_enabled) { ++ } else if (an_enabled) { + state->link = 0; + } else if (state->link) { + xpcs_resolve_pma(xpcs, state); +@@ -1007,7 +1010,8 @@ static int xpcs_get_state_c37_1000basex( + { + int lpa, bmsr; + +- if (state->an_enabled) { ++ if (linkmode_test_bit(ETHTOOL_LINK_MODE_Autoneg_BIT, ++ state->advertising)) { + /* Reset link state */ + state->link = false; + diff --git a/target/linux/generic/backport-6.1/715-29-v6.4-net-pcs-xpcs-fix-incorrect-number-of-interfaces.patch b/target/linux/generic/backport-6.1/715-29-v6.4-net-pcs-xpcs-fix-incorrect-number-of-interfaces.patch new file mode 100644 index 000000000..7cae8515f --- /dev/null +++ b/target/linux/generic/backport-6.1/715-29-v6.4-net-pcs-xpcs-fix-incorrect-number-of-interfaces.patch @@ -0,0 +1,30 @@ +From 43fb622d91a9f408322735d2f736495c1009f575 Mon Sep 17 00:00:00 2001 +From: "Russell King (Oracle)" +Date: Tue, 9 May 2023 12:50:04 +0100 +Subject: [PATCH] net: pcs: xpcs: fix incorrect number of interfaces + +In synopsys_xpcs_compat[], the DW_XPCS_2500BASEX entry was setting +the number of interfaces using the xpcs_2500basex_features array +rather than xpcs_2500basex_interfaces. This causes us to overflow +the array of interfaces. Fix this. + +Fixes: f27abde3042a ("net: pcs: add 2500BASEX support for Intel mGbE controller") +Signed-off-by: Russell King (Oracle) +Reviewed-by: Andrew Lunn +Reviewed-by: Leon Romanovsky +Signed-off-by: David S. Miller +--- + drivers/net/pcs/pcs-xpcs.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/drivers/net/pcs/pcs-xpcs.c ++++ b/drivers/net/pcs/pcs-xpcs.c +@@ -1211,7 +1211,7 @@ static const struct xpcs_compat synopsys + [DW_XPCS_2500BASEX] = { + .supported = xpcs_2500basex_features, + .interface = xpcs_2500basex_interfaces, +- .num_interfaces = ARRAY_SIZE(xpcs_2500basex_features), ++ .num_interfaces = ARRAY_SIZE(xpcs_2500basex_interfaces), + .an_mode = DW_2500BASEX, + }, + }; diff --git a/target/linux/generic/backport-6.1/715-v6.9-01-net-phy-qcom-qca808x-fix-logic-error-in-LED-brightne.patch b/target/linux/generic/backport-6.1/715-v6.9-01-net-phy-qcom-qca808x-fix-logic-error-in-LED-brightne.patch new file mode 100644 index 000000000..0a7b5358f --- /dev/null +++ b/target/linux/generic/backport-6.1/715-v6.9-01-net-phy-qcom-qca808x-fix-logic-error-in-LED-brightne.patch @@ -0,0 +1,36 @@ +From f2ec98566775dd4341ec1dcf93aa5859c60de826 Mon Sep 17 00:00:00 2001 +From: Christian Marangi +Date: Thu, 1 Feb 2024 14:46:00 +0100 +Subject: [PATCH 1/2] net: phy: qcom: qca808x: fix logic error in LED + brightness set + +In switching to using phy_modify_mmd and a more short version of the +LED ON/OFF condition in later revision, it was made a logic error where + +value ? QCA808X_LED_FORCE_ON : QCA808X_LED_FORCE_OFF is always true as +value is always OR with QCA808X_LED_FORCE_EN due to missing () +resulting in the testing condition being QCA808X_LED_FORCE_EN | value. + +Add the () to apply the correct condition and restore correct +functionality of the brightness ON/OFF. + +Fixes: 7196062b64ee ("net: phy: at803x: add LED support for qca808x") +Signed-off-by: Christian Marangi +Signed-off-by: David S. Miller +--- + drivers/net/phy/qcom/qca808x.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +--- a/drivers/net/phy/qcom/qca808x.c ++++ b/drivers/net/phy/qcom/qca808x.c +@@ -820,8 +820,8 @@ static int qca808x_led_brightness_set(st + + return phy_modify_mmd(phydev, MDIO_MMD_AN, reg, + QCA808X_LED_FORCE_EN | QCA808X_LED_FORCE_MODE_MASK, +- QCA808X_LED_FORCE_EN | value ? QCA808X_LED_FORCE_ON : +- QCA808X_LED_FORCE_OFF); ++ QCA808X_LED_FORCE_EN | (value ? QCA808X_LED_FORCE_ON : ++ QCA808X_LED_FORCE_OFF)); + } + + static int qca808x_led_blink_set(struct phy_device *phydev, u8 index, diff --git a/target/linux/generic/backport-6.1/715-v6.9-02-net-phy-qcom-qca808x-default-to-LED-active-High-if-n.patch b/target/linux/generic/backport-6.1/715-v6.9-02-net-phy-qcom-qca808x-default-to-LED-active-High-if-n.patch new file mode 100644 index 000000000..e32ed7f77 --- /dev/null +++ b/target/linux/generic/backport-6.1/715-v6.9-02-net-phy-qcom-qca808x-default-to-LED-active-High-if-n.patch @@ -0,0 +1,41 @@ +From f203c8c77c7616c099647636f4c67d59a45fe8a2 Mon Sep 17 00:00:00 2001 +From: Christian Marangi +Date: Thu, 1 Feb 2024 14:46:01 +0100 +Subject: [PATCH 2/2] net: phy: qcom: qca808x: default to LED active High if + not set + +qca808x PHY provide support for the led_polarity_set OP to configure +and apply the active-low property but on PHY reset, the Active High bit +is not set resulting in the LED driven as active-low. + +To fix this, check if active-low is not set in DT and enable Active High +polarity by default to restore correct funcionality of the LED. + +Fixes: 7196062b64ee ("net: phy: at803x: add LED support for qca808x") +Signed-off-by: Christian Marangi +Signed-off-by: David S. Miller +--- + drivers/net/phy/qcom/qca808x.c | 10 ++++++++++ + 1 file changed, 10 insertions(+) + +--- a/drivers/net/phy/qcom/qca808x.c ++++ b/drivers/net/phy/qcom/qca808x.c +@@ -290,8 +290,18 @@ static int qca808x_probe(struct phy_devi + + static int qca808x_config_init(struct phy_device *phydev) + { ++ struct qca808x_priv *priv = phydev->priv; + int ret; + ++ /* Default to LED Active High if active-low not in DT */ ++ if (priv->led_polarity_mode == -1) { ++ ret = phy_set_bits_mmd(phydev, MDIO_MMD_AN, ++ QCA808X_MMD7_LED_POLARITY_CTRL, ++ QCA808X_LED_ACTIVE_HIGH); ++ if (ret) ++ return ret; ++ } ++ + /* Active adc&vga on 802.3az for the link 1000M and 100M */ + ret = phy_modify_mmd(phydev, MDIO_MMD_PCS, QCA808X_PHY_MMD3_ADDR_CLD_CTRL7, + QCA808X_8023AZ_AFE_CTRL_MASK, QCA808X_8023AZ_AFE_EN); diff --git a/target/linux/generic/backport-6.1/716-v6.9-02-net-phy-add-support-for-scanning-PHY-in-PHY-packages.patch b/target/linux/generic/backport-6.1/716-v6.9-02-net-phy-add-support-for-scanning-PHY-in-PHY-packages.patch new file mode 100644 index 000000000..b355031aa --- /dev/null +++ b/target/linux/generic/backport-6.1/716-v6.9-02-net-phy-add-support-for-scanning-PHY-in-PHY-packages.patch @@ -0,0 +1,211 @@ +From 385ef48f468696d6d172eb367656a3466fa0408d Mon Sep 17 00:00:00 2001 +From: Christian Marangi +Date: Tue, 6 Feb 2024 18:31:05 +0100 +Subject: [PATCH 02/10] net: phy: add support for scanning PHY in PHY packages + nodes + +Add support for scanning PHY in PHY package nodes. PHY packages nodes +are just container for actual PHY on the MDIO bus. + +Their PHY address defined in the PHY package node are absolute and +reflect the address on the MDIO bus. + +mdio_bus.c and of_mdio.c is updated to now support and parse also +PHY package subnode by checking if the node name match +"ethernet-phy-package". + +As PHY package reg is mandatory and each PHY in the PHY package must +have a reg, every invalid PHY Package node is ignored and will be +skipped by the autoscan fallback. + +Signed-off-by: Christian Marangi +Reviewed-by: Andrew Lunn +Signed-off-by: David S. Miller +--- + drivers/net/mdio/of_mdio.c | 79 +++++++++++++++++++++++++++----------- + drivers/net/phy/mdio_bus.c | 44 +++++++++++++++++---- + 2 files changed, 92 insertions(+), 31 deletions(-) + +--- a/drivers/net/mdio/of_mdio.c ++++ b/drivers/net/mdio/of_mdio.c +@@ -138,6 +138,53 @@ bool of_mdiobus_child_is_phy(struct devi + } + EXPORT_SYMBOL(of_mdiobus_child_is_phy); + ++static int __of_mdiobus_parse_phys(struct mii_bus *mdio, struct device_node *np, ++ bool *scanphys) ++{ ++ struct device_node *child; ++ int addr, rc = 0; ++ ++ /* Loop over the child nodes and register a phy_device for each phy */ ++ for_each_available_child_of_node(np, child) { ++ if (of_node_name_eq(child, "ethernet-phy-package")) { ++ /* Ignore invalid ethernet-phy-package node */ ++ if (!of_find_property(child, "reg", NULL)) ++ continue; ++ ++ rc = __of_mdiobus_parse_phys(mdio, child, NULL); ++ if (rc && rc != -ENODEV) ++ goto exit; ++ ++ continue; ++ } ++ ++ addr = of_mdio_parse_addr(&mdio->dev, child); ++ if (addr < 0) { ++ /* Skip scanning for invalid ethernet-phy-package node */ ++ if (scanphys) ++ *scanphys = true; ++ continue; ++ } ++ ++ if (of_mdiobus_child_is_phy(child)) ++ rc = of_mdiobus_register_phy(mdio, child, addr); ++ else ++ rc = of_mdiobus_register_device(mdio, child, addr); ++ ++ if (rc == -ENODEV) ++ dev_err(&mdio->dev, ++ "MDIO device at address %d is missing.\n", ++ addr); ++ else if (rc) ++ goto exit; ++ } ++ ++ return 0; ++exit: ++ of_node_put(child); ++ return rc; ++} ++ + /** + * __of_mdiobus_register - Register mii_bus and create PHYs from the device tree + * @mdio: pointer to mii_bus structure +@@ -179,33 +226,18 @@ int __of_mdiobus_register(struct mii_bus + return rc; + + /* Loop over the child nodes and register a phy_device for each phy */ +- for_each_available_child_of_node(np, child) { +- addr = of_mdio_parse_addr(&mdio->dev, child); +- if (addr < 0) { +- scanphys = true; +- continue; +- } +- +- if (of_mdiobus_child_is_phy(child)) +- rc = of_mdiobus_register_phy(mdio, child, addr); +- else +- rc = of_mdiobus_register_device(mdio, child, addr); +- +- if (rc == -ENODEV) +- dev_err(&mdio->dev, +- "MDIO device at address %d is missing.\n", +- addr); +- else if (rc) +- goto unregister; +- } ++ rc = __of_mdiobus_parse_phys(mdio, np, &scanphys); ++ if (rc) ++ goto unregister; + + if (!scanphys) + return 0; + + /* auto scan for PHYs with empty reg property */ + for_each_available_child_of_node(np, child) { +- /* Skip PHYs with reg property set */ +- if (of_find_property(child, "reg", NULL)) ++ /* Skip PHYs with reg property set or ethernet-phy-package node */ ++ if (of_find_property(child, "reg", NULL) || ++ of_node_name_eq(child, "ethernet-phy-package")) + continue; + + for (addr = 0; addr < PHY_MAX_ADDR; addr++) { +@@ -226,15 +258,16 @@ int __of_mdiobus_register(struct mii_bus + if (!rc) + break; + if (rc != -ENODEV) +- goto unregister; ++ goto put_unregister; + } + } + } + + return 0; + +-unregister: ++put_unregister: + of_node_put(child); ++unregister: + mdiobus_unregister(mdio); + return rc; + } +--- a/drivers/net/phy/mdio_bus.c ++++ b/drivers/net/phy/mdio_bus.c +@@ -448,19 +448,34 @@ EXPORT_SYMBOL(of_mdio_find_bus); + * found, set the of_node pointer for the mdio device. This allows + * auto-probed phy devices to be supplied with information passed in + * via DT. ++ * If a PHY package is found, PHY is searched also there. + */ +-static void of_mdiobus_link_mdiodev(struct mii_bus *bus, +- struct mdio_device *mdiodev) ++static int of_mdiobus_find_phy(struct device *dev, struct mdio_device *mdiodev, ++ struct device_node *np) + { +- struct device *dev = &mdiodev->dev; + struct device_node *child; + +- if (dev->of_node || !bus->dev.of_node) +- return; +- +- for_each_available_child_of_node(bus->dev.of_node, child) { ++ for_each_available_child_of_node(np, child) { + int addr; + ++ if (of_node_name_eq(child, "ethernet-phy-package")) { ++ /* Validate PHY package reg presence */ ++ if (!of_find_property(child, "reg", NULL)) { ++ of_node_put(child); ++ return -EINVAL; ++ } ++ ++ if (!of_mdiobus_find_phy(dev, mdiodev, child)) { ++ /* The refcount for the PHY package will be ++ * incremented later when PHY join the Package. ++ */ ++ of_node_put(child); ++ return 0; ++ } ++ ++ continue; ++ } ++ + addr = of_mdio_parse_addr(dev, child); + if (addr < 0) + continue; +@@ -470,9 +485,22 @@ static void of_mdiobus_link_mdiodev(stru + /* The refcount on "child" is passed to the mdio + * device. Do _not_ use of_node_put(child) here. + */ +- return; ++ return 0; + } + } ++ ++ return -ENODEV; ++} ++ ++static void of_mdiobus_link_mdiodev(struct mii_bus *bus, ++ struct mdio_device *mdiodev) ++{ ++ struct device *dev = &mdiodev->dev; ++ ++ if (dev->of_node || !bus->dev.of_node) ++ return; ++ ++ of_mdiobus_find_phy(dev, mdiodev, bus->dev.of_node); + } + #else /* !IS_ENABLED(CONFIG_OF_MDIO) */ + static inline void of_mdiobus_link_mdiodev(struct mii_bus *mdio, diff --git a/target/linux/generic/backport-6.1/716-v6.9-03-net-phy-add-devm-of_phy_package_join-helper.patch b/target/linux/generic/backport-6.1/716-v6.9-03-net-phy-add-devm-of_phy_package_join-helper.patch new file mode 100644 index 000000000..3c7bf6c13 --- /dev/null +++ b/target/linux/generic/backport-6.1/716-v6.9-03-net-phy-add-devm-of_phy_package_join-helper.patch @@ -0,0 +1,185 @@ +From 471e8fd3afcef5a9f9089f0bd21965ad9ba35c91 Mon Sep 17 00:00:00 2001 +From: Christian Marangi +Date: Tue, 6 Feb 2024 18:31:06 +0100 +Subject: [PATCH 03/10] net: phy: add devm/of_phy_package_join helper + +Add devm/of_phy_package_join helper to join PHYs in a PHY package. These +are variant of the manual phy_package_join with the difference that +these will use DT nodes to derive the base_addr instead of manually +passing an hardcoded value. + +An additional value is added in phy_package_shared, "np" to reference +the PHY package node pointer in specific PHY driver probe_once and +config_init_once functions to make use of additional specific properties +defined in the PHY package node in DT. + +The np value is filled only with of_phy_package_join if a valid PHY +package node is found. A valid PHY package node must have the node name +set to "ethernet-phy-package". + +Signed-off-by: Christian Marangi +Reviewed-by: Andrew Lunn +Signed-off-by: David S. Miller +--- + drivers/net/phy/phy_device.c | 96 ++++++++++++++++++++++++++++++++++++ + include/linux/phy.h | 6 +++ + 2 files changed, 102 insertions(+) + +--- a/drivers/net/phy/phy_device.c ++++ b/drivers/net/phy/phy_device.c +@@ -1648,6 +1648,7 @@ int phy_package_join(struct phy_device * + shared->priv_size = priv_size; + } + shared->base_addr = base_addr; ++ shared->np = NULL; + refcount_set(&shared->refcnt, 1); + bus->shared[base_addr] = shared; + } else { +@@ -1671,6 +1672,63 @@ err_unlock: + EXPORT_SYMBOL_GPL(phy_package_join); + + /** ++ * of_phy_package_join - join a common PHY group in PHY package ++ * @phydev: target phy_device struct ++ * @priv_size: if non-zero allocate this amount of bytes for private data ++ * ++ * This is a variant of phy_package_join for PHY package defined in DT. ++ * ++ * The parent node of the @phydev is checked as a valid PHY package node ++ * structure (by matching the node name "ethernet-phy-package") and the ++ * base_addr for the PHY package is passed to phy_package_join. ++ * ++ * With this configuration the shared struct will also have the np value ++ * filled to use additional DT defined properties in PHY specific ++ * probe_once and config_init_once PHY package OPs. ++ * ++ * Returns < 0 on error, 0 on success. Esp. calling phy_package_join() ++ * with the same cookie but a different priv_size is an error. Or a parent ++ * node is not detected or is not valid or doesn't match the expected node ++ * name for PHY package. ++ */ ++int of_phy_package_join(struct phy_device *phydev, size_t priv_size) ++{ ++ struct device_node *node = phydev->mdio.dev.of_node; ++ struct device_node *package_node; ++ u32 base_addr; ++ int ret; ++ ++ if (!node) ++ return -EINVAL; ++ ++ package_node = of_get_parent(node); ++ if (!package_node) ++ return -EINVAL; ++ ++ if (!of_node_name_eq(package_node, "ethernet-phy-package")) { ++ ret = -EINVAL; ++ goto exit; ++ } ++ ++ if (of_property_read_u32(package_node, "reg", &base_addr)) { ++ ret = -EINVAL; ++ goto exit; ++ } ++ ++ ret = phy_package_join(phydev, base_addr, priv_size); ++ if (ret) ++ goto exit; ++ ++ phydev->shared->np = package_node; ++ ++ return 0; ++exit: ++ of_node_put(package_node); ++ return ret; ++} ++EXPORT_SYMBOL_GPL(of_phy_package_join); ++ ++/** + * phy_package_leave - leave a common PHY group + * @phydev: target phy_device struct + * +@@ -1686,6 +1744,10 @@ void phy_package_leave(struct phy_device + if (!shared) + return; + ++ /* Decrease the node refcount on leave if present */ ++ if (shared->np) ++ of_node_put(shared->np); ++ + if (refcount_dec_and_mutex_lock(&shared->refcnt, &bus->shared_lock)) { + bus->shared[shared->base_addr] = NULL; + mutex_unlock(&bus->shared_lock); +@@ -1739,6 +1801,40 @@ int devm_phy_package_join(struct device + EXPORT_SYMBOL_GPL(devm_phy_package_join); + + /** ++ * devm_of_phy_package_join - resource managed of_phy_package_join() ++ * @dev: device that is registering this PHY package ++ * @phydev: target phy_device struct ++ * @priv_size: if non-zero allocate this amount of bytes for private data ++ * ++ * Managed of_phy_package_join(). Shared storage fetched by this function, ++ * phy_package_leave() is automatically called on driver detach. See ++ * of_phy_package_join() for more information. ++ */ ++int devm_of_phy_package_join(struct device *dev, struct phy_device *phydev, ++ size_t priv_size) ++{ ++ struct phy_device **ptr; ++ int ret; ++ ++ ptr = devres_alloc(devm_phy_package_leave, sizeof(*ptr), ++ GFP_KERNEL); ++ if (!ptr) ++ return -ENOMEM; ++ ++ ret = of_phy_package_join(phydev, priv_size); ++ ++ if (!ret) { ++ *ptr = phydev; ++ devres_add(dev, ptr); ++ } else { ++ devres_free(ptr); ++ } ++ ++ return ret; ++} ++EXPORT_SYMBOL_GPL(devm_of_phy_package_join); ++ ++/** + * phy_detach - detach a PHY device from its network device + * @phydev: target phy_device struct + * +--- a/include/linux/phy.h ++++ b/include/linux/phy.h +@@ -321,6 +321,7 @@ struct mdio_bus_stats { + * struct phy_package_shared - Shared information in PHY packages + * @base_addr: Base PHY address of PHY package used to combine PHYs + * in one package and for offset calculation of phy_package_read/write ++ * @np: Pointer to the Device Node if PHY package defined in DT + * @refcnt: Number of PHYs connected to this shared data + * @flags: Initialization of PHY package + * @priv_size: Size of the shared private data @priv +@@ -332,6 +333,8 @@ struct mdio_bus_stats { + */ + struct phy_package_shared { + u8 base_addr; ++ /* With PHY package defined in DT this points to the PHY package node */ ++ struct device_node *np; + refcount_t refcnt; + unsigned long flags; + size_t priv_size; +@@ -1793,9 +1796,12 @@ int phy_ethtool_set_link_ksettings(struc + const struct ethtool_link_ksettings *cmd); + int phy_ethtool_nway_reset(struct net_device *ndev); + int phy_package_join(struct phy_device *phydev, int base_addr, size_t priv_size); ++int of_phy_package_join(struct phy_device *phydev, size_t priv_size); + void phy_package_leave(struct phy_device *phydev); + int devm_phy_package_join(struct device *dev, struct phy_device *phydev, + int base_addr, size_t priv_size); ++int devm_of_phy_package_join(struct device *dev, struct phy_device *phydev, ++ size_t priv_size); + + #if IS_ENABLED(CONFIG_PHYLIB) + int __init mdio_bus_init(void); diff --git a/target/linux/generic/backport-6.1/716-v6.9-04-net-phy-qcom-move-more-function-to-shared-library.patch b/target/linux/generic/backport-6.1/716-v6.9-04-net-phy-qcom-move-more-function-to-shared-library.patch new file mode 100644 index 000000000..e93572563 --- /dev/null +++ b/target/linux/generic/backport-6.1/716-v6.9-04-net-phy-qcom-move-more-function-to-shared-library.patch @@ -0,0 +1,583 @@ +From 737eb75a815f9c08dcbb6631db57f4f4b0540a5b Mon Sep 17 00:00:00 2001 +From: Christian Marangi +Date: Tue, 6 Feb 2024 18:31:07 +0100 +Subject: [PATCH 04/10] net: phy: qcom: move more function to shared library + +Move more function to shared library in preparation for introduction of +new PHY Family qca807x that will make use of both functions from at803x +and qca808x as it's a transition PHY with some implementation of at803x +and some from the new qca808x. + +Signed-off-by: Christian Marangi +Reviewed-by: Andrew Lunn +Signed-off-by: David S. Miller +--- + drivers/net/phy/qcom/at803x.c | 35 ----- + drivers/net/phy/qcom/qca808x.c | 205 ---------------------------- + drivers/net/phy/qcom/qcom-phy-lib.c | 193 ++++++++++++++++++++++++++ + drivers/net/phy/qcom/qcom.h | 51 +++++++ + 4 files changed, 244 insertions(+), 240 deletions(-) + +--- a/drivers/net/phy/qcom/at803x.c ++++ b/drivers/net/phy/qcom/at803x.c +@@ -504,41 +504,6 @@ static void at803x_link_change_notify(st + } + } + +-static int at803x_read_status(struct phy_device *phydev) +-{ +- struct at803x_ss_mask ss_mask = { 0 }; +- int err, old_link = phydev->link; +- +- /* Update the link, but return if there was an error */ +- err = genphy_update_link(phydev); +- if (err) +- return err; +- +- /* why bother the PHY if nothing can have changed */ +- if (phydev->autoneg == AUTONEG_ENABLE && old_link && phydev->link) +- return 0; +- +- phydev->speed = SPEED_UNKNOWN; +- phydev->duplex = DUPLEX_UNKNOWN; +- phydev->pause = 0; +- phydev->asym_pause = 0; +- +- err = genphy_read_lpa(phydev); +- if (err < 0) +- return err; +- +- ss_mask.speed_mask = AT803X_SS_SPEED_MASK; +- ss_mask.speed_shift = __bf_shf(AT803X_SS_SPEED_MASK); +- err = at803x_read_specific_status(phydev, ss_mask); +- if (err < 0) +- return err; +- +- if (phydev->autoneg == AUTONEG_ENABLE && phydev->autoneg_complete) +- phy_resolve_aneg_pause(phydev); +- +- return 0; +-} +- + static int at803x_config_aneg(struct phy_device *phydev) + { + struct at803x_priv *priv = phydev->priv; +--- a/drivers/net/phy/qcom/qca808x.c ++++ b/drivers/net/phy/qcom/qca808x.c +@@ -2,7 +2,6 @@ + + #include + #include +-#include + + #include "qcom.h" + +@@ -63,55 +62,6 @@ + #define QCA808X_DBG_AN_TEST 0xb + #define QCA808X_HIBERNATION_EN BIT(15) + +-#define QCA808X_CDT_ENABLE_TEST BIT(15) +-#define QCA808X_CDT_INTER_CHECK_DIS BIT(13) +-#define QCA808X_CDT_STATUS BIT(11) +-#define QCA808X_CDT_LENGTH_UNIT BIT(10) +- +-#define QCA808X_MMD3_CDT_STATUS 0x8064 +-#define QCA808X_MMD3_CDT_DIAG_PAIR_A 0x8065 +-#define QCA808X_MMD3_CDT_DIAG_PAIR_B 0x8066 +-#define QCA808X_MMD3_CDT_DIAG_PAIR_C 0x8067 +-#define QCA808X_MMD3_CDT_DIAG_PAIR_D 0x8068 +-#define QCA808X_CDT_DIAG_LENGTH_SAME_SHORT GENMASK(15, 8) +-#define QCA808X_CDT_DIAG_LENGTH_CROSS_SHORT GENMASK(7, 0) +- +-#define QCA808X_CDT_CODE_PAIR_A GENMASK(15, 12) +-#define QCA808X_CDT_CODE_PAIR_B GENMASK(11, 8) +-#define QCA808X_CDT_CODE_PAIR_C GENMASK(7, 4) +-#define QCA808X_CDT_CODE_PAIR_D GENMASK(3, 0) +- +-#define QCA808X_CDT_STATUS_STAT_TYPE GENMASK(1, 0) +-#define QCA808X_CDT_STATUS_STAT_FAIL FIELD_PREP_CONST(QCA808X_CDT_STATUS_STAT_TYPE, 0) +-#define QCA808X_CDT_STATUS_STAT_NORMAL FIELD_PREP_CONST(QCA808X_CDT_STATUS_STAT_TYPE, 1) +-#define QCA808X_CDT_STATUS_STAT_SAME_OPEN FIELD_PREP_CONST(QCA808X_CDT_STATUS_STAT_TYPE, 2) +-#define QCA808X_CDT_STATUS_STAT_SAME_SHORT FIELD_PREP_CONST(QCA808X_CDT_STATUS_STAT_TYPE, 3) +- +-#define QCA808X_CDT_STATUS_STAT_MDI GENMASK(3, 2) +-#define QCA808X_CDT_STATUS_STAT_MDI1 FIELD_PREP_CONST(QCA808X_CDT_STATUS_STAT_MDI, 1) +-#define QCA808X_CDT_STATUS_STAT_MDI2 FIELD_PREP_CONST(QCA808X_CDT_STATUS_STAT_MDI, 2) +-#define QCA808X_CDT_STATUS_STAT_MDI3 FIELD_PREP_CONST(QCA808X_CDT_STATUS_STAT_MDI, 3) +- +-/* NORMAL are MDI with type set to 0 */ +-#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_NORMAL QCA808X_CDT_STATUS_STAT_MDI1 +-#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_OPEN (QCA808X_CDT_STATUS_STAT_SAME_OPEN |\ +- QCA808X_CDT_STATUS_STAT_MDI1) +-#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_SHORT (QCA808X_CDT_STATUS_STAT_SAME_SHORT |\ +- QCA808X_CDT_STATUS_STAT_MDI1) +-#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_NORMAL QCA808X_CDT_STATUS_STAT_MDI2 +-#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_OPEN (QCA808X_CDT_STATUS_STAT_SAME_OPEN |\ +- QCA808X_CDT_STATUS_STAT_MDI2) +-#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_SHORT (QCA808X_CDT_STATUS_STAT_SAME_SHORT |\ +- QCA808X_CDT_STATUS_STAT_MDI2) +-#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_NORMAL QCA808X_CDT_STATUS_STAT_MDI3 +-#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_OPEN (QCA808X_CDT_STATUS_STAT_SAME_OPEN |\ +- QCA808X_CDT_STATUS_STAT_MDI3) +-#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_SHORT (QCA808X_CDT_STATUS_STAT_SAME_SHORT |\ +- QCA808X_CDT_STATUS_STAT_MDI3) +- +-/* Added for reference of existence but should be handled by wait_for_completion already */ +-#define QCA808X_CDT_STATUS_STAT_BUSY (BIT(1) | BIT(3)) +- + #define QCA808X_MMD7_LED_GLOBAL 0x8073 + #define QCA808X_LED_BLINK_1 GENMASK(11, 6) + #define QCA808X_LED_BLINK_2 GENMASK(5, 0) +@@ -406,86 +356,6 @@ static int qca808x_soft_reset(struct phy + return ret; + } + +-static bool qca808x_cdt_fault_length_valid(int cdt_code) +-{ +- switch (cdt_code) { +- case QCA808X_CDT_STATUS_STAT_SAME_SHORT: +- case QCA808X_CDT_STATUS_STAT_SAME_OPEN: +- case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_NORMAL: +- case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_OPEN: +- case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_SHORT: +- case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_NORMAL: +- case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_OPEN: +- case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_SHORT: +- case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_NORMAL: +- case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_OPEN: +- case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_SHORT: +- return true; +- default: +- return false; +- } +-} +- +-static int qca808x_cable_test_result_trans(int cdt_code) +-{ +- switch (cdt_code) { +- case QCA808X_CDT_STATUS_STAT_NORMAL: +- return ETHTOOL_A_CABLE_RESULT_CODE_OK; +- case QCA808X_CDT_STATUS_STAT_SAME_SHORT: +- return ETHTOOL_A_CABLE_RESULT_CODE_SAME_SHORT; +- case QCA808X_CDT_STATUS_STAT_SAME_OPEN: +- return ETHTOOL_A_CABLE_RESULT_CODE_OPEN; +- case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_NORMAL: +- case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_OPEN: +- case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_SHORT: +- case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_NORMAL: +- case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_OPEN: +- case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_SHORT: +- case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_NORMAL: +- case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_OPEN: +- case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_SHORT: +- return ETHTOOL_A_CABLE_RESULT_CODE_CROSS_SHORT; +- case QCA808X_CDT_STATUS_STAT_FAIL: +- default: +- return ETHTOOL_A_CABLE_RESULT_CODE_UNSPEC; +- } +-} +- +-static int qca808x_cdt_fault_length(struct phy_device *phydev, int pair, +- int result) +-{ +- int val; +- u32 cdt_length_reg = 0; +- +- switch (pair) { +- case ETHTOOL_A_CABLE_PAIR_A: +- cdt_length_reg = QCA808X_MMD3_CDT_DIAG_PAIR_A; +- break; +- case ETHTOOL_A_CABLE_PAIR_B: +- cdt_length_reg = QCA808X_MMD3_CDT_DIAG_PAIR_B; +- break; +- case ETHTOOL_A_CABLE_PAIR_C: +- cdt_length_reg = QCA808X_MMD3_CDT_DIAG_PAIR_C; +- break; +- case ETHTOOL_A_CABLE_PAIR_D: +- cdt_length_reg = QCA808X_MMD3_CDT_DIAG_PAIR_D; +- break; +- default: +- return -EINVAL; +- } +- +- val = phy_read_mmd(phydev, MDIO_MMD_PCS, cdt_length_reg); +- if (val < 0) +- return val; +- +- if (result == ETHTOOL_A_CABLE_RESULT_CODE_SAME_SHORT) +- val = FIELD_GET(QCA808X_CDT_DIAG_LENGTH_SAME_SHORT, val); +- else +- val = FIELD_GET(QCA808X_CDT_DIAG_LENGTH_CROSS_SHORT, val); +- +- return at803x_cdt_fault_length(val); +-} +- + static int qca808x_cable_test_start(struct phy_device *phydev) + { + int ret; +@@ -526,81 +396,6 @@ static int qca808x_cable_test_start(stru + + return 0; + } +- +-static int qca808x_cable_test_get_pair_status(struct phy_device *phydev, u8 pair, +- u16 status) +-{ +- int length, result; +- u16 pair_code; +- +- switch (pair) { +- case ETHTOOL_A_CABLE_PAIR_A: +- pair_code = FIELD_GET(QCA808X_CDT_CODE_PAIR_A, status); +- break; +- case ETHTOOL_A_CABLE_PAIR_B: +- pair_code = FIELD_GET(QCA808X_CDT_CODE_PAIR_B, status); +- break; +- case ETHTOOL_A_CABLE_PAIR_C: +- pair_code = FIELD_GET(QCA808X_CDT_CODE_PAIR_C, status); +- break; +- case ETHTOOL_A_CABLE_PAIR_D: +- pair_code = FIELD_GET(QCA808X_CDT_CODE_PAIR_D, status); +- break; +- default: +- return -EINVAL; +- } +- +- result = qca808x_cable_test_result_trans(pair_code); +- ethnl_cable_test_result(phydev, pair, result); +- +- if (qca808x_cdt_fault_length_valid(pair_code)) { +- length = qca808x_cdt_fault_length(phydev, pair, result); +- ethnl_cable_test_fault_length(phydev, pair, length); +- } +- +- return 0; +-} +- +-static int qca808x_cable_test_get_status(struct phy_device *phydev, bool *finished) +-{ +- int ret, val; +- +- *finished = false; +- +- val = QCA808X_CDT_ENABLE_TEST | +- QCA808X_CDT_LENGTH_UNIT; +- ret = at803x_cdt_start(phydev, val); +- if (ret) +- return ret; +- +- ret = at803x_cdt_wait_for_completion(phydev, QCA808X_CDT_ENABLE_TEST); +- if (ret) +- return ret; +- +- val = phy_read_mmd(phydev, MDIO_MMD_PCS, QCA808X_MMD3_CDT_STATUS); +- if (val < 0) +- return val; +- +- ret = qca808x_cable_test_get_pair_status(phydev, ETHTOOL_A_CABLE_PAIR_A, val); +- if (ret) +- return ret; +- +- ret = qca808x_cable_test_get_pair_status(phydev, ETHTOOL_A_CABLE_PAIR_B, val); +- if (ret) +- return ret; +- +- ret = qca808x_cable_test_get_pair_status(phydev, ETHTOOL_A_CABLE_PAIR_C, val); +- if (ret) +- return ret; +- +- ret = qca808x_cable_test_get_pair_status(phydev, ETHTOOL_A_CABLE_PAIR_D, val); +- if (ret) +- return ret; +- +- *finished = true; +- +- return 0; +-} + + static int qca808x_get_features(struct phy_device *phydev) + { +--- a/drivers/net/phy/qcom/qcom-phy-lib.c ++++ b/drivers/net/phy/qcom/qcom-phy-lib.c +@@ -5,6 +5,7 @@ + + #include + #include ++#include + + #include "qcom.h" + +@@ -311,6 +312,42 @@ int at803x_prepare_config_aneg(struct ph + } + EXPORT_SYMBOL_GPL(at803x_prepare_config_aneg); + ++int at803x_read_status(struct phy_device *phydev) ++{ ++ struct at803x_ss_mask ss_mask = { 0 }; ++ int err, old_link = phydev->link; ++ ++ /* Update the link, but return if there was an error */ ++ err = genphy_update_link(phydev); ++ if (err) ++ return err; ++ ++ /* why bother the PHY if nothing can have changed */ ++ if (phydev->autoneg == AUTONEG_ENABLE && old_link && phydev->link) ++ return 0; ++ ++ phydev->speed = SPEED_UNKNOWN; ++ phydev->duplex = DUPLEX_UNKNOWN; ++ phydev->pause = 0; ++ phydev->asym_pause = 0; ++ ++ err = genphy_read_lpa(phydev); ++ if (err < 0) ++ return err; ++ ++ ss_mask.speed_mask = AT803X_SS_SPEED_MASK; ++ ss_mask.speed_shift = __bf_shf(AT803X_SS_SPEED_MASK); ++ err = at803x_read_specific_status(phydev, ss_mask); ++ if (err < 0) ++ return err; ++ ++ if (phydev->autoneg == AUTONEG_ENABLE && phydev->autoneg_complete) ++ phy_resolve_aneg_pause(phydev); ++ ++ return 0; ++} ++EXPORT_SYMBOL_GPL(at803x_read_status); ++ + static int at803x_get_downshift(struct phy_device *phydev, u8 *d) + { + int val; +@@ -427,3 +464,159 @@ int at803x_cdt_wait_for_completion(struc + return ret < 0 ? ret : 0; + } + EXPORT_SYMBOL_GPL(at803x_cdt_wait_for_completion); ++ ++static bool qca808x_cdt_fault_length_valid(int cdt_code) ++{ ++ switch (cdt_code) { ++ case QCA808X_CDT_STATUS_STAT_SAME_SHORT: ++ case QCA808X_CDT_STATUS_STAT_SAME_OPEN: ++ case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_NORMAL: ++ case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_OPEN: ++ case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_SHORT: ++ case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_NORMAL: ++ case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_OPEN: ++ case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_SHORT: ++ case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_NORMAL: ++ case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_OPEN: ++ case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_SHORT: ++ return true; ++ default: ++ return false; ++ } ++} ++ ++static int qca808x_cable_test_result_trans(int cdt_code) ++{ ++ switch (cdt_code) { ++ case QCA808X_CDT_STATUS_STAT_NORMAL: ++ return ETHTOOL_A_CABLE_RESULT_CODE_OK; ++ case QCA808X_CDT_STATUS_STAT_SAME_SHORT: ++ return ETHTOOL_A_CABLE_RESULT_CODE_SAME_SHORT; ++ case QCA808X_CDT_STATUS_STAT_SAME_OPEN: ++ return ETHTOOL_A_CABLE_RESULT_CODE_OPEN; ++ case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_NORMAL: ++ case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_OPEN: ++ case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_SHORT: ++ case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_NORMAL: ++ case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_OPEN: ++ case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_SHORT: ++ case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_NORMAL: ++ case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_OPEN: ++ case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_SHORT: ++ return ETHTOOL_A_CABLE_RESULT_CODE_CROSS_SHORT; ++ case QCA808X_CDT_STATUS_STAT_FAIL: ++ default: ++ return ETHTOOL_A_CABLE_RESULT_CODE_UNSPEC; ++ } ++} ++ ++static int qca808x_cdt_fault_length(struct phy_device *phydev, int pair, ++ int result) ++{ ++ int val; ++ u32 cdt_length_reg = 0; ++ ++ switch (pair) { ++ case ETHTOOL_A_CABLE_PAIR_A: ++ cdt_length_reg = QCA808X_MMD3_CDT_DIAG_PAIR_A; ++ break; ++ case ETHTOOL_A_CABLE_PAIR_B: ++ cdt_length_reg = QCA808X_MMD3_CDT_DIAG_PAIR_B; ++ break; ++ case ETHTOOL_A_CABLE_PAIR_C: ++ cdt_length_reg = QCA808X_MMD3_CDT_DIAG_PAIR_C; ++ break; ++ case ETHTOOL_A_CABLE_PAIR_D: ++ cdt_length_reg = QCA808X_MMD3_CDT_DIAG_PAIR_D; ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ val = phy_read_mmd(phydev, MDIO_MMD_PCS, cdt_length_reg); ++ if (val < 0) ++ return val; ++ ++ if (result == ETHTOOL_A_CABLE_RESULT_CODE_SAME_SHORT) ++ val = FIELD_GET(QCA808X_CDT_DIAG_LENGTH_SAME_SHORT, val); ++ else ++ val = FIELD_GET(QCA808X_CDT_DIAG_LENGTH_CROSS_SHORT, val); ++ ++ return at803x_cdt_fault_length(val); ++} ++ ++static int qca808x_cable_test_get_pair_status(struct phy_device *phydev, u8 pair, ++ u16 status) ++{ ++ int length, result; ++ u16 pair_code; ++ ++ switch (pair) { ++ case ETHTOOL_A_CABLE_PAIR_A: ++ pair_code = FIELD_GET(QCA808X_CDT_CODE_PAIR_A, status); ++ break; ++ case ETHTOOL_A_CABLE_PAIR_B: ++ pair_code = FIELD_GET(QCA808X_CDT_CODE_PAIR_B, status); ++ break; ++ case ETHTOOL_A_CABLE_PAIR_C: ++ pair_code = FIELD_GET(QCA808X_CDT_CODE_PAIR_C, status); ++ break; ++ case ETHTOOL_A_CABLE_PAIR_D: ++ pair_code = FIELD_GET(QCA808X_CDT_CODE_PAIR_D, status); ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ result = qca808x_cable_test_result_trans(pair_code); ++ ethnl_cable_test_result(phydev, pair, result); ++ ++ if (qca808x_cdt_fault_length_valid(pair_code)) { ++ length = qca808x_cdt_fault_length(phydev, pair, result); ++ ethnl_cable_test_fault_length(phydev, pair, length); ++ } ++ ++ return 0; ++} ++ ++int qca808x_cable_test_get_status(struct phy_device *phydev, bool *finished) ++{ ++ int ret, val; ++ ++ *finished = false; ++ ++ val = QCA808X_CDT_ENABLE_TEST | ++ QCA808X_CDT_LENGTH_UNIT; ++ ret = at803x_cdt_start(phydev, val); ++ if (ret) ++ return ret; ++ ++ ret = at803x_cdt_wait_for_completion(phydev, QCA808X_CDT_ENABLE_TEST); ++ if (ret) ++ return ret; ++ ++ val = phy_read_mmd(phydev, MDIO_MMD_PCS, QCA808X_MMD3_CDT_STATUS); ++ if (val < 0) ++ return val; ++ ++ ret = qca808x_cable_test_get_pair_status(phydev, ETHTOOL_A_CABLE_PAIR_A, val); ++ if (ret) ++ return ret; ++ ++ ret = qca808x_cable_test_get_pair_status(phydev, ETHTOOL_A_CABLE_PAIR_B, val); ++ if (ret) ++ return ret; ++ ++ ret = qca808x_cable_test_get_pair_status(phydev, ETHTOOL_A_CABLE_PAIR_C, val); ++ if (ret) ++ return ret; ++ ++ ret = qca808x_cable_test_get_pair_status(phydev, ETHTOOL_A_CABLE_PAIR_D, val); ++ if (ret) ++ return ret; ++ ++ *finished = true; ++ ++ return 0; ++} ++EXPORT_SYMBOL_GPL(qca808x_cable_test_get_status); +--- a/drivers/net/phy/qcom/qcom.h ++++ b/drivers/net/phy/qcom/qcom.h +@@ -54,6 +54,55 @@ + #define AT803X_CDT_STATUS_STAT_MASK GENMASK(9, 8) + #define AT803X_CDT_STATUS_DELTA_TIME_MASK GENMASK(7, 0) + ++#define QCA808X_CDT_ENABLE_TEST BIT(15) ++#define QCA808X_CDT_INTER_CHECK_DIS BIT(13) ++#define QCA808X_CDT_STATUS BIT(11) ++#define QCA808X_CDT_LENGTH_UNIT BIT(10) ++ ++#define QCA808X_MMD3_CDT_STATUS 0x8064 ++#define QCA808X_MMD3_CDT_DIAG_PAIR_A 0x8065 ++#define QCA808X_MMD3_CDT_DIAG_PAIR_B 0x8066 ++#define QCA808X_MMD3_CDT_DIAG_PAIR_C 0x8067 ++#define QCA808X_MMD3_CDT_DIAG_PAIR_D 0x8068 ++#define QCA808X_CDT_DIAG_LENGTH_SAME_SHORT GENMASK(15, 8) ++#define QCA808X_CDT_DIAG_LENGTH_CROSS_SHORT GENMASK(7, 0) ++ ++#define QCA808X_CDT_CODE_PAIR_A GENMASK(15, 12) ++#define QCA808X_CDT_CODE_PAIR_B GENMASK(11, 8) ++#define QCA808X_CDT_CODE_PAIR_C GENMASK(7, 4) ++#define QCA808X_CDT_CODE_PAIR_D GENMASK(3, 0) ++ ++#define QCA808X_CDT_STATUS_STAT_TYPE GENMASK(1, 0) ++#define QCA808X_CDT_STATUS_STAT_FAIL FIELD_PREP_CONST(QCA808X_CDT_STATUS_STAT_TYPE, 0) ++#define QCA808X_CDT_STATUS_STAT_NORMAL FIELD_PREP_CONST(QCA808X_CDT_STATUS_STAT_TYPE, 1) ++#define QCA808X_CDT_STATUS_STAT_SAME_OPEN FIELD_PREP_CONST(QCA808X_CDT_STATUS_STAT_TYPE, 2) ++#define QCA808X_CDT_STATUS_STAT_SAME_SHORT FIELD_PREP_CONST(QCA808X_CDT_STATUS_STAT_TYPE, 3) ++ ++#define QCA808X_CDT_STATUS_STAT_MDI GENMASK(3, 2) ++#define QCA808X_CDT_STATUS_STAT_MDI1 FIELD_PREP_CONST(QCA808X_CDT_STATUS_STAT_MDI, 1) ++#define QCA808X_CDT_STATUS_STAT_MDI2 FIELD_PREP_CONST(QCA808X_CDT_STATUS_STAT_MDI, 2) ++#define QCA808X_CDT_STATUS_STAT_MDI3 FIELD_PREP_CONST(QCA808X_CDT_STATUS_STAT_MDI, 3) ++ ++/* NORMAL are MDI with type set to 0 */ ++#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_NORMAL QCA808X_CDT_STATUS_STAT_MDI1 ++#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_OPEN (QCA808X_CDT_STATUS_STAT_SAME_OPEN |\ ++ QCA808X_CDT_STATUS_STAT_MDI1) ++#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_SHORT (QCA808X_CDT_STATUS_STAT_SAME_SHORT |\ ++ QCA808X_CDT_STATUS_STAT_MDI1) ++#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_NORMAL QCA808X_CDT_STATUS_STAT_MDI2 ++#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_OPEN (QCA808X_CDT_STATUS_STAT_SAME_OPEN |\ ++ QCA808X_CDT_STATUS_STAT_MDI2) ++#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_SHORT (QCA808X_CDT_STATUS_STAT_SAME_SHORT |\ ++ QCA808X_CDT_STATUS_STAT_MDI2) ++#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_NORMAL QCA808X_CDT_STATUS_STAT_MDI3 ++#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_OPEN (QCA808X_CDT_STATUS_STAT_SAME_OPEN |\ ++ QCA808X_CDT_STATUS_STAT_MDI3) ++#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_SHORT (QCA808X_CDT_STATUS_STAT_SAME_SHORT |\ ++ QCA808X_CDT_STATUS_STAT_MDI3) ++ ++/* Added for reference of existence but should be handled by wait_for_completion already */ ++#define QCA808X_CDT_STATUS_STAT_BUSY (BIT(1) | BIT(3)) ++ + #define AT803X_LOC_MAC_ADDR_0_15_OFFSET 0x804C + #define AT803X_LOC_MAC_ADDR_16_31_OFFSET 0x804B + #define AT803X_LOC_MAC_ADDR_32_47_OFFSET 0x804A +@@ -110,6 +159,7 @@ int at803x_read_specific_status(struct p + struct at803x_ss_mask ss_mask); + int at803x_config_mdix(struct phy_device *phydev, u8 ctrl); + int at803x_prepare_config_aneg(struct phy_device *phydev); ++int at803x_read_status(struct phy_device *phydev); + int at803x_get_tunable(struct phy_device *phydev, + struct ethtool_tunable *tuna, void *data); + int at803x_set_tunable(struct phy_device *phydev, +@@ -118,3 +168,4 @@ int at803x_cdt_fault_length(int dt); + int at803x_cdt_start(struct phy_device *phydev, u32 cdt_start); + int at803x_cdt_wait_for_completion(struct phy_device *phydev, + u32 cdt_en); ++int qca808x_cable_test_get_status(struct phy_device *phydev, bool *finished); diff --git a/target/linux/generic/backport-6.1/716-v6.9-06-net-phy-provide-whether-link-has-changed-in-c37_read.patch b/target/linux/generic/backport-6.1/716-v6.9-06-net-phy-provide-whether-link-has-changed-in-c37_read.patch new file mode 100644 index 000000000..acaa4a644 --- /dev/null +++ b/target/linux/generic/backport-6.1/716-v6.9-06-net-phy-provide-whether-link-has-changed-in-c37_read.patch @@ -0,0 +1,100 @@ +From 9b1d5e055508393561e26bd1720f4c2639b03b1a Mon Sep 17 00:00:00 2001 +From: Christian Marangi +Date: Tue, 6 Feb 2024 18:31:09 +0100 +Subject: [PATCH 06/10] net: phy: provide whether link has changed in + c37_read_status + +Some PHY driver might require additional regs call after +genphy_c37_read_status() is called. + +Expand genphy_c37_read_status to provide a bool wheather the link has +changed or not to permit PHY driver to skip additional regs call if +nothing has changed. + +Every user of genphy_c37_read_status() is updated with the new +additional bool. + +Signed-off-by: Christian Marangi +Reviewed-by: Andrew Lunn +Signed-off-by: David S. Miller +--- + drivers/net/phy/broadcom.c | 3 ++- + drivers/net/phy/phy_device.c | 11 +++++++++-- + drivers/net/phy/qcom/at803x.c | 3 ++- + include/linux/phy.h | 2 +- + 4 files changed, 14 insertions(+), 5 deletions(-) + +--- a/drivers/net/phy/broadcom.c ++++ b/drivers/net/phy/broadcom.c +@@ -609,10 +609,11 @@ static int bcm54616s_config_aneg(struct + static int bcm54616s_read_status(struct phy_device *phydev) + { + struct bcm54616s_phy_priv *priv = phydev->priv; ++ bool changed; + int err; + + if (priv->mode_1000bx_en) +- err = genphy_c37_read_status(phydev); ++ err = genphy_c37_read_status(phydev, &changed); + else + err = genphy_read_status(phydev); + +--- a/drivers/net/phy/phy_device.c ++++ b/drivers/net/phy/phy_device.c +@@ -2549,12 +2549,15 @@ EXPORT_SYMBOL(genphy_read_status); + /** + * genphy_c37_read_status - check the link status and update current link state + * @phydev: target phy_device struct ++ * @changed: pointer where to store if link changed + * + * Description: Check the link, then figure out the current state + * by comparing what we advertise with what the link partner + * advertises. This function is for Clause 37 1000Base-X mode. ++ * ++ * If link has changed, @changed is set to true, false otherwise. + */ +-int genphy_c37_read_status(struct phy_device *phydev) ++int genphy_c37_read_status(struct phy_device *phydev, bool *changed) + { + int lpa, err, old_link = phydev->link; + +@@ -2564,9 +2567,13 @@ int genphy_c37_read_status(struct phy_de + return err; + + /* why bother the PHY if nothing can have changed */ +- if (phydev->autoneg == AUTONEG_ENABLE && old_link && phydev->link) ++ if (phydev->autoneg == AUTONEG_ENABLE && old_link && phydev->link) { ++ *changed = false; + return 0; ++ } + ++ /* Signal link has changed */ ++ *changed = true; + phydev->duplex = DUPLEX_UNKNOWN; + phydev->pause = 0; + phydev->asym_pause = 0; +--- a/drivers/net/phy/qcom/at803x.c ++++ b/drivers/net/phy/qcom/at803x.c +@@ -912,9 +912,10 @@ static int at8031_config_intr(struct phy + static int at8031_read_status(struct phy_device *phydev) + { + struct at803x_priv *priv = phydev->priv; ++ bool changed; + + if (priv->is_1000basex) +- return genphy_c37_read_status(phydev); ++ return genphy_c37_read_status(phydev, &changed); + + return at803x_read_status(phydev); + } +--- a/include/linux/phy.h ++++ b/include/linux/phy.h +@@ -1688,7 +1688,7 @@ int genphy_write_mmd_unsupported(struct + + /* Clause 37 */ + int genphy_c37_config_aneg(struct phy_device *phydev); +-int genphy_c37_read_status(struct phy_device *phydev); ++int genphy_c37_read_status(struct phy_device *phydev, bool *changed); + + /* Clause 45 PHY */ + int genphy_c45_restart_aneg(struct phy_device *phydev); diff --git a/target/linux/generic/backport-6.1/716-v6.9-07-net-phy-qcom-add-support-for-QCA807x-PHY-Family.patch b/target/linux/generic/backport-6.1/716-v6.9-07-net-phy-qcom-add-support-for-QCA807x-PHY-Family.patch new file mode 100644 index 000000000..bbf0f76d6 --- /dev/null +++ b/target/linux/generic/backport-6.1/716-v6.9-07-net-phy-qcom-add-support-for-QCA807x-PHY-Family.patch @@ -0,0 +1,668 @@ +From d1cb613efbd3cd7d0c000167816beb3f248f5eb8 Mon Sep 17 00:00:00 2001 +From: Robert Marko +Date: Tue, 6 Feb 2024 18:31:10 +0100 +Subject: [PATCH 07/10] net: phy: qcom: add support for QCA807x PHY Family + +This adds driver for the Qualcomm QCA8072 and QCA8075 PHY-s. + +They are 2 or 5 port IEEE 802.3 clause 22 compliant 10BASE-Te, +100BASE-TX and 1000BASE-T PHY-s. + +They feature 2 SerDes, one for PSGMII or QSGMII connection with +MAC, while second one is SGMII for connection to MAC or fiber. + +Both models have a combo port that supports 1000BASE-X and +100BASE-FX fiber. + +PHY package can be configured in 3 mode following this table: + + First Serdes mode Second Serdes mode +Option 1 PSGMII for copper Disabled + ports 0-4 +Option 2 PSGMII for copper 1000BASE-X / 100BASE-FX + ports 0-4 +Option 3 QSGMII for copper SGMII for + ports 0-3 copper port 4 + +Each PHY inside of QCA807x series has 4 digitally controlled +output only pins that natively drive LED-s. +But some vendors used these to driver generic LED-s controlled +by userspace, so lets enable registering each PHY as GPIO +controller and add driver for it. + +These are commonly used in Qualcomm IPQ40xx, IPQ60xx and IPQ807x +boards. + +Co-developed-by: Christian Marangi +Signed-off-by: Robert Marko +Signed-off-by: Christian Marangi +Reviewed-by: Andrew Lunn +Signed-off-by: David S. Miller +--- + drivers/net/phy/qcom/Kconfig | 8 + + drivers/net/phy/qcom/Makefile | 1 + + drivers/net/phy/qcom/qca807x.c | 597 +++++++++++++++++++++++++++++++++ + 3 files changed, 606 insertions(+) + create mode 100644 drivers/net/phy/qcom/qca807x.c + +--- a/drivers/net/phy/qcom/Kconfig ++++ b/drivers/net/phy/qcom/Kconfig +@@ -20,3 +20,11 @@ config QCA808X_PHY + select QCOM_NET_PHYLIB + help + Currently supports the QCA8081 model ++ ++config QCA807X_PHY ++ tristate "Qualcomm QCA807x PHYs" ++ select QCOM_NET_PHYLIB ++ depends on OF_MDIO ++ help ++ Currently supports the Qualcomm QCA8072, QCA8075 and the PSGMII ++ control PHY. +--- a/drivers/net/phy/qcom/Makefile ++++ b/drivers/net/phy/qcom/Makefile +@@ -3,3 +3,4 @@ obj-$(CONFIG_QCOM_NET_PHYLIB) += qcom-ph + obj-$(CONFIG_AT803X_PHY) += at803x.o + obj-$(CONFIG_QCA83XX_PHY) += qca83xx.o + obj-$(CONFIG_QCA808X_PHY) += qca808x.o ++obj-$(CONFIG_QCA807X_PHY) += qca807x.o +--- /dev/null ++++ b/drivers/net/phy/qcom/qca807x.c +@@ -0,0 +1,597 @@ ++// SPDX-License-Identifier: GPL-2.0-or-later ++/* ++ * Copyright (c) 2023 Sartura Ltd. ++ * ++ * Author: Robert Marko ++ * Christian Marangi ++ * ++ * Qualcomm QCA8072 and QCA8075 PHY driver ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "qcom.h" ++ ++#define QCA807X_CHIP_CONFIGURATION 0x1f ++#define QCA807X_BT_BX_REG_SEL BIT(15) ++#define QCA807X_BT_BX_REG_SEL_FIBER 0 ++#define QCA807X_BT_BX_REG_SEL_COPPER 1 ++#define QCA807X_CHIP_CONFIGURATION_MODE_CFG_MASK GENMASK(3, 0) ++#define QCA807X_CHIP_CONFIGURATION_MODE_QSGMII_SGMII 4 ++#define QCA807X_CHIP_CONFIGURATION_MODE_PSGMII_FIBER 3 ++#define QCA807X_CHIP_CONFIGURATION_MODE_PSGMII_ALL_COPPER 0 ++ ++#define QCA807X_MEDIA_SELECT_STATUS 0x1a ++#define QCA807X_MEDIA_DETECTED_COPPER BIT(5) ++#define QCA807X_MEDIA_DETECTED_1000_BASE_X BIT(4) ++#define QCA807X_MEDIA_DETECTED_100_BASE_FX BIT(3) ++ ++#define QCA807X_MMD7_FIBER_MODE_AUTO_DETECTION 0x807e ++#define QCA807X_MMD7_FIBER_MODE_AUTO_DETECTION_EN BIT(0) ++ ++#define QCA807X_MMD7_1000BASE_T_POWER_SAVE_PER_CABLE_LENGTH 0x801a ++#define QCA807X_CONTROL_DAC_MASK GENMASK(2, 0) ++/* List of tweaks enabled by this bit: ++ * - With both FULL amplitude and FULL bias current: bias current ++ * is set to half. ++ * - With only DSP amplitude: bias current is set to half and ++ * is set to 1/4 with cable < 10m. ++ * - With DSP bias current (included both DSP amplitude and ++ * DSP bias current): bias current is half the detected current ++ * with cable < 10m. ++ */ ++#define QCA807X_CONTROL_DAC_BIAS_CURRENT_TWEAK BIT(2) ++#define QCA807X_CONTROL_DAC_DSP_BIAS_CURRENT BIT(1) ++#define QCA807X_CONTROL_DAC_DSP_AMPLITUDE BIT(0) ++ ++#define QCA807X_MMD7_LED_100N_1 0x8074 ++#define QCA807X_MMD7_LED_100N_2 0x8075 ++#define QCA807X_MMD7_LED_1000N_1 0x8076 ++#define QCA807X_MMD7_LED_1000N_2 0x8077 ++ ++#define QCA807X_MMD7_LED_CTRL(x) (0x8074 + ((x) * 2)) ++#define QCA807X_MMD7_LED_FORCE_CTRL(x) (0x8075 + ((x) * 2)) ++ ++#define QCA807X_GPIO_FORCE_EN BIT(15) ++#define QCA807X_GPIO_FORCE_MODE_MASK GENMASK(14, 13) ++ ++#define QCA807X_FUNCTION_CONTROL 0x10 ++#define QCA807X_FC_MDI_CROSSOVER_MODE_MASK GENMASK(6, 5) ++#define QCA807X_FC_MDI_CROSSOVER_AUTO 3 ++#define QCA807X_FC_MDI_CROSSOVER_MANUAL_MDIX 1 ++#define QCA807X_FC_MDI_CROSSOVER_MANUAL_MDI 0 ++ ++/* PQSGMII Analog PHY specific */ ++#define PQSGMII_CTRL_REG 0x0 ++#define PQSGMII_ANALOG_SW_RESET BIT(6) ++#define PQSGMII_DRIVE_CONTROL_1 0xb ++#define PQSGMII_TX_DRIVER_MASK GENMASK(7, 4) ++#define PQSGMII_TX_DRIVER_140MV 0x0 ++#define PQSGMII_TX_DRIVER_160MV 0x1 ++#define PQSGMII_TX_DRIVER_180MV 0x2 ++#define PQSGMII_TX_DRIVER_200MV 0x3 ++#define PQSGMII_TX_DRIVER_220MV 0x4 ++#define PQSGMII_TX_DRIVER_240MV 0x5 ++#define PQSGMII_TX_DRIVER_260MV 0x6 ++#define PQSGMII_TX_DRIVER_280MV 0x7 ++#define PQSGMII_TX_DRIVER_300MV 0x8 ++#define PQSGMII_TX_DRIVER_320MV 0x9 ++#define PQSGMII_TX_DRIVER_400MV 0xa ++#define PQSGMII_TX_DRIVER_500MV 0xb ++#define PQSGMII_TX_DRIVER_600MV 0xc ++#define PQSGMII_MODE_CTRL 0x6d ++#define PQSGMII_MODE_CTRL_AZ_WORKAROUND_MASK BIT(0) ++#define PQSGMII_MMD3_SERDES_CONTROL 0x805a ++ ++#define PHY_ID_QCA8072 0x004dd0b2 ++#define PHY_ID_QCA8075 0x004dd0b1 ++ ++#define QCA807X_COMBO_ADDR_OFFSET 4 ++#define QCA807X_PQSGMII_ADDR_OFFSET 5 ++#define SERDES_RESET_SLEEP 100 ++ ++enum qca807x_global_phy { ++ QCA807X_COMBO_ADDR = 4, ++ QCA807X_PQSGMII_ADDR = 5, ++}; ++ ++struct qca807x_shared_priv { ++ unsigned int package_mode; ++ u32 tx_drive_strength; ++}; ++ ++struct qca807x_gpio_priv { ++ struct phy_device *phy; ++}; ++ ++struct qca807x_priv { ++ bool dac_full_amplitude; ++ bool dac_full_bias_current; ++ bool dac_disable_bias_current_tweak; ++}; ++ ++static int qca807x_cable_test_start(struct phy_device *phydev) ++{ ++ /* we do all the (time consuming) work later */ ++ return 0; ++} ++ ++#ifdef CONFIG_GPIOLIB ++static int qca807x_gpio_get_direction(struct gpio_chip *gc, unsigned int offset) ++{ ++ return GPIO_LINE_DIRECTION_OUT; ++} ++ ++static int qca807x_gpio_get(struct gpio_chip *gc, unsigned int offset) ++{ ++ struct qca807x_gpio_priv *priv = gpiochip_get_data(gc); ++ u16 reg; ++ int val; ++ ++ reg = QCA807X_MMD7_LED_FORCE_CTRL(offset); ++ val = phy_read_mmd(priv->phy, MDIO_MMD_AN, reg); ++ ++ return FIELD_GET(QCA807X_GPIO_FORCE_MODE_MASK, val); ++} ++ ++static void qca807x_gpio_set(struct gpio_chip *gc, unsigned int offset, int value) ++{ ++ struct qca807x_gpio_priv *priv = gpiochip_get_data(gc); ++ u16 reg; ++ int val; ++ ++ reg = QCA807X_MMD7_LED_FORCE_CTRL(offset); ++ ++ val = phy_read_mmd(priv->phy, MDIO_MMD_AN, reg); ++ val &= ~QCA807X_GPIO_FORCE_MODE_MASK; ++ val |= QCA807X_GPIO_FORCE_EN; ++ val |= FIELD_PREP(QCA807X_GPIO_FORCE_MODE_MASK, value); ++ ++ phy_write_mmd(priv->phy, MDIO_MMD_AN, reg, val); ++} ++ ++static int qca807x_gpio_dir_out(struct gpio_chip *gc, unsigned int offset, int value) ++{ ++ qca807x_gpio_set(gc, offset, value); ++ ++ return 0; ++} ++ ++static int qca807x_gpio(struct phy_device *phydev) ++{ ++ struct device *dev = &phydev->mdio.dev; ++ struct qca807x_gpio_priv *priv; ++ struct gpio_chip *gc; ++ ++ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); ++ if (!priv) ++ return -ENOMEM; ++ ++ priv->phy = phydev; ++ ++ gc = devm_kzalloc(dev, sizeof(*gc), GFP_KERNEL); ++ if (!gc) ++ return -ENOMEM; ++ ++ gc->label = dev_name(dev); ++ gc->base = -1; ++ gc->ngpio = 2; ++ gc->parent = dev; ++ gc->owner = THIS_MODULE; ++ gc->can_sleep = true; ++ gc->get_direction = qca807x_gpio_get_direction; ++ gc->direction_output = qca807x_gpio_dir_out; ++ gc->get = qca807x_gpio_get; ++ gc->set = qca807x_gpio_set; ++ ++ return devm_gpiochip_add_data(dev, gc, priv); ++} ++#endif ++ ++static int qca807x_read_fiber_status(struct phy_device *phydev) ++{ ++ bool changed; ++ int ss, err; ++ ++ err = genphy_c37_read_status(phydev, &changed); ++ if (err || !changed) ++ return err; ++ ++ /* Read the QCA807x PHY-Specific Status register fiber page, ++ * which indicates the speed and duplex that the PHY is actually ++ * using, irrespective of whether we are in autoneg mode or not. ++ */ ++ ss = phy_read(phydev, AT803X_SPECIFIC_STATUS); ++ if (ss < 0) ++ return ss; ++ ++ phydev->speed = SPEED_UNKNOWN; ++ phydev->duplex = DUPLEX_UNKNOWN; ++ if (ss & AT803X_SS_SPEED_DUPLEX_RESOLVED) { ++ switch (FIELD_GET(AT803X_SS_SPEED_MASK, ss)) { ++ case AT803X_SS_SPEED_100: ++ phydev->speed = SPEED_100; ++ break; ++ case AT803X_SS_SPEED_1000: ++ phydev->speed = SPEED_1000; ++ break; ++ } ++ ++ if (ss & AT803X_SS_DUPLEX) ++ phydev->duplex = DUPLEX_FULL; ++ else ++ phydev->duplex = DUPLEX_HALF; ++ } ++ ++ return 0; ++} ++ ++static int qca807x_read_status(struct phy_device *phydev) ++{ ++ if (linkmode_test_bit(ETHTOOL_LINK_MODE_FIBRE_BIT, phydev->supported)) { ++ switch (phydev->port) { ++ case PORT_FIBRE: ++ return qca807x_read_fiber_status(phydev); ++ case PORT_TP: ++ return at803x_read_status(phydev); ++ default: ++ return -EINVAL; ++ } ++ } ++ ++ return at803x_read_status(phydev); ++} ++ ++static int qca807x_phy_package_probe_once(struct phy_device *phydev) ++{ ++ struct phy_package_shared *shared = phydev->shared; ++ struct qca807x_shared_priv *priv = shared->priv; ++ unsigned int tx_drive_strength; ++ const char *package_mode_name; ++ ++ /* Default to 600mw if not defined */ ++ if (of_property_read_u32(shared->np, "qcom,tx-drive-strength-milliwatt", ++ &tx_drive_strength)) ++ tx_drive_strength = 600; ++ ++ switch (tx_drive_strength) { ++ case 140: ++ priv->tx_drive_strength = PQSGMII_TX_DRIVER_140MV; ++ break; ++ case 160: ++ priv->tx_drive_strength = PQSGMII_TX_DRIVER_160MV; ++ break; ++ case 180: ++ priv->tx_drive_strength = PQSGMII_TX_DRIVER_180MV; ++ break; ++ case 200: ++ priv->tx_drive_strength = PQSGMII_TX_DRIVER_200MV; ++ break; ++ case 220: ++ priv->tx_drive_strength = PQSGMII_TX_DRIVER_220MV; ++ break; ++ case 240: ++ priv->tx_drive_strength = PQSGMII_TX_DRIVER_240MV; ++ break; ++ case 260: ++ priv->tx_drive_strength = PQSGMII_TX_DRIVER_260MV; ++ break; ++ case 280: ++ priv->tx_drive_strength = PQSGMII_TX_DRIVER_280MV; ++ break; ++ case 300: ++ priv->tx_drive_strength = PQSGMII_TX_DRIVER_300MV; ++ break; ++ case 320: ++ priv->tx_drive_strength = PQSGMII_TX_DRIVER_320MV; ++ break; ++ case 400: ++ priv->tx_drive_strength = PQSGMII_TX_DRIVER_400MV; ++ break; ++ case 500: ++ priv->tx_drive_strength = PQSGMII_TX_DRIVER_500MV; ++ break; ++ case 600: ++ priv->tx_drive_strength = PQSGMII_TX_DRIVER_600MV; ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ priv->package_mode = PHY_INTERFACE_MODE_NA; ++ if (!of_property_read_string(shared->np, "qcom,package-mode", ++ &package_mode_name)) { ++ if (!strcasecmp(package_mode_name, ++ phy_modes(PHY_INTERFACE_MODE_PSGMII))) ++ priv->package_mode = PHY_INTERFACE_MODE_PSGMII; ++ else if (!strcasecmp(package_mode_name, ++ phy_modes(PHY_INTERFACE_MODE_QSGMII))) ++ priv->package_mode = PHY_INTERFACE_MODE_QSGMII; ++ else ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++ ++static int qca807x_phy_package_config_init_once(struct phy_device *phydev) ++{ ++ struct phy_package_shared *shared = phydev->shared; ++ struct qca807x_shared_priv *priv = shared->priv; ++ int val, ret; ++ ++ phy_lock_mdio_bus(phydev); ++ ++ /* Set correct PHY package mode */ ++ val = __phy_package_read(phydev, QCA807X_COMBO_ADDR, ++ QCA807X_CHIP_CONFIGURATION); ++ val &= ~QCA807X_CHIP_CONFIGURATION_MODE_CFG_MASK; ++ /* package_mode can be QSGMII or PSGMII and we validate ++ * this in probe_once. ++ * With package_mode to NA, we default to PSGMII. ++ */ ++ switch (priv->package_mode) { ++ case PHY_INTERFACE_MODE_QSGMII: ++ val |= QCA807X_CHIP_CONFIGURATION_MODE_QSGMII_SGMII; ++ break; ++ case PHY_INTERFACE_MODE_PSGMII: ++ default: ++ val |= QCA807X_CHIP_CONFIGURATION_MODE_PSGMII_ALL_COPPER; ++ } ++ ret = __phy_package_write(phydev, QCA807X_COMBO_ADDR, ++ QCA807X_CHIP_CONFIGURATION, val); ++ if (ret) ++ goto exit; ++ ++ /* After mode change Serdes reset is required */ ++ val = __phy_package_read(phydev, QCA807X_PQSGMII_ADDR, ++ PQSGMII_CTRL_REG); ++ val &= ~PQSGMII_ANALOG_SW_RESET; ++ ret = __phy_package_write(phydev, QCA807X_PQSGMII_ADDR, ++ PQSGMII_CTRL_REG, val); ++ if (ret) ++ goto exit; ++ ++ msleep(SERDES_RESET_SLEEP); ++ ++ val = __phy_package_read(phydev, QCA807X_PQSGMII_ADDR, ++ PQSGMII_CTRL_REG); ++ val |= PQSGMII_ANALOG_SW_RESET; ++ ret = __phy_package_write(phydev, QCA807X_PQSGMII_ADDR, ++ PQSGMII_CTRL_REG, val); ++ if (ret) ++ goto exit; ++ ++ /* Workaround to enable AZ transmitting ability */ ++ val = __phy_package_read_mmd(phydev, QCA807X_PQSGMII_ADDR, ++ MDIO_MMD_PMAPMD, PQSGMII_MODE_CTRL); ++ val &= ~PQSGMII_MODE_CTRL_AZ_WORKAROUND_MASK; ++ ret = __phy_package_write_mmd(phydev, QCA807X_PQSGMII_ADDR, ++ MDIO_MMD_PMAPMD, PQSGMII_MODE_CTRL, val); ++ if (ret) ++ goto exit; ++ ++ /* Set PQSGMII TX AMP strength */ ++ val = __phy_package_read(phydev, QCA807X_PQSGMII_ADDR, ++ PQSGMII_DRIVE_CONTROL_1); ++ val &= ~PQSGMII_TX_DRIVER_MASK; ++ val |= FIELD_PREP(PQSGMII_TX_DRIVER_MASK, priv->tx_drive_strength); ++ ret = __phy_package_write(phydev, QCA807X_PQSGMII_ADDR, ++ PQSGMII_DRIVE_CONTROL_1, val); ++ if (ret) ++ goto exit; ++ ++ /* Prevent PSGMII going into hibernation via PSGMII self test */ ++ val = __phy_package_read_mmd(phydev, QCA807X_COMBO_ADDR, ++ MDIO_MMD_PCS, PQSGMII_MMD3_SERDES_CONTROL); ++ val &= ~BIT(1); ++ ret = __phy_package_write_mmd(phydev, QCA807X_COMBO_ADDR, ++ MDIO_MMD_PCS, PQSGMII_MMD3_SERDES_CONTROL, val); ++ ++exit: ++ phy_unlock_mdio_bus(phydev); ++ ++ return ret; ++} ++ ++static int qca807x_sfp_insert(void *upstream, const struct sfp_eeprom_id *id) ++{ ++ struct phy_device *phydev = upstream; ++ __ETHTOOL_DECLARE_LINK_MODE_MASK(support) = { 0, }; ++ phy_interface_t iface; ++ int ret; ++ DECLARE_PHY_INTERFACE_MASK(interfaces); ++ ++ sfp_parse_support(phydev->sfp_bus, id, support, interfaces); ++ iface = sfp_select_interface(phydev->sfp_bus, support); ++ ++ dev_info(&phydev->mdio.dev, "%s SFP module inserted\n", phy_modes(iface)); ++ ++ switch (iface) { ++ case PHY_INTERFACE_MODE_1000BASEX: ++ case PHY_INTERFACE_MODE_100BASEX: ++ /* Set PHY mode to PSGMII combo (1/4 copper + combo ports) mode */ ++ ret = phy_modify(phydev, ++ QCA807X_CHIP_CONFIGURATION, ++ QCA807X_CHIP_CONFIGURATION_MODE_CFG_MASK, ++ QCA807X_CHIP_CONFIGURATION_MODE_PSGMII_FIBER); ++ /* Enable fiber mode autodection (1000Base-X or 100Base-FX) */ ++ ret = phy_set_bits_mmd(phydev, ++ MDIO_MMD_AN, ++ QCA807X_MMD7_FIBER_MODE_AUTO_DETECTION, ++ QCA807X_MMD7_FIBER_MODE_AUTO_DETECTION_EN); ++ /* Select fiber page */ ++ ret = phy_clear_bits(phydev, ++ QCA807X_CHIP_CONFIGURATION, ++ QCA807X_BT_BX_REG_SEL); ++ ++ phydev->port = PORT_FIBRE; ++ break; ++ default: ++ dev_err(&phydev->mdio.dev, "Incompatible SFP module inserted\n"); ++ return -EINVAL; ++ } ++ ++ return ret; ++} ++ ++static void qca807x_sfp_remove(void *upstream) ++{ ++ struct phy_device *phydev = upstream; ++ ++ /* Select copper page */ ++ phy_set_bits(phydev, ++ QCA807X_CHIP_CONFIGURATION, ++ QCA807X_BT_BX_REG_SEL); ++ ++ phydev->port = PORT_TP; ++} ++ ++static const struct sfp_upstream_ops qca807x_sfp_ops = { ++ .attach = phy_sfp_attach, ++ .detach = phy_sfp_detach, ++ .module_insert = qca807x_sfp_insert, ++ .module_remove = qca807x_sfp_remove, ++}; ++ ++static int qca807x_probe(struct phy_device *phydev) ++{ ++ struct device_node *node = phydev->mdio.dev.of_node; ++ struct qca807x_shared_priv *shared_priv; ++ struct device *dev = &phydev->mdio.dev; ++ struct phy_package_shared *shared; ++ struct qca807x_priv *priv; ++ int ret; ++ ++ ret = devm_of_phy_package_join(dev, phydev, sizeof(*shared_priv)); ++ if (ret) ++ return ret; ++ ++ if (phy_package_probe_once(phydev)) { ++ ret = qca807x_phy_package_probe_once(phydev); ++ if (ret) ++ return ret; ++ } ++ ++ shared = phydev->shared; ++ shared_priv = shared->priv; ++ ++ /* Make sure PHY follow PHY package mode if enforced */ ++ if (shared_priv->package_mode != PHY_INTERFACE_MODE_NA && ++ phydev->interface != shared_priv->package_mode) ++ return -EINVAL; ++ ++ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); ++ if (!priv) ++ return -ENOMEM; ++ ++ priv->dac_full_amplitude = of_property_read_bool(node, "qcom,dac-full-amplitude"); ++ priv->dac_full_bias_current = of_property_read_bool(node, "qcom,dac-full-bias-current"); ++ priv->dac_disable_bias_current_tweak = of_property_read_bool(node, ++ "qcom,dac-disable-bias-current-tweak"); ++ ++ if (IS_ENABLED(CONFIG_GPIOLIB)) { ++ /* Do not register a GPIO controller unless flagged for it */ ++ if (of_property_read_bool(node, "gpio-controller")) { ++ ret = qca807x_gpio(phydev); ++ if (ret) ++ return ret; ++ } ++ } ++ ++ /* Attach SFP bus on combo port*/ ++ if (phy_read(phydev, QCA807X_CHIP_CONFIGURATION)) { ++ ret = phy_sfp_probe(phydev, &qca807x_sfp_ops); ++ if (ret) ++ return ret; ++ linkmode_set_bit(ETHTOOL_LINK_MODE_FIBRE_BIT, phydev->supported); ++ linkmode_set_bit(ETHTOOL_LINK_MODE_FIBRE_BIT, phydev->advertising); ++ } ++ ++ phydev->priv = priv; ++ ++ return 0; ++} ++ ++static int qca807x_config_init(struct phy_device *phydev) ++{ ++ struct qca807x_priv *priv = phydev->priv; ++ u16 control_dac; ++ int ret; ++ ++ if (phy_package_init_once(phydev)) { ++ ret = qca807x_phy_package_config_init_once(phydev); ++ if (ret) ++ return ret; ++ } ++ ++ control_dac = phy_read_mmd(phydev, MDIO_MMD_AN, ++ QCA807X_MMD7_1000BASE_T_POWER_SAVE_PER_CABLE_LENGTH); ++ control_dac &= ~QCA807X_CONTROL_DAC_MASK; ++ if (!priv->dac_full_amplitude) ++ control_dac |= QCA807X_CONTROL_DAC_DSP_AMPLITUDE; ++ if (!priv->dac_full_amplitude) ++ control_dac |= QCA807X_CONTROL_DAC_DSP_BIAS_CURRENT; ++ if (!priv->dac_disable_bias_current_tweak) ++ control_dac |= QCA807X_CONTROL_DAC_BIAS_CURRENT_TWEAK; ++ return phy_write_mmd(phydev, MDIO_MMD_AN, ++ QCA807X_MMD7_1000BASE_T_POWER_SAVE_PER_CABLE_LENGTH, ++ control_dac); ++} ++ ++static struct phy_driver qca807x_drivers[] = { ++ { ++ PHY_ID_MATCH_EXACT(PHY_ID_QCA8072), ++ .name = "Qualcomm QCA8072", ++ .flags = PHY_POLL_CABLE_TEST, ++ /* PHY_GBIT_FEATURES */ ++ .probe = qca807x_probe, ++ .config_init = qca807x_config_init, ++ .read_status = qca807x_read_status, ++ .config_intr = at803x_config_intr, ++ .handle_interrupt = at803x_handle_interrupt, ++ .soft_reset = genphy_soft_reset, ++ .get_tunable = at803x_get_tunable, ++ .set_tunable = at803x_set_tunable, ++ .resume = genphy_resume, ++ .suspend = genphy_suspend, ++ .cable_test_start = qca807x_cable_test_start, ++ .cable_test_get_status = qca808x_cable_test_get_status, ++ }, ++ { ++ PHY_ID_MATCH_EXACT(PHY_ID_QCA8075), ++ .name = "Qualcomm QCA8075", ++ .flags = PHY_POLL_CABLE_TEST, ++ /* PHY_GBIT_FEATURES */ ++ .probe = qca807x_probe, ++ .config_init = qca807x_config_init, ++ .read_status = qca807x_read_status, ++ .config_intr = at803x_config_intr, ++ .handle_interrupt = at803x_handle_interrupt, ++ .soft_reset = genphy_soft_reset, ++ .get_tunable = at803x_get_tunable, ++ .set_tunable = at803x_set_tunable, ++ .resume = genphy_resume, ++ .suspend = genphy_suspend, ++ .cable_test_start = qca807x_cable_test_start, ++ .cable_test_get_status = qca808x_cable_test_get_status, ++ }, ++}; ++module_phy_driver(qca807x_drivers); ++ ++static struct mdio_device_id __maybe_unused qca807x_tbl[] = { ++ { PHY_ID_MATCH_EXACT(PHY_ID_QCA8072) }, ++ { PHY_ID_MATCH_EXACT(PHY_ID_QCA8075) }, ++ { } ++}; ++ ++MODULE_AUTHOR("Robert Marko "); ++MODULE_AUTHOR("Christian Marangi "); ++MODULE_DESCRIPTION("Qualcomm QCA807x PHY driver"); ++MODULE_DEVICE_TABLE(mdio, qca807x_tbl); ++MODULE_LICENSE("GPL"); diff --git a/target/linux/generic/backport-6.1/716-v6.9-08-net-phy-qcom-move-common-qca808x-LED-define-to-share.patch b/target/linux/generic/backport-6.1/716-v6.9-08-net-phy-qcom-move-common-qca808x-LED-define-to-share.patch new file mode 100644 index 000000000..cf4d74e8c --- /dev/null +++ b/target/linux/generic/backport-6.1/716-v6.9-08-net-phy-qcom-move-common-qca808x-LED-define-to-share.patch @@ -0,0 +1,179 @@ +From ee9d9807bee0e6af8ca2a4db6f0d1dc0e5b41f44 Mon Sep 17 00:00:00 2001 +From: Christian Marangi +Date: Tue, 6 Feb 2024 18:31:11 +0100 +Subject: [PATCH 08/10] net: phy: qcom: move common qca808x LED define to + shared header + +The LED implementation of qca808x and qca807x is the same but qca807x +supports also Fiber port and have different hw control bits for Fiber +port. + +In preparation for qca807x introduction, move all the common define to +shared header. + +Signed-off-by: Christian Marangi +Reviewed-by: Andrew Lunn +Signed-off-by: David S. Miller +--- + drivers/net/phy/qcom/qca808x.c | 65 ---------------------------------- + drivers/net/phy/qcom/qcom.h | 65 ++++++++++++++++++++++++++++++++++ + 2 files changed, 65 insertions(+), 65 deletions(-) + +--- a/drivers/net/phy/qcom/qca808x.c ++++ b/drivers/net/phy/qcom/qca808x.c +@@ -62,29 +62,6 @@ + #define QCA808X_DBG_AN_TEST 0xb + #define QCA808X_HIBERNATION_EN BIT(15) + +-#define QCA808X_MMD7_LED_GLOBAL 0x8073 +-#define QCA808X_LED_BLINK_1 GENMASK(11, 6) +-#define QCA808X_LED_BLINK_2 GENMASK(5, 0) +-/* Values are the same for both BLINK_1 and BLINK_2 */ +-#define QCA808X_LED_BLINK_FREQ_MASK GENMASK(5, 3) +-#define QCA808X_LED_BLINK_FREQ_2HZ FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x0) +-#define QCA808X_LED_BLINK_FREQ_4HZ FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x1) +-#define QCA808X_LED_BLINK_FREQ_8HZ FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x2) +-#define QCA808X_LED_BLINK_FREQ_16HZ FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x3) +-#define QCA808X_LED_BLINK_FREQ_32HZ FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x4) +-#define QCA808X_LED_BLINK_FREQ_64HZ FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x5) +-#define QCA808X_LED_BLINK_FREQ_128HZ FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x6) +-#define QCA808X_LED_BLINK_FREQ_256HZ FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x7) +-#define QCA808X_LED_BLINK_DUTY_MASK GENMASK(2, 0) +-#define QCA808X_LED_BLINK_DUTY_50_50 FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x0) +-#define QCA808X_LED_BLINK_DUTY_75_25 FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x1) +-#define QCA808X_LED_BLINK_DUTY_25_75 FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x2) +-#define QCA808X_LED_BLINK_DUTY_33_67 FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x3) +-#define QCA808X_LED_BLINK_DUTY_67_33 FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x4) +-#define QCA808X_LED_BLINK_DUTY_17_83 FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x5) +-#define QCA808X_LED_BLINK_DUTY_83_17 FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x6) +-#define QCA808X_LED_BLINK_DUTY_8_92 FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x7) +- + #define QCA808X_MMD7_LED2_CTRL 0x8074 + #define QCA808X_MMD7_LED2_FORCE_CTRL 0x8075 + #define QCA808X_MMD7_LED1_CTRL 0x8076 +@@ -92,51 +69,9 @@ + #define QCA808X_MMD7_LED0_CTRL 0x8078 + #define QCA808X_MMD7_LED_CTRL(x) (0x8078 - ((x) * 2)) + +-/* LED hw control pattern is the same for every LED */ +-#define QCA808X_LED_PATTERN_MASK GENMASK(15, 0) +-#define QCA808X_LED_SPEED2500_ON BIT(15) +-#define QCA808X_LED_SPEED2500_BLINK BIT(14) +-/* Follow blink trigger even if duplex or speed condition doesn't match */ +-#define QCA808X_LED_BLINK_CHECK_BYPASS BIT(13) +-#define QCA808X_LED_FULL_DUPLEX_ON BIT(12) +-#define QCA808X_LED_HALF_DUPLEX_ON BIT(11) +-#define QCA808X_LED_TX_BLINK BIT(10) +-#define QCA808X_LED_RX_BLINK BIT(9) +-#define QCA808X_LED_TX_ON_10MS BIT(8) +-#define QCA808X_LED_RX_ON_10MS BIT(7) +-#define QCA808X_LED_SPEED1000_ON BIT(6) +-#define QCA808X_LED_SPEED100_ON BIT(5) +-#define QCA808X_LED_SPEED10_ON BIT(4) +-#define QCA808X_LED_COLLISION_BLINK BIT(3) +-#define QCA808X_LED_SPEED1000_BLINK BIT(2) +-#define QCA808X_LED_SPEED100_BLINK BIT(1) +-#define QCA808X_LED_SPEED10_BLINK BIT(0) +- + #define QCA808X_MMD7_LED0_FORCE_CTRL 0x8079 + #define QCA808X_MMD7_LED_FORCE_CTRL(x) (0x8079 - ((x) * 2)) + +-/* LED force ctrl is the same for every LED +- * No documentation exist for this, not even internal one +- * with NDA as QCOM gives only info about configuring +- * hw control pattern rules and doesn't indicate any way +- * to force the LED to specific mode. +- * These define comes from reverse and testing and maybe +- * lack of some info or some info are not entirely correct. +- * For the basic LED control and hw control these finding +- * are enough to support LED control in all the required APIs. +- * +- * On doing some comparison with implementation with qca807x, +- * it was found that it's 1:1 equal to it and confirms all the +- * reverse done. It was also found further specification with the +- * force mode and the blink modes. +- */ +-#define QCA808X_LED_FORCE_EN BIT(15) +-#define QCA808X_LED_FORCE_MODE_MASK GENMASK(14, 13) +-#define QCA808X_LED_FORCE_BLINK_1 FIELD_PREP(QCA808X_LED_FORCE_MODE_MASK, 0x3) +-#define QCA808X_LED_FORCE_BLINK_2 FIELD_PREP(QCA808X_LED_FORCE_MODE_MASK, 0x2) +-#define QCA808X_LED_FORCE_ON FIELD_PREP(QCA808X_LED_FORCE_MODE_MASK, 0x1) +-#define QCA808X_LED_FORCE_OFF FIELD_PREP(QCA808X_LED_FORCE_MODE_MASK, 0x0) +- + #define QCA808X_MMD7_LED_POLARITY_CTRL 0x901a + /* QSDK sets by default 0x46 to this reg that sets BIT 6 for + * LED to active high. It's not clear what BIT 3 and BIT 4 does. +--- a/drivers/net/phy/qcom/qcom.h ++++ b/drivers/net/phy/qcom/qcom.h +@@ -103,6 +103,71 @@ + /* Added for reference of existence but should be handled by wait_for_completion already */ + #define QCA808X_CDT_STATUS_STAT_BUSY (BIT(1) | BIT(3)) + ++#define QCA808X_MMD7_LED_GLOBAL 0x8073 ++#define QCA808X_LED_BLINK_1 GENMASK(11, 6) ++#define QCA808X_LED_BLINK_2 GENMASK(5, 0) ++/* Values are the same for both BLINK_1 and BLINK_2 */ ++#define QCA808X_LED_BLINK_FREQ_MASK GENMASK(5, 3) ++#define QCA808X_LED_BLINK_FREQ_2HZ FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x0) ++#define QCA808X_LED_BLINK_FREQ_4HZ FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x1) ++#define QCA808X_LED_BLINK_FREQ_8HZ FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x2) ++#define QCA808X_LED_BLINK_FREQ_16HZ FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x3) ++#define QCA808X_LED_BLINK_FREQ_32HZ FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x4) ++#define QCA808X_LED_BLINK_FREQ_64HZ FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x5) ++#define QCA808X_LED_BLINK_FREQ_128HZ FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x6) ++#define QCA808X_LED_BLINK_FREQ_256HZ FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x7) ++#define QCA808X_LED_BLINK_DUTY_MASK GENMASK(2, 0) ++#define QCA808X_LED_BLINK_DUTY_50_50 FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x0) ++#define QCA808X_LED_BLINK_DUTY_75_25 FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x1) ++#define QCA808X_LED_BLINK_DUTY_25_75 FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x2) ++#define QCA808X_LED_BLINK_DUTY_33_67 FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x3) ++#define QCA808X_LED_BLINK_DUTY_67_33 FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x4) ++#define QCA808X_LED_BLINK_DUTY_17_83 FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x5) ++#define QCA808X_LED_BLINK_DUTY_83_17 FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x6) ++#define QCA808X_LED_BLINK_DUTY_8_92 FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x7) ++ ++/* LED hw control pattern is the same for every LED */ ++#define QCA808X_LED_PATTERN_MASK GENMASK(15, 0) ++#define QCA808X_LED_SPEED2500_ON BIT(15) ++#define QCA808X_LED_SPEED2500_BLINK BIT(14) ++/* Follow blink trigger even if duplex or speed condition doesn't match */ ++#define QCA808X_LED_BLINK_CHECK_BYPASS BIT(13) ++#define QCA808X_LED_FULL_DUPLEX_ON BIT(12) ++#define QCA808X_LED_HALF_DUPLEX_ON BIT(11) ++#define QCA808X_LED_TX_BLINK BIT(10) ++#define QCA808X_LED_RX_BLINK BIT(9) ++#define QCA808X_LED_TX_ON_10MS BIT(8) ++#define QCA808X_LED_RX_ON_10MS BIT(7) ++#define QCA808X_LED_SPEED1000_ON BIT(6) ++#define QCA808X_LED_SPEED100_ON BIT(5) ++#define QCA808X_LED_SPEED10_ON BIT(4) ++#define QCA808X_LED_COLLISION_BLINK BIT(3) ++#define QCA808X_LED_SPEED1000_BLINK BIT(2) ++#define QCA808X_LED_SPEED100_BLINK BIT(1) ++#define QCA808X_LED_SPEED10_BLINK BIT(0) ++ ++/* LED force ctrl is the same for every LED ++ * No documentation exist for this, not even internal one ++ * with NDA as QCOM gives only info about configuring ++ * hw control pattern rules and doesn't indicate any way ++ * to force the LED to specific mode. ++ * These define comes from reverse and testing and maybe ++ * lack of some info or some info are not entirely correct. ++ * For the basic LED control and hw control these finding ++ * are enough to support LED control in all the required APIs. ++ * ++ * On doing some comparison with implementation with qca807x, ++ * it was found that it's 1:1 equal to it and confirms all the ++ * reverse done. It was also found further specification with the ++ * force mode and the blink modes. ++ */ ++#define QCA808X_LED_FORCE_EN BIT(15) ++#define QCA808X_LED_FORCE_MODE_MASK GENMASK(14, 13) ++#define QCA808X_LED_FORCE_BLINK_1 FIELD_PREP(QCA808X_LED_FORCE_MODE_MASK, 0x3) ++#define QCA808X_LED_FORCE_BLINK_2 FIELD_PREP(QCA808X_LED_FORCE_MODE_MASK, 0x2) ++#define QCA808X_LED_FORCE_ON FIELD_PREP(QCA808X_LED_FORCE_MODE_MASK, 0x1) ++#define QCA808X_LED_FORCE_OFF FIELD_PREP(QCA808X_LED_FORCE_MODE_MASK, 0x0) ++ + #define AT803X_LOC_MAC_ADDR_0_15_OFFSET 0x804C + #define AT803X_LOC_MAC_ADDR_16_31_OFFSET 0x804B + #define AT803X_LOC_MAC_ADDR_32_47_OFFSET 0x804A diff --git a/target/linux/generic/backport-6.1/716-v6.9-09-net-phy-qcom-generalize-some-qca808x-LED-functions.patch b/target/linux/generic/backport-6.1/716-v6.9-09-net-phy-qcom-generalize-some-qca808x-LED-functions.patch new file mode 100644 index 000000000..da73c1d3b --- /dev/null +++ b/target/linux/generic/backport-6.1/716-v6.9-09-net-phy-qcom-generalize-some-qca808x-LED-functions.patch @@ -0,0 +1,172 @@ +From 47b930d0dd437af927145dba50a2e2ea1ba97c67 Mon Sep 17 00:00:00 2001 +From: Christian Marangi +Date: Tue, 6 Feb 2024 18:31:12 +0100 +Subject: [PATCH 09/10] net: phy: qcom: generalize some qca808x LED functions + +Generalize some qca808x LED functions in preparation for qca807x LED +support. + +The LED implementation of qca808x and qca807x is the same but qca807x +supports also Fiber port and have different hw control bits for Fiber +port. To limit code duplication introduce micro functions that takes reg +instead of LED index to tweak all the supported LED modes. + +Signed-off-by: Christian Marangi +Signed-off-by: David S. Miller +--- + drivers/net/phy/qcom/qca808x.c | 38 +++----------------- + drivers/net/phy/qcom/qcom-phy-lib.c | 54 +++++++++++++++++++++++++++++ + drivers/net/phy/qcom/qcom.h | 7 ++++ + 3 files changed, 65 insertions(+), 34 deletions(-) + +--- a/drivers/net/phy/qcom/qca808x.c ++++ b/drivers/net/phy/qcom/qca808x.c +@@ -437,9 +437,7 @@ static int qca808x_led_hw_control_enable + return -EINVAL; + + reg = QCA808X_MMD7_LED_FORCE_CTRL(index); +- +- return phy_clear_bits_mmd(phydev, MDIO_MMD_AN, reg, +- QCA808X_LED_FORCE_EN); ++ return qca808x_led_reg_hw_control_enable(phydev, reg); + } + + static int qca808x_led_hw_is_supported(struct phy_device *phydev, u8 index, +@@ -480,16 +478,12 @@ static int qca808x_led_hw_control_set(st + static bool qca808x_led_hw_control_status(struct phy_device *phydev, u8 index) + { + u16 reg; +- int val; + + if (index > 2) + return false; + + reg = QCA808X_MMD7_LED_FORCE_CTRL(index); +- +- val = phy_read_mmd(phydev, MDIO_MMD_AN, reg); +- +- return !(val & QCA808X_LED_FORCE_EN); ++ return qca808x_led_reg_hw_control_status(phydev, reg); + } + + static int qca808x_led_hw_control_get(struct phy_device *phydev, u8 index, +@@ -557,44 +551,20 @@ static int qca808x_led_brightness_set(st + } + + reg = QCA808X_MMD7_LED_FORCE_CTRL(index); +- +- return phy_modify_mmd(phydev, MDIO_MMD_AN, reg, +- QCA808X_LED_FORCE_EN | QCA808X_LED_FORCE_MODE_MASK, +- QCA808X_LED_FORCE_EN | (value ? QCA808X_LED_FORCE_ON : +- QCA808X_LED_FORCE_OFF)); ++ return qca808x_led_reg_brightness_set(phydev, reg, value); + } + + static int qca808x_led_blink_set(struct phy_device *phydev, u8 index, + unsigned long *delay_on, + unsigned long *delay_off) + { +- int ret; + u16 reg; + + if (index > 2) + return -EINVAL; + + reg = QCA808X_MMD7_LED_FORCE_CTRL(index); +- +- /* Set blink to 50% off, 50% on at 4Hz by default */ +- ret = phy_modify_mmd(phydev, MDIO_MMD_AN, QCA808X_MMD7_LED_GLOBAL, +- QCA808X_LED_BLINK_FREQ_MASK | QCA808X_LED_BLINK_DUTY_MASK, +- QCA808X_LED_BLINK_FREQ_4HZ | QCA808X_LED_BLINK_DUTY_50_50); +- if (ret) +- return ret; +- +- /* We use BLINK_1 for normal blinking */ +- ret = phy_modify_mmd(phydev, MDIO_MMD_AN, reg, +- QCA808X_LED_FORCE_EN | QCA808X_LED_FORCE_MODE_MASK, +- QCA808X_LED_FORCE_EN | QCA808X_LED_FORCE_BLINK_1); +- if (ret) +- return ret; +- +- /* We set blink to 4Hz, aka 250ms */ +- *delay_on = 250 / 2; +- *delay_off = 250 / 2; +- +- return 0; ++ return qca808x_led_reg_blink_set(phydev, reg, delay_on, delay_off); + } + + static int qca808x_led_polarity_set(struct phy_device *phydev, int index, +--- a/drivers/net/phy/qcom/qcom-phy-lib.c ++++ b/drivers/net/phy/qcom/qcom-phy-lib.c +@@ -620,3 +620,57 @@ int qca808x_cable_test_get_status(struct + return 0; + } + EXPORT_SYMBOL_GPL(qca808x_cable_test_get_status); ++ ++int qca808x_led_reg_hw_control_enable(struct phy_device *phydev, u16 reg) ++{ ++ return phy_clear_bits_mmd(phydev, MDIO_MMD_AN, reg, ++ QCA808X_LED_FORCE_EN); ++} ++EXPORT_SYMBOL_GPL(qca808x_led_reg_hw_control_enable); ++ ++bool qca808x_led_reg_hw_control_status(struct phy_device *phydev, u16 reg) ++{ ++ int val; ++ ++ val = phy_read_mmd(phydev, MDIO_MMD_AN, reg); ++ return !(val & QCA808X_LED_FORCE_EN); ++} ++EXPORT_SYMBOL_GPL(qca808x_led_reg_hw_control_status); ++ ++int qca808x_led_reg_brightness_set(struct phy_device *phydev, ++ u16 reg, enum led_brightness value) ++{ ++ return phy_modify_mmd(phydev, MDIO_MMD_AN, reg, ++ QCA808X_LED_FORCE_EN | QCA808X_LED_FORCE_MODE_MASK, ++ QCA808X_LED_FORCE_EN | (value ? QCA808X_LED_FORCE_ON : ++ QCA808X_LED_FORCE_OFF)); ++} ++EXPORT_SYMBOL_GPL(qca808x_led_reg_brightness_set); ++ ++int qca808x_led_reg_blink_set(struct phy_device *phydev, u16 reg, ++ unsigned long *delay_on, ++ unsigned long *delay_off) ++{ ++ int ret; ++ ++ /* Set blink to 50% off, 50% on at 4Hz by default */ ++ ret = phy_modify_mmd(phydev, MDIO_MMD_AN, QCA808X_MMD7_LED_GLOBAL, ++ QCA808X_LED_BLINK_FREQ_MASK | QCA808X_LED_BLINK_DUTY_MASK, ++ QCA808X_LED_BLINK_FREQ_4HZ | QCA808X_LED_BLINK_DUTY_50_50); ++ if (ret) ++ return ret; ++ ++ /* We use BLINK_1 for normal blinking */ ++ ret = phy_modify_mmd(phydev, MDIO_MMD_AN, reg, ++ QCA808X_LED_FORCE_EN | QCA808X_LED_FORCE_MODE_MASK, ++ QCA808X_LED_FORCE_EN | QCA808X_LED_FORCE_BLINK_1); ++ if (ret) ++ return ret; ++ ++ /* We set blink to 4Hz, aka 250ms */ ++ *delay_on = 250 / 2; ++ *delay_off = 250 / 2; ++ ++ return 0; ++} ++EXPORT_SYMBOL_GPL(qca808x_led_reg_blink_set); +--- a/drivers/net/phy/qcom/qcom.h ++++ b/drivers/net/phy/qcom/qcom.h +@@ -234,3 +234,10 @@ int at803x_cdt_start(struct phy_device * + int at803x_cdt_wait_for_completion(struct phy_device *phydev, + u32 cdt_en); + int qca808x_cable_test_get_status(struct phy_device *phydev, bool *finished); ++int qca808x_led_reg_hw_control_enable(struct phy_device *phydev, u16 reg); ++bool qca808x_led_reg_hw_control_status(struct phy_device *phydev, u16 reg); ++int qca808x_led_reg_brightness_set(struct phy_device *phydev, ++ u16 reg, enum led_brightness value); ++int qca808x_led_reg_blink_set(struct phy_device *phydev, u16 reg, ++ unsigned long *delay_on, ++ unsigned long *delay_off); diff --git a/target/linux/generic/backport-6.1/716-v6.9-10-net-phy-qca807x-add-support-for-configurable-LED.patch b/target/linux/generic/backport-6.1/716-v6.9-10-net-phy-qca807x-add-support-for-configurable-LED.patch new file mode 100644 index 000000000..3bd36f6ff --- /dev/null +++ b/target/linux/generic/backport-6.1/716-v6.9-10-net-phy-qca807x-add-support-for-configurable-LED.patch @@ -0,0 +1,326 @@ +From f508a226b517a6a8afd78a317de46bc83e3e3d51 Mon Sep 17 00:00:00 2001 +From: Christian Marangi +Date: Tue, 6 Feb 2024 18:31:13 +0100 +Subject: [PATCH 10/10] net: phy: qca807x: add support for configurable LED + +QCA8072/5 have up to 2 LEDs attached for PHY. + +LEDs can be configured to be ON/hw blink or be set to HW control. + +Hw blink mode is set to blink at 4Hz or 250ms. + +PHY can support both copper (TP) or fiber (FIBRE) kind and supports +different HW control modes based on the port type. + +HW control modes supported for netdev trigger for copper ports are: +- LINK_10 +- LINK_100 +- LINK_1000 +- TX +- RX +- FULL_DUPLEX +- HALF_DUPLEX + +HW control modes supported for netdev trigger for fiber ports are: +- LINK_100 +- LINK_1000 +- TX +- RX +- FULL_DUPLEX +- HALF_DUPLEX + +LED support conflicts with GPIO controller feature and must be disabled +if gpio-controller is used for the PHY. + +Signed-off-by: Christian Marangi +Signed-off-by: David S. Miller +--- + drivers/net/phy/qcom/qca807x.c | 256 ++++++++++++++++++++++++++++++++- + 1 file changed, 254 insertions(+), 2 deletions(-) + +--- a/drivers/net/phy/qcom/qca807x.c ++++ b/drivers/net/phy/qcom/qca807x.c +@@ -57,8 +57,18 @@ + #define QCA807X_MMD7_LED_CTRL(x) (0x8074 + ((x) * 2)) + #define QCA807X_MMD7_LED_FORCE_CTRL(x) (0x8075 + ((x) * 2)) + +-#define QCA807X_GPIO_FORCE_EN BIT(15) +-#define QCA807X_GPIO_FORCE_MODE_MASK GENMASK(14, 13) ++/* LED hw control pattern for fiber port */ ++#define QCA807X_LED_FIBER_PATTERN_MASK GENMASK(11, 1) ++#define QCA807X_LED_FIBER_TXACT_BLK_EN BIT(10) ++#define QCA807X_LED_FIBER_RXACT_BLK_EN BIT(9) ++#define QCA807X_LED_FIBER_FDX_ON_EN BIT(6) ++#define QCA807X_LED_FIBER_HDX_ON_EN BIT(5) ++#define QCA807X_LED_FIBER_1000BX_ON_EN BIT(2) ++#define QCA807X_LED_FIBER_100FX_ON_EN BIT(1) ++ ++/* Some device repurpose the LED as GPIO out */ ++#define QCA807X_GPIO_FORCE_EN QCA808X_LED_FORCE_EN ++#define QCA807X_GPIO_FORCE_MODE_MASK QCA808X_LED_FORCE_MODE_MASK + + #define QCA807X_FUNCTION_CONTROL 0x10 + #define QCA807X_FC_MDI_CROSSOVER_MODE_MASK GENMASK(6, 5) +@@ -121,6 +131,233 @@ static int qca807x_cable_test_start(stru + return 0; + } + ++static int qca807x_led_parse_netdev(struct phy_device *phydev, unsigned long rules, ++ u16 *offload_trigger) ++{ ++ /* Parsing specific to netdev trigger */ ++ switch (phydev->port) { ++ case PORT_TP: ++ if (test_bit(TRIGGER_NETDEV_TX, &rules)) ++ *offload_trigger |= QCA808X_LED_TX_BLINK; ++ if (test_bit(TRIGGER_NETDEV_RX, &rules)) ++ *offload_trigger |= QCA808X_LED_RX_BLINK; ++ if (test_bit(TRIGGER_NETDEV_LINK_10, &rules)) ++ *offload_trigger |= QCA808X_LED_SPEED10_ON; ++ if (test_bit(TRIGGER_NETDEV_LINK_100, &rules)) ++ *offload_trigger |= QCA808X_LED_SPEED100_ON; ++ if (test_bit(TRIGGER_NETDEV_LINK_1000, &rules)) ++ *offload_trigger |= QCA808X_LED_SPEED1000_ON; ++ if (test_bit(TRIGGER_NETDEV_HALF_DUPLEX, &rules)) ++ *offload_trigger |= QCA808X_LED_HALF_DUPLEX_ON; ++ if (test_bit(TRIGGER_NETDEV_FULL_DUPLEX, &rules)) ++ *offload_trigger |= QCA808X_LED_FULL_DUPLEX_ON; ++ break; ++ case PORT_FIBRE: ++ if (test_bit(TRIGGER_NETDEV_TX, &rules)) ++ *offload_trigger |= QCA807X_LED_FIBER_TXACT_BLK_EN; ++ if (test_bit(TRIGGER_NETDEV_RX, &rules)) ++ *offload_trigger |= QCA807X_LED_FIBER_RXACT_BLK_EN; ++ if (test_bit(TRIGGER_NETDEV_LINK_100, &rules)) ++ *offload_trigger |= QCA807X_LED_FIBER_100FX_ON_EN; ++ if (test_bit(TRIGGER_NETDEV_LINK_1000, &rules)) ++ *offload_trigger |= QCA807X_LED_FIBER_1000BX_ON_EN; ++ if (test_bit(TRIGGER_NETDEV_HALF_DUPLEX, &rules)) ++ *offload_trigger |= QCA807X_LED_FIBER_HDX_ON_EN; ++ if (test_bit(TRIGGER_NETDEV_FULL_DUPLEX, &rules)) ++ *offload_trigger |= QCA807X_LED_FIBER_FDX_ON_EN; ++ break; ++ default: ++ return -EOPNOTSUPP; ++ } ++ ++ if (rules && !*offload_trigger) ++ return -EOPNOTSUPP; ++ ++ return 0; ++} ++ ++static int qca807x_led_hw_control_enable(struct phy_device *phydev, u8 index) ++{ ++ u16 reg; ++ ++ if (index > 1) ++ return -EINVAL; ++ ++ reg = QCA807X_MMD7_LED_FORCE_CTRL(index); ++ return qca808x_led_reg_hw_control_enable(phydev, reg); ++} ++ ++static int qca807x_led_hw_is_supported(struct phy_device *phydev, u8 index, ++ unsigned long rules) ++{ ++ u16 offload_trigger = 0; ++ ++ if (index > 1) ++ return -EINVAL; ++ ++ return qca807x_led_parse_netdev(phydev, rules, &offload_trigger); ++} ++ ++static int qca807x_led_hw_control_set(struct phy_device *phydev, u8 index, ++ unsigned long rules) ++{ ++ u16 reg, mask, offload_trigger = 0; ++ int ret; ++ ++ if (index > 1) ++ return -EINVAL; ++ ++ ret = qca807x_led_parse_netdev(phydev, rules, &offload_trigger); ++ if (ret) ++ return ret; ++ ++ ret = qca807x_led_hw_control_enable(phydev, index); ++ if (ret) ++ return ret; ++ ++ switch (phydev->port) { ++ case PORT_TP: ++ reg = QCA807X_MMD7_LED_CTRL(index); ++ mask = QCA808X_LED_PATTERN_MASK; ++ break; ++ case PORT_FIBRE: ++ /* HW control pattern bits are in LED FORCE reg */ ++ reg = QCA807X_MMD7_LED_FORCE_CTRL(index); ++ mask = QCA807X_LED_FIBER_PATTERN_MASK; ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ return phy_modify_mmd(phydev, MDIO_MMD_AN, reg, mask, ++ offload_trigger); ++} ++ ++static bool qca807x_led_hw_control_status(struct phy_device *phydev, u8 index) ++{ ++ u16 reg; ++ ++ if (index > 1) ++ return false; ++ ++ reg = QCA807X_MMD7_LED_FORCE_CTRL(index); ++ return qca808x_led_reg_hw_control_status(phydev, reg); ++} ++ ++static int qca807x_led_hw_control_get(struct phy_device *phydev, u8 index, ++ unsigned long *rules) ++{ ++ u16 reg; ++ int val; ++ ++ if (index > 1) ++ return -EINVAL; ++ ++ /* Check if we have hw control enabled */ ++ if (qca807x_led_hw_control_status(phydev, index)) ++ return -EINVAL; ++ ++ /* Parsing specific to netdev trigger */ ++ switch (phydev->port) { ++ case PORT_TP: ++ reg = QCA807X_MMD7_LED_CTRL(index); ++ val = phy_read_mmd(phydev, MDIO_MMD_AN, reg); ++ if (val & QCA808X_LED_TX_BLINK) ++ set_bit(TRIGGER_NETDEV_TX, rules); ++ if (val & QCA808X_LED_RX_BLINK) ++ set_bit(TRIGGER_NETDEV_RX, rules); ++ if (val & QCA808X_LED_SPEED10_ON) ++ set_bit(TRIGGER_NETDEV_LINK_10, rules); ++ if (val & QCA808X_LED_SPEED100_ON) ++ set_bit(TRIGGER_NETDEV_LINK_100, rules); ++ if (val & QCA808X_LED_SPEED1000_ON) ++ set_bit(TRIGGER_NETDEV_LINK_1000, rules); ++ if (val & QCA808X_LED_HALF_DUPLEX_ON) ++ set_bit(TRIGGER_NETDEV_HALF_DUPLEX, rules); ++ if (val & QCA808X_LED_FULL_DUPLEX_ON) ++ set_bit(TRIGGER_NETDEV_FULL_DUPLEX, rules); ++ break; ++ case PORT_FIBRE: ++ /* HW control pattern bits are in LED FORCE reg */ ++ reg = QCA807X_MMD7_LED_FORCE_CTRL(index); ++ val = phy_read_mmd(phydev, MDIO_MMD_AN, reg); ++ if (val & QCA807X_LED_FIBER_TXACT_BLK_EN) ++ set_bit(TRIGGER_NETDEV_TX, rules); ++ if (val & QCA807X_LED_FIBER_RXACT_BLK_EN) ++ set_bit(TRIGGER_NETDEV_RX, rules); ++ if (val & QCA807X_LED_FIBER_100FX_ON_EN) ++ set_bit(TRIGGER_NETDEV_LINK_100, rules); ++ if (val & QCA807X_LED_FIBER_1000BX_ON_EN) ++ set_bit(TRIGGER_NETDEV_LINK_1000, rules); ++ if (val & QCA807X_LED_FIBER_HDX_ON_EN) ++ set_bit(TRIGGER_NETDEV_HALF_DUPLEX, rules); ++ if (val & QCA807X_LED_FIBER_FDX_ON_EN) ++ set_bit(TRIGGER_NETDEV_FULL_DUPLEX, rules); ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++ ++static int qca807x_led_hw_control_reset(struct phy_device *phydev, u8 index) ++{ ++ u16 reg, mask; ++ ++ if (index > 1) ++ return -EINVAL; ++ ++ switch (phydev->port) { ++ case PORT_TP: ++ reg = QCA807X_MMD7_LED_CTRL(index); ++ mask = QCA808X_LED_PATTERN_MASK; ++ break; ++ case PORT_FIBRE: ++ /* HW control pattern bits are in LED FORCE reg */ ++ reg = QCA807X_MMD7_LED_FORCE_CTRL(index); ++ mask = QCA807X_LED_FIBER_PATTERN_MASK; ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ return phy_clear_bits_mmd(phydev, MDIO_MMD_AN, reg, mask); ++} ++ ++static int qca807x_led_brightness_set(struct phy_device *phydev, ++ u8 index, enum led_brightness value) ++{ ++ u16 reg; ++ int ret; ++ ++ if (index > 1) ++ return -EINVAL; ++ ++ /* If we are setting off the LED reset any hw control rule */ ++ if (!value) { ++ ret = qca807x_led_hw_control_reset(phydev, index); ++ if (ret) ++ return ret; ++ } ++ ++ reg = QCA807X_MMD7_LED_FORCE_CTRL(index); ++ return qca808x_led_reg_brightness_set(phydev, reg, value); ++} ++ ++static int qca807x_led_blink_set(struct phy_device *phydev, u8 index, ++ unsigned long *delay_on, ++ unsigned long *delay_off) ++{ ++ u16 reg; ++ ++ if (index > 1) ++ return -EINVAL; ++ ++ reg = QCA807X_MMD7_LED_FORCE_CTRL(index); ++ return qca808x_led_reg_blink_set(phydev, reg, delay_on, delay_off); ++} ++ + #ifdef CONFIG_GPIOLIB + static int qca807x_gpio_get_direction(struct gpio_chip *gc, unsigned int offset) + { +@@ -496,6 +733,16 @@ static int qca807x_probe(struct phy_devi + "qcom,dac-disable-bias-current-tweak"); + + if (IS_ENABLED(CONFIG_GPIOLIB)) { ++ /* Make sure we don't have mixed leds node and gpio-controller ++ * to prevent registering leds and having gpio-controller usage ++ * conflicting with them. ++ */ ++ if (of_find_property(node, "leds", NULL) && ++ of_find_property(node, "gpio-controller", NULL)) { ++ phydev_err(phydev, "Invalid property detected. LEDs and gpio-controller are mutually exclusive."); ++ return -EINVAL; ++ } ++ + /* Do not register a GPIO controller unless flagged for it */ + if (of_property_read_bool(node, "gpio-controller")) { + ret = qca807x_gpio(phydev); +@@ -580,6 +827,11 @@ static struct phy_driver qca807x_drivers + .suspend = genphy_suspend, + .cable_test_start = qca807x_cable_test_start, + .cable_test_get_status = qca808x_cable_test_get_status, ++ .led_brightness_set = qca807x_led_brightness_set, ++ .led_blink_set = qca807x_led_blink_set, ++ .led_hw_is_supported = qca807x_led_hw_is_supported, ++ .led_hw_control_set = qca807x_led_hw_control_set, ++ .led_hw_control_get = qca807x_led_hw_control_get, + }, + }; + module_phy_driver(qca807x_drivers); diff --git a/target/linux/generic/backport-6.1/717-v6.9-net-phy-qca807x-move-interface-mode-check-to-.config.patch b/target/linux/generic/backport-6.1/717-v6.9-net-phy-qca807x-move-interface-mode-check-to-.config.patch new file mode 100644 index 000000000..53652c38f --- /dev/null +++ b/target/linux/generic/backport-6.1/717-v6.9-net-phy-qca807x-move-interface-mode-check-to-.config.patch @@ -0,0 +1,51 @@ +From 3be0d950b62852a693182cb678948f481de02825 Mon Sep 17 00:00:00 2001 +From: Robert Marko +Date: Mon, 12 Feb 2024 12:49:34 +0100 +Subject: [PATCH] net: phy: qca807x: move interface mode check to + .config_init_once + +Currently, we are checking whether the PHY package mode matches the +individual PHY interface modes at PHY package probe time, but at that time +we only know the PHY package mode and not the individual PHY interface +modes as of_get_phy_mode() that populates it will only get called once the +netdev to which PHY-s are attached to is being probed and thus this check +will always fail and return -EINVAL. + +So, lets move this check to .config_init_once as at that point individual +PHY interface modes should be populated. + +Fixes: d1cb613efbd3 ("net: phy: qcom: add support for QCA807x PHY Family") +Signed-off-by: Robert Marko +Reviewed-by: Andrew Lunn +Link: https://lore.kernel.org/r/20240212115043.1725918-1-robimarko@gmail.com +Signed-off-by: Paolo Abeni +--- + drivers/net/phy/qcom/qca807x.c | 10 +++++----- + 1 file changed, 5 insertions(+), 5 deletions(-) + +--- a/drivers/net/phy/qcom/qca807x.c ++++ b/drivers/net/phy/qcom/qca807x.c +@@ -562,6 +562,11 @@ static int qca807x_phy_package_config_in + struct qca807x_shared_priv *priv = shared->priv; + int val, ret; + ++ /* Make sure PHY follow PHY package mode if enforced */ ++ if (priv->package_mode != PHY_INTERFACE_MODE_NA && ++ phydev->interface != priv->package_mode) ++ return -EINVAL; ++ + phy_lock_mdio_bus(phydev); + + /* Set correct PHY package mode */ +@@ -718,11 +723,6 @@ static int qca807x_probe(struct phy_devi + shared = phydev->shared; + shared_priv = shared->priv; + +- /* Make sure PHY follow PHY package mode if enforced */ +- if (shared_priv->package_mode != PHY_INTERFACE_MODE_NA && +- phydev->interface != shared_priv->package_mode) +- return -EINVAL; +- + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; diff --git a/target/linux/generic/backport-6.1/718-v6.9-net-phy-qcom-at803x-fix-kernel-panic-with-at8031_pro.patch b/target/linux/generic/backport-6.1/718-v6.9-net-phy-qcom-at803x-fix-kernel-panic-with-at8031_pro.patch new file mode 100644 index 000000000..9b9ce2a3c --- /dev/null +++ b/target/linux/generic/backport-6.1/718-v6.9-net-phy-qcom-at803x-fix-kernel-panic-with-at8031_pro.patch @@ -0,0 +1,45 @@ +From 6a4aee277740d04ac0fd54cfa17cc28261932ddc Mon Sep 17 00:00:00 2001 +From: Christian Marangi +Date: Mon, 25 Mar 2024 20:06:19 +0100 +Subject: [PATCH] net: phy: qcom: at803x: fix kernel panic with at8031_probe + +On reworking and splitting the at803x driver, in splitting function of +at803x PHYs it was added a NULL dereference bug where priv is referenced +before it's actually allocated and then is tried to write to for the +is_1000basex and is_fiber variables in the case of at8031, writing on +the wrong address. + +Fix this by correctly setting priv local variable only after +at803x_probe is called and actually allocates priv in the phydev struct. + +Reported-by: William Wortel +Cc: +Fixes: 25d2ba94005f ("net: phy: at803x: move specific at8031 probe mode check to dedicated probe") +Signed-off-by: Christian Marangi +Reviewed-by: Andrew Lunn +Link: https://lore.kernel.org/r/20240325190621.2665-1-ansuelsmth@gmail.com +Signed-off-by: Paolo Abeni +--- + drivers/net/phy/qcom/at803x.c | 4 +++- + 1 file changed, 3 insertions(+), 1 deletion(-) + +--- a/drivers/net/phy/qcom/at803x.c ++++ b/drivers/net/phy/qcom/at803x.c +@@ -797,7 +797,7 @@ static int at8031_parse_dt(struct phy_de + + static int at8031_probe(struct phy_device *phydev) + { +- struct at803x_priv *priv = phydev->priv; ++ struct at803x_priv *priv; + int mode_cfg; + int ccr; + int ret; +@@ -806,6 +806,8 @@ static int at8031_probe(struct phy_devic + if (ret) + return ret; + ++ priv = phydev->priv; ++ + /* Only supported on AR8031/AR8033, the AR8030/AR8035 use strapping + * options. + */ diff --git a/target/linux/generic/backport-6.1/720-v6.9-net-mdio-ipq4019-add-support-for-clock-frequency-pro.patch b/target/linux/generic/backport-6.1/720-v6.9-net-mdio-ipq4019-add-support-for-clock-frequency-pro.patch new file mode 100644 index 000000000..e6a240dbd --- /dev/null +++ b/target/linux/generic/backport-6.1/720-v6.9-net-mdio-ipq4019-add-support-for-clock-frequency-pro.patch @@ -0,0 +1,205 @@ +From bdce82e960d1205d118662f575cec39379984e34 Mon Sep 17 00:00:00 2001 +From: Christian Marangi +Date: Wed, 31 Jan 2024 03:26:04 +0100 +Subject: [PATCH] net: mdio: ipq4019: add support for clock-frequency property + +The IPQ4019 MDIO internally divide the clock feed by AHB based on the +MDIO_MODE reg. On reset or power up, the default value for the +divider is 0xff that reflect the divider set to /256. + +This makes the MDC run at a very low rate, that is, considering AHB is +always fixed to 100Mhz, a value of 390KHz. + +This hasn't have been a problem as MDIO wasn't used for time sensitive +operation, it is now that on IPQ807x is usually mounted with PHY that +requires MDIO to load their firmware (example Aquantia PHY). + +To handle this problem and permit to set the correct designed MDC +frequency for the SoC add support for the standard "clock-frequency" +property for the MDIO node. + +The divider supports value from /1 to /256 and the common value are to +set it to /16 to reflect 6.25Mhz or to /8 on newer platform to reflect +12.5Mhz. + +To scan if the requested rate is supported by the divider, loop with +each supported divider and stop when the requested rate match the final +rate with the current divider. An error is returned if the rate doesn't +match any value. + +On MDIO reset, the divider is restored to the requested value to prevent +any kind of downclocking caused by the divider reverting to a default +value. + +To follow 802.3 spec of 2.5MHz of default value, if divider is set at +/256 and "clock-frequency" is not set in DT, assume nobody set the +divider and try to find the closest MDC rate to 2.5MHz. (in the case of +AHB set to 100MHz, it's 1.5625MHz) + +While at is also document other bits of the MDIO_MODE reg to have a +clear idea of what is actually applied there. + +Documentation of some BITs is skipped as they are marked as reserved and +their usage is not clear (RES 11:9 GENPHY 16:13 RES1 19:17) + +Signed-off-by: Christian Marangi +Reviewed-by: Andrew Lunn +Signed-off-by: David S. Miller +--- + drivers/net/mdio/mdio-ipq4019.c | 109 ++++++++++++++++++++++++++++++-- + 1 file changed, 103 insertions(+), 6 deletions(-) + +--- a/drivers/net/mdio/mdio-ipq4019.c ++++ b/drivers/net/mdio/mdio-ipq4019.c +@@ -14,6 +14,20 @@ + #include + + #define MDIO_MODE_REG 0x40 ++#define MDIO_MODE_MDC_MODE BIT(12) ++/* 0 = Clause 22, 1 = Clause 45 */ ++#define MDIO_MODE_C45 BIT(8) ++#define MDIO_MODE_DIV_MASK GENMASK(7, 0) ++#define MDIO_MODE_DIV(x) FIELD_PREP(MDIO_MODE_DIV_MASK, (x) - 1) ++#define MDIO_MODE_DIV_1 0x0 ++#define MDIO_MODE_DIV_2 0x1 ++#define MDIO_MODE_DIV_4 0x3 ++#define MDIO_MODE_DIV_8 0x7 ++#define MDIO_MODE_DIV_16 0xf ++#define MDIO_MODE_DIV_32 0x1f ++#define MDIO_MODE_DIV_64 0x3f ++#define MDIO_MODE_DIV_128 0x7f ++#define MDIO_MODE_DIV_256 0xff + #define MDIO_ADDR_REG 0x44 + #define MDIO_DATA_WRITE_REG 0x48 + #define MDIO_DATA_READ_REG 0x4c +@@ -26,9 +40,6 @@ + #define MDIO_CMD_ACCESS_CODE_C45_WRITE 1 + #define MDIO_CMD_ACCESS_CODE_C45_READ 2 + +-/* 0 = Clause 22, 1 = Clause 45 */ +-#define MDIO_MODE_C45 BIT(8) +- + #define IPQ4019_MDIO_TIMEOUT 10000 + #define IPQ4019_MDIO_SLEEP 10 + +@@ -41,6 +52,7 @@ struct ipq4019_mdio_data { + void __iomem *membase; + void __iomem *eth_ldo_rdy; + struct clk *mdio_clk; ++ unsigned int mdc_rate; + }; + + static int ipq4019_mdio_wait_busy(struct mii_bus *bus) +@@ -179,6 +191,38 @@ static int ipq4019_mdio_write(struct mii + return 0; + } + ++static int ipq4019_mdio_set_div(struct ipq4019_mdio_data *priv) ++{ ++ unsigned long ahb_rate; ++ int div; ++ u32 val; ++ ++ /* If we don't have a clock for AHB use the fixed value */ ++ ahb_rate = IPQ_MDIO_CLK_RATE; ++ if (priv->mdio_clk) ++ ahb_rate = clk_get_rate(priv->mdio_clk); ++ ++ /* MDC rate is ahb_rate/(MDIO_MODE_DIV + 1) ++ * While supported, internal documentation doesn't ++ * assure correct functionality of the MDIO bus ++ * with divider of 1, 2 or 4. ++ */ ++ for (div = 8; div <= 256; div *= 2) { ++ /* The requested rate is supported by the div */ ++ if (priv->mdc_rate == DIV_ROUND_UP(ahb_rate, div)) { ++ val = readl(priv->membase + MDIO_MODE_REG); ++ val &= ~MDIO_MODE_DIV_MASK; ++ val |= MDIO_MODE_DIV(div); ++ writel(val, priv->membase + MDIO_MODE_REG); ++ ++ return 0; ++ } ++ } ++ ++ /* The requested rate is not supported */ ++ return -EINVAL; ++} ++ + static int ipq_mdio_reset(struct mii_bus *bus) + { + struct ipq4019_mdio_data *priv = bus->priv; +@@ -201,10 +245,58 @@ static int ipq_mdio_reset(struct mii_bus + return ret; + + ret = clk_prepare_enable(priv->mdio_clk); +- if (ret == 0) +- mdelay(10); ++ if (ret) ++ return ret; ++ ++ mdelay(10); + +- return ret; ++ /* Restore MDC rate */ ++ return ipq4019_mdio_set_div(priv); ++} ++ ++static void ipq4019_mdio_select_mdc_rate(struct platform_device *pdev, ++ struct ipq4019_mdio_data *priv) ++{ ++ unsigned long ahb_rate; ++ int div; ++ u32 val; ++ ++ /* MDC rate defined in DT, we don't have to decide a default value */ ++ if (!of_property_read_u32(pdev->dev.of_node, "clock-frequency", ++ &priv->mdc_rate)) ++ return; ++ ++ /* If we don't have a clock for AHB use the fixed value */ ++ ahb_rate = IPQ_MDIO_CLK_RATE; ++ if (priv->mdio_clk) ++ ahb_rate = clk_get_rate(priv->mdio_clk); ++ ++ /* Check what is the current div set */ ++ val = readl(priv->membase + MDIO_MODE_REG); ++ div = FIELD_GET(MDIO_MODE_DIV_MASK, val); ++ ++ /* div is not set to the default value of /256 ++ * Probably someone changed that (bootloader, other drivers) ++ * Keep this and don't overwrite it. ++ */ ++ if (div != MDIO_MODE_DIV_256) { ++ priv->mdc_rate = DIV_ROUND_UP(ahb_rate, div + 1); ++ return; ++ } ++ ++ /* If div is /256 assume nobody have set this value and ++ * try to find one MDC rate that is close the 802.3 spec of ++ * 2.5MHz ++ */ ++ for (div = 256; div >= 8; div /= 2) { ++ /* Stop as soon as we found a divider that ++ * reached the closest value to 2.5MHz ++ */ ++ if (DIV_ROUND_UP(ahb_rate, div) > 2500000) ++ break; ++ ++ priv->mdc_rate = DIV_ROUND_UP(ahb_rate, div); ++ } + } + + static int ipq4019_mdio_probe(struct platform_device *pdev) +@@ -228,6 +320,11 @@ static int ipq4019_mdio_probe(struct pla + if (IS_ERR(priv->mdio_clk)) + return PTR_ERR(priv->mdio_clk); + ++ ipq4019_mdio_select_mdc_rate(pdev, priv); ++ ret = ipq4019_mdio_set_div(priv); ++ if (ret) ++ return ret; ++ + /* The platform resource is provided on the chipset IPQ5018 */ + /* This resource is optional */ + res = platform_get_resource(pdev, IORESOURCE_MEM, 1); diff --git a/target/linux/generic/backport-6.1/721-v6.7-net-phy-aquantia-drop-wrong-endianness-conversion-fo.patch b/target/linux/generic/backport-6.1/721-v6.7-net-phy-aquantia-drop-wrong-endianness-conversion-fo.patch new file mode 100644 index 000000000..d32a7edf9 --- /dev/null +++ b/target/linux/generic/backport-6.1/721-v6.7-net-phy-aquantia-drop-wrong-endianness-conversion-fo.patch @@ -0,0 +1,92 @@ +From 7edce370d87a23e8ed46af5b76a9fef1e341b67b Mon Sep 17 00:00:00 2001 +From: Christian Marangi +Date: Tue, 28 Nov 2023 14:59:28 +0100 +Subject: [PATCH] net: phy: aquantia: drop wrong endianness conversion for addr + and CRC + +On further testing on BE target with kernel test robot, it was notice +that the endianness conversion for addr and CRC in fw_load_memory was +wrong. + +Drop the cpu_to_le32 conversion for addr load as it's not needed. + +Use get_unaligned_le32 instead of get_unaligned for FW data word load to +correctly convert data in the correct order to follow system endian. + +Also drop the cpu_to_be32 for CRC calculation as it's wrong and would +cause different CRC on BE system. +The loaded word is swapped internally and MAILBOX calculates the CRC on +the swapped word. To correctly calculate the CRC to be later matched +with the one from MAILBOX, use an u8 struct and swap the word there to +keep the same order on both LE and BE for crc_ccitt_false function. +Also add additional comments on how the CRC verification for the loaded +section works. + +CRC is calculated as we load the section and verified with the MAILBOX +only after the entire section is loaded to skip additional slowdown by +loop the section data again. + +Reported-by: kernel test robot +Closes: https://lore.kernel.org/oe-kbuild-all/202311210414.sEJZjlcD-lkp@intel.com/ +Fixes: e93984ebc1c8 ("net: phy: aquantia: add firmware load support") +Tested-by: Robert Marko # ipq8072 LE device +Signed-off-by: Christian Marangi +Link: https://lore.kernel.org/r/20231128135928.9841-1-ansuelsmth@gmail.com +Signed-off-by: Jakub Kicinski +--- + drivers/net/phy/aquantia/aquantia_firmware.c | 24 ++++++++++++-------- + 1 file changed, 14 insertions(+), 10 deletions(-) + +--- a/drivers/net/phy/aquantia/aquantia_firmware.c ++++ b/drivers/net/phy/aquantia/aquantia_firmware.c +@@ -93,9 +93,6 @@ static int aqr_fw_load_memory(struct phy + u16 crc = 0, up_crc; + size_t pos; + +- /* PHY expect addr in LE */ +- addr = (__force u32)cpu_to_le32(addr); +- + phy_write_mmd(phydev, MDIO_MMD_VEND1, + VEND1_GLOBAL_MAILBOX_INTERFACE1, + VEND1_GLOBAL_MAILBOX_INTERFACE1_CRC_RESET); +@@ -110,10 +107,11 @@ static int aqr_fw_load_memory(struct phy + * If a firmware that is not word aligned is found, please report upstream. + */ + for (pos = 0; pos < len; pos += sizeof(u32)) { ++ u8 crc_data[4]; + u32 word; + + /* FW data is always stored in little-endian */ +- word = get_unaligned((const u32 *)(data + pos)); ++ word = get_unaligned_le32((const u32 *)(data + pos)); + + phy_write_mmd(phydev, MDIO_MMD_VEND1, VEND1_GLOBAL_MAILBOX_INTERFACE5, + VEND1_GLOBAL_MAILBOX_INTERFACE5_MSW_DATA(word)); +@@ -124,15 +122,21 @@ static int aqr_fw_load_memory(struct phy + VEND1_GLOBAL_MAILBOX_INTERFACE1_EXECUTE | + VEND1_GLOBAL_MAILBOX_INTERFACE1_WRITE); + +- /* calculate CRC as we load data to the mailbox. +- * We convert word to big-endian as PHY is BE and mailbox will +- * return a BE CRC. ++ /* Word is swapped internally and MAILBOX CRC is calculated ++ * using big-endian order. Mimic what the PHY does to have a ++ * matching CRC... + */ +- word = (__force u32)cpu_to_be32(word); +- crc = crc_ccitt_false(crc, (u8 *)&word, sizeof(word)); +- } ++ crc_data[0] = word >> 24; ++ crc_data[1] = word >> 16; ++ crc_data[2] = word >> 8; ++ crc_data[3] = word; + ++ /* ...calculate CRC as we load data... */ ++ crc = crc_ccitt_false(crc, crc_data, sizeof(crc_data)); ++ } ++ /* ...gets CRC from MAILBOX after we have loaded the entire section... */ + up_crc = phy_read_mmd(phydev, MDIO_MMD_VEND1, VEND1_GLOBAL_MAILBOX_INTERFACE2); ++ /* ...and make sure it does match our calculated CRC */ + if (crc != up_crc) { + phydev_err(phydev, "CRC mismatch: calculated 0x%04x PHY 0x%04x\n", + crc, up_crc); diff --git a/target/linux/generic/backport-6.1/724-v6.2-net-ethernet-mtk_eth_soc-enable-flow-offloading-supp.patch b/target/linux/generic/backport-6.1/724-v6.2-net-ethernet-mtk_eth_soc-enable-flow-offloading-supp.patch index 816aa6778..cf0d2a3a8 100644 --- a/target/linux/generic/backport-6.1/724-v6.2-net-ethernet-mtk_eth_soc-enable-flow-offloading-supp.patch +++ b/target/linux/generic/backport-6.1/724-v6.2-net-ethernet-mtk_eth_soc-enable-flow-offloading-supp.patch @@ -16,7 +16,7 @@ Signed-off-by: Jakub Kicinski --- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c +++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c -@@ -4333,6 +4333,7 @@ static const struct mtk_soc_data mt7986_ +@@ -4332,6 +4332,7 @@ static const struct mtk_soc_data mt7986_ .hw_features = MTK_HW_FEATURES, .required_clks = MT7986_CLKS_BITMAP, .required_pctl = false, diff --git a/target/linux/generic/backport-6.1/729-07-v6.1-net-ethernet-mtk_eth_soc-remove-cpu_relax-in-mtk_pen.patch b/target/linux/generic/backport-6.1/729-07-v6.1-net-ethernet-mtk_eth_soc-remove-cpu_relax-in-mtk_pen.patch index cefe1eeff..5a79242e3 100644 --- a/target/linux/generic/backport-6.1/729-07-v6.1-net-ethernet-mtk_eth_soc-remove-cpu_relax-in-mtk_pen.patch +++ b/target/linux/generic/backport-6.1/729-07-v6.1-net-ethernet-mtk_eth_soc-remove-cpu_relax-in-mtk_pen.patch @@ -12,7 +12,7 @@ Signed-off-by: David S. Miller --- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c +++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c -@@ -3480,11 +3480,8 @@ static void mtk_pending_work(struct work +@@ -3479,11 +3479,8 @@ static void mtk_pending_work(struct work rtnl_lock(); dev_dbg(eth->dev, "[%s][%d] reset\n", __func__, __LINE__); @@ -25,7 +25,7 @@ Signed-off-by: David S. Miller /* stop all devices to make sure that dma is properly shut down */ for (i = 0; i < MTK_MAC_COUNT; i++) { if (!eth->netdev[i]) -@@ -3518,7 +3515,7 @@ static void mtk_pending_work(struct work +@@ -3517,7 +3514,7 @@ static void mtk_pending_work(struct work dev_dbg(eth->dev, "[%s][%d] reset done\n", __func__, __LINE__); diff --git a/target/linux/generic/backport-6.1/729-18-v6.3-net-ethernet-mtk_eth_soc-introduce-mtk_hw_reset-util.patch b/target/linux/generic/backport-6.1/729-18-v6.3-net-ethernet-mtk_eth_soc-introduce-mtk_hw_reset-util.patch index c91861a8f..fde0af25d 100644 --- a/target/linux/generic/backport-6.1/729-18-v6.3-net-ethernet-mtk_eth_soc-introduce-mtk_hw_reset-util.patch +++ b/target/linux/generic/backport-6.1/729-18-v6.3-net-ethernet-mtk_eth_soc-introduce-mtk_hw_reset-util.patch @@ -16,7 +16,7 @@ Signed-off-by: Paolo Abeni --- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c +++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c -@@ -3256,6 +3256,27 @@ static void mtk_set_mcr_max_rx(struct mt +@@ -3255,6 +3255,27 @@ static void mtk_set_mcr_max_rx(struct mt mtk_w32(mac->hw, mcr_new, MTK_MAC_MCR(mac->id)); } @@ -44,7 +44,7 @@ Signed-off-by: Paolo Abeni static int mtk_hw_init(struct mtk_eth *eth) { u32 dma_mask = ETHSYS_DMA_AG_MAP_PDMA | ETHSYS_DMA_AG_MAP_QDMA | -@@ -3295,22 +3316,9 @@ static int mtk_hw_init(struct mtk_eth *e +@@ -3294,22 +3315,9 @@ static int mtk_hw_init(struct mtk_eth *e return 0; } diff --git a/target/linux/generic/backport-6.1/729-19-v6.3-net-ethernet-mtk_eth_soc-introduce-mtk_hw_warm_reset.patch b/target/linux/generic/backport-6.1/729-19-v6.3-net-ethernet-mtk_eth_soc-introduce-mtk_hw_warm_reset.patch index 6597eb5b7..e6a94f616 100644 --- a/target/linux/generic/backport-6.1/729-19-v6.3-net-ethernet-mtk_eth_soc-introduce-mtk_hw_warm_reset.patch +++ b/target/linux/generic/backport-6.1/729-19-v6.3-net-ethernet-mtk_eth_soc-introduce-mtk_hw_warm_reset.patch @@ -17,7 +17,7 @@ Signed-off-by: Paolo Abeni --- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c +++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c -@@ -3277,7 +3277,54 @@ static void mtk_hw_reset(struct mtk_eth +@@ -3276,7 +3276,54 @@ static void mtk_hw_reset(struct mtk_eth 0x3ffffff); } @@ -73,7 +73,7 @@ Signed-off-by: Paolo Abeni { u32 dma_mask = ETHSYS_DMA_AG_MAP_PDMA | ETHSYS_DMA_AG_MAP_QDMA | ETHSYS_DMA_AG_MAP_PPE; -@@ -3316,7 +3363,12 @@ static int mtk_hw_init(struct mtk_eth *e +@@ -3315,7 +3362,12 @@ static int mtk_hw_init(struct mtk_eth *e return 0; } @@ -87,7 +87,7 @@ Signed-off-by: Paolo Abeni if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V2)) { /* Set FE to PDMAv2 if necessary */ -@@ -3507,7 +3559,7 @@ static void mtk_pending_work(struct work +@@ -3506,7 +3558,7 @@ static void mtk_pending_work(struct work if (eth->dev->pins) pinctrl_select_state(eth->dev->pins->p, eth->dev->pins->default_state); @@ -96,7 +96,7 @@ Signed-off-by: Paolo Abeni /* restart DMA and enable IRQs */ for (i = 0; i < MTK_MAC_COUNT; i++) { -@@ -4109,7 +4161,7 @@ static int mtk_probe(struct platform_dev +@@ -4108,7 +4160,7 @@ static int mtk_probe(struct platform_dev eth->msg_enable = netif_msg_init(mtk_msg_level, MTK_DEFAULT_MSG_ENABLE); INIT_WORK(ð->pending_work, mtk_pending_work); diff --git a/target/linux/generic/backport-6.1/729-20-v6.3-net-ethernet-mtk_eth_soc-align-reset-procedure-to-ve.patch b/target/linux/generic/backport-6.1/729-20-v6.3-net-ethernet-mtk_eth_soc-align-reset-procedure-to-ve.patch index 55ab19f4c..7fdf7aa58 100644 --- a/target/linux/generic/backport-6.1/729-20-v6.3-net-ethernet-mtk_eth_soc-align-reset-procedure-to-ve.patch +++ b/target/linux/generic/backport-6.1/729-20-v6.3-net-ethernet-mtk_eth_soc-align-reset-procedure-to-ve.patch @@ -16,7 +16,7 @@ Signed-off-by: Paolo Abeni --- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c +++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c -@@ -2844,14 +2844,29 @@ static void mtk_dma_free(struct mtk_eth +@@ -2843,14 +2843,29 @@ static void mtk_dma_free(struct mtk_eth kfree(eth->scratch_head); } @@ -48,7 +48,7 @@ Signed-off-by: Paolo Abeni schedule_work(ð->pending_work); } -@@ -3331,15 +3346,17 @@ static int mtk_hw_init(struct mtk_eth *e +@@ -3330,15 +3345,17 @@ static int mtk_hw_init(struct mtk_eth *e const struct mtk_reg_map *reg_map = eth->soc->reg_map; int i, val, ret; @@ -72,7 +72,7 @@ Signed-off-by: Paolo Abeni if (eth->ethsys) regmap_update_bits(eth->ethsys, ETHSYS_DMA_AG_MAP, dma_mask, -@@ -3468,8 +3485,10 @@ static int mtk_hw_init(struct mtk_eth *e +@@ -3467,8 +3484,10 @@ static int mtk_hw_init(struct mtk_eth *e return 0; err_disable_pm: @@ -85,7 +85,7 @@ Signed-off-by: Paolo Abeni return ret; } -@@ -3531,30 +3550,53 @@ static int mtk_do_ioctl(struct net_devic +@@ -3530,30 +3549,53 @@ static int mtk_do_ioctl(struct net_devic return -EOPNOTSUPP; } @@ -148,7 +148,7 @@ Signed-off-by: Paolo Abeni if (eth->dev->pins) pinctrl_select_state(eth->dev->pins->p, -@@ -3565,15 +3607,19 @@ static void mtk_pending_work(struct work +@@ -3564,15 +3606,19 @@ static void mtk_pending_work(struct work for (i = 0; i < MTK_MAC_COUNT; i++) { if (!test_bit(i, &restart)) continue; diff --git a/target/linux/generic/backport-6.1/729-21-v6.3-net-ethernet-mtk_eth_soc-add-dma-checks-to-mtk_hw_re.patch b/target/linux/generic/backport-6.1/729-21-v6.3-net-ethernet-mtk_eth_soc-add-dma-checks-to-mtk_hw_re.patch index d5a7c0eba..a77f3da7f 100644 --- a/target/linux/generic/backport-6.1/729-21-v6.3-net-ethernet-mtk_eth_soc-add-dma-checks-to-mtk_hw_re.patch +++ b/target/linux/generic/backport-6.1/729-21-v6.3-net-ethernet-mtk_eth_soc-add-dma-checks-to-mtk_hw_re.patch @@ -49,7 +49,7 @@ Signed-off-by: Paolo Abeni }; /* strings used by ethtool */ -@@ -3339,6 +3345,102 @@ static void mtk_hw_warm_reset(struct mtk +@@ -3338,6 +3344,102 @@ static void mtk_hw_warm_reset(struct mtk val, rst_mask); } @@ -152,7 +152,7 @@ Signed-off-by: Paolo Abeni static int mtk_hw_init(struct mtk_eth *eth, bool reset) { u32 dma_mask = ETHSYS_DMA_AG_MAP_PDMA | ETHSYS_DMA_AG_MAP_QDMA | -@@ -3657,6 +3759,7 @@ static int mtk_cleanup(struct mtk_eth *e +@@ -3656,6 +3758,7 @@ static int mtk_cleanup(struct mtk_eth *e mtk_unreg_dev(eth); mtk_free_dev(eth); cancel_work_sync(ð->pending_work); @@ -160,7 +160,7 @@ Signed-off-by: Paolo Abeni return 0; } -@@ -4094,6 +4197,7 @@ static int mtk_probe(struct platform_dev +@@ -4093,6 +4196,7 @@ static int mtk_probe(struct platform_dev eth->rx_dim.mode = DIM_CQ_PERIOD_MODE_START_FROM_EQE; INIT_WORK(ð->rx_dim.work, mtk_dim_rx); @@ -168,7 +168,7 @@ Signed-off-by: Paolo Abeni eth->tx_dim.mode = DIM_CQ_PERIOD_MODE_START_FROM_EQE; INIT_WORK(ð->tx_dim.work, mtk_dim_tx); -@@ -4296,6 +4400,8 @@ static int mtk_probe(struct platform_dev +@@ -4295,6 +4399,8 @@ static int mtk_probe(struct platform_dev netif_napi_add(ð->dummy_dev, ð->rx_napi, mtk_napi_rx); platform_set_drvdata(pdev, eth); diff --git a/target/linux/generic/backport-6.1/729-22-v6.3-net-ethernet-mtk_wed-add-reset-reset_complete-callba.patch b/target/linux/generic/backport-6.1/729-22-v6.3-net-ethernet-mtk_wed-add-reset-reset_complete-callba.patch index c21d094ae..3f047da75 100644 --- a/target/linux/generic/backport-6.1/729-22-v6.3-net-ethernet-mtk_wed-add-reset-reset_complete-callba.patch +++ b/target/linux/generic/backport-6.1/729-22-v6.3-net-ethernet-mtk_wed-add-reset-reset_complete-callba.patch @@ -14,7 +14,7 @@ Signed-off-by: Paolo Abeni --- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c +++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c -@@ -3688,6 +3688,11 @@ static void mtk_pending_work(struct work +@@ -3687,6 +3687,11 @@ static void mtk_pending_work(struct work set_bit(MTK_RESETTING, ð->state); mtk_prepare_for_reset(eth); @@ -26,7 +26,7 @@ Signed-off-by: Paolo Abeni /* stop all devices to make sure that dma is properly shut down */ for (i = 0; i < MTK_MAC_COUNT; i++) { -@@ -3725,6 +3730,8 @@ static void mtk_pending_work(struct work +@@ -3724,6 +3729,8 @@ static void mtk_pending_work(struct work clear_bit(MTK_RESETTING, ð->state); diff --git a/target/linux/generic/backport-6.1/730-02-v6.3-net-ethernet-mtk_eth_soc-increase-tx-ring-side-for-Q.patch b/target/linux/generic/backport-6.1/730-02-v6.3-net-ethernet-mtk_eth_soc-increase-tx-ring-side-for-Q.patch index 046a58122..41d1bceac 100644 --- a/target/linux/generic/backport-6.1/730-02-v6.3-net-ethernet-mtk_eth_soc-increase-tx-ring-side-for-Q.patch +++ b/target/linux/generic/backport-6.1/730-02-v6.3-net-ethernet-mtk_eth_soc-increase-tx-ring-side-for-Q.patch @@ -12,7 +12,7 @@ Signed-off-by: Felix Fietkau --- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c +++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c -@@ -944,7 +944,7 @@ static int mtk_init_fq_dma(struct mtk_et +@@ -943,7 +943,7 @@ static int mtk_init_fq_dma(struct mtk_et { const struct mtk_soc_data *soc = eth->soc; dma_addr_t phy_ring_tail; @@ -21,7 +21,7 @@ Signed-off-by: Felix Fietkau dma_addr_t dma_addr; int i; -@@ -2208,19 +2208,25 @@ static int mtk_tx_alloc(struct mtk_eth * +@@ -2207,19 +2207,25 @@ static int mtk_tx_alloc(struct mtk_eth * struct mtk_tx_ring *ring = ð->tx_ring; int i, sz = soc->txrx.txd_size; struct mtk_tx_dma_v2 *txd; @@ -51,7 +51,7 @@ Signed-off-by: Felix Fietkau u32 next_ptr = ring->phys + next * sz; txd = ring->dma + i * sz; -@@ -2240,22 +2246,22 @@ static int mtk_tx_alloc(struct mtk_eth * +@@ -2239,22 +2245,22 @@ static int mtk_tx_alloc(struct mtk_eth * * descriptors in ring->dma_pdma. */ if (!MTK_HAS_CAPS(soc->caps, MTK_QDMA)) { @@ -79,7 +79,7 @@ Signed-off-by: Felix Fietkau ring->thresh = MAX_SKB_FRAGS; /* make sure that all changes to the dma ring are flushed before we -@@ -2267,14 +2273,14 @@ static int mtk_tx_alloc(struct mtk_eth * +@@ -2266,14 +2272,14 @@ static int mtk_tx_alloc(struct mtk_eth * mtk_w32(eth, ring->phys, soc->reg_map->qdma.ctx_ptr); mtk_w32(eth, ring->phys, soc->reg_map->qdma.dtx_ptr); mtk_w32(eth, @@ -96,7 +96,7 @@ Signed-off-by: Felix Fietkau mtk_w32(eth, 0, MT7628_TX_CTX_IDX0); mtk_w32(eth, MT7628_PST_DTX_IDX0, soc->reg_map->pdma.rst_idx); } -@@ -2292,7 +2298,7 @@ static void mtk_tx_clean(struct mtk_eth +@@ -2291,7 +2297,7 @@ static void mtk_tx_clean(struct mtk_eth int i; if (ring->buf) { @@ -105,7 +105,7 @@ Signed-off-by: Felix Fietkau mtk_tx_unmap(eth, &ring->buf[i], NULL, false); kfree(ring->buf); ring->buf = NULL; -@@ -2300,14 +2306,14 @@ static void mtk_tx_clean(struct mtk_eth +@@ -2299,14 +2305,14 @@ static void mtk_tx_clean(struct mtk_eth if (ring->dma) { dma_free_coherent(eth->dma_dev, @@ -122,7 +122,7 @@ Signed-off-by: Felix Fietkau ring->dma_pdma, ring->phys_pdma); ring->dma_pdma = NULL; } -@@ -2832,7 +2838,7 @@ static void mtk_dma_free(struct mtk_eth +@@ -2831,7 +2837,7 @@ static void mtk_dma_free(struct mtk_eth netdev_reset_queue(eth->netdev[i]); if (eth->scratch_ring) { dma_free_coherent(eth->dma_dev, diff --git a/target/linux/generic/backport-6.1/730-03-v6.3-net-ethernet-mtk_eth_soc-avoid-port_mg-assignment-on.patch b/target/linux/generic/backport-6.1/730-03-v6.3-net-ethernet-mtk_eth_soc-avoid-port_mg-assignment-on.patch index 7e879ca1d..c9823daa1 100644 --- a/target/linux/generic/backport-6.1/730-03-v6.3-net-ethernet-mtk_eth_soc-avoid-port_mg-assignment-on.patch +++ b/target/linux/generic/backport-6.1/730-03-v6.3-net-ethernet-mtk_eth_soc-avoid-port_mg-assignment-on.patch @@ -12,7 +12,7 @@ Signed-off-by: Felix Fietkau --- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c +++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c -@@ -4479,7 +4479,7 @@ static const struct mtk_soc_data mt7621_ +@@ -4478,7 +4478,7 @@ static const struct mtk_soc_data mt7621_ .hw_features = MTK_HW_FEATURES, .required_clks = MT7621_CLKS_BITMAP, .required_pctl = false, @@ -21,7 +21,7 @@ Signed-off-by: Felix Fietkau .hash_offset = 2, .foe_entry_size = sizeof(struct mtk_foe_entry) - 16, .txrx = { -@@ -4518,7 +4518,7 @@ static const struct mtk_soc_data mt7623_ +@@ -4517,7 +4517,7 @@ static const struct mtk_soc_data mt7623_ .hw_features = MTK_HW_FEATURES, .required_clks = MT7623_CLKS_BITMAP, .required_pctl = true, diff --git a/target/linux/generic/backport-6.1/730-04-v6.3-net-ethernet-mtk_eth_soc-implement-multi-queue-suppo.patch b/target/linux/generic/backport-6.1/730-04-v6.3-net-ethernet-mtk_eth_soc-implement-multi-queue-suppo.patch index 2f669bde3..0d462b0c8 100644 --- a/target/linux/generic/backport-6.1/730-04-v6.3-net-ethernet-mtk_eth_soc-implement-multi-queue-suppo.patch +++ b/target/linux/generic/backport-6.1/730-04-v6.3-net-ethernet-mtk_eth_soc-implement-multi-queue-suppo.patch @@ -139,7 +139,7 @@ Signed-off-by: Felix Fietkau /* Configure duplex */ if (duplex == DUPLEX_FULL) mcr |= MAC_MCR_FORCE_DPX; -@@ -1105,7 +1180,8 @@ static void mtk_tx_set_dma_desc_v1(struc +@@ -1104,7 +1179,8 @@ static void mtk_tx_set_dma_desc_v1(struc WRITE_ONCE(desc->txd1, info->addr); @@ -149,7 +149,7 @@ Signed-off-by: Felix Fietkau if (info->last) data |= TX_DMA_LS0; WRITE_ONCE(desc->txd3, data); -@@ -1139,9 +1215,6 @@ static void mtk_tx_set_dma_desc_v2(struc +@@ -1138,9 +1214,6 @@ static void mtk_tx_set_dma_desc_v2(struc data |= TX_DMA_LS0; WRITE_ONCE(desc->txd3, data); @@ -159,7 +159,7 @@ Signed-off-by: Felix Fietkau data = (mac->id + 1) << TX_DMA_FPORT_SHIFT_V2; /* forward port */ data |= TX_DMA_SWC_V2 | QID_BITS_V2(info->qid); WRITE_ONCE(desc->txd4, data); -@@ -1185,11 +1258,12 @@ static int mtk_tx_map(struct sk_buff *sk +@@ -1184,11 +1257,12 @@ static int mtk_tx_map(struct sk_buff *sk .gso = gso, .csum = skb->ip_summed == CHECKSUM_PARTIAL, .vlan = skb_vlan_tag_present(skb), @@ -173,7 +173,7 @@ Signed-off-by: Felix Fietkau struct mtk_mac *mac = netdev_priv(dev); struct mtk_eth *eth = mac->hw; const struct mtk_soc_data *soc = eth->soc; -@@ -1197,8 +1271,10 @@ static int mtk_tx_map(struct sk_buff *sk +@@ -1196,8 +1270,10 @@ static int mtk_tx_map(struct sk_buff *sk struct mtk_tx_dma *itxd_pdma, *txd_pdma; struct mtk_tx_buf *itx_buf, *tx_buf; int i, n_desc = 1; @@ -184,7 +184,7 @@ Signed-off-by: Felix Fietkau itxd = ring->next_free; itxd_pdma = qdma_to_pdma(ring, itxd); if (itxd == ring->last_free) -@@ -1247,7 +1323,7 @@ static int mtk_tx_map(struct sk_buff *sk +@@ -1246,7 +1322,7 @@ static int mtk_tx_map(struct sk_buff *sk memset(&txd_info, 0, sizeof(struct mtk_tx_dma_desc_info)); txd_info.size = min_t(unsigned int, frag_size, soc->txrx.dma_max_len); @@ -193,7 +193,7 @@ Signed-off-by: Felix Fietkau txd_info.last = i == skb_shinfo(skb)->nr_frags - 1 && !(frag_size - txd_info.size); txd_info.addr = skb_frag_dma_map(eth->dma_dev, frag, -@@ -1286,7 +1362,7 @@ static int mtk_tx_map(struct sk_buff *sk +@@ -1285,7 +1361,7 @@ static int mtk_tx_map(struct sk_buff *sk txd_pdma->txd2 |= TX_DMA_LS1; } @@ -202,7 +202,7 @@ Signed-off-by: Felix Fietkau skb_tx_timestamp(skb); ring->next_free = mtk_qdma_phys_to_virt(ring, txd->txd2); -@@ -1298,8 +1374,7 @@ static int mtk_tx_map(struct sk_buff *sk +@@ -1297,8 +1373,7 @@ static int mtk_tx_map(struct sk_buff *sk wmb(); if (MTK_HAS_CAPS(soc->caps, MTK_QDMA)) { @@ -212,7 +212,7 @@ Signed-off-by: Felix Fietkau mtk_w32(eth, txd->txd2, soc->reg_map->qdma.ctx_ptr); } else { int next_idx; -@@ -1368,7 +1443,7 @@ static void mtk_wake_queue(struct mtk_et +@@ -1367,7 +1442,7 @@ static void mtk_wake_queue(struct mtk_et for (i = 0; i < MTK_MAC_COUNT; i++) { if (!eth->netdev[i]) continue; @@ -221,7 +221,7 @@ Signed-off-by: Felix Fietkau } } -@@ -1392,7 +1467,7 @@ static netdev_tx_t mtk_start_xmit(struct +@@ -1391,7 +1466,7 @@ static netdev_tx_t mtk_start_xmit(struct tx_num = mtk_cal_txd_req(eth, skb); if (unlikely(atomic_read(&ring->free_count) <= tx_num)) { @@ -230,7 +230,7 @@ Signed-off-by: Felix Fietkau netif_err(eth, tx_queued, dev, "Tx Ring full when queue awake!\n"); spin_unlock(ð->page_lock); -@@ -1418,7 +1493,7 @@ static netdev_tx_t mtk_start_xmit(struct +@@ -1417,7 +1492,7 @@ static netdev_tx_t mtk_start_xmit(struct goto drop; if (unlikely(atomic_read(&ring->free_count) <= ring->thresh)) @@ -239,7 +239,7 @@ Signed-off-by: Felix Fietkau spin_unlock(ð->page_lock); -@@ -1585,10 +1660,12 @@ static int mtk_xdp_submit_frame(struct m +@@ -1584,10 +1659,12 @@ static int mtk_xdp_submit_frame(struct m struct skb_shared_info *sinfo = xdp_get_shared_info_from_frame(xdpf); const struct mtk_soc_data *soc = eth->soc; struct mtk_tx_ring *ring = ð->tx_ring; @@ -252,7 +252,7 @@ Signed-off-by: Felix Fietkau }; int err, index = 0, n_desc = 1, nr_frags; struct mtk_tx_buf *htx_buf, *tx_buf; -@@ -1638,6 +1715,7 @@ static int mtk_xdp_submit_frame(struct m +@@ -1637,6 +1714,7 @@ static int mtk_xdp_submit_frame(struct m memset(&txd_info, 0, sizeof(struct mtk_tx_dma_desc_info)); txd_info.size = skb_frag_size(&sinfo->frags[index]); txd_info.last = index + 1 == nr_frags; @@ -260,7 +260,7 @@ Signed-off-by: Felix Fietkau data = skb_frag_address(&sinfo->frags[index]); index++; -@@ -1992,8 +2070,46 @@ rx_done: +@@ -1991,8 +2069,46 @@ rx_done: return done; } @@ -308,7 +308,7 @@ Signed-off-by: Felix Fietkau { const struct mtk_reg_map *reg_map = eth->soc->reg_map; struct mtk_tx_ring *ring = ð->tx_ring; -@@ -2025,12 +2141,9 @@ static int mtk_poll_tx_qdma(struct mtk_e +@@ -2024,12 +2140,9 @@ static int mtk_poll_tx_qdma(struct mtk_e break; if (tx_buf->data != (void *)MTK_DMA_DUMMY_DESC) { @@ -323,7 +323,7 @@ Signed-off-by: Felix Fietkau budget--; } mtk_tx_unmap(eth, tx_buf, &bq, true); -@@ -2049,7 +2162,7 @@ static int mtk_poll_tx_qdma(struct mtk_e +@@ -2048,7 +2161,7 @@ static int mtk_poll_tx_qdma(struct mtk_e } static int mtk_poll_tx_pdma(struct mtk_eth *eth, int budget, @@ -332,7 +332,7 @@ Signed-off-by: Felix Fietkau { struct mtk_tx_ring *ring = ð->tx_ring; struct mtk_tx_buf *tx_buf; -@@ -2067,12 +2180,8 @@ static int mtk_poll_tx_pdma(struct mtk_e +@@ -2066,12 +2179,8 @@ static int mtk_poll_tx_pdma(struct mtk_e break; if (tx_buf->data != (void *)MTK_DMA_DUMMY_DESC) { @@ -347,7 +347,7 @@ Signed-off-by: Felix Fietkau budget--; } mtk_tx_unmap(eth, tx_buf, &bq, true); -@@ -2094,26 +2203,15 @@ static int mtk_poll_tx(struct mtk_eth *e +@@ -2093,26 +2202,15 @@ static int mtk_poll_tx(struct mtk_eth *e { struct mtk_tx_ring *ring = ð->tx_ring; struct dim_sample dim_sample = {}; @@ -379,7 +379,7 @@ Signed-off-by: Felix Fietkau dim_update_sample(eth->tx_events, eth->tx_packets, eth->tx_bytes, &dim_sample); -@@ -2123,7 +2221,7 @@ static int mtk_poll_tx(struct mtk_eth *e +@@ -2122,7 +2220,7 @@ static int mtk_poll_tx(struct mtk_eth *e (atomic_read(&ring->free_count) > ring->thresh)) mtk_wake_queue(eth); @@ -388,7 +388,7 @@ Signed-off-by: Felix Fietkau } static void mtk_handle_status_irq(struct mtk_eth *eth) -@@ -2209,6 +2307,7 @@ static int mtk_tx_alloc(struct mtk_eth * +@@ -2208,6 +2306,7 @@ static int mtk_tx_alloc(struct mtk_eth * int i, sz = soc->txrx.txd_size; struct mtk_tx_dma_v2 *txd; int ring_size; @@ -396,7 +396,7 @@ Signed-off-by: Felix Fietkau if (MTK_HAS_CAPS(soc->caps, MTK_QDMA)) ring_size = MTK_QDMA_RING_SIZE; -@@ -2276,8 +2375,25 @@ static int mtk_tx_alloc(struct mtk_eth * +@@ -2275,8 +2374,25 @@ static int mtk_tx_alloc(struct mtk_eth * ring->phys + ((ring_size - 1) * sz), soc->reg_map->qdma.crx_ptr); mtk_w32(eth, ring->last_free_ptr, soc->reg_map->qdma.drx_ptr); @@ -424,7 +424,7 @@ Signed-off-by: Felix Fietkau } else { mtk_w32(eth, ring->phys_pdma, MT7628_TX_BASE_PTR0); mtk_w32(eth, ring_size, MT7628_TX_MAX_CNT0); -@@ -2962,7 +3078,7 @@ static int mtk_start_dma(struct mtk_eth +@@ -2961,7 +3077,7 @@ static int mtk_start_dma(struct mtk_eth if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V2)) val |= MTK_MUTLI_CNT | MTK_RESV_BUF | MTK_WCOMP_EN | MTK_DMAD_WR_WDONE | @@ -433,7 +433,7 @@ Signed-off-by: Felix Fietkau else val |= MTK_RX_BT_32DWORDS; mtk_w32(eth, val, reg_map->qdma.glo_cfg); -@@ -3008,6 +3124,45 @@ static void mtk_gdm_config(struct mtk_et +@@ -3007,6 +3123,45 @@ static void mtk_gdm_config(struct mtk_et mtk_w32(eth, 0, MTK_RST_GL); } @@ -479,7 +479,7 @@ Signed-off-by: Felix Fietkau static int mtk_open(struct net_device *dev) { struct mtk_mac *mac = netdev_priv(dev); -@@ -3050,7 +3205,8 @@ static int mtk_open(struct net_device *d +@@ -3049,7 +3204,8 @@ static int mtk_open(struct net_device *d refcount_inc(ð->dma_refcnt); phylink_start(mac->phylink); @@ -489,7 +489,7 @@ Signed-off-by: Felix Fietkau return 0; } -@@ -3759,8 +3915,12 @@ static int mtk_unreg_dev(struct mtk_eth +@@ -3758,8 +3914,12 @@ static int mtk_unreg_dev(struct mtk_eth int i; for (i = 0; i < MTK_MAC_COUNT; i++) { @@ -502,7 +502,7 @@ Signed-off-by: Felix Fietkau unregister_netdev(eth->netdev[i]); } -@@ -3977,6 +4137,23 @@ static int mtk_set_rxnfc(struct net_devi +@@ -3976,6 +4136,23 @@ static int mtk_set_rxnfc(struct net_devi return ret; } @@ -526,7 +526,7 @@ Signed-off-by: Felix Fietkau static const struct ethtool_ops mtk_ethtool_ops = { .get_link_ksettings = mtk_get_link_ksettings, .set_link_ksettings = mtk_set_link_ksettings, -@@ -4011,6 +4188,7 @@ static const struct net_device_ops mtk_n +@@ -4010,6 +4187,7 @@ static const struct net_device_ops mtk_n .ndo_setup_tc = mtk_eth_setup_tc, .ndo_bpf = mtk_xdp, .ndo_xdp_xmit = mtk_xdp_xmit, @@ -534,7 +534,7 @@ Signed-off-by: Felix Fietkau }; static int mtk_add_mac(struct mtk_eth *eth, struct device_node *np) -@@ -4020,6 +4198,7 @@ static int mtk_add_mac(struct mtk_eth *e +@@ -4019,6 +4197,7 @@ static int mtk_add_mac(struct mtk_eth *e struct phylink *phylink; struct mtk_mac *mac; int id, err; @@ -542,7 +542,7 @@ Signed-off-by: Felix Fietkau if (!_id) { dev_err(eth->dev, "missing mac id\n"); -@@ -4037,7 +4216,10 @@ static int mtk_add_mac(struct mtk_eth *e +@@ -4036,7 +4215,10 @@ static int mtk_add_mac(struct mtk_eth *e return -EINVAL; } @@ -554,7 +554,7 @@ Signed-off-by: Felix Fietkau if (!eth->netdev[id]) { dev_err(eth->dev, "alloc_etherdev failed\n"); return -ENOMEM; -@@ -4145,6 +4327,11 @@ static int mtk_add_mac(struct mtk_eth *e +@@ -4144,6 +4326,11 @@ static int mtk_add_mac(struct mtk_eth *e else eth->netdev[id]->max_mtu = MTK_MAX_RX_LENGTH_2K - MTK_RX_ETH_HLEN; diff --git a/target/linux/generic/backport-6.1/730-09-v6.3-net-ethernet-mtk_eth_soc-fix-VLAN-rx-hardware-accele.patch b/target/linux/generic/backport-6.1/730-09-v6.3-net-ethernet-mtk_eth_soc-fix-VLAN-rx-hardware-accele.patch index b8e3452f3..53d8ff472 100644 --- a/target/linux/generic/backport-6.1/730-09-v6.3-net-ethernet-mtk_eth_soc-fix-VLAN-rx-hardware-accele.patch +++ b/target/linux/generic/backport-6.1/730-09-v6.3-net-ethernet-mtk_eth_soc-fix-VLAN-rx-hardware-accele.patch @@ -22,7 +22,7 @@ Signed-off-by: Felix Fietkau #include "mtk_eth_soc.h" #include "mtk_wed.h" -@@ -2021,16 +2022,22 @@ static int mtk_poll_rx(struct napi_struc +@@ -2020,16 +2021,22 @@ static int mtk_poll_rx(struct napi_struc htons(RX_DMA_VPID(trxd.rxd4)), RX_DMA_VID(trxd.rxd4)); } else if (trxd.rxd2 & RX_DMA_VTAG) { @@ -52,7 +52,7 @@ Signed-off-by: Felix Fietkau } skb_record_rx_queue(skb, 0); -@@ -2858,15 +2865,30 @@ static netdev_features_t mtk_fix_feature +@@ -2857,15 +2864,30 @@ static netdev_features_t mtk_fix_feature static int mtk_set_features(struct net_device *dev, netdev_features_t features) { @@ -88,7 +88,7 @@ Signed-off-by: Felix Fietkau } /* wait for DMA to finish whatever it is doing before we start using it again */ -@@ -3163,11 +3185,45 @@ found: +@@ -3162,11 +3184,45 @@ found: return NOTIFY_DONE; } @@ -135,7 +135,7 @@ Signed-off-by: Felix Fietkau err = phylink_of_phy_connect(mac->phylink, mac->of_node, 0); if (err) { -@@ -3688,6 +3744,10 @@ static int mtk_hw_init(struct mtk_eth *e +@@ -3687,6 +3743,10 @@ static int mtk_hw_init(struct mtk_eth *e */ val = mtk_r32(eth, MTK_CDMQ_IG_CTRL); mtk_w32(eth, val | MTK_CDMQ_STAG_EN, MTK_CDMQ_IG_CTRL); @@ -146,7 +146,7 @@ Signed-off-by: Felix Fietkau /* Enable RX VLan Offloading */ mtk_w32(eth, 1, MTK_CDMP_EG_CTRL); -@@ -3907,6 +3967,12 @@ static int mtk_free_dev(struct mtk_eth * +@@ -3906,6 +3966,12 @@ static int mtk_free_dev(struct mtk_eth * free_netdev(eth->netdev[i]); } diff --git a/target/linux/generic/backport-6.1/730-12-v6.3-net-ethernet-mtk_eth_soc-disable-hardware-DSA-untagg.patch b/target/linux/generic/backport-6.1/730-12-v6.3-net-ethernet-mtk_eth_soc-disable-hardware-DSA-untagg.patch index a88df2b8e..08d72fc82 100644 --- a/target/linux/generic/backport-6.1/730-12-v6.3-net-ethernet-mtk_eth_soc-disable-hardware-DSA-untagg.patch +++ b/target/linux/generic/backport-6.1/730-12-v6.3-net-ethernet-mtk_eth_soc-disable-hardware-DSA-untagg.patch @@ -20,7 +20,7 @@ Signed-off-by: Jakub Kicinski --- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c +++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c -@@ -3201,7 +3201,8 @@ static int mtk_open(struct net_device *d +@@ -3200,7 +3200,8 @@ static int mtk_open(struct net_device *d struct mtk_eth *eth = mac->hw; int i, err; @@ -30,7 +30,7 @@ Signed-off-by: Jakub Kicinski for (i = 0; i < ARRAY_SIZE(eth->dsa_meta); i++) { struct metadata_dst *md_dst = eth->dsa_meta[i]; -@@ -3218,7 +3219,8 @@ static int mtk_open(struct net_device *d +@@ -3217,7 +3218,8 @@ static int mtk_open(struct net_device *d } } else { /* Hardware special tag parsing needs to be disabled if at least diff --git a/target/linux/generic/backport-6.1/730-13-v6.3-net-ethernet-mtk_eth_soc-enable-special-tag-when-any.patch b/target/linux/generic/backport-6.1/730-13-v6.3-net-ethernet-mtk_eth_soc-enable-special-tag-when-any.patch index 8da728b9e..30c32d5cc 100644 --- a/target/linux/generic/backport-6.1/730-13-v6.3-net-ethernet-mtk_eth_soc-enable-special-tag-when-any.patch +++ b/target/linux/generic/backport-6.1/730-13-v6.3-net-ethernet-mtk_eth_soc-enable-special-tag-when-any.patch @@ -23,7 +23,7 @@ Signed-off-by: David S. Miller --- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c +++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c -@@ -3136,7 +3136,7 @@ static void mtk_gdm_config(struct mtk_et +@@ -3135,7 +3135,7 @@ static void mtk_gdm_config(struct mtk_et val |= config; @@ -32,7 +32,7 @@ Signed-off-by: David S. Miller val |= MTK_GDMA_SPECIAL_TAG; mtk_w32(eth, val, MTK_GDMA_FWD_CFG(i)); -@@ -3201,8 +3201,7 @@ static int mtk_open(struct net_device *d +@@ -3200,8 +3200,7 @@ static int mtk_open(struct net_device *d struct mtk_eth *eth = mac->hw; int i, err; @@ -42,7 +42,7 @@ Signed-off-by: David S. Miller for (i = 0; i < ARRAY_SIZE(eth->dsa_meta); i++) { struct metadata_dst *md_dst = eth->dsa_meta[i]; -@@ -3219,8 +3218,7 @@ static int mtk_open(struct net_device *d +@@ -3218,8 +3217,7 @@ static int mtk_open(struct net_device *d } } else { /* Hardware special tag parsing needs to be disabled if at least diff --git a/target/linux/generic/backport-6.1/730-14-v6.3-net-ethernet-mtk_eth_soc-fix-DSA-TX-tag-hwaccel-for-.patch b/target/linux/generic/backport-6.1/730-14-v6.3-net-ethernet-mtk_eth_soc-fix-DSA-TX-tag-hwaccel-for-.patch index 51cd572ab..eb3b0c63b 100644 --- a/target/linux/generic/backport-6.1/730-14-v6.3-net-ethernet-mtk_eth_soc-fix-DSA-TX-tag-hwaccel-for-.patch +++ b/target/linux/generic/backport-6.1/730-14-v6.3-net-ethernet-mtk_eth_soc-fix-DSA-TX-tag-hwaccel-for-.patch @@ -77,7 +77,7 @@ Signed-off-by: David S. Miller --- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c +++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c -@@ -1877,7 +1877,9 @@ static int mtk_poll_rx(struct napi_struc +@@ -1876,7 +1876,9 @@ static int mtk_poll_rx(struct napi_struc while (done < budget) { unsigned int pktlen, *rxdcsum; @@ -87,7 +87,7 @@ Signed-off-by: David S. Miller dma_addr_t dma_addr; u32 hash, reason; int mac = 0; -@@ -2017,27 +2019,29 @@ static int mtk_poll_rx(struct napi_struc +@@ -2016,27 +2018,29 @@ static int mtk_poll_rx(struct napi_struc if (netdev->features & NETIF_F_HW_VLAN_CTAG_RX) { if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V2)) { diff --git a/target/linux/generic/backport-6.1/733-v6.3-18-net-ethernet-mtk_eth_soc-add-support-for-MT7981.patch b/target/linux/generic/backport-6.1/733-v6.3-18-net-ethernet-mtk_eth_soc-add-support-for-MT7981.patch index a1247218b..3f3f73183 100644 --- a/target/linux/generic/backport-6.1/733-v6.3-18-net-ethernet-mtk_eth_soc-add-support-for-MT7981.patch +++ b/target/linux/generic/backport-6.1/733-v6.3-18-net-ethernet-mtk_eth_soc-add-support-for-MT7981.patch @@ -57,7 +57,7 @@ Signed-off-by: Jakub Kicinski mtk_eth_path_name(path), __func__, updated); --- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c +++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c -@@ -4803,6 +4803,26 @@ static const struct mtk_soc_data mt7629_ +@@ -4802,6 +4802,26 @@ static const struct mtk_soc_data mt7629_ }, }; @@ -84,7 +84,7 @@ Signed-off-by: Jakub Kicinski static const struct mtk_soc_data mt7986_data = { .reg_map = &mt7986_reg_map, .ana_rgc3 = 0x128, -@@ -4845,6 +4865,7 @@ const struct of_device_id of_mtk_match[] +@@ -4844,6 +4864,7 @@ const struct of_device_id of_mtk_match[] { .compatible = "mediatek,mt7622-eth", .data = &mt7622_data}, { .compatible = "mediatek,mt7623-eth", .data = &mt7623_data}, { .compatible = "mediatek,mt7629-eth", .data = &mt7629_data}, diff --git a/target/linux/generic/backport-6.1/733-v6.3-19-net-ethernet-mtk_eth_soc-set-MDIO-bus-clock-frequenc.patch b/target/linux/generic/backport-6.1/733-v6.3-19-net-ethernet-mtk_eth_soc-set-MDIO-bus-clock-frequenc.patch index 1cb1f4053..c60a4337e 100644 --- a/target/linux/generic/backport-6.1/733-v6.3-19-net-ethernet-mtk_eth_soc-set-MDIO-bus-clock-frequenc.patch +++ b/target/linux/generic/backport-6.1/733-v6.3-19-net-ethernet-mtk_eth_soc-set-MDIO-bus-clock-frequenc.patch @@ -21,7 +21,7 @@ Signed-off-by: Jakub Kicinski --- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c +++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c -@@ -744,8 +744,10 @@ static const struct phylink_mac_ops mtk_ +@@ -743,8 +743,10 @@ static const struct phylink_mac_ops mtk_ static int mtk_mdio_init(struct mtk_eth *eth) { @@ -32,7 +32,7 @@ Signed-off-by: Jakub Kicinski mii_np = of_get_child_by_name(eth->dev->of_node, "mdio-bus"); if (!mii_np) { -@@ -772,6 +774,25 @@ static int mtk_mdio_init(struct mtk_eth +@@ -771,6 +773,25 @@ static int mtk_mdio_init(struct mtk_eth eth->mii_bus->parent = eth->dev; snprintf(eth->mii_bus->id, MII_BUS_ID_SIZE, "%pOFn", mii_np); diff --git a/target/linux/generic/backport-6.1/733-v6.3-20-net-ethernet-mtk_eth_soc-switch-to-external-PCS-driv.patch b/target/linux/generic/backport-6.1/733-v6.3-20-net-ethernet-mtk_eth_soc-switch-to-external-PCS-driv.patch index 110944658..d73eccedb 100644 --- a/target/linux/generic/backport-6.1/733-v6.3-20-net-ethernet-mtk_eth_soc-switch-to-external-PCS-driv.patch +++ b/target/linux/generic/backport-6.1/733-v6.3-20-net-ethernet-mtk_eth_soc-switch-to-external-PCS-driv.patch @@ -60,7 +60,7 @@ Signed-off-by: Jakub Kicinski } return NULL; -@@ -4016,8 +4017,17 @@ static int mtk_unreg_dev(struct mtk_eth +@@ -4015,8 +4016,17 @@ static int mtk_unreg_dev(struct mtk_eth return 0; } @@ -78,7 +78,7 @@ Signed-off-by: Jakub Kicinski mtk_unreg_dev(eth); mtk_free_dev(eth); cancel_work_sync(ð->pending_work); -@@ -4457,6 +4467,36 @@ void mtk_eth_set_dma_device(struct mtk_e +@@ -4456,6 +4466,36 @@ void mtk_eth_set_dma_device(struct mtk_e rtnl_unlock(); } @@ -115,7 +115,7 @@ Signed-off-by: Jakub Kicinski static int mtk_probe(struct platform_device *pdev) { struct resource *res = NULL; -@@ -4520,13 +4560,7 @@ static int mtk_probe(struct platform_dev +@@ -4519,13 +4559,7 @@ static int mtk_probe(struct platform_dev } if (MTK_HAS_CAPS(eth->soc->caps, MTK_SGMII)) { @@ -130,7 +130,7 @@ Signed-off-by: Jakub Kicinski if (err) return err; -@@ -4537,14 +4571,17 @@ static int mtk_probe(struct platform_dev +@@ -4536,14 +4570,17 @@ static int mtk_probe(struct platform_dev "mediatek,pctl"); if (IS_ERR(eth->pctl)) { dev_err(&pdev->dev, "no pctl regmap found\n"); @@ -151,7 +151,7 @@ Signed-off-by: Jakub Kicinski } if (eth->soc->offload_version) { -@@ -4703,6 +4740,8 @@ err_deinit_hw: +@@ -4702,6 +4739,8 @@ err_deinit_hw: mtk_hw_deinit(eth); err_wed_exit: mtk_wed_exit(); diff --git a/target/linux/generic/backport-6.1/733-v6.4-23-net-ethernet-mtk_eth_soc-ppe-add-support-for-flow-ac.patch b/target/linux/generic/backport-6.1/733-v6.4-23-net-ethernet-mtk_eth_soc-ppe-add-support-for-flow-ac.patch index 93eaffa19..5c3de5759 100644 --- a/target/linux/generic/backport-6.1/733-v6.4-23-net-ethernet-mtk_eth_soc-ppe-add-support-for-flow-ac.patch +++ b/target/linux/generic/backport-6.1/733-v6.4-23-net-ethernet-mtk_eth_soc-ppe-add-support-for-flow-ac.patch @@ -27,7 +27,7 @@ Signed-off-by: Jakub Kicinski --- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c +++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c -@@ -4691,8 +4691,8 @@ static int mtk_probe(struct platform_dev +@@ -4690,8 +4690,8 @@ static int mtk_probe(struct platform_dev for (i = 0; i < num_ppe; i++) { u32 ppe_addr = eth->soc->reg_map->ppe_base + i * 0x400; @@ -38,7 +38,7 @@ Signed-off-by: Jakub Kicinski if (!eth->ppe[i]) { err = -ENOMEM; goto err_deinit_ppe; -@@ -4816,6 +4816,7 @@ static const struct mtk_soc_data mt7622_ +@@ -4815,6 +4815,7 @@ static const struct mtk_soc_data mt7622_ .required_pctl = false, .offload_version = 2, .hash_offset = 2, @@ -46,7 +46,7 @@ Signed-off-by: Jakub Kicinski .foe_entry_size = sizeof(struct mtk_foe_entry) - 16, .txrx = { .txd_size = sizeof(struct mtk_tx_dma), -@@ -4853,6 +4854,7 @@ static const struct mtk_soc_data mt7629_ +@@ -4852,6 +4853,7 @@ static const struct mtk_soc_data mt7629_ .hw_features = MTK_HW_FEATURES, .required_clks = MT7629_CLKS_BITMAP, .required_pctl = false, @@ -54,7 +54,7 @@ Signed-off-by: Jakub Kicinski .txrx = { .txd_size = sizeof(struct mtk_tx_dma), .rxd_size = sizeof(struct mtk_rx_dma), -@@ -4873,6 +4875,7 @@ static const struct mtk_soc_data mt7981_ +@@ -4872,6 +4874,7 @@ static const struct mtk_soc_data mt7981_ .offload_version = 2, .hash_offset = 4, .foe_entry_size = sizeof(struct mtk_foe_entry), @@ -62,7 +62,7 @@ Signed-off-by: Jakub Kicinski .txrx = { .txd_size = sizeof(struct mtk_tx_dma_v2), .rxd_size = sizeof(struct mtk_rx_dma_v2), -@@ -4893,6 +4896,7 @@ static const struct mtk_soc_data mt7986_ +@@ -4892,6 +4895,7 @@ static const struct mtk_soc_data mt7986_ .offload_version = 2, .hash_offset = 4, .foe_entry_size = sizeof(struct mtk_foe_entry), diff --git a/target/linux/generic/backport-6.1/733-v6.4-25-net-ethernet-mtk_eth_soc-drop-generic-vlan-rx-offloa.patch b/target/linux/generic/backport-6.1/733-v6.4-25-net-ethernet-mtk_eth_soc-drop-generic-vlan-rx-offloa.patch index 217e517c3..7e3d5a330 100644 --- a/target/linux/generic/backport-6.1/733-v6.4-25-net-ethernet-mtk_eth_soc-drop-generic-vlan-rx-offloa.patch +++ b/target/linux/generic/backport-6.1/733-v6.4-25-net-ethernet-mtk_eth_soc-drop-generic-vlan-rx-offloa.patch @@ -17,7 +17,7 @@ Signed-off-by: Felix Fietkau --- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c +++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c -@@ -1897,9 +1897,7 @@ static int mtk_poll_rx(struct napi_struc +@@ -1896,9 +1896,7 @@ static int mtk_poll_rx(struct napi_struc while (done < budget) { unsigned int pktlen, *rxdcsum; @@ -27,7 +27,7 @@ Signed-off-by: Felix Fietkau dma_addr_t dma_addr; u32 hash, reason; int mac = 0; -@@ -2034,36 +2032,21 @@ static int mtk_poll_rx(struct napi_struc +@@ -2033,36 +2031,21 @@ static int mtk_poll_rx(struct napi_struc skb_checksum_none_assert(skb); skb->protocol = eth_type_trans(skb, netdev); @@ -70,7 +70,7 @@ Signed-off-by: Felix Fietkau skb_record_rx_queue(skb, 0); napi_gro_receive(napi, skb); -@@ -2889,29 +2872,11 @@ static netdev_features_t mtk_fix_feature +@@ -2888,29 +2871,11 @@ static netdev_features_t mtk_fix_feature static int mtk_set_features(struct net_device *dev, netdev_features_t features) { @@ -100,7 +100,7 @@ Signed-off-by: Felix Fietkau return 0; } -@@ -3225,30 +3190,6 @@ static int mtk_open(struct net_device *d +@@ -3224,30 +3189,6 @@ static int mtk_open(struct net_device *d struct mtk_eth *eth = mac->hw; int i, err; @@ -131,7 +131,7 @@ Signed-off-by: Felix Fietkau err = phylink_of_phy_connect(mac->phylink, mac->of_node, 0); if (err) { netdev_err(dev, "%s: could not attach PHY: %d\n", __func__, -@@ -3287,6 +3228,35 @@ static int mtk_open(struct net_device *d +@@ -3286,6 +3227,35 @@ static int mtk_open(struct net_device *d phylink_start(mac->phylink); netif_tx_start_all_queues(dev); @@ -167,7 +167,7 @@ Signed-off-by: Felix Fietkau return 0; } -@@ -3771,10 +3741,9 @@ static int mtk_hw_init(struct mtk_eth *e +@@ -3770,10 +3740,9 @@ static int mtk_hw_init(struct mtk_eth *e if (!MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V2)) { val = mtk_r32(eth, MTK_CDMP_IG_CTRL); mtk_w32(eth, val | MTK_CDMP_STAG_EN, MTK_CDMP_IG_CTRL); @@ -180,7 +180,7 @@ Signed-off-by: Felix Fietkau /* set interrupt delays based on current Net DIM sample */ mtk_dim_rx(ð->rx_dim.work); -@@ -4414,7 +4383,7 @@ static int mtk_add_mac(struct mtk_eth *e +@@ -4413,7 +4382,7 @@ static int mtk_add_mac(struct mtk_eth *e eth->netdev[id]->hw_features |= NETIF_F_LRO; eth->netdev[id]->vlan_features = eth->soc->hw_features & diff --git a/target/linux/generic/backport-6.1/734-v6.8-net-phy-bcm54612e-add-suspend-resume.patch b/target/linux/generic/backport-6.1/734-v6.8-net-phy-bcm54612e-add-suspend-resume.patch new file mode 100644 index 000000000..64d1b160d --- /dev/null +++ b/target/linux/generic/backport-6.1/734-v6.8-net-phy-bcm54612e-add-suspend-resume.patch @@ -0,0 +1,27 @@ +From 380b50ae3a04222334a3779b3787eba844b1177f Mon Sep 17 00:00:00 2001 +From: Marco von Rosenberg +Date: Thu, 16 Nov 2023 20:32:31 +0100 +Subject: net: phy: broadcom: Wire suspend/resume for BCM54612E + +The BCM54612E ethernet PHY supports IDDQ-SR. +Therefore wire-up the suspend and resume callbacks +to point to bcm54xx_suspend() and bcm54xx_resume(). + +Signed-off-by: Marco von Rosenberg +Acked-by: Florian Fainelli +Signed-off-by: David S. Miller +--- + drivers/net/phy/broadcom.c | 2 ++ + 1 file changed, 2 insertions(+) + +--- a/drivers/net/phy/broadcom.c ++++ b/drivers/net/phy/broadcom.c +@@ -942,6 +942,8 @@ static struct phy_driver broadcom_driver + .config_intr = bcm_phy_config_intr, + .handle_interrupt = bcm_phy_handle_interrupt, + .link_change_notify = bcm54xx_link_change_notify, ++ .suspend = bcm54xx_suspend, ++ .resume = bcm54xx_resume, + }, { + .phy_id = PHY_ID_BCM54616S, + .phy_id_mask = 0xfffffff0, diff --git a/target/linux/generic/backport-6.1/750-v6.5-01-net-ethernet-mtk_ppe-add-MTK_FOE_ENTRY_V-1-2-_SIZE-m.patch b/target/linux/generic/backport-6.1/750-v6.5-01-net-ethernet-mtk_ppe-add-MTK_FOE_ENTRY_V-1-2-_SIZE-m.patch index d7d1c08fc..afce0cf04 100644 --- a/target/linux/generic/backport-6.1/750-v6.5-01-net-ethernet-mtk_ppe-add-MTK_FOE_ENTRY_V-1-2-_SIZE-m.patch +++ b/target/linux/generic/backport-6.1/750-v6.5-01-net-ethernet-mtk_ppe-add-MTK_FOE_ENTRY_V-1-2-_SIZE-m.patch @@ -17,7 +17,7 @@ Signed-off-by: David S. Miller --- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c +++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c -@@ -4765,7 +4765,7 @@ static const struct mtk_soc_data mt7621_ +@@ -4764,7 +4764,7 @@ static const struct mtk_soc_data mt7621_ .required_pctl = false, .offload_version = 1, .hash_offset = 2, @@ -26,7 +26,7 @@ Signed-off-by: David S. Miller .txrx = { .txd_size = sizeof(struct mtk_tx_dma), .rxd_size = sizeof(struct mtk_rx_dma), -@@ -4786,7 +4786,7 @@ static const struct mtk_soc_data mt7622_ +@@ -4785,7 +4785,7 @@ static const struct mtk_soc_data mt7622_ .offload_version = 2, .hash_offset = 2, .has_accounting = true, @@ -35,7 +35,7 @@ Signed-off-by: David S. Miller .txrx = { .txd_size = sizeof(struct mtk_tx_dma), .rxd_size = sizeof(struct mtk_rx_dma), -@@ -4805,7 +4805,7 @@ static const struct mtk_soc_data mt7623_ +@@ -4804,7 +4804,7 @@ static const struct mtk_soc_data mt7623_ .required_pctl = true, .offload_version = 1, .hash_offset = 2, @@ -44,7 +44,7 @@ Signed-off-by: David S. Miller .txrx = { .txd_size = sizeof(struct mtk_tx_dma), .rxd_size = sizeof(struct mtk_rx_dma), -@@ -4843,8 +4843,8 @@ static const struct mtk_soc_data mt7981_ +@@ -4842,8 +4842,8 @@ static const struct mtk_soc_data mt7981_ .required_pctl = false, .offload_version = 2, .hash_offset = 4, @@ -54,7 +54,7 @@ Signed-off-by: David S. Miller .txrx = { .txd_size = sizeof(struct mtk_tx_dma_v2), .rxd_size = sizeof(struct mtk_rx_dma_v2), -@@ -4864,8 +4864,8 @@ static const struct mtk_soc_data mt7986_ +@@ -4863,8 +4863,8 @@ static const struct mtk_soc_data mt7986_ .required_pctl = false, .offload_version = 2, .hash_offset = 4, diff --git a/target/linux/generic/backport-6.1/750-v6.5-02-net-ethernet-mtk_eth_soc-remove-incorrect-PLL-config.patch b/target/linux/generic/backport-6.1/750-v6.5-02-net-ethernet-mtk_eth_soc-remove-incorrect-PLL-config.patch index fb54f404b..28bf2b6d5 100644 --- a/target/linux/generic/backport-6.1/750-v6.5-02-net-ethernet-mtk_eth_soc-remove-incorrect-PLL-config.patch +++ b/target/linux/generic/backport-6.1/750-v6.5-02-net-ethernet-mtk_eth_soc-remove-incorrect-PLL-config.patch @@ -95,7 +95,7 @@ Signed-off-by: Paolo Abeni /* mt7623_pad_clk_setup */ for (i = 0 ; i < NUM_TRGMII_CTRL; i++) -@@ -4342,13 +4314,19 @@ static int mtk_add_mac(struct mtk_eth *e +@@ -4341,13 +4313,19 @@ static int mtk_add_mac(struct mtk_eth *e mac->phylink_config.mac_capabilities = MAC_ASYM_PAUSE | MAC_SYM_PAUSE | MAC_10 | MAC_100 | MAC_1000 | MAC_2500FD; @@ -121,7 +121,7 @@ Signed-off-by: Paolo Abeni if (MTK_HAS_CAPS(mac->hw->soc->caps, MTK_TRGMII) && !mac->id) __set_bit(PHY_INTERFACE_MODE_TRGMII, -@@ -4806,6 +4784,7 @@ static const struct mtk_soc_data mt7623_ +@@ -4805,6 +4783,7 @@ static const struct mtk_soc_data mt7623_ .offload_version = 1, .hash_offset = 2, .foe_entry_size = MTK_FOE_ENTRY_V1_SIZE, diff --git a/target/linux/generic/backport-6.1/750-v6.5-03-net-ethernet-mtk_eth_soc-remove-mac_pcs_get_state-an.patch b/target/linux/generic/backport-6.1/750-v6.5-03-net-ethernet-mtk_eth_soc-remove-mac_pcs_get_state-an.patch index 500fad3b8..fd458a67b 100644 --- a/target/linux/generic/backport-6.1/750-v6.5-03-net-ethernet-mtk_eth_soc-remove-mac_pcs_get_state-an.patch +++ b/target/linux/generic/backport-6.1/750-v6.5-03-net-ethernet-mtk_eth_soc-remove-mac_pcs_get_state-an.patch @@ -62,15 +62,15 @@ Signed-off-by: Paolo Abeni static void mtk_mac_link_down(struct phylink_config *config, unsigned int mode, phy_interface_t interface) { -@@ -708,7 +676,6 @@ static void mtk_mac_link_up(struct phyli +@@ -707,7 +675,6 @@ static void mtk_mac_link_up(struct phyli + static const struct phylink_mac_ops mtk_phylink_ops = { - .validate = phylink_generic_validate, .mac_select_pcs = mtk_mac_select_pcs, - .mac_pcs_get_state = mtk_mac_pcs_get_state, .mac_config = mtk_mac_config, .mac_finish = mtk_mac_finish, .mac_link_down = mtk_mac_link_down, -@@ -4309,8 +4276,6 @@ static int mtk_add_mac(struct mtk_eth *e +@@ -4308,8 +4275,6 @@ static int mtk_add_mac(struct mtk_eth *e mac->phylink_config.dev = ð->netdev[id]->dev; mac->phylink_config.type = PHYLINK_NETDEV; diff --git a/target/linux/generic/backport-6.1/750-v6.5-05-net-ethernet-mtk_eth_soc-add-version-in-mtk_soc_data.patch b/target/linux/generic/backport-6.1/750-v6.5-05-net-ethernet-mtk_eth_soc-add-version-in-mtk_soc_data.patch index 3b1225e94..aa259e717 100644 --- a/target/linux/generic/backport-6.1/750-v6.5-05-net-ethernet-mtk_eth_soc-add-version-in-mtk_soc_data.patch +++ b/target/linux/generic/backport-6.1/750-v6.5-05-net-ethernet-mtk_eth_soc-add-version-in-mtk_soc_data.patch @@ -32,7 +32,7 @@ Signed-off-by: Jakub Kicinski val |= MTK_QTX_SCH_LEAKY_BUCKET_EN; if (IS_ENABLED(CONFIG_SOC_MT7621)) { -@@ -955,7 +955,7 @@ static bool mtk_rx_get_desc(struct mtk_e +@@ -954,7 +954,7 @@ static bool mtk_rx_get_desc(struct mtk_e rxd->rxd1 = READ_ONCE(dma_rxd->rxd1); rxd->rxd3 = READ_ONCE(dma_rxd->rxd3); rxd->rxd4 = READ_ONCE(dma_rxd->rxd4); @@ -41,7 +41,7 @@ Signed-off-by: Jakub Kicinski rxd->rxd5 = READ_ONCE(dma_rxd->rxd5); rxd->rxd6 = READ_ONCE(dma_rxd->rxd6); } -@@ -1013,7 +1013,7 @@ static int mtk_init_fq_dma(struct mtk_et +@@ -1012,7 +1012,7 @@ static int mtk_init_fq_dma(struct mtk_et txd->txd3 = TX_DMA_PLEN0(MTK_QDMA_PAGE_SIZE); txd->txd4 = 0; @@ -50,7 +50,7 @@ Signed-off-by: Jakub Kicinski txd->txd5 = 0; txd->txd6 = 0; txd->txd7 = 0; -@@ -1204,7 +1204,7 @@ static void mtk_tx_set_dma_desc(struct n +@@ -1203,7 +1203,7 @@ static void mtk_tx_set_dma_desc(struct n struct mtk_mac *mac = netdev_priv(dev); struct mtk_eth *eth = mac->hw; @@ -59,7 +59,7 @@ Signed-off-by: Jakub Kicinski mtk_tx_set_dma_desc_v2(dev, txd, info); else mtk_tx_set_dma_desc_v1(dev, txd, info); -@@ -1511,7 +1511,7 @@ static void mtk_update_rx_cpu_idx(struct +@@ -1510,7 +1510,7 @@ static void mtk_update_rx_cpu_idx(struct static bool mtk_page_pool_enabled(struct mtk_eth *eth) { @@ -68,7 +68,7 @@ Signed-off-by: Jakub Kicinski } static struct page_pool *mtk_create_page_pool(struct mtk_eth *eth, -@@ -1853,7 +1853,7 @@ static int mtk_poll_rx(struct napi_struc +@@ -1852,7 +1852,7 @@ static int mtk_poll_rx(struct napi_struc break; /* find out which mac the packet come from. values start at 1 */ @@ -77,7 +77,7 @@ Signed-off-by: Jakub Kicinski mac = RX_DMA_GET_SPORT_V2(trxd.rxd5) - 1; else if (!MTK_HAS_CAPS(eth->soc->caps, MTK_SOC_MT7628) && !(trxd.rxd4 & RX_DMA_SPECIAL_TAG)) -@@ -1949,7 +1949,7 @@ static int mtk_poll_rx(struct napi_struc +@@ -1948,7 +1948,7 @@ static int mtk_poll_rx(struct napi_struc skb->dev = netdev; bytes += skb->len; @@ -86,7 +86,7 @@ Signed-off-by: Jakub Kicinski reason = FIELD_GET(MTK_RXD5_PPE_CPU_REASON, trxd.rxd5); hash = trxd.rxd5 & MTK_RXD5_FOE_ENTRY; if (hash != MTK_RXD5_FOE_ENTRY) -@@ -1974,8 +1974,8 @@ static int mtk_poll_rx(struct napi_struc +@@ -1973,8 +1973,8 @@ static int mtk_poll_rx(struct napi_struc /* When using VLAN untagging in combination with DSA, the * hardware treats the MTK special tag as a VLAN and untags it. */ @@ -97,7 +97,7 @@ Signed-off-by: Jakub Kicinski unsigned int port = RX_DMA_VPID(trxd.rxd3) & GENMASK(2, 0); if (port < ARRAY_SIZE(eth->dsa_meta) && -@@ -2285,7 +2285,7 @@ static int mtk_tx_alloc(struct mtk_eth * +@@ -2284,7 +2284,7 @@ static int mtk_tx_alloc(struct mtk_eth * txd->txd2 = next_ptr; txd->txd3 = TX_DMA_LS0 | TX_DMA_OWNER_CPU; txd->txd4 = 0; @@ -106,7 +106,7 @@ Signed-off-by: Jakub Kicinski txd->txd5 = 0; txd->txd6 = 0; txd->txd7 = 0; -@@ -2338,14 +2338,14 @@ static int mtk_tx_alloc(struct mtk_eth * +@@ -2337,14 +2337,14 @@ static int mtk_tx_alloc(struct mtk_eth * FIELD_PREP(MTK_QTX_SCH_MIN_RATE_MAN, 1) | FIELD_PREP(MTK_QTX_SCH_MIN_RATE_EXP, 4) | MTK_QTX_SCH_LEAKY_BUCKET_SIZE; @@ -123,7 +123,7 @@ Signed-off-by: Jakub Kicinski mtk_w32(eth, val, soc->reg_map->qdma.tx_sch_rate + 4); } else { mtk_w32(eth, ring->phys_pdma, MT7628_TX_BASE_PTR0); -@@ -2474,7 +2474,7 @@ static int mtk_rx_alloc(struct mtk_eth * +@@ -2473,7 +2473,7 @@ static int mtk_rx_alloc(struct mtk_eth * rxd->rxd3 = 0; rxd->rxd4 = 0; @@ -132,7 +132,7 @@ Signed-off-by: Jakub Kicinski rxd->rxd5 = 0; rxd->rxd6 = 0; rxd->rxd7 = 0; -@@ -3025,7 +3025,7 @@ static int mtk_start_dma(struct mtk_eth +@@ -3024,7 +3024,7 @@ static int mtk_start_dma(struct mtk_eth MTK_TX_BT_32DWORDS | MTK_NDP_CO_PRO | MTK_RX_2B_OFFSET | MTK_TX_WB_DDONE; @@ -141,7 +141,7 @@ Signed-off-by: Jakub Kicinski val |= MTK_MUTLI_CNT | MTK_RESV_BUF | MTK_WCOMP_EN | MTK_DMAD_WR_WDONE | MTK_CHK_DDONE_EN | MTK_LEAKY_BUCKET_EN; -@@ -3167,7 +3167,7 @@ static int mtk_open(struct net_device *d +@@ -3166,7 +3166,7 @@ static int mtk_open(struct net_device *d phylink_start(mac->phylink); netif_tx_start_all_queues(dev); @@ -150,7 +150,7 @@ Signed-off-by: Jakub Kicinski return 0; if (mtk_uses_dsa(dev) && !eth->prog) { -@@ -3432,7 +3432,7 @@ static void mtk_hw_reset(struct mtk_eth +@@ -3431,7 +3431,7 @@ static void mtk_hw_reset(struct mtk_eth { u32 val; @@ -159,7 +159,7 @@ Signed-off-by: Jakub Kicinski regmap_write(eth->ethsys, ETHSYS_FE_RST_CHK_IDLE_EN, 0); val = RSTCTRL_PPE0_V2; } else { -@@ -3444,7 +3444,7 @@ static void mtk_hw_reset(struct mtk_eth +@@ -3443,7 +3443,7 @@ static void mtk_hw_reset(struct mtk_eth ethsys_reset(eth, RSTCTRL_ETH | RSTCTRL_FE | val); @@ -168,7 +168,7 @@ Signed-off-by: Jakub Kicinski regmap_write(eth->ethsys, ETHSYS_FE_RST_CHK_IDLE_EN, 0x3ffffff); } -@@ -3470,7 +3470,7 @@ static void mtk_hw_warm_reset(struct mtk +@@ -3469,7 +3469,7 @@ static void mtk_hw_warm_reset(struct mtk return; } @@ -177,7 +177,7 @@ Signed-off-by: Jakub Kicinski rst_mask = RSTCTRL_ETH | RSTCTRL_PPE0_V2; else rst_mask = RSTCTRL_ETH | RSTCTRL_PPE0; -@@ -3640,7 +3640,7 @@ static int mtk_hw_init(struct mtk_eth *e +@@ -3639,7 +3639,7 @@ static int mtk_hw_init(struct mtk_eth *e else mtk_hw_reset(eth); @@ -186,7 +186,7 @@ Signed-off-by: Jakub Kicinski /* Set FE to PDMAv2 if necessary */ val = mtk_r32(eth, MTK_FE_GLO_MISC); mtk_w32(eth, val | BIT(4), MTK_FE_GLO_MISC); -@@ -3677,7 +3677,7 @@ static int mtk_hw_init(struct mtk_eth *e +@@ -3676,7 +3676,7 @@ static int mtk_hw_init(struct mtk_eth *e */ val = mtk_r32(eth, MTK_CDMQ_IG_CTRL); mtk_w32(eth, val | MTK_CDMQ_STAG_EN, MTK_CDMQ_IG_CTRL); @@ -195,7 +195,7 @@ Signed-off-by: Jakub Kicinski val = mtk_r32(eth, MTK_CDMP_IG_CTRL); mtk_w32(eth, val | MTK_CDMP_STAG_EN, MTK_CDMP_IG_CTRL); -@@ -3699,7 +3699,7 @@ static int mtk_hw_init(struct mtk_eth *e +@@ -3698,7 +3698,7 @@ static int mtk_hw_init(struct mtk_eth *e mtk_w32(eth, eth->soc->txrx.rx_irq_done_mask, reg_map->qdma.int_grp + 4); mtk_w32(eth, 0x21021000, MTK_FE_INT_GRP); @@ -204,7 +204,7 @@ Signed-off-by: Jakub Kicinski /* PSE should not drop port8 and port9 packets from WDMA Tx */ mtk_w32(eth, 0x00000300, PSE_DROP_CFG); -@@ -4488,7 +4488,7 @@ static int mtk_probe(struct platform_dev +@@ -4487,7 +4487,7 @@ static int mtk_probe(struct platform_dev } } @@ -213,7 +213,7 @@ Signed-off-by: Jakub Kicinski res = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!res) { err = -EINVAL; -@@ -4596,9 +4596,8 @@ static int mtk_probe(struct platform_dev +@@ -4595,9 +4595,8 @@ static int mtk_probe(struct platform_dev } if (eth->soc->offload_version) { @@ -224,7 +224,7 @@ Signed-off-by: Jakub Kicinski num_ppe = min_t(u32, ARRAY_SIZE(eth->ppe), num_ppe); for (i = 0; i < num_ppe; i++) { u32 ppe_addr = eth->soc->reg_map->ppe_base + i * 0x400; -@@ -4690,6 +4689,7 @@ static const struct mtk_soc_data mt2701_ +@@ -4689,6 +4688,7 @@ static const struct mtk_soc_data mt2701_ .hw_features = MTK_HW_FEATURES, .required_clks = MT7623_CLKS_BITMAP, .required_pctl = true, @@ -232,7 +232,7 @@ Signed-off-by: Jakub Kicinski .txrx = { .txd_size = sizeof(struct mtk_tx_dma), .rxd_size = sizeof(struct mtk_rx_dma), -@@ -4706,6 +4706,7 @@ static const struct mtk_soc_data mt7621_ +@@ -4705,6 +4705,7 @@ static const struct mtk_soc_data mt7621_ .hw_features = MTK_HW_FEATURES, .required_clks = MT7621_CLKS_BITMAP, .required_pctl = false, @@ -240,7 +240,7 @@ Signed-off-by: Jakub Kicinski .offload_version = 1, .hash_offset = 2, .foe_entry_size = MTK_FOE_ENTRY_V1_SIZE, -@@ -4726,6 +4727,7 @@ static const struct mtk_soc_data mt7622_ +@@ -4725,6 +4726,7 @@ static const struct mtk_soc_data mt7622_ .hw_features = MTK_HW_FEATURES, .required_clks = MT7622_CLKS_BITMAP, .required_pctl = false, @@ -248,7 +248,7 @@ Signed-off-by: Jakub Kicinski .offload_version = 2, .hash_offset = 2, .has_accounting = true, -@@ -4746,6 +4748,7 @@ static const struct mtk_soc_data mt7623_ +@@ -4745,6 +4747,7 @@ static const struct mtk_soc_data mt7623_ .hw_features = MTK_HW_FEATURES, .required_clks = MT7623_CLKS_BITMAP, .required_pctl = true, @@ -256,7 +256,7 @@ Signed-off-by: Jakub Kicinski .offload_version = 1, .hash_offset = 2, .foe_entry_size = MTK_FOE_ENTRY_V1_SIZE, -@@ -4768,6 +4771,7 @@ static const struct mtk_soc_data mt7629_ +@@ -4767,6 +4770,7 @@ static const struct mtk_soc_data mt7629_ .required_clks = MT7629_CLKS_BITMAP, .required_pctl = false, .has_accounting = true, @@ -264,7 +264,7 @@ Signed-off-by: Jakub Kicinski .txrx = { .txd_size = sizeof(struct mtk_tx_dma), .rxd_size = sizeof(struct mtk_rx_dma), -@@ -4785,6 +4789,7 @@ static const struct mtk_soc_data mt7981_ +@@ -4784,6 +4788,7 @@ static const struct mtk_soc_data mt7981_ .hw_features = MTK_HW_FEATURES, .required_clks = MT7981_CLKS_BITMAP, .required_pctl = false, @@ -272,7 +272,7 @@ Signed-off-by: Jakub Kicinski .offload_version = 2, .hash_offset = 4, .has_accounting = true, -@@ -4806,6 +4811,7 @@ static const struct mtk_soc_data mt7986_ +@@ -4805,6 +4810,7 @@ static const struct mtk_soc_data mt7986_ .hw_features = MTK_HW_FEATURES, .required_clks = MT7986_CLKS_BITMAP, .required_pctl = false, @@ -280,7 +280,7 @@ Signed-off-by: Jakub Kicinski .offload_version = 2, .hash_offset = 4, .has_accounting = true, -@@ -4826,6 +4832,7 @@ static const struct mtk_soc_data rt5350_ +@@ -4825,6 +4831,7 @@ static const struct mtk_soc_data rt5350_ .hw_features = MTK_HW_FEATURES_MT7628, .required_clks = MT7628_CLKS_BITMAP, .required_pctl = false, diff --git a/target/linux/generic/backport-6.1/750-v6.5-07-net-ethernet-mtk_eth_soc-rely-on-MTK_MAX_DEVS-and-re.patch b/target/linux/generic/backport-6.1/750-v6.5-07-net-ethernet-mtk_eth_soc-rely-on-MTK_MAX_DEVS-and-re.patch index 807165831..3db23f789 100644 --- a/target/linux/generic/backport-6.1/750-v6.5-07-net-ethernet-mtk_eth_soc-rely-on-MTK_MAX_DEVS-and-re.patch +++ b/target/linux/generic/backport-6.1/750-v6.5-07-net-ethernet-mtk_eth_soc-rely-on-MTK_MAX_DEVS-and-re.patch @@ -17,7 +17,7 @@ Signed-off-by: Jakub Kicinski --- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c +++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c -@@ -881,7 +881,7 @@ static void mtk_stats_update(struct mtk_ +@@ -880,7 +880,7 @@ static void mtk_stats_update(struct mtk_ { int i; @@ -26,7 +26,7 @@ Signed-off-by: Jakub Kicinski if (!eth->mac[i] || !eth->mac[i]->hw_stats) continue; if (spin_trylock(ð->mac[i]->hw_stats->stats_lock)) { -@@ -1386,7 +1386,7 @@ static int mtk_queue_stopped(struct mtk_ +@@ -1385,7 +1385,7 @@ static int mtk_queue_stopped(struct mtk_ { int i; @@ -35,7 +35,7 @@ Signed-off-by: Jakub Kicinski if (!eth->netdev[i]) continue; if (netif_queue_stopped(eth->netdev[i])) -@@ -1400,7 +1400,7 @@ static void mtk_wake_queue(struct mtk_et +@@ -1399,7 +1399,7 @@ static void mtk_wake_queue(struct mtk_et { int i; @@ -44,7 +44,7 @@ Signed-off-by: Jakub Kicinski if (!eth->netdev[i]) continue; netif_tx_wake_all_queues(eth->netdev[i]); -@@ -1859,7 +1859,7 @@ static int mtk_poll_rx(struct napi_struc +@@ -1858,7 +1858,7 @@ static int mtk_poll_rx(struct napi_struc !(trxd.rxd4 & RX_DMA_SPECIAL_TAG)) mac = RX_DMA_GET_SPORT(trxd.rxd4) - 1; @@ -53,7 +53,7 @@ Signed-off-by: Jakub Kicinski !eth->netdev[mac])) goto release_desc; -@@ -2899,7 +2899,7 @@ static void mtk_dma_free(struct mtk_eth +@@ -2898,7 +2898,7 @@ static void mtk_dma_free(struct mtk_eth const struct mtk_soc_data *soc = eth->soc; int i; @@ -62,7 +62,7 @@ Signed-off-by: Jakub Kicinski if (eth->netdev[i]) netdev_reset_queue(eth->netdev[i]); if (eth->scratch_ring) { -@@ -3053,8 +3053,13 @@ static void mtk_gdm_config(struct mtk_et +@@ -3052,8 +3052,13 @@ static void mtk_gdm_config(struct mtk_et if (MTK_HAS_CAPS(eth->soc->caps, MTK_SOC_MT7628)) return; @@ -78,7 +78,7 @@ Signed-off-by: Jakub Kicinski /* default setup the forward port to send frame to PDMA */ val &= ~0xffff; -@@ -3064,7 +3069,7 @@ static void mtk_gdm_config(struct mtk_et +@@ -3063,7 +3068,7 @@ static void mtk_gdm_config(struct mtk_et val |= config; @@ -87,7 +87,7 @@ Signed-off-by: Jakub Kicinski val |= MTK_GDMA_SPECIAL_TAG; mtk_w32(eth, val, MTK_GDMA_FWD_CFG(i)); -@@ -3661,15 +3666,15 @@ static int mtk_hw_init(struct mtk_eth *e +@@ -3660,15 +3665,15 @@ static int mtk_hw_init(struct mtk_eth *e * up with the more appropriate value when mtk_mac_config call is being * invoked. */ @@ -109,7 +109,7 @@ Signed-off-by: Jakub Kicinski } /* Indicates CDM to parse the MTK special tag from CPU -@@ -3849,7 +3854,7 @@ static void mtk_pending_work(struct work +@@ -3848,7 +3853,7 @@ static void mtk_pending_work(struct work mtk_prepare_for_reset(eth); /* stop all devices to make sure that dma is properly shut down */ @@ -118,7 +118,7 @@ Signed-off-by: Jakub Kicinski if (!eth->netdev[i] || !netif_running(eth->netdev[i])) continue; -@@ -3865,8 +3870,8 @@ static void mtk_pending_work(struct work +@@ -3864,8 +3869,8 @@ static void mtk_pending_work(struct work mtk_hw_init(eth, true); /* restart DMA and enable IRQs */ @@ -129,7 +129,7 @@ Signed-off-by: Jakub Kicinski continue; if (mtk_open(eth->netdev[i])) { -@@ -3893,7 +3898,7 @@ static int mtk_free_dev(struct mtk_eth * +@@ -3892,7 +3897,7 @@ static int mtk_free_dev(struct mtk_eth * { int i; @@ -138,7 +138,7 @@ Signed-off-by: Jakub Kicinski if (!eth->netdev[i]) continue; free_netdev(eth->netdev[i]); -@@ -3912,7 +3917,7 @@ static int mtk_unreg_dev(struct mtk_eth +@@ -3911,7 +3916,7 @@ static int mtk_unreg_dev(struct mtk_eth { int i; @@ -147,7 +147,7 @@ Signed-off-by: Jakub Kicinski struct mtk_mac *mac; if (!eth->netdev[i]) continue; -@@ -4213,7 +4218,7 @@ static int mtk_add_mac(struct mtk_eth *e +@@ -4212,7 +4217,7 @@ static int mtk_add_mac(struct mtk_eth *e } id = be32_to_cpup(_id); @@ -156,7 +156,7 @@ Signed-off-by: Jakub Kicinski dev_err(eth->dev, "%d is not a valid mac id\n", id); return -EINVAL; } -@@ -4358,7 +4363,7 @@ void mtk_eth_set_dma_device(struct mtk_e +@@ -4357,7 +4362,7 @@ void mtk_eth_set_dma_device(struct mtk_e rtnl_lock(); @@ -165,7 +165,7 @@ Signed-off-by: Jakub Kicinski dev = eth->netdev[i]; if (!dev || !(dev->flags & IFF_UP)) -@@ -4664,7 +4669,7 @@ static int mtk_remove(struct platform_de +@@ -4663,7 +4668,7 @@ static int mtk_remove(struct platform_de int i; /* stop all devices to make sure that dma is properly shut down */ diff --git a/target/linux/generic/backport-6.1/750-v6.5-08-net-ethernet-mtk_eth_soc-add-NETSYS_V3-version-suppo.patch b/target/linux/generic/backport-6.1/750-v6.5-08-net-ethernet-mtk_eth_soc-add-NETSYS_V3-version-suppo.patch index 1a9b31f52..e40dc3d0a 100644 --- a/target/linux/generic/backport-6.1/750-v6.5-08-net-ethernet-mtk_eth_soc-add-NETSYS_V3-version-suppo.patch +++ b/target/linux/generic/backport-6.1/750-v6.5-08-net-ethernet-mtk_eth_soc-add-NETSYS_V3-version-suppo.patch @@ -18,7 +18,7 @@ Signed-off-by: Jakub Kicinski --- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c +++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c -@@ -861,17 +861,32 @@ void mtk_stats_update_mac(struct mtk_mac +@@ -860,17 +860,32 @@ void mtk_stats_update_mac(struct mtk_mac mtk_r32(mac->hw, reg_map->gdm1_cnt + 0x20 + offs); hw_stats->rx_flow_control_packets += mtk_r32(mac->hw, reg_map->gdm1_cnt + 0x24 + offs); @@ -62,7 +62,7 @@ Signed-off-by: Jakub Kicinski } u64_stats_update_end(&hw_stats->syncp); -@@ -1175,7 +1190,10 @@ static void mtk_tx_set_dma_desc_v2(struc +@@ -1174,7 +1189,10 @@ static void mtk_tx_set_dma_desc_v2(struc data |= TX_DMA_LS0; WRITE_ONCE(desc->txd3, data); @@ -74,7 +74,7 @@ Signed-off-by: Jakub Kicinski data |= TX_DMA_SWC_V2 | QID_BITS_V2(info->qid); WRITE_ONCE(desc->txd4, data); -@@ -1186,6 +1204,8 @@ static void mtk_tx_set_dma_desc_v2(struc +@@ -1185,6 +1203,8 @@ static void mtk_tx_set_dma_desc_v2(struc /* tx checksum offload */ if (info->csum) data |= TX_DMA_CHKSUM_V2; @@ -83,7 +83,7 @@ Signed-off-by: Jakub Kicinski } WRITE_ONCE(desc->txd5, data); -@@ -1251,8 +1271,7 @@ static int mtk_tx_map(struct sk_buff *sk +@@ -1250,8 +1270,7 @@ static int mtk_tx_map(struct sk_buff *sk mtk_tx_set_dma_desc(dev, itxd, &txd_info); itx_buf->flags |= MTK_TX_FLAGS_SINGLE0; @@ -93,7 +93,7 @@ Signed-off-by: Jakub Kicinski setup_tx_buf(eth, itx_buf, itxd_pdma, txd_info.addr, txd_info.size, k++); -@@ -1300,8 +1319,7 @@ static int mtk_tx_map(struct sk_buff *sk +@@ -1299,8 +1318,7 @@ static int mtk_tx_map(struct sk_buff *sk memset(tx_buf, 0, sizeof(*tx_buf)); tx_buf->data = (void *)MTK_DMA_DUMMY_DESC; tx_buf->flags |= MTK_TX_FLAGS_PAGE0; @@ -103,7 +103,7 @@ Signed-off-by: Jakub Kicinski setup_tx_buf(eth, tx_buf, txd_pdma, txd_info.addr, txd_info.size, k++); -@@ -1603,7 +1621,7 @@ static int mtk_xdp_frame_map(struct mtk_ +@@ -1602,7 +1620,7 @@ static int mtk_xdp_frame_map(struct mtk_ } mtk_tx_set_dma_desc(dev, txd, txd_info); @@ -112,7 +112,7 @@ Signed-off-by: Jakub Kicinski tx_buf->type = dma_map ? MTK_TYPE_XDP_NDO : MTK_TYPE_XDP_TX; tx_buf->data = (void *)MTK_DMA_DUMMY_DESC; -@@ -1853,11 +1871,24 @@ static int mtk_poll_rx(struct napi_struc +@@ -1852,11 +1870,24 @@ static int mtk_poll_rx(struct napi_struc break; /* find out which mac the packet come from. values start at 1 */ @@ -141,7 +141,7 @@ Signed-off-by: Jakub Kicinski if (unlikely(mac < 0 || mac >= MTK_MAX_DEVS || !eth->netdev[mac])) -@@ -2079,7 +2110,6 @@ static int mtk_poll_tx_qdma(struct mtk_e +@@ -2078,7 +2109,6 @@ static int mtk_poll_tx_qdma(struct mtk_e while ((cpu != dma) && budget) { u32 next_cpu = desc->txd2; @@ -149,7 +149,7 @@ Signed-off-by: Jakub Kicinski desc = mtk_qdma_phys_to_virt(ring, desc->txd2); if ((desc->txd3 & TX_DMA_OWNER_CPU) == 0) -@@ -2087,15 +2117,13 @@ static int mtk_poll_tx_qdma(struct mtk_e +@@ -2086,15 +2116,13 @@ static int mtk_poll_tx_qdma(struct mtk_e tx_buf = mtk_desc_to_tx_buf(ring, desc, eth->soc->txrx.txd_size); @@ -167,7 +167,7 @@ Signed-off-by: Jakub Kicinski budget--; } -@@ -3704,7 +3732,24 @@ static int mtk_hw_init(struct mtk_eth *e +@@ -3703,7 +3731,24 @@ static int mtk_hw_init(struct mtk_eth *e mtk_w32(eth, eth->soc->txrx.rx_irq_done_mask, reg_map->qdma.int_grp + 4); mtk_w32(eth, 0x21021000, MTK_FE_INT_GRP); @@ -193,7 +193,7 @@ Signed-off-by: Jakub Kicinski /* PSE should not drop port8 and port9 packets from WDMA Tx */ mtk_w32(eth, 0x00000300, PSE_DROP_CFG); -@@ -4266,7 +4311,11 @@ static int mtk_add_mac(struct mtk_eth *e +@@ -4265,7 +4310,11 @@ static int mtk_add_mac(struct mtk_eth *e } spin_lock_init(&mac->hw_stats->stats_lock); u64_stats_init(&mac->hw_stats->syncp); diff --git a/target/linux/generic/backport-6.1/750-v6.5-11-net-ethernet-mtk_eth_soc-add-basic-support-for-MT798.patch b/target/linux/generic/backport-6.1/750-v6.5-11-net-ethernet-mtk_eth_soc-add-basic-support-for-MT798.patch index 8c24321dd..8b1493ce1 100644 --- a/target/linux/generic/backport-6.1/750-v6.5-11-net-ethernet-mtk_eth_soc-add-basic-support-for-MT798.patch +++ b/target/linux/generic/backport-6.1/750-v6.5-11-net-ethernet-mtk_eth_soc-add-basic-support-for-MT798.patch @@ -219,7 +219,7 @@ Signed-off-by: Jakub Kicinski return; err_phy: -@@ -725,11 +841,15 @@ static int mtk_mdio_init(struct mtk_eth +@@ -724,11 +840,15 @@ static int mtk_mdio_init(struct mtk_eth } divider = min_t(unsigned int, DIV_ROUND_UP(MDC_MAX_FREQ, max_clk), 63); @@ -239,7 +239,7 @@ Signed-off-by: Jakub Kicinski dev_dbg(eth->dev, "MDC is running on %d Hz\n", MDC_MAX_FREQ / divider); -@@ -1190,10 +1310,19 @@ static void mtk_tx_set_dma_desc_v2(struc +@@ -1189,10 +1309,19 @@ static void mtk_tx_set_dma_desc_v2(struc data |= TX_DMA_LS0; WRITE_ONCE(desc->txd3, data); @@ -263,7 +263,7 @@ Signed-off-by: Jakub Kicinski data |= TX_DMA_SWC_V2 | QID_BITS_V2(info->qid); WRITE_ONCE(desc->txd4, data); -@@ -4360,6 +4489,17 @@ static int mtk_add_mac(struct mtk_eth *e +@@ -4359,6 +4488,17 @@ static int mtk_add_mac(struct mtk_eth *e mac->phylink_config.supported_interfaces); } @@ -281,7 +281,7 @@ Signed-off-by: Jakub Kicinski phylink = phylink_create(&mac->phylink_config, of_fwnode_handle(mac->of_node), phy_mode, &mtk_phylink_ops); -@@ -4880,6 +5020,24 @@ static const struct mtk_soc_data mt7986_ +@@ -4879,6 +5019,24 @@ static const struct mtk_soc_data mt7986_ }, }; @@ -306,7 +306,7 @@ Signed-off-by: Jakub Kicinski static const struct mtk_soc_data rt5350_data = { .reg_map = &mt7628_reg_map, .caps = MT7628_CAPS, -@@ -4898,14 +5056,15 @@ static const struct mtk_soc_data rt5350_ +@@ -4897,14 +5055,15 @@ static const struct mtk_soc_data rt5350_ }; const struct of_device_id of_mtk_match[] = { diff --git a/target/linux/generic/backport-6.1/750-v6.5-12-net-ethernet-mtk_eth_soc-enable-page_pool-support-fo.patch b/target/linux/generic/backport-6.1/750-v6.5-12-net-ethernet-mtk_eth_soc-enable-page_pool-support-fo.patch index 3dc4662d1..bb48dadec 100644 --- a/target/linux/generic/backport-6.1/750-v6.5-12-net-ethernet-mtk_eth_soc-enable-page_pool-support-fo.patch +++ b/target/linux/generic/backport-6.1/750-v6.5-12-net-ethernet-mtk_eth_soc-enable-page_pool-support-fo.patch @@ -16,7 +16,7 @@ Signed-off-by: Jakub Kicinski --- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c +++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c -@@ -1658,7 +1658,7 @@ static void mtk_update_rx_cpu_idx(struct +@@ -1657,7 +1657,7 @@ static void mtk_update_rx_cpu_idx(struct static bool mtk_page_pool_enabled(struct mtk_eth *eth) { diff --git a/target/linux/generic/backport-6.1/750-v6.5-13-net-ethernet-mtk_eth_soc-enable-nft-hw-flowtable_off.patch b/target/linux/generic/backport-6.1/750-v6.5-13-net-ethernet-mtk_eth_soc-enable-nft-hw-flowtable_off.patch index 32f26d7d2..bb8deb984 100644 --- a/target/linux/generic/backport-6.1/750-v6.5-13-net-ethernet-mtk_eth_soc-enable-nft-hw-flowtable_off.patch +++ b/target/linux/generic/backport-6.1/750-v6.5-13-net-ethernet-mtk_eth_soc-enable-nft-hw-flowtable_off.patch @@ -18,7 +18,7 @@ Signed-off-by: Jakub Kicinski --- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c +++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c -@@ -5028,6 +5028,9 @@ static const struct mtk_soc_data mt7988_ +@@ -5027,6 +5027,9 @@ static const struct mtk_soc_data mt7988_ .required_clks = MT7988_CLKS_BITMAP, .required_pctl = false, .version = 3, diff --git a/target/linux/generic/backport-6.1/750-v6.5-14-net-ethernet-mtk_eth_soc-support-per-flow-accounting.patch b/target/linux/generic/backport-6.1/750-v6.5-14-net-ethernet-mtk_eth_soc-support-per-flow-accounting.patch index 876bdd5dd..c985b6a9c 100644 --- a/target/linux/generic/backport-6.1/750-v6.5-14-net-ethernet-mtk_eth_soc-support-per-flow-accounting.patch +++ b/target/linux/generic/backport-6.1/750-v6.5-14-net-ethernet-mtk_eth_soc-support-per-flow-accounting.patch @@ -20,7 +20,7 @@ Signed-off-by: Paolo Abeni --- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c +++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c -@@ -5030,6 +5030,7 @@ static const struct mtk_soc_data mt7988_ +@@ -5029,6 +5029,7 @@ static const struct mtk_soc_data mt7988_ .version = 3, .offload_version = 2, .hash_offset = 4, diff --git a/target/linux/generic/backport-6.1/750-v6.5-17-net-ethernet-mtk_eth_soc-add-reset-bits-for-MT7988.patch b/target/linux/generic/backport-6.1/750-v6.5-17-net-ethernet-mtk_eth_soc-add-reset-bits-for-MT7988.patch index 05a18364d..e87e0f252 100644 --- a/target/linux/generic/backport-6.1/750-v6.5-17-net-ethernet-mtk_eth_soc-add-reset-bits-for-MT7988.patch +++ b/target/linux/generic/backport-6.1/750-v6.5-17-net-ethernet-mtk_eth_soc-add-reset-bits-for-MT7988.patch @@ -16,7 +16,7 @@ Signed-off-by: Jakub Kicinski --- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c +++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c -@@ -3594,19 +3594,34 @@ static void mtk_hw_reset(struct mtk_eth +@@ -3593,19 +3593,34 @@ static void mtk_hw_reset(struct mtk_eth { u32 val; @@ -56,7 +56,7 @@ Signed-off-by: Jakub Kicinski regmap_write(eth->ethsys, ETHSYS_FE_RST_CHK_IDLE_EN, 0x3ffffff); } -@@ -3632,13 +3647,21 @@ static void mtk_hw_warm_reset(struct mtk +@@ -3631,13 +3646,21 @@ static void mtk_hw_warm_reset(struct mtk return; } @@ -83,7 +83,7 @@ Signed-off-by: Jakub Kicinski regmap_update_bits(eth->ethsys, ETHSYS_RSTCTRL, rst_mask, rst_mask); -@@ -3990,11 +4013,17 @@ static void mtk_prepare_for_reset(struct +@@ -3989,11 +4012,17 @@ static void mtk_prepare_for_reset(struct u32 val; int i; @@ -106,7 +106,7 @@ Signed-off-by: Jakub Kicinski /* adjust PPE configurations to prepare for reset */ for (i = 0; i < ARRAY_SIZE(eth->ppe); i++) -@@ -4055,11 +4084,18 @@ static void mtk_pending_work(struct work +@@ -4054,11 +4083,18 @@ static void mtk_pending_work(struct work } } diff --git a/target/linux/generic/backport-6.1/750-v6.5-18-net-ethernet-mtk_eth_soc-add-support-for-in-SoC-SRAM.patch b/target/linux/generic/backport-6.1/750-v6.5-18-net-ethernet-mtk_eth_soc-add-support-for-in-SoC-SRAM.patch index 74ac8dc89..2d0951a9e 100644 --- a/target/linux/generic/backport-6.1/750-v6.5-18-net-ethernet-mtk_eth_soc-add-support-for-in-SoC-SRAM.patch +++ b/target/linux/generic/backport-6.1/750-v6.5-18-net-ethernet-mtk_eth_soc-add-support-for-in-SoC-SRAM.patch @@ -20,7 +20,7 @@ Signed-off-by: Jakub Kicinski --- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c +++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c -@@ -1118,10 +1118,13 @@ static int mtk_init_fq_dma(struct mtk_et +@@ -1117,10 +1117,13 @@ static int mtk_init_fq_dma(struct mtk_et dma_addr_t dma_addr; int i; @@ -38,7 +38,7 @@ Signed-off-by: Jakub Kicinski if (unlikely(!eth->scratch_ring)) return -ENOMEM; -@@ -2429,8 +2432,14 @@ static int mtk_tx_alloc(struct mtk_eth * +@@ -2428,8 +2431,14 @@ static int mtk_tx_alloc(struct mtk_eth * if (!ring->buf) goto no_tx_mem; @@ -55,7 +55,7 @@ Signed-off-by: Jakub Kicinski if (!ring->dma) goto no_tx_mem; -@@ -2529,8 +2538,7 @@ static void mtk_tx_clean(struct mtk_eth +@@ -2528,8 +2537,7 @@ static void mtk_tx_clean(struct mtk_eth kfree(ring->buf); ring->buf = NULL; } @@ -65,7 +65,7 @@ Signed-off-by: Jakub Kicinski dma_free_coherent(eth->dma_dev, ring->dma_size * soc->txrx.txd_size, ring->dma, ring->phys); -@@ -2549,9 +2557,14 @@ static int mtk_rx_alloc(struct mtk_eth * +@@ -2548,9 +2556,14 @@ static int mtk_rx_alloc(struct mtk_eth * { const struct mtk_reg_map *reg_map = eth->soc->reg_map; struct mtk_rx_ring *ring; @@ -81,7 +81,7 @@ Signed-off-by: Jakub Kicinski if (rx_flag == MTK_RX_FLAGS_QDMA) { if (ring_no) return -EINVAL; -@@ -2586,9 +2599,20 @@ static int mtk_rx_alloc(struct mtk_eth * +@@ -2585,9 +2598,20 @@ static int mtk_rx_alloc(struct mtk_eth * ring->page_pool = pp; } @@ -105,7 +105,7 @@ Signed-off-by: Jakub Kicinski if (!ring->dma) return -ENOMEM; -@@ -2673,7 +2697,7 @@ static int mtk_rx_alloc(struct mtk_eth * +@@ -2672,7 +2696,7 @@ static int mtk_rx_alloc(struct mtk_eth * return 0; } @@ -114,7 +114,7 @@ Signed-off-by: Jakub Kicinski { int i; -@@ -2696,7 +2720,7 @@ static void mtk_rx_clean(struct mtk_eth +@@ -2695,7 +2719,7 @@ static void mtk_rx_clean(struct mtk_eth ring->data = NULL; } @@ -123,7 +123,7 @@ Signed-off-by: Jakub Kicinski dma_free_coherent(eth->dma_dev, ring->dma_size * eth->soc->txrx.rxd_size, ring->dma, ring->phys); -@@ -3059,7 +3083,7 @@ static void mtk_dma_free(struct mtk_eth +@@ -3058,7 +3082,7 @@ static void mtk_dma_free(struct mtk_eth for (i = 0; i < MTK_MAX_DEVS; i++) if (eth->netdev[i]) netdev_reset_queue(eth->netdev[i]); @@ -132,7 +132,7 @@ Signed-off-by: Jakub Kicinski dma_free_coherent(eth->dma_dev, MTK_QDMA_RING_SIZE * soc->txrx.txd_size, eth->scratch_ring, eth->phy_scratch_ring); -@@ -3067,13 +3091,13 @@ static void mtk_dma_free(struct mtk_eth +@@ -3066,13 +3090,13 @@ static void mtk_dma_free(struct mtk_eth eth->phy_scratch_ring = 0; } mtk_tx_clean(eth); @@ -149,7 +149,7 @@ Signed-off-by: Jakub Kicinski } kfree(eth->scratch_head); -@@ -4641,7 +4665,7 @@ static int mtk_sgmii_init(struct mtk_eth +@@ -4640,7 +4664,7 @@ static int mtk_sgmii_init(struct mtk_eth static int mtk_probe(struct platform_device *pdev) { @@ -158,7 +158,7 @@ Signed-off-by: Jakub Kicinski struct device_node *mac_np; struct mtk_eth *eth; int err, i; -@@ -4661,6 +4685,20 @@ static int mtk_probe(struct platform_dev +@@ -4660,6 +4684,20 @@ static int mtk_probe(struct platform_dev if (MTK_HAS_CAPS(eth->soc->caps, MTK_SOC_MT7628)) eth->ip_align = NET_IP_ALIGN; @@ -179,7 +179,7 @@ Signed-off-by: Jakub Kicinski spin_lock_init(ð->page_lock); spin_lock_init(ð->tx_irq_lock); spin_lock_init(ð->rx_irq_lock); -@@ -4724,6 +4762,18 @@ static int mtk_probe(struct platform_dev +@@ -4723,6 +4761,18 @@ static int mtk_probe(struct platform_dev err = -EINVAL; goto err_destroy_sgmii; } diff --git a/target/linux/generic/backport-6.1/750-v6.5-19-net-ethernet-mtk_eth_soc-support-36-bit-DMA-addressi.patch b/target/linux/generic/backport-6.1/750-v6.5-19-net-ethernet-mtk_eth_soc-support-36-bit-DMA-addressi.patch index 1584dfd07..416c4f48e 100644 --- a/target/linux/generic/backport-6.1/750-v6.5-19-net-ethernet-mtk_eth_soc-support-36-bit-DMA-addressi.patch +++ b/target/linux/generic/backport-6.1/750-v6.5-19-net-ethernet-mtk_eth_soc-support-36-bit-DMA-addressi.patch @@ -19,7 +19,7 @@ Signed-off-by: Jakub Kicinski --- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c +++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c -@@ -1311,6 +1311,10 @@ static void mtk_tx_set_dma_desc_v2(struc +@@ -1310,6 +1310,10 @@ static void mtk_tx_set_dma_desc_v2(struc data = TX_DMA_PLEN0(info->size); if (info->last) data |= TX_DMA_LS0; @@ -30,7 +30,7 @@ Signed-off-by: Jakub Kicinski WRITE_ONCE(desc->txd3, data); /* set forward port */ -@@ -1980,6 +1984,7 @@ static int mtk_poll_rx(struct napi_struc +@@ -1979,6 +1983,7 @@ static int mtk_poll_rx(struct napi_struc bool xdp_flush = false; int idx; struct sk_buff *skb; @@ -38,7 +38,7 @@ Signed-off-by: Jakub Kicinski u8 *data, *new_data; struct mtk_rx_dma_v2 *rxd, trxd; int done = 0, bytes = 0; -@@ -2095,7 +2100,10 @@ static int mtk_poll_rx(struct napi_struc +@@ -2094,7 +2099,10 @@ static int mtk_poll_rx(struct napi_struc goto release_desc; } @@ -50,7 +50,7 @@ Signed-off-by: Jakub Kicinski ring->buf_size, DMA_FROM_DEVICE); skb = build_skb(data, ring->frag_size); -@@ -2161,6 +2169,9 @@ release_desc: +@@ -2160,6 +2168,9 @@ release_desc: else rxd->rxd2 = RX_DMA_PREP_PLEN0(ring->buf_size); @@ -60,7 +60,7 @@ Signed-off-by: Jakub Kicinski ring->calc_idx = idx; done++; } -@@ -2653,6 +2664,9 @@ static int mtk_rx_alloc(struct mtk_eth * +@@ -2652,6 +2663,9 @@ static int mtk_rx_alloc(struct mtk_eth * else rxd->rxd2 = RX_DMA_PREP_PLEN0(ring->buf_size); @@ -70,7 +70,7 @@ Signed-off-by: Jakub Kicinski rxd->rxd3 = 0; rxd->rxd4 = 0; if (mtk_is_netsys_v2_or_greater(eth)) { -@@ -2699,6 +2713,7 @@ static int mtk_rx_alloc(struct mtk_eth * +@@ -2698,6 +2712,7 @@ static int mtk_rx_alloc(struct mtk_eth * static void mtk_rx_clean(struct mtk_eth *eth, struct mtk_rx_ring *ring, bool in_sram) { @@ -78,7 +78,7 @@ Signed-off-by: Jakub Kicinski int i; if (ring->data && ring->dma) { -@@ -2712,7 +2727,10 @@ static void mtk_rx_clean(struct mtk_eth +@@ -2711,7 +2726,10 @@ static void mtk_rx_clean(struct mtk_eth if (!rxd->rxd1) continue; @@ -90,7 +90,7 @@ Signed-off-by: Jakub Kicinski ring->buf_size, DMA_FROM_DEVICE); mtk_rx_put_buff(ring, ring->data[i], false); } -@@ -4699,6 +4717,14 @@ static int mtk_probe(struct platform_dev +@@ -4698,6 +4716,14 @@ static int mtk_probe(struct platform_dev } } diff --git a/target/linux/generic/backport-6.1/750-v6.5-20-net-ethernet-mtk_eth_soc-fix-uninitialized-variable.patch b/target/linux/generic/backport-6.1/750-v6.5-20-net-ethernet-mtk_eth_soc-fix-uninitialized-variable.patch new file mode 100644 index 000000000..d761866d0 --- /dev/null +++ b/target/linux/generic/backport-6.1/750-v6.5-20-net-ethernet-mtk_eth_soc-fix-uninitialized-variable.patch @@ -0,0 +1,44 @@ +From e10a35abb3da12b812cfb6fc6137926a0c81e39a Mon Sep 17 00:00:00 2001 +From: Daniel Golle +Date: Sun, 10 Sep 2023 22:40:30 +0100 +Subject: [PATCH] net: ethernet: mtk_eth_soc: fix uninitialized variable + +Variable dma_addr in function mtk_poll_rx can be uninitialized on +some of the error paths. In practise this doesn't matter, even random +data present in uninitialized stack memory can safely be used in the +way it happens in the error path. + +However, in order to make Smatch happy make sure the variable is +always initialized. + +Signed-off-by: Daniel Golle +Signed-off-by: David S. Miller +--- + drivers/net/ethernet/mediatek/mtk_eth_soc.c | 5 +++-- + 1 file changed, 3 insertions(+), 2 deletions(-) + +--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c ++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c +@@ -1987,11 +1987,11 @@ static int mtk_poll_rx(struct napi_struc + u8 *data, *new_data; + struct mtk_rx_dma_v2 *rxd, trxd; + int done = 0, bytes = 0; ++ dma_addr_t dma_addr = DMA_MAPPING_ERROR; + + while (done < budget) { + unsigned int pktlen, *rxdcsum; + struct net_device *netdev; +- dma_addr_t dma_addr; + u32 hash, reason; + int mac = 0; + +@@ -2168,7 +2168,8 @@ release_desc: + else + rxd->rxd2 = RX_DMA_PREP_PLEN0(ring->buf_size); + +- if (MTK_HAS_CAPS(eth->soc->caps, MTK_36BIT_DMA)) ++ if (MTK_HAS_CAPS(eth->soc->caps, MTK_36BIT_DMA) && ++ likely(dma_addr != DMA_MAPPING_ERROR)) + rxd->rxd2 |= RX_DMA_PREP_ADDR64(dma_addr); + + ring->calc_idx = idx; diff --git a/target/linux/generic/backport-6.1/750-v6.5-21-net-ethernet-mtk_eth_soc-fix-pse_port-configuration-.patch b/target/linux/generic/backport-6.1/750-v6.5-21-net-ethernet-mtk_eth_soc-fix-pse_port-configuration-.patch new file mode 100644 index 000000000..ac3e3a3e6 --- /dev/null +++ b/target/linux/generic/backport-6.1/750-v6.5-21-net-ethernet-mtk_eth_soc-fix-pse_port-configuration-.patch @@ -0,0 +1,33 @@ +From 5a124b1fd3e6cb15a943f0cdfe96aa8f6d3d2f39 Mon Sep 17 00:00:00 2001 +From: Lorenzo Bianconi +Date: Sat, 9 Sep 2023 20:41:56 +0200 +Subject: [PATCH] net: ethernet: mtk_eth_soc: fix pse_port configuration for + MT7988 + +MT7988 SoC support 3 NICs. Fix pse_port configuration in +mtk_flow_set_output_device routine if the traffic is offloaded to eth2. +Rely on mtk_pse_port definitions. + +Fixes: 88efedf517e6 ("net: ethernet: mtk_eth_soc: enable nft hw flowtable_offload for MT7988 SoC") +Signed-off-by: Lorenzo Bianconi +Signed-off-by: David S. Miller +--- + drivers/net/ethernet/mediatek/mtk_ppe_offload.c | 6 ++++-- + 1 file changed, 4 insertions(+), 2 deletions(-) + +--- a/drivers/net/ethernet/mediatek/mtk_ppe_offload.c ++++ b/drivers/net/ethernet/mediatek/mtk_ppe_offload.c +@@ -214,9 +214,11 @@ mtk_flow_set_output_device(struct mtk_et + dsa_port = mtk_flow_get_dsa_port(&dev); + + if (dev == eth->netdev[0]) +- pse_port = 1; ++ pse_port = PSE_GDM1_PORT; + else if (dev == eth->netdev[1]) +- pse_port = 2; ++ pse_port = PSE_GDM2_PORT; ++ else if (dev == eth->netdev[2]) ++ pse_port = PSE_GDM3_PORT; + else + return -EOPNOTSUPP; + diff --git a/target/linux/generic/pending-6.1/736-01-net-ethernet-mtk_eth_soc-add-code-for-offloading-flo.patch b/target/linux/generic/backport-6.1/751-01-v6.4-net-ethernet-mtk_eth_soc-add-code-for-offloading-flo.patch similarity index 96% rename from target/linux/generic/pending-6.1/736-01-net-ethernet-mtk_eth_soc-add-code-for-offloading-flo.patch rename to target/linux/generic/backport-6.1/751-01-v6.4-net-ethernet-mtk_eth_soc-add-code-for-offloading-flo.patch index c29c63bb1..46da5b283 100644 --- a/target/linux/generic/pending-6.1/736-01-net-ethernet-mtk_eth_soc-add-code-for-offloading-flo.patch +++ b/target/linux/generic/backport-6.1/751-01-v6.4-net-ethernet-mtk_eth_soc-add-code-for-offloading-flo.patch @@ -19,7 +19,7 @@ Signed-off-by: Felix Fietkau --- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h +++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h -@@ -1451,6 +1451,9 @@ int mtk_gmac_rgmii_path_setup(struct mtk +@@ -1435,6 +1435,9 @@ int mtk_gmac_rgmii_path_setup(struct mtk int mtk_eth_offload_init(struct mtk_eth *eth); int mtk_eth_setup_tc(struct net_device *dev, enum tc_setup_type type, void *type_data); @@ -31,7 +31,7 @@ Signed-off-by: Felix Fietkau --- a/drivers/net/ethernet/mediatek/mtk_ppe_offload.c +++ b/drivers/net/ethernet/mediatek/mtk_ppe_offload.c -@@ -235,7 +235,8 @@ out: +@@ -237,7 +237,8 @@ out: } static int @@ -41,7 +41,7 @@ Signed-off-by: Felix Fietkau { struct flow_rule *rule = flow_cls_offload_flow_rule(f); struct flow_action_entry *act; -@@ -452,6 +453,7 @@ mtk_flow_offload_replace(struct mtk_eth +@@ -454,6 +455,7 @@ mtk_flow_offload_replace(struct mtk_eth entry->cookie = f->cookie; memcpy(&entry->data, &foe, sizeof(entry->data)); entry->wed_index = wed_index; @@ -49,7 +49,7 @@ Signed-off-by: Felix Fietkau err = mtk_foe_entry_commit(eth->ppe[entry->ppe_index], entry); if (err < 0) -@@ -520,25 +522,15 @@ mtk_flow_offload_stats(struct mtk_eth *e +@@ -522,25 +524,15 @@ mtk_flow_offload_stats(struct mtk_eth *e static DEFINE_MUTEX(mtk_flow_offload_mutex); @@ -78,7 +78,7 @@ Signed-off-by: Felix Fietkau break; case FLOW_CLS_DESTROY: err = mtk_flow_offload_destroy(eth, cls); -@@ -556,6 +548,23 @@ mtk_eth_setup_tc_block_cb(enum tc_setup_ +@@ -558,6 +550,23 @@ mtk_eth_setup_tc_block_cb(enum tc_setup_ } static int diff --git a/target/linux/generic/pending-6.1/736-02-net-ethernet-mediatek-mtk_ppe-prefer-newly-added-l2-.patch b/target/linux/generic/backport-6.1/751-02-v6.4-net-ethernet-mediatek-mtk_ppe-prefer-newly-added-l2-.patch similarity index 94% rename from target/linux/generic/pending-6.1/736-02-net-ethernet-mediatek-mtk_ppe-prefer-newly-added-l2-.patch rename to target/linux/generic/backport-6.1/751-02-v6.4-net-ethernet-mediatek-mtk_ppe-prefer-newly-added-l2-.patch index 74cb562a5..b1796641a 100644 --- a/target/linux/generic/pending-6.1/736-02-net-ethernet-mediatek-mtk_ppe-prefer-newly-added-l2-.patch +++ b/target/linux/generic/backport-6.1/751-02-v6.4-net-ethernet-mediatek-mtk_ppe-prefer-newly-added-l2-.patch @@ -12,7 +12,7 @@ Signed-off-by: Felix Fietkau --- a/drivers/net/ethernet/mediatek/mtk_ppe.c +++ b/drivers/net/ethernet/mediatek/mtk_ppe.c -@@ -657,10 +657,20 @@ void mtk_foe_entry_clear(struct mtk_ppe +@@ -656,10 +656,20 @@ void mtk_foe_entry_clear(struct mtk_ppe static int mtk_foe_entry_commit_l2(struct mtk_ppe *ppe, struct mtk_flow_entry *entry) { diff --git a/target/linux/generic/pending-6.1/736-03-net-ethernet-mtk_eth_soc-improve-keeping-track-of-of.patch b/target/linux/generic/backport-6.1/751-03-v6.4-net-ethernet-mtk_eth_soc-improve-keeping-track-of-of.patch similarity index 94% rename from target/linux/generic/pending-6.1/736-03-net-ethernet-mtk_eth_soc-improve-keeping-track-of-of.patch rename to target/linux/generic/backport-6.1/751-03-v6.4-net-ethernet-mtk_eth_soc-improve-keeping-track-of-of.patch index ecdb345d6..8b8a8e11c 100644 --- a/target/linux/generic/pending-6.1/736-03-net-ethernet-mtk_eth_soc-improve-keeping-track-of-of.patch +++ b/target/linux/generic/backport-6.1/751-03-v6.4-net-ethernet-mtk_eth_soc-improve-keeping-track-of-of.patch @@ -15,7 +15,7 @@ Signed-off-by: Felix Fietkau --- a/drivers/net/ethernet/mediatek/mtk_ppe.c +++ b/drivers/net/ethernet/mediatek/mtk_ppe.c -@@ -477,42 +477,43 @@ int mtk_foe_entry_set_queue(struct mtk_e +@@ -476,42 +476,43 @@ int mtk_foe_entry_set_queue(struct mtk_e return 0; } @@ -75,7 +75,7 @@ Signed-off-by: Felix Fietkau struct mtk_foe_entry *hwe = mtk_foe_get_entry(ppe, entry->hash); hwe->ib1 &= ~MTK_FOE_IB1_STATE; -@@ -532,7 +533,8 @@ __mtk_foe_entry_clear(struct mtk_ppe *pp +@@ -531,7 +532,8 @@ __mtk_foe_entry_clear(struct mtk_ppe *pp if (entry->type != MTK_FLOW_TYPE_L2_SUBFLOW) return; @@ -85,7 +85,7 @@ Signed-off-by: Felix Fietkau kfree(entry); } -@@ -548,66 +550,55 @@ static int __mtk_foe_entry_idle_time(str +@@ -547,66 +549,55 @@ static int __mtk_foe_entry_idle_time(str return now - timestamp; } @@ -181,7 +181,7 @@ Signed-off-by: Felix Fietkau } static void -@@ -650,7 +641,8 @@ __mtk_foe_entry_commit(struct mtk_ppe *p +@@ -649,7 +640,8 @@ __mtk_foe_entry_commit(struct mtk_ppe *p void mtk_foe_entry_clear(struct mtk_ppe *ppe, struct mtk_flow_entry *entry) { spin_lock_bh(&ppe_lock); @@ -191,7 +191,7 @@ Signed-off-by: Felix Fietkau spin_unlock_bh(&ppe_lock); } -@@ -697,8 +689,8 @@ mtk_foe_entry_commit_subflow(struct mtk_ +@@ -696,8 +688,8 @@ mtk_foe_entry_commit_subflow(struct mtk_ { const struct mtk_soc_data *soc = ppe->eth->soc; struct mtk_flow_entry *flow_info; @@ -201,7 +201,7 @@ Signed-off-by: Felix Fietkau u32 ib1_mask = mtk_get_ib1_pkt_type_mask(ppe->eth) | MTK_FOE_IB1_UDP; int type; -@@ -706,30 +698,30 @@ mtk_foe_entry_commit_subflow(struct mtk_ +@@ -705,30 +697,30 @@ mtk_foe_entry_commit_subflow(struct mtk_ if (!flow_info) return; @@ -242,7 +242,7 @@ Signed-off-by: Felix Fietkau } void __mtk_ppe_check_skb(struct mtk_ppe *ppe, struct sk_buff *skb, u16 hash) -@@ -739,9 +731,11 @@ void __mtk_ppe_check_skb(struct mtk_ppe +@@ -738,9 +730,11 @@ void __mtk_ppe_check_skb(struct mtk_ppe struct mtk_foe_entry *hwe = mtk_foe_get_entry(ppe, hash); struct mtk_flow_entry *entry; struct mtk_foe_bridge key = {}; @@ -254,7 +254,7 @@ Signed-off-by: Felix Fietkau u8 *tag; spin_lock_bh(&ppe_lock); -@@ -749,20 +743,14 @@ void __mtk_ppe_check_skb(struct mtk_ppe +@@ -748,20 +742,14 @@ void __mtk_ppe_check_skb(struct mtk_ppe if (FIELD_GET(MTK_FOE_IB1_STATE, hwe->ib1) == MTK_FOE_STATE_BIND) goto out; @@ -281,7 +281,7 @@ Signed-off-by: Felix Fietkau continue; } -@@ -813,9 +801,17 @@ out: +@@ -810,9 +798,17 @@ out: int mtk_foe_entry_idle_time(struct mtk_ppe *ppe, struct mtk_flow_entry *entry) { diff --git a/target/linux/generic/pending-6.1/736-04-net-ethernet-mediatek-fix-ppe-flow-accounting-for-L2.patch b/target/linux/generic/backport-6.1/751-04-v6.4-net-ethernet-mediatek-fix-ppe-flow-accounting-for-L2.patch similarity index 93% rename from target/linux/generic/pending-6.1/736-04-net-ethernet-mediatek-fix-ppe-flow-accounting-for-L2.patch rename to target/linux/generic/backport-6.1/751-04-v6.4-net-ethernet-mediatek-fix-ppe-flow-accounting-for-L2.patch index e323baeb1..e20f94f1d 100644 --- a/target/linux/generic/pending-6.1/736-04-net-ethernet-mediatek-fix-ppe-flow-accounting-for-L2.patch +++ b/target/linux/generic/backport-6.1/751-04-v6.4-net-ethernet-mediatek-fix-ppe-flow-accounting-for-L2.patch @@ -14,7 +14,7 @@ Signed-off-by: Felix Fietkau --- a/drivers/net/ethernet/mediatek/mtk_ppe.c +++ b/drivers/net/ethernet/mediatek/mtk_ppe.c -@@ -80,9 +80,9 @@ static int mtk_ppe_mib_wait_busy(struct +@@ -79,9 +79,9 @@ static int mtk_ppe_mib_wait_busy(struct int ret; u32 val; @@ -27,7 +27,7 @@ Signed-off-by: Felix Fietkau if (ret) dev_err(ppe->dev, "MIB table busy"); -@@ -90,17 +90,31 @@ static int mtk_ppe_mib_wait_busy(struct +@@ -89,17 +89,31 @@ static int mtk_ppe_mib_wait_busy(struct return ret; } @@ -61,7 +61,7 @@ Signed-off-by: Felix Fietkau cnt_r0 = readl(ppe->base + MTK_PPE_MIB_SER_R0); cnt_r1 = readl(ppe->base + MTK_PPE_MIB_SER_R1); -@@ -109,19 +123,19 @@ static int mtk_mib_entry_read(struct mtk +@@ -108,19 +122,19 @@ static int mtk_mib_entry_read(struct mtk if (mtk_is_netsys_v3_or_greater(ppe->eth)) { /* 64 bit for each counter */ u32 cnt_r3 = readl(ppe->base + MTK_PPE_MIB_SER_R3); @@ -86,7 +86,7 @@ Signed-off-by: Felix Fietkau } static void mtk_ppe_cache_clear(struct mtk_ppe *ppe) -@@ -520,13 +534,6 @@ __mtk_foe_entry_clear(struct mtk_ppe *pp +@@ -519,13 +533,6 @@ __mtk_foe_entry_clear(struct mtk_ppe *pp hwe->ib1 |= FIELD_PREP(MTK_FOE_IB1_STATE, MTK_FOE_STATE_INVALID); dma_wmb(); mtk_ppe_cache_clear(ppe); @@ -100,7 +100,7 @@ Signed-off-by: Felix Fietkau } entry->hash = 0xffff; -@@ -551,11 +558,14 @@ static int __mtk_foe_entry_idle_time(str +@@ -550,11 +557,14 @@ static int __mtk_foe_entry_idle_time(str } static bool @@ -116,7 +116,7 @@ Signed-off-by: Felix Fietkau int len; if (hash == 0xffff) -@@ -566,18 +576,35 @@ mtk_flow_entry_update(struct mtk_ppe *pp +@@ -565,18 +575,35 @@ mtk_flow_entry_update(struct mtk_ppe *pp memcpy(&foe, hwe, len); if (!mtk_flow_entry_match(ppe->eth, entry, &foe, len) || @@ -155,7 +155,7 @@ Signed-off-by: Felix Fietkau struct mtk_flow_entry *cur; struct hlist_node *tmp; int idle; -@@ -586,7 +613,9 @@ mtk_flow_entry_update_l2(struct mtk_ppe +@@ -585,7 +612,9 @@ mtk_flow_entry_update_l2(struct mtk_ppe hlist_for_each_entry_safe(cur, tmp, &entry->l2_flows, l2_list) { int cur_idle; @@ -166,7 +166,7 @@ Signed-off-by: Felix Fietkau __mtk_foe_entry_clear(ppe, entry, false); continue; } -@@ -601,10 +630,29 @@ mtk_flow_entry_update_l2(struct mtk_ppe +@@ -600,10 +629,29 @@ mtk_flow_entry_update_l2(struct mtk_ppe } } @@ -196,7 +196,7 @@ Signed-off-by: Felix Fietkau struct mtk_eth *eth = ppe->eth; u16 timestamp = mtk_eth_timestamp(eth); struct mtk_foe_entry *hwe; -@@ -635,6 +683,12 @@ __mtk_foe_entry_commit(struct mtk_ppe *p +@@ -634,6 +682,12 @@ __mtk_foe_entry_commit(struct mtk_ppe *p dma_wmb(); @@ -209,7 +209,7 @@ Signed-off-by: Felix Fietkau mtk_ppe_cache_clear(ppe); } -@@ -799,21 +853,6 @@ out: +@@ -796,21 +850,6 @@ out: spin_unlock_bh(&ppe_lock); } @@ -231,7 +231,7 @@ Signed-off-by: Felix Fietkau int mtk_ppe_prepare_reset(struct mtk_ppe *ppe) { if (!ppe) -@@ -841,32 +880,6 @@ int mtk_ppe_prepare_reset(struct mtk_ppe +@@ -838,32 +877,6 @@ int mtk_ppe_prepare_reset(struct mtk_ppe return mtk_ppe_wait_busy(ppe); } @@ -308,7 +308,7 @@ Signed-off-by: Felix Fietkau seq_printf(m, "%05x %s %7s", i, --- a/drivers/net/ethernet/mediatek/mtk_ppe_offload.c +++ b/drivers/net/ethernet/mediatek/mtk_ppe_offload.c -@@ -499,24 +499,21 @@ static int +@@ -501,24 +501,21 @@ static int mtk_flow_offload_stats(struct mtk_eth *eth, struct flow_cls_offload *f) { struct mtk_flow_entry *entry; diff --git a/target/linux/generic/backport-6.1/752-01-v6.6-net-ethernet-mtk_wed-add-some-more-info-in-wed_txinf.patch b/target/linux/generic/backport-6.1/752-01-v6.6-net-ethernet-mtk_wed-add-some-more-info-in-wed_txinf.patch new file mode 100644 index 000000000..a224b6262 --- /dev/null +++ b/target/linux/generic/backport-6.1/752-01-v6.6-net-ethernet-mtk_wed-add-some-more-info-in-wed_txinf.patch @@ -0,0 +1,45 @@ +From: Lorenzo Bianconi +Date: Sun, 27 Aug 2023 19:31:41 +0200 +Subject: [PATCH] net: ethernet: mtk_wed: add some more info in wed_txinfo_show + handler + +Add some new info in Wireless Ethernet Dispatcher wed_txinfo_show +debugfs handler useful during debugging. + +Signed-off-by: Lorenzo Bianconi +Link: https://lore.kernel.org/r/3390292655d568180b73d2a25576f61aa63310e5.1693157377.git.lorenzo@kernel.org +Signed-off-by: Jakub Kicinski +--- + +--- a/drivers/net/ethernet/mediatek/mtk_wed_debugfs.c ++++ b/drivers/net/ethernet/mediatek/mtk_wed_debugfs.c +@@ -127,8 +127,17 @@ wed_txinfo_show(struct seq_file *s, void + DUMP_WDMA_RING(WDMA_RING_RX(0)), + DUMP_WDMA_RING(WDMA_RING_RX(1)), + +- DUMP_STR("TX FREE"), ++ DUMP_STR("WED TX FREE"), + DUMP_WED(WED_RX_MIB(0)), ++ DUMP_WED_RING(WED_RING_RX(0)), ++ DUMP_WED(WED_WPDMA_RX_COHERENT_MIB(0)), ++ DUMP_WED(WED_RX_MIB(1)), ++ DUMP_WED_RING(WED_RING_RX(1)), ++ DUMP_WED(WED_WPDMA_RX_COHERENT_MIB(1)), ++ ++ DUMP_STR("WED WPDMA TX FREE"), ++ DUMP_WED_RING(WED_WPDMA_RING_RX(0)), ++ DUMP_WED_RING(WED_WPDMA_RING_RX(1)), + }; + struct mtk_wed_hw *hw = s->private; + struct mtk_wed_device *dev = hw->wed_dev; +--- a/drivers/net/ethernet/mediatek/mtk_wed_regs.h ++++ b/drivers/net/ethernet/mediatek/mtk_wed_regs.h +@@ -266,6 +266,8 @@ struct mtk_wdma_desc { + + #define MTK_WED_WPDMA_TX_MIB(_n) (0x5a0 + (_n) * 4) + #define MTK_WED_WPDMA_TX_COHERENT_MIB(_n) (0x5d0 + (_n) * 4) ++#define MTK_WED_WPDMA_RX_MIB(_n) (0x5e0 + (_n) * 4) ++#define MTK_WED_WPDMA_RX_COHERENT_MIB(_n) (0x5f0 + (_n) * 4) + + #define MTK_WED_WPDMA_RING_TX(_n) (0x600 + (_n) * 0x10) + #define MTK_WED_WPDMA_RING_RX(_n) (0x700 + (_n) * 0x10) diff --git a/target/linux/generic/backport-6.1/752-02-v6.6-net-ethernet-mtk_wed-minor-change-in-wed_-tx-rx-info.patch b/target/linux/generic/backport-6.1/752-02-v6.6-net-ethernet-mtk_wed-minor-change-in-wed_-tx-rx-info.patch new file mode 100644 index 000000000..df6edfdf9 --- /dev/null +++ b/target/linux/generic/backport-6.1/752-02-v6.6-net-ethernet-mtk_wed-minor-change-in-wed_-tx-rx-info.patch @@ -0,0 +1,47 @@ +From: Lorenzo Bianconi +Date: Sun, 27 Aug 2023 19:33:47 +0200 +Subject: [PATCH] net: ethernet: mtk_wed: minor change in wed_{tx,rx}info_show + +No functional changes, just cosmetic ones. + +Signed-off-by: Lorenzo Bianconi +Link: https://lore.kernel.org/r/71e046c72a978745f0435af265dda610aa9bfbcf.1693157578.git.lorenzo@kernel.org +Signed-off-by: Jakub Kicinski +--- + +--- a/drivers/net/ethernet/mediatek/mtk_wed_debugfs.c ++++ b/drivers/net/ethernet/mediatek/mtk_wed_debugfs.c +@@ -84,7 +84,6 @@ dump_wed_regs(struct seq_file *s, struct + } + } + +- + static int + wed_txinfo_show(struct seq_file *s, void *data) + { +@@ -142,10 +141,8 @@ wed_txinfo_show(struct seq_file *s, void + struct mtk_wed_hw *hw = s->private; + struct mtk_wed_device *dev = hw->wed_dev; + +- if (!dev) +- return 0; +- +- dump_wed_regs(s, dev, regs, ARRAY_SIZE(regs)); ++ if (dev) ++ dump_wed_regs(s, dev, regs, ARRAY_SIZE(regs)); + + return 0; + } +@@ -217,10 +214,8 @@ wed_rxinfo_show(struct seq_file *s, void + struct mtk_wed_hw *hw = s->private; + struct mtk_wed_device *dev = hw->wed_dev; + +- if (!dev) +- return 0; +- +- dump_wed_regs(s, dev, regs, ARRAY_SIZE(regs)); ++ if (dev) ++ dump_wed_regs(s, dev, regs, ARRAY_SIZE(regs)); + + return 0; + } diff --git a/target/linux/generic/backport-6.1/752-03-v6.6-net-ethernet-mtk_eth_soc-rely-on-mtk_pse_port-defini.patch b/target/linux/generic/backport-6.1/752-03-v6.6-net-ethernet-mtk_eth_soc-rely-on-mtk_pse_port-defini.patch new file mode 100644 index 000000000..0bf9dea24 --- /dev/null +++ b/target/linux/generic/backport-6.1/752-03-v6.6-net-ethernet-mtk_eth_soc-rely-on-mtk_pse_port-defini.patch @@ -0,0 +1,29 @@ +From: Lorenzo Bianconi +Date: Tue, 12 Sep 2023 10:22:56 +0200 +Subject: [PATCH] net: ethernet: mtk_eth_soc: rely on mtk_pse_port definitions + in mtk_flow_set_output_device + +Similar to ethernet ports, rely on mtk_pse_port definitions for +pse wdma ports as well. + +Signed-off-by: Lorenzo Bianconi +Reviewed-by: Simon Horman +Link: https://lore.kernel.org/r/b86bdb717e963e3246c1dec5f736c810703cf056.1694506814.git.lorenzo@kernel.org +Signed-off-by: Paolo Abeni +--- + +--- a/drivers/net/ethernet/mediatek/mtk_ppe_offload.c ++++ b/drivers/net/ethernet/mediatek/mtk_ppe_offload.c +@@ -196,10 +196,10 @@ mtk_flow_set_output_device(struct mtk_et + if (mtk_is_netsys_v2_or_greater(eth)) { + switch (info.wdma_idx) { + case 0: +- pse_port = 8; ++ pse_port = PSE_WDMA0_PORT; + break; + case 1: +- pse_port = 9; ++ pse_port = PSE_WDMA1_PORT; + break; + default: + return -EINVAL; diff --git a/target/linux/generic/backport-6.1/752-04-v6.6-net-ethernet-mtk_wed-check-update_wo_rx_stats-in-mtk.patch b/target/linux/generic/backport-6.1/752-04-v6.6-net-ethernet-mtk_wed-check-update_wo_rx_stats-in-mtk.patch new file mode 100644 index 000000000..c99e1334d --- /dev/null +++ b/target/linux/generic/backport-6.1/752-04-v6.6-net-ethernet-mtk_wed-check-update_wo_rx_stats-in-mtk.patch @@ -0,0 +1,26 @@ +From: Lorenzo Bianconi +Date: Tue, 12 Sep 2023 10:28:00 +0200 +Subject: [PATCH] net: ethernet: mtk_wed: check update_wo_rx_stats in + mtk_wed_update_rx_stats() + +Check if update_wo_rx_stats function pointer is properly set in +mtk_wed_update_rx_stats routine before accessing it. + +Signed-off-by: Lorenzo Bianconi +Reviewed-by: Simon Horman +Link: https://lore.kernel.org/r/b0d233386e059bccb59f18f69afb79a7806e5ded.1694507226.git.lorenzo@kernel.org +Signed-off-by: Paolo Abeni +--- + +--- a/drivers/net/ethernet/mediatek/mtk_wed_mcu.c ++++ b/drivers/net/ethernet/mediatek/mtk_wed_mcu.c +@@ -68,6 +68,9 @@ mtk_wed_update_rx_stats(struct mtk_wed_d + struct mtk_wed_wo_rx_stats *stats; + int i; + ++ if (!wed->wlan.update_wo_rx_stats) ++ return; ++ + if (count * sizeof(*stats) > skb->len - sizeof(u32)) + return; + diff --git a/target/linux/generic/backport-6.1/752-05-v6.7-net-ethernet-mtk_wed-do-not-assume-offload-callbacks.patch b/target/linux/generic/backport-6.1/752-05-v6.7-net-ethernet-mtk_wed-do-not-assume-offload-callbacks.patch new file mode 100644 index 000000000..cd7fb92e2 --- /dev/null +++ b/target/linux/generic/backport-6.1/752-05-v6.7-net-ethernet-mtk_wed-do-not-assume-offload-callbacks.patch @@ -0,0 +1,68 @@ +From: Lorenzo Bianconi +Date: Wed, 13 Sep 2023 20:42:47 +0200 +Subject: [PATCH] net: ethernet: mtk_wed: do not assume offload callbacks are + always set + +Check if wlan.offload_enable and wlan.offload_disable callbacks are set +in mtk_wed_flow_add/mtk_wed_flow_remove since mt7996 will not rely +on them. + +Signed-off-by: Lorenzo Bianconi +Reviewed-by: Simon Horman +Signed-off-by: David S. Miller +--- + +--- a/drivers/net/ethernet/mediatek/mtk_wed.c ++++ b/drivers/net/ethernet/mediatek/mtk_wed.c +@@ -1712,19 +1712,20 @@ mtk_wed_irq_set_mask(struct mtk_wed_devi + int mtk_wed_flow_add(int index) + { + struct mtk_wed_hw *hw = hw_list[index]; +- int ret; ++ int ret = 0; + +- if (!hw || !hw->wed_dev) +- return -ENODEV; ++ mutex_lock(&hw_lock); + +- if (hw->num_flows) { +- hw->num_flows++; +- return 0; ++ if (!hw || !hw->wed_dev) { ++ ret = -ENODEV; ++ goto out; + } + +- mutex_lock(&hw_lock); +- if (!hw->wed_dev) { +- ret = -ENODEV; ++ if (!hw->wed_dev->wlan.offload_enable) ++ goto out; ++ ++ if (hw->num_flows) { ++ hw->num_flows++; + goto out; + } + +@@ -1743,14 +1744,15 @@ void mtk_wed_flow_remove(int index) + { + struct mtk_wed_hw *hw = hw_list[index]; + +- if (!hw) +- return; ++ mutex_lock(&hw_lock); + +- if (--hw->num_flows) +- return; ++ if (!hw || !hw->wed_dev) ++ goto out; + +- mutex_lock(&hw_lock); +- if (!hw->wed_dev) ++ if (!hw->wed_dev->wlan.offload_disable) ++ goto out; ++ ++ if (--hw->num_flows) + goto out; + + hw->wed_dev->wlan.offload_disable(hw->wed_dev); diff --git a/target/linux/generic/backport-6.1/752-06-v6.7-net-ethernet-mtk_wed-introduce-versioning-utility-ro.patch b/target/linux/generic/backport-6.1/752-06-v6.7-net-ethernet-mtk_wed-introduce-versioning-utility-ro.patch new file mode 100644 index 000000000..294818865 --- /dev/null +++ b/target/linux/generic/backport-6.1/752-06-v6.7-net-ethernet-mtk_wed-introduce-versioning-utility-ro.patch @@ -0,0 +1,232 @@ +From: Lorenzo Bianconi +Date: Mon, 18 Sep 2023 12:29:05 +0200 +Subject: [PATCH] net: ethernet: mtk_wed: introduce versioning utility routines + +Similar to mtk_eth_soc, introduce the following wed versioning +utility routines: +- mtk_wed_is_v1 +- mtk_wed_is_v2 + +This is a preliminary patch to introduce WED support for MT7988 SoC + +Signed-off-by: Lorenzo Bianconi +Signed-off-by: Paolo Abeni +--- + +--- a/drivers/net/ethernet/mediatek/mtk_wed.c ++++ b/drivers/net/ethernet/mediatek/mtk_wed.c +@@ -277,7 +277,7 @@ mtk_wed_assign(struct mtk_wed_device *de + if (!hw->wed_dev) + goto out; + +- if (hw->version == 1) ++ if (mtk_wed_is_v1(hw)) + return NULL; + + /* MT7986 WED devices do not have any pcie slot restrictions */ +@@ -358,7 +358,7 @@ mtk_wed_tx_buffer_alloc(struct mtk_wed_d + desc->buf0 = cpu_to_le32(buf_phys); + desc->buf1 = cpu_to_le32(buf_phys + txd_size); + +- if (dev->hw->version == 1) ++ if (mtk_wed_is_v1(dev->hw)) + ctrl = FIELD_PREP(MTK_WDMA_DESC_CTRL_LEN0, txd_size) | + FIELD_PREP(MTK_WDMA_DESC_CTRL_LEN1, + MTK_WED_BUF_SIZE - txd_size) | +@@ -497,7 +497,7 @@ mtk_wed_set_ext_int(struct mtk_wed_devic + { + u32 mask = MTK_WED_EXT_INT_STATUS_ERROR_MASK; + +- if (dev->hw->version == 1) ++ if (mtk_wed_is_v1(dev->hw)) + mask |= MTK_WED_EXT_INT_STATUS_TX_DRV_R_RESP_ERR; + else + mask |= MTK_WED_EXT_INT_STATUS_RX_FBUF_LO_TH | +@@ -576,7 +576,7 @@ mtk_wed_dma_disable(struct mtk_wed_devic + MTK_WDMA_GLO_CFG_RX_INFO1_PRERES | + MTK_WDMA_GLO_CFG_RX_INFO2_PRERES); + +- if (dev->hw->version == 1) { ++ if (mtk_wed_is_v1(dev->hw)) { + regmap_write(dev->hw->mirror, dev->hw->index * 4, 0); + wdma_clr(dev, MTK_WDMA_GLO_CFG, + MTK_WDMA_GLO_CFG_RX_INFO3_PRERES); +@@ -605,7 +605,7 @@ mtk_wed_stop(struct mtk_wed_device *dev) + wdma_w32(dev, MTK_WDMA_INT_GRP2, 0); + wed_w32(dev, MTK_WED_WPDMA_INT_MASK, 0); + +- if (dev->hw->version == 1) ++ if (mtk_wed_is_v1(dev->hw)) + return; + + wed_w32(dev, MTK_WED_EXT_INT_MASK1, 0); +@@ -624,7 +624,7 @@ mtk_wed_deinit(struct mtk_wed_device *de + MTK_WED_CTRL_WED_TX_BM_EN | + MTK_WED_CTRL_WED_TX_FREE_AGENT_EN); + +- if (dev->hw->version == 1) ++ if (mtk_wed_is_v1(dev->hw)) + return; + + wed_clr(dev, MTK_WED_CTRL, +@@ -730,7 +730,7 @@ mtk_wed_bus_init(struct mtk_wed_device * + static void + mtk_wed_set_wpdma(struct mtk_wed_device *dev) + { +- if (dev->hw->version == 1) { ++ if (mtk_wed_is_v1(dev->hw)) { + wed_w32(dev, MTK_WED_WPDMA_CFG_BASE, dev->wlan.wpdma_phys); + } else { + mtk_wed_bus_init(dev); +@@ -761,7 +761,7 @@ mtk_wed_hw_init_early(struct mtk_wed_dev + MTK_WED_WDMA_GLO_CFG_IDLE_DMAD_SUPPLY; + wed_m32(dev, MTK_WED_WDMA_GLO_CFG, mask, set); + +- if (dev->hw->version == 1) { ++ if (mtk_wed_is_v1(dev->hw)) { + u32 offset = dev->hw->index ? 0x04000400 : 0; + + wdma_set(dev, MTK_WDMA_GLO_CFG, +@@ -934,7 +934,7 @@ mtk_wed_hw_init(struct mtk_wed_device *d + + wed_w32(dev, MTK_WED_TX_BM_BUF_LEN, MTK_WED_PKT_SIZE); + +- if (dev->hw->version == 1) { ++ if (mtk_wed_is_v1(dev->hw)) { + wed_w32(dev, MTK_WED_TX_BM_TKID, + FIELD_PREP(MTK_WED_TX_BM_TKID_START, + dev->wlan.token_start) | +@@ -967,7 +967,7 @@ mtk_wed_hw_init(struct mtk_wed_device *d + + mtk_wed_reset(dev, MTK_WED_RESET_TX_BM); + +- if (dev->hw->version == 1) { ++ if (mtk_wed_is_v1(dev->hw)) { + wed_set(dev, MTK_WED_CTRL, + MTK_WED_CTRL_WED_TX_BM_EN | + MTK_WED_CTRL_WED_TX_FREE_AGENT_EN); +@@ -1217,7 +1217,7 @@ mtk_wed_reset_dma(struct mtk_wed_device + } + + dev->init_done = false; +- if (dev->hw->version == 1) ++ if (mtk_wed_is_v1(dev->hw)) + return; + + if (!busy) { +@@ -1343,7 +1343,7 @@ mtk_wed_configure_irq(struct mtk_wed_dev + MTK_WED_CTRL_WED_TX_BM_EN | + MTK_WED_CTRL_WED_TX_FREE_AGENT_EN); + +- if (dev->hw->version == 1) { ++ if (mtk_wed_is_v1(dev->hw)) { + wed_w32(dev, MTK_WED_PCIE_INT_TRIGGER, + MTK_WED_PCIE_INT_TRIGGER_STATUS); + +@@ -1416,7 +1416,7 @@ mtk_wed_dma_enable(struct mtk_wed_device + MTK_WDMA_GLO_CFG_RX_INFO1_PRERES | + MTK_WDMA_GLO_CFG_RX_INFO2_PRERES); + +- if (dev->hw->version == 1) { ++ if (mtk_wed_is_v1(dev->hw)) { + wdma_set(dev, MTK_WDMA_GLO_CFG, + MTK_WDMA_GLO_CFG_RX_INFO3_PRERES); + } else { +@@ -1465,7 +1465,7 @@ mtk_wed_start(struct mtk_wed_device *dev + + mtk_wed_set_ext_int(dev, true); + +- if (dev->hw->version == 1) { ++ if (mtk_wed_is_v1(dev->hw)) { + u32 val = dev->wlan.wpdma_phys | MTK_PCIE_MIRROR_MAP_EN | + FIELD_PREP(MTK_PCIE_MIRROR_MAP_WED_ID, + dev->hw->index); +@@ -1550,7 +1550,7 @@ mtk_wed_attach(struct mtk_wed_device *de + } + + mtk_wed_hw_init_early(dev); +- if (hw->version == 1) { ++ if (mtk_wed_is_v1(hw)) { + regmap_update_bits(hw->hifsys, HIFSYS_DMA_AG_MAP, + BIT(hw->index), 0); + } else { +@@ -1618,7 +1618,7 @@ static int + mtk_wed_txfree_ring_setup(struct mtk_wed_device *dev, void __iomem *regs) + { + struct mtk_wed_ring *ring = &dev->txfree_ring; +- int i, index = dev->hw->version == 1; ++ int i, index = mtk_wed_is_v1(dev->hw); + + /* + * For txfree event handling, the same DMA ring is shared between WED +@@ -1676,7 +1676,7 @@ mtk_wed_irq_get(struct mtk_wed_device *d + { + u32 val, ext_mask = MTK_WED_EXT_INT_STATUS_ERROR_MASK; + +- if (dev->hw->version == 1) ++ if (mtk_wed_is_v1(dev->hw)) + ext_mask |= MTK_WED_EXT_INT_STATUS_TX_DRV_R_RESP_ERR; + else + ext_mask |= MTK_WED_EXT_INT_STATUS_RX_FBUF_LO_TH | +@@ -1843,7 +1843,7 @@ mtk_wed_setup_tc(struct mtk_wed_device * + { + struct mtk_wed_hw *hw = wed->hw; + +- if (hw->version < 2) ++ if (mtk_wed_is_v1(hw)) + return -EOPNOTSUPP; + + switch (type) { +@@ -1917,9 +1917,9 @@ void mtk_wed_add_hw(struct device_node * + hw->wdma = wdma; + hw->index = index; + hw->irq = irq; +- hw->version = mtk_is_netsys_v1(eth) ? 1 : 2; ++ hw->version = eth->soc->version; + +- if (hw->version == 1) { ++ if (mtk_wed_is_v1(hw)) { + hw->mirror = syscon_regmap_lookup_by_phandle(eth_np, + "mediatek,pcie-mirror"); + hw->hifsys = syscon_regmap_lookup_by_phandle(eth_np, +--- a/drivers/net/ethernet/mediatek/mtk_wed.h ++++ b/drivers/net/ethernet/mediatek/mtk_wed.h +@@ -40,6 +40,16 @@ struct mtk_wdma_info { + }; + + #ifdef CONFIG_NET_MEDIATEK_SOC_WED ++static inline bool mtk_wed_is_v1(struct mtk_wed_hw *hw) ++{ ++ return hw->version == 1; ++} ++ ++static inline bool mtk_wed_is_v2(struct mtk_wed_hw *hw) ++{ ++ return hw->version == 2; ++} ++ + static inline void + wed_w32(struct mtk_wed_device *dev, u32 reg, u32 val) + { +--- a/drivers/net/ethernet/mediatek/mtk_wed_debugfs.c ++++ b/drivers/net/ethernet/mediatek/mtk_wed_debugfs.c +@@ -263,7 +263,7 @@ void mtk_wed_hw_add_debugfs(struct mtk_w + debugfs_create_u32("regidx", 0600, dir, &hw->debugfs_reg); + debugfs_create_file_unsafe("regval", 0600, dir, hw, &fops_regval); + debugfs_create_file_unsafe("txinfo", 0400, dir, hw, &wed_txinfo_fops); +- if (hw->version != 1) ++ if (!mtk_wed_is_v1(hw)) + debugfs_create_file_unsafe("rxinfo", 0400, dir, hw, + &wed_rxinfo_fops); + } +--- a/drivers/net/ethernet/mediatek/mtk_wed_mcu.c ++++ b/drivers/net/ethernet/mediatek/mtk_wed_mcu.c +@@ -207,7 +207,7 @@ int mtk_wed_mcu_msg_update(struct mtk_we + { + struct mtk_wed_wo *wo = dev->hw->wed_wo; + +- if (dev->hw->version == 1) ++ if (mtk_wed_is_v1(dev->hw)) + return 0; + + if (WARN_ON(!wo)) diff --git a/target/linux/generic/backport-6.1/752-07-v6.7-net-ethernet-mtk_wed-do-not-configure-rx-offload-if-.patch b/target/linux/generic/backport-6.1/752-07-v6.7-net-ethernet-mtk_wed-do-not-configure-rx-offload-if-.patch new file mode 100644 index 000000000..bc34aa33a --- /dev/null +++ b/target/linux/generic/backport-6.1/752-07-v6.7-net-ethernet-mtk_wed-do-not-configure-rx-offload-if-.patch @@ -0,0 +1,234 @@ +From: Lorenzo Bianconi +Date: Mon, 18 Sep 2023 12:29:06 +0200 +Subject: [PATCH] net: ethernet: mtk_wed: do not configure rx offload if not + supported + +Check if rx offload is supported running mtk_wed_get_rx_capa routine +before configuring it. This is a preliminary patch to introduce Wireless +Ethernet Dispatcher (WED) support for MT7988 SoC. + +Co-developed-by: Sujuan Chen +Signed-off-by: Sujuan Chen +Signed-off-by: Lorenzo Bianconi +Signed-off-by: Paolo Abeni +--- + +--- a/drivers/net/ethernet/mediatek/mtk_wed.c ++++ b/drivers/net/ethernet/mediatek/mtk_wed.c +@@ -605,7 +605,7 @@ mtk_wed_stop(struct mtk_wed_device *dev) + wdma_w32(dev, MTK_WDMA_INT_GRP2, 0); + wed_w32(dev, MTK_WED_WPDMA_INT_MASK, 0); + +- if (mtk_wed_is_v1(dev->hw)) ++ if (!mtk_wed_get_rx_capa(dev)) + return; + + wed_w32(dev, MTK_WED_EXT_INT_MASK1, 0); +@@ -732,16 +732,21 @@ mtk_wed_set_wpdma(struct mtk_wed_device + { + if (mtk_wed_is_v1(dev->hw)) { + wed_w32(dev, MTK_WED_WPDMA_CFG_BASE, dev->wlan.wpdma_phys); +- } else { +- mtk_wed_bus_init(dev); +- +- wed_w32(dev, MTK_WED_WPDMA_CFG_BASE, dev->wlan.wpdma_int); +- wed_w32(dev, MTK_WED_WPDMA_CFG_INT_MASK, dev->wlan.wpdma_mask); +- wed_w32(dev, MTK_WED_WPDMA_CFG_TX, dev->wlan.wpdma_tx); +- wed_w32(dev, MTK_WED_WPDMA_CFG_TX_FREE, dev->wlan.wpdma_txfree); +- wed_w32(dev, MTK_WED_WPDMA_RX_GLO_CFG, dev->wlan.wpdma_rx_glo); +- wed_w32(dev, MTK_WED_WPDMA_RX_RING, dev->wlan.wpdma_rx); ++ return; + } ++ ++ mtk_wed_bus_init(dev); ++ ++ wed_w32(dev, MTK_WED_WPDMA_CFG_BASE, dev->wlan.wpdma_int); ++ wed_w32(dev, MTK_WED_WPDMA_CFG_INT_MASK, dev->wlan.wpdma_mask); ++ wed_w32(dev, MTK_WED_WPDMA_CFG_TX, dev->wlan.wpdma_tx); ++ wed_w32(dev, MTK_WED_WPDMA_CFG_TX_FREE, dev->wlan.wpdma_txfree); ++ ++ if (!mtk_wed_get_rx_capa(dev)) ++ return; ++ ++ wed_w32(dev, MTK_WED_WPDMA_RX_GLO_CFG, dev->wlan.wpdma_rx_glo); ++ wed_w32(dev, MTK_WED_WPDMA_RX_RING, dev->wlan.wpdma_rx); + } + + static void +@@ -973,15 +978,17 @@ mtk_wed_hw_init(struct mtk_wed_device *d + MTK_WED_CTRL_WED_TX_FREE_AGENT_EN); + } else { + wed_clr(dev, MTK_WED_TX_TKID_CTRL, MTK_WED_TX_TKID_CTRL_PAUSE); +- /* rx hw init */ +- wed_w32(dev, MTK_WED_WPDMA_RX_D_RST_IDX, +- MTK_WED_WPDMA_RX_D_RST_CRX_IDX | +- MTK_WED_WPDMA_RX_D_RST_DRV_IDX); +- wed_w32(dev, MTK_WED_WPDMA_RX_D_RST_IDX, 0); +- +- mtk_wed_rx_buffer_hw_init(dev); +- mtk_wed_rro_hw_init(dev); +- mtk_wed_route_qm_hw_init(dev); ++ if (mtk_wed_get_rx_capa(dev)) { ++ /* rx hw init */ ++ wed_w32(dev, MTK_WED_WPDMA_RX_D_RST_IDX, ++ MTK_WED_WPDMA_RX_D_RST_CRX_IDX | ++ MTK_WED_WPDMA_RX_D_RST_DRV_IDX); ++ wed_w32(dev, MTK_WED_WPDMA_RX_D_RST_IDX, 0); ++ ++ mtk_wed_rx_buffer_hw_init(dev); ++ mtk_wed_rro_hw_init(dev); ++ mtk_wed_route_qm_hw_init(dev); ++ } + } + + wed_clr(dev, MTK_WED_TX_BM_CTRL, MTK_WED_TX_BM_CTRL_PAUSE); +@@ -1353,8 +1360,6 @@ mtk_wed_configure_irq(struct mtk_wed_dev + + wed_clr(dev, MTK_WED_WDMA_INT_CTRL, wdma_mask); + } else { +- wdma_mask |= FIELD_PREP(MTK_WDMA_INT_MASK_TX_DONE, +- GENMASK(1, 0)); + /* initail tx interrupt trigger */ + wed_w32(dev, MTK_WED_WPDMA_INT_CTRL_TX, + MTK_WED_WPDMA_INT_CTRL_TX0_DONE_EN | +@@ -1373,15 +1378,20 @@ mtk_wed_configure_irq(struct mtk_wed_dev + FIELD_PREP(MTK_WED_WPDMA_INT_CTRL_TX_FREE_DONE_TRIG, + dev->wlan.txfree_tbit)); + +- wed_w32(dev, MTK_WED_WPDMA_INT_CTRL_RX, +- MTK_WED_WPDMA_INT_CTRL_RX0_EN | +- MTK_WED_WPDMA_INT_CTRL_RX0_CLR | +- MTK_WED_WPDMA_INT_CTRL_RX1_EN | +- MTK_WED_WPDMA_INT_CTRL_RX1_CLR | +- FIELD_PREP(MTK_WED_WPDMA_INT_CTRL_RX0_DONE_TRIG, +- dev->wlan.rx_tbit[0]) | +- FIELD_PREP(MTK_WED_WPDMA_INT_CTRL_RX1_DONE_TRIG, +- dev->wlan.rx_tbit[1])); ++ if (mtk_wed_get_rx_capa(dev)) { ++ wed_w32(dev, MTK_WED_WPDMA_INT_CTRL_RX, ++ MTK_WED_WPDMA_INT_CTRL_RX0_EN | ++ MTK_WED_WPDMA_INT_CTRL_RX0_CLR | ++ MTK_WED_WPDMA_INT_CTRL_RX1_EN | ++ MTK_WED_WPDMA_INT_CTRL_RX1_CLR | ++ FIELD_PREP(MTK_WED_WPDMA_INT_CTRL_RX0_DONE_TRIG, ++ dev->wlan.rx_tbit[0]) | ++ FIELD_PREP(MTK_WED_WPDMA_INT_CTRL_RX1_DONE_TRIG, ++ dev->wlan.rx_tbit[1])); ++ ++ wdma_mask |= FIELD_PREP(MTK_WDMA_INT_MASK_TX_DONE, ++ GENMASK(1, 0)); ++ } + + wed_w32(dev, MTK_WED_WDMA_INT_CLR, wdma_mask); + wed_set(dev, MTK_WED_WDMA_INT_CTRL, +@@ -1400,6 +1410,8 @@ mtk_wed_configure_irq(struct mtk_wed_dev + static void + mtk_wed_dma_enable(struct mtk_wed_device *dev) + { ++ int i; ++ + wed_set(dev, MTK_WED_WPDMA_INT_CTRL, MTK_WED_WPDMA_INT_CTRL_SUBRT_ADV); + + wed_set(dev, MTK_WED_GLO_CFG, +@@ -1419,33 +1431,33 @@ mtk_wed_dma_enable(struct mtk_wed_device + if (mtk_wed_is_v1(dev->hw)) { + wdma_set(dev, MTK_WDMA_GLO_CFG, + MTK_WDMA_GLO_CFG_RX_INFO3_PRERES); +- } else { +- int i; ++ return; ++ } + +- wed_set(dev, MTK_WED_WPDMA_CTRL, +- MTK_WED_WPDMA_CTRL_SDL1_FIXED); ++ wed_set(dev, MTK_WED_WPDMA_CTRL, ++ MTK_WED_WPDMA_CTRL_SDL1_FIXED); ++ wed_set(dev, MTK_WED_WPDMA_GLO_CFG, ++ MTK_WED_WPDMA_GLO_CFG_RX_DRV_R0_PKT_PROC | ++ MTK_WED_WPDMA_GLO_CFG_RX_DRV_R0_CRX_SYNC); ++ wed_clr(dev, MTK_WED_WPDMA_GLO_CFG, ++ MTK_WED_WPDMA_GLO_CFG_TX_TKID_KEEP | ++ MTK_WED_WPDMA_GLO_CFG_TX_DMAD_DW3_PREV); + +- wed_set(dev, MTK_WED_WDMA_GLO_CFG, +- MTK_WED_WDMA_GLO_CFG_TX_DRV_EN | +- MTK_WED_WDMA_GLO_CFG_TX_DDONE_CHK); ++ if (!mtk_wed_get_rx_capa(dev)) ++ return; + +- wed_set(dev, MTK_WED_WPDMA_GLO_CFG, +- MTK_WED_WPDMA_GLO_CFG_RX_DRV_R0_PKT_PROC | +- MTK_WED_WPDMA_GLO_CFG_RX_DRV_R0_CRX_SYNC); +- +- wed_clr(dev, MTK_WED_WPDMA_GLO_CFG, +- MTK_WED_WPDMA_GLO_CFG_TX_TKID_KEEP | +- MTK_WED_WPDMA_GLO_CFG_TX_DMAD_DW3_PREV); ++ wed_set(dev, MTK_WED_WDMA_GLO_CFG, ++ MTK_WED_WDMA_GLO_CFG_TX_DRV_EN | ++ MTK_WED_WDMA_GLO_CFG_TX_DDONE_CHK); + +- wed_set(dev, MTK_WED_WPDMA_RX_D_GLO_CFG, +- MTK_WED_WPDMA_RX_D_RX_DRV_EN | +- FIELD_PREP(MTK_WED_WPDMA_RX_D_RXD_READ_LEN, 0x18) | +- FIELD_PREP(MTK_WED_WPDMA_RX_D_INIT_PHASE_RXEN_SEL, +- 0x2)); ++ wed_set(dev, MTK_WED_WPDMA_RX_D_GLO_CFG, ++ MTK_WED_WPDMA_RX_D_RX_DRV_EN | ++ FIELD_PREP(MTK_WED_WPDMA_RX_D_RXD_READ_LEN, 0x18) | ++ FIELD_PREP(MTK_WED_WPDMA_RX_D_INIT_PHASE_RXEN_SEL, ++ 0x2)); + +- for (i = 0; i < MTK_WED_RX_QUEUES; i++) +- mtk_wed_check_wfdma_rx_fill(dev, i); +- } ++ for (i = 0; i < MTK_WED_RX_QUEUES; i++) ++ mtk_wed_check_wfdma_rx_fill(dev, i); + } + + static void +@@ -1472,7 +1484,7 @@ mtk_wed_start(struct mtk_wed_device *dev + + val |= BIT(0) | (BIT(1) * !!dev->hw->index); + regmap_write(dev->hw->mirror, dev->hw->index * 4, val); +- } else { ++ } else if (mtk_wed_get_rx_capa(dev)) { + /* driver set mid ready and only once */ + wed_w32(dev, MTK_WED_EXT_INT_MASK1, + MTK_WED_EXT_INT_STATUS_WPDMA_MID_RDY); +@@ -1484,7 +1496,6 @@ mtk_wed_start(struct mtk_wed_device *dev + + if (mtk_wed_rro_cfg(dev)) + return; +- + } + + mtk_wed_set_512_support(dev, dev->wlan.wcid_512); +@@ -1550,13 +1561,14 @@ mtk_wed_attach(struct mtk_wed_device *de + } + + mtk_wed_hw_init_early(dev); +- if (mtk_wed_is_v1(hw)) { ++ if (mtk_wed_is_v1(hw)) + regmap_update_bits(hw->hifsys, HIFSYS_DMA_AG_MAP, + BIT(hw->index), 0); +- } else { ++ else + dev->rev_id = wed_r32(dev, MTK_WED_REV_ID); ++ ++ if (mtk_wed_get_rx_capa(dev)) + ret = mtk_wed_wo_init(hw); +- } + out: + if (ret) { + dev_err(dev->hw->dev, "failed to attach wed device\n"); +--- a/drivers/net/ethernet/mediatek/mtk_wed_mcu.c ++++ b/drivers/net/ethernet/mediatek/mtk_wed_mcu.c +@@ -207,7 +207,7 @@ int mtk_wed_mcu_msg_update(struct mtk_we + { + struct mtk_wed_wo *wo = dev->hw->wed_wo; + +- if (mtk_wed_is_v1(dev->hw)) ++ if (!mtk_wed_get_rx_capa(dev)) + return 0; + + if (WARN_ON(!wo)) diff --git a/target/linux/generic/backport-6.1/752-08-v6.7-net-ethernet-mtk_wed-rename-mtk_rxbm_desc-in-mtk_wed.patch b/target/linux/generic/backport-6.1/752-08-v6.7-net-ethernet-mtk_wed-rename-mtk_rxbm_desc-in-mtk_wed.patch new file mode 100644 index 000000000..d83434fb2 --- /dev/null +++ b/target/linux/generic/backport-6.1/752-08-v6.7-net-ethernet-mtk_wed-rename-mtk_rxbm_desc-in-mtk_wed.patch @@ -0,0 +1,52 @@ +From: Lorenzo Bianconi +Date: Mon, 18 Sep 2023 12:29:07 +0200 +Subject: [PATCH] net: ethernet: mtk_wed: rename mtk_rxbm_desc in + mtk_wed_bm_desc + +Rename mtk_rxbm_desc structure in mtk_wed_bm_desc since it will be used +even on tx side by MT7988 SoC. + +Signed-off-by: Lorenzo Bianconi +Signed-off-by: Paolo Abeni +--- + +--- a/drivers/net/ethernet/mediatek/mtk_wed.c ++++ b/drivers/net/ethernet/mediatek/mtk_wed.c +@@ -421,7 +421,7 @@ free_pagelist: + static int + mtk_wed_rx_buffer_alloc(struct mtk_wed_device *dev) + { +- struct mtk_rxbm_desc *desc; ++ struct mtk_wed_bm_desc *desc; + dma_addr_t desc_phys; + + dev->rx_buf_ring.size = dev->wlan.rx_nbuf; +@@ -441,7 +441,7 @@ mtk_wed_rx_buffer_alloc(struct mtk_wed_d + static void + mtk_wed_free_rx_buffer(struct mtk_wed_device *dev) + { +- struct mtk_rxbm_desc *desc = dev->rx_buf_ring.desc; ++ struct mtk_wed_bm_desc *desc = dev->rx_buf_ring.desc; + + if (!desc) + return; +--- a/include/linux/soc/mediatek/mtk_wed.h ++++ b/include/linux/soc/mediatek/mtk_wed.h +@@ -45,7 +45,7 @@ enum mtk_wed_wo_cmd { + MTK_WED_WO_CMD_WED_END + }; + +-struct mtk_rxbm_desc { ++struct mtk_wed_bm_desc { + __le32 buf0; + __le32 token; + } __packed __aligned(4); +@@ -105,7 +105,7 @@ struct mtk_wed_device { + struct { + int size; + struct page_frag_cache rx_page; +- struct mtk_rxbm_desc *desc; ++ struct mtk_wed_bm_desc *desc; + dma_addr_t desc_phys; + } rx_buf_ring; + diff --git a/target/linux/generic/backport-6.1/752-09-v6.7-net-ethernet-mtk_wed-introduce-mtk_wed_buf-structure.patch b/target/linux/generic/backport-6.1/752-09-v6.7-net-ethernet-mtk_wed-introduce-mtk_wed_buf-structure.patch new file mode 100644 index 000000000..8000a8759 --- /dev/null +++ b/target/linux/generic/backport-6.1/752-09-v6.7-net-ethernet-mtk_wed-introduce-mtk_wed_buf-structure.patch @@ -0,0 +1,87 @@ +From: Lorenzo Bianconi +Date: Mon, 18 Sep 2023 12:29:08 +0200 +Subject: [PATCH] net: ethernet: mtk_wed: introduce mtk_wed_buf structure + +Introduce mtk_wed_buf structure to store both virtual and physical +addresses allocated in mtk_wed_tx_buffer_alloc() routine. This is a +preliminary patch to add WED support for MT7988 SoC since it relies on a +different dma descriptor layout not storing page dma addresses. + +Co-developed-by: Sujuan Chen +Signed-off-by: Sujuan Chen +Signed-off-by: Lorenzo Bianconi +Signed-off-by: Paolo Abeni +--- + +--- a/drivers/net/ethernet/mediatek/mtk_wed.c ++++ b/drivers/net/ethernet/mediatek/mtk_wed.c +@@ -299,9 +299,9 @@ out: + static int + mtk_wed_tx_buffer_alloc(struct mtk_wed_device *dev) + { ++ struct mtk_wed_buf *page_list; + struct mtk_wdma_desc *desc; + dma_addr_t desc_phys; +- void **page_list; + int token = dev->wlan.token_start; + int ring_size; + int n_pages; +@@ -342,7 +342,8 @@ mtk_wed_tx_buffer_alloc(struct mtk_wed_d + return -ENOMEM; + } + +- page_list[page_idx++] = page; ++ page_list[page_idx].p = page; ++ page_list[page_idx++].phy_addr = page_phys; + dma_sync_single_for_cpu(dev->hw->dev, page_phys, PAGE_SIZE, + DMA_BIDIRECTIONAL); + +@@ -386,8 +387,8 @@ mtk_wed_tx_buffer_alloc(struct mtk_wed_d + static void + mtk_wed_free_tx_buffer(struct mtk_wed_device *dev) + { ++ struct mtk_wed_buf *page_list = dev->tx_buf_ring.pages; + struct mtk_wdma_desc *desc = dev->tx_buf_ring.desc; +- void **page_list = dev->tx_buf_ring.pages; + int page_idx; + int i; + +@@ -399,13 +400,12 @@ mtk_wed_free_tx_buffer(struct mtk_wed_de + + for (i = 0, page_idx = 0; i < dev->tx_buf_ring.size; + i += MTK_WED_BUF_PER_PAGE) { +- void *page = page_list[page_idx++]; +- dma_addr_t buf_addr; ++ dma_addr_t buf_addr = page_list[page_idx].phy_addr; ++ void *page = page_list[page_idx++].p; + + if (!page) + break; + +- buf_addr = le32_to_cpu(desc[i].buf0); + dma_unmap_page(dev->hw->dev, buf_addr, PAGE_SIZE, + DMA_BIDIRECTIONAL); + __free_page(page); +--- a/include/linux/soc/mediatek/mtk_wed.h ++++ b/include/linux/soc/mediatek/mtk_wed.h +@@ -76,6 +76,11 @@ struct mtk_wed_wo_rx_stats { + __le32 rx_drop_cnt; + }; + ++struct mtk_wed_buf { ++ void *p; ++ dma_addr_t phy_addr; ++}; ++ + struct mtk_wed_device { + #ifdef CONFIG_NET_MEDIATEK_SOC_WED + const struct mtk_wed_ops *ops; +@@ -97,7 +102,7 @@ struct mtk_wed_device { + + struct { + int size; +- void **pages; ++ struct mtk_wed_buf *pages; + struct mtk_wdma_desc *desc; + dma_addr_t desc_phys; + } tx_buf_ring; diff --git a/target/linux/generic/backport-6.1/752-10-v6.7-net-ethernet-mtk_wed-move-mem_region-array-out-of-mt.patch b/target/linux/generic/backport-6.1/752-10-v6.7-net-ethernet-mtk_wed-move-mem_region-array-out-of-mt.patch new file mode 100644 index 000000000..98d782b1d --- /dev/null +++ b/target/linux/generic/backport-6.1/752-10-v6.7-net-ethernet-mtk_wed-move-mem_region-array-out-of-mt.patch @@ -0,0 +1,88 @@ +From: Lorenzo Bianconi +Date: Mon, 18 Sep 2023 12:29:09 +0200 +Subject: [PATCH] net: ethernet: mtk_wed: move mem_region array out of + mtk_wed_mcu_load_firmware + +Remove mtk_wed_wo_memory_region boot structure in mtk_wed_wo. +This is a preliminary patch to introduce WED support for MT7988 SoC. + +Signed-off-by: Lorenzo Bianconi +Signed-off-by: Paolo Abeni +--- + +--- a/drivers/net/ethernet/mediatek/mtk_wed_mcu.c ++++ b/drivers/net/ethernet/mediatek/mtk_wed_mcu.c +@@ -16,14 +16,30 @@ + #include "mtk_wed_wo.h" + #include "mtk_wed.h" + ++static struct mtk_wed_wo_memory_region mem_region[] = { ++ [MTK_WED_WO_REGION_EMI] = { ++ .name = "wo-emi", ++ }, ++ [MTK_WED_WO_REGION_ILM] = { ++ .name = "wo-ilm", ++ }, ++ [MTK_WED_WO_REGION_DATA] = { ++ .name = "wo-data", ++ .shared = true, ++ }, ++ [MTK_WED_WO_REGION_BOOT] = { ++ .name = "wo-boot", ++ }, ++}; ++ + static u32 wo_r32(struct mtk_wed_wo *wo, u32 reg) + { +- return readl(wo->boot.addr + reg); ++ return readl(mem_region[MTK_WED_WO_REGION_BOOT].addr + reg); + } + + static void wo_w32(struct mtk_wed_wo *wo, u32 reg, u32 val) + { +- writel(val, wo->boot.addr + reg); ++ writel(val, mem_region[MTK_WED_WO_REGION_BOOT].addr + reg); + } + + static struct sk_buff * +@@ -294,18 +310,6 @@ next: + static int + mtk_wed_mcu_load_firmware(struct mtk_wed_wo *wo) + { +- static struct mtk_wed_wo_memory_region mem_region[] = { +- [MTK_WED_WO_REGION_EMI] = { +- .name = "wo-emi", +- }, +- [MTK_WED_WO_REGION_ILM] = { +- .name = "wo-ilm", +- }, +- [MTK_WED_WO_REGION_DATA] = { +- .name = "wo-data", +- .shared = true, +- }, +- }; + const struct mtk_wed_fw_trailer *trailer; + const struct firmware *fw; + const char *fw_name; +@@ -319,11 +323,6 @@ mtk_wed_mcu_load_firmware(struct mtk_wed + return ret; + } + +- wo->boot.name = "wo-boot"; +- ret = mtk_wed_get_memory_region(wo, &wo->boot); +- if (ret) +- return ret; +- + /* set dummy cr */ + wed_w32(wo->hw->wed_dev, MTK_WED_SCR0 + 4 * MTK_WED_DUMMY_CR_FWDL, + wo->hw->index + 1); +--- a/drivers/net/ethernet/mediatek/mtk_wed_wo.h ++++ b/drivers/net/ethernet/mediatek/mtk_wed_wo.h +@@ -228,7 +228,6 @@ struct mtk_wed_wo_queue { + + struct mtk_wed_wo { + struct mtk_wed_hw *hw; +- struct mtk_wed_wo_memory_region boot; + + struct mtk_wed_wo_queue q_tx; + struct mtk_wed_wo_queue q_rx; diff --git a/target/linux/generic/backport-6.1/752-11-v6.7-net-ethernet-mtk_wed-make-memory-region-optional.patch b/target/linux/generic/backport-6.1/752-11-v6.7-net-ethernet-mtk_wed-make-memory-region-optional.patch new file mode 100644 index 000000000..48b0d0204 --- /dev/null +++ b/target/linux/generic/backport-6.1/752-11-v6.7-net-ethernet-mtk_wed-make-memory-region-optional.patch @@ -0,0 +1,71 @@ +From: Lorenzo Bianconi +Date: Mon, 18 Sep 2023 12:29:10 +0200 +Subject: [PATCH] net: ethernet: mtk_wed: make memory region optional + +Make mtk_wed_wo_memory_region optionals. +This is a preliminary patch to introduce Wireless Ethernet Dispatcher +support for MT7988 SoC since MT7988 WED fw image will have a different +layout. + +Signed-off-by: Lorenzo Bianconi +Signed-off-by: Paolo Abeni +--- + +--- a/drivers/net/ethernet/mediatek/mtk_wed_mcu.c ++++ b/drivers/net/ethernet/mediatek/mtk_wed_mcu.c +@@ -234,19 +234,13 @@ int mtk_wed_mcu_msg_update(struct mtk_we + } + + static int +-mtk_wed_get_memory_region(struct mtk_wed_wo *wo, ++mtk_wed_get_memory_region(struct mtk_wed_hw *hw, int index, + struct mtk_wed_wo_memory_region *region) + { + struct reserved_mem *rmem; + struct device_node *np; +- int index; + +- index = of_property_match_string(wo->hw->node, "memory-region-names", +- region->name); +- if (index < 0) +- return index; +- +- np = of_parse_phandle(wo->hw->node, "memory-region", index); ++ np = of_parse_phandle(hw->node, "memory-region", index); + if (!np) + return -ENODEV; + +@@ -258,7 +252,7 @@ mtk_wed_get_memory_region(struct mtk_wed + + region->phy_addr = rmem->base; + region->size = rmem->size; +- region->addr = devm_ioremap(wo->hw->dev, region->phy_addr, region->size); ++ region->addr = devm_ioremap(hw->dev, region->phy_addr, region->size); + + return !region->addr ? -EINVAL : 0; + } +@@ -271,6 +265,9 @@ mtk_wed_mcu_run_firmware(struct mtk_wed_ + const struct mtk_wed_fw_trailer *trailer; + const struct mtk_wed_fw_region *fw_region; + ++ if (!region->phy_addr || !region->size) ++ return 0; ++ + trailer_ptr = fw->data + fw->size - sizeof(*trailer); + trailer = (const struct mtk_wed_fw_trailer *)trailer_ptr; + region_ptr = trailer_ptr - trailer->num_region * sizeof(*fw_region); +@@ -318,7 +315,13 @@ mtk_wed_mcu_load_firmware(struct mtk_wed + + /* load firmware region metadata */ + for (i = 0; i < ARRAY_SIZE(mem_region); i++) { +- ret = mtk_wed_get_memory_region(wo, &mem_region[i]); ++ int index = of_property_match_string(wo->hw->node, ++ "memory-region-names", ++ mem_region[i].name); ++ if (index < 0) ++ continue; ++ ++ ret = mtk_wed_get_memory_region(wo->hw, index, &mem_region[i]); + if (ret) + return ret; + } diff --git a/target/linux/generic/backport-6.1/752-13-v6.7-net-ethernet-mtk_wed-add-mtk_wed_soc_data-structure.patch b/target/linux/generic/backport-6.1/752-13-v6.7-net-ethernet-mtk_wed-add-mtk_wed_soc_data-structure.patch new file mode 100644 index 000000000..c43114fb5 --- /dev/null +++ b/target/linux/generic/backport-6.1/752-13-v6.7-net-ethernet-mtk_wed-add-mtk_wed_soc_data-structure.patch @@ -0,0 +1,217 @@ +From: Lorenzo Bianconi +Date: Mon, 18 Sep 2023 12:29:12 +0200 +Subject: [PATCH] net: ethernet: mtk_wed: add mtk_wed_soc_data structure + +Introduce mtk_wed_soc_data utility structure to contain per-SoC +definitions. + +Signed-off-by: Lorenzo Bianconi +Signed-off-by: Paolo Abeni +--- + +--- a/drivers/net/ethernet/mediatek/mtk_wed.c ++++ b/drivers/net/ethernet/mediatek/mtk_wed.c +@@ -48,6 +48,26 @@ struct mtk_wed_flow_block_priv { + struct net_device *dev; + }; + ++static const struct mtk_wed_soc_data mt7622_data = { ++ .regmap = { ++ .tx_bm_tkid = 0x088, ++ .wpdma_rx_ring0 = 0x770, ++ .reset_idx_tx_mask = GENMASK(3, 0), ++ .reset_idx_rx_mask = GENMASK(17, 16), ++ }, ++ .wdma_desc_size = sizeof(struct mtk_wdma_desc), ++}; ++ ++static const struct mtk_wed_soc_data mt7986_data = { ++ .regmap = { ++ .tx_bm_tkid = 0x0c8, ++ .wpdma_rx_ring0 = 0x770, ++ .reset_idx_tx_mask = GENMASK(1, 0), ++ .reset_idx_rx_mask = GENMASK(7, 6), ++ }, ++ .wdma_desc_size = 2 * sizeof(struct mtk_wdma_desc), ++}; ++ + static void + wed_m32(struct mtk_wed_device *dev, u32 reg, u32 mask, u32 val) + { +@@ -746,7 +766,7 @@ mtk_wed_set_wpdma(struct mtk_wed_device + return; + + wed_w32(dev, MTK_WED_WPDMA_RX_GLO_CFG, dev->wlan.wpdma_rx_glo); +- wed_w32(dev, MTK_WED_WPDMA_RX_RING, dev->wlan.wpdma_rx); ++ wed_w32(dev, dev->hw->soc->regmap.wpdma_rx_ring0, dev->wlan.wpdma_rx); + } + + static void +@@ -940,22 +960,10 @@ mtk_wed_hw_init(struct mtk_wed_device *d + wed_w32(dev, MTK_WED_TX_BM_BUF_LEN, MTK_WED_PKT_SIZE); + + if (mtk_wed_is_v1(dev->hw)) { +- wed_w32(dev, MTK_WED_TX_BM_TKID, +- FIELD_PREP(MTK_WED_TX_BM_TKID_START, +- dev->wlan.token_start) | +- FIELD_PREP(MTK_WED_TX_BM_TKID_END, +- dev->wlan.token_start + +- dev->wlan.nbuf - 1)); + wed_w32(dev, MTK_WED_TX_BM_DYN_THR, + FIELD_PREP(MTK_WED_TX_BM_DYN_THR_LO, 1) | + MTK_WED_TX_BM_DYN_THR_HI); + } else { +- wed_w32(dev, MTK_WED_TX_BM_TKID_V2, +- FIELD_PREP(MTK_WED_TX_BM_TKID_START, +- dev->wlan.token_start) | +- FIELD_PREP(MTK_WED_TX_BM_TKID_END, +- dev->wlan.token_start + +- dev->wlan.nbuf - 1)); + wed_w32(dev, MTK_WED_TX_BM_DYN_THR, + FIELD_PREP(MTK_WED_TX_BM_DYN_THR_LO_V2, 0) | + MTK_WED_TX_BM_DYN_THR_HI_V2); +@@ -970,6 +978,11 @@ mtk_wed_hw_init(struct mtk_wed_device *d + MTK_WED_TX_TKID_DYN_THR_HI); + } + ++ wed_w32(dev, dev->hw->soc->regmap.tx_bm_tkid, ++ FIELD_PREP(MTK_WED_TX_BM_TKID_START, dev->wlan.token_start) | ++ FIELD_PREP(MTK_WED_TX_BM_TKID_END, ++ dev->wlan.token_start + dev->wlan.nbuf - 1)); ++ + mtk_wed_reset(dev, MTK_WED_RESET_TX_BM); + + if (mtk_wed_is_v1(dev->hw)) { +@@ -1104,13 +1117,8 @@ mtk_wed_rx_reset(struct mtk_wed_device * + if (ret) { + mtk_wed_reset(dev, MTK_WED_RESET_WED_RX_DMA); + } else { +- struct mtk_eth *eth = dev->hw->eth; +- +- if (mtk_is_netsys_v2_or_greater(eth)) +- wed_set(dev, MTK_WED_RESET_IDX, +- MTK_WED_RESET_IDX_RX_V2); +- else +- wed_set(dev, MTK_WED_RESET_IDX, MTK_WED_RESET_IDX_RX); ++ wed_set(dev, MTK_WED_RESET_IDX, ++ dev->hw->soc->regmap.reset_idx_rx_mask); + wed_w32(dev, MTK_WED_RESET_IDX, 0); + } + +@@ -1163,7 +1171,8 @@ mtk_wed_reset_dma(struct mtk_wed_device + if (busy) { + mtk_wed_reset(dev, MTK_WED_RESET_WED_TX_DMA); + } else { +- wed_w32(dev, MTK_WED_RESET_IDX, MTK_WED_RESET_IDX_TX); ++ wed_w32(dev, MTK_WED_RESET_IDX, ++ dev->hw->soc->regmap.reset_idx_tx_mask); + wed_w32(dev, MTK_WED_RESET_IDX, 0); + } + +@@ -1255,7 +1264,6 @@ static int + mtk_wed_wdma_rx_ring_setup(struct mtk_wed_device *dev, int idx, int size, + bool reset) + { +- u32 desc_size = sizeof(struct mtk_wdma_desc) * dev->hw->version; + struct mtk_wed_ring *wdma; + + if (idx >= ARRAY_SIZE(dev->rx_wdma)) +@@ -1263,7 +1271,7 @@ mtk_wed_wdma_rx_ring_setup(struct mtk_we + + wdma = &dev->rx_wdma[idx]; + if (!reset && mtk_wed_ring_alloc(dev, wdma, MTK_WED_WDMA_RING_SIZE, +- desc_size, true)) ++ dev->hw->soc->wdma_desc_size, true)) + return -ENOMEM; + + wdma_w32(dev, MTK_WDMA_RING_RX(idx) + MTK_WED_RING_OFS_BASE, +@@ -1284,7 +1292,6 @@ static int + mtk_wed_wdma_tx_ring_setup(struct mtk_wed_device *dev, int idx, int size, + bool reset) + { +- u32 desc_size = sizeof(struct mtk_wdma_desc) * dev->hw->version; + struct mtk_wed_ring *wdma; + + if (idx >= ARRAY_SIZE(dev->tx_wdma)) +@@ -1292,7 +1299,7 @@ mtk_wed_wdma_tx_ring_setup(struct mtk_we + + wdma = &dev->tx_wdma[idx]; + if (!reset && mtk_wed_ring_alloc(dev, wdma, MTK_WED_WDMA_RING_SIZE, +- desc_size, true)) ++ dev->hw->soc->wdma_desc_size, true)) + return -ENOMEM; + + wdma_w32(dev, MTK_WDMA_RING_TX(idx) + MTK_WED_RING_OFS_BASE, +@@ -1931,7 +1938,12 @@ void mtk_wed_add_hw(struct device_node * + hw->irq = irq; + hw->version = eth->soc->version; + +- if (mtk_wed_is_v1(hw)) { ++ switch (hw->version) { ++ case 2: ++ hw->soc = &mt7986_data; ++ break; ++ default: ++ case 1: + hw->mirror = syscon_regmap_lookup_by_phandle(eth_np, + "mediatek,pcie-mirror"); + hw->hifsys = syscon_regmap_lookup_by_phandle(eth_np, +@@ -1945,6 +1957,8 @@ void mtk_wed_add_hw(struct device_node * + regmap_write(hw->mirror, 0, 0); + regmap_write(hw->mirror, 4, 0); + } ++ hw->soc = &mt7622_data; ++ break; + } + + mtk_wed_hw_add_debugfs(hw); +--- a/drivers/net/ethernet/mediatek/mtk_wed.h ++++ b/drivers/net/ethernet/mediatek/mtk_wed.h +@@ -12,7 +12,18 @@ + struct mtk_eth; + struct mtk_wed_wo; + ++struct mtk_wed_soc_data { ++ struct { ++ u32 tx_bm_tkid; ++ u32 wpdma_rx_ring0; ++ u32 reset_idx_tx_mask; ++ u32 reset_idx_rx_mask; ++ } regmap; ++ u32 wdma_desc_size; ++}; ++ + struct mtk_wed_hw { ++ const struct mtk_wed_soc_data *soc; + struct device_node *node; + struct mtk_eth *eth; + struct regmap *regs; +--- a/drivers/net/ethernet/mediatek/mtk_wed_regs.h ++++ b/drivers/net/ethernet/mediatek/mtk_wed_regs.h +@@ -100,8 +100,6 @@ struct mtk_wdma_desc { + + #define MTK_WED_TX_BM_BASE 0x084 + +-#define MTK_WED_TX_BM_TKID 0x088 +-#define MTK_WED_TX_BM_TKID_V2 0x0c8 + #define MTK_WED_TX_BM_TKID_START GENMASK(15, 0) + #define MTK_WED_TX_BM_TKID_END GENMASK(31, 16) + +@@ -160,9 +158,6 @@ struct mtk_wdma_desc { + #define MTK_WED_GLO_CFG_RX_2B_OFFSET BIT(31) + + #define MTK_WED_RESET_IDX 0x20c +-#define MTK_WED_RESET_IDX_TX GENMASK(3, 0) +-#define MTK_WED_RESET_IDX_RX GENMASK(17, 16) +-#define MTK_WED_RESET_IDX_RX_V2 GENMASK(7, 6) + #define MTK_WED_RESET_WPDMA_IDX_RX GENMASK(31, 30) + + #define MTK_WED_TX_MIB(_n) (0x2a0 + (_n) * 4) +@@ -286,7 +281,6 @@ struct mtk_wdma_desc { + #define MTK_WED_WPDMA_RX_D_RST_DRV_IDX GENMASK(25, 24) + + #define MTK_WED_WPDMA_RX_GLO_CFG 0x76c +-#define MTK_WED_WPDMA_RX_RING 0x770 + + #define MTK_WED_WPDMA_RX_D_MIB(_n) (0x774 + (_n) * 4) + #define MTK_WED_WPDMA_RX_D_PROCESSED_MIB(_n) (0x784 + (_n) * 4) diff --git a/target/linux/generic/backport-6.1/752-14-v6.7-net-ethernet-mtk_wed-introduce-WED-support-for-MT798.patch b/target/linux/generic/backport-6.1/752-14-v6.7-net-ethernet-mtk_wed-introduce-WED-support-for-MT798.patch new file mode 100644 index 000000000..f874899c5 --- /dev/null +++ b/target/linux/generic/backport-6.1/752-14-v6.7-net-ethernet-mtk_wed-introduce-WED-support-for-MT798.patch @@ -0,0 +1,1280 @@ +From: Sujuan Chen +Date: Mon, 18 Sep 2023 12:29:13 +0200 +Subject: [PATCH] net: ethernet: mtk_wed: introduce WED support for MT7988 + +Similar to MT7986 and MT7622, enable Wireless Ethernet Ditpatcher for +MT7988 in order to offload traffic forwarded from LAN/WLAN to WLAN/LAN + +Co-developed-by: Lorenzo Bianconi +Signed-off-by: Lorenzo Bianconi +Signed-off-by: Sujuan Chen +Signed-off-by: Paolo Abeni +--- + +--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c ++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c +@@ -195,6 +195,7 @@ static const struct mtk_reg_map mt7988_r + .wdma_base = { + [0] = 0x4800, + [1] = 0x4c00, ++ [2] = 0x5000, + }, + .pse_iq_sta = 0x0180, + .pse_oq_sta = 0x01a0, +--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h ++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h +@@ -1132,7 +1132,7 @@ struct mtk_reg_map { + u32 gdm1_cnt; + u32 gdma_to_ppe; + u32 ppe_base; +- u32 wdma_base[2]; ++ u32 wdma_base[3]; + u32 pse_iq_sta; + u32 pse_oq_sta; + }; +--- a/drivers/net/ethernet/mediatek/mtk_ppe_offload.c ++++ b/drivers/net/ethernet/mediatek/mtk_ppe_offload.c +@@ -201,6 +201,9 @@ mtk_flow_set_output_device(struct mtk_et + case 1: + pse_port = PSE_WDMA1_PORT; + break; ++ case 2: ++ pse_port = PSE_WDMA2_PORT; ++ break; + default: + return -EINVAL; + } +--- a/drivers/net/ethernet/mediatek/mtk_wed.c ++++ b/drivers/net/ethernet/mediatek/mtk_wed.c +@@ -16,17 +16,19 @@ + #include + #include + #include "mtk_eth_soc.h" +-#include "mtk_wed_regs.h" + #include "mtk_wed.h" + #include "mtk_ppe.h" + #include "mtk_wed_wo.h" + + #define MTK_PCIE_BASE(n) (0x1a143000 + (n) * 0x2000) + +-#define MTK_WED_PKT_SIZE 1900 ++#define MTK_WED_PKT_SIZE 1920 + #define MTK_WED_BUF_SIZE 2048 ++#define MTK_WED_PAGE_BUF_SIZE 128 + #define MTK_WED_BUF_PER_PAGE (PAGE_SIZE / 2048) ++#define MTK_WED_RX_PAGE_BUF_PER_PAGE (PAGE_SIZE / 128) + #define MTK_WED_RX_RING_SIZE 1536 ++#define MTK_WED_RX_PG_BM_CNT 8192 + + #define MTK_WED_TX_RING_SIZE 2048 + #define MTK_WED_WDMA_RING_SIZE 1024 +@@ -40,7 +42,10 @@ + #define MTK_WED_RRO_QUE_CNT 8192 + #define MTK_WED_MIOD_ENTRY_CNT 128 + +-static struct mtk_wed_hw *hw_list[2]; ++#define MTK_WED_TX_BM_DMA_SIZE 65536 ++#define MTK_WED_TX_BM_PKT_CNT 32768 ++ ++static struct mtk_wed_hw *hw_list[3]; + static DEFINE_MUTEX(hw_lock); + + struct mtk_wed_flow_block_priv { +@@ -55,6 +60,7 @@ static const struct mtk_wed_soc_data mt7 + .reset_idx_tx_mask = GENMASK(3, 0), + .reset_idx_rx_mask = GENMASK(17, 16), + }, ++ .tx_ring_desc_size = sizeof(struct mtk_wdma_desc), + .wdma_desc_size = sizeof(struct mtk_wdma_desc), + }; + +@@ -65,6 +71,18 @@ static const struct mtk_wed_soc_data mt7 + .reset_idx_tx_mask = GENMASK(1, 0), + .reset_idx_rx_mask = GENMASK(7, 6), + }, ++ .tx_ring_desc_size = sizeof(struct mtk_wdma_desc), ++ .wdma_desc_size = 2 * sizeof(struct mtk_wdma_desc), ++}; ++ ++static const struct mtk_wed_soc_data mt7988_data = { ++ .regmap = { ++ .tx_bm_tkid = 0x0c8, ++ .wpdma_rx_ring0 = 0x7d0, ++ .reset_idx_tx_mask = GENMASK(1, 0), ++ .reset_idx_rx_mask = GENMASK(7, 6), ++ }, ++ .tx_ring_desc_size = sizeof(struct mtk_wed_bm_desc), + .wdma_desc_size = 2 * sizeof(struct mtk_wdma_desc), + }; + +@@ -319,33 +337,38 @@ out: + static int + mtk_wed_tx_buffer_alloc(struct mtk_wed_device *dev) + { ++ u32 desc_size = dev->hw->soc->tx_ring_desc_size; ++ int i, page_idx = 0, n_pages, ring_size; ++ int token = dev->wlan.token_start; + struct mtk_wed_buf *page_list; +- struct mtk_wdma_desc *desc; + dma_addr_t desc_phys; +- int token = dev->wlan.token_start; +- int ring_size; +- int n_pages; +- int i, page_idx; ++ void *desc_ptr; + +- ring_size = dev->wlan.nbuf & ~(MTK_WED_BUF_PER_PAGE - 1); +- n_pages = ring_size / MTK_WED_BUF_PER_PAGE; ++ if (!mtk_wed_is_v3_or_greater(dev->hw)) { ++ ring_size = dev->wlan.nbuf & ~(MTK_WED_BUF_PER_PAGE - 1); ++ dev->tx_buf_ring.size = ring_size; ++ } else { ++ dev->tx_buf_ring.size = MTK_WED_TX_BM_DMA_SIZE; ++ ring_size = MTK_WED_TX_BM_PKT_CNT; ++ } ++ n_pages = dev->tx_buf_ring.size / MTK_WED_BUF_PER_PAGE; + + page_list = kcalloc(n_pages, sizeof(*page_list), GFP_KERNEL); + if (!page_list) + return -ENOMEM; + +- dev->tx_buf_ring.size = ring_size; + dev->tx_buf_ring.pages = page_list; + +- desc = dma_alloc_coherent(dev->hw->dev, ring_size * sizeof(*desc), +- &desc_phys, GFP_KERNEL); +- if (!desc) ++ desc_ptr = dma_alloc_coherent(dev->hw->dev, ++ dev->tx_buf_ring.size * desc_size, ++ &desc_phys, GFP_KERNEL); ++ if (!desc_ptr) + return -ENOMEM; + +- dev->tx_buf_ring.desc = desc; ++ dev->tx_buf_ring.desc = desc_ptr; + dev->tx_buf_ring.desc_phys = desc_phys; + +- for (i = 0, page_idx = 0; i < ring_size; i += MTK_WED_BUF_PER_PAGE) { ++ for (i = 0; i < ring_size; i += MTK_WED_BUF_PER_PAGE) { + dma_addr_t page_phys, buf_phys; + struct page *page; + void *buf; +@@ -371,28 +394,31 @@ mtk_wed_tx_buffer_alloc(struct mtk_wed_d + buf_phys = page_phys; + + for (s = 0; s < MTK_WED_BUF_PER_PAGE; s++) { +- u32 txd_size; +- u32 ctrl; +- +- txd_size = dev->wlan.init_buf(buf, buf_phys, token++); ++ struct mtk_wdma_desc *desc = desc_ptr; + + desc->buf0 = cpu_to_le32(buf_phys); +- desc->buf1 = cpu_to_le32(buf_phys + txd_size); ++ if (!mtk_wed_is_v3_or_greater(dev->hw)) { ++ u32 txd_size, ctrl; + +- if (mtk_wed_is_v1(dev->hw)) +- ctrl = FIELD_PREP(MTK_WDMA_DESC_CTRL_LEN0, txd_size) | +- FIELD_PREP(MTK_WDMA_DESC_CTRL_LEN1, +- MTK_WED_BUF_SIZE - txd_size) | +- MTK_WDMA_DESC_CTRL_LAST_SEG1; +- else +- ctrl = FIELD_PREP(MTK_WDMA_DESC_CTRL_LEN0, txd_size) | +- FIELD_PREP(MTK_WDMA_DESC_CTRL_LEN1_V2, +- MTK_WED_BUF_SIZE - txd_size) | +- MTK_WDMA_DESC_CTRL_LAST_SEG0; +- desc->ctrl = cpu_to_le32(ctrl); +- desc->info = 0; +- desc++; ++ txd_size = dev->wlan.init_buf(buf, buf_phys, ++ token++); ++ desc->buf1 = cpu_to_le32(buf_phys + txd_size); ++ ctrl = FIELD_PREP(MTK_WDMA_DESC_CTRL_LEN0, txd_size); ++ if (mtk_wed_is_v1(dev->hw)) ++ ctrl |= MTK_WDMA_DESC_CTRL_LAST_SEG1 | ++ FIELD_PREP(MTK_WDMA_DESC_CTRL_LEN1, ++ MTK_WED_BUF_SIZE - txd_size); ++ else ++ ctrl |= MTK_WDMA_DESC_CTRL_LAST_SEG0 | ++ FIELD_PREP(MTK_WDMA_DESC_CTRL_LEN1_V2, ++ MTK_WED_BUF_SIZE - txd_size); ++ desc->ctrl = cpu_to_le32(ctrl); ++ desc->info = 0; ++ } else { ++ desc->ctrl = cpu_to_le32(token << 16); ++ } + ++ desc_ptr += desc_size; + buf += MTK_WED_BUF_SIZE; + buf_phys += MTK_WED_BUF_SIZE; + } +@@ -408,31 +434,31 @@ static void + mtk_wed_free_tx_buffer(struct mtk_wed_device *dev) + { + struct mtk_wed_buf *page_list = dev->tx_buf_ring.pages; +- struct mtk_wdma_desc *desc = dev->tx_buf_ring.desc; +- int page_idx; +- int i; ++ struct mtk_wed_hw *hw = dev->hw; ++ int i, page_idx = 0; + + if (!page_list) + return; + +- if (!desc) ++ if (!dev->tx_buf_ring.desc) + goto free_pagelist; + +- for (i = 0, page_idx = 0; i < dev->tx_buf_ring.size; +- i += MTK_WED_BUF_PER_PAGE) { +- dma_addr_t buf_addr = page_list[page_idx].phy_addr; ++ for (i = 0; i < dev->tx_buf_ring.size; i += MTK_WED_BUF_PER_PAGE) { ++ dma_addr_t page_phy = page_list[page_idx].phy_addr; + void *page = page_list[page_idx++].p; + + if (!page) + break; + +- dma_unmap_page(dev->hw->dev, buf_addr, PAGE_SIZE, ++ dma_unmap_page(dev->hw->dev, page_phy, PAGE_SIZE, + DMA_BIDIRECTIONAL); + __free_page(page); + } + +- dma_free_coherent(dev->hw->dev, dev->tx_buf_ring.size * sizeof(*desc), +- desc, dev->tx_buf_ring.desc_phys); ++ dma_free_coherent(dev->hw->dev, ++ dev->tx_buf_ring.size * hw->soc->tx_ring_desc_size, ++ dev->tx_buf_ring.desc, ++ dev->tx_buf_ring.desc_phys); + + free_pagelist: + kfree(page_list); +@@ -517,13 +543,23 @@ mtk_wed_set_ext_int(struct mtk_wed_devic + { + u32 mask = MTK_WED_EXT_INT_STATUS_ERROR_MASK; + +- if (mtk_wed_is_v1(dev->hw)) ++ switch (dev->hw->version) { ++ case 1: + mask |= MTK_WED_EXT_INT_STATUS_TX_DRV_R_RESP_ERR; +- else ++ break; ++ case 2: + mask |= MTK_WED_EXT_INT_STATUS_RX_FBUF_LO_TH | + MTK_WED_EXT_INT_STATUS_RX_FBUF_HI_TH | + MTK_WED_EXT_INT_STATUS_RX_DRV_COHERENT | + MTK_WED_EXT_INT_STATUS_TX_DMA_W_RESP_ERR; ++ break; ++ case 3: ++ mask = MTK_WED_EXT_INT_STATUS_RX_DRV_COHERENT | ++ MTK_WED_EXT_INT_STATUS_TKID_WO_PYLD; ++ break; ++ default: ++ break; ++ } + + if (!dev->hw->num_flows) + mask &= ~MTK_WED_EXT_INT_STATUS_TKID_WO_PYLD; +@@ -535,6 +571,9 @@ mtk_wed_set_ext_int(struct mtk_wed_devic + static void + mtk_wed_set_512_support(struct mtk_wed_device *dev, bool enable) + { ++ if (!mtk_wed_is_v2(dev->hw)) ++ return; ++ + if (enable) { + wed_w32(dev, MTK_WED_TXDP_CTRL, MTK_WED_TXDP_DW9_OVERWR); + wed_w32(dev, MTK_WED_TXP_DW1, +@@ -609,6 +648,14 @@ mtk_wed_dma_disable(struct mtk_wed_devic + MTK_WED_WPDMA_RX_D_RX_DRV_EN); + wed_clr(dev, MTK_WED_WDMA_GLO_CFG, + MTK_WED_WDMA_GLO_CFG_TX_DDONE_CHK); ++ ++ if (mtk_wed_is_v3_or_greater(dev->hw) && ++ mtk_wed_get_rx_capa(dev)) { ++ wdma_clr(dev, MTK_WDMA_PREF_TX_CFG, ++ MTK_WDMA_PREF_TX_CFG_PREF_EN); ++ wdma_clr(dev, MTK_WDMA_PREF_RX_CFG, ++ MTK_WDMA_PREF_RX_CFG_PREF_EN); ++ } + } + + mtk_wed_set_512_support(dev, false); +@@ -651,6 +698,14 @@ mtk_wed_deinit(struct mtk_wed_device *de + MTK_WED_CTRL_RX_ROUTE_QM_EN | + MTK_WED_CTRL_WED_RX_BM_EN | + MTK_WED_CTRL_RX_RRO_QM_EN); ++ ++ if (mtk_wed_is_v3_or_greater(dev->hw)) { ++ wed_clr(dev, MTK_WED_CTRL, MTK_WED_CTRL_TX_AMSDU_EN); ++ wed_clr(dev, MTK_WED_RESET, MTK_WED_RESET_TX_AMSDU); ++ wed_clr(dev, MTK_WED_PCIE_INT_CTRL, ++ MTK_WED_PCIE_INT_CTRL_MSK_EN_POLA | ++ MTK_WED_PCIE_INT_CTRL_MSK_IRQ_FILTER); ++ } + } + + static void +@@ -700,21 +755,37 @@ mtk_wed_detach(struct mtk_wed_device *de + mutex_unlock(&hw_lock); + } + +-#define PCIE_BASE_ADDR0 0x11280000 + static void + mtk_wed_bus_init(struct mtk_wed_device *dev) + { + switch (dev->wlan.bus_type) { + case MTK_WED_BUS_PCIE: { + struct device_node *np = dev->hw->eth->dev->of_node; +- struct regmap *regs; + +- regs = syscon_regmap_lookup_by_phandle(np, +- "mediatek,wed-pcie"); +- if (IS_ERR(regs)) +- break; ++ if (mtk_wed_is_v2(dev->hw)) { ++ struct regmap *regs; ++ ++ regs = syscon_regmap_lookup_by_phandle(np, ++ "mediatek,wed-pcie"); ++ if (IS_ERR(regs)) ++ break; + +- regmap_update_bits(regs, 0, BIT(0), BIT(0)); ++ regmap_update_bits(regs, 0, BIT(0), BIT(0)); ++ } ++ ++ if (dev->wlan.msi) { ++ wed_w32(dev, MTK_WED_PCIE_CFG_INTM, ++ dev->hw->pcie_base | 0xc08); ++ wed_w32(dev, MTK_WED_PCIE_CFG_BASE, ++ dev->hw->pcie_base | 0xc04); ++ wed_w32(dev, MTK_WED_PCIE_INT_TRIGGER, BIT(8)); ++ } else { ++ wed_w32(dev, MTK_WED_PCIE_CFG_INTM, ++ dev->hw->pcie_base | 0x180); ++ wed_w32(dev, MTK_WED_PCIE_CFG_BASE, ++ dev->hw->pcie_base | 0x184); ++ wed_w32(dev, MTK_WED_PCIE_INT_TRIGGER, BIT(24)); ++ } + + wed_w32(dev, MTK_WED_PCIE_INT_CTRL, + FIELD_PREP(MTK_WED_PCIE_INT_CTRL_POLL_EN, 2)); +@@ -722,19 +793,9 @@ mtk_wed_bus_init(struct mtk_wed_device * + /* pcie interrupt control: pola/source selection */ + wed_set(dev, MTK_WED_PCIE_INT_CTRL, + MTK_WED_PCIE_INT_CTRL_MSK_EN_POLA | +- FIELD_PREP(MTK_WED_PCIE_INT_CTRL_SRC_SEL, 1)); +- wed_r32(dev, MTK_WED_PCIE_INT_CTRL); +- +- wed_w32(dev, MTK_WED_PCIE_CFG_INTM, PCIE_BASE_ADDR0 | 0x180); +- wed_w32(dev, MTK_WED_PCIE_CFG_BASE, PCIE_BASE_ADDR0 | 0x184); +- +- /* pcie interrupt status trigger register */ +- wed_w32(dev, MTK_WED_PCIE_INT_TRIGGER, BIT(24)); +- wed_r32(dev, MTK_WED_PCIE_INT_TRIGGER); +- +- /* pola setting */ +- wed_set(dev, MTK_WED_PCIE_INT_CTRL, +- MTK_WED_PCIE_INT_CTRL_MSK_EN_POLA); ++ MTK_WED_PCIE_INT_CTRL_MSK_IRQ_FILTER | ++ FIELD_PREP(MTK_WED_PCIE_INT_CTRL_SRC_SEL, ++ dev->hw->index)); + break; + } + case MTK_WED_BUS_AXI: +@@ -772,18 +833,19 @@ mtk_wed_set_wpdma(struct mtk_wed_device + static void + mtk_wed_hw_init_early(struct mtk_wed_device *dev) + { +- u32 mask, set; ++ u32 set = FIELD_PREP(MTK_WED_WDMA_GLO_CFG_BT_SIZE, 2); ++ u32 mask = MTK_WED_WDMA_GLO_CFG_BT_SIZE; + + mtk_wed_deinit(dev); + mtk_wed_reset(dev, MTK_WED_RESET_WED); + mtk_wed_set_wpdma(dev); + +- mask = MTK_WED_WDMA_GLO_CFG_BT_SIZE | +- MTK_WED_WDMA_GLO_CFG_DYNAMIC_DMAD_RECYCLE | +- MTK_WED_WDMA_GLO_CFG_RX_DIS_FSM_AUTO_IDLE; +- set = FIELD_PREP(MTK_WED_WDMA_GLO_CFG_BT_SIZE, 2) | +- MTK_WED_WDMA_GLO_CFG_DYNAMIC_SKIP_DMAD_PREP | +- MTK_WED_WDMA_GLO_CFG_IDLE_DMAD_SUPPLY; ++ if (!mtk_wed_is_v3_or_greater(dev->hw)) { ++ mask |= MTK_WED_WDMA_GLO_CFG_DYNAMIC_DMAD_RECYCLE | ++ MTK_WED_WDMA_GLO_CFG_RX_DIS_FSM_AUTO_IDLE; ++ set |= MTK_WED_WDMA_GLO_CFG_DYNAMIC_SKIP_DMAD_PREP | ++ MTK_WED_WDMA_GLO_CFG_IDLE_DMAD_SUPPLY; ++ } + wed_m32(dev, MTK_WED_WDMA_GLO_CFG, mask, set); + + if (mtk_wed_is_v1(dev->hw)) { +@@ -931,11 +993,18 @@ mtk_wed_route_qm_hw_init(struct mtk_wed_ + } + + /* configure RX_ROUTE_QM */ +- wed_clr(dev, MTK_WED_RTQM_GLO_CFG, MTK_WED_RTQM_Q_RST); +- wed_clr(dev, MTK_WED_RTQM_GLO_CFG, MTK_WED_RTQM_TXDMAD_FPORT); +- wed_set(dev, MTK_WED_RTQM_GLO_CFG, +- FIELD_PREP(MTK_WED_RTQM_TXDMAD_FPORT, 0x3 + dev->hw->index)); +- wed_clr(dev, MTK_WED_RTQM_GLO_CFG, MTK_WED_RTQM_Q_RST); ++ if (mtk_wed_is_v2(dev->hw)) { ++ wed_clr(dev, MTK_WED_RTQM_GLO_CFG, MTK_WED_RTQM_Q_RST); ++ wed_clr(dev, MTK_WED_RTQM_GLO_CFG, MTK_WED_RTQM_TXDMAD_FPORT); ++ wed_set(dev, MTK_WED_RTQM_GLO_CFG, ++ FIELD_PREP(MTK_WED_RTQM_TXDMAD_FPORT, ++ 0x3 + dev->hw->index)); ++ wed_clr(dev, MTK_WED_RTQM_GLO_CFG, MTK_WED_RTQM_Q_RST); ++ } else { ++ wed_set(dev, MTK_WED_RTQM_ENQ_CFG0, ++ FIELD_PREP(MTK_WED_RTQM_ENQ_CFG_TXDMAD_FPORT, ++ 0x3 + dev->hw->index)); ++ } + /* enable RX_ROUTE_QM */ + wed_set(dev, MTK_WED_CTRL, MTK_WED_CTRL_RX_ROUTE_QM_EN); + } +@@ -948,22 +1017,30 @@ mtk_wed_hw_init(struct mtk_wed_device *d + + dev->init_done = true; + mtk_wed_set_ext_int(dev, false); +- wed_w32(dev, MTK_WED_TX_BM_CTRL, +- MTK_WED_TX_BM_CTRL_PAUSE | +- FIELD_PREP(MTK_WED_TX_BM_CTRL_VLD_GRP_NUM, +- dev->tx_buf_ring.size / 128) | +- FIELD_PREP(MTK_WED_TX_BM_CTRL_RSV_GRP_NUM, +- MTK_WED_TX_RING_SIZE / 256)); + + wed_w32(dev, MTK_WED_TX_BM_BASE, dev->tx_buf_ring.desc_phys); +- + wed_w32(dev, MTK_WED_TX_BM_BUF_LEN, MTK_WED_PKT_SIZE); + + if (mtk_wed_is_v1(dev->hw)) { ++ wed_w32(dev, MTK_WED_TX_BM_CTRL, ++ MTK_WED_TX_BM_CTRL_PAUSE | ++ FIELD_PREP(MTK_WED_TX_BM_CTRL_VLD_GRP_NUM, ++ dev->tx_buf_ring.size / 128) | ++ FIELD_PREP(MTK_WED_TX_BM_CTRL_RSV_GRP_NUM, ++ MTK_WED_TX_RING_SIZE / 256)); + wed_w32(dev, MTK_WED_TX_BM_DYN_THR, + FIELD_PREP(MTK_WED_TX_BM_DYN_THR_LO, 1) | + MTK_WED_TX_BM_DYN_THR_HI); +- } else { ++ } else if (mtk_wed_is_v2(dev->hw)) { ++ wed_w32(dev, MTK_WED_TX_BM_CTRL, ++ MTK_WED_TX_BM_CTRL_PAUSE | ++ FIELD_PREP(MTK_WED_TX_BM_CTRL_VLD_GRP_NUM, ++ dev->tx_buf_ring.size / 128) | ++ FIELD_PREP(MTK_WED_TX_BM_CTRL_RSV_GRP_NUM, ++ MTK_WED_TX_RING_SIZE / 256)); ++ wed_w32(dev, MTK_WED_TX_TKID_DYN_THR, ++ FIELD_PREP(MTK_WED_TX_TKID_DYN_THR_LO, 0) | ++ MTK_WED_TX_TKID_DYN_THR_HI); + wed_w32(dev, MTK_WED_TX_BM_DYN_THR, + FIELD_PREP(MTK_WED_TX_BM_DYN_THR_LO_V2, 0) | + MTK_WED_TX_BM_DYN_THR_HI_V2); +@@ -973,9 +1050,6 @@ mtk_wed_hw_init(struct mtk_wed_device *d + dev->tx_buf_ring.size / 128) | + FIELD_PREP(MTK_WED_TX_TKID_CTRL_RSV_GRP_NUM, + dev->tx_buf_ring.size / 128)); +- wed_w32(dev, MTK_WED_TX_TKID_DYN_THR, +- FIELD_PREP(MTK_WED_TX_TKID_DYN_THR_LO, 0) | +- MTK_WED_TX_TKID_DYN_THR_HI); + } + + wed_w32(dev, dev->hw->soc->regmap.tx_bm_tkid, +@@ -985,26 +1059,62 @@ mtk_wed_hw_init(struct mtk_wed_device *d + + mtk_wed_reset(dev, MTK_WED_RESET_TX_BM); + ++ if (mtk_wed_is_v3_or_greater(dev->hw)) { ++ /* switch to new bm architecture */ ++ wed_clr(dev, MTK_WED_TX_BM_CTRL, ++ MTK_WED_TX_BM_CTRL_LEGACY_EN); ++ ++ wed_w32(dev, MTK_WED_TX_TKID_CTRL, ++ MTK_WED_TX_TKID_CTRL_PAUSE | ++ FIELD_PREP(MTK_WED_TX_TKID_CTRL_VLD_GRP_NUM_V3, ++ dev->wlan.nbuf / 128) | ++ FIELD_PREP(MTK_WED_TX_TKID_CTRL_RSV_GRP_NUM_V3, ++ dev->wlan.nbuf / 128)); ++ /* return SKBID + SDP back to bm */ ++ wed_set(dev, MTK_WED_TX_TKID_CTRL, ++ MTK_WED_TX_TKID_CTRL_FREE_FORMAT); ++ ++ wed_w32(dev, MTK_WED_TX_BM_INIT_PTR, ++ MTK_WED_TX_BM_PKT_CNT | ++ MTK_WED_TX_BM_INIT_SW_TAIL_IDX); ++ } ++ + if (mtk_wed_is_v1(dev->hw)) { + wed_set(dev, MTK_WED_CTRL, + MTK_WED_CTRL_WED_TX_BM_EN | + MTK_WED_CTRL_WED_TX_FREE_AGENT_EN); +- } else { +- wed_clr(dev, MTK_WED_TX_TKID_CTRL, MTK_WED_TX_TKID_CTRL_PAUSE); +- if (mtk_wed_get_rx_capa(dev)) { +- /* rx hw init */ +- wed_w32(dev, MTK_WED_WPDMA_RX_D_RST_IDX, +- MTK_WED_WPDMA_RX_D_RST_CRX_IDX | +- MTK_WED_WPDMA_RX_D_RST_DRV_IDX); +- wed_w32(dev, MTK_WED_WPDMA_RX_D_RST_IDX, 0); +- +- mtk_wed_rx_buffer_hw_init(dev); +- mtk_wed_rro_hw_init(dev); +- mtk_wed_route_qm_hw_init(dev); +- } ++ } else if (mtk_wed_get_rx_capa(dev)) { ++ /* rx hw init */ ++ wed_w32(dev, MTK_WED_WPDMA_RX_D_RST_IDX, ++ MTK_WED_WPDMA_RX_D_RST_CRX_IDX | ++ MTK_WED_WPDMA_RX_D_RST_DRV_IDX); ++ wed_w32(dev, MTK_WED_WPDMA_RX_D_RST_IDX, 0); ++ ++ /* reset prefetch index of ring */ ++ wed_set(dev, MTK_WED_WPDMA_RX_D_PREF_RX0_SIDX, ++ MTK_WED_WPDMA_RX_D_PREF_SIDX_IDX_CLR); ++ wed_clr(dev, MTK_WED_WPDMA_RX_D_PREF_RX0_SIDX, ++ MTK_WED_WPDMA_RX_D_PREF_SIDX_IDX_CLR); ++ ++ wed_set(dev, MTK_WED_WPDMA_RX_D_PREF_RX1_SIDX, ++ MTK_WED_WPDMA_RX_D_PREF_SIDX_IDX_CLR); ++ wed_clr(dev, MTK_WED_WPDMA_RX_D_PREF_RX1_SIDX, ++ MTK_WED_WPDMA_RX_D_PREF_SIDX_IDX_CLR); ++ ++ /* reset prefetch FIFO of ring */ ++ wed_set(dev, MTK_WED_WPDMA_RX_D_PREF_FIFO_CFG, ++ MTK_WED_WPDMA_RX_D_PREF_FIFO_CFG_R0_CLR | ++ MTK_WED_WPDMA_RX_D_PREF_FIFO_CFG_R1_CLR); ++ wed_w32(dev, MTK_WED_WPDMA_RX_D_PREF_FIFO_CFG, 0); ++ ++ mtk_wed_rx_buffer_hw_init(dev); ++ mtk_wed_rro_hw_init(dev); ++ mtk_wed_route_qm_hw_init(dev); + } + + wed_clr(dev, MTK_WED_TX_BM_CTRL, MTK_WED_TX_BM_CTRL_PAUSE); ++ if (!mtk_wed_is_v1(dev->hw)) ++ wed_clr(dev, MTK_WED_TX_TKID_CTRL, MTK_WED_TX_TKID_CTRL_PAUSE); + } + + static void +@@ -1302,6 +1412,24 @@ mtk_wed_wdma_tx_ring_setup(struct mtk_we + dev->hw->soc->wdma_desc_size, true)) + return -ENOMEM; + ++ if (mtk_wed_is_v3_or_greater(dev->hw)) { ++ struct mtk_wdma_desc *desc = wdma->desc; ++ int i; ++ ++ for (i = 0; i < MTK_WED_WDMA_RING_SIZE; i++) { ++ desc->buf0 = 0; ++ desc->ctrl = cpu_to_le32(MTK_WDMA_DESC_CTRL_DMA_DONE); ++ desc->buf1 = 0; ++ desc->info = cpu_to_le32(MTK_WDMA_TXD0_DESC_INFO_DMA_DONE); ++ desc++; ++ desc->buf0 = 0; ++ desc->ctrl = cpu_to_le32(MTK_WDMA_DESC_CTRL_DMA_DONE); ++ desc->buf1 = 0; ++ desc->info = cpu_to_le32(MTK_WDMA_TXD1_DESC_INFO_DMA_DONE); ++ desc++; ++ } ++ } ++ + wdma_w32(dev, MTK_WDMA_RING_TX(idx) + MTK_WED_RING_OFS_BASE, + wdma->desc_phys); + wdma_w32(dev, MTK_WDMA_RING_TX(idx) + MTK_WED_RING_OFS_COUNT, +@@ -1367,6 +1495,9 @@ mtk_wed_configure_irq(struct mtk_wed_dev + + wed_clr(dev, MTK_WED_WDMA_INT_CTRL, wdma_mask); + } else { ++ if (mtk_wed_is_v3_or_greater(dev->hw)) ++ wed_set(dev, MTK_WED_CTRL, MTK_WED_CTRL_TX_TKID_ALI_EN); ++ + /* initail tx interrupt trigger */ + wed_w32(dev, MTK_WED_WPDMA_INT_CTRL_TX, + MTK_WED_WPDMA_INT_CTRL_TX0_DONE_EN | +@@ -1419,33 +1550,60 @@ mtk_wed_dma_enable(struct mtk_wed_device + { + int i; + +- wed_set(dev, MTK_WED_WPDMA_INT_CTRL, MTK_WED_WPDMA_INT_CTRL_SUBRT_ADV); ++ if (!mtk_wed_is_v3_or_greater(dev->hw)) { ++ wed_set(dev, MTK_WED_WPDMA_INT_CTRL, ++ MTK_WED_WPDMA_INT_CTRL_SUBRT_ADV); ++ wed_set(dev, MTK_WED_WPDMA_GLO_CFG, ++ MTK_WED_WPDMA_GLO_CFG_TX_DRV_EN | ++ MTK_WED_WPDMA_GLO_CFG_RX_DRV_EN); ++ wdma_set(dev, MTK_WDMA_GLO_CFG, ++ MTK_WDMA_GLO_CFG_TX_DMA_EN | ++ MTK_WDMA_GLO_CFG_RX_INFO1_PRERES | ++ MTK_WDMA_GLO_CFG_RX_INFO2_PRERES); ++ wed_set(dev, MTK_WED_WPDMA_CTRL, MTK_WED_WPDMA_CTRL_SDL1_FIXED); ++ } else { ++ wed_set(dev, MTK_WED_WPDMA_GLO_CFG, ++ MTK_WED_WPDMA_GLO_CFG_TX_DRV_EN | ++ MTK_WED_WPDMA_GLO_CFG_RX_DRV_EN | ++ MTK_WED_WPDMA_GLO_CFG_RX_DDONE2_WR); ++ wdma_set(dev, MTK_WDMA_GLO_CFG, MTK_WDMA_GLO_CFG_TX_DMA_EN); ++ } + + wed_set(dev, MTK_WED_GLO_CFG, + MTK_WED_GLO_CFG_TX_DMA_EN | + MTK_WED_GLO_CFG_RX_DMA_EN); +- wed_set(dev, MTK_WED_WPDMA_GLO_CFG, +- MTK_WED_WPDMA_GLO_CFG_TX_DRV_EN | +- MTK_WED_WPDMA_GLO_CFG_RX_DRV_EN); ++ + wed_set(dev, MTK_WED_WDMA_GLO_CFG, + MTK_WED_WDMA_GLO_CFG_RX_DRV_EN); + +- wdma_set(dev, MTK_WDMA_GLO_CFG, +- MTK_WDMA_GLO_CFG_TX_DMA_EN | +- MTK_WDMA_GLO_CFG_RX_INFO1_PRERES | +- MTK_WDMA_GLO_CFG_RX_INFO2_PRERES); +- + if (mtk_wed_is_v1(dev->hw)) { + wdma_set(dev, MTK_WDMA_GLO_CFG, + MTK_WDMA_GLO_CFG_RX_INFO3_PRERES); + return; + } + +- wed_set(dev, MTK_WED_WPDMA_CTRL, +- MTK_WED_WPDMA_CTRL_SDL1_FIXED); + wed_set(dev, MTK_WED_WPDMA_GLO_CFG, + MTK_WED_WPDMA_GLO_CFG_RX_DRV_R0_PKT_PROC | + MTK_WED_WPDMA_GLO_CFG_RX_DRV_R0_CRX_SYNC); ++ ++ if (mtk_wed_is_v3_or_greater(dev->hw)) { ++ wed_set(dev, MTK_WED_WDMA_RX_PREF_CFG, ++ FIELD_PREP(MTK_WED_WDMA_RX_PREF_BURST_SIZE, 0x10) | ++ FIELD_PREP(MTK_WED_WDMA_RX_PREF_LOW_THRES, 0x8)); ++ wed_clr(dev, MTK_WED_WDMA_RX_PREF_CFG, ++ MTK_WED_WDMA_RX_PREF_DDONE2_EN); ++ wed_set(dev, MTK_WED_WDMA_RX_PREF_CFG, MTK_WED_WDMA_RX_PREF_EN); ++ ++ wed_clr(dev, MTK_WED_WPDMA_GLO_CFG, ++ MTK_WED_WPDMA_GLO_CFG_TX_DDONE_CHK_LAST); ++ wed_set(dev, MTK_WED_WPDMA_GLO_CFG, ++ MTK_WED_WPDMA_GLO_CFG_TX_DDONE_CHK | ++ MTK_WED_WPDMA_GLO_CFG_RX_DRV_EVENT_PKT_FMT_CHK | ++ MTK_WED_WPDMA_GLO_CFG_RX_DRV_UNS_VER_FORCE_4); ++ ++ wdma_set(dev, MTK_WDMA_PREF_RX_CFG, MTK_WDMA_PREF_RX_CFG_PREF_EN); ++ } ++ + wed_clr(dev, MTK_WED_WPDMA_GLO_CFG, + MTK_WED_WPDMA_GLO_CFG_TX_TKID_KEEP | + MTK_WED_WPDMA_GLO_CFG_TX_DMAD_DW3_PREV); +@@ -1457,11 +1615,22 @@ mtk_wed_dma_enable(struct mtk_wed_device + MTK_WED_WDMA_GLO_CFG_TX_DRV_EN | + MTK_WED_WDMA_GLO_CFG_TX_DDONE_CHK); + ++ wed_clr(dev, MTK_WED_WPDMA_RX_D_GLO_CFG, MTK_WED_WPDMA_RX_D_RXD_READ_LEN); + wed_set(dev, MTK_WED_WPDMA_RX_D_GLO_CFG, + MTK_WED_WPDMA_RX_D_RX_DRV_EN | + FIELD_PREP(MTK_WED_WPDMA_RX_D_RXD_READ_LEN, 0x18) | +- FIELD_PREP(MTK_WED_WPDMA_RX_D_INIT_PHASE_RXEN_SEL, +- 0x2)); ++ FIELD_PREP(MTK_WED_WPDMA_RX_D_INIT_PHASE_RXEN_SEL, 0x2)); ++ ++ if (mtk_wed_is_v3_or_greater(dev->hw)) { ++ wed_set(dev, MTK_WED_WPDMA_RX_D_PREF_CFG, ++ MTK_WED_WPDMA_RX_D_PREF_EN | ++ FIELD_PREP(MTK_WED_WPDMA_RX_D_PREF_BURST_SIZE, 0x10) | ++ FIELD_PREP(MTK_WED_WPDMA_RX_D_PREF_LOW_THRES, 0x8)); ++ ++ wed_set(dev, MTK_WED_RRO_RX_D_CFG(2), MTK_WED_RRO_RX_D_DRV_EN); ++ wdma_set(dev, MTK_WDMA_PREF_TX_CFG, MTK_WDMA_PREF_TX_CFG_PREF_EN); ++ wdma_set(dev, MTK_WDMA_WRBK_TX_CFG, MTK_WDMA_WRBK_TX_CFG_WRBK_EN); ++ } + + for (i = 0; i < MTK_WED_RX_QUEUES; i++) + mtk_wed_check_wfdma_rx_fill(dev, i); +@@ -1501,6 +1670,12 @@ mtk_wed_start(struct mtk_wed_device *dev + wed_r32(dev, MTK_WED_EXT_INT_MASK1); + wed_r32(dev, MTK_WED_EXT_INT_MASK2); + ++ if (mtk_wed_is_v3_or_greater(dev->hw)) { ++ wed_w32(dev, MTK_WED_EXT_INT_MASK3, ++ MTK_WED_EXT_INT_STATUS_WPDMA_MID_RDY); ++ wed_r32(dev, MTK_WED_EXT_INT_MASK3); ++ } ++ + if (mtk_wed_rro_cfg(dev)) + return; + } +@@ -1552,6 +1727,7 @@ mtk_wed_attach(struct mtk_wed_device *de + dev->irq = hw->irq; + dev->wdma_idx = hw->index; + dev->version = hw->version; ++ dev->hw->pcie_base = mtk_wed_get_pcie_base(dev); + + if (hw->eth->dma_dev == hw->eth->dev && + of_dma_is_coherent(hw->eth->dev->of_node)) +@@ -1619,6 +1795,23 @@ mtk_wed_tx_ring_setup(struct mtk_wed_dev + ring->reg_base = MTK_WED_RING_TX(idx); + ring->wpdma = regs; + ++ if (mtk_wed_is_v3_or_greater(dev->hw) && idx == 1) { ++ /* reset prefetch index */ ++ wed_set(dev, MTK_WED_WDMA_RX_PREF_CFG, ++ MTK_WED_WDMA_RX_PREF_RX0_SIDX_CLR | ++ MTK_WED_WDMA_RX_PREF_RX1_SIDX_CLR); ++ ++ wed_clr(dev, MTK_WED_WDMA_RX_PREF_CFG, ++ MTK_WED_WDMA_RX_PREF_RX0_SIDX_CLR | ++ MTK_WED_WDMA_RX_PREF_RX1_SIDX_CLR); ++ ++ /* reset prefetch FIFO */ ++ wed_w32(dev, MTK_WED_WDMA_RX_PREF_FIFO_CFG, ++ MTK_WED_WDMA_RX_PREF_FIFO_RX0_CLR | ++ MTK_WED_WDMA_RX_PREF_FIFO_RX1_CLR); ++ wed_w32(dev, MTK_WED_WDMA_RX_PREF_FIFO_CFG, 0); ++ } ++ + /* WED -> WPDMA */ + wpdma_tx_w32(dev, idx, MTK_WED_RING_OFS_BASE, ring->desc_phys); + wpdma_tx_w32(dev, idx, MTK_WED_RING_OFS_COUNT, MTK_WED_TX_RING_SIZE); +@@ -1693,15 +1886,13 @@ mtk_wed_rx_ring_setup(struct mtk_wed_dev + static u32 + mtk_wed_irq_get(struct mtk_wed_device *dev, u32 mask) + { +- u32 val, ext_mask = MTK_WED_EXT_INT_STATUS_ERROR_MASK; ++ u32 val, ext_mask; + +- if (mtk_wed_is_v1(dev->hw)) +- ext_mask |= MTK_WED_EXT_INT_STATUS_TX_DRV_R_RESP_ERR; ++ if (mtk_wed_is_v3_or_greater(dev->hw)) ++ ext_mask = MTK_WED_EXT_INT_STATUS_RX_DRV_COHERENT | ++ MTK_WED_EXT_INT_STATUS_TKID_WO_PYLD; + else +- ext_mask |= MTK_WED_EXT_INT_STATUS_RX_FBUF_LO_TH | +- MTK_WED_EXT_INT_STATUS_RX_FBUF_HI_TH | +- MTK_WED_EXT_INT_STATUS_RX_DRV_COHERENT | +- MTK_WED_EXT_INT_STATUS_TX_DMA_W_RESP_ERR; ++ ext_mask = MTK_WED_EXT_INT_STATUS_ERROR_MASK; + + val = wed_r32(dev, MTK_WED_EXT_INT_STATUS); + wed_w32(dev, MTK_WED_EXT_INT_STATUS, val); +@@ -1942,6 +2133,9 @@ void mtk_wed_add_hw(struct device_node * + case 2: + hw->soc = &mt7986_data; + break; ++ case 3: ++ hw->soc = &mt7988_data; ++ break; + default: + case 1: + hw->mirror = syscon_regmap_lookup_by_phandle(eth_np, +--- a/drivers/net/ethernet/mediatek/mtk_wed.h ++++ b/drivers/net/ethernet/mediatek/mtk_wed.h +@@ -9,6 +9,8 @@ + #include + #include + ++#include "mtk_wed_regs.h" ++ + struct mtk_eth; + struct mtk_wed_wo; + +@@ -19,6 +21,7 @@ struct mtk_wed_soc_data { + u32 reset_idx_tx_mask; + u32 reset_idx_rx_mask; + } regmap; ++ u32 tx_ring_desc_size; + u32 wdma_desc_size; + }; + +@@ -35,6 +38,7 @@ struct mtk_wed_hw { + struct dentry *debugfs_dir; + struct mtk_wed_device *wed_dev; + struct mtk_wed_wo *wed_wo; ++ u32 pcie_base; + u32 debugfs_reg; + u32 num_flows; + u8 version; +@@ -61,6 +65,16 @@ static inline bool mtk_wed_is_v2(struct + return hw->version == 2; + } + ++static inline bool mtk_wed_is_v3(struct mtk_wed_hw *hw) ++{ ++ return hw->version == 3; ++} ++ ++static inline bool mtk_wed_is_v3_or_greater(struct mtk_wed_hw *hw) ++{ ++ return hw->version > 2; ++} ++ + static inline void + wed_w32(struct mtk_wed_device *dev, u32 reg, u32 val) + { +@@ -143,6 +157,21 @@ wpdma_txfree_w32(struct mtk_wed_device * + writel(val, dev->txfree_ring.wpdma + reg); + } + ++static inline u32 mtk_wed_get_pcie_base(struct mtk_wed_device *dev) ++{ ++ if (!mtk_wed_is_v3_or_greater(dev->hw)) ++ return MTK_WED_PCIE_BASE; ++ ++ switch (dev->hw->index) { ++ case 1: ++ return MTK_WED_PCIE_BASE1; ++ case 2: ++ return MTK_WED_PCIE_BASE2; ++ default: ++ return MTK_WED_PCIE_BASE0; ++ } ++} ++ + void mtk_wed_add_hw(struct device_node *np, struct mtk_eth *eth, + void __iomem *wdma, phys_addr_t wdma_phy, + int index); +--- a/drivers/net/ethernet/mediatek/mtk_wed_mcu.c ++++ b/drivers/net/ethernet/mediatek/mtk_wed_mcu.c +@@ -331,10 +331,22 @@ mtk_wed_mcu_load_firmware(struct mtk_wed + wo->hw->index + 1); + + /* load firmware */ +- if (of_device_is_compatible(wo->hw->node, "mediatek,mt7981-wed")) +- fw_name = MT7981_FIRMWARE_WO; +- else +- fw_name = wo->hw->index ? MT7986_FIRMWARE_WO1 : MT7986_FIRMWARE_WO0; ++ switch (wo->hw->version) { ++ case 2: ++ if (of_device_is_compatible(wo->hw->node, ++ "mediatek,mt7981-wed")) ++ fw_name = MT7981_FIRMWARE_WO; ++ else ++ fw_name = wo->hw->index ? MT7986_FIRMWARE_WO1 ++ : MT7986_FIRMWARE_WO0; ++ break; ++ case 3: ++ fw_name = wo->hw->index ? MT7988_FIRMWARE_WO1 ++ : MT7988_FIRMWARE_WO0; ++ break; ++ default: ++ return -EINVAL; ++ } + + ret = request_firmware(&fw, fw_name, wo->hw->dev); + if (ret) +@@ -355,15 +367,16 @@ mtk_wed_mcu_load_firmware(struct mtk_wed + } + + /* set the start address */ +- boot_cr = wo->hw->index ? MTK_WO_MCU_CFG_LS_WA_BOOT_ADDR_ADDR +- : MTK_WO_MCU_CFG_LS_WM_BOOT_ADDR_ADDR; ++ if (!mtk_wed_is_v3_or_greater(wo->hw) && wo->hw->index) ++ boot_cr = MTK_WO_MCU_CFG_LS_WA_BOOT_ADDR_ADDR; ++ else ++ boot_cr = MTK_WO_MCU_CFG_LS_WM_BOOT_ADDR_ADDR; + wo_w32(wo, boot_cr, mem_region[MTK_WED_WO_REGION_EMI].phy_addr >> 16); + /* wo firmware reset */ + wo_w32(wo, MTK_WO_MCU_CFG_LS_WF_MCCR_CLR_ADDR, 0xc00); + +- val = wo_r32(wo, MTK_WO_MCU_CFG_LS_WF_MCU_CFG_WM_WA_ADDR); +- val |= wo->hw->index ? MTK_WO_MCU_CFG_LS_WF_WM_WA_WA_CPU_RSTB_MASK +- : MTK_WO_MCU_CFG_LS_WF_WM_WA_WM_CPU_RSTB_MASK; ++ val = wo_r32(wo, MTK_WO_MCU_CFG_LS_WF_MCU_CFG_WM_WA_ADDR) | ++ MTK_WO_MCU_CFG_LS_WF_WM_WA_WM_CPU_RSTB_MASK; + wo_w32(wo, MTK_WO_MCU_CFG_LS_WF_MCU_CFG_WM_WA_ADDR, val); + out: + release_firmware(fw); +@@ -398,3 +411,5 @@ int mtk_wed_mcu_init(struct mtk_wed_wo * + MODULE_FIRMWARE(MT7981_FIRMWARE_WO); + MODULE_FIRMWARE(MT7986_FIRMWARE_WO0); + MODULE_FIRMWARE(MT7986_FIRMWARE_WO1); ++MODULE_FIRMWARE(MT7988_FIRMWARE_WO0); ++MODULE_FIRMWARE(MT7988_FIRMWARE_WO1); +--- a/drivers/net/ethernet/mediatek/mtk_wed_regs.h ++++ b/drivers/net/ethernet/mediatek/mtk_wed_regs.h +@@ -13,6 +13,9 @@ + #define MTK_WDMA_DESC_CTRL_LAST_SEG0 BIT(30) + #define MTK_WDMA_DESC_CTRL_DMA_DONE BIT(31) + ++#define MTK_WDMA_TXD0_DESC_INFO_DMA_DONE BIT(29) ++#define MTK_WDMA_TXD1_DESC_INFO_DMA_DONE BIT(31) ++ + struct mtk_wdma_desc { + __le32 buf0; + __le32 ctrl; +@@ -37,6 +40,7 @@ struct mtk_wdma_desc { + #define MTK_WED_RESET_WDMA_INT_AGENT BIT(19) + #define MTK_WED_RESET_RX_RRO_QM BIT(20) + #define MTK_WED_RESET_RX_ROUTE_QM BIT(21) ++#define MTK_WED_RESET_TX_AMSDU BIT(22) + #define MTK_WED_RESET_WED BIT(31) + + #define MTK_WED_CTRL 0x00c +@@ -44,6 +48,9 @@ struct mtk_wdma_desc { + #define MTK_WED_CTRL_WPDMA_INT_AGENT_BUSY BIT(1) + #define MTK_WED_CTRL_WDMA_INT_AGENT_EN BIT(2) + #define MTK_WED_CTRL_WDMA_INT_AGENT_BUSY BIT(3) ++#define MTK_WED_CTRL_WED_RX_IND_CMD_EN BIT(5) ++#define MTK_WED_CTRL_WED_RX_PG_BM_EN BIT(6) ++#define MTK_WED_CTRL_WED_RX_PG_BM_BUSY BIT(7) + #define MTK_WED_CTRL_WED_TX_BM_EN BIT(8) + #define MTK_WED_CTRL_WED_TX_BM_BUSY BIT(9) + #define MTK_WED_CTRL_WED_TX_FREE_AGENT_EN BIT(10) +@@ -54,9 +61,14 @@ struct mtk_wdma_desc { + #define MTK_WED_CTRL_RX_RRO_QM_BUSY BIT(15) + #define MTK_WED_CTRL_RX_ROUTE_QM_EN BIT(16) + #define MTK_WED_CTRL_RX_ROUTE_QM_BUSY BIT(17) ++#define MTK_WED_CTRL_TX_TKID_ALI_EN BIT(20) ++#define MTK_WED_CTRL_TX_TKID_ALI_BUSY BIT(21) ++#define MTK_WED_CTRL_TX_AMSDU_EN BIT(22) ++#define MTK_WED_CTRL_TX_AMSDU_BUSY BIT(23) + #define MTK_WED_CTRL_FINAL_DIDX_READ BIT(24) + #define MTK_WED_CTRL_ETH_DMAD_FMT BIT(25) + #define MTK_WED_CTRL_MIB_READ_CLEAR BIT(28) ++#define MTK_WED_CTRL_FLD_MIB_RD_CLR BIT(28) + + #define MTK_WED_EXT_INT_STATUS 0x020 + #define MTK_WED_EXT_INT_STATUS_TF_LEN_ERR BIT(0) +@@ -89,6 +101,7 @@ struct mtk_wdma_desc { + #define MTK_WED_EXT_INT_MASK 0x028 + #define MTK_WED_EXT_INT_MASK1 0x02c + #define MTK_WED_EXT_INT_MASK2 0x030 ++#define MTK_WED_EXT_INT_MASK3 0x034 + + #define MTK_WED_STATUS 0x060 + #define MTK_WED_STATUS_TX GENMASK(15, 8) +@@ -96,9 +109,14 @@ struct mtk_wdma_desc { + #define MTK_WED_TX_BM_CTRL 0x080 + #define MTK_WED_TX_BM_CTRL_VLD_GRP_NUM GENMASK(6, 0) + #define MTK_WED_TX_BM_CTRL_RSV_GRP_NUM GENMASK(22, 16) ++#define MTK_WED_TX_BM_CTRL_LEGACY_EN BIT(26) ++#define MTK_WED_TX_TKID_CTRL_FREE_FORMAT BIT(27) + #define MTK_WED_TX_BM_CTRL_PAUSE BIT(28) + + #define MTK_WED_TX_BM_BASE 0x084 ++#define MTK_WED_TX_BM_INIT_PTR 0x088 ++#define MTK_WED_TX_BM_SW_TAIL_IDX GENMASK(16, 0) ++#define MTK_WED_TX_BM_INIT_SW_TAIL_IDX BIT(16) + + #define MTK_WED_TX_BM_TKID_START GENMASK(15, 0) + #define MTK_WED_TX_BM_TKID_END GENMASK(31, 16) +@@ -122,6 +140,9 @@ struct mtk_wdma_desc { + #define MTK_WED_TX_TKID_CTRL_RSV_GRP_NUM GENMASK(22, 16) + #define MTK_WED_TX_TKID_CTRL_PAUSE BIT(28) + ++#define MTK_WED_TX_TKID_CTRL_VLD_GRP_NUM_V3 GENMASK(7, 0) ++#define MTK_WED_TX_TKID_CTRL_RSV_GRP_NUM_V3 GENMASK(23, 16) ++ + #define MTK_WED_TX_TKID_DYN_THR 0x0e0 + #define MTK_WED_TX_TKID_DYN_THR_LO GENMASK(6, 0) + #define MTK_WED_TX_TKID_DYN_THR_HI GENMASK(22, 16) +@@ -199,12 +220,15 @@ struct mtk_wdma_desc { + #define MTK_WED_WPDMA_GLO_CFG_RX_DRV_R1_PKT_PROC BIT(5) + #define MTK_WED_WPDMA_GLO_CFG_RX_DRV_R0_CRX_SYNC BIT(6) + #define MTK_WED_WPDMA_GLO_CFG_RX_DRV_R1_CRX_SYNC BIT(7) +-#define MTK_WED_WPDMA_GLO_CFG_RX_DRV_EVENT_PKT_FMT_VER GENMASK(18, 16) ++#define MTK_WED_WPDMA_GLO_CFG_RX_DRV_EVENT_PKT_FMT_VER GENMASK(15, 12) ++#define MTK_WED_WPDMA_GLO_CFG_RX_DRV_UNS_VER_FORCE_4 BIT(18) + #define MTK_WED_WPDMA_GLO_CFG_RX_DRV_UNSUPPORT_FMT BIT(19) +-#define MTK_WED_WPDMA_GLO_CFG_RX_DRV_UEVENT_PKT_FMT_CHK BIT(20) ++#define MTK_WED_WPDMA_GLO_CFG_RX_DRV_EVENT_PKT_FMT_CHK BIT(20) + #define MTK_WED_WPDMA_GLO_CFG_RX_DDONE2_WR BIT(21) + #define MTK_WED_WPDMA_GLO_CFG_TX_TKID_KEEP BIT(24) ++#define MTK_WED_WPDMA_GLO_CFG_TX_DDONE_CHK_LAST BIT(25) + #define MTK_WED_WPDMA_GLO_CFG_TX_DMAD_DW3_PREV BIT(28) ++#define MTK_WED_WPDMA_GLO_CFG_TX_DDONE_CHK BIT(30) + + #define MTK_WED_WPDMA_RESET_IDX 0x50c + #define MTK_WED_WPDMA_RESET_IDX_TX GENMASK(3, 0) +@@ -250,9 +274,10 @@ struct mtk_wdma_desc { + #define MTK_WED_PCIE_INT_TRIGGER_STATUS BIT(16) + + #define MTK_WED_PCIE_INT_CTRL 0x57c +-#define MTK_WED_PCIE_INT_CTRL_MSK_EN_POLA BIT(20) +-#define MTK_WED_PCIE_INT_CTRL_SRC_SEL GENMASK(17, 16) + #define MTK_WED_PCIE_INT_CTRL_POLL_EN GENMASK(13, 12) ++#define MTK_WED_PCIE_INT_CTRL_SRC_SEL GENMASK(17, 16) ++#define MTK_WED_PCIE_INT_CTRL_MSK_EN_POLA BIT(20) ++#define MTK_WED_PCIE_INT_CTRL_MSK_IRQ_FILTER BIT(21) + + #define MTK_WED_WPDMA_CFG_BASE 0x580 + #define MTK_WED_WPDMA_CFG_INT_MASK 0x584 +@@ -286,6 +311,20 @@ struct mtk_wdma_desc { + #define MTK_WED_WPDMA_RX_D_PROCESSED_MIB(_n) (0x784 + (_n) * 4) + #define MTK_WED_WPDMA_RX_D_COHERENT_MIB 0x78c + ++#define MTK_WED_WPDMA_RX_D_PREF_CFG 0x7b4 ++#define MTK_WED_WPDMA_RX_D_PREF_EN BIT(0) ++#define MTK_WED_WPDMA_RX_D_PREF_BURST_SIZE GENMASK(12, 8) ++#define MTK_WED_WPDMA_RX_D_PREF_LOW_THRES GENMASK(21, 16) ++ ++#define MTK_WED_WPDMA_RX_D_PREF_RX0_SIDX 0x7b8 ++#define MTK_WED_WPDMA_RX_D_PREF_SIDX_IDX_CLR BIT(15) ++ ++#define MTK_WED_WPDMA_RX_D_PREF_RX1_SIDX 0x7bc ++ ++#define MTK_WED_WPDMA_RX_D_PREF_FIFO_CFG 0x7c0 ++#define MTK_WED_WPDMA_RX_D_PREF_FIFO_CFG_R0_CLR BIT(0) ++#define MTK_WED_WPDMA_RX_D_PREF_FIFO_CFG_R1_CLR BIT(16) ++ + #define MTK_WED_WDMA_RING_TX 0x800 + + #define MTK_WED_WDMA_TX_MIB 0x810 +@@ -293,6 +332,18 @@ struct mtk_wdma_desc { + #define MTK_WED_WDMA_RING_RX(_n) (0x900 + (_n) * 0x10) + #define MTK_WED_WDMA_RX_THRES(_n) (0x940 + (_n) * 0x4) + ++#define MTK_WED_WDMA_RX_PREF_CFG 0x950 ++#define MTK_WED_WDMA_RX_PREF_EN BIT(0) ++#define MTK_WED_WDMA_RX_PREF_BURST_SIZE GENMASK(12, 8) ++#define MTK_WED_WDMA_RX_PREF_LOW_THRES GENMASK(21, 16) ++#define MTK_WED_WDMA_RX_PREF_RX0_SIDX_CLR BIT(24) ++#define MTK_WED_WDMA_RX_PREF_RX1_SIDX_CLR BIT(25) ++#define MTK_WED_WDMA_RX_PREF_DDONE2_EN BIT(26) ++ ++#define MTK_WED_WDMA_RX_PREF_FIFO_CFG 0x95C ++#define MTK_WED_WDMA_RX_PREF_FIFO_RX0_CLR BIT(0) ++#define MTK_WED_WDMA_RX_PREF_FIFO_RX1_CLR BIT(16) ++ + #define MTK_WED_WDMA_GLO_CFG 0xa04 + #define MTK_WED_WDMA_GLO_CFG_TX_DRV_EN BIT(0) + #define MTK_WED_WDMA_GLO_CFG_TX_DDONE_CHK BIT(1) +@@ -325,6 +376,7 @@ struct mtk_wdma_desc { + #define MTK_WED_WDMA_INT_TRIGGER_RX_DONE GENMASK(17, 16) + + #define MTK_WED_WDMA_INT_CTRL 0xa2c ++#define MTK_WED_WDMA_INT_POLL_PRD GENMASK(7, 0) + #define MTK_WED_WDMA_INT_CTRL_POLL_SRC_SEL GENMASK(17, 16) + + #define MTK_WED_WDMA_CFG_BASE 0xaa0 +@@ -388,6 +440,18 @@ struct mtk_wdma_desc { + #define MTK_WDMA_INT_GRP1 0x250 + #define MTK_WDMA_INT_GRP2 0x254 + ++#define MTK_WDMA_PREF_TX_CFG 0x2d0 ++#define MTK_WDMA_PREF_TX_CFG_PREF_EN BIT(0) ++ ++#define MTK_WDMA_PREF_RX_CFG 0x2dc ++#define MTK_WDMA_PREF_RX_CFG_PREF_EN BIT(0) ++ ++#define MTK_WDMA_WRBK_TX_CFG 0x300 ++#define MTK_WDMA_WRBK_TX_CFG_WRBK_EN BIT(30) ++ ++#define MTK_WDMA_WRBK_RX_CFG 0x344 ++#define MTK_WDMA_WRBK_RX_CFG_WRBK_EN BIT(30) ++ + #define MTK_PCIE_MIRROR_MAP(n) ((n) ? 0x4 : 0x0) + #define MTK_PCIE_MIRROR_MAP_EN BIT(0) + #define MTK_PCIE_MIRROR_MAP_WED_ID BIT(1) +@@ -401,6 +465,30 @@ struct mtk_wdma_desc { + #define MTK_WED_RTQM_Q_DBG_BYPASS BIT(5) + #define MTK_WED_RTQM_TXDMAD_FPORT GENMASK(23, 20) + ++#define MTK_WED_RTQM_IGRS0_I2HW_DMAD_CNT 0xb1c ++#define MTK_WED_RTQM_IGRS0_I2H_DMAD_CNT(_n) (0xb20 + (_n) * 0x4) ++#define MTK_WED_RTQM_IGRS0_I2HW_PKT_CNT 0xb28 ++#define MTK_WED_RTQM_IGRS0_I2H_PKT_CNT(_n) (0xb2c + (_n) * 0x4) ++#define MTK_WED_RTQM_IGRS0_FDROP_CNT 0xb34 ++ ++#define MTK_WED_RTQM_IGRS1_I2HW_DMAD_CNT 0xb44 ++#define MTK_WED_RTQM_IGRS1_I2H_DMAD_CNT(_n) (0xb48 + (_n) * 0x4) ++#define MTK_WED_RTQM_IGRS1_I2HW_PKT_CNT 0xb50 ++#define MTK_WED_RTQM_IGRS1_I2H_PKT_CNT(_n) (0xb54 + (_n) * 0x4) ++#define MTK_WED_RTQM_IGRS1_FDROP_CNT 0xb5c ++ ++#define MTK_WED_RTQM_IGRS2_I2HW_DMAD_CNT 0xb6c ++#define MTK_WED_RTQM_IGRS2_I2H_DMAD_CNT(_n) (0xb70 + (_n) * 0x4) ++#define MTK_WED_RTQM_IGRS2_I2HW_PKT_CNT 0xb78 ++#define MTK_WED_RTQM_IGRS2_I2H_PKT_CNT(_n) (0xb7c + (_n) * 0x4) ++#define MTK_WED_RTQM_IGRS2_FDROP_CNT 0xb84 ++ ++#define MTK_WED_RTQM_IGRS3_I2HW_DMAD_CNT 0xb94 ++#define MTK_WED_RTQM_IGRS3_I2H_DMAD_CNT(_n) (0xb98 + (_n) * 0x4) ++#define MTK_WED_RTQM_IGRS3_I2HW_PKT_CNT 0xba0 ++#define MTK_WED_RTQM_IGRS3_I2H_PKT_CNT(_n) (0xba4 + (_n) * 0x4) ++#define MTK_WED_RTQM_IGRS3_FDROP_CNT 0xbac ++ + #define MTK_WED_RTQM_R2H_MIB(_n) (0xb70 + (_n) * 0x4) + #define MTK_WED_RTQM_R2Q_MIB(_n) (0xb78 + (_n) * 0x4) + #define MTK_WED_RTQM_Q2N_MIB 0xb80 +@@ -409,6 +497,24 @@ struct mtk_wdma_desc { + #define MTK_WED_RTQM_Q2B_MIB 0xb8c + #define MTK_WED_RTQM_PFDBK_MIB 0xb90 + ++#define MTK_WED_RTQM_ENQ_CFG0 0xbb8 ++#define MTK_WED_RTQM_ENQ_CFG_TXDMAD_FPORT GENMASK(15, 12) ++ ++#define MTK_WED_RTQM_FDROP_MIB 0xb84 ++#define MTK_WED_RTQM_ENQ_I2Q_DMAD_CNT 0xbbc ++#define MTK_WED_RTQM_ENQ_I2N_DMAD_CNT 0xbc0 ++#define MTK_WED_RTQM_ENQ_I2Q_PKT_CNT 0xbc4 ++#define MTK_WED_RTQM_ENQ_I2N_PKT_CNT 0xbc8 ++#define MTK_WED_RTQM_ENQ_USED_ENTRY_CNT 0xbcc ++#define MTK_WED_RTQM_ENQ_ERR_CNT 0xbd0 ++ ++#define MTK_WED_RTQM_DEQ_DMAD_CNT 0xbd8 ++#define MTK_WED_RTQM_DEQ_Q2I_DMAD_CNT 0xbdc ++#define MTK_WED_RTQM_DEQ_PKT_CNT 0xbe0 ++#define MTK_WED_RTQM_DEQ_Q2I_PKT_CNT 0xbe4 ++#define MTK_WED_RTQM_DEQ_USED_PFDBK_CNT 0xbe8 ++#define MTK_WED_RTQM_DEQ_ERR_CNT 0xbec ++ + #define MTK_WED_RROQM_GLO_CFG 0xc04 + #define MTK_WED_RROQM_RST_IDX 0xc08 + #define MTK_WED_RROQM_RST_IDX_MIOD BIT(0) +@@ -458,7 +564,116 @@ struct mtk_wdma_desc { + #define MTK_WED_RX_BM_INTF 0xd9c + #define MTK_WED_RX_BM_ERR_STS 0xda8 + ++#define MTK_RRO_IND_CMD_SIGNATURE 0xe00 ++#define MTK_RRO_IND_CMD_DMA_IDX GENMASK(11, 0) ++#define MTK_RRO_IND_CMD_MAGIC_CNT GENMASK(30, 28) ++ ++#define MTK_WED_IND_CMD_RX_CTRL0 0xe04 ++#define MTK_WED_IND_CMD_PROC_IDX GENMASK(11, 0) ++#define MTK_WED_IND_CMD_PREFETCH_FREE_CNT GENMASK(19, 16) ++#define MTK_WED_IND_CMD_MAGIC_CNT GENMASK(30, 28) ++ ++#define MTK_WED_IND_CMD_RX_CTRL1 0xe08 ++#define MTK_WED_IND_CMD_RX_CTRL2 0xe0c ++#define MTK_WED_IND_CMD_MAX_CNT GENMASK(11, 0) ++#define MTK_WED_IND_CMD_BASE_M GENMASK(19, 16) ++ ++#define MTK_WED_RRO_CFG0 0xe10 ++#define MTK_WED_RRO_CFG1 0xe14 ++#define MTK_WED_RRO_CFG1_MAX_WIN_SZ GENMASK(31, 29) ++#define MTK_WED_RRO_CFG1_ACK_SN_BASE_M GENMASK(19, 16) ++#define MTK_WED_RRO_CFG1_PARTICL_SE_ID GENMASK(11, 0) ++ ++#define MTK_WED_ADDR_ELEM_CFG0 0xe18 ++#define MTK_WED_ADDR_ELEM_CFG1 0xe1c ++#define MTK_WED_ADDR_ELEM_PREFETCH_FREE_CNT GENMASK(19, 16) ++ ++#define MTK_WED_ADDR_ELEM_TBL_CFG 0xe20 ++#define MTK_WED_ADDR_ELEM_TBL_OFFSET GENMASK(6, 0) ++#define MTK_WED_ADDR_ELEM_TBL_RD_RDY BIT(28) ++#define MTK_WED_ADDR_ELEM_TBL_WR_RDY BIT(29) ++#define MTK_WED_ADDR_ELEM_TBL_RD BIT(30) ++#define MTK_WED_ADDR_ELEM_TBL_WR BIT(31) ++ ++#define MTK_WED_RADDR_ELEM_TBL_WDATA 0xe24 ++#define MTK_WED_RADDR_ELEM_TBL_RDATA 0xe28 ++ ++#define MTK_WED_PN_CHECK_CFG 0xe30 ++#define MTK_WED_PN_CHECK_SE_ID GENMASK(11, 0) ++#define MTK_WED_PN_CHECK_RD_RDY BIT(28) ++#define MTK_WED_PN_CHECK_WR_RDY BIT(29) ++#define MTK_WED_PN_CHECK_RD BIT(30) ++#define MTK_WED_PN_CHECK_WR BIT(31) ++ ++#define MTK_WED_PN_CHECK_WDATA_M 0xe38 ++#define MTK_WED_PN_CHECK_IS_FIRST BIT(17) ++ ++#define MTK_WED_RRO_MSDU_PG_RING_CFG(_n) (0xe44 + (_n) * 0x8) ++ ++#define MTK_WED_RRO_MSDU_PG_RING2_CFG 0xe58 ++#define MTK_WED_RRO_MSDU_PG_DRV_CLR BIT(26) ++#define MTK_WED_RRO_MSDU_PG_DRV_EN BIT(31) ++ ++#define MTK_WED_RRO_MSDU_PG_CTRL0(_n) (0xe5c + (_n) * 0xc) ++#define MTK_WED_RRO_MSDU_PG_CTRL1(_n) (0xe60 + (_n) * 0xc) ++#define MTK_WED_RRO_MSDU_PG_CTRL2(_n) (0xe64 + (_n) * 0xc) ++ ++#define MTK_WED_RRO_RX_D_RX(_n) (0xe80 + (_n) * 0x10) ++ ++#define MTK_WED_RRO_RX_MAGIC_CNT BIT(13) ++ ++#define MTK_WED_RRO_RX_D_CFG(_n) (0xea0 + (_n) * 0x4) ++#define MTK_WED_RRO_RX_D_DRV_CLR BIT(26) ++#define MTK_WED_RRO_RX_D_DRV_EN BIT(31) ++ ++#define MTK_WED_RRO_PG_BM_RX_DMAM 0xeb0 ++#define MTK_WED_RRO_PG_BM_RX_SDL0 GENMASK(13, 0) ++ ++#define MTK_WED_RRO_PG_BM_BASE 0xeb4 ++#define MTK_WED_RRO_PG_BM_INIT_PTR 0xeb8 ++#define MTK_WED_RRO_PG_BM_SW_TAIL_IDX GENMASK(15, 0) ++#define MTK_WED_RRO_PG_BM_INIT_SW_TAIL_IDX BIT(16) ++ ++#define MTK_WED_WPDMA_INT_CTRL_RRO_RX 0xeec ++#define MTK_WED_WPDMA_INT_CTRL_RRO_RX0_EN BIT(0) ++#define MTK_WED_WPDMA_INT_CTRL_RRO_RX0_CLR BIT(1) ++#define MTK_WED_WPDMA_INT_CTRL_RRO_RX0_DONE_TRIG GENMASK(6, 2) ++#define MTK_WED_WPDMA_INT_CTRL_RRO_RX1_EN BIT(8) ++#define MTK_WED_WPDMA_INT_CTRL_RRO_RX1_CLR BIT(9) ++#define MTK_WED_WPDMA_INT_CTRL_RRO_RX1_DONE_TRIG GENMASK(14, 10) ++ ++#define MTK_WED_WPDMA_INT_CTRL_RRO_MSDU_PG 0xef4 ++#define MTK_WED_WPDMA_INT_CTRL_RRO_PG0_EN BIT(0) ++#define MTK_WED_WPDMA_INT_CTRL_RRO_PG0_CLR BIT(1) ++#define MTK_WED_WPDMA_INT_CTRL_RRO_PG0_DONE_TRIG GENMASK(6, 2) ++#define MTK_WED_WPDMA_INT_CTRL_RRO_PG1_EN BIT(8) ++#define MTK_WED_WPDMA_INT_CTRL_RRO_PG1_CLR BIT(9) ++#define MTK_WED_WPDMA_INT_CTRL_RRO_PG1_DONE_TRIG GENMASK(14, 10) ++#define MTK_WED_WPDMA_INT_CTRL_RRO_PG2_EN BIT(16) ++#define MTK_WED_WPDMA_INT_CTRL_RRO_PG2_CLR BIT(17) ++#define MTK_WED_WPDMA_INT_CTRL_RRO_PG2_DONE_TRIG GENMASK(22, 18) ++ ++#define MTK_WED_RX_IND_CMD_CNT0 0xf20 ++#define MTK_WED_RX_IND_CMD_DBG_CNT_EN BIT(31) ++ ++#define MTK_WED_RX_IND_CMD_CNT(_n) (0xf20 + (_n) * 0x4) ++#define MTK_WED_IND_CMD_MAGIC_CNT_FAIL_CNT GENMASK(15, 0) ++ ++#define MTK_WED_RX_ADDR_ELEM_CNT(_n) (0xf48 + (_n) * 0x4) ++#define MTK_WED_ADDR_ELEM_SIG_FAIL_CNT GENMASK(15, 0) ++#define MTK_WED_ADDR_ELEM_FIRST_SIG_FAIL_CNT GENMASK(31, 16) ++#define MTK_WED_ADDR_ELEM_ACKSN_CNT GENMASK(27, 0) ++ ++#define MTK_WED_RX_MSDU_PG_CNT(_n) (0xf5c + (_n) * 0x4) ++ ++#define MTK_WED_RX_PN_CHK_CNT 0xf70 ++#define MTK_WED_PN_CHK_FAIL_CNT GENMASK(15, 0) ++ + #define MTK_WED_WOCPU_VIEW_MIOD_BASE 0x8000 + #define MTK_WED_PCIE_INT_MASK 0x0 + ++#define MTK_WED_PCIE_BASE 0x11280000 ++#define MTK_WED_PCIE_BASE0 0x11300000 ++#define MTK_WED_PCIE_BASE1 0x11310000 ++#define MTK_WED_PCIE_BASE2 0x11290000 + #endif +--- a/drivers/net/ethernet/mediatek/mtk_wed_wo.h ++++ b/drivers/net/ethernet/mediatek/mtk_wed_wo.h +@@ -91,6 +91,8 @@ enum mtk_wed_dummy_cr_idx { + #define MT7981_FIRMWARE_WO "mediatek/mt7981_wo.bin" + #define MT7986_FIRMWARE_WO0 "mediatek/mt7986_wo_0.bin" + #define MT7986_FIRMWARE_WO1 "mediatek/mt7986_wo_1.bin" ++#define MT7988_FIRMWARE_WO0 "mediatek/mt7988_wo_0.bin" ++#define MT7988_FIRMWARE_WO1 "mediatek/mt7988_wo_1.bin" + + #define MTK_WO_MCU_CFG_LS_BASE 0 + #define MTK_WO_MCU_CFG_LS_HW_VER_ADDR (MTK_WO_MCU_CFG_LS_BASE + 0x000) +--- a/include/linux/soc/mediatek/mtk_wed.h ++++ b/include/linux/soc/mediatek/mtk_wed.h +@@ -139,6 +139,8 @@ struct mtk_wed_device { + u32 wpdma_rx; + + bool wcid_512; ++ bool hw_rro; ++ bool msi; + + u16 token_start; + unsigned int nbuf; +@@ -212,10 +214,12 @@ mtk_wed_device_attach(struct mtk_wed_dev + return ret; + } + +-static inline bool +-mtk_wed_get_rx_capa(struct mtk_wed_device *dev) ++static inline bool mtk_wed_get_rx_capa(struct mtk_wed_device *dev) + { + #ifdef CONFIG_NET_MEDIATEK_SOC_WED ++ if (dev->version == 3) ++ return dev->wlan.hw_rro; ++ + return dev->version != 1; + #else + return false; diff --git a/target/linux/generic/backport-6.1/752-15-v6.7-net-ethernet-mtk_wed-refactor-mtk_wed_check_wfdma_rx.patch b/target/linux/generic/backport-6.1/752-15-v6.7-net-ethernet-mtk_wed-refactor-mtk_wed_check_wfdma_rx.patch new file mode 100644 index 000000000..e91ae69d0 --- /dev/null +++ b/target/linux/generic/backport-6.1/752-15-v6.7-net-ethernet-mtk_wed-refactor-mtk_wed_check_wfdma_rx.patch @@ -0,0 +1,95 @@ +From: Lorenzo Bianconi +Date: Mon, 18 Sep 2023 12:29:14 +0200 +Subject: [PATCH] net: ethernet: mtk_wed: refactor mtk_wed_check_wfdma_rx_fill + routine + +Refactor mtk_wed_check_wfdma_rx_fill() in order to be reused adding HW +receive offload support for MT7988 SoC. + +Co-developed-by: Sujuan Chen +Signed-off-by: Sujuan Chen +Signed-off-by: Lorenzo Bianconi +Signed-off-by: Paolo Abeni +--- + +--- a/drivers/net/ethernet/mediatek/mtk_wed.c ++++ b/drivers/net/ethernet/mediatek/mtk_wed.c +@@ -585,22 +585,15 @@ mtk_wed_set_512_support(struct mtk_wed_d + } + } + +-#define MTK_WFMDA_RX_DMA_EN BIT(2) +-static void +-mtk_wed_check_wfdma_rx_fill(struct mtk_wed_device *dev, int idx) ++static int ++mtk_wed_check_wfdma_rx_fill(struct mtk_wed_device *dev, ++ struct mtk_wed_ring *ring) + { +- u32 val; + int i; + +- if (!(dev->rx_ring[idx].flags & MTK_WED_RING_CONFIGURED)) +- return; /* queue is not configured by mt76 */ +- + for (i = 0; i < 3; i++) { +- u32 cur_idx; ++ u32 cur_idx = readl(ring->wpdma + MTK_WED_RING_OFS_CPU_IDX); + +- cur_idx = wed_r32(dev, +- MTK_WED_WPDMA_RING_RX_DATA(idx) + +- MTK_WED_RING_OFS_CPU_IDX); + if (cur_idx == MTK_WED_RX_RING_SIZE - 1) + break; + +@@ -609,12 +602,10 @@ mtk_wed_check_wfdma_rx_fill(struct mtk_w + + if (i == 3) { + dev_err(dev->hw->dev, "rx dma enable failed\n"); +- return; ++ return -ETIMEDOUT; + } + +- val = wifi_r32(dev, dev->wlan.wpdma_rx_glo - dev->wlan.phy_base) | +- MTK_WFMDA_RX_DMA_EN; +- wifi_w32(dev, dev->wlan.wpdma_rx_glo - dev->wlan.phy_base, val); ++ return 0; + } + + static void +@@ -1545,6 +1536,7 @@ mtk_wed_configure_irq(struct mtk_wed_dev + wed_w32(dev, MTK_WED_INT_MASK, irq_mask); + } + ++#define MTK_WFMDA_RX_DMA_EN BIT(2) + static void + mtk_wed_dma_enable(struct mtk_wed_device *dev) + { +@@ -1632,8 +1624,26 @@ mtk_wed_dma_enable(struct mtk_wed_device + wdma_set(dev, MTK_WDMA_WRBK_TX_CFG, MTK_WDMA_WRBK_TX_CFG_WRBK_EN); + } + +- for (i = 0; i < MTK_WED_RX_QUEUES; i++) +- mtk_wed_check_wfdma_rx_fill(dev, i); ++ for (i = 0; i < MTK_WED_RX_QUEUES; i++) { ++ struct mtk_wed_ring *ring = &dev->rx_ring[i]; ++ u32 val; ++ ++ if (!(ring->flags & MTK_WED_RING_CONFIGURED)) ++ continue; /* queue is not configured by mt76 */ ++ ++ if (mtk_wed_check_wfdma_rx_fill(dev, ring)) { ++ dev_err(dev->hw->dev, ++ "rx_ring(%d) dma enable failed\n", i); ++ continue; ++ } ++ ++ val = wifi_r32(dev, ++ dev->wlan.wpdma_rx_glo - ++ dev->wlan.phy_base) | MTK_WFMDA_RX_DMA_EN; ++ wifi_w32(dev, ++ dev->wlan.wpdma_rx_glo - dev->wlan.phy_base, ++ val); ++ } + } + + static void diff --git a/target/linux/generic/backport-6.1/752-16-v6.7-net-ethernet-mtk_wed-introduce-partial-AMSDU-offload.patch b/target/linux/generic/backport-6.1/752-16-v6.7-net-ethernet-mtk_wed-introduce-partial-AMSDU-offload.patch new file mode 100644 index 000000000..6534d73d8 --- /dev/null +++ b/target/linux/generic/backport-6.1/752-16-v6.7-net-ethernet-mtk_wed-introduce-partial-AMSDU-offload.patch @@ -0,0 +1,465 @@ +From: Sujuan Chen +Date: Mon, 18 Sep 2023 12:29:15 +0200 +Subject: [PATCH] net: ethernet: mtk_wed: introduce partial AMSDU offload + support for MT7988 + +Introduce partial AMSDU offload support for MT7988 SoC in order to merge +in hw packets belonging to the same AMSDU before passing them to the +WLAN nic. + +Co-developed-by: Lorenzo Bianconi +Signed-off-by: Lorenzo Bianconi +Signed-off-by: Sujuan Chen +Signed-off-by: Paolo Abeni +--- + +--- a/drivers/net/ethernet/mediatek/mtk_ppe.c ++++ b/drivers/net/ethernet/mediatek/mtk_ppe.c +@@ -438,7 +438,8 @@ int mtk_foe_entry_set_pppoe(struct mtk_e + } + + int mtk_foe_entry_set_wdma(struct mtk_eth *eth, struct mtk_foe_entry *entry, +- int wdma_idx, int txq, int bss, int wcid) ++ int wdma_idx, int txq, int bss, int wcid, ++ bool amsdu_en) + { + struct mtk_foe_mac_info *l2 = mtk_foe_entry_l2(eth, entry); + u32 *ib2 = mtk_foe_entry_ib2(eth, entry); +@@ -450,6 +451,7 @@ int mtk_foe_entry_set_wdma(struct mtk_et + MTK_FOE_IB2_WDMA_WINFO_V2; + l2->w3info = FIELD_PREP(MTK_FOE_WINFO_WCID_V3, wcid) | + FIELD_PREP(MTK_FOE_WINFO_BSS_V3, bss); ++ l2->amsdu = FIELD_PREP(MTK_FOE_WINFO_AMSDU_EN, amsdu_en); + break; + case 2: + *ib2 &= ~MTK_FOE_IB2_PORT_MG_V2; +--- a/drivers/net/ethernet/mediatek/mtk_ppe.h ++++ b/drivers/net/ethernet/mediatek/mtk_ppe.h +@@ -88,13 +88,13 @@ enum { + #define MTK_FOE_WINFO_BSS_V3 GENMASK(23, 16) + #define MTK_FOE_WINFO_WCID_V3 GENMASK(15, 0) + +-#define MTK_FOE_WINFO_PAO_USR_INFO GENMASK(15, 0) +-#define MTK_FOE_WINFO_PAO_TID GENMASK(19, 16) +-#define MTK_FOE_WINFO_PAO_IS_FIXEDRATE BIT(20) +-#define MTK_FOE_WINFO_PAO_IS_PRIOR BIT(21) +-#define MTK_FOE_WINFO_PAO_IS_SP BIT(22) +-#define MTK_FOE_WINFO_PAO_HF BIT(23) +-#define MTK_FOE_WINFO_PAO_AMSDU_EN BIT(24) ++#define MTK_FOE_WINFO_AMSDU_USR_INFO GENMASK(15, 0) ++#define MTK_FOE_WINFO_AMSDU_TID GENMASK(19, 16) ++#define MTK_FOE_WINFO_AMSDU_IS_FIXEDRATE BIT(20) ++#define MTK_FOE_WINFO_AMSDU_IS_PRIOR BIT(21) ++#define MTK_FOE_WINFO_AMSDU_IS_SP BIT(22) ++#define MTK_FOE_WINFO_AMSDU_HF BIT(23) ++#define MTK_FOE_WINFO_AMSDU_EN BIT(24) + + enum { + MTK_FOE_STATE_INVALID, +@@ -123,7 +123,7 @@ struct mtk_foe_mac_info { + + /* netsys_v3 */ + u32 w3info; +- u32 wpao; ++ u32 amsdu; + }; + + /* software-only entry type */ +@@ -394,7 +394,8 @@ int mtk_foe_entry_set_vlan(struct mtk_et + int mtk_foe_entry_set_pppoe(struct mtk_eth *eth, struct mtk_foe_entry *entry, + int sid); + int mtk_foe_entry_set_wdma(struct mtk_eth *eth, struct mtk_foe_entry *entry, +- int wdma_idx, int txq, int bss, int wcid); ++ int wdma_idx, int txq, int bss, int wcid, ++ bool amsdu_en); + int mtk_foe_entry_set_queue(struct mtk_eth *eth, struct mtk_foe_entry *entry, + unsigned int queue); + int mtk_foe_entry_commit(struct mtk_ppe *ppe, struct mtk_flow_entry *entry); +--- a/drivers/net/ethernet/mediatek/mtk_ppe_offload.c ++++ b/drivers/net/ethernet/mediatek/mtk_ppe_offload.c +@@ -111,6 +111,7 @@ mtk_flow_get_wdma_info(struct net_device + info->queue = path->mtk_wdma.queue; + info->bss = path->mtk_wdma.bss; + info->wcid = path->mtk_wdma.wcid; ++ info->amsdu = path->mtk_wdma.amsdu; + + return 0; + } +@@ -192,7 +193,7 @@ mtk_flow_set_output_device(struct mtk_et + + if (mtk_flow_get_wdma_info(dev, dest_mac, &info) == 0) { + mtk_foe_entry_set_wdma(eth, foe, info.wdma_idx, info.queue, +- info.bss, info.wcid); ++ info.bss, info.wcid, info.amsdu); + if (mtk_is_netsys_v2_or_greater(eth)) { + switch (info.wdma_idx) { + case 0: +--- a/drivers/net/ethernet/mediatek/mtk_wed.c ++++ b/drivers/net/ethernet/mediatek/mtk_wed.c +@@ -29,6 +29,8 @@ + #define MTK_WED_RX_PAGE_BUF_PER_PAGE (PAGE_SIZE / 128) + #define MTK_WED_RX_RING_SIZE 1536 + #define MTK_WED_RX_PG_BM_CNT 8192 ++#define MTK_WED_AMSDU_BUF_SIZE (PAGE_SIZE << 4) ++#define MTK_WED_AMSDU_NPAGES 32 + + #define MTK_WED_TX_RING_SIZE 2048 + #define MTK_WED_WDMA_RING_SIZE 1024 +@@ -172,6 +174,23 @@ mtk_wdma_rx_reset(struct mtk_wed_device + return ret; + } + ++static u32 ++mtk_wed_check_busy(struct mtk_wed_device *dev, u32 reg, u32 mask) ++{ ++ return !!(wed_r32(dev, reg) & mask); ++} ++ ++static int ++mtk_wed_poll_busy(struct mtk_wed_device *dev, u32 reg, u32 mask) ++{ ++ int sleep = 15000; ++ int timeout = 100 * sleep; ++ u32 val; ++ ++ return read_poll_timeout(mtk_wed_check_busy, val, !val, sleep, ++ timeout, false, dev, reg, mask); ++} ++ + static void + mtk_wdma_tx_reset(struct mtk_wed_device *dev) + { +@@ -335,6 +354,118 @@ out: + } + + static int ++mtk_wed_amsdu_buffer_alloc(struct mtk_wed_device *dev) ++{ ++ struct mtk_wed_hw *hw = dev->hw; ++ struct mtk_wed_amsdu *wed_amsdu; ++ int i; ++ ++ if (!mtk_wed_is_v3_or_greater(hw)) ++ return 0; ++ ++ wed_amsdu = devm_kcalloc(hw->dev, MTK_WED_AMSDU_NPAGES, ++ sizeof(*wed_amsdu), GFP_KERNEL); ++ if (!wed_amsdu) ++ return -ENOMEM; ++ ++ for (i = 0; i < MTK_WED_AMSDU_NPAGES; i++) { ++ void *ptr; ++ ++ /* each segment is 64K */ ++ ptr = (void *)__get_free_pages(GFP_KERNEL | __GFP_NOWARN | ++ __GFP_ZERO | __GFP_COMP | ++ GFP_DMA32, ++ get_order(MTK_WED_AMSDU_BUF_SIZE)); ++ if (!ptr) ++ goto error; ++ ++ wed_amsdu[i].txd = ptr; ++ wed_amsdu[i].txd_phy = dma_map_single(hw->dev, ptr, ++ MTK_WED_AMSDU_BUF_SIZE, ++ DMA_TO_DEVICE); ++ if (dma_mapping_error(hw->dev, wed_amsdu[i].txd_phy)) ++ goto error; ++ } ++ dev->hw->wed_amsdu = wed_amsdu; ++ ++ return 0; ++ ++error: ++ for (i--; i >= 0; i--) ++ dma_unmap_single(hw->dev, wed_amsdu[i].txd_phy, ++ MTK_WED_AMSDU_BUF_SIZE, DMA_TO_DEVICE); ++ return -ENOMEM; ++} ++ ++static void ++mtk_wed_amsdu_free_buffer(struct mtk_wed_device *dev) ++{ ++ struct mtk_wed_amsdu *wed_amsdu = dev->hw->wed_amsdu; ++ int i; ++ ++ if (!wed_amsdu) ++ return; ++ ++ for (i = 0; i < MTK_WED_AMSDU_NPAGES; i++) { ++ dma_unmap_single(dev->hw->dev, wed_amsdu[i].txd_phy, ++ MTK_WED_AMSDU_BUF_SIZE, DMA_TO_DEVICE); ++ free_pages((unsigned long)wed_amsdu[i].txd, ++ get_order(MTK_WED_AMSDU_BUF_SIZE)); ++ } ++} ++ ++static int ++mtk_wed_amsdu_init(struct mtk_wed_device *dev) ++{ ++ struct mtk_wed_amsdu *wed_amsdu = dev->hw->wed_amsdu; ++ int i, ret; ++ ++ if (!wed_amsdu) ++ return 0; ++ ++ for (i = 0; i < MTK_WED_AMSDU_NPAGES; i++) ++ wed_w32(dev, MTK_WED_AMSDU_HIFTXD_BASE_L(i), ++ wed_amsdu[i].txd_phy); ++ ++ /* init all sta parameter */ ++ wed_w32(dev, MTK_WED_AMSDU_STA_INFO_INIT, MTK_WED_AMSDU_STA_RMVL | ++ MTK_WED_AMSDU_STA_WTBL_HDRT_MODE | ++ FIELD_PREP(MTK_WED_AMSDU_STA_MAX_AMSDU_LEN, ++ dev->wlan.amsdu_max_len >> 8) | ++ FIELD_PREP(MTK_WED_AMSDU_STA_MAX_AMSDU_NUM, ++ dev->wlan.amsdu_max_subframes)); ++ ++ wed_w32(dev, MTK_WED_AMSDU_STA_INFO, MTK_WED_AMSDU_STA_INFO_DO_INIT); ++ ++ ret = mtk_wed_poll_busy(dev, MTK_WED_AMSDU_STA_INFO, ++ MTK_WED_AMSDU_STA_INFO_DO_INIT); ++ if (ret) { ++ dev_err(dev->hw->dev, "amsdu initialization failed\n"); ++ return ret; ++ } ++ ++ /* init partial amsdu offload txd src */ ++ wed_set(dev, MTK_WED_AMSDU_HIFTXD_CFG, ++ FIELD_PREP(MTK_WED_AMSDU_HIFTXD_SRC, dev->hw->index)); ++ ++ /* init qmem */ ++ wed_set(dev, MTK_WED_AMSDU_PSE, MTK_WED_AMSDU_PSE_RESET); ++ ret = mtk_wed_poll_busy(dev, MTK_WED_MON_AMSDU_QMEM_STS1, BIT(29)); ++ if (ret) { ++ pr_info("%s: amsdu qmem initialization failed\n", __func__); ++ return ret; ++ } ++ ++ /* eagle E1 PCIE1 tx ring 22 flow control issue */ ++ if (dev->wlan.id == 0x7991) ++ wed_clr(dev, MTK_WED_AMSDU_FIFO, MTK_WED_AMSDU_IS_PRIOR0_RING); ++ ++ wed_set(dev, MTK_WED_CTRL, MTK_WED_CTRL_TX_AMSDU_EN); ++ ++ return 0; ++} ++ ++static int + mtk_wed_tx_buffer_alloc(struct mtk_wed_device *dev) + { + u32 desc_size = dev->hw->soc->tx_ring_desc_size; +@@ -708,6 +839,7 @@ __mtk_wed_detach(struct mtk_wed_device * + + mtk_wdma_rx_reset(dev); + mtk_wed_reset(dev, MTK_WED_RESET_WED); ++ mtk_wed_amsdu_free_buffer(dev); + mtk_wed_free_tx_buffer(dev); + mtk_wed_free_tx_rings(dev); + +@@ -1128,23 +1260,6 @@ mtk_wed_ring_reset(struct mtk_wed_ring * + } + } + +-static u32 +-mtk_wed_check_busy(struct mtk_wed_device *dev, u32 reg, u32 mask) +-{ +- return !!(wed_r32(dev, reg) & mask); +-} +- +-static int +-mtk_wed_poll_busy(struct mtk_wed_device *dev, u32 reg, u32 mask) +-{ +- int sleep = 15000; +- int timeout = 100 * sleep; +- u32 val; +- +- return read_poll_timeout(mtk_wed_check_busy, val, !val, sleep, +- timeout, false, dev, reg, mask); +-} +- + static int + mtk_wed_rx_reset(struct mtk_wed_device *dev) + { +@@ -1691,6 +1806,7 @@ mtk_wed_start(struct mtk_wed_device *dev + } + + mtk_wed_set_512_support(dev, dev->wlan.wcid_512); ++ mtk_wed_amsdu_init(dev); + + mtk_wed_dma_enable(dev); + dev->running = true; +@@ -1747,6 +1863,10 @@ mtk_wed_attach(struct mtk_wed_device *de + if (ret) + goto out; + ++ ret = mtk_wed_amsdu_buffer_alloc(dev); ++ if (ret) ++ goto out; ++ + if (mtk_wed_get_rx_capa(dev)) { + ret = mtk_wed_rro_alloc(dev); + if (ret) +--- a/drivers/net/ethernet/mediatek/mtk_wed.h ++++ b/drivers/net/ethernet/mediatek/mtk_wed.h +@@ -25,6 +25,11 @@ struct mtk_wed_soc_data { + u32 wdma_desc_size; + }; + ++struct mtk_wed_amsdu { ++ void *txd; ++ dma_addr_t txd_phy; ++}; ++ + struct mtk_wed_hw { + const struct mtk_wed_soc_data *soc; + struct device_node *node; +@@ -38,6 +43,7 @@ struct mtk_wed_hw { + struct dentry *debugfs_dir; + struct mtk_wed_device *wed_dev; + struct mtk_wed_wo *wed_wo; ++ struct mtk_wed_amsdu *wed_amsdu; + u32 pcie_base; + u32 debugfs_reg; + u32 num_flows; +@@ -52,6 +58,7 @@ struct mtk_wdma_info { + u8 queue; + u16 wcid; + u8 bss; ++ u8 amsdu; + }; + + #ifdef CONFIG_NET_MEDIATEK_SOC_WED +--- a/drivers/net/ethernet/mediatek/mtk_wed_regs.h ++++ b/drivers/net/ethernet/mediatek/mtk_wed_regs.h +@@ -672,6 +672,82 @@ struct mtk_wdma_desc { + #define MTK_WED_WOCPU_VIEW_MIOD_BASE 0x8000 + #define MTK_WED_PCIE_INT_MASK 0x0 + ++#define MTK_WED_AMSDU_FIFO 0x1800 ++#define MTK_WED_AMSDU_IS_PRIOR0_RING BIT(10) ++ ++#define MTK_WED_AMSDU_STA_INFO 0x01810 ++#define MTK_WED_AMSDU_STA_INFO_DO_INIT BIT(0) ++#define MTK_WED_AMSDU_STA_INFO_SET_INIT BIT(1) ++ ++#define MTK_WED_AMSDU_STA_INFO_INIT 0x01814 ++#define MTK_WED_AMSDU_STA_WTBL_HDRT_MODE BIT(0) ++#define MTK_WED_AMSDU_STA_RMVL BIT(1) ++#define MTK_WED_AMSDU_STA_MAX_AMSDU_LEN GENMASK(7, 2) ++#define MTK_WED_AMSDU_STA_MAX_AMSDU_NUM GENMASK(11, 8) ++ ++#define MTK_WED_AMSDU_HIFTXD_BASE_L(_n) (0x1980 + (_n) * 0x4) ++ ++#define MTK_WED_AMSDU_PSE 0x1910 ++#define MTK_WED_AMSDU_PSE_RESET BIT(16) ++ ++#define MTK_WED_AMSDU_HIFTXD_CFG 0x1968 ++#define MTK_WED_AMSDU_HIFTXD_SRC GENMASK(16, 15) ++ ++#define MTK_WED_MON_AMSDU_FIFO_DMAD 0x1a34 ++ ++#define MTK_WED_MON_AMSDU_ENG_DMAD(_n) (0x1a80 + (_n) * 0x50) ++#define MTK_WED_MON_AMSDU_ENG_QFPL(_n) (0x1a84 + (_n) * 0x50) ++#define MTK_WED_MON_AMSDU_ENG_QENI(_n) (0x1a88 + (_n) * 0x50) ++#define MTK_WED_MON_AMSDU_ENG_QENO(_n) (0x1a8c + (_n) * 0x50) ++#define MTK_WED_MON_AMSDU_ENG_MERG(_n) (0x1a90 + (_n) * 0x50) ++ ++#define MTK_WED_MON_AMSDU_ENG_CNT8(_n) (0x1a94 + (_n) * 0x50) ++#define MTK_WED_AMSDU_ENG_MAX_QGPP_CNT GENMASK(10, 0) ++#define MTK_WED_AMSDU_ENG_MAX_PL_CNT GENMASK(27, 16) ++ ++#define MTK_WED_MON_AMSDU_ENG_CNT9(_n) (0x1a98 + (_n) * 0x50) ++#define MTK_WED_AMSDU_ENG_CUR_ENTRY GENMASK(10, 0) ++#define MTK_WED_AMSDU_ENG_MAX_BUF_MERGED GENMASK(20, 16) ++#define MTK_WED_AMSDU_ENG_MAX_MSDU_MERGED GENMASK(28, 24) ++ ++#define MTK_WED_MON_AMSDU_QMEM_STS1 0x1e04 ++ ++#define MTK_WED_MON_AMSDU_QMEM_CNT(_n) (0x1e0c + (_n) * 0x4) ++#define MTK_WED_AMSDU_QMEM_FQ_CNT GENMASK(27, 16) ++#define MTK_WED_AMSDU_QMEM_SP_QCNT GENMASK(11, 0) ++#define MTK_WED_AMSDU_QMEM_TID0_QCNT GENMASK(27, 16) ++#define MTK_WED_AMSDU_QMEM_TID1_QCNT GENMASK(11, 0) ++#define MTK_WED_AMSDU_QMEM_TID2_QCNT GENMASK(27, 16) ++#define MTK_WED_AMSDU_QMEM_TID3_QCNT GENMASK(11, 0) ++#define MTK_WED_AMSDU_QMEM_TID4_QCNT GENMASK(27, 16) ++#define MTK_WED_AMSDU_QMEM_TID5_QCNT GENMASK(11, 0) ++#define MTK_WED_AMSDU_QMEM_TID6_QCNT GENMASK(27, 16) ++#define MTK_WED_AMSDU_QMEM_TID7_QCNT GENMASK(11, 0) ++ ++#define MTK_WED_MON_AMSDU_QMEM_PTR(_n) (0x1e20 + (_n) * 0x4) ++#define MTK_WED_AMSDU_QMEM_FQ_HEAD GENMASK(27, 16) ++#define MTK_WED_AMSDU_QMEM_SP_QHEAD GENMASK(11, 0) ++#define MTK_WED_AMSDU_QMEM_TID0_QHEAD GENMASK(27, 16) ++#define MTK_WED_AMSDU_QMEM_TID1_QHEAD GENMASK(11, 0) ++#define MTK_WED_AMSDU_QMEM_TID2_QHEAD GENMASK(27, 16) ++#define MTK_WED_AMSDU_QMEM_TID3_QHEAD GENMASK(11, 0) ++#define MTK_WED_AMSDU_QMEM_TID4_QHEAD GENMASK(27, 16) ++#define MTK_WED_AMSDU_QMEM_TID5_QHEAD GENMASK(11, 0) ++#define MTK_WED_AMSDU_QMEM_TID6_QHEAD GENMASK(27, 16) ++#define MTK_WED_AMSDU_QMEM_TID7_QHEAD GENMASK(11, 0) ++#define MTK_WED_AMSDU_QMEM_FQ_TAIL GENMASK(27, 16) ++#define MTK_WED_AMSDU_QMEM_SP_QTAIL GENMASK(11, 0) ++#define MTK_WED_AMSDU_QMEM_TID0_QTAIL GENMASK(27, 16) ++#define MTK_WED_AMSDU_QMEM_TID1_QTAIL GENMASK(11, 0) ++#define MTK_WED_AMSDU_QMEM_TID2_QTAIL GENMASK(27, 16) ++#define MTK_WED_AMSDU_QMEM_TID3_QTAIL GENMASK(11, 0) ++#define MTK_WED_AMSDU_QMEM_TID4_QTAIL GENMASK(27, 16) ++#define MTK_WED_AMSDU_QMEM_TID5_QTAIL GENMASK(11, 0) ++#define MTK_WED_AMSDU_QMEM_TID6_QTAIL GENMASK(27, 16) ++#define MTK_WED_AMSDU_QMEM_TID7_QTAIL GENMASK(11, 0) ++ ++#define MTK_WED_MON_AMSDU_HIFTXD_FETCH_MSDU(_n) (0x1ec4 + (_n) * 0x4) ++ + #define MTK_WED_PCIE_BASE 0x11280000 + #define MTK_WED_PCIE_BASE0 0x11300000 + #define MTK_WED_PCIE_BASE1 0x11310000 +--- a/include/linux/netdevice.h ++++ b/include/linux/netdevice.h +@@ -928,6 +928,7 @@ struct net_device_path { + u8 queue; + u16 wcid; + u8 bss; ++ u8 amsdu; + } mtk_wdma; + }; + }; +--- a/include/linux/soc/mediatek/mtk_wed.h ++++ b/include/linux/soc/mediatek/mtk_wed.h +@@ -129,6 +129,7 @@ struct mtk_wed_device { + enum mtk_wed_bus_tye bus_type; + void __iomem *base; + u32 phy_base; ++ u32 id; + + u32 wpdma_phys; + u32 wpdma_int; +@@ -147,10 +148,12 @@ struct mtk_wed_device { + unsigned int rx_nbuf; + unsigned int rx_npkt; + unsigned int rx_size; ++ unsigned int amsdu_max_len; + + u8 tx_tbit[MTK_WED_TX_QUEUES]; + u8 rx_tbit[MTK_WED_RX_QUEUES]; + u8 txfree_tbit; ++ u8 amsdu_max_subframes; + + u32 (*init_buf)(void *ptr, dma_addr_t phys, int token_id); + int (*offload_enable)(struct mtk_wed_device *wed); +@@ -224,6 +227,15 @@ static inline bool mtk_wed_get_rx_capa(s + #else + return false; + #endif ++} ++ ++static inline bool mtk_wed_is_amsdu_supported(struct mtk_wed_device *dev) ++{ ++#ifdef CONFIG_NET_MEDIATEK_SOC_WED ++ return dev->version == 3; ++#else ++ return false; ++#endif + } + + #ifdef CONFIG_NET_MEDIATEK_SOC_WED diff --git a/target/linux/generic/backport-6.1/752-17-v6.7-net-ethernet-mtk_wed-introduce-hw_rro-support-for-MT.patch b/target/linux/generic/backport-6.1/752-17-v6.7-net-ethernet-mtk_wed-introduce-hw_rro-support-for-MT.patch new file mode 100644 index 000000000..0cf4c1887 --- /dev/null +++ b/target/linux/generic/backport-6.1/752-17-v6.7-net-ethernet-mtk_wed-introduce-hw_rro-support-for-MT.patch @@ -0,0 +1,483 @@ +From: Sujuan Chen +Date: Mon, 18 Sep 2023 12:29:16 +0200 +Subject: [PATCH] net: ethernet: mtk_wed: introduce hw_rro support for MT7988 + +MT7988 SoC support 802.11 receive reordering offload in hw while +MT7986 SoC implements it through the firmware running on the mcu. + +Co-developed-by: Lorenzo Bianconi +Signed-off-by: Lorenzo Bianconi +Signed-off-by: Sujuan Chen +Signed-off-by: Paolo Abeni +--- + +--- a/drivers/net/ethernet/mediatek/mtk_wed.c ++++ b/drivers/net/ethernet/mediatek/mtk_wed.c +@@ -26,7 +26,7 @@ + #define MTK_WED_BUF_SIZE 2048 + #define MTK_WED_PAGE_BUF_SIZE 128 + #define MTK_WED_BUF_PER_PAGE (PAGE_SIZE / 2048) +-#define MTK_WED_RX_PAGE_BUF_PER_PAGE (PAGE_SIZE / 128) ++#define MTK_WED_RX_BUF_PER_PAGE (PAGE_SIZE / MTK_WED_PAGE_BUF_SIZE) + #define MTK_WED_RX_RING_SIZE 1536 + #define MTK_WED_RX_PG_BM_CNT 8192 + #define MTK_WED_AMSDU_BUF_SIZE (PAGE_SIZE << 4) +@@ -596,6 +596,68 @@ free_pagelist: + } + + static int ++mtk_wed_hwrro_buffer_alloc(struct mtk_wed_device *dev) ++{ ++ int n_pages = MTK_WED_RX_PG_BM_CNT / MTK_WED_RX_BUF_PER_PAGE; ++ struct mtk_wed_buf *page_list; ++ struct mtk_wed_bm_desc *desc; ++ dma_addr_t desc_phys; ++ int i, page_idx = 0; ++ ++ if (!dev->wlan.hw_rro) ++ return 0; ++ ++ page_list = kcalloc(n_pages, sizeof(*page_list), GFP_KERNEL); ++ if (!page_list) ++ return -ENOMEM; ++ ++ dev->hw_rro.size = dev->wlan.rx_nbuf & ~(MTK_WED_BUF_PER_PAGE - 1); ++ dev->hw_rro.pages = page_list; ++ desc = dma_alloc_coherent(dev->hw->dev, ++ dev->wlan.rx_nbuf * sizeof(*desc), ++ &desc_phys, GFP_KERNEL); ++ if (!desc) ++ return -ENOMEM; ++ ++ dev->hw_rro.desc = desc; ++ dev->hw_rro.desc_phys = desc_phys; ++ ++ for (i = 0; i < MTK_WED_RX_PG_BM_CNT; i += MTK_WED_RX_BUF_PER_PAGE) { ++ dma_addr_t page_phys, buf_phys; ++ struct page *page; ++ int s; ++ ++ page = __dev_alloc_page(GFP_KERNEL); ++ if (!page) ++ return -ENOMEM; ++ ++ page_phys = dma_map_page(dev->hw->dev, page, 0, PAGE_SIZE, ++ DMA_BIDIRECTIONAL); ++ if (dma_mapping_error(dev->hw->dev, page_phys)) { ++ __free_page(page); ++ return -ENOMEM; ++ } ++ ++ page_list[page_idx].p = page; ++ page_list[page_idx++].phy_addr = page_phys; ++ dma_sync_single_for_cpu(dev->hw->dev, page_phys, PAGE_SIZE, ++ DMA_BIDIRECTIONAL); ++ ++ buf_phys = page_phys; ++ for (s = 0; s < MTK_WED_RX_BUF_PER_PAGE; s++) { ++ desc->buf0 = cpu_to_le32(buf_phys); ++ buf_phys += MTK_WED_PAGE_BUF_SIZE; ++ desc++; ++ } ++ ++ dma_sync_single_for_device(dev->hw->dev, page_phys, PAGE_SIZE, ++ DMA_BIDIRECTIONAL); ++ } ++ ++ return 0; ++} ++ ++static int + mtk_wed_rx_buffer_alloc(struct mtk_wed_device *dev) + { + struct mtk_wed_bm_desc *desc; +@@ -612,7 +674,42 @@ mtk_wed_rx_buffer_alloc(struct mtk_wed_d + dev->rx_buf_ring.desc_phys = desc_phys; + dev->wlan.init_rx_buf(dev, dev->wlan.rx_npkt); + +- return 0; ++ return mtk_wed_hwrro_buffer_alloc(dev); ++} ++ ++static void ++mtk_wed_hwrro_free_buffer(struct mtk_wed_device *dev) ++{ ++ struct mtk_wed_buf *page_list = dev->hw_rro.pages; ++ struct mtk_wed_bm_desc *desc = dev->hw_rro.desc; ++ int i, page_idx = 0; ++ ++ if (!dev->wlan.hw_rro) ++ return; ++ ++ if (!page_list) ++ return; ++ ++ if (!desc) ++ goto free_pagelist; ++ ++ for (i = 0; i < MTK_WED_RX_PG_BM_CNT; i += MTK_WED_RX_BUF_PER_PAGE) { ++ dma_addr_t buf_addr = page_list[page_idx].phy_addr; ++ void *page = page_list[page_idx++].p; ++ ++ if (!page) ++ break; ++ ++ dma_unmap_page(dev->hw->dev, buf_addr, PAGE_SIZE, ++ DMA_BIDIRECTIONAL); ++ __free_page(page); ++ } ++ ++ dma_free_coherent(dev->hw->dev, dev->hw_rro.size * sizeof(*desc), ++ desc, dev->hw_rro.desc_phys); ++ ++free_pagelist: ++ kfree(page_list); + } + + static void +@@ -626,6 +723,28 @@ mtk_wed_free_rx_buffer(struct mtk_wed_de + dev->wlan.release_rx_buf(dev); + dma_free_coherent(dev->hw->dev, dev->rx_buf_ring.size * sizeof(*desc), + desc, dev->rx_buf_ring.desc_phys); ++ ++ mtk_wed_hwrro_free_buffer(dev); ++} ++ ++static void ++mtk_wed_hwrro_init(struct mtk_wed_device *dev) ++{ ++ if (!mtk_wed_get_rx_capa(dev) || !dev->wlan.hw_rro) ++ return; ++ ++ wed_set(dev, MTK_WED_RRO_PG_BM_RX_DMAM, ++ FIELD_PREP(MTK_WED_RRO_PG_BM_RX_SDL0, 128)); ++ ++ wed_w32(dev, MTK_WED_RRO_PG_BM_BASE, dev->hw_rro.desc_phys); ++ ++ wed_w32(dev, MTK_WED_RRO_PG_BM_INIT_PTR, ++ MTK_WED_RRO_PG_BM_INIT_SW_TAIL_IDX | ++ FIELD_PREP(MTK_WED_RRO_PG_BM_SW_TAIL_IDX, ++ MTK_WED_RX_PG_BM_CNT)); ++ ++ /* enable rx_page_bm to fetch dmad */ ++ wed_set(dev, MTK_WED_CTRL, MTK_WED_CTRL_WED_RX_PG_BM_EN); + } + + static void +@@ -639,6 +758,8 @@ mtk_wed_rx_buffer_hw_init(struct mtk_wed + wed_w32(dev, MTK_WED_RX_BM_DYN_ALLOC_TH, + FIELD_PREP(MTK_WED_RX_BM_DYN_ALLOC_TH_H, 0xffff)); + wed_set(dev, MTK_WED_CTRL, MTK_WED_CTRL_WED_RX_BM_EN); ++ ++ mtk_wed_hwrro_init(dev); + } + + static void +@@ -934,6 +1055,8 @@ mtk_wed_bus_init(struct mtk_wed_device * + static void + mtk_wed_set_wpdma(struct mtk_wed_device *dev) + { ++ int i; ++ + if (mtk_wed_is_v1(dev->hw)) { + wed_w32(dev, MTK_WED_WPDMA_CFG_BASE, dev->wlan.wpdma_phys); + return; +@@ -951,6 +1074,15 @@ mtk_wed_set_wpdma(struct mtk_wed_device + + wed_w32(dev, MTK_WED_WPDMA_RX_GLO_CFG, dev->wlan.wpdma_rx_glo); + wed_w32(dev, dev->hw->soc->regmap.wpdma_rx_ring0, dev->wlan.wpdma_rx); ++ ++ if (!dev->wlan.hw_rro) ++ return; ++ ++ wed_w32(dev, MTK_WED_RRO_RX_D_CFG(0), dev->wlan.wpdma_rx_rro[0]); ++ wed_w32(dev, MTK_WED_RRO_RX_D_CFG(1), dev->wlan.wpdma_rx_rro[1]); ++ for (i = 0; i < MTK_WED_RX_PAGE_QUEUES; i++) ++ wed_w32(dev, MTK_WED_RRO_MSDU_PG_RING_CFG(i), ++ dev->wlan.wpdma_rx_pg + i * 0x10); + } + + static void +@@ -1762,6 +1894,165 @@ mtk_wed_dma_enable(struct mtk_wed_device + } + + static void ++mtk_wed_start_hw_rro(struct mtk_wed_device *dev, u32 irq_mask, bool reset) ++{ ++ int i; ++ ++ wed_w32(dev, MTK_WED_WPDMA_INT_MASK, irq_mask); ++ wed_w32(dev, MTK_WED_INT_MASK, irq_mask); ++ ++ if (!mtk_wed_get_rx_capa(dev) || !dev->wlan.hw_rro) ++ return; ++ ++ wed_set(dev, MTK_WED_RRO_RX_D_CFG(2), MTK_WED_RRO_MSDU_PG_DRV_CLR); ++ wed_w32(dev, MTK_WED_RRO_MSDU_PG_RING2_CFG, ++ MTK_WED_RRO_MSDU_PG_DRV_CLR); ++ ++ wed_w32(dev, MTK_WED_WPDMA_INT_CTRL_RRO_RX, ++ MTK_WED_WPDMA_INT_CTRL_RRO_RX0_EN | ++ MTK_WED_WPDMA_INT_CTRL_RRO_RX0_CLR | ++ MTK_WED_WPDMA_INT_CTRL_RRO_RX1_EN | ++ MTK_WED_WPDMA_INT_CTRL_RRO_RX1_CLR | ++ FIELD_PREP(MTK_WED_WPDMA_INT_CTRL_RRO_RX0_DONE_TRIG, ++ dev->wlan.rro_rx_tbit[0]) | ++ FIELD_PREP(MTK_WED_WPDMA_INT_CTRL_RRO_RX1_DONE_TRIG, ++ dev->wlan.rro_rx_tbit[1])); ++ ++ wed_w32(dev, MTK_WED_WPDMA_INT_CTRL_RRO_MSDU_PG, ++ MTK_WED_WPDMA_INT_CTRL_RRO_PG0_EN | ++ MTK_WED_WPDMA_INT_CTRL_RRO_PG0_CLR | ++ MTK_WED_WPDMA_INT_CTRL_RRO_PG1_EN | ++ MTK_WED_WPDMA_INT_CTRL_RRO_PG1_CLR | ++ MTK_WED_WPDMA_INT_CTRL_RRO_PG2_EN | ++ MTK_WED_WPDMA_INT_CTRL_RRO_PG2_CLR | ++ FIELD_PREP(MTK_WED_WPDMA_INT_CTRL_RRO_PG0_DONE_TRIG, ++ dev->wlan.rx_pg_tbit[0]) | ++ FIELD_PREP(MTK_WED_WPDMA_INT_CTRL_RRO_PG1_DONE_TRIG, ++ dev->wlan.rx_pg_tbit[1]) | ++ FIELD_PREP(MTK_WED_WPDMA_INT_CTRL_RRO_PG2_DONE_TRIG, ++ dev->wlan.rx_pg_tbit[2])); ++ ++ /* RRO_MSDU_PG_RING2_CFG1_FLD_DRV_EN should be enabled after ++ * WM FWDL completed, otherwise RRO_MSDU_PG ring may broken ++ */ ++ wed_set(dev, MTK_WED_RRO_MSDU_PG_RING2_CFG, ++ MTK_WED_RRO_MSDU_PG_DRV_EN); ++ ++ for (i = 0; i < MTK_WED_RX_QUEUES; i++) { ++ struct mtk_wed_ring *ring = &dev->rx_rro_ring[i]; ++ ++ if (!(ring->flags & MTK_WED_RING_CONFIGURED)) ++ continue; ++ ++ if (mtk_wed_check_wfdma_rx_fill(dev, ring)) ++ dev_err(dev->hw->dev, ++ "rx_rro_ring(%d) initialization failed\n", i); ++ } ++ ++ for (i = 0; i < MTK_WED_RX_PAGE_QUEUES; i++) { ++ struct mtk_wed_ring *ring = &dev->rx_page_ring[i]; ++ ++ if (!(ring->flags & MTK_WED_RING_CONFIGURED)) ++ continue; ++ ++ if (mtk_wed_check_wfdma_rx_fill(dev, ring)) ++ dev_err(dev->hw->dev, ++ "rx_page_ring(%d) initialization failed\n", i); ++ } ++} ++ ++static void ++mtk_wed_rro_rx_ring_setup(struct mtk_wed_device *dev, int idx, ++ void __iomem *regs) ++{ ++ struct mtk_wed_ring *ring = &dev->rx_rro_ring[idx]; ++ ++ ring->wpdma = regs; ++ wed_w32(dev, MTK_WED_RRO_RX_D_RX(idx) + MTK_WED_RING_OFS_BASE, ++ readl(regs)); ++ wed_w32(dev, MTK_WED_RRO_RX_D_RX(idx) + MTK_WED_RING_OFS_COUNT, ++ readl(regs + MTK_WED_RING_OFS_COUNT)); ++ ring->flags |= MTK_WED_RING_CONFIGURED; ++} ++ ++static void ++mtk_wed_msdu_pg_rx_ring_setup(struct mtk_wed_device *dev, int idx, void __iomem *regs) ++{ ++ struct mtk_wed_ring *ring = &dev->rx_page_ring[idx]; ++ ++ ring->wpdma = regs; ++ wed_w32(dev, MTK_WED_RRO_MSDU_PG_CTRL0(idx) + MTK_WED_RING_OFS_BASE, ++ readl(regs)); ++ wed_w32(dev, MTK_WED_RRO_MSDU_PG_CTRL0(idx) + MTK_WED_RING_OFS_COUNT, ++ readl(regs + MTK_WED_RING_OFS_COUNT)); ++ ring->flags |= MTK_WED_RING_CONFIGURED; ++} ++ ++static int ++mtk_wed_ind_rx_ring_setup(struct mtk_wed_device *dev, void __iomem *regs) ++{ ++ struct mtk_wed_ring *ring = &dev->ind_cmd_ring; ++ u32 val = readl(regs + MTK_WED_RING_OFS_COUNT); ++ int i, count = 0; ++ ++ ring->wpdma = regs; ++ wed_w32(dev, MTK_WED_IND_CMD_RX_CTRL1 + MTK_WED_RING_OFS_BASE, ++ readl(regs) & 0xfffffff0); ++ ++ wed_w32(dev, MTK_WED_IND_CMD_RX_CTRL1 + MTK_WED_RING_OFS_COUNT, ++ readl(regs + MTK_WED_RING_OFS_COUNT)); ++ ++ /* ack sn cr */ ++ wed_w32(dev, MTK_WED_RRO_CFG0, dev->wlan.phy_base + ++ dev->wlan.ind_cmd.ack_sn_addr); ++ wed_w32(dev, MTK_WED_RRO_CFG1, ++ FIELD_PREP(MTK_WED_RRO_CFG1_MAX_WIN_SZ, ++ dev->wlan.ind_cmd.win_size) | ++ FIELD_PREP(MTK_WED_RRO_CFG1_PARTICL_SE_ID, ++ dev->wlan.ind_cmd.particular_sid)); ++ ++ /* particular session addr element */ ++ wed_w32(dev, MTK_WED_ADDR_ELEM_CFG0, ++ dev->wlan.ind_cmd.particular_se_phys); ++ ++ for (i = 0; i < dev->wlan.ind_cmd.se_group_nums; i++) { ++ wed_w32(dev, MTK_WED_RADDR_ELEM_TBL_WDATA, ++ dev->wlan.ind_cmd.addr_elem_phys[i] >> 4); ++ wed_w32(dev, MTK_WED_ADDR_ELEM_TBL_CFG, ++ MTK_WED_ADDR_ELEM_TBL_WR | (i & 0x7f)); ++ ++ val = wed_r32(dev, MTK_WED_ADDR_ELEM_TBL_CFG); ++ while (!(val & MTK_WED_ADDR_ELEM_TBL_WR_RDY) && count++ < 100) ++ val = wed_r32(dev, MTK_WED_ADDR_ELEM_TBL_CFG); ++ if (count >= 100) ++ dev_err(dev->hw->dev, ++ "write ba session base failed\n"); ++ } ++ ++ /* pn check init */ ++ for (i = 0; i < dev->wlan.ind_cmd.particular_sid; i++) { ++ wed_w32(dev, MTK_WED_PN_CHECK_WDATA_M, ++ MTK_WED_PN_CHECK_IS_FIRST); ++ ++ wed_w32(dev, MTK_WED_PN_CHECK_CFG, MTK_WED_PN_CHECK_WR | ++ FIELD_PREP(MTK_WED_PN_CHECK_SE_ID, i)); ++ ++ count = 0; ++ val = wed_r32(dev, MTK_WED_PN_CHECK_CFG); ++ while (!(val & MTK_WED_PN_CHECK_WR_RDY) && count++ < 100) ++ val = wed_r32(dev, MTK_WED_PN_CHECK_CFG); ++ if (count >= 100) ++ dev_err(dev->hw->dev, ++ "session(%d) initialization failed\n", i); ++ } ++ ++ wed_w32(dev, MTK_WED_RX_IND_CMD_CNT0, MTK_WED_RX_IND_CMD_DBG_CNT_EN); ++ wed_set(dev, MTK_WED_CTRL, MTK_WED_CTRL_WED_RX_IND_CMD_EN); ++ ++ return 0; ++} ++ ++static void + mtk_wed_start(struct mtk_wed_device *dev, u32 irq_mask) + { + int i; +@@ -2215,6 +2506,10 @@ void mtk_wed_add_hw(struct device_node * + .detach = mtk_wed_detach, + .ppe_check = mtk_wed_ppe_check, + .setup_tc = mtk_wed_setup_tc, ++ .start_hw_rro = mtk_wed_start_hw_rro, ++ .rro_rx_ring_setup = mtk_wed_rro_rx_ring_setup, ++ .msdu_pg_rx_ring_setup = mtk_wed_msdu_pg_rx_ring_setup, ++ .ind_rx_ring_setup = mtk_wed_ind_rx_ring_setup, + }; + struct device_node *eth_np = eth->dev->of_node; + struct platform_device *pdev; +--- a/include/linux/soc/mediatek/mtk_wed.h ++++ b/include/linux/soc/mediatek/mtk_wed.h +@@ -10,6 +10,7 @@ + + #define MTK_WED_TX_QUEUES 2 + #define MTK_WED_RX_QUEUES 2 ++#define MTK_WED_RX_PAGE_QUEUES 3 + + #define WED_WO_STA_REC 0x6 + +@@ -99,6 +100,9 @@ struct mtk_wed_device { + struct mtk_wed_ring txfree_ring; + struct mtk_wed_ring tx_wdma[MTK_WED_TX_QUEUES]; + struct mtk_wed_ring rx_wdma[MTK_WED_RX_QUEUES]; ++ struct mtk_wed_ring rx_rro_ring[MTK_WED_RX_QUEUES]; ++ struct mtk_wed_ring rx_page_ring[MTK_WED_RX_PAGE_QUEUES]; ++ struct mtk_wed_ring ind_cmd_ring; + + struct { + int size; +@@ -120,6 +124,13 @@ struct mtk_wed_device { + dma_addr_t fdbk_phys; + } rro; + ++ struct { ++ int size; ++ struct mtk_wed_buf *pages; ++ struct mtk_wed_bm_desc *desc; ++ dma_addr_t desc_phys; ++ } hw_rro; ++ + /* filled by driver: */ + struct { + union { +@@ -138,6 +149,8 @@ struct mtk_wed_device { + u32 wpdma_txfree; + u32 wpdma_rx_glo; + u32 wpdma_rx; ++ u32 wpdma_rx_rro[MTK_WED_RX_QUEUES]; ++ u32 wpdma_rx_pg; + + bool wcid_512; + bool hw_rro; +@@ -152,9 +165,20 @@ struct mtk_wed_device { + + u8 tx_tbit[MTK_WED_TX_QUEUES]; + u8 rx_tbit[MTK_WED_RX_QUEUES]; ++ u8 rro_rx_tbit[MTK_WED_RX_QUEUES]; ++ u8 rx_pg_tbit[MTK_WED_RX_PAGE_QUEUES]; + u8 txfree_tbit; + u8 amsdu_max_subframes; + ++ struct { ++ u8 se_group_nums; ++ u16 win_size; ++ u16 particular_sid; ++ u32 ack_sn_addr; ++ dma_addr_t particular_se_phys; ++ dma_addr_t addr_elem_phys[1024]; ++ } ind_cmd; ++ + u32 (*init_buf)(void *ptr, dma_addr_t phys, int token_id); + int (*offload_enable)(struct mtk_wed_device *wed); + void (*offload_disable)(struct mtk_wed_device *wed); +@@ -193,6 +217,14 @@ struct mtk_wed_ops { + void (*irq_set_mask)(struct mtk_wed_device *dev, u32 mask); + int (*setup_tc)(struct mtk_wed_device *wed, struct net_device *dev, + enum tc_setup_type type, void *type_data); ++ void (*start_hw_rro)(struct mtk_wed_device *dev, u32 irq_mask, ++ bool reset); ++ void (*rro_rx_ring_setup)(struct mtk_wed_device *dev, int ring, ++ void __iomem *regs); ++ void (*msdu_pg_rx_ring_setup)(struct mtk_wed_device *dev, int ring, ++ void __iomem *regs); ++ int (*ind_rx_ring_setup)(struct mtk_wed_device *dev, ++ void __iomem *regs); + }; + + extern const struct mtk_wed_ops __rcu *mtk_soc_wed_ops; +@@ -264,6 +296,15 @@ static inline bool mtk_wed_is_amsdu_supp + #define mtk_wed_device_dma_reset(_dev) (_dev)->ops->reset_dma(_dev) + #define mtk_wed_device_setup_tc(_dev, _netdev, _type, _type_data) \ + (_dev)->ops->setup_tc(_dev, _netdev, _type, _type_data) ++#define mtk_wed_device_start_hw_rro(_dev, _mask, _reset) \ ++ (_dev)->ops->start_hw_rro(_dev, _mask, _reset) ++#define mtk_wed_device_rro_rx_ring_setup(_dev, _ring, _regs) \ ++ (_dev)->ops->rro_rx_ring_setup(_dev, _ring, _regs) ++#define mtk_wed_device_msdu_pg_rx_ring_setup(_dev, _ring, _regs) \ ++ (_dev)->ops->msdu_pg_rx_ring_setup(_dev, _ring, _regs) ++#define mtk_wed_device_ind_rx_ring_setup(_dev, _regs) \ ++ (_dev)->ops->ind_rx_ring_setup(_dev, _regs) ++ + #else + static inline bool mtk_wed_device_active(struct mtk_wed_device *dev) + { +@@ -283,6 +324,10 @@ static inline bool mtk_wed_device_active + #define mtk_wed_device_stop(_dev) do {} while (0) + #define mtk_wed_device_dma_reset(_dev) do {} while (0) + #define mtk_wed_device_setup_tc(_dev, _netdev, _type, _type_data) -EOPNOTSUPP ++#define mtk_wed_device_start_hw_rro(_dev, _mask, _reset) do {} while (0) ++#define mtk_wed_device_rro_rx_ring_setup(_dev, _ring, _regs) -ENODEV ++#define mtk_wed_device_msdu_pg_rx_ring_setup(_dev, _ring, _regs) -ENODEV ++#define mtk_wed_device_ind_rx_ring_setup(_dev, _regs) -ENODEV + #endif + + #endif diff --git a/target/linux/generic/backport-6.1/752-18-v6.7-net-ethernet-mtk_wed-debugfs-move-wed_v2-specific-re.patch b/target/linux/generic/backport-6.1/752-18-v6.7-net-ethernet-mtk_wed-debugfs-move-wed_v2-specific-re.patch new file mode 100644 index 000000000..5ea43a444 --- /dev/null +++ b/target/linux/generic/backport-6.1/752-18-v6.7-net-ethernet-mtk_wed-debugfs-move-wed_v2-specific-re.patch @@ -0,0 +1,78 @@ +From: Lorenzo Bianconi +Date: Mon, 18 Sep 2023 12:29:17 +0200 +Subject: [PATCH] net: ethernet: mtk_wed: debugfs: move wed_v2 specific regs + out of regs array + +Move specific WED2.0 debugfs entries out of regs array. This is a +preliminary patch to introduce WED 3.0 debugfs info. + +Signed-off-by: Lorenzo Bianconi +Signed-off-by: Paolo Abeni +--- + +--- a/drivers/net/ethernet/mediatek/mtk_wed_debugfs.c ++++ b/drivers/net/ethernet/mediatek/mtk_wed_debugfs.c +@@ -151,7 +151,7 @@ DEFINE_SHOW_ATTRIBUTE(wed_txinfo); + static int + wed_rxinfo_show(struct seq_file *s, void *data) + { +- static const struct reg_dump regs[] = { ++ static const struct reg_dump regs_common[] = { + DUMP_STR("WPDMA RX"), + DUMP_WPDMA_RX_RING(0), + DUMP_WPDMA_RX_RING(1), +@@ -169,7 +169,7 @@ wed_rxinfo_show(struct seq_file *s, void + DUMP_WED_RING(WED_RING_RX_DATA(0)), + DUMP_WED_RING(WED_RING_RX_DATA(1)), + +- DUMP_STR("WED RRO"), ++ DUMP_STR("WED WO RRO"), + DUMP_WED_RRO_RING(WED_RROQM_MIOD_CTRL0), + DUMP_WED(WED_RROQM_MID_MIB), + DUMP_WED(WED_RROQM_MOD_MIB), +@@ -180,17 +180,6 @@ wed_rxinfo_show(struct seq_file *s, void + DUMP_WED(WED_RROQM_FDBK_ANC_MIB), + DUMP_WED(WED_RROQM_FDBK_ANC2H_MIB), + +- DUMP_STR("WED Route QM"), +- DUMP_WED(WED_RTQM_R2H_MIB(0)), +- DUMP_WED(WED_RTQM_R2Q_MIB(0)), +- DUMP_WED(WED_RTQM_Q2H_MIB(0)), +- DUMP_WED(WED_RTQM_R2H_MIB(1)), +- DUMP_WED(WED_RTQM_R2Q_MIB(1)), +- DUMP_WED(WED_RTQM_Q2H_MIB(1)), +- DUMP_WED(WED_RTQM_Q2N_MIB), +- DUMP_WED(WED_RTQM_Q2B_MIB), +- DUMP_WED(WED_RTQM_PFDBK_MIB), +- + DUMP_STR("WED WDMA TX"), + DUMP_WED(WED_WDMA_TX_MIB), + DUMP_WED_RING(WED_WDMA_RING_TX), +@@ -211,11 +200,25 @@ wed_rxinfo_show(struct seq_file *s, void + DUMP_WED(WED_RX_BM_INTF), + DUMP_WED(WED_RX_BM_ERR_STS), + }; ++ static const struct reg_dump regs_wed_v2[] = { ++ DUMP_STR("WED Route QM"), ++ DUMP_WED(WED_RTQM_R2H_MIB(0)), ++ DUMP_WED(WED_RTQM_R2Q_MIB(0)), ++ DUMP_WED(WED_RTQM_Q2H_MIB(0)), ++ DUMP_WED(WED_RTQM_R2H_MIB(1)), ++ DUMP_WED(WED_RTQM_R2Q_MIB(1)), ++ DUMP_WED(WED_RTQM_Q2H_MIB(1)), ++ DUMP_WED(WED_RTQM_Q2N_MIB), ++ DUMP_WED(WED_RTQM_Q2B_MIB), ++ DUMP_WED(WED_RTQM_PFDBK_MIB), ++ }; + struct mtk_wed_hw *hw = s->private; + struct mtk_wed_device *dev = hw->wed_dev; + +- if (dev) +- dump_wed_regs(s, dev, regs, ARRAY_SIZE(regs)); ++ if (dev) { ++ dump_wed_regs(s, dev, regs_common, ARRAY_SIZE(regs_common)); ++ dump_wed_regs(s, dev, regs_wed_v2, ARRAY_SIZE(regs_wed_v2)); ++ } + + return 0; + } diff --git a/target/linux/generic/backport-6.1/752-19-v6.7-net-ethernet-mtk_wed-debugfs-add-WED-3.0-debugfs-ent.patch b/target/linux/generic/backport-6.1/752-19-v6.7-net-ethernet-mtk_wed-debugfs-add-WED-3.0-debugfs-ent.patch new file mode 100644 index 000000000..f491d2fd8 --- /dev/null +++ b/target/linux/generic/backport-6.1/752-19-v6.7-net-ethernet-mtk_wed-debugfs-add-WED-3.0-debugfs-ent.patch @@ -0,0 +1,432 @@ +From: Sujuan Chen +Date: Mon, 18 Sep 2023 12:29:18 +0200 +Subject: [PATCH] net: ethernet: mtk_wed: debugfs: add WED 3.0 debugfs entries + +Introduce WED3.0 debugfs entries useful for debugging. + +Co-developed-by: Lorenzo Bianconi +Signed-off-by: Lorenzo Bianconi +Signed-off-by: Sujuan Chen +Signed-off-by: Paolo Abeni +--- + +--- a/drivers/net/ethernet/mediatek/mtk_wed_debugfs.c ++++ b/drivers/net/ethernet/mediatek/mtk_wed_debugfs.c +@@ -11,6 +11,7 @@ struct reg_dump { + u16 offset; + u8 type; + u8 base; ++ u32 mask; + }; + + enum { +@@ -25,6 +26,8 @@ enum { + + #define DUMP_STR(_str) { _str, 0, DUMP_TYPE_STRING } + #define DUMP_REG(_reg, ...) { #_reg, MTK_##_reg, __VA_ARGS__ } ++#define DUMP_REG_MASK(_reg, _mask) \ ++ { #_mask, MTK_##_reg, DUMP_TYPE_WED, 0, MTK_##_mask } + #define DUMP_RING(_prefix, _base, ...) \ + { _prefix " BASE", _base, __VA_ARGS__ }, \ + { _prefix " CNT", _base + 0x4, __VA_ARGS__ }, \ +@@ -32,6 +35,7 @@ enum { + { _prefix " DIDX", _base + 0xc, __VA_ARGS__ } + + #define DUMP_WED(_reg) DUMP_REG(_reg, DUMP_TYPE_WED) ++#define DUMP_WED_MASK(_reg, _mask) DUMP_REG_MASK(_reg, _mask) + #define DUMP_WED_RING(_base) DUMP_RING(#_base, MTK_##_base, DUMP_TYPE_WED) + + #define DUMP_WDMA(_reg) DUMP_REG(_reg, DUMP_TYPE_WDMA) +@@ -212,12 +216,58 @@ wed_rxinfo_show(struct seq_file *s, void + DUMP_WED(WED_RTQM_Q2B_MIB), + DUMP_WED(WED_RTQM_PFDBK_MIB), + }; ++ static const struct reg_dump regs_wed_v3[] = { ++ DUMP_STR("WED RX RRO DATA"), ++ DUMP_WED_RING(WED_RRO_RX_D_RX(0)), ++ DUMP_WED_RING(WED_RRO_RX_D_RX(1)), ++ ++ DUMP_STR("WED RX MSDU PAGE"), ++ DUMP_WED_RING(WED_RRO_MSDU_PG_CTRL0(0)), ++ DUMP_WED_RING(WED_RRO_MSDU_PG_CTRL0(1)), ++ DUMP_WED_RING(WED_RRO_MSDU_PG_CTRL0(2)), ++ ++ DUMP_STR("WED RX IND CMD"), ++ DUMP_WED(WED_IND_CMD_RX_CTRL1), ++ DUMP_WED_MASK(WED_IND_CMD_RX_CTRL2, WED_IND_CMD_MAX_CNT), ++ DUMP_WED_MASK(WED_IND_CMD_RX_CTRL0, WED_IND_CMD_PROC_IDX), ++ DUMP_WED_MASK(RRO_IND_CMD_SIGNATURE, RRO_IND_CMD_DMA_IDX), ++ DUMP_WED_MASK(WED_IND_CMD_RX_CTRL0, WED_IND_CMD_MAGIC_CNT), ++ DUMP_WED_MASK(RRO_IND_CMD_SIGNATURE, RRO_IND_CMD_MAGIC_CNT), ++ DUMP_WED_MASK(WED_IND_CMD_RX_CTRL0, ++ WED_IND_CMD_PREFETCH_FREE_CNT), ++ DUMP_WED_MASK(WED_RRO_CFG1, WED_RRO_CFG1_PARTICL_SE_ID), ++ ++ DUMP_STR("WED ADDR ELEM"), ++ DUMP_WED(WED_ADDR_ELEM_CFG0), ++ DUMP_WED_MASK(WED_ADDR_ELEM_CFG1, ++ WED_ADDR_ELEM_PREFETCH_FREE_CNT), ++ ++ DUMP_STR("WED Route QM"), ++ DUMP_WED(WED_RTQM_ENQ_I2Q_DMAD_CNT), ++ DUMP_WED(WED_RTQM_ENQ_I2N_DMAD_CNT), ++ DUMP_WED(WED_RTQM_ENQ_I2Q_PKT_CNT), ++ DUMP_WED(WED_RTQM_ENQ_I2N_PKT_CNT), ++ DUMP_WED(WED_RTQM_ENQ_USED_ENTRY_CNT), ++ DUMP_WED(WED_RTQM_ENQ_ERR_CNT), ++ ++ DUMP_WED(WED_RTQM_DEQ_DMAD_CNT), ++ DUMP_WED(WED_RTQM_DEQ_Q2I_DMAD_CNT), ++ DUMP_WED(WED_RTQM_DEQ_PKT_CNT), ++ DUMP_WED(WED_RTQM_DEQ_Q2I_PKT_CNT), ++ DUMP_WED(WED_RTQM_DEQ_USED_PFDBK_CNT), ++ DUMP_WED(WED_RTQM_DEQ_ERR_CNT), ++ }; + struct mtk_wed_hw *hw = s->private; + struct mtk_wed_device *dev = hw->wed_dev; + + if (dev) { + dump_wed_regs(s, dev, regs_common, ARRAY_SIZE(regs_common)); +- dump_wed_regs(s, dev, regs_wed_v2, ARRAY_SIZE(regs_wed_v2)); ++ if (mtk_wed_is_v2(hw)) ++ dump_wed_regs(s, dev, ++ regs_wed_v2, ARRAY_SIZE(regs_wed_v2)); ++ else ++ dump_wed_regs(s, dev, ++ regs_wed_v3, ARRAY_SIZE(regs_wed_v3)); + } + + return 0; +@@ -225,6 +275,314 @@ wed_rxinfo_show(struct seq_file *s, void + DEFINE_SHOW_ATTRIBUTE(wed_rxinfo); + + static int ++wed_amsdu_show(struct seq_file *s, void *data) ++{ ++ static const struct reg_dump regs[] = { ++ DUMP_STR("WED AMDSU INFO"), ++ DUMP_WED(WED_MON_AMSDU_FIFO_DMAD), ++ ++ DUMP_STR("WED AMDSU ENG0 INFO"), ++ DUMP_WED(WED_MON_AMSDU_ENG_DMAD(0)), ++ DUMP_WED(WED_MON_AMSDU_ENG_QFPL(0)), ++ DUMP_WED(WED_MON_AMSDU_ENG_QENI(0)), ++ DUMP_WED(WED_MON_AMSDU_ENG_QENO(0)), ++ DUMP_WED(WED_MON_AMSDU_ENG_MERG(0)), ++ DUMP_WED_MASK(WED_MON_AMSDU_ENG_CNT8(0), ++ WED_AMSDU_ENG_MAX_PL_CNT), ++ DUMP_WED_MASK(WED_MON_AMSDU_ENG_CNT8(0), ++ WED_AMSDU_ENG_MAX_QGPP_CNT), ++ DUMP_WED_MASK(WED_MON_AMSDU_ENG_CNT9(0), ++ WED_AMSDU_ENG_CUR_ENTRY), ++ DUMP_WED_MASK(WED_MON_AMSDU_ENG_CNT9(0), ++ WED_AMSDU_ENG_MAX_BUF_MERGED), ++ DUMP_WED_MASK(WED_MON_AMSDU_ENG_CNT9(0), ++ WED_AMSDU_ENG_MAX_MSDU_MERGED), ++ ++ DUMP_STR("WED AMDSU ENG1 INFO"), ++ DUMP_WED(WED_MON_AMSDU_ENG_DMAD(1)), ++ DUMP_WED(WED_MON_AMSDU_ENG_QFPL(1)), ++ DUMP_WED(WED_MON_AMSDU_ENG_QENI(1)), ++ DUMP_WED(WED_MON_AMSDU_ENG_QENO(1)), ++ DUMP_WED(WED_MON_AMSDU_ENG_MERG(1)), ++ DUMP_WED_MASK(WED_MON_AMSDU_ENG_CNT8(1), ++ WED_AMSDU_ENG_MAX_PL_CNT), ++ DUMP_WED_MASK(WED_MON_AMSDU_ENG_CNT8(1), ++ WED_AMSDU_ENG_MAX_QGPP_CNT), ++ DUMP_WED_MASK(WED_MON_AMSDU_ENG_CNT9(1), ++ WED_AMSDU_ENG_CUR_ENTRY), ++ DUMP_WED_MASK(WED_MON_AMSDU_ENG_CNT9(2), ++ WED_AMSDU_ENG_MAX_BUF_MERGED), ++ DUMP_WED_MASK(WED_MON_AMSDU_ENG_CNT9(2), ++ WED_AMSDU_ENG_MAX_MSDU_MERGED), ++ ++ DUMP_STR("WED AMDSU ENG2 INFO"), ++ DUMP_WED(WED_MON_AMSDU_ENG_DMAD(2)), ++ DUMP_WED(WED_MON_AMSDU_ENG_QFPL(2)), ++ DUMP_WED(WED_MON_AMSDU_ENG_QENI(2)), ++ DUMP_WED(WED_MON_AMSDU_ENG_QENO(2)), ++ DUMP_WED(WED_MON_AMSDU_ENG_MERG(2)), ++ DUMP_WED_MASK(WED_MON_AMSDU_ENG_CNT8(2), ++ WED_AMSDU_ENG_MAX_PL_CNT), ++ DUMP_WED_MASK(WED_MON_AMSDU_ENG_CNT8(2), ++ WED_AMSDU_ENG_MAX_QGPP_CNT), ++ DUMP_WED_MASK(WED_MON_AMSDU_ENG_CNT9(2), ++ WED_AMSDU_ENG_CUR_ENTRY), ++ DUMP_WED_MASK(WED_MON_AMSDU_ENG_CNT9(2), ++ WED_AMSDU_ENG_MAX_BUF_MERGED), ++ DUMP_WED_MASK(WED_MON_AMSDU_ENG_CNT9(2), ++ WED_AMSDU_ENG_MAX_MSDU_MERGED), ++ ++ DUMP_STR("WED AMDSU ENG3 INFO"), ++ DUMP_WED(WED_MON_AMSDU_ENG_DMAD(3)), ++ DUMP_WED(WED_MON_AMSDU_ENG_QFPL(3)), ++ DUMP_WED(WED_MON_AMSDU_ENG_QENI(3)), ++ DUMP_WED(WED_MON_AMSDU_ENG_QENO(3)), ++ DUMP_WED(WED_MON_AMSDU_ENG_MERG(3)), ++ DUMP_WED_MASK(WED_MON_AMSDU_ENG_CNT8(3), ++ WED_AMSDU_ENG_MAX_PL_CNT), ++ DUMP_WED_MASK(WED_MON_AMSDU_ENG_CNT8(3), ++ WED_AMSDU_ENG_MAX_QGPP_CNT), ++ DUMP_WED_MASK(WED_MON_AMSDU_ENG_CNT9(3), ++ WED_AMSDU_ENG_CUR_ENTRY), ++ DUMP_WED_MASK(WED_MON_AMSDU_ENG_CNT9(3), ++ WED_AMSDU_ENG_MAX_BUF_MERGED), ++ DUMP_WED_MASK(WED_MON_AMSDU_ENG_CNT9(3), ++ WED_AMSDU_ENG_MAX_MSDU_MERGED), ++ ++ DUMP_STR("WED AMDSU ENG4 INFO"), ++ DUMP_WED(WED_MON_AMSDU_ENG_DMAD(4)), ++ DUMP_WED(WED_MON_AMSDU_ENG_QFPL(4)), ++ DUMP_WED(WED_MON_AMSDU_ENG_QENI(4)), ++ DUMP_WED(WED_MON_AMSDU_ENG_QENO(4)), ++ DUMP_WED(WED_MON_AMSDU_ENG_MERG(4)), ++ DUMP_WED_MASK(WED_MON_AMSDU_ENG_CNT8(4), ++ WED_AMSDU_ENG_MAX_PL_CNT), ++ DUMP_WED_MASK(WED_MON_AMSDU_ENG_CNT8(4), ++ WED_AMSDU_ENG_MAX_QGPP_CNT), ++ DUMP_WED_MASK(WED_MON_AMSDU_ENG_CNT9(4), ++ WED_AMSDU_ENG_CUR_ENTRY), ++ DUMP_WED_MASK(WED_MON_AMSDU_ENG_CNT9(4), ++ WED_AMSDU_ENG_MAX_BUF_MERGED), ++ DUMP_WED_MASK(WED_MON_AMSDU_ENG_CNT9(4), ++ WED_AMSDU_ENG_MAX_MSDU_MERGED), ++ ++ DUMP_STR("WED AMDSU ENG5 INFO"), ++ DUMP_WED(WED_MON_AMSDU_ENG_DMAD(5)), ++ DUMP_WED(WED_MON_AMSDU_ENG_QFPL(5)), ++ DUMP_WED(WED_MON_AMSDU_ENG_QENI(5)), ++ DUMP_WED(WED_MON_AMSDU_ENG_QENO(5)), ++ DUMP_WED(WED_MON_AMSDU_ENG_MERG(5)), ++ DUMP_WED_MASK(WED_MON_AMSDU_ENG_CNT8(5), ++ WED_AMSDU_ENG_MAX_PL_CNT), ++ DUMP_WED_MASK(WED_MON_AMSDU_ENG_CNT8(5), ++ WED_AMSDU_ENG_MAX_QGPP_CNT), ++ DUMP_WED_MASK(WED_MON_AMSDU_ENG_CNT9(5), ++ WED_AMSDU_ENG_CUR_ENTRY), ++ DUMP_WED_MASK(WED_MON_AMSDU_ENG_CNT9(5), ++ WED_AMSDU_ENG_MAX_BUF_MERGED), ++ DUMP_WED_MASK(WED_MON_AMSDU_ENG_CNT9(5), ++ WED_AMSDU_ENG_MAX_MSDU_MERGED), ++ ++ DUMP_STR("WED AMDSU ENG6 INFO"), ++ DUMP_WED(WED_MON_AMSDU_ENG_DMAD(6)), ++ DUMP_WED(WED_MON_AMSDU_ENG_QFPL(6)), ++ DUMP_WED(WED_MON_AMSDU_ENG_QENI(6)), ++ DUMP_WED(WED_MON_AMSDU_ENG_QENO(6)), ++ DUMP_WED(WED_MON_AMSDU_ENG_MERG(6)), ++ DUMP_WED_MASK(WED_MON_AMSDU_ENG_CNT8(6), ++ WED_AMSDU_ENG_MAX_PL_CNT), ++ DUMP_WED_MASK(WED_MON_AMSDU_ENG_CNT8(6), ++ WED_AMSDU_ENG_MAX_QGPP_CNT), ++ DUMP_WED_MASK(WED_MON_AMSDU_ENG_CNT9(6), ++ WED_AMSDU_ENG_CUR_ENTRY), ++ DUMP_WED_MASK(WED_MON_AMSDU_ENG_CNT9(6), ++ WED_AMSDU_ENG_MAX_BUF_MERGED), ++ DUMP_WED_MASK(WED_MON_AMSDU_ENG_CNT9(6), ++ WED_AMSDU_ENG_MAX_MSDU_MERGED), ++ ++ DUMP_STR("WED AMDSU ENG7 INFO"), ++ DUMP_WED(WED_MON_AMSDU_ENG_DMAD(7)), ++ DUMP_WED(WED_MON_AMSDU_ENG_QFPL(7)), ++ DUMP_WED(WED_MON_AMSDU_ENG_QENI(7)), ++ DUMP_WED(WED_MON_AMSDU_ENG_QENO(7)), ++ DUMP_WED(WED_MON_AMSDU_ENG_MERG(7)), ++ DUMP_WED_MASK(WED_MON_AMSDU_ENG_CNT8(7), ++ WED_AMSDU_ENG_MAX_PL_CNT), ++ DUMP_WED_MASK(WED_MON_AMSDU_ENG_CNT8(7), ++ WED_AMSDU_ENG_MAX_QGPP_CNT), ++ DUMP_WED_MASK(WED_MON_AMSDU_ENG_CNT9(7), ++ WED_AMSDU_ENG_CUR_ENTRY), ++ DUMP_WED_MASK(WED_MON_AMSDU_ENG_CNT9(7), ++ WED_AMSDU_ENG_MAX_BUF_MERGED), ++ DUMP_WED_MASK(WED_MON_AMSDU_ENG_CNT9(4), ++ WED_AMSDU_ENG_MAX_MSDU_MERGED), ++ ++ DUMP_STR("WED AMDSU ENG8 INFO"), ++ DUMP_WED(WED_MON_AMSDU_ENG_DMAD(8)), ++ DUMP_WED(WED_MON_AMSDU_ENG_QFPL(8)), ++ DUMP_WED(WED_MON_AMSDU_ENG_QENI(8)), ++ DUMP_WED(WED_MON_AMSDU_ENG_QENO(8)), ++ DUMP_WED(WED_MON_AMSDU_ENG_MERG(8)), ++ DUMP_WED_MASK(WED_MON_AMSDU_ENG_CNT8(8), ++ WED_AMSDU_ENG_MAX_PL_CNT), ++ DUMP_WED_MASK(WED_MON_AMSDU_ENG_CNT8(8), ++ WED_AMSDU_ENG_MAX_QGPP_CNT), ++ DUMP_WED_MASK(WED_MON_AMSDU_ENG_CNT9(8), ++ WED_AMSDU_ENG_CUR_ENTRY), ++ DUMP_WED_MASK(WED_MON_AMSDU_ENG_CNT9(8), ++ WED_AMSDU_ENG_MAX_BUF_MERGED), ++ DUMP_WED_MASK(WED_MON_AMSDU_ENG_CNT9(8), ++ WED_AMSDU_ENG_MAX_MSDU_MERGED), ++ ++ DUMP_STR("WED QMEM INFO"), ++ DUMP_WED_MASK(WED_MON_AMSDU_QMEM_CNT(0), WED_AMSDU_QMEM_FQ_CNT), ++ DUMP_WED_MASK(WED_MON_AMSDU_QMEM_CNT(0), WED_AMSDU_QMEM_SP_QCNT), ++ DUMP_WED_MASK(WED_MON_AMSDU_QMEM_CNT(1), WED_AMSDU_QMEM_TID0_QCNT), ++ DUMP_WED_MASK(WED_MON_AMSDU_QMEM_CNT(1), WED_AMSDU_QMEM_TID1_QCNT), ++ DUMP_WED_MASK(WED_MON_AMSDU_QMEM_CNT(2), WED_AMSDU_QMEM_TID2_QCNT), ++ DUMP_WED_MASK(WED_MON_AMSDU_QMEM_CNT(2), WED_AMSDU_QMEM_TID3_QCNT), ++ DUMP_WED_MASK(WED_MON_AMSDU_QMEM_CNT(3), WED_AMSDU_QMEM_TID4_QCNT), ++ DUMP_WED_MASK(WED_MON_AMSDU_QMEM_CNT(3), WED_AMSDU_QMEM_TID5_QCNT), ++ DUMP_WED_MASK(WED_MON_AMSDU_QMEM_CNT(4), WED_AMSDU_QMEM_TID6_QCNT), ++ DUMP_WED_MASK(WED_MON_AMSDU_QMEM_CNT(4), WED_AMSDU_QMEM_TID7_QCNT), ++ ++ DUMP_STR("WED QMEM HEAD INFO"), ++ DUMP_WED_MASK(WED_MON_AMSDU_QMEM_PTR(0), WED_AMSDU_QMEM_FQ_HEAD), ++ DUMP_WED_MASK(WED_MON_AMSDU_QMEM_PTR(0), WED_AMSDU_QMEM_SP_QHEAD), ++ DUMP_WED_MASK(WED_MON_AMSDU_QMEM_PTR(1), WED_AMSDU_QMEM_TID0_QHEAD), ++ DUMP_WED_MASK(WED_MON_AMSDU_QMEM_PTR(1), WED_AMSDU_QMEM_TID1_QHEAD), ++ DUMP_WED_MASK(WED_MON_AMSDU_QMEM_PTR(2), WED_AMSDU_QMEM_TID2_QHEAD), ++ DUMP_WED_MASK(WED_MON_AMSDU_QMEM_PTR(2), WED_AMSDU_QMEM_TID3_QHEAD), ++ DUMP_WED_MASK(WED_MON_AMSDU_QMEM_PTR(3), WED_AMSDU_QMEM_TID4_QHEAD), ++ DUMP_WED_MASK(WED_MON_AMSDU_QMEM_PTR(3), WED_AMSDU_QMEM_TID5_QHEAD), ++ DUMP_WED_MASK(WED_MON_AMSDU_QMEM_PTR(4), WED_AMSDU_QMEM_TID6_QHEAD), ++ DUMP_WED_MASK(WED_MON_AMSDU_QMEM_PTR(4), WED_AMSDU_QMEM_TID7_QHEAD), ++ ++ DUMP_STR("WED QMEM TAIL INFO"), ++ DUMP_WED_MASK(WED_MON_AMSDU_QMEM_PTR(5), WED_AMSDU_QMEM_FQ_TAIL), ++ DUMP_WED_MASK(WED_MON_AMSDU_QMEM_PTR(5), WED_AMSDU_QMEM_SP_QTAIL), ++ DUMP_WED_MASK(WED_MON_AMSDU_QMEM_PTR(6), WED_AMSDU_QMEM_TID0_QTAIL), ++ DUMP_WED_MASK(WED_MON_AMSDU_QMEM_PTR(6), WED_AMSDU_QMEM_TID1_QTAIL), ++ DUMP_WED_MASK(WED_MON_AMSDU_QMEM_PTR(7), WED_AMSDU_QMEM_TID2_QTAIL), ++ DUMP_WED_MASK(WED_MON_AMSDU_QMEM_PTR(7), WED_AMSDU_QMEM_TID3_QTAIL), ++ DUMP_WED_MASK(WED_MON_AMSDU_QMEM_PTR(8), WED_AMSDU_QMEM_TID4_QTAIL), ++ DUMP_WED_MASK(WED_MON_AMSDU_QMEM_PTR(8), WED_AMSDU_QMEM_TID5_QTAIL), ++ DUMP_WED_MASK(WED_MON_AMSDU_QMEM_PTR(9), WED_AMSDU_QMEM_TID6_QTAIL), ++ DUMP_WED_MASK(WED_MON_AMSDU_QMEM_PTR(9), WED_AMSDU_QMEM_TID7_QTAIL), ++ ++ DUMP_STR("WED HIFTXD MSDU INFO"), ++ DUMP_WED(WED_MON_AMSDU_HIFTXD_FETCH_MSDU(1)), ++ DUMP_WED(WED_MON_AMSDU_HIFTXD_FETCH_MSDU(2)), ++ DUMP_WED(WED_MON_AMSDU_HIFTXD_FETCH_MSDU(3)), ++ DUMP_WED(WED_MON_AMSDU_HIFTXD_FETCH_MSDU(4)), ++ DUMP_WED(WED_MON_AMSDU_HIFTXD_FETCH_MSDU(5)), ++ DUMP_WED(WED_MON_AMSDU_HIFTXD_FETCH_MSDU(6)), ++ DUMP_WED(WED_MON_AMSDU_HIFTXD_FETCH_MSDU(7)), ++ DUMP_WED(WED_MON_AMSDU_HIFTXD_FETCH_MSDU(8)), ++ DUMP_WED(WED_MON_AMSDU_HIFTXD_FETCH_MSDU(9)), ++ DUMP_WED(WED_MON_AMSDU_HIFTXD_FETCH_MSDU(10)), ++ DUMP_WED(WED_MON_AMSDU_HIFTXD_FETCH_MSDU(11)), ++ DUMP_WED(WED_MON_AMSDU_HIFTXD_FETCH_MSDU(12)), ++ DUMP_WED(WED_MON_AMSDU_HIFTXD_FETCH_MSDU(13)), ++ }; ++ struct mtk_wed_hw *hw = s->private; ++ struct mtk_wed_device *dev = hw->wed_dev; ++ ++ if (dev) ++ dump_wed_regs(s, dev, regs, ARRAY_SIZE(regs)); ++ ++ return 0; ++} ++DEFINE_SHOW_ATTRIBUTE(wed_amsdu); ++ ++static int ++wed_rtqm_show(struct seq_file *s, void *data) ++{ ++ static const struct reg_dump regs[] = { ++ DUMP_STR("WED Route QM IGRS0(N2H + Recycle)"), ++ DUMP_WED(WED_RTQM_IGRS0_I2HW_DMAD_CNT), ++ DUMP_WED(WED_RTQM_IGRS0_I2H_DMAD_CNT(0)), ++ DUMP_WED(WED_RTQM_IGRS0_I2H_DMAD_CNT(1)), ++ DUMP_WED(WED_RTQM_IGRS0_I2HW_PKT_CNT), ++ DUMP_WED(WED_RTQM_IGRS0_I2H_PKT_CNT(0)), ++ DUMP_WED(WED_RTQM_IGRS0_I2H_PKT_CNT(0)), ++ DUMP_WED(WED_RTQM_IGRS0_FDROP_CNT), ++ ++ DUMP_STR("WED Route QM IGRS1(Legacy)"), ++ DUMP_WED(WED_RTQM_IGRS1_I2HW_DMAD_CNT), ++ DUMP_WED(WED_RTQM_IGRS1_I2H_DMAD_CNT(0)), ++ DUMP_WED(WED_RTQM_IGRS1_I2H_DMAD_CNT(1)), ++ DUMP_WED(WED_RTQM_IGRS1_I2HW_PKT_CNT), ++ DUMP_WED(WED_RTQM_IGRS1_I2H_PKT_CNT(0)), ++ DUMP_WED(WED_RTQM_IGRS1_I2H_PKT_CNT(1)), ++ DUMP_WED(WED_RTQM_IGRS1_FDROP_CNT), ++ ++ DUMP_STR("WED Route QM IGRS2(RRO3.0)"), ++ DUMP_WED(WED_RTQM_IGRS2_I2HW_DMAD_CNT), ++ DUMP_WED(WED_RTQM_IGRS2_I2H_DMAD_CNT(0)), ++ DUMP_WED(WED_RTQM_IGRS2_I2H_DMAD_CNT(1)), ++ DUMP_WED(WED_RTQM_IGRS2_I2HW_PKT_CNT), ++ DUMP_WED(WED_RTQM_IGRS2_I2H_PKT_CNT(0)), ++ DUMP_WED(WED_RTQM_IGRS2_I2H_PKT_CNT(1)), ++ DUMP_WED(WED_RTQM_IGRS2_FDROP_CNT), ++ ++ DUMP_STR("WED Route QM IGRS3(DEBUG)"), ++ DUMP_WED(WED_RTQM_IGRS2_I2HW_DMAD_CNT), ++ DUMP_WED(WED_RTQM_IGRS3_I2H_DMAD_CNT(0)), ++ DUMP_WED(WED_RTQM_IGRS3_I2H_DMAD_CNT(1)), ++ DUMP_WED(WED_RTQM_IGRS3_I2HW_PKT_CNT), ++ DUMP_WED(WED_RTQM_IGRS3_I2H_PKT_CNT(0)), ++ DUMP_WED(WED_RTQM_IGRS3_I2H_PKT_CNT(1)), ++ DUMP_WED(WED_RTQM_IGRS3_FDROP_CNT), ++ }; ++ struct mtk_wed_hw *hw = s->private; ++ struct mtk_wed_device *dev = hw->wed_dev; ++ ++ if (dev) ++ dump_wed_regs(s, dev, regs, ARRAY_SIZE(regs)); ++ ++ return 0; ++} ++DEFINE_SHOW_ATTRIBUTE(wed_rtqm); ++ ++static int ++wed_rro_show(struct seq_file *s, void *data) ++{ ++ static const struct reg_dump regs[] = { ++ DUMP_STR("RRO/IND CMD CNT"), ++ DUMP_WED(WED_RX_IND_CMD_CNT(1)), ++ DUMP_WED(WED_RX_IND_CMD_CNT(2)), ++ DUMP_WED(WED_RX_IND_CMD_CNT(3)), ++ DUMP_WED(WED_RX_IND_CMD_CNT(4)), ++ DUMP_WED(WED_RX_IND_CMD_CNT(5)), ++ DUMP_WED(WED_RX_IND_CMD_CNT(6)), ++ DUMP_WED(WED_RX_IND_CMD_CNT(7)), ++ DUMP_WED(WED_RX_IND_CMD_CNT(8)), ++ DUMP_WED_MASK(WED_RX_IND_CMD_CNT(9), ++ WED_IND_CMD_MAGIC_CNT_FAIL_CNT), ++ ++ DUMP_WED(WED_RX_ADDR_ELEM_CNT(0)), ++ DUMP_WED_MASK(WED_RX_ADDR_ELEM_CNT(1), ++ WED_ADDR_ELEM_SIG_FAIL_CNT), ++ DUMP_WED(WED_RX_MSDU_PG_CNT(1)), ++ DUMP_WED(WED_RX_MSDU_PG_CNT(2)), ++ DUMP_WED(WED_RX_MSDU_PG_CNT(3)), ++ DUMP_WED(WED_RX_MSDU_PG_CNT(4)), ++ DUMP_WED(WED_RX_MSDU_PG_CNT(5)), ++ DUMP_WED_MASK(WED_RX_PN_CHK_CNT, ++ WED_PN_CHK_FAIL_CNT), ++ }; ++ struct mtk_wed_hw *hw = s->private; ++ struct mtk_wed_device *dev = hw->wed_dev; ++ ++ if (dev) ++ dump_wed_regs(s, dev, regs, ARRAY_SIZE(regs)); ++ ++ return 0; ++} ++DEFINE_SHOW_ATTRIBUTE(wed_rro); ++ ++static int + mtk_wed_reg_set(void *data, u64 val) + { + struct mtk_wed_hw *hw = data; +@@ -266,7 +624,16 @@ void mtk_wed_hw_add_debugfs(struct mtk_w + debugfs_create_u32("regidx", 0600, dir, &hw->debugfs_reg); + debugfs_create_file_unsafe("regval", 0600, dir, hw, &fops_regval); + debugfs_create_file_unsafe("txinfo", 0400, dir, hw, &wed_txinfo_fops); +- if (!mtk_wed_is_v1(hw)) ++ if (!mtk_wed_is_v1(hw)) { + debugfs_create_file_unsafe("rxinfo", 0400, dir, hw, + &wed_rxinfo_fops); ++ if (mtk_wed_is_v3_or_greater(hw)) { ++ debugfs_create_file_unsafe("amsdu", 0400, dir, hw, ++ &wed_amsdu_fops); ++ debugfs_create_file_unsafe("rtqm", 0400, dir, hw, ++ &wed_rtqm_fops); ++ debugfs_create_file_unsafe("rro", 0400, dir, hw, ++ &wed_rro_fops); ++ } ++ } + } diff --git a/target/linux/generic/backport-6.1/752-20-v6.7-net-ethernet-mtk_wed-add-wed-3.0-reset-support.patch b/target/linux/generic/backport-6.1/752-20-v6.7-net-ethernet-mtk_wed-add-wed-3.0-reset-support.patch new file mode 100644 index 000000000..aaaabf05e --- /dev/null +++ b/target/linux/generic/backport-6.1/752-20-v6.7-net-ethernet-mtk_wed-add-wed-3.0-reset-support.patch @@ -0,0 +1,587 @@ +From: Sujuan Chen +Date: Mon, 18 Sep 2023 12:29:19 +0200 +Subject: [PATCH] net: ethernet: mtk_wed: add wed 3.0 reset support + +Introduce support for resetting Wireless Ethernet Dispatcher 3.0 +available on MT988 SoC. + +Co-developed-by: Lorenzo Bianconi +Signed-off-by: Lorenzo Bianconi +Signed-off-by: Sujuan Chen +Signed-off-by: Paolo Abeni +--- + +--- a/drivers/net/ethernet/mediatek/mtk_wed.c ++++ b/drivers/net/ethernet/mediatek/mtk_wed.c +@@ -148,6 +148,90 @@ mtk_wdma_read_reset(struct mtk_wed_devic + return wdma_r32(dev, MTK_WDMA_GLO_CFG); + } + ++static void ++mtk_wdma_v3_rx_reset(struct mtk_wed_device *dev) ++{ ++ u32 status; ++ ++ if (!mtk_wed_is_v3_or_greater(dev->hw)) ++ return; ++ ++ wdma_clr(dev, MTK_WDMA_PREF_TX_CFG, MTK_WDMA_PREF_TX_CFG_PREF_EN); ++ wdma_clr(dev, MTK_WDMA_PREF_RX_CFG, MTK_WDMA_PREF_RX_CFG_PREF_EN); ++ ++ if (read_poll_timeout(wdma_r32, status, ++ !(status & MTK_WDMA_PREF_TX_CFG_PREF_BUSY), ++ 0, 10000, false, dev, MTK_WDMA_PREF_TX_CFG)) ++ dev_err(dev->hw->dev, "rx reset failed\n"); ++ ++ if (read_poll_timeout(wdma_r32, status, ++ !(status & MTK_WDMA_PREF_RX_CFG_PREF_BUSY), ++ 0, 10000, false, dev, MTK_WDMA_PREF_RX_CFG)) ++ dev_err(dev->hw->dev, "rx reset failed\n"); ++ ++ wdma_clr(dev, MTK_WDMA_WRBK_TX_CFG, MTK_WDMA_WRBK_TX_CFG_WRBK_EN); ++ wdma_clr(dev, MTK_WDMA_WRBK_RX_CFG, MTK_WDMA_WRBK_RX_CFG_WRBK_EN); ++ ++ if (read_poll_timeout(wdma_r32, status, ++ !(status & MTK_WDMA_WRBK_TX_CFG_WRBK_BUSY), ++ 0, 10000, false, dev, MTK_WDMA_WRBK_TX_CFG)) ++ dev_err(dev->hw->dev, "rx reset failed\n"); ++ ++ if (read_poll_timeout(wdma_r32, status, ++ !(status & MTK_WDMA_WRBK_RX_CFG_WRBK_BUSY), ++ 0, 10000, false, dev, MTK_WDMA_WRBK_RX_CFG)) ++ dev_err(dev->hw->dev, "rx reset failed\n"); ++ ++ /* prefetch FIFO */ ++ wdma_w32(dev, MTK_WDMA_PREF_RX_FIFO_CFG, ++ MTK_WDMA_PREF_RX_FIFO_CFG_RING0_CLEAR | ++ MTK_WDMA_PREF_RX_FIFO_CFG_RING1_CLEAR); ++ wdma_clr(dev, MTK_WDMA_PREF_RX_FIFO_CFG, ++ MTK_WDMA_PREF_RX_FIFO_CFG_RING0_CLEAR | ++ MTK_WDMA_PREF_RX_FIFO_CFG_RING1_CLEAR); ++ ++ /* core FIFO */ ++ wdma_w32(dev, MTK_WDMA_XDMA_RX_FIFO_CFG, ++ MTK_WDMA_XDMA_RX_FIFO_CFG_RX_PAR_FIFO_CLEAR | ++ MTK_WDMA_XDMA_RX_FIFO_CFG_RX_CMD_FIFO_CLEAR | ++ MTK_WDMA_XDMA_RX_FIFO_CFG_RX_DMAD_FIFO_CLEAR | ++ MTK_WDMA_XDMA_RX_FIFO_CFG_RX_ARR_FIFO_CLEAR | ++ MTK_WDMA_XDMA_RX_FIFO_CFG_RX_LEN_FIFO_CLEAR | ++ MTK_WDMA_XDMA_RX_FIFO_CFG_RX_WID_FIFO_CLEAR | ++ MTK_WDMA_XDMA_RX_FIFO_CFG_RX_BID_FIFO_CLEAR); ++ wdma_clr(dev, MTK_WDMA_XDMA_RX_FIFO_CFG, ++ MTK_WDMA_XDMA_RX_FIFO_CFG_RX_PAR_FIFO_CLEAR | ++ MTK_WDMA_XDMA_RX_FIFO_CFG_RX_CMD_FIFO_CLEAR | ++ MTK_WDMA_XDMA_RX_FIFO_CFG_RX_DMAD_FIFO_CLEAR | ++ MTK_WDMA_XDMA_RX_FIFO_CFG_RX_ARR_FIFO_CLEAR | ++ MTK_WDMA_XDMA_RX_FIFO_CFG_RX_LEN_FIFO_CLEAR | ++ MTK_WDMA_XDMA_RX_FIFO_CFG_RX_WID_FIFO_CLEAR | ++ MTK_WDMA_XDMA_RX_FIFO_CFG_RX_BID_FIFO_CLEAR); ++ ++ /* writeback FIFO */ ++ wdma_w32(dev, MTK_WDMA_WRBK_RX_FIFO_CFG(0), ++ MTK_WDMA_WRBK_RX_FIFO_CFG_RING_CLEAR); ++ wdma_w32(dev, MTK_WDMA_WRBK_RX_FIFO_CFG(1), ++ MTK_WDMA_WRBK_RX_FIFO_CFG_RING_CLEAR); ++ ++ wdma_clr(dev, MTK_WDMA_WRBK_RX_FIFO_CFG(0), ++ MTK_WDMA_WRBK_RX_FIFO_CFG_RING_CLEAR); ++ wdma_clr(dev, MTK_WDMA_WRBK_RX_FIFO_CFG(1), ++ MTK_WDMA_WRBK_RX_FIFO_CFG_RING_CLEAR); ++ ++ /* prefetch ring status */ ++ wdma_w32(dev, MTK_WDMA_PREF_SIDX_CFG, ++ MTK_WDMA_PREF_SIDX_CFG_RX_RING_CLEAR); ++ wdma_clr(dev, MTK_WDMA_PREF_SIDX_CFG, ++ MTK_WDMA_PREF_SIDX_CFG_RX_RING_CLEAR); ++ ++ /* writeback ring status */ ++ wdma_w32(dev, MTK_WDMA_WRBK_SIDX_CFG, ++ MTK_WDMA_WRBK_SIDX_CFG_RX_RING_CLEAR); ++ wdma_clr(dev, MTK_WDMA_WRBK_SIDX_CFG, ++ MTK_WDMA_WRBK_SIDX_CFG_RX_RING_CLEAR); ++} ++ + static int + mtk_wdma_rx_reset(struct mtk_wed_device *dev) + { +@@ -160,6 +244,7 @@ mtk_wdma_rx_reset(struct mtk_wed_device + if (ret) + dev_err(dev->hw->dev, "rx reset failed\n"); + ++ mtk_wdma_v3_rx_reset(dev); + wdma_w32(dev, MTK_WDMA_RESET_IDX, MTK_WDMA_RESET_IDX_RX); + wdma_w32(dev, MTK_WDMA_RESET_IDX, 0); + +@@ -192,6 +277,84 @@ mtk_wed_poll_busy(struct mtk_wed_device + } + + static void ++mtk_wdma_v3_tx_reset(struct mtk_wed_device *dev) ++{ ++ u32 status; ++ ++ if (!mtk_wed_is_v3_or_greater(dev->hw)) ++ return; ++ ++ wdma_clr(dev, MTK_WDMA_PREF_TX_CFG, MTK_WDMA_PREF_TX_CFG_PREF_EN); ++ wdma_clr(dev, MTK_WDMA_PREF_RX_CFG, MTK_WDMA_PREF_RX_CFG_PREF_EN); ++ ++ if (read_poll_timeout(wdma_r32, status, ++ !(status & MTK_WDMA_PREF_TX_CFG_PREF_BUSY), ++ 0, 10000, false, dev, MTK_WDMA_PREF_TX_CFG)) ++ dev_err(dev->hw->dev, "tx reset failed\n"); ++ ++ if (read_poll_timeout(wdma_r32, status, ++ !(status & MTK_WDMA_PREF_RX_CFG_PREF_BUSY), ++ 0, 10000, false, dev, MTK_WDMA_PREF_RX_CFG)) ++ dev_err(dev->hw->dev, "tx reset failed\n"); ++ ++ wdma_clr(dev, MTK_WDMA_WRBK_TX_CFG, MTK_WDMA_WRBK_TX_CFG_WRBK_EN); ++ wdma_clr(dev, MTK_WDMA_WRBK_RX_CFG, MTK_WDMA_WRBK_RX_CFG_WRBK_EN); ++ ++ if (read_poll_timeout(wdma_r32, status, ++ !(status & MTK_WDMA_WRBK_TX_CFG_WRBK_BUSY), ++ 0, 10000, false, dev, MTK_WDMA_WRBK_TX_CFG)) ++ dev_err(dev->hw->dev, "tx reset failed\n"); ++ ++ if (read_poll_timeout(wdma_r32, status, ++ !(status & MTK_WDMA_WRBK_RX_CFG_WRBK_BUSY), ++ 0, 10000, false, dev, MTK_WDMA_WRBK_RX_CFG)) ++ dev_err(dev->hw->dev, "tx reset failed\n"); ++ ++ /* prefetch FIFO */ ++ wdma_w32(dev, MTK_WDMA_PREF_TX_FIFO_CFG, ++ MTK_WDMA_PREF_TX_FIFO_CFG_RING0_CLEAR | ++ MTK_WDMA_PREF_TX_FIFO_CFG_RING1_CLEAR); ++ wdma_clr(dev, MTK_WDMA_PREF_TX_FIFO_CFG, ++ MTK_WDMA_PREF_TX_FIFO_CFG_RING0_CLEAR | ++ MTK_WDMA_PREF_TX_FIFO_CFG_RING1_CLEAR); ++ ++ /* core FIFO */ ++ wdma_w32(dev, MTK_WDMA_XDMA_TX_FIFO_CFG, ++ MTK_WDMA_XDMA_TX_FIFO_CFG_TX_PAR_FIFO_CLEAR | ++ MTK_WDMA_XDMA_TX_FIFO_CFG_TX_CMD_FIFO_CLEAR | ++ MTK_WDMA_XDMA_TX_FIFO_CFG_TX_DMAD_FIFO_CLEAR | ++ MTK_WDMA_XDMA_TX_FIFO_CFG_TX_ARR_FIFO_CLEAR); ++ wdma_clr(dev, MTK_WDMA_XDMA_TX_FIFO_CFG, ++ MTK_WDMA_XDMA_TX_FIFO_CFG_TX_PAR_FIFO_CLEAR | ++ MTK_WDMA_XDMA_TX_FIFO_CFG_TX_CMD_FIFO_CLEAR | ++ MTK_WDMA_XDMA_TX_FIFO_CFG_TX_DMAD_FIFO_CLEAR | ++ MTK_WDMA_XDMA_TX_FIFO_CFG_TX_ARR_FIFO_CLEAR); ++ ++ /* writeback FIFO */ ++ wdma_w32(dev, MTK_WDMA_WRBK_TX_FIFO_CFG(0), ++ MTK_WDMA_WRBK_TX_FIFO_CFG_RING_CLEAR); ++ wdma_w32(dev, MTK_WDMA_WRBK_TX_FIFO_CFG(1), ++ MTK_WDMA_WRBK_TX_FIFO_CFG_RING_CLEAR); ++ ++ wdma_clr(dev, MTK_WDMA_WRBK_TX_FIFO_CFG(0), ++ MTK_WDMA_WRBK_TX_FIFO_CFG_RING_CLEAR); ++ wdma_clr(dev, MTK_WDMA_WRBK_TX_FIFO_CFG(1), ++ MTK_WDMA_WRBK_TX_FIFO_CFG_RING_CLEAR); ++ ++ /* prefetch ring status */ ++ wdma_w32(dev, MTK_WDMA_PREF_SIDX_CFG, ++ MTK_WDMA_PREF_SIDX_CFG_TX_RING_CLEAR); ++ wdma_clr(dev, MTK_WDMA_PREF_SIDX_CFG, ++ MTK_WDMA_PREF_SIDX_CFG_TX_RING_CLEAR); ++ ++ /* writeback ring status */ ++ wdma_w32(dev, MTK_WDMA_WRBK_SIDX_CFG, ++ MTK_WDMA_WRBK_SIDX_CFG_TX_RING_CLEAR); ++ wdma_clr(dev, MTK_WDMA_WRBK_SIDX_CFG, ++ MTK_WDMA_WRBK_SIDX_CFG_TX_RING_CLEAR); ++} ++ ++static void + mtk_wdma_tx_reset(struct mtk_wed_device *dev) + { + u32 status, mask = MTK_WDMA_GLO_CFG_TX_DMA_BUSY; +@@ -202,6 +365,7 @@ mtk_wdma_tx_reset(struct mtk_wed_device + !(status & mask), 0, 10000)) + dev_err(dev->hw->dev, "tx reset failed\n"); + ++ mtk_wdma_v3_tx_reset(dev); + wdma_w32(dev, MTK_WDMA_RESET_IDX, MTK_WDMA_RESET_IDX_TX); + wdma_w32(dev, MTK_WDMA_RESET_IDX, 0); + +@@ -1405,13 +1569,33 @@ mtk_wed_rx_reset(struct mtk_wed_device * + if (ret) + return ret; + ++ if (dev->wlan.hw_rro) { ++ wed_clr(dev, MTK_WED_CTRL, MTK_WED_CTRL_WED_RX_IND_CMD_EN); ++ mtk_wed_poll_busy(dev, MTK_WED_RRO_RX_HW_STS, ++ MTK_WED_RX_IND_CMD_BUSY); ++ mtk_wed_reset(dev, MTK_WED_RESET_RRO_RX_TO_PG); ++ } ++ + wed_clr(dev, MTK_WED_WPDMA_RX_D_GLO_CFG, MTK_WED_WPDMA_RX_D_RX_DRV_EN); + ret = mtk_wed_poll_busy(dev, MTK_WED_WPDMA_RX_D_GLO_CFG, + MTK_WED_WPDMA_RX_D_RX_DRV_BUSY); ++ if (!ret && mtk_wed_is_v3_or_greater(dev->hw)) ++ ret = mtk_wed_poll_busy(dev, MTK_WED_WPDMA_RX_D_PREF_CFG, ++ MTK_WED_WPDMA_RX_D_PREF_BUSY); + if (ret) { + mtk_wed_reset(dev, MTK_WED_RESET_WPDMA_INT_AGENT); + mtk_wed_reset(dev, MTK_WED_RESET_WPDMA_RX_D_DRV); + } else { ++ if (mtk_wed_is_v3_or_greater(dev->hw)) { ++ /* 1.a. disable prefetch HW */ ++ wed_clr(dev, MTK_WED_WPDMA_RX_D_PREF_CFG, ++ MTK_WED_WPDMA_RX_D_PREF_EN); ++ mtk_wed_poll_busy(dev, MTK_WED_WPDMA_RX_D_PREF_CFG, ++ MTK_WED_WPDMA_RX_D_PREF_BUSY); ++ wed_w32(dev, MTK_WED_WPDMA_RX_D_RST_IDX, ++ MTK_WED_WPDMA_RX_D_RST_DRV_IDX_ALL); ++ } ++ + wed_w32(dev, MTK_WED_WPDMA_RX_D_RST_IDX, + MTK_WED_WPDMA_RX_D_RST_CRX_IDX | + MTK_WED_WPDMA_RX_D_RST_DRV_IDX); +@@ -1439,23 +1623,52 @@ mtk_wed_rx_reset(struct mtk_wed_device * + wed_w32(dev, MTK_WED_RROQM_RST_IDX, 0); + } + ++ if (dev->wlan.hw_rro) { ++ /* disable rro msdu page drv */ ++ wed_clr(dev, MTK_WED_RRO_MSDU_PG_RING2_CFG, ++ MTK_WED_RRO_MSDU_PG_DRV_EN); ++ ++ /* disable rro data drv */ ++ wed_clr(dev, MTK_WED_RRO_RX_D_CFG(2), MTK_WED_RRO_RX_D_DRV_EN); ++ ++ /* rro msdu page drv reset */ ++ wed_w32(dev, MTK_WED_RRO_MSDU_PG_RING2_CFG, ++ MTK_WED_RRO_MSDU_PG_DRV_CLR); ++ mtk_wed_poll_busy(dev, MTK_WED_RRO_MSDU_PG_RING2_CFG, ++ MTK_WED_RRO_MSDU_PG_DRV_CLR); ++ ++ /* rro data drv reset */ ++ wed_w32(dev, MTK_WED_RRO_RX_D_CFG(2), ++ MTK_WED_RRO_RX_D_DRV_CLR); ++ mtk_wed_poll_busy(dev, MTK_WED_RRO_RX_D_CFG(2), ++ MTK_WED_RRO_RX_D_DRV_CLR); ++ } ++ + /* reset route qm */ + wed_clr(dev, MTK_WED_CTRL, MTK_WED_CTRL_RX_ROUTE_QM_EN); + ret = mtk_wed_poll_busy(dev, MTK_WED_CTRL, + MTK_WED_CTRL_RX_ROUTE_QM_BUSY); +- if (ret) ++ if (ret) { + mtk_wed_reset(dev, MTK_WED_RESET_RX_ROUTE_QM); +- else +- wed_set(dev, MTK_WED_RTQM_GLO_CFG, +- MTK_WED_RTQM_Q_RST); ++ } else if (mtk_wed_is_v3_or_greater(dev->hw)) { ++ wed_set(dev, MTK_WED_RTQM_RST, BIT(0)); ++ wed_clr(dev, MTK_WED_RTQM_RST, BIT(0)); ++ mtk_wed_reset(dev, MTK_WED_RESET_RX_ROUTE_QM); ++ } else { ++ wed_set(dev, MTK_WED_RTQM_GLO_CFG, MTK_WED_RTQM_Q_RST); ++ } + + /* reset tx wdma */ + mtk_wdma_tx_reset(dev); + + /* reset tx wdma drv */ + wed_clr(dev, MTK_WED_WDMA_GLO_CFG, MTK_WED_WDMA_GLO_CFG_TX_DRV_EN); +- mtk_wed_poll_busy(dev, MTK_WED_CTRL, +- MTK_WED_CTRL_WDMA_INT_AGENT_BUSY); ++ if (mtk_wed_is_v3_or_greater(dev->hw)) ++ mtk_wed_poll_busy(dev, MTK_WED_WPDMA_STATUS, ++ MTK_WED_WPDMA_STATUS_TX_DRV); ++ else ++ mtk_wed_poll_busy(dev, MTK_WED_CTRL, ++ MTK_WED_CTRL_WDMA_INT_AGENT_BUSY); + mtk_wed_reset(dev, MTK_WED_RESET_WDMA_TX_DRV); + + /* reset wed rx dma */ +@@ -1476,6 +1689,14 @@ mtk_wed_rx_reset(struct mtk_wed_device * + MTK_WED_CTRL_WED_RX_BM_BUSY); + mtk_wed_reset(dev, MTK_WED_RESET_RX_BM); + ++ if (dev->wlan.hw_rro) { ++ wed_clr(dev, MTK_WED_CTRL, MTK_WED_CTRL_WED_RX_PG_BM_EN); ++ mtk_wed_poll_busy(dev, MTK_WED_CTRL, ++ MTK_WED_CTRL_WED_RX_PG_BM_BUSY); ++ wed_set(dev, MTK_WED_RESET, MTK_WED_RESET_RX_PG_BM); ++ wed_clr(dev, MTK_WED_RESET, MTK_WED_RESET_RX_PG_BM); ++ } ++ + /* wo change to enable state */ + val = MTK_WED_WO_STATE_ENABLE; + ret = mtk_wed_mcu_send_msg(wo, MTK_WED_MODULE_ID_WO, +@@ -1493,6 +1714,7 @@ mtk_wed_rx_reset(struct mtk_wed_device * + false); + } + mtk_wed_free_rx_buffer(dev); ++ mtk_wed_hwrro_free_buffer(dev); + + return 0; + } +@@ -1526,15 +1748,41 @@ mtk_wed_reset_dma(struct mtk_wed_device + + /* 2. reset WDMA rx DMA */ + busy = !!mtk_wdma_rx_reset(dev); +- wed_clr(dev, MTK_WED_WDMA_GLO_CFG, MTK_WED_WDMA_GLO_CFG_RX_DRV_EN); ++ if (mtk_wed_is_v3_or_greater(dev->hw)) { ++ val = MTK_WED_WDMA_GLO_CFG_RX_DIS_FSM_AUTO_IDLE | ++ wed_r32(dev, MTK_WED_WDMA_GLO_CFG); ++ val &= ~MTK_WED_WDMA_GLO_CFG_RX_DRV_EN; ++ wed_w32(dev, MTK_WED_WDMA_GLO_CFG, val); ++ } else { ++ wed_clr(dev, MTK_WED_WDMA_GLO_CFG, ++ MTK_WED_WDMA_GLO_CFG_RX_DRV_EN); ++ } ++ + if (!busy) + busy = mtk_wed_poll_busy(dev, MTK_WED_WDMA_GLO_CFG, + MTK_WED_WDMA_GLO_CFG_RX_DRV_BUSY); ++ if (!busy && mtk_wed_is_v3_or_greater(dev->hw)) ++ busy = mtk_wed_poll_busy(dev, MTK_WED_WDMA_RX_PREF_CFG, ++ MTK_WED_WDMA_RX_PREF_BUSY); + + if (busy) { + mtk_wed_reset(dev, MTK_WED_RESET_WDMA_INT_AGENT); + mtk_wed_reset(dev, MTK_WED_RESET_WDMA_RX_DRV); + } else { ++ if (mtk_wed_is_v3_or_greater(dev->hw)) { ++ /* 1.a. disable prefetch HW */ ++ wed_clr(dev, MTK_WED_WDMA_RX_PREF_CFG, ++ MTK_WED_WDMA_RX_PREF_EN); ++ mtk_wed_poll_busy(dev, MTK_WED_WDMA_RX_PREF_CFG, ++ MTK_WED_WDMA_RX_PREF_BUSY); ++ wed_clr(dev, MTK_WED_WDMA_RX_PREF_CFG, ++ MTK_WED_WDMA_RX_PREF_DDONE2_EN); ++ ++ /* 2. Reset dma index */ ++ wed_w32(dev, MTK_WED_WDMA_RESET_IDX, ++ MTK_WED_WDMA_RESET_IDX_RX_ALL); ++ } ++ + wed_w32(dev, MTK_WED_WDMA_RESET_IDX, + MTK_WED_WDMA_RESET_IDX_RX | MTK_WED_WDMA_RESET_IDX_DRV); + wed_w32(dev, MTK_WED_WDMA_RESET_IDX, 0); +@@ -1550,8 +1798,13 @@ mtk_wed_reset_dma(struct mtk_wed_device + wed_clr(dev, MTK_WED_CTRL, MTK_WED_CTRL_WED_TX_FREE_AGENT_EN); + + for (i = 0; i < 100; i++) { +- val = wed_r32(dev, MTK_WED_TX_BM_INTF); +- if (FIELD_GET(MTK_WED_TX_BM_INTF_TKFIFO_FDEP, val) == 0x40) ++ if (mtk_wed_is_v1(dev->hw)) ++ val = FIELD_GET(MTK_WED_TX_BM_INTF_TKFIFO_FDEP, ++ wed_r32(dev, MTK_WED_TX_BM_INTF)); ++ else ++ val = FIELD_GET(MTK_WED_TX_TKID_INTF_TKFIFO_FDEP, ++ wed_r32(dev, MTK_WED_TX_TKID_INTF)); ++ if (val == 0x40) + break; + } + +@@ -1573,6 +1826,8 @@ mtk_wed_reset_dma(struct mtk_wed_device + mtk_wed_reset(dev, MTK_WED_RESET_WPDMA_INT_AGENT); + mtk_wed_reset(dev, MTK_WED_RESET_WPDMA_TX_DRV); + mtk_wed_reset(dev, MTK_WED_RESET_WPDMA_RX_DRV); ++ if (mtk_wed_is_v3_or_greater(dev->hw)) ++ wed_w32(dev, MTK_WED_RX1_CTRL2, 0); + } else { + wed_w32(dev, MTK_WED_WPDMA_RESET_IDX, + MTK_WED_WPDMA_RESET_IDX_TX | +@@ -1589,7 +1844,14 @@ mtk_wed_reset_dma(struct mtk_wed_device + wed_w32(dev, MTK_WED_RESET_IDX, 0); + } + +- mtk_wed_rx_reset(dev); ++ if (mtk_wed_is_v3_or_greater(dev->hw)) { ++ /* reset amsdu engine */ ++ wed_clr(dev, MTK_WED_CTRL, MTK_WED_CTRL_TX_AMSDU_EN); ++ mtk_wed_reset(dev, MTK_WED_RESET_TX_AMSDU); ++ } ++ ++ if (mtk_wed_get_rx_capa(dev)) ++ mtk_wed_rx_reset(dev); + } + + static int +@@ -1841,6 +2103,7 @@ mtk_wed_dma_enable(struct mtk_wed_device + MTK_WED_WPDMA_GLO_CFG_RX_DRV_UNS_VER_FORCE_4); + + wdma_set(dev, MTK_WDMA_PREF_RX_CFG, MTK_WDMA_PREF_RX_CFG_PREF_EN); ++ wdma_set(dev, MTK_WDMA_WRBK_RX_CFG, MTK_WDMA_WRBK_RX_CFG_WRBK_EN); + } + + wed_clr(dev, MTK_WED_WPDMA_GLO_CFG, +@@ -1904,6 +2167,12 @@ mtk_wed_start_hw_rro(struct mtk_wed_devi + if (!mtk_wed_get_rx_capa(dev) || !dev->wlan.hw_rro) + return; + ++ if (reset) { ++ wed_set(dev, MTK_WED_RRO_MSDU_PG_RING2_CFG, ++ MTK_WED_RRO_MSDU_PG_DRV_EN); ++ return; ++ } ++ + wed_set(dev, MTK_WED_RRO_RX_D_CFG(2), MTK_WED_RRO_MSDU_PG_DRV_CLR); + wed_w32(dev, MTK_WED_RRO_MSDU_PG_RING2_CFG, + MTK_WED_RRO_MSDU_PG_DRV_CLR); +--- a/drivers/net/ethernet/mediatek/mtk_wed_regs.h ++++ b/drivers/net/ethernet/mediatek/mtk_wed_regs.h +@@ -28,6 +28,8 @@ struct mtk_wdma_desc { + #define MTK_WED_RESET 0x008 + #define MTK_WED_RESET_TX_BM BIT(0) + #define MTK_WED_RESET_RX_BM BIT(1) ++#define MTK_WED_RESET_RX_PG_BM BIT(2) ++#define MTK_WED_RESET_RRO_RX_TO_PG BIT(3) + #define MTK_WED_RESET_TX_FREE_AGENT BIT(4) + #define MTK_WED_RESET_WPDMA_TX_DRV BIT(8) + #define MTK_WED_RESET_WPDMA_RX_DRV BIT(9) +@@ -106,6 +108,9 @@ struct mtk_wdma_desc { + #define MTK_WED_STATUS 0x060 + #define MTK_WED_STATUS_TX GENMASK(15, 8) + ++#define MTK_WED_WPDMA_STATUS 0x068 ++#define MTK_WED_WPDMA_STATUS_TX_DRV GENMASK(15, 8) ++ + #define MTK_WED_TX_BM_CTRL 0x080 + #define MTK_WED_TX_BM_CTRL_VLD_GRP_NUM GENMASK(6, 0) + #define MTK_WED_TX_BM_CTRL_RSV_GRP_NUM GENMASK(22, 16) +@@ -140,6 +145,9 @@ struct mtk_wdma_desc { + #define MTK_WED_TX_TKID_CTRL_RSV_GRP_NUM GENMASK(22, 16) + #define MTK_WED_TX_TKID_CTRL_PAUSE BIT(28) + ++#define MTK_WED_TX_TKID_INTF 0x0dc ++#define MTK_WED_TX_TKID_INTF_TKFIFO_FDEP GENMASK(25, 16) ++ + #define MTK_WED_TX_TKID_CTRL_VLD_GRP_NUM_V3 GENMASK(7, 0) + #define MTK_WED_TX_TKID_CTRL_RSV_GRP_NUM_V3 GENMASK(23, 16) + +@@ -190,6 +198,7 @@ struct mtk_wdma_desc { + #define MTK_WED_RING_RX_DATA(_n) (0x420 + (_n) * 0x10) + + #define MTK_WED_SCR0 0x3c0 ++#define MTK_WED_RX1_CTRL2 0x418 + #define MTK_WED_WPDMA_INT_TRIGGER 0x504 + #define MTK_WED_WPDMA_INT_TRIGGER_RX_DONE BIT(1) + #define MTK_WED_WPDMA_INT_TRIGGER_TX_DONE GENMASK(5, 4) +@@ -303,6 +312,7 @@ struct mtk_wdma_desc { + + #define MTK_WED_WPDMA_RX_D_RST_IDX 0x760 + #define MTK_WED_WPDMA_RX_D_RST_CRX_IDX GENMASK(17, 16) ++#define MTK_WED_WPDMA_RX_D_RST_DRV_IDX_ALL BIT(20) + #define MTK_WED_WPDMA_RX_D_RST_DRV_IDX GENMASK(25, 24) + + #define MTK_WED_WPDMA_RX_GLO_CFG 0x76c +@@ -313,6 +323,7 @@ struct mtk_wdma_desc { + + #define MTK_WED_WPDMA_RX_D_PREF_CFG 0x7b4 + #define MTK_WED_WPDMA_RX_D_PREF_EN BIT(0) ++#define MTK_WED_WPDMA_RX_D_PREF_BUSY BIT(1) + #define MTK_WED_WPDMA_RX_D_PREF_BURST_SIZE GENMASK(12, 8) + #define MTK_WED_WPDMA_RX_D_PREF_LOW_THRES GENMASK(21, 16) + +@@ -334,11 +345,13 @@ struct mtk_wdma_desc { + + #define MTK_WED_WDMA_RX_PREF_CFG 0x950 + #define MTK_WED_WDMA_RX_PREF_EN BIT(0) ++#define MTK_WED_WDMA_RX_PREF_BUSY BIT(1) + #define MTK_WED_WDMA_RX_PREF_BURST_SIZE GENMASK(12, 8) + #define MTK_WED_WDMA_RX_PREF_LOW_THRES GENMASK(21, 16) + #define MTK_WED_WDMA_RX_PREF_RX0_SIDX_CLR BIT(24) + #define MTK_WED_WDMA_RX_PREF_RX1_SIDX_CLR BIT(25) + #define MTK_WED_WDMA_RX_PREF_DDONE2_EN BIT(26) ++#define MTK_WED_WDMA_RX_PREF_DDONE2_BUSY BIT(27) + + #define MTK_WED_WDMA_RX_PREF_FIFO_CFG 0x95C + #define MTK_WED_WDMA_RX_PREF_FIFO_RX0_CLR BIT(0) +@@ -367,6 +380,7 @@ struct mtk_wdma_desc { + + #define MTK_WED_WDMA_RESET_IDX 0xa08 + #define MTK_WED_WDMA_RESET_IDX_RX GENMASK(17, 16) ++#define MTK_WED_WDMA_RESET_IDX_RX_ALL BIT(20) + #define MTK_WED_WDMA_RESET_IDX_DRV GENMASK(25, 24) + + #define MTK_WED_WDMA_INT_CLR 0xa24 +@@ -437,21 +451,62 @@ struct mtk_wdma_desc { + #define MTK_WDMA_INT_MASK_RX_DELAY BIT(30) + #define MTK_WDMA_INT_MASK_RX_COHERENT BIT(31) + ++#define MTK_WDMA_XDMA_TX_FIFO_CFG 0x238 ++#define MTK_WDMA_XDMA_TX_FIFO_CFG_TX_PAR_FIFO_CLEAR BIT(0) ++#define MTK_WDMA_XDMA_TX_FIFO_CFG_TX_CMD_FIFO_CLEAR BIT(4) ++#define MTK_WDMA_XDMA_TX_FIFO_CFG_TX_DMAD_FIFO_CLEAR BIT(8) ++#define MTK_WDMA_XDMA_TX_FIFO_CFG_TX_ARR_FIFO_CLEAR BIT(12) ++ ++#define MTK_WDMA_XDMA_RX_FIFO_CFG 0x23c ++#define MTK_WDMA_XDMA_RX_FIFO_CFG_RX_PAR_FIFO_CLEAR BIT(0) ++#define MTK_WDMA_XDMA_RX_FIFO_CFG_RX_CMD_FIFO_CLEAR BIT(4) ++#define MTK_WDMA_XDMA_RX_FIFO_CFG_RX_DMAD_FIFO_CLEAR BIT(8) ++#define MTK_WDMA_XDMA_RX_FIFO_CFG_RX_ARR_FIFO_CLEAR BIT(12) ++#define MTK_WDMA_XDMA_RX_FIFO_CFG_RX_LEN_FIFO_CLEAR BIT(15) ++#define MTK_WDMA_XDMA_RX_FIFO_CFG_RX_WID_FIFO_CLEAR BIT(18) ++#define MTK_WDMA_XDMA_RX_FIFO_CFG_RX_BID_FIFO_CLEAR BIT(21) ++ + #define MTK_WDMA_INT_GRP1 0x250 + #define MTK_WDMA_INT_GRP2 0x254 + + #define MTK_WDMA_PREF_TX_CFG 0x2d0 + #define MTK_WDMA_PREF_TX_CFG_PREF_EN BIT(0) ++#define MTK_WDMA_PREF_TX_CFG_PREF_BUSY BIT(1) + + #define MTK_WDMA_PREF_RX_CFG 0x2dc + #define MTK_WDMA_PREF_RX_CFG_PREF_EN BIT(0) ++#define MTK_WDMA_PREF_RX_CFG_PREF_BUSY BIT(1) ++ ++#define MTK_WDMA_PREF_RX_FIFO_CFG 0x2e0 ++#define MTK_WDMA_PREF_RX_FIFO_CFG_RING0_CLEAR BIT(0) ++#define MTK_WDMA_PREF_RX_FIFO_CFG_RING1_CLEAR BIT(16) ++ ++#define MTK_WDMA_PREF_TX_FIFO_CFG 0x2d4 ++#define MTK_WDMA_PREF_TX_FIFO_CFG_RING0_CLEAR BIT(0) ++#define MTK_WDMA_PREF_TX_FIFO_CFG_RING1_CLEAR BIT(16) ++ ++#define MTK_WDMA_PREF_SIDX_CFG 0x2e4 ++#define MTK_WDMA_PREF_SIDX_CFG_TX_RING_CLEAR GENMASK(3, 0) ++#define MTK_WDMA_PREF_SIDX_CFG_RX_RING_CLEAR GENMASK(5, 4) + + #define MTK_WDMA_WRBK_TX_CFG 0x300 ++#define MTK_WDMA_WRBK_TX_CFG_WRBK_BUSY BIT(0) + #define MTK_WDMA_WRBK_TX_CFG_WRBK_EN BIT(30) + ++#define MTK_WDMA_WRBK_TX_FIFO_CFG(_n) (0x304 + (_n) * 0x4) ++#define MTK_WDMA_WRBK_TX_FIFO_CFG_RING_CLEAR BIT(0) ++ + #define MTK_WDMA_WRBK_RX_CFG 0x344 ++#define MTK_WDMA_WRBK_RX_CFG_WRBK_BUSY BIT(0) + #define MTK_WDMA_WRBK_RX_CFG_WRBK_EN BIT(30) + ++#define MTK_WDMA_WRBK_RX_FIFO_CFG(_n) (0x348 + (_n) * 0x4) ++#define MTK_WDMA_WRBK_RX_FIFO_CFG_RING_CLEAR BIT(0) ++ ++#define MTK_WDMA_WRBK_SIDX_CFG 0x388 ++#define MTK_WDMA_WRBK_SIDX_CFG_TX_RING_CLEAR GENMASK(3, 0) ++#define MTK_WDMA_WRBK_SIDX_CFG_RX_RING_CLEAR GENMASK(5, 4) ++ + #define MTK_PCIE_MIRROR_MAP(n) ((n) ? 0x4 : 0x0) + #define MTK_PCIE_MIRROR_MAP_EN BIT(0) + #define MTK_PCIE_MIRROR_MAP_WED_ID BIT(1) +@@ -465,6 +520,8 @@ struct mtk_wdma_desc { + #define MTK_WED_RTQM_Q_DBG_BYPASS BIT(5) + #define MTK_WED_RTQM_TXDMAD_FPORT GENMASK(23, 20) + ++#define MTK_WED_RTQM_RST 0xb04 ++ + #define MTK_WED_RTQM_IGRS0_I2HW_DMAD_CNT 0xb1c + #define MTK_WED_RTQM_IGRS0_I2H_DMAD_CNT(_n) (0xb20 + (_n) * 0x4) + #define MTK_WED_RTQM_IGRS0_I2HW_PKT_CNT 0xb28 +@@ -653,6 +710,9 @@ struct mtk_wdma_desc { + #define MTK_WED_WPDMA_INT_CTRL_RRO_PG2_CLR BIT(17) + #define MTK_WED_WPDMA_INT_CTRL_RRO_PG2_DONE_TRIG GENMASK(22, 18) + ++#define MTK_WED_RRO_RX_HW_STS 0xf00 ++#define MTK_WED_RX_IND_CMD_BUSY GENMASK(31, 0) ++ + #define MTK_WED_RX_IND_CMD_CNT0 0xf20 + #define MTK_WED_RX_IND_CMD_DBG_CNT_EN BIT(31) + diff --git a/target/linux/generic/backport-6.1/760-v6.9-net-phy-aquantia-add-AQR111-and-AQR111B0-PHY-ID.patch b/target/linux/generic/backport-6.1/760-v6.9-net-phy-aquantia-add-AQR111-and-AQR111B0-PHY-ID.patch new file mode 100644 index 000000000..32ac37e8d --- /dev/null +++ b/target/linux/generic/backport-6.1/760-v6.9-net-phy-aquantia-add-AQR111-and-AQR111B0-PHY-ID.patch @@ -0,0 +1,102 @@ +From 038ba1dc4e54d51d953f5618d8eb5dd39bd9de25 Mon Sep 17 00:00:00 2001 +From: Christian Marangi +Date: Tue, 13 Feb 2024 14:35:51 +0100 +Subject: [PATCH] net: phy: aquantia: add AQR111 and AQR111B0 PHY ID + +Add Aquantia AQR111 and AQR111B0 PHY ID. These PHY advertise 10G speed +but actually supports up to 5G speed, hence some manual fixup is needed. + +The Aquantia AQR111B0 PHY is just a variant of the AQR111 with smaller +chip size. + +Signed-off-by: Christian Marangi +Reviewed-by: Andrew Lunn +Reviewed-by: Andrew Lunn +Link: https://lore.kernel.org/r/20240213133558.1836-1-ansuelsmth@gmail.com +Signed-off-by: Paolo Abeni +--- + drivers/net/phy/aquantia/aquantia_main.c | 52 ++++++++++++++++++++++++ + 1 file changed, 52 insertions(+) + +--- a/drivers/net/phy/aquantia/aquantia_main.c ++++ b/drivers/net/phy/aquantia/aquantia_main.c +@@ -22,6 +22,8 @@ + #define PHY_ID_AQR107 0x03a1b4e0 + #define PHY_ID_AQCS109 0x03a1b5c2 + #define PHY_ID_AQR405 0x03a1b4b0 ++#define PHY_ID_AQR111 0x03a1b610 ++#define PHY_ID_AQR111B0 0x03a1b612 + #define PHY_ID_AQR113C 0x31c31c12 + + #define MDIO_PHYXS_VEND_IF_STATUS 0xe812 +@@ -670,6 +672,16 @@ static int aqr107_probe(struct phy_devic + return aqr_hwmon_probe(phydev); + } + ++static int aqr111_config_init(struct phy_device *phydev) ++{ ++ /* AQR111 reports supporting speed up to 10G, ++ * however only speeds up to 5G are supported. ++ */ ++ phy_set_max_speed(phydev, SPEED_5000); ++ ++ return aqr107_config_init(phydev); ++} ++ + static struct phy_driver aqr_driver[] = { + { + PHY_ID_MATCH_MODEL(PHY_ID_AQ1202), +@@ -744,6 +756,44 @@ static struct phy_driver aqr_driver[] = + .link_change_notify = aqr107_link_change_notify, + }, + { ++ PHY_ID_MATCH_MODEL(PHY_ID_AQR111), ++ .name = "Aquantia AQR111", ++ .probe = aqr107_probe, ++ .get_rate_matching = aqr107_get_rate_matching, ++ .config_init = aqr111_config_init, ++ .config_aneg = aqr_config_aneg, ++ .config_intr = aqr_config_intr, ++ .handle_interrupt = aqr_handle_interrupt, ++ .read_status = aqr107_read_status, ++ .get_tunable = aqr107_get_tunable, ++ .set_tunable = aqr107_set_tunable, ++ .suspend = aqr107_suspend, ++ .resume = aqr107_resume, ++ .get_sset_count = aqr107_get_sset_count, ++ .get_strings = aqr107_get_strings, ++ .get_stats = aqr107_get_stats, ++ .link_change_notify = aqr107_link_change_notify, ++}, ++{ ++ PHY_ID_MATCH_MODEL(PHY_ID_AQR111B0), ++ .name = "Aquantia AQR111B0", ++ .probe = aqr107_probe, ++ .get_rate_matching = aqr107_get_rate_matching, ++ .config_init = aqr111_config_init, ++ .config_aneg = aqr_config_aneg, ++ .config_intr = aqr_config_intr, ++ .handle_interrupt = aqr_handle_interrupt, ++ .read_status = aqr107_read_status, ++ .get_tunable = aqr107_get_tunable, ++ .set_tunable = aqr107_set_tunable, ++ .suspend = aqr107_suspend, ++ .resume = aqr107_resume, ++ .get_sset_count = aqr107_get_sset_count, ++ .get_strings = aqr107_get_strings, ++ .get_stats = aqr107_get_stats, ++ .link_change_notify = aqr107_link_change_notify, ++}, ++{ + PHY_ID_MATCH_MODEL(PHY_ID_AQR405), + .name = "Aquantia AQR405", + .config_aneg = aqr_config_aneg, +@@ -782,6 +832,8 @@ static struct mdio_device_id __maybe_unu + { PHY_ID_MATCH_MODEL(PHY_ID_AQR107) }, + { PHY_ID_MATCH_MODEL(PHY_ID_AQCS109) }, + { PHY_ID_MATCH_MODEL(PHY_ID_AQR405) }, ++ { PHY_ID_MATCH_MODEL(PHY_ID_AQR111) }, ++ { PHY_ID_MATCH_MODEL(PHY_ID_AQR111B0) }, + { PHY_ID_MATCH_MODEL(PHY_ID_AQR113C) }, + { } + }; diff --git a/target/linux/generic/backport-6.1/761-v6.9-net-phy-aquantia-add-AQR113-PHY-ID.patch b/target/linux/generic/backport-6.1/761-v6.9-net-phy-aquantia-add-AQR113-PHY-ID.patch new file mode 100644 index 000000000..073bbbd49 --- /dev/null +++ b/target/linux/generic/backport-6.1/761-v6.9-net-phy-aquantia-add-AQR113-PHY-ID.patch @@ -0,0 +1,60 @@ +From 71b605d32017e5b8d257db7344bc2f8e8fcc973e Mon Sep 17 00:00:00 2001 +From: Christian Marangi +Date: Thu, 15 Feb 2024 16:30:05 +0100 +Subject: [PATCH] net: phy: aquantia: add AQR113 PHY ID + +Add Aquantia AQR113 PHY ID. Aquantia AQR113 is just a chip size variant of +the already supported AQR133C where the only difference is the PHY ID +and the hw chip size. + +Signed-off-by: Christian Marangi +Reviewed-by: Andrew Lunn +Signed-off-by: David S. Miller +--- + drivers/net/phy/aquantia/aquantia_main.c | 21 +++++++++++++++++++++ + 1 file changed, 21 insertions(+) + +--- a/drivers/net/phy/aquantia/aquantia_main.c ++++ b/drivers/net/phy/aquantia/aquantia_main.c +@@ -24,6 +24,7 @@ + #define PHY_ID_AQR405 0x03a1b4b0 + #define PHY_ID_AQR111 0x03a1b610 + #define PHY_ID_AQR111B0 0x03a1b612 ++#define PHY_ID_AQR113 0x31c31c40 + #define PHY_ID_AQR113C 0x31c31c12 + + #define MDIO_PHYXS_VEND_IF_STATUS 0xe812 +@@ -802,6 +803,25 @@ static struct phy_driver aqr_driver[] = + .read_status = aqr_read_status, + }, + { ++ PHY_ID_MATCH_MODEL(PHY_ID_AQR113), ++ .name = "Aquantia AQR113", ++ .probe = aqr107_probe, ++ .get_rate_matching = aqr107_get_rate_matching, ++ .config_init = aqr107_config_init, ++ .config_aneg = aqr_config_aneg, ++ .config_intr = aqr_config_intr, ++ .handle_interrupt = aqr_handle_interrupt, ++ .read_status = aqr107_read_status, ++ .get_tunable = aqr107_get_tunable, ++ .set_tunable = aqr107_set_tunable, ++ .suspend = aqr107_suspend, ++ .resume = aqr107_resume, ++ .get_sset_count = aqr107_get_sset_count, ++ .get_strings = aqr107_get_strings, ++ .get_stats = aqr107_get_stats, ++ .link_change_notify = aqr107_link_change_notify, ++}, ++{ + PHY_ID_MATCH_MODEL(PHY_ID_AQR113C), + .name = "Aquantia AQR113C", + .probe = aqr107_probe, +@@ -834,6 +854,7 @@ static struct mdio_device_id __maybe_unu + { PHY_ID_MATCH_MODEL(PHY_ID_AQR405) }, + { PHY_ID_MATCH_MODEL(PHY_ID_AQR111) }, + { PHY_ID_MATCH_MODEL(PHY_ID_AQR111B0) }, ++ { PHY_ID_MATCH_MODEL(PHY_ID_AQR113) }, + { PHY_ID_MATCH_MODEL(PHY_ID_AQR113C) }, + { } + }; diff --git a/target/linux/generic/backport-6.1/762-v6.9-net-phy-aquantia-add-AQR813-PHY-ID.patch b/target/linux/generic/backport-6.1/762-v6.9-net-phy-aquantia-add-AQR813-PHY-ID.patch new file mode 100644 index 000000000..965b8c91b --- /dev/null +++ b/target/linux/generic/backport-6.1/762-v6.9-net-phy-aquantia-add-AQR813-PHY-ID.patch @@ -0,0 +1,59 @@ +From 6d47302a3f0ba31445478d518d98bd55918bc8ab Mon Sep 17 00:00:00 2001 +From: Christian Marangi +Date: Thu, 15 Feb 2024 22:43:30 +0100 +Subject: [PATCH] net: phy: aquantia: add AQR813 PHY ID + +Aquantia AQR813 is the Octal Port variant of the AQR113. Add PHY ID for +it to provide support for it. + +Signed-off-by: Christian Marangi +Reviewed-by: Andrew Lunn +Signed-off-by: David S. Miller +--- + drivers/net/phy/aquantia/aquantia_main.c | 21 +++++++++++++++++++++ + 1 file changed, 21 insertions(+) + +--- a/drivers/net/phy/aquantia/aquantia_main.c ++++ b/drivers/net/phy/aquantia/aquantia_main.c +@@ -26,6 +26,7 @@ + #define PHY_ID_AQR111B0 0x03a1b612 + #define PHY_ID_AQR113 0x31c31c40 + #define PHY_ID_AQR113C 0x31c31c12 ++#define PHY_ID_AQR813 0x31c31cb2 + + #define MDIO_PHYXS_VEND_IF_STATUS 0xe812 + #define MDIO_PHYXS_VEND_IF_STATUS_TYPE_MASK GENMASK(7, 3) +@@ -840,6 +841,25 @@ static struct phy_driver aqr_driver[] = + .get_stats = aqr107_get_stats, + .link_change_notify = aqr107_link_change_notify, + }, ++{ ++ PHY_ID_MATCH_MODEL(PHY_ID_AQR813), ++ .name = "Aquantia AQR813", ++ .probe = aqr107_probe, ++ .get_rate_matching = aqr107_get_rate_matching, ++ .config_init = aqr107_config_init, ++ .config_aneg = aqr_config_aneg, ++ .config_intr = aqr_config_intr, ++ .handle_interrupt = aqr_handle_interrupt, ++ .read_status = aqr107_read_status, ++ .get_tunable = aqr107_get_tunable, ++ .set_tunable = aqr107_set_tunable, ++ .suspend = aqr107_suspend, ++ .resume = aqr107_resume, ++ .get_sset_count = aqr107_get_sset_count, ++ .get_strings = aqr107_get_strings, ++ .get_stats = aqr107_get_stats, ++ .link_change_notify = aqr107_link_change_notify, ++}, + }; + + module_phy_driver(aqr_driver); +@@ -856,6 +876,7 @@ static struct mdio_device_id __maybe_unu + { PHY_ID_MATCH_MODEL(PHY_ID_AQR111B0) }, + { PHY_ID_MATCH_MODEL(PHY_ID_AQR113) }, + { PHY_ID_MATCH_MODEL(PHY_ID_AQR113C) }, ++ { PHY_ID_MATCH_MODEL(PHY_ID_AQR813) }, + { } + }; + diff --git a/target/linux/generic/backport-6.1/765-v6.10-net-dsa-introduce-dsa_phylink_to_port.patch b/target/linux/generic/backport-6.1/765-v6.10-net-dsa-introduce-dsa_phylink_to_port.patch new file mode 100644 index 000000000..4cef7194e --- /dev/null +++ b/target/linux/generic/backport-6.1/765-v6.10-net-dsa-introduce-dsa_phylink_to_port.patch @@ -0,0 +1,99 @@ +From f13b2b33c7674fa0988dfaa9adb95d7d912b489f Mon Sep 17 00:00:00 2001 +From: "Russell King (Oracle)" +Date: Wed, 10 Apr 2024 20:42:38 +0100 +Subject: [PATCH 1/2] net: dsa: introduce dsa_phylink_to_port() + +We convert from a phylink_config struct to a dsa_port struct in many +places, let's provide a helper for this. + +Reviewed-by: Andrew Lunn +Reviewed-by: Florian Fainelli +Signed-off-by: Russell King (Oracle) +Reviewed-by: Vladimir Oltean +Link: https://lore.kernel.org/r/E1rudqA-006K9B-85@rmk-PC.armlinux.org.uk +Signed-off-by: Jakub Kicinski +--- + include/net/dsa.h | 6 ++++++ + net/dsa/port.c | 12 ++++++------ + 2 files changed, 12 insertions(+), 6 deletions(-) + +--- a/include/net/dsa.h ++++ b/include/net/dsa.h +@@ -337,6 +337,12 @@ struct dsa_port { + struct list_head vlans; + }; + ++static inline struct dsa_port * ++dsa_phylink_to_port(struct phylink_config *config) ++{ ++ return container_of(config, struct dsa_port, pl_config); ++} ++ + /* TODO: ideally DSA ports would have a single dp->link_dp member, + * and no dst->rtable nor this struct dsa_link would be needed, + * but this would require some more complex tree walking, +--- a/net/dsa/port.c ++++ b/net/dsa/port.c +@@ -1552,7 +1552,7 @@ static void dsa_port_phylink_validate(st + unsigned long *supported, + struct phylink_link_state *state) + { +- struct dsa_port *dp = container_of(config, struct dsa_port, pl_config); ++ struct dsa_port *dp = dsa_phylink_to_port(config); + struct dsa_switch *ds = dp->ds; + + if (!ds->ops->phylink_validate) { +@@ -1567,7 +1567,7 @@ static void dsa_port_phylink_validate(st + static void dsa_port_phylink_mac_pcs_get_state(struct phylink_config *config, + struct phylink_link_state *state) + { +- struct dsa_port *dp = container_of(config, struct dsa_port, pl_config); ++ struct dsa_port *dp = dsa_phylink_to_port(config); + struct dsa_switch *ds = dp->ds; + int err; + +@@ -1589,7 +1589,7 @@ static struct phylink_pcs * + dsa_port_phylink_mac_select_pcs(struct phylink_config *config, + phy_interface_t interface) + { +- struct dsa_port *dp = container_of(config, struct dsa_port, pl_config); ++ struct dsa_port *dp = dsa_phylink_to_port(config); + struct phylink_pcs *pcs = ERR_PTR(-EOPNOTSUPP); + struct dsa_switch *ds = dp->ds; + +@@ -1603,7 +1603,7 @@ static void dsa_port_phylink_mac_config( + unsigned int mode, + const struct phylink_link_state *state) + { +- struct dsa_port *dp = container_of(config, struct dsa_port, pl_config); ++ struct dsa_port *dp = dsa_phylink_to_port(config); + struct dsa_switch *ds = dp->ds; + + if (!ds->ops->phylink_mac_config) +@@ -1614,7 +1614,7 @@ static void dsa_port_phylink_mac_config( + + static void dsa_port_phylink_mac_an_restart(struct phylink_config *config) + { +- struct dsa_port *dp = container_of(config, struct dsa_port, pl_config); ++ struct dsa_port *dp = dsa_phylink_to_port(config); + struct dsa_switch *ds = dp->ds; + + if (!ds->ops->phylink_mac_an_restart) +@@ -1627,7 +1627,7 @@ static void dsa_port_phylink_mac_link_do + unsigned int mode, + phy_interface_t interface) + { +- struct dsa_port *dp = container_of(config, struct dsa_port, pl_config); ++ struct dsa_port *dp = dsa_phylink_to_port(config); + struct phy_device *phydev = NULL; + struct dsa_switch *ds = dp->ds; + +@@ -1650,7 +1650,7 @@ static void dsa_port_phylink_mac_link_up + int speed, int duplex, + bool tx_pause, bool rx_pause) + { +- struct dsa_port *dp = container_of(config, struct dsa_port, pl_config); ++ struct dsa_port *dp = dsa_phylink_to_port(config); + struct dsa_switch *ds = dp->ds; + + if (!ds->ops->phylink_mac_link_up) { diff --git a/target/linux/generic/backport-6.1/766-v6.10-net-dsa-allow-DSA-switch-drivers-to-provide-their-ow.patch b/target/linux/generic/backport-6.1/766-v6.10-net-dsa-allow-DSA-switch-drivers-to-provide-their-ow.patch new file mode 100644 index 000000000..0119925ab --- /dev/null +++ b/target/linux/generic/backport-6.1/766-v6.10-net-dsa-allow-DSA-switch-drivers-to-provide-their-ow.patch @@ -0,0 +1,117 @@ +From c22d8240fcd73a1c3ec8dcb055bd583fb970c375 Mon Sep 17 00:00:00 2001 +From: "Russell King (Oracle)" +Date: Wed, 10 Apr 2024 20:42:43 +0100 +Subject: [PATCH 2/2] net: dsa: allow DSA switch drivers to provide their own + phylink mac ops + +Rather than having a shim for each and every phylink MAC operation, +allow DSA switch drivers to provide their own ops structure. When a +DSA driver provides the phylink MAC operations, the shimmed ops must +not be provided, so fail an attempt to register a switch with both +the phylink_mac_ops in struct dsa_switch and the phylink_mac_* +operations populated in dsa_switch_ops populated. + +Signed-off-by: Russell King (Oracle) +Reviewed-by: Vladimir Oltean +Reviewed-by: Florian Fainelli +Link: https://lore.kernel.org/r/E1rudqF-006K9H-Cc@rmk-PC.armlinux.org.uk +Signed-off-by: Jakub Kicinski +--- + include/net/dsa.h | 5 +++++ + net/dsa/dsa.c | 11 +++++++++++ + net/dsa/port.c | 26 ++++++++++++++++++++------ + 3 files changed, 36 insertions(+), 6 deletions(-) + +--- a/include/net/dsa.h ++++ b/include/net/dsa.h +@@ -468,6 +468,11 @@ struct dsa_switch { + const struct dsa_switch_ops *ops; + + /* ++ * Allow a DSA switch driver to override the phylink MAC ops ++ */ ++ const struct phylink_mac_ops *phylink_mac_ops; ++ ++ /* + * Slave mii_bus and devices for the individual ports. + */ + u32 phys_mii_mask; +--- a/net/dsa/port.c ++++ b/net/dsa/port.c +@@ -1675,6 +1675,7 @@ static const struct phylink_mac_ops dsa_ + + int dsa_port_phylink_create(struct dsa_port *dp) + { ++ const struct phylink_mac_ops *mac_ops; + struct dsa_switch *ds = dp->ds; + phy_interface_t mode; + struct phylink *pl; +@@ -1694,8 +1695,12 @@ int dsa_port_phylink_create(struct dsa_p + if (ds->ops->phylink_get_caps) + ds->ops->phylink_get_caps(ds, dp->index, &dp->pl_config); + +- pl = phylink_create(&dp->pl_config, of_fwnode_handle(dp->dn), +- mode, &dsa_port_phylink_mac_ops); ++ mac_ops = &dsa_port_phylink_mac_ops; ++ if (ds->phylink_mac_ops) ++ mac_ops = ds->phylink_mac_ops; ++ ++ pl = phylink_create(&dp->pl_config, of_fwnode_handle(dp->dn), mode, ++ mac_ops); + if (IS_ERR(pl)) { + pr_err("error creating PHYLINK: %ld\n", PTR_ERR(pl)); + return PTR_ERR(pl); +@@ -1961,12 +1966,23 @@ static void dsa_shared_port_validate_of( + dn, dsa_port_is_cpu(dp) ? "CPU" : "DSA", dp->index); + } + ++static void dsa_shared_port_link_down(struct dsa_port *dp) ++{ ++ struct dsa_switch *ds = dp->ds; ++ ++ if (ds->phylink_mac_ops && ds->phylink_mac_ops->mac_link_down) ++ ds->phylink_mac_ops->mac_link_down(&dp->pl_config, MLO_AN_FIXED, ++ PHY_INTERFACE_MODE_NA); ++ else if (ds->ops->phylink_mac_link_down) ++ ds->ops->phylink_mac_link_down(ds, dp->index, MLO_AN_FIXED, ++ PHY_INTERFACE_MODE_NA); ++} ++ + int dsa_shared_port_link_register_of(struct dsa_port *dp) + { + struct dsa_switch *ds = dp->ds; + bool missing_link_description; + bool missing_phy_mode; +- int port = dp->index; + + dsa_shared_port_validate_of(dp, &missing_phy_mode, + &missing_link_description); +@@ -1982,9 +1998,7 @@ int dsa_shared_port_link_register_of(str + "Skipping phylink registration for %s port %d\n", + dsa_port_is_cpu(dp) ? "CPU" : "DSA", dp->index); + } else { +- if (ds->ops->phylink_mac_link_down) +- ds->ops->phylink_mac_link_down(ds, port, +- MLO_AN_FIXED, PHY_INTERFACE_MODE_NA); ++ dsa_shared_port_link_down(dp); + + return dsa_shared_port_phylink_register(dp); + } +--- a/net/dsa/dsa2.c ++++ b/net/dsa/dsa2.c +@@ -1758,6 +1758,15 @@ static int dsa_switch_probe(struct dsa_s + if (!ds->num_ports) + return -EINVAL; + ++ if (ds->phylink_mac_ops) { ++ if (ds->ops->phylink_mac_select_pcs || ++ ds->ops->phylink_mac_config || ++ ds->ops->phylink_mac_link_down || ++ ds->ops->phylink_mac_link_up || ++ ds->ops->adjust_link) ++ return -EINVAL; ++ } ++ + if (np) { + err = dsa_switch_parse_of(ds, np); + if (err) diff --git a/target/linux/generic/backport-6.1/770-net-introduce-napi_is_scheduled-helper.patch b/target/linux/generic/backport-6.1/770-net-introduce-napi_is_scheduled-helper.patch new file mode 100644 index 000000000..789b93e9f --- /dev/null +++ b/target/linux/generic/backport-6.1/770-net-introduce-napi_is_scheduled-helper.patch @@ -0,0 +1,96 @@ +From 7f3eb2174512fe6c9c0f062e96eccb0d3cc6d5cd Mon Sep 17 00:00:00 2001 +From: Christian Marangi +Date: Wed, 18 Oct 2023 14:35:47 +0200 +Subject: [PATCH] net: introduce napi_is_scheduled helper + +We currently have napi_if_scheduled_mark_missed that can be used to +check if napi is scheduled but that does more thing than simply checking +it and return a bool. Some driver already implement custom function to +check if napi is scheduled. + +Drop these custom function and introduce napi_is_scheduled that simply +check if napi is scheduled atomically. + +Update any driver and code that implement a similar check and instead +use this new helper. + +Signed-off-by: Christian Marangi +Signed-off-by: Paolo Abeni +--- + drivers/net/ethernet/chelsio/cxgb3/sge.c | 8 -------- + drivers/net/wireless/realtek/rtw89/core.c | 2 +- + include/linux/netdevice.h | 23 +++++++++++++++++++++++ + net/core/dev.c | 2 +- + 4 files changed, 25 insertions(+), 10 deletions(-) + +--- a/drivers/net/ethernet/chelsio/cxgb3/sge.c ++++ b/drivers/net/ethernet/chelsio/cxgb3/sge.c +@@ -2507,14 +2507,6 @@ static int napi_rx_handler(struct napi_s + return work_done; + } + +-/* +- * Returns true if the device is already scheduled for polling. +- */ +-static inline int napi_is_scheduled(struct napi_struct *napi) +-{ +- return test_bit(NAPI_STATE_SCHED, &napi->state); +-} +- + /** + * process_pure_responses - process pure responses from a response queue + * @adap: the adapter +--- a/drivers/net/wireless/realtek/rtw89/core.c ++++ b/drivers/net/wireless/realtek/rtw89/core.c +@@ -1479,7 +1479,7 @@ static void rtw89_core_rx_to_mac80211(st + struct napi_struct *napi = &rtwdev->napi; + + /* In low power mode, napi isn't scheduled. Receive it to netif. */ +- if (unlikely(!test_bit(NAPI_STATE_SCHED, &napi->state))) ++ if (unlikely(!napi_is_scheduled(napi))) + napi = NULL; + + rtw89_core_hw_to_sband_rate(rx_status); +--- a/include/linux/netdevice.h ++++ b/include/linux/netdevice.h +@@ -468,6 +468,29 @@ static inline bool napi_prefer_busy_poll + return test_bit(NAPI_STATE_PREFER_BUSY_POLL, &n->state); + } + ++/** ++ * napi_is_scheduled - test if NAPI is scheduled ++ * @n: NAPI context ++ * ++ * This check is "best-effort". With no locking implemented, ++ * a NAPI can be scheduled or terminate right after this check ++ * and produce not precise results. ++ * ++ * NAPI_STATE_SCHED is an internal state, napi_is_scheduled ++ * should not be used normally and napi_schedule should be ++ * used instead. ++ * ++ * Use only if the driver really needs to check if a NAPI ++ * is scheduled for example in the context of delayed timer ++ * that can be skipped if a NAPI is already scheduled. ++ * ++ * Return True if NAPI is scheduled, False otherwise. ++ */ ++static inline bool napi_is_scheduled(struct napi_struct *n) ++{ ++ return test_bit(NAPI_STATE_SCHED, &n->state); ++} ++ + bool napi_schedule_prep(struct napi_struct *n); + + /** +--- a/net/core/dev.c ++++ b/net/core/dev.c +@@ -6533,7 +6533,7 @@ static int __napi_poll(struct napi_struc + * accidentally calling ->poll() when NAPI is not scheduled. + */ + work = 0; +- if (test_bit(NAPI_STATE_SCHED, &n->state)) { ++ if (napi_is_scheduled(n)) { + work = n->poll(n, weight); + trace_napi_poll(n, work, weight); + } diff --git a/target/linux/generic/backport-6.1/771-v6.7-01-net-stmmac-improve-TX-timer-arm-logic.patch b/target/linux/generic/backport-6.1/771-v6.7-01-net-stmmac-improve-TX-timer-arm-logic.patch new file mode 100644 index 000000000..aa0d730bc --- /dev/null +++ b/target/linux/generic/backport-6.1/771-v6.7-01-net-stmmac-improve-TX-timer-arm-logic.patch @@ -0,0 +1,77 @@ +From 2d1a42cf7f77cda54dbbee18d00b1200e7bc22aa Mon Sep 17 00:00:00 2001 +From: Christian Marangi +Date: Wed, 18 Oct 2023 14:35:48 +0200 +Subject: [PATCH 1/3] net: stmmac: improve TX timer arm logic + +There is currently a problem with the TX timer getting armed multiple +unnecessary times causing big performance regression on some device that +suffer from heavy handling of hrtimer rearm. + +The use of the TX timer is an old implementation that predates the napi +implementation and the interrupt enable/disable handling. + +Due to stmmac being a very old code, the TX timer was never evaluated +again with this new implementation and was kept there causing +performance regression. The performance regression started to appear +with kernel version 4.19 with 8fce33317023 ("net: stmmac: Rework coalesce +timer and fix multi-queue races") where the timer was reduced to 1ms +causing it to be armed 40 times more than before. + +Decreasing the timer made the problem more present and caused the +regression in the other of 600-700mbps on some device (regression where +this was notice is ipq806x). + +The problem is in the fact that handling the hrtimer on some target is +expensive and recent kernel made the timer armed much more times. +A solution that was proposed was reverting the hrtimer change and use +mod_timer but such solution would still hide the real problem in the +current implementation. + +To fix the regression, apply some additional logic and skip arming the +timer when not needed. + +Arm the timer ONLY if a napi is not already scheduled. Running the timer +is redundant since the same function (stmmac_tx_clean) will run in the +napi TX poll. Also try to cancel any timer if a napi is scheduled to +prevent redundant run of TX call. + +With the following new logic the original performance are restored while +keeping using the hrtimer. + +Signed-off-by: Christian Marangi +Signed-off-by: Paolo Abeni +--- + .../net/ethernet/stmicro/stmmac/stmmac_main.c | 18 +++++++++++++++--- + 1 file changed, 15 insertions(+), 3 deletions(-) + +--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c ++++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +@@ -2974,13 +2974,25 @@ static void stmmac_tx_timer_arm(struct s + { + struct stmmac_tx_queue *tx_q = &priv->dma_conf.tx_queue[queue]; + u32 tx_coal_timer = priv->tx_coal_timer[queue]; ++ struct stmmac_channel *ch; ++ struct napi_struct *napi; + + if (!tx_coal_timer) + return; + +- hrtimer_start(&tx_q->txtimer, +- STMMAC_COAL_TIMER(tx_coal_timer), +- HRTIMER_MODE_REL); ++ ch = &priv->channel[tx_q->queue_index]; ++ napi = tx_q->xsk_pool ? &ch->rxtx_napi : &ch->tx_napi; ++ ++ /* Arm timer only if napi is not already scheduled. ++ * Try to cancel any timer if napi is scheduled, timer will be armed ++ * again in the next scheduled napi. ++ */ ++ if (unlikely(!napi_is_scheduled(napi))) ++ hrtimer_start(&tx_q->txtimer, ++ STMMAC_COAL_TIMER(tx_coal_timer), ++ HRTIMER_MODE_REL); ++ else ++ hrtimer_try_to_cancel(&tx_q->txtimer); + } + + /** diff --git a/target/linux/generic/backport-6.1/771-v6.7-02-net-stmmac-move-TX-timer-arm-after-DMA-enable.patch b/target/linux/generic/backport-6.1/771-v6.7-02-net-stmmac-move-TX-timer-arm-after-DMA-enable.patch new file mode 100644 index 000000000..873612065 --- /dev/null +++ b/target/linux/generic/backport-6.1/771-v6.7-02-net-stmmac-move-TX-timer-arm-after-DMA-enable.patch @@ -0,0 +1,96 @@ +From a594166387fe08e6f5a32130c400249a35b298f9 Mon Sep 17 00:00:00 2001 +From: Christian Marangi +Date: Wed, 18 Oct 2023 14:35:49 +0200 +Subject: [PATCH 2/3] net: stmmac: move TX timer arm after DMA enable + +Move TX timer arm call after DMA interrupt is enabled again. + +The TX timer arm function changed logic and now is skipped if a napi is +already scheduled. By moving the TX timer arm call after DMA is enabled, +we permit to correctly skip if a DMA interrupt has been fired and a napi +has been scheduled again. + +Signed-off-by: Christian Marangi +Signed-off-by: Paolo Abeni +--- + .../net/ethernet/stmicro/stmmac/stmmac_main.c | 22 +++++++++++++++---- + 1 file changed, 18 insertions(+), 4 deletions(-) + +--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c ++++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +@@ -2527,9 +2527,13 @@ static void stmmac_bump_dma_threshold(st + * @priv: driver private structure + * @budget: napi budget limiting this functions packet handling + * @queue: TX queue index ++ * @pending_packets: signal to arm the TX coal timer + * Description: it reclaims the transmit resources after transmission completes. ++ * If some packets still needs to be handled, due to TX coalesce, set ++ * pending_packets to true to make NAPI arm the TX coal timer. + */ +-static int stmmac_tx_clean(struct stmmac_priv *priv, int budget, u32 queue) ++static int stmmac_tx_clean(struct stmmac_priv *priv, int budget, u32 queue, ++ bool *pending_packets) + { + struct stmmac_tx_queue *tx_q = &priv->dma_conf.tx_queue[queue]; + unsigned int bytes_compl = 0, pkts_compl = 0; +@@ -2692,7 +2696,7 @@ static int stmmac_tx_clean(struct stmmac + + /* We still have pending packets, let's call for a new scheduling */ + if (tx_q->dirty_tx != tx_q->cur_tx) +- stmmac_tx_timer_arm(priv, queue); ++ *pending_packets = true; + + __netif_tx_unlock_bh(netdev_get_tx_queue(priv->dev, queue)); + +@@ -5490,12 +5494,13 @@ static int stmmac_napi_poll_tx(struct na + struct stmmac_channel *ch = + container_of(napi, struct stmmac_channel, tx_napi); + struct stmmac_priv *priv = ch->priv_data; ++ bool pending_packets = false; + u32 chan = ch->index; + int work_done; + + priv->xstats.napi_poll++; + +- work_done = stmmac_tx_clean(priv, budget, chan); ++ work_done = stmmac_tx_clean(priv, budget, chan, &pending_packets); + work_done = min(work_done, budget); + + if (work_done < budget && napi_complete_done(napi, work_done)) { +@@ -5506,6 +5511,10 @@ static int stmmac_napi_poll_tx(struct na + spin_unlock_irqrestore(&ch->lock, flags); + } + ++ /* TX still have packet to handle, check if we need to arm tx timer */ ++ if (pending_packets) ++ stmmac_tx_timer_arm(priv, chan); ++ + return work_done; + } + +@@ -5514,12 +5523,13 @@ static int stmmac_napi_poll_rxtx(struct + struct stmmac_channel *ch = + container_of(napi, struct stmmac_channel, rxtx_napi); + struct stmmac_priv *priv = ch->priv_data; ++ bool tx_pending_packets = false; + int rx_done, tx_done, rxtx_done; + u32 chan = ch->index; + + priv->xstats.napi_poll++; + +- tx_done = stmmac_tx_clean(priv, budget, chan); ++ tx_done = stmmac_tx_clean(priv, budget, chan, &tx_pending_packets); + tx_done = min(tx_done, budget); + + rx_done = stmmac_rx_zc(priv, budget, chan); +@@ -5544,6 +5554,10 @@ static int stmmac_napi_poll_rxtx(struct + spin_unlock_irqrestore(&ch->lock, flags); + } + ++ /* TX still have packet to handle, check if we need to arm tx timer */ ++ if (tx_pending_packets) ++ stmmac_tx_timer_arm(priv, chan); ++ + return min(rxtx_done, budget - 1); + } + diff --git a/target/linux/generic/backport-6.1/771-v6.7-03-net-stmmac-increase-TX-coalesce-timer-to-5ms.patch b/target/linux/generic/backport-6.1/771-v6.7-03-net-stmmac-increase-TX-coalesce-timer-to-5ms.patch new file mode 100644 index 000000000..bce54eba4 --- /dev/null +++ b/target/linux/generic/backport-6.1/771-v6.7-03-net-stmmac-increase-TX-coalesce-timer-to-5ms.patch @@ -0,0 +1,38 @@ +From 039550960a2235cfe2dfaa773df9f98f8da31a0c Mon Sep 17 00:00:00 2001 +From: Christian Marangi +Date: Wed, 18 Oct 2023 14:35:50 +0200 +Subject: [PATCH 3/3] net: stmmac: increase TX coalesce timer to 5ms + +Commit 8fce33317023 ("net: stmmac: Rework coalesce timer and fix +multi-queue races") decreased the TX coalesce timer from 40ms to 1ms. + +This caused some performance regression on some target (regression was +reported at least on ipq806x) in the order of 600mbps dropping from +gigabit handling to only 200mbps. + +The problem was identified in the TX timer getting armed too much time. +While this was fixed and improved in another commit, performance can be +improved even further by increasing the timer delay a bit moving from +1ms to 5ms. + +The value is a good balance between battery saving by prevending too +much interrupt to be generated and permitting good performance for +internet oriented devices. + +Signed-off-by: Christian Marangi +Signed-off-by: Paolo Abeni +--- + drivers/net/ethernet/stmicro/stmmac/common.h | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/drivers/net/ethernet/stmicro/stmmac/common.h ++++ b/drivers/net/ethernet/stmicro/stmmac/common.h +@@ -287,7 +287,7 @@ struct stmmac_safety_stats { + #define MIN_DMA_RIWT 0x10 + #define DEF_DMA_RIWT 0xa0 + /* Tx coalesce parameters */ +-#define STMMAC_COAL_TX_TIMER 1000 ++#define STMMAC_COAL_TX_TIMER 5000 + #define STMMAC_MAX_COAL_TX_TICK 100000 + #define STMMAC_TX_MAX_FRAMES 256 + #define STMMAC_TX_FRAMES 25 diff --git a/target/linux/generic/backport-6.1/778-v6.3-01-net-dsa-qca8k-add-QCA8K_ATU_TABLE_SIZE-define-for-fd.patch b/target/linux/generic/backport-6.1/778-v6.3-01-net-dsa-qca8k-add-QCA8K_ATU_TABLE_SIZE-define-for-fd.patch new file mode 100644 index 000000000..9331fd536 --- /dev/null +++ b/target/linux/generic/backport-6.1/778-v6.3-01-net-dsa-qca8k-add-QCA8K_ATU_TABLE_SIZE-define-for-fd.patch @@ -0,0 +1,63 @@ +From e03cea60c3db8c6b011cc36ecef9281dff8377f3 Mon Sep 17 00:00:00 2001 +From: Christian Marangi +Date: Wed, 25 Jan 2023 21:35:16 +0100 +Subject: [PATCH] net: dsa: qca8k: add QCA8K_ATU_TABLE_SIZE define for fdb + access + +Add and use QCA8K_ATU_TABLE_SIZE instead of hardcoding the ATU size with +a pure number and using sizeof on the array. + +Signed-off-by: Christian Marangi +Signed-off-by: David S. Miller +--- + drivers/net/dsa/qca/qca8k-common.c | 10 ++++++---- + drivers/net/dsa/qca/qca8k.h | 2 ++ + 2 files changed, 8 insertions(+), 4 deletions(-) + +--- a/drivers/net/dsa/qca/qca8k-common.c ++++ b/drivers/net/dsa/qca/qca8k-common.c +@@ -150,11 +150,12 @@ static int qca8k_busy_wait(struct qca8k_ + + static int qca8k_fdb_read(struct qca8k_priv *priv, struct qca8k_fdb *fdb) + { +- u32 reg[3]; ++ u32 reg[QCA8K_ATU_TABLE_SIZE]; + int ret; + + /* load the ARL table into an array */ +- ret = qca8k_bulk_read(priv, QCA8K_REG_ATU_DATA0, reg, sizeof(reg)); ++ ret = qca8k_bulk_read(priv, QCA8K_REG_ATU_DATA0, reg, ++ QCA8K_ATU_TABLE_SIZE * sizeof(u32)); + if (ret) + return ret; + +@@ -178,7 +179,7 @@ static int qca8k_fdb_read(struct qca8k_p + static void qca8k_fdb_write(struct qca8k_priv *priv, u16 vid, u8 port_mask, + const u8 *mac, u8 aging) + { +- u32 reg[3] = { 0 }; ++ u32 reg[QCA8K_ATU_TABLE_SIZE] = { 0 }; + + /* vid - 83:72 */ + reg[2] = FIELD_PREP(QCA8K_ATU_VID_MASK, vid); +@@ -195,7 +196,8 @@ static void qca8k_fdb_write(struct qca8k + reg[0] |= FIELD_PREP(QCA8K_ATU_ADDR5_MASK, mac[5]); + + /* load the array into the ARL table */ +- qca8k_bulk_write(priv, QCA8K_REG_ATU_DATA0, reg, sizeof(reg)); ++ qca8k_bulk_write(priv, QCA8K_REG_ATU_DATA0, reg, ++ QCA8K_ATU_TABLE_SIZE * sizeof(u32)); + } + + static int qca8k_fdb_access(struct qca8k_priv *priv, enum qca8k_fdb_cmd cmd, +--- a/drivers/net/dsa/qca/qca8k.h ++++ b/drivers/net/dsa/qca/qca8k.h +@@ -148,6 +148,8 @@ + #define QCA8K_REG_IPV4_PRI_ADDR_MASK 0x474 + + /* Lookup registers */ ++#define QCA8K_ATU_TABLE_SIZE 3 /* 12 bytes wide table / sizeof(u32) */ ++ + #define QCA8K_REG_ATU_DATA0 0x600 + #define QCA8K_ATU_ADDR2_MASK GENMASK(31, 24) + #define QCA8K_ATU_ADDR3_MASK GENMASK(23, 16) diff --git a/target/linux/generic/backport-6.1/778-v6.3-02-net-dsa-qca8k-convert-to-regmap-read-write-API.patch b/target/linux/generic/backport-6.1/778-v6.3-02-net-dsa-qca8k-convert-to-regmap-read-write-API.patch new file mode 100644 index 000000000..b8f8071b0 --- /dev/null +++ b/target/linux/generic/backport-6.1/778-v6.3-02-net-dsa-qca8k-convert-to-regmap-read-write-API.patch @@ -0,0 +1,261 @@ +From c766e077d927e1775902c18827205ea2ade3a35d Mon Sep 17 00:00:00 2001 +From: Christian Marangi +Date: Wed, 25 Jan 2023 21:35:17 +0100 +Subject: [PATCH] net: dsa: qca8k: convert to regmap read/write API + +Convert qca8k to regmap read/write bulk API. The mgmt eth can write up +to 32 bytes of data at times. Currently we use a custom function to do +it but regmap now supports declaration of read/write bulk even without a +bus. + +Drop the custom function and rework the regmap function to this new +implementation. + +Rework the qca8k_fdb_read/write function to use the new +regmap_bulk_read/write as the old qca8k_bulk_read/write are now dropped. + +Cc: Mark Brown +Signed-off-by: Christian Marangi +Signed-off-by: David S. Miller +--- + drivers/net/dsa/qca/qca8k-8xxx.c | 92 ++++++++++++++++++++++++------ + drivers/net/dsa/qca/qca8k-common.c | 47 ++------------- + drivers/net/dsa/qca/qca8k.h | 3 - + 3 files changed, 77 insertions(+), 65 deletions(-) + +--- a/drivers/net/dsa/qca/qca8k-8xxx.c ++++ b/drivers/net/dsa/qca/qca8k-8xxx.c +@@ -425,16 +425,12 @@ qca8k_regmap_update_bits_eth(struct qca8 + } + + static int +-qca8k_regmap_read(void *ctx, uint32_t reg, uint32_t *val) ++qca8k_read_mii(struct qca8k_priv *priv, uint32_t reg, uint32_t *val) + { +- struct qca8k_priv *priv = (struct qca8k_priv *)ctx; + struct mii_bus *bus = priv->bus; + u16 r1, r2, page; + int ret; + +- if (!qca8k_read_eth(priv, reg, val, sizeof(*val))) +- return 0; +- + qca8k_split_addr(reg, &r1, &r2, &page); + + mutex_lock_nested(&bus->mdio_lock, MDIO_MUTEX_NESTED); +@@ -451,16 +447,12 @@ exit: + } + + static int +-qca8k_regmap_write(void *ctx, uint32_t reg, uint32_t val) ++qca8k_write_mii(struct qca8k_priv *priv, uint32_t reg, uint32_t val) + { +- struct qca8k_priv *priv = (struct qca8k_priv *)ctx; + struct mii_bus *bus = priv->bus; + u16 r1, r2, page; + int ret; + +- if (!qca8k_write_eth(priv, reg, &val, sizeof(val))) +- return 0; +- + qca8k_split_addr(reg, &r1, &r2, &page); + + mutex_lock_nested(&bus->mdio_lock, MDIO_MUTEX_NESTED); +@@ -477,17 +469,14 @@ exit: + } + + static int +-qca8k_regmap_update_bits(void *ctx, uint32_t reg, uint32_t mask, uint32_t write_val) ++qca8k_regmap_update_bits_mii(struct qca8k_priv *priv, uint32_t reg, ++ uint32_t mask, uint32_t write_val) + { +- struct qca8k_priv *priv = (struct qca8k_priv *)ctx; + struct mii_bus *bus = priv->bus; + u16 r1, r2, page; + u32 val; + int ret; + +- if (!qca8k_regmap_update_bits_eth(priv, reg, mask, write_val)) +- return 0; +- + qca8k_split_addr(reg, &r1, &r2, &page); + + mutex_lock_nested(&bus->mdio_lock, MDIO_MUTEX_NESTED); +@@ -510,17 +499,84 @@ exit: + return ret; + } + ++static int ++qca8k_bulk_read(void *ctx, const void *reg_buf, size_t reg_len, ++ void *val_buf, size_t val_len) ++{ ++ int i, count = val_len / sizeof(u32), ret; ++ u32 reg = *(u32 *)reg_buf & U16_MAX; ++ struct qca8k_priv *priv = ctx; ++ ++ if (priv->mgmt_master && ++ !qca8k_read_eth(priv, reg, val_buf, val_len)) ++ return 0; ++ ++ /* loop count times and increment reg of 4 */ ++ for (i = 0; i < count; i++, reg += sizeof(u32)) { ++ ret = qca8k_read_mii(priv, reg, val_buf + i); ++ if (ret < 0) ++ return ret; ++ } ++ ++ return 0; ++} ++ ++static int ++qca8k_bulk_gather_write(void *ctx, const void *reg_buf, size_t reg_len, ++ const void *val_buf, size_t val_len) ++{ ++ int i, count = val_len / sizeof(u32), ret; ++ u32 reg = *(u32 *)reg_buf & U16_MAX; ++ struct qca8k_priv *priv = ctx; ++ u32 *val = (u32 *)val_buf; ++ ++ if (priv->mgmt_master && ++ !qca8k_write_eth(priv, reg, val, val_len)) ++ return 0; ++ ++ /* loop count times, increment reg of 4 and increment val ptr to ++ * the next value ++ */ ++ for (i = 0; i < count; i++, reg += sizeof(u32), val++) { ++ ret = qca8k_write_mii(priv, reg, *val); ++ if (ret < 0) ++ return ret; ++ } ++ ++ return 0; ++} ++ ++static int ++qca8k_bulk_write(void *ctx, const void *data, size_t bytes) ++{ ++ return qca8k_bulk_gather_write(ctx, data, sizeof(u16), data + sizeof(u16), ++ bytes - sizeof(u16)); ++} ++ ++static int ++qca8k_regmap_update_bits(void *ctx, uint32_t reg, uint32_t mask, uint32_t write_val) ++{ ++ struct qca8k_priv *priv = ctx; ++ ++ if (!qca8k_regmap_update_bits_eth(priv, reg, mask, write_val)) ++ return 0; ++ ++ return qca8k_regmap_update_bits_mii(priv, reg, mask, write_val); ++} ++ + static struct regmap_config qca8k_regmap_config = { + .reg_bits = 16, + .val_bits = 32, + .reg_stride = 4, + .max_register = 0x16ac, /* end MIB - Port6 range */ +- .reg_read = qca8k_regmap_read, +- .reg_write = qca8k_regmap_write, ++ .read = qca8k_bulk_read, ++ .write = qca8k_bulk_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 */ ++ .max_raw_read = 32, /* mgmt eth can read/write up to 8 registers at time */ ++ .max_raw_write = 32, + }; + + static int +@@ -2112,8 +2168,6 @@ static SIMPLE_DEV_PM_OPS(qca8k_pm_ops, + + static const struct qca8k_info_ops qca8xxx_ops = { + .autocast_mib = qca8k_get_ethtool_stats_eth, +- .read_eth = qca8k_read_eth, +- .write_eth = qca8k_write_eth, + }; + + static const struct qca8k_match_data qca8327 = { +--- a/drivers/net/dsa/qca/qca8k-common.c ++++ b/drivers/net/dsa/qca/qca8k-common.c +@@ -101,45 +101,6 @@ const struct regmap_access_table qca8k_r + .n_yes_ranges = ARRAY_SIZE(qca8k_readable_ranges), + }; + +-/* TODO: remove these extra ops when we can support regmap bulk read/write */ +-static int qca8k_bulk_read(struct qca8k_priv *priv, u32 reg, u32 *val, int len) +-{ +- int i, count = len / sizeof(u32), ret; +- +- if (priv->mgmt_master && priv->info->ops->read_eth && +- !priv->info->ops->read_eth(priv, reg, val, len)) +- return 0; +- +- for (i = 0; i < count; i++) { +- ret = regmap_read(priv->regmap, reg + (i * 4), val + i); +- if (ret < 0) +- return ret; +- } +- +- return 0; +-} +- +-/* TODO: remove these extra ops when we can support regmap bulk read/write */ +-static int qca8k_bulk_write(struct qca8k_priv *priv, u32 reg, u32 *val, int len) +-{ +- int i, count = len / sizeof(u32), ret; +- u32 tmp; +- +- if (priv->mgmt_master && priv->info->ops->write_eth && +- !priv->info->ops->write_eth(priv, reg, val, len)) +- return 0; +- +- for (i = 0; i < count; i++) { +- tmp = val[i]; +- +- ret = regmap_write(priv->regmap, reg + (i * 4), tmp); +- if (ret < 0) +- return ret; +- } +- +- return 0; +-} +- + static int qca8k_busy_wait(struct qca8k_priv *priv, u32 reg, u32 mask) + { + u32 val; +@@ -154,8 +115,8 @@ static int qca8k_fdb_read(struct qca8k_p + int ret; + + /* load the ARL table into an array */ +- ret = qca8k_bulk_read(priv, QCA8K_REG_ATU_DATA0, reg, +- QCA8K_ATU_TABLE_SIZE * sizeof(u32)); ++ ret = regmap_bulk_read(priv->regmap, QCA8K_REG_ATU_DATA0, reg, ++ QCA8K_ATU_TABLE_SIZE); + if (ret) + return ret; + +@@ -196,8 +157,8 @@ static void qca8k_fdb_write(struct qca8k + reg[0] |= FIELD_PREP(QCA8K_ATU_ADDR5_MASK, mac[5]); + + /* load the array into the ARL table */ +- qca8k_bulk_write(priv, QCA8K_REG_ATU_DATA0, reg, +- QCA8K_ATU_TABLE_SIZE * sizeof(u32)); ++ regmap_bulk_write(priv->regmap, QCA8K_REG_ATU_DATA0, reg, ++ QCA8K_ATU_TABLE_SIZE); + } + + static int qca8k_fdb_access(struct qca8k_priv *priv, enum qca8k_fdb_cmd cmd, +--- a/drivers/net/dsa/qca/qca8k.h ++++ b/drivers/net/dsa/qca/qca8k.h +@@ -330,9 +330,6 @@ struct qca8k_priv; + + struct qca8k_info_ops { + int (*autocast_mib)(struct dsa_switch *ds, int port, u64 *data); +- /* TODO: remove these extra ops when we can support regmap bulk read/write */ +- int (*read_eth)(struct qca8k_priv *priv, u32 reg, u32 *val, int len); +- int (*write_eth)(struct qca8k_priv *priv, u32 reg, u32 *val, int len); + }; + + struct qca8k_match_data { diff --git a/target/linux/generic/backport-6.1/779-v6.5-net-dsa-qca8k-enable-use_single_write-for-qca8xxx.patch b/target/linux/generic/backport-6.1/779-v6.5-net-dsa-qca8k-enable-use_single_write-for-qca8xxx.patch new file mode 100644 index 000000000..3619440f0 --- /dev/null +++ b/target/linux/generic/backport-6.1/779-v6.5-net-dsa-qca8k-enable-use_single_write-for-qca8xxx.patch @@ -0,0 +1,78 @@ +From 2c39dd025da489cf87d26469d9f5ff19715324a0 Mon Sep 17 00:00:00 2001 +From: Christian Marangi +Date: Mon, 24 Jul 2023 05:25:28 +0200 +Subject: [PATCH 1/4] net: dsa: qca8k: enable use_single_write for qca8xxx + +The qca8xxx switch supports 2 way to write reg values, a slow way using +mdio and a fast way by sending specially crafted mgmt packet to +read/write reg. + +The fast way can support up to 32 bytes of data as eth packet are used +to send/receive. + +This correctly works for almost the entire regmap of the switch but with +the use of some kernel selftests for dsa drivers it was found a funny +and interesting hw defect/limitation. + +For some specific reg, bulk write won't work and will result in writing +only part of the requested regs resulting in half data written. This was +especially hard to track and discover due to the total strangeness of +the problem and also by the specific regs where this occurs. + +This occurs in the specific regs of the ATU table, where multiple entry +needs to be written to compose the entire entry. +It was discovered that with a bulk write of 12 bytes on +QCA8K_REG_ATU_DATA0 only QCA8K_REG_ATU_DATA0 and QCA8K_REG_ATU_DATA2 +were written, but QCA8K_REG_ATU_DATA1 was always zero. +Tcpdump was used to make sure the specially crafted packet was correct +and this was confirmed. + +The problem was hard to track as the lack of QCA8K_REG_ATU_DATA1 +resulted in an entry somehow possible as the first bytes of the mac +address are set in QCA8K_REG_ATU_DATA0 and the entry type is set in +QCA8K_REG_ATU_DATA2. + +Funlly enough writing QCA8K_REG_ATU_DATA1 results in the same problem +with QCA8K_REG_ATU_DATA2 empty and QCA8K_REG_ATU_DATA1 and +QCA8K_REG_ATU_FUNC correctly written. +A speculation on the problem might be that there are some kind of +indirection internally when accessing these regs and they can't be +accessed all together, due to the fact that it's really a table mapped +somewhere in the switch SRAM. + +Even more funny is the fact that every other reg was tested with all +kind of combination and they are not affected by this problem. Read +operation was also tested and always worked so it's not affected by this +problem. + +The problem is not present if we limit writing a single reg at times. + +To handle this hardware defect, enable use_single_write so that bulk +api can correctly split the write in multiple different operation +effectively reverting to a non-bulk write. + +Cc: Mark Brown +Fixes: c766e077d927 ("net: dsa: qca8k: convert to regmap read/write API") +Signed-off-by: Christian Marangi +Cc: stable@vger.kernel.org +Signed-off-by: David S. Miller +--- + drivers/net/dsa/qca/qca8k-8xxx.c | 7 +++++-- + 1 file changed, 5 insertions(+), 2 deletions(-) + +--- a/drivers/net/dsa/qca/qca8k-8xxx.c ++++ b/drivers/net/dsa/qca/qca8k-8xxx.c +@@ -575,8 +575,11 @@ static struct regmap_config qca8k_regmap + .rd_table = &qca8k_readable_table, + .disable_locking = true, /* Locking is handled by qca8k read/write */ + .cache_type = REGCACHE_NONE, /* Explicitly disable CACHE */ +- .max_raw_read = 32, /* mgmt eth can read/write up to 8 registers at time */ +- .max_raw_write = 32, ++ .max_raw_read = 32, /* mgmt eth can read up to 8 registers at time */ ++ /* ATU regs suffer from a bug where some data are not correctly ++ * written. Disable bulk write to correctly write ATU entry. ++ */ ++ .use_single_write = true, + }; + + static int diff --git a/target/linux/generic/backport-6.1/780-v6.6-01-net-dsa-qca8k-make-learning-configurable-and-keep-of.patch b/target/linux/generic/backport-6.1/780-v6.6-01-net-dsa-qca8k-make-learning-configurable-and-keep-of.patch new file mode 100644 index 000000000..d31789370 --- /dev/null +++ b/target/linux/generic/backport-6.1/780-v6.6-01-net-dsa-qca8k-make-learning-configurable-and-keep-of.patch @@ -0,0 +1,146 @@ +From 23cfc7172e5297d0bee49ac6f6f8248d1cf0820d Mon Sep 17 00:00:00 2001 +From: Christian Marangi +Date: Sun, 30 Jul 2023 09:41:10 +0200 +Subject: [PATCH 1/4] net: dsa: qca8k: make learning configurable and keep off + if standalone + +Address learning should initially be turned off by the driver for port +operation in standalone mode, then the DSA core handles changes to it +via ds->ops->port_bridge_flags(). + +Currently this is not the case for qca8k where learning is enabled +unconditionally in qca8k_setup for every user port. + +Handle ports configured in standalone mode by making the learning +configurable and not enabling it by default. + +Implement .port_pre_bridge_flags and .port_bridge_flags dsa ops to +enable learning for bridge that request it and tweak +.port_stp_state_set to correctly disable learning when port is +configured in standalone mode. + +Signed-off-by: Christian Marangi +Reviewed-by: Vladimir Oltean +Reviewed-by: Florian Fainelli +Link: https://lore.kernel.org/r/20230730074113.21889-2-ansuelsmth@gmail.com +Signed-off-by: Paolo Abeni +--- + drivers/net/dsa/qca/qca8k-8xxx.c | 7 +++-- + drivers/net/dsa/qca/qca8k-common.c | 48 ++++++++++++++++++++++++++++++ + drivers/net/dsa/qca/qca8k.h | 6 ++++ + 3 files changed, 58 insertions(+), 3 deletions(-) + +--- a/drivers/net/dsa/qca/qca8k-8xxx.c ++++ b/drivers/net/dsa/qca/qca8k-8xxx.c +@@ -1905,9 +1905,8 @@ qca8k_setup(struct dsa_switch *ds) + if (ret) + return ret; + +- /* Enable ARP Auto-learning by default */ +- ret = regmap_set_bits(priv->regmap, QCA8K_PORT_LOOKUP_CTRL(i), +- QCA8K_PORT_LOOKUP_LEARN); ++ ret = regmap_clear_bits(priv->regmap, QCA8K_PORT_LOOKUP_CTRL(i), ++ QCA8K_PORT_LOOKUP_LEARN); + if (ret) + return ret; + +@@ -2013,6 +2012,8 @@ static const struct dsa_switch_ops qca8k + .port_change_mtu = qca8k_port_change_mtu, + .port_max_mtu = qca8k_port_max_mtu, + .port_stp_state_set = qca8k_port_stp_state_set, ++ .port_pre_bridge_flags = qca8k_port_pre_bridge_flags, ++ .port_bridge_flags = qca8k_port_bridge_flags, + .port_bridge_join = qca8k_port_bridge_join, + .port_bridge_leave = qca8k_port_bridge_leave, + .port_fast_age = qca8k_port_fast_age, +--- a/drivers/net/dsa/qca/qca8k-common.c ++++ b/drivers/net/dsa/qca/qca8k-common.c +@@ -565,9 +565,26 @@ int qca8k_get_mac_eee(struct dsa_switch + return 0; + } + ++static int qca8k_port_configure_learning(struct dsa_switch *ds, int port, ++ bool learning) ++{ ++ struct qca8k_priv *priv = ds->priv; ++ ++ if (learning) ++ return regmap_set_bits(priv->regmap, ++ QCA8K_PORT_LOOKUP_CTRL(port), ++ QCA8K_PORT_LOOKUP_LEARN); ++ else ++ return regmap_clear_bits(priv->regmap, ++ QCA8K_PORT_LOOKUP_CTRL(port), ++ QCA8K_PORT_LOOKUP_LEARN); ++} ++ + void qca8k_port_stp_state_set(struct dsa_switch *ds, int port, u8 state) + { ++ struct dsa_port *dp = dsa_to_port(ds, port); + struct qca8k_priv *priv = ds->priv; ++ bool learning = false; + u32 stp_state; + + switch (state) { +@@ -582,8 +599,11 @@ void qca8k_port_stp_state_set(struct dsa + break; + case BR_STATE_LEARNING: + stp_state = QCA8K_PORT_LOOKUP_STATE_LEARNING; ++ learning = dp->learning; + break; + case BR_STATE_FORWARDING: ++ learning = dp->learning; ++ fallthrough; + default: + stp_state = QCA8K_PORT_LOOKUP_STATE_FORWARD; + break; +@@ -591,6 +611,34 @@ void qca8k_port_stp_state_set(struct dsa + + qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(port), + QCA8K_PORT_LOOKUP_STATE_MASK, stp_state); ++ ++ qca8k_port_configure_learning(ds, port, learning); ++} ++ ++int qca8k_port_pre_bridge_flags(struct dsa_switch *ds, int port, ++ struct switchdev_brport_flags flags, ++ struct netlink_ext_ack *extack) ++{ ++ if (flags.mask & ~BR_LEARNING) ++ return -EINVAL; ++ ++ return 0; ++} ++ ++int qca8k_port_bridge_flags(struct dsa_switch *ds, int port, ++ struct switchdev_brport_flags flags, ++ struct netlink_ext_ack *extack) ++{ ++ int ret; ++ ++ if (flags.mask & BR_LEARNING) { ++ ret = qca8k_port_configure_learning(ds, port, ++ flags.val & BR_LEARNING); ++ if (ret) ++ return ret; ++ } ++ ++ return 0; + } + + int qca8k_port_bridge_join(struct dsa_switch *ds, int port, +--- a/drivers/net/dsa/qca/qca8k.h ++++ b/drivers/net/dsa/qca/qca8k.h +@@ -448,6 +448,12 @@ int qca8k_get_mac_eee(struct dsa_switch + + /* Common bridge function */ + void qca8k_port_stp_state_set(struct dsa_switch *ds, int port, u8 state); ++int qca8k_port_pre_bridge_flags(struct dsa_switch *ds, int port, ++ struct switchdev_brport_flags flags, ++ struct netlink_ext_ack *extack); ++int qca8k_port_bridge_flags(struct dsa_switch *ds, int port, ++ struct switchdev_brport_flags flags, ++ struct netlink_ext_ack *extack); + int qca8k_port_bridge_join(struct dsa_switch *ds, int port, + struct dsa_bridge bridge, + bool *tx_fwd_offload, diff --git a/target/linux/generic/backport-6.1/780-v6.6-02-net-dsa-qca8k-limit-user-ports-access-to-the-first-C.patch b/target/linux/generic/backport-6.1/780-v6.6-02-net-dsa-qca8k-limit-user-ports-access-to-the-first-C.patch new file mode 100644 index 000000000..4b457f67d --- /dev/null +++ b/target/linux/generic/backport-6.1/780-v6.6-02-net-dsa-qca8k-limit-user-ports-access-to-the-first-C.patch @@ -0,0 +1,53 @@ +From 18e8feae4a807994e4906d659116d249bfecd4c5 Mon Sep 17 00:00:00 2001 +From: Christian Marangi +Date: Sun, 30 Jul 2023 09:41:11 +0200 +Subject: [PATCH 2/4] net: dsa: qca8k: limit user ports access to the first CPU + port on setup + +In preparation for multi-CPU support, set CPU port LOOKUP MEMBER outside +the port loop and setup the LOOKUP MEMBER mask for user ports only to +the first CPU port. + +This is to handle flooding condition where every CPU port is set as +target and prevent packet duplication for unknown frames from user ports. + +Secondary CPU port LOOKUP MEMBER mask will be setup later when +port_change_master will be implemented. + +Signed-off-by: Christian Marangi +Reviewed-by: Simon Horman +Reviewed-by: Florian Fainelli +Reviewed-by: Vladimir Oltean +Link: https://lore.kernel.org/r/20230730074113.21889-3-ansuelsmth@gmail.com +Signed-off-by: Paolo Abeni +--- + drivers/net/dsa/qca/qca8k-8xxx.c | 14 ++++++-------- + 1 file changed, 6 insertions(+), 8 deletions(-) + +--- a/drivers/net/dsa/qca/qca8k-8xxx.c ++++ b/drivers/net/dsa/qca/qca8k-8xxx.c +@@ -1885,18 +1885,16 @@ qca8k_setup(struct dsa_switch *ds) + if (ret) + return ret; + ++ /* CPU port gets connected to all user ports of the switch */ ++ ret = qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(cpu_port), ++ QCA8K_PORT_LOOKUP_MEMBER, dsa_user_ports(ds)); ++ if (ret) ++ return ret; ++ + /* Setup connection between CPU port & user ports + * Configure specific switch configuration for ports + */ + for (i = 0; i < QCA8K_NUM_PORTS; i++) { +- /* CPU port gets connected to all user ports of the switch */ +- if (dsa_is_cpu_port(ds, i)) { +- ret = qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(i), +- QCA8K_PORT_LOOKUP_MEMBER, dsa_user_ports(ds)); +- if (ret) +- return ret; +- } +- + /* Individual user ports get connected to CPU port only */ + if (dsa_is_user_port(ds, i)) { + ret = qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(i), diff --git a/target/linux/generic/backport-6.1/780-v6.6-03-net-dsa-qca8k-move-qca8xxx-hol-fixup-to-separate-fun.patch b/target/linux/generic/backport-6.1/780-v6.6-03-net-dsa-qca8k-move-qca8xxx-hol-fixup-to-separate-fun.patch new file mode 100644 index 000000000..f556628b5 --- /dev/null +++ b/target/linux/generic/backport-6.1/780-v6.6-03-net-dsa-qca8k-move-qca8xxx-hol-fixup-to-separate-fun.patch @@ -0,0 +1,111 @@ +From a9108b0712bf018dc69020864b21485b71b17dfc Mon Sep 17 00:00:00 2001 +From: Christian Marangi +Date: Sun, 30 Jul 2023 09:41:12 +0200 +Subject: [PATCH 3/4] net: dsa: qca8k: move qca8xxx hol fixup to separate + function + +Move qca8xxx hol fixup to separate function to tidy things up and to +permit using a more efficent loop in future patch. + +Signed-off-by: Christian Marangi +Reviewed-by: Florian Fainelli +Link: https://lore.kernel.org/r/20230730074113.21889-4-ansuelsmth@gmail.com +Signed-off-by: Paolo Abeni +--- + drivers/net/dsa/qca/qca8k-8xxx.c | 78 +++++++++++++++++--------------- + 1 file changed, 42 insertions(+), 36 deletions(-) + +--- a/drivers/net/dsa/qca/qca8k-8xxx.c ++++ b/drivers/net/dsa/qca/qca8k-8xxx.c +@@ -1795,6 +1795,46 @@ static int qca8k_connect_tag_protocol(st + return 0; + } + ++static void qca8k_setup_hol_fixup(struct qca8k_priv *priv, int port) ++{ ++ u32 mask; ++ ++ switch (port) { ++ /* 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); ++ } ++ regmap_write(priv->regmap, QCA8K_REG_PORT_HOL_CTRL0(port), 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; ++ regmap_update_bits(priv->regmap, QCA8K_REG_PORT_HOL_CTRL1(port), ++ QCA8K_PORT_HOL_CTRL1_ING_BUF_MASK | ++ QCA8K_PORT_HOL_CTRL1_EG_PRI_BUF_EN | ++ QCA8K_PORT_HOL_CTRL1_EG_PORT_BUF_EN | ++ QCA8K_PORT_HOL_CTRL1_WRED_EN, ++ mask); ++} ++ + static int + qca8k_setup(struct dsa_switch *ds) + { +@@ -1930,42 +1970,8 @@ qca8k_setup(struct dsa_switch *ds) + * missing settings to improve switch stability under load condition. + * This problem is limited to qca8337 and other qca8k switch are not affected. + */ +- if (priv->switch_id == QCA8K_ID_QCA8337) { +- switch (i) { +- /* The 2 CPU port and port 5 requires some different +- * priority than any other ports. +- */ +- case 0: +- case 5: +- case 6: +- mask = QCA8K_PORT_HOL_CTRL0_EG_PRI0(0x3) | +- QCA8K_PORT_HOL_CTRL0_EG_PRI1(0x4) | +- QCA8K_PORT_HOL_CTRL0_EG_PRI2(0x4) | +- QCA8K_PORT_HOL_CTRL0_EG_PRI3(0x4) | +- QCA8K_PORT_HOL_CTRL0_EG_PRI4(0x6) | +- QCA8K_PORT_HOL_CTRL0_EG_PRI5(0x8) | +- QCA8K_PORT_HOL_CTRL0_EG_PORT(0x1e); +- break; +- default: +- mask = QCA8K_PORT_HOL_CTRL0_EG_PRI0(0x3) | +- QCA8K_PORT_HOL_CTRL0_EG_PRI1(0x4) | +- QCA8K_PORT_HOL_CTRL0_EG_PRI2(0x6) | +- QCA8K_PORT_HOL_CTRL0_EG_PRI3(0x8) | +- QCA8K_PORT_HOL_CTRL0_EG_PORT(0x19); +- } +- qca8k_write(priv, QCA8K_REG_PORT_HOL_CTRL0(i), mask); +- +- mask = QCA8K_PORT_HOL_CTRL1_ING(0x6) | +- QCA8K_PORT_HOL_CTRL1_EG_PRI_BUF_EN | +- QCA8K_PORT_HOL_CTRL1_EG_PORT_BUF_EN | +- QCA8K_PORT_HOL_CTRL1_WRED_EN; +- qca8k_rmw(priv, QCA8K_REG_PORT_HOL_CTRL1(i), +- QCA8K_PORT_HOL_CTRL1_ING_BUF_MASK | +- QCA8K_PORT_HOL_CTRL1_EG_PRI_BUF_EN | +- QCA8K_PORT_HOL_CTRL1_EG_PORT_BUF_EN | +- QCA8K_PORT_HOL_CTRL1_WRED_EN, +- mask); +- } ++ if (priv->switch_id == QCA8K_ID_QCA8337) ++ qca8k_setup_hol_fixup(priv, i); + } + + /* Special GLOBAL_FC_THRESH value are needed for ar8327 switch */ diff --git a/target/linux/generic/backport-6.1/780-v6.6-04-net-dsa-qca8k-use-dsa_for_each-macro-instead-of-for-.patch b/target/linux/generic/backport-6.1/780-v6.6-04-net-dsa-qca8k-use-dsa_for_each-macro-instead-of-for-.patch new file mode 100644 index 000000000..faa0142ca --- /dev/null +++ b/target/linux/generic/backport-6.1/780-v6.6-04-net-dsa-qca8k-use-dsa_for_each-macro-instead-of-for-.patch @@ -0,0 +1,158 @@ +From 01e6f8ad8d26ced14b0cf288c42e55d03a7c5070 Mon Sep 17 00:00:00 2001 +From: Christian Marangi +Date: Sun, 30 Jul 2023 09:41:13 +0200 +Subject: [PATCH 4/4] net: dsa: qca8k: use dsa_for_each macro instead of for + loop + +Convert for loop to dsa_for_each macro to save some redundant write on +unconnected/unused port and tidy things up. + +Signed-off-by: Christian Marangi +Reviewed-by: Florian Fainelli +Link: https://lore.kernel.org/r/20230730074113.21889-5-ansuelsmth@gmail.com +Signed-off-by: Paolo Abeni +--- + drivers/net/dsa/qca/qca8k-8xxx.c | 107 ++++++++++++++++--------------- + 1 file changed, 54 insertions(+), 53 deletions(-) + +--- a/drivers/net/dsa/qca/qca8k-8xxx.c ++++ b/drivers/net/dsa/qca/qca8k-8xxx.c +@@ -1839,7 +1839,8 @@ static int + qca8k_setup(struct dsa_switch *ds) + { + struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv; +- int cpu_port, ret, i; ++ struct dsa_port *dp; ++ int cpu_port, ret; + u32 mask; + + cpu_port = qca8k_find_cpu_port(ds); +@@ -1890,27 +1891,27 @@ qca8k_setup(struct dsa_switch *ds) + dev_warn(priv->dev, "mib init failed"); + + /* Initial setup of all ports */ +- for (i = 0; i < QCA8K_NUM_PORTS; i++) { ++ dsa_switch_for_each_port(dp, ds) { + /* Disable forwarding by default on all ports */ +- ret = qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(i), ++ ret = qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(dp->index), + QCA8K_PORT_LOOKUP_MEMBER, 0); + if (ret) + return ret; ++ } + +- /* Enable QCA header mode on all cpu ports */ +- if (dsa_is_cpu_port(ds, i)) { +- ret = qca8k_write(priv, QCA8K_REG_PORT_HDR_CTRL(i), +- FIELD_PREP(QCA8K_PORT_HDR_CTRL_TX_MASK, QCA8K_PORT_HDR_CTRL_ALL) | +- FIELD_PREP(QCA8K_PORT_HDR_CTRL_RX_MASK, QCA8K_PORT_HDR_CTRL_ALL)); +- if (ret) { +- dev_err(priv->dev, "failed enabling QCA header mode"); +- return ret; +- } ++ /* Disable MAC by default on all user ports */ ++ dsa_switch_for_each_user_port(dp, ds) ++ qca8k_port_set_status(priv, dp->index, 0); ++ ++ /* Enable QCA header mode on all cpu ports */ ++ dsa_switch_for_each_cpu_port(dp, ds) { ++ ret = qca8k_write(priv, QCA8K_REG_PORT_HDR_CTRL(dp->index), ++ 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 on port %d", dp->index); ++ 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 +@@ -1932,48 +1933,48 @@ qca8k_setup(struct dsa_switch *ds) + return ret; + + /* Setup connection between CPU port & user ports +- * Configure specific switch configuration for ports ++ * Individual user ports get connected to CPU port only + */ +- for (i = 0; i < QCA8K_NUM_PORTS; i++) { +- /* Individual user ports get connected to CPU port only */ +- if (dsa_is_user_port(ds, i)) { +- ret = qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(i), +- QCA8K_PORT_LOOKUP_MEMBER, +- BIT(cpu_port)); +- if (ret) +- return ret; +- +- ret = regmap_clear_bits(priv->regmap, QCA8K_PORT_LOOKUP_CTRL(i), +- QCA8K_PORT_LOOKUP_LEARN); +- if (ret) +- return ret; +- +- /* For port based vlans to work we need to set the +- * default egress vid +- */ +- ret = qca8k_rmw(priv, QCA8K_EGRESS_VLAN(i), +- QCA8K_EGREES_VLAN_PORT_MASK(i), +- QCA8K_EGREES_VLAN_PORT(i, QCA8K_PORT_VID_DEF)); +- if (ret) +- return ret; +- +- ret = qca8k_write(priv, QCA8K_REG_PORT_VLAN_CTRL0(i), +- QCA8K_PORT_VLAN_CVID(QCA8K_PORT_VID_DEF) | +- QCA8K_PORT_VLAN_SVID(QCA8K_PORT_VID_DEF)); +- if (ret) +- return ret; +- } ++ dsa_switch_for_each_user_port(dp, ds) { ++ u8 port = dp->index; + +- /* 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. ++ ret = qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(port), ++ QCA8K_PORT_LOOKUP_MEMBER, ++ BIT(cpu_port)); ++ if (ret) ++ return ret; ++ ++ ret = regmap_clear_bits(priv->regmap, QCA8K_PORT_LOOKUP_CTRL(port), ++ QCA8K_PORT_LOOKUP_LEARN); ++ if (ret) ++ return ret; ++ ++ /* For port based vlans to work we need to set the ++ * default egress vid + */ +- if (priv->switch_id == QCA8K_ID_QCA8337) +- qca8k_setup_hol_fixup(priv, i); ++ ret = qca8k_rmw(priv, QCA8K_EGRESS_VLAN(port), ++ QCA8K_EGREES_VLAN_PORT_MASK(port), ++ QCA8K_EGREES_VLAN_PORT(port, QCA8K_PORT_VID_DEF)); ++ if (ret) ++ return ret; ++ ++ ret = qca8k_write(priv, QCA8K_REG_PORT_VLAN_CTRL0(port), ++ QCA8K_PORT_VLAN_CVID(QCA8K_PORT_VID_DEF) | ++ QCA8K_PORT_VLAN_SVID(QCA8K_PORT_VID_DEF)); ++ if (ret) ++ return ret; + } + ++ /* The port 5 of the qca8337 have some problem in flood condition. The ++ * original legacy driver had some specific buffer and priority settings ++ * for the different port suggested by the QCA switch team. Add this ++ * missing settings to improve switch stability under load condition. ++ * This problem is limited to qca8337 and other qca8k switch are not affected. ++ */ ++ if (priv->switch_id == QCA8K_ID_QCA8337) ++ dsa_switch_for_each_available_port(dp, ds) ++ qca8k_setup_hol_fixup(priv, dp->index); ++ + /* 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) | diff --git a/target/linux/generic/backport-6.1/781-v6.6-01-net-dsa-qca8k-fix-regmap-bulk-read-write-methods-on-.patch b/target/linux/generic/backport-6.1/781-v6.6-01-net-dsa-qca8k-fix-regmap-bulk-read-write-methods-on-.patch new file mode 100644 index 000000000..632f422d1 --- /dev/null +++ b/target/linux/generic/backport-6.1/781-v6.6-01-net-dsa-qca8k-fix-regmap-bulk-read-write-methods-on-.patch @@ -0,0 +1,61 @@ +From 5652d1741574eb89cc02576e50ee3e348bd6dd77 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Marek=20Beh=C3=BAn?= +Date: Wed, 4 Oct 2023 11:19:03 +0200 +Subject: [PATCH 1/2] net: dsa: qca8k: fix regmap bulk read/write methods on + big endian systems +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Commit c766e077d927 ("net: dsa: qca8k: convert to regmap read/write +API") introduced bulk read/write methods to qca8k's regmap. + +The regmap bulk read/write methods get the register address in a buffer +passed as a void pointer parameter (the same buffer contains also the +read/written values). The register address occupies only as many bytes +as it requires at the beginning of this buffer. For example if the +.reg_bits member in regmap_config is 16 (as is the case for this +driver), the register address occupies only the first 2 bytes in this +buffer, so it can be cast to u16. + +But the original commit implementing these bulk read/write methods cast +the buffer to u32: + u32 reg = *(u32 *)reg_buf & U16_MAX; +taking the first 4 bytes. This works on little endian systems where the +first 2 bytes of the buffer correspond to the low 16-bits, but it +obviously cannot work on big endian systems. + +Fix this by casting the beginning of the buffer to u16 as + u32 reg = *(u16 *)reg_buf; + +Fixes: c766e077d927 ("net: dsa: qca8k: convert to regmap read/write API") +Signed-off-by: Marek Behún +Tested-by: Christian Marangi +Reviewed-by: Christian Marangi +Signed-off-by: David S. Miller +--- + drivers/net/dsa/qca/qca8k-8xxx.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +--- a/drivers/net/dsa/qca/qca8k-8xxx.c ++++ b/drivers/net/dsa/qca/qca8k-8xxx.c +@@ -504,8 +504,8 @@ qca8k_bulk_read(void *ctx, const void *r + void *val_buf, size_t val_len) + { + int i, count = val_len / sizeof(u32), ret; +- u32 reg = *(u32 *)reg_buf & U16_MAX; + struct qca8k_priv *priv = ctx; ++ u32 reg = *(u16 *)reg_buf; + + if (priv->mgmt_master && + !qca8k_read_eth(priv, reg, val_buf, val_len)) +@@ -526,8 +526,8 @@ qca8k_bulk_gather_write(void *ctx, const + const void *val_buf, size_t val_len) + { + int i, count = val_len / sizeof(u32), ret; +- u32 reg = *(u32 *)reg_buf & U16_MAX; + struct qca8k_priv *priv = ctx; ++ u32 reg = *(u16 *)reg_buf; + u32 *val = (u32 *)val_buf; + + if (priv->mgmt_master && diff --git a/target/linux/generic/backport-6.1/790-01-v6.2-net-dsa-mt7530-remove-redundant-assignment.patch b/target/linux/generic/backport-6.1/790-01-v6.2-net-dsa-mt7530-remove-redundant-assignment.patch new file mode 100644 index 000000000..e0319fd35 --- /dev/null +++ b/target/linux/generic/backport-6.1/790-01-v6.2-net-dsa-mt7530-remove-redundant-assignment.patch @@ -0,0 +1,32 @@ +From 27b692012840f658c84a3102c0aeca2676f03132 Mon Sep 17 00:00:00 2001 +From: Daniel Golle +Date: Thu, 15 Dec 2022 03:47:59 +0000 +Subject: [PATCH 01/48] net: dsa: mt7530: remove redundant assignment + +Russell King correctly pointed out that the MAC_2500FD capability is +already added for port 5 (if not in RGMII mode) and port 6 (which only +supports SGMII) by mt7531_mac_port_get_caps. Remove the reduntant +setting of this capability flag which was added by a previous commit. + +Fixes: e19de30d2080 ("net: dsa: mt7530: add support for in-band link status") +Reported-by: Russell King (Oracle) +Reviewed-by: Russell King (Oracle) +Signed-off-by: Daniel Golle +Link: https://lore.kernel.org/r/Y5qY7x6la5TxZxzX@makrotopia.org +Signed-off-by: Jakub Kicinski +--- + drivers/net/dsa/mt7530.c | 3 --- + 1 file changed, 3 deletions(-) + +--- a/drivers/net/dsa/mt7530.c ++++ b/drivers/net/dsa/mt7530.c +@@ -3198,9 +3198,6 @@ static void mt753x_phylink_get_caps(stru + config->mac_capabilities = MAC_ASYM_PAUSE | MAC_SYM_PAUSE | + MAC_10 | MAC_100 | MAC_1000FD; + +- if ((priv->id == ID_MT7531) && mt753x_is_mac_port(port)) +- config->mac_capabilities |= MAC_2500FD; +- + /* This driver does not make use of the speed, duplex, pause or the + * advertisement in its mac_config, so it is safe to mark this driver + * as non-legacy. diff --git a/target/linux/generic/backport-6.1/788-v6.3-net-dsa-mt7530-use-external-PCS-driver.patch b/target/linux/generic/backport-6.1/790-02-v6.4-net-dsa-mt7530-use-external-PCS-driver.patch similarity index 84% rename from target/linux/generic/backport-6.1/788-v6.3-net-dsa-mt7530-use-external-PCS-driver.patch rename to target/linux/generic/backport-6.1/790-02-v6.4-net-dsa-mt7530-use-external-PCS-driver.patch index c6570badd..2697f2e56 100644 --- a/target/linux/generic/backport-6.1/788-v6.3-net-dsa-mt7530-use-external-PCS-driver.patch +++ b/target/linux/generic/backport-6.1/790-02-v6.4-net-dsa-mt7530-use-external-PCS-driver.patch @@ -1,60 +1,23 @@ -From patchwork Thu Mar 9 10:57:44 2023 -Content-Type: text/plain; charset="utf-8" -MIME-Version: 1.0 -Content-Transfer-Encoding: 8bit -X-Patchwork-Submitter: Daniel Golle -X-Patchwork-Id: 13167235 -X-Patchwork-Delegate: kuba@kernel.org -Return-Path: -Date: Thu, 9 Mar 2023 10:57:44 +0000 +From 05dc5ea089f947a69a5db092ef4cad6a0f3c96ce Mon Sep 17 00:00:00 2001 From: Daniel Golle -To: netdev@vger.kernel.org, linux-mediatek@lists.infradead.org, - linux-arm-kernel@lists.infradead.org, linux-kernel@vger.kernel.org, - Russell King , - Heiner Kallweit , - Lorenzo Bianconi , - Mark Lee , - John Crispin , Felix Fietkau , - AngeloGioacchino Del Regno - , - Matthias Brugger , - DENG Qingfang , - Landen Chao , - Sean Wang , - Paolo Abeni , - Jakub Kicinski , - Eric Dumazet , - "David S. Miller" , - Vladimir Oltean , - Florian Fainelli , - Andrew Lunn , - Vladimir Oltean -Cc: =?iso-8859-1?q?Bj=F8rn?= Mork , - Frank Wunderlich , - Alexander Couzens -Subject: [PATCH net-next v13 11/16] net: dsa: mt7530: use external PCS driver -Message-ID: - <2ac2ee40d3b0e705461b50613fda6a7edfdbc4b3.1678357225.git.daniel@makrotopia.org> -References: +Date: Sun, 19 Mar 2023 12:58:43 +0000 +Subject: [PATCH 02/48] net: dsa: mt7530: use external PCS driver MIME-Version: 1.0 -Content-Disposition: inline -In-Reply-To: -Precedence: bulk -List-ID: -X-Mailing-List: netdev@vger.kernel.org -X-Patchwork-Delegate: kuba@kernel.org +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit Implement regmap access wrappers, for now only to be used by the -pcs-mtk driver. -Make use of external PCS driver and drop the reduntant implementation -in mt7530.c. +pcs-mtk-lynxi driver. +Make use of this external PCS driver and drop the now reduntant +implementation in mt7530.c. As a nice side effect the SGMII registers can now also more easily be inspected for debugging via /sys/kernel/debug/regmap. -Reviewed-by: Russell King (Oracle) Tested-by: Bjørn Mork Signed-off-by: Daniel Golle Tested-by: Frank Wunderlich +Reviewed-by: Russell King (Oracle) +Signed-off-by: Jakub Kicinski --- drivers/net/dsa/Kconfig | 1 + drivers/net/dsa/mt7530.c | 277 ++++++++++----------------------------- @@ -238,7 +201,7 @@ Tested-by: Frank Wunderlich default: return NULL; } -@@ -3257,86 +3141,6 @@ static void mt7530_pcs_get_state(struct +@@ -3254,86 +3138,6 @@ static void mt7530_pcs_get_state(struct state->pause |= MLO_PAUSE_TX; } @@ -325,7 +288,7 @@ Tested-by: Frank Wunderlich static int mt753x_pcs_config(struct phylink_pcs *pcs, unsigned int mode, phy_interface_t interface, const unsigned long *advertising, -@@ -3356,18 +3160,57 @@ static const struct phylink_pcs_ops mt75 +@@ -3353,18 +3157,57 @@ static const struct phylink_pcs_ops mt75 .pcs_an_restart = mt7530_pcs_an_restart, }; @@ -389,7 +352,7 @@ Tested-by: Frank Wunderlich int i, ret; /* Initialise the PCS devices */ -@@ -3375,8 +3218,6 @@ mt753x_setup(struct dsa_switch *ds) +@@ -3372,8 +3215,6 @@ mt753x_setup(struct dsa_switch *ds) priv->pcs[i].pcs.ops = priv->info->pcs_ops; priv->pcs[i].priv = priv; priv->pcs[i].port = i; @@ -398,7 +361,7 @@ Tested-by: Frank Wunderlich } ret = priv->info->sw_setup(ds); -@@ -3391,6 +3232,16 @@ mt753x_setup(struct dsa_switch *ds) +@@ -3388,6 +3229,16 @@ mt753x_setup(struct dsa_switch *ds) if (ret && priv->irq) mt7530_free_irq_common(priv); @@ -415,7 +378,7 @@ Tested-by: Frank Wunderlich return ret; } -@@ -3483,7 +3334,7 @@ static const struct mt753x_info mt753x_t +@@ -3480,7 +3331,7 @@ static const struct mt753x_info mt753x_t }, [ID_MT7531] = { .id = ID_MT7531, @@ -424,7 +387,7 @@ Tested-by: Frank Wunderlich .sw_setup = mt7531_setup, .phy_read = mt7531_ind_phy_read, .phy_write = mt7531_ind_phy_write, -@@ -3591,7 +3442,7 @@ static void +@@ -3588,7 +3439,7 @@ static void mt7530_remove(struct mdio_device *mdiodev) { struct mt7530_priv *priv = dev_get_drvdata(&mdiodev->dev); @@ -433,7 +396,7 @@ Tested-by: Frank Wunderlich if (!priv) return; -@@ -3610,6 +3461,10 @@ mt7530_remove(struct mdio_device *mdiode +@@ -3607,6 +3458,10 @@ mt7530_remove(struct mdio_device *mdiode mt7530_free_irq(priv); dsa_unregister_switch(priv->ds); diff --git a/target/linux/generic/backport-6.1/790-v6.4-0001-net-dsa-mt7530-make-some-noise-if-register-read-fail.patch b/target/linux/generic/backport-6.1/790-03-v6.4-net-dsa-mt7530-make-some-noise-if-register-read-fail.patch similarity index 88% rename from target/linux/generic/backport-6.1/790-v6.4-0001-net-dsa-mt7530-make-some-noise-if-register-read-fail.patch rename to target/linux/generic/backport-6.1/790-03-v6.4-net-dsa-mt7530-make-some-noise-if-register-read-fail.patch index 4d024b063..d8f559b41 100644 --- a/target/linux/generic/backport-6.1/790-v6.4-0001-net-dsa-mt7530-make-some-noise-if-register-read-fail.patch +++ b/target/linux/generic/backport-6.1/790-03-v6.4-net-dsa-mt7530-make-some-noise-if-register-read-fail.patch @@ -1,7 +1,7 @@ -From c3552d3f85f06cf4b4818bd84c4fcc09d8d45165 Mon Sep 17 00:00:00 2001 +From cafbb70e148e7a4e318dcc1e36a96643815b6245 Mon Sep 17 00:00:00 2001 From: Daniel Golle Date: Mon, 3 Apr 2023 02:17:19 +0100 -Subject: [PATCH 01/13] net: dsa: mt7530: make some noise if register read +Subject: [PATCH 03/48] net: dsa: mt7530: make some noise if register read fails Simply returning the negative error value instead of the read value diff --git a/target/linux/generic/backport-6.1/790-v6.4-0002-net-dsa-mt7530-refactor-SGMII-PCS-creation.patch b/target/linux/generic/backport-6.1/790-04-v6.4-net-dsa-mt7530-refactor-SGMII-PCS-creation.patch similarity index 92% rename from target/linux/generic/backport-6.1/790-v6.4-0002-net-dsa-mt7530-refactor-SGMII-PCS-creation.patch rename to target/linux/generic/backport-6.1/790-04-v6.4-net-dsa-mt7530-refactor-SGMII-PCS-creation.patch index ef0bdeedd..1bf19a813 100644 --- a/target/linux/generic/backport-6.1/790-v6.4-0002-net-dsa-mt7530-refactor-SGMII-PCS-creation.patch +++ b/target/linux/generic/backport-6.1/790-04-v6.4-net-dsa-mt7530-refactor-SGMII-PCS-creation.patch @@ -1,7 +1,7 @@ -From b896355fc4988216d4f38582d07add9252a795ae Mon Sep 17 00:00:00 2001 +From 8f83ad87e2df26ddf9b8afd4d2873644a872d929 Mon Sep 17 00:00:00 2001 From: Daniel Golle Date: Mon, 3 Apr 2023 02:17:30 +0100 -Subject: [PATCH 02/13] net: dsa: mt7530: refactor SGMII PCS creation +Subject: [PATCH 04/48] net: dsa: mt7530: refactor SGMII PCS creation Instead of macro templates use a dedidated function and allocated regmap_config when creating the regmaps for the pcs-mtk-lynxi @@ -18,7 +18,7 @@ Signed-off-by: David S. Miller --- a/drivers/net/dsa/mt7530.c +++ b/drivers/net/dsa/mt7530.c -@@ -3192,26 +3192,56 @@ static const struct regmap_bus mt7531_re +@@ -3189,26 +3189,56 @@ static const struct regmap_bus mt7531_re .reg_update_bits = mt7530_regmap_update_bits, }; @@ -88,7 +88,7 @@ Signed-off-by: David S. Miller int i, ret; /* Initialise the PCS devices */ -@@ -3233,15 +3263,11 @@ mt753x_setup(struct dsa_switch *ds) +@@ -3230,15 +3260,11 @@ mt753x_setup(struct dsa_switch *ds) if (ret && priv->irq) mt7530_free_irq_common(priv); diff --git a/target/linux/generic/backport-6.1/790-v6.4-0003-net-dsa-mt7530-use-unlocked-regmap-accessors.patch b/target/linux/generic/backport-6.1/790-05-v6.4-net-dsa-mt7530-use-unlocked-regmap-accessors.patch similarity index 86% rename from target/linux/generic/backport-6.1/790-v6.4-0003-net-dsa-mt7530-use-unlocked-regmap-accessors.patch rename to target/linux/generic/backport-6.1/790-05-v6.4-net-dsa-mt7530-use-unlocked-regmap-accessors.patch index dad12607a..bd28b4be7 100644 --- a/target/linux/generic/backport-6.1/790-v6.4-0003-net-dsa-mt7530-use-unlocked-regmap-accessors.patch +++ b/target/linux/generic/backport-6.1/790-05-v6.4-net-dsa-mt7530-use-unlocked-regmap-accessors.patch @@ -1,7 +1,7 @@ -From 33396408776385f3d2f6069646169a6b5b28e3b3 Mon Sep 17 00:00:00 2001 +From efb41b8e9b7bbb08ace1930373bff63d4f5cc6e2 Mon Sep 17 00:00:00 2001 From: Daniel Golle Date: Mon, 3 Apr 2023 02:17:40 +0100 -Subject: [PATCH 03/13] net: dsa: mt7530: use unlocked regmap accessors +Subject: [PATCH 05/48] net: dsa: mt7530: use unlocked regmap accessors Instead of wrapping the locked register accessor functions, use the unlocked variants and add locking wrapper functions to let regmap @@ -19,7 +19,7 @@ Signed-off-by: David S. Miller --- a/drivers/net/dsa/mt7530.c +++ b/drivers/net/dsa/mt7530.c -@@ -3165,7 +3165,7 @@ static int mt7530_regmap_read(void *cont +@@ -3162,7 +3162,7 @@ static int mt7530_regmap_read(void *cont { struct mt7530_priv *priv = context; @@ -28,7 +28,7 @@ Signed-off-by: David S. Miller return 0; }; -@@ -3173,23 +3173,25 @@ static int mt7530_regmap_write(void *con +@@ -3170,23 +3170,25 @@ static int mt7530_regmap_write(void *con { struct mt7530_priv *priv = context; @@ -62,7 +62,7 @@ Signed-off-by: David S. Miller }; static int -@@ -3215,6 +3217,9 @@ mt7531_create_sgmii(struct mt7530_priv * +@@ -3212,6 +3214,9 @@ mt7531_create_sgmii(struct mt7530_priv * mt7531_pcs_config[i]->reg_stride = 4; mt7531_pcs_config[i]->reg_base = MT7531_SGMII_REG_BASE(5 + i); mt7531_pcs_config[i]->max_register = 0x17c; diff --git a/target/linux/generic/backport-6.1/790-v6.4-0004-net-dsa-mt7530-use-regmap-to-access-switch-register-.patch b/target/linux/generic/backport-6.1/790-06-v6.4-net-dsa-mt7530-use-regmap-to-access-switch-register-.patch similarity index 92% rename from target/linux/generic/backport-6.1/790-v6.4-0004-net-dsa-mt7530-use-regmap-to-access-switch-register-.patch rename to target/linux/generic/backport-6.1/790-06-v6.4-net-dsa-mt7530-use-regmap-to-access-switch-register-.patch index 7dc524dbb..42c225d91 100644 --- a/target/linux/generic/backport-6.1/790-v6.4-0004-net-dsa-mt7530-use-regmap-to-access-switch-register-.patch +++ b/target/linux/generic/backport-6.1/790-06-v6.4-net-dsa-mt7530-use-regmap-to-access-switch-register-.patch @@ -1,7 +1,7 @@ -From 743cba4345cb366248f9d375c6a9e50243dc0677 Mon Sep 17 00:00:00 2001 +From c7945d11a060797c31b3f47d9c9e515b0bf2082f Mon Sep 17 00:00:00 2001 From: Daniel Golle Date: Mon, 3 Apr 2023 02:17:52 +0100 -Subject: [PATCH 04/13] net: dsa: mt7530: use regmap to access switch register +Subject: [PATCH 06/48] net: dsa: mt7530: use regmap to access switch register space Use regmap API to access the switch register space. @@ -133,7 +133,7 @@ Signed-off-by: David S. Miller } static void -@@ -3161,22 +3182,6 @@ static const struct phylink_pcs_ops mt75 +@@ -3158,22 +3179,6 @@ static const struct phylink_pcs_ops mt75 .pcs_an_restart = mt7530_pcs_an_restart, }; @@ -156,7 +156,7 @@ Signed-off-by: David S. Miller static void mt7530_mdio_regmap_lock(void *mdio_lock) { -@@ -3189,7 +3194,7 @@ mt7530_mdio_regmap_unlock(void *mdio_loc +@@ -3186,7 +3191,7 @@ mt7530_mdio_regmap_unlock(void *mdio_loc mutex_unlock(mdio_lock); } @@ -165,7 +165,7 @@ Signed-off-by: David S. Miller .reg_write = mt7530_regmap_write, .reg_read = mt7530_regmap_read, }; -@@ -3222,7 +3227,7 @@ mt7531_create_sgmii(struct mt7530_priv * +@@ -3219,7 +3224,7 @@ mt7531_create_sgmii(struct mt7530_priv * mt7531_pcs_config[i]->lock_arg = &priv->bus->mdio_lock; regmap = devm_regmap_init(priv->dev, @@ -174,7 +174,7 @@ Signed-off-by: David S. Miller mt7531_pcs_config[i]); if (IS_ERR(regmap)) { ret = PTR_ERR(regmap); -@@ -3388,6 +3393,7 @@ MODULE_DEVICE_TABLE(of, mt7530_of_match) +@@ -3385,6 +3390,7 @@ MODULE_DEVICE_TABLE(of, mt7530_of_match) static int mt7530_probe(struct mdio_device *mdiodev) { @@ -182,7 +182,7 @@ Signed-off-by: David S. Miller struct mt7530_priv *priv; struct device_node *dn; -@@ -3467,6 +3473,21 @@ mt7530_probe(struct mdio_device *mdiodev +@@ -3464,6 +3470,21 @@ mt7530_probe(struct mdio_device *mdiodev mutex_init(&priv->reg_mutex); dev_set_drvdata(&mdiodev->dev, priv); diff --git a/target/linux/generic/backport-6.1/790-v6.4-0005-net-dsa-mt7530-move-SGMII-PCS-creation-to-mt7530_pro.patch b/target/linux/generic/backport-6.1/790-07-v6.4-net-dsa-mt7530-move-SGMII-PCS-creation-to-mt7530_pro.patch similarity index 79% rename from target/linux/generic/backport-6.1/790-v6.4-0005-net-dsa-mt7530-move-SGMII-PCS-creation-to-mt7530_pro.patch rename to target/linux/generic/backport-6.1/790-07-v6.4-net-dsa-mt7530-move-SGMII-PCS-creation-to-mt7530_pro.patch index a9fefab77..9cd817c05 100644 --- a/target/linux/generic/backport-6.1/790-v6.4-0005-net-dsa-mt7530-move-SGMII-PCS-creation-to-mt7530_pro.patch +++ b/target/linux/generic/backport-6.1/790-07-v6.4-net-dsa-mt7530-move-SGMII-PCS-creation-to-mt7530_pro.patch @@ -1,7 +1,7 @@ -From f3cf1d06e2aef644b426c23b4bb570780b1f8d47 Mon Sep 17 00:00:00 2001 +From 6b64b5bc3045a7e1ee5278df4f4ff315cd43d88a Mon Sep 17 00:00:00 2001 From: Daniel Golle Date: Mon, 3 Apr 2023 02:18:04 +0100 -Subject: [PATCH 05/13] net: dsa: mt7530: move SGMII PCS creation to +Subject: [PATCH 07/48] net: dsa: mt7530: move SGMII PCS creation to mt7530_probe function Move creating the SGMII PCS from mt753x_setup() to the more appropriate @@ -18,7 +18,7 @@ Signed-off-by: David S. Miller --- a/drivers/net/dsa/mt7530.c +++ b/drivers/net/dsa/mt7530.c -@@ -3273,12 +3273,6 @@ mt753x_setup(struct dsa_switch *ds) +@@ -3270,12 +3270,6 @@ mt753x_setup(struct dsa_switch *ds) if (ret && priv->irq) mt7530_free_irq_common(priv); @@ -31,7 +31,7 @@ Signed-off-by: David S. Miller return ret; } -@@ -3396,6 +3390,7 @@ mt7530_probe(struct mdio_device *mdiodev +@@ -3393,6 +3387,7 @@ mt7530_probe(struct mdio_device *mdiodev static struct regmap_config *regmap_config; struct mt7530_priv *priv; struct device_node *dn; @@ -39,7 +39,7 @@ Signed-off-by: David S. Miller dn = mdiodev->dev.of_node; -@@ -3488,6 +3483,12 @@ mt7530_probe(struct mdio_device *mdiodev +@@ -3485,6 +3480,12 @@ mt7530_probe(struct mdio_device *mdiodev if (IS_ERR(priv->regmap)) return PTR_ERR(priv->regmap); diff --git a/target/linux/generic/backport-6.1/790-v6.4-0006-net-dsa-mt7530-introduce-mutex-helpers.patch b/target/linux/generic/backport-6.1/790-08-v6.4-net-dsa-mt7530-introduce-mutex-helpers.patch similarity index 98% rename from target/linux/generic/backport-6.1/790-v6.4-0006-net-dsa-mt7530-introduce-mutex-helpers.patch rename to target/linux/generic/backport-6.1/790-08-v6.4-net-dsa-mt7530-introduce-mutex-helpers.patch index a063f78bb..4f77078ee 100644 --- a/target/linux/generic/backport-6.1/790-v6.4-0006-net-dsa-mt7530-introduce-mutex-helpers.patch +++ b/target/linux/generic/backport-6.1/790-08-v6.4-net-dsa-mt7530-introduce-mutex-helpers.patch @@ -1,7 +1,7 @@ -From e4729ae7c095c0c87794bff47ea43e35d69de986 Mon Sep 17 00:00:00 2001 +From 504d39cbda402df3e6fd123d040520393b6a6297 Mon Sep 17 00:00:00 2001 From: Daniel Golle Date: Mon, 3 Apr 2023 02:18:16 +0100 -Subject: [PATCH 06/13] net: dsa: mt7530: introduce mutex helpers +Subject: [PATCH 08/48] net: dsa: mt7530: introduce mutex helpers As the MDIO bus lock only needs to be involved if actually operating on an MDIO-connected switch we will need to skip locking for built-in diff --git a/target/linux/generic/backport-6.1/790-v6.4-0007-net-dsa-mt7530-move-p5_intf_modes-function-to-mt7530.patch b/target/linux/generic/backport-6.1/790-09-v6.4-net-dsa-mt7530-move-p5_intf_modes-function-to-mt7530.patch similarity index 94% rename from target/linux/generic/backport-6.1/790-v6.4-0007-net-dsa-mt7530-move-p5_intf_modes-function-to-mt7530.patch rename to target/linux/generic/backport-6.1/790-09-v6.4-net-dsa-mt7530-move-p5_intf_modes-function-to-mt7530.patch index cc5ca5167..9cb0b2dd6 100644 --- a/target/linux/generic/backport-6.1/790-v6.4-0007-net-dsa-mt7530-move-p5_intf_modes-function-to-mt7530.patch +++ b/target/linux/generic/backport-6.1/790-09-v6.4-net-dsa-mt7530-move-p5_intf_modes-function-to-mt7530.patch @@ -1,7 +1,7 @@ -From 0d7ae94a0c581f86939bebec0b6ccd66e640d1d8 Mon Sep 17 00:00:00 2001 +From dbef24b66807eef7498740fa8b8441bee64a96c4 Mon Sep 17 00:00:00 2001 From: Daniel Golle Date: Mon, 3 Apr 2023 02:18:28 +0100 -Subject: [PATCH 07/13] net: dsa: mt7530: move p5_intf_modes() function to +Subject: [PATCH 09/48] net: dsa: mt7530: move p5_intf_modes() function to mt7530.c In preparation of splitting mt7530.c into a driver for MDIO-connected diff --git a/target/linux/generic/backport-6.1/790-v6.4-0008-net-dsa-mt7530-introduce-mt7530_probe_common-helper-.patch b/target/linux/generic/backport-6.1/790-10-v6.4-net-dsa-mt7530-introduce-mt7530_probe_common-helper-.patch similarity index 93% rename from target/linux/generic/backport-6.1/790-v6.4-0008-net-dsa-mt7530-introduce-mt7530_probe_common-helper-.patch rename to target/linux/generic/backport-6.1/790-10-v6.4-net-dsa-mt7530-introduce-mt7530_probe_common-helper-.patch index 3dc6f389c..a6af68282 100644 --- a/target/linux/generic/backport-6.1/790-v6.4-0008-net-dsa-mt7530-introduce-mt7530_probe_common-helper-.patch +++ b/target/linux/generic/backport-6.1/790-10-v6.4-net-dsa-mt7530-introduce-mt7530_probe_common-helper-.patch @@ -1,7 +1,7 @@ -From 4d632005c90e253c000d0db73b7cdb9d8dc2e2dd Mon Sep 17 00:00:00 2001 +From a0c6527a38d518ff175c1b6ce248e9b06cc98d3b Mon Sep 17 00:00:00 2001 From: Daniel Golle Date: Mon, 3 Apr 2023 02:18:39 +0100 -Subject: [PATCH 08/13] net: dsa: mt7530: introduce mt7530_probe_common helper +Subject: [PATCH 10/48] net: dsa: mt7530: introduce mt7530_probe_common helper function Move commonly used parts from mt7530_probe into new mt7530_probe_common @@ -17,7 +17,7 @@ Signed-off-by: David S. Miller --- a/drivers/net/dsa/mt7530.c +++ b/drivers/net/dsa/mt7530.c -@@ -3402,44 +3402,21 @@ static const struct of_device_id mt7530_ +@@ -3399,44 +3399,21 @@ static const struct of_device_id mt7530_ MODULE_DEVICE_TABLE(of, mt7530_of_match); static int @@ -67,7 +67,7 @@ Signed-off-by: David S. Miller if (!priv->info) return -EINVAL; -@@ -3453,23 +3430,53 @@ mt7530_probe(struct mdio_device *mdiodev +@@ -3450,23 +3427,53 @@ mt7530_probe(struct mdio_device *mdiodev return -EINVAL; priv->id = priv->info->id; @@ -131,7 +131,7 @@ Signed-off-by: David S. Miller priv->reset = devm_gpiod_get_optional(&mdiodev->dev, "reset", GPIOD_OUT_LOW); if (IS_ERR(priv->reset)) { -@@ -3478,12 +3485,15 @@ mt7530_probe(struct mdio_device *mdiodev +@@ -3475,12 +3482,15 @@ mt7530_probe(struct mdio_device *mdiodev } } diff --git a/target/linux/generic/backport-6.1/790-v6.4-0009-net-dsa-mt7530-introduce-mt7530_remove_common-helper.patch b/target/linux/generic/backport-6.1/790-11-v6.4-net-dsa-mt7530-introduce-mt7530_remove_common-helper.patch similarity index 83% rename from target/linux/generic/backport-6.1/790-v6.4-0009-net-dsa-mt7530-introduce-mt7530_remove_common-helper.patch rename to target/linux/generic/backport-6.1/790-11-v6.4-net-dsa-mt7530-introduce-mt7530_remove_common-helper.patch index 9c9f1ac7f..4192753e8 100644 --- a/target/linux/generic/backport-6.1/790-v6.4-0009-net-dsa-mt7530-introduce-mt7530_remove_common-helper.patch +++ b/target/linux/generic/backport-6.1/790-11-v6.4-net-dsa-mt7530-introduce-mt7530_remove_common-helper.patch @@ -1,7 +1,7 @@ -From 69b838d2629e6b82bcd9e0ab3c1c03f46e5e01d3 Mon Sep 17 00:00:00 2001 +From a7783b0b6f3b38abd34cecf515811691714dee57 Mon Sep 17 00:00:00 2001 From: Daniel Golle Date: Mon, 3 Apr 2023 02:18:50 +0100 -Subject: [PATCH 09/13] net: dsa: mt7530: introduce mt7530_remove_common helper +Subject: [PATCH 11/48] net: dsa: mt7530: introduce mt7530_remove_common helper function Move commonly used parts from mt7530_remove into new @@ -17,7 +17,7 @@ Signed-off-by: David S. Miller --- a/drivers/net/dsa/mt7530.c +++ b/drivers/net/dsa/mt7530.c -@@ -3520,6 +3520,17 @@ mt7530_probe(struct mdio_device *mdiodev +@@ -3517,6 +3517,17 @@ mt7530_probe(struct mdio_device *mdiodev } static void @@ -35,7 +35,7 @@ Signed-off-by: David S. Miller mt7530_remove(struct mdio_device *mdiodev) { struct mt7530_priv *priv = dev_get_drvdata(&mdiodev->dev); -@@ -3538,15 +3549,10 @@ mt7530_remove(struct mdio_device *mdiode +@@ -3535,15 +3546,10 @@ mt7530_remove(struct mdio_device *mdiode dev_err(priv->dev, "Failed to disable io pwr: %d\n", ret); diff --git a/target/linux/generic/backport-6.1/790-v6.4-0010-net-dsa-mt7530-introduce-separate-MDIO-driver.patch b/target/linux/generic/backport-6.1/790-12-v6.4-net-dsa-mt7530-introduce-separate-MDIO-driver.patch similarity index 95% rename from target/linux/generic/backport-6.1/790-v6.4-0010-net-dsa-mt7530-introduce-separate-MDIO-driver.patch rename to target/linux/generic/backport-6.1/790-12-v6.4-net-dsa-mt7530-introduce-separate-MDIO-driver.patch index 82008223e..72a499381 100644 --- a/target/linux/generic/backport-6.1/790-v6.4-0010-net-dsa-mt7530-introduce-separate-MDIO-driver.patch +++ b/target/linux/generic/backport-6.1/790-12-v6.4-net-dsa-mt7530-introduce-separate-MDIO-driver.patch @@ -1,7 +1,7 @@ -From 8eceed6dbd74067dbf4d8e39f14734f4d2f35176 Mon Sep 17 00:00:00 2001 +From 5313432ca1e1a0677ad7b4f17a7e0186473f47aa Mon Sep 17 00:00:00 2001 From: Daniel Golle Date: Mon, 3 Apr 2023 02:19:13 +0100 -Subject: [PATCH 10/13] net: dsa: mt7530: introduce separate MDIO driver +Subject: [PATCH 12/48] net: dsa: mt7530: introduce separate MDIO driver Split MT7530 switch driver into a common part and a part specific for MDIO connected switches and multi-chip modules. @@ -15,12 +15,12 @@ Reviewed-by: Andrew Lunn Signed-off-by: David S. Miller --- MAINTAINERS | 1 + - drivers/net/dsa/Kconfig | 16 +- + drivers/net/dsa/Kconfig | 18 ++- drivers/net/dsa/Makefile | 1 + drivers/net/dsa/mt7530-mdio.c | 271 ++++++++++++++++++++++++++++++++++ drivers/net/dsa/mt7530.c | 264 +-------------------------------- drivers/net/dsa/mt7530.h | 6 + - 6 files changed, 301 insertions(+), 258 deletions(-) + 6 files changed, 302 insertions(+), 259 deletions(-) create mode 100644 drivers/net/dsa/mt7530-mdio.c --- a/MAINTAINERS @@ -35,10 +35,13 @@ Signed-off-by: David S. Miller --- a/drivers/net/dsa/Kconfig +++ b/drivers/net/dsa/Kconfig -@@ -37,10 +37,22 @@ config NET_DSA_MT7530 - tristate "MediaTek MT753x and MT7621 Ethernet switch support" +@@ -34,13 +34,25 @@ config NET_DSA_LANTIQ_GSWIP + the xrx200 / VR9 SoC. + + config NET_DSA_MT7530 +- tristate "MediaTek MT753x and MT7621 Ethernet switch support" ++ tristate "MediaTek MT7530 and MT7531 Ethernet switch support" select NET_DSA_TAG_MTK - select MEDIATEK_GE_PHY + imply NET_DSA_MT7530_MDIO + help + This enables support for the MediaTek MT7530 and MT7531 Ethernet @@ -49,6 +52,7 @@ Signed-off-by: David S. Miller +config NET_DSA_MT7530_MDIO + tristate "MediaTek MT7530 MDIO interface driver" + depends on NET_DSA_MT7530 + select MEDIATEK_GE_PHY select PCS_MTK_LYNXI help - This enables support for the MediaTek MT7530, MT7531, and MT7621 @@ -416,7 +420,7 @@ Signed-off-by: David S. Miller static u32 mt7530_mii_read(struct mt7530_priv *priv, u32 reg) { -@@ -3199,72 +3150,6 @@ static const struct phylink_pcs_ops mt75 +@@ -3196,72 +3147,6 @@ static const struct phylink_pcs_ops mt75 .pcs_an_restart = mt7530_pcs_an_restart, }; @@ -489,7 +493,7 @@ Signed-off-by: David S. Miller static int mt753x_setup(struct dsa_switch *ds) { -@@ -3323,7 +3208,7 @@ static int mt753x_set_mac_eee(struct dsa +@@ -3320,7 +3205,7 @@ static int mt753x_set_mac_eee(struct dsa return 0; } @@ -498,7 +502,7 @@ Signed-off-by: David S. Miller .get_tag_protocol = mtk_get_tag_protocol, .setup = mt753x_setup, .preferred_default_local_cpu_port = mt753x_preferred_default_local_cpu_port, -@@ -3358,8 +3243,9 @@ static const struct dsa_switch_ops mt753 +@@ -3355,8 +3240,9 @@ static const struct dsa_switch_ops mt753 .get_mac_eee = mt753x_get_mac_eee, .set_mac_eee = mt753x_set_mac_eee, }; @@ -509,7 +513,7 @@ Signed-off-by: David S. Miller [ID_MT7621] = { .id = ID_MT7621, .pcs_ops = &mt7530_pcs_ops, -@@ -3392,16 +3278,9 @@ static const struct mt753x_info mt753x_t +@@ -3389,16 +3275,9 @@ static const struct mt753x_info mt753x_t .mac_port_config = mt7531_mac_config, }, }; @@ -528,7 +532,7 @@ Signed-off-by: David S. Miller mt7530_probe_common(struct mt7530_priv *priv) { struct device *dev = priv->dev; -@@ -3438,88 +3317,9 @@ mt7530_probe_common(struct mt7530_priv * +@@ -3435,88 +3314,9 @@ mt7530_probe_common(struct mt7530_priv * return 0; } @@ -619,7 +623,7 @@ Signed-off-by: David S. Miller mt7530_remove_common(struct mt7530_priv *priv) { if (priv->irq) -@@ -3529,55 +3329,7 @@ mt7530_remove_common(struct mt7530_priv +@@ -3526,55 +3326,7 @@ mt7530_remove_common(struct mt7530_priv mutex_destroy(&priv->reg_mutex); } diff --git a/target/linux/generic/backport-6.1/790-v6.4-0011-net-dsa-mt7530-skip-locking-if-MDIO-bus-isn-t-presen.patch b/target/linux/generic/backport-6.1/790-13-v6.4-net-dsa-mt7530-skip-locking-if-MDIO-bus-isn-t-presen.patch similarity index 91% rename from target/linux/generic/backport-6.1/790-v6.4-0011-net-dsa-mt7530-skip-locking-if-MDIO-bus-isn-t-presen.patch rename to target/linux/generic/backport-6.1/790-13-v6.4-net-dsa-mt7530-skip-locking-if-MDIO-bus-isn-t-presen.patch index 01011ed1a..dae7dd4a4 100644 --- a/target/linux/generic/backport-6.1/790-v6.4-0011-net-dsa-mt7530-skip-locking-if-MDIO-bus-isn-t-presen.patch +++ b/target/linux/generic/backport-6.1/790-13-v6.4-net-dsa-mt7530-skip-locking-if-MDIO-bus-isn-t-presen.patch @@ -1,7 +1,7 @@ -From a52cadbf76593f8fcb2f4f62cb006e3f2a22ad06 Mon Sep 17 00:00:00 2001 +From 86e1168a214b7ab0883acf1e7a6885a7a949e3e7 Mon Sep 17 00:00:00 2001 From: Daniel Golle Date: Mon, 3 Apr 2023 02:19:28 +0100 -Subject: [PATCH 11/13] net: dsa: mt7530: skip locking if MDIO bus isn't +Subject: [PATCH 13/48] net: dsa: mt7530: skip locking if MDIO bus isn't present As MT7530 and MT7531 internally use 32-bit wide registers, each access diff --git a/target/linux/generic/backport-6.1/790-v6.4-0012-net-dsa-mt7530-introduce-driver-for-MT7988-built-in-.patch b/target/linux/generic/backport-6.1/790-14-v6.4-net-dsa-mt7530-introduce-driver-for-MT7988-built-in-.patch similarity index 97% rename from target/linux/generic/backport-6.1/790-v6.4-0012-net-dsa-mt7530-introduce-driver-for-MT7988-built-in-.patch rename to target/linux/generic/backport-6.1/790-14-v6.4-net-dsa-mt7530-introduce-driver-for-MT7988-built-in-.patch index b158a8946..f5573fc6c 100644 --- a/target/linux/generic/backport-6.1/790-v6.4-0012-net-dsa-mt7530-introduce-driver-for-MT7988-built-in-.patch +++ b/target/linux/generic/backport-6.1/790-14-v6.4-net-dsa-mt7530-introduce-driver-for-MT7988-built-in-.patch @@ -1,7 +1,7 @@ -From b361015763fedea439f13b336b15ef7bdf1f7d4f Mon Sep 17 00:00:00 2001 +From a1b87b6322db9186c8689710fe3e98f59e540949 Mon Sep 17 00:00:00 2001 From: Daniel Golle Date: Mon, 3 Apr 2023 02:19:40 +0100 -Subject: [PATCH 12/13] net: dsa: mt7530: introduce driver for MT7988 built-in +Subject: [PATCH 14/48] net: dsa: mt7530: introduce driver for MT7988 built-in switch Add driver for the built-in Gigabit Ethernet switch which can be found @@ -42,9 +42,9 @@ Signed-off-by: David S. Miller --- a/drivers/net/dsa/Kconfig +++ b/drivers/net/dsa/Kconfig -@@ -38,6 +38,7 @@ config NET_DSA_MT7530 +@@ -37,6 +37,7 @@ config NET_DSA_MT7530 + tristate "MediaTek MT7530 and MT7531 Ethernet switch support" select NET_DSA_TAG_MTK - select MEDIATEK_GE_PHY imply NET_DSA_MT7530_MDIO + imply NET_DSA_MT7530_MMIO help @@ -336,7 +336,7 @@ Signed-off-by: David S. Miller static void mt753x_phylink_get_caps(struct dsa_switch *ds, int port, struct phylink_config *config) { -@@ -3210,6 +3305,27 @@ static int mt753x_set_mac_eee(struct dsa +@@ -3207,6 +3302,27 @@ static int mt753x_set_mac_eee(struct dsa return 0; } @@ -364,7 +364,7 @@ Signed-off-by: David S. Miller const struct dsa_switch_ops mt7530_switch_ops = { .get_tag_protocol = mtk_get_tag_protocol, .setup = mt753x_setup, -@@ -3279,6 +3395,17 @@ const struct mt753x_info mt753x_table[] +@@ -3276,6 +3392,17 @@ const struct mt753x_info mt753x_table[] .mac_port_get_caps = mt7531_mac_port_get_caps, .mac_port_config = mt7531_mac_config, }, diff --git a/target/linux/generic/backport-6.1/790-v6.4-0013-net-dsa-mt7530-fix-support-for-MT7531BE.patch b/target/linux/generic/backport-6.1/790-15-v6.4-net-dsa-mt7530-fix-support-for-MT7531BE.patch similarity index 95% rename from target/linux/generic/backport-6.1/790-v6.4-0013-net-dsa-mt7530-fix-support-for-MT7531BE.patch rename to target/linux/generic/backport-6.1/790-15-v6.4-net-dsa-mt7530-fix-support-for-MT7531BE.patch index 35f8cf939..40209b030 100644 --- a/target/linux/generic/backport-6.1/790-v6.4-0013-net-dsa-mt7530-fix-support-for-MT7531BE.patch +++ b/target/linux/generic/backport-6.1/790-15-v6.4-net-dsa-mt7530-fix-support-for-MT7531BE.patch @@ -1,7 +1,7 @@ -From eb1dd407b4be7ca38166a38c56c8edf52c6a399f Mon Sep 17 00:00:00 2001 +From ed01748319b25456c5226ed0cb5e49e970da0e4f Mon Sep 17 00:00:00 2001 From: Daniel Golle Date: Sun, 16 Apr 2023 13:08:14 +0100 -Subject: [PATCH 13/13] net: dsa: mt7530: fix support for MT7531BE +Subject: [PATCH 15/48] net: dsa: mt7530: fix support for MT7531BE MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit @@ -73,7 +73,7 @@ Signed-off-by: Jakub Kicinski } --- a/drivers/net/dsa/mt7530.c +++ b/drivers/net/dsa/mt7530.c -@@ -3272,6 +3272,12 @@ mt753x_setup(struct dsa_switch *ds) +@@ -3269,6 +3269,12 @@ mt753x_setup(struct dsa_switch *ds) if (ret && priv->irq) mt7530_free_irq_common(priv); diff --git a/target/linux/generic/backport-6.1/790-17-v6.5-net-dsa-mt7530-update-PCS-driver-to-use-neg_mode.patch b/target/linux/generic/backport-6.1/790-17-v6.5-net-dsa-mt7530-update-PCS-driver-to-use-neg_mode.patch new file mode 100644 index 000000000..78e332b1c --- /dev/null +++ b/target/linux/generic/backport-6.1/790-17-v6.5-net-dsa-mt7530-update-PCS-driver-to-use-neg_mode.patch @@ -0,0 +1,35 @@ +From fb6858e2c3b931433ea4d25871c272ee4c01bd99 Mon Sep 17 00:00:00 2001 +From: "Russell King (Oracle)" +Date: Fri, 16 Jun 2023 13:07:29 +0100 +Subject: [PATCH 17/48] net: dsa: mt7530: update PCS driver to use neg_mode + +Update mt7530's embedded PCS driver to use neg_mode, even though it +makes no use of it or the "mode" argument. This makes the driver +consistent with converted drivers. + +Signed-off-by: Russell King (Oracle) +Link: https://lore.kernel.org/r/E1qA8Ej-00EaGR-Fk@rmk-PC.armlinux.org.uk +Signed-off-by: Jakub Kicinski +--- + drivers/net/dsa/mt7530.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +--- a/drivers/net/dsa/mt7530.c ++++ b/drivers/net/dsa/mt7530.c +@@ -3225,7 +3225,7 @@ static void mt7530_pcs_get_state(struct + state->pause |= MLO_PAUSE_TX; + } + +-static int mt753x_pcs_config(struct phylink_pcs *pcs, unsigned int mode, ++static int mt753x_pcs_config(struct phylink_pcs *pcs, unsigned int neg_mode, + phy_interface_t interface, + const unsigned long *advertising, + bool permit_pause_to_mac) +@@ -3253,6 +3253,7 @@ mt753x_setup(struct dsa_switch *ds) + /* Initialise the PCS devices */ + for (i = 0; i < priv->ds->num_ports; i++) { + priv->pcs[i].pcs.ops = priv->info->pcs_ops; ++ priv->pcs[i].pcs.neg_mode = true; + priv->pcs[i].priv = priv; + priv->pcs[i].port = i; + } diff --git a/target/linux/generic/backport-6.1/790-18-v6.7-net-dsa-mt7530-Convert-to-platform-remove-callback-r.patch b/target/linux/generic/backport-6.1/790-18-v6.7-net-dsa-mt7530-Convert-to-platform-remove-callback-r.patch new file mode 100644 index 000000000..ffcf51dab --- /dev/null +++ b/target/linux/generic/backport-6.1/790-18-v6.7-net-dsa-mt7530-Convert-to-platform-remove-callback-r.patch @@ -0,0 +1,58 @@ +From 03ede98ecc29b59fb364f735d6de0e6a4c1735fc Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= +Date: Mon, 18 Sep 2023 21:19:12 +0200 +Subject: [PATCH 18/48] net: dsa: mt7530: Convert to platform remove callback + returning void +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +The .remove() callback for a platform driver returns an int which makes +many driver authors wrongly assume it's possible to do error handling by +returning an error code. However the value returned is ignored (apart +from emitting a warning) and this typically results in resource leaks. +To improve here there is a quest to make the remove callback return +void. In the first step of this quest all drivers are converted to +.remove_new() which already returns void. Eventually after all drivers +are converted, .remove_new() is renamed to .remove(). + +Trivially convert this driver from always returning zero in the remove +callback to the void returning variant. + +Signed-off-by: Uwe Kleine-König +Reviewed-by: Andrew Lunn +Reviewed-by: Florian Fainelli +Acked-by: Daniel Golle +Signed-off-by: David S. Miller +--- + drivers/net/dsa/mt7530-mmio.c | 7 ++----- + 1 file changed, 2 insertions(+), 5 deletions(-) + +--- a/drivers/net/dsa/mt7530-mmio.c ++++ b/drivers/net/dsa/mt7530-mmio.c +@@ -62,15 +62,12 @@ mt7988_probe(struct platform_device *pde + return dsa_register_switch(priv->ds); + } + +-static int +-mt7988_remove(struct platform_device *pdev) ++static void mt7988_remove(struct platform_device *pdev) + { + struct mt7530_priv *priv = platform_get_drvdata(pdev); + + if (priv) + mt7530_remove_common(priv); +- +- return 0; + } + + static void mt7988_shutdown(struct platform_device *pdev) +@@ -87,7 +84,7 @@ static void mt7988_shutdown(struct platf + + static struct platform_driver mt7988_platform_driver = { + .probe = mt7988_probe, +- .remove = mt7988_remove, ++ .remove_new = mt7988_remove, + .shutdown = mt7988_shutdown, + .driver = { + .name = "mt7530-mmio", diff --git a/target/linux/generic/backport-6.1/790-19-v6.7-net-dsa-mt753x-remove-mt753x_phylink_pcs_link_up.patch b/target/linux/generic/backport-6.1/790-19-v6.7-net-dsa-mt753x-remove-mt753x_phylink_pcs_link_up.patch new file mode 100644 index 000000000..d69ee7f10 --- /dev/null +++ b/target/linux/generic/backport-6.1/790-19-v6.7-net-dsa-mt753x-remove-mt753x_phylink_pcs_link_up.patch @@ -0,0 +1,51 @@ +From 1a1a723d47c046d6c251651c9ade589040dafacf Mon Sep 17 00:00:00 2001 +From: "Russell King (Oracle)" +Date: Wed, 27 Sep 2023 13:13:56 +0100 +Subject: [PATCH 19/48] net: dsa: mt753x: remove mt753x_phylink_pcs_link_up() + +Remove the mt753x_phylink_pcs_link_up() function for two reasons: + +1) priv->pcs[i].pcs.neg_mode is set true, meaning it doesn't take a + MLO_AN_FIXED anymore, but one of PHYLINK_PCS_NEG_*. However, this + is inconsequential due to... +2) priv->pcs[port].pcs.ops is always initialised to point at + mt7530_pcs_ops, which does not have a pcs_link_up() member. + +So, let's remove mt753x_phylink_pcs_link_up() entirely. + +Signed-off-by: Russell King (Oracle) +Reviewed-by: Vladimir Oltean +Reviewed-by: Florian Fainelli +Link: https://lore.kernel.org/r/E1qlTQS-008BWe-Va@rmk-PC.armlinux.org.uk +Signed-off-by: Jakub Kicinski +--- + drivers/net/dsa/mt7530.c | 11 ----------- + 1 file changed, 11 deletions(-) + +--- a/drivers/net/dsa/mt7530.c ++++ b/drivers/net/dsa/mt7530.c +@@ -3040,15 +3040,6 @@ static void mt753x_phylink_mac_link_down + mt7530_clear(priv, MT7530_PMCR_P(port), PMCR_LINK_SETTINGS_MASK); + } + +-static void mt753x_phylink_pcs_link_up(struct phylink_pcs *pcs, +- unsigned int mode, +- phy_interface_t interface, +- int speed, int duplex) +-{ +- if (pcs->ops->pcs_link_up) +- pcs->ops->pcs_link_up(pcs, mode, interface, speed, duplex); +-} +- + static void mt753x_phylink_mac_link_up(struct dsa_switch *ds, int port, + unsigned int mode, + phy_interface_t interface, +@@ -3137,8 +3128,6 @@ mt7531_cpu_port_config(struct dsa_switch + return ret; + mt7530_write(priv, MT7530_PMCR_P(port), + PMCR_CPU_PORT_SETTING(priv->id)); +- mt753x_phylink_pcs_link_up(&priv->pcs[port].pcs, MLO_AN_FIXED, +- interface, speed, DUPLEX_FULL); + mt753x_phylink_mac_link_up(ds, port, MLO_AN_FIXED, interface, NULL, + speed, DUPLEX_FULL, true, true); + diff --git a/target/linux/generic/backport-6.1/790-20-v6.7-net-dsa-mt7530-replace-deprecated-strncpy-with-ethto.patch b/target/linux/generic/backport-6.1/790-20-v6.7-net-dsa-mt7530-replace-deprecated-strncpy-with-ethto.patch new file mode 100644 index 000000000..8af682027 --- /dev/null +++ b/target/linux/generic/backport-6.1/790-20-v6.7-net-dsa-mt7530-replace-deprecated-strncpy-with-ethto.patch @@ -0,0 +1,40 @@ +From 51e9c79a4d40fa3a64c0fbdab1ebbebf7cce2dba Mon Sep 17 00:00:00 2001 +From: Justin Stitt +Date: Mon, 9 Oct 2023 18:29:19 +0000 +Subject: [PATCH 20/48] net: dsa: mt7530: replace deprecated strncpy with + ethtool_sprintf + +`strncpy` is deprecated for use on NUL-terminated destination strings +[1] and as such we should prefer more robust and less ambiguous string +interfaces. + +ethtool_sprintf() is designed specifically for get_strings() usage. +Let's replace strncpy in favor of this more robust and easier to +understand interface. + +Link: https://www.kernel.org/doc/html/latest/process/deprecated.html#strncpy-on-nul-terminated-strings [1] +Link: https://manpages.debian.org/testing/linux-manual-4.8/strscpy.9.en.html [2] +Link: https://github.com/KSPP/linux/issues/90 +Signed-off-by: Justin Stitt +Reviewed-by: Kees Cook +Acked-by: Daniel Golle +Reviewed-by: Florian Fainelli +Reviewed-by: AngeloGioacchino Del Regno +Link: https://lore.kernel.org/r/20231009-strncpy-drivers-net-dsa-mt7530-c-v1-1-ec6677a6436a@google.com +Signed-off-by: Jakub Kicinski +--- + drivers/net/dsa/mt7530.c | 3 +-- + 1 file changed, 1 insertion(+), 2 deletions(-) + +--- a/drivers/net/dsa/mt7530.c ++++ b/drivers/net/dsa/mt7530.c +@@ -843,8 +843,7 @@ mt7530_get_strings(struct dsa_switch *ds + return; + + for (i = 0; i < ARRAY_SIZE(mt7530_mib); i++) +- strncpy(data + i * ETH_GSTRING_LEN, mt7530_mib[i].name, +- ETH_GSTRING_LEN); ++ ethtool_sprintf(&data, "%s", mt7530_mib[i].name); + } + + static void diff --git a/target/linux/generic/backport-6.1/790-21-v6.9-net-dsa-mt7530-support-OF-based-registration-of-swit.patch b/target/linux/generic/backport-6.1/790-21-v6.9-net-dsa-mt7530-support-OF-based-registration-of-swit.patch new file mode 100644 index 000000000..4c6c05773 --- /dev/null +++ b/target/linux/generic/backport-6.1/790-21-v6.9-net-dsa-mt7530-support-OF-based-registration-of-swit.patch @@ -0,0 +1,116 @@ +From 38be6fdf7e93431e91aac3884837b22236325f68 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Ar=C4=B1n=C3=A7=20=C3=9CNAL?= +Date: Mon, 22 Jan 2024 08:34:31 +0300 +Subject: [PATCH 21/48] net: dsa: mt7530: support OF-based registration of + switch MDIO bus +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Currently the MDIO bus of the switches the MT7530 DSA subdriver controls +can only be registered as non-OF-based. Bring support for registering the +bus OF-based. + +The subdrivers that control switches [with MDIO bus] probed on OF must +follow this logic to support all cases properly: + +No switch MDIO bus defined: Populate ds->user_mii_bus, register the MDIO +bus, set the interrupts for PHYs if "interrupt-controller" is defined at +the switch node. This case should only be covered for the switches which +their dt-bindings documentation didn't document the MDIO bus from the +start. This is to keep supporting the device trees that do not describe the +MDIO bus on the device tree but the MDIO bus is being used nonetheless. + +Switch MDIO bus defined: Don't populate ds->user_mii_bus, register the MDIO +bus, set the interrupts for PHYs if ["interrupt-controller" is defined at +the switch node and "interrupts" is defined at the PHY nodes under the +switch MDIO bus node]. + +Switch MDIO bus defined but explicitly disabled: If the device tree says +status = "disabled" for the MDIO bus, we shouldn't need an MDIO bus at all. +Instead, just exit as early as possible and do not call any MDIO API. + +The use of ds->user_mii_bus is inappropriate when the MDIO bus of the +switch is described on the device tree [1], which is why we don't populate +ds->user_mii_bus in that case. + +Link: https://lore.kernel.org/netdev/20231213120656.x46fyad6ls7sqyzv@skbuf/ [1] +Suggested-by: David Bauer +Signed-off-by: Arınç ÃœNAL +Reviewed-by: Vladimir Oltean +Link: https://lore.kernel.org/r/20240122053431.7751-1-arinc.unal@arinc9.com +Signed-off-by: Paolo Abeni +--- + drivers/net/dsa/mt7530.c | 34 ++++++++++++++++++++++++++-------- + 1 file changed, 26 insertions(+), 8 deletions(-) + +--- a/drivers/net/dsa/mt7530.c ++++ b/drivers/net/dsa/mt7530.c +@@ -2350,24 +2350,40 @@ mt7530_free_irq_common(struct mt7530_pri + static void + mt7530_free_irq(struct mt7530_priv *priv) + { +- mt7530_free_mdio_irq(priv); ++ struct device_node *mnp, *np = priv->dev->of_node; ++ ++ mnp = of_get_child_by_name(np, "mdio"); ++ if (!mnp) ++ mt7530_free_mdio_irq(priv); ++ of_node_put(mnp); ++ + mt7530_free_irq_common(priv); + } + + static int + mt7530_setup_mdio(struct mt7530_priv *priv) + { ++ struct device_node *mnp, *np = priv->dev->of_node; + struct dsa_switch *ds = priv->ds; + struct device *dev = priv->dev; + struct mii_bus *bus; + static int idx; +- int ret; ++ int ret = 0; ++ ++ mnp = of_get_child_by_name(np, "mdio"); ++ ++ if (mnp && !of_device_is_available(mnp)) ++ goto out; + + bus = devm_mdiobus_alloc(dev); +- if (!bus) +- return -ENOMEM; ++ if (!bus) { ++ ret = -ENOMEM; ++ goto out; ++ } ++ ++ if (!mnp) ++ ds->slave_mii_bus = bus; + +- ds->slave_mii_bus = bus; + bus->priv = priv; + bus->name = KBUILD_MODNAME "-mii"; + snprintf(bus->id, MII_BUS_ID_SIZE, KBUILD_MODNAME "-%d", idx++); +@@ -2376,16 +2392,18 @@ mt7530_setup_mdio(struct mt7530_priv *pr + bus->parent = dev; + bus->phy_mask = ~ds->phys_mii_mask; + +- if (priv->irq) ++ if (priv->irq && !mnp) + mt7530_setup_mdio_irq(priv); + +- ret = devm_mdiobus_register(dev, bus); ++ ret = devm_of_mdiobus_register(dev, bus, mnp); + if (ret) { + dev_err(dev, "failed to register MDIO bus: %d\n", ret); +- if (priv->irq) ++ if (priv->irq && !mnp) + mt7530_free_mdio_irq(priv); + } + ++out: ++ of_node_put(mnp); + return ret; + } + diff --git a/target/linux/generic/backport-6.1/790-22-v6.8-net-dsa-mt7530-fix-10M-100M-speed-on-MT7988-switch.patch b/target/linux/generic/backport-6.1/790-22-v6.8-net-dsa-mt7530-fix-10M-100M-speed-on-MT7988-switch.patch new file mode 100644 index 000000000..0b141b4a3 --- /dev/null +++ b/target/linux/generic/backport-6.1/790-22-v6.8-net-dsa-mt7530-fix-10M-100M-speed-on-MT7988-switch.patch @@ -0,0 +1,34 @@ +From 022a254fafce88367914dfc8168fe687fc528cdb Mon Sep 17 00:00:00 2001 +From: Daniel Golle +Date: Wed, 24 Jan 2024 05:17:25 +0000 +Subject: [PATCH 22/48] net: dsa: mt7530: fix 10M/100M speed on MT7988 switch +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Setup PMCR port register for actual speed and duplex on internally +connected PHYs of the MT7988 built-in switch. This fixes links with +speeds other than 1000M. + +Fixes: 110c18bfed41 ("net: dsa: mt7530: introduce driver for MT7988 built-in switch") +Signed-off-by: Daniel Golle +Reviewed-by: Vladimir Oltean +Acked-by: Arınç ÃœNAL +Link: https://lore.kernel.org/r/a5b04dfa8256d8302f402545a51ac4c626fdba25.1706071272.git.daniel@makrotopia.org +Signed-off-by: Jakub Kicinski +--- + drivers/net/dsa/mt7530.c | 3 +-- + 1 file changed, 1 insertion(+), 2 deletions(-) + +--- a/drivers/net/dsa/mt7530.c ++++ b/drivers/net/dsa/mt7530.c +@@ -3072,8 +3072,7 @@ static void mt753x_phylink_mac_link_up(s + /* MT753x MAC works in 1G full duplex mode for all up-clocked + * variants. + */ +- if (interface == PHY_INTERFACE_MODE_INTERNAL || +- interface == PHY_INTERFACE_MODE_TRGMII || ++ if (interface == PHY_INTERFACE_MODE_TRGMII || + (phy_interface_mode_is_8023z(interface))) { + speed = SPEED_1000; + duplex = DUPLEX_FULL; diff --git a/target/linux/generic/backport-6.1/790-23-v6.9-net-dsa-mt7530-always-trap-frames-to-active-CPU-port.patch b/target/linux/generic/backport-6.1/790-23-v6.9-net-dsa-mt7530-always-trap-frames-to-active-CPU-port.patch new file mode 100644 index 000000000..44d8e07c1 --- /dev/null +++ b/target/linux/generic/backport-6.1/790-23-v6.9-net-dsa-mt7530-always-trap-frames-to-active-CPU-port.patch @@ -0,0 +1,125 @@ +From a385398f77fad9eabe7cdc253e1a356484acc316 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Ar=C4=B1n=C3=A7=20=C3=9CNAL?= +Date: Mon, 22 Jan 2024 08:35:52 +0300 +Subject: [PATCH 23/48] net: dsa: mt7530: always trap frames to active CPU port + on MT7530 +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +On the MT7530 switch, the CPU_PORT field indicates which CPU port to trap +frames to, regardless of the affinity of the inbound user port. + +When multiple CPU ports are in use, if the DSA conduit interface is down, +trapped frames won't be passed to the conduit interface. + +To make trapping frames work including this case, implement +ds->ops->conduit_state_change() on this subdriver and set the CPU_PORT +field to the numerically smallest CPU port whose conduit interface is up. +Introduce the active_cpu_ports field to store the information of the active +CPU ports. Correct the macros, CPU_PORT is bits 4 through 6 of the +register. + +Add a comment to explain frame trapping for this switch. + +Currently, the driver doesn't support the use of multiple CPU ports so this +is not necessarily a bug fix. + +Suggested-by: Vladimir Oltean +Suggested-by: Russell King (Oracle) +Signed-off-by: Arınç ÃœNAL +Reviewed-by: Vladimir Oltean +Link: https://lore.kernel.org/r/20240122-for-netnext-mt7530-improvements-1-v3-1-042401f2b279@arinc9.com +Signed-off-by: Jakub Kicinski +--- + drivers/net/dsa/mt7530.c | 35 +++++++++++++++++++++++++++++++---- + drivers/net/dsa/mt7530.h | 6 ++++-- + 2 files changed, 35 insertions(+), 6 deletions(-) + +--- a/drivers/net/dsa/mt7530.c ++++ b/drivers/net/dsa/mt7530.c +@@ -1239,10 +1239,6 @@ mt753x_cpu_port_enable(struct dsa_switch + mt7530_set(priv, MT7530_MFC, BC_FFP(BIT(port)) | UNM_FFP(BIT(port)) | + UNU_FFP(BIT(port))); + +- /* Set CPU port number */ +- if (priv->id == ID_MT7530 || priv->id == ID_MT7621) +- mt7530_rmw(priv, MT7530_MFC, CPU_MASK, CPU_EN | CPU_PORT(port)); +- + /* Add the CPU port to the CPU port bitmap for MT7531. Trapped frames + * will be forwarded to the CPU port that is affine to the inbound user + * port. +@@ -3314,6 +3310,36 @@ static int mt753x_set_mac_eee(struct dsa + return 0; + } + ++static void ++mt753x_conduit_state_change(struct dsa_switch *ds, ++ const struct net_device *conduit, ++ bool operational) ++{ ++ struct dsa_port *cpu_dp = conduit->dsa_ptr; ++ struct mt7530_priv *priv = ds->priv; ++ int val = 0; ++ u8 mask; ++ ++ /* Set the CPU port to trap frames to for MT7530. Trapped frames will be ++ * forwarded to the numerically smallest CPU port whose conduit ++ * interface is up. ++ */ ++ if (priv->id != ID_MT7530 && priv->id != ID_MT7621) ++ return; ++ ++ mask = BIT(cpu_dp->index); ++ ++ if (operational) ++ priv->active_cpu_ports |= mask; ++ else ++ priv->active_cpu_ports &= ~mask; ++ ++ if (priv->active_cpu_ports) ++ val = CPU_EN | CPU_PORT(__ffs(priv->active_cpu_ports)); ++ ++ mt7530_rmw(priv, MT7530_MFC, CPU_EN | CPU_PORT_MASK, val); ++} ++ + static int mt7988_pad_setup(struct dsa_switch *ds, phy_interface_t interface) + { + return 0; +@@ -3369,6 +3395,7 @@ const struct dsa_switch_ops mt7530_switc + .phylink_mac_link_up = mt753x_phylink_mac_link_up, + .get_mac_eee = mt753x_get_mac_eee, + .set_mac_eee = mt753x_set_mac_eee, ++ .master_state_change = mt753x_conduit_state_change, + }; + EXPORT_SYMBOL_GPL(mt7530_switch_ops); + +--- a/drivers/net/dsa/mt7530.h ++++ b/drivers/net/dsa/mt7530.h +@@ -45,8 +45,8 @@ enum mt753x_id { + #define UNU_FFP(x) (((x) & 0xff) << 8) + #define UNU_FFP_MASK UNU_FFP(~0) + #define CPU_EN BIT(7) +-#define CPU_PORT(x) ((x) << 4) +-#define CPU_MASK (0xf << 4) ++#define CPU_PORT_MASK GENMASK(6, 4) ++#define CPU_PORT(x) FIELD_PREP(CPU_PORT_MASK, x) + #define MIRROR_EN BIT(3) + #define MIRROR_PORT(x) ((x) & 0x7) + #define MIRROR_MASK 0x7 +@@ -783,6 +783,7 @@ struct mt753x_info { + * @irq_domain: IRQ domain of the switch irq_chip + * @irq_enable: IRQ enable bits, synced to SYS_INT_EN + * @create_sgmii: Pointer to function creating SGMII PCS instance(s) ++ * @active_cpu_ports: Holding the active CPU ports + */ + struct mt7530_priv { + struct device *dev; +@@ -809,6 +810,7 @@ struct mt7530_priv { + struct irq_domain *irq_domain; + u32 irq_enable; + int (*create_sgmii)(struct mt7530_priv *priv, bool dual_sgmii); ++ u8 active_cpu_ports; + }; + + struct mt7530_hw_vlan_entry { diff --git a/target/linux/generic/backport-6.1/790-24-v6.9-net-dsa-mt7530-use-p5_interface_select-as-data-type-.patch b/target/linux/generic/backport-6.1/790-24-v6.9-net-dsa-mt7530-use-p5_interface_select-as-data-type-.patch new file mode 100644 index 000000000..3454948b8 --- /dev/null +++ b/target/linux/generic/backport-6.1/790-24-v6.9-net-dsa-mt7530-use-p5_interface_select-as-data-type-.patch @@ -0,0 +1,45 @@ +From e3c8f69af69e6c4022094309445c009faf5e8cef Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Ar=C4=B1n=C3=A7=20=C3=9CNAL?= +Date: Mon, 22 Jan 2024 08:35:53 +0300 +Subject: [PATCH 24/48] net: dsa: mt7530: use p5_interface_select as data type + for p5_intf_sel +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Use the p5_interface_select enumeration as the data type for the +p5_intf_sel field. This ensures p5_intf_sel can only take the values +defined in the p5_interface_select enumeration. + +Remove the explicit assignment of 0 to P5_DISABLED as the first enum item +is automatically assigned 0. + +Signed-off-by: Arınç ÃœNAL +Acked-by: Daniel Golle +Reviewed-by: Vladimir Oltean +Link: https://lore.kernel.org/r/20240122-for-netnext-mt7530-improvements-1-v3-2-042401f2b279@arinc9.com +Signed-off-by: Jakub Kicinski +--- + drivers/net/dsa/mt7530.h | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +--- a/drivers/net/dsa/mt7530.h ++++ b/drivers/net/dsa/mt7530.h +@@ -713,7 +713,7 @@ struct mt7530_port { + + /* Port 5 interface select definitions */ + enum p5_interface_select { +- P5_DISABLED = 0, ++ P5_DISABLED, + P5_INTF_SEL_PHY_P0, + P5_INTF_SEL_PHY_P4, + P5_INTF_SEL_GMAC5, +@@ -799,7 +799,7 @@ struct mt7530_priv { + bool mcm; + phy_interface_t p6_interface; + phy_interface_t p5_interface; +- unsigned int p5_intf_sel; ++ enum p5_interface_select p5_intf_sel; + u8 mirror_rx; + u8 mirror_tx; + struct mt7530_port ports[MT7530_NUM_PORTS]; diff --git a/target/linux/generic/backport-6.1/790-25-v6.9-net-dsa-mt7530-store-port-5-SGMII-capability-of-MT75.patch b/target/linux/generic/backport-6.1/790-25-v6.9-net-dsa-mt7530-store-port-5-SGMII-capability-of-MT75.patch new file mode 100644 index 000000000..357579e2b --- /dev/null +++ b/target/linux/generic/backport-6.1/790-25-v6.9-net-dsa-mt7530-store-port-5-SGMII-capability-of-MT75.patch @@ -0,0 +1,227 @@ +From b0d590a5cdd95ed863717b279751d6166083889f Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Ar=C4=B1n=C3=A7=20=C3=9CNAL?= +Date: Mon, 22 Jan 2024 08:35:54 +0300 +Subject: [PATCH 25/48] net: dsa: mt7530: store port 5 SGMII capability of + MT7531 +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Introduce the p5_sgmii field to store the information for whether port 5 +has got SGMII or not. Instead of reading the MT7531_TOP_SIG_SR register +multiple times, the register will be read once and the value will be +stored on the p5_sgmii field. This saves unnecessary reads of the +register. + +Move the comment about MT7531AE and MT7531BE to mt7531_setup(), where the +switch is identified. + +Get rid of mt7531_dual_sgmii_supported() now that priv->p5_sgmii stores the +information. Address the code where mt7531_dual_sgmii_supported() is used. + +Get rid of mt7531_is_rgmii_port() which just prints the opposite of +priv->p5_sgmii. + +Instead of calling mt7531_pll_setup() then returning, do not call it if +port 5 is SGMII. + +Remove P5_INTF_SEL_GMAC5_SGMII. The p5_interface_select enum is supposed to +represent the mode that port 5 is being used in, not the hardware +information of port 5. Set p5_intf_sel to P5_INTF_SEL_GMAC5 instead, if +port 5 is not dsa_is_unused_port(). + +Signed-off-by: Arınç ÃœNAL +Acked-by: Daniel Golle +Reviewed-by: Vladimir Oltean +Link: https://lore.kernel.org/r/20240122-for-netnext-mt7530-improvements-1-v3-3-042401f2b279@arinc9.com +Signed-off-by: Jakub Kicinski +--- + drivers/net/dsa/mt7530-mdio.c | 7 ++--- + drivers/net/dsa/mt7530.c | 48 ++++++++++++----------------------- + drivers/net/dsa/mt7530.h | 6 +++-- + 3 files changed, 22 insertions(+), 39 deletions(-) + +--- a/drivers/net/dsa/mt7530-mdio.c ++++ b/drivers/net/dsa/mt7530-mdio.c +@@ -81,17 +81,14 @@ static const struct regmap_bus mt7530_re + }; + + static int +-mt7531_create_sgmii(struct mt7530_priv *priv, bool dual_sgmii) ++mt7531_create_sgmii(struct mt7530_priv *priv) + { + struct regmap_config *mt7531_pcs_config[2] = {}; + struct phylink_pcs *pcs; + struct regmap *regmap; + int i, ret = 0; + +- /* MT7531AE has two SGMII units for port 5 and port 6 +- * MT7531BE has only one SGMII unit for port 6 +- */ +- for (i = dual_sgmii ? 0 : 1; i < 2; i++) { ++ for (i = priv->p5_sgmii ? 0 : 1; i < 2; i++) { + mt7531_pcs_config[i] = devm_kzalloc(priv->dev, + sizeof(struct regmap_config), + GFP_KERNEL); +--- a/drivers/net/dsa/mt7530.c ++++ b/drivers/net/dsa/mt7530.c +@@ -487,15 +487,6 @@ mt7530_pad_clk_setup(struct dsa_switch * + return 0; + } + +-static bool mt7531_dual_sgmii_supported(struct mt7530_priv *priv) +-{ +- u32 val; +- +- val = mt7530_read(priv, MT7531_TOP_SIG_SR); +- +- return (val & PAD_DUAL_SGMII_EN) != 0; +-} +- + static int + mt7531_pad_setup(struct dsa_switch *ds, phy_interface_t interface) + { +@@ -510,9 +501,6 @@ mt7531_pll_setup(struct mt7530_priv *pri + u32 xtal; + u32 val; + +- if (mt7531_dual_sgmii_supported(priv)) +- return; +- + val = mt7530_read(priv, MT7531_CREV); + top_sig = mt7530_read(priv, MT7531_TOP_SIG_SR); + hwstrap = mt7530_read(priv, MT7531_HWTRAP); +@@ -927,8 +915,6 @@ static const char *p5_intf_modes(unsigne + return "PHY P4"; + case P5_INTF_SEL_GMAC5: + return "GMAC5"; +- case P5_INTF_SEL_GMAC5_SGMII: +- return "GMAC5_SGMII"; + default: + return "unknown"; + } +@@ -2697,6 +2683,12 @@ mt7531_setup(struct dsa_switch *ds) + return -ENODEV; + } + ++ /* MT7531AE has got two SGMII units. One for port 5, one for port 6. ++ * MT7531BE has got only one SGMII unit which is for port 6. ++ */ ++ val = mt7530_read(priv, MT7531_TOP_SIG_SR); ++ priv->p5_sgmii = !!(val & PAD_DUAL_SGMII_EN); ++ + /* all MACs must be forced link-down before sw reset */ + for (i = 0; i < MT7530_NUM_PORTS; i++) + mt7530_write(priv, MT7530_PMCR_P(i), MT7531_FORCE_LNK); +@@ -2706,21 +2698,18 @@ mt7531_setup(struct dsa_switch *ds) + SYS_CTRL_PHY_RST | SYS_CTRL_SW_RST | + SYS_CTRL_REG_RST); + +- mt7531_pll_setup(priv); +- +- if (mt7531_dual_sgmii_supported(priv)) { +- priv->p5_intf_sel = P5_INTF_SEL_GMAC5_SGMII; +- ++ if (!priv->p5_sgmii) { ++ mt7531_pll_setup(priv); ++ } else { + /* Let ds->slave_mii_bus be able to access external phy. */ + mt7530_rmw(priv, MT7531_GPIO_MODE1, MT7531_GPIO11_RG_RXD2_MASK, + MT7531_EXT_P_MDC_11); + mt7530_rmw(priv, MT7531_GPIO_MODE1, MT7531_GPIO12_RG_RXD3_MASK, + MT7531_EXT_P_MDIO_12); +- } else { +- priv->p5_intf_sel = P5_INTF_SEL_GMAC5; + } +- dev_dbg(ds->dev, "P5 support %s interface\n", +- p5_intf_modes(priv->p5_intf_sel)); ++ ++ if (!dsa_is_unused_port(ds, 5)) ++ priv->p5_intf_sel = P5_INTF_SEL_GMAC5; + + mt7530_rmw(priv, MT7531_GPIO_MODE0, MT7531_GPIO0_MASK, + MT7531_GPIO0_INTERRUPT); +@@ -2787,11 +2776,6 @@ static void mt7530_mac_port_get_caps(str + } + } + +-static bool mt7531_is_rgmii_port(struct mt7530_priv *priv, u32 port) +-{ +- return (port == 5) && (priv->p5_intf_sel != P5_INTF_SEL_GMAC5_SGMII); +-} +- + static void mt7531_mac_port_get_caps(struct dsa_switch *ds, int port, + struct phylink_config *config) + { +@@ -2804,7 +2788,7 @@ static void mt7531_mac_port_get_caps(str + break; + + case 5: /* 2nd cpu port supports either rgmii or sgmii/8023z */ +- if (mt7531_is_rgmii_port(priv, port)) { ++ if (!priv->p5_sgmii) { + phy_interface_set_rgmii(config->supported_interfaces); + break; + } +@@ -2871,7 +2855,7 @@ static int mt7531_rgmii_setup(struct mt7 + { + u32 val; + +- if (!mt7531_is_rgmii_port(priv, port)) { ++ if (priv->p5_sgmii) { + dev_err(priv->dev, "RGMII mode is not available for port %d\n", + port); + return -EINVAL; +@@ -3114,7 +3098,7 @@ mt7531_cpu_port_config(struct dsa_switch + + switch (port) { + case 5: +- if (mt7531_is_rgmii_port(priv, port)) ++ if (!priv->p5_sgmii) + interface = PHY_INTERFACE_MODE_RGMII; + else + interface = PHY_INTERFACE_MODE_2500BASEX; +@@ -3272,7 +3256,7 @@ mt753x_setup(struct dsa_switch *ds) + mt7530_free_irq_common(priv); + + if (priv->create_sgmii) { +- ret = priv->create_sgmii(priv, mt7531_dual_sgmii_supported(priv)); ++ ret = priv->create_sgmii(priv); + if (ret && priv->irq) + mt7530_free_irq(priv); + } +--- a/drivers/net/dsa/mt7530.h ++++ b/drivers/net/dsa/mt7530.h +@@ -717,7 +717,6 @@ enum p5_interface_select { + P5_INTF_SEL_PHY_P0, + P5_INTF_SEL_PHY_P4, + P5_INTF_SEL_GMAC5, +- P5_INTF_SEL_GMAC5_SGMII, + }; + + struct mt7530_priv; +@@ -779,6 +778,8 @@ struct mt753x_info { + * registers + * @p6_interface Holding the current port 6 interface + * @p5_intf_sel: Holding the current port 5 interface select ++ * @p5_sgmii: Flag for distinguishing if port 5 of the MT7531 switch ++ * has got SGMII + * @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 +@@ -800,6 +801,7 @@ struct mt7530_priv { + phy_interface_t p6_interface; + phy_interface_t p5_interface; + enum p5_interface_select p5_intf_sel; ++ bool p5_sgmii; + u8 mirror_rx; + u8 mirror_tx; + struct mt7530_port ports[MT7530_NUM_PORTS]; +@@ -809,7 +811,7 @@ struct mt7530_priv { + int irq; + struct irq_domain *irq_domain; + u32 irq_enable; +- int (*create_sgmii)(struct mt7530_priv *priv, bool dual_sgmii); ++ int (*create_sgmii)(struct mt7530_priv *priv); + u8 active_cpu_ports; + }; + diff --git a/target/linux/generic/backport-6.1/790-26-v6.9-net-dsa-mt7530-improve-comments-regarding-switch-por.patch b/target/linux/generic/backport-6.1/790-26-v6.9-net-dsa-mt7530-improve-comments-regarding-switch-por.patch new file mode 100644 index 000000000..46dbd53ed --- /dev/null +++ b/target/linux/generic/backport-6.1/790-26-v6.9-net-dsa-mt7530-improve-comments-regarding-switch-por.patch @@ -0,0 +1,133 @@ +From 0dcde4c1e7c47822a6b00d6f96b7f19e51536026 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Ar=C4=B1n=C3=A7=20=C3=9CNAL?= +Date: Mon, 22 Jan 2024 08:35:55 +0300 +Subject: [PATCH 26/48] net: dsa: mt7530: improve comments regarding switch + ports +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +There's no logic to numerically order the CPU ports. Just state the port +number instead. + +Remove the irrelevant PHY muxing information from +mt7530_mac_port_get_caps(). Explain the supported MII modes instead. + +Remove the out of place PHY muxing information from +mt753x_phylink_mac_config(). The function is for MT7530, MT7531, and the +switch on the MT7988 SoC but there's no PHY muxing on MT7531 or the switch +on the MT7988 SoC. + +These comments were gradually introduced with the commits below. +commit ca366d6c889b ("net: dsa: mt7530: Convert to PHYLINK API") +commit 38f790a80560 ("net: dsa: mt7530: Add support for port 5") +commit 88bdef8be9f6 ("net: dsa: mt7530: Extend device data ready for adding +a new hardware") +commit c288575f7810 ("net: dsa: mt7530: Add the support of MT7531 switch") + +Signed-off-by: Arınç ÃœNAL +Acked-by: Daniel Golle +Reviewed-by: Andrew Lunn +Reviewed-by: Vladimir Oltean +Link: https://lore.kernel.org/r/20240122-for-netnext-mt7530-improvements-1-v3-4-042401f2b279@arinc9.com +Signed-off-by: Jakub Kicinski +--- + drivers/net/dsa/mt7530.c | 30 ++++++++++++++++++++---------- + 1 file changed, 20 insertions(+), 10 deletions(-) + +--- a/drivers/net/dsa/mt7530.c ++++ b/drivers/net/dsa/mt7530.c +@@ -2754,12 +2754,14 @@ static void mt7530_mac_port_get_caps(str + struct phylink_config *config) + { + switch (port) { +- case 0 ... 4: /* Internal phy */ ++ /* Ports which are connected to switch PHYs. There is no MII pinout. */ ++ case 0 ... 4: + __set_bit(PHY_INTERFACE_MODE_GMII, + config->supported_interfaces); + break; + +- case 5: /* 2nd cpu port with phy of port 0 or 4 / external phy */ ++ /* Port 5 supports rgmii with delays, mii, and gmii. */ ++ case 5: + phy_interface_set_rgmii(config->supported_interfaces); + __set_bit(PHY_INTERFACE_MODE_MII, + config->supported_interfaces); +@@ -2767,7 +2769,8 @@ static void mt7530_mac_port_get_caps(str + config->supported_interfaces); + break; + +- case 6: /* 1st cpu port */ ++ /* Port 6 supports rgmii and trgmii. */ ++ case 6: + __set_bit(PHY_INTERFACE_MODE_RGMII, + config->supported_interfaces); + __set_bit(PHY_INTERFACE_MODE_TRGMII, +@@ -2782,19 +2785,24 @@ static void mt7531_mac_port_get_caps(str + struct mt7530_priv *priv = ds->priv; + + switch (port) { +- case 0 ... 4: /* Internal phy */ ++ /* Ports which are connected to switch PHYs. There is no MII pinout. */ ++ case 0 ... 4: + __set_bit(PHY_INTERFACE_MODE_GMII, + config->supported_interfaces); + break; + +- case 5: /* 2nd cpu port supports either rgmii or sgmii/8023z */ ++ /* Port 5 supports rgmii with delays on MT7531BE, sgmii/802.3z on ++ * MT7531AE. ++ */ ++ case 5: + if (!priv->p5_sgmii) { + phy_interface_set_rgmii(config->supported_interfaces); + break; + } + fallthrough; + +- case 6: /* 1st cpu port supports sgmii/8023z only */ ++ /* Port 6 supports sgmii/802.3z. */ ++ case 6: + __set_bit(PHY_INTERFACE_MODE_SGMII, + config->supported_interfaces); + __set_bit(PHY_INTERFACE_MODE_1000BASEX, +@@ -2813,11 +2821,13 @@ static void mt7988_mac_port_get_caps(str + phy_interface_zero(config->supported_interfaces); + + switch (port) { +- case 0 ... 4: /* Internal phy */ ++ /* Ports which are connected to switch PHYs. There is no MII pinout. */ ++ case 0 ... 4: + __set_bit(PHY_INTERFACE_MODE_INTERNAL, + config->supported_interfaces); + break; + ++ /* Port 6 is connected to SoC's XGMII MAC. There is no MII pinout. */ + case 6: + __set_bit(PHY_INTERFACE_MODE_INTERNAL, + config->supported_interfaces); +@@ -2981,12 +2991,12 @@ mt753x_phylink_mac_config(struct dsa_swi + u32 mcr_cur, mcr_new; + + switch (port) { +- case 0 ... 4: /* Internal phy */ ++ case 0 ... 4: + if (state->interface != PHY_INTERFACE_MODE_GMII && + state->interface != PHY_INTERFACE_MODE_INTERNAL) + goto unsupported; + break; +- case 5: /* 2nd cpu port with phy of port 0 or 4 / external phy */ ++ case 5: + if (priv->p5_interface == state->interface) + break; + +@@ -2996,7 +3006,7 @@ mt753x_phylink_mac_config(struct dsa_swi + if (priv->p5_intf_sel != P5_DISABLED) + priv->p5_interface = state->interface; + break; +- case 6: /* 1st cpu port */ ++ case 6: + if (priv->p6_interface == state->interface) + break; + diff --git a/target/linux/generic/backport-6.1/790-27-v6.9-net-dsa-mt7530-improve-code-path-for-setting-up-port.patch b/target/linux/generic/backport-6.1/790-27-v6.9-net-dsa-mt7530-improve-code-path-for-setting-up-port.patch new file mode 100644 index 000000000..43f629b24 --- /dev/null +++ b/target/linux/generic/backport-6.1/790-27-v6.9-net-dsa-mt7530-improve-code-path-for-setting-up-port.patch @@ -0,0 +1,95 @@ +From 0f03d9bfa1da1d26cb950e4b35b1ff7b9be1828f Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Ar=C4=B1n=C3=A7=20=C3=9CNAL?= +Date: Mon, 22 Jan 2024 08:35:56 +0300 +Subject: [PATCH 27/48] net: dsa: mt7530: improve code path for setting up port + 5 +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +There're two code paths for setting up port 5: + +mt7530_setup() +-> mt7530_setup_port5() + +mt753x_phylink_mac_config() +-> mt753x_mac_config() + -> mt7530_mac_config() + -> mt7530_setup_port5() + +Currently mt7530_setup_port5() from mt7530_setup() always runs. If port 5 +is used as a CPU, DSA, or user port, mt7530_setup_port5() from +mt753x_phylink_mac_config() won't run. That is because priv->p5_interface +set on mt7530_setup_port5() will match state->interface on +mt753x_phylink_mac_config() which will stop running mt7530_setup_port5() +again. + +Therefore, mt7530_setup_port5() will never run from +mt753x_phylink_mac_config(). + +Address this by not running mt7530_setup_port5() from mt7530_setup() if +port 5 is used as a CPU, DSA, or user port. This driver isn't in the +dsa_switches_apply_workarounds[] array so phylink will always be present. + +To keep the cases where port 5 isn't controlled by phylink working as +before, preserve the mt7530_setup_port5() call from mt7530_setup(). + +Do not set priv->p5_intf_sel to P5_DISABLED. It is already set to that when +"priv" is allocated. + +Move setting the interface to a more specific location. It's supposed to be +overwritten if PHY muxing is detected. + +Improve the comment which explains the process. + +Signed-off-by: Arınç ÃœNAL +Reviewed-by: Vladimir Oltean +Link: https://lore.kernel.org/r/20240122-for-netnext-mt7530-improvements-1-v3-5-042401f2b279@arinc9.com +Signed-off-by: Jakub Kicinski +--- + drivers/net/dsa/mt7530.c | 17 ++++++++--------- + 1 file changed, 8 insertions(+), 9 deletions(-) + +--- a/drivers/net/dsa/mt7530.c ++++ b/drivers/net/dsa/mt7530.c +@@ -2532,16 +2532,15 @@ mt7530_setup(struct dsa_switch *ds) + return ret; + + /* Setup port 5 */ +- priv->p5_intf_sel = P5_DISABLED; +- interface = PHY_INTERFACE_MODE_NA; +- + if (!dsa_is_unused_port(ds, 5)) { + priv->p5_intf_sel = P5_INTF_SEL_GMAC5; +- ret = of_get_phy_mode(dsa_to_port(ds, 5)->dn, &interface); +- if (ret && ret != -ENODEV) +- return ret; + } else { +- /* Scan the ethernet nodes. look for GMAC1, lookup used phy */ ++ /* Scan the ethernet nodes. Look for GMAC1, lookup the used PHY. ++ * Set priv->p5_intf_sel to the appropriate value if PHY muxing ++ * is detected. ++ */ ++ interface = PHY_INTERFACE_MODE_NA; ++ + for_each_child_of_node(dn, mac_np) { + if (!of_device_is_compatible(mac_np, + "mediatek,eth-mac")) +@@ -2572,6 +2571,8 @@ mt7530_setup(struct dsa_switch *ds) + of_node_put(phy_node); + break; + } ++ ++ mt7530_setup_port5(ds, interface); + } + + #ifdef CONFIG_GPIOLIB +@@ -2582,8 +2583,6 @@ mt7530_setup(struct dsa_switch *ds) + } + #endif /* CONFIG_GPIOLIB */ + +- mt7530_setup_port5(ds, interface); +- + /* Flush the FDB table */ + ret = mt7530_fdb_cmd(priv, MT7530_FDB_FLUSH, NULL); + if (ret < 0) diff --git a/target/linux/generic/backport-6.1/790-28-v6.9-net-dsa-mt7530-do-not-set-priv-p5_interface-on-mt753.patch b/target/linux/generic/backport-6.1/790-28-v6.9-net-dsa-mt7530-do-not-set-priv-p5_interface-on-mt753.patch new file mode 100644 index 000000000..385d58772 --- /dev/null +++ b/target/linux/generic/backport-6.1/790-28-v6.9-net-dsa-mt7530-do-not-set-priv-p5_interface-on-mt753.patch @@ -0,0 +1,42 @@ +From 120581c81ad19704a9325505c83a82b7e760e96e Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Ar=C4=B1n=C3=A7=20=C3=9CNAL?= +Date: Mon, 22 Jan 2024 08:35:57 +0300 +Subject: [PATCH 28/48] net: dsa: mt7530: do not set priv->p5_interface on + mt7530_setup_port5() +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Running mt7530_setup_port5() from mt7530_setup() used to handle all cases +of configuring port 5, including phylink. + +Setting priv->p5_interface under mt7530_setup_port5() makes sure that +mt7530_setup_port5() from mt753x_phylink_mac_config() won't run. + +The commit ("net: dsa: mt7530: improve code path for setting up port 5") +makes so that mt7530_setup_port5() from mt7530_setup() runs only on +non-phylink cases. + +Get rid of unnecessarily setting priv->p5_interface under +mt7530_setup_port5() as port 5 phylink configuration will be done by +running mt7530_setup_port5() from mt753x_phylink_mac_config() now. + +Signed-off-by: Arınç ÃœNAL +Reviewed-by: Vladimir Oltean +Link: https://lore.kernel.org/r/20240122-for-netnext-mt7530-improvements-1-v3-6-042401f2b279@arinc9.com +Signed-off-by: Jakub Kicinski +--- + drivers/net/dsa/mt7530.c | 2 -- + 1 file changed, 2 deletions(-) + +--- a/drivers/net/dsa/mt7530.c ++++ b/drivers/net/dsa/mt7530.c +@@ -985,8 +985,6 @@ static void mt7530_setup_port5(struct ds + dev_dbg(ds->dev, "Setup P5, HWTRAP=0x%x, intf_sel=%s, phy-mode=%s\n", + val, p5_intf_modes(priv->p5_intf_sel), phy_modes(interface)); + +- priv->p5_interface = interface; +- + unlock_exit: + mutex_unlock(&priv->reg_mutex); + } diff --git a/target/linux/generic/backport-6.1/790-29-v6.9-net-dsa-mt7530-do-not-run-mt7530_setup_port5-if-port.patch b/target/linux/generic/backport-6.1/790-29-v6.9-net-dsa-mt7530-do-not-run-mt7530_setup_port5-if-port.patch new file mode 100644 index 000000000..b4c0b75c4 --- /dev/null +++ b/target/linux/generic/backport-6.1/790-29-v6.9-net-dsa-mt7530-do-not-run-mt7530_setup_port5-if-port.patch @@ -0,0 +1,62 @@ +From 3b423061eb3a62e59b57939ae1e1234756a0f6a1 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Ar=C4=B1n=C3=A7=20=C3=9CNAL?= +Date: Mon, 22 Jan 2024 08:35:58 +0300 +Subject: [PATCH 29/48] net: dsa: mt7530: do not run mt7530_setup_port5() if + port 5 is disabled +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +There's no need to run all the code on mt7530_setup_port5() if port 5 is +disabled. The only case for calling mt7530_setup_port5() from +mt7530_setup() is when PHY muxing is enabled. That is because port 5 is not +defined as a port on the devicetree, therefore, it cannot be controlled by +phylink. + +Because of this, run mt7530_setup_port5() if priv->p5_intf_sel is +P5_INTF_SEL_PHY_P0 or P5_INTF_SEL_PHY_P4. Remove the P5_DISABLED case from +mt7530_setup_port5(). + +Stop initialising the interface variable as the remaining cases will always +call mt7530_setup_port5() with it initialised. + +Signed-off-by: Arınç ÃœNAL +Reviewed-by: Vladimir Oltean +Link: https://lore.kernel.org/r/20240122-for-netnext-mt7530-improvements-1-v3-7-042401f2b279@arinc9.com +Signed-off-by: Jakub Kicinski +--- + drivers/net/dsa/mt7530.c | 9 +++------ + 1 file changed, 3 insertions(+), 6 deletions(-) + +--- a/drivers/net/dsa/mt7530.c ++++ b/drivers/net/dsa/mt7530.c +@@ -949,9 +949,6 @@ static void mt7530_setup_port5(struct ds + /* MT7530_P5_MODE_GMAC: P5 -> External phy or 2nd GMAC */ + val &= ~MHWTRAP_P5_DIS; + break; +- case P5_DISABLED: +- interface = PHY_INTERFACE_MODE_NA; +- break; + default: + dev_err(ds->dev, "Unsupported p5_intf_sel %d\n", + priv->p5_intf_sel); +@@ -2537,8 +2534,6 @@ mt7530_setup(struct dsa_switch *ds) + * Set priv->p5_intf_sel to the appropriate value if PHY muxing + * is detected. + */ +- interface = PHY_INTERFACE_MODE_NA; +- + for_each_child_of_node(dn, mac_np) { + if (!of_device_is_compatible(mac_np, + "mediatek,eth-mac")) +@@ -2570,7 +2565,9 @@ mt7530_setup(struct dsa_switch *ds) + break; + } + +- mt7530_setup_port5(ds, interface); ++ if (priv->p5_intf_sel == P5_INTF_SEL_PHY_P0 || ++ priv->p5_intf_sel == P5_INTF_SEL_PHY_P4) ++ mt7530_setup_port5(ds, interface); + } + + #ifdef CONFIG_GPIOLIB diff --git a/target/linux/generic/backport-6.1/790-30-v6.9-net-dsa-mt7530-empty-default-case-on-mt7530_setup_po.patch b/target/linux/generic/backport-6.1/790-30-v6.9-net-dsa-mt7530-empty-default-case-on-mt7530_setup_po.patch new file mode 100644 index 000000000..89527e2b3 --- /dev/null +++ b/target/linux/generic/backport-6.1/790-30-v6.9-net-dsa-mt7530-empty-default-case-on-mt7530_setup_po.patch @@ -0,0 +1,58 @@ +From 959f4ac4940bebb84bdd25ac61470b3965e1e475 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Ar=C4=B1n=C3=A7=20=C3=9CNAL?= +Date: Tue, 6 Feb 2024 01:08:02 +0300 +Subject: [PATCH 30/48] net: dsa: mt7530: empty default case on + mt7530_setup_port5() +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +There're two code paths for setting up port 5: + +mt7530_setup() +-> mt7530_setup_port5() + +mt753x_phylink_mac_config() +-> mt753x_mac_config() + -> mt7530_mac_config() + -> mt7530_setup_port5() + +On the first code path, priv->p5_intf_sel is either set to +P5_INTF_SEL_PHY_P0 or P5_INTF_SEL_PHY_P4 when mt7530_setup_port5() is run. + +On the second code path, priv->p5_intf_sel is set to P5_INTF_SEL_GMAC5 when +mt7530_setup_port5() is run. + +Empty the default case which will never run but is needed nonetheless to +handle all the remaining enumeration values. + +Reviewed-by: Vladimir Oltean +Reviewed-by: Russell King (Oracle) +Signed-off-by: Arınç ÃœNAL +Link: https://lore.kernel.org/r/20240206-for-netnext-mt7530-improvements-2-v5-1-d7d92a185cb1@arinc9.com +Signed-off-by: Jakub Kicinski +--- + drivers/net/dsa/mt7530.c | 5 +---- + 1 file changed, 1 insertion(+), 4 deletions(-) + +--- a/drivers/net/dsa/mt7530.c ++++ b/drivers/net/dsa/mt7530.c +@@ -950,9 +950,7 @@ static void mt7530_setup_port5(struct ds + val &= ~MHWTRAP_P5_DIS; + break; + default: +- dev_err(ds->dev, "Unsupported p5_intf_sel %d\n", +- priv->p5_intf_sel); +- goto unlock_exit; ++ break; + } + + /* Setup RGMII settings */ +@@ -982,7 +980,6 @@ static void mt7530_setup_port5(struct ds + dev_dbg(ds->dev, "Setup P5, HWTRAP=0x%x, intf_sel=%s, phy-mode=%s\n", + val, p5_intf_modes(priv->p5_intf_sel), phy_modes(interface)); + +-unlock_exit: + mutex_unlock(&priv->reg_mutex); + } + diff --git a/target/linux/generic/backport-6.1/790-31-v6.9-net-dsa-mt7530-move-XTAL-check-to-mt7530_setup.patch b/target/linux/generic/backport-6.1/790-31-v6.9-net-dsa-mt7530-move-XTAL-check-to-mt7530_setup.patch new file mode 100644 index 000000000..0dc4baf0c --- /dev/null +++ b/target/linux/generic/backport-6.1/790-31-v6.9-net-dsa-mt7530-move-XTAL-check-to-mt7530_setup.patch @@ -0,0 +1,53 @@ +From 9ec0a800c8e0850e1358b7402d6af557af81cb38 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Ar=C4=B1n=C3=A7=20=C3=9CNAL?= +Date: Tue, 6 Feb 2024 01:08:03 +0300 +Subject: [PATCH 31/48] net: dsa: mt7530: move XTAL check to mt7530_setup() +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +The crystal frequency concerns the switch core. The frequency should be +checked when the switch is being set up so the driver can reject the +unsupported hardware earlier and without requiring port 6 to be used. + +Move it to mt7530_setup(). Drop the unnecessary function printing. + +Reviewed-by: Andrew Lunn +Reviewed-by: Vladimir Oltean +Reviewed-by: Russell King (Oracle) +Signed-off-by: Arınç ÃœNAL +Link: https://lore.kernel.org/r/20240206-for-netnext-mt7530-improvements-2-v5-2-d7d92a185cb1@arinc9.com +Signed-off-by: Jakub Kicinski +--- + drivers/net/dsa/mt7530.c | 13 ++++++------- + 1 file changed, 6 insertions(+), 7 deletions(-) + +--- a/drivers/net/dsa/mt7530.c ++++ b/drivers/net/dsa/mt7530.c +@@ -422,13 +422,6 @@ mt7530_pad_clk_setup(struct dsa_switch * + + xtal = mt7530_read(priv, MT7530_MHWTRAP) & HWTRAP_XTAL_MASK; + +- if (xtal == HWTRAP_XTAL_20MHZ) { +- dev_err(priv->dev, +- "%s: MT7530 with a 20MHz XTAL is not supported!\n", +- __func__); +- return -EINVAL; +- } +- + switch (interface) { + case PHY_INTERFACE_MODE_RGMII: + trgint = 0; +@@ -2461,6 +2454,12 @@ mt7530_setup(struct dsa_switch *ds) + return -ENODEV; + } + ++ if ((val & HWTRAP_XTAL_MASK) == HWTRAP_XTAL_20MHZ) { ++ dev_err(priv->dev, ++ "MT7530 with a 20MHz XTAL is not supported!\n"); ++ return -EINVAL; ++ } ++ + /* Reset the switch through internal reset */ + mt7530_write(priv, MT7530_SYS_CTRL, + SYS_CTRL_PHY_RST | SYS_CTRL_SW_RST | diff --git a/target/linux/generic/backport-6.1/790-32-v6.9-net-dsa-mt7530-simplify-mt7530_pad_clk_setup.patch b/target/linux/generic/backport-6.1/790-32-v6.9-net-dsa-mt7530-simplify-mt7530_pad_clk_setup.patch new file mode 100644 index 000000000..46e1b50f2 --- /dev/null +++ b/target/linux/generic/backport-6.1/790-32-v6.9-net-dsa-mt7530-simplify-mt7530_pad_clk_setup.patch @@ -0,0 +1,146 @@ +From 7199c736aa8cd9c69ae681a9c733408372c2ce76 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Ar=C4=B1n=C3=A7=20=C3=9CNAL?= +Date: Tue, 6 Feb 2024 01:08:04 +0300 +Subject: [PATCH 32/48] net: dsa: mt7530: simplify mt7530_pad_clk_setup() +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +This code is from before this driver was converted to phylink API. Phylink +deals with the unsupported interface cases before mt7530_pad_clk_setup() is +run. Therefore, the default case would never run. However, it must be +defined nonetheless to handle all the remaining enumeration values, the +phy-modes. + +Switch to if statement for RGMII and return which simplifies the code and +saves an indent. + +Set P6_INTF_MODE, which is the three least significant bits of the +MT7530_P6ECR register, to 0 for RGMII even though it will already be 0 +after reset. This is to keep supporting dynamic reconfiguration of the port +in the case the interface changes from TRGMII to RGMII. + +Disable the TRGMII clocks for all cases. They will be enabled if TRGMII is +being used. + +Read XTAL after checking for RGMII as it's only needed for the TRGMII +interface mode. + +Reviewed-by: Daniel Golle +Reviewed-by: Russell King (Oracle) +Signed-off-by: Arınç ÃœNAL +Reviewed-by: Vladimir Oltean +Link: https://lore.kernel.org/r/20240206-for-netnext-mt7530-improvements-2-v5-3-d7d92a185cb1@arinc9.com +Signed-off-by: Jakub Kicinski +--- + drivers/net/dsa/mt7530.c | 91 ++++++++++++++++++---------------------- + 1 file changed, 40 insertions(+), 51 deletions(-) + +--- a/drivers/net/dsa/mt7530.c ++++ b/drivers/net/dsa/mt7530.c +@@ -418,65 +418,54 @@ static int + mt7530_pad_clk_setup(struct dsa_switch *ds, phy_interface_t interface) + { + struct mt7530_priv *priv = ds->priv; +- u32 ncpo1, ssc_delta, trgint, xtal; ++ u32 ncpo1, ssc_delta, xtal; + +- xtal = mt7530_read(priv, MT7530_MHWTRAP) & HWTRAP_XTAL_MASK; ++ /* Disable the MT7530 TRGMII clocks */ ++ core_clear(priv, CORE_TRGMII_GSW_CLK_CG, REG_TRGMIICK_EN); + +- switch (interface) { +- case PHY_INTERFACE_MODE_RGMII: +- trgint = 0; +- break; +- case PHY_INTERFACE_MODE_TRGMII: +- trgint = 1; +- if (xtal == HWTRAP_XTAL_25MHZ) +- ssc_delta = 0x57; +- else +- ssc_delta = 0x87; +- if (priv->id == ID_MT7621) { +- /* PLL frequency: 125MHz: 1.0GBit */ +- if (xtal == HWTRAP_XTAL_40MHZ) +- ncpo1 = 0x0640; +- if (xtal == HWTRAP_XTAL_25MHZ) +- ncpo1 = 0x0a00; +- } else { /* PLL frequency: 250MHz: 2.0Gbit */ +- if (xtal == HWTRAP_XTAL_40MHZ) +- ncpo1 = 0x0c80; +- if (xtal == HWTRAP_XTAL_25MHZ) +- ncpo1 = 0x1400; +- } +- break; +- default: +- dev_err(priv->dev, "xMII interface %d not supported\n", +- interface); +- return -EINVAL; ++ if (interface == PHY_INTERFACE_MODE_RGMII) { ++ mt7530_rmw(priv, MT7530_P6ECR, P6_INTF_MODE_MASK, ++ P6_INTF_MODE(0)); ++ return 0; + } + +- mt7530_rmw(priv, MT7530_P6ECR, P6_INTF_MODE_MASK, +- P6_INTF_MODE(trgint)); ++ mt7530_rmw(priv, MT7530_P6ECR, P6_INTF_MODE_MASK, P6_INTF_MODE(1)); + +- if (trgint) { +- /* Disable the MT7530 TRGMII clocks */ +- core_clear(priv, CORE_TRGMII_GSW_CLK_CG, REG_TRGMIICK_EN); +- +- /* Setup the MT7530 TRGMII Tx Clock */ +- core_write(priv, CORE_PLL_GROUP5, RG_LCDDS_PCW_NCPO1(ncpo1)); +- core_write(priv, CORE_PLL_GROUP6, RG_LCDDS_PCW_NCPO0(0)); +- core_write(priv, CORE_PLL_GROUP10, RG_LCDDS_SSC_DELTA(ssc_delta)); +- core_write(priv, CORE_PLL_GROUP11, RG_LCDDS_SSC_DELTA1(ssc_delta)); +- core_write(priv, CORE_PLL_GROUP4, +- RG_SYSPLL_DDSFBK_EN | RG_SYSPLL_BIAS_EN | +- RG_SYSPLL_BIAS_LPF_EN); +- core_write(priv, CORE_PLL_GROUP2, +- RG_SYSPLL_EN_NORMAL | RG_SYSPLL_VODEN | +- RG_SYSPLL_POSDIV(1)); +- core_write(priv, CORE_PLL_GROUP7, +- RG_LCDDS_PCW_NCPO_CHG | RG_LCCDS_C(3) | +- RG_LCDDS_PWDB | RG_LCDDS_ISO_EN); ++ xtal = mt7530_read(priv, MT7530_MHWTRAP) & HWTRAP_XTAL_MASK; + +- /* Enable the MT7530 TRGMII clocks */ +- core_set(priv, CORE_TRGMII_GSW_CLK_CG, REG_TRGMIICK_EN); ++ if (xtal == HWTRAP_XTAL_25MHZ) ++ ssc_delta = 0x57; ++ else ++ ssc_delta = 0x87; ++ ++ if (priv->id == ID_MT7621) { ++ /* PLL frequency: 125MHz: 1.0GBit */ ++ if (xtal == HWTRAP_XTAL_40MHZ) ++ ncpo1 = 0x0640; ++ if (xtal == HWTRAP_XTAL_25MHZ) ++ ncpo1 = 0x0a00; ++ } else { /* PLL frequency: 250MHz: 2.0Gbit */ ++ if (xtal == HWTRAP_XTAL_40MHZ) ++ ncpo1 = 0x0c80; ++ if (xtal == HWTRAP_XTAL_25MHZ) ++ ncpo1 = 0x1400; + } + ++ /* Setup the MT7530 TRGMII Tx Clock */ ++ core_write(priv, CORE_PLL_GROUP5, RG_LCDDS_PCW_NCPO1(ncpo1)); ++ core_write(priv, CORE_PLL_GROUP6, RG_LCDDS_PCW_NCPO0(0)); ++ core_write(priv, CORE_PLL_GROUP10, RG_LCDDS_SSC_DELTA(ssc_delta)); ++ core_write(priv, CORE_PLL_GROUP11, RG_LCDDS_SSC_DELTA1(ssc_delta)); ++ core_write(priv, CORE_PLL_GROUP4, RG_SYSPLL_DDSFBK_EN | ++ RG_SYSPLL_BIAS_EN | RG_SYSPLL_BIAS_LPF_EN); ++ core_write(priv, CORE_PLL_GROUP2, RG_SYSPLL_EN_NORMAL | ++ RG_SYSPLL_VODEN | RG_SYSPLL_POSDIV(1)); ++ core_write(priv, CORE_PLL_GROUP7, RG_LCDDS_PCW_NCPO_CHG | ++ RG_LCCDS_C(3) | RG_LCDDS_PWDB | RG_LCDDS_ISO_EN); ++ ++ /* Enable the MT7530 TRGMII clocks */ ++ core_set(priv, CORE_TRGMII_GSW_CLK_CG, REG_TRGMIICK_EN); ++ + return 0; + } + diff --git a/target/linux/generic/backport-6.1/790-33-v6.9-net-dsa-mt7530-call-port-6-setup-from-mt7530_mac_con.patch b/target/linux/generic/backport-6.1/790-33-v6.9-net-dsa-mt7530-call-port-6-setup-from-mt7530_mac_con.patch new file mode 100644 index 000000000..7d78b7df7 --- /dev/null +++ b/target/linux/generic/backport-6.1/790-33-v6.9-net-dsa-mt7530-call-port-6-setup-from-mt7530_mac_con.patch @@ -0,0 +1,97 @@ +From 921a7deee767aa157b5372863a4c1cac53e5c53a Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Ar=C4=B1n=C3=A7=20=C3=9CNAL?= +Date: Tue, 6 Feb 2024 01:08:05 +0300 +Subject: [PATCH 33/48] net: dsa: mt7530: call port 6 setup from + mt7530_mac_config() +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +mt7530_pad_clk_setup() is called if port 6 is enabled. It used to do more +things than setting up port 6. That part was moved to more appropriate +locations, mt7530_setup() and mt7530_pll_setup(). + +Now that all it does is set up port 6, rename it to mt7530_setup_port6(), +and move it to a more appropriate location, under mt7530_mac_config(). + +Change mt7530_setup_port6() to void as there're no error cases. + +Leave an empty mt7530_pad_clk_setup() to satisfy the pad_setup function +pointer. + +This is the code path for setting up the ports before: + +dsa_switch_ops :: phylink_mac_config() -> mt753x_phylink_mac_config() +-> mt753x_mac_config() + -> mt753x_info :: mac_port_config() -> mt7530_mac_config() + -> mt7530_setup_port5() +-> mt753x_pad_setup() + -> mt753x_info :: pad_setup() -> mt7530_pad_clk_setup() + +This is after: + +dsa_switch_ops :: phylink_mac_config() -> mt753x_phylink_mac_config() +-> mt753x_mac_config() + -> mt753x_info :: mac_port_config() -> mt7530_mac_config() + -> mt7530_setup_port5() + -> mt7530_setup_port6() + +Reviewed-by: Vladimir Oltean +Reviewed-by: Russell King (Oracle) +Signed-off-by: Arınç ÃœNAL +Link: https://lore.kernel.org/r/20240206-for-netnext-mt7530-improvements-2-v5-4-d7d92a185cb1@arinc9.com +Signed-off-by: Jakub Kicinski +--- + drivers/net/dsa/mt7530.c | 19 +++++++++++-------- + 1 file changed, 11 insertions(+), 8 deletions(-) + +--- a/drivers/net/dsa/mt7530.c ++++ b/drivers/net/dsa/mt7530.c +@@ -414,8 +414,8 @@ mt753x_preferred_default_local_cpu_port( + } + + /* Setup port 6 interface mode and TRGMII TX circuit */ +-static int +-mt7530_pad_clk_setup(struct dsa_switch *ds, phy_interface_t interface) ++static void ++mt7530_setup_port6(struct dsa_switch *ds, phy_interface_t interface) + { + struct mt7530_priv *priv = ds->priv; + u32 ncpo1, ssc_delta, xtal; +@@ -426,7 +426,7 @@ mt7530_pad_clk_setup(struct dsa_switch * + if (interface == PHY_INTERFACE_MODE_RGMII) { + mt7530_rmw(priv, MT7530_P6ECR, P6_INTF_MODE_MASK, + P6_INTF_MODE(0)); +- return 0; ++ return; + } + + mt7530_rmw(priv, MT7530_P6ECR, P6_INTF_MODE_MASK, P6_INTF_MODE(1)); +@@ -465,7 +465,11 @@ mt7530_pad_clk_setup(struct dsa_switch * + + /* Enable the MT7530 TRGMII clocks */ + core_set(priv, CORE_TRGMII_GSW_CLK_CG, REG_TRGMIICK_EN); ++} + ++static int ++mt7530_pad_clk_setup(struct dsa_switch *ds, phy_interface_t interface) ++{ + return 0; + } + +@@ -2829,11 +2833,10 @@ mt7530_mac_config(struct dsa_switch *ds, + { + struct mt7530_priv *priv = ds->priv; + +- /* Only need to setup port5. */ +- if (port != 5) +- return 0; +- +- mt7530_setup_port5(priv->ds, interface); ++ if (port == 5) ++ mt7530_setup_port5(priv->ds, interface); ++ else if (port == 6) ++ mt7530_setup_port6(priv->ds, interface); + + return 0; + } diff --git a/target/linux/generic/backport-6.1/790-34-v6.9-net-dsa-mt7530-remove-pad_setup-function-pointer.patch b/target/linux/generic/backport-6.1/790-34-v6.9-net-dsa-mt7530-remove-pad_setup-function-pointer.patch new file mode 100644 index 000000000..725830e76 --- /dev/null +++ b/target/linux/generic/backport-6.1/790-34-v6.9-net-dsa-mt7530-remove-pad_setup-function-pointer.patch @@ -0,0 +1,148 @@ +From fcbc5d900fa53f79963fe4626069739ee5567b4b Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Ar=C4=B1n=C3=A7=20=C3=9CNAL?= +Date: Tue, 6 Feb 2024 01:08:06 +0300 +Subject: [PATCH 34/48] net: dsa: mt7530: remove pad_setup function pointer +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +The pad_setup function pointer was introduced with 88bdef8be9f6 ("net: dsa: +mt7530: Extend device data ready for adding a new hardware"). It was being +used to set up the core clock and port 6 of the MT7530 switch, and pll of +the MT7531 switch. + +All of these were moved to more appropriate locations, and it was never +used for the switch on the MT7988 SoC. Therefore, this function pointer +hasn't got a use anymore. Remove it. + +Acked-by: Daniel Golle +Reviewed-by: Vladimir Oltean +Reviewed-by: Russell King (Oracle) +Signed-off-by: Arınç ÃœNAL +Link: https://lore.kernel.org/r/20240206-for-netnext-mt7530-improvements-2-v5-5-d7d92a185cb1@arinc9.com +Signed-off-by: Jakub Kicinski +--- + drivers/net/dsa/mt7530.c | 36 ++---------------------------------- + drivers/net/dsa/mt7530.h | 3 --- + 2 files changed, 2 insertions(+), 37 deletions(-) + +--- a/drivers/net/dsa/mt7530.c ++++ b/drivers/net/dsa/mt7530.c +@@ -467,18 +467,6 @@ mt7530_setup_port6(struct dsa_switch *ds + core_set(priv, CORE_TRGMII_GSW_CLK_CG, REG_TRGMIICK_EN); + } + +-static int +-mt7530_pad_clk_setup(struct dsa_switch *ds, phy_interface_t interface) +-{ +- return 0; +-} +- +-static int +-mt7531_pad_setup(struct dsa_switch *ds, phy_interface_t interface) +-{ +- return 0; +-} +- + static void + mt7531_pll_setup(struct mt7530_priv *priv) + { +@@ -2820,14 +2808,6 @@ static void mt7988_mac_port_get_caps(str + } + + static int +-mt753x_pad_setup(struct dsa_switch *ds, const struct phylink_link_state *state) +-{ +- struct mt7530_priv *priv = ds->priv; +- +- return priv->info->pad_setup(ds, state->interface); +-} +- +-static int + mt7530_mac_config(struct dsa_switch *ds, int port, unsigned int mode, + phy_interface_t interface) + { +@@ -2992,8 +2972,6 @@ mt753x_phylink_mac_config(struct dsa_swi + if (priv->p6_interface == state->interface) + break; + +- mt753x_pad_setup(ds, state); +- + if (mt753x_mac_config(ds, port, mode, state) < 0) + goto unsupported; + +@@ -3316,11 +3294,6 @@ mt753x_conduit_state_change(struct dsa_s + mt7530_rmw(priv, MT7530_MFC, CPU_EN | CPU_PORT_MASK, val); + } + +-static int mt7988_pad_setup(struct dsa_switch *ds, phy_interface_t interface) +-{ +- return 0; +-} +- + static int mt7988_setup(struct dsa_switch *ds) + { + struct mt7530_priv *priv = ds->priv; +@@ -3382,7 +3355,6 @@ const struct mt753x_info mt753x_table[] + .sw_setup = mt7530_setup, + .phy_read = mt7530_phy_read, + .phy_write = mt7530_phy_write, +- .pad_setup = mt7530_pad_clk_setup, + .mac_port_get_caps = mt7530_mac_port_get_caps, + .mac_port_config = mt7530_mac_config, + }, +@@ -3392,7 +3364,6 @@ const struct mt753x_info mt753x_table[] + .sw_setup = mt7530_setup, + .phy_read = mt7530_phy_read, + .phy_write = mt7530_phy_write, +- .pad_setup = mt7530_pad_clk_setup, + .mac_port_get_caps = mt7530_mac_port_get_caps, + .mac_port_config = mt7530_mac_config, + }, +@@ -3402,7 +3373,6 @@ const struct mt753x_info mt753x_table[] + .sw_setup = mt7531_setup, + .phy_read = mt7531_ind_phy_read, + .phy_write = mt7531_ind_phy_write, +- .pad_setup = mt7531_pad_setup, + .cpu_port_config = mt7531_cpu_port_config, + .mac_port_get_caps = mt7531_mac_port_get_caps, + .mac_port_config = mt7531_mac_config, +@@ -3413,7 +3383,6 @@ const struct mt753x_info mt753x_table[] + .sw_setup = mt7988_setup, + .phy_read = mt7531_ind_phy_read, + .phy_write = mt7531_ind_phy_write, +- .pad_setup = mt7988_pad_setup, + .cpu_port_config = mt7988_cpu_port_config, + .mac_port_get_caps = mt7988_mac_port_get_caps, + .mac_port_config = mt7988_mac_config, +@@ -3443,9 +3412,8 @@ mt7530_probe_common(struct mt7530_priv * + /* Sanity check if these required device operations are filled + * properly. + */ +- if (!priv->info->sw_setup || !priv->info->pad_setup || +- !priv->info->phy_read || !priv->info->phy_write || +- !priv->info->mac_port_get_caps || ++ if (!priv->info->sw_setup || !priv->info->phy_read || ++ !priv->info->phy_write || !priv->info->mac_port_get_caps || + !priv->info->mac_port_config) + return -EINVAL; + +--- a/drivers/net/dsa/mt7530.h ++++ b/drivers/net/dsa/mt7530.h +@@ -732,8 +732,6 @@ struct mt753x_pcs { + * @sw_setup: Holding the handler to a device initialization + * @phy_read: Holding the way reading PHY port + * @phy_write: Holding the way writing PHY port +- * @pad_setup: Holding the way setting up the bus pad for a certain +- * MAC port + * @phy_mode_supported: Check if the PHY type is being supported on a certain + * port + * @mac_port_validate: Holding the way to set addition validate type for a +@@ -749,7 +747,6 @@ struct mt753x_info { + int (*sw_setup)(struct dsa_switch *ds); + int (*phy_read)(struct mt7530_priv *priv, int port, int regnum); + int (*phy_write)(struct mt7530_priv *priv, int port, int regnum, u16 val); +- int (*pad_setup)(struct dsa_switch *ds, phy_interface_t interface); + int (*cpu_port_config)(struct dsa_switch *ds, int port); + void (*mac_port_get_caps)(struct dsa_switch *ds, int port, + struct phylink_config *config); diff --git a/target/linux/generic/backport-6.1/790-35-v6.9-net-dsa-mt7530-correct-port-capabilities-of-MT7988.patch b/target/linux/generic/backport-6.1/790-35-v6.9-net-dsa-mt7530-correct-port-capabilities-of-MT7988.patch new file mode 100644 index 000000000..606c48234 --- /dev/null +++ b/target/linux/generic/backport-6.1/790-35-v6.9-net-dsa-mt7530-correct-port-capabilities-of-MT7988.patch @@ -0,0 +1,36 @@ +From 58a94eb63233bb2ede13b183b6a6a03aa0a2dfc3 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Ar=C4=B1n=C3=A7=20=C3=9CNAL?= +Date: Tue, 6 Feb 2024 01:08:07 +0300 +Subject: [PATCH 35/48] net: dsa: mt7530: correct port capabilities of MT7988 +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +On the switch on the MT7988 SoC, as shown in Block Diagram 8.1.1.3 on page +125 of "MT7988A Wi-Fi 7 Generation Router Platform: Datasheet (Open +Version) v0.1", there are only 4 PHYs. That's port 0 to 3. Set the case for +ports which connect to switch PHYs to '0 ... 3'. + +Port 4 and 5 are not used at all in this design. + +Link: https://wiki.banana-pi.org/Banana_Pi_BPI-R4#Documents [1] +Acked-by: Daniel Golle +Reviewed-by: Vladimir Oltean +Signed-off-by: Arınç ÃœNAL +Link: https://lore.kernel.org/r/20240206-for-netnext-mt7530-improvements-2-v5-6-d7d92a185cb1@arinc9.com +Signed-off-by: Jakub Kicinski +--- + drivers/net/dsa/mt7530.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/drivers/net/dsa/mt7530.c ++++ b/drivers/net/dsa/mt7530.c +@@ -2793,7 +2793,7 @@ static void mt7988_mac_port_get_caps(str + + switch (port) { + /* Ports which are connected to switch PHYs. There is no MII pinout. */ +- case 0 ... 4: ++ case 0 ... 3: + __set_bit(PHY_INTERFACE_MODE_INTERNAL, + config->supported_interfaces); + break; diff --git a/target/linux/generic/backport-6.1/790-36-v6.9-net-dsa-mt7530-do-not-clear-config-supported_interfa.patch b/target/linux/generic/backport-6.1/790-36-v6.9-net-dsa-mt7530-do-not-clear-config-supported_interfa.patch new file mode 100644 index 000000000..e2f1f2343 --- /dev/null +++ b/target/linux/generic/backport-6.1/790-36-v6.9-net-dsa-mt7530-do-not-clear-config-supported_interfa.patch @@ -0,0 +1,38 @@ +From 4440ce33074be6bd55d1a0c8b5f4b6d433ae2c74 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Ar=C4=B1n=C3=A7=20=C3=9CNAL?= +Date: Tue, 6 Feb 2024 01:08:08 +0300 +Subject: [PATCH 36/48] net: dsa: mt7530: do not clear + config->supported_interfaces +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +There's no need to clear the config->supported_interfaces bitmap before +reporting the supported interfaces as all bits in the bitmap will already +be initialized to zero when the phylink_config structure is allocated. The +"config" pointer points to &dp->phylink_config, and "dp" is allocated by +dsa_port_touch() with kzalloc(), so all its fields are filled with zeroes. + +There's no code that would change the bitmap beforehand. Remove it. + +Acked-by: Daniel Golle +Reviewed-by: Vladimir Oltean +Reviewed-by: Russell King (Oracle) +Signed-off-by: Arınç ÃœNAL +Link: https://lore.kernel.org/r/20240206-for-netnext-mt7530-improvements-2-v5-7-d7d92a185cb1@arinc9.com +Signed-off-by: Jakub Kicinski +--- + drivers/net/dsa/mt7530.c | 2 -- + 1 file changed, 2 deletions(-) + +--- a/drivers/net/dsa/mt7530.c ++++ b/drivers/net/dsa/mt7530.c +@@ -2789,8 +2789,6 @@ static void mt7531_mac_port_get_caps(str + static void mt7988_mac_port_get_caps(struct dsa_switch *ds, int port, + struct phylink_config *config) + { +- phy_interface_zero(config->supported_interfaces); +- + switch (port) { + /* Ports which are connected to switch PHYs. There is no MII pinout. */ + case 0 ... 3: diff --git a/target/linux/generic/backport-6.1/790-37-v6.9-net-dsa-mt7530-remove-.mac_port_config-for-MT7988-an.patch b/target/linux/generic/backport-6.1/790-37-v6.9-net-dsa-mt7530-remove-.mac_port_config-for-MT7988-an.patch new file mode 100644 index 000000000..be6fe39f3 --- /dev/null +++ b/target/linux/generic/backport-6.1/790-37-v6.9-net-dsa-mt7530-remove-.mac_port_config-for-MT7988-an.patch @@ -0,0 +1,81 @@ +From 2f507aaeb1a12044f2376a255c2afff1f7432b0b Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Ar=C4=B1n=C3=A7=20=C3=9CNAL?= +Date: Fri, 1 Mar 2024 12:42:57 +0200 +Subject: [PATCH 37/48] net: dsa: mt7530: remove .mac_port_config for MT7988 + and make it optional +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +For the switch on the MT7988 SoC, the mac_port_config member for ID_MT7988 +in mt753x_table is not needed as the interfaces of all MACs are already +handled on mt7988_mac_port_get_caps(). + +Therefore, remove the mac_port_config member from ID_MT7988 in +mt753x_table. Before calling priv->info->mac_port_config(), if there's no +mac_port_config member in mt753x_table, exit mt753x_mac_config() +successfully. + +Remove calling priv->info->mac_port_config() from the sanity check as the +sanity check requires a pointer to a mac_port_config function to be +non-NULL. This will fail for MT7988 as mac_port_config won't be a member of +its info table. + +Co-developed-by: Daniel Golle +Signed-off-by: Daniel Golle +Signed-off-by: Arınç ÃœNAL +Reviewed-by: Vladimir Oltean +Reviewed-by: Russell King (Oracle) +Signed-off-by: Paolo Abeni +--- + drivers/net/dsa/mt7530.c | 18 ++++-------------- + 1 file changed, 4 insertions(+), 14 deletions(-) + +--- a/drivers/net/dsa/mt7530.c ++++ b/drivers/net/dsa/mt7530.c +@@ -2872,17 +2872,6 @@ static bool mt753x_is_mac_port(u32 port) + } + + static int +-mt7988_mac_config(struct dsa_switch *ds, int port, unsigned int mode, +- phy_interface_t interface) +-{ +- if (dsa_is_cpu_port(ds, port) && +- interface == PHY_INTERFACE_MODE_INTERNAL) +- return 0; +- +- return -EINVAL; +-} +- +-static int + mt7531_mac_config(struct dsa_switch *ds, int port, unsigned int mode, + phy_interface_t interface) + { +@@ -2922,6 +2911,9 @@ mt753x_mac_config(struct dsa_switch *ds, + { + struct mt7530_priv *priv = ds->priv; + ++ if (!priv->info->mac_port_config) ++ return 0; ++ + return priv->info->mac_port_config(ds, port, mode, state->interface); + } + +@@ -3383,7 +3375,6 @@ const struct mt753x_info mt753x_table[] + .phy_write = mt7531_ind_phy_write, + .cpu_port_config = mt7988_cpu_port_config, + .mac_port_get_caps = mt7988_mac_port_get_caps, +- .mac_port_config = mt7988_mac_config, + }, + }; + EXPORT_SYMBOL_GPL(mt753x_table); +@@ -3411,8 +3402,7 @@ mt7530_probe_common(struct mt7530_priv * + * properly. + */ + if (!priv->info->sw_setup || !priv->info->phy_read || +- !priv->info->phy_write || !priv->info->mac_port_get_caps || +- !priv->info->mac_port_config) ++ !priv->info->phy_write || !priv->info->mac_port_get_caps) + return -EINVAL; + + priv->id = priv->info->id; diff --git a/target/linux/generic/backport-6.1/790-38-v6.9-net-dsa-mt7530-set-interrupt-register-only-for-MT753.patch b/target/linux/generic/backport-6.1/790-38-v6.9-net-dsa-mt7530-set-interrupt-register-only-for-MT753.patch new file mode 100644 index 000000000..b8473ed12 --- /dev/null +++ b/target/linux/generic/backport-6.1/790-38-v6.9-net-dsa-mt7530-set-interrupt-register-only-for-MT753.patch @@ -0,0 +1,31 @@ +From 0e297b1c662825f7dcd97272323c81f502987e0f Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Ar=C4=B1n=C3=A7=20=C3=9CNAL?= +Date: Fri, 1 Mar 2024 12:42:58 +0200 +Subject: [PATCH 38/48] net: dsa: mt7530: set interrupt register only for + MT7530 +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Setting this register related to interrupts is only needed for the MT7530 +switch. Make an exclusive check to ensure this. + +Signed-off-by: Arınç ÃœNAL +Acked-by: Daniel Golle +Tested-by: Daniel Golle +Signed-off-by: Paolo Abeni +--- + drivers/net/dsa/mt7530.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/drivers/net/dsa/mt7530.c ++++ b/drivers/net/dsa/mt7530.c +@@ -2259,7 +2259,7 @@ mt7530_setup_irq(struct mt7530_priv *pri + } + + /* This register must be set for MT7530 to properly fire interrupts */ +- if (priv->id != ID_MT7531) ++ if (priv->id == ID_MT7530 || priv->id == ID_MT7621) + mt7530_set(priv, MT7530_TOP_SIG_CTRL, TOP_SIG_CTRL_NORMAL); + + ret = request_threaded_irq(priv->irq, NULL, mt7530_irq_thread_fn, diff --git a/target/linux/generic/backport-6.1/790-39-v6.9-net-dsa-mt7530-do-not-use-SW_PHY_RST-to-reset-MT7531.patch b/target/linux/generic/backport-6.1/790-39-v6.9-net-dsa-mt7530-do-not-use-SW_PHY_RST-to-reset-MT7531.patch new file mode 100644 index 000000000..216a78108 --- /dev/null +++ b/target/linux/generic/backport-6.1/790-39-v6.9-net-dsa-mt7530-do-not-use-SW_PHY_RST-to-reset-MT7531.patch @@ -0,0 +1,41 @@ +From 0eb6bc551371070325b6606cc3bed6734ecad87d Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Ar=C4=B1n=C3=A7=20=C3=9CNAL?= +Date: Fri, 1 Mar 2024 12:42:59 +0200 +Subject: [PATCH 39/48] net: dsa: mt7530: do not use SW_PHY_RST to reset MT7531 + switch +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +According to the document MT7531 Reference Manual for Development Board +v1.0, the SW_PHY_RST bit on the SYS_CTRL register doesn't exist for +MT7531. This is likely why forcing link down on all ports is necessary for +MT7531. + +Therefore, do not set SW_PHY_RST on mt7531_setup(). + +Signed-off-by: Arınç ÃœNAL +Signed-off-by: Paolo Abeni +--- + drivers/net/dsa/mt7530.c | 6 ++---- + 1 file changed, 2 insertions(+), 4 deletions(-) + +--- a/drivers/net/dsa/mt7530.c ++++ b/drivers/net/dsa/mt7530.c +@@ -2660,14 +2660,12 @@ mt7531_setup(struct dsa_switch *ds) + val = mt7530_read(priv, MT7531_TOP_SIG_SR); + priv->p5_sgmii = !!(val & PAD_DUAL_SGMII_EN); + +- /* all MACs must be forced link-down before sw reset */ ++ /* Force link down on all ports before internal reset */ + for (i = 0; i < MT7530_NUM_PORTS; i++) + mt7530_write(priv, MT7530_PMCR_P(i), MT7531_FORCE_LNK); + + /* Reset the switch through internal reset */ +- mt7530_write(priv, MT7530_SYS_CTRL, +- SYS_CTRL_PHY_RST | SYS_CTRL_SW_RST | +- SYS_CTRL_REG_RST); ++ mt7530_write(priv, MT7530_SYS_CTRL, SYS_CTRL_SW_RST | SYS_CTRL_REG_RST); + + if (!priv->p5_sgmii) { + mt7531_pll_setup(priv); diff --git a/target/linux/generic/backport-6.1/790-40-v6.9-net-dsa-mt7530-get-rid-of-useless-error-returns-on-p.patch b/target/linux/generic/backport-6.1/790-40-v6.9-net-dsa-mt7530-get-rid-of-useless-error-returns-on-p.patch new file mode 100644 index 000000000..f920a6604 --- /dev/null +++ b/target/linux/generic/backport-6.1/790-40-v6.9-net-dsa-mt7530-get-rid-of-useless-error-returns-on-p.patch @@ -0,0 +1,217 @@ +From bb20b1b4d832de4eb98ec7c22906db7c04e3f7c5 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Ar=C4=B1n=C3=A7=20=C3=9CNAL?= +Date: Fri, 1 Mar 2024 12:43:00 +0200 +Subject: [PATCH 40/48] net: dsa: mt7530: get rid of useless error returns on + phylink code path +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Remove error returns on the cases where they are already handled with the +function the mac_port_get_caps member in mt753x_table points to. + +mt7531_mac_config() is also called from mt7531_cpu_port_config() outside of +phylink but the port and interface modes are already handled there. + +Change the functions and the mac_port_config function pointer to void now +that there're no error returns anymore. + +Remove mt753x_is_mac_port() that used to help the said error returns. + +On mt7531_mac_config(), switch to if statements to simplify the code. + +Remove internal phy cases from mt753x_phylink_mac_config(), there is no +need to check the interface mode as that's already handled with the +function the mac_port_get_caps member in mt753x_table points to. + +Acked-by: Daniel Golle +Tested-by: Daniel Golle +Signed-off-by: Arınç ÃœNAL +Reviewed-by: Russell King (Oracle) +Signed-off-by: Paolo Abeni +--- + drivers/net/dsa/mt7530.c | 81 ++++++++-------------------------------- + drivers/net/dsa/mt7530.h | 6 +-- + 2 files changed, 19 insertions(+), 68 deletions(-) + +--- a/drivers/net/dsa/mt7530.c ++++ b/drivers/net/dsa/mt7530.c +@@ -2803,7 +2803,7 @@ static void mt7988_mac_port_get_caps(str + } + } + +-static int ++static void + mt7530_mac_config(struct dsa_switch *ds, int port, unsigned int mode, + phy_interface_t interface) + { +@@ -2813,22 +2813,14 @@ mt7530_mac_config(struct dsa_switch *ds, + mt7530_setup_port5(priv->ds, interface); + else if (port == 6) + mt7530_setup_port6(priv->ds, interface); +- +- return 0; + } + +-static int mt7531_rgmii_setup(struct mt7530_priv *priv, u32 port, +- phy_interface_t interface, +- struct phy_device *phydev) ++static void mt7531_rgmii_setup(struct mt7530_priv *priv, u32 port, ++ phy_interface_t interface, ++ struct phy_device *phydev) + { + u32 val; + +- if (priv->p5_sgmii) { +- dev_err(priv->dev, "RGMII mode is not available for port %d\n", +- port); +- return -EINVAL; +- } +- + val = mt7530_read(priv, MT7531_CLKGEN_CTRL); + val |= GP_CLK_EN; + val &= ~GP_MODE_MASK; +@@ -2856,20 +2848,14 @@ static int mt7531_rgmii_setup(struct mt7 + case PHY_INTERFACE_MODE_RGMII_ID: + break; + default: +- return -EINVAL; ++ break; + } + } +- mt7530_write(priv, MT7531_CLKGEN_CTRL, val); + +- return 0; +-} +- +-static bool mt753x_is_mac_port(u32 port) +-{ +- return (port == 5 || port == 6); ++ mt7530_write(priv, MT7531_CLKGEN_CTRL, val); + } + +-static int ++static void + mt7531_mac_config(struct dsa_switch *ds, int port, unsigned int mode, + phy_interface_t interface) + { +@@ -2877,42 +2863,21 @@ mt7531_mac_config(struct dsa_switch *ds, + struct phy_device *phydev; + struct dsa_port *dp; + +- if (!mt753x_is_mac_port(port)) { +- dev_err(priv->dev, "port %d is not a MAC port\n", port); +- return -EINVAL; +- } +- +- switch (interface) { +- case PHY_INTERFACE_MODE_RGMII: +- case PHY_INTERFACE_MODE_RGMII_ID: +- case PHY_INTERFACE_MODE_RGMII_RXID: +- case PHY_INTERFACE_MODE_RGMII_TXID: ++ if (phy_interface_mode_is_rgmii(interface)) { + dp = dsa_to_port(ds, port); + phydev = dp->slave->phydev; +- return mt7531_rgmii_setup(priv, port, interface, phydev); +- case PHY_INTERFACE_MODE_SGMII: +- case PHY_INTERFACE_MODE_NA: +- case PHY_INTERFACE_MODE_1000BASEX: +- case PHY_INTERFACE_MODE_2500BASEX: +- /* handled in SGMII PCS driver */ +- return 0; +- default: +- return -EINVAL; ++ mt7531_rgmii_setup(priv, port, interface, phydev); + } +- +- return -EINVAL; + } + +-static int ++static void + mt753x_mac_config(struct dsa_switch *ds, int port, unsigned int mode, + const struct phylink_link_state *state) + { + struct mt7530_priv *priv = ds->priv; + +- if (!priv->info->mac_port_config) +- return 0; +- +- return priv->info->mac_port_config(ds, port, mode, state->interface); ++ if (priv->info->mac_port_config) ++ priv->info->mac_port_config(ds, port, mode, state->interface); + } + + static struct phylink_pcs * +@@ -2941,17 +2906,11 @@ mt753x_phylink_mac_config(struct dsa_swi + u32 mcr_cur, mcr_new; + + switch (port) { +- case 0 ... 4: +- if (state->interface != PHY_INTERFACE_MODE_GMII && +- state->interface != PHY_INTERFACE_MODE_INTERNAL) +- goto unsupported; +- break; + case 5: + if (priv->p5_interface == state->interface) + break; + +- if (mt753x_mac_config(ds, port, mode, state) < 0) +- goto unsupported; ++ mt753x_mac_config(ds, port, mode, state); + + if (priv->p5_intf_sel != P5_DISABLED) + priv->p5_interface = state->interface; +@@ -2960,16 +2919,10 @@ mt753x_phylink_mac_config(struct dsa_swi + if (priv->p6_interface == state->interface) + break; + +- if (mt753x_mac_config(ds, port, mode, state) < 0) +- goto unsupported; ++ mt753x_mac_config(ds, port, mode, state); + + priv->p6_interface = state->interface; + break; +- default: +-unsupported: +- dev_err(ds->dev, "%s: unsupported %s port: %i\n", +- __func__, phy_modes(state->interface), port); +- return; + } + + mcr_cur = mt7530_read(priv, MT7530_PMCR_P(port)); +@@ -3052,7 +3005,6 @@ mt7531_cpu_port_config(struct dsa_switch + struct mt7530_priv *priv = ds->priv; + phy_interface_t interface; + int speed; +- int ret; + + switch (port) { + case 5: +@@ -3077,9 +3029,8 @@ mt7531_cpu_port_config(struct dsa_switch + else + speed = SPEED_1000; + +- ret = mt7531_mac_config(ds, port, MLO_AN_FIXED, interface); +- if (ret) +- return ret; ++ mt7531_mac_config(ds, port, MLO_AN_FIXED, interface); ++ + mt7530_write(priv, MT7530_PMCR_P(port), + PMCR_CPU_PORT_SETTING(priv->id)); + mt753x_phylink_mac_link_up(ds, port, MLO_AN_FIXED, interface, NULL, +--- a/drivers/net/dsa/mt7530.h ++++ b/drivers/net/dsa/mt7530.h +@@ -753,9 +753,9 @@ struct mt753x_info { + void (*mac_port_validate)(struct dsa_switch *ds, int port, + phy_interface_t interface, + unsigned long *supported); +- int (*mac_port_config)(struct dsa_switch *ds, int port, +- unsigned int mode, +- phy_interface_t interface); ++ void (*mac_port_config)(struct dsa_switch *ds, int port, ++ unsigned int mode, ++ phy_interface_t interface); + }; + + /* struct mt7530_priv - This is the main data structure for holding the state diff --git a/target/linux/generic/backport-6.1/790-41-v6.9-net-dsa-mt7530-get-rid-of-priv-info-cpu_port_config.patch b/target/linux/generic/backport-6.1/790-41-v6.9-net-dsa-mt7530-get-rid-of-priv-info-cpu_port_config.patch new file mode 100644 index 000000000..eb0a5706e --- /dev/null +++ b/target/linux/generic/backport-6.1/790-41-v6.9-net-dsa-mt7530-get-rid-of-priv-info-cpu_port_config.patch @@ -0,0 +1,305 @@ +From 8554f6a7914d28b179671540f527897d85c88809 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Ar=C4=B1n=C3=A7=20=C3=9CNAL?= +Date: Fri, 1 Mar 2024 12:43:01 +0200 +Subject: [PATCH 41/48] net: dsa: mt7530: get rid of + priv->info->cpu_port_config() +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +priv->info->cpu_port_config() is used for MT7531 and the switch on the +MT7988 SoC. It sets up the ports described as a CPU port earlier than the +phylink code path would do. + +This function is useless as: +- Configuring the MACs can be done from the phylink_mac_config code path + instead. +- All the link configuration it does on the CPU ports are later undone with + the port_enable, phylink_mac_config, and then phylink_mac_link_up code + path [1]. + +priv->p5_interface and priv->p6_interface were being used to prevent +configuring the MACs from the phylink_mac_config code path. Remove them now +that they hold no purpose. + +Remove priv->info->cpu_port_config(). On mt753x_phylink_mac_config, switch +to if statements to simplify the code. + +Remove the overwriting of the speed and duplex interfaces for certain +interface modes. Phylink already provides the speed and duplex variables +with proper values. Phylink already sets the max speed of TRGMII to +SPEED_1000. Add SPEED_2500 for PHY_INTERFACE_MODE_2500BASEX to where the +speed and EEE bits are set instead. + +On the switch on the MT7988 SoC, PHY_INTERFACE_MODE_INTERNAL is being used +to describe the interface mode of the 10G MAC, which is of port 6. On +mt7988_cpu_port_config() PMCR_FORCE_SPEED_1000 was set via the +PMCR_CPU_PORT_SETTING() mask. Add SPEED_10000 case to where the speed bits +are set to cover this. No need to add it to where the EEE bits are set as +the "MT7988A Wi-Fi 7 Generation Router Platform: Datasheet (Open Version) +v0.1" document shows that these bits don't exist on the MT7530_PMCR_P(6) +register. + +Remove the definition of PMCR_CPU_PORT_SETTING() now that it holds no +purpose. + +Change mt753x_cpu_port_enable() to void now that there're no error cases +left. + +Link: https://lore.kernel.org/netdev/ZHy2jQLesdYFMQtO@shell.armlinux.org.uk/ [1] +Suggested-by: Russell King (Oracle) +Signed-off-by: Arınç ÃœNAL +Signed-off-by: Paolo Abeni +--- + drivers/net/dsa/mt7530.c | 114 +++------------------------------------ + drivers/net/dsa/mt7530.h | 11 ---- + 2 files changed, 7 insertions(+), 118 deletions(-) + +--- a/drivers/net/dsa/mt7530.c ++++ b/drivers/net/dsa/mt7530.c +@@ -1170,18 +1170,10 @@ mt753x_trap_frames(struct mt7530_priv *p + MT753X_BPDU_CPU_ONLY); + } + +-static int ++static void + mt753x_cpu_port_enable(struct dsa_switch *ds, int port) + { + struct mt7530_priv *priv = ds->priv; +- int ret; +- +- /* Setup max capability of CPU port at first */ +- if (priv->info->cpu_port_config) { +- ret = priv->info->cpu_port_config(ds, port); +- if (ret) +- return ret; +- } + + /* Enable Mediatek header mode on the cpu port */ + mt7530_write(priv, MT7530_PVC_P(port), +@@ -1207,8 +1199,6 @@ mt753x_cpu_port_enable(struct dsa_switch + /* Set to fallback mode for independent VLAN learning */ + mt7530_rmw(priv, MT7530_PCR_P(port), PCR_PORT_VLAN_MASK, + MT7530_PORT_FALLBACK_MODE); +- +- return 0; + } + + static int +@@ -2461,8 +2451,6 @@ mt7530_setup(struct dsa_switch *ds) + val |= MHWTRAP_MANUAL; + mt7530_write(priv, MT7530_MHWTRAP, val); + +- priv->p6_interface = PHY_INTERFACE_MODE_NA; +- + if ((val & HWTRAP_XTAL_MASK) == HWTRAP_XTAL_40MHZ) + mt7530_pll_setup(priv); + +@@ -2480,9 +2468,7 @@ mt7530_setup(struct dsa_switch *ds) + mt7530_set(priv, MT7530_PSC_P(i), SA_DIS); + + if (dsa_is_cpu_port(ds, i)) { +- ret = mt753x_cpu_port_enable(ds, i); +- if (ret) +- return ret; ++ mt753x_cpu_port_enable(ds, i); + } else { + mt7530_port_disable(ds, i); + +@@ -2589,9 +2575,7 @@ mt7531_setup_common(struct dsa_switch *d + mt7530_set(priv, MT7531_DBG_CNT(i), MT7531_DIS_CLR); + + if (dsa_is_cpu_port(ds, i)) { +- ret = mt753x_cpu_port_enable(ds, i); +- if (ret) +- return ret; ++ mt753x_cpu_port_enable(ds, i); + } else { + mt7530_port_disable(ds, i); + +@@ -2683,10 +2667,6 @@ mt7531_setup(struct dsa_switch *ds) + mt7530_rmw(priv, MT7531_GPIO_MODE0, MT7531_GPIO0_MASK, + MT7531_GPIO0_INTERRUPT); + +- /* Let phylink decide the interface later. */ +- priv->p5_interface = PHY_INTERFACE_MODE_NA; +- priv->p6_interface = PHY_INTERFACE_MODE_NA; +- + /* Enable Energy-Efficient Ethernet (EEE) and PHY core PLL, since + * phy_device has not yet been created provided for + * phy_[read,write]_mmd_indirect is called, we provide our own +@@ -2905,26 +2885,9 @@ mt753x_phylink_mac_config(struct dsa_swi + struct mt7530_priv *priv = ds->priv; + u32 mcr_cur, mcr_new; + +- switch (port) { +- case 5: +- if (priv->p5_interface == state->interface) +- break; +- ++ if (port == 5 || port == 6) + mt753x_mac_config(ds, port, mode, state); + +- if (priv->p5_intf_sel != P5_DISABLED) +- priv->p5_interface = state->interface; +- break; +- case 6: +- if (priv->p6_interface == state->interface) +- break; +- +- mt753x_mac_config(ds, port, mode, state); +- +- priv->p6_interface = state->interface; +- break; +- } +- + mcr_cur = mt7530_read(priv, MT7530_PMCR_P(port)); + mcr_new = mcr_cur; + mcr_new &= ~PMCR_LINK_SETTINGS_MASK; +@@ -2960,17 +2923,10 @@ static void mt753x_phylink_mac_link_up(s + + mcr = PMCR_RX_EN | PMCR_TX_EN | PMCR_FORCE_LNK; + +- /* MT753x MAC works in 1G full duplex mode for all up-clocked +- * variants. +- */ +- if (interface == PHY_INTERFACE_MODE_TRGMII || +- (phy_interface_mode_is_8023z(interface))) { +- speed = SPEED_1000; +- duplex = DUPLEX_FULL; +- } +- + switch (speed) { + case SPEED_1000: ++ case SPEED_2500: ++ case SPEED_10000: + mcr |= PMCR_FORCE_SPEED_1000; + break; + case SPEED_100: +@@ -2988,6 +2944,7 @@ static void mt753x_phylink_mac_link_up(s + if (mode == MLO_AN_PHY && phydev && phy_init_eee(phydev, false) >= 0) { + switch (speed) { + case SPEED_1000: ++ case SPEED_2500: + mcr |= PMCR_FORCE_EEE1G; + break; + case SPEED_100: +@@ -2999,61 +2956,6 @@ static void mt753x_phylink_mac_link_up(s + mt7530_set(priv, MT7530_PMCR_P(port), mcr); + } + +-static int +-mt7531_cpu_port_config(struct dsa_switch *ds, int port) +-{ +- struct mt7530_priv *priv = ds->priv; +- phy_interface_t interface; +- int speed; +- +- switch (port) { +- case 5: +- if (!priv->p5_sgmii) +- interface = PHY_INTERFACE_MODE_RGMII; +- else +- interface = PHY_INTERFACE_MODE_2500BASEX; +- +- priv->p5_interface = interface; +- break; +- case 6: +- interface = PHY_INTERFACE_MODE_2500BASEX; +- +- priv->p6_interface = interface; +- break; +- default: +- return -EINVAL; +- } +- +- if (interface == PHY_INTERFACE_MODE_2500BASEX) +- speed = SPEED_2500; +- else +- speed = SPEED_1000; +- +- mt7531_mac_config(ds, port, MLO_AN_FIXED, interface); +- +- mt7530_write(priv, MT7530_PMCR_P(port), +- PMCR_CPU_PORT_SETTING(priv->id)); +- mt753x_phylink_mac_link_up(ds, port, MLO_AN_FIXED, interface, NULL, +- speed, DUPLEX_FULL, true, true); +- +- return 0; +-} +- +-static int +-mt7988_cpu_port_config(struct dsa_switch *ds, int port) +-{ +- struct mt7530_priv *priv = ds->priv; +- +- mt7530_write(priv, MT7530_PMCR_P(port), +- PMCR_CPU_PORT_SETTING(priv->id)); +- +- mt753x_phylink_mac_link_up(ds, port, MLO_AN_FIXED, +- PHY_INTERFACE_MODE_INTERNAL, NULL, +- SPEED_10000, DUPLEX_FULL, true, true); +- +- return 0; +-} +- + static void mt753x_phylink_get_caps(struct dsa_switch *ds, int port, + struct phylink_config *config) + { +@@ -3312,7 +3214,6 @@ const struct mt753x_info mt753x_table[] + .sw_setup = mt7531_setup, + .phy_read = mt7531_ind_phy_read, + .phy_write = mt7531_ind_phy_write, +- .cpu_port_config = mt7531_cpu_port_config, + .mac_port_get_caps = mt7531_mac_port_get_caps, + .mac_port_config = mt7531_mac_config, + }, +@@ -3322,7 +3223,6 @@ const struct mt753x_info mt753x_table[] + .sw_setup = mt7988_setup, + .phy_read = mt7531_ind_phy_read, + .phy_write = mt7531_ind_phy_write, +- .cpu_port_config = mt7988_cpu_port_config, + .mac_port_get_caps = mt7988_mac_port_get_caps, + }, + }; +--- a/drivers/net/dsa/mt7530.h ++++ b/drivers/net/dsa/mt7530.h +@@ -340,13 +340,6 @@ enum mt7530_vlan_port_acc_frm { + PMCR_TX_FC_EN | PMCR_RX_FC_EN | \ + PMCR_FORCE_FDX | PMCR_FORCE_LNK | \ + PMCR_FORCE_EEE1G | PMCR_FORCE_EEE100) +-#define PMCR_CPU_PORT_SETTING(id) (PMCR_FORCE_MODE_ID((id)) | \ +- PMCR_IFG_XMIT(1) | PMCR_MAC_MODE | \ +- PMCR_BACKOFF_EN | PMCR_BACKPR_EN | \ +- PMCR_TX_EN | PMCR_RX_EN | \ +- PMCR_TX_FC_EN | PMCR_RX_FC_EN | \ +- PMCR_FORCE_SPEED_1000 | \ +- PMCR_FORCE_FDX | PMCR_FORCE_LNK) + + #define MT7530_PMEEECR_P(x) (0x3004 + (x) * 0x100) + #define WAKEUP_TIME_1000(x) (((x) & 0xFF) << 24) +@@ -747,7 +740,6 @@ struct mt753x_info { + int (*sw_setup)(struct dsa_switch *ds); + int (*phy_read)(struct mt7530_priv *priv, int port, int regnum); + int (*phy_write)(struct mt7530_priv *priv, int port, int regnum, u16 val); +- int (*cpu_port_config)(struct dsa_switch *ds, int port); + void (*mac_port_get_caps)(struct dsa_switch *ds, int port, + struct phylink_config *config); + void (*mac_port_validate)(struct dsa_switch *ds, int port, +@@ -773,7 +765,6 @@ struct mt753x_info { + * @ports: Holding the state among ports + * @reg_mutex: The lock for protecting among process accessing + * registers +- * @p6_interface Holding the current port 6 interface + * @p5_intf_sel: Holding the current port 5 interface select + * @p5_sgmii: Flag for distinguishing if port 5 of the MT7531 switch + * has got SGMII +@@ -795,8 +786,6 @@ struct mt7530_priv { + const struct mt753x_info *info; + unsigned int id; + bool mcm; +- phy_interface_t p6_interface; +- phy_interface_t p5_interface; + enum p5_interface_select p5_intf_sel; + bool p5_sgmii; + u8 mirror_rx; diff --git a/target/linux/generic/backport-6.1/790-42-v6.9-net-dsa-mt7530-get-rid-of-mt753x_mac_config.patch b/target/linux/generic/backport-6.1/790-42-v6.9-net-dsa-mt7530-get-rid-of-mt753x_mac_config.patch new file mode 100644 index 000000000..c83a62780 --- /dev/null +++ b/target/linux/generic/backport-6.1/790-42-v6.9-net-dsa-mt7530-get-rid-of-mt753x_mac_config.patch @@ -0,0 +1,48 @@ +From 0c282205ef8c6dd6d2c145fac1fb6aba3e65c02d Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Ar=C4=B1n=C3=A7=20=C3=9CNAL?= +Date: Fri, 1 Mar 2024 12:43:02 +0200 +Subject: [PATCH 42/48] net: dsa: mt7530: get rid of mt753x_mac_config() +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +There is no need for a separate function to call +priv->info->mac_port_config(). Call it from mt753x_phylink_mac_config() +instead and remove mt753x_mac_config(). + +Signed-off-by: Arınç ÃœNAL +Signed-off-by: Paolo Abeni +--- + drivers/net/dsa/mt7530.c | 14 ++------------ + 1 file changed, 2 insertions(+), 12 deletions(-) + +--- a/drivers/net/dsa/mt7530.c ++++ b/drivers/net/dsa/mt7530.c +@@ -2850,16 +2850,6 @@ mt7531_mac_config(struct dsa_switch *ds, + } + } + +-static void +-mt753x_mac_config(struct dsa_switch *ds, int port, unsigned int mode, +- const struct phylink_link_state *state) +-{ +- struct mt7530_priv *priv = ds->priv; +- +- if (priv->info->mac_port_config) +- priv->info->mac_port_config(ds, port, mode, state->interface); +-} +- + static struct phylink_pcs * + mt753x_phylink_mac_select_pcs(struct dsa_switch *ds, int port, + phy_interface_t interface) +@@ -2885,8 +2875,8 @@ mt753x_phylink_mac_config(struct dsa_swi + struct mt7530_priv *priv = ds->priv; + u32 mcr_cur, mcr_new; + +- if (port == 5 || port == 6) +- mt753x_mac_config(ds, port, mode, state); ++ if ((port == 5 || port == 6) && priv->info->mac_port_config) ++ priv->info->mac_port_config(ds, port, mode, state->interface); + + mcr_cur = mt7530_read(priv, MT7530_PMCR_P(port)); + mcr_new = mcr_cur; diff --git a/target/linux/generic/backport-6.1/790-43-v6.9-net-dsa-mt7530-put-initialising-PCS-devices-code-bac.patch b/target/linux/generic/backport-6.1/790-43-v6.9-net-dsa-mt7530-put-initialising-PCS-devices-code-bac.patch new file mode 100644 index 000000000..84ee7416b --- /dev/null +++ b/target/linux/generic/backport-6.1/790-43-v6.9-net-dsa-mt7530-put-initialising-PCS-devices-code-bac.patch @@ -0,0 +1,57 @@ +From d55c300aa1fe240aa3eba18550ba6c4e2c4bd157 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Ar=C4=B1n=C3=A7=20=C3=9CNAL?= +Date: Fri, 1 Mar 2024 12:43:03 +0200 +Subject: [PATCH 43/48] net: dsa: mt7530: put initialising PCS devices code + back to original order +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +The commit fae463084032 ("net: dsa: mt753x: fix pcs conversion regression") +fixes regression caused by cpu_port_config manually calling phylink +operations. cpu_port_config was deemed useless and was removed. Therefore, +put initialising PCS devices code back to its original order. + +Signed-off-by: Arınç ÃœNAL +Signed-off-by: Paolo Abeni +--- + drivers/net/dsa/mt7530.c | 20 ++++++++++---------- + 1 file changed, 10 insertions(+), 10 deletions(-) + +--- a/drivers/net/dsa/mt7530.c ++++ b/drivers/net/dsa/mt7530.c +@@ -3034,17 +3034,9 @@ static int + mt753x_setup(struct dsa_switch *ds) + { + struct mt7530_priv *priv = ds->priv; +- int i, ret; ++ int ret = priv->info->sw_setup(ds); ++ int i; + +- /* Initialise the PCS devices */ +- for (i = 0; i < priv->ds->num_ports; i++) { +- priv->pcs[i].pcs.ops = priv->info->pcs_ops; +- priv->pcs[i].pcs.neg_mode = true; +- priv->pcs[i].priv = priv; +- priv->pcs[i].port = i; +- } +- +- ret = priv->info->sw_setup(ds); + if (ret) + return ret; + +@@ -3056,6 +3048,14 @@ mt753x_setup(struct dsa_switch *ds) + if (ret && priv->irq) + mt7530_free_irq_common(priv); + ++ /* Initialise the PCS devices */ ++ for (i = 0; i < priv->ds->num_ports; i++) { ++ priv->pcs[i].pcs.ops = priv->info->pcs_ops; ++ priv->pcs[i].pcs.neg_mode = true; ++ priv->pcs[i].priv = priv; ++ priv->pcs[i].port = i; ++ } ++ + if (priv->create_sgmii) { + ret = priv->create_sgmii(priv); + if (ret && priv->irq) diff --git a/target/linux/generic/backport-6.1/790-44-v6.9-net-dsa-mt7530-sort-link-settings-ops-and-force-link.patch b/target/linux/generic/backport-6.1/790-44-v6.9-net-dsa-mt7530-sort-link-settings-ops-and-force-link.patch new file mode 100644 index 000000000..814a9d06e --- /dev/null +++ b/target/linux/generic/backport-6.1/790-44-v6.9-net-dsa-mt7530-sort-link-settings-ops-and-force-link.patch @@ -0,0 +1,68 @@ +From a9544caa482a7ed215117a902f04185216997831 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Ar=C4=B1n=C3=A7=20=C3=9CNAL?= +Date: Fri, 1 Mar 2024 12:43:04 +0200 +Subject: [PATCH 44/48] net: dsa: mt7530: sort link settings ops and force link + down on all ports +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +port_enable and port_disable clears the link settings. Move that to +mt7530_setup() and mt7531_setup_common() which set up the switches. This +way, the link settings are cleared on all ports at setup, and then only +once with phylink_mac_link_down() when a link goes down. + +Enable force mode at setup to apply the force part of the link settings. +This ensures that disabled ports will have their link down. + +Suggested-by: Vladimir Oltean +Signed-off-by: Arınç ÃœNAL +Signed-off-by: Paolo Abeni +--- + drivers/net/dsa/mt7530.c | 14 ++++++++++++-- + 1 file changed, 12 insertions(+), 2 deletions(-) + +--- a/drivers/net/dsa/mt7530.c ++++ b/drivers/net/dsa/mt7530.c +@@ -1222,7 +1222,6 @@ mt7530_port_enable(struct dsa_switch *ds + priv->ports[port].enable = true; + mt7530_rmw(priv, MT7530_PCR_P(port), PCR_MATRIX_MASK, + priv->ports[port].pm); +- mt7530_clear(priv, MT7530_PMCR_P(port), PMCR_LINK_SETTINGS_MASK); + + mutex_unlock(&priv->reg_mutex); + +@@ -1242,7 +1241,6 @@ mt7530_port_disable(struct dsa_switch *d + priv->ports[port].enable = false; + mt7530_rmw(priv, MT7530_PCR_P(port), PCR_MATRIX_MASK, + PCR_MATRIX_CLR); +- mt7530_clear(priv, MT7530_PMCR_P(port), PMCR_LINK_SETTINGS_MASK); + + mutex_unlock(&priv->reg_mutex); + } +@@ -2460,6 +2458,12 @@ mt7530_setup(struct dsa_switch *ds) + mt7530_mib_reset(ds); + + for (i = 0; i < MT7530_NUM_PORTS; i++) { ++ /* Clear link settings and enable force mode to force link down ++ * on all ports until they're enabled later. ++ */ ++ mt7530_rmw(priv, MT7530_PMCR_P(i), PMCR_LINK_SETTINGS_MASK | ++ PMCR_FORCE_MODE, PMCR_FORCE_MODE); ++ + /* Disable forwarding by default on all ports */ + mt7530_rmw(priv, MT7530_PCR_P(i), PCR_MATRIX_MASK, + PCR_MATRIX_CLR); +@@ -2565,6 +2569,12 @@ mt7531_setup_common(struct dsa_switch *d + UNU_FFP_MASK); + + for (i = 0; i < MT7530_NUM_PORTS; i++) { ++ /* Clear link settings and enable force mode to force link down ++ * on all ports until they're enabled later. ++ */ ++ mt7530_rmw(priv, MT7530_PMCR_P(i), PMCR_LINK_SETTINGS_MASK | ++ MT7531_FORCE_MODE, MT7531_FORCE_MODE); ++ + /* Disable forwarding by default on all ports */ + mt7530_rmw(priv, MT7530_PCR_P(i), PCR_MATRIX_MASK, + PCR_MATRIX_CLR); diff --git a/target/linux/generic/backport-6.1/790-45-v6.9-net-dsa-mt7530-simplify-link-operations.patch b/target/linux/generic/backport-6.1/790-45-v6.9-net-dsa-mt7530-simplify-link-operations.patch new file mode 100644 index 000000000..d9f91a932 --- /dev/null +++ b/target/linux/generic/backport-6.1/790-45-v6.9-net-dsa-mt7530-simplify-link-operations.patch @@ -0,0 +1,83 @@ +From 89017cac5f6fbaab23955818c31b3c7f1eb26f4a Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Ar=C4=B1n=C3=A7=20=C3=9CNAL?= +Date: Fri, 1 Mar 2024 12:43:05 +0200 +Subject: [PATCH 45/48] net: dsa: mt7530: simplify link operations +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +The "MT7621 Giga Switch Programming Guide v0.3", "MT7531 Reference Manual +for Development Board v1.0", and "MT7988A Wi-Fi 7 Generation Router +Platform: Datasheet (Open Version) v0.1" documents show that these bits are +enabled at reset: + +PMCR_IFG_XMIT(1) (not part of PMCR_LINK_SETTINGS_MASK) +PMCR_MAC_MODE (not part of PMCR_LINK_SETTINGS_MASK) +PMCR_TX_EN +PMCR_RX_EN +PMCR_BACKOFF_EN (not part of PMCR_LINK_SETTINGS_MASK) +PMCR_BACKPR_EN (not part of PMCR_LINK_SETTINGS_MASK) +PMCR_TX_FC_EN +PMCR_RX_FC_EN + +These bits also don't exist on the MT7530_PMCR_P(6) register of the switch +on the MT7988 SoC: + +PMCR_IFG_XMIT() +PMCR_MAC_MODE +PMCR_BACKOFF_EN +PMCR_BACKPR_EN + +Remove the setting of the bits not part of PMCR_LINK_SETTINGS_MASK on +phylink_mac_config as they're already set. + +The bit for setting the port on force mode is already done on +mt7530_setup() and mt7531_setup_common(). So get rid of +PMCR_FORCE_MODE_ID() which helped determine which bit to use for the switch +model. + +Signed-off-by: Arınç ÃœNAL +Signed-off-by: Paolo Abeni +--- + drivers/net/dsa/mt7530.c | 12 +----------- + drivers/net/dsa/mt7530.h | 2 -- + 2 files changed, 1 insertion(+), 13 deletions(-) + +--- a/drivers/net/dsa/mt7530.c ++++ b/drivers/net/dsa/mt7530.c +@@ -2883,23 +2883,13 @@ mt753x_phylink_mac_config(struct dsa_swi + const struct phylink_link_state *state) + { + struct mt7530_priv *priv = ds->priv; +- u32 mcr_cur, mcr_new; + + if ((port == 5 || port == 6) && priv->info->mac_port_config) + priv->info->mac_port_config(ds, port, mode, state->interface); + +- mcr_cur = mt7530_read(priv, MT7530_PMCR_P(port)); +- mcr_new = mcr_cur; +- mcr_new &= ~PMCR_LINK_SETTINGS_MASK; +- mcr_new |= PMCR_IFG_XMIT(1) | PMCR_MAC_MODE | PMCR_BACKOFF_EN | +- PMCR_BACKPR_EN | PMCR_FORCE_MODE_ID(priv->id); +- + /* Are we connected to external phy */ + if (port == 5 && dsa_is_user_port(ds, 5)) +- mcr_new |= PMCR_EXT_PHY; +- +- if (mcr_new != mcr_cur) +- mt7530_write(priv, MT7530_PMCR_P(port), mcr_new); ++ mt7530_set(priv, MT7530_PMCR_P(port), PMCR_EXT_PHY); + } + + static void mt753x_phylink_mac_link_down(struct dsa_switch *ds, int port, +--- a/drivers/net/dsa/mt7530.h ++++ b/drivers/net/dsa/mt7530.h +@@ -333,8 +333,6 @@ enum mt7530_vlan_port_acc_frm { + MT7531_FORCE_DPX | \ + MT7531_FORCE_RX_FC | \ + MT7531_FORCE_TX_FC) +-#define PMCR_FORCE_MODE_ID(id) ((((id) == ID_MT7531) || ((id) == ID_MT7988)) ? \ +- MT7531_FORCE_MODE : PMCR_FORCE_MODE) + #define PMCR_LINK_SETTINGS_MASK (PMCR_TX_EN | PMCR_FORCE_SPEED_1000 | \ + PMCR_RX_EN | PMCR_FORCE_SPEED_100 | \ + PMCR_TX_FC_EN | PMCR_RX_FC_EN | \ diff --git a/target/linux/generic/backport-6.1/790-49-v6.10-net-dsa-mt7530-provide-own-phylink-MAC-operations.patch b/target/linux/generic/backport-6.1/790-49-v6.10-net-dsa-mt7530-provide-own-phylink-MAC-operations.patch new file mode 100644 index 000000000..8962e560f --- /dev/null +++ b/target/linux/generic/backport-6.1/790-49-v6.10-net-dsa-mt7530-provide-own-phylink-MAC-operations.patch @@ -0,0 +1,135 @@ +From 5754b3bdcd872aa229881b8f07f84a8404c7d72a Mon Sep 17 00:00:00 2001 +From: "Russell King (Oracle)" +Date: Fri, 12 Apr 2024 16:15:34 +0100 +Subject: [PATCH 1/5] net: dsa: mt7530: provide own phylink MAC operations +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Convert mt753x to provide its own phylink MAC operations, thus avoiding +the shim layer in DSA's port.c + +Signed-off-by: Russell King (Oracle) +Tested-by: Arınç ÃœNAL +Link: https://lore.kernel.org/r/E1rvIco-006bQu-Fq@rmk-PC.armlinux.org.uk +Signed-off-by: Paolo Abeni +--- + drivers/net/dsa/mt7530.c | 46 +++++++++++++++++++++++++--------------- + 1 file changed, 29 insertions(+), 17 deletions(-) + +--- a/drivers/net/dsa/mt7530.c ++++ b/drivers/net/dsa/mt7530.c +@@ -2861,28 +2861,34 @@ mt7531_mac_config(struct dsa_switch *ds, + } + + static struct phylink_pcs * +-mt753x_phylink_mac_select_pcs(struct dsa_switch *ds, int port, ++mt753x_phylink_mac_select_pcs(struct phylink_config *config, + phy_interface_t interface) + { +- struct mt7530_priv *priv = ds->priv; ++ struct dsa_port *dp = dsa_phylink_to_port(config); ++ struct mt7530_priv *priv = dp->ds->priv; + + switch (interface) { + case PHY_INTERFACE_MODE_TRGMII: +- return &priv->pcs[port].pcs; ++ return &priv->pcs[dp->index].pcs; + case PHY_INTERFACE_MODE_SGMII: + case PHY_INTERFACE_MODE_1000BASEX: + case PHY_INTERFACE_MODE_2500BASEX: +- return priv->ports[port].sgmii_pcs; ++ return priv->ports[dp->index].sgmii_pcs; + default: + return NULL; + } + } + + static void +-mt753x_phylink_mac_config(struct dsa_switch *ds, int port, unsigned int mode, ++mt753x_phylink_mac_config(struct phylink_config *config, unsigned int mode, + const struct phylink_link_state *state) + { +- struct mt7530_priv *priv = ds->priv; ++ struct dsa_port *dp = dsa_phylink_to_port(config); ++ struct dsa_switch *ds = dp->ds; ++ struct mt7530_priv *priv; ++ int port = dp->index; ++ ++ priv = ds->priv; + + if ((port == 5 || port == 6) && priv->info->mac_port_config) + priv->info->mac_port_config(ds, port, mode, state->interface); +@@ -2892,23 +2898,25 @@ mt753x_phylink_mac_config(struct dsa_swi + mt7530_set(priv, MT7530_PMCR_P(port), PMCR_EXT_PHY); + } + +-static void mt753x_phylink_mac_link_down(struct dsa_switch *ds, int port, ++static void mt753x_phylink_mac_link_down(struct phylink_config *config, + unsigned int mode, + phy_interface_t interface) + { +- struct mt7530_priv *priv = ds->priv; ++ struct dsa_port *dp = dsa_phylink_to_port(config); ++ struct mt7530_priv *priv = dp->ds->priv; + +- mt7530_clear(priv, MT7530_PMCR_P(port), PMCR_LINK_SETTINGS_MASK); ++ mt7530_clear(priv, MT7530_PMCR_P(dp->index), PMCR_LINK_SETTINGS_MASK); + } + +-static void mt753x_phylink_mac_link_up(struct dsa_switch *ds, int port, ++static void mt753x_phylink_mac_link_up(struct phylink_config *config, ++ struct phy_device *phydev, + unsigned int mode, + phy_interface_t interface, +- struct phy_device *phydev, + int speed, int duplex, + bool tx_pause, bool rx_pause) + { +- struct mt7530_priv *priv = ds->priv; ++ struct dsa_port *dp = dsa_phylink_to_port(config); ++ struct mt7530_priv *priv = dp->ds->priv; + u32 mcr; + + mcr = PMCR_RX_EN | PMCR_TX_EN | PMCR_FORCE_LNK; +@@ -2943,7 +2951,7 @@ static void mt753x_phylink_mac_link_up(s + } + } + +- mt7530_set(priv, MT7530_PMCR_P(port), mcr); ++ mt7530_set(priv, MT7530_PMCR_P(dp->index), mcr); + } + + static void mt753x_phylink_get_caps(struct dsa_switch *ds, int port, +@@ -3169,16 +3177,19 @@ const struct dsa_switch_ops mt7530_switc + .port_mirror_add = mt753x_port_mirror_add, + .port_mirror_del = mt753x_port_mirror_del, + .phylink_get_caps = mt753x_phylink_get_caps, +- .phylink_mac_select_pcs = mt753x_phylink_mac_select_pcs, +- .phylink_mac_config = mt753x_phylink_mac_config, +- .phylink_mac_link_down = mt753x_phylink_mac_link_down, +- .phylink_mac_link_up = mt753x_phylink_mac_link_up, + .get_mac_eee = mt753x_get_mac_eee, + .set_mac_eee = mt753x_set_mac_eee, + .master_state_change = mt753x_conduit_state_change, + }; + EXPORT_SYMBOL_GPL(mt7530_switch_ops); + ++static const struct phylink_mac_ops mt753x_phylink_mac_ops = { ++ .mac_select_pcs = mt753x_phylink_mac_select_pcs, ++ .mac_config = mt753x_phylink_mac_config, ++ .mac_link_down = mt753x_phylink_mac_link_down, ++ .mac_link_up = mt753x_phylink_mac_link_up, ++}; ++ + const struct mt753x_info mt753x_table[] = { + [ID_MT7621] = { + .id = ID_MT7621, +@@ -3248,6 +3259,7 @@ mt7530_probe_common(struct mt7530_priv * + priv->dev = dev; + priv->ds->priv = priv; + priv->ds->ops = &mt7530_switch_ops; ++ priv->ds->phylink_mac_ops = &mt753x_phylink_mac_ops; + mutex_init(&priv->reg_mutex); + dev_set_drvdata(dev, priv); + diff --git a/target/linux/generic/backport-6.1/790-51-v6.10-net-dsa-mt7530-fix-port-mirroring-for-MT7988-SoC-swi.patch b/target/linux/generic/backport-6.1/790-51-v6.10-net-dsa-mt7530-fix-port-mirroring-for-MT7988-SoC-swi.patch new file mode 100644 index 000000000..cd5b7b287 --- /dev/null +++ b/target/linux/generic/backport-6.1/790-51-v6.10-net-dsa-mt7530-fix-port-mirroring-for-MT7988-SoC-swi.patch @@ -0,0 +1,49 @@ +From 019a17a5e76940ea86114838d1d638d4dc8d3750 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Ar=C4=B1n=C3=A7=20=C3=9CNAL?= +Date: Sat, 13 Apr 2024 16:01:40 +0300 +Subject: [PATCH 3/5] net: dsa: mt7530: fix port mirroring for MT7988 SoC + switch +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +The "MT7988A Wi-Fi 7 Generation Router Platform: Datasheet (Open Version) +v0.1" document shows bits 16 to 18 as the MIRROR_PORT field of the CPU +forward control register. Currently, the MT7530 DSA subdriver configures +bits 0 to 2 of the CPU forward control register which breaks the port +mirroring feature for the MT7988 SoC switch. + +Fix this by using the MT7531_MIRROR_PORT_GET() and MT7531_MIRROR_PORT_SET() +macros which utilise the correct bits. + +Fixes: 110c18bfed41 ("net: dsa: mt7530: introduce driver for MT7988 built-in switch") +Signed-off-by: Arınç ÃœNAL +Acked-by: Daniel Golle +Signed-off-by: David S. Miller +--- + drivers/net/dsa/mt7530.c | 10 ++++++---- + 1 file changed, 6 insertions(+), 4 deletions(-) + +--- a/drivers/net/dsa/mt7530.c ++++ b/drivers/net/dsa/mt7530.c +@@ -1890,14 +1890,16 @@ mt7530_port_vlan_del(struct dsa_switch * + + static int mt753x_mirror_port_get(unsigned int id, u32 val) + { +- return (id == ID_MT7531) ? MT7531_MIRROR_PORT_GET(val) : +- MIRROR_PORT(val); ++ return (id == ID_MT7531 || id == ID_MT7988) ? ++ MT7531_MIRROR_PORT_GET(val) : ++ MIRROR_PORT(val); + } + + static int mt753x_mirror_port_set(unsigned int id, u32 val) + { +- return (id == ID_MT7531) ? MT7531_MIRROR_PORT_SET(val) : +- MIRROR_PORT(val); ++ return (id == ID_MT7531 || id == ID_MT7988) ? ++ MT7531_MIRROR_PORT_SET(val) : ++ MIRROR_PORT(val); + } + + static int mt753x_port_mirror_add(struct dsa_switch *ds, int port, diff --git a/target/linux/generic/backport-6.1/790-52-v6.10-net-dsa-mt7530-mdio-read-PHY-address-of-switch-from-.patch b/target/linux/generic/backport-6.1/790-52-v6.10-net-dsa-mt7530-mdio-read-PHY-address-of-switch-from-.patch new file mode 100644 index 000000000..e9c8f955c --- /dev/null +++ b/target/linux/generic/backport-6.1/790-52-v6.10-net-dsa-mt7530-mdio-read-PHY-address-of-switch-from-.patch @@ -0,0 +1,238 @@ +From 5053a6cf1d50d785078562470d2a63695a9f3bf2 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Ar=C4=B1n=C3=A7=20=C3=9CNAL?= +Date: Thu, 18 Apr 2024 08:35:30 +0300 +Subject: [PATCH 4/5] net: dsa: mt7530-mdio: read PHY address of switch from + device tree +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Read the PHY address the switch listens on from the reg property of the +switch node on the device tree. This change brings support for MT7530 +switches on boards with such bootstrapping configuration where the switch +listens on a different PHY address than the hardcoded PHY address on the +driver, 31. + +As described on the "MT7621 Programming Guide v0.4" document, the MT7530 +switch and its PHYs can be configured to listen on the range of 7-12, +15-20, 23-28, and 31 and 0-4 PHY addresses. + +There are operations where the switch PHY registers are used. For the PHY +address of the control PHY, transform the MT753X_CTRL_PHY_ADDR constant +into a macro and use it. The PHY address for the control PHY is 0 when the +switch listens on 31. In any other case, it is one greater than the PHY +address the switch listens on. + +Reviewed-by: Daniel Golle +Tested-by: Daniel Golle +Reviewed-by: Florian Fainelli +Signed-off-by: Arınç ÃœNAL +Signed-off-by: Paolo Abeni +--- + drivers/net/dsa/mt7530-mdio.c | 28 +++++++++++++------------- + drivers/net/dsa/mt7530.c | 37 +++++++++++++++++++++++------------ + drivers/net/dsa/mt7530.h | 4 +++- + 3 files changed, 41 insertions(+), 28 deletions(-) + +--- a/drivers/net/dsa/mt7530-mdio.c ++++ b/drivers/net/dsa/mt7530-mdio.c +@@ -18,7 +18,8 @@ + static int + mt7530_regmap_write(void *context, unsigned int reg, unsigned int val) + { +- struct mii_bus *bus = context; ++ struct mt7530_priv *priv = context; ++ struct mii_bus *bus = priv->bus; + u16 page, r, lo, hi; + int ret; + +@@ -27,36 +28,35 @@ mt7530_regmap_write(void *context, unsig + lo = val & 0xffff; + hi = val >> 16; + +- /* MT7530 uses 31 as the pseudo port */ +- ret = bus->write(bus, 0x1f, 0x1f, page); ++ ret = bus->write(bus, priv->mdiodev->addr, 0x1f, page); + if (ret < 0) + return ret; + +- ret = bus->write(bus, 0x1f, r, lo); ++ ret = bus->write(bus, priv->mdiodev->addr, r, lo); + if (ret < 0) + return ret; + +- ret = bus->write(bus, 0x1f, 0x10, hi); ++ ret = bus->write(bus, priv->mdiodev->addr, 0x10, hi); + return ret; + } + + static int + mt7530_regmap_read(void *context, unsigned int reg, unsigned int *val) + { +- struct mii_bus *bus = context; ++ struct mt7530_priv *priv = context; ++ struct mii_bus *bus = priv->bus; + u16 page, r, lo, hi; + int ret; + + page = (reg >> 6) & 0x3ff; + r = (reg >> 2) & 0xf; + +- /* MT7530 uses 31 as the pseudo port */ +- ret = bus->write(bus, 0x1f, 0x1f, page); ++ ret = bus->write(bus, priv->mdiodev->addr, 0x1f, page); + if (ret < 0) + return ret; + +- lo = bus->read(bus, 0x1f, r); +- hi = bus->read(bus, 0x1f, 0x10); ++ lo = bus->read(bus, priv->mdiodev->addr, r); ++ hi = bus->read(bus, priv->mdiodev->addr, 0x10); + + *val = (hi << 16) | (lo & 0xffff); + +@@ -107,8 +107,7 @@ mt7531_create_sgmii(struct mt7530_priv * + mt7531_pcs_config[i]->unlock = mt7530_mdio_regmap_unlock; + mt7531_pcs_config[i]->lock_arg = &priv->bus->mdio_lock; + +- regmap = devm_regmap_init(priv->dev, +- &mt7530_regmap_bus, priv->bus, ++ regmap = devm_regmap_init(priv->dev, &mt7530_regmap_bus, priv, + mt7531_pcs_config[i]); + if (IS_ERR(regmap)) { + ret = PTR_ERR(regmap); +@@ -153,6 +152,7 @@ mt7530_probe(struct mdio_device *mdiodev + + priv->bus = mdiodev->bus; + priv->dev = &mdiodev->dev; ++ priv->mdiodev = mdiodev; + + ret = mt7530_probe_common(priv); + if (ret) +@@ -203,8 +203,8 @@ mt7530_probe(struct mdio_device *mdiodev + regmap_config->reg_stride = 4; + regmap_config->max_register = MT7530_CREV; + regmap_config->disable_locking = true; +- priv->regmap = devm_regmap_init(priv->dev, &mt7530_regmap_bus, +- priv->bus, regmap_config); ++ priv->regmap = devm_regmap_init(priv->dev, &mt7530_regmap_bus, priv, ++ regmap_config); + if (IS_ERR(priv->regmap)) + return PTR_ERR(priv->regmap); + +--- a/drivers/net/dsa/mt7530.c ++++ b/drivers/net/dsa/mt7530.c +@@ -86,22 +86,26 @@ core_read_mmd_indirect(struct mt7530_pri + int value, ret; + + /* Write the desired MMD Devad */ +- ret = bus->write(bus, 0, MII_MMD_CTRL, devad); ++ ret = bus->write(bus, MT753X_CTRL_PHY_ADDR(priv->mdiodev->addr), ++ MII_MMD_CTRL, devad); + if (ret < 0) + goto err; + + /* Write the desired MMD register address */ +- ret = bus->write(bus, 0, MII_MMD_DATA, prtad); ++ ret = bus->write(bus, MT753X_CTRL_PHY_ADDR(priv->mdiodev->addr), ++ MII_MMD_DATA, prtad); + if (ret < 0) + goto err; + + /* Select the Function : DATA with no post increment */ +- ret = bus->write(bus, 0, MII_MMD_CTRL, (devad | MII_MMD_CTRL_NOINCR)); ++ ret = bus->write(bus, MT753X_CTRL_PHY_ADDR(priv->mdiodev->addr), ++ MII_MMD_CTRL, devad | MII_MMD_CTRL_NOINCR); + if (ret < 0) + goto err; + + /* Read the content of the MMD's selected register */ +- value = bus->read(bus, 0, MII_MMD_DATA); ++ value = bus->read(bus, MT753X_CTRL_PHY_ADDR(priv->mdiodev->addr), ++ MII_MMD_DATA); + + return value; + err: +@@ -118,22 +122,26 @@ core_write_mmd_indirect(struct mt7530_pr + int ret; + + /* Write the desired MMD Devad */ +- ret = bus->write(bus, 0, MII_MMD_CTRL, devad); ++ ret = bus->write(bus, MT753X_CTRL_PHY_ADDR(priv->mdiodev->addr), ++ MII_MMD_CTRL, devad); + if (ret < 0) + goto err; + + /* Write the desired MMD register address */ +- ret = bus->write(bus, 0, MII_MMD_DATA, prtad); ++ ret = bus->write(bus, MT753X_CTRL_PHY_ADDR(priv->mdiodev->addr), ++ MII_MMD_DATA, prtad); + if (ret < 0) + goto err; + + /* Select the Function : DATA with no post increment */ +- ret = bus->write(bus, 0, MII_MMD_CTRL, (devad | MII_MMD_CTRL_NOINCR)); ++ ret = bus->write(bus, MT753X_CTRL_PHY_ADDR(priv->mdiodev->addr), ++ MII_MMD_CTRL, devad | MII_MMD_CTRL_NOINCR); + if (ret < 0) + goto err; + + /* Write the data into MMD's selected register */ +- ret = bus->write(bus, 0, MII_MMD_DATA, data); ++ ret = bus->write(bus, MT753X_CTRL_PHY_ADDR(priv->mdiodev->addr), ++ MII_MMD_DATA, data); + err: + if (ret < 0) + dev_err(&bus->dev, +@@ -2684,16 +2692,19 @@ mt7531_setup(struct dsa_switch *ds) + * phy_[read,write]_mmd_indirect is called, we provide our own + * mt7531_ind_mmd_phy_[read,write] to complete this function. + */ +- val = mt7531_ind_c45_phy_read(priv, MT753X_CTRL_PHY_ADDR, ++ val = mt7531_ind_c45_phy_read(priv, ++ MT753X_CTRL_PHY_ADDR(priv->mdiodev->addr), + MDIO_MMD_VEND2, CORE_PLL_GROUP4); + val |= MT7531_RG_SYSPLL_DMY2 | MT7531_PHY_PLL_BYPASS_MODE; + val &= ~MT7531_PHY_PLL_OFF; +- mt7531_ind_c45_phy_write(priv, MT753X_CTRL_PHY_ADDR, MDIO_MMD_VEND2, +- CORE_PLL_GROUP4, val); ++ mt7531_ind_c45_phy_write(priv, ++ MT753X_CTRL_PHY_ADDR(priv->mdiodev->addr), ++ MDIO_MMD_VEND2, CORE_PLL_GROUP4, val); + + /* Disable EEE advertisement on the switch PHYs. */ +- for (i = MT753X_CTRL_PHY_ADDR; +- i < MT753X_CTRL_PHY_ADDR + MT7530_NUM_PHYS; i++) { ++ for (i = MT753X_CTRL_PHY_ADDR(priv->mdiodev->addr); ++ i < MT753X_CTRL_PHY_ADDR(priv->mdiodev->addr) + MT7530_NUM_PHYS; ++ i++) { + mt7531_ind_c45_phy_write(priv, i, MDIO_MMD_AN, MDIO_AN_EEE_ADV, + 0); + } +--- a/drivers/net/dsa/mt7530.h ++++ b/drivers/net/dsa/mt7530.h +@@ -629,7 +629,7 @@ enum mt7531_clk_skew { + #define MT7531_PHY_PLL_OFF BIT(5) + #define MT7531_PHY_PLL_BYPASS_MODE BIT(4) + +-#define MT753X_CTRL_PHY_ADDR 0 ++#define MT753X_CTRL_PHY_ADDR(addr) ((addr + 1) & 0x1f) + + #define CORE_PLL_GROUP5 0x404 + #define RG_LCDDS_PCW_NCPO1(x) ((x) & 0xffff) +@@ -771,6 +771,7 @@ struct mt753x_info { + * @irq_enable: IRQ enable bits, synced to SYS_INT_EN + * @create_sgmii: Pointer to function creating SGMII PCS instance(s) + * @active_cpu_ports: Holding the active CPU ports ++ * @mdiodev: The pointer to the MDIO device structure + */ + struct mt7530_priv { + struct device *dev; +@@ -797,6 +798,7 @@ struct mt7530_priv { + u32 irq_enable; + int (*create_sgmii)(struct mt7530_priv *priv); + u8 active_cpu_ports; ++ struct mdio_device *mdiodev; + }; + + struct mt7530_hw_vlan_entry { diff --git a/target/linux/generic/backport-6.1/790-53-v6.10-net-dsa-mt7530-simplify-core-operations.patch b/target/linux/generic/backport-6.1/790-53-v6.10-net-dsa-mt7530-simplify-core-operations.patch new file mode 100644 index 000000000..d9d70f1d4 --- /dev/null +++ b/target/linux/generic/backport-6.1/790-53-v6.10-net-dsa-mt7530-simplify-core-operations.patch @@ -0,0 +1,186 @@ +From 9764a08b3d260f4e7799d34bbfe64463db940d74 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Ar=C4=B1n=C3=A7=20=C3=9CNAL?= +Date: Thu, 18 Apr 2024 08:35:31 +0300 +Subject: [PATCH 5/5] net: dsa: mt7530: simplify core operations +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +The core_rmw() function calls core_read_mmd_indirect() to read the +requested register, and then calls core_write_mmd_indirect() to write the +requested value to the register. Because Clause 22 is used to access Clause +45 registers, some operations on core_write_mmd_indirect() are +unnecessarily run. Get rid of core_read_mmd_indirect() and +core_write_mmd_indirect(), and run only the necessary operations on +core_write() and core_rmw(). + +Reviewed-by: Daniel Golle +Tested-by: Daniel Golle +Signed-off-by: Arınç ÃœNAL +Signed-off-by: Paolo Abeni +--- + drivers/net/dsa/mt7530.c | 108 ++++++++++++++++----------------------- + 1 file changed, 43 insertions(+), 65 deletions(-) + +--- a/drivers/net/dsa/mt7530.c ++++ b/drivers/net/dsa/mt7530.c +@@ -74,116 +74,94 @@ static const struct mt7530_mib_desc mt75 + MIB_DESC(1, 0xb8, "RxArlDrop"), + }; + +-/* Since phy_device has not yet been created and +- * phy_{read,write}_mmd_indirect is not available, we provide our own +- * core_{read,write}_mmd_indirect with core_{clear,write,set} wrappers +- * to complete this function. +- */ +-static int +-core_read_mmd_indirect(struct mt7530_priv *priv, int prtad, int devad) ++static void ++mt7530_mutex_lock(struct mt7530_priv *priv) ++{ ++ if (priv->bus) ++ mutex_lock_nested(&priv->bus->mdio_lock, MDIO_MUTEX_NESTED); ++} ++ ++static void ++mt7530_mutex_unlock(struct mt7530_priv *priv) ++{ ++ if (priv->bus) ++ mutex_unlock(&priv->bus->mdio_lock); ++} ++ ++static void ++core_write(struct mt7530_priv *priv, u32 reg, u32 val) + { + struct mii_bus *bus = priv->bus; +- int value, ret; ++ int ret; ++ ++ mt7530_mutex_lock(priv); + + /* Write the desired MMD Devad */ + ret = bus->write(bus, MT753X_CTRL_PHY_ADDR(priv->mdiodev->addr), +- MII_MMD_CTRL, devad); ++ MII_MMD_CTRL, MDIO_MMD_VEND2); + if (ret < 0) + goto err; + + /* Write the desired MMD register address */ + ret = bus->write(bus, MT753X_CTRL_PHY_ADDR(priv->mdiodev->addr), +- MII_MMD_DATA, prtad); ++ MII_MMD_DATA, reg); + if (ret < 0) + goto err; + + /* Select the Function : DATA with no post increment */ + ret = bus->write(bus, MT753X_CTRL_PHY_ADDR(priv->mdiodev->addr), +- MII_MMD_CTRL, devad | MII_MMD_CTRL_NOINCR); ++ MII_MMD_CTRL, MDIO_MMD_VEND2 | MII_MMD_CTRL_NOINCR); + if (ret < 0) + goto err; + +- /* Read the content of the MMD's selected register */ +- value = bus->read(bus, MT753X_CTRL_PHY_ADDR(priv->mdiodev->addr), +- MII_MMD_DATA); +- +- return value; ++ /* Write the data into MMD's selected register */ ++ ret = bus->write(bus, MT753X_CTRL_PHY_ADDR(priv->mdiodev->addr), ++ MII_MMD_DATA, val); + err: +- dev_err(&bus->dev, "failed to read mmd register\n"); ++ if (ret < 0) ++ dev_err(&bus->dev, "failed to write mmd register\n"); + +- return ret; ++ mt7530_mutex_unlock(priv); + } + +-static int +-core_write_mmd_indirect(struct mt7530_priv *priv, int prtad, +- int devad, u32 data) ++static void ++core_rmw(struct mt7530_priv *priv, u32 reg, u32 mask, u32 set) + { + struct mii_bus *bus = priv->bus; ++ u32 val; + int ret; + ++ mt7530_mutex_lock(priv); ++ + /* Write the desired MMD Devad */ + ret = bus->write(bus, MT753X_CTRL_PHY_ADDR(priv->mdiodev->addr), +- MII_MMD_CTRL, devad); ++ MII_MMD_CTRL, MDIO_MMD_VEND2); + if (ret < 0) + goto err; + + /* Write the desired MMD register address */ + ret = bus->write(bus, MT753X_CTRL_PHY_ADDR(priv->mdiodev->addr), +- MII_MMD_DATA, prtad); ++ MII_MMD_DATA, reg); + if (ret < 0) + goto err; + + /* Select the Function : DATA with no post increment */ + ret = bus->write(bus, MT753X_CTRL_PHY_ADDR(priv->mdiodev->addr), +- MII_MMD_CTRL, devad | MII_MMD_CTRL_NOINCR); ++ MII_MMD_CTRL, MDIO_MMD_VEND2 | MII_MMD_CTRL_NOINCR); + if (ret < 0) + goto err; + ++ /* Read the content of the MMD's selected register */ ++ val = bus->read(bus, MT753X_CTRL_PHY_ADDR(priv->mdiodev->addr), ++ MII_MMD_DATA); ++ val &= ~mask; ++ val |= set; + /* Write the data into MMD's selected register */ + ret = bus->write(bus, MT753X_CTRL_PHY_ADDR(priv->mdiodev->addr), +- MII_MMD_DATA, data); ++ MII_MMD_DATA, val); + err: + if (ret < 0) +- dev_err(&bus->dev, +- "failed to write mmd register\n"); +- return ret; +-} +- +-static void +-mt7530_mutex_lock(struct mt7530_priv *priv) +-{ +- if (priv->bus) +- mutex_lock_nested(&priv->bus->mdio_lock, MDIO_MUTEX_NESTED); +-} +- +-static void +-mt7530_mutex_unlock(struct mt7530_priv *priv) +-{ +- if (priv->bus) +- mutex_unlock(&priv->bus->mdio_lock); +-} +- +-static void +-core_write(struct mt7530_priv *priv, u32 reg, u32 val) +-{ +- mt7530_mutex_lock(priv); +- +- core_write_mmd_indirect(priv, reg, MDIO_MMD_VEND2, val); +- +- mt7530_mutex_unlock(priv); +-} +- +-static void +-core_rmw(struct mt7530_priv *priv, u32 reg, u32 mask, u32 set) +-{ +- u32 val; +- +- mt7530_mutex_lock(priv); +- +- val = core_read_mmd_indirect(priv, reg, MDIO_MMD_VEND2); +- val &= ~mask; +- val |= set; +- core_write_mmd_indirect(priv, reg, MDIO_MMD_VEND2, val); ++ dev_err(&bus->dev, "failed to write mmd register\n"); + + mt7530_mutex_unlock(priv); + } diff --git a/target/linux/generic/backport-6.1/790-54-v6.10-net-dsa-mt7530-disable-EEE-abilities-on-failure-on-M.patch b/target/linux/generic/backport-6.1/790-54-v6.10-net-dsa-mt7530-disable-EEE-abilities-on-failure-on-M.patch new file mode 100644 index 000000000..44cf60cf1 --- /dev/null +++ b/target/linux/generic/backport-6.1/790-54-v6.10-net-dsa-mt7530-disable-EEE-abilities-on-failure-on-M.patch @@ -0,0 +1,88 @@ +From 856e8954a0a88d1a4d2b43e9002b9249131a156f Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Ar=C4=B1n=C3=A7=20=C3=9CNAL?= +Date: Mon, 22 Apr 2024 10:15:08 +0300 +Subject: [PATCH 01/15] net: dsa: mt7530: disable EEE abilities on failure on + MT7531 and MT7988 +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +The MT7531_FORCE_EEE1G and MT7531_FORCE_EEE100 bits let the +PMCR_FORCE_EEE1G and PMCR_FORCE_EEE100 bits determine the 1G/100 EEE +abilities of the MAC. If MT7531_FORCE_EEE1G and MT7531_FORCE_EEE100 are +unset, the abilities are left to be determined by PHY auto polling. + +The commit 40b5d2f15c09 ("net: dsa: mt7530: Add support for EEE features") +made it so that the PMCR_FORCE_EEE1G and PMCR_FORCE_EEE100 bits are set on +mt753x_phylink_mac_link_up(). But it did not set the MT7531_FORCE_EEE1G and +MT7531_FORCE_EEE100 bits. Because of this, the EEE abilities will be +determined by PHY auto polling, regardless of the result of phy_init_eee(). + +Define these bits and add them to the MT7531_FORCE_MODE mask which is set +in mt7531_setup_common(). With this, there won't be any EEE abilities set +when phy_init_eee() returns a negative value. + +Thanks to Russell for explaining when phy_init_eee() could return a +negative value below. + +Looking at phy_init_eee(), it could return a negative value when: + +1. phydev->drv is NULL +2. if genphy_c45_eee_is_active() returns negative +3. if genphy_c45_eee_is_active() returns zero, it returns -EPROTONOSUPPORT +4. if phy_set_bits_mmd() fails (e.g. communication error with the PHY) + +If we then look at genphy_c45_eee_is_active(), then: + +genphy_c45_read_eee_adv() and genphy_c45_read_eee_lpa() propagate their +non-zero return values, otherwise this function returns zero or positive +integer. + +If we then look at genphy_c45_read_eee_adv(), then a failure of +phy_read_mmd() would cause a negative value to be returned. + +Looking at genphy_c45_read_eee_lpa(), the same is true. + +So, it can be summarised as: + +- phydev->drv is NULL +- there is a communication error accessing the PHY +- EEE is not active + +otherwise, it returns zero on success. + +If one wishes to determine whether an error occurred vs EEE not being +supported through negotiation for the negotiated speed, if it returns +-EPROTONOSUPPORT in the latter case. Other error codes mean either the +driver has been unloaded or communication error. + +In conclusion, determining the EEE abilities by PHY auto polling shouldn't +result in having any EEE abilities enabled, when one of the last two +situations in the summary happens. And it seems that if phydev->drv is +NULL, there would be bigger problems with the device than a broken link. So +this is not a bugfix. + +Signed-off-by: Arınç ÃœNAL +--- + drivers/net/dsa/mt7530.h | 6 +++++- + 1 file changed, 5 insertions(+), 1 deletion(-) + +--- a/drivers/net/dsa/mt7530.h ++++ b/drivers/net/dsa/mt7530.h +@@ -328,11 +328,15 @@ enum mt7530_vlan_port_acc_frm { + #define MT7531_FORCE_DPX BIT(29) + #define MT7531_FORCE_RX_FC BIT(28) + #define MT7531_FORCE_TX_FC BIT(27) ++#define MT7531_FORCE_EEE100 BIT(26) ++#define MT7531_FORCE_EEE1G BIT(25) + #define MT7531_FORCE_MODE (MT7531_FORCE_LNK | \ + MT7531_FORCE_SPD | \ + MT7531_FORCE_DPX | \ + MT7531_FORCE_RX_FC | \ +- MT7531_FORCE_TX_FC) ++ MT7531_FORCE_TX_FC | \ ++ MT7531_FORCE_EEE100 | \ ++ MT7531_FORCE_EEE1G) + #define PMCR_LINK_SETTINGS_MASK (PMCR_TX_EN | PMCR_FORCE_SPEED_1000 | \ + PMCR_RX_EN | PMCR_FORCE_SPEED_100 | \ + PMCR_TX_FC_EN | PMCR_RX_FC_EN | \ diff --git a/target/linux/generic/backport-6.1/790-55-v6.10-net-dsa-mt7530-refactor-MT7530_PMCR_P.patch b/target/linux/generic/backport-6.1/790-55-v6.10-net-dsa-mt7530-refactor-MT7530_PMCR_P.patch new file mode 100644 index 000000000..158e5f8c0 --- /dev/null +++ b/target/linux/generic/backport-6.1/790-55-v6.10-net-dsa-mt7530-refactor-MT7530_PMCR_P.patch @@ -0,0 +1,200 @@ +From 712ad00d2f43814c81a7abfcbc339690a05fb6a0 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Ar=C4=B1n=C3=A7=20=C3=9CNAL?= +Date: Mon, 22 Apr 2024 10:15:09 +0300 +Subject: [PATCH 02/15] net: dsa: mt7530: refactor MT7530_PMCR_P() +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +The MT7530_PMCR_P() registers are on MT7530, MT7531, and the switch on the +MT7988 SoC. Rename the definition for them to MT753X_PMCR_P(). Bit 15 is +for MT7530 only. Add MT7530 prefix to the definition for bit 15. + +Use GENMASK and FIELD_PREP for PMCR_IFG_XMIT(). + +Rename PMCR_TX_EN and PMCR_RX_EN to PMCR_MAC_TX_EN and PMCR_MAC_TX_EN to +follow the naming on the "MT7621 Giga Switch Programming Guide v0.3", +"MT7531 Reference Manual for Development Board v1.0", and "MT7988A Wi-Fi 7 +Generation Router Platform: Datasheet (Open Version) v0.1" documents. + +These documents show that PMCR_RX_FC_EN is at bit 5. Correct this along +with renaming it to PMCR_FORCE_RX_FC_EN, and the same for PMCR_TX_FC_EN. + +Remove PMCR_SPEED_MASK which doesn't have a use. + +Rename the force mode definitions for MT7531 to FORCE_MODE. Add MASK at the +end for the mask that includes all force mode definitions. + +Signed-off-by: Arınç ÃœNAL +--- + drivers/net/dsa/mt7530.c | 24 ++++++++--------- + drivers/net/dsa/mt7530.h | 58 +++++++++++++++++++++------------------- + 2 files changed, 42 insertions(+), 40 deletions(-) + +--- a/drivers/net/dsa/mt7530.c ++++ b/drivers/net/dsa/mt7530.c +@@ -903,7 +903,7 @@ static void mt7530_setup_port5(struct ds + val &= ~MHWTRAP_P5_MAC_SEL & ~MHWTRAP_P5_DIS; + + /* Setup the MAC by default for the cpu port */ +- mt7530_write(priv, MT7530_PMCR_P(5), 0x56300); ++ mt7530_write(priv, MT753X_PMCR_P(5), 0x56300); + break; + case P5_INTF_SEL_GMAC5: + /* MT7530_P5_MODE_GMAC: P5 -> External phy or 2nd GMAC */ +@@ -2449,8 +2449,8 @@ mt7530_setup(struct dsa_switch *ds) + /* Clear link settings and enable force mode to force link down + * on all ports until they're enabled later. + */ +- mt7530_rmw(priv, MT7530_PMCR_P(i), PMCR_LINK_SETTINGS_MASK | +- PMCR_FORCE_MODE, PMCR_FORCE_MODE); ++ mt7530_rmw(priv, MT753X_PMCR_P(i), PMCR_LINK_SETTINGS_MASK | ++ MT7530_FORCE_MODE, MT7530_FORCE_MODE); + + /* Disable forwarding by default on all ports */ + mt7530_rmw(priv, MT7530_PCR_P(i), PCR_MATRIX_MASK, +@@ -2560,8 +2560,8 @@ mt7531_setup_common(struct dsa_switch *d + /* Clear link settings and enable force mode to force link down + * on all ports until they're enabled later. + */ +- mt7530_rmw(priv, MT7530_PMCR_P(i), PMCR_LINK_SETTINGS_MASK | +- MT7531_FORCE_MODE, MT7531_FORCE_MODE); ++ mt7530_rmw(priv, MT753X_PMCR_P(i), PMCR_LINK_SETTINGS_MASK | ++ MT7531_FORCE_MODE_MASK, MT7531_FORCE_MODE_MASK); + + /* Disable forwarding by default on all ports */ + mt7530_rmw(priv, MT7530_PCR_P(i), PCR_MATRIX_MASK, +@@ -2644,7 +2644,7 @@ mt7531_setup(struct dsa_switch *ds) + + /* Force link down on all ports before internal reset */ + for (i = 0; i < MT7530_NUM_PORTS; i++) +- mt7530_write(priv, MT7530_PMCR_P(i), MT7531_FORCE_LNK); ++ mt7530_write(priv, MT753X_PMCR_P(i), MT7531_FORCE_MODE_LNK); + + /* Reset the switch through internal reset */ + mt7530_write(priv, MT7530_SYS_CTRL, SYS_CTRL_SW_RST | SYS_CTRL_REG_RST); +@@ -2886,7 +2886,7 @@ mt753x_phylink_mac_config(struct phylink + + /* Are we connected to external phy */ + if (port == 5 && dsa_is_user_port(ds, 5)) +- mt7530_set(priv, MT7530_PMCR_P(port), PMCR_EXT_PHY); ++ mt7530_set(priv, MT753X_PMCR_P(port), PMCR_EXT_PHY); + } + + static void mt753x_phylink_mac_link_down(struct phylink_config *config, +@@ -2896,7 +2896,7 @@ static void mt753x_phylink_mac_link_down + struct dsa_port *dp = dsa_phylink_to_port(config); + struct mt7530_priv *priv = dp->ds->priv; + +- mt7530_clear(priv, MT7530_PMCR_P(dp->index), PMCR_LINK_SETTINGS_MASK); ++ mt7530_clear(priv, MT753X_PMCR_P(dp->index), PMCR_LINK_SETTINGS_MASK); + } + + static void mt753x_phylink_mac_link_up(struct phylink_config *config, +@@ -2910,7 +2910,7 @@ static void mt753x_phylink_mac_link_up(s + struct mt7530_priv *priv = dp->ds->priv; + u32 mcr; + +- mcr = PMCR_RX_EN | PMCR_TX_EN | PMCR_FORCE_LNK; ++ mcr = PMCR_MAC_RX_EN | PMCR_MAC_TX_EN | PMCR_FORCE_LNK; + + switch (speed) { + case SPEED_1000: +@@ -2925,9 +2925,9 @@ static void mt753x_phylink_mac_link_up(s + if (duplex == DUPLEX_FULL) { + mcr |= PMCR_FORCE_FDX; + if (tx_pause) +- mcr |= PMCR_TX_FC_EN; ++ mcr |= PMCR_FORCE_TX_FC_EN; + if (rx_pause) +- mcr |= PMCR_RX_FC_EN; ++ mcr |= PMCR_FORCE_RX_FC_EN; + } + + if (mode == MLO_AN_PHY && phydev && phy_init_eee(phydev, false) >= 0) { +@@ -2942,7 +2942,7 @@ static void mt753x_phylink_mac_link_up(s + } + } + +- mt7530_set(priv, MT7530_PMCR_P(dp->index), mcr); ++ mt7530_set(priv, MT753X_PMCR_P(dp->index), mcr); + } + + static void mt753x_phylink_get_caps(struct dsa_switch *ds, int port, +--- a/drivers/net/dsa/mt7530.h ++++ b/drivers/net/dsa/mt7530.h +@@ -304,44 +304,46 @@ enum mt7530_vlan_port_acc_frm { + #define G0_PORT_VID_DEF G0_PORT_VID(0) + + /* Register for port MAC control register */ +-#define MT7530_PMCR_P(x) (0x3000 + ((x) * 0x100)) +-#define PMCR_IFG_XMIT(x) (((x) & 0x3) << 18) ++#define MT753X_PMCR_P(x) (0x3000 + ((x) * 0x100)) ++#define PMCR_IFG_XMIT_MASK GENMASK(19, 18) ++#define PMCR_IFG_XMIT(x) FIELD_PREP(PMCR_IFG_XMIT_MASK, x) + #define PMCR_EXT_PHY BIT(17) + #define PMCR_MAC_MODE BIT(16) +-#define PMCR_FORCE_MODE BIT(15) +-#define PMCR_TX_EN BIT(14) +-#define PMCR_RX_EN BIT(13) ++#define MT7530_FORCE_MODE BIT(15) ++#define PMCR_MAC_TX_EN BIT(14) ++#define PMCR_MAC_RX_EN BIT(13) + #define PMCR_BACKOFF_EN BIT(9) + #define PMCR_BACKPR_EN BIT(8) + #define PMCR_FORCE_EEE1G BIT(7) + #define PMCR_FORCE_EEE100 BIT(6) +-#define PMCR_TX_FC_EN BIT(5) +-#define PMCR_RX_FC_EN BIT(4) ++#define PMCR_FORCE_RX_FC_EN BIT(5) ++#define PMCR_FORCE_TX_FC_EN BIT(4) + #define PMCR_FORCE_SPEED_1000 BIT(3) + #define PMCR_FORCE_SPEED_100 BIT(2) + #define PMCR_FORCE_FDX BIT(1) + #define PMCR_FORCE_LNK BIT(0) +-#define PMCR_SPEED_MASK (PMCR_FORCE_SPEED_100 | \ +- PMCR_FORCE_SPEED_1000) +-#define MT7531_FORCE_LNK BIT(31) +-#define MT7531_FORCE_SPD BIT(30) +-#define MT7531_FORCE_DPX BIT(29) +-#define MT7531_FORCE_RX_FC BIT(28) +-#define MT7531_FORCE_TX_FC BIT(27) +-#define MT7531_FORCE_EEE100 BIT(26) +-#define MT7531_FORCE_EEE1G BIT(25) +-#define MT7531_FORCE_MODE (MT7531_FORCE_LNK | \ +- MT7531_FORCE_SPD | \ +- MT7531_FORCE_DPX | \ +- MT7531_FORCE_RX_FC | \ +- MT7531_FORCE_TX_FC | \ +- MT7531_FORCE_EEE100 | \ +- MT7531_FORCE_EEE1G) +-#define PMCR_LINK_SETTINGS_MASK (PMCR_TX_EN | PMCR_FORCE_SPEED_1000 | \ +- PMCR_RX_EN | PMCR_FORCE_SPEED_100 | \ +- PMCR_TX_FC_EN | PMCR_RX_FC_EN | \ +- PMCR_FORCE_FDX | PMCR_FORCE_LNK | \ +- PMCR_FORCE_EEE1G | PMCR_FORCE_EEE100) ++#define MT7531_FORCE_MODE_LNK BIT(31) ++#define MT7531_FORCE_MODE_SPD BIT(30) ++#define MT7531_FORCE_MODE_DPX BIT(29) ++#define MT7531_FORCE_MODE_RX_FC BIT(28) ++#define MT7531_FORCE_MODE_TX_FC BIT(27) ++#define MT7531_FORCE_MODE_EEE100 BIT(26) ++#define MT7531_FORCE_MODE_EEE1G BIT(25) ++#define MT7531_FORCE_MODE_MASK (MT7531_FORCE_MODE_LNK | \ ++ MT7531_FORCE_MODE_SPD | \ ++ MT7531_FORCE_MODE_DPX | \ ++ MT7531_FORCE_MODE_RX_FC | \ ++ MT7531_FORCE_MODE_TX_FC | \ ++ MT7531_FORCE_MODE_EEE100 | \ ++ MT7531_FORCE_MODE_EEE1G) ++#define PMCR_LINK_SETTINGS_MASK (PMCR_MAC_TX_EN | PMCR_MAC_RX_EN | \ ++ PMCR_FORCE_EEE1G | \ ++ PMCR_FORCE_EEE100 | \ ++ PMCR_FORCE_RX_FC_EN | \ ++ PMCR_FORCE_TX_FC_EN | \ ++ PMCR_FORCE_SPEED_1000 | \ ++ PMCR_FORCE_SPEED_100 | \ ++ PMCR_FORCE_FDX | PMCR_FORCE_LNK) + + #define MT7530_PMEEECR_P(x) (0x3004 + (x) * 0x100) + #define WAKEUP_TIME_1000(x) (((x) & 0xFF) << 24) diff --git a/target/linux/generic/backport-6.1/790-56-v6.10-net-dsa-mt7530-rename-p5_intf_sel-and-use-only-for-M.patch b/target/linux/generic/backport-6.1/790-56-v6.10-net-dsa-mt7530-rename-p5_intf_sel-and-use-only-for-M.patch new file mode 100644 index 000000000..9a0ce7c36 --- /dev/null +++ b/target/linux/generic/backport-6.1/790-56-v6.10-net-dsa-mt7530-rename-p5_intf_sel-and-use-only-for-M.patch @@ -0,0 +1,185 @@ +From 875ec5b67ab88e969b171e6e9ea803e3ed759614 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Ar=C4=B1n=C3=A7=20=C3=9CNAL?= +Date: Mon, 22 Apr 2024 10:15:10 +0300 +Subject: [PATCH 03/15] net: dsa: mt7530: rename p5_intf_sel and use only for + MT7530 switch +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +The p5_intf_sel pointer is used to store the information of whether PHY +muxing is used or not. PHY muxing is a feature specific to port 5 of the +MT7530 switch. Do not use it for other switch models. + +Rename the pointer to p5_mode to store the mode the port is being used in. +Rename the p5_interface_select enum to mt7530_p5_mode, the string +representation to mt7530_p5_mode_str, and the enum elements. + +If PHY muxing is not detected, the default mode, GMAC5, will be used. + +Signed-off-by: Arınç ÃœNAL +--- + drivers/net/dsa/mt7530.c | 62 +++++++++++++++++----------------------- + drivers/net/dsa/mt7530.h | 15 +++++----- + 2 files changed, 33 insertions(+), 44 deletions(-) + +--- a/drivers/net/dsa/mt7530.c ++++ b/drivers/net/dsa/mt7530.c +@@ -864,19 +864,15 @@ mt7530_set_ageing_time(struct dsa_switch + return 0; + } + +-static const char *p5_intf_modes(unsigned int p5_interface) ++static const char *mt7530_p5_mode_str(unsigned int mode) + { +- switch (p5_interface) { +- case P5_DISABLED: +- return "DISABLED"; +- case P5_INTF_SEL_PHY_P0: +- return "PHY P0"; +- case P5_INTF_SEL_PHY_P4: +- return "PHY P4"; +- case P5_INTF_SEL_GMAC5: +- return "GMAC5"; ++ switch (mode) { ++ case MUX_PHY_P0: ++ return "MUX PHY P0"; ++ case MUX_PHY_P4: ++ return "MUX PHY P4"; + default: +- return "unknown"; ++ return "GMAC5"; + } + } + +@@ -893,23 +889,23 @@ static void mt7530_setup_port5(struct ds + val |= MHWTRAP_MANUAL | MHWTRAP_P5_MAC_SEL | MHWTRAP_P5_DIS; + val &= ~MHWTRAP_P5_RGMII_MODE & ~MHWTRAP_PHY0_SEL; + +- switch (priv->p5_intf_sel) { +- case P5_INTF_SEL_PHY_P0: +- /* MT7530_P5_MODE_GPHY_P0: 2nd GMAC -> P5 -> P0 */ ++ switch (priv->p5_mode) { ++ /* MUX_PHY_P0: P0 -> P5 -> SoC MAC */ ++ case MUX_PHY_P0: + val |= MHWTRAP_PHY0_SEL; + fallthrough; +- case P5_INTF_SEL_PHY_P4: +- /* MT7530_P5_MODE_GPHY_P4: 2nd GMAC -> P5 -> P4 */ ++ ++ /* MUX_PHY_P4: P4 -> P5 -> SoC MAC */ ++ case MUX_PHY_P4: + val &= ~MHWTRAP_P5_MAC_SEL & ~MHWTRAP_P5_DIS; + + /* Setup the MAC by default for the cpu port */ + mt7530_write(priv, MT753X_PMCR_P(5), 0x56300); + break; +- case P5_INTF_SEL_GMAC5: +- /* MT7530_P5_MODE_GMAC: P5 -> External phy or 2nd GMAC */ +- val &= ~MHWTRAP_P5_DIS; +- break; ++ ++ /* GMAC5: P5 -> SoC MAC or external PHY */ + default: ++ val &= ~MHWTRAP_P5_DIS; + break; + } + +@@ -937,8 +933,8 @@ static void mt7530_setup_port5(struct ds + + mt7530_write(priv, MT7530_MHWTRAP, val); + +- dev_dbg(ds->dev, "Setup P5, HWTRAP=0x%x, intf_sel=%s, phy-mode=%s\n", +- val, p5_intf_modes(priv->p5_intf_sel), phy_modes(interface)); ++ dev_dbg(ds->dev, "Setup P5, HWTRAP=0x%x, mode=%s, phy-mode=%s\n", val, ++ mt7530_p5_mode_str(priv->p5_mode), phy_modes(interface)); + + mutex_unlock(&priv->reg_mutex); + } +@@ -2481,13 +2477,11 @@ mt7530_setup(struct dsa_switch *ds) + if (ret) + return ret; + +- /* Setup port 5 */ +- if (!dsa_is_unused_port(ds, 5)) { +- priv->p5_intf_sel = P5_INTF_SEL_GMAC5; +- } else { ++ /* Check for PHY muxing on port 5 */ ++ if (dsa_is_unused_port(ds, 5)) { + /* Scan the ethernet nodes. Look for GMAC1, lookup the used PHY. +- * Set priv->p5_intf_sel to the appropriate value if PHY muxing +- * is detected. ++ * Set priv->p5_mode to the appropriate value if PHY muxing is ++ * detected. + */ + for_each_child_of_node(dn, mac_np) { + if (!of_device_is_compatible(mac_np, +@@ -2511,17 +2505,16 @@ mt7530_setup(struct dsa_switch *ds) + } + id = of_mdio_parse_addr(ds->dev, phy_node); + if (id == 0) +- priv->p5_intf_sel = P5_INTF_SEL_PHY_P0; ++ priv->p5_mode = MUX_PHY_P0; + if (id == 4) +- priv->p5_intf_sel = P5_INTF_SEL_PHY_P4; ++ priv->p5_mode = MUX_PHY_P4; + } + of_node_put(mac_np); + of_node_put(phy_node); + break; + } + +- if (priv->p5_intf_sel == P5_INTF_SEL_PHY_P0 || +- priv->p5_intf_sel == P5_INTF_SEL_PHY_P4) ++ if (priv->p5_mode == MUX_PHY_P0 || priv->p5_mode == MUX_PHY_P4) + mt7530_setup_port5(ds, interface); + } + +@@ -2659,9 +2652,6 @@ mt7531_setup(struct dsa_switch *ds) + MT7531_EXT_P_MDIO_12); + } + +- if (!dsa_is_unused_port(ds, 5)) +- priv->p5_intf_sel = P5_INTF_SEL_GMAC5; +- + mt7530_rmw(priv, MT7531_GPIO_MODE0, MT7531_GPIO0_MASK, + MT7531_GPIO0_INTERRUPT); + +--- a/drivers/net/dsa/mt7530.h ++++ b/drivers/net/dsa/mt7530.h +@@ -708,12 +708,11 @@ struct mt7530_port { + struct phylink_pcs *sgmii_pcs; + }; + +-/* Port 5 interface select definitions */ +-enum p5_interface_select { +- P5_DISABLED, +- P5_INTF_SEL_PHY_P0, +- P5_INTF_SEL_PHY_P4, +- P5_INTF_SEL_GMAC5, ++/* Port 5 mode definitions of the MT7530 switch */ ++enum mt7530_p5_mode { ++ GMAC5, ++ MUX_PHY_P0, ++ MUX_PHY_P4, + }; + + struct mt7530_priv; +@@ -769,7 +768,7 @@ struct mt753x_info { + * @ports: Holding the state among ports + * @reg_mutex: The lock for protecting among process accessing + * registers +- * @p5_intf_sel: Holding the current port 5 interface select ++ * @p5_mode: Holding the current mode of port 5 of the MT7530 switch + * @p5_sgmii: Flag for distinguishing if port 5 of the MT7531 switch + * has got SGMII + * @irq: IRQ number of the switch +@@ -791,7 +790,7 @@ struct mt7530_priv { + const struct mt753x_info *info; + unsigned int id; + bool mcm; +- enum p5_interface_select p5_intf_sel; ++ enum mt7530_p5_mode p5_mode; + bool p5_sgmii; + u8 mirror_rx; + u8 mirror_tx; diff --git a/target/linux/generic/backport-6.1/790-57-v6.10-net-dsa-mt7530-rename-mt753x_bpdu_port_fw-enum-to-mt.patch b/target/linux/generic/backport-6.1/790-57-v6.10-net-dsa-mt7530-rename-mt753x_bpdu_port_fw-enum-to-mt.patch new file mode 100644 index 000000000..c8ffd5f07 --- /dev/null +++ b/target/linux/generic/backport-6.1/790-57-v6.10-net-dsa-mt7530-rename-mt753x_bpdu_port_fw-enum-to-mt.patch @@ -0,0 +1,169 @@ +From 83fe3df057e641cd0e88425e579d7a5a370ca430 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Ar=C4=B1n=C3=A7=20=C3=9CNAL?= +Date: Mon, 22 Apr 2024 10:15:11 +0300 +Subject: [PATCH 04/15] net: dsa: mt7530: rename mt753x_bpdu_port_fw enum to + mt753x_to_cpu_fw +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +The mt753x_bpdu_port_fw enum is globally used for manipulating the process +of deciding the forwardable ports, specifically concerning the CPU port(s). +Therefore, rename it and the values in it to mt753x_to_cpu_fw. + +Change FOLLOW_MFC to SYSTEM_DEFAULT to be on par with the switch documents. + +Signed-off-by: Arınç ÃœNAL +--- + drivers/net/dsa/mt7530.c | 44 ++++++++++------------- + drivers/net/dsa/mt7530.h | 76 ++++++++++++++++++++-------------------- + 2 files changed, 56 insertions(+), 64 deletions(-) + +--- a/drivers/net/dsa/mt7530.c ++++ b/drivers/net/dsa/mt7530.c +@@ -1114,42 +1114,34 @@ mt753x_trap_frames(struct mt7530_priv *p + * VLAN-untagged. + */ + mt7530_rmw(priv, MT753X_BPC, +- MT753X_PAE_BPDU_FR | MT753X_PAE_EG_TAG_MASK | +- MT753X_PAE_PORT_FW_MASK | MT753X_BPDU_EG_TAG_MASK | +- MT753X_BPDU_PORT_FW_MASK, +- MT753X_PAE_BPDU_FR | +- MT753X_PAE_EG_TAG(MT7530_VLAN_EG_UNTAGGED) | +- MT753X_PAE_PORT_FW(MT753X_BPDU_CPU_ONLY) | +- MT753X_BPDU_EG_TAG(MT7530_VLAN_EG_UNTAGGED) | +- MT753X_BPDU_CPU_ONLY); ++ PAE_BPDU_FR | PAE_EG_TAG_MASK | PAE_PORT_FW_MASK | ++ BPDU_EG_TAG_MASK | BPDU_PORT_FW_MASK, ++ PAE_BPDU_FR | PAE_EG_TAG(MT7530_VLAN_EG_UNTAGGED) | ++ PAE_PORT_FW(TO_CPU_FW_CPU_ONLY) | ++ BPDU_EG_TAG(MT7530_VLAN_EG_UNTAGGED) | ++ TO_CPU_FW_CPU_ONLY); + + /* Trap frames with :01 and :02 MAC DAs to the CPU port(s) and egress + * them VLAN-untagged. + */ + mt7530_rmw(priv, MT753X_RGAC1, +- MT753X_R02_BPDU_FR | MT753X_R02_EG_TAG_MASK | +- MT753X_R02_PORT_FW_MASK | MT753X_R01_BPDU_FR | +- MT753X_R01_EG_TAG_MASK | MT753X_R01_PORT_FW_MASK, +- MT753X_R02_BPDU_FR | +- MT753X_R02_EG_TAG(MT7530_VLAN_EG_UNTAGGED) | +- MT753X_R02_PORT_FW(MT753X_BPDU_CPU_ONLY) | +- MT753X_R01_BPDU_FR | +- MT753X_R01_EG_TAG(MT7530_VLAN_EG_UNTAGGED) | +- MT753X_BPDU_CPU_ONLY); ++ R02_BPDU_FR | R02_EG_TAG_MASK | R02_PORT_FW_MASK | ++ R01_BPDU_FR | R01_EG_TAG_MASK | R01_PORT_FW_MASK, ++ R02_BPDU_FR | R02_EG_TAG(MT7530_VLAN_EG_UNTAGGED) | ++ R02_PORT_FW(TO_CPU_FW_CPU_ONLY) | R01_BPDU_FR | ++ R01_EG_TAG(MT7530_VLAN_EG_UNTAGGED) | ++ TO_CPU_FW_CPU_ONLY); + + /* Trap frames with :03 and :0E MAC DAs to the CPU port(s) and egress + * them VLAN-untagged. + */ + mt7530_rmw(priv, MT753X_RGAC2, +- MT753X_R0E_BPDU_FR | MT753X_R0E_EG_TAG_MASK | +- MT753X_R0E_PORT_FW_MASK | MT753X_R03_BPDU_FR | +- MT753X_R03_EG_TAG_MASK | MT753X_R03_PORT_FW_MASK, +- MT753X_R0E_BPDU_FR | +- MT753X_R0E_EG_TAG(MT7530_VLAN_EG_UNTAGGED) | +- MT753X_R0E_PORT_FW(MT753X_BPDU_CPU_ONLY) | +- MT753X_R03_BPDU_FR | +- MT753X_R03_EG_TAG(MT7530_VLAN_EG_UNTAGGED) | +- MT753X_BPDU_CPU_ONLY); ++ R0E_BPDU_FR | R0E_EG_TAG_MASK | R0E_PORT_FW_MASK | ++ R03_BPDU_FR | R03_EG_TAG_MASK | R03_PORT_FW_MASK, ++ R0E_BPDU_FR | R0E_EG_TAG(MT7530_VLAN_EG_UNTAGGED) | ++ R0E_PORT_FW(TO_CPU_FW_CPU_ONLY) | R03_BPDU_FR | ++ R03_EG_TAG(MT7530_VLAN_EG_UNTAGGED) | ++ TO_CPU_FW_CPU_ONLY); + } + + static void +--- a/drivers/net/dsa/mt7530.h ++++ b/drivers/net/dsa/mt7530.h +@@ -67,47 +67,47 @@ enum mt753x_id { + #define MT753X_MIRROR_MASK(id) ((((id) == ID_MT7531) || ((id) == ID_MT7988)) ? \ + MT7531_MIRROR_MASK : MIRROR_MASK) + +-/* Registers for BPDU and PAE frame control*/ ++/* Register for BPDU and PAE frame control */ + #define MT753X_BPC 0x24 +-#define MT753X_PAE_BPDU_FR BIT(25) +-#define MT753X_PAE_EG_TAG_MASK GENMASK(24, 22) +-#define MT753X_PAE_EG_TAG(x) FIELD_PREP(MT753X_PAE_EG_TAG_MASK, x) +-#define MT753X_PAE_PORT_FW_MASK GENMASK(18, 16) +-#define MT753X_PAE_PORT_FW(x) FIELD_PREP(MT753X_PAE_PORT_FW_MASK, x) +-#define MT753X_BPDU_EG_TAG_MASK GENMASK(8, 6) +-#define MT753X_BPDU_EG_TAG(x) FIELD_PREP(MT753X_BPDU_EG_TAG_MASK, x) +-#define MT753X_BPDU_PORT_FW_MASK GENMASK(2, 0) ++#define PAE_BPDU_FR BIT(25) ++#define PAE_EG_TAG_MASK GENMASK(24, 22) ++#define PAE_EG_TAG(x) FIELD_PREP(PAE_EG_TAG_MASK, x) ++#define PAE_PORT_FW_MASK GENMASK(18, 16) ++#define PAE_PORT_FW(x) FIELD_PREP(PAE_PORT_FW_MASK, x) ++#define BPDU_EG_TAG_MASK GENMASK(8, 6) ++#define BPDU_EG_TAG(x) FIELD_PREP(BPDU_EG_TAG_MASK, x) ++#define BPDU_PORT_FW_MASK GENMASK(2, 0) + +-/* Register for :01 and :02 MAC DA frame control */ ++/* Register for 01-80-C2-00-00-[01,02] MAC DA frame control */ + #define MT753X_RGAC1 0x28 +-#define MT753X_R02_BPDU_FR BIT(25) +-#define MT753X_R02_EG_TAG_MASK GENMASK(24, 22) +-#define MT753X_R02_EG_TAG(x) FIELD_PREP(MT753X_R02_EG_TAG_MASK, x) +-#define MT753X_R02_PORT_FW_MASK GENMASK(18, 16) +-#define MT753X_R02_PORT_FW(x) FIELD_PREP(MT753X_R02_PORT_FW_MASK, x) +-#define MT753X_R01_BPDU_FR BIT(9) +-#define MT753X_R01_EG_TAG_MASK GENMASK(8, 6) +-#define MT753X_R01_EG_TAG(x) FIELD_PREP(MT753X_R01_EG_TAG_MASK, x) +-#define MT753X_R01_PORT_FW_MASK GENMASK(2, 0) ++#define R02_BPDU_FR BIT(25) ++#define R02_EG_TAG_MASK GENMASK(24, 22) ++#define R02_EG_TAG(x) FIELD_PREP(R02_EG_TAG_MASK, x) ++#define R02_PORT_FW_MASK GENMASK(18, 16) ++#define R02_PORT_FW(x) FIELD_PREP(R02_PORT_FW_MASK, x) ++#define R01_BPDU_FR BIT(9) ++#define R01_EG_TAG_MASK GENMASK(8, 6) ++#define R01_EG_TAG(x) FIELD_PREP(R01_EG_TAG_MASK, x) ++#define R01_PORT_FW_MASK GENMASK(2, 0) + +-/* Register for :03 and :0E MAC DA frame control */ ++/* Register for 01-80-C2-00-00-[03,0E] MAC DA frame control */ + #define MT753X_RGAC2 0x2c +-#define MT753X_R0E_BPDU_FR BIT(25) +-#define MT753X_R0E_EG_TAG_MASK GENMASK(24, 22) +-#define MT753X_R0E_EG_TAG(x) FIELD_PREP(MT753X_R0E_EG_TAG_MASK, x) +-#define MT753X_R0E_PORT_FW_MASK GENMASK(18, 16) +-#define MT753X_R0E_PORT_FW(x) FIELD_PREP(MT753X_R0E_PORT_FW_MASK, x) +-#define MT753X_R03_BPDU_FR BIT(9) +-#define MT753X_R03_EG_TAG_MASK GENMASK(8, 6) +-#define MT753X_R03_EG_TAG(x) FIELD_PREP(MT753X_R03_EG_TAG_MASK, x) +-#define MT753X_R03_PORT_FW_MASK GENMASK(2, 0) ++#define R0E_BPDU_FR BIT(25) ++#define R0E_EG_TAG_MASK GENMASK(24, 22) ++#define R0E_EG_TAG(x) FIELD_PREP(R0E_EG_TAG_MASK, x) ++#define R0E_PORT_FW_MASK GENMASK(18, 16) ++#define R0E_PORT_FW(x) FIELD_PREP(R0E_PORT_FW_MASK, x) ++#define R03_BPDU_FR BIT(9) ++#define R03_EG_TAG_MASK GENMASK(8, 6) ++#define R03_EG_TAG(x) FIELD_PREP(R03_EG_TAG_MASK, x) ++#define R03_PORT_FW_MASK GENMASK(2, 0) + +-enum mt753x_bpdu_port_fw { +- MT753X_BPDU_FOLLOW_MFC, +- MT753X_BPDU_CPU_EXCLUDE = 4, +- MT753X_BPDU_CPU_INCLUDE = 5, +- MT753X_BPDU_CPU_ONLY = 6, +- MT753X_BPDU_DROP = 7, ++enum mt753x_to_cpu_fw { ++ TO_CPU_FW_SYSTEM_DEFAULT, ++ TO_CPU_FW_CPU_EXCLUDE = 4, ++ TO_CPU_FW_CPU_INCLUDE = 5, ++ TO_CPU_FW_CPU_ONLY = 6, ++ TO_CPU_FW_DROP = 7, + }; + + /* Registers for address table access */ diff --git a/target/linux/generic/backport-6.1/790-58-v6.10-net-dsa-mt7530-refactor-MT7530_MFC-and-MT7531_CFC-ad.patch b/target/linux/generic/backport-6.1/790-58-v6.10-net-dsa-mt7530-refactor-MT7530_MFC-and-MT7531_CFC-ad.patch new file mode 100644 index 000000000..c977fe46c --- /dev/null +++ b/target/linux/generic/backport-6.1/790-58-v6.10-net-dsa-mt7530-refactor-MT7530_MFC-and-MT7531_CFC-ad.patch @@ -0,0 +1,201 @@ +From 1dbc1bdc2869e6d2929235c70d64e393aa5a5fa2 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Ar=C4=B1n=C3=A7=20=C3=9CNAL?= +Date: Mon, 22 Apr 2024 10:15:12 +0300 +Subject: [PATCH 05/15] net: dsa: mt7530: refactor MT7530_MFC and MT7531_CFC, + add MT7531_QRY_FFP +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +The MT7530_MFC register is on MT7530, MT7531, and the switch on the MT7988 +SoC. Rename it to MT753X_MFC. Bit 7 to 0 differs between MT7530 and +MT7531/MT7988. Add MT7530 prefix to these definitions, and define the +IGMP/MLD Query Frame Flooding Ports mask for MT7531. + +Rename the cases of MIRROR_MASK to MIRROR_PORT_MASK. + +Move mt753x_mirror_port_get() and mt753x_port_mirror_set() to mt7530.h as +macros. + +Signed-off-by: Arınç ÃœNAL +--- + drivers/net/dsa/mt7530.c | 38 ++++++++-------------- + drivers/net/dsa/mt7530.h | 69 +++++++++++++++++++++++++--------------- + 2 files changed, 57 insertions(+), 50 deletions(-) + +--- a/drivers/net/dsa/mt7530.c ++++ b/drivers/net/dsa/mt7530.c +@@ -1154,7 +1154,7 @@ mt753x_cpu_port_enable(struct dsa_switch + PORT_SPEC_TAG); + + /* Enable flooding on the CPU port */ +- mt7530_set(priv, MT7530_MFC, BC_FFP(BIT(port)) | UNM_FFP(BIT(port)) | ++ mt7530_set(priv, MT753X_MFC, BC_FFP(BIT(port)) | UNM_FFP(BIT(port)) | + UNU_FFP(BIT(port))); + + /* Add the CPU port to the CPU port bitmap for MT7531. Trapped frames +@@ -1318,15 +1318,15 @@ mt7530_port_bridge_flags(struct dsa_swit + flags.val & BR_LEARNING ? 0 : SA_DIS); + + if (flags.mask & BR_FLOOD) +- mt7530_rmw(priv, MT7530_MFC, UNU_FFP(BIT(port)), ++ mt7530_rmw(priv, MT753X_MFC, UNU_FFP(BIT(port)), + flags.val & BR_FLOOD ? UNU_FFP(BIT(port)) : 0); + + if (flags.mask & BR_MCAST_FLOOD) +- mt7530_rmw(priv, MT7530_MFC, UNM_FFP(BIT(port)), ++ mt7530_rmw(priv, MT753X_MFC, UNM_FFP(BIT(port)), + flags.val & BR_MCAST_FLOOD ? UNM_FFP(BIT(port)) : 0); + + if (flags.mask & BR_BCAST_FLOOD) +- mt7530_rmw(priv, MT7530_MFC, BC_FFP(BIT(port)), ++ mt7530_rmw(priv, MT753X_MFC, BC_FFP(BIT(port)), + flags.val & BR_BCAST_FLOOD ? BC_FFP(BIT(port)) : 0); + + return 0; +@@ -1862,20 +1862,6 @@ mt7530_port_vlan_del(struct dsa_switch * + return 0; + } + +-static int mt753x_mirror_port_get(unsigned int id, u32 val) +-{ +- return (id == ID_MT7531 || id == ID_MT7988) ? +- MT7531_MIRROR_PORT_GET(val) : +- MIRROR_PORT(val); +-} +- +-static int mt753x_mirror_port_set(unsigned int id, u32 val) +-{ +- return (id == ID_MT7531 || id == ID_MT7988) ? +- MT7531_MIRROR_PORT_SET(val) : +- MIRROR_PORT(val); +-} +- + static int mt753x_port_mirror_add(struct dsa_switch *ds, int port, + struct dsa_mall_mirror_tc_entry *mirror, + bool ingress, struct netlink_ext_ack *extack) +@@ -1891,14 +1877,14 @@ static int mt753x_port_mirror_add(struct + val = mt7530_read(priv, MT753X_MIRROR_REG(priv->id)); + + /* MT7530 only supports one monitor port */ +- monitor_port = mt753x_mirror_port_get(priv->id, val); ++ monitor_port = MT753X_MIRROR_PORT_GET(priv->id, val); + if (val & MT753X_MIRROR_EN(priv->id) && + monitor_port != mirror->to_local_port) + return -EEXIST; + + val |= MT753X_MIRROR_EN(priv->id); +- val &= ~MT753X_MIRROR_MASK(priv->id); +- val |= mt753x_mirror_port_set(priv->id, mirror->to_local_port); ++ val &= ~MT753X_MIRROR_PORT_MASK(priv->id); ++ val |= MT753X_MIRROR_PORT_SET(priv->id, mirror->to_local_port); + mt7530_write(priv, MT753X_MIRROR_REG(priv->id), val); + + val = mt7530_read(priv, MT7530_PCR_P(port)); +@@ -2538,7 +2524,7 @@ mt7531_setup_common(struct dsa_switch *d + mt7530_mib_reset(ds); + + /* Disable flooding on all ports */ +- mt7530_clear(priv, MT7530_MFC, BC_FFP_MASK | UNM_FFP_MASK | ++ mt7530_clear(priv, MT753X_MFC, BC_FFP_MASK | UNM_FFP_MASK | + UNU_FFP_MASK); + + for (i = 0; i < MT7530_NUM_PORTS; i++) { +@@ -3100,10 +3086,12 @@ mt753x_conduit_state_change(struct dsa_s + else + priv->active_cpu_ports &= ~mask; + +- if (priv->active_cpu_ports) +- val = CPU_EN | CPU_PORT(__ffs(priv->active_cpu_ports)); ++ if (priv->active_cpu_ports) { ++ val = MT7530_CPU_EN | ++ MT7530_CPU_PORT(__ffs(priv->active_cpu_ports)); ++ } + +- mt7530_rmw(priv, MT7530_MFC, CPU_EN | CPU_PORT_MASK, val); ++ mt7530_rmw(priv, MT753X_MFC, MT7530_CPU_EN | MT7530_CPU_PORT_MASK, val); + } + + static int mt7988_setup(struct dsa_switch *ds) +--- a/drivers/net/dsa/mt7530.h ++++ b/drivers/net/dsa/mt7530.h +@@ -36,36 +36,55 @@ enum mt753x_id { + #define MT753X_AGC 0xc + #define LOCAL_EN BIT(7) + +-/* Registers to mac forward control for unknown frames */ +-#define MT7530_MFC 0x10 +-#define BC_FFP(x) (((x) & 0xff) << 24) +-#define BC_FFP_MASK BC_FFP(~0) +-#define UNM_FFP(x) (((x) & 0xff) << 16) +-#define UNM_FFP_MASK UNM_FFP(~0) +-#define UNU_FFP(x) (((x) & 0xff) << 8) +-#define UNU_FFP_MASK UNU_FFP(~0) +-#define CPU_EN BIT(7) +-#define CPU_PORT_MASK GENMASK(6, 4) +-#define CPU_PORT(x) FIELD_PREP(CPU_PORT_MASK, x) +-#define MIRROR_EN BIT(3) +-#define MIRROR_PORT(x) ((x) & 0x7) +-#define MIRROR_MASK 0x7 ++/* Register for MAC forward control */ ++#define MT753X_MFC 0x10 ++#define BC_FFP_MASK GENMASK(31, 24) ++#define BC_FFP(x) FIELD_PREP(BC_FFP_MASK, x) ++#define UNM_FFP_MASK GENMASK(23, 16) ++#define UNM_FFP(x) FIELD_PREP(UNM_FFP_MASK, x) ++#define UNU_FFP_MASK GENMASK(15, 8) ++#define UNU_FFP(x) FIELD_PREP(UNU_FFP_MASK, x) ++#define MT7530_CPU_EN BIT(7) ++#define MT7530_CPU_PORT_MASK GENMASK(6, 4) ++#define MT7530_CPU_PORT(x) FIELD_PREP(MT7530_CPU_PORT_MASK, x) ++#define MT7530_MIRROR_EN BIT(3) ++#define MT7530_MIRROR_PORT_MASK GENMASK(2, 0) ++#define MT7530_MIRROR_PORT_GET(x) FIELD_GET(MT7530_MIRROR_PORT_MASK, x) ++#define MT7530_MIRROR_PORT_SET(x) FIELD_PREP(MT7530_MIRROR_PORT_MASK, x) ++#define MT7531_QRY_FFP_MASK GENMASK(7, 0) ++#define MT7531_QRY_FFP(x) FIELD_PREP(MT7531_QRY_FFP_MASK, x) + +-/* Registers for CPU forward control */ ++/* Register for CPU forward control */ + #define MT7531_CFC 0x4 + #define MT7531_MIRROR_EN BIT(19) +-#define MT7531_MIRROR_MASK (MIRROR_MASK << 16) +-#define MT7531_MIRROR_PORT_GET(x) (((x) >> 16) & MIRROR_MASK) +-#define MT7531_MIRROR_PORT_SET(x) (((x) & MIRROR_MASK) << 16) ++#define MT7531_MIRROR_PORT_MASK GENMASK(18, 16) ++#define MT7531_MIRROR_PORT_GET(x) FIELD_GET(MT7531_MIRROR_PORT_MASK, x) ++#define MT7531_MIRROR_PORT_SET(x) FIELD_PREP(MT7531_MIRROR_PORT_MASK, x) + #define MT7531_CPU_PMAP_MASK GENMASK(7, 0) + #define MT7531_CPU_PMAP(x) FIELD_PREP(MT7531_CPU_PMAP_MASK, x) + +-#define MT753X_MIRROR_REG(id) ((((id) == ID_MT7531) || ((id) == ID_MT7988)) ? \ +- MT7531_CFC : MT7530_MFC) +-#define MT753X_MIRROR_EN(id) ((((id) == ID_MT7531) || ((id) == ID_MT7988)) ? \ +- MT7531_MIRROR_EN : MIRROR_EN) +-#define MT753X_MIRROR_MASK(id) ((((id) == ID_MT7531) || ((id) == ID_MT7988)) ? \ +- MT7531_MIRROR_MASK : MIRROR_MASK) ++#define MT753X_MIRROR_REG(id) ((id == ID_MT7531 || \ ++ id == ID_MT7988) ? \ ++ MT7531_CFC : MT753X_MFC) ++ ++#define MT753X_MIRROR_EN(id) ((id == ID_MT7531 || \ ++ id == ID_MT7988) ? \ ++ MT7531_MIRROR_EN : MT7530_MIRROR_EN) ++ ++#define MT753X_MIRROR_PORT_MASK(id) ((id == ID_MT7531 || \ ++ id == ID_MT7988) ? \ ++ MT7531_MIRROR_PORT_MASK : \ ++ MT7530_MIRROR_PORT_MASK) ++ ++#define MT753X_MIRROR_PORT_GET(id, val) ((id == ID_MT7531 || \ ++ id == ID_MT7988) ? \ ++ MT7531_MIRROR_PORT_GET(val) : \ ++ MT7530_MIRROR_PORT_GET(val)) ++ ++#define MT753X_MIRROR_PORT_SET(id, val) ((id == ID_MT7531 || \ ++ id == ID_MT7988) ? \ ++ MT7531_MIRROR_PORT_SET(val) : \ ++ MT7530_MIRROR_PORT_SET(val)) + + /* Register for BPDU and PAE frame control */ + #define MT753X_BPC 0x24 diff --git a/target/linux/generic/backport-6.1/790-59-v6.10-net-dsa-mt7530-refactor-MT7530_HWTRAP-and-MT7530_MHW.patch b/target/linux/generic/backport-6.1/790-59-v6.10-net-dsa-mt7530-refactor-MT7530_HWTRAP-and-MT7530_MHW.patch new file mode 100644 index 000000000..3c487d21f --- /dev/null +++ b/target/linux/generic/backport-6.1/790-59-v6.10-net-dsa-mt7530-refactor-MT7530_HWTRAP-and-MT7530_MHW.patch @@ -0,0 +1,257 @@ +From 3ccf67597d35c06a7319e407b1c42f78a7966779 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Ar=C4=B1n=C3=A7=20=C3=9CNAL?= +Date: Mon, 22 Apr 2024 10:15:13 +0300 +Subject: [PATCH 06/15] net: dsa: mt7530: refactor MT7530_HWTRAP and + MT7530_MHWTRAP +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +The MT7530_HWTRAP and MT7530_MHWTRAP registers are on MT7530 and MT7531. +It's called hardware trap on MT7530, software trap on MT7531. That's +because some bits of the trap on MT7530 cannot be modified by software +whilst all bits of the trap on MT7531 can. Rename the definitions for them +to MT753X_TRAP and MT753X_MTRAP. Add MT7530 and MT7531 prefixes to the +definitions specific to the switch model. + +Remove the extra parentheses from MT7530_XTAL_40MHZ and MT7530_XTAL_20MHZ. + +Rename MHWTRAP_PHY0_SEL, MHWTRAP_MANUAL, and MHWTRAP_PHY_ACCESS to be on +par with the "MT7621 Giga Switch Programming Guide v0.3" document. + +Make an enumaration for the XTAL frequency. Set the data type of the xtal +variable on mt7531_pll_setup() to it. + +Signed-off-by: Arınç ÃœNAL +--- + drivers/net/dsa/mt7530.c | 59 ++++++++++++++++++++-------------------- + drivers/net/dsa/mt7530.h | 50 ++++++++++++++++------------------ + 2 files changed, 54 insertions(+), 55 deletions(-) + +--- a/drivers/net/dsa/mt7530.c ++++ b/drivers/net/dsa/mt7530.c +@@ -417,23 +417,23 @@ mt7530_setup_port6(struct dsa_switch *ds + + mt7530_rmw(priv, MT7530_P6ECR, P6_INTF_MODE_MASK, P6_INTF_MODE(1)); + +- xtal = mt7530_read(priv, MT7530_MHWTRAP) & HWTRAP_XTAL_MASK; ++ xtal = mt7530_read(priv, MT753X_MTRAP) & MT7530_XTAL_MASK; + +- if (xtal == HWTRAP_XTAL_25MHZ) ++ if (xtal == MT7530_XTAL_25MHZ) + ssc_delta = 0x57; + else + ssc_delta = 0x87; + + if (priv->id == ID_MT7621) { + /* PLL frequency: 125MHz: 1.0GBit */ +- if (xtal == HWTRAP_XTAL_40MHZ) ++ if (xtal == MT7530_XTAL_40MHZ) + ncpo1 = 0x0640; +- if (xtal == HWTRAP_XTAL_25MHZ) ++ if (xtal == MT7530_XTAL_25MHZ) + ncpo1 = 0x0a00; + } else { /* PLL frequency: 250MHz: 2.0Gbit */ +- if (xtal == HWTRAP_XTAL_40MHZ) ++ if (xtal == MT7530_XTAL_40MHZ) + ncpo1 = 0x0c80; +- if (xtal == HWTRAP_XTAL_25MHZ) ++ if (xtal == MT7530_XTAL_25MHZ) + ncpo1 = 0x1400; + } + +@@ -456,19 +456,20 @@ mt7530_setup_port6(struct dsa_switch *ds + static void + mt7531_pll_setup(struct mt7530_priv *priv) + { ++ enum mt7531_xtal_fsel xtal; + u32 top_sig; + u32 hwstrap; +- u32 xtal; + u32 val; + + val = mt7530_read(priv, MT7531_CREV); + top_sig = mt7530_read(priv, MT7531_TOP_SIG_SR); +- hwstrap = mt7530_read(priv, MT7531_HWTRAP); ++ hwstrap = mt7530_read(priv, MT753X_TRAP); + if ((val & CHIP_REV_M) > 0) +- xtal = (top_sig & PAD_MCM_SMI_EN) ? HWTRAP_XTAL_FSEL_40MHZ : +- HWTRAP_XTAL_FSEL_25MHZ; ++ xtal = (top_sig & PAD_MCM_SMI_EN) ? MT7531_XTAL_FSEL_40MHZ : ++ MT7531_XTAL_FSEL_25MHZ; + else +- xtal = hwstrap & HWTRAP_XTAL_FSEL_MASK; ++ xtal = (hwstrap & MT7531_XTAL25) ? MT7531_XTAL_FSEL_25MHZ : ++ MT7531_XTAL_FSEL_40MHZ; + + /* Step 1 : Disable MT7531 COREPLL */ + val = mt7530_read(priv, MT7531_PLLGP_EN); +@@ -497,13 +498,13 @@ mt7531_pll_setup(struct mt7530_priv *pri + usleep_range(25, 35); + + switch (xtal) { +- case HWTRAP_XTAL_FSEL_25MHZ: ++ case MT7531_XTAL_FSEL_25MHZ: + val = mt7530_read(priv, MT7531_PLLGP_CR0); + val &= ~RG_COREPLL_SDM_PCW_M; + val |= 0x140000 << RG_COREPLL_SDM_PCW_S; + mt7530_write(priv, MT7531_PLLGP_CR0, val); + break; +- case HWTRAP_XTAL_FSEL_40MHZ: ++ case MT7531_XTAL_FSEL_40MHZ: + val = mt7530_read(priv, MT7531_PLLGP_CR0); + val &= ~RG_COREPLL_SDM_PCW_M; + val |= 0x190000 << RG_COREPLL_SDM_PCW_S; +@@ -884,20 +885,20 @@ static void mt7530_setup_port5(struct ds + + mutex_lock(&priv->reg_mutex); + +- val = mt7530_read(priv, MT7530_MHWTRAP); ++ val = mt7530_read(priv, MT753X_MTRAP); + +- val |= MHWTRAP_MANUAL | MHWTRAP_P5_MAC_SEL | MHWTRAP_P5_DIS; +- val &= ~MHWTRAP_P5_RGMII_MODE & ~MHWTRAP_PHY0_SEL; ++ val |= MT7530_CHG_TRAP | MT7530_P5_MAC_SEL | MT7530_P5_DIS; ++ val &= ~MT7530_P5_RGMII_MODE & ~MT7530_P5_PHY0_SEL; + + switch (priv->p5_mode) { + /* MUX_PHY_P0: P0 -> P5 -> SoC MAC */ + case MUX_PHY_P0: +- val |= MHWTRAP_PHY0_SEL; ++ val |= MT7530_P5_PHY0_SEL; + fallthrough; + + /* MUX_PHY_P4: P4 -> P5 -> SoC MAC */ + case MUX_PHY_P4: +- val &= ~MHWTRAP_P5_MAC_SEL & ~MHWTRAP_P5_DIS; ++ val &= ~MT7530_P5_MAC_SEL & ~MT7530_P5_DIS; + + /* Setup the MAC by default for the cpu port */ + mt7530_write(priv, MT753X_PMCR_P(5), 0x56300); +@@ -905,13 +906,13 @@ static void mt7530_setup_port5(struct ds + + /* GMAC5: P5 -> SoC MAC or external PHY */ + default: +- val &= ~MHWTRAP_P5_DIS; ++ val &= ~MT7530_P5_DIS; + break; + } + + /* Setup RGMII settings */ + if (phy_interface_mode_is_rgmii(interface)) { +- val |= MHWTRAP_P5_RGMII_MODE; ++ val |= MT7530_P5_RGMII_MODE; + + /* P5 RGMII RX Clock Control: delay setting for 1000M */ + mt7530_write(priv, MT7530_P5RGMIIRXCR, CSR_RGMII_EDGE_ALIGN); +@@ -931,7 +932,7 @@ static void mt7530_setup_port5(struct ds + P5_IO_CLK_DRV(1) | P5_IO_DATA_DRV(1)); + } + +- mt7530_write(priv, MT7530_MHWTRAP, val); ++ mt7530_write(priv, MT753X_MTRAP, val); + + dev_dbg(ds->dev, "Setup P5, HWTRAP=0x%x, mode=%s, phy-mode=%s\n", val, + mt7530_p5_mode_str(priv->p5_mode), phy_modes(interface)); +@@ -2370,7 +2371,7 @@ mt7530_setup(struct dsa_switch *ds) + } + + /* Waiting for MT7530 got to stable */ +- INIT_MT7530_DUMMY_POLL(&p, priv, MT7530_HWTRAP); ++ INIT_MT7530_DUMMY_POLL(&p, priv, MT753X_TRAP); + ret = readx_poll_timeout(_mt7530_read, &p, val, val != 0, + 20, 1000000); + if (ret < 0) { +@@ -2385,7 +2386,7 @@ mt7530_setup(struct dsa_switch *ds) + return -ENODEV; + } + +- if ((val & HWTRAP_XTAL_MASK) == HWTRAP_XTAL_20MHZ) { ++ if ((val & MT7530_XTAL_MASK) == MT7530_XTAL_20MHZ) { + dev_err(priv->dev, + "MT7530 with a 20MHz XTAL is not supported!\n"); + return -EINVAL; +@@ -2406,12 +2407,12 @@ mt7530_setup(struct dsa_switch *ds) + RD_TAP_MASK, RD_TAP(16)); + + /* Enable port 6 */ +- val = mt7530_read(priv, MT7530_MHWTRAP); +- val &= ~MHWTRAP_P6_DIS & ~MHWTRAP_PHY_ACCESS; +- val |= MHWTRAP_MANUAL; +- mt7530_write(priv, MT7530_MHWTRAP, val); ++ val = mt7530_read(priv, MT753X_MTRAP); ++ val &= ~MT7530_P6_DIS & ~MT7530_PHY_INDIRECT_ACCESS; ++ val |= MT7530_CHG_TRAP; ++ mt7530_write(priv, MT753X_MTRAP, val); + +- if ((val & HWTRAP_XTAL_MASK) == HWTRAP_XTAL_40MHZ) ++ if ((val & MT7530_XTAL_MASK) == MT7530_XTAL_40MHZ) + mt7530_pll_setup(priv); + + mt753x_trap_frames(priv); +@@ -2591,7 +2592,7 @@ mt7531_setup(struct dsa_switch *ds) + } + + /* Waiting for MT7530 got to stable */ +- INIT_MT7530_DUMMY_POLL(&p, priv, MT7530_HWTRAP); ++ INIT_MT7530_DUMMY_POLL(&p, priv, MT753X_TRAP); + ret = readx_poll_timeout(_mt7530_read, &p, val, val != 0, + 20, 1000000); + if (ret < 0) { +--- a/drivers/net/dsa/mt7530.h ++++ b/drivers/net/dsa/mt7530.h +@@ -495,32 +495,30 @@ enum mt7531_clk_skew { + MT7531_CLK_SKEW_REVERSE = 3, + }; + +-/* Register for hw trap status */ +-#define MT7530_HWTRAP 0x7800 +-#define HWTRAP_XTAL_MASK (BIT(10) | BIT(9)) +-#define HWTRAP_XTAL_25MHZ (BIT(10) | BIT(9)) +-#define HWTRAP_XTAL_40MHZ (BIT(10)) +-#define HWTRAP_XTAL_20MHZ (BIT(9)) ++/* Register for trap status */ ++#define MT753X_TRAP 0x7800 ++#define MT7530_XTAL_MASK (BIT(10) | BIT(9)) ++#define MT7530_XTAL_25MHZ (BIT(10) | BIT(9)) ++#define MT7530_XTAL_40MHZ BIT(10) ++#define MT7530_XTAL_20MHZ BIT(9) ++#define MT7531_XTAL25 BIT(7) + +-#define MT7531_HWTRAP 0x7800 +-#define HWTRAP_XTAL_FSEL_MASK BIT(7) +-#define HWTRAP_XTAL_FSEL_25MHZ BIT(7) +-#define HWTRAP_XTAL_FSEL_40MHZ 0 +-/* Unique fields of (M)HWSTRAP for MT7531 */ +-#define XTAL_FSEL_S 7 +-#define XTAL_FSEL_M BIT(7) +-#define PHY_EN BIT(6) +-#define CHG_STRAP BIT(8) ++/* Register for trap modification */ ++#define MT753X_MTRAP 0x7804 ++#define MT7530_P5_PHY0_SEL BIT(20) ++#define MT7530_CHG_TRAP BIT(16) ++#define MT7530_P5_MAC_SEL BIT(13) ++#define MT7530_P6_DIS BIT(8) ++#define MT7530_P5_RGMII_MODE BIT(7) ++#define MT7530_P5_DIS BIT(6) ++#define MT7530_PHY_INDIRECT_ACCESS BIT(5) ++#define MT7531_CHG_STRAP BIT(8) ++#define MT7531_PHY_EN BIT(6) + +-/* Register for hw trap modification */ +-#define MT7530_MHWTRAP 0x7804 +-#define MHWTRAP_PHY0_SEL BIT(20) +-#define MHWTRAP_MANUAL BIT(16) +-#define MHWTRAP_P5_MAC_SEL BIT(13) +-#define MHWTRAP_P6_DIS BIT(8) +-#define MHWTRAP_P5_RGMII_MODE BIT(7) +-#define MHWTRAP_P5_DIS BIT(6) +-#define MHWTRAP_PHY_ACCESS BIT(5) ++enum mt7531_xtal_fsel { ++ MT7531_XTAL_FSEL_25MHZ, ++ MT7531_XTAL_FSEL_40MHZ, ++}; + + /* Register for TOP signal control */ + #define MT7530_TOP_SIG_CTRL 0x7808 diff --git a/target/linux/generic/backport-6.1/790-60-v6.10-net-dsa-mt7530-move-MT753X_MTRAP-operations-for-MT75.patch b/target/linux/generic/backport-6.1/790-60-v6.10-net-dsa-mt7530-move-MT753X_MTRAP-operations-for-MT75.patch new file mode 100644 index 000000000..cfc38f81d --- /dev/null +++ b/target/linux/generic/backport-6.1/790-60-v6.10-net-dsa-mt7530-move-MT753X_MTRAP-operations-for-MT75.patch @@ -0,0 +1,117 @@ +From 2982f395c9a513b168f1e685588f70013cba2f5f Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Ar=C4=B1n=C3=A7=20=C3=9CNAL?= +Date: Mon, 22 Apr 2024 10:15:14 +0300 +Subject: [PATCH 07/15] net: dsa: mt7530: move MT753X_MTRAP operations for + MT7530 +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +On MT7530, the media-independent interfaces of port 5 and 6 are controlled +by the MT7530_P5_DIS and MT7530_P6_DIS bits of the hardware trap. Deal with +these bits only when the relevant port is being enabled or disabled. This +ensures that these ports will be disabled when they are not in use. + +Do not set MT7530_CHG_TRAP on mt7530_setup_port5() as that's already being +done on mt7530_setup(). + +Instead of globally setting MT7530_P5_MAC_SEL, clear it, then set it only +on the appropriate case. + +If PHY muxing is detected, clear MT7530_P5_DIS before calling +mt7530_setup_port5(). + +Signed-off-by: Arınç ÃœNAL +--- + drivers/net/dsa/mt7530.c | 38 +++++++++++++++++++++++++++----------- + 1 file changed, 27 insertions(+), 11 deletions(-) + +--- a/drivers/net/dsa/mt7530.c ++++ b/drivers/net/dsa/mt7530.c +@@ -887,8 +887,7 @@ static void mt7530_setup_port5(struct ds + + val = mt7530_read(priv, MT753X_MTRAP); + +- val |= MT7530_CHG_TRAP | MT7530_P5_MAC_SEL | MT7530_P5_DIS; +- val &= ~MT7530_P5_RGMII_MODE & ~MT7530_P5_PHY0_SEL; ++ val &= ~MT7530_P5_PHY0_SEL & ~MT7530_P5_MAC_SEL & ~MT7530_P5_RGMII_MODE; + + switch (priv->p5_mode) { + /* MUX_PHY_P0: P0 -> P5 -> SoC MAC */ +@@ -898,15 +897,13 @@ static void mt7530_setup_port5(struct ds + + /* MUX_PHY_P4: P4 -> P5 -> SoC MAC */ + case MUX_PHY_P4: +- val &= ~MT7530_P5_MAC_SEL & ~MT7530_P5_DIS; +- + /* Setup the MAC by default for the cpu port */ + mt7530_write(priv, MT753X_PMCR_P(5), 0x56300); + break; + + /* GMAC5: P5 -> SoC MAC or external PHY */ + default: +- val &= ~MT7530_P5_DIS; ++ val |= MT7530_P5_MAC_SEL; + break; + } + +@@ -1200,6 +1197,14 @@ mt7530_port_enable(struct dsa_switch *ds + + mutex_unlock(&priv->reg_mutex); + ++ if (priv->id != ID_MT7530 && priv->id != ID_MT7621) ++ return 0; ++ ++ if (port == 5) ++ mt7530_clear(priv, MT753X_MTRAP, MT7530_P5_DIS); ++ else if (port == 6) ++ mt7530_clear(priv, MT753X_MTRAP, MT7530_P6_DIS); ++ + return 0; + } + +@@ -1218,6 +1223,14 @@ mt7530_port_disable(struct dsa_switch *d + PCR_MATRIX_CLR); + + mutex_unlock(&priv->reg_mutex); ++ ++ if (priv->id != ID_MT7530 && priv->id != ID_MT7621) ++ return; ++ ++ if (port == 5) ++ mt7530_set(priv, MT753X_MTRAP, MT7530_P5_DIS); ++ else if (port == 6) ++ mt7530_set(priv, MT753X_MTRAP, MT7530_P6_DIS); + } + + static int +@@ -2406,11 +2419,11 @@ mt7530_setup(struct dsa_switch *ds) + mt7530_rmw(priv, MT7530_TRGMII_RD(i), + RD_TAP_MASK, RD_TAP(16)); + +- /* Enable port 6 */ +- val = mt7530_read(priv, MT753X_MTRAP); +- val &= ~MT7530_P6_DIS & ~MT7530_PHY_INDIRECT_ACCESS; +- val |= MT7530_CHG_TRAP; +- mt7530_write(priv, MT753X_MTRAP, val); ++ /* Allow modifying the trap and directly access PHY registers via the ++ * MDIO bus the switch is on. ++ */ ++ mt7530_rmw(priv, MT753X_MTRAP, MT7530_CHG_TRAP | ++ MT7530_PHY_INDIRECT_ACCESS, MT7530_CHG_TRAP); + + if ((val & MT7530_XTAL_MASK) == MT7530_XTAL_40MHZ) + mt7530_pll_setup(priv); +@@ -2493,8 +2506,11 @@ mt7530_setup(struct dsa_switch *ds) + break; + } + +- if (priv->p5_mode == MUX_PHY_P0 || priv->p5_mode == MUX_PHY_P4) ++ if (priv->p5_mode == MUX_PHY_P0 || ++ priv->p5_mode == MUX_PHY_P4) { ++ mt7530_clear(priv, MT753X_MTRAP, MT7530_P5_DIS); + mt7530_setup_port5(ds, interface); ++ } + } + + #ifdef CONFIG_GPIOLIB diff --git a/target/linux/generic/backport-6.1/790-61-v6.10-net-dsa-mt7530-return-mt7530_setup_mdio-mt7531_setup.patch b/target/linux/generic/backport-6.1/790-61-v6.10-net-dsa-mt7530-return-mt7530_setup_mdio-mt7531_setup.patch new file mode 100644 index 000000000..178ac8022 --- /dev/null +++ b/target/linux/generic/backport-6.1/790-61-v6.10-net-dsa-mt7530-return-mt7530_setup_mdio-mt7531_setup.patch @@ -0,0 +1,39 @@ +From 1f5669efca65564c7533704917f79003c6b36c9c Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Ar=C4=B1n=C3=A7=20=C3=9CNAL?= +Date: Mon, 22 Apr 2024 10:15:15 +0300 +Subject: [PATCH 08/15] net: dsa: mt7530: return mt7530_setup_mdio & + mt7531_setup_common on error +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +The mt7530_setup_mdio() and mt7531_setup_common() functions should be +checked for errors. Return if the functions return a non-zero value. + +Signed-off-by: Arınç ÃœNAL +--- + drivers/net/dsa/mt7530.c | 6 +++++- + 1 file changed, 5 insertions(+), 1 deletion(-) + +--- a/drivers/net/dsa/mt7530.c ++++ b/drivers/net/dsa/mt7530.c +@@ -2672,7 +2672,9 @@ mt7531_setup(struct dsa_switch *ds) + 0); + } + +- mt7531_setup_common(ds); ++ ret = mt7531_setup_common(ds); ++ if (ret) ++ return ret; + + /* Setup VLAN ID 0 for VLAN-unaware bridges */ + ret = mt7530_setup_vlan0(priv); +@@ -3031,6 +3033,8 @@ mt753x_setup(struct dsa_switch *ds) + ret = mt7530_setup_mdio(priv); + if (ret && priv->irq) + mt7530_free_irq_common(priv); ++ if (ret) ++ return ret; + + /* Initialise the PCS devices */ + for (i = 0; i < priv->ds->num_ports; i++) { diff --git a/target/linux/generic/backport-6.1/790-62-v6.10-net-dsa-mt7530-define-MAC-speed-capabilities-per-swi.patch b/target/linux/generic/backport-6.1/790-62-v6.10-net-dsa-mt7530-define-MAC-speed-capabilities-per-swi.patch new file mode 100644 index 000000000..af8edf5a4 --- /dev/null +++ b/target/linux/generic/backport-6.1/790-62-v6.10-net-dsa-mt7530-define-MAC-speed-capabilities-per-swi.patch @@ -0,0 +1,75 @@ +From 6cc2d4ccd77509df74b7b8ef46bbc6ba0a571318 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Ar=C4=B1n=C3=A7=20=C3=9CNAL?= +Date: Mon, 22 Apr 2024 10:15:16 +0300 +Subject: [PATCH 09/15] net: dsa: mt7530: define MAC speed capabilities per + switch model +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +With the support of the MT7988 SoC switch, the MAC speed capabilities +defined on mt753x_phylink_get_caps() won't apply to all switch models +anymore. Move them to more appropriate locations instead of overwriting +config->mac_capabilities. + +Remove the comment on mt753x_phylink_get_caps() as it's become invalid with +the support of MT7531 and MT7988 SoC switch. + +Add break to case 6 of mt7988_mac_port_get_caps() to be explicit. + +Signed-off-by: Arınç ÃœNAL +--- + drivers/net/dsa/mt7530.c | 15 ++++++++++----- + 1 file changed, 10 insertions(+), 5 deletions(-) + +--- a/drivers/net/dsa/mt7530.c ++++ b/drivers/net/dsa/mt7530.c +@@ -2690,6 +2690,8 @@ mt7531_setup(struct dsa_switch *ds) + static void mt7530_mac_port_get_caps(struct dsa_switch *ds, int port, + struct phylink_config *config) + { ++ config->mac_capabilities |= MAC_10 | MAC_100 | MAC_1000FD; ++ + switch (port) { + /* Ports which are connected to switch PHYs. There is no MII pinout. */ + case 0 ... 4: +@@ -2721,6 +2723,8 @@ static void mt7531_mac_port_get_caps(str + { + struct mt7530_priv *priv = ds->priv; + ++ config->mac_capabilities |= MAC_10 | MAC_100 | MAC_1000FD; ++ + switch (port) { + /* Ports which are connected to switch PHYs. There is no MII pinout. */ + case 0 ... 4: +@@ -2760,14 +2764,17 @@ static void mt7988_mac_port_get_caps(str + case 0 ... 3: + __set_bit(PHY_INTERFACE_MODE_INTERNAL, + config->supported_interfaces); ++ ++ config->mac_capabilities |= MAC_10 | MAC_100 | MAC_1000FD; + break; + + /* Port 6 is connected to SoC's XGMII MAC. There is no MII pinout. */ + case 6: + __set_bit(PHY_INTERFACE_MODE_INTERNAL, + config->supported_interfaces); +- config->mac_capabilities = MAC_ASYM_PAUSE | MAC_SYM_PAUSE | +- MAC_10000FD; ++ ++ config->mac_capabilities |= MAC_10000FD; ++ break; + } + } + +@@ -2937,9 +2944,7 @@ static void mt753x_phylink_get_caps(stru + { + struct mt7530_priv *priv = ds->priv; + +- /* This switch only supports full-duplex at 1Gbps */ +- config->mac_capabilities = MAC_ASYM_PAUSE | MAC_SYM_PAUSE | +- MAC_10 | MAC_100 | MAC_1000FD; ++ config->mac_capabilities = MAC_ASYM_PAUSE | MAC_SYM_PAUSE; + + /* This driver does not make use of the speed, duplex, pause or the + * advertisement in its mac_config, so it is safe to mark this driver diff --git a/target/linux/generic/backport-6.1/790-63-v6.10-net-dsa-mt7530-get-rid-of-function-sanity-check.patch b/target/linux/generic/backport-6.1/790-63-v6.10-net-dsa-mt7530-get-rid-of-function-sanity-check.patch new file mode 100644 index 000000000..3825952dc --- /dev/null +++ b/target/linux/generic/backport-6.1/790-63-v6.10-net-dsa-mt7530-get-rid-of-function-sanity-check.patch @@ -0,0 +1,33 @@ +From dd0f15fc877c10567699190bce0f55e96f4ad6b5 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Ar=C4=B1n=C3=A7=20=C3=9CNAL?= +Date: Mon, 22 Apr 2024 10:15:17 +0300 +Subject: [PATCH 10/15] net: dsa: mt7530: get rid of function sanity check +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Get rid of checking whether functions are filled properly. priv->info which +is an mt753x_info structure is filled and checked for before this check. +It's unnecessary checking whether it's filled properly. + +Signed-off-by: Arınç ÃœNAL +--- + drivers/net/dsa/mt7530.c | 7 ------- + 1 file changed, 7 deletions(-) + +--- a/drivers/net/dsa/mt7530.c ++++ b/drivers/net/dsa/mt7530.c +@@ -3235,13 +3235,6 @@ mt7530_probe_common(struct mt7530_priv * + if (!priv->info) + return -EINVAL; + +- /* Sanity check if these required device operations are filled +- * properly. +- */ +- if (!priv->info->sw_setup || !priv->info->phy_read || +- !priv->info->phy_write || !priv->info->mac_port_get_caps) +- return -EINVAL; +- + priv->id = priv->info->id; + priv->dev = dev; + priv->ds->priv = priv; diff --git a/target/linux/generic/backport-6.1/790-64-v6.10-net-dsa-mt7530-refactor-MT7530_PMEEECR_P.patch b/target/linux/generic/backport-6.1/790-64-v6.10-net-dsa-mt7530-refactor-MT7530_PMEEECR_P.patch new file mode 100644 index 000000000..df4745801 --- /dev/null +++ b/target/linux/generic/backport-6.1/790-64-v6.10-net-dsa-mt7530-refactor-MT7530_PMEEECR_P.patch @@ -0,0 +1,71 @@ +From 2dff9759602b069f97ccc939e15a47ca051b2983 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Ar=C4=B1n=C3=A7=20=C3=9CNAL?= +Date: Mon, 22 Apr 2024 10:15:18 +0300 +Subject: [PATCH 11/15] net: dsa: mt7530: refactor MT7530_PMEEECR_P() +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +The MT7530_PMEEECR_P() register is on MT7530, MT7531, and the switch on the +MT7988 SoC. Rename the definition for them to MT753X_PMEEECR_P(). Use the +FIELD_PREP and FIELD_GET macros. Rename GET_LPI_THRESH() and +SET_LPI_THRESH() to LPI_THRESH_GET() and LPI_THRESH_SET(). + +Signed-off-by: Arınç ÃœNAL +--- + drivers/net/dsa/mt7530.c | 8 ++++---- + drivers/net/dsa/mt7530.h | 13 +++++++------ + 2 files changed, 11 insertions(+), 10 deletions(-) + +--- a/drivers/net/dsa/mt7530.c ++++ b/drivers/net/dsa/mt7530.c +@@ -3062,10 +3062,10 @@ static int mt753x_get_mac_eee(struct dsa + struct ethtool_eee *e) + { + struct mt7530_priv *priv = ds->priv; +- u32 eeecr = mt7530_read(priv, MT7530_PMEEECR_P(port)); ++ u32 eeecr = mt7530_read(priv, MT753X_PMEEECR_P(port)); + + e->tx_lpi_enabled = !(eeecr & LPI_MODE_EN); +- e->tx_lpi_timer = GET_LPI_THRESH(eeecr); ++ e->tx_lpi_timer = LPI_THRESH_GET(eeecr); + + return 0; + } +@@ -3079,11 +3079,11 @@ static int mt753x_set_mac_eee(struct dsa + if (e->tx_lpi_timer > 0xFFF) + return -EINVAL; + +- set = SET_LPI_THRESH(e->tx_lpi_timer); ++ set = LPI_THRESH_SET(e->tx_lpi_timer); + if (!e->tx_lpi_enabled) + /* Force LPI Mode without a delay */ + set |= LPI_MODE_EN; +- mt7530_rmw(priv, MT7530_PMEEECR_P(port), mask, set); ++ mt7530_rmw(priv, MT753X_PMEEECR_P(port), mask, set); + + return 0; + } +--- a/drivers/net/dsa/mt7530.h ++++ b/drivers/net/dsa/mt7530.h +@@ -364,13 +364,14 @@ enum mt7530_vlan_port_acc_frm { + PMCR_FORCE_SPEED_100 | \ + PMCR_FORCE_FDX | PMCR_FORCE_LNK) + +-#define MT7530_PMEEECR_P(x) (0x3004 + (x) * 0x100) +-#define WAKEUP_TIME_1000(x) (((x) & 0xFF) << 24) +-#define WAKEUP_TIME_100(x) (((x) & 0xFF) << 16) ++#define MT753X_PMEEECR_P(x) (0x3004 + (x) * 0x100) ++#define WAKEUP_TIME_1000_MASK GENMASK(31, 24) ++#define WAKEUP_TIME_1000(x) FIELD_PREP(WAKEUP_TIME_1000_MASK, x) ++#define WAKEUP_TIME_100_MASK GENMASK(23, 16) ++#define WAKEUP_TIME_100(x) FIELD_PREP(WAKEUP_TIME_100_MASK, x) + #define LPI_THRESH_MASK GENMASK(15, 4) +-#define LPI_THRESH_SHT 4 +-#define SET_LPI_THRESH(x) (((x) << LPI_THRESH_SHT) & LPI_THRESH_MASK) +-#define GET_LPI_THRESH(x) (((x) & LPI_THRESH_MASK) >> LPI_THRESH_SHT) ++#define LPI_THRESH_GET(x) FIELD_GET(LPI_THRESH_MASK, x) ++#define LPI_THRESH_SET(x) FIELD_PREP(LPI_THRESH_MASK, x) + #define LPI_MODE_EN BIT(0) + + #define MT7530_PMSR_P(x) (0x3008 + (x) * 0x100) diff --git a/target/linux/generic/backport-6.1/790-65-v6.10-net-dsa-mt7530-get-rid-of-mac_port_validate-member-o.patch b/target/linux/generic/backport-6.1/790-65-v6.10-net-dsa-mt7530-get-rid-of-mac_port_validate-member-o.patch new file mode 100644 index 000000000..7ce2d4c04 --- /dev/null +++ b/target/linux/generic/backport-6.1/790-65-v6.10-net-dsa-mt7530-get-rid-of-mac_port_validate-member-o.patch @@ -0,0 +1,46 @@ +From 21d67c2fabfe40baf33202d3287b67b6c16f8382 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Ar=C4=B1n=C3=A7=20=C3=9CNAL?= +Date: Mon, 22 Apr 2024 10:15:19 +0300 +Subject: [PATCH 12/15] net: dsa: mt7530: get rid of mac_port_validate member + of mt753x_info +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +The mac_port_validate member of the mt753x_info structure is not being +used, remove it. Improve the member description section in the process. + +Signed-off-by: Arınç ÃœNAL +--- + drivers/net/dsa/mt7530.h | 10 +++------- + 1 file changed, 3 insertions(+), 7 deletions(-) + +--- a/drivers/net/dsa/mt7530.h ++++ b/drivers/net/dsa/mt7530.h +@@ -743,13 +743,12 @@ struct mt753x_pcs { + + /* struct mt753x_info - This is the main data structure for holding the specific + * part for each supported device ++ * @id: Holding the identifier to a switch model ++ * @pcs_ops: Holding the pointer to the MAC PCS operations structure + * @sw_setup: Holding the handler to a device initialization + * @phy_read: Holding the way reading PHY port + * @phy_write: Holding the way writing PHY port +- * @phy_mode_supported: Check if the PHY type is being supported on a certain +- * port +- * @mac_port_validate: Holding the way to set addition validate type for a +- * certan MAC port ++ * @mac_port_get_caps: Holding the handler that provides MAC capabilities + * @mac_port_config: Holding the way setting up the PHY attribute to a + * certain MAC port + */ +@@ -763,9 +762,6 @@ struct mt753x_info { + int (*phy_write)(struct mt7530_priv *priv, int port, int regnum, u16 val); + void (*mac_port_get_caps)(struct dsa_switch *ds, int port, + struct phylink_config *config); +- void (*mac_port_validate)(struct dsa_switch *ds, int port, +- phy_interface_t interface, +- unsigned long *supported); + void (*mac_port_config)(struct dsa_switch *ds, int port, + unsigned int mode, + phy_interface_t interface); diff --git a/target/linux/generic/backport-6.1/790-66-v6.10-net-dsa-mt7530-use-priv-ds-num_ports-instead-of-MT75.patch b/target/linux/generic/backport-6.1/790-66-v6.10-net-dsa-mt7530-use-priv-ds-num_ports-instead-of-MT75.patch new file mode 100644 index 000000000..e9512421c --- /dev/null +++ b/target/linux/generic/backport-6.1/790-66-v6.10-net-dsa-mt7530-use-priv-ds-num_ports-instead-of-MT75.patch @@ -0,0 +1,57 @@ +From 6efc8ae3eb0363328f479191a0cf0dc12a16e090 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Ar=C4=B1n=C3=A7=20=C3=9CNAL?= +Date: Mon, 22 Apr 2024 10:15:20 +0300 +Subject: [PATCH 13/15] net: dsa: mt7530: use priv->ds->num_ports instead of + MT7530_NUM_PORTS +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Use priv->ds->num_ports on all for loops which configure the switch +registers. In the future, the value of MT7530_NUM_PORTS will depend on +priv->id. Therefore, this change prepares the subdriver for a simpler +implementation. + +Signed-off-by: Arınç ÃœNAL +--- + drivers/net/dsa/mt7530.c | 8 ++++---- + 1 file changed, 4 insertions(+), 4 deletions(-) + +--- a/drivers/net/dsa/mt7530.c ++++ b/drivers/net/dsa/mt7530.c +@@ -1418,7 +1418,7 @@ mt7530_port_set_vlan_unaware(struct dsa_ + mt7530_rmw(priv, MT7530_PPBV1_P(port), G0_PORT_VID_MASK, + G0_PORT_VID_DEF); + +- for (i = 0; i < MT7530_NUM_PORTS; i++) { ++ for (i = 0; i < priv->ds->num_ports; i++) { + if (dsa_is_user_port(ds, i) && + dsa_port_is_vlan_filtering(dsa_to_port(ds, i))) { + all_user_ports_removed = false; +@@ -2433,7 +2433,7 @@ mt7530_setup(struct dsa_switch *ds) + /* Enable and reset MIB counters */ + mt7530_mib_reset(ds); + +- for (i = 0; i < MT7530_NUM_PORTS; i++) { ++ for (i = 0; i < priv->ds->num_ports; i++) { + /* Clear link settings and enable force mode to force link down + * on all ports until they're enabled later. + */ +@@ -2544,7 +2544,7 @@ mt7531_setup_common(struct dsa_switch *d + mt7530_clear(priv, MT753X_MFC, BC_FFP_MASK | UNM_FFP_MASK | + UNU_FFP_MASK); + +- for (i = 0; i < MT7530_NUM_PORTS; i++) { ++ for (i = 0; i < priv->ds->num_ports; i++) { + /* Clear link settings and enable force mode to force link down + * on all ports until they're enabled later. + */ +@@ -2631,7 +2631,7 @@ mt7531_setup(struct dsa_switch *ds) + priv->p5_sgmii = !!(val & PAD_DUAL_SGMII_EN); + + /* Force link down on all ports before internal reset */ +- for (i = 0; i < MT7530_NUM_PORTS; i++) ++ for (i = 0; i < priv->ds->num_ports; i++) + mt7530_write(priv, MT753X_PMCR_P(i), MT7531_FORCE_MODE_LNK); + + /* Reset the switch through internal reset */ diff --git a/target/linux/generic/backport-6.1/790-67-v6.10-net-dsa-mt7530-do-not-pass-port-variable-to-mt7531_r.patch b/target/linux/generic/backport-6.1/790-67-v6.10-net-dsa-mt7530-do-not-pass-port-variable-to-mt7531_r.patch new file mode 100644 index 000000000..3b3330bdc --- /dev/null +++ b/target/linux/generic/backport-6.1/790-67-v6.10-net-dsa-mt7530-do-not-pass-port-variable-to-mt7531_r.patch @@ -0,0 +1,37 @@ +From 4794c12e3aefe05dd0063c2b6b0101854b143bac Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Ar=C4=B1n=C3=A7=20=C3=9CNAL?= +Date: Mon, 22 Apr 2024 10:15:21 +0300 +Subject: [PATCH 14/15] net: dsa: mt7530: do not pass port variable to + mt7531_rgmii_setup() +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +The mt7531_rgmii_setup() function does not use the port variable, do not +pass the variable to it. + +Signed-off-by: Arınç ÃœNAL +--- + drivers/net/dsa/mt7530.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/drivers/net/dsa/mt7530.c ++++ b/drivers/net/dsa/mt7530.c +@@ -2790,7 +2790,7 @@ mt7530_mac_config(struct dsa_switch *ds, + mt7530_setup_port6(priv->ds, interface); + } + +-static void mt7531_rgmii_setup(struct mt7530_priv *priv, u32 port, ++static void mt7531_rgmii_setup(struct mt7530_priv *priv, + phy_interface_t interface, + struct phy_device *phydev) + { +@@ -2841,7 +2841,7 @@ mt7531_mac_config(struct dsa_switch *ds, + if (phy_interface_mode_is_rgmii(interface)) { + dp = dsa_to_port(ds, port); + phydev = dp->slave->phydev; +- mt7531_rgmii_setup(priv, port, interface, phydev); ++ mt7531_rgmii_setup(priv, interface, phydev); + } + } + diff --git a/target/linux/generic/backport-6.1/790-68-v6.10-net-dsa-mt7530-explain-exposing-MDIO-bus-of-MT7531AE.patch b/target/linux/generic/backport-6.1/790-68-v6.10-net-dsa-mt7530-explain-exposing-MDIO-bus-of-MT7531AE.patch new file mode 100644 index 000000000..6d28e5b5f --- /dev/null +++ b/target/linux/generic/backport-6.1/790-68-v6.10-net-dsa-mt7530-explain-exposing-MDIO-bus-of-MT7531AE.patch @@ -0,0 +1,33 @@ +From c45832fe783f468aaaace09ae95a30cbf0acf724 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Ar=C4=B1n=C3=A7=20=C3=9CNAL?= +Date: Mon, 22 Apr 2024 10:15:22 +0300 +Subject: [PATCH 15/15] net: dsa: mt7530: explain exposing MDIO bus of MT7531AE + better +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Unlike MT7531BE, the GPIO 6-12 pins are not used for RGMII on MT7531AE. +Therefore, the GPIO 11-12 pins are set to function as MDC and MDIO to +expose the MDIO bus of the switch. Replace the comment with a better +explanation. + +Signed-off-by: Arınç ÃœNAL +--- + drivers/net/dsa/mt7530.c | 5 ++++- + 1 file changed, 4 insertions(+), 1 deletion(-) + +--- a/drivers/net/dsa/mt7530.c ++++ b/drivers/net/dsa/mt7530.c +@@ -2640,7 +2640,10 @@ mt7531_setup(struct dsa_switch *ds) + if (!priv->p5_sgmii) { + mt7531_pll_setup(priv); + } else { +- /* Let ds->slave_mii_bus be able to access external phy. */ ++ /* Unlike MT7531BE, the GPIO 6-12 pins are not used for RGMII on ++ * MT7531AE. Set the GPIO 11-12 pins to function as MDC and MDIO ++ * to expose the MDIO bus of the switch. ++ */ + mt7530_rmw(priv, MT7531_GPIO_MODE1, MT7531_GPIO11_RG_RXD2_MASK, + MT7531_EXT_P_MDC_11); + mt7530_rmw(priv, MT7531_GPIO_MODE1, MT7531_GPIO12_RG_RXD3_MASK, diff --git a/target/linux/generic/backport-6.1/790-69-v6.10-net-dsa-mt7530-do-not-set-MT7530_P5_DIS-when-PHY-.patch b/target/linux/generic/backport-6.1/790-69-v6.10-net-dsa-mt7530-do-not-set-MT7530_P5_DIS-when-PHY-.patch new file mode 100644 index 000000000..29079e03c --- /dev/null +++ b/target/linux/generic/backport-6.1/790-69-v6.10-net-dsa-mt7530-do-not-set-MT7530_P5_DIS-when-PHY-.patch @@ -0,0 +1,45 @@ +From 16e6592cd5c5bd74d8890973489f60176c692614 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Ar=C4=B1n=C3=A7=20=C3=9CNAL?= +Date: Sun, 28 Apr 2024 12:19:58 +0300 +Subject: [PATCH] net: dsa: mt7530: do not set MT7530_P5_DIS when PHY muxing is + being used +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +DSA initalises the ds->num_ports amount of ports in +dsa_switch_touch_ports(). When the PHY muxing feature is in use, port 5 +won't be defined in the device tree. Because of this, the type member of +the dsa_port structure for this port will be assigned DSA_PORT_TYPE_UNUSED. +The dsa_port_setup() function calls ds->ops->port_disable() when the port +type is DSA_PORT_TYPE_UNUSED. + +The MT7530_P5_DIS bit is unset in mt7530_setup() when PHY muxing is being +used. mt7530_port_disable() which is assigned to ds->ops->port_disable() is +called afterwards. Currently, mt7530_port_disable() sets MT7530_P5_DIS +which breaks network connectivity when PHY muxing is being used. + +Therefore, do not set MT7530_P5_DIS when PHY muxing is being used. + +Fixes: 377174c5760c ("net: dsa: mt7530: move MT753X_MTRAP operations for MT7530") +Reported-by: Daniel Golle +Signed-off-by: Arınç ÃœNAL +Reviewed-by: Andrew Lunn +Link: https://lore.kernel.org/r/20240428-for-netnext-mt7530-do-not-disable-port5-when-phy-muxing-v2-1-bb7c37d293f8@arinc9.com +Signed-off-by: Paolo Abeni +--- + drivers/net/dsa/mt7530.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +--- a/drivers/net/dsa/mt7530.c ++++ b/drivers/net/dsa/mt7530.c +@@ -1227,7 +1227,8 @@ mt7530_port_disable(struct dsa_switch *d + if (priv->id != ID_MT7530 && priv->id != ID_MT7621) + return; + +- if (port == 5) ++ /* Do not set MT7530_P5_DIS when port 5 is being used for PHY muxing. */ ++ if (port == 5 && priv->p5_mode == GMAC5) + mt7530_set(priv, MT753X_MTRAP, MT7530_P5_DIS); + else if (port == 6) + mt7530_set(priv, MT753X_MTRAP, MT7530_P6_DIS); diff --git a/target/linux/generic/backport-6.1/790-70-v6.10-796-net-dsa-mt7530-detect-PHY-muxing-when-PHY-is-defined.patch b/target/linux/generic/backport-6.1/790-70-v6.10-796-net-dsa-mt7530-detect-PHY-muxing-when-PHY-is-defined.patch new file mode 100644 index 000000000..69bbb8e22 --- /dev/null +++ b/target/linux/generic/backport-6.1/790-70-v6.10-796-net-dsa-mt7530-detect-PHY-muxing-when-PHY-is-defined.patch @@ -0,0 +1,45 @@ +From d8dcf5bd6d0eace9f7c1daa14b63b3925b09d033 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Ar=C4=B1n=C3=A7=20=C3=9CNAL?= +Date: Tue, 30 Apr 2024 08:01:33 +0300 +Subject: [PATCH] net: dsa: mt7530: detect PHY muxing when PHY is defined on + switch MDIO bus +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Currently, the MT7530 DSA subdriver configures the MT7530 switch to provide +direct access to switch PHYs, meaning, the switch PHYs listen on the MDIO +bus the switch listens on. The PHY muxing feature makes use of this. + +This is problematic as the PHY may be attached before the switch is +initialised, in which case, the PHY will fail to be attached. + +Since commit 91374ba537bd ("net: dsa: mt7530: support OF-based registration +of switch MDIO bus"), we can describe the switch PHYs on the MDIO bus of +the switch on the device tree. Extend the check to detect PHY muxing when +the PHY is defined on the MDIO bus of the switch on the device tree. + +When the PHY is described this way, the switch will be initialised first, +then the switch MDIO bus will be registered. Only after these steps, the +PHY will be attached. + +Signed-off-by: Arınç ÃœNAL +Reviewed-by: Daniel Golle +Link: https://lore.kernel.org/r/20240430-b4-for-netnext-mt7530-use-switch-mdio-bus-for-phy-muxing-v2-1-9104d886d0db@arinc9.com +Signed-off-by: Paolo Abeni +--- + drivers/net/dsa/mt7530.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +--- a/drivers/net/dsa/mt7530.c ++++ b/drivers/net/dsa/mt7530.c +@@ -2489,7 +2489,8 @@ mt7530_setup(struct dsa_switch *ds) + if (!phy_node) + continue; + +- if (phy_node->parent == priv->dev->of_node->parent) { ++ if (phy_node->parent == priv->dev->of_node->parent || ++ phy_node->parent->parent == priv->dev->of_node) { + ret = of_get_phy_mode(mac_np, &interface); + if (ret && ret != -ENODEV) { + of_node_put(mac_np); diff --git a/target/linux/generic/backport-6.1/791-v6.2-01-net-phy-Add-driver-for-Motorcomm-yt8521-gigabit-ethernet.patch b/target/linux/generic/backport-6.1/791-v6.2-01-net-phy-Add-driver-for-Motorcomm-yt8521-gigabit-ethernet.patch index e366def8f..3cf98acc8 100644 --- a/target/linux/generic/backport-6.1/791-v6.2-01-net-phy-Add-driver-for-Motorcomm-yt8521-gigabit-ethernet.patch +++ b/target/linux/generic/backport-6.1/791-v6.2-01-net-phy-Add-driver-for-Motorcomm-yt8521-gigabit-ethernet.patch @@ -31,7 +31,7 @@ Signed-off-by: David S. Miller F: drivers/net/phy/motorcomm.c --- a/drivers/net/phy/Kconfig +++ b/drivers/net/phy/Kconfig -@@ -260,7 +260,7 @@ config MOTORCOMM_PHY +@@ -257,7 +257,7 @@ config MOTORCOMM_PHY tristate "Motorcomm PHYs" help Enables support for Motorcomm network PHYs. diff --git a/target/linux/generic/backport-6.1/791-v6.2-03-net-phy-add-Motorcomm-YT8531S-phy-id.patch b/target/linux/generic/backport-6.1/791-v6.2-03-net-phy-add-Motorcomm-YT8531S-phy-id.patch index a2f22ee99..d22cc6942 100644 --- a/target/linux/generic/backport-6.1/791-v6.2-03-net-phy-add-Motorcomm-YT8531S-phy-id.patch +++ b/target/linux/generic/backport-6.1/791-v6.2-03-net-phy-add-Motorcomm-YT8531S-phy-id.patch @@ -22,7 +22,7 @@ Signed-off-by: David S. Miller --- a/drivers/net/phy/Kconfig +++ b/drivers/net/phy/Kconfig -@@ -260,7 +260,7 @@ config MOTORCOMM_PHY +@@ -257,7 +257,7 @@ config MOTORCOMM_PHY tristate "Motorcomm PHYs" help Enables support for Motorcomm network PHYs. diff --git a/target/linux/generic/backport-6.1/791-v6.3-09-net-phy-Add-driver-for-Motorcomm-yt8531-gigabit-ethernet.patch b/target/linux/generic/backport-6.1/791-v6.3-09-net-phy-Add-driver-for-Motorcomm-yt8531-gigabit-ethernet.patch index 35495978e..60eea4fa4 100644 --- a/target/linux/generic/backport-6.1/791-v6.3-09-net-phy-Add-driver-for-Motorcomm-yt8531-gigabit-ethernet.patch +++ b/target/linux/generic/backport-6.1/791-v6.3-09-net-phy-Add-driver-for-Motorcomm-yt8531-gigabit-ethernet.patch @@ -19,7 +19,7 @@ Signed-off-by: David S. Miller --- a/drivers/net/phy/Kconfig +++ b/drivers/net/phy/Kconfig -@@ -260,7 +260,7 @@ config MOTORCOMM_PHY +@@ -257,7 +257,7 @@ config MOTORCOMM_PHY tristate "Motorcomm PHYs" help Enables support for Motorcomm network PHYs. diff --git a/target/linux/generic/backport-6.1/792-v6.6-net-phylink-add-pcs_enable-pcs_disable-methods.patch b/target/linux/generic/backport-6.1/792-v6.6-net-phylink-add-pcs_enable-pcs_disable-methods.patch index eac8966a4..f82c8fc62 100644 --- a/target/linux/generic/backport-6.1/792-v6.6-net-phylink-add-pcs_enable-pcs_disable-methods.patch +++ b/target/linux/generic/backport-6.1/792-v6.6-net-phylink-add-pcs_enable-pcs_disable-methods.patch @@ -27,15 +27,15 @@ Signed-off-by: David S. Miller }; /** -@@ -71,6 +75,7 @@ struct phylink { - struct mutex state_mutex; +@@ -72,6 +76,7 @@ struct phylink { struct phylink_link_state phy_state; struct work_struct resolve; + unsigned int pcs_neg_mode; + unsigned int pcs_state; bool mac_link_dropped; bool using_mac_select_pcs; -@@ -987,6 +992,22 @@ static void phylink_mac_pcs_an_restart(s +@@ -992,6 +997,22 @@ static void phylink_resolve_an_pause(str } } @@ -55,16 +55,17 @@ Signed-off-by: David S. Miller + return err; +} + - static void phylink_major_config(struct phylink *pl, bool restart, - const struct phylink_link_state *state) - { -@@ -1023,11 +1044,16 @@ static void phylink_major_config(struct + static int phylink_pcs_config(struct phylink_pcs *pcs, unsigned int neg_mode, + const struct phylink_link_state *state, + bool permit_pause_to_mac) +@@ -1094,11 +1115,17 @@ static void phylink_major_config(struct /* If we have a new PCS, switch to the new PCS after preparing the MAC * for the change. */ - if (pcs_changed) + if (pcs_changed) { + phylink_pcs_disable(pl->pcs); ++ pl->pcs = pcs; + } @@ -73,18 +74,18 @@ Signed-off-by: David S. Miller + if (pl->pcs_state == PCS_STATE_STARTING || pcs_changed) + phylink_pcs_enable(pl->pcs); + - if (pl->pcs) { - err = pl->pcs->ops->pcs_config(pl->pcs, pl->cur_link_an_mode, - state->interface, -@@ -1499,6 +1525,7 @@ struct phylink *phylink_create(struct ph + neg_mode = pl->cur_link_an_mode; + if (pl->pcs && pl->pcs->neg_mode) + neg_mode = pl->pcs_neg_mode; +@@ -1586,6 +1613,7 @@ struct phylink *phylink_create(struct ph + pl->link_config.pause = MLO_PAUSE_AN; pl->link_config.speed = SPEED_UNKNOWN; pl->link_config.duplex = DUPLEX_UNKNOWN; - pl->link_config.an_enabled = true; + pl->pcs_state = PCS_STATE_DOWN; pl->mac_ops = mac_ops; __set_bit(PHYLINK_DISABLE_STOPPED, &pl->phylink_disable_state); timer_setup(&pl->link_poll, phylink_fixed_poll, 0); -@@ -1900,6 +1927,8 @@ void phylink_start(struct phylink *pl) +@@ -1987,6 +2015,8 @@ void phylink_start(struct phylink *pl) if (pl->netdev) netif_carrier_off(pl->netdev); @@ -93,7 +94,7 @@ Signed-off-by: David S. Miller /* Apply the link configuration to the MAC when starting. This allows * a fixed-link to start with the correct parameters, and also * ensures that we set the appropriate advertisement for Serdes links. -@@ -1910,6 +1939,8 @@ void phylink_start(struct phylink *pl) +@@ -1997,6 +2027,8 @@ void phylink_start(struct phylink *pl) */ phylink_mac_initial_config(pl, true); @@ -102,7 +103,7 @@ Signed-off-by: David S. Miller phylink_enable_and_run_resolve(pl, PHYLINK_DISABLE_STOPPED); if (pl->cfg_link_an_mode == MLO_AN_FIXED && pl->link_gpio) { -@@ -1928,15 +1959,9 @@ void phylink_start(struct phylink *pl) +@@ -2015,15 +2047,9 @@ void phylink_start(struct phylink *pl) poll = true; } @@ -120,7 +121,7 @@ Signed-off-by: David S. Miller if (poll) mod_timer(&pl->link_poll, jiffies + HZ); if (pl->phydev) -@@ -1973,6 +1998,10 @@ void phylink_stop(struct phylink *pl) +@@ -2060,6 +2086,10 @@ void phylink_stop(struct phylink *pl) } phylink_run_resolve_and_disable(pl, PHYLINK_DISABLE_STOPPED); @@ -133,7 +134,7 @@ Signed-off-by: David S. Miller --- a/include/linux/phylink.h +++ b/include/linux/phylink.h -@@ -446,6 +446,8 @@ struct phylink_pcs { +@@ -533,6 +533,8 @@ struct phylink_pcs { /** * struct phylink_pcs_ops - MAC PCS operations structure. * @pcs_validate: validate the link configuration. @@ -142,7 +143,7 @@ Signed-off-by: David S. Miller * @pcs_get_state: read the current MAC PCS link state from the hardware. * @pcs_config: configure the MAC PCS for the selected mode and state. * @pcs_an_restart: restart 802.3z BaseX autonegotiation. -@@ -455,6 +457,8 @@ struct phylink_pcs { +@@ -542,6 +544,8 @@ struct phylink_pcs { struct phylink_pcs_ops { int (*pcs_validate)(struct phylink_pcs *pcs, unsigned long *supported, const struct phylink_link_state *state); @@ -150,8 +151,8 @@ Signed-off-by: David S. Miller + void (*pcs_disable)(struct phylink_pcs *pcs); void (*pcs_get_state)(struct phylink_pcs *pcs, struct phylink_link_state *state); - int (*pcs_config)(struct phylink_pcs *pcs, unsigned int mode, -@@ -485,6 +489,18 @@ int pcs_validate(struct phylink_pcs *pcs + int (*pcs_config)(struct phylink_pcs *pcs, unsigned int neg_mode, +@@ -572,6 +576,18 @@ int pcs_validate(struct phylink_pcs *pcs const struct phylink_link_state *state); /** diff --git a/target/linux/generic/backport-6.1/793-v6.6-net-pcs-lynxi-implement-pcs_disable-op.patch b/target/linux/generic/backport-6.1/793-v6.6-net-pcs-lynxi-implement-pcs_disable-op.patch index eb9b4b7c0..6b6369761 100644 --- a/target/linux/generic/backport-6.1/793-v6.6-net-pcs-lynxi-implement-pcs_disable-op.patch +++ b/target/linux/generic/backport-6.1/793-v6.6-net-pcs-lynxi-implement-pcs_disable-op.patch @@ -22,7 +22,7 @@ Signed-off-by: Jakub Kicinski --- a/drivers/net/pcs/pcs-mtk-lynxi.c +++ b/drivers/net/pcs/pcs-mtk-lynxi.c -@@ -241,11 +241,19 @@ static void mtk_pcs_lynxi_link_up(struct +@@ -234,11 +234,19 @@ static void mtk_pcs_lynxi_link_up(struct } } diff --git a/target/linux/generic/backport-6.1/794-v6.2-net-core-Allow-live-renaming-when-an-interface-is-up.patch b/target/linux/generic/backport-6.1/794-v6.2-net-core-Allow-live-renaming-when-an-interface-is-up.patch new file mode 100644 index 000000000..c4141eee9 --- /dev/null +++ b/target/linux/generic/backport-6.1/794-v6.2-net-core-Allow-live-renaming-when-an-interface-is-up.patch @@ -0,0 +1,136 @@ +From: Andy Ren +Date: Mon, 7 Nov 2022 09:42:42 -0800 +Subject: [PATCH] net/core: Allow live renaming when an interface is up + +Allow a network interface to be renamed when the interface +is up. + +As described in the netconsole documentation [1], when netconsole is +used as a built-in, it will bring up the specified interface as soon as +possible. As a result, user space will not be able to rename the +interface since the kernel disallows renaming of interfaces that are +administratively up unless the 'IFF_LIVE_RENAME_OK' private flag was set +by the kernel. + +The original solution [2] to this problem was to add a new parameter to +the netconsole configuration parameters that allows renaming of +the interface used by netconsole while it is administratively up. +However, during the discussion that followed, it became apparent that we +have no reason to keep the current restriction and instead we should +allow user space to rename interfaces regardless of their administrative +state: + +1. The restriction was put in place over 20 years ago when renaming was +only possible via IOCTL and before rtnetlink started notifying user +space about such changes like it does today. + +2. The 'IFF_LIVE_RENAME_OK' flag was added over 3 years ago in version +5.2 and no regressions were reported. + +3. In-kernel listeners to 'NETDEV_CHANGENAME' do not seem to care about +the administrative state of interface. + +Therefore, allow user space to rename running interfaces by removing the +restriction and the associated 'IFF_LIVE_RENAME_OK' flag. Help in +possible triage by emitting a message to the kernel log that an +interface was renamed while UP. + +[1] https://www.kernel.org/doc/Documentation/networking/netconsole.rst +[2] https://lore.kernel.org/netdev/20221102002420.2613004-1-andy.ren@getcruise.com/ + +Signed-off-by: Andy Ren +Reviewed-by: Ido Schimmel +Reviewed-by: David Ahern +Signed-off-by: David S. Miller +--- + +--- a/include/linux/netdevice.h ++++ b/include/linux/netdevice.h +@@ -1691,7 +1691,6 @@ struct net_device_ops { + * @IFF_FAILOVER: device is a failover master device + * @IFF_FAILOVER_SLAVE: device is lower dev of a failover master device + * @IFF_L3MDEV_RX_HANDLER: only invoke the rx handler of L3 master device +- * @IFF_LIVE_RENAME_OK: rename is allowed while device is up and running + * @IFF_TX_SKB_NO_LINEAR: device/driver is capable of xmitting frames with + * skb_headlen(skb) == 0 (data starts from frag0) + * @IFF_CHANGE_PROTO_DOWN: device supports setting carrier via IFLA_PROTO_DOWN +@@ -1727,7 +1726,7 @@ enum netdev_priv_flags { + IFF_FAILOVER = 1<<27, + IFF_FAILOVER_SLAVE = 1<<28, + IFF_L3MDEV_RX_HANDLER = 1<<29, +- IFF_LIVE_RENAME_OK = 1<<30, ++ /* was IFF_LIVE_RENAME_OK */ + IFF_TX_SKB_NO_LINEAR = BIT_ULL(31), + IFF_CHANGE_PROTO_DOWN = BIT_ULL(32), + }; +@@ -1762,7 +1761,6 @@ enum netdev_priv_flags { + #define IFF_FAILOVER IFF_FAILOVER + #define IFF_FAILOVER_SLAVE IFF_FAILOVER_SLAVE + #define IFF_L3MDEV_RX_HANDLER IFF_L3MDEV_RX_HANDLER +-#define IFF_LIVE_RENAME_OK IFF_LIVE_RENAME_OK + #define IFF_TX_SKB_NO_LINEAR IFF_TX_SKB_NO_LINEAR + + /* Specifies the type of the struct net_device::ml_priv pointer */ +--- a/net/core/dev.c ++++ b/net/core/dev.c +@@ -1188,22 +1188,6 @@ int dev_change_name(struct net_device *d + + net = dev_net(dev); + +- /* Some auto-enslaved devices e.g. failover slaves are +- * special, as userspace might rename the device after +- * the interface had been brought up and running since +- * the point kernel initiated auto-enslavement. Allow +- * live name change even when these slave devices are +- * up and running. +- * +- * Typically, users of these auto-enslaving devices +- * don't actually care about slave name change, as +- * they are supposed to operate on master interface +- * directly. +- */ +- if (dev->flags & IFF_UP && +- likely(!(dev->priv_flags & IFF_LIVE_RENAME_OK))) +- return -EBUSY; +- + down_write(&devnet_rename_sem); + + if (strncmp(newname, dev->name, IFNAMSIZ) == 0) { +@@ -1220,7 +1204,8 @@ int dev_change_name(struct net_device *d + } + + if (oldname[0] && !strchr(oldname, '%')) +- netdev_info(dev, "renamed from %s\n", oldname); ++ netdev_info(dev, "renamed from %s%s\n", oldname, ++ dev->flags & IFF_UP ? " (while UP)" : ""); + + old_assign_type = dev->name_assign_type; + dev->name_assign_type = NET_NAME_RENAMED; +--- a/net/core/failover.c ++++ b/net/core/failover.c +@@ -80,14 +80,14 @@ static int failover_slave_register(struc + goto err_upper_link; + } + +- slave_dev->priv_flags |= (IFF_FAILOVER_SLAVE | IFF_LIVE_RENAME_OK); ++ slave_dev->priv_flags |= IFF_FAILOVER_SLAVE; + + if (fops && fops->slave_register && + !fops->slave_register(slave_dev, failover_dev)) + return NOTIFY_OK; + + netdev_upper_dev_unlink(slave_dev, failover_dev); +- slave_dev->priv_flags &= ~(IFF_FAILOVER_SLAVE | IFF_LIVE_RENAME_OK); ++ slave_dev->priv_flags &= ~IFF_FAILOVER_SLAVE; + err_upper_link: + netdev_rx_handler_unregister(slave_dev); + done: +@@ -121,7 +121,7 @@ int failover_slave_unregister(struct net + + netdev_rx_handler_unregister(slave_dev); + netdev_upper_dev_unlink(slave_dev, failover_dev); +- slave_dev->priv_flags &= ~(IFF_FAILOVER_SLAVE | IFF_LIVE_RENAME_OK); ++ slave_dev->priv_flags &= ~IFF_FAILOVER_SLAVE; + + if (fops && fops->slave_unregister && + !fops->slave_unregister(slave_dev, failover_dev)) diff --git a/target/linux/generic/backport-6.1/796-v6.5-01-usbnet-ipheth-fix-risk-of-NULL-pointer-deallocation.patch b/target/linux/generic/backport-6.1/796-v6.5-01-usbnet-ipheth-fix-risk-of-NULL-pointer-deallocation.patch new file mode 100644 index 000000000..d9d6f36fc --- /dev/null +++ b/target/linux/generic/backport-6.1/796-v6.5-01-usbnet-ipheth-fix-risk-of-NULL-pointer-deallocation.patch @@ -0,0 +1,30 @@ +From 2203718c2f59ffdd6c78d54e5add594aebb4461e Mon Sep 17 00:00:00 2001 +From: Georgi Valkov +Date: Wed, 7 Jun 2023 15:56:59 +0200 +Subject: [PATCH 1/4] usbnet: ipheth: fix risk of NULL pointer deallocation + +The cleanup precedure in ipheth_probe will attempt to free a +NULL pointer in dev->ctrl_buf if the memory allocation for +this buffer is not successful. While kfree ignores NULL pointers, +and the existing code is safe, it is a better design to rearrange +the goto labels and avoid this. + +Signed-off-by: Georgi Valkov +Signed-off-by: Foster Snowhill +Signed-off-by: David S. Miller +--- + drivers/net/usb/ipheth.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/drivers/net/usb/ipheth.c ++++ b/drivers/net/usb/ipheth.c +@@ -510,8 +510,8 @@ err_register_netdev: + ipheth_free_urbs(dev); + err_alloc_urbs: + err_get_macaddr: +-err_alloc_ctrl_buf: + kfree(dev->ctrl_buf); ++err_alloc_ctrl_buf: + err_endpoints: + free_netdev(netdev); + return retval; diff --git a/target/linux/generic/backport-6.1/796-v6.5-02-usbnet-ipheth-transmit-URBs-without-trailing-padding.patch b/target/linux/generic/backport-6.1/796-v6.5-02-usbnet-ipheth-transmit-URBs-without-trailing-padding.patch new file mode 100644 index 000000000..adfec356d --- /dev/null +++ b/target/linux/generic/backport-6.1/796-v6.5-02-usbnet-ipheth-transmit-URBs-without-trailing-padding.patch @@ -0,0 +1,35 @@ +From 3e65efcca87a9bb5f3b864e0a43d167bc0a8688c Mon Sep 17 00:00:00 2001 +From: Foster Snowhill +Date: Wed, 7 Jun 2023 15:57:00 +0200 +Subject: [PATCH 2/4] usbnet: ipheth: transmit URBs without trailing padding + +The behaviour of the official iOS tethering driver on macOS is to not +transmit any trailing padding at the end of URBs. This is applicable +to both NCM and legacy modes, including older devices. + +Adapt the driver to not include trailing padding in TX URBs, matching +the behaviour of the official macOS driver. + +Signed-off-by: Foster Snowhill +Tested-by: Georgi Valkov +Signed-off-by: David S. Miller +--- + drivers/net/usb/ipheth.c | 4 +--- + 1 file changed, 1 insertion(+), 3 deletions(-) + +--- a/drivers/net/usb/ipheth.c ++++ b/drivers/net/usb/ipheth.c +@@ -373,12 +373,10 @@ static netdev_tx_t ipheth_tx(struct sk_b + } + + memcpy(dev->tx_buf, skb->data, skb->len); +- if (skb->len < IPHETH_BUF_SIZE) +- memset(dev->tx_buf + skb->len, 0, IPHETH_BUF_SIZE - skb->len); + + usb_fill_bulk_urb(dev->tx_urb, udev, + usb_sndbulkpipe(udev, dev->bulk_out), +- dev->tx_buf, IPHETH_BUF_SIZE, ++ dev->tx_buf, skb->len, + ipheth_sndbulk_callback, + dev); + dev->tx_urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; diff --git a/target/linux/generic/backport-6.1/796-v6.5-03-usbnet-ipheth-add-CDC-NCM-support.patch b/target/linux/generic/backport-6.1/796-v6.5-03-usbnet-ipheth-add-CDC-NCM-support.patch new file mode 100644 index 000000000..e3f2b9c33 --- /dev/null +++ b/target/linux/generic/backport-6.1/796-v6.5-03-usbnet-ipheth-add-CDC-NCM-support.patch @@ -0,0 +1,326 @@ +From a2d274c62e44b1995c170595db3865c6fe701226 Mon Sep 17 00:00:00 2001 +From: Foster Snowhill +Date: Wed, 7 Jun 2023 15:57:01 +0200 +Subject: [PATCH 3/4] usbnet: ipheth: add CDC NCM support + +Recent iOS releases support CDC NCM encapsulation on RX. This mode is +the default on macOS and Windows. In this mode, an iOS device may include +one or more Ethernet frames inside a single URB. + +Freshly booted iOS devices start in legacy mode, but are put into +NCM mode by the official Apple driver. When reconnecting such a device +from a macOS/Windows machine to a Linux host, the device stays in +NCM mode, making it unusable with the legacy ipheth driver code. + +To correctly support such a device, the driver has to either support +the NCM mode too, or put the device back into legacy mode. + +To match the behaviour of the macOS/Windows driver, and since there +is no documented control command to revert to legacy mode, implement +NCM support. The device is attempted to be put into NCM mode by default, +and falls back to legacy mode if the attempt fails. + +Signed-off-by: Foster Snowhill +Tested-by: Georgi Valkov +Signed-off-by: David S. Miller +--- + drivers/net/usb/ipheth.c | 180 +++++++++++++++++++++++++++++++++------ + 1 file changed, 155 insertions(+), 25 deletions(-) + +--- a/drivers/net/usb/ipheth.c ++++ b/drivers/net/usb/ipheth.c +@@ -52,6 +52,7 @@ + #include + #include + #include ++#include + + #define USB_VENDOR_APPLE 0x05ac + +@@ -59,8 +60,12 @@ + #define IPHETH_USBINTF_SUBCLASS 253 + #define IPHETH_USBINTF_PROTO 1 + +-#define IPHETH_BUF_SIZE 1514 + #define IPHETH_IP_ALIGN 2 /* padding at front of URB */ ++#define IPHETH_NCM_HEADER_SIZE (12 + 96) /* NCMH + NCM0 */ ++#define IPHETH_TX_BUF_SIZE ETH_FRAME_LEN ++#define IPHETH_RX_BUF_SIZE_LEGACY (IPHETH_IP_ALIGN + ETH_FRAME_LEN) ++#define IPHETH_RX_BUF_SIZE_NCM 65536 ++ + #define IPHETH_TX_TIMEOUT (5 * HZ) + + #define IPHETH_INTFNUM 2 +@@ -71,6 +76,7 @@ + #define IPHETH_CTRL_TIMEOUT (5 * HZ) + + #define IPHETH_CMD_GET_MACADDR 0x00 ++#define IPHETH_CMD_ENABLE_NCM 0x04 + #define IPHETH_CMD_CARRIER_CHECK 0x45 + + #define IPHETH_CARRIER_CHECK_TIMEOUT round_jiffies_relative(1 * HZ) +@@ -97,6 +103,8 @@ struct ipheth_device { + u8 bulk_out; + struct delayed_work carrier_work; + bool confirmed_pairing; ++ int (*rcvbulk_callback)(struct urb *urb); ++ size_t rx_buf_len; + }; + + static int ipheth_rx_submit(struct ipheth_device *dev, gfp_t mem_flags); +@@ -116,12 +124,12 @@ static int ipheth_alloc_urbs(struct iphe + if (rx_urb == NULL) + goto free_tx_urb; + +- tx_buf = usb_alloc_coherent(iphone->udev, IPHETH_BUF_SIZE, ++ tx_buf = usb_alloc_coherent(iphone->udev, IPHETH_TX_BUF_SIZE, + GFP_KERNEL, &tx_urb->transfer_dma); + if (tx_buf == NULL) + goto free_rx_urb; + +- rx_buf = usb_alloc_coherent(iphone->udev, IPHETH_BUF_SIZE + IPHETH_IP_ALIGN, ++ rx_buf = usb_alloc_coherent(iphone->udev, iphone->rx_buf_len, + GFP_KERNEL, &rx_urb->transfer_dma); + if (rx_buf == NULL) + goto free_tx_buf; +@@ -134,7 +142,7 @@ static int ipheth_alloc_urbs(struct iphe + return 0; + + free_tx_buf: +- usb_free_coherent(iphone->udev, IPHETH_BUF_SIZE, tx_buf, ++ usb_free_coherent(iphone->udev, IPHETH_TX_BUF_SIZE, tx_buf, + tx_urb->transfer_dma); + free_rx_urb: + usb_free_urb(rx_urb); +@@ -146,9 +154,9 @@ error_nomem: + + static void ipheth_free_urbs(struct ipheth_device *iphone) + { +- usb_free_coherent(iphone->udev, IPHETH_BUF_SIZE + IPHETH_IP_ALIGN, iphone->rx_buf, ++ usb_free_coherent(iphone->udev, iphone->rx_buf_len, iphone->rx_buf, + iphone->rx_urb->transfer_dma); +- usb_free_coherent(iphone->udev, IPHETH_BUF_SIZE, iphone->tx_buf, ++ usb_free_coherent(iphone->udev, IPHETH_TX_BUF_SIZE, iphone->tx_buf, + iphone->tx_urb->transfer_dma); + usb_free_urb(iphone->rx_urb); + usb_free_urb(iphone->tx_urb); +@@ -160,15 +168,106 @@ static void ipheth_kill_urbs(struct iphe + usb_kill_urb(dev->rx_urb); + } + +-static void ipheth_rcvbulk_callback(struct urb *urb) ++static int ipheth_consume_skb(char *buf, int len, struct ipheth_device *dev) + { +- struct ipheth_device *dev; + struct sk_buff *skb; +- int status; ++ ++ skb = dev_alloc_skb(len); ++ if (!skb) { ++ dev->net->stats.rx_dropped++; ++ return -ENOMEM; ++ } ++ ++ skb_put_data(skb, buf, len); ++ skb->dev = dev->net; ++ skb->protocol = eth_type_trans(skb, dev->net); ++ ++ dev->net->stats.rx_packets++; ++ dev->net->stats.rx_bytes += len; ++ netif_rx(skb); ++ ++ return 0; ++} ++ ++static int ipheth_rcvbulk_callback_legacy(struct urb *urb) ++{ ++ struct ipheth_device *dev; ++ char *buf; ++ int len; ++ ++ dev = urb->context; ++ ++ if (urb->actual_length <= IPHETH_IP_ALIGN) { ++ dev->net->stats.rx_length_errors++; ++ return -EINVAL; ++ } ++ len = urb->actual_length - IPHETH_IP_ALIGN; ++ buf = urb->transfer_buffer + IPHETH_IP_ALIGN; ++ ++ return ipheth_consume_skb(buf, len, dev); ++} ++ ++static int ipheth_rcvbulk_callback_ncm(struct urb *urb) ++{ ++ struct usb_cdc_ncm_nth16 *ncmh; ++ struct usb_cdc_ncm_ndp16 *ncm0; ++ struct usb_cdc_ncm_dpe16 *dpe; ++ struct ipheth_device *dev; ++ int retval = -EINVAL; + char *buf; + int len; + + dev = urb->context; ++ ++ if (urb->actual_length < IPHETH_NCM_HEADER_SIZE) { ++ dev->net->stats.rx_length_errors++; ++ return retval; ++ } ++ ++ ncmh = urb->transfer_buffer; ++ if (ncmh->dwSignature != cpu_to_le32(USB_CDC_NCM_NTH16_SIGN) || ++ le16_to_cpu(ncmh->wNdpIndex) >= urb->actual_length) { ++ dev->net->stats.rx_errors++; ++ return retval; ++ } ++ ++ ncm0 = urb->transfer_buffer + le16_to_cpu(ncmh->wNdpIndex); ++ if (ncm0->dwSignature != cpu_to_le32(USB_CDC_NCM_NDP16_NOCRC_SIGN) || ++ le16_to_cpu(ncmh->wHeaderLength) + le16_to_cpu(ncm0->wLength) >= ++ urb->actual_length) { ++ dev->net->stats.rx_errors++; ++ return retval; ++ } ++ ++ dpe = ncm0->dpe16; ++ while (le16_to_cpu(dpe->wDatagramIndex) != 0 && ++ le16_to_cpu(dpe->wDatagramLength) != 0) { ++ if (le16_to_cpu(dpe->wDatagramIndex) >= urb->actual_length || ++ le16_to_cpu(dpe->wDatagramIndex) + ++ le16_to_cpu(dpe->wDatagramLength) > urb->actual_length) { ++ dev->net->stats.rx_length_errors++; ++ return retval; ++ } ++ ++ buf = urb->transfer_buffer + le16_to_cpu(dpe->wDatagramIndex); ++ len = le16_to_cpu(dpe->wDatagramLength); ++ ++ retval = ipheth_consume_skb(buf, len, dev); ++ if (retval != 0) ++ return retval; ++ ++ dpe++; ++ } ++ ++ return 0; ++} ++ ++static void ipheth_rcvbulk_callback(struct urb *urb) ++{ ++ struct ipheth_device *dev; ++ int retval, status; ++ ++ dev = urb->context; + if (dev == NULL) + return; + +@@ -191,25 +290,27 @@ static void ipheth_rcvbulk_callback(stru + dev->net->stats.rx_length_errors++; + return; + } +- len = urb->actual_length - IPHETH_IP_ALIGN; +- buf = urb->transfer_buffer + IPHETH_IP_ALIGN; + +- skb = dev_alloc_skb(len); +- if (!skb) { +- dev_err(&dev->intf->dev, "%s: dev_alloc_skb: -ENOMEM\n", +- __func__); +- dev->net->stats.rx_dropped++; ++ /* RX URBs starting with 0x00 0x01 do not encapsulate Ethernet frames, ++ * but rather are control frames. Their purpose is not documented, and ++ * they don't affect driver functionality, okay to drop them. ++ * There is usually just one 4-byte control frame as the very first ++ * URB received from the bulk IN endpoint. ++ */ ++ if (unlikely ++ (((char *)urb->transfer_buffer)[0] == 0 && ++ ((char *)urb->transfer_buffer)[1] == 1)) ++ goto rx_submit; ++ ++ retval = dev->rcvbulk_callback(urb); ++ if (retval != 0) { ++ dev_err(&dev->intf->dev, "%s: callback retval: %d\n", ++ __func__, retval); + return; + } + +- skb_put_data(skb, buf, len); +- skb->dev = dev->net; +- skb->protocol = eth_type_trans(skb, dev->net); +- +- dev->net->stats.rx_packets++; +- dev->net->stats.rx_bytes += len; ++rx_submit: + dev->confirmed_pairing = true; +- netif_rx(skb); + ipheth_rx_submit(dev, GFP_ATOMIC); + } + +@@ -310,6 +411,27 @@ static int ipheth_get_macaddr(struct iph + return retval; + } + ++static int ipheth_enable_ncm(struct ipheth_device *dev) ++{ ++ struct usb_device *udev = dev->udev; ++ int retval; ++ ++ retval = usb_control_msg(udev, ++ usb_sndctrlpipe(udev, IPHETH_CTRL_ENDP), ++ IPHETH_CMD_ENABLE_NCM, /* request */ ++ 0x41, /* request type */ ++ 0x00, /* value */ ++ 0x02, /* index */ ++ NULL, ++ 0, ++ IPHETH_CTRL_TIMEOUT); ++ ++ dev_info(&dev->intf->dev, "%s: usb_control_msg: %d\n", ++ __func__, retval); ++ ++ return retval; ++} ++ + static int ipheth_rx_submit(struct ipheth_device *dev, gfp_t mem_flags) + { + struct usb_device *udev = dev->udev; +@@ -317,7 +439,7 @@ static int ipheth_rx_submit(struct iphet + + usb_fill_bulk_urb(dev->rx_urb, udev, + usb_rcvbulkpipe(udev, dev->bulk_in), +- dev->rx_buf, IPHETH_BUF_SIZE + IPHETH_IP_ALIGN, ++ dev->rx_buf, dev->rx_buf_len, + ipheth_rcvbulk_callback, + dev); + dev->rx_urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; +@@ -365,7 +487,7 @@ static netdev_tx_t ipheth_tx(struct sk_b + int retval; + + /* Paranoid */ +- if (skb->len > IPHETH_BUF_SIZE) { ++ if (skb->len > IPHETH_TX_BUF_SIZE) { + WARN(1, "%s: skb too large: %d bytes\n", __func__, skb->len); + dev->net->stats.tx_dropped++; + dev_kfree_skb_any(skb); +@@ -448,6 +570,8 @@ static int ipheth_probe(struct usb_inter + dev->net = netdev; + dev->intf = intf; + dev->confirmed_pairing = false; ++ dev->rx_buf_len = IPHETH_RX_BUF_SIZE_LEGACY; ++ dev->rcvbulk_callback = ipheth_rcvbulk_callback_legacy; + /* Set up endpoints */ + hintf = usb_altnum_to_altsetting(intf, IPHETH_ALT_INTFNUM); + if (hintf == NULL) { +@@ -479,6 +603,12 @@ static int ipheth_probe(struct usb_inter + if (retval) + goto err_get_macaddr; + ++ retval = ipheth_enable_ncm(dev); ++ if (!retval) { ++ dev->rx_buf_len = IPHETH_RX_BUF_SIZE_NCM; ++ dev->rcvbulk_callback = ipheth_rcvbulk_callback_ncm; ++ } ++ + INIT_DELAYED_WORK(&dev->carrier_work, ipheth_carrier_check_work); + + retval = ipheth_alloc_urbs(dev); diff --git a/target/linux/generic/backport-6.1/796-v6.5-04-usbnet-ipheth-update-Kconfig-description.patch b/target/linux/generic/backport-6.1/796-v6.5-04-usbnet-ipheth-update-Kconfig-description.patch new file mode 100644 index 000000000..9089c4d8c --- /dev/null +++ b/target/linux/generic/backport-6.1/796-v6.5-04-usbnet-ipheth-update-Kconfig-description.patch @@ -0,0 +1,36 @@ +From 0c6e9d32ef0ccfcf2d875cbcff23bf345a54d585 Mon Sep 17 00:00:00 2001 +From: Foster Snowhill +Date: Wed, 7 Jun 2023 15:57:02 +0200 +Subject: [PATCH 4/4] usbnet: ipheth: update Kconfig description + +This module has for a long time not been limited to iPhone <= 3GS. +Update description to match the actual state of the driver. + +Remove dead link from 2010, instead reference an existing userspace +iOS device pairing implementation as part of libimobiledevice. + +Signed-off-by: Foster Snowhill +Signed-off-by: David S. Miller +--- + drivers/net/usb/Kconfig | 10 ++++------ + 1 file changed, 4 insertions(+), 6 deletions(-) + +--- a/drivers/net/usb/Kconfig ++++ b/drivers/net/usb/Kconfig +@@ -583,12 +583,10 @@ config USB_IPHETH + default n + help + Module used to share Internet connection (tethering) from your +- iPhone (Original, 3G and 3GS) to your system. +- Note that you need userspace libraries and programs that are needed +- to pair your device with your system and that understand the iPhone +- protocol. +- +- For more information: http://giagio.com/wiki/moin.cgi/iPhoneEthernetDriver ++ iPhone to your system. ++ Note that you need a corresponding userspace library/program ++ to pair your device with your system, for example usbmuxd ++ . + + config USB_SIERRA_NET + tristate "USB-to-WWAN Driver for Sierra Wireless modems" diff --git a/target/linux/generic/backport-6.1/797-6.7-net-dsa-mv88e6xxx-fix-marvell-6350-switch-probing.patch b/target/linux/generic/backport-6.1/797-6.7-net-dsa-mv88e6xxx-fix-marvell-6350-switch-probing.patch new file mode 100644 index 000000000..36083bbaf --- /dev/null +++ b/target/linux/generic/backport-6.1/797-6.7-net-dsa-mv88e6xxx-fix-marvell-6350-switch-probing.patch @@ -0,0 +1,89 @@ +From b3f1a164c7f742503dc7159011f7ad6b092b660e Mon Sep 17 00:00:00 2001 +From: Greg Ungerer +Date: Fri, 24 Nov 2023 14:15:28 +1000 +Subject: [PATCH] net: dsa: mv88e6xxx: fix marvell 6350 switch probing + +As of commit de5c9bf40c45 ("net: phylink: require supported_interfaces to +be filled") Marvell 88e6350 switches fail to be probed: + + ... + mv88e6085 d0072004.mdio-mii:11: switch 0x3710 detected: Marvell 88E6350, revision 2 + mv88e6085 d0072004.mdio-mii:11: phylink: error: empty supported_interfaces + error creating PHYLINK: -22 + mv88e6085: probe of d0072004.mdio-mii:11 failed with error -22 + ... + +The problem stems from the use of mv88e6185_phylink_get_caps() to get +the device capabilities. Create a new dedicated phylink_get_caps for the +6351 family (which the 6350 is one of) to properly support their set of +capabilities. + +According to chip.h the 6351 switch family includes the 6171, 6175, 6350 +and 6351 switches, so update each of these to use the correct +phylink_get_caps. + +Fixes: de5c9bf40c45 ("net: phylink: require supported_interfaces to be filled") +Signed-off-by: Greg Ungerer +Reviewed-by: Andrew Lunn +Signed-off-by: David S. Miller +--- + drivers/net/dsa/mv88e6xxx/chip.c | 20 ++++++++++++++++---- + 1 file changed, 16 insertions(+), 4 deletions(-) + +--- a/drivers/net/dsa/mv88e6xxx/chip.c ++++ b/drivers/net/dsa/mv88e6xxx/chip.c +@@ -652,6 +652,18 @@ static void mv88e6250_phylink_get_caps(s + config->mac_capabilities = MAC_SYM_PAUSE | MAC_10 | MAC_100; + } + ++static void mv88e6351_phylink_get_caps(struct mv88e6xxx_chip *chip, int port, ++ struct phylink_config *config) ++{ ++ unsigned long *supported = config->supported_interfaces; ++ ++ /* Translate the default cmode */ ++ mv88e6xxx_translate_cmode(chip->ports[port].cmode, supported); ++ ++ config->mac_capabilities = MAC_SYM_PAUSE | MAC_10 | MAC_100 | ++ MAC_1000FD; ++} ++ + static int mv88e6352_get_port4_serdes_cmode(struct mv88e6xxx_chip *chip) + { + u16 reg, val; +@@ -4501,7 +4513,7 @@ static const struct mv88e6xxx_ops mv88e6 + .vtu_loadpurge = mv88e6352_g1_vtu_loadpurge, + .stu_getnext = mv88e6352_g1_stu_getnext, + .stu_loadpurge = mv88e6352_g1_stu_loadpurge, +- .phylink_get_caps = mv88e6185_phylink_get_caps, ++ .phylink_get_caps = mv88e6351_phylink_get_caps, + }; + + static const struct mv88e6xxx_ops mv88e6172_ops = { +@@ -4604,7 +4616,7 @@ static const struct mv88e6xxx_ops mv88e6 + .vtu_loadpurge = mv88e6352_g1_vtu_loadpurge, + .stu_getnext = mv88e6352_g1_stu_getnext, + .stu_loadpurge = mv88e6352_g1_stu_loadpurge, +- .phylink_get_caps = mv88e6185_phylink_get_caps, ++ .phylink_get_caps = mv88e6351_phylink_get_caps, + }; + + static const struct mv88e6xxx_ops mv88e6176_ops = { +@@ -5281,7 +5293,7 @@ static const struct mv88e6xxx_ops mv88e6 + .vtu_loadpurge = mv88e6352_g1_vtu_loadpurge, + .stu_getnext = mv88e6352_g1_stu_getnext, + .stu_loadpurge = mv88e6352_g1_stu_loadpurge, +- .phylink_get_caps = mv88e6185_phylink_get_caps, ++ .phylink_get_caps = mv88e6351_phylink_get_caps, + }; + + static const struct mv88e6xxx_ops mv88e6351_ops = { +@@ -5327,7 +5339,7 @@ static const struct mv88e6xxx_ops mv88e6 + .stu_loadpurge = mv88e6352_g1_stu_loadpurge, + .avb_ops = &mv88e6352_avb_ops, + .ptp_ops = &mv88e6352_ptp_ops, +- .phylink_get_caps = mv88e6185_phylink_get_caps, ++ .phylink_get_caps = mv88e6351_phylink_get_caps, + }; + + static const struct mv88e6xxx_ops mv88e6352_ops = { diff --git a/target/linux/generic/backport-6.1/798-v6.10-net-phy-air_en8811h-Add-the-Airoha-EN8811H-PHY-drive.patch b/target/linux/generic/backport-6.1/798-v6.10-net-phy-air_en8811h-Add-the-Airoha-EN8811H-PHY-drive.patch new file mode 100644 index 000000000..7f963b3cf --- /dev/null +++ b/target/linux/generic/backport-6.1/798-v6.10-net-phy-air_en8811h-Add-the-Airoha-EN8811H-PHY-drive.patch @@ -0,0 +1,1140 @@ +From 71e79430117d56c409c5ea485a263bc0d8083390 Mon Sep 17 00:00:00 2001 +From: Eric Woudstra +Date: Tue, 26 Mar 2024 17:23:05 +0100 +Subject: [PATCH] net: phy: air_en8811h: Add the Airoha EN8811H PHY driver + +Add the driver for the Airoha EN8811H 2.5 Gigabit PHY. The phy supports +100/1000/2500 Mbps with auto negotiation only. + +The driver uses two firmware files, for which updated versions are added to +linux-firmware already. + +Note: At phy-address + 8 there is another device on the mdio bus, that +belongs to the EN881H. While the original driver writes to it, Airoha +has confirmed this is not needed. Therefore, communication with this +device is not included in this driver. + +Signed-off-by: Eric Woudstra +Reviewed-by: Andrew Lunn +Link: https://lore.kernel.org/r/20240326162305.303598-3-ericwouds@gmail.com +Signed-off-by: Jakub Kicinski +--- + drivers/net/phy/Kconfig | 5 + + drivers/net/phy/Makefile | 1 + + drivers/net/phy/air_en8811h.c | 1086 +++++++++++++++++++++++++++++++++ + 3 files changed, 1092 insertions(+) + create mode 100644 drivers/net/phy/air_en8811h.c + +--- a/drivers/net/phy/Kconfig ++++ b/drivers/net/phy/Kconfig +@@ -63,6 +63,11 @@ config SFP + + comment "MII PHY device drivers" + ++config AIR_EN8811H_PHY ++ tristate "Airoha EN8811H 2.5 Gigabit PHY" ++ help ++ Currently supports the Airoha EN8811H PHY. ++ + config AMD_PHY + tristate "AMD PHYs" + help +--- a/drivers/net/phy/Makefile ++++ b/drivers/net/phy/Makefile +@@ -32,6 +32,7 @@ obj-y += $(sfp-obj-y) $(sfp-obj-m) + + obj-$(CONFIG_ADIN_PHY) += adin.o + obj-$(CONFIG_ADIN1100_PHY) += adin1100.o ++obj-$(CONFIG_AIR_EN8811H_PHY) += air_en8811h.o + obj-$(CONFIG_AMD_PHY) += amd.o + obj-$(CONFIG_AQUANTIA_PHY) += aquantia/ + obj-$(CONFIG_AX88796B_PHY) += ax88796b.o +--- /dev/null ++++ b/drivers/net/phy/air_en8811h.c +@@ -0,0 +1,1086 @@ ++// SPDX-License-Identifier: GPL-2.0+ ++/* ++ * Driver for the Airoha EN8811H 2.5 Gigabit PHY. ++ * ++ * Limitations of the EN8811H: ++ * - Only full duplex supported ++ * - Forced speed (AN off) is not supported by hardware (100Mbps) ++ * ++ * Source originated from airoha's en8811h.c and en8811h.h v1.2.1 ++ * ++ * Copyright (C) 2023 Airoha Technology Corp. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++ ++#define EN8811H_PHY_ID 0x03a2a411 ++ ++#define EN8811H_MD32_DM "airoha/EthMD32.dm.bin" ++#define EN8811H_MD32_DSP "airoha/EthMD32.DSP.bin" ++ ++#define AIR_FW_ADDR_DM 0x00000000 ++#define AIR_FW_ADDR_DSP 0x00100000 ++ ++/* MII Registers */ ++#define AIR_AUX_CTRL_STATUS 0x1d ++#define AIR_AUX_CTRL_STATUS_SPEED_MASK GENMASK(4, 2) ++#define AIR_AUX_CTRL_STATUS_SPEED_100 0x4 ++#define AIR_AUX_CTRL_STATUS_SPEED_1000 0x8 ++#define AIR_AUX_CTRL_STATUS_SPEED_2500 0xc ++ ++#define AIR_EXT_PAGE_ACCESS 0x1f ++#define AIR_PHY_PAGE_STANDARD 0x0000 ++#define AIR_PHY_PAGE_EXTENDED_4 0x0004 ++ ++/* MII Registers Page 4*/ ++#define AIR_BPBUS_MODE 0x10 ++#define AIR_BPBUS_MODE_ADDR_FIXED 0x0000 ++#define AIR_BPBUS_MODE_ADDR_INCR BIT(15) ++#define AIR_BPBUS_WR_ADDR_HIGH 0x11 ++#define AIR_BPBUS_WR_ADDR_LOW 0x12 ++#define AIR_BPBUS_WR_DATA_HIGH 0x13 ++#define AIR_BPBUS_WR_DATA_LOW 0x14 ++#define AIR_BPBUS_RD_ADDR_HIGH 0x15 ++#define AIR_BPBUS_RD_ADDR_LOW 0x16 ++#define AIR_BPBUS_RD_DATA_HIGH 0x17 ++#define AIR_BPBUS_RD_DATA_LOW 0x18 ++ ++/* Registers on MDIO_MMD_VEND1 */ ++#define EN8811H_PHY_FW_STATUS 0x8009 ++#define EN8811H_PHY_READY 0x02 ++ ++#define AIR_PHY_MCU_CMD_1 0x800c ++#define AIR_PHY_MCU_CMD_1_MODE1 0x0 ++#define AIR_PHY_MCU_CMD_2 0x800d ++#define AIR_PHY_MCU_CMD_2_MODE1 0x0 ++#define AIR_PHY_MCU_CMD_3 0x800e ++#define AIR_PHY_MCU_CMD_3_MODE1 0x1101 ++#define AIR_PHY_MCU_CMD_3_DOCMD 0x1100 ++#define AIR_PHY_MCU_CMD_4 0x800f ++#define AIR_PHY_MCU_CMD_4_MODE1 0x0002 ++#define AIR_PHY_MCU_CMD_4_INTCLR 0x00e4 ++ ++/* Registers on MDIO_MMD_VEND2 */ ++#define AIR_PHY_LED_BCR 0x021 ++#define AIR_PHY_LED_BCR_MODE_MASK GENMASK(1, 0) ++#define AIR_PHY_LED_BCR_TIME_TEST BIT(2) ++#define AIR_PHY_LED_BCR_CLK_EN BIT(3) ++#define AIR_PHY_LED_BCR_EXT_CTRL BIT(15) ++ ++#define AIR_PHY_LED_DUR_ON 0x022 ++ ++#define AIR_PHY_LED_DUR_BLINK 0x023 ++ ++#define AIR_PHY_LED_ON(i) (0x024 + ((i) * 2)) ++#define AIR_PHY_LED_ON_MASK (GENMASK(6, 0) | BIT(8)) ++#define AIR_PHY_LED_ON_LINK1000 BIT(0) ++#define AIR_PHY_LED_ON_LINK100 BIT(1) ++#define AIR_PHY_LED_ON_LINK10 BIT(2) ++#define AIR_PHY_LED_ON_LINKDOWN BIT(3) ++#define AIR_PHY_LED_ON_FDX BIT(4) /* Full duplex */ ++#define AIR_PHY_LED_ON_HDX BIT(5) /* Half duplex */ ++#define AIR_PHY_LED_ON_FORCE_ON BIT(6) ++#define AIR_PHY_LED_ON_LINK2500 BIT(8) ++#define AIR_PHY_LED_ON_POLARITY BIT(14) ++#define AIR_PHY_LED_ON_ENABLE BIT(15) ++ ++#define AIR_PHY_LED_BLINK(i) (0x025 + ((i) * 2)) ++#define AIR_PHY_LED_BLINK_1000TX BIT(0) ++#define AIR_PHY_LED_BLINK_1000RX BIT(1) ++#define AIR_PHY_LED_BLINK_100TX BIT(2) ++#define AIR_PHY_LED_BLINK_100RX BIT(3) ++#define AIR_PHY_LED_BLINK_10TX BIT(4) ++#define AIR_PHY_LED_BLINK_10RX BIT(5) ++#define AIR_PHY_LED_BLINK_COLLISION BIT(6) ++#define AIR_PHY_LED_BLINK_RX_CRC_ERR BIT(7) ++#define AIR_PHY_LED_BLINK_RX_IDLE_ERR BIT(8) ++#define AIR_PHY_LED_BLINK_FORCE_BLINK BIT(9) ++#define AIR_PHY_LED_BLINK_2500TX BIT(10) ++#define AIR_PHY_LED_BLINK_2500RX BIT(11) ++ ++/* Registers on BUCKPBUS */ ++#define EN8811H_2P5G_LPA 0x3b30 ++#define EN8811H_2P5G_LPA_2P5G BIT(0) ++ ++#define EN8811H_FW_VERSION 0x3b3c ++ ++#define EN8811H_POLARITY 0xca0f8 ++#define EN8811H_POLARITY_TX_NORMAL BIT(0) ++#define EN8811H_POLARITY_RX_REVERSE BIT(1) ++ ++#define EN8811H_GPIO_OUTPUT 0xcf8b8 ++#define EN8811H_GPIO_OUTPUT_345 (BIT(3) | BIT(4) | BIT(5)) ++ ++#define EN8811H_FW_CTRL_1 0x0f0018 ++#define EN8811H_FW_CTRL_1_START 0x0 ++#define EN8811H_FW_CTRL_1_FINISH 0x1 ++#define EN8811H_FW_CTRL_2 0x800000 ++#define EN8811H_FW_CTRL_2_LOADING BIT(11) ++ ++/* Led definitions */ ++#define EN8811H_LED_COUNT 3 ++ ++/* Default LED setup: ++ * GPIO5 <-> LED0 On: Link detected, blink Rx/Tx ++ * GPIO4 <-> LED1 On: Link detected at 2500 or 1000 Mbps ++ * GPIO3 <-> LED2 On: Link detected at 2500 or 100 Mbps ++ */ ++#define AIR_DEFAULT_TRIGGER_LED0 (BIT(TRIGGER_NETDEV_LINK) | \ ++ BIT(TRIGGER_NETDEV_RX) | \ ++ BIT(TRIGGER_NETDEV_TX)) ++#define AIR_DEFAULT_TRIGGER_LED1 (BIT(TRIGGER_NETDEV_LINK_2500) | \ ++ BIT(TRIGGER_NETDEV_LINK_1000)) ++#define AIR_DEFAULT_TRIGGER_LED2 (BIT(TRIGGER_NETDEV_LINK_2500) | \ ++ BIT(TRIGGER_NETDEV_LINK_100)) ++ ++struct led { ++ unsigned long rules; ++ unsigned long state; ++}; ++ ++struct en8811h_priv { ++ u32 firmware_version; ++ bool mcu_needs_restart; ++ struct led led[EN8811H_LED_COUNT]; ++}; ++ ++enum { ++ AIR_PHY_LED_STATE_FORCE_ON, ++ AIR_PHY_LED_STATE_FORCE_BLINK, ++}; ++ ++enum { ++ AIR_PHY_LED_DUR_BLINK_32MS, ++ AIR_PHY_LED_DUR_BLINK_64MS, ++ AIR_PHY_LED_DUR_BLINK_128MS, ++ AIR_PHY_LED_DUR_BLINK_256MS, ++ AIR_PHY_LED_DUR_BLINK_512MS, ++ AIR_PHY_LED_DUR_BLINK_1024MS, ++}; ++ ++enum { ++ AIR_LED_DISABLE, ++ AIR_LED_ENABLE, ++}; ++ ++enum { ++ AIR_ACTIVE_LOW, ++ AIR_ACTIVE_HIGH, ++}; ++ ++enum { ++ AIR_LED_MODE_DISABLE, ++ AIR_LED_MODE_USER_DEFINE, ++}; ++ ++#define AIR_PHY_LED_DUR_UNIT 1024 ++#define AIR_PHY_LED_DUR (AIR_PHY_LED_DUR_UNIT << AIR_PHY_LED_DUR_BLINK_64MS) ++ ++static const unsigned long en8811h_led_trig = BIT(TRIGGER_NETDEV_FULL_DUPLEX) | ++ BIT(TRIGGER_NETDEV_LINK) | ++ BIT(TRIGGER_NETDEV_LINK_10) | ++ BIT(TRIGGER_NETDEV_LINK_100) | ++ BIT(TRIGGER_NETDEV_LINK_1000) | ++ BIT(TRIGGER_NETDEV_LINK_2500) | ++ BIT(TRIGGER_NETDEV_RX) | ++ BIT(TRIGGER_NETDEV_TX); ++ ++static int air_phy_read_page(struct phy_device *phydev) ++{ ++ return __phy_read(phydev, AIR_EXT_PAGE_ACCESS); ++} ++ ++static int air_phy_write_page(struct phy_device *phydev, int page) ++{ ++ return __phy_write(phydev, AIR_EXT_PAGE_ACCESS, page); ++} ++ ++static int __air_buckpbus_reg_write(struct phy_device *phydev, ++ u32 pbus_address, u32 pbus_data) ++{ ++ int ret; ++ ++ ret = __phy_write(phydev, AIR_BPBUS_MODE, AIR_BPBUS_MODE_ADDR_FIXED); ++ if (ret < 0) ++ return ret; ++ ++ ret = __phy_write(phydev, AIR_BPBUS_WR_ADDR_HIGH, ++ upper_16_bits(pbus_address)); ++ if (ret < 0) ++ return ret; ++ ++ ret = __phy_write(phydev, AIR_BPBUS_WR_ADDR_LOW, ++ lower_16_bits(pbus_address)); ++ if (ret < 0) ++ return ret; ++ ++ ret = __phy_write(phydev, AIR_BPBUS_WR_DATA_HIGH, ++ upper_16_bits(pbus_data)); ++ if (ret < 0) ++ return ret; ++ ++ ret = __phy_write(phydev, AIR_BPBUS_WR_DATA_LOW, ++ lower_16_bits(pbus_data)); ++ if (ret < 0) ++ return ret; ++ ++ return 0; ++} ++ ++static int air_buckpbus_reg_write(struct phy_device *phydev, ++ u32 pbus_address, u32 pbus_data) ++{ ++ int saved_page; ++ int ret = 0; ++ ++ saved_page = phy_select_page(phydev, AIR_PHY_PAGE_EXTENDED_4); ++ ++ if (saved_page >= 0) { ++ ret = __air_buckpbus_reg_write(phydev, pbus_address, ++ pbus_data); ++ if (ret < 0) ++ phydev_err(phydev, "%s 0x%08x failed: %d\n", __func__, ++ pbus_address, ret); ++ } ++ ++ return phy_restore_page(phydev, saved_page, ret); ++} ++ ++static int __air_buckpbus_reg_read(struct phy_device *phydev, ++ u32 pbus_address, u32 *pbus_data) ++{ ++ int pbus_data_low, pbus_data_high; ++ int ret; ++ ++ ret = __phy_write(phydev, AIR_BPBUS_MODE, AIR_BPBUS_MODE_ADDR_FIXED); ++ if (ret < 0) ++ return ret; ++ ++ ret = __phy_write(phydev, AIR_BPBUS_RD_ADDR_HIGH, ++ upper_16_bits(pbus_address)); ++ if (ret < 0) ++ return ret; ++ ++ ret = __phy_write(phydev, AIR_BPBUS_RD_ADDR_LOW, ++ lower_16_bits(pbus_address)); ++ if (ret < 0) ++ return ret; ++ ++ pbus_data_high = __phy_read(phydev, AIR_BPBUS_RD_DATA_HIGH); ++ if (pbus_data_high < 0) ++ return ret; ++ ++ pbus_data_low = __phy_read(phydev, AIR_BPBUS_RD_DATA_LOW); ++ if (pbus_data_low < 0) ++ return ret; ++ ++ *pbus_data = pbus_data_low | (pbus_data_high << 16); ++ return 0; ++} ++ ++static int air_buckpbus_reg_read(struct phy_device *phydev, ++ u32 pbus_address, u32 *pbus_data) ++{ ++ int saved_page; ++ int ret = 0; ++ ++ saved_page = phy_select_page(phydev, AIR_PHY_PAGE_EXTENDED_4); ++ ++ if (saved_page >= 0) { ++ ret = __air_buckpbus_reg_read(phydev, pbus_address, pbus_data); ++ if (ret < 0) ++ phydev_err(phydev, "%s 0x%08x failed: %d\n", __func__, ++ pbus_address, ret); ++ } ++ ++ return phy_restore_page(phydev, saved_page, ret); ++} ++ ++static int __air_buckpbus_reg_modify(struct phy_device *phydev, ++ u32 pbus_address, u32 mask, u32 set) ++{ ++ int pbus_data_low, pbus_data_high; ++ u32 pbus_data_old, pbus_data_new; ++ int ret; ++ ++ ret = __phy_write(phydev, AIR_BPBUS_MODE, AIR_BPBUS_MODE_ADDR_FIXED); ++ if (ret < 0) ++ return ret; ++ ++ ret = __phy_write(phydev, AIR_BPBUS_RD_ADDR_HIGH, ++ upper_16_bits(pbus_address)); ++ if (ret < 0) ++ return ret; ++ ++ ret = __phy_write(phydev, AIR_BPBUS_RD_ADDR_LOW, ++ lower_16_bits(pbus_address)); ++ if (ret < 0) ++ return ret; ++ ++ pbus_data_high = __phy_read(phydev, AIR_BPBUS_RD_DATA_HIGH); ++ if (pbus_data_high < 0) ++ return ret; ++ ++ pbus_data_low = __phy_read(phydev, AIR_BPBUS_RD_DATA_LOW); ++ if (pbus_data_low < 0) ++ return ret; ++ ++ pbus_data_old = pbus_data_low | (pbus_data_high << 16); ++ pbus_data_new = (pbus_data_old & ~mask) | set; ++ if (pbus_data_new == pbus_data_old) ++ return 0; ++ ++ ret = __phy_write(phydev, AIR_BPBUS_WR_ADDR_HIGH, ++ upper_16_bits(pbus_address)); ++ if (ret < 0) ++ return ret; ++ ++ ret = __phy_write(phydev, AIR_BPBUS_WR_ADDR_LOW, ++ lower_16_bits(pbus_address)); ++ if (ret < 0) ++ return ret; ++ ++ ret = __phy_write(phydev, AIR_BPBUS_WR_DATA_HIGH, ++ upper_16_bits(pbus_data_new)); ++ if (ret < 0) ++ return ret; ++ ++ ret = __phy_write(phydev, AIR_BPBUS_WR_DATA_LOW, ++ lower_16_bits(pbus_data_new)); ++ if (ret < 0) ++ return ret; ++ ++ return 0; ++} ++ ++static int air_buckpbus_reg_modify(struct phy_device *phydev, ++ u32 pbus_address, u32 mask, u32 set) ++{ ++ int saved_page; ++ int ret = 0; ++ ++ saved_page = phy_select_page(phydev, AIR_PHY_PAGE_EXTENDED_4); ++ ++ if (saved_page >= 0) { ++ ret = __air_buckpbus_reg_modify(phydev, pbus_address, mask, ++ set); ++ if (ret < 0) ++ phydev_err(phydev, "%s 0x%08x failed: %d\n", __func__, ++ pbus_address, ret); ++ } ++ ++ return phy_restore_page(phydev, saved_page, ret); ++} ++ ++static int __air_write_buf(struct phy_device *phydev, u32 address, ++ const struct firmware *fw) ++{ ++ unsigned int offset; ++ int ret; ++ u16 val; ++ ++ ret = __phy_write(phydev, AIR_BPBUS_MODE, AIR_BPBUS_MODE_ADDR_INCR); ++ if (ret < 0) ++ return ret; ++ ++ ret = __phy_write(phydev, AIR_BPBUS_WR_ADDR_HIGH, ++ upper_16_bits(address)); ++ if (ret < 0) ++ return ret; ++ ++ ret = __phy_write(phydev, AIR_BPBUS_WR_ADDR_LOW, ++ lower_16_bits(address)); ++ if (ret < 0) ++ return ret; ++ ++ for (offset = 0; offset < fw->size; offset += 4) { ++ val = get_unaligned_le16(&fw->data[offset + 2]); ++ ret = __phy_write(phydev, AIR_BPBUS_WR_DATA_HIGH, val); ++ if (ret < 0) ++ return ret; ++ ++ val = get_unaligned_le16(&fw->data[offset]); ++ ret = __phy_write(phydev, AIR_BPBUS_WR_DATA_LOW, val); ++ if (ret < 0) ++ return ret; ++ } ++ ++ return 0; ++} ++ ++static int air_write_buf(struct phy_device *phydev, u32 address, ++ const struct firmware *fw) ++{ ++ int saved_page; ++ int ret = 0; ++ ++ saved_page = phy_select_page(phydev, AIR_PHY_PAGE_EXTENDED_4); ++ ++ if (saved_page >= 0) { ++ ret = __air_write_buf(phydev, address, fw); ++ if (ret < 0) ++ phydev_err(phydev, "%s 0x%08x failed: %d\n", __func__, ++ address, ret); ++ } ++ ++ return phy_restore_page(phydev, saved_page, ret); ++} ++ ++static int en8811h_wait_mcu_ready(struct phy_device *phydev) ++{ ++ int ret, reg_value; ++ ++ /* Because of mdio-lock, may have to wait for multiple loads */ ++ ret = phy_read_mmd_poll_timeout(phydev, MDIO_MMD_VEND1, ++ EN8811H_PHY_FW_STATUS, reg_value, ++ reg_value == EN8811H_PHY_READY, ++ 20000, 7500000, true); ++ if (ret) { ++ phydev_err(phydev, "MCU not ready: 0x%x\n", reg_value); ++ return -ENODEV; ++ } ++ ++ return 0; ++} ++ ++static int en8811h_load_firmware(struct phy_device *phydev) ++{ ++ struct en8811h_priv *priv = phydev->priv; ++ struct device *dev = &phydev->mdio.dev; ++ const struct firmware *fw1, *fw2; ++ int ret; ++ ++ ret = request_firmware_direct(&fw1, EN8811H_MD32_DM, dev); ++ if (ret < 0) ++ return ret; ++ ++ ret = request_firmware_direct(&fw2, EN8811H_MD32_DSP, dev); ++ if (ret < 0) ++ goto en8811h_load_firmware_rel1; ++ ++ ret = air_buckpbus_reg_write(phydev, EN8811H_FW_CTRL_1, ++ EN8811H_FW_CTRL_1_START); ++ if (ret < 0) ++ goto en8811h_load_firmware_out; ++ ++ ret = air_buckpbus_reg_modify(phydev, EN8811H_FW_CTRL_2, ++ EN8811H_FW_CTRL_2_LOADING, ++ EN8811H_FW_CTRL_2_LOADING); ++ if (ret < 0) ++ goto en8811h_load_firmware_out; ++ ++ ret = air_write_buf(phydev, AIR_FW_ADDR_DM, fw1); ++ if (ret < 0) ++ goto en8811h_load_firmware_out; ++ ++ ret = air_write_buf(phydev, AIR_FW_ADDR_DSP, fw2); ++ if (ret < 0) ++ goto en8811h_load_firmware_out; ++ ++ ret = air_buckpbus_reg_modify(phydev, EN8811H_FW_CTRL_2, ++ EN8811H_FW_CTRL_2_LOADING, 0); ++ if (ret < 0) ++ goto en8811h_load_firmware_out; ++ ++ ret = air_buckpbus_reg_write(phydev, EN8811H_FW_CTRL_1, ++ EN8811H_FW_CTRL_1_FINISH); ++ if (ret < 0) ++ goto en8811h_load_firmware_out; ++ ++ ret = en8811h_wait_mcu_ready(phydev); ++ ++ air_buckpbus_reg_read(phydev, EN8811H_FW_VERSION, ++ &priv->firmware_version); ++ phydev_info(phydev, "MD32 firmware version: %08x\n", ++ priv->firmware_version); ++ ++en8811h_load_firmware_out: ++ release_firmware(fw2); ++ ++en8811h_load_firmware_rel1: ++ release_firmware(fw1); ++ ++ if (ret < 0) ++ phydev_err(phydev, "Load firmware failed: %d\n", ret); ++ ++ return ret; ++} ++ ++static int en8811h_restart_mcu(struct phy_device *phydev) ++{ ++ int ret; ++ ++ ret = air_buckpbus_reg_write(phydev, EN8811H_FW_CTRL_1, ++ EN8811H_FW_CTRL_1_START); ++ if (ret < 0) ++ return ret; ++ ++ ret = air_buckpbus_reg_write(phydev, EN8811H_FW_CTRL_1, ++ EN8811H_FW_CTRL_1_FINISH); ++ if (ret < 0) ++ return ret; ++ ++ return en8811h_wait_mcu_ready(phydev); ++} ++ ++static int air_hw_led_on_set(struct phy_device *phydev, u8 index, bool on) ++{ ++ struct en8811h_priv *priv = phydev->priv; ++ bool changed; ++ ++ if (index >= EN8811H_LED_COUNT) ++ return -EINVAL; ++ ++ if (on) ++ changed = !test_and_set_bit(AIR_PHY_LED_STATE_FORCE_ON, ++ &priv->led[index].state); ++ else ++ changed = !!test_and_clear_bit(AIR_PHY_LED_STATE_FORCE_ON, ++ &priv->led[index].state); ++ ++ changed |= (priv->led[index].rules != 0); ++ ++ if (changed) ++ return phy_modify_mmd(phydev, MDIO_MMD_VEND2, ++ AIR_PHY_LED_ON(index), ++ AIR_PHY_LED_ON_MASK, ++ on ? AIR_PHY_LED_ON_FORCE_ON : 0); ++ ++ return 0; ++} ++ ++static int air_hw_led_blink_set(struct phy_device *phydev, u8 index, ++ bool blinking) ++{ ++ struct en8811h_priv *priv = phydev->priv; ++ bool changed; ++ ++ if (index >= EN8811H_LED_COUNT) ++ return -EINVAL; ++ ++ if (blinking) ++ changed = !test_and_set_bit(AIR_PHY_LED_STATE_FORCE_BLINK, ++ &priv->led[index].state); ++ else ++ changed = !!test_and_clear_bit(AIR_PHY_LED_STATE_FORCE_BLINK, ++ &priv->led[index].state); ++ ++ changed |= (priv->led[index].rules != 0); ++ ++ if (changed) ++ return phy_write_mmd(phydev, MDIO_MMD_VEND2, ++ AIR_PHY_LED_BLINK(index), ++ blinking ? ++ AIR_PHY_LED_BLINK_FORCE_BLINK : 0); ++ else ++ return 0; ++} ++ ++static int air_led_blink_set(struct phy_device *phydev, u8 index, ++ unsigned long *delay_on, ++ unsigned long *delay_off) ++{ ++ struct en8811h_priv *priv = phydev->priv; ++ bool blinking = false; ++ int err; ++ ++ if (index >= EN8811H_LED_COUNT) ++ return -EINVAL; ++ ++ if (delay_on && delay_off && (*delay_on > 0) && (*delay_off > 0)) { ++ blinking = true; ++ *delay_on = 50; ++ *delay_off = 50; ++ } ++ ++ err = air_hw_led_blink_set(phydev, index, blinking); ++ if (err) ++ return err; ++ ++ /* led-blink set, so switch led-on off */ ++ err = air_hw_led_on_set(phydev, index, false); ++ if (err) ++ return err; ++ ++ /* hw-control is off*/ ++ if (!!test_bit(AIR_PHY_LED_STATE_FORCE_BLINK, &priv->led[index].state)) ++ priv->led[index].rules = 0; ++ ++ return 0; ++} ++ ++static int air_led_brightness_set(struct phy_device *phydev, u8 index, ++ enum led_brightness value) ++{ ++ struct en8811h_priv *priv = phydev->priv; ++ int err; ++ ++ if (index >= EN8811H_LED_COUNT) ++ return -EINVAL; ++ ++ /* led-on set, so switch led-blink off */ ++ err = air_hw_led_blink_set(phydev, index, false); ++ if (err) ++ return err; ++ ++ err = air_hw_led_on_set(phydev, index, (value != LED_OFF)); ++ if (err) ++ return err; ++ ++ /* hw-control is off */ ++ if (!!test_bit(AIR_PHY_LED_STATE_FORCE_ON, &priv->led[index].state)) ++ priv->led[index].rules = 0; ++ ++ return 0; ++} ++ ++static int air_led_hw_control_get(struct phy_device *phydev, u8 index, ++ unsigned long *rules) ++{ ++ struct en8811h_priv *priv = phydev->priv; ++ ++ if (index >= EN8811H_LED_COUNT) ++ return -EINVAL; ++ ++ *rules = priv->led[index].rules; ++ ++ return 0; ++}; ++ ++static int air_led_hw_control_set(struct phy_device *phydev, u8 index, ++ unsigned long rules) ++{ ++ struct en8811h_priv *priv = phydev->priv; ++ u16 on = 0, blink = 0; ++ int ret; ++ ++ if (index >= EN8811H_LED_COUNT) ++ return -EINVAL; ++ ++ priv->led[index].rules = rules; ++ ++ if (rules & BIT(TRIGGER_NETDEV_FULL_DUPLEX)) ++ on |= AIR_PHY_LED_ON_FDX; ++ ++ if (rules & (BIT(TRIGGER_NETDEV_LINK_10) | BIT(TRIGGER_NETDEV_LINK))) ++ on |= AIR_PHY_LED_ON_LINK10; ++ ++ if (rules & (BIT(TRIGGER_NETDEV_LINK_100) | BIT(TRIGGER_NETDEV_LINK))) ++ on |= AIR_PHY_LED_ON_LINK100; ++ ++ if (rules & (BIT(TRIGGER_NETDEV_LINK_1000) | BIT(TRIGGER_NETDEV_LINK))) ++ on |= AIR_PHY_LED_ON_LINK1000; ++ ++ if (rules & (BIT(TRIGGER_NETDEV_LINK_2500) | BIT(TRIGGER_NETDEV_LINK))) ++ on |= AIR_PHY_LED_ON_LINK2500; ++ ++ if (rules & BIT(TRIGGER_NETDEV_RX)) { ++ blink |= AIR_PHY_LED_BLINK_10RX | ++ AIR_PHY_LED_BLINK_100RX | ++ AIR_PHY_LED_BLINK_1000RX | ++ AIR_PHY_LED_BLINK_2500RX; ++ } ++ ++ if (rules & BIT(TRIGGER_NETDEV_TX)) { ++ blink |= AIR_PHY_LED_BLINK_10TX | ++ AIR_PHY_LED_BLINK_100TX | ++ AIR_PHY_LED_BLINK_1000TX | ++ AIR_PHY_LED_BLINK_2500TX; ++ } ++ ++ if (blink || on) { ++ /* switch hw-control on, so led-on and led-blink are off */ ++ clear_bit(AIR_PHY_LED_STATE_FORCE_ON, ++ &priv->led[index].state); ++ clear_bit(AIR_PHY_LED_STATE_FORCE_BLINK, ++ &priv->led[index].state); ++ } else { ++ priv->led[index].rules = 0; ++ } ++ ++ ret = phy_modify_mmd(phydev, MDIO_MMD_VEND2, AIR_PHY_LED_ON(index), ++ AIR_PHY_LED_ON_MASK, on); ++ ++ if (ret < 0) ++ return ret; ++ ++ return phy_write_mmd(phydev, MDIO_MMD_VEND2, AIR_PHY_LED_BLINK(index), ++ blink); ++}; ++ ++static int air_led_init(struct phy_device *phydev, u8 index, u8 state, u8 pol) ++{ ++ int val = 0; ++ int err; ++ ++ if (index >= EN8811H_LED_COUNT) ++ return -EINVAL; ++ ++ if (state == AIR_LED_ENABLE) ++ val |= AIR_PHY_LED_ON_ENABLE; ++ else ++ val &= ~AIR_PHY_LED_ON_ENABLE; ++ ++ if (pol == AIR_ACTIVE_HIGH) ++ val |= AIR_PHY_LED_ON_POLARITY; ++ else ++ val &= ~AIR_PHY_LED_ON_POLARITY; ++ ++ err = phy_modify_mmd(phydev, MDIO_MMD_VEND2, AIR_PHY_LED_ON(index), ++ AIR_PHY_LED_ON_ENABLE | ++ AIR_PHY_LED_ON_POLARITY, val); ++ ++ if (err < 0) ++ return err; ++ ++ return 0; ++} ++ ++static int air_leds_init(struct phy_device *phydev, int num, int dur, int mode) ++{ ++ struct en8811h_priv *priv = phydev->priv; ++ int ret, i; ++ ++ ret = phy_write_mmd(phydev, MDIO_MMD_VEND2, AIR_PHY_LED_DUR_BLINK, ++ dur); ++ if (ret < 0) ++ return ret; ++ ++ ret = phy_write_mmd(phydev, MDIO_MMD_VEND2, AIR_PHY_LED_DUR_ON, ++ dur >> 1); ++ if (ret < 0) ++ return ret; ++ ++ switch (mode) { ++ case AIR_LED_MODE_DISABLE: ++ ret = phy_modify_mmd(phydev, MDIO_MMD_VEND2, AIR_PHY_LED_BCR, ++ AIR_PHY_LED_BCR_EXT_CTRL | ++ AIR_PHY_LED_BCR_MODE_MASK, 0); ++ if (ret < 0) ++ return ret; ++ break; ++ case AIR_LED_MODE_USER_DEFINE: ++ ret = phy_modify_mmd(phydev, MDIO_MMD_VEND2, AIR_PHY_LED_BCR, ++ AIR_PHY_LED_BCR_EXT_CTRL | ++ AIR_PHY_LED_BCR_CLK_EN, ++ AIR_PHY_LED_BCR_EXT_CTRL | ++ AIR_PHY_LED_BCR_CLK_EN); ++ if (ret < 0) ++ return ret; ++ break; ++ default: ++ phydev_err(phydev, "LED mode %d is not supported\n", mode); ++ return -EINVAL; ++ } ++ ++ for (i = 0; i < num; ++i) { ++ ret = air_led_init(phydev, i, AIR_LED_ENABLE, AIR_ACTIVE_HIGH); ++ if (ret < 0) { ++ phydev_err(phydev, "LED%d init failed: %d\n", i, ret); ++ return ret; ++ } ++ air_led_hw_control_set(phydev, i, priv->led[i].rules); ++ } ++ ++ return 0; ++} ++ ++static int en8811h_led_hw_is_supported(struct phy_device *phydev, u8 index, ++ unsigned long rules) ++{ ++ if (index >= EN8811H_LED_COUNT) ++ return -EINVAL; ++ ++ /* All combinations of the supported triggers are allowed */ ++ if (rules & ~en8811h_led_trig) ++ return -EOPNOTSUPP; ++ ++ return 0; ++}; ++ ++static int en8811h_probe(struct phy_device *phydev) ++{ ++ struct en8811h_priv *priv; ++ int ret; ++ ++ priv = devm_kzalloc(&phydev->mdio.dev, sizeof(struct en8811h_priv), ++ GFP_KERNEL); ++ if (!priv) ++ return -ENOMEM; ++ phydev->priv = priv; ++ ++ ret = en8811h_load_firmware(phydev); ++ if (ret < 0) ++ return ret; ++ ++ /* mcu has just restarted after firmware load */ ++ priv->mcu_needs_restart = false; ++ ++ priv->led[0].rules = AIR_DEFAULT_TRIGGER_LED0; ++ priv->led[1].rules = AIR_DEFAULT_TRIGGER_LED1; ++ priv->led[2].rules = AIR_DEFAULT_TRIGGER_LED2; ++ ++ /* MDIO_DEVS1/2 empty, so set mmds_present bits here */ ++ phydev->c45_ids.mmds_present |= MDIO_DEVS_PMAPMD | MDIO_DEVS_AN; ++ ++ ret = air_leds_init(phydev, EN8811H_LED_COUNT, AIR_PHY_LED_DUR, ++ AIR_LED_MODE_DISABLE); ++ if (ret < 0) { ++ phydev_err(phydev, "Failed to disable leds: %d\n", ret); ++ return ret; ++ } ++ ++ /* Configure led gpio pins as output */ ++ ret = air_buckpbus_reg_modify(phydev, EN8811H_GPIO_OUTPUT, ++ EN8811H_GPIO_OUTPUT_345, ++ EN8811H_GPIO_OUTPUT_345); ++ if (ret < 0) ++ return ret; ++ ++ return 0; ++} ++ ++static int en8811h_config_init(struct phy_device *phydev) ++{ ++ struct en8811h_priv *priv = phydev->priv; ++ struct device *dev = &phydev->mdio.dev; ++ u32 pbus_value; ++ int ret; ++ ++ /* If restart happened in .probe(), no need to restart now */ ++ if (priv->mcu_needs_restart) { ++ ret = en8811h_restart_mcu(phydev); ++ if (ret < 0) ++ return ret; ++ } else { ++ /* Next calls to .config_init() mcu needs to restart */ ++ priv->mcu_needs_restart = true; ++ } ++ ++ /* Select mode 1, the only mode supported. ++ * Configures the SerDes for 2500Base-X with rate adaptation ++ */ ++ ret = phy_write_mmd(phydev, MDIO_MMD_VEND1, AIR_PHY_MCU_CMD_1, ++ AIR_PHY_MCU_CMD_1_MODE1); ++ if (ret < 0) ++ return ret; ++ ret = phy_write_mmd(phydev, MDIO_MMD_VEND1, AIR_PHY_MCU_CMD_2, ++ AIR_PHY_MCU_CMD_2_MODE1); ++ if (ret < 0) ++ return ret; ++ ret = phy_write_mmd(phydev, MDIO_MMD_VEND1, AIR_PHY_MCU_CMD_3, ++ AIR_PHY_MCU_CMD_3_MODE1); ++ if (ret < 0) ++ return ret; ++ ret = phy_write_mmd(phydev, MDIO_MMD_VEND1, AIR_PHY_MCU_CMD_4, ++ AIR_PHY_MCU_CMD_4_MODE1); ++ if (ret < 0) ++ return ret; ++ ++ /* Serdes polarity */ ++ pbus_value = 0; ++ if (device_property_read_bool(dev, "airoha,pnswap-rx")) ++ pbus_value |= EN8811H_POLARITY_RX_REVERSE; ++ else ++ pbus_value &= ~EN8811H_POLARITY_RX_REVERSE; ++ if (device_property_read_bool(dev, "airoha,pnswap-tx")) ++ pbus_value &= ~EN8811H_POLARITY_TX_NORMAL; ++ else ++ pbus_value |= EN8811H_POLARITY_TX_NORMAL; ++ ret = air_buckpbus_reg_modify(phydev, EN8811H_POLARITY, ++ EN8811H_POLARITY_RX_REVERSE | ++ EN8811H_POLARITY_TX_NORMAL, pbus_value); ++ if (ret < 0) ++ return ret; ++ ++ ret = air_leds_init(phydev, EN8811H_LED_COUNT, AIR_PHY_LED_DUR, ++ AIR_LED_MODE_USER_DEFINE); ++ if (ret < 0) { ++ phydev_err(phydev, "Failed to initialize leds: %d\n", ret); ++ return ret; ++ } ++ ++ return 0; ++} ++ ++static int en8811h_get_features(struct phy_device *phydev) ++{ ++ linkmode_set_bit_array(phy_basic_ports_array, ++ ARRAY_SIZE(phy_basic_ports_array), ++ phydev->supported); ++ ++ return genphy_c45_pma_read_abilities(phydev); ++} ++ ++static int en8811h_get_rate_matching(struct phy_device *phydev, ++ phy_interface_t iface) ++{ ++ return RATE_MATCH_PAUSE; ++} ++ ++static int en8811h_config_aneg(struct phy_device *phydev) ++{ ++ bool changed = false; ++ int ret; ++ u32 adv; ++ ++ if (phydev->autoneg == AUTONEG_DISABLE) { ++ phydev_warn(phydev, "Disabling autoneg is not supported\n"); ++ return -EINVAL; ++ } ++ ++ adv = linkmode_adv_to_mii_10gbt_adv_t(phydev->advertising); ++ ++ ret = phy_modify_mmd_changed(phydev, MDIO_MMD_AN, MDIO_AN_10GBT_CTRL, ++ MDIO_AN_10GBT_CTRL_ADV2_5G, adv); ++ if (ret < 0) ++ return ret; ++ if (ret > 0) ++ changed = true; ++ ++ return __genphy_config_aneg(phydev, changed); ++} ++ ++static int en8811h_read_status(struct phy_device *phydev) ++{ ++ struct en8811h_priv *priv = phydev->priv; ++ u32 pbus_value; ++ int ret, val; ++ ++ ret = genphy_update_link(phydev); ++ if (ret) ++ return ret; ++ ++ phydev->master_slave_get = MASTER_SLAVE_CFG_UNSUPPORTED; ++ phydev->master_slave_state = MASTER_SLAVE_STATE_UNSUPPORTED; ++ phydev->speed = SPEED_UNKNOWN; ++ phydev->duplex = DUPLEX_UNKNOWN; ++ phydev->pause = 0; ++ phydev->asym_pause = 0; ++ phydev->rate_matching = RATE_MATCH_PAUSE; ++ ++ ret = genphy_read_master_slave(phydev); ++ if (ret < 0) ++ return ret; ++ ++ ret = genphy_read_lpa(phydev); ++ if (ret < 0) ++ return ret; ++ ++ /* Get link partner 2.5GBASE-T ability from vendor register */ ++ ret = air_buckpbus_reg_read(phydev, EN8811H_2P5G_LPA, &pbus_value); ++ if (ret < 0) ++ return ret; ++ linkmode_mod_bit(ETHTOOL_LINK_MODE_2500baseT_Full_BIT, ++ phydev->lp_advertising, ++ pbus_value & EN8811H_2P5G_LPA_2P5G); ++ ++ if (phydev->autoneg_complete) ++ phy_resolve_aneg_pause(phydev); ++ ++ if (!phydev->link) ++ return 0; ++ ++ /* Get real speed from vendor register */ ++ val = phy_read(phydev, AIR_AUX_CTRL_STATUS); ++ if (val < 0) ++ return val; ++ switch (val & AIR_AUX_CTRL_STATUS_SPEED_MASK) { ++ case AIR_AUX_CTRL_STATUS_SPEED_2500: ++ phydev->speed = SPEED_2500; ++ break; ++ case AIR_AUX_CTRL_STATUS_SPEED_1000: ++ phydev->speed = SPEED_1000; ++ break; ++ case AIR_AUX_CTRL_STATUS_SPEED_100: ++ phydev->speed = SPEED_100; ++ break; ++ } ++ ++ /* Firmware before version 24011202 has no vendor register 2P5G_LPA. ++ * Assume link partner advertised it if connected at 2500Mbps. ++ */ ++ if (priv->firmware_version < 0x24011202) { ++ linkmode_mod_bit(ETHTOOL_LINK_MODE_2500baseT_Full_BIT, ++ phydev->lp_advertising, ++ phydev->speed == SPEED_2500); ++ } ++ ++ /* Only supports full duplex */ ++ phydev->duplex = DUPLEX_FULL; ++ ++ return 0; ++} ++ ++static int en8811h_clear_intr(struct phy_device *phydev) ++{ ++ int ret; ++ ++ ret = phy_write_mmd(phydev, MDIO_MMD_VEND1, AIR_PHY_MCU_CMD_3, ++ AIR_PHY_MCU_CMD_3_DOCMD); ++ if (ret < 0) ++ return ret; ++ ++ ret = phy_write_mmd(phydev, MDIO_MMD_VEND1, AIR_PHY_MCU_CMD_4, ++ AIR_PHY_MCU_CMD_4_INTCLR); ++ if (ret < 0) ++ return ret; ++ ++ return 0; ++} ++ ++static irqreturn_t en8811h_handle_interrupt(struct phy_device *phydev) ++{ ++ int ret; ++ ++ ret = en8811h_clear_intr(phydev); ++ if (ret < 0) { ++ phy_error(phydev); ++ return IRQ_NONE; ++ } ++ ++ phy_trigger_machine(phydev); ++ ++ return IRQ_HANDLED; ++} ++ ++static struct phy_driver en8811h_driver[] = { ++{ ++ PHY_ID_MATCH_MODEL(EN8811H_PHY_ID), ++ .name = "Airoha EN8811H", ++ .probe = en8811h_probe, ++ .get_features = en8811h_get_features, ++ .config_init = en8811h_config_init, ++ .get_rate_matching = en8811h_get_rate_matching, ++ .config_aneg = en8811h_config_aneg, ++ .read_status = en8811h_read_status, ++ .config_intr = en8811h_clear_intr, ++ .handle_interrupt = en8811h_handle_interrupt, ++ .led_hw_is_supported = en8811h_led_hw_is_supported, ++ .read_page = air_phy_read_page, ++ .write_page = air_phy_write_page, ++ .led_blink_set = air_led_blink_set, ++ .led_brightness_set = air_led_brightness_set, ++ .led_hw_control_set = air_led_hw_control_set, ++ .led_hw_control_get = air_led_hw_control_get, ++} }; ++ ++module_phy_driver(en8811h_driver); ++ ++static struct mdio_device_id __maybe_unused en8811h_tbl[] = { ++ { PHY_ID_MATCH_MODEL(EN8811H_PHY_ID) }, ++ { } ++}; ++ ++MODULE_DEVICE_TABLE(mdio, en8811h_tbl); ++MODULE_FIRMWARE(EN8811H_MD32_DM); ++MODULE_FIRMWARE(EN8811H_MD32_DSP); ++ ++MODULE_DESCRIPTION("Airoha EN8811H PHY drivers"); ++MODULE_AUTHOR("Airoha"); ++MODULE_AUTHOR("Eric Woudstra "); ++MODULE_LICENSE("GPL"); diff --git a/target/linux/generic/backport-6.1/799-v6.10-net-phy-air_en8811h-fix-some-error-codes.patch b/target/linux/generic/backport-6.1/799-v6.10-net-phy-air_en8811h-fix-some-error-codes.patch new file mode 100644 index 000000000..1bd0eefe7 --- /dev/null +++ b/target/linux/generic/backport-6.1/799-v6.10-net-phy-air_en8811h-fix-some-error-codes.patch @@ -0,0 +1,47 @@ +From 87c33315af380ca12a2e59ac94edad4fe0481b4c Mon Sep 17 00:00:00 2001 +From: Dan Carpenter +Date: Fri, 5 Apr 2024 13:08:59 +0300 +Subject: [PATCH] net: phy: air_en8811h: fix some error codes + +These error paths accidentally return "ret" which is zero/success +instead of the correct error code. + +Fixes: 71e79430117d ("net: phy: air_en8811h: Add the Airoha EN8811H PHY driver") +Signed-off-by: Dan Carpenter +Reviewed-by: Simon Horman +Link: https://lore.kernel.org/r/7ef2e230-dfb7-4a77-8973-9e5be1a99fc2@moroto.mountain +Signed-off-by: Jakub Kicinski +--- + drivers/net/phy/air_en8811h.c | 8 ++++---- + 1 file changed, 4 insertions(+), 4 deletions(-) + +--- a/drivers/net/phy/air_en8811h.c ++++ b/drivers/net/phy/air_en8811h.c +@@ -272,11 +272,11 @@ static int __air_buckpbus_reg_read(struc + + pbus_data_high = __phy_read(phydev, AIR_BPBUS_RD_DATA_HIGH); + if (pbus_data_high < 0) +- return ret; ++ return pbus_data_high; + + pbus_data_low = __phy_read(phydev, AIR_BPBUS_RD_DATA_LOW); + if (pbus_data_low < 0) +- return ret; ++ return pbus_data_low; + + *pbus_data = pbus_data_low | (pbus_data_high << 16); + return 0; +@@ -323,11 +323,11 @@ static int __air_buckpbus_reg_modify(str + + pbus_data_high = __phy_read(phydev, AIR_BPBUS_RD_DATA_HIGH); + if (pbus_data_high < 0) +- return ret; ++ return pbus_data_high; + + pbus_data_low = __phy_read(phydev, AIR_BPBUS_RD_DATA_LOW); + if (pbus_data_low < 0) +- return ret; ++ return pbus_data_low; + + pbus_data_old = pbus_data_low | (pbus_data_high << 16); + pbus_data_new = (pbus_data_old & ~mask) | set; diff --git a/target/linux/generic/backport-6.1/801-v6.4-01-net-dsa-qca8k-move-qca8k_port_to_phy-to-header.patch b/target/linux/generic/backport-6.1/801-v6.4-01-net-dsa-qca8k-move-qca8k_port_to_phy-to-header.patch index 07becafbe..f6a025fa1 100644 --- a/target/linux/generic/backport-6.1/801-v6.4-01-net-dsa-qca8k-move-qca8k_port_to_phy-to-header.patch +++ b/target/linux/generic/backport-6.1/801-v6.4-01-net-dsa-qca8k-move-qca8k_port_to_phy-to-header.patch @@ -20,7 +20,7 @@ Signed-off-by: David S. Miller --- a/drivers/net/dsa/qca/qca8k-8xxx.c +++ b/drivers/net/dsa/qca/qca8k-8xxx.c -@@ -730,21 +730,6 @@ err_clear_skb: +@@ -789,21 +789,6 @@ err_clear_skb: return ret; } @@ -44,7 +44,7 @@ Signed-off-by: David S. Miller { --- a/drivers/net/dsa/qca/qca8k.h +++ b/drivers/net/dsa/qca/qca8k.h -@@ -422,6 +422,20 @@ struct qca8k_fdb { +@@ -421,6 +421,20 @@ struct qca8k_fdb { u8 mac[6]; }; diff --git a/target/linux/generic/backport-6.1/801-v6.4-02-net-dsa-qca8k-add-LEDs-basic-support.patch b/target/linux/generic/backport-6.1/801-v6.4-02-net-dsa-qca8k-add-LEDs-basic-support.patch index 414ba97e9..409fe9c7a 100644 --- a/target/linux/generic/backport-6.1/801-v6.4-02-net-dsa-qca8k-add-LEDs-basic-support.patch +++ b/target/linux/generic/backport-6.1/801-v6.4-02-net-dsa-qca8k-add-LEDs-basic-support.patch @@ -71,7 +71,7 @@ Signed-off-by: David S. Miller static void qca8k_split_addr(u32 regaddr, u16 *r1, u16 *r2, u16 *page) -@@ -1751,6 +1752,10 @@ qca8k_setup(struct dsa_switch *ds) +@@ -1851,6 +1852,10 @@ qca8k_setup(struct dsa_switch *ds) if (ret) return ret; @@ -386,7 +386,7 @@ Signed-off-by: David S. Miller #define QCA8K_GOL_MAC_ADDR0 0x60 #define QCA8K_GOL_MAC_ADDR1 0x64 #define QCA8K_MAX_FRAME_SIZE 0x78 -@@ -383,6 +429,19 @@ struct qca8k_pcs { +@@ -382,6 +428,19 @@ struct qca8k_pcs { int port; }; @@ -406,7 +406,7 @@ Signed-off-by: David S. Miller struct qca8k_priv { u8 switch_id; u8 switch_revision; -@@ -407,6 +466,7 @@ struct qca8k_priv { +@@ -406,6 +465,7 @@ struct qca8k_priv { struct qca8k_pcs pcs_port_0; struct qca8k_pcs pcs_port_6; const struct qca8k_match_data *info; diff --git a/target/linux/generic/backport-6.1/801-v6.4-05-net-phy-Add-a-binding-for-PHY-LEDs.patch b/target/linux/generic/backport-6.1/801-v6.4-05-net-phy-Add-a-binding-for-PHY-LEDs.patch index a3184513e..0d2c0bcd8 100644 --- a/target/linux/generic/backport-6.1/801-v6.4-05-net-phy-Add-a-binding-for-PHY-LEDs.patch +++ b/target/linux/generic/backport-6.1/801-v6.4-05-net-phy-Add-a-binding-for-PHY-LEDs.patch @@ -48,7 +48,7 @@ Signed-off-by: David S. Miller #include #include #include -@@ -644,6 +646,7 @@ struct phy_device *phy_device_create(str +@@ -642,6 +644,7 @@ struct phy_device *phy_device_create(str device_initialize(&mdiodev->dev); dev->state = PHY_DOWN; @@ -56,7 +56,7 @@ Signed-off-by: David S. Miller mutex_init(&dev->lock); INIT_DELAYED_WORK(&dev->state_queue, phy_state_machine); -@@ -2931,6 +2934,74 @@ static bool phy_drv_supports_irq(struct +@@ -3035,6 +3038,74 @@ static bool phy_drv_supports_irq(struct return phydrv->config_intr && phydrv->handle_interrupt; } @@ -131,7 +131,7 @@ Signed-off-by: David S. Miller /** * fwnode_mdio_find_device - Given a fwnode, find the mdio_device * @fwnode: pointer to the mdio_device's fwnode -@@ -3109,6 +3180,11 @@ static int phy_probe(struct device *dev) +@@ -3213,6 +3284,11 @@ static int phy_probe(struct device *dev) /* Set the state to READY by default */ phydev->state = PHY_READY; @@ -153,7 +153,7 @@ Signed-off-by: David S. Miller #include #include #include -@@ -593,6 +594,7 @@ struct macsec_ops; +@@ -606,6 +607,7 @@ struct macsec_ops; * @phy_num_led_triggers: Number of triggers in @phy_led_triggers * @led_link_trigger: LED trigger for link up/down * @last_triggered: last LED trigger for link speed @@ -161,7 +161,7 @@ Signed-off-by: David S. Miller * @master_slave_set: User requested master/slave configuration * @master_slave_get: Current master/slave advertisement * @master_slave_state: Current master/slave configuration -@@ -685,6 +687,7 @@ struct phy_device { +@@ -698,6 +700,7 @@ struct phy_device { struct phy_led_trigger *led_link_trigger; #endif @@ -169,7 +169,7 @@ Signed-off-by: David S. Miller /* * Interrupt number for this PHY -@@ -759,6 +762,19 @@ struct phy_tdr_config { +@@ -772,6 +775,19 @@ struct phy_tdr_config { #define PHY_PAIR_ALL -1 /** diff --git a/target/linux/generic/backport-6.1/801-v6.4-06-net-phy-phy_device-Call-into-the-PHY-driver-to-set-L.patch b/target/linux/generic/backport-6.1/801-v6.4-06-net-phy-phy_device-Call-into-the-PHY-driver-to-set-L.patch index 57db12ed4..4873c40a7 100644 --- a/target/linux/generic/backport-6.1/801-v6.4-06-net-phy-phy_device-Call-into-the-PHY-driver-to-set-L.patch +++ b/target/linux/generic/backport-6.1/801-v6.4-06-net-phy-phy_device-Call-into-the-PHY-driver-to-set-L.patch @@ -20,7 +20,7 @@ Signed-off-by: David S. Miller --- a/drivers/net/phy/phy_device.c +++ b/drivers/net/phy/phy_device.c -@@ -2934,11 +2934,18 @@ static bool phy_drv_supports_irq(struct +@@ -3038,11 +3038,18 @@ static bool phy_drv_supports_irq(struct return phydrv->config_intr && phydrv->handle_interrupt; } @@ -41,7 +41,7 @@ Signed-off-by: David S. Miller } static int of_phy_led(struct phy_device *phydev, -@@ -2955,12 +2962,14 @@ static int of_phy_led(struct phy_device +@@ -3059,12 +3066,14 @@ static int of_phy_led(struct phy_device return -ENOMEM; cdev = &phyled->led_cdev; @@ -59,7 +59,7 @@ Signed-off-by: David S. Miller init_data.fwnode = of_fwnode_handle(led); --- a/include/linux/phy.h +++ b/include/linux/phy.h -@@ -765,15 +765,19 @@ struct phy_tdr_config { +@@ -778,15 +778,19 @@ struct phy_tdr_config { * struct phy_led: An LED driven by the PHY * * @list: List of LEDs @@ -79,7 +79,7 @@ Signed-off-by: David S. Miller /** * struct phy_driver - Driver structure for a particular PHY type * -@@ -988,6 +992,15 @@ struct phy_driver { +@@ -1001,6 +1005,15 @@ struct phy_driver { int (*get_sqi)(struct phy_device *dev); /** @get_sqi_max: Get the maximum signal quality indication */ int (*get_sqi_max)(struct phy_device *dev); diff --git a/target/linux/generic/backport-6.1/801-v6.4-08-net-phy-phy_device-Call-into-the-PHY-driver-to-set-L.patch b/target/linux/generic/backport-6.1/801-v6.4-08-net-phy-phy_device-Call-into-the-PHY-driver-to-set-L.patch index e868722af..00bdcc546 100644 --- a/target/linux/generic/backport-6.1/801-v6.4-08-net-phy-phy_device-Call-into-the-PHY-driver-to-set-L.patch +++ b/target/linux/generic/backport-6.1/801-v6.4-08-net-phy-phy_device-Call-into-the-PHY-driver-to-set-L.patch @@ -18,7 +18,7 @@ Signed-off-by: David S. Miller --- a/drivers/net/phy/phy_device.c +++ b/drivers/net/phy/phy_device.c -@@ -2948,6 +2948,22 @@ static int phy_led_set_brightness(struct +@@ -3052,6 +3052,22 @@ static int phy_led_set_brightness(struct return err; } @@ -41,7 +41,7 @@ Signed-off-by: David S. Miller static int of_phy_led(struct phy_device *phydev, struct device_node *led) { -@@ -2970,6 +2986,8 @@ static int of_phy_led(struct phy_device +@@ -3074,6 +3090,8 @@ static int of_phy_led(struct phy_device if (phydev->drv->led_brightness_set) cdev->brightness_set_blocking = phy_led_set_brightness; @@ -52,7 +52,7 @@ Signed-off-by: David S. Miller init_data.fwnode = of_fwnode_handle(led); --- a/include/linux/phy.h +++ b/include/linux/phy.h -@@ -1001,6 +1001,18 @@ struct phy_driver { +@@ -1014,6 +1014,18 @@ struct phy_driver { */ int (*led_brightness_set)(struct phy_device *dev, u8 index, enum led_brightness value); diff --git a/target/linux/generic/backport-6.1/806-v6.5-net-dsa-qca8k-add-support-for-additional-modes-for-n.patch b/target/linux/generic/backport-6.1/806-v6.5-net-dsa-qca8k-add-support-for-additional-modes-for-n.patch new file mode 100644 index 000000000..ac322e193 --- /dev/null +++ b/target/linux/generic/backport-6.1/806-v6.5-net-dsa-qca8k-add-support-for-additional-modes-for-n.patch @@ -0,0 +1,64 @@ +From 2555f35a4f428a9bfdf09aa0459dbfdf59a24a9a Mon Sep 17 00:00:00 2001 +From: Christian Marangi +Date: Wed, 21 Jun 2023 11:54:09 +0200 +Subject: [PATCH] net: dsa: qca8k: add support for additional modes for netdev + trigger + +The QCA8K switch supports additional modes that can be handled in +hardware for the LED netdev trigger. + +Add these additional modes to further support the Switch LEDs and +offload more blink modes. + +Add additional modes: +- link_10 +- link_100 +- link_1000 +- half_duplex +- full_duplex + +Signed-off-by: Christian Marangi +Reviewed-by: Andrew Lunn +Reviewed-by: Florian Fainelli +Link: https://lore.kernel.org/r/20230621095409.25859-1-ansuelsmth@gmail.com +Signed-off-by: Jakub Kicinski +--- + drivers/net/dsa/qca/qca8k-leds.c | 20 ++++++++++++++++++++ + 1 file changed, 20 insertions(+) + +--- a/drivers/net/dsa/qca/qca8k-leds.c ++++ b/drivers/net/dsa/qca/qca8k-leds.c +@@ -68,6 +68,16 @@ qca8k_parse_netdev(unsigned long rules, + *offload_trigger |= QCA8K_LED_TX_BLINK_MASK; + if (test_bit(TRIGGER_NETDEV_RX, &rules)) + *offload_trigger |= QCA8K_LED_RX_BLINK_MASK; ++ if (test_bit(TRIGGER_NETDEV_LINK_10, &rules)) ++ *offload_trigger |= QCA8K_LED_LINK_10M_EN_MASK; ++ if (test_bit(TRIGGER_NETDEV_LINK_100, &rules)) ++ *offload_trigger |= QCA8K_LED_LINK_100M_EN_MASK; ++ if (test_bit(TRIGGER_NETDEV_LINK_1000, &rules)) ++ *offload_trigger |= QCA8K_LED_LINK_1000M_EN_MASK; ++ if (test_bit(TRIGGER_NETDEV_HALF_DUPLEX, &rules)) ++ *offload_trigger |= QCA8K_LED_HALF_DUPLEX_MASK; ++ if (test_bit(TRIGGER_NETDEV_FULL_DUPLEX, &rules)) ++ *offload_trigger |= QCA8K_LED_FULL_DUPLEX_MASK; + + if (rules && !*offload_trigger) + return -EOPNOTSUPP; +@@ -322,6 +332,16 @@ qca8k_cled_hw_control_get(struct led_cla + set_bit(TRIGGER_NETDEV_TX, rules); + if (val & QCA8K_LED_RX_BLINK_MASK) + set_bit(TRIGGER_NETDEV_RX, rules); ++ if (val & QCA8K_LED_LINK_10M_EN_MASK) ++ set_bit(TRIGGER_NETDEV_LINK_10, rules); ++ if (val & QCA8K_LED_LINK_100M_EN_MASK) ++ set_bit(TRIGGER_NETDEV_LINK_100, rules); ++ if (val & QCA8K_LED_LINK_1000M_EN_MASK) ++ set_bit(TRIGGER_NETDEV_LINK_1000, rules); ++ if (val & QCA8K_LED_HALF_DUPLEX_MASK) ++ set_bit(TRIGGER_NETDEV_HALF_DUPLEX, rules); ++ if (val & QCA8K_LED_FULL_DUPLEX_MASK) ++ set_bit(TRIGGER_NETDEV_FULL_DUPLEX, rules); + + return 0; + } diff --git a/target/linux/generic/backport-6.1/807-v6.5-01-net-dsa-mv88e6xxx-pass-directly-chip-structure-to-mv.patch b/target/linux/generic/backport-6.1/807-v6.5-01-net-dsa-mv88e6xxx-pass-directly-chip-structure-to-mv.patch new file mode 100644 index 000000000..8c062dc3b --- /dev/null +++ b/target/linux/generic/backport-6.1/807-v6.5-01-net-dsa-mv88e6xxx-pass-directly-chip-structure-to-mv.patch @@ -0,0 +1,64 @@ +From 4f86eb098e18fd0f032877dfa1a7e8c1503ca409 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Alexis=20Lothor=C3=A9?= +Date: Mon, 29 May 2023 10:02:41 +0200 +Subject: [PATCH 1/6] net: dsa: mv88e6xxx: pass directly chip structure to + mv88e6xxx_phy_is_internal +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Since this function is a simple helper, we do not need to pass a full +dsa_switch structure, we can directly pass the mv88e6xxx_chip structure. +Doing so will allow to share this function with any other function +not manipulating dsa_switch structure but needing info about number of +internal phys + +Signed-off-by: Alexis Lothoré +Reviewed-by: Russell King (Oracle) +Reviewed-by: Florian Fainelli +Signed-off-by: Jakub Kicinski +--- + drivers/net/dsa/mv88e6xxx/chip.c | 10 ++++------ + 1 file changed, 4 insertions(+), 6 deletions(-) + +--- a/drivers/net/dsa/mv88e6xxx/chip.c ++++ b/drivers/net/dsa/mv88e6xxx/chip.c +@@ -470,10 +470,8 @@ restore_link: + return err; + } + +-static int mv88e6xxx_phy_is_internal(struct dsa_switch *ds, int port) ++static int mv88e6xxx_phy_is_internal(struct mv88e6xxx_chip *chip, int port) + { +- struct mv88e6xxx_chip *chip = ds->priv; +- + return port < chip->info->num_internal_phys; + } + +@@ -591,7 +589,7 @@ static void mv88e6095_phylink_get_caps(s + + config->mac_capabilities = MAC_SYM_PAUSE | MAC_10 | MAC_100; + +- if (mv88e6xxx_phy_is_internal(chip->ds, port)) { ++ if (mv88e6xxx_phy_is_internal(chip, port)) { + __set_bit(PHY_INTERFACE_MODE_MII, config->supported_interfaces); + } else { + if (cmode < ARRAY_SIZE(mv88e6185_phy_interface_modes) && +@@ -851,7 +849,7 @@ static void mv88e6xxx_get_caps(struct ds + chip->info->ops->phylink_get_caps(chip, port, config); + mv88e6xxx_reg_unlock(chip); + +- if (mv88e6xxx_phy_is_internal(ds, port)) { ++ if (mv88e6xxx_phy_is_internal(chip, port)) { + __set_bit(PHY_INTERFACE_MODE_INTERNAL, + config->supported_interfaces); + /* Internal ports with no phy-mode need GMII for PHYLIB */ +@@ -872,7 +870,7 @@ static void mv88e6xxx_mac_config(struct + + mv88e6xxx_reg_lock(chip); + +- if (mode != MLO_AN_PHY || !mv88e6xxx_phy_is_internal(ds, port)) { ++ if (mode != MLO_AN_PHY || !mv88e6xxx_phy_is_internal(chip, port)) { + /* In inband mode, the link may come up at any time while the + * link is not forced down. Force the link down while we + * reconfigure the interface mode. diff --git a/target/linux/generic/backport-6.1/807-v6.5-02-net-dsa-mv88e6xxx-use-mv88e6xxx_phy_is_internal-in-m.patch b/target/linux/generic/backport-6.1/807-v6.5-02-net-dsa-mv88e6xxx-use-mv88e6xxx_phy_is_internal-in-m.patch new file mode 100644 index 000000000..75a21a931 --- /dev/null +++ b/target/linux/generic/backport-6.1/807-v6.5-02-net-dsa-mv88e6xxx-use-mv88e6xxx_phy_is_internal-in-m.patch @@ -0,0 +1,31 @@ +From 73cbfad9296eed004992806e056db5b48583ca41 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Alexis=20Lothor=C3=A9?= +Date: Mon, 29 May 2023 10:02:42 +0200 +Subject: [PATCH 2/6] net: dsa: mv88e6xxx: use mv88e6xxx_phy_is_internal in + mv88e6xxx_port_ppu_updates +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Make sure to use existing helper to get internal PHYs count instead of +redoing it manually + +Signed-off-by: Alexis Lothoré +Reviewed-by: Russell King (Oracle) +Reviewed-by: Florian Fainelli +Signed-off-by: Jakub Kicinski +--- + drivers/net/dsa/mv88e6xxx/chip.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/drivers/net/dsa/mv88e6xxx/chip.c ++++ b/drivers/net/dsa/mv88e6xxx/chip.c +@@ -484,7 +484,7 @@ static int mv88e6xxx_port_ppu_updates(st + * report whether the port is internal. + */ + if (chip->info->family == MV88E6XXX_FAMILY_6250) +- return port < chip->info->num_internal_phys; ++ return mv88e6xxx_phy_is_internal(chip, port); + + err = mv88e6xxx_port_read(chip, port, MV88E6XXX_PORT_STS, ®); + if (err) { diff --git a/target/linux/generic/backport-6.1/807-v6.5-03-net-dsa-mv88e6xxx-add-field-to-specify-internal-phys.patch b/target/linux/generic/backport-6.1/807-v6.5-03-net-dsa-mv88e6xxx-add-field-to-specify-internal-phys.patch new file mode 100644 index 000000000..e24dca819 --- /dev/null +++ b/target/linux/generic/backport-6.1/807-v6.5-03-net-dsa-mv88e6xxx-add-field-to-specify-internal-phys.patch @@ -0,0 +1,69 @@ +From 1414d30660d201f515a9d877571ceea9ca190b6a Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Alexis=20Lothor=C3=A9?= +Date: Mon, 29 May 2023 10:02:43 +0200 +Subject: [PATCH 3/6] net: dsa: mv88e6xxx: add field to specify internal phys + layout +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +mv88e6xxx currently assumes that switch equipped with internal phys have +those phys mapped contiguously starting from port 0 (see +mv88e6xxx_phy_is_internal). However, some switches have internal PHYs but +NOT starting from port 0. For example 88e6393X, 88E6193X and 88E6191X have +integrated PHYs available on ports 1 to 8 +To properly support this offset, add a new field to allow specifying an +internal PHYs layout. If field is not set, default layout is assumed (start +at port 0) + +Signed-off-by: Alexis Lothoré +Reviewed-by: Andrew Lunn +Signed-off-by: Jakub Kicinski +--- + drivers/net/dsa/mv88e6xxx/chip.c | 4 +++- + drivers/net/dsa/mv88e6xxx/chip.h | 5 +++++ + drivers/net/dsa/mv88e6xxx/global2.c | 5 ++++- + 3 files changed, 12 insertions(+), 2 deletions(-) + +--- a/drivers/net/dsa/mv88e6xxx/chip.c ++++ b/drivers/net/dsa/mv88e6xxx/chip.c +@@ -472,7 +472,9 @@ restore_link: + + static int mv88e6xxx_phy_is_internal(struct mv88e6xxx_chip *chip, int port) + { +- return port < chip->info->num_internal_phys; ++ return port >= chip->info->internal_phys_offset && ++ port < chip->info->num_internal_phys + ++ chip->info->internal_phys_offset; + } + + static int mv88e6xxx_port_ppu_updates(struct mv88e6xxx_chip *chip, int port) +--- a/drivers/net/dsa/mv88e6xxx/chip.h ++++ b/drivers/net/dsa/mv88e6xxx/chip.h +@@ -167,6 +167,11 @@ struct mv88e6xxx_info { + + /* Supports PTP */ + bool ptp_support; ++ ++ /* Internal PHY start index. 0 means that internal PHYs range starts at ++ * port 0, 1 means internal PHYs range starts at port 1, etc ++ */ ++ unsigned int internal_phys_offset; + }; + + struct mv88e6xxx_atu_entry { +--- a/drivers/net/dsa/mv88e6xxx/global2.c ++++ b/drivers/net/dsa/mv88e6xxx/global2.c +@@ -1185,8 +1185,11 @@ int mv88e6xxx_g2_irq_mdio_setup(struct m + struct mii_bus *bus) + { + int phy, irq, err, err_phy; ++ int phy_start = chip->info->internal_phys_offset; ++ int phy_end = chip->info->internal_phys_offset + ++ chip->info->num_internal_phys; + +- for (phy = 0; phy < chip->info->num_internal_phys; phy++) { ++ for (phy = phy_start; phy < phy_end; phy++) { + irq = irq_find_mapping(chip->g2_irq.domain, phy); + if (irq < 0) { + err = irq; diff --git a/target/linux/generic/backport-6.1/807-v6.5-04-net-dsa-mv88e6xxx-fix-88E6393X-family-internal-phys-.patch b/target/linux/generic/backport-6.1/807-v6.5-04-net-dsa-mv88e6xxx-fix-88E6393X-family-internal-phys-.patch new file mode 100644 index 000000000..cadc70fd7 --- /dev/null +++ b/target/linux/generic/backport-6.1/807-v6.5-04-net-dsa-mv88e6xxx-fix-88E6393X-family-internal-phys-.patch @@ -0,0 +1,52 @@ +From eb8c75f82a6711387f3b9e03e28923f3e75a761b Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Alexis=20Lothor=C3=A9?= +Date: Mon, 29 May 2023 10:02:44 +0200 +Subject: [PATCH 4/6] net: dsa: mv88e6xxx: fix 88E6393X family internal phys + layout +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +88E6393X/88E6193X/88E6191X switches have in fact 8 internal PHYs, but those +are not present starting at port 0: supported ports go from 1 to 8 + +Signed-off-by: Alexis Lothoré +Reviewed-by: Andrew Lunn +Reviewed-by: Florian Fainelli +Signed-off-by: Jakub Kicinski +--- + drivers/net/dsa/mv88e6xxx/chip.c | 9 ++++++--- + 1 file changed, 6 insertions(+), 3 deletions(-) + +--- a/drivers/net/dsa/mv88e6xxx/chip.c ++++ b/drivers/net/dsa/mv88e6xxx/chip.c +@@ -5998,7 +5998,8 @@ static const struct mv88e6xxx_info mv88e + .name = "Marvell 88E6191X", + .num_databases = 4096, + .num_ports = 11, /* 10 + Z80 */ +- .num_internal_phys = 9, ++ .num_internal_phys = 8, ++ .internal_phys_offset = 1, + .max_vid = 8191, + .max_sid = 63, + .port_base_addr = 0x0, +@@ -6021,7 +6022,8 @@ static const struct mv88e6xxx_info mv88e + .name = "Marvell 88E6193X", + .num_databases = 4096, + .num_ports = 11, /* 10 + Z80 */ +- .num_internal_phys = 9, ++ .num_internal_phys = 8, ++ .internal_phys_offset = 1, + .max_vid = 8191, + .max_sid = 63, + .port_base_addr = 0x0, +@@ -6340,7 +6342,8 @@ static const struct mv88e6xxx_info mv88e + .name = "Marvell 88E6393X", + .num_databases = 4096, + .num_ports = 11, /* 10 + Z80 */ +- .num_internal_phys = 9, ++ .num_internal_phys = 8, ++ .internal_phys_offset = 1, + .max_vid = 8191, + .max_sid = 63, + .port_base_addr = 0x0, diff --git a/target/linux/generic/backport-6.1/807-v6.5-05-net-dsa-mv88e6xxx-pass-mv88e6xxx_chip-structure-to-p.patch b/target/linux/generic/backport-6.1/807-v6.5-05-net-dsa-mv88e6xxx-pass-mv88e6xxx_chip-structure-to-p.patch new file mode 100644 index 000000000..e2d3b57cc --- /dev/null +++ b/target/linux/generic/backport-6.1/807-v6.5-05-net-dsa-mv88e6xxx-pass-mv88e6xxx_chip-structure-to-p.patch @@ -0,0 +1,110 @@ +From cef945452c8468efce75ba0dc8420510a5b84af9 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Alexis=20Lothor=C3=A9?= +Date: Mon, 29 May 2023 10:02:45 +0200 +Subject: [PATCH 5/6] net: dsa: mv88e6xxx: pass mv88e6xxx_chip structure to + port_max_speed_mode +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Some switches families have minor differences on supported link speed for +ports. Instead of redefining a new port_max_speed_mode for each different +configuration, allow to pass mv88e6xxx_chip structure to allow +differentiating those chips by known chip id + +Signed-off-by: Alexis Lothoré +Reviewed-by: Florian Fainelli +Signed-off-by: Jakub Kicinski +--- + drivers/net/dsa/mv88e6xxx/chip.c | 2 +- + drivers/net/dsa/mv88e6xxx/chip.h | 3 ++- + drivers/net/dsa/mv88e6xxx/port.c | 12 ++++++++---- + drivers/net/dsa/mv88e6xxx/port.h | 12 ++++++++---- + 4 files changed, 19 insertions(+), 10 deletions(-) + +--- a/drivers/net/dsa/mv88e6xxx/chip.c ++++ b/drivers/net/dsa/mv88e6xxx/chip.c +@@ -3350,7 +3350,7 @@ static int mv88e6xxx_setup_port(struct m + caps = pl_config.mac_capabilities; + + if (chip->info->ops->port_max_speed_mode) +- mode = chip->info->ops->port_max_speed_mode(port); ++ mode = chip->info->ops->port_max_speed_mode(chip, port); + else + mode = PHY_INTERFACE_MODE_NA; + +--- a/drivers/net/dsa/mv88e6xxx/chip.h ++++ b/drivers/net/dsa/mv88e6xxx/chip.h +@@ -514,7 +514,8 @@ struct mv88e6xxx_ops { + int speed, int duplex); + + /* What interface mode should be used for maximum speed? */ +- phy_interface_t (*port_max_speed_mode)(int port); ++ phy_interface_t (*port_max_speed_mode)(struct mv88e6xxx_chip *chip, ++ int port); + + int (*port_tag_remap)(struct mv88e6xxx_chip *chip, int port); + +--- a/drivers/net/dsa/mv88e6xxx/port.c ++++ b/drivers/net/dsa/mv88e6xxx/port.c +@@ -342,7 +342,8 @@ int mv88e6341_port_set_speed_duplex(stru + duplex); + } + +-phy_interface_t mv88e6341_port_max_speed_mode(int port) ++phy_interface_t mv88e6341_port_max_speed_mode(struct mv88e6xxx_chip *chip, ++ int port) + { + if (port == 5) + return PHY_INTERFACE_MODE_2500BASEX; +@@ -381,7 +382,8 @@ int mv88e6390_port_set_speed_duplex(stru + duplex); + } + +-phy_interface_t mv88e6390_port_max_speed_mode(int port) ++phy_interface_t mv88e6390_port_max_speed_mode(struct mv88e6xxx_chip *chip, ++ int port) + { + if (port == 9 || port == 10) + return PHY_INTERFACE_MODE_2500BASEX; +@@ -403,7 +405,8 @@ int mv88e6390x_port_set_speed_duplex(str + duplex); + } + +-phy_interface_t mv88e6390x_port_max_speed_mode(int port) ++phy_interface_t mv88e6390x_port_max_speed_mode(struct mv88e6xxx_chip *chip, ++ int port) + { + if (port == 9 || port == 10) + return PHY_INTERFACE_MODE_XAUI; +@@ -500,7 +503,8 @@ int mv88e6393x_port_set_speed_duplex(str + return 0; + } + +-phy_interface_t mv88e6393x_port_max_speed_mode(int port) ++phy_interface_t mv88e6393x_port_max_speed_mode(struct mv88e6xxx_chip *chip, ++ int port) + { + if (port == 0 || port == 9 || port == 10) + return PHY_INTERFACE_MODE_10GBASER; +--- a/drivers/net/dsa/mv88e6xxx/port.h ++++ b/drivers/net/dsa/mv88e6xxx/port.h +@@ -359,10 +359,14 @@ int mv88e6390x_port_set_speed_duplex(str + int mv88e6393x_port_set_speed_duplex(struct mv88e6xxx_chip *chip, int port, + int speed, int duplex); + +-phy_interface_t mv88e6341_port_max_speed_mode(int port); +-phy_interface_t mv88e6390_port_max_speed_mode(int port); +-phy_interface_t mv88e6390x_port_max_speed_mode(int port); +-phy_interface_t mv88e6393x_port_max_speed_mode(int port); ++phy_interface_t mv88e6341_port_max_speed_mode(struct mv88e6xxx_chip *chip, ++ int port); ++phy_interface_t mv88e6390_port_max_speed_mode(struct mv88e6xxx_chip *chip, ++ int port); ++phy_interface_t mv88e6390x_port_max_speed_mode(struct mv88e6xxx_chip *chip, ++ int port); ++phy_interface_t mv88e6393x_port_max_speed_mode(struct mv88e6xxx_chip *chip, ++ int port); + + int mv88e6xxx_port_set_state(struct mv88e6xxx_chip *chip, int port, u8 state); + diff --git a/target/linux/generic/backport-6.1/807-v6.5-06-net-dsa-mv88e6xxx-enable-support-for-88E6361-switch.patch b/target/linux/generic/backport-6.1/807-v6.5-06-net-dsa-mv88e6xxx-enable-support-for-88E6361-switch.patch new file mode 100644 index 000000000..471e6a390 --- /dev/null +++ b/target/linux/generic/backport-6.1/807-v6.5-06-net-dsa-mv88e6xxx-enable-support-for-88E6361-switch.patch @@ -0,0 +1,153 @@ +From 23680321789863bab2d60af507858ce50ff9f56a Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Alexis=20Lothor=C3=A9?= +Date: Mon, 29 May 2023 10:02:46 +0200 +Subject: [PATCH 6/6] net: dsa: mv88e6xxx: enable support for 88E6361 switch +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Marvell 88E6361 is an 8-port switch derived from the +88E6393X/88E9193X/88E6191X switches family. It can benefit from the +existing mv88e6xxx driver by simply adding the proper switch description in +the driver. Main differences with other switches from this +family are: +- 8 ports exposed (instead of 11): ports 1, 2 and 8 not available +- No 5GBase-x nor SFI/USXGMII support + +Signed-off-by: Alexis Lothoré +Reviewed-by: Andrew Lunn +Signed-off-by: Jakub Kicinski +--- + drivers/net/dsa/mv88e6xxx/chip.c | 42 ++++++++++++++++++++++++++++---- + drivers/net/dsa/mv88e6xxx/chip.h | 3 ++- + drivers/net/dsa/mv88e6xxx/port.c | 14 ++++++++--- + drivers/net/dsa/mv88e6xxx/port.h | 1 + + 4 files changed, 51 insertions(+), 9 deletions(-) + +--- a/drivers/net/dsa/mv88e6xxx/chip.c ++++ b/drivers/net/dsa/mv88e6xxx/chip.c +@@ -809,6 +809,8 @@ static void mv88e6393x_phylink_get_caps( + unsigned long *supported = config->supported_interfaces; + bool is_6191x = + chip->info->prod_num == MV88E6XXX_PORT_SWITCH_ID_PROD_6191X; ++ bool is_6361 = ++ chip->info->prod_num == MV88E6XXX_PORT_SWITCH_ID_PROD_6361; + + mv88e6xxx_translate_cmode(chip->ports[port].cmode, supported); + +@@ -823,13 +825,17 @@ static void mv88e6393x_phylink_get_caps( + /* 6191X supports >1G modes only on port 10 */ + if (!is_6191x || port == 10) { + __set_bit(PHY_INTERFACE_MODE_2500BASEX, supported); +- __set_bit(PHY_INTERFACE_MODE_5GBASER, supported); +- __set_bit(PHY_INTERFACE_MODE_10GBASER, supported); ++ config->mac_capabilities |= MAC_2500FD; ++ ++ /* 6361 only supports up to 2500BaseX */ ++ if (!is_6361) { ++ __set_bit(PHY_INTERFACE_MODE_5GBASER, supported); ++ __set_bit(PHY_INTERFACE_MODE_10GBASER, supported); ++ config->mac_capabilities |= MAC_5000FD | ++ MAC_10000FD; ++ } + /* FIXME: USXGMII is not supported yet */ + /* __set_bit(PHY_INTERFACE_MODE_USXGMII, supported); */ +- +- config->mac_capabilities |= MAC_2500FD | MAC_5000FD | +- MAC_10000FD; + } + } + +@@ -6285,6 +6291,32 @@ static const struct mv88e6xxx_info mv88e + .ptp_support = true, + .ops = &mv88e6352_ops, + }, ++ [MV88E6361] = { ++ .prod_num = MV88E6XXX_PORT_SWITCH_ID_PROD_6361, ++ .family = MV88E6XXX_FAMILY_6393, ++ .name = "Marvell 88E6361", ++ .num_databases = 4096, ++ .num_macs = 16384, ++ .num_ports = 11, ++ /* Ports 1, 2 and 8 are not routed */ ++ .invalid_port_mask = BIT(1) | BIT(2) | BIT(8), ++ .num_internal_phys = 5, ++ .internal_phys_offset = 3, ++ .max_vid = 4095, ++ .max_sid = 63, ++ .port_base_addr = 0x0, ++ .phy_base_addr = 0x0, ++ .global1_addr = 0x1b, ++ .global2_addr = 0x1c, ++ .age_time_coeff = 3750, ++ .g1_irqs = 10, ++ .g2_irqs = 14, ++ .atu_move_port_mask = 0x1f, ++ .pvt = true, ++ .multi_chip = true, ++ .ptp_support = true, ++ .ops = &mv88e6393x_ops, ++ }, + [MV88E6390] = { + .prod_num = MV88E6XXX_PORT_SWITCH_ID_PROD_6390, + .family = MV88E6XXX_FAMILY_6390, +--- a/drivers/net/dsa/mv88e6xxx/chip.h ++++ b/drivers/net/dsa/mv88e6xxx/chip.h +@@ -82,6 +82,7 @@ enum mv88e6xxx_model { + MV88E6350, + MV88E6351, + MV88E6352, ++ MV88E6361, + MV88E6390, + MV88E6390X, + MV88E6393X, +@@ -100,7 +101,7 @@ enum mv88e6xxx_family { + MV88E6XXX_FAMILY_6351, /* 6171 6175 6350 6351 */ + MV88E6XXX_FAMILY_6352, /* 6172 6176 6240 6352 */ + MV88E6XXX_FAMILY_6390, /* 6190 6190X 6191 6290 6390 6390X */ +- MV88E6XXX_FAMILY_6393, /* 6191X 6193X 6393X */ ++ MV88E6XXX_FAMILY_6393, /* 6191X 6193X 6361 6393X */ + }; + + /** +--- a/drivers/net/dsa/mv88e6xxx/port.c ++++ b/drivers/net/dsa/mv88e6xxx/port.c +@@ -424,6 +424,10 @@ int mv88e6393x_port_set_speed_duplex(str + u16 reg, ctrl; + int err; + ++ if (chip->info->prod_num == MV88E6XXX_PORT_SWITCH_ID_PROD_6361 && ++ speed > 2500) ++ return -EOPNOTSUPP; ++ + if (speed == 200 && port != 0) + return -EOPNOTSUPP; + +@@ -506,10 +510,14 @@ int mv88e6393x_port_set_speed_duplex(str + phy_interface_t mv88e6393x_port_max_speed_mode(struct mv88e6xxx_chip *chip, + int port) + { +- if (port == 0 || port == 9 || port == 10) +- return PHY_INTERFACE_MODE_10GBASER; + +- return PHY_INTERFACE_MODE_NA; ++ if (port != 0 && port != 9 && port != 10) ++ return PHY_INTERFACE_MODE_NA; ++ ++ if (chip->info->prod_num == MV88E6XXX_PORT_SWITCH_ID_PROD_6361) ++ return PHY_INTERFACE_MODE_2500BASEX; ++ ++ return PHY_INTERFACE_MODE_10GBASER; + } + + static int mv88e6xxx_port_set_cmode(struct mv88e6xxx_chip *chip, int port, +--- a/drivers/net/dsa/mv88e6xxx/port.h ++++ b/drivers/net/dsa/mv88e6xxx/port.h +@@ -133,6 +133,7 @@ + #define MV88E6XXX_PORT_SWITCH_ID_PROD_6220 0x2200 + #define MV88E6XXX_PORT_SWITCH_ID_PROD_6240 0x2400 + #define MV88E6XXX_PORT_SWITCH_ID_PROD_6250 0x2500 ++#define MV88E6XXX_PORT_SWITCH_ID_PROD_6361 0x2610 + #define MV88E6XXX_PORT_SWITCH_ID_PROD_6290 0x2900 + #define MV88E6XXX_PORT_SWITCH_ID_PROD_6321 0x3100 + #define MV88E6XXX_PORT_SWITCH_ID_PROD_6141 0x3400 diff --git a/target/linux/generic/backport-6.1/810-v6.3-i915-Move-list_count-to-list.h-as-list_count_nodes-f.patch b/target/linux/generic/backport-6.1/810-v6.3-i915-Move-list_count-to-list.h-as-list_count_nodes-f.patch new file mode 100644 index 000000000..5c4206da1 --- /dev/null +++ b/target/linux/generic/backport-6.1/810-v6.3-i915-Move-list_count-to-list.h-as-list_count_nodes-f.patch @@ -0,0 +1,75 @@ +From 4d70c74659d9746502b23d055dba03d1d28ec388 Mon Sep 17 00:00:00 2001 +From: Andy Shevchenko +Date: Wed, 30 Nov 2022 15:48:35 +0200 +Subject: [PATCH] i915: Move list_count() to list.h as list_count_nodes() for + broader use + +Some of the existing users, and definitely will be new ones, want to +count existing nodes in the list. Provide a generic API for that by +moving code from i915 to list.h. + +Reviewed-by: Lucas De Marchi +Acked-by: Jani Nikula +Signed-off-by: Andy Shevchenko +Link: https://lore.kernel.org/r/20221130134838.23805-1-andriy.shevchenko@linux.intel.com +Signed-off-by: Greg Kroah-Hartman +--- + drivers/gpu/drm/i915/gt/intel_engine_cs.c | 15 ++------------- + include/linux/list.h | 15 +++++++++++++++ + 2 files changed, 17 insertions(+), 13 deletions(-) + +--- a/drivers/gpu/drm/i915/gt/intel_execlists_submission.c ++++ b/drivers/gpu/drm/i915/gt/intel_execlists_submission.c +@@ -4157,17 +4157,6 @@ void intel_execlists_show_requests(struc + spin_unlock_irqrestore(&sched_engine->lock, flags); + } + +-static unsigned long list_count(struct list_head *list) +-{ +- struct list_head *pos; +- unsigned long count = 0; +- +- list_for_each(pos, list) +- count++; +- +- return count; +-} +- + void intel_execlists_dump_active_requests(struct intel_engine_cs *engine, + struct i915_request *hung_rq, + struct drm_printer *m) +@@ -4178,8 +4167,8 @@ void intel_execlists_dump_active_request + + intel_engine_dump_active_requests(&engine->sched_engine->requests, hung_rq, m); + +- drm_printf(m, "\tOn hold?: %lu\n", +- list_count(&engine->sched_engine->hold)); ++ drm_printf(m, "\tOn hold?: %zu\n", ++ list_count_nodes(&engine->sched_engine->hold)); + + spin_unlock_irqrestore(&engine->sched_engine->lock, flags); + } +--- a/include/linux/list.h ++++ b/include/linux/list.h +@@ -656,6 +656,21 @@ static inline void list_splice_tail_init + pos = n, n = pos->prev) + + /** ++ * list_count_nodes - count nodes in the list ++ * @head: the head for your list. ++ */ ++static inline size_t list_count_nodes(struct list_head *head) ++{ ++ struct list_head *pos; ++ size_t count = 0; ++ ++ list_for_each(pos, head) ++ count++; ++ ++ return count; ++} ++ ++/** + * list_entry_is_head - test if the entry points to the head of the list + * @pos: the type * to cursor + * @head: the head for your list. diff --git a/target/linux/generic/backport-6.1/814-v6.6-0001-nvmem-sunxi_sid-Convert-to-devm_platform_ioremap_res.patch b/target/linux/generic/backport-6.1/814-v6.6-0001-nvmem-sunxi_sid-Convert-to-devm_platform_ioremap_res.patch new file mode 100644 index 000000000..bf7a816bb --- /dev/null +++ b/target/linux/generic/backport-6.1/814-v6.6-0001-nvmem-sunxi_sid-Convert-to-devm_platform_ioremap_res.patch @@ -0,0 +1,36 @@ +From 9ccfcbeb8f32ff89e99b36cb9cdebaa0d1b44ed1 Mon Sep 17 00:00:00 2001 +From: Yangtao Li +Date: Wed, 23 Aug 2023 14:27:24 +0100 +Subject: [PATCH] nvmem: sunxi_sid: Convert to devm_platform_ioremap_resource() + +Use devm_platform_ioremap_resource() to simplify code. + +Signed-off-by: Yangtao Li +Acked-by: Jernej Skrabec +Signed-off-by: Srinivas Kandagatla +Link: https://lore.kernel.org/r/20230823132744.350618-3-srinivas.kandagatla@linaro.org +Signed-off-by: Greg Kroah-Hartman +--- + drivers/nvmem/sunxi_sid.c | 4 +--- + 1 file changed, 1 insertion(+), 3 deletions(-) + +--- a/drivers/nvmem/sunxi_sid.c ++++ b/drivers/nvmem/sunxi_sid.c +@@ -125,7 +125,6 @@ static int sun8i_sid_read_by_reg(void *c + static int sunxi_sid_probe(struct platform_device *pdev) + { + struct device *dev = &pdev->dev; +- struct resource *res; + struct nvmem_config *nvmem_cfg; + struct nvmem_device *nvmem; + struct sunxi_sid *sid; +@@ -142,8 +141,7 @@ static int sunxi_sid_probe(struct platfo + return -EINVAL; + sid->value_offset = cfg->value_offset; + +- res = platform_get_resource(pdev, IORESOURCE_MEM, 0); +- sid->base = devm_ioremap_resource(dev, res); ++ sid->base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(sid->base)) + return PTR_ERR(sid->base); + diff --git a/target/linux/generic/backport-6.1/814-v6.6-0002-nvmem-brcm_nvram-Use-devm_platform_get_and_ioremap_r.patch b/target/linux/generic/backport-6.1/814-v6.6-0002-nvmem-brcm_nvram-Use-devm_platform_get_and_ioremap_r.patch new file mode 100644 index 000000000..f142d735d --- /dev/null +++ b/target/linux/generic/backport-6.1/814-v6.6-0002-nvmem-brcm_nvram-Use-devm_platform_get_and_ioremap_r.patch @@ -0,0 +1,30 @@ +From cfadd0e7d9225566f320bc4dc716682be910be6c Mon Sep 17 00:00:00 2001 +From: Yangtao Li +Date: Wed, 23 Aug 2023 14:27:25 +0100 +Subject: [PATCH] nvmem: brcm_nvram: Use + devm_platform_get_and_ioremap_resource() + +Convert platform_get_resource(), devm_ioremap_resource() to a single +call to devm_platform_get_and_ioremap_resource(), as this is exactly +what this function does. + +Signed-off-by: Yangtao Li +Signed-off-by: Srinivas Kandagatla +Link: https://lore.kernel.org/r/20230823132744.350618-4-srinivas.kandagatla@linaro.org +Signed-off-by: Greg Kroah-Hartman +--- + drivers/nvmem/brcm_nvram.c | 3 +-- + 1 file changed, 1 insertion(+), 2 deletions(-) + +--- a/drivers/nvmem/brcm_nvram.c ++++ b/drivers/nvmem/brcm_nvram.c +@@ -159,8 +159,7 @@ static int brcm_nvram_probe(struct platf + return -ENOMEM; + priv->dev = dev; + +- res = platform_get_resource(pdev, IORESOURCE_MEM, 0); +- priv->base = devm_ioremap_resource(dev, res); ++ priv->base = devm_platform_get_and_ioremap_resource(pdev, 0, &res); + if (IS_ERR(priv->base)) + return PTR_ERR(priv->base); + diff --git a/target/linux/generic/backport-6.1/814-v6.6-0003-nvmem-lpc18xx_otp-Convert-to-devm_platform_ioremap_r.patch b/target/linux/generic/backport-6.1/814-v6.6-0003-nvmem-lpc18xx_otp-Convert-to-devm_platform_ioremap_r.patch new file mode 100644 index 000000000..0395bbf8b --- /dev/null +++ b/target/linux/generic/backport-6.1/814-v6.6-0003-nvmem-lpc18xx_otp-Convert-to-devm_platform_ioremap_r.patch @@ -0,0 +1,34 @@ +From 0b49178e2b6b4aac3c7fa3ce8d8c02208a13b988 Mon Sep 17 00:00:00 2001 +From: Yangtao Li +Date: Wed, 23 Aug 2023 14:27:26 +0100 +Subject: [PATCH] nvmem: lpc18xx_otp: Convert to + devm_platform_ioremap_resource() + +Use devm_platform_ioremap_resource() to simplify code. + +Signed-off-by: Yangtao Li +Signed-off-by: Srinivas Kandagatla +Link: https://lore.kernel.org/r/20230823132744.350618-5-srinivas.kandagatla@linaro.org +Signed-off-by: Greg Kroah-Hartman +--- + drivers/nvmem/lpc18xx_otp.c | 4 +--- + 1 file changed, 1 insertion(+), 3 deletions(-) + +--- a/drivers/nvmem/lpc18xx_otp.c ++++ b/drivers/nvmem/lpc18xx_otp.c +@@ -68,14 +68,12 @@ static int lpc18xx_otp_probe(struct plat + { + struct nvmem_device *nvmem; + struct lpc18xx_otp *otp; +- struct resource *res; + + otp = devm_kzalloc(&pdev->dev, sizeof(*otp), GFP_KERNEL); + if (!otp) + return -ENOMEM; + +- res = platform_get_resource(pdev, IORESOURCE_MEM, 0); +- otp->base = devm_ioremap_resource(&pdev->dev, res); ++ otp->base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(otp->base)) + return PTR_ERR(otp->base); + diff --git a/target/linux/generic/backport-6.1/814-v6.6-0004-nvmem-meson-mx-efuse-Convert-to-devm_platform_iorema.patch b/target/linux/generic/backport-6.1/814-v6.6-0004-nvmem-meson-mx-efuse-Convert-to-devm_platform_iorema.patch new file mode 100644 index 000000000..da077eb4b --- /dev/null +++ b/target/linux/generic/backport-6.1/814-v6.6-0004-nvmem-meson-mx-efuse-Convert-to-devm_platform_iorema.patch @@ -0,0 +1,36 @@ +From 0a223a097709b99a0ba738d6be5b4f52c04ffb64 Mon Sep 17 00:00:00 2001 +From: Yangtao Li +Date: Wed, 23 Aug 2023 14:27:27 +0100 +Subject: [PATCH] nvmem: meson-mx-efuse: Convert to + devm_platform_ioremap_resource() + +Use devm_platform_ioremap_resource() to simplify code. + +Signed-off-by: Yangtao Li +Signed-off-by: Srinivas Kandagatla +Link: https://lore.kernel.org/r/20230823132744.350618-6-srinivas.kandagatla@linaro.org +Signed-off-by: Greg Kroah-Hartman +--- + drivers/nvmem/meson-mx-efuse.c | 4 +--- + 1 file changed, 1 insertion(+), 3 deletions(-) + +--- a/drivers/nvmem/meson-mx-efuse.c ++++ b/drivers/nvmem/meson-mx-efuse.c +@@ -194,7 +194,6 @@ static int meson_mx_efuse_probe(struct p + { + const struct meson_mx_efuse_platform_data *drvdata; + struct meson_mx_efuse *efuse; +- struct resource *res; + + drvdata = of_device_get_match_data(&pdev->dev); + if (!drvdata) +@@ -204,8 +203,7 @@ static int meson_mx_efuse_probe(struct p + if (!efuse) + return -ENOMEM; + +- res = platform_get_resource(pdev, IORESOURCE_MEM, 0); +- efuse->base = devm_ioremap_resource(&pdev->dev, res); ++ efuse->base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(efuse->base)) + return PTR_ERR(efuse->base); + diff --git a/target/linux/generic/backport-6.1/814-v6.6-0005-nvmem-rockchip-efuse-Use-devm_platform_get_and_iorem.patch b/target/linux/generic/backport-6.1/814-v6.6-0005-nvmem-rockchip-efuse-Use-devm_platform_get_and_iorem.patch new file mode 100644 index 000000000..dc144ddfb --- /dev/null +++ b/target/linux/generic/backport-6.1/814-v6.6-0005-nvmem-rockchip-efuse-Use-devm_platform_get_and_iorem.patch @@ -0,0 +1,31 @@ +From 94904db28db49ac8fbb2a273d25156db26a3a985 Mon Sep 17 00:00:00 2001 +From: Yangtao Li +Date: Wed, 23 Aug 2023 14:27:28 +0100 +Subject: [PATCH] nvmem: rockchip-efuse: Use + devm_platform_get_and_ioremap_resource() + +Convert platform_get_resource(), devm_ioremap_resource() to a single +call to devm_platform_get_and_ioremap_resource(), as this is exactly +what this function does. + +Signed-off-by: Yangtao Li +Reviewed-by: Heiko Stuebner +Signed-off-by: Srinivas Kandagatla +Link: https://lore.kernel.org/r/20230823132744.350618-7-srinivas.kandagatla@linaro.org +Signed-off-by: Greg Kroah-Hartman +--- + drivers/nvmem/rockchip-efuse.c | 3 +-- + 1 file changed, 1 insertion(+), 2 deletions(-) + +--- a/drivers/nvmem/rockchip-efuse.c ++++ b/drivers/nvmem/rockchip-efuse.c +@@ -267,8 +267,7 @@ static int rockchip_efuse_probe(struct p + if (!efuse) + return -ENOMEM; + +- res = platform_get_resource(pdev, IORESOURCE_MEM, 0); +- efuse->base = devm_ioremap_resource(dev, res); ++ efuse->base = devm_platform_get_and_ioremap_resource(pdev, 0, &res); + if (IS_ERR(efuse->base)) + return PTR_ERR(efuse->base); + diff --git a/target/linux/generic/backport-6.1/814-v6.6-0006-nvmem-stm32-romem-Use-devm_platform_get_and_ioremap_.patch b/target/linux/generic/backport-6.1/814-v6.6-0006-nvmem-stm32-romem-Use-devm_platform_get_and_ioremap_.patch new file mode 100644 index 000000000..99e20939c --- /dev/null +++ b/target/linux/generic/backport-6.1/814-v6.6-0006-nvmem-stm32-romem-Use-devm_platform_get_and_ioremap_.patch @@ -0,0 +1,30 @@ +From 0a4a8c0d238fec1fa4b85591524ef42ad261cb97 Mon Sep 17 00:00:00 2001 +From: Yangtao Li +Date: Wed, 23 Aug 2023 14:27:29 +0100 +Subject: [PATCH] nvmem: stm32-romem: Use + devm_platform_get_and_ioremap_resource() + +Convert platform_get_resource(), devm_ioremap_resource() to a single +call to devm_platform_get_and_ioremap_resource(), as this is exactly +what this function does. + +Signed-off-by: Yangtao Li +Signed-off-by: Srinivas Kandagatla +Link: https://lore.kernel.org/r/20230823132744.350618-8-srinivas.kandagatla@linaro.org +Signed-off-by: Greg Kroah-Hartman +--- + drivers/nvmem/stm32-romem.c | 3 +-- + 1 file changed, 1 insertion(+), 2 deletions(-) + +--- a/drivers/nvmem/stm32-romem.c ++++ b/drivers/nvmem/stm32-romem.c +@@ -196,8 +196,7 @@ static int stm32_romem_probe(struct plat + if (!priv) + return -ENOMEM; + +- res = platform_get_resource(pdev, IORESOURCE_MEM, 0); +- priv->base = devm_ioremap_resource(dev, res); ++ priv->base = devm_platform_get_and_ioremap_resource(pdev, 0, &res); + if (IS_ERR(priv->base)) + return PTR_ERR(priv->base); + diff --git a/target/linux/generic/backport-6.1/814-v6.6-0007-nvmem-qfprom-do-some-cleanup.patch b/target/linux/generic/backport-6.1/814-v6.6-0007-nvmem-qfprom-do-some-cleanup.patch new file mode 100644 index 000000000..6d93752e2 --- /dev/null +++ b/target/linux/generic/backport-6.1/814-v6.6-0007-nvmem-qfprom-do-some-cleanup.patch @@ -0,0 +1,59 @@ +From 0bc0d6dc2a9a05ae6729b4622f09782d9f230815 Mon Sep 17 00:00:00 2001 +From: Yangtao Li +Date: Wed, 23 Aug 2023 14:27:30 +0100 +Subject: [PATCH] nvmem: qfprom: do some cleanup + +Use devm_platform_ioremap_resource() and +devm_platform_get_and_ioremap_resource() to simplify code. +BTW convert to use dev_err_probe() instead of open it. + +Signed-off-by: Yangtao Li +Signed-off-by: Srinivas Kandagatla +Link: https://lore.kernel.org/r/20230823132744.350618-9-srinivas.kandagatla@linaro.org +Signed-off-by: Greg Kroah-Hartman +--- + drivers/nvmem/qfprom.c | 17 +++++------------ + 1 file changed, 5 insertions(+), 12 deletions(-) + +--- a/drivers/nvmem/qfprom.c ++++ b/drivers/nvmem/qfprom.c +@@ -374,8 +374,7 @@ static int qfprom_probe(struct platform_ + return -ENOMEM; + + /* The corrected section is always provided */ +- res = platform_get_resource(pdev, IORESOURCE_MEM, 0); +- priv->qfpcorrected = devm_ioremap_resource(dev, res); ++ priv->qfpcorrected = devm_platform_get_and_ioremap_resource(pdev, 0, &res); + if (IS_ERR(priv->qfpcorrected)) + return PTR_ERR(priv->qfpcorrected); + +@@ -402,12 +401,10 @@ static int qfprom_probe(struct platform_ + priv->qfpraw = devm_ioremap_resource(dev, res); + if (IS_ERR(priv->qfpraw)) + return PTR_ERR(priv->qfpraw); +- res = platform_get_resource(pdev, IORESOURCE_MEM, 2); +- priv->qfpconf = devm_ioremap_resource(dev, res); ++ priv->qfpconf = devm_platform_ioremap_resource(pdev, 2); + if (IS_ERR(priv->qfpconf)) + return PTR_ERR(priv->qfpconf); +- res = platform_get_resource(pdev, IORESOURCE_MEM, 3); +- priv->qfpsecurity = devm_ioremap_resource(dev, res); ++ priv->qfpsecurity = devm_platform_ioremap_resource(pdev, 3); + if (IS_ERR(priv->qfpsecurity)) + return PTR_ERR(priv->qfpsecurity); + +@@ -427,12 +424,8 @@ static int qfprom_probe(struct platform_ + return PTR_ERR(priv->vcc); + + priv->secclk = devm_clk_get(dev, "core"); +- if (IS_ERR(priv->secclk)) { +- ret = PTR_ERR(priv->secclk); +- if (ret != -EPROBE_DEFER) +- dev_err(dev, "Error getting clock: %d\n", ret); +- return ret; +- } ++ if (IS_ERR(priv->secclk)) ++ return dev_err_probe(dev, PTR_ERR(priv->secclk), "Error getting clock\n"); + + /* Only enable writing if we have SoC data. */ + if (priv->soc_data) diff --git a/target/linux/generic/backport-6.1/814-v6.6-0008-nvmem-uniphier-Use-devm_platform_get_and_ioremap_res.patch b/target/linux/generic/backport-6.1/814-v6.6-0008-nvmem-uniphier-Use-devm_platform_get_and_ioremap_res.patch new file mode 100644 index 000000000..3e328a4c9 --- /dev/null +++ b/target/linux/generic/backport-6.1/814-v6.6-0008-nvmem-uniphier-Use-devm_platform_get_and_ioremap_res.patch @@ -0,0 +1,29 @@ +From 6ac41c556e22a0d7d267c9b9d48681d73af4b368 Mon Sep 17 00:00:00 2001 +From: Yangtao Li +Date: Wed, 23 Aug 2023 14:27:31 +0100 +Subject: [PATCH] nvmem: uniphier: Use devm_platform_get_and_ioremap_resource() + +Convert platform_get_resource(), devm_ioremap_resource() to a single +call to devm_platform_get_and_ioremap_resource(), as this is exactly +what this function does. + +Signed-off-by: Yangtao Li +Signed-off-by: Srinivas Kandagatla +Link: https://lore.kernel.org/r/20230823132744.350618-10-srinivas.kandagatla@linaro.org +Signed-off-by: Greg Kroah-Hartman +--- + drivers/nvmem/uniphier-efuse.c | 3 +-- + 1 file changed, 1 insertion(+), 2 deletions(-) + +--- a/drivers/nvmem/uniphier-efuse.c ++++ b/drivers/nvmem/uniphier-efuse.c +@@ -41,8 +41,7 @@ static int uniphier_efuse_probe(struct p + if (!priv) + return -ENOMEM; + +- res = platform_get_resource(pdev, IORESOURCE_MEM, 0); +- priv->base = devm_ioremap_resource(dev, res); ++ priv->base = devm_platform_get_and_ioremap_resource(pdev, 0, &res); + if (IS_ERR(priv->base)) + return PTR_ERR(priv->base); + diff --git a/target/linux/generic/backport-6.1/814-v6.6-0009-nvmem-add-new-NXP-QorIQ-eFuse-driver.patch b/target/linux/generic/backport-6.1/814-v6.6-0009-nvmem-add-new-NXP-QorIQ-eFuse-driver.patch new file mode 100644 index 000000000..acbe18e27 --- /dev/null +++ b/target/linux/generic/backport-6.1/814-v6.6-0009-nvmem-add-new-NXP-QorIQ-eFuse-driver.patch @@ -0,0 +1,133 @@ +From c8efcf7a86ebf2ff48584d270b3070a7075bc345 Mon Sep 17 00:00:00 2001 +From: Richard Alpe +Date: Mon, 10 Apr 2023 10:20:51 +0200 +Subject: [PATCH] nvmem: add new NXP QorIQ eFuse driver +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Add SFP (Security Fuse Processor) read support for NXP (Freescale) +QorIQ series SOC's. + +This patch adds support for the T1023 SOC using the SFP offset from +the existing T1023 device tree. In theory this should also work for +T1024, T1014 and T1013 which uses the same SFP base offset. + +Signed-off-by: Richard Alpe +Reviewed-by: Niklas Söderlund +Signed-off-by: Srinivas Kandagatla +--- + drivers/nvmem/Kconfig | 12 ++++++ + drivers/nvmem/Makefile | 2 + + drivers/nvmem/qoriq-efuse.c | 78 +++++++++++++++++++++++++++++++++++++ + 3 files changed, 92 insertions(+) + create mode 100644 drivers/nvmem/qoriq-efuse.c + +--- a/drivers/nvmem/Kconfig ++++ b/drivers/nvmem/Kconfig +@@ -392,4 +392,16 @@ config NVMEM_ZYNQMP + + If sure, say yes. If unsure, say no. + ++config NVMEM_QORIQ_EFUSE ++ tristate "NXP QorIQ eFuse support" ++ depends on PPC_85xx || COMPILE_TEST ++ depends on HAS_IOMEM ++ help ++ This driver provides read support for the eFuses (SFP) on NXP QorIQ ++ series SoC's. This includes secure boot settings, the globally unique ++ NXP ID 'FUIDR' and the OEM unique ID 'OUIDR'. ++ ++ This driver can also be built as a module. If so, the module ++ will be called nvmem_qoriq_efuse. ++ + endif +--- a/drivers/nvmem/Makefile ++++ b/drivers/nvmem/Makefile +@@ -77,3 +77,5 @@ obj-$(CONFIG_NVMEM_VF610_OCOTP) += nvme + nvmem-vf610-ocotp-y := vf610-ocotp.o + obj-$(CONFIG_NVMEM_ZYNQMP) += nvmem_zynqmp_nvmem.o + nvmem_zynqmp_nvmem-y := zynqmp_nvmem.o ++obj-$(CONFIG_NVMEM_QORIQ_EFUSE) += nvmem-qoriq-efuse.o ++nvmem-qoriq-efuse-y := qoriq-efuse.o +--- /dev/null ++++ b/drivers/nvmem/qoriq-efuse.c +@@ -0,0 +1,78 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (C) 2023 Westermo Network Technologies AB ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++struct qoriq_efuse_priv { ++ void __iomem *base; ++}; ++ ++static int qoriq_efuse_read(void *context, unsigned int offset, void *val, ++ size_t bytes) ++{ ++ struct qoriq_efuse_priv *priv = context; ++ ++ /* .stride = 4 so offset is guaranteed to be aligned */ ++ __ioread32_copy(val, priv->base + offset, bytes / 4); ++ ++ /* Ignore trailing bytes (there shouldn't be any) */ ++ ++ return 0; ++} ++ ++static int qoriq_efuse_probe(struct platform_device *pdev) ++{ ++ struct nvmem_config config = { ++ .dev = &pdev->dev, ++ .read_only = true, ++ .reg_read = qoriq_efuse_read, ++ .stride = sizeof(u32), ++ .word_size = sizeof(u32), ++ .name = "qoriq_efuse_read", ++ .id = NVMEM_DEVID_AUTO, ++ .root_only = true, ++ }; ++ struct qoriq_efuse_priv *priv; ++ struct nvmem_device *nvmem; ++ struct resource *res; ++ ++ priv = devm_kzalloc(config.dev, sizeof(*priv), GFP_KERNEL); ++ if (!priv) ++ return -ENOMEM; ++ ++ priv->base = devm_platform_get_and_ioremap_resource(pdev, 0, &res); ++ if (IS_ERR(priv->base)) ++ return PTR_ERR(priv->base); ++ ++ config.size = resource_size(res); ++ config.priv = priv; ++ nvmem = devm_nvmem_register(config.dev, &config); ++ ++ return PTR_ERR_OR_ZERO(nvmem); ++} ++ ++static const struct of_device_id qoriq_efuse_of_match[] = { ++ { .compatible = "fsl,t1023-sfp", }, ++ {/* sentinel */}, ++}; ++MODULE_DEVICE_TABLE(of, qoriq_efuse_of_match); ++ ++static struct platform_driver qoriq_efuse_driver = { ++ .probe = qoriq_efuse_probe, ++ .driver = { ++ .name = "qoriq-efuse", ++ .of_match_table = qoriq_efuse_of_match, ++ }, ++}; ++module_platform_driver(qoriq_efuse_driver); ++ ++MODULE_AUTHOR("Richard Alpe "); ++MODULE_DESCRIPTION("NXP QorIQ Security Fuse Processor (SFP) Reader"); ++MODULE_LICENSE("GPL"); diff --git a/target/linux/generic/backport-6.1/814-v6.6-0011-nvmem-Kconfig-Fix-typo-drive-driver.patch b/target/linux/generic/backport-6.1/814-v6.6-0011-nvmem-Kconfig-Fix-typo-drive-driver.patch new file mode 100644 index 000000000..67f30e34a --- /dev/null +++ b/target/linux/generic/backport-6.1/814-v6.6-0011-nvmem-Kconfig-Fix-typo-drive-driver.patch @@ -0,0 +1,37 @@ +From 9d53d595f688c9837e88a919229cc61a165c7b9e Mon Sep 17 00:00:00 2001 +From: Diederik de Haas +Date: Mon, 24 Jul 2023 13:36:22 +0200 +Subject: [PATCH] nvmem: Kconfig: Fix typo "drive" -> "driver" + +Fix typo where "driver" was meant instead of "drive". +While at it, also capitalize "OTP". + +Signed-off-by: Diederik de Haas +Reviewed-by: Heiko Stuebner +Signed-off-by: Srinivas Kandagatla +--- + drivers/nvmem/Kconfig | 6 +++--- + 1 file changed, 3 insertions(+), 3 deletions(-) + +--- a/drivers/nvmem/Kconfig ++++ b/drivers/nvmem/Kconfig +@@ -247,7 +247,7 @@ config NVMEM_ROCKCHIP_EFUSE + depends on ARCH_ROCKCHIP || COMPILE_TEST + depends on HAS_IOMEM + help +- This is a simple drive to dump specified values of Rockchip SoC ++ This is a simple driver to dump specified values of Rockchip SoC + from eFuse, such as cpu-leakage. + + This driver can also be built as a module. If so, the module +@@ -258,8 +258,8 @@ config NVMEM_ROCKCHIP_OTP + depends on ARCH_ROCKCHIP || COMPILE_TEST + depends on HAS_IOMEM + help +- This is a simple drive to dump specified values of Rockchip SoC +- from otp, such as cpu-leakage. ++ This is a simple driver to dump specified values of Rockchip SoC ++ from OTP, such as cpu-leakage. + + This driver can also be built as a module. If so, the module + will be called nvmem_rockchip_otp. diff --git a/target/linux/generic/backport-6.1/814-v6.6-0012-nvmem-sec-qfprom-Add-Qualcomm-secure-QFPROM-support.patch b/target/linux/generic/backport-6.1/814-v6.6-0012-nvmem-sec-qfprom-Add-Qualcomm-secure-QFPROM-support.patch new file mode 100644 index 000000000..af4a2926d --- /dev/null +++ b/target/linux/generic/backport-6.1/814-v6.6-0012-nvmem-sec-qfprom-Add-Qualcomm-secure-QFPROM-support.patch @@ -0,0 +1,152 @@ +From 0a9ec38c47c1ca4528aa058e2b9ea61901a7e632 Mon Sep 17 00:00:00 2001 +From: Komal Bajaj +Date: Tue, 1 Aug 2023 12:10:25 +0530 +Subject: [PATCH] nvmem: sec-qfprom: Add Qualcomm secure QFPROM support + +For some of the Qualcomm SoC's, it is possible that +some of the fuse regions or entire qfprom region is +protected from non-secure access. In such situations, +the OS will have to use secure calls to read the region. +With that motivation, add secure qfprom driver. + +Signed-off-by: Komal Bajaj +Signed-off-by: Srinivas Kandagatla +--- + drivers/nvmem/Kconfig | 13 ++++++ + drivers/nvmem/Makefile | 2 + + drivers/nvmem/sec-qfprom.c | 96 ++++++++++++++++++++++++++++++++++++++ + 3 files changed, 111 insertions(+) + create mode 100644 drivers/nvmem/sec-qfprom.c + +--- a/drivers/nvmem/Kconfig ++++ b/drivers/nvmem/Kconfig +@@ -226,6 +226,19 @@ config NVMEM_QCOM_QFPROM + This driver can also be built as a module. If so, the module + will be called nvmem_qfprom. + ++config NVMEM_QCOM_SEC_QFPROM ++ tristate "QCOM SECURE QFPROM Support" ++ depends on ARCH_QCOM || COMPILE_TEST ++ depends on HAS_IOMEM ++ depends on OF ++ select QCOM_SCM ++ help ++ Say y here to enable secure QFPROM support. The secure QFPROM provides access ++ functions for QFPROM data to rest of the drivers via nvmem interface. ++ ++ This driver can also be built as a module. If so, the module will be called ++ nvmem_sec_qfprom. ++ + config NVMEM_RAVE_SP_EEPROM + tristate "Rave SP EEPROM Support" + depends on RAVE_SP_CORE +--- a/drivers/nvmem/Makefile ++++ b/drivers/nvmem/Makefile +@@ -46,6 +46,8 @@ obj-$(CONFIG_NVMEM_NINTENDO_OTP) += nvme + nvmem-nintendo-otp-y := nintendo-otp.o + obj-$(CONFIG_NVMEM_QCOM_QFPROM) += nvmem_qfprom.o + nvmem_qfprom-y := qfprom.o ++obj-$(CONFIG_NVMEM_QCOM_SEC_QFPROM) += nvmem_sec_qfprom.o ++nvmem_sec_qfprom-y := sec-qfprom.o + obj-$(CONFIG_NVMEM_RAVE_SP_EEPROM) += nvmem-rave-sp-eeprom.o + nvmem-rave-sp-eeprom-y := rave-sp-eeprom.o + obj-$(CONFIG_NVMEM_RMEM) += nvmem-rmem.o +--- /dev/null ++++ b/drivers/nvmem/sec-qfprom.c +@@ -0,0 +1,96 @@ ++// SPDX-License-Identifier: GPL-2.0-only ++/* ++ * Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++ ++/** ++ * struct sec_qfprom - structure holding secure qfprom attributes ++ * ++ * @base: starting physical address for secure qfprom corrected address space. ++ * @dev: qfprom device structure. ++ */ ++struct sec_qfprom { ++ phys_addr_t base; ++ struct device *dev; ++}; ++ ++static int sec_qfprom_reg_read(void *context, unsigned int reg, void *_val, size_t bytes) ++{ ++ struct sec_qfprom *priv = context; ++ unsigned int i; ++ u8 *val = _val; ++ u32 read_val; ++ u8 *tmp; ++ ++ for (i = 0; i < bytes; i++, reg++) { ++ if (i == 0 || reg % 4 == 0) { ++ if (qcom_scm_io_readl(priv->base + (reg & ~3), &read_val)) { ++ dev_err(priv->dev, "Couldn't access fuse register\n"); ++ return -EINVAL; ++ } ++ tmp = (u8 *)&read_val; ++ } ++ ++ val[i] = tmp[reg & 3]; ++ } ++ ++ return 0; ++} ++ ++static int sec_qfprom_probe(struct platform_device *pdev) ++{ ++ struct nvmem_config econfig = { ++ .name = "sec-qfprom", ++ .stride = 1, ++ .word_size = 1, ++ .id = NVMEM_DEVID_AUTO, ++ .reg_read = sec_qfprom_reg_read, ++ }; ++ struct device *dev = &pdev->dev; ++ struct nvmem_device *nvmem; ++ struct sec_qfprom *priv; ++ struct resource *res; ++ ++ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); ++ if (!priv) ++ return -ENOMEM; ++ ++ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ if (!res) ++ return -EINVAL; ++ ++ priv->base = res->start; ++ ++ econfig.size = resource_size(res); ++ econfig.dev = dev; ++ econfig.priv = priv; ++ ++ priv->dev = dev; ++ ++ nvmem = devm_nvmem_register(dev, &econfig); ++ ++ return PTR_ERR_OR_ZERO(nvmem); ++} ++ ++static const struct of_device_id sec_qfprom_of_match[] = { ++ { .compatible = "qcom,sec-qfprom" }, ++ {/* sentinel */}, ++}; ++MODULE_DEVICE_TABLE(of, sec_qfprom_of_match); ++ ++static struct platform_driver qfprom_driver = { ++ .probe = sec_qfprom_probe, ++ .driver = { ++ .name = "qcom_sec_qfprom", ++ .of_match_table = sec_qfprom_of_match, ++ }, ++}; ++module_platform_driver(qfprom_driver); ++MODULE_DESCRIPTION("Qualcomm Secure QFPROM driver"); ++MODULE_LICENSE("GPL"); diff --git a/target/linux/generic/backport-6.1/814-v6.6-0013-nvmem-u-boot-env-Replace-zero-length-array-with-DECL.patch b/target/linux/generic/backport-6.1/814-v6.6-0013-nvmem-u-boot-env-Replace-zero-length-array-with-DECL.patch new file mode 100644 index 000000000..dab8ec2c2 --- /dev/null +++ b/target/linux/generic/backport-6.1/814-v6.6-0013-nvmem-u-boot-env-Replace-zero-length-array-with-DECL.patch @@ -0,0 +1,30 @@ +From c32f2186acc9abb4d766361255d7ddf07d15eeb2 Mon Sep 17 00:00:00 2001 +From: Atul Raut +Date: Sun, 30 Jul 2023 15:39:15 -0700 +Subject: [PATCH] nvmem: u-boot-env:: Replace zero-length array with + DECLARE_FLEX_ARRAY() helper + +We are moving toward replacing zero-length arrays with C99 flexible-array +members since they are deprecated. Therefore, the new DECLARE_FLEX_ARRAY() +helper macro should be used to replace the zero-length array declaration. + +This fixes warnings such as: +./drivers/nvmem/u-boot-env.c:50:9-13: WARNING use flexible-array member instead (https://www.kernel.org/doc/html/latest/process/deprecated.html#zero-length-and-one-element-arrays) + +Signed-off-by: Atul Raut +Signed-off-by: Srinivas Kandagatla +--- + drivers/nvmem/u-boot-env.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/drivers/nvmem/u-boot-env.c ++++ b/drivers/nvmem/u-boot-env.c +@@ -47,7 +47,7 @@ struct u_boot_env_image_broadcom { + __le32 magic; + __le32 len; + __le32 crc32; +- uint8_t data[0]; ++ DECLARE_FLEX_ARRAY(uint8_t, data); + } __packed; + + static int u_boot_env_read(void *context, unsigned int offset, void *val, diff --git a/target/linux/generic/backport-6.1/814-v6.6-0014-nvmem-core-Create-all-cells-before-adding-the-nvmem-.patch b/target/linux/generic/backport-6.1/814-v6.6-0014-nvmem-core-Create-all-cells-before-adding-the-nvmem-.patch new file mode 100644 index 000000000..f9532f39c --- /dev/null +++ b/target/linux/generic/backport-6.1/814-v6.6-0014-nvmem-core-Create-all-cells-before-adding-the-nvmem-.patch @@ -0,0 +1,40 @@ +From 104af6a5b199eb4dc7970d1304aef38ac5a6ed54 Mon Sep 17 00:00:00 2001 +From: Miquel Raynal +Date: Tue, 8 Aug 2023 08:29:26 +0200 +Subject: [PATCH] nvmem: core: Create all cells before adding the nvmem device + +Let's pack all the cells creation in one place, so they are all created +before we add the nvmem device. + +Signed-off-by: Miquel Raynal +Reviewed-by: Michael Walle +Signed-off-by: Srinivas Kandagatla +--- + drivers/nvmem/core.c | 10 +++++----- + 1 file changed, 5 insertions(+), 5 deletions(-) + +--- a/drivers/nvmem/core.c ++++ b/drivers/nvmem/core.c +@@ -998,17 +998,17 @@ struct nvmem_device *nvmem_register(cons + if (rval) + goto err_remove_cells; + +- dev_dbg(&nvmem->dev, "Registering nvmem device %s\n", config->name); +- +- rval = device_add(&nvmem->dev); ++ rval = nvmem_add_cells_from_fixed_layout(nvmem); + if (rval) + goto err_remove_cells; + +- rval = nvmem_add_cells_from_fixed_layout(nvmem); ++ rval = nvmem_add_cells_from_layout(nvmem); + if (rval) + goto err_remove_cells; + +- rval = nvmem_add_cells_from_layout(nvmem); ++ dev_dbg(&nvmem->dev, "Registering nvmem device %s\n", config->name); ++ ++ rval = device_add(&nvmem->dev); + if (rval) + goto err_remove_cells; + diff --git a/target/linux/generic/backport-6.1/814-v6.6-0015-nvmem-core-Return-NULL-when-no-nvmem-layout-is-found.patch b/target/linux/generic/backport-6.1/814-v6.6-0015-nvmem-core-Return-NULL-when-no-nvmem-layout-is-found.patch new file mode 100644 index 000000000..8f64d0c28 --- /dev/null +++ b/target/linux/generic/backport-6.1/814-v6.6-0015-nvmem-core-Return-NULL-when-no-nvmem-layout-is-found.patch @@ -0,0 +1,35 @@ +From 6c7f48ea2e663b679aa8e60d8d8e1e6306a644f9 Mon Sep 17 00:00:00 2001 +From: Miquel Raynal +Date: Tue, 8 Aug 2023 08:29:27 +0200 +Subject: [PATCH] nvmem: core: Return NULL when no nvmem layout is found + +Currently, of_nvmem_layout_get_container() returns NULL on error, or an +error pointer if either CONFIG_NVMEM or CONFIG_OF is turned off. We +should likely avoid this kind of mix for two reasons: to clarify the +intend and anyway fix the !CONFIG_OF which will likely always if we use +this helper somewhere else. Let's just return NULL when no layout is +found, we don't need an error value here. + +Link: https://staticthinking.wordpress.com/2022/08/01/mixing-error-pointers-and-null/ +Fixes: 266570f496b9 ("nvmem: core: introduce NVMEM layouts") +Reported-by: kernel test robot +Reported-by: Dan Carpenter +Closes: https://lore.kernel.org/r/202308030002.DnSFOrMB-lkp@intel.com/ +Signed-off-by: Miquel Raynal +Reviewed-by: Michael Walle +Signed-off-by: Srinivas Kandagatla +--- + include/linux/nvmem-consumer.h | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/include/linux/nvmem-consumer.h ++++ b/include/linux/nvmem-consumer.h +@@ -256,7 +256,7 @@ static inline struct nvmem_device *of_nv + static inline struct device_node * + of_nvmem_layout_get_container(struct nvmem_device *nvmem) + { +- return ERR_PTR(-EOPNOTSUPP); ++ return NULL; + } + #endif /* CONFIG_NVMEM && CONFIG_OF */ + diff --git a/target/linux/generic/backport-6.1/814-v6.6-0016-nvmem-core-Do-not-open-code-existing-functions.patch b/target/linux/generic/backport-6.1/814-v6.6-0016-nvmem-core-Do-not-open-code-existing-functions.patch new file mode 100644 index 000000000..28d8bba19 --- /dev/null +++ b/target/linux/generic/backport-6.1/814-v6.6-0016-nvmem-core-Do-not-open-code-existing-functions.patch @@ -0,0 +1,29 @@ +From b8257f61b4ddac6d7d0e19a5a4e8b07afb3b4ed3 Mon Sep 17 00:00:00 2001 +From: Miquel Raynal +Date: Tue, 8 Aug 2023 08:29:28 +0200 +Subject: [PATCH] nvmem: core: Do not open-code existing functions + +Use of_nvmem_layout_get_container() instead of hardcoding it. + +Signed-off-by: Miquel Raynal +Reviewed-by: Michael Walle +Signed-off-by: Srinivas Kandagatla +--- + drivers/nvmem/core.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +--- a/drivers/nvmem/core.c ++++ b/drivers/nvmem/core.c +@@ -786,10 +786,10 @@ EXPORT_SYMBOL_GPL(nvmem_layout_unregiste + + static struct nvmem_layout *nvmem_layout_get(struct nvmem_device *nvmem) + { +- struct device_node *layout_np, *np = nvmem->dev.of_node; ++ struct device_node *layout_np; + struct nvmem_layout *l, *layout = ERR_PTR(-EPROBE_DEFER); + +- layout_np = of_get_child_by_name(np, "nvmem-layout"); ++ layout_np = of_nvmem_layout_get_container(nvmem); + if (!layout_np) + return NULL; + diff --git a/target/linux/generic/backport-6.1/814-v6.6-0017-nvmem-core-Notify-when-a-new-layout-is-registered.patch b/target/linux/generic/backport-6.1/814-v6.6-0017-nvmem-core-Notify-when-a-new-layout-is-registered.patch new file mode 100644 index 000000000..b62a0e18d --- /dev/null +++ b/target/linux/generic/backport-6.1/814-v6.6-0017-nvmem-core-Notify-when-a-new-layout-is-registered.patch @@ -0,0 +1,44 @@ +From 0991afbe4b1805e7f0113ef10d7c5f0698a739e4 Mon Sep 17 00:00:00 2001 +From: Miquel Raynal +Date: Tue, 8 Aug 2023 08:29:29 +0200 +Subject: [PATCH] nvmem: core: Notify when a new layout is registered + +Tell listeners a new layout was introduced and is now available. + +Signed-off-by: Miquel Raynal +Signed-off-by: Srinivas Kandagatla +--- + drivers/nvmem/core.c | 4 ++++ + include/linux/nvmem-consumer.h | 2 ++ + 2 files changed, 6 insertions(+) + +--- a/drivers/nvmem/core.c ++++ b/drivers/nvmem/core.c +@@ -772,12 +772,16 @@ int __nvmem_layout_register(struct nvmem + list_add(&layout->node, &nvmem_layouts); + spin_unlock(&nvmem_layout_lock); + ++ blocking_notifier_call_chain(&nvmem_notifier, NVMEM_LAYOUT_ADD, layout); ++ + return 0; + } + EXPORT_SYMBOL_GPL(__nvmem_layout_register); + + void nvmem_layout_unregister(struct nvmem_layout *layout) + { ++ blocking_notifier_call_chain(&nvmem_notifier, NVMEM_LAYOUT_REMOVE, layout); ++ + spin_lock(&nvmem_layout_lock); + list_del(&layout->node); + spin_unlock(&nvmem_layout_lock); +--- a/include/linux/nvmem-consumer.h ++++ b/include/linux/nvmem-consumer.h +@@ -43,6 +43,8 @@ enum { + NVMEM_REMOVE, + NVMEM_CELL_ADD, + NVMEM_CELL_REMOVE, ++ NVMEM_LAYOUT_ADD, ++ NVMEM_LAYOUT_REMOVE, + }; + + #if IS_ENABLED(CONFIG_NVMEM) diff --git a/target/linux/generic/backport-6.1/815-v6.6-1-leds-turris-omnia-Use-sysfs_emit-instead-of-sprintf.patch b/target/linux/generic/backport-6.1/815-v6.6-1-leds-turris-omnia-Use-sysfs_emit-instead-of-sprintf.patch new file mode 100644 index 000000000..e17be439b --- /dev/null +++ b/target/linux/generic/backport-6.1/815-v6.6-1-leds-turris-omnia-Use-sysfs_emit-instead-of-sprintf.patch @@ -0,0 +1,29 @@ +From a75b58a46423cfd9b1f73581f4bd2ac2ae743996 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Marek=20Beh=C3=BAn?= +Date: Wed, 2 Aug 2023 18:07:45 +0200 +Subject: [PATCH 1/6] leds: turris-omnia: Use sysfs_emit() instead of sprintf() +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Use the dedicated sysfs_emit() function instead of sprintf() in sysfs +attribute accessor brightness_show(). + +Signed-off-by: Marek Behún +Link: https://lore.kernel.org/r/20230802160748.11208-4-kabel@kernel.org +Signed-off-by: Lee Jones +--- + drivers/leds/leds-turris-omnia.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/drivers/leds/leds-turris-omnia.c ++++ b/drivers/leds/leds-turris-omnia.c +@@ -194,7 +194,7 @@ static ssize_t brightness_show(struct de + if (ret < 0) + return ret; + +- return sprintf(buf, "%d\n", ret); ++ return sysfs_emit(buf, "%d\n", ret); + } + + static ssize_t brightness_store(struct device *dev, struct device_attribute *a, diff --git a/target/linux/generic/backport-6.1/815-v6.7-2-leds-turris-omnia-Make-set_brightness-more-efficient.patch b/target/linux/generic/backport-6.1/815-v6.7-2-leds-turris-omnia-Make-set_brightness-more-efficient.patch new file mode 100644 index 000000000..d84ad6712 --- /dev/null +++ b/target/linux/generic/backport-6.1/815-v6.7-2-leds-turris-omnia-Make-set_brightness-more-efficient.patch @@ -0,0 +1,207 @@ +From a64c3c1357275b1fd61bc9734f638cdb5d8a8bbb Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Marek=20Beh=C3=BAn?= +Date: Mon, 18 Sep 2023 18:11:02 +0200 +Subject: [PATCH 4/6] leds: turris-omnia: Make set_brightness() more efficient +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Implement caching of the LED color and state values that are sent to MCU +in order to make the set_brightness() operation more efficient by +avoiding I2C transactions which are not needed. + +On Turris Omnia's MCU, which acts as the RGB LED controller, each LED +has a RGB color, and a ON/OFF state, which are configurable via I2C +commands CMD_LED_COLOR and CMD_LED_STATE. + +The CMD_LED_COLOR command sends 5 bytes and the CMD_LED_STATE command 2 +bytes over the I2C bus, which operates at 100 kHz. With I2C overhead +this allows ~1670 color changing commands and ~3200 state changing +commands per second (or around 1000 color + state changes per second). +This may seem more than enough, but the issue is that the I2C bus is +shared with another peripheral, the MCU. The MCU exposes an interrupt +interface, and it can trigger hundreds of interrupts per second. Each +time, we need to read the interrupt state register over this I2C bus. +Whenever we are sending a LED color/state changing command, the +interrupt reading is waiting. + +Currently, every time LED brightness or LED multi intensity is changed, +we send a CMD_LED_STATE command, and if the computed color (brightness +adjusted multi_intensity) is non-zero, we also send a CMD_LED_COLOR +command. + +Consider for example the situation when we have a netdev trigger enabled +for a LED. The netdev trigger does not change the LED color, only the +brightness (either to 0 or to currently configured brightness), and so +there is no need to send the CMD_LED_COLOR command. But each change of +brightness to 0 sends one CMD_LED_STATE command, and each change of +brightness to max_brightness sends one CMD_LED_STATE command and one +CMD_LED_COLOR command: + set_brightness(0) -> CMD_LED_STATE + set_brightness(255) -> CMD_LED_STATE + CMD_LED_COLOR + (unnecessary) + +We can avoid the unnecessary I2C transactions if we cache the values of +state and color that are sent to the controller. If the color does not +change from the one previously sent, there is no need to do the +CMD_LED_COLOR I2C transaction, and if the state does not change, there +is no need to do the CMD_LED_STATE transaction. + +Because we need to make sure that our cached values are consistent with +the controller state, add explicit setting of the LED color to white at +probe time (this is the default setting when MCU resets, but does not +necessarily need to be the case, for example if U-Boot played with the +LED colors). + +Signed-off-by: Marek Behún +Link: https://lore.kernel.org/r/20230918161104.20860-3-kabel@kernel.org +Signed-off-by: Lee Jones +--- + drivers/leds/leds-turris-omnia.c | 96 ++++++++++++++++++++++++++------ + 1 file changed, 78 insertions(+), 18 deletions(-) + +--- a/drivers/leds/leds-turris-omnia.c ++++ b/drivers/leds/leds-turris-omnia.c +@@ -30,6 +30,8 @@ + struct omnia_led { + struct led_classdev_mc mc_cdev; + struct mc_subled subled_info[OMNIA_LED_NUM_CHANNELS]; ++ u8 cached_channels[OMNIA_LED_NUM_CHANNELS]; ++ bool on; + int reg; + }; + +@@ -72,36 +74,82 @@ static int omnia_cmd_read_u8(const struc + return -EIO; + } + ++static int omnia_led_send_color_cmd(const struct i2c_client *client, ++ struct omnia_led *led) ++{ ++ char cmd[5]; ++ int ret; ++ ++ cmd[0] = CMD_LED_COLOR; ++ cmd[1] = led->reg; ++ cmd[2] = led->subled_info[0].brightness; ++ cmd[3] = led->subled_info[1].brightness; ++ cmd[4] = led->subled_info[2].brightness; ++ ++ /* Send the color change command */ ++ ret = i2c_master_send(client, cmd, 5); ++ if (ret < 0) ++ return ret; ++ ++ /* Cache the RGB channel brightnesses */ ++ for (int i = 0; i < OMNIA_LED_NUM_CHANNELS; ++i) ++ led->cached_channels[i] = led->subled_info[i].brightness; ++ ++ return 0; ++} ++ ++/* Determine if the computed RGB channels are different from the cached ones */ ++static bool omnia_led_channels_changed(struct omnia_led *led) ++{ ++ for (int i = 0; i < OMNIA_LED_NUM_CHANNELS; ++i) ++ if (led->subled_info[i].brightness != led->cached_channels[i]) ++ return true; ++ ++ return false; ++} ++ + static int omnia_led_brightness_set_blocking(struct led_classdev *cdev, + enum led_brightness brightness) + { + struct led_classdev_mc *mc_cdev = lcdev_to_mccdev(cdev); + struct omnia_leds *leds = dev_get_drvdata(cdev->dev->parent); + struct omnia_led *led = to_omnia_led(mc_cdev); +- u8 buf[5], state; +- int ret; ++ int err = 0; + + mutex_lock(&leds->lock); + +- led_mc_calc_color_components(&led->mc_cdev, brightness); ++ /* ++ * Only recalculate RGB brightnesses from intensities if brightness is ++ * non-zero. Otherwise we won't be using them and we can save ourselves ++ * some software divisions (Omnia's CPU does not implement the division ++ * instruction). ++ */ ++ if (brightness) { ++ led_mc_calc_color_components(mc_cdev, brightness); ++ ++ /* ++ * Send color command only if brightness is non-zero and the RGB ++ * channel brightnesses changed. ++ */ ++ if (omnia_led_channels_changed(led)) ++ err = omnia_led_send_color_cmd(leds->client, led); ++ } + +- buf[0] = CMD_LED_COLOR; +- buf[1] = led->reg; +- buf[2] = mc_cdev->subled_info[0].brightness; +- buf[3] = mc_cdev->subled_info[1].brightness; +- buf[4] = mc_cdev->subled_info[2].brightness; +- +- state = CMD_LED_STATE_LED(led->reg); +- if (buf[2] || buf[3] || buf[4]) +- state |= CMD_LED_STATE_ON; +- +- ret = omnia_cmd_write_u8(leds->client, CMD_LED_STATE, state); +- if (ret >= 0 && (state & CMD_LED_STATE_ON)) +- ret = i2c_master_send(leds->client, buf, 5); ++ /* Send on/off state change only if (bool)brightness changed */ ++ if (!err && !brightness != !led->on) { ++ u8 state = CMD_LED_STATE_LED(led->reg); ++ ++ if (brightness) ++ state |= CMD_LED_STATE_ON; ++ ++ err = omnia_cmd_write_u8(leds->client, CMD_LED_STATE, state); ++ if (!err) ++ led->on = !!brightness; ++ } + + mutex_unlock(&leds->lock); + +- return ret; ++ return err; + } + + static int omnia_led_register(struct i2c_client *client, struct omnia_led *led, +@@ -129,11 +177,15 @@ static int omnia_led_register(struct i2c + } + + led->subled_info[0].color_index = LED_COLOR_ID_RED; +- led->subled_info[0].channel = 0; + led->subled_info[1].color_index = LED_COLOR_ID_GREEN; +- led->subled_info[1].channel = 1; + led->subled_info[2].color_index = LED_COLOR_ID_BLUE; +- led->subled_info[2].channel = 2; ++ ++ /* Initial color is white */ ++ for (int i = 0; i < OMNIA_LED_NUM_CHANNELS; ++i) { ++ led->subled_info[i].intensity = 255; ++ led->subled_info[i].brightness = 255; ++ led->subled_info[i].channel = i; ++ } + + led->mc_cdev.subled_info = led->subled_info; + led->mc_cdev.num_colors = OMNIA_LED_NUM_CHANNELS; +@@ -162,6 +214,14 @@ static int omnia_led_register(struct i2c + return ret; + } + ++ /* Set initial color and cache it */ ++ ret = omnia_led_send_color_cmd(client, led); ++ if (ret < 0) { ++ dev_err(dev, "Cannot set LED %pOF initial color: %i\n", np, ++ ret); ++ return ret; ++ } ++ + ret = devm_led_classdev_multicolor_register_ext(dev, &led->mc_cdev, + &init_data); + if (ret < 0) { diff --git a/target/linux/generic/backport-6.1/815-v6.7-3-leds-turris-omnia-Support-HW-controlled-mode-via-pri.patch b/target/linux/generic/backport-6.1/815-v6.7-3-leds-turris-omnia-Support-HW-controlled-mode-via-pri.patch new file mode 100644 index 000000000..00773ab0f --- /dev/null +++ b/target/linux/generic/backport-6.1/815-v6.7-3-leds-turris-omnia-Support-HW-controlled-mode-via-pri.patch @@ -0,0 +1,202 @@ +From e965e0f6de60874fc0a0caed9a9e0122999e0c7b Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Marek=20Beh=C3=BAn?= +Date: Mon, 18 Sep 2023 18:11:03 +0200 +Subject: [PATCH 5/6] leds: turris-omnia: Support HW controlled mode via + private trigger +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Add support for enabling MCU controlled mode of the Turris Omnia LEDs +via a LED private trigger called "omnia-mcu". Recall that private LED +triggers will only be listed in the sysfs trigger file for LEDs that +support them (currently there is no user of this mechanism). + +When in MCU controlled mode, the user can still set LED color, but the +blinking is done by MCU, which does different things for different LEDs: +- WAN LED is blinked according to the LED[0] pin of the WAN PHY +- LAN LEDs are blinked according to the LED[0] output of the + corresponding port of the LAN switch +- PCIe LEDs are blinked according to the logical OR of the MiniPCIe port + LED pins + +In the future I want to make the netdev trigger to transparently offload +the blinking to the HW if user sets compatible settings for the netdev +trigger (for LEDs associated with network devices). +There was some work on this already, and hopefully we will be able to +complete it sometime, but for now there are still multiple blockers for +this, and even if there weren't, we still would not be able to configure +HW controlled mode for the LEDs associated with MiniPCIe ports. + +In the meantime let's support HW controlled mode via the private LED +trigger mechanism. If, in the future, we manage to complete the netdev +trigger offloading, we can still keep this private trigger for backwards +compatibility, if needed. + +We also set "omnia-mcu" to cdev->default_trigger, so that the MCU keeps +control until the user first wants to take over it. If a different +default trigger is specified in device-tree via the +'linux,default-trigger' property, LED class will overwrite +cdev->default_trigger, and so the DT property will be respected. + +Signed-off-by: Marek Behún +Link: https://lore.kernel.org/r/20230918161104.20860-4-kabel@kernel.org +Signed-off-by: Lee Jones +--- + drivers/leds/Kconfig | 1 + + drivers/leds/leds-turris-omnia.c | 98 +++++++++++++++++++++++++++++--- + 2 files changed, 91 insertions(+), 8 deletions(-) + +--- a/drivers/leds/Kconfig ++++ b/drivers/leds/Kconfig +@@ -164,6 +164,7 @@ config LEDS_TURRIS_OMNIA + depends on I2C + depends on MACH_ARMADA_38X || COMPILE_TEST + depends on OF ++ select LEDS_TRIGGERS + help + This option enables basic support for the LEDs found on the front + side of CZ.NIC's Turris Omnia router. There are 12 RGB LEDs on the +--- a/drivers/leds/leds-turris-omnia.c ++++ b/drivers/leds/leds-turris-omnia.c +@@ -31,7 +31,7 @@ struct omnia_led { + struct led_classdev_mc mc_cdev; + struct mc_subled subled_info[OMNIA_LED_NUM_CHANNELS]; + u8 cached_channels[OMNIA_LED_NUM_CHANNELS]; +- bool on; ++ bool on, hwtrig; + int reg; + }; + +@@ -120,12 +120,14 @@ static int omnia_led_brightness_set_bloc + + /* + * Only recalculate RGB brightnesses from intensities if brightness is +- * non-zero. Otherwise we won't be using them and we can save ourselves +- * some software divisions (Omnia's CPU does not implement the division +- * instruction). ++ * non-zero (if it is zero and the LED is in HW blinking mode, we use ++ * max_brightness as brightness). Otherwise we won't be using them and ++ * we can save ourselves some software divisions (Omnia's CPU does not ++ * implement the division instruction). + */ +- if (brightness) { +- led_mc_calc_color_components(mc_cdev, brightness); ++ if (brightness || led->hwtrig) { ++ led_mc_calc_color_components(mc_cdev, brightness ?: ++ cdev->max_brightness); + + /* + * Send color command only if brightness is non-zero and the RGB +@@ -135,8 +137,11 @@ static int omnia_led_brightness_set_bloc + err = omnia_led_send_color_cmd(leds->client, led); + } + +- /* Send on/off state change only if (bool)brightness changed */ +- if (!err && !brightness != !led->on) { ++ /* ++ * Send on/off state change only if (bool)brightness changed and the LED ++ * is not being blinked by HW. ++ */ ++ if (!err && !led->hwtrig && !brightness != !led->on) { + u8 state = CMD_LED_STATE_LED(led->reg); + + if (brightness) +@@ -152,6 +157,71 @@ static int omnia_led_brightness_set_bloc + return err; + } + ++static struct led_hw_trigger_type omnia_hw_trigger_type; ++ ++static int omnia_hwtrig_activate(struct led_classdev *cdev) ++{ ++ struct led_classdev_mc *mc_cdev = lcdev_to_mccdev(cdev); ++ struct omnia_leds *leds = dev_get_drvdata(cdev->dev->parent); ++ struct omnia_led *led = to_omnia_led(mc_cdev); ++ int err = 0; ++ ++ mutex_lock(&leds->lock); ++ ++ if (!led->on) { ++ /* ++ * If the LED is off (brightness was set to 0), the last ++ * configured color was not necessarily sent to the MCU. ++ * Recompute with max_brightness and send if needed. ++ */ ++ led_mc_calc_color_components(mc_cdev, cdev->max_brightness); ++ ++ if (omnia_led_channels_changed(led)) ++ err = omnia_led_send_color_cmd(leds->client, led); ++ } ++ ++ if (!err) { ++ /* Put the LED into MCU controlled mode */ ++ err = omnia_cmd_write_u8(leds->client, CMD_LED_MODE, ++ CMD_LED_MODE_LED(led->reg)); ++ if (!err) ++ led->hwtrig = true; ++ } ++ ++ mutex_unlock(&leds->lock); ++ ++ return err; ++} ++ ++static void omnia_hwtrig_deactivate(struct led_classdev *cdev) ++{ ++ struct omnia_leds *leds = dev_get_drvdata(cdev->dev->parent); ++ struct omnia_led *led = to_omnia_led(lcdev_to_mccdev(cdev)); ++ int err; ++ ++ mutex_lock(&leds->lock); ++ ++ led->hwtrig = false; ++ ++ /* Put the LED into software mode */ ++ err = omnia_cmd_write_u8(leds->client, CMD_LED_MODE, ++ CMD_LED_MODE_LED(led->reg) | ++ CMD_LED_MODE_USER); ++ ++ mutex_unlock(&leds->lock); ++ ++ if (err < 0) ++ dev_err(cdev->dev, "Cannot put LED to software mode: %i\n", ++ err); ++} ++ ++static struct led_trigger omnia_hw_trigger = { ++ .name = "omnia-mcu", ++ .activate = omnia_hwtrig_activate, ++ .deactivate = omnia_hwtrig_deactivate, ++ .trigger_type = &omnia_hw_trigger_type, ++}; ++ + static int omnia_led_register(struct i2c_client *client, struct omnia_led *led, + struct device_node *np) + { +@@ -195,6 +265,12 @@ static int omnia_led_register(struct i2c + cdev = &led->mc_cdev.led_cdev; + cdev->max_brightness = 255; + cdev->brightness_set_blocking = omnia_led_brightness_set_blocking; ++ cdev->trigger_type = &omnia_hw_trigger_type; ++ /* ++ * Use the omnia-mcu trigger as the default trigger. It may be rewritten ++ * by LED class from the linux,default-trigger property. ++ */ ++ cdev->default_trigger = omnia_hw_trigger.name; + + /* put the LED into software mode */ + ret = omnia_cmd_write_u8(client, CMD_LED_MODE, +@@ -309,6 +385,12 @@ static int omnia_leds_probe(struct i2c_c + + mutex_init(&leds->lock); + ++ ret = devm_led_trigger_register(dev, &omnia_hw_trigger); ++ if (ret < 0) { ++ dev_err(dev, "Cannot register private LED trigger: %d\n", ret); ++ return ret; ++ } ++ + led = &leds->leds[0]; + for_each_available_child_of_node(np, child) { + ret = omnia_led_register(client, led, child); diff --git a/target/linux/generic/backport-6.1/815-v6.7-4-leds-turris-omnia-Add-support-for-enabling-disabling.patch b/target/linux/generic/backport-6.1/815-v6.7-4-leds-turris-omnia-Add-support-for-enabling-disabling.patch new file mode 100644 index 000000000..e98397922 --- /dev/null +++ b/target/linux/generic/backport-6.1/815-v6.7-4-leds-turris-omnia-Add-support-for-enabling-disabling.patch @@ -0,0 +1,244 @@ +From 0efb3f9609d3de5a7d8c31e3835d7eb3e6adce79 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Marek=20Beh=C3=BAn?= +Date: Mon, 18 Sep 2023 18:11:04 +0200 +Subject: [PATCH 6/6] leds: turris-omnia: Add support for enabling/disabling HW + gamma correction +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +If the MCU on Turris Omnia is running newer firmware versions, the LED +controller supports RGB gamma correction (and enables it by default for +newer boards). + +Determine whether the gamma correction setting feature is supported and +add the ability to set it via sysfs attribute file. + +Signed-off-by: Marek Behún +Link: https://lore.kernel.org/r/20230918161104.20860-5-kabel@kernel.org +Signed-off-by: Lee Jones +--- + .../sysfs-class-led-driver-turris-omnia | 14 ++ + drivers/leds/leds-turris-omnia.c | 137 +++++++++++++++--- + 2 files changed, 134 insertions(+), 17 deletions(-) + +--- a/Documentation/ABI/testing/sysfs-class-led-driver-turris-omnia ++++ b/Documentation/ABI/testing/sysfs-class-led-driver-turris-omnia +@@ -12,3 +12,17 @@ Description: (RW) On the front panel of + able to change this setting from software. + + Format: %i ++ ++What: /sys/class/leds//device/gamma_correction ++Date: August 2023 ++KernelVersion: 6.6 ++Contact: Marek Behún ++Description: (RW) Newer versions of the microcontroller firmware of the ++ Turris Omnia router support gamma correction for the RGB LEDs. ++ This feature can be enabled/disabled by writing to this file. ++ ++ If the feature is not supported because the MCU firmware is too ++ old, the file always reads as 0, and writing to the file results ++ in the EOPNOTSUPP error. ++ ++ Format: %i +--- a/drivers/leds/leds-turris-omnia.c ++++ b/drivers/leds/leds-turris-omnia.c +@@ -15,17 +15,30 @@ + #define OMNIA_BOARD_LEDS 12 + #define OMNIA_LED_NUM_CHANNELS 3 + +-#define CMD_LED_MODE 3 +-#define CMD_LED_MODE_LED(l) ((l) & 0x0f) +-#define CMD_LED_MODE_USER 0x10 +- +-#define CMD_LED_STATE 4 +-#define CMD_LED_STATE_LED(l) ((l) & 0x0f) +-#define CMD_LED_STATE_ON 0x10 +- +-#define CMD_LED_COLOR 5 +-#define CMD_LED_SET_BRIGHTNESS 7 +-#define CMD_LED_GET_BRIGHTNESS 8 ++/* MCU controller commands at I2C address 0x2a */ ++#define OMNIA_MCU_I2C_ADDR 0x2a ++ ++#define CMD_GET_STATUS_WORD 0x01 ++#define STS_FEATURES_SUPPORTED BIT(2) ++ ++#define CMD_GET_FEATURES 0x10 ++#define FEAT_LED_GAMMA_CORRECTION BIT(5) ++ ++/* LED controller commands at I2C address 0x2b */ ++#define CMD_LED_MODE 0x03 ++#define CMD_LED_MODE_LED(l) ((l) & 0x0f) ++#define CMD_LED_MODE_USER 0x10 ++ ++#define CMD_LED_STATE 0x04 ++#define CMD_LED_STATE_LED(l) ((l) & 0x0f) ++#define CMD_LED_STATE_ON 0x10 ++ ++#define CMD_LED_COLOR 0x05 ++#define CMD_LED_SET_BRIGHTNESS 0x07 ++#define CMD_LED_GET_BRIGHTNESS 0x08 ++ ++#define CMD_SET_GAMMA_CORRECTION 0x30 ++#define CMD_GET_GAMMA_CORRECTION 0x31 + + struct omnia_led { + struct led_classdev_mc mc_cdev; +@@ -40,6 +53,7 @@ struct omnia_led { + struct omnia_leds { + struct i2c_client *client; + struct mutex lock; ++ bool has_gamma_correction; + struct omnia_led leds[]; + }; + +@@ -50,30 +64,42 @@ static int omnia_cmd_write_u8(const stru + return i2c_master_send(client, buf, sizeof(buf)); + } + +-static int omnia_cmd_read_u8(const struct i2c_client *client, u8 cmd) ++static int omnia_cmd_read_raw(struct i2c_adapter *adapter, u8 addr, u8 cmd, ++ void *reply, size_t len) + { + struct i2c_msg msgs[2]; +- u8 reply; + int ret; + +- msgs[0].addr = client->addr; ++ msgs[0].addr = addr; + msgs[0].flags = 0; + msgs[0].len = 1; + msgs[0].buf = &cmd; +- msgs[1].addr = client->addr; ++ msgs[1].addr = addr; + msgs[1].flags = I2C_M_RD; +- msgs[1].len = 1; +- msgs[1].buf = &reply; ++ msgs[1].len = len; ++ msgs[1].buf = reply; + +- ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs)); ++ ret = i2c_transfer(adapter, msgs, ARRAY_SIZE(msgs)); + if (likely(ret == ARRAY_SIZE(msgs))) +- return reply; ++ return len; + else if (ret < 0) + return ret; + else + return -EIO; + } + ++static int omnia_cmd_read_u8(const struct i2c_client *client, u8 cmd) ++{ ++ u8 reply; ++ int ret; ++ ++ ret = omnia_cmd_read_raw(client->adapter, client->addr, cmd, &reply, 1); ++ if (ret < 0) ++ return ret; ++ ++ return reply; ++} ++ + static int omnia_led_send_color_cmd(const struct i2c_client *client, + struct omnia_led *led) + { +@@ -352,12 +378,74 @@ static ssize_t brightness_store(struct d + } + static DEVICE_ATTR_RW(brightness); + ++static ssize_t gamma_correction_show(struct device *dev, ++ struct device_attribute *a, char *buf) ++{ ++ struct i2c_client *client = to_i2c_client(dev); ++ struct omnia_leds *leds = i2c_get_clientdata(client); ++ int ret; ++ ++ if (leds->has_gamma_correction) { ++ ret = omnia_cmd_read_u8(client, CMD_GET_GAMMA_CORRECTION); ++ if (ret < 0) ++ return ret; ++ } else { ++ ret = 0; ++ } ++ ++ return sysfs_emit(buf, "%d\n", !!ret); ++} ++ ++static ssize_t gamma_correction_store(struct device *dev, ++ struct device_attribute *a, ++ const char *buf, size_t count) ++{ ++ struct i2c_client *client = to_i2c_client(dev); ++ struct omnia_leds *leds = i2c_get_clientdata(client); ++ bool val; ++ int ret; ++ ++ if (!leds->has_gamma_correction) ++ return -EOPNOTSUPP; ++ ++ if (kstrtobool(buf, &val) < 0) ++ return -EINVAL; ++ ++ ret = omnia_cmd_write_u8(client, CMD_SET_GAMMA_CORRECTION, val); ++ ++ return ret < 0 ? ret : count; ++} ++static DEVICE_ATTR_RW(gamma_correction); ++ + static struct attribute *omnia_led_controller_attrs[] = { + &dev_attr_brightness.attr, ++ &dev_attr_gamma_correction.attr, + NULL, + }; + ATTRIBUTE_GROUPS(omnia_led_controller); + ++static int omnia_mcu_get_features(const struct i2c_client *client) ++{ ++ u16 reply; ++ int err; ++ ++ err = omnia_cmd_read_raw(client->adapter, OMNIA_MCU_I2C_ADDR, ++ CMD_GET_STATUS_WORD, &reply, sizeof(reply)); ++ if (err < 0) ++ return err; ++ ++ /* Check whether MCU firmware supports the CMD_GET_FEAUTRES command */ ++ if (!(le16_to_cpu(reply) & STS_FEATURES_SUPPORTED)) ++ return 0; ++ ++ err = omnia_cmd_read_raw(client->adapter, OMNIA_MCU_I2C_ADDR, ++ CMD_GET_FEATURES, &reply, sizeof(reply)); ++ if (err < 0) ++ return err; ++ ++ return le16_to_cpu(reply); ++} ++ + static int omnia_leds_probe(struct i2c_client *client, + const struct i2c_device_id *id) + { +@@ -383,6 +471,21 @@ static int omnia_leds_probe(struct i2c_c + leds->client = client; + i2c_set_clientdata(client, leds); + ++ ret = omnia_mcu_get_features(client); ++ if (ret < 0) { ++ dev_err(dev, "Cannot determine MCU supported features: %d\n", ++ ret); ++ return ret; ++ } ++ ++ leds->has_gamma_correction = ret & FEAT_LED_GAMMA_CORRECTION; ++ if (!leds->has_gamma_correction) { ++ dev_info(dev, ++ "Your board's MCU firmware does not support the LED gamma correction feature.\n"); ++ dev_info(dev, ++ "Consider upgrading MCU firmware with the omnia-mcutool utility.\n"); ++ } ++ + mutex_init(&leds->lock); + + ret = devm_led_trigger_register(dev, &omnia_hw_trigger); diff --git a/target/linux/generic/backport-6.1/815-v6.7-5-leds-turris-omnia-Fix-brightness-setting-and-trigger.patch b/target/linux/generic/backport-6.1/815-v6.7-5-leds-turris-omnia-Fix-brightness-setting-and-trigger.patch new file mode 100644 index 000000000..b0cebdcf1 --- /dev/null +++ b/target/linux/generic/backport-6.1/815-v6.7-5-leds-turris-omnia-Fix-brightness-setting-and-trigger.patch @@ -0,0 +1,167 @@ +From ffec49d391c5f0195360912b216aa24dbc9b53c8 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Marek=20Beh=C3=BAn?= +Date: Mon, 16 Oct 2023 16:15:38 +0200 +Subject: [PATCH] leds: turris-omnia: Fix brightness setting and trigger + activating +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +I have improperly refactored commits + 4d5ed2621c24 ("leds: turris-omnia: Make set_brightness() more efficient") +and + aaf38273cf76 ("leds: turris-omnia: Support HW controlled mode via private trigger") +after Lee requested a change in API semantics of the new functions I +introduced in commit + 28350bc0ac77 ("leds: turris-omnia: Do not use SMBUS calls"). + +Before the change, the function omnia_cmd_write_u8() returned 0 on +success, and afterwards it returned a positive value (number of bytes +written). The latter version was applied, but the following commits did +not properly account for this change. + +This results in non-functional LED's .brightness_set_blocking() and +trigger's .activate() methods. + +The main reasoning behind the semantics change was that read/write +methods should return the number of read/written bytes on success. +It was pointed to me [1] that this is not always true (for example the +regmap API does not do so), and since the driver never uses this number +of read/written bytes information, I decided to fix this issue by +changing the functions to the original semantics (return 0 on success). + +[1] https://lore.kernel.org/linux-gpio/ZQnn+Gi0xVlsGCYA@smile.fi.intel.com/ + +Fixes: 28350bc0ac77 ("leds: turris-omnia: Do not use SMBUS calls") +Signed-off-by: Marek Behún +--- + drivers/leds/leds-turris-omnia.c | 37 +++++++++++++++++--------------- + 1 file changed, 20 insertions(+), 17 deletions(-) + +--- a/drivers/leds/leds-turris-omnia.c ++++ b/drivers/leds/leds-turris-omnia.c +@@ -60,8 +60,11 @@ struct omnia_leds { + static int omnia_cmd_write_u8(const struct i2c_client *client, u8 cmd, u8 val) + { + u8 buf[2] = { cmd, val }; ++ int ret; ++ ++ ret = i2c_master_send(client, buf, sizeof(buf)); + +- return i2c_master_send(client, buf, sizeof(buf)); ++ return ret < 0 ? ret : 0; + } + + static int omnia_cmd_read_raw(struct i2c_adapter *adapter, u8 addr, u8 cmd, +@@ -81,7 +84,7 @@ static int omnia_cmd_read_raw(struct i2c + + ret = i2c_transfer(adapter, msgs, ARRAY_SIZE(msgs)); + if (likely(ret == ARRAY_SIZE(msgs))) +- return len; ++ return 0; + else if (ret < 0) + return ret; + else +@@ -91,11 +94,11 @@ static int omnia_cmd_read_raw(struct i2c + static int omnia_cmd_read_u8(const struct i2c_client *client, u8 cmd) + { + u8 reply; +- int ret; ++ int err; + +- ret = omnia_cmd_read_raw(client->adapter, client->addr, cmd, &reply, 1); +- if (ret < 0) +- return ret; ++ err = omnia_cmd_read_raw(client->adapter, client->addr, cmd, &reply, 1); ++ if (err) ++ return err; + + return reply; + } +@@ -236,7 +239,7 @@ static void omnia_hwtrig_deactivate(stru + + mutex_unlock(&leds->lock); + +- if (err < 0) ++ if (err) + dev_err(cdev->dev, "Cannot put LED to software mode: %i\n", + err); + } +@@ -302,7 +305,7 @@ static int omnia_led_register(struct i2c + ret = omnia_cmd_write_u8(client, CMD_LED_MODE, + CMD_LED_MODE_LED(led->reg) | + CMD_LED_MODE_USER); +- if (ret < 0) { ++ if (ret) { + dev_err(dev, "Cannot set LED %pOF to software mode: %i\n", np, + ret); + return ret; +@@ -311,7 +314,7 @@ static int omnia_led_register(struct i2c + /* disable the LED */ + ret = omnia_cmd_write_u8(client, CMD_LED_STATE, + CMD_LED_STATE_LED(led->reg)); +- if (ret < 0) { ++ if (ret) { + dev_err(dev, "Cannot set LED %pOF brightness: %i\n", np, ret); + return ret; + } +@@ -364,7 +367,7 @@ static ssize_t brightness_store(struct d + { + struct i2c_client *client = to_i2c_client(dev); + unsigned long brightness; +- int ret; ++ int err; + + if (kstrtoul(buf, 10, &brightness)) + return -EINVAL; +@@ -372,9 +375,9 @@ static ssize_t brightness_store(struct d + if (brightness > 100) + return -EINVAL; + +- ret = omnia_cmd_write_u8(client, CMD_LED_SET_BRIGHTNESS, brightness); ++ err = omnia_cmd_write_u8(client, CMD_LED_SET_BRIGHTNESS, brightness); + +- return ret < 0 ? ret : count; ++ return err ?: count; + } + static DEVICE_ATTR_RW(brightness); + +@@ -403,7 +406,7 @@ static ssize_t gamma_correction_store(st + struct i2c_client *client = to_i2c_client(dev); + struct omnia_leds *leds = i2c_get_clientdata(client); + bool val; +- int ret; ++ int err; + + if (!leds->has_gamma_correction) + return -EOPNOTSUPP; +@@ -411,9 +414,9 @@ static ssize_t gamma_correction_store(st + if (kstrtobool(buf, &val) < 0) + return -EINVAL; + +- ret = omnia_cmd_write_u8(client, CMD_SET_GAMMA_CORRECTION, val); ++ err = omnia_cmd_write_u8(client, CMD_SET_GAMMA_CORRECTION, val); + +- return ret < 0 ? ret : count; ++ return err ?: count; + } + static DEVICE_ATTR_RW(gamma_correction); + +@@ -431,7 +434,7 @@ static int omnia_mcu_get_features(const + + err = omnia_cmd_read_raw(client->adapter, OMNIA_MCU_I2C_ADDR, + CMD_GET_STATUS_WORD, &reply, sizeof(reply)); +- if (err < 0) ++ if (err) + return err; + + /* Check whether MCU firmware supports the CMD_GET_FEAUTRES command */ +@@ -440,7 +443,7 @@ static int omnia_mcu_get_features(const + + err = omnia_cmd_read_raw(client->adapter, OMNIA_MCU_I2C_ADDR, + CMD_GET_FEATURES, &reply, sizeof(reply)); +- if (err < 0) ++ if (err) + return err; + + return le16_to_cpu(reply); diff --git a/target/linux/generic/backport-6.1/816-v6.7-0001-nvmem-qfprom-Mark-core-clk-as-optional.patch b/target/linux/generic/backport-6.1/816-v6.7-0001-nvmem-qfprom-Mark-core-clk-as-optional.patch new file mode 100644 index 000000000..66d402814 --- /dev/null +++ b/target/linux/generic/backport-6.1/816-v6.7-0001-nvmem-qfprom-Mark-core-clk-as-optional.patch @@ -0,0 +1,37 @@ +From 16724d6ea40a2c9315f5a0d81005dfa4d7a6da24 Mon Sep 17 00:00:00 2001 +From: Luca Weiss +Date: Fri, 20 Oct 2023 11:55:40 +0100 +Subject: [PATCH] nvmem: qfprom: Mark core clk as optional + +On some platforms like sc7280 on non-ChromeOS devices the core clock +cannot be touched by Linux so we cannot provide it. Mark it as optional +as accessing qfprom for reading works without it but we still prohibit +writing if we cannot provide the clock. + +Signed-off-by: Luca Weiss +Reviewed-by: Douglas Anderson +Signed-off-by: Srinivas Kandagatla +Link: https://lore.kernel.org/r/20231020105545.216052-2-srinivas.kandagatla@linaro.org +Signed-off-by: Greg Kroah-Hartman +--- + drivers/nvmem/qfprom.c | 6 +++--- + 1 file changed, 3 insertions(+), 3 deletions(-) + +--- a/drivers/nvmem/qfprom.c ++++ b/drivers/nvmem/qfprom.c +@@ -423,12 +423,12 @@ static int qfprom_probe(struct platform_ + if (IS_ERR(priv->vcc)) + return PTR_ERR(priv->vcc); + +- priv->secclk = devm_clk_get(dev, "core"); ++ priv->secclk = devm_clk_get_optional(dev, "core"); + if (IS_ERR(priv->secclk)) + return dev_err_probe(dev, PTR_ERR(priv->secclk), "Error getting clock\n"); + +- /* Only enable writing if we have SoC data. */ +- if (priv->soc_data) ++ /* Only enable writing if we have SoC data and a valid clock */ ++ if (priv->soc_data && priv->secclk) + econfig.reg_write = qfprom_reg_write; + } + diff --git a/target/linux/generic/backport-6.1/816-v6.7-0002-nvmem-add-explicit-config-option-to-read-old-syntax-.patch b/target/linux/generic/backport-6.1/816-v6.7-0002-nvmem-add-explicit-config-option-to-read-old-syntax-.patch new file mode 100644 index 000000000..be293e6f2 --- /dev/null +++ b/target/linux/generic/backport-6.1/816-v6.7-0002-nvmem-add-explicit-config-option-to-read-old-syntax-.patch @@ -0,0 +1,330 @@ +From 2cc3b37f5b6df8189d55d0e812d9658ce256dfec Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= +Date: Fri, 20 Oct 2023 11:55:41 +0100 +Subject: [PATCH] nvmem: add explicit config option to read old syntax fixed OF + cells +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Binding for fixed NVMEM cells defined directly as NVMEM device subnodes +has been deprecated. It has been replaced by the "fixed-layout" NVMEM +layout binding. + +New syntax is meant to be clearer and should help avoiding imprecise +bindings. + +NVMEM subsystem already supports the new binding. It should be a good +idea to limit support for old syntax to existing drivers that actually +support & use it (we can't break backward compatibility!). That way we +additionally encourage new bindings & drivers to ignore deprecated +binding. + +It wasn't clear (to me) if rtc and w1 code actually uses old syntax +fixed cells. I enabled them to don't risk any breakage. + +Signed-off-by: RafaÅ‚ MiÅ‚ecki +[for meson-{efuse,mx-efuse}.c] +Acked-by: Martin Blumenstingl +[for mtk-efuse.c, nvmem/core.c, nvmem-provider.h] +Reviewed-by: AngeloGioacchino Del Regno +[MT8192, MT8195 Chromebooks] +Tested-by: AngeloGioacchino Del Regno +[for microchip-otpc.c] +Reviewed-by: Claudiu Beznea +[SAMA7G5-EK] +Tested-by: Claudiu Beznea +Acked-by: Jernej Skrabec +Signed-off-by: Srinivas Kandagatla +Link: https://lore.kernel.org/r/20231020105545.216052-3-srinivas.kandagatla@linaro.org +Signed-off-by: Greg Kroah-Hartman +--- + drivers/mtd/mtdcore.c | 2 ++ + drivers/nvmem/apple-efuses.c | 1 + + drivers/nvmem/core.c | 8 +++++--- + drivers/nvmem/imx-ocotp-scu.c | 1 + + drivers/nvmem/imx-ocotp.c | 1 + + drivers/nvmem/meson-efuse.c | 1 + + drivers/nvmem/meson-mx-efuse.c | 1 + + drivers/nvmem/microchip-otpc.c | 1 + + drivers/nvmem/mtk-efuse.c | 1 + + drivers/nvmem/qcom-spmi-sdam.c | 1 + + drivers/nvmem/qfprom.c | 1 + + drivers/nvmem/rave-sp-eeprom.c | 1 + + drivers/nvmem/rockchip-efuse.c | 1 + + drivers/nvmem/sc27xx-efuse.c | 1 + + drivers/nvmem/sec-qfprom.c | 1 + + drivers/nvmem/sprd-efuse.c | 1 + + drivers/nvmem/stm32-romem.c | 1 + + drivers/nvmem/sunplus-ocotp.c | 1 + + drivers/nvmem/sunxi_sid.c | 1 + + drivers/nvmem/uniphier-efuse.c | 1 + + drivers/nvmem/zynqmp_nvmem.c | 1 + + drivers/rtc/nvmem.c | 1 + + drivers/w1/slaves/w1_ds250x.c | 1 + + include/linux/nvmem-provider.h | 2 ++ + 24 files changed, 30 insertions(+), 3 deletions(-) + +--- a/drivers/mtd/mtdcore.c ++++ b/drivers/mtd/mtdcore.c +@@ -523,6 +523,7 @@ static int mtd_nvmem_add(struct mtd_info + config.dev = &mtd->dev; + config.name = dev_name(&mtd->dev); + config.owner = THIS_MODULE; ++ config.add_legacy_fixed_of_cells = of_device_is_compatible(node, "nvmem-cells"); + config.reg_read = mtd_nvmem_reg_read; + config.size = mtd->size; + config.word_size = 1; +@@ -891,6 +892,7 @@ static struct nvmem_device *mtd_otp_nvme + config.name = compatible; + config.id = NVMEM_DEVID_AUTO; + config.owner = THIS_MODULE; ++ config.add_legacy_fixed_of_cells = true; + config.type = NVMEM_TYPE_OTP; + config.root_only = true; + config.ignore_wp = true; +--- a/drivers/nvmem/apple-efuses.c ++++ b/drivers/nvmem/apple-efuses.c +@@ -36,6 +36,7 @@ static int apple_efuses_probe(struct pla + struct resource *res; + struct nvmem_config config = { + .dev = &pdev->dev, ++ .add_legacy_fixed_of_cells = true, + .read_only = true, + .reg_read = apple_efuses_read, + .stride = sizeof(u32), +--- a/drivers/nvmem/core.c ++++ b/drivers/nvmem/core.c +@@ -998,9 +998,11 @@ struct nvmem_device *nvmem_register(cons + if (rval) + goto err_remove_cells; + +- rval = nvmem_add_cells_from_legacy_of(nvmem); +- if (rval) +- goto err_remove_cells; ++ if (config->add_legacy_fixed_of_cells) { ++ rval = nvmem_add_cells_from_legacy_of(nvmem); ++ if (rval) ++ goto err_remove_cells; ++ } + + rval = nvmem_add_cells_from_fixed_layout(nvmem); + if (rval) +--- a/drivers/nvmem/imx-ocotp-scu.c ++++ b/drivers/nvmem/imx-ocotp-scu.c +@@ -220,6 +220,7 @@ static int imx_scu_ocotp_write(void *con + + static struct nvmem_config imx_scu_ocotp_nvmem_config = { + .name = "imx-scu-ocotp", ++ .add_legacy_fixed_of_cells = true, + .read_only = false, + .word_size = 4, + .stride = 1, +--- a/drivers/nvmem/imx-ocotp.c ++++ b/drivers/nvmem/imx-ocotp.c +@@ -616,6 +616,7 @@ static int imx_ocotp_probe(struct platfo + return PTR_ERR(priv->clk); + + priv->params = of_device_get_match_data(&pdev->dev); ++ imx_ocotp_nvmem_config.add_legacy_fixed_of_cells = true; + imx_ocotp_nvmem_config.size = 4 * priv->params->nregs; + imx_ocotp_nvmem_config.dev = dev; + imx_ocotp_nvmem_config.priv = priv; +--- a/drivers/nvmem/meson-efuse.c ++++ b/drivers/nvmem/meson-efuse.c +@@ -74,6 +74,7 @@ static int meson_efuse_probe(struct plat + + econfig->dev = dev; + econfig->name = dev_name(dev); ++ econfig->add_legacy_fixed_of_cells = true; + econfig->stride = 1; + econfig->word_size = 1; + econfig->reg_read = meson_efuse_read; +--- a/drivers/nvmem/meson-mx-efuse.c ++++ b/drivers/nvmem/meson-mx-efuse.c +@@ -211,6 +211,7 @@ static int meson_mx_efuse_probe(struct p + efuse->config.owner = THIS_MODULE; + efuse->config.dev = &pdev->dev; + efuse->config.priv = efuse; ++ efuse->config.add_legacy_fixed_of_cells = true; + efuse->config.stride = drvdata->word_size; + efuse->config.word_size = drvdata->word_size; + efuse->config.size = SZ_512; +--- a/drivers/nvmem/microchip-otpc.c ++++ b/drivers/nvmem/microchip-otpc.c +@@ -261,6 +261,7 @@ static int mchp_otpc_probe(struct platfo + return ret; + + mchp_nvmem_config.dev = otpc->dev; ++ mchp_nvmem_config.add_legacy_fixed_of_cells = true; + mchp_nvmem_config.size = size; + mchp_nvmem_config.priv = otpc; + nvmem = devm_nvmem_register(&pdev->dev, &mchp_nvmem_config); +--- a/drivers/nvmem/mtk-efuse.c ++++ b/drivers/nvmem/mtk-efuse.c +@@ -83,6 +83,7 @@ static int mtk_efuse_probe(struct platfo + return PTR_ERR(priv->base); + + pdata = device_get_match_data(dev); ++ econfig.add_legacy_fixed_of_cells = true; + econfig.stride = 1; + econfig.word_size = 1; + econfig.reg_read = mtk_reg_read; +--- a/drivers/nvmem/qcom-spmi-sdam.c ++++ b/drivers/nvmem/qcom-spmi-sdam.c +@@ -142,6 +142,7 @@ static int sdam_probe(struct platform_de + sdam->sdam_config.name = "spmi_sdam"; + sdam->sdam_config.id = NVMEM_DEVID_AUTO; + sdam->sdam_config.owner = THIS_MODULE; ++ sdam->sdam_config.add_legacy_fixed_of_cells = true; + sdam->sdam_config.stride = 1; + sdam->sdam_config.word_size = 1; + sdam->sdam_config.reg_read = sdam_read; +--- a/drivers/nvmem/qfprom.c ++++ b/drivers/nvmem/qfprom.c +@@ -357,6 +357,7 @@ static int qfprom_probe(struct platform_ + { + struct nvmem_config econfig = { + .name = "qfprom", ++ .add_legacy_fixed_of_cells = true, + .stride = 1, + .word_size = 1, + .id = NVMEM_DEVID_AUTO, +--- a/drivers/nvmem/rave-sp-eeprom.c ++++ b/drivers/nvmem/rave-sp-eeprom.c +@@ -328,6 +328,7 @@ static int rave_sp_eeprom_probe(struct p + of_property_read_string(np, "zii,eeprom-name", &config.name); + config.priv = eeprom; + config.dev = dev; ++ config.add_legacy_fixed_of_cells = true; + config.size = size; + config.reg_read = rave_sp_eeprom_reg_read; + config.reg_write = rave_sp_eeprom_reg_write; +--- a/drivers/nvmem/rockchip-efuse.c ++++ b/drivers/nvmem/rockchip-efuse.c +@@ -205,6 +205,7 @@ static int rockchip_rk3399_efuse_read(vo + + static struct nvmem_config econfig = { + .name = "rockchip-efuse", ++ .add_legacy_fixed_of_cells = true, + .stride = 1, + .word_size = 1, + .read_only = true, +--- a/drivers/nvmem/sc27xx-efuse.c ++++ b/drivers/nvmem/sc27xx-efuse.c +@@ -248,6 +248,7 @@ static int sc27xx_efuse_probe(struct pla + econfig.reg_read = sc27xx_efuse_read; + econfig.priv = efuse; + econfig.dev = &pdev->dev; ++ econfig.add_legacy_fixed_of_cells = true; + nvmem = devm_nvmem_register(&pdev->dev, &econfig); + if (IS_ERR(nvmem)) { + dev_err(&pdev->dev, "failed to register nvmem config\n"); +--- a/drivers/nvmem/sec-qfprom.c ++++ b/drivers/nvmem/sec-qfprom.c +@@ -47,6 +47,7 @@ static int sec_qfprom_probe(struct platf + { + struct nvmem_config econfig = { + .name = "sec-qfprom", ++ .add_legacy_fixed_of_cells = true, + .stride = 1, + .word_size = 1, + .id = NVMEM_DEVID_AUTO, +--- a/drivers/nvmem/sprd-efuse.c ++++ b/drivers/nvmem/sprd-efuse.c +@@ -408,6 +408,7 @@ static int sprd_efuse_probe(struct platf + econfig.read_only = false; + econfig.name = "sprd-efuse"; + econfig.size = efuse->data->blk_nums * SPRD_EFUSE_BLOCK_WIDTH; ++ econfig.add_legacy_fixed_of_cells = true; + econfig.reg_read = sprd_efuse_read; + econfig.reg_write = sprd_efuse_write; + econfig.priv = efuse; +--- a/drivers/nvmem/stm32-romem.c ++++ b/drivers/nvmem/stm32-romem.c +@@ -207,6 +207,7 @@ static int stm32_romem_probe(struct plat + priv->cfg.priv = priv; + priv->cfg.owner = THIS_MODULE; + priv->cfg.type = NVMEM_TYPE_OTP; ++ priv->cfg.add_legacy_fixed_of_cells = true; + + priv->lower = 0; + +--- a/drivers/nvmem/sunplus-ocotp.c ++++ b/drivers/nvmem/sunplus-ocotp.c +@@ -145,6 +145,7 @@ disable_clk: + + static struct nvmem_config sp_ocotp_nvmem_config = { + .name = "sp-ocotp", ++ .add_legacy_fixed_of_cells = true, + .read_only = true, + .word_size = 1, + .size = QAC628_OTP_SIZE, +--- a/drivers/nvmem/sunxi_sid.c ++++ b/drivers/nvmem/sunxi_sid.c +@@ -154,6 +154,7 @@ static int sunxi_sid_probe(struct platfo + nvmem_cfg->dev = dev; + nvmem_cfg->name = "sunxi-sid"; + nvmem_cfg->type = NVMEM_TYPE_OTP; ++ nvmem_cfg->add_legacy_fixed_of_cells = true; + nvmem_cfg->read_only = true; + nvmem_cfg->size = cfg->size; + nvmem_cfg->word_size = 1; +--- a/drivers/nvmem/uniphier-efuse.c ++++ b/drivers/nvmem/uniphier-efuse.c +@@ -52,6 +52,7 @@ static int uniphier_efuse_probe(struct p + econfig.size = resource_size(res); + econfig.priv = priv; + econfig.dev = dev; ++ econfig.add_legacy_fixed_of_cells = true; + nvmem = devm_nvmem_register(dev, &econfig); + + return PTR_ERR_OR_ZERO(nvmem); +--- a/drivers/nvmem/zynqmp_nvmem.c ++++ b/drivers/nvmem/zynqmp_nvmem.c +@@ -58,6 +58,7 @@ static int zynqmp_nvmem_probe(struct pla + + priv->dev = dev; + econfig.dev = dev; ++ econfig.add_legacy_fixed_of_cells = true; + econfig.reg_read = zynqmp_nvmem_read; + econfig.priv = priv; + +--- a/drivers/rtc/nvmem.c ++++ b/drivers/rtc/nvmem.c +@@ -21,6 +21,7 @@ int devm_rtc_nvmem_register(struct rtc_d + + nvmem_config->dev = dev; + nvmem_config->owner = rtc->owner; ++ nvmem_config->add_legacy_fixed_of_cells = true; + nvmem = devm_nvmem_register(dev, nvmem_config); + if (IS_ERR(nvmem)) + dev_err(dev, "failed to register nvmem device for RTC\n"); +--- a/drivers/w1/slaves/w1_ds250x.c ++++ b/drivers/w1/slaves/w1_ds250x.c +@@ -168,6 +168,7 @@ static int w1_eprom_add_slave(struct w1_ + struct nvmem_device *nvmem; + struct nvmem_config nvmem_cfg = { + .dev = &sl->dev, ++ .add_legacy_fixed_of_cells = true, + .reg_read = w1_nvmem_read, + .type = NVMEM_TYPE_OTP, + .read_only = true, +--- a/include/linux/nvmem-provider.h ++++ b/include/linux/nvmem-provider.h +@@ -82,6 +82,7 @@ struct nvmem_cell_info { + * @owner: Pointer to exporter module. Used for refcounting. + * @cells: Optional array of pre-defined NVMEM cells. + * @ncells: Number of elements in cells. ++ * @add_legacy_fixed_of_cells: Read fixed NVMEM cells from old OF syntax. + * @keepout: Optional array of keepout ranges (sorted ascending by start). + * @nkeepout: Number of elements in the keepout array. + * @type: Type of the nvmem storage +@@ -112,6 +113,7 @@ struct nvmem_config { + struct module *owner; + const struct nvmem_cell_info *cells; + int ncells; ++ bool add_legacy_fixed_of_cells; + const struct nvmem_keepout *keepout; + unsigned int nkeepout; + enum nvmem_type type; diff --git a/target/linux/generic/backport-6.1/816-v6.7-0003-nvmem-Use-device_get_match_data.patch b/target/linux/generic/backport-6.1/816-v6.7-0003-nvmem-Use-device_get_match_data.patch new file mode 100644 index 000000000..84c029398 --- /dev/null +++ b/target/linux/generic/backport-6.1/816-v6.7-0003-nvmem-Use-device_get_match_data.patch @@ -0,0 +1,77 @@ +From 0720219f4d34a88a9badb4de70cfad7585687d48 Mon Sep 17 00:00:00 2001 +From: Rob Herring +Date: Fri, 20 Oct 2023 11:55:45 +0100 +Subject: [PATCH] nvmem: Use device_get_match_data() + +Use preferred device_get_match_data() instead of of_match_device() to +get the driver match data. With this, adjust the includes to explicitly +include the correct headers. + +Signed-off-by: Rob Herring +Signed-off-by: Srinivas Kandagatla +Link: https://lore.kernel.org/r/20231020105545.216052-7-srinivas.kandagatla@linaro.org +Signed-off-by: Greg Kroah-Hartman +--- + drivers/nvmem/mxs-ocotp.c | 10 ++++------ + drivers/nvmem/stm32-romem.c | 7 ++++--- + 2 files changed, 8 insertions(+), 9 deletions(-) + +--- a/drivers/nvmem/mxs-ocotp.c ++++ b/drivers/nvmem/mxs-ocotp.c +@@ -13,8 +13,9 @@ + #include + #include + #include +-#include ++#include + #include ++#include + #include + #include + +@@ -140,11 +141,10 @@ static int mxs_ocotp_probe(struct platfo + struct device *dev = &pdev->dev; + const struct mxs_data *data; + struct mxs_ocotp *otp; +- const struct of_device_id *match; + int ret; + +- match = of_match_device(dev->driver->of_match_table, dev); +- if (!match || !match->data) ++ data = device_get_match_data(dev); ++ if (!data) + return -EINVAL; + + otp = devm_kzalloc(dev, sizeof(*otp), GFP_KERNEL); +@@ -169,8 +169,6 @@ static int mxs_ocotp_probe(struct platfo + if (ret) + return ret; + +- data = match->data; +- + ocotp_config.size = data->size; + ocotp_config.priv = otp; + ocotp_config.dev = dev; +--- a/drivers/nvmem/stm32-romem.c ++++ b/drivers/nvmem/stm32-romem.c +@@ -10,7 +10,9 @@ + #include + #include + #include +-#include ++#include ++#include ++#include + #include + + #include "stm32-bsec-optee-ta.h" +@@ -211,8 +213,7 @@ static int stm32_romem_probe(struct plat + + priv->lower = 0; + +- cfg = (const struct stm32_romem_cfg *) +- of_match_device(dev->driver->of_match_table, dev)->data; ++ cfg = device_get_match_data(dev); + if (!cfg) { + priv->cfg.read_only = true; + priv->cfg.size = resource_size(res); diff --git a/target/linux/generic/backport-6.1/816-v6.7-0004-Revert-nvmem-add-new-config-option.patch b/target/linux/generic/backport-6.1/816-v6.7-0004-Revert-nvmem-add-new-config-option.patch new file mode 100644 index 000000000..7d80ad37f --- /dev/null +++ b/target/linux/generic/backport-6.1/816-v6.7-0004-Revert-nvmem-add-new-config-option.patch @@ -0,0 +1,77 @@ +From f4cf4e5db331a5ce69e3f0b21d322cac0f4e4b5d Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= +Date: Mon, 23 Oct 2023 12:27:59 +0200 +Subject: [PATCH] Revert "nvmem: add new config option" +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +This reverts commit 517f14d9cf3533d5ab4fded195ab6f80a92e378f. + +Config option "no_of_node" is no longer needed since adding a more +explicit and targeted option "add_legacy_fixed_of_cells". + +That "no_of_node" config option was needed *earlier* to help mtd's case. + +DT nodes of MTD partitions (that are also NVMEM devices) may contain +subnodes. Those SHOULD NOT be treated as NVMEM fixed cells. + +To prevent NVMEM core code from parsing subnodes a "no_of_node" option +was added (and set to true in mtd) to make for_each_child_of_node() in +NVMEM a no-op. That was a bit hacky because it was messing with +"of_node" pointer to achieve some side-effect. + +With the introduction of "add_legacy_fixed_of_cells" config option +things got more explicit. MTD subsystem simply tells NVMEM when to look +for fixed cells and there is no need to hack "of_node" pointer anymore. + +Signed-off-by: RafaÅ‚ MiÅ‚ecki +Reviewed-by: Miquel Raynal +Signed-off-by: Srinivas Kandagatla +Link: https://lore.kernel.org/r/20231023102759.31529-1-zajec5@gmail.com +Signed-off-by: Greg Kroah-Hartman +--- + drivers/mtd/mtdcore.c | 1 - + drivers/nvmem/core.c | 2 +- + include/linux/nvmem-provider.h | 2 -- + 3 files changed, 1 insertion(+), 4 deletions(-) + +--- a/drivers/mtd/mtdcore.c ++++ b/drivers/mtd/mtdcore.c +@@ -531,7 +531,6 @@ static int mtd_nvmem_add(struct mtd_info + config.read_only = true; + config.root_only = true; + config.ignore_wp = true; +- config.no_of_node = !of_device_is_compatible(node, "nvmem-cells"); + config.priv = mtd; + + mtd->nvmem = nvmem_register(&config); +--- a/drivers/nvmem/core.c ++++ b/drivers/nvmem/core.c +@@ -936,7 +936,7 @@ struct nvmem_device *nvmem_register(cons + nvmem->nkeepout = config->nkeepout; + if (config->of_node) + nvmem->dev.of_node = config->of_node; +- else if (!config->no_of_node) ++ else + nvmem->dev.of_node = config->dev->of_node; + + switch (config->id) { +--- a/include/linux/nvmem-provider.h ++++ b/include/linux/nvmem-provider.h +@@ -89,7 +89,6 @@ struct nvmem_cell_info { + * @read_only: Device is read-only. + * @root_only: Device is accessibly to root only. + * @of_node: If given, this will be used instead of the parent's of_node. +- * @no_of_node: Device should not use the parent's of_node even if it's !NULL. + * @reg_read: Callback to read data. + * @reg_write: Callback to write data. + * @size: Device size. +@@ -122,7 +121,6 @@ struct nvmem_config { + bool ignore_wp; + struct nvmem_layout *layout; + struct device_node *of_node; +- bool no_of_node; + nvmem_reg_read_t reg_read; + nvmem_reg_write_t reg_write; + int size; diff --git a/target/linux/generic/backport-6.1/816-v6.7-0005-nvmem-Do-not-expect-fixed-layouts-to-grab-a-layout-d.patch b/target/linux/generic/backport-6.1/816-v6.7-0005-nvmem-Do-not-expect-fixed-layouts-to-grab-a-layout-d.patch new file mode 100644 index 000000000..bd5ceaabf --- /dev/null +++ b/target/linux/generic/backport-6.1/816-v6.7-0005-nvmem-Do-not-expect-fixed-layouts-to-grab-a-layout-d.patch @@ -0,0 +1,45 @@ +From b7c1e53751cb3990153084f31c41f25fde3b629c Mon Sep 17 00:00:00 2001 +From: Miquel Raynal +Date: Fri, 24 Nov 2023 20:38:14 +0100 +Subject: [PATCH] nvmem: Do not expect fixed layouts to grab a layout driver +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Two series lived in parallel for some time, which led to this situation: +- The nvmem-layout container is used for dynamic layouts +- We now expect fixed layouts to also use the nvmem-layout container but +this does not require any additional driver, the support is built-in the +nvmem core. + +Ensure we don't refuse to probe for wrong reasons. + +Fixes: 27f699e578b1 ("nvmem: core: add support for fixed cells *layout*") +Cc: stable@vger.kernel.org +Reported-by: Luca Ceresoli +Signed-off-by: Miquel Raynal +Tested-by: RafaÅ‚ MiÅ‚ecki +Tested-by: Luca Ceresoli +Reviewed-by: Luca Ceresoli + +Link: https://lore.kernel.org/r/20231124193814.360552-1-miquel.raynal@bootlin.com +Signed-off-by: Greg Kroah-Hartman +--- + drivers/nvmem/core.c | 6 ++++++ + 1 file changed, 6 insertions(+) + +--- a/drivers/nvmem/core.c ++++ b/drivers/nvmem/core.c +@@ -797,6 +797,12 @@ static struct nvmem_layout *nvmem_layout + if (!layout_np) + return NULL; + ++ /* Fixed layouts don't have a matching driver */ ++ if (of_device_is_compatible(layout_np, "fixed-layout")) { ++ of_node_put(layout_np); ++ return NULL; ++ } ++ + /* + * In case the nvmem device was built-in while the layout was built as a + * module, we shall manually request the layout driver loading otherwise diff --git a/target/linux/generic/backport-6.1/816-v6.7-0006-nvmem-brcm_nvram-store-a-copy-of-NVRAM-content.patch b/target/linux/generic/backport-6.1/816-v6.7-0006-nvmem-brcm_nvram-store-a-copy-of-NVRAM-content.patch new file mode 100644 index 000000000..d49a20599 --- /dev/null +++ b/target/linux/generic/backport-6.1/816-v6.7-0006-nvmem-brcm_nvram-store-a-copy-of-NVRAM-content.patch @@ -0,0 +1,261 @@ +From 1e37bf84afacd5ba17b7a13a18ca2bc78aff05c0 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= +Date: Fri, 15 Dec 2023 11:13:58 +0000 +Subject: [PATCH] nvmem: brcm_nvram: store a copy of NVRAM content +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +This driver uses MMIO access for reading NVRAM from a flash device. +Underneath there is a flash controller that reads data and provides +mapping window. + +Using MMIO interface affects controller configuration and may break real +controller driver. It was reported by multiple users of devices with +NVRAM stored on NAND. + +Modify driver to read & cache NVRAM content during init and use that +copy to provide NVMEM data when requested. On NAND flashes due to their +alignment NVRAM partitions can be quite big (1 MiB and more) while +actual NVRAM content stays quite small (usually 16 to 32 KiB). To avoid +allocating so much memory check for actual data length. + +Link: https://lore.kernel.org/linux-mtd/CACna6rwf3_9QVjYcM+847biTX=K0EoWXuXcSMkJO1Vy_5vmVqA@mail.gmail.com/ +Fixes: 3fef9ed0627a ("nvmem: brcm_nvram: new driver exposing Broadcom's NVRAM") +Cc: +Cc: Arınç ÃœNAL +Cc: Florian Fainelli +Cc: Scott Branden +Signed-off-by: RafaÅ‚ MiÅ‚ecki +Acked-by: Arınç ÃœNAL +Signed-off-by: Srinivas Kandagatla +Link: https://lore.kernel.org/r/20231215111358.316727-3-srinivas.kandagatla@linaro.org +Signed-off-by: Greg Kroah-Hartman +--- + drivers/nvmem/brcm_nvram.c | 134 ++++++++++++++++++++++++++----------- + 1 file changed, 94 insertions(+), 40 deletions(-) + +--- a/drivers/nvmem/brcm_nvram.c ++++ b/drivers/nvmem/brcm_nvram.c +@@ -17,9 +17,23 @@ + + #define NVRAM_MAGIC "FLSH" + ++/** ++ * struct brcm_nvram - driver state internal struct ++ * ++ * @dev: NVMEM device pointer ++ * @nvmem_size: Size of the whole space available for NVRAM ++ * @data: NVRAM data copy stored to avoid poking underlaying flash controller ++ * @data_len: NVRAM data size ++ * @padding_byte: Padding value used to fill remaining space ++ * @cells: Array of discovered NVMEM cells ++ * @ncells: Number of elements in cells ++ */ + struct brcm_nvram { + struct device *dev; +- void __iomem *base; ++ size_t nvmem_size; ++ uint8_t *data; ++ size_t data_len; ++ uint8_t padding_byte; + struct nvmem_cell_info *cells; + int ncells; + }; +@@ -36,10 +50,47 @@ static int brcm_nvram_read(void *context + size_t bytes) + { + struct brcm_nvram *priv = context; +- u8 *dst = val; ++ size_t to_copy; ++ ++ if (offset + bytes > priv->data_len) ++ to_copy = max_t(ssize_t, (ssize_t)priv->data_len - offset, 0); ++ else ++ to_copy = bytes; ++ ++ memcpy(val, priv->data + offset, to_copy); ++ ++ memset((uint8_t *)val + to_copy, priv->padding_byte, bytes - to_copy); ++ ++ return 0; ++} ++ ++static int brcm_nvram_copy_data(struct brcm_nvram *priv, struct platform_device *pdev) ++{ ++ struct resource *res; ++ void __iomem *base; ++ ++ base = devm_platform_get_and_ioremap_resource(pdev, 0, &res); ++ if (IS_ERR(base)) ++ return PTR_ERR(base); ++ ++ priv->nvmem_size = resource_size(res); ++ ++ priv->padding_byte = readb(base + priv->nvmem_size - 1); ++ for (priv->data_len = priv->nvmem_size; ++ priv->data_len; ++ priv->data_len--) { ++ if (readb(base + priv->data_len - 1) != priv->padding_byte) ++ break; ++ } ++ WARN(priv->data_len > SZ_128K, "Unexpected (big) NVRAM size: %zu B\n", priv->data_len); + +- while (bytes--) +- *dst++ = readb(priv->base + offset++); ++ priv->data = devm_kzalloc(priv->dev, priv->data_len, GFP_KERNEL); ++ if (!priv->data) ++ return -ENOMEM; ++ ++ memcpy_fromio(priv->data, base, priv->data_len); ++ ++ bcm47xx_nvram_init_from_iomem(base, priv->data_len); + + return 0; + } +@@ -67,8 +118,13 @@ static int brcm_nvram_add_cells(struct b + size_t len) + { + struct device *dev = priv->dev; +- char *var, *value, *eq; ++ char *var, *value; ++ uint8_t tmp; + int idx; ++ int err = 0; ++ ++ tmp = priv->data[len - 1]; ++ priv->data[len - 1] = '\0'; + + priv->ncells = 0; + for (var = data + sizeof(struct brcm_nvram_header); +@@ -78,67 +134,68 @@ static int brcm_nvram_add_cells(struct b + } + + priv->cells = devm_kcalloc(dev, priv->ncells, sizeof(*priv->cells), GFP_KERNEL); +- if (!priv->cells) +- return -ENOMEM; ++ if (!priv->cells) { ++ err = -ENOMEM; ++ goto out; ++ } + + for (var = data + sizeof(struct brcm_nvram_header), idx = 0; + var < (char *)data + len && *var; + var = value + strlen(value) + 1, idx++) { ++ char *eq, *name; ++ + eq = strchr(var, '='); + if (!eq) + break; + *eq = '\0'; ++ name = devm_kstrdup(dev, var, GFP_KERNEL); ++ *eq = '='; ++ if (!name) { ++ err = -ENOMEM; ++ goto out; ++ } + value = eq + 1; + +- priv->cells[idx].name = devm_kstrdup(dev, var, GFP_KERNEL); +- if (!priv->cells[idx].name) +- return -ENOMEM; ++ priv->cells[idx].name = name; + priv->cells[idx].offset = value - (char *)data; + priv->cells[idx].bytes = strlen(value); + priv->cells[idx].np = of_get_child_by_name(dev->of_node, priv->cells[idx].name); +- if (!strcmp(var, "et0macaddr") || +- !strcmp(var, "et1macaddr") || +- !strcmp(var, "et2macaddr")) { ++ if (!strcmp(name, "et0macaddr") || ++ !strcmp(name, "et1macaddr") || ++ !strcmp(name, "et2macaddr")) { + priv->cells[idx].raw_len = strlen(value); + priv->cells[idx].bytes = ETH_ALEN; + priv->cells[idx].read_post_process = brcm_nvram_read_post_process_macaddr; + } + } + +- return 0; ++out: ++ priv->data[len - 1] = tmp; ++ return err; + } + + static int brcm_nvram_parse(struct brcm_nvram *priv) + { ++ struct brcm_nvram_header *header = (struct brcm_nvram_header *)priv->data; + struct device *dev = priv->dev; +- struct brcm_nvram_header header; +- uint8_t *data; + size_t len; + int err; + +- memcpy_fromio(&header, priv->base, sizeof(header)); +- +- if (memcmp(header.magic, NVRAM_MAGIC, 4)) { ++ if (memcmp(header->magic, NVRAM_MAGIC, 4)) { + dev_err(dev, "Invalid NVRAM magic\n"); + return -EINVAL; + } + +- len = le32_to_cpu(header.len); +- +- data = kzalloc(len, GFP_KERNEL); +- if (!data) +- return -ENOMEM; +- +- memcpy_fromio(data, priv->base, len); +- data[len - 1] = '\0'; +- +- err = brcm_nvram_add_cells(priv, data, len); +- if (err) { +- dev_err(dev, "Failed to add cells: %d\n", err); +- return err; ++ len = le32_to_cpu(header->len); ++ if (len > priv->nvmem_size) { ++ dev_err(dev, "NVRAM length (%zd) exceeds mapped size (%zd)\n", len, ++ priv->nvmem_size); ++ return -EINVAL; + } + +- kfree(data); ++ err = brcm_nvram_add_cells(priv, priv->data, len); ++ if (err) ++ dev_err(dev, "Failed to add cells: %d\n", err); + + return 0; + } +@@ -150,7 +207,6 @@ static int brcm_nvram_probe(struct platf + .reg_read = brcm_nvram_read, + }; + struct device *dev = &pdev->dev; +- struct resource *res; + struct brcm_nvram *priv; + int err; + +@@ -159,21 +215,19 @@ static int brcm_nvram_probe(struct platf + return -ENOMEM; + priv->dev = dev; + +- priv->base = devm_platform_get_and_ioremap_resource(pdev, 0, &res); +- if (IS_ERR(priv->base)) +- return PTR_ERR(priv->base); ++ err = brcm_nvram_copy_data(priv, pdev); ++ if (err) ++ return err; + + err = brcm_nvram_parse(priv); + if (err) + return err; + +- bcm47xx_nvram_init_from_iomem(priv->base, resource_size(res)); +- + config.dev = dev; + config.cells = priv->cells; + config.ncells = priv->ncells; + config.priv = priv; +- config.size = resource_size(res); ++ config.size = priv->nvmem_size; + + return PTR_ERR_OR_ZERO(devm_nvmem_register(dev, &config)); + } diff --git a/target/linux/generic/backport-6.1/817-v6.9-0001-dt-bindings-leds-Add-FUNCTION-defines-for-per-band-W.patch b/target/linux/generic/backport-6.1/817-v6.9-0001-dt-bindings-leds-Add-FUNCTION-defines-for-per-band-W.patch new file mode 100644 index 000000000..bf36e19fc --- /dev/null +++ b/target/linux/generic/backport-6.1/817-v6.9-0001-dt-bindings-leds-Add-FUNCTION-defines-for-per-band-W.patch @@ -0,0 +1,34 @@ +From ec18a2a83b8b9f7e39c80105ea148c769c46227b Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= +Date: Wed, 17 Jan 2024 16:17:36 +0100 +Subject: [PATCH] dt-bindings: leds: Add FUNCTION defines for per-band WLANs +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Most wireless routers and access points can operate in multiple bands +simultaneously. Vendors often equip their devices with per-band LEDs. + +Add defines for those very common functions to allow cleaner & clearer +bindings. + +Signed-off-by: RafaÅ‚ MiÅ‚ecki +Acked-by: Rob Herring +Link: https://lore.kernel.org/r/20240117151736.27440-1-zajec5@gmail.com +Signed-off-by: Lee Jones +--- + include/dt-bindings/leds/common.h | 3 +++ + 1 file changed, 3 insertions(+) + +--- a/include/dt-bindings/leds/common.h ++++ b/include/dt-bindings/leds/common.h +@@ -101,6 +101,9 @@ + #define LED_FUNCTION_USB "usb" + #define LED_FUNCTION_WAN "wan" + #define LED_FUNCTION_WLAN "wlan" ++#define LED_FUNCTION_WLAN_2GHZ "wlan-2ghz" ++#define LED_FUNCTION_WLAN_5GHZ "wlan-5ghz" ++#define LED_FUNCTION_WLAN_6GHZ "wlan-6ghz" + #define LED_FUNCTION_WPS "wps" + + #endif /* __DT_BINDINGS_LEDS_H */ diff --git a/target/linux/generic/backport-6.1/817-v6.9-0002-dt-bindings-leds-Add-LED_FUNCTION_WAN_ONLINE-for-Int.patch b/target/linux/generic/backport-6.1/817-v6.9-0002-dt-bindings-leds-Add-LED_FUNCTION_WAN_ONLINE-for-Int.patch new file mode 100644 index 000000000..eabb9bbe9 --- /dev/null +++ b/target/linux/generic/backport-6.1/817-v6.9-0002-dt-bindings-leds-Add-LED_FUNCTION_WAN_ONLINE-for-Int.patch @@ -0,0 +1,35 @@ +From 64e558500d2d04878b8a6d6578850c475171d6ba Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= +Date: Fri, 23 Feb 2024 12:22:23 +0100 +Subject: [PATCH] dt-bindings: leds: Add LED_FUNCTION_WAN_ONLINE for Internet + access +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +It's common for routers to have LED indicating link on the WAN port. + +Some devices however have an extra LED that's meant to be used if WAN +connection is actually "online" (there is Internet access available). + +It was suggested to add #define for such use case. + +Link: https://lore.kernel.org/linux-devicetree/80e92209-5578-44e7-bd4b-603a29053ddf@collabora.com/T/#u +Signed-off-by: RafaÅ‚ MiÅ‚ecki +Reviewed-by: AngeloGioacchino Del Regno +Link: https://lore.kernel.org/r/20240223112223.1368-1-zajec5@gmail.com +Signed-off-by: Lee Jones +--- + include/dt-bindings/leds/common.h | 1 + + 1 file changed, 1 insertion(+) + +--- a/include/dt-bindings/leds/common.h ++++ b/include/dt-bindings/leds/common.h +@@ -100,6 +100,7 @@ + #define LED_FUNCTION_TX "tx" + #define LED_FUNCTION_USB "usb" + #define LED_FUNCTION_WAN "wan" ++#define LED_FUNCTION_WAN_ONLINE "wan-online" + #define LED_FUNCTION_WLAN "wlan" + #define LED_FUNCTION_WLAN_2GHZ "wlan-2ghz" + #define LED_FUNCTION_WLAN_5GHZ "wlan-5ghz" diff --git a/target/linux/generic/backport-6.1/818-v6.8-of-device-Export-of_device_make_bus_id.patch b/target/linux/generic/backport-6.1/818-v6.8-of-device-Export-of_device_make_bus_id.patch new file mode 100644 index 000000000..564fe9822 --- /dev/null +++ b/target/linux/generic/backport-6.1/818-v6.8-of-device-Export-of_device_make_bus_id.patch @@ -0,0 +1,140 @@ +From 7f38b70042fcaa49219045bd1a9a2836e27a58ac Mon Sep 17 00:00:00 2001 +From: Miquel Raynal +Date: Fri, 15 Dec 2023 11:15:27 +0000 +Subject: [PATCH] of: device: Export of_device_make_bus_id() + +This helper is really handy to create unique device names based on their +device tree path, we may need it outside of the OF core (in the NVMEM +subsystem) so let's export it. As this helper has nothing patform +specific, let's move it to of/device.c instead of of/platform.c so we +can add its prototype to of_device.h. + +Signed-off-by: Miquel Raynal +Acked-by: Rob Herring +Signed-off-by: Srinivas Kandagatla +Link: https://lore.kernel.org/r/20231215111536.316972-2-srinivas.kandagatla@linaro.org +Signed-off-by: Greg Kroah-Hartman +--- + drivers/of/device.c | 41 +++++++++++++++++++++++++++++++++++++++ + drivers/of/platform.c | 40 -------------------------------------- + include/linux/of_device.h | 6 ++++++ + 3 files changed, 47 insertions(+), 40 deletions(-) + +--- a/drivers/of/device.c ++++ b/drivers/of/device.c +@@ -395,3 +395,44 @@ int of_device_uevent_modalias(struct dev + return 0; + } + EXPORT_SYMBOL_GPL(of_device_uevent_modalias); ++ ++/** ++ * of_device_make_bus_id - Use the device node data to assign a unique name ++ * @dev: pointer to device structure that is linked to a device tree node ++ * ++ * This routine will first try using the translated bus address to ++ * derive a unique name. If it cannot, then it will prepend names from ++ * parent nodes until a unique name can be derived. ++ */ ++void of_device_make_bus_id(struct device *dev) ++{ ++ struct device_node *node = dev->of_node; ++ const __be32 *reg; ++ u64 addr; ++ u32 mask; ++ ++ /* Construct the name, using parent nodes if necessary to ensure uniqueness */ ++ while (node->parent) { ++ /* ++ * If the address can be translated, then that is as much ++ * uniqueness as we need. Make it the first component and return ++ */ ++ reg = of_get_property(node, "reg", NULL); ++ if (reg && (addr = of_translate_address(node, reg)) != OF_BAD_ADDR) { ++ if (!of_property_read_u32(node, "mask", &mask)) ++ dev_set_name(dev, dev_name(dev) ? "%llx.%x.%pOFn:%s" : "%llx.%x.%pOFn", ++ addr, ffs(mask) - 1, node, dev_name(dev)); ++ ++ else ++ dev_set_name(dev, dev_name(dev) ? "%llx.%pOFn:%s" : "%llx.%pOFn", ++ addr, node, dev_name(dev)); ++ return; ++ } ++ ++ /* format arguments only used if dev_name() resolves to NULL */ ++ dev_set_name(dev, dev_name(dev) ? "%s:%s" : "%s", ++ kbasename(node->full_name), dev_name(dev)); ++ node = node->parent; ++ } ++} ++EXPORT_SYMBOL_GPL(of_device_make_bus_id); +--- a/drivers/of/platform.c ++++ b/drivers/of/platform.c +@@ -64,46 +64,6 @@ EXPORT_SYMBOL(of_find_device_by_node); + */ + + /** +- * of_device_make_bus_id - Use the device node data to assign a unique name +- * @dev: pointer to device structure that is linked to a device tree node +- * +- * This routine will first try using the translated bus address to +- * derive a unique name. If it cannot, then it will prepend names from +- * parent nodes until a unique name can be derived. +- */ +-static void of_device_make_bus_id(struct device *dev) +-{ +- struct device_node *node = dev->of_node; +- const __be32 *reg; +- u64 addr; +- u32 mask; +- +- /* Construct the name, using parent nodes if necessary to ensure uniqueness */ +- while (node->parent) { +- /* +- * If the address can be translated, then that is as much +- * uniqueness as we need. Make it the first component and return +- */ +- reg = of_get_property(node, "reg", NULL); +- if (reg && (addr = of_translate_address(node, reg)) != OF_BAD_ADDR) { +- if (!of_property_read_u32(node, "mask", &mask)) +- dev_set_name(dev, dev_name(dev) ? "%llx.%x.%pOFn:%s" : "%llx.%x.%pOFn", +- addr, ffs(mask) - 1, node, dev_name(dev)); +- +- else +- dev_set_name(dev, dev_name(dev) ? "%llx.%pOFn:%s" : "%llx.%pOFn", +- addr, node, dev_name(dev)); +- return; +- } +- +- /* format arguments only used if dev_name() resolves to NULL */ +- dev_set_name(dev, dev_name(dev) ? "%s:%s" : "%s", +- kbasename(node->full_name), dev_name(dev)); +- node = node->parent; +- } +-} +- +-/** + * of_device_alloc - Allocate and initialize an of_device + * @np: device node to assign to device + * @bus_id: Name to assign to the device. May be null to use default name. +--- a/include/linux/of_device.h ++++ b/include/linux/of_device.h +@@ -56,6 +56,9 @@ static inline int of_dma_configure(struc + { + return of_dma_configure_id(dev, np, force_dma, NULL); + } ++ ++void of_device_make_bus_id(struct device *dev); ++ + #else /* CONFIG_OF */ + + static inline int of_driver_match_device(struct device *dev, +@@ -113,6 +116,9 @@ static inline int of_dma_configure(struc + { + return 0; + } ++ ++static inline void of_device_make_bus_id(struct device *dev) {} ++ + #endif /* CONFIG_OF */ + + #endif /* _LINUX_OF_DEVICE_H */ diff --git a/target/linux/generic/backport-6.1/819-v6.8-0001-nvmem-Move-of_nvmem_layout_get_container-in-another-.patch b/target/linux/generic/backport-6.1/819-v6.8-0001-nvmem-Move-of_nvmem_layout_get_container-in-another-.patch new file mode 100644 index 000000000..2093fac8a --- /dev/null +++ b/target/linux/generic/backport-6.1/819-v6.8-0001-nvmem-Move-of_nvmem_layout_get_container-in-another-.patch @@ -0,0 +1,95 @@ +From 4a1a40233b4a9fc159a5c7a27dc34c5c7bc5be55 Mon Sep 17 00:00:00 2001 +From: Miquel Raynal +Date: Fri, 15 Dec 2023 11:15:28 +0000 +Subject: [PATCH] nvmem: Move of_nvmem_layout_get_container() in another header + +nvmem-consumer.h is included by consumer devices, extracting data from +NVMEM devices whereas nvmem-provider.h is included by devices providing +NVMEM content. + +The only users of of_nvmem_layout_get_container() outside of the core +are layout drivers, so better move its prototype to nvmem-provider.h. + +While we do so, we also move the kdoc associated with the function to +the header rather than the .c file. + +Signed-off-by: Miquel Raynal +Signed-off-by: Srinivas Kandagatla +Link: https://lore.kernel.org/r/20231215111536.316972-3-srinivas.kandagatla@linaro.org +Signed-off-by: Greg Kroah-Hartman +--- + drivers/nvmem/core.c | 8 -------- + include/linux/nvmem-consumer.h | 7 ------- + include/linux/nvmem-provider.h | 21 +++++++++++++++++++++ + 3 files changed, 21 insertions(+), 15 deletions(-) + +--- a/drivers/nvmem/core.c ++++ b/drivers/nvmem/core.c +@@ -848,14 +848,6 @@ static int nvmem_add_cells_from_layout(s + } + + #if IS_ENABLED(CONFIG_OF) +-/** +- * of_nvmem_layout_get_container() - Get OF node to layout container. +- * +- * @nvmem: nvmem device. +- * +- * Return: a node pointer with refcount incremented or NULL if no +- * container exists. Use of_node_put() on it when done. +- */ + struct device_node *of_nvmem_layout_get_container(struct nvmem_device *nvmem) + { + return of_get_child_by_name(nvmem->dev.of_node, "nvmem-layout"); +--- a/include/linux/nvmem-consumer.h ++++ b/include/linux/nvmem-consumer.h +@@ -241,7 +241,6 @@ struct nvmem_cell *of_nvmem_cell_get(str + const char *id); + struct nvmem_device *of_nvmem_device_get(struct device_node *np, + const char *name); +-struct device_node *of_nvmem_layout_get_container(struct nvmem_device *nvmem); + #else + static inline struct nvmem_cell *of_nvmem_cell_get(struct device_node *np, + const char *id) +@@ -254,12 +253,6 @@ static inline struct nvmem_device *of_nv + { + return ERR_PTR(-EOPNOTSUPP); + } +- +-static inline struct device_node * +-of_nvmem_layout_get_container(struct nvmem_device *nvmem) +-{ +- return NULL; +-} + #endif /* CONFIG_NVMEM && CONFIG_OF */ + + #endif /* ifndef _LINUX_NVMEM_CONSUMER_H */ +--- a/include/linux/nvmem-provider.h ++++ b/include/linux/nvmem-provider.h +@@ -244,6 +244,27 @@ nvmem_layout_get_match_data(struct nvmem + + #endif /* CONFIG_NVMEM */ + ++#if IS_ENABLED(CONFIG_NVMEM) && IS_ENABLED(CONFIG_OF) ++ ++/** ++ * of_nvmem_layout_get_container() - Get OF node of layout container ++ * ++ * @nvmem: nvmem device ++ * ++ * Return: a node pointer with refcount incremented or NULL if no ++ * container exists. Use of_node_put() on it when done. ++ */ ++struct device_node *of_nvmem_layout_get_container(struct nvmem_device *nvmem); ++ ++#else /* CONFIG_NVMEM && CONFIG_OF */ ++ ++static inline struct device_node *of_nvmem_layout_get_container(struct nvmem_device *nvmem) ++{ ++ return NULL; ++} ++ ++#endif /* CONFIG_NVMEM && CONFIG_OF */ ++ + #define module_nvmem_layout_driver(__layout_driver) \ + module_driver(__layout_driver, nvmem_layout_register, \ + nvmem_layout_unregister) diff --git a/target/linux/generic/backport-6.1/819-v6.8-0002-nvmem-Create-a-header-for-internal-sharing.patch b/target/linux/generic/backport-6.1/819-v6.8-0002-nvmem-Create-a-header-for-internal-sharing.patch new file mode 100644 index 000000000..e722109f9 --- /dev/null +++ b/target/linux/generic/backport-6.1/819-v6.8-0002-nvmem-Create-a-header-for-internal-sharing.patch @@ -0,0 +1,91 @@ +From ec9c08a1cb8dc5e8e003f95f5f62de41dde235bb Mon Sep 17 00:00:00 2001 +From: Miquel Raynal +Date: Fri, 15 Dec 2023 11:15:29 +0000 +Subject: [PATCH] nvmem: Create a header for internal sharing + +Before adding all the NVMEM layout bus infrastructure to the core, let's +move the main nvmem_device structure in an internal header, only +available to the core. This way all the additional code can be added in +a dedicated file in order to keep the current core file tidy. + +Signed-off-by: Miquel Raynal +Signed-off-by: Srinivas Kandagatla +Link: https://lore.kernel.org/r/20231215111536.316972-4-srinivas.kandagatla@linaro.org +Signed-off-by: Greg Kroah-Hartman +--- + drivers/nvmem/core.c | 24 +----------------------- + drivers/nvmem/internals.h | 35 +++++++++++++++++++++++++++++++++++ + 2 files changed, 36 insertions(+), 23 deletions(-) + create mode 100644 drivers/nvmem/internals.h + +--- a/drivers/nvmem/core.c ++++ b/drivers/nvmem/core.c +@@ -20,29 +20,7 @@ + #include + #include + +-struct nvmem_device { +- struct module *owner; +- struct device dev; +- int stride; +- int word_size; +- int id; +- struct kref refcnt; +- size_t size; +- bool read_only; +- bool root_only; +- int flags; +- enum nvmem_type type; +- struct bin_attribute eeprom; +- struct device *base_dev; +- struct list_head cells; +- const struct nvmem_keepout *keepout; +- unsigned int nkeepout; +- nvmem_reg_read_t reg_read; +- nvmem_reg_write_t reg_write; +- struct gpio_desc *wp_gpio; +- struct nvmem_layout *layout; +- void *priv; +-}; ++#include "internals.h" + + #define to_nvmem_device(d) container_of(d, struct nvmem_device, dev) + +--- /dev/null ++++ b/drivers/nvmem/internals.h +@@ -0,0 +1,35 @@ ++/* SPDX-License-Identifier: GPL-2.0 */ ++ ++#ifndef _LINUX_NVMEM_INTERNALS_H ++#define _LINUX_NVMEM_INTERNALS_H ++ ++#include ++#include ++#include ++ ++struct nvmem_device { ++ struct module *owner; ++ struct device dev; ++ struct list_head node; ++ int stride; ++ int word_size; ++ int id; ++ struct kref refcnt; ++ size_t size; ++ bool read_only; ++ bool root_only; ++ int flags; ++ enum nvmem_type type; ++ struct bin_attribute eeprom; ++ struct device *base_dev; ++ struct list_head cells; ++ const struct nvmem_keepout *keepout; ++ unsigned int nkeepout; ++ nvmem_reg_read_t reg_read; ++ nvmem_reg_write_t reg_write; ++ struct gpio_desc *wp_gpio; ++ struct nvmem_layout *layout; ++ void *priv; ++}; ++ ++#endif /* ifndef _LINUX_NVMEM_INTERNALS_H */ diff --git a/target/linux/generic/backport-6.1/819-v6.8-0003-nvmem-Simplify-the-add_cells-hook.patch b/target/linux/generic/backport-6.1/819-v6.8-0003-nvmem-Simplify-the-add_cells-hook.patch new file mode 100644 index 000000000..db2d8c1b4 --- /dev/null +++ b/target/linux/generic/backport-6.1/819-v6.8-0003-nvmem-Simplify-the-add_cells-hook.patch @@ -0,0 +1,79 @@ +From 1b7c298a4ecbc28cc6ee94005734bff55eb83d22 Mon Sep 17 00:00:00 2001 +From: Miquel Raynal +Date: Fri, 15 Dec 2023 11:15:30 +0000 +Subject: [PATCH] nvmem: Simplify the ->add_cells() hook + +The layout entry is not used and will anyway be made useless by the new +layout bus infrastructure coming next, so drop it. While at it, clarify +the kdoc entry. + +Signed-off-by: Miquel Raynal +Signed-off-by: Srinivas Kandagatla +Link: https://lore.kernel.org/r/20231215111536.316972-5-srinivas.kandagatla@linaro.org +Signed-off-by: Greg Kroah-Hartman +--- + drivers/nvmem/core.c | 2 +- + drivers/nvmem/layouts/onie-tlv.c | 3 +-- + drivers/nvmem/layouts/sl28vpd.c | 3 +-- + include/linux/nvmem-provider.h | 8 +++----- + 4 files changed, 6 insertions(+), 10 deletions(-) + +--- a/drivers/nvmem/core.c ++++ b/drivers/nvmem/core.c +@@ -817,7 +817,7 @@ static int nvmem_add_cells_from_layout(s + int ret; + + if (layout && layout->add_cells) { +- ret = layout->add_cells(&nvmem->dev, nvmem, layout); ++ ret = layout->add_cells(&nvmem->dev, nvmem); + if (ret) + return ret; + } +--- a/drivers/nvmem/layouts/onie-tlv.c ++++ b/drivers/nvmem/layouts/onie-tlv.c +@@ -182,8 +182,7 @@ static bool onie_tlv_crc_is_valid(struct + return true; + } + +-static int onie_tlv_parse_table(struct device *dev, struct nvmem_device *nvmem, +- struct nvmem_layout *layout) ++static int onie_tlv_parse_table(struct device *dev, struct nvmem_device *nvmem) + { + struct onie_tlv_hdr hdr; + size_t table_len, data_len, hdr_len; +--- a/drivers/nvmem/layouts/sl28vpd.c ++++ b/drivers/nvmem/layouts/sl28vpd.c +@@ -80,8 +80,7 @@ static int sl28vpd_v1_check_crc(struct d + return 0; + } + +-static int sl28vpd_add_cells(struct device *dev, struct nvmem_device *nvmem, +- struct nvmem_layout *layout) ++static int sl28vpd_add_cells(struct device *dev, struct nvmem_device *nvmem) + { + const struct nvmem_cell_info *pinfo; + struct nvmem_cell_info info = {0}; +--- a/include/linux/nvmem-provider.h ++++ b/include/linux/nvmem-provider.h +@@ -156,9 +156,8 @@ struct nvmem_cell_table { + * + * @name: Layout name. + * @of_match_table: Open firmware match table. +- * @add_cells: Will be called if a nvmem device is found which +- * has this layout. The function will add layout +- * specific cells with nvmem_add_one_cell(). ++ * @add_cells: Called to populate the layout using ++ * nvmem_add_one_cell(). + * @fixup_cell_info: Will be called before a cell is added. Can be + * used to modify the nvmem_cell_info. + * @owner: Pointer to struct module. +@@ -172,8 +171,7 @@ struct nvmem_cell_table { + struct nvmem_layout { + const char *name; + const struct of_device_id *of_match_table; +- int (*add_cells)(struct device *dev, struct nvmem_device *nvmem, +- struct nvmem_layout *layout); ++ int (*add_cells)(struct device *dev, struct nvmem_device *nvmem); + void (*fixup_cell_info)(struct nvmem_device *nvmem, + struct nvmem_layout *layout, + struct nvmem_cell_info *cell); diff --git a/target/linux/generic/backport-6.1/819-v6.8-0004-nvmem-Move-and-rename-fixup_cell_info.patch b/target/linux/generic/backport-6.1/819-v6.8-0004-nvmem-Move-and-rename-fixup_cell_info.patch new file mode 100644 index 000000000..65aa37f83 --- /dev/null +++ b/target/linux/generic/backport-6.1/819-v6.8-0004-nvmem-Move-and-rename-fixup_cell_info.patch @@ -0,0 +1,169 @@ +From 1172460e716784ac7e1049a537bdca8edbf97360 Mon Sep 17 00:00:00 2001 +From: Miquel Raynal +Date: Fri, 15 Dec 2023 11:15:31 +0000 +Subject: [PATCH] nvmem: Move and rename ->fixup_cell_info() + +This hook is meant to be used by any provider and instantiating a layout +just for this is useless. Let's instead move this hook to the nvmem +device and add it to the config structure to be easily shared by the +providers. + +While at moving this hook, rename it ->fixup_dt_cell_info() to clarify +its main intended purpose. + +Signed-off-by: Miquel Raynal +Signed-off-by: Srinivas Kandagatla +Link: https://lore.kernel.org/r/20231215111536.316972-6-srinivas.kandagatla@linaro.org +Signed-off-by: Greg Kroah-Hartman +--- + drivers/nvmem/core.c | 6 +++--- + drivers/nvmem/imx-ocotp.c | 11 +++-------- + drivers/nvmem/internals.h | 2 ++ + drivers/nvmem/mtk-efuse.c | 11 +++-------- + include/linux/nvmem-provider.h | 9 ++++----- + 5 files changed, 15 insertions(+), 24 deletions(-) + +--- a/drivers/nvmem/core.c ++++ b/drivers/nvmem/core.c +@@ -676,7 +676,6 @@ static int nvmem_validate_keepouts(struc + + static int nvmem_add_cells_from_dt(struct nvmem_device *nvmem, struct device_node *np) + { +- struct nvmem_layout *layout = nvmem->layout; + struct device *dev = &nvmem->dev; + struct device_node *child; + const __be32 *addr; +@@ -706,8 +705,8 @@ static int nvmem_add_cells_from_dt(struc + + info.np = of_node_get(child); + +- if (layout && layout->fixup_cell_info) +- layout->fixup_cell_info(nvmem, layout, &info); ++ if (nvmem->fixup_dt_cell_info) ++ nvmem->fixup_dt_cell_info(nvmem, &info); + + ret = nvmem_add_one_cell(nvmem, &info); + kfree(info.name); +@@ -896,6 +895,7 @@ struct nvmem_device *nvmem_register(cons + + kref_init(&nvmem->refcnt); + INIT_LIST_HEAD(&nvmem->cells); ++ nvmem->fixup_dt_cell_info = config->fixup_dt_cell_info; + + nvmem->owner = config->owner; + if (!nvmem->owner && config->dev->driver) +--- a/drivers/nvmem/imx-ocotp.c ++++ b/drivers/nvmem/imx-ocotp.c +@@ -584,17 +584,12 @@ static const struct of_device_id imx_oco + }; + MODULE_DEVICE_TABLE(of, imx_ocotp_dt_ids); + +-static void imx_ocotp_fixup_cell_info(struct nvmem_device *nvmem, +- struct nvmem_layout *layout, +- struct nvmem_cell_info *cell) ++static void imx_ocotp_fixup_dt_cell_info(struct nvmem_device *nvmem, ++ struct nvmem_cell_info *cell) + { + cell->read_post_process = imx_ocotp_cell_pp; + } + +-static struct nvmem_layout imx_ocotp_layout = { +- .fixup_cell_info = imx_ocotp_fixup_cell_info, +-}; +- + static int imx_ocotp_probe(struct platform_device *pdev) + { + struct device *dev = &pdev->dev; +@@ -620,7 +615,7 @@ static int imx_ocotp_probe(struct platfo + imx_ocotp_nvmem_config.size = 4 * priv->params->nregs; + imx_ocotp_nvmem_config.dev = dev; + imx_ocotp_nvmem_config.priv = priv; +- imx_ocotp_nvmem_config.layout = &imx_ocotp_layout; ++ imx_ocotp_nvmem_config.fixup_dt_cell_info = &imx_ocotp_fixup_dt_cell_info; + + priv->config = &imx_ocotp_nvmem_config; + +--- a/drivers/nvmem/internals.h ++++ b/drivers/nvmem/internals.h +@@ -23,6 +23,8 @@ struct nvmem_device { + struct bin_attribute eeprom; + struct device *base_dev; + struct list_head cells; ++ void (*fixup_dt_cell_info)(struct nvmem_device *nvmem, ++ struct nvmem_cell_info *cell); + const struct nvmem_keepout *keepout; + unsigned int nkeepout; + nvmem_reg_read_t reg_read; +--- a/drivers/nvmem/mtk-efuse.c ++++ b/drivers/nvmem/mtk-efuse.c +@@ -45,9 +45,8 @@ static int mtk_efuse_gpu_speedbin_pp(voi + return 0; + } + +-static void mtk_efuse_fixup_cell_info(struct nvmem_device *nvmem, +- struct nvmem_layout *layout, +- struct nvmem_cell_info *cell) ++static void mtk_efuse_fixup_dt_cell_info(struct nvmem_device *nvmem, ++ struct nvmem_cell_info *cell) + { + size_t sz = strlen(cell->name); + +@@ -61,10 +60,6 @@ static void mtk_efuse_fixup_cell_info(st + cell->read_post_process = mtk_efuse_gpu_speedbin_pp; + } + +-static struct nvmem_layout mtk_efuse_layout = { +- .fixup_cell_info = mtk_efuse_fixup_cell_info, +-}; +- + static int mtk_efuse_probe(struct platform_device *pdev) + { + struct device *dev = &pdev->dev; +@@ -91,7 +86,7 @@ static int mtk_efuse_probe(struct platfo + econfig.priv = priv; + econfig.dev = dev; + if (pdata->uses_post_processing) +- econfig.layout = &mtk_efuse_layout; ++ econfig.fixup_dt_cell_info = &mtk_efuse_fixup_dt_cell_info; + nvmem = devm_nvmem_register(dev, &econfig); + + return PTR_ERR_OR_ZERO(nvmem); +--- a/include/linux/nvmem-provider.h ++++ b/include/linux/nvmem-provider.h +@@ -83,6 +83,8 @@ struct nvmem_cell_info { + * @cells: Optional array of pre-defined NVMEM cells. + * @ncells: Number of elements in cells. + * @add_legacy_fixed_of_cells: Read fixed NVMEM cells from old OF syntax. ++ * @fixup_dt_cell_info: Will be called before a cell is added. Can be ++ * used to modify the nvmem_cell_info. + * @keepout: Optional array of keepout ranges (sorted ascending by start). + * @nkeepout: Number of elements in the keepout array. + * @type: Type of the nvmem storage +@@ -113,6 +115,8 @@ struct nvmem_config { + const struct nvmem_cell_info *cells; + int ncells; + bool add_legacy_fixed_of_cells; ++ void (*fixup_dt_cell_info)(struct nvmem_device *nvmem, ++ struct nvmem_cell_info *cell); + const struct nvmem_keepout *keepout; + unsigned int nkeepout; + enum nvmem_type type; +@@ -158,8 +162,6 @@ struct nvmem_cell_table { + * @of_match_table: Open firmware match table. + * @add_cells: Called to populate the layout using + * nvmem_add_one_cell(). +- * @fixup_cell_info: Will be called before a cell is added. Can be +- * used to modify the nvmem_cell_info. + * @owner: Pointer to struct module. + * @node: List node. + * +@@ -172,9 +174,6 @@ struct nvmem_layout { + const char *name; + const struct of_device_id *of_match_table; + int (*add_cells)(struct device *dev, struct nvmem_device *nvmem); +- void (*fixup_cell_info)(struct nvmem_device *nvmem, +- struct nvmem_layout *layout, +- struct nvmem_cell_info *cell); + + /* private */ + struct module *owner; diff --git a/target/linux/generic/backport-6.1/819-v6.8-0005-nvmem-core-Rework-layouts-to-become-regular-devices.patch b/target/linux/generic/backport-6.1/819-v6.8-0005-nvmem-core-Rework-layouts-to-become-regular-devices.patch new file mode 100644 index 000000000..188133234 --- /dev/null +++ b/target/linux/generic/backport-6.1/819-v6.8-0005-nvmem-core-Rework-layouts-to-become-regular-devices.patch @@ -0,0 +1,763 @@ +From fc29fd821d9ac2ae3d32a722fac39ce874efb883 Mon Sep 17 00:00:00 2001 +From: Miquel Raynal +Date: Fri, 15 Dec 2023 11:15:32 +0000 +Subject: [PATCH] nvmem: core: Rework layouts to become regular devices +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Current layout support was initially written without modules support in +mind. When the requirement for module support rose, the existing base +was improved to adopt modularization support, but kind of a design flaw +was introduced. With the existing implementation, when a storage device +registers into NVMEM, the core tries to hook a layout (if any) and +populates its cells immediately. This means, if the hardware description +expects a layout to be hooked up, but no driver was provided for that, +the storage medium will fail to probe and try later from +scratch. Even if we consider that the hardware description shall be +correct, we could still probe the storage device (especially if it +contains the rootfs). + +One way to overcome this situation is to consider the layouts as +devices, and leverage the native notifier mechanism. When a new NVMEM +device is registered, we can populate its nvmem-layout child, if any, +and wait for the matching to be done in order to get the cells (the +waiting can be easily done with the NVMEM notifiers). If the layout +driver is compiled as a module, it should automatically be loaded. This +way, there is no strong order to enforce, any NVMEM device creation +or NVMEM layout driver insertion will be observed as a new event which +may lead to the creation of additional cells, without disturbing the +probes with costly (and sometimes endless) deferrals. + +In order to achieve that goal we create a new bus for the nvmem-layouts +with minimal logic to match nvmem-layout devices with nvmem-layout +drivers. All this infrastructure code is created in the layouts.c file. + +Signed-off-by: Miquel Raynal +Tested-by: RafaÅ‚ MiÅ‚ecki +Signed-off-by: Srinivas Kandagatla +Link: https://lore.kernel.org/r/20231215111536.316972-7-srinivas.kandagatla@linaro.org +Signed-off-by: Greg Kroah-Hartman +--- + drivers/nvmem/Kconfig | 1 + + drivers/nvmem/Makefile | 2 + + drivers/nvmem/core.c | 170 ++++++++++---------------- + drivers/nvmem/internals.h | 21 ++++ + drivers/nvmem/layouts.c | 201 +++++++++++++++++++++++++++++++ + drivers/nvmem/layouts/Kconfig | 8 ++ + drivers/nvmem/layouts/onie-tlv.c | 24 +++- + drivers/nvmem/layouts/sl28vpd.c | 24 +++- + include/linux/nvmem-provider.h | 38 +++--- + 9 files changed, 354 insertions(+), 135 deletions(-) + create mode 100644 drivers/nvmem/layouts.c + +--- a/drivers/nvmem/Kconfig ++++ b/drivers/nvmem/Kconfig +@@ -1,6 +1,7 @@ + # SPDX-License-Identifier: GPL-2.0-only + menuconfig NVMEM + bool "NVMEM Support" ++ imply NVMEM_LAYOUTS + help + Support for NVMEM(Non Volatile Memory) devices like EEPROM, EFUSES... + +--- a/drivers/nvmem/Makefile ++++ b/drivers/nvmem/Makefile +@@ -5,6 +5,8 @@ + + obj-$(CONFIG_NVMEM) += nvmem_core.o + nvmem_core-y := core.o ++obj-$(CONFIG_NVMEM_LAYOUTS) += nvmem_layouts.o ++nvmem_layouts-y := layouts.o + obj-y += layouts/ + + # Devices +--- a/drivers/nvmem/core.c ++++ b/drivers/nvmem/core.c +@@ -56,9 +56,6 @@ static LIST_HEAD(nvmem_lookup_list); + + static BLOCKING_NOTIFIER_HEAD(nvmem_notifier); + +-static DEFINE_SPINLOCK(nvmem_layout_lock); +-static LIST_HEAD(nvmem_layouts); +- + static int __nvmem_reg_read(struct nvmem_device *nvmem, unsigned int offset, + void *val, size_t bytes) + { +@@ -741,97 +738,22 @@ static int nvmem_add_cells_from_fixed_la + return err; + } + +-int __nvmem_layout_register(struct nvmem_layout *layout, struct module *owner) ++int nvmem_layout_register(struct nvmem_layout *layout) + { +- layout->owner = owner; +- +- spin_lock(&nvmem_layout_lock); +- list_add(&layout->node, &nvmem_layouts); +- spin_unlock(&nvmem_layout_lock); +- +- blocking_notifier_call_chain(&nvmem_notifier, NVMEM_LAYOUT_ADD, layout); ++ if (!layout->add_cells) ++ return -EINVAL; + +- return 0; ++ /* Populate the cells */ ++ return layout->add_cells(&layout->nvmem->dev, layout->nvmem); + } +-EXPORT_SYMBOL_GPL(__nvmem_layout_register); ++EXPORT_SYMBOL_GPL(nvmem_layout_register); + + void nvmem_layout_unregister(struct nvmem_layout *layout) + { +- blocking_notifier_call_chain(&nvmem_notifier, NVMEM_LAYOUT_REMOVE, layout); +- +- spin_lock(&nvmem_layout_lock); +- list_del(&layout->node); +- spin_unlock(&nvmem_layout_lock); ++ /* Keep the API even with an empty stub in case we need it later */ + } + EXPORT_SYMBOL_GPL(nvmem_layout_unregister); + +-static struct nvmem_layout *nvmem_layout_get(struct nvmem_device *nvmem) +-{ +- struct device_node *layout_np; +- struct nvmem_layout *l, *layout = ERR_PTR(-EPROBE_DEFER); +- +- layout_np = of_nvmem_layout_get_container(nvmem); +- if (!layout_np) +- return NULL; +- +- /* Fixed layouts don't have a matching driver */ +- if (of_device_is_compatible(layout_np, "fixed-layout")) { +- of_node_put(layout_np); +- return NULL; +- } +- +- /* +- * In case the nvmem device was built-in while the layout was built as a +- * module, we shall manually request the layout driver loading otherwise +- * we'll never have any match. +- */ +- of_request_module(layout_np); +- +- spin_lock(&nvmem_layout_lock); +- +- list_for_each_entry(l, &nvmem_layouts, node) { +- if (of_match_node(l->of_match_table, layout_np)) { +- if (try_module_get(l->owner)) +- layout = l; +- +- break; +- } +- } +- +- spin_unlock(&nvmem_layout_lock); +- of_node_put(layout_np); +- +- return layout; +-} +- +-static void nvmem_layout_put(struct nvmem_layout *layout) +-{ +- if (layout) +- module_put(layout->owner); +-} +- +-static int nvmem_add_cells_from_layout(struct nvmem_device *nvmem) +-{ +- struct nvmem_layout *layout = nvmem->layout; +- int ret; +- +- if (layout && layout->add_cells) { +- ret = layout->add_cells(&nvmem->dev, nvmem); +- if (ret) +- return ret; +- } +- +- return 0; +-} +- +-#if IS_ENABLED(CONFIG_OF) +-struct device_node *of_nvmem_layout_get_container(struct nvmem_device *nvmem) +-{ +- return of_get_child_by_name(nvmem->dev.of_node, "nvmem-layout"); +-} +-EXPORT_SYMBOL_GPL(of_nvmem_layout_get_container); +-#endif +- + const void *nvmem_layout_get_match_data(struct nvmem_device *nvmem, + struct nvmem_layout *layout) + { +@@ -839,7 +761,7 @@ const void *nvmem_layout_get_match_data( + const struct of_device_id *match; + + layout_np = of_nvmem_layout_get_container(nvmem); +- match = of_match_node(layout->of_match_table, layout_np); ++ match = of_match_node(layout->dev.driver->of_match_table, layout_np); + + return match ? match->data : NULL; + } +@@ -951,19 +873,6 @@ struct nvmem_device *nvmem_register(cons + goto err_put_device; + } + +- /* +- * If the driver supplied a layout by config->layout, the module +- * pointer will be NULL and nvmem_layout_put() will be a noop. +- */ +- nvmem->layout = config->layout ?: nvmem_layout_get(nvmem); +- if (IS_ERR(nvmem->layout)) { +- rval = PTR_ERR(nvmem->layout); +- nvmem->layout = NULL; +- +- if (rval == -EPROBE_DEFER) +- goto err_teardown_compat; +- } +- + if (config->cells) { + rval = nvmem_add_cells(nvmem, config->cells, config->ncells); + if (rval) +@@ -984,24 +893,24 @@ struct nvmem_device *nvmem_register(cons + if (rval) + goto err_remove_cells; + +- rval = nvmem_add_cells_from_layout(nvmem); +- if (rval) +- goto err_remove_cells; +- + dev_dbg(&nvmem->dev, "Registering nvmem device %s\n", config->name); + + rval = device_add(&nvmem->dev); + if (rval) + goto err_remove_cells; + ++ rval = nvmem_populate_layout(nvmem); ++ if (rval) ++ goto err_remove_dev; ++ + blocking_notifier_call_chain(&nvmem_notifier, NVMEM_ADD, nvmem); + + return nvmem; + ++err_remove_dev: ++ device_del(&nvmem->dev); + err_remove_cells: + nvmem_device_remove_all_cells(nvmem); +- nvmem_layout_put(nvmem->layout); +-err_teardown_compat: + if (config->compat) + nvmem_sysfs_remove_compat(nvmem, config); + err_put_device: +@@ -1023,7 +932,7 @@ static void nvmem_device_release(struct + device_remove_bin_file(nvmem->base_dev, &nvmem->eeprom); + + nvmem_device_remove_all_cells(nvmem); +- nvmem_layout_put(nvmem->layout); ++ nvmem_destroy_layout(nvmem); + device_unregister(&nvmem->dev); + } + +@@ -1325,6 +1234,12 @@ nvmem_cell_get_from_lookup(struct device + return cell; + } + ++static void nvmem_layout_module_put(struct nvmem_device *nvmem) ++{ ++ if (nvmem->layout && nvmem->layout->dev.driver) ++ module_put(nvmem->layout->dev.driver->owner); ++} ++ + #if IS_ENABLED(CONFIG_OF) + static struct nvmem_cell_entry * + nvmem_find_cell_entry_by_node(struct nvmem_device *nvmem, struct device_node *np) +@@ -1343,6 +1258,18 @@ nvmem_find_cell_entry_by_node(struct nvm + return cell; + } + ++static int nvmem_layout_module_get_optional(struct nvmem_device *nvmem) ++{ ++ if (!nvmem->layout) ++ return 0; ++ ++ if (!nvmem->layout->dev.driver || ++ !try_module_get(nvmem->layout->dev.driver->owner)) ++ return -EPROBE_DEFER; ++ ++ return 0; ++} ++ + /** + * of_nvmem_cell_get() - Get a nvmem cell from given device node and cell id + * +@@ -1405,16 +1332,29 @@ struct nvmem_cell *of_nvmem_cell_get(str + return ERR_CAST(nvmem); + } + ++ ret = nvmem_layout_module_get_optional(nvmem); ++ if (ret) { ++ of_node_put(cell_np); ++ __nvmem_device_put(nvmem); ++ return ERR_PTR(ret); ++ } ++ + cell_entry = nvmem_find_cell_entry_by_node(nvmem, cell_np); + of_node_put(cell_np); + if (!cell_entry) { + __nvmem_device_put(nvmem); +- return ERR_PTR(-ENOENT); ++ nvmem_layout_module_put(nvmem); ++ if (nvmem->layout) ++ return ERR_PTR(-EPROBE_DEFER); ++ else ++ return ERR_PTR(-ENOENT); + } + + cell = nvmem_create_cell(cell_entry, id, cell_index); +- if (IS_ERR(cell)) ++ if (IS_ERR(cell)) { + __nvmem_device_put(nvmem); ++ nvmem_layout_module_put(nvmem); ++ } + + return cell; + } +@@ -1528,6 +1468,7 @@ void nvmem_cell_put(struct nvmem_cell *c + + kfree(cell); + __nvmem_device_put(nvmem); ++ nvmem_layout_module_put(nvmem); + } + EXPORT_SYMBOL_GPL(nvmem_cell_put); + +@@ -2105,11 +2046,22 @@ EXPORT_SYMBOL_GPL(nvmem_dev_name); + + static int __init nvmem_init(void) + { +- return bus_register(&nvmem_bus_type); ++ int ret; ++ ++ ret = bus_register(&nvmem_bus_type); ++ if (ret) ++ return ret; ++ ++ ret = nvmem_layout_bus_register(); ++ if (ret) ++ bus_unregister(&nvmem_bus_type); ++ ++ return ret; + } + + static void __exit nvmem_exit(void) + { ++ nvmem_layout_bus_unregister(); + bus_unregister(&nvmem_bus_type); + } + +--- a/drivers/nvmem/internals.h ++++ b/drivers/nvmem/internals.h +@@ -34,4 +34,25 @@ struct nvmem_device { + void *priv; + }; + ++#if IS_ENABLED(CONFIG_OF) ++int nvmem_layout_bus_register(void); ++void nvmem_layout_bus_unregister(void); ++int nvmem_populate_layout(struct nvmem_device *nvmem); ++void nvmem_destroy_layout(struct nvmem_device *nvmem); ++#else /* CONFIG_OF */ ++static inline int nvmem_layout_bus_register(void) ++{ ++ return 0; ++} ++ ++static inline void nvmem_layout_bus_unregister(void) {} ++ ++static inline int nvmem_populate_layout(struct nvmem_device *nvmem) ++{ ++ return 0; ++} ++ ++static inline void nvmem_destroy_layout(struct nvmem_device *nvmem) { } ++#endif /* CONFIG_OF */ ++ + #endif /* ifndef _LINUX_NVMEM_INTERNALS_H */ +--- /dev/null ++++ b/drivers/nvmem/layouts.c +@@ -0,0 +1,201 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * NVMEM layout bus handling ++ * ++ * Copyright (C) 2023 Bootlin ++ * Author: Miquel Raynal ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "internals.h" ++ ++#define to_nvmem_layout_driver(drv) \ ++ (container_of((drv), struct nvmem_layout_driver, driver)) ++#define to_nvmem_layout_device(_dev) \ ++ container_of((_dev), struct nvmem_layout, dev) ++ ++static int nvmem_layout_bus_match(struct device *dev, struct device_driver *drv) ++{ ++ return of_driver_match_device(dev, drv); ++} ++ ++static int nvmem_layout_bus_probe(struct device *dev) ++{ ++ struct nvmem_layout_driver *drv = to_nvmem_layout_driver(dev->driver); ++ struct nvmem_layout *layout = to_nvmem_layout_device(dev); ++ ++ if (!drv->probe || !drv->remove) ++ return -EINVAL; ++ ++ return drv->probe(layout); ++} ++ ++static void nvmem_layout_bus_remove(struct device *dev) ++{ ++ struct nvmem_layout_driver *drv = to_nvmem_layout_driver(dev->driver); ++ struct nvmem_layout *layout = to_nvmem_layout_device(dev); ++ ++ return drv->remove(layout); ++} ++ ++static struct bus_type nvmem_layout_bus_type = { ++ .name = "nvmem-layout", ++ .match = nvmem_layout_bus_match, ++ .probe = nvmem_layout_bus_probe, ++ .remove = nvmem_layout_bus_remove, ++}; ++ ++int nvmem_layout_driver_register(struct nvmem_layout_driver *drv) ++{ ++ drv->driver.bus = &nvmem_layout_bus_type; ++ ++ return driver_register(&drv->driver); ++} ++EXPORT_SYMBOL_GPL(nvmem_layout_driver_register); ++ ++void nvmem_layout_driver_unregister(struct nvmem_layout_driver *drv) ++{ ++ driver_unregister(&drv->driver); ++} ++EXPORT_SYMBOL_GPL(nvmem_layout_driver_unregister); ++ ++static void nvmem_layout_release_device(struct device *dev) ++{ ++ struct nvmem_layout *layout = to_nvmem_layout_device(dev); ++ ++ of_node_put(layout->dev.of_node); ++ kfree(layout); ++} ++ ++static int nvmem_layout_create_device(struct nvmem_device *nvmem, ++ struct device_node *np) ++{ ++ struct nvmem_layout *layout; ++ struct device *dev; ++ int ret; ++ ++ layout = kzalloc(sizeof(*layout), GFP_KERNEL); ++ if (!layout) ++ return -ENOMEM; ++ ++ /* Create a bidirectional link */ ++ layout->nvmem = nvmem; ++ nvmem->layout = layout; ++ ++ /* Device model registration */ ++ dev = &layout->dev; ++ device_initialize(dev); ++ dev->parent = &nvmem->dev; ++ dev->bus = &nvmem_layout_bus_type; ++ dev->release = nvmem_layout_release_device; ++ dev->coherent_dma_mask = DMA_BIT_MASK(32); ++ dev->dma_mask = &dev->coherent_dma_mask; ++ device_set_node(dev, of_fwnode_handle(of_node_get(np))); ++ of_device_make_bus_id(dev); ++ of_msi_configure(dev, dev->of_node); ++ ++ ret = device_add(dev); ++ if (ret) { ++ put_device(dev); ++ return ret; ++ } ++ ++ return 0; ++} ++ ++static const struct of_device_id of_nvmem_layout_skip_table[] = { ++ { .compatible = "fixed-layout", }, ++ {} ++}; ++ ++static int nvmem_layout_bus_populate(struct nvmem_device *nvmem, ++ struct device_node *layout_dn) ++{ ++ int ret; ++ ++ /* Make sure it has a compatible property */ ++ if (!of_get_property(layout_dn, "compatible", NULL)) { ++ pr_debug("%s() - skipping %pOF, no compatible prop\n", ++ __func__, layout_dn); ++ return 0; ++ } ++ ++ /* Fixed layouts are parsed manually somewhere else for now */ ++ if (of_match_node(of_nvmem_layout_skip_table, layout_dn)) { ++ pr_debug("%s() - skipping %pOF node\n", __func__, layout_dn); ++ return 0; ++ } ++ ++ if (of_node_check_flag(layout_dn, OF_POPULATED_BUS)) { ++ pr_debug("%s() - skipping %pOF, already populated\n", ++ __func__, layout_dn); ++ ++ return 0; ++ } ++ ++ /* NVMEM layout buses expect only a single device representing the layout */ ++ ret = nvmem_layout_create_device(nvmem, layout_dn); ++ if (ret) ++ return ret; ++ ++ of_node_set_flag(layout_dn, OF_POPULATED_BUS); ++ ++ return 0; ++} ++ ++struct device_node *of_nvmem_layout_get_container(struct nvmem_device *nvmem) ++{ ++ return of_get_child_by_name(nvmem->dev.of_node, "nvmem-layout"); ++} ++EXPORT_SYMBOL_GPL(of_nvmem_layout_get_container); ++ ++/* ++ * Returns the number of devices populated, 0 if the operation was not relevant ++ * for this nvmem device, an error code otherwise. ++ */ ++int nvmem_populate_layout(struct nvmem_device *nvmem) ++{ ++ struct device_node *layout_dn; ++ int ret; ++ ++ layout_dn = of_nvmem_layout_get_container(nvmem); ++ if (!layout_dn) ++ return 0; ++ ++ /* Populate the layout device */ ++ device_links_supplier_sync_state_pause(); ++ ret = nvmem_layout_bus_populate(nvmem, layout_dn); ++ device_links_supplier_sync_state_resume(); ++ ++ of_node_put(layout_dn); ++ return ret; ++} ++ ++void nvmem_destroy_layout(struct nvmem_device *nvmem) ++{ ++ struct device *dev; ++ ++ if (!nvmem->layout) ++ return; ++ ++ dev = &nvmem->layout->dev; ++ of_node_clear_flag(dev->of_node, OF_POPULATED_BUS); ++ device_unregister(dev); ++} ++ ++int nvmem_layout_bus_register(void) ++{ ++ return bus_register(&nvmem_layout_bus_type); ++} ++ ++void nvmem_layout_bus_unregister(void) ++{ ++ bus_unregister(&nvmem_layout_bus_type); ++} +--- a/drivers/nvmem/layouts/Kconfig ++++ b/drivers/nvmem/layouts/Kconfig +@@ -1,5 +1,11 @@ + # SPDX-License-Identifier: GPL-2.0 + ++config NVMEM_LAYOUTS ++ bool ++ depends on OF ++ ++if NVMEM_LAYOUTS ++ + menu "Layout Types" + + config NVMEM_LAYOUT_SL28_VPD +@@ -21,3 +27,5 @@ config NVMEM_LAYOUT_ONIE_TLV + If unsure, say N. + + endmenu ++ ++endif +--- a/drivers/nvmem/layouts/onie-tlv.c ++++ b/drivers/nvmem/layouts/onie-tlv.c +@@ -225,16 +225,32 @@ static int onie_tlv_parse_table(struct d + return 0; + } + ++static int onie_tlv_probe(struct nvmem_layout *layout) ++{ ++ layout->add_cells = onie_tlv_parse_table; ++ ++ return nvmem_layout_register(layout); ++} ++ ++static void onie_tlv_remove(struct nvmem_layout *layout) ++{ ++ nvmem_layout_unregister(layout); ++} ++ + static const struct of_device_id onie_tlv_of_match_table[] = { + { .compatible = "onie,tlv-layout", }, + {}, + }; + MODULE_DEVICE_TABLE(of, onie_tlv_of_match_table); + +-static struct nvmem_layout onie_tlv_layout = { +- .name = "ONIE tlv layout", +- .of_match_table = onie_tlv_of_match_table, +- .add_cells = onie_tlv_parse_table, ++static struct nvmem_layout_driver onie_tlv_layout = { ++ .driver = { ++ .owner = THIS_MODULE, ++ .name = "onie-tlv-layout", ++ .of_match_table = onie_tlv_of_match_table, ++ }, ++ .probe = onie_tlv_probe, ++ .remove = onie_tlv_remove, + }; + module_nvmem_layout_driver(onie_tlv_layout); + +--- a/drivers/nvmem/layouts/sl28vpd.c ++++ b/drivers/nvmem/layouts/sl28vpd.c +@@ -134,16 +134,32 @@ static int sl28vpd_add_cells(struct devi + return 0; + } + ++static int sl28vpd_probe(struct nvmem_layout *layout) ++{ ++ layout->add_cells = sl28vpd_add_cells; ++ ++ return nvmem_layout_register(layout); ++} ++ ++static void sl28vpd_remove(struct nvmem_layout *layout) ++{ ++ nvmem_layout_unregister(layout); ++} ++ + static const struct of_device_id sl28vpd_of_match_table[] = { + { .compatible = "kontron,sl28-vpd" }, + {}, + }; + MODULE_DEVICE_TABLE(of, sl28vpd_of_match_table); + +-static struct nvmem_layout sl28vpd_layout = { +- .name = "sl28-vpd", +- .of_match_table = sl28vpd_of_match_table, +- .add_cells = sl28vpd_add_cells, ++static struct nvmem_layout_driver sl28vpd_layout = { ++ .driver = { ++ .owner = THIS_MODULE, ++ .name = "kontron-sl28vpd-layout", ++ .of_match_table = sl28vpd_of_match_table, ++ }, ++ .probe = sl28vpd_probe, ++ .remove = sl28vpd_remove, + }; + module_nvmem_layout_driver(sl28vpd_layout); + +--- a/include/linux/nvmem-provider.h ++++ b/include/linux/nvmem-provider.h +@@ -9,6 +9,7 @@ + #ifndef _LINUX_NVMEM_PROVIDER_H + #define _LINUX_NVMEM_PROVIDER_H + ++#include + #include + #include + #include +@@ -158,12 +159,11 @@ struct nvmem_cell_table { + /** + * struct nvmem_layout - NVMEM layout definitions + * +- * @name: Layout name. +- * @of_match_table: Open firmware match table. +- * @add_cells: Called to populate the layout using +- * nvmem_add_one_cell(). +- * @owner: Pointer to struct module. +- * @node: List node. ++ * @dev: Device-model layout device. ++ * @nvmem: The underlying NVMEM device ++ * @add_cells: Will be called if a nvmem device is found which ++ * has this layout. The function will add layout ++ * specific cells with nvmem_add_one_cell(). + * + * A nvmem device can hold a well defined structure which can just be + * evaluated during runtime. For example a TLV list, or a list of "name=val" +@@ -171,13 +171,15 @@ struct nvmem_cell_table { + * cells. + */ + struct nvmem_layout { +- const char *name; +- const struct of_device_id *of_match_table; ++ struct device dev; ++ struct nvmem_device *nvmem; + int (*add_cells)(struct device *dev, struct nvmem_device *nvmem); ++}; + +- /* private */ +- struct module *owner; +- struct list_head node; ++struct nvmem_layout_driver { ++ struct device_driver driver; ++ int (*probe)(struct nvmem_layout *layout); ++ void (*remove)(struct nvmem_layout *layout); + }; + + #if IS_ENABLED(CONFIG_NVMEM) +@@ -194,11 +196,15 @@ void nvmem_del_cell_table(struct nvmem_c + int nvmem_add_one_cell(struct nvmem_device *nvmem, + const struct nvmem_cell_info *info); + +-int __nvmem_layout_register(struct nvmem_layout *layout, struct module *owner); +-#define nvmem_layout_register(layout) \ +- __nvmem_layout_register(layout, THIS_MODULE) ++int nvmem_layout_register(struct nvmem_layout *layout); + void nvmem_layout_unregister(struct nvmem_layout *layout); + ++int nvmem_layout_driver_register(struct nvmem_layout_driver *drv); ++void nvmem_layout_driver_unregister(struct nvmem_layout_driver *drv); ++#define module_nvmem_layout_driver(__nvmem_layout_driver) \ ++ module_driver(__nvmem_layout_driver, nvmem_layout_driver_register, \ ++ nvmem_layout_driver_unregister) ++ + const void *nvmem_layout_get_match_data(struct nvmem_device *nvmem, + struct nvmem_layout *layout); + +@@ -262,8 +268,4 @@ static inline struct device_node *of_nvm + + #endif /* CONFIG_NVMEM && CONFIG_OF */ + +-#define module_nvmem_layout_driver(__layout_driver) \ +- module_driver(__layout_driver, nvmem_layout_register, \ +- nvmem_layout_unregister) +- + #endif /* ifndef _LINUX_NVMEM_PROVIDER_H */ diff --git a/target/linux/generic/backport-6.1/819-v6.8-0006-nvmem-core-Expose-cells-through-sysfs.patch b/target/linux/generic/backport-6.1/819-v6.8-0006-nvmem-core-Expose-cells-through-sysfs.patch new file mode 100644 index 000000000..89872bec2 --- /dev/null +++ b/target/linux/generic/backport-6.1/819-v6.8-0006-nvmem-core-Expose-cells-through-sysfs.patch @@ -0,0 +1,240 @@ +From 0331c611949fffdf486652450901a4dc52bc5cca Mon Sep 17 00:00:00 2001 +From: Miquel Raynal +Date: Fri, 15 Dec 2023 11:15:34 +0000 +Subject: [PATCH] nvmem: core: Expose cells through sysfs +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +The binary content of nvmem devices is available to the user so in the +easiest cases, finding the content of a cell is rather easy as it is +just a matter of looking at a known and fixed offset. However, nvmem +layouts have been recently introduced to cope with more advanced +situations, where the offset and size of the cells is not known in +advance or is dynamic. When using layouts, more advanced parsers are +used by the kernel in order to give direct access to the content of each +cell, regardless of its position/size in the underlying +device. Unfortunately, these information are not accessible by users, +unless by fully re-implementing the parser logic in userland. + +Let's expose the cells and their content through sysfs to avoid these +situations. Of course the relevant NVMEM sysfs Kconfig option must be +enabled for this support to be available. + +Not all nvmem devices expose cells. Indeed, the .bin_attrs attribute +group member will be filled at runtime only when relevant and will +remain empty otherwise. In this case, as the cells attribute group will +be empty, it will not lead to any additional folder/file creation. + +Exposed cells are read-only. There is, in practice, everything in the +core to support a write path, but as I don't see any need for that, I +prefer to keep the interface simple (and probably safer). The interface +is documented as being in the "testing" state which means we can later +add a write attribute if though relevant. + +Signed-off-by: Miquel Raynal +Tested-by: RafaÅ‚ MiÅ‚ecki +Tested-by: Chen-Yu Tsai +Signed-off-by: Srinivas Kandagatla +Link: https://lore.kernel.org/r/20231215111536.316972-9-srinivas.kandagatla@linaro.org +Signed-off-by: Greg Kroah-Hartman +--- + drivers/nvmem/core.c | 135 +++++++++++++++++++++++++++++++++++++- + drivers/nvmem/internals.h | 1 + + 2 files changed, 135 insertions(+), 1 deletion(-) + +--- a/drivers/nvmem/core.c ++++ b/drivers/nvmem/core.c +@@ -300,6 +300,43 @@ static umode_t nvmem_bin_attr_is_visible + return nvmem_bin_attr_get_umode(nvmem); + } + ++static struct nvmem_cell *nvmem_create_cell(struct nvmem_cell_entry *entry, ++ const char *id, int index); ++ ++static ssize_t nvmem_cell_attr_read(struct file *filp, struct kobject *kobj, ++ struct bin_attribute *attr, char *buf, ++ loff_t pos, size_t count) ++{ ++ struct nvmem_cell_entry *entry; ++ struct nvmem_cell *cell = NULL; ++ size_t cell_sz, read_len; ++ void *content; ++ ++ entry = attr->private; ++ cell = nvmem_create_cell(entry, entry->name, 0); ++ if (IS_ERR(cell)) ++ return PTR_ERR(cell); ++ ++ if (!cell) ++ return -EINVAL; ++ ++ content = nvmem_cell_read(cell, &cell_sz); ++ if (IS_ERR(content)) { ++ read_len = PTR_ERR(content); ++ goto destroy_cell; ++ } ++ ++ read_len = min_t(unsigned int, cell_sz - pos, count); ++ memcpy(buf, content + pos, read_len); ++ kfree(content); ++ ++destroy_cell: ++ kfree_const(cell->id); ++ kfree(cell); ++ ++ return read_len; ++} ++ + /* default read/write permissions */ + static struct bin_attribute bin_attr_rw_nvmem = { + .attr = { +@@ -321,11 +358,21 @@ static const struct attribute_group nvme + .is_bin_visible = nvmem_bin_attr_is_visible, + }; + ++/* Cell attributes will be dynamically allocated */ ++static struct attribute_group nvmem_cells_group = { ++ .name = "cells", ++}; ++ + static const struct attribute_group *nvmem_dev_groups[] = { + &nvmem_bin_group, + NULL, + }; + ++static const struct attribute_group *nvmem_cells_groups[] = { ++ &nvmem_cells_group, ++ NULL, ++}; ++ + static struct bin_attribute bin_attr_nvmem_eeprom_compat = { + .attr = { + .name = "eeprom", +@@ -381,6 +428,68 @@ static void nvmem_sysfs_remove_compat(st + device_remove_bin_file(nvmem->base_dev, &nvmem->eeprom); + } + ++static int nvmem_populate_sysfs_cells(struct nvmem_device *nvmem) ++{ ++ struct bin_attribute **cells_attrs, *attrs; ++ struct nvmem_cell_entry *entry; ++ unsigned int ncells = 0, i = 0; ++ int ret = 0; ++ ++ mutex_lock(&nvmem_mutex); ++ ++ if (list_empty(&nvmem->cells) || nvmem->sysfs_cells_populated) { ++ nvmem_cells_group.bin_attrs = NULL; ++ goto unlock_mutex; ++ } ++ ++ /* Allocate an array of attributes with a sentinel */ ++ ncells = list_count_nodes(&nvmem->cells); ++ cells_attrs = devm_kcalloc(&nvmem->dev, ncells + 1, ++ sizeof(struct bin_attribute *), GFP_KERNEL); ++ if (!cells_attrs) { ++ ret = -ENOMEM; ++ goto unlock_mutex; ++ } ++ ++ attrs = devm_kcalloc(&nvmem->dev, ncells, sizeof(struct bin_attribute), GFP_KERNEL); ++ if (!attrs) { ++ ret = -ENOMEM; ++ goto unlock_mutex; ++ } ++ ++ /* Initialize each attribute to take the name and size of the cell */ ++ list_for_each_entry(entry, &nvmem->cells, node) { ++ sysfs_bin_attr_init(&attrs[i]); ++ attrs[i].attr.name = devm_kasprintf(&nvmem->dev, GFP_KERNEL, ++ "%s@%x", entry->name, ++ entry->offset); ++ attrs[i].attr.mode = 0444; ++ attrs[i].size = entry->bytes; ++ attrs[i].read = &nvmem_cell_attr_read; ++ attrs[i].private = entry; ++ if (!attrs[i].attr.name) { ++ ret = -ENOMEM; ++ goto unlock_mutex; ++ } ++ ++ cells_attrs[i] = &attrs[i]; ++ i++; ++ } ++ ++ nvmem_cells_group.bin_attrs = cells_attrs; ++ ++ ret = devm_device_add_groups(&nvmem->dev, nvmem_cells_groups); ++ if (ret) ++ goto unlock_mutex; ++ ++ nvmem->sysfs_cells_populated = true; ++ ++unlock_mutex: ++ mutex_unlock(&nvmem_mutex); ++ ++ return ret; ++} ++ + #else /* CONFIG_NVMEM_SYSFS */ + + static int nvmem_sysfs_setup_compat(struct nvmem_device *nvmem, +@@ -740,11 +849,25 @@ static int nvmem_add_cells_from_fixed_la + + int nvmem_layout_register(struct nvmem_layout *layout) + { ++ int ret; ++ + if (!layout->add_cells) + return -EINVAL; + + /* Populate the cells */ +- return layout->add_cells(&layout->nvmem->dev, layout->nvmem); ++ ret = layout->add_cells(&layout->nvmem->dev, layout->nvmem); ++ if (ret) ++ return ret; ++ ++#ifdef CONFIG_NVMEM_SYSFS ++ ret = nvmem_populate_sysfs_cells(layout->nvmem); ++ if (ret) { ++ nvmem_device_remove_all_cells(layout->nvmem); ++ return ret; ++ } ++#endif ++ ++ return 0; + } + EXPORT_SYMBOL_GPL(nvmem_layout_register); + +@@ -903,10 +1026,20 @@ struct nvmem_device *nvmem_register(cons + if (rval) + goto err_remove_dev; + ++#ifdef CONFIG_NVMEM_SYSFS ++ rval = nvmem_populate_sysfs_cells(nvmem); ++ if (rval) ++ goto err_destroy_layout; ++#endif ++ + blocking_notifier_call_chain(&nvmem_notifier, NVMEM_ADD, nvmem); + + return nvmem; + ++#ifdef CONFIG_NVMEM_SYSFS ++err_destroy_layout: ++ nvmem_destroy_layout(nvmem); ++#endif + err_remove_dev: + device_del(&nvmem->dev); + err_remove_cells: +--- a/drivers/nvmem/internals.h ++++ b/drivers/nvmem/internals.h +@@ -32,6 +32,7 @@ struct nvmem_device { + struct gpio_desc *wp_gpio; + struct nvmem_layout *layout; + void *priv; ++ bool sysfs_cells_populated; + }; + + #if IS_ENABLED(CONFIG_OF) diff --git a/target/linux/generic/backport-6.1/819-v6.8-0007-nvmem-stm32-add-support-for-STM32MP25-BSEC-to-contro.patch b/target/linux/generic/backport-6.1/819-v6.8-0007-nvmem-stm32-add-support-for-STM32MP25-BSEC-to-contro.patch new file mode 100644 index 000000000..f686222f8 --- /dev/null +++ b/target/linux/generic/backport-6.1/819-v6.8-0007-nvmem-stm32-add-support-for-STM32MP25-BSEC-to-contro.patch @@ -0,0 +1,65 @@ +From f0ac5b23039610619ca4a4805528553ecb6bc815 Mon Sep 17 00:00:00 2001 +From: Patrick Delaunay +Date: Fri, 15 Dec 2023 11:15:36 +0000 +Subject: [PATCH] nvmem: stm32: add support for STM32MP25 BSEC to control OTP + data + +On STM32MP25, OTP area may be read/written by using BSEC (boot, security +and OTP control). The BSEC internal peripheral is only managed by the +secure world. + +The 12 Kbits of OTP (effective) are organized into the following regions: +- lower OTP (OTP0 to OTP127) = 4096 lower OTP bits, + bitwise (1-bit) programmable +- mid OTP (OTP128 to OTP255) = 4096 middle OTP bits, + bulk (32-bit) programmable +- upper OTP (OTP256 to OTP383) = 4096 upper OTP bits, + bulk (32-bit) programmable, + only accessible when BSEC is in closed state. + +As HWKEY and ECIES key are only accessible by ROM code; +only 368 OTP words are managed in this driver (OTP0 to OTP267). + +This patch adds the STM32MP25 configuration for reading and writing +the OTP data using the OP-TEE BSEC TA services. + +Signed-off-by: Patrick Delaunay +Signed-off-by: Srinivas Kandagatla +Link: https://lore.kernel.org/r/20231215111536.316972-11-srinivas.kandagatla@linaro.org +Signed-off-by: Greg Kroah-Hartman +--- + drivers/nvmem/stm32-romem.c | 16 ++++++++++++++++ + 1 file changed, 16 insertions(+) + +--- a/drivers/nvmem/stm32-romem.c ++++ b/drivers/nvmem/stm32-romem.c +@@ -269,6 +269,19 @@ static const struct stm32_romem_cfg stm3 + .ta = true, + }; + ++/* ++ * STM32MP25 BSEC OTP: 3 regions of 32-bits data words ++ * lower OTP (OTP0 to OTP127), bitwise (1-bit) programmable ++ * mid OTP (OTP128 to OTP255), bulk (32-bit) programmable ++ * upper OTP (OTP256 to OTP383), bulk (32-bit) programmable ++ * but no access to HWKEY and ECIES key: limited at OTP367 ++ */ ++static const struct stm32_romem_cfg stm32mp25_bsec_cfg = { ++ .size = 368 * 4, ++ .lower = 127, ++ .ta = true, ++}; ++ + static const struct of_device_id stm32_romem_of_match[] __maybe_unused = { + { .compatible = "st,stm32f4-otp", }, { + .compatible = "st,stm32mp15-bsec", +@@ -276,6 +289,9 @@ static const struct of_device_id stm32_r + }, { + .compatible = "st,stm32mp13-bsec", + .data = (void *)&stm32mp13_bsec_cfg, ++ }, { ++ .compatible = "st,stm32mp25-bsec", ++ .data = (void *)&stm32mp25_bsec_cfg, + }, + { /* sentinel */ }, + }; diff --git a/target/linux/generic/backport-6.1/819-v6.8-0008-nvmem-layouts-refactor-.add_cells-callback-arguments.patch b/target/linux/generic/backport-6.1/819-v6.8-0008-nvmem-layouts-refactor-.add_cells-callback-arguments.patch new file mode 100644 index 000000000..1bf3ba35b --- /dev/null +++ b/target/linux/generic/backport-6.1/819-v6.8-0008-nvmem-layouts-refactor-.add_cells-callback-arguments.patch @@ -0,0 +1,94 @@ +From 401df0d4f4098ecc9c5278da2f50756d62e5b37d Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= +Date: Tue, 19 Dec 2023 13:01:03 +0100 +Subject: [PATCH] nvmem: layouts: refactor .add_cells() callback arguments +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Simply pass whole "struct nvmem_layout" instead of single variables. +There is nothing in "struct nvmem_layout" that we have to hide from +layout drivers. They also access it during .probe() and .remove(). + +Thanks to this change: + +1. API gets more consistent + All layouts drivers callbacks get the same argument + +2. Layouts get correct device + Before this change NVMEM core code was passing NVMEM device instead + of layout device. That resulted in: + * Confusing prints + * Calling devm_*() helpers on wrong device + * Helpers like of_device_get_match_data() dereferencing NULLs + +3. It gets possible to get match data + First of all nvmem_layout_get_match_data() requires passing "struct + nvmem_layout" which .add_cells() callback didn't have before this. It + doesn't matter much as it's rather useless now anyway (and will be + dropped). + What's more important however is that of_device_get_match_data() can + be used now thanks to owning a proper device pointer. + +Signed-off-by: RafaÅ‚ MiÅ‚ecki +Reviewed-by: Miquel Raynal +Reviewed-by: Michael Walle +Link: https://lore.kernel.org/r/20231219120104.3422-1-zajec5@gmail.com +Signed-off-by: Greg Kroah-Hartman +--- + drivers/nvmem/core.c | 2 +- + drivers/nvmem/layouts/onie-tlv.c | 4 +++- + drivers/nvmem/layouts/sl28vpd.c | 4 +++- + include/linux/nvmem-provider.h | 2 +- + 4 files changed, 8 insertions(+), 4 deletions(-) + +--- a/drivers/nvmem/core.c ++++ b/drivers/nvmem/core.c +@@ -855,7 +855,7 @@ int nvmem_layout_register(struct nvmem_l + return -EINVAL; + + /* Populate the cells */ +- ret = layout->add_cells(&layout->nvmem->dev, layout->nvmem); ++ ret = layout->add_cells(layout); + if (ret) + return ret; + +--- a/drivers/nvmem/layouts/onie-tlv.c ++++ b/drivers/nvmem/layouts/onie-tlv.c +@@ -182,8 +182,10 @@ static bool onie_tlv_crc_is_valid(struct + return true; + } + +-static int onie_tlv_parse_table(struct device *dev, struct nvmem_device *nvmem) ++static int onie_tlv_parse_table(struct nvmem_layout *layout) + { ++ struct nvmem_device *nvmem = layout->nvmem; ++ struct device *dev = &layout->dev; + struct onie_tlv_hdr hdr; + size_t table_len, data_len, hdr_len; + u8 *table, *data; +--- a/drivers/nvmem/layouts/sl28vpd.c ++++ b/drivers/nvmem/layouts/sl28vpd.c +@@ -80,8 +80,10 @@ static int sl28vpd_v1_check_crc(struct d + return 0; + } + +-static int sl28vpd_add_cells(struct device *dev, struct nvmem_device *nvmem) ++static int sl28vpd_add_cells(struct nvmem_layout *layout) + { ++ struct nvmem_device *nvmem = layout->nvmem; ++ struct device *dev = &layout->dev; + const struct nvmem_cell_info *pinfo; + struct nvmem_cell_info info = {0}; + struct device_node *layout_np; +--- a/include/linux/nvmem-provider.h ++++ b/include/linux/nvmem-provider.h +@@ -173,7 +173,7 @@ struct nvmem_cell_table { + struct nvmem_layout { + struct device dev; + struct nvmem_device *nvmem; +- int (*add_cells)(struct device *dev, struct nvmem_device *nvmem); ++ int (*add_cells)(struct nvmem_layout *layout); + }; + + struct nvmem_layout_driver { diff --git a/target/linux/generic/backport-6.1/819-v6.8-0009-nvmem-drop-nvmem_layout_get_match_data.patch b/target/linux/generic/backport-6.1/819-v6.8-0009-nvmem-drop-nvmem_layout_get_match_data.patch new file mode 100644 index 000000000..514b5f2de --- /dev/null +++ b/target/linux/generic/backport-6.1/819-v6.8-0009-nvmem-drop-nvmem_layout_get_match_data.patch @@ -0,0 +1,72 @@ +From 43f60e3fb62edc7bd8891de8779fb422f4ae23ae Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= +Date: Tue, 19 Dec 2023 13:01:04 +0100 +Subject: [PATCH] nvmem: drop nvmem_layout_get_match_data() +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Thanks for layouts refactoring we now have "struct device" associated +with layout. Also its OF pointer points directly to the "nvmem-layout" +DT node. + +All it takes to get match data is a generic of_device_get_match_data(). + +Signed-off-by: RafaÅ‚ MiÅ‚ecki +Reviewed-by: Miquel Raynal +Reviewed-by: Michael Walle +Link: https://lore.kernel.org/r/20231219120104.3422-2-zajec5@gmail.com +Signed-off-by: Greg Kroah-Hartman +--- + drivers/nvmem/core.c | 13 ------------- + include/linux/nvmem-provider.h | 10 ---------- + 2 files changed, 23 deletions(-) + +--- a/drivers/nvmem/core.c ++++ b/drivers/nvmem/core.c +@@ -877,19 +877,6 @@ void nvmem_layout_unregister(struct nvme + } + EXPORT_SYMBOL_GPL(nvmem_layout_unregister); + +-const void *nvmem_layout_get_match_data(struct nvmem_device *nvmem, +- struct nvmem_layout *layout) +-{ +- struct device_node __maybe_unused *layout_np; +- const struct of_device_id *match; +- +- layout_np = of_nvmem_layout_get_container(nvmem); +- match = of_match_node(layout->dev.driver->of_match_table, layout_np); +- +- return match ? match->data : NULL; +-} +-EXPORT_SYMBOL_GPL(nvmem_layout_get_match_data); +- + /** + * nvmem_register() - Register a nvmem device for given nvmem_config. + * Also creates a binary entry in /sys/bus/nvmem/devices/dev-name/nvmem +--- a/include/linux/nvmem-provider.h ++++ b/include/linux/nvmem-provider.h +@@ -205,9 +205,6 @@ void nvmem_layout_driver_unregister(stru + module_driver(__nvmem_layout_driver, nvmem_layout_driver_register, \ + nvmem_layout_driver_unregister) + +-const void *nvmem_layout_get_match_data(struct nvmem_device *nvmem, +- struct nvmem_layout *layout); +- + #else + + static inline struct nvmem_device *nvmem_register(const struct nvmem_config *c) +@@ -238,13 +235,6 @@ static inline int nvmem_layout_register( + + static inline void nvmem_layout_unregister(struct nvmem_layout *layout) {} + +-static inline const void * +-nvmem_layout_get_match_data(struct nvmem_device *nvmem, +- struct nvmem_layout *layout) +-{ +- return NULL; +-} +- + #endif /* CONFIG_NVMEM */ + + #if IS_ENABLED(CONFIG_NVMEM) && IS_ENABLED(CONFIG_OF) diff --git a/target/linux/generic/backport-6.1/819-v6.8-0010-nvmem-core-add-nvmem_dev_size-helper.patch b/target/linux/generic/backport-6.1/819-v6.8-0010-nvmem-core-add-nvmem_dev_size-helper.patch new file mode 100644 index 000000000..aa0bbaa0c --- /dev/null +++ b/target/linux/generic/backport-6.1/819-v6.8-0010-nvmem-core-add-nvmem_dev_size-helper.patch @@ -0,0 +1,53 @@ +From 33cf42e68efc8ff529a7eee08a4f0ba8c8d0a207 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= +Date: Thu, 21 Dec 2023 18:34:17 +0100 +Subject: [PATCH] nvmem: core: add nvmem_dev_size() helper +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +This is required by layouts that need to read whole NVMEM content. It's +especially useful for NVMEM devices without hardcoded layout (like +U-Boot environment data block). + +Signed-off-by: RafaÅ‚ MiÅ‚ecki +Reviewed-by: Miquel Raynal +Link: https://lore.kernel.org/r/20231221173421.13737-2-zajec5@gmail.com +Signed-off-by: Greg Kroah-Hartman +--- + drivers/nvmem/core.c | 13 +++++++++++++ + include/linux/nvmem-consumer.h | 1 + + 2 files changed, 14 insertions(+) + +--- a/drivers/nvmem/core.c ++++ b/drivers/nvmem/core.c +@@ -2164,6 +2164,19 @@ const char *nvmem_dev_name(struct nvmem_ + } + EXPORT_SYMBOL_GPL(nvmem_dev_name); + ++/** ++ * nvmem_dev_size() - Get the size of a given nvmem device. ++ * ++ * @nvmem: nvmem device. ++ * ++ * Return: size of the nvmem device. ++ */ ++size_t nvmem_dev_size(struct nvmem_device *nvmem) ++{ ++ return nvmem->size; ++} ++EXPORT_SYMBOL_GPL(nvmem_dev_size); ++ + static int __init nvmem_init(void) + { + int ret; +--- a/include/linux/nvmem-consumer.h ++++ b/include/linux/nvmem-consumer.h +@@ -81,6 +81,7 @@ int nvmem_device_cell_write(struct nvmem + struct nvmem_cell_info *info, void *buf); + + const char *nvmem_dev_name(struct nvmem_device *nvmem); ++size_t nvmem_dev_size(struct nvmem_device *nvmem); + + void nvmem_add_cell_lookups(struct nvmem_cell_lookup *entries, + size_t nentries); diff --git a/target/linux/generic/backport-6.1/819-v6.8-0011-nvmem-u-boot-env-use-nvmem_add_one_cell-nvmem-subsys.patch b/target/linux/generic/backport-6.1/819-v6.8-0011-nvmem-u-boot-env-use-nvmem_add_one_cell-nvmem-subsys.patch new file mode 100644 index 000000000..fc826f3f7 --- /dev/null +++ b/target/linux/generic/backport-6.1/819-v6.8-0011-nvmem-u-boot-env-use-nvmem_add_one_cell-nvmem-subsys.patch @@ -0,0 +1,126 @@ +From 7c8979b42b1a9c5604f431ba804928e55919263c Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= +Date: Thu, 21 Dec 2023 18:34:18 +0100 +Subject: [PATCH] nvmem: u-boot-env: use nvmem_add_one_cell() nvmem subsystem + helper +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Simplify adding NVMEM cells. + +Signed-off-by: RafaÅ‚ MiÅ‚ecki +Reviewed-by: Miquel Raynal +Link: https://lore.kernel.org/r/20231221173421.13737-3-zajec5@gmail.com +Signed-off-by: Greg Kroah-Hartman +--- + drivers/nvmem/u-boot-env.c | 55 +++++++++++++++----------------------- + 1 file changed, 21 insertions(+), 34 deletions(-) + +--- a/drivers/nvmem/u-boot-env.c ++++ b/drivers/nvmem/u-boot-env.c +@@ -23,13 +23,10 @@ enum u_boot_env_format { + + struct u_boot_env { + struct device *dev; ++ struct nvmem_device *nvmem; + enum u_boot_env_format format; + + struct mtd_info *mtd; +- +- /* Cells */ +- struct nvmem_cell_info *cells; +- int ncells; + }; + + struct u_boot_env_image_single { +@@ -94,43 +91,36 @@ static int u_boot_env_read_post_process_ + static int u_boot_env_add_cells(struct u_boot_env *priv, uint8_t *buf, + size_t data_offset, size_t data_len) + { ++ struct nvmem_device *nvmem = priv->nvmem; + struct device *dev = priv->dev; + char *data = buf + data_offset; + char *var, *value, *eq; +- int idx; +- +- priv->ncells = 0; +- for (var = data; var < data + data_len && *var; var += strlen(var) + 1) +- priv->ncells++; +- +- priv->cells = devm_kcalloc(dev, priv->ncells, sizeof(*priv->cells), GFP_KERNEL); +- if (!priv->cells) +- return -ENOMEM; + +- for (var = data, idx = 0; ++ for (var = data; + var < data + data_len && *var; +- var = value + strlen(value) + 1, idx++) { ++ var = value + strlen(value) + 1) { ++ struct nvmem_cell_info info = {}; ++ + eq = strchr(var, '='); + if (!eq) + break; + *eq = '\0'; + value = eq + 1; + +- priv->cells[idx].name = devm_kstrdup(dev, var, GFP_KERNEL); +- if (!priv->cells[idx].name) ++ info.name = devm_kstrdup(dev, var, GFP_KERNEL); ++ if (!info.name) + return -ENOMEM; +- priv->cells[idx].offset = data_offset + value - data; +- priv->cells[idx].bytes = strlen(value); +- priv->cells[idx].np = of_get_child_by_name(dev->of_node, priv->cells[idx].name); ++ info.offset = data_offset + value - data; ++ info.bytes = strlen(value); ++ info.np = of_get_child_by_name(dev->of_node, info.name); + if (!strcmp(var, "ethaddr")) { +- priv->cells[idx].raw_len = strlen(value); +- priv->cells[idx].bytes = ETH_ALEN; +- priv->cells[idx].read_post_process = u_boot_env_read_post_process_ethaddr; ++ info.raw_len = strlen(value); ++ info.bytes = ETH_ALEN; ++ info.read_post_process = u_boot_env_read_post_process_ethaddr; + } +- } + +- if (WARN_ON(idx != priv->ncells)) +- priv->ncells = idx; ++ nvmem_add_one_cell(nvmem, &info); ++ } + + return 0; + } +@@ -209,7 +199,6 @@ static int u_boot_env_probe(struct platf + struct device *dev = &pdev->dev; + struct device_node *np = dev->of_node; + struct u_boot_env *priv; +- int err; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) +@@ -224,17 +213,15 @@ static int u_boot_env_probe(struct platf + return PTR_ERR(priv->mtd); + } + +- err = u_boot_env_parse(priv); +- if (err) +- return err; +- + config.dev = dev; +- config.cells = priv->cells; +- config.ncells = priv->ncells; + config.priv = priv; + config.size = priv->mtd->size; + +- return PTR_ERR_OR_ZERO(devm_nvmem_register(dev, &config)); ++ priv->nvmem = devm_nvmem_register(dev, &config); ++ if (IS_ERR(priv->nvmem)) ++ return PTR_ERR(priv->nvmem); ++ ++ return u_boot_env_parse(priv); + } + + static const struct of_device_id u_boot_env_of_match_table[] = { diff --git a/target/linux/generic/backport-6.1/819-v6.8-0012-nvmem-u-boot-env-use-nvmem-device-helpers.patch b/target/linux/generic/backport-6.1/819-v6.8-0012-nvmem-u-boot-env-use-nvmem-device-helpers.patch new file mode 100644 index 000000000..70abc7cf1 --- /dev/null +++ b/target/linux/generic/backport-6.1/819-v6.8-0012-nvmem-u-boot-env-use-nvmem-device-helpers.patch @@ -0,0 +1,81 @@ +From a832556d23c5a11115f300011a5874d6107a0d62 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= +Date: Thu, 21 Dec 2023 18:34:19 +0100 +Subject: [PATCH] nvmem: u-boot-env: use nvmem device helpers +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Use nvmem_dev_size() and nvmem_device_read() to make this driver less +mtd dependent. + +Signed-off-by: RafaÅ‚ MiÅ‚ecki +Reviewed-by: Miquel Raynal +Link: https://lore.kernel.org/r/20231221173421.13737-4-zajec5@gmail.com +Signed-off-by: Greg Kroah-Hartman +--- + drivers/nvmem/u-boot-env.c | 23 +++++++++++++++-------- + 1 file changed, 15 insertions(+), 8 deletions(-) + +--- a/drivers/nvmem/u-boot-env.c ++++ b/drivers/nvmem/u-boot-env.c +@@ -127,27 +127,34 @@ static int u_boot_env_add_cells(struct u + + static int u_boot_env_parse(struct u_boot_env *priv) + { ++ struct nvmem_device *nvmem = priv->nvmem; + struct device *dev = priv->dev; + size_t crc32_data_offset; + size_t crc32_data_len; + size_t crc32_offset; + size_t data_offset; + size_t data_len; ++ size_t dev_size; + uint32_t crc32; + uint32_t calc; +- size_t bytes; + uint8_t *buf; ++ int bytes; + int err; + +- buf = kcalloc(1, priv->mtd->size, GFP_KERNEL); ++ dev_size = nvmem_dev_size(nvmem); ++ ++ buf = kcalloc(1, dev_size, GFP_KERNEL); + if (!buf) { + err = -ENOMEM; + goto err_out; + } + +- err = mtd_read(priv->mtd, 0, priv->mtd->size, &bytes, buf); +- if ((err && !mtd_is_bitflip(err)) || bytes != priv->mtd->size) { +- dev_err(dev, "Failed to read from mtd: %d\n", err); ++ bytes = nvmem_device_read(nvmem, 0, dev_size, buf); ++ if (bytes < 0) { ++ err = bytes; ++ goto err_kfree; ++ } else if (bytes != dev_size) { ++ err = -EIO; + goto err_kfree; + } + +@@ -169,8 +176,8 @@ static int u_boot_env_parse(struct u_boo + break; + } + crc32 = le32_to_cpu(*(__le32 *)(buf + crc32_offset)); +- crc32_data_len = priv->mtd->size - crc32_data_offset; +- data_len = priv->mtd->size - data_offset; ++ crc32_data_len = dev_size - crc32_data_offset; ++ data_len = dev_size - data_offset; + + calc = crc32(~0, buf + crc32_data_offset, crc32_data_len) ^ ~0L; + if (calc != crc32) { +@@ -179,7 +186,7 @@ static int u_boot_env_parse(struct u_boo + goto err_kfree; + } + +- buf[priv->mtd->size - 1] = '\0'; ++ buf[dev_size - 1] = '\0'; + err = u_boot_env_add_cells(priv, buf, data_offset, data_len); + if (err) + dev_err(dev, "Failed to add cells: %d\n", err); diff --git a/target/linux/generic/backport-6.1/819-v6.8-0013-nvmem-u-boot-env-improve-coding-style.patch b/target/linux/generic/backport-6.1/819-v6.8-0013-nvmem-u-boot-env-improve-coding-style.patch new file mode 100644 index 000000000..273cfed87 --- /dev/null +++ b/target/linux/generic/backport-6.1/819-v6.8-0013-nvmem-u-boot-env-improve-coding-style.patch @@ -0,0 +1,62 @@ +From 6bafe07c930676d6430be471310958070816a595 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= +Date: Thu, 21 Dec 2023 18:34:20 +0100 +Subject: [PATCH] nvmem: u-boot-env: improve coding style +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +1. Prefer kzalloc() over kcalloc() + See memory-allocation.rst which says: "to be on the safe side it's + best to use routines that set memory to zero, like kzalloc()" +2. Drop dev_err() for u_boot_env_add_cells() fail + It can fail only on -ENOMEM. We don't want to print error then. +3. Add extra "crc32_addr" variable + It makes code reading header's crc32 easier to understand / review. + +Signed-off-by: RafaÅ‚ MiÅ‚ecki +Reviewed-by: Miquel Raynal +Link: https://lore.kernel.org/r/20231221173421.13737-5-zajec5@gmail.com +Signed-off-by: Greg Kroah-Hartman +--- + drivers/nvmem/u-boot-env.c | 8 ++++---- + 1 file changed, 4 insertions(+), 4 deletions(-) + +--- a/drivers/nvmem/u-boot-env.c ++++ b/drivers/nvmem/u-boot-env.c +@@ -132,6 +132,7 @@ static int u_boot_env_parse(struct u_boo + size_t crc32_data_offset; + size_t crc32_data_len; + size_t crc32_offset; ++ __le32 *crc32_addr; + size_t data_offset; + size_t data_len; + size_t dev_size; +@@ -143,7 +144,7 @@ static int u_boot_env_parse(struct u_boo + + dev_size = nvmem_dev_size(nvmem); + +- buf = kcalloc(1, dev_size, GFP_KERNEL); ++ buf = kzalloc(dev_size, GFP_KERNEL); + if (!buf) { + err = -ENOMEM; + goto err_out; +@@ -175,7 +176,8 @@ static int u_boot_env_parse(struct u_boo + data_offset = offsetof(struct u_boot_env_image_broadcom, data); + break; + } +- crc32 = le32_to_cpu(*(__le32 *)(buf + crc32_offset)); ++ crc32_addr = (__le32 *)(buf + crc32_offset); ++ crc32 = le32_to_cpu(*crc32_addr); + crc32_data_len = dev_size - crc32_data_offset; + data_len = dev_size - data_offset; + +@@ -188,8 +190,6 @@ static int u_boot_env_parse(struct u_boo + + buf[dev_size - 1] = '\0'; + err = u_boot_env_add_cells(priv, buf, data_offset, data_len); +- if (err) +- dev_err(dev, "Failed to add cells: %d\n", err); + + err_kfree: + kfree(buf); diff --git a/target/linux/generic/backport-6.1/820-v6.4-net-phy-fix-circular-LEDS_CLASS-dependencies.patch b/target/linux/generic/backport-6.1/820-v6.4-net-phy-fix-circular-LEDS_CLASS-dependencies.patch index 1de086417..3b6840369 100644 --- a/target/linux/generic/backport-6.1/820-v6.4-net-phy-fix-circular-LEDS_CLASS-dependencies.patch +++ b/target/linux/generic/backport-6.1/820-v6.4-net-phy-fix-circular-LEDS_CLASS-dependencies.patch @@ -53,7 +53,7 @@ Signed-off-by: Jakub Kicinski tristate "MDIO Bus/PHY emulation with fixed speed/link PHYs" --- a/drivers/net/phy/phy_device.c +++ b/drivers/net/phy/phy_device.c -@@ -3210,7 +3210,8 @@ static int phy_probe(struct device *dev) +@@ -3314,7 +3314,8 @@ static int phy_probe(struct device *dev) /* Get the LEDs from the device tree, and instantiate standard * LEDs for them. */ diff --git a/target/linux/generic/backport-6.1/821-v6.4-net-phy-Fix-reading-LED-reg-property.patch b/target/linux/generic/backport-6.1/821-v6.4-net-phy-Fix-reading-LED-reg-property.patch index d6081d0e6..622bb9e94 100644 --- a/target/linux/generic/backport-6.1/821-v6.4-net-phy-Fix-reading-LED-reg-property.patch +++ b/target/linux/generic/backport-6.1/821-v6.4-net-phy-Fix-reading-LED-reg-property.patch @@ -18,7 +18,7 @@ Signed-off-by: Jakub Kicinski --- a/drivers/net/phy/phy_device.c +++ b/drivers/net/phy/phy_device.c -@@ -2971,6 +2971,7 @@ static int of_phy_led(struct phy_device +@@ -3075,6 +3075,7 @@ static int of_phy_led(struct phy_device struct led_init_data init_data = {}; struct led_classdev *cdev; struct phy_led *phyled; @@ -26,7 +26,7 @@ Signed-off-by: Jakub Kicinski int err; phyled = devm_kzalloc(dev, sizeof(*phyled), GFP_KERNEL); -@@ -2980,10 +2981,13 @@ static int of_phy_led(struct phy_device +@@ -3084,10 +3085,13 @@ static int of_phy_led(struct phy_device cdev = &phyled->led_cdev; phyled->phydev = phydev; diff --git a/target/linux/generic/backport-6.1/822-v6.4-net-phy-Manual-remove-LEDs-to-ensure-correct-orderin.patch b/target/linux/generic/backport-6.1/822-v6.4-net-phy-Manual-remove-LEDs-to-ensure-correct-orderin.patch index 8f076be64..80197e963 100644 --- a/target/linux/generic/backport-6.1/822-v6.4-net-phy-Manual-remove-LEDs-to-ensure-correct-orderin.patch +++ b/target/linux/generic/backport-6.1/822-v6.4-net-phy-Manual-remove-LEDs-to-ensure-correct-orderin.patch @@ -22,7 +22,7 @@ Signed-off-by: David S. Miller --- a/drivers/net/phy/phy_device.c +++ b/drivers/net/phy/phy_device.c -@@ -2964,6 +2964,15 @@ static int phy_led_blink_set(struct led_ +@@ -3068,6 +3068,15 @@ static int phy_led_blink_set(struct led_ return err; } @@ -38,7 +38,7 @@ Signed-off-by: David S. Miller static int of_phy_led(struct phy_device *phydev, struct device_node *led) { -@@ -2997,7 +3006,7 @@ static int of_phy_led(struct phy_device +@@ -3101,7 +3110,7 @@ static int of_phy_led(struct phy_device init_data.fwnode = of_fwnode_handle(led); init_data.devname_mandatory = true; @@ -47,7 +47,7 @@ Signed-off-by: David S. Miller if (err) return err; -@@ -3026,6 +3035,7 @@ static int of_phy_leds(struct phy_device +@@ -3130,6 +3139,7 @@ static int of_phy_leds(struct phy_device err = of_phy_led(phydev, led); if (err) { of_node_put(led); @@ -55,7 +55,7 @@ Signed-off-by: David S. Miller return err; } } -@@ -3231,6 +3241,9 @@ static int phy_remove(struct device *dev +@@ -3335,6 +3345,9 @@ static int phy_remove(struct device *dev cancel_delayed_work_sync(&phydev->state_queue); diff --git a/target/linux/generic/backport-6.1/826-v6.6-02-net-phy-phy_device-Call-into-the-PHY-driver-to-set-L.patch b/target/linux/generic/backport-6.1/826-v6.6-02-net-phy-phy_device-Call-into-the-PHY-driver-to-set-L.patch index 6cd798fc5..505513a53 100644 --- a/target/linux/generic/backport-6.1/826-v6.6-02-net-phy-phy_device-Call-into-the-PHY-driver-to-set-L.patch +++ b/target/linux/generic/backport-6.1/826-v6.6-02-net-phy-phy_device-Call-into-the-PHY-driver-to-set-L.patch @@ -23,7 +23,7 @@ Signed-off-by: Jakub Kicinski --- a/drivers/net/phy/phy_device.c +++ b/drivers/net/phy/phy_device.c -@@ -2964,6 +2964,61 @@ static int phy_led_blink_set(struct led_ +@@ -3068,6 +3068,61 @@ static int phy_led_blink_set(struct led_ return err; } @@ -85,7 +85,7 @@ Signed-off-by: Jakub Kicinski static void phy_leds_unregister(struct phy_device *phydev) { struct phy_led *phyled; -@@ -3001,6 +3056,19 @@ static int of_phy_led(struct phy_device +@@ -3105,6 +3160,19 @@ static int of_phy_led(struct phy_device cdev->brightness_set_blocking = phy_led_set_brightness; if (phydev->drv->led_blink_set) cdev->blink_set = phy_led_blink_set; @@ -107,7 +107,7 @@ Signed-off-by: Jakub Kicinski init_data.fwnode = of_fwnode_handle(led); --- a/include/linux/phy.h +++ b/include/linux/phy.h -@@ -1013,6 +1013,39 @@ struct phy_driver { +@@ -1026,6 +1026,39 @@ struct phy_driver { int (*led_blink_set)(struct phy_device *dev, u8 index, unsigned long *delay_on, unsigned long *delay_off); diff --git a/target/linux/generic/backport-6.1/831-v6.7-rtc-rtc7301-Support-byte-addressed-IO.patch b/target/linux/generic/backport-6.1/831-v6.7-rtc-rtc7301-Support-byte-addressed-IO.patch new file mode 100644 index 000000000..ddda6e4e7 --- /dev/null +++ b/target/linux/generic/backport-6.1/831-v6.7-rtc-rtc7301-Support-byte-addressed-IO.patch @@ -0,0 +1,93 @@ +From edd25a77e69b7c546c28077e5dffe72c54c0afe8 Mon Sep 17 00:00:00 2001 +From: Linus Walleij +Date: Thu, 21 Sep 2023 22:18:12 +0200 +Subject: [PATCH 2/4] rtc: rtc7301: Support byte-addressed IO + +The old RTC7301 driver in OpenWrt used byte access, but the +current mainline Linux driver uses 32bit word access. + +Make this configurable using device properties using the +standard property "reg-io-width" in e.g. device tree. + +This is needed for the USRobotics USR8200 which has the +chip connected using byte accesses. + +Debugging and testing by Howard Harte. + +Signed-off-by: Linus Walleij +--- + drivers/rtc/rtc-r7301.c | 35 +++++++++++++++++++++++++++++++++-- + 1 file changed, 33 insertions(+), 2 deletions(-) + +--- a/drivers/rtc/rtc-r7301.c ++++ b/drivers/rtc/rtc-r7301.c +@@ -14,6 +14,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -55,12 +56,23 @@ struct rtc7301_priv { + u8 bank; + }; + +-static const struct regmap_config rtc7301_regmap_config = { ++/* ++ * When the device is memory-mapped, some platforms pack the registers into ++ * 32-bit access using the lower 8 bits at each 4-byte stride, while others ++ * expose them as simply consecutive bytes. ++ */ ++static const struct regmap_config rtc7301_regmap_32_config = { + .reg_bits = 32, + .val_bits = 8, + .reg_stride = 4, + }; + ++static const struct regmap_config rtc7301_regmap_8_config = { ++ .reg_bits = 8, ++ .val_bits = 8, ++ .reg_stride = 1, ++}; ++ + static u8 rtc7301_read(struct rtc7301_priv *priv, unsigned int reg) + { + int reg_stride = regmap_get_reg_stride(priv->regmap); +@@ -356,7 +368,9 @@ static int __init rtc7301_rtc_probe(stru + void __iomem *regs; + struct rtc7301_priv *priv; + struct rtc_device *rtc; ++ static const struct regmap_config *mapconf; + int ret; ++ u32 val; + + priv = devm_kzalloc(&dev->dev, sizeof(*priv), GFP_KERNEL); + if (!priv) +@@ -366,8 +380,25 @@ static int __init rtc7301_rtc_probe(stru + if (IS_ERR(regs)) + return PTR_ERR(regs); + ++ ret = device_property_read_u32(&dev->dev, "reg-io-width", &val); ++ if (ret) ++ /* Default to 32bit accesses */ ++ val = 4; ++ ++ switch (val) { ++ case 1: ++ mapconf = &rtc7301_regmap_8_config; ++ break; ++ case 4: ++ mapconf = &rtc7301_regmap_32_config; ++ break; ++ default: ++ dev_err(&dev->dev, "invalid reg-io-width %d\n", val); ++ return -EINVAL; ++ } ++ + priv->regmap = devm_regmap_init_mmio(&dev->dev, regs, +- &rtc7301_regmap_config); ++ mapconf); + if (IS_ERR(priv->regmap)) + return PTR_ERR(priv->regmap); + diff --git a/target/linux/generic/backport-6.1/832-v6.7-net-phy-amd-Support-the-Altima-AMI101L.patch b/target/linux/generic/backport-6.1/832-v6.7-net-phy-amd-Support-the-Altima-AMI101L.patch new file mode 100644 index 000000000..e8e73c1e5 --- /dev/null +++ b/target/linux/generic/backport-6.1/832-v6.7-net-phy-amd-Support-the-Altima-AMI101L.patch @@ -0,0 +1,82 @@ +From 49e5663b505070424e18099841943f34342aa405 Mon Sep 17 00:00:00 2001 +From: Linus Walleij +Date: Sun, 24 Sep 2023 01:09:01 +0200 +Subject: [PATCH] net: phy: amd: Support the Altima AMI101L + +The Altima AC101L is obviously compatible with the AMD PHY, +as seen by reading the datasheet. + +Datasheet: https://docs.broadcom.com/doc/AC101L-DS05-405-RDS.pdf + +Signed-off-by: Linus Walleij +--- + drivers/net/phy/Kconfig | 4 ++-- + drivers/net/phy/amd.c | 33 +++++++++++++++++++++++---------- + 2 files changed, 25 insertions(+), 12 deletions(-) + +--- a/drivers/net/phy/Kconfig ++++ b/drivers/net/phy/Kconfig +@@ -77,9 +77,9 @@ config AIR_EN8811H_PHY + Currently supports the Airoha EN8811H PHY. + + config AMD_PHY +- tristate "AMD PHYs" ++ tristate "AMD and Altima PHYs" + help +- Currently supports the am79c874 ++ Currently supports the AMD am79c874 and Altima AC101L. + + config MESON_GXL_PHY + tristate "Amlogic Meson GXL Internal PHY" +--- a/drivers/net/phy/amd.c ++++ b/drivers/net/phy/amd.c +@@ -13,6 +13,7 @@ + #include + #include + ++#define PHY_ID_AC101L 0x00225520 + #define PHY_ID_AM79C874 0x0022561b + + #define MII_AM79C_IR 17 /* Interrupt Status/Control Register */ +@@ -87,19 +88,31 @@ static irqreturn_t am79c_handle_interrup + return IRQ_HANDLED; + } + +-static struct phy_driver am79c_driver[] = { { +- .phy_id = PHY_ID_AM79C874, +- .name = "AM79C874", +- .phy_id_mask = 0xfffffff0, +- /* PHY_BASIC_FEATURES */ +- .config_init = am79c_config_init, +- .config_intr = am79c_config_intr, +- .handle_interrupt = am79c_handle_interrupt, +-} }; ++static struct phy_driver am79c_drivers[] = { ++ { ++ .phy_id = PHY_ID_AM79C874, ++ .name = "AM79C874", ++ .phy_id_mask = 0xfffffff0, ++ /* PHY_BASIC_FEATURES */ ++ .config_init = am79c_config_init, ++ .config_intr = am79c_config_intr, ++ .handle_interrupt = am79c_handle_interrupt, ++ }, ++ { ++ .phy_id = PHY_ID_AC101L, ++ .name = "AC101L", ++ .phy_id_mask = 0xfffffff0, ++ /* PHY_BASIC_FEATURES */ ++ .config_init = am79c_config_init, ++ .config_intr = am79c_config_intr, ++ .handle_interrupt = am79c_handle_interrupt, ++ }, ++}; + +-module_phy_driver(am79c_driver); ++module_phy_driver(am79c_drivers); + + static struct mdio_device_id __maybe_unused amd_tbl[] = { ++ { PHY_ID_AC101L, 0xfffffff0 }, + { PHY_ID_AM79C874, 0xfffffff0 }, + { } + }; diff --git a/target/linux/generic/backport-6.1/833-v6.8-leds-core-Add-more-colors-from-DT-bindings-to-led_co.patch.patch b/target/linux/generic/backport-6.1/833-v6.8-leds-core-Add-more-colors-from-DT-bindings-to-led_co.patch.patch new file mode 100644 index 000000000..b71df6fa5 --- /dev/null +++ b/target/linux/generic/backport-6.1/833-v6.8-leds-core-Add-more-colors-from-DT-bindings-to-led_co.patch.patch @@ -0,0 +1,29 @@ +From a067943129b4ec6b835e02cfd5fbef01093c1471 Mon Sep 17 00:00:00 2001 +From: Ondrej Jirman +Date: Sun, 8 Oct 2023 16:40:13 +0200 +Subject: [PATCH] leds: core: Add more colors from DT bindings to led_colors + +The colors are already part of DT bindings. Make sure the kernel is +able to convert them to strings. + +Signed-off-by: Ondrej Jirman +Link: https://lore.kernel.org/r/20231008144014.1180334-1-megi@xff.cz +Signed-off-by: Lee Jones +--- + drivers/leds/led-core.c | 5 +++++ + 1 file changed, 5 insertions(+) + +--- a/drivers/leds/led-core.c ++++ b/drivers/leds/led-core.c +@@ -36,6 +36,11 @@ const char * const led_colors[LED_COLOR_ + [LED_COLOR_ID_IR] = "ir", + [LED_COLOR_ID_MULTI] = "multicolor", + [LED_COLOR_ID_RGB] = "rgb", ++ [LED_COLOR_ID_PURPLE] = "purple", ++ [LED_COLOR_ID_ORANGE] = "orange", ++ [LED_COLOR_ID_PINK] = "pink", ++ [LED_COLOR_ID_CYAN] = "cyan", ++ [LED_COLOR_ID_LIME] = "lime", + }; + EXPORT_SYMBOL_GPL(led_colors); + diff --git a/target/linux/generic/backport-6.1/834-v6.8-leds-trigger-netdev-Extend-speeds-up-to-10G.patch b/target/linux/generic/backport-6.1/834-v6.8-leds-trigger-netdev-Extend-speeds-up-to-10G.patch new file mode 100644 index 000000000..1c8e014a1 --- /dev/null +++ b/target/linux/generic/backport-6.1/834-v6.8-leds-trigger-netdev-Extend-speeds-up-to-10G.patch @@ -0,0 +1,111 @@ +From bc8e1da69a68d9871773b657d18400a7941cbdef Mon Sep 17 00:00:00 2001 +From: Daniel Golle +Date: Tue, 28 Nov 2023 04:00:10 +0000 +Subject: [PATCH] leds: trigger: netdev: Extend speeds up to 10G + +Add 2.5G, 5G and 10G as available speeds to the netdev LED trigger. + +Signed-off-by: Daniel Golle +Reviewed-by: Andrew Lunn +Link: https://lore.kernel.org/r/99e7d3304c6bba7f4863a4a80764a869855f2085.1701143925.git.daniel@makrotopia.org +Signed-off-by: Lee Jones +--- + drivers/leds/trigger/ledtrig-netdev.c | 32 ++++++++++++++++++++++++++- + include/linux/leds.h | 3 +++ + 2 files changed, 34 insertions(+), 1 deletion(-) + +--- a/drivers/leds/trigger/ledtrig-netdev.c ++++ b/drivers/leds/trigger/ledtrig-netdev.c +@@ -99,6 +99,18 @@ static void set_baseline_state(struct le + trigger_data->link_speed == SPEED_1000) + blink_on = true; + ++ if (test_bit(TRIGGER_NETDEV_LINK_2500, &trigger_data->mode) && ++ trigger_data->link_speed == SPEED_2500) ++ blink_on = true; ++ ++ if (test_bit(TRIGGER_NETDEV_LINK_5000, &trigger_data->mode) && ++ trigger_data->link_speed == SPEED_5000) ++ blink_on = true; ++ ++ if (test_bit(TRIGGER_NETDEV_LINK_10000, &trigger_data->mode) && ++ trigger_data->link_speed == SPEED_10000) ++ blink_on = true; ++ + if (test_bit(TRIGGER_NETDEV_HALF_DUPLEX, &trigger_data->mode) && + trigger_data->duplex == DUPLEX_HALF) + blink_on = true; +@@ -286,6 +298,9 @@ static ssize_t netdev_led_attr_show(stru + case TRIGGER_NETDEV_LINK_10: + case TRIGGER_NETDEV_LINK_100: + case TRIGGER_NETDEV_LINK_1000: ++ case TRIGGER_NETDEV_LINK_2500: ++ case TRIGGER_NETDEV_LINK_5000: ++ case TRIGGER_NETDEV_LINK_10000: + case TRIGGER_NETDEV_HALF_DUPLEX: + case TRIGGER_NETDEV_FULL_DUPLEX: + case TRIGGER_NETDEV_TX: +@@ -316,6 +331,9 @@ static ssize_t netdev_led_attr_store(str + case TRIGGER_NETDEV_LINK_10: + case TRIGGER_NETDEV_LINK_100: + case TRIGGER_NETDEV_LINK_1000: ++ case TRIGGER_NETDEV_LINK_2500: ++ case TRIGGER_NETDEV_LINK_5000: ++ case TRIGGER_NETDEV_LINK_10000: + case TRIGGER_NETDEV_HALF_DUPLEX: + case TRIGGER_NETDEV_FULL_DUPLEX: + case TRIGGER_NETDEV_TX: +@@ -334,7 +352,10 @@ static ssize_t netdev_led_attr_store(str + if (test_bit(TRIGGER_NETDEV_LINK, &mode) && + (test_bit(TRIGGER_NETDEV_LINK_10, &mode) || + test_bit(TRIGGER_NETDEV_LINK_100, &mode) || +- test_bit(TRIGGER_NETDEV_LINK_1000, &mode))) ++ test_bit(TRIGGER_NETDEV_LINK_1000, &mode) || ++ test_bit(TRIGGER_NETDEV_LINK_2500, &mode) || ++ test_bit(TRIGGER_NETDEV_LINK_5000, &mode) || ++ test_bit(TRIGGER_NETDEV_LINK_10000, &mode))) + return -EINVAL; + + cancel_delayed_work_sync(&trigger_data->work); +@@ -364,6 +385,9 @@ DEFINE_NETDEV_TRIGGER(link, TRIGGER_NETD + DEFINE_NETDEV_TRIGGER(link_10, TRIGGER_NETDEV_LINK_10); + DEFINE_NETDEV_TRIGGER(link_100, TRIGGER_NETDEV_LINK_100); + DEFINE_NETDEV_TRIGGER(link_1000, TRIGGER_NETDEV_LINK_1000); ++DEFINE_NETDEV_TRIGGER(link_2500, TRIGGER_NETDEV_LINK_2500); ++DEFINE_NETDEV_TRIGGER(link_5000, TRIGGER_NETDEV_LINK_5000); ++DEFINE_NETDEV_TRIGGER(link_10000, TRIGGER_NETDEV_LINK_10000); + DEFINE_NETDEV_TRIGGER(half_duplex, TRIGGER_NETDEV_HALF_DUPLEX); + DEFINE_NETDEV_TRIGGER(full_duplex, TRIGGER_NETDEV_FULL_DUPLEX); + DEFINE_NETDEV_TRIGGER(tx, TRIGGER_NETDEV_TX); +@@ -422,6 +446,9 @@ static struct attribute *netdev_trig_att + &dev_attr_link_10.attr, + &dev_attr_link_100.attr, + &dev_attr_link_1000.attr, ++ &dev_attr_link_2500.attr, ++ &dev_attr_link_5000.attr, ++ &dev_attr_link_10000.attr, + &dev_attr_full_duplex.attr, + &dev_attr_half_duplex.attr, + &dev_attr_rx.attr, +@@ -519,6 +546,9 @@ static void netdev_trig_work(struct work + test_bit(TRIGGER_NETDEV_LINK_10, &trigger_data->mode) || + test_bit(TRIGGER_NETDEV_LINK_100, &trigger_data->mode) || + test_bit(TRIGGER_NETDEV_LINK_1000, &trigger_data->mode) || ++ test_bit(TRIGGER_NETDEV_LINK_2500, &trigger_data->mode) || ++ test_bit(TRIGGER_NETDEV_LINK_5000, &trigger_data->mode) || ++ test_bit(TRIGGER_NETDEV_LINK_10000, &trigger_data->mode) || + test_bit(TRIGGER_NETDEV_HALF_DUPLEX, &trigger_data->mode) || + test_bit(TRIGGER_NETDEV_FULL_DUPLEX, &trigger_data->mode); + interval = jiffies_to_msecs( +--- a/include/linux/leds.h ++++ b/include/linux/leds.h +@@ -533,6 +533,9 @@ enum led_trigger_netdev_modes { + TRIGGER_NETDEV_LINK_10, + TRIGGER_NETDEV_LINK_100, + TRIGGER_NETDEV_LINK_1000, ++ TRIGGER_NETDEV_LINK_2500, ++ TRIGGER_NETDEV_LINK_5000, ++ TRIGGER_NETDEV_LINK_10000, + TRIGGER_NETDEV_HALF_DUPLEX, + TRIGGER_NETDEV_FULL_DUPLEX, + TRIGGER_NETDEV_TX, diff --git a/target/linux/generic/backport-6.1/835-v6.9-net-phy-add-support-for-PHY-LEDs-polarity-modes.patch b/target/linux/generic/backport-6.1/835-v6.9-net-phy-add-support-for-PHY-LEDs-polarity-modes.patch new file mode 100644 index 000000000..0182e6d1a --- /dev/null +++ b/target/linux/generic/backport-6.1/835-v6.9-net-phy-add-support-for-PHY-LEDs-polarity-modes.patch @@ -0,0 +1,98 @@ +From 7ae215ee7bb855f13c80565470fc7f67db4ba82f Mon Sep 17 00:00:00 2001 +From: Christian Marangi +Date: Thu, 25 Jan 2024 21:36:59 +0100 +Subject: [PATCH 3/5] net: phy: add support for PHY LEDs polarity modes + +Add support for PHY LEDs polarity modes. Some PHY require LED to be set +to active low to be turned ON. Adds support for this by declaring +active-low property in DT. + +PHY driver needs to declare .led_polarity_set() to configure LED +polarity modes. Function will pass the index with the LED index and a +bitmap with all the required modes to set. + +Current supported modes are: +- active-low with the flag PHY_LED_ACTIVE_LOW. LED is set to active-low + to turn it ON. +- inactive-high-impedance with the flag PHY_LED_INACTIVE_HIGH_IMPEDANCE. + LED is set to high impedance to turn it OFF. + +Signed-off-by: Christian Marangi +Reviewed-by: Andrew Lunn +Link: https://lore.kernel.org/r/20240125203702.4552-4-ansuelsmth@gmail.com +Signed-off-by: Jakub Kicinski +--- + drivers/net/phy/phy_device.c | 16 ++++++++++++++++ + include/linux/phy.h | 22 ++++++++++++++++++++++ + 2 files changed, 38 insertions(+) + +--- a/drivers/net/phy/phy_device.c ++++ b/drivers/net/phy/phy_device.c +@@ -3138,6 +3138,7 @@ static int of_phy_led(struct phy_device + struct device *dev = &phydev->mdio.dev; + struct led_init_data init_data = {}; + struct led_classdev *cdev; ++ unsigned long modes = 0; + struct phy_led *phyled; + u32 index; + int err; +@@ -3155,6 +3156,21 @@ static int of_phy_led(struct phy_device + if (index > U8_MAX) + return -EINVAL; + ++ if (of_property_read_bool(led, "active-low")) ++ set_bit(PHY_LED_ACTIVE_LOW, &modes); ++ if (of_property_read_bool(led, "inactive-high-impedance")) ++ set_bit(PHY_LED_INACTIVE_HIGH_IMPEDANCE, &modes); ++ ++ if (modes) { ++ /* Return error if asked to set polarity modes but not supported */ ++ if (!phydev->drv->led_polarity_set) ++ return -EINVAL; ++ ++ err = phydev->drv->led_polarity_set(phydev, index, modes); ++ if (err) ++ return err; ++ } ++ + phyled->index = index; + if (phydev->drv->led_brightness_set) + cdev->brightness_set_blocking = phy_led_set_brightness; +--- a/include/linux/phy.h ++++ b/include/linux/phy.h +@@ -791,6 +791,15 @@ struct phy_led { + + #define to_phy_led(d) container_of(d, struct phy_led, led_cdev) + ++/* Modes for PHY LED configuration */ ++enum phy_led_modes { ++ PHY_LED_ACTIVE_LOW = 0, ++ PHY_LED_INACTIVE_HIGH_IMPEDANCE = 1, ++ ++ /* keep it last */ ++ __PHY_LED_MODES_NUM, ++}; ++ + /** + * struct phy_driver - Driver structure for a particular PHY type + * +@@ -1059,6 +1068,19 @@ struct phy_driver { + int (*led_hw_control_get)(struct phy_device *dev, u8 index, + unsigned long *rules); + ++ /** ++ * @led_polarity_set: Set the LED polarity modes ++ * @dev: PHY device which has the LED ++ * @index: Which LED of the PHY device ++ * @modes: bitmap of LED polarity modes ++ * ++ * Configure LED with all the required polarity modes in @modes ++ * to make it correctly turn ON or OFF. ++ * ++ * Returns 0, or an error code. ++ */ ++ int (*led_polarity_set)(struct phy_device *dev, int index, ++ unsigned long modes); + }; + #define to_phy_driver(d) container_of(to_mdio_common_driver(d), \ + struct phy_driver, mdiodrv) diff --git a/target/linux/generic/backport-6.1/836-v6.7-leds-trigger-netdev-fix-RTNL-handling-to-prevent-pot.patch b/target/linux/generic/backport-6.1/836-v6.7-leds-trigger-netdev-fix-RTNL-handling-to-prevent-pot.patch new file mode 100644 index 000000000..cbb917203 --- /dev/null +++ b/target/linux/generic/backport-6.1/836-v6.7-leds-trigger-netdev-fix-RTNL-handling-to-prevent-pot.patch @@ -0,0 +1,170 @@ +From fe2b1226656afae56702d1d84c6900f6b67df297 Mon Sep 17 00:00:00 2001 +From: Heiner Kallweit +Date: Fri, 1 Dec 2023 11:23:22 +0100 +Subject: [PATCH] leds: trigger: netdev: fix RTNL handling to prevent potential + deadlock + +When working on LED support for r8169 I got the following lockdep +warning. Easiest way to prevent this scenario seems to be to take +the RTNL lock before the trigger_data lock in set_device_name(). + +====================================================== +WARNING: possible circular locking dependency detected +6.7.0-rc2-next-20231124+ #2 Not tainted +------------------------------------------------------ +bash/383 is trying to acquire lock: +ffff888103aa1c68 (&trigger_data->lock){+.+.}-{3:3}, at: netdev_trig_notify+0xec/0x190 [ledtrig_netdev] + +but task is already holding lock: +ffffffff8cddf808 (rtnl_mutex){+.+.}-{3:3}, at: rtnl_lock+0x12/0x20 + +which lock already depends on the new lock. + +the existing dependency chain (in reverse order) is: + +-> #1 (rtnl_mutex){+.+.}-{3:3}: + __mutex_lock+0x9b/0xb50 + mutex_lock_nested+0x16/0x20 + rtnl_lock+0x12/0x20 + set_device_name+0xa9/0x120 [ledtrig_netdev] + netdev_trig_activate+0x1a1/0x230 [ledtrig_netdev] + led_trigger_set+0x172/0x2c0 + led_trigger_write+0xf1/0x140 + sysfs_kf_bin_write+0x5d/0x80 + kernfs_fop_write_iter+0x15d/0x210 + vfs_write+0x1f0/0x510 + ksys_write+0x6c/0xf0 + __x64_sys_write+0x14/0x20 + do_syscall_64+0x3f/0xf0 + entry_SYSCALL_64_after_hwframe+0x6c/0x74 + +-> #0 (&trigger_data->lock){+.+.}-{3:3}: + __lock_acquire+0x1459/0x25a0 + lock_acquire+0xc8/0x2d0 + __mutex_lock+0x9b/0xb50 + mutex_lock_nested+0x16/0x20 + netdev_trig_notify+0xec/0x190 [ledtrig_netdev] + call_netdevice_register_net_notifiers+0x5a/0x100 + register_netdevice_notifier+0x85/0x120 + netdev_trig_activate+0x1d4/0x230 [ledtrig_netdev] + led_trigger_set+0x172/0x2c0 + led_trigger_write+0xf1/0x140 + sysfs_kf_bin_write+0x5d/0x80 + kernfs_fop_write_iter+0x15d/0x210 + vfs_write+0x1f0/0x510 + ksys_write+0x6c/0xf0 + __x64_sys_write+0x14/0x20 + do_syscall_64+0x3f/0xf0 + entry_SYSCALL_64_after_hwframe+0x6c/0x74 + +other info that might help us debug this: + + Possible unsafe locking scenario: + + CPU0 CPU1 + ---- ---- + lock(rtnl_mutex); + lock(&trigger_data->lock); + lock(rtnl_mutex); + lock(&trigger_data->lock); + + *** DEADLOCK *** + +8 locks held by bash/383: + #0: ffff888103ff33f0 (sb_writers#3){.+.+}-{0:0}, at: ksys_write+0x6c/0xf0 + #1: ffff888103aa1e88 (&of->mutex){+.+.}-{3:3}, at: kernfs_fop_write_iter+0x114/0x210 + #2: ffff8881036f1890 (kn->active#82){.+.+}-{0:0}, at: kernfs_fop_write_iter+0x11d/0x210 + #3: ffff888108e2c358 (&led_cdev->led_access){+.+.}-{3:3}, at: led_trigger_write+0x30/0x140 + #4: ffffffff8cdd9e10 (triggers_list_lock){++++}-{3:3}, at: led_trigger_write+0x75/0x140 + #5: ffff888108e2c270 (&led_cdev->trigger_lock){++++}-{3:3}, at: led_trigger_write+0xe3/0x140 + #6: ffffffff8cdde3d0 (pernet_ops_rwsem){++++}-{3:3}, at: register_netdevice_notifier+0x1c/0x120 + #7: ffffffff8cddf808 (rtnl_mutex){+.+.}-{3:3}, at: rtnl_lock+0x12/0x20 + +stack backtrace: +CPU: 0 PID: 383 Comm: bash Not tainted 6.7.0-rc2-next-20231124+ #2 +Hardware name: Default string Default string/Default string, BIOS ADLN.M6.SODIMM.ZB.CY.015 08/08/2023 +Call Trace: + + dump_stack_lvl+0x5c/0xd0 + dump_stack+0x10/0x20 + print_circular_bug+0x2dd/0x410 + check_noncircular+0x131/0x150 + __lock_acquire+0x1459/0x25a0 + lock_acquire+0xc8/0x2d0 + ? netdev_trig_notify+0xec/0x190 [ledtrig_netdev] + __mutex_lock+0x9b/0xb50 + ? netdev_trig_notify+0xec/0x190 [ledtrig_netdev] + ? __this_cpu_preempt_check+0x13/0x20 + ? netdev_trig_notify+0xec/0x190 [ledtrig_netdev] + ? __cancel_work_timer+0x11c/0x1b0 + ? __mutex_lock+0x123/0xb50 + mutex_lock_nested+0x16/0x20 + ? mutex_lock_nested+0x16/0x20 + netdev_trig_notify+0xec/0x190 [ledtrig_netdev] + call_netdevice_register_net_notifiers+0x5a/0x100 + register_netdevice_notifier+0x85/0x120 + netdev_trig_activate+0x1d4/0x230 [ledtrig_netdev] + led_trigger_set+0x172/0x2c0 + ? preempt_count_add+0x49/0xc0 + led_trigger_write+0xf1/0x140 + sysfs_kf_bin_write+0x5d/0x80 + kernfs_fop_write_iter+0x15d/0x210 + vfs_write+0x1f0/0x510 + ksys_write+0x6c/0xf0 + __x64_sys_write+0x14/0x20 + do_syscall_64+0x3f/0xf0 + entry_SYSCALL_64_after_hwframe+0x6c/0x74 +RIP: 0033:0x7f269055d034 +Code: c7 00 16 00 00 00 b8 ff ff ff ff c3 66 2e 0f 1f 84 00 00 00 00 00 f3 0f 1e fa 80 3d 35 c3 0d 00 00 74 13 b8 01 00 00 00 0f 05 <48> 3d 00 f0 ff ff 77 54 c3 0f 1f 00 48 83 ec 28 48 89 54 24 18 48 +RSP: 002b:00007ffddb7ef748 EFLAGS: 00000202 ORIG_RAX: 0000000000000001 +RAX: ffffffffffffffda RBX: 0000000000000007 RCX: 00007f269055d034 +RDX: 0000000000000007 RSI: 000055bf5f4af3c0 RDI: 0000000000000001 +RBP: 000055bf5f4af3c0 R08: 0000000000000073 R09: 0000000000000001 +R10: 0000000000000000 R11: 0000000000000202 R12: 0000000000000007 +R13: 00007f26906325c0 R14: 00007f269062ff20 R15: 0000000000000000 + + +Fixes: d5e01266e7f5 ("leds: trigger: netdev: add additional specific link speed mode") +Cc: stable@vger.kernel.org +Signed-off-by: Heiner Kallweit +Reviewed-by: Andrew Lunn +Acked-by: Lee Jones +Link: https://lore.kernel.org/r/fb5c8294-2a10-4bf5-8f10-3d2b77d2757e@gmail.com +Signed-off-by: Jakub Kicinski +--- + drivers/leds/trigger/ledtrig-netdev.c | 11 +++++++---- + 1 file changed, 7 insertions(+), 4 deletions(-) + +--- a/drivers/leds/trigger/ledtrig-netdev.c ++++ b/drivers/leds/trigger/ledtrig-netdev.c +@@ -235,6 +235,11 @@ static int set_device_name(struct led_ne + { + cancel_delayed_work_sync(&trigger_data->work); + ++ /* ++ * Take RTNL lock before trigger_data lock to prevent potential ++ * deadlock with netdev notifier registration. ++ */ ++ rtnl_lock(); + mutex_lock(&trigger_data->lock); + + if (trigger_data->net_dev) { +@@ -254,16 +259,14 @@ static int set_device_name(struct led_ne + trigger_data->carrier_link_up = false; + trigger_data->link_speed = SPEED_UNKNOWN; + trigger_data->duplex = DUPLEX_UNKNOWN; +- if (trigger_data->net_dev != NULL) { +- rtnl_lock(); ++ if (trigger_data->net_dev) + get_device_state(trigger_data); +- rtnl_unlock(); +- } + + trigger_data->last_activity = 0; + + set_baseline_state(trigger_data); + mutex_unlock(&trigger_data->lock); ++ rtnl_unlock(); + + return 0; + } diff --git a/target/linux/generic/backport-6.1/837-v6.4-net-phy-hide-the-PHYLIB_LEDS-knob.patch b/target/linux/generic/backport-6.1/837-v6.4-net-phy-hide-the-PHYLIB_LEDS-knob.patch new file mode 100644 index 000000000..51b4bbbfc --- /dev/null +++ b/target/linux/generic/backport-6.1/837-v6.4-net-phy-hide-the-PHYLIB_LEDS-knob.patch @@ -0,0 +1,43 @@ +From 9b78d919632b7149d311aaad5a977e4b48b10321 Mon Sep 17 00:00:00 2001 +From: Paolo Abeni +Date: Wed, 26 Apr 2023 10:15:31 +0200 +Subject: [PATCH] net: phy: hide the PHYLIB_LEDS knob + +commit 4bb7aac70b5d ("net: phy: fix circular LEDS_CLASS dependencies") +solved a build failure, but introduces a new config knob with a default +'y' value: PHYLIB_LEDS. + +The latter is against the current new config policy. The exception +was raised to allow the user to catch bad configurations without led +support. + +Anyway the current definition of PHYLIB_LEDS does not fit the above +goal: if LEDS_CLASS is disabled, the new config will be available +only with PHYLIB disabled, too. + +Hide the mentioned config, to preserve the randconfig testing done so +far, while respecting the mentioned policy. + +Suggested-by: Andrew Lunn +Suggested-by: Arnd Bergmann +Signed-off-by: Paolo Abeni +Link: https://lore.kernel.org/r/d82489be8ed911c383c3447e9abf469995ccf39a.1682496488.git.pabeni@redhat.com +Signed-off-by: Paolo Abeni +--- + drivers/net/phy/Kconfig | 4 +--- + 1 file changed, 1 insertion(+), 3 deletions(-) + +--- a/drivers/net/phy/Kconfig ++++ b/drivers/net/phy/Kconfig +@@ -45,10 +45,8 @@ config LED_TRIGGER_PHY + for any speed known to the PHY. + + config PHYLIB_LEDS +- bool "Support probing LEDs from device tree" ++ def_bool OF + depends on LEDS_CLASS=y || LEDS_CLASS=PHYLIB +- depends on OF +- default y + help + When LED class support is enabled, phylib can automatically + probe LED setting from device tree. diff --git a/target/linux/generic/backport-6.1/838-v6.9-leds-trigger-netdev-Fix-kernel-panic-on-interface-re.patch b/target/linux/generic/backport-6.1/838-v6.9-leds-trigger-netdev-Fix-kernel-panic-on-interface-re.patch new file mode 100644 index 000000000..8d391678f --- /dev/null +++ b/target/linux/generic/backport-6.1/838-v6.9-leds-trigger-netdev-Fix-kernel-panic-on-interface-re.patch @@ -0,0 +1,55 @@ +From 12ce20e02e532f101b725d71c52a36c5cc8ad1e6 Mon Sep 17 00:00:00 2001 +From: Christian Marangi +Date: Sun, 4 Feb 2024 00:54:01 +0100 +Subject: [PATCH] leds: trigger: netdev: Fix kernel panic on interface rename + trig notify + +Commit d5e01266e7f5 ("leds: trigger: netdev: add additional specific link +speed mode") in the various changes, reworked the way to set the LINKUP +mode in commit cee4bd16c319 ("leds: trigger: netdev: Recheck +NETDEV_LED_MODE_LINKUP on dev rename") and moved it to a generic function. + +This changed the logic where, in the previous implementation the dev +from the trigger event was used to check if the carrier was ok, but in +the new implementation with the generic function, the dev in +trigger_data is used instead. + +This is problematic and cause a possible kernel panic due to the fact +that the dev in the trigger_data still reference the old one as the +new one (passed from the trigger event) still has to be hold and saved +in the trigger_data struct (done in the NETDEV_REGISTER case). + +On calling of get_device_state(), an invalid net_dev is used and this +cause a kernel panic. + +To handle this correctly, move the call to get_device_state() after the +new net_dev is correctly set in trigger_data (in the NETDEV_REGISTER +case) and correctly parse the new dev. + +Fixes: d5e01266e7f5 ("leds: trigger: netdev: add additional specific link speed mode") +Cc: stable@vger.kernel.org +Signed-off-by: Christian Marangi +Reviewed-by: Andrew Lunn +Link: https://lore.kernel.org/r/20240203235413.1146-1-ansuelsmth@gmail.com +Signed-off-by: Lee Jones +--- + drivers/leds/trigger/ledtrig-netdev.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +--- a/drivers/leds/trigger/ledtrig-netdev.c ++++ b/drivers/leds/trigger/ledtrig-netdev.c +@@ -489,12 +489,12 @@ static int netdev_trig_notify(struct not + trigger_data->duplex = DUPLEX_UNKNOWN; + switch (evt) { + case NETDEV_CHANGENAME: +- get_device_state(trigger_data); +- fallthrough; + case NETDEV_REGISTER: + dev_put(trigger_data->net_dev); + dev_hold(dev); + trigger_data->net_dev = dev; ++ if (evt == NETDEV_CHANGENAME) ++ get_device_state(trigger_data); + break; + case NETDEV_UNREGISTER: + dev_put(trigger_data->net_dev); diff --git a/target/linux/generic/backport-6.1/880-v6.3-mfd-axp20x-Fix-order-of-pek-rise-and-fall-events.patch b/target/linux/generic/backport-6.1/880-v6.3-mfd-axp20x-Fix-order-of-pek-rise-and-fall-events.patch new file mode 100644 index 000000000..23681ba93 --- /dev/null +++ b/target/linux/generic/backport-6.1/880-v6.3-mfd-axp20x-Fix-order-of-pek-rise-and-fall-events.patch @@ -0,0 +1,80 @@ +From 8781ba7f45695af3ab8e8d1b55a31f527c9201a3 Mon Sep 17 00:00:00 2001 +From: Aren Moynihan +Date: Thu, 8 Dec 2022 17:02:26 -0500 +Subject: [PATCH] mfd: axp20x: Fix order of pek rise and fall events + +The power button can get "stuck" if the rising edge and falling edge irq +are read in the same pass. This can often be triggered when resuming +from suspend if the power button is released before the kernel handles +the interrupt. + +Swapping the order of the rise and fall events makes sure that the press +event is handled first, which prevents this situation. + +Signed-off-by: Aren Moynihan +Reviewed-by: Samuel Holland +Tested-by: Samuel Holland +Acked-by: Chen-Yu Tsai +Signed-off-by: Lee Jones +Link: https://lore.kernel.org/r/20221208220225.635414-1-aren@peacevolution.org +--- + include/linux/mfd/axp20x.h | 15 ++++++++++----- + 1 file changed, 10 insertions(+), 5 deletions(-) + +--- a/include/linux/mfd/axp20x.h ++++ b/include/linux/mfd/axp20x.h +@@ -432,8 +432,9 @@ enum { + AXP152_IRQ_PEK_SHORT, + AXP152_IRQ_PEK_LONG, + AXP152_IRQ_TIMER, +- AXP152_IRQ_PEK_RIS_EDGE, ++ /* out of bit order to make sure the press event is handled first */ + AXP152_IRQ_PEK_FAL_EDGE, ++ AXP152_IRQ_PEK_RIS_EDGE, + AXP152_IRQ_GPIO3_INPUT, + AXP152_IRQ_GPIO2_INPUT, + AXP152_IRQ_GPIO1_INPUT, +@@ -472,8 +473,9 @@ enum { + AXP20X_IRQ_LOW_PWR_LVL1, + AXP20X_IRQ_LOW_PWR_LVL2, + AXP20X_IRQ_TIMER, +- AXP20X_IRQ_PEK_RIS_EDGE, ++ /* out of bit order to make sure the press event is handled first */ + AXP20X_IRQ_PEK_FAL_EDGE, ++ AXP20X_IRQ_PEK_RIS_EDGE, + AXP20X_IRQ_GPIO3_INPUT, + AXP20X_IRQ_GPIO2_INPUT, + AXP20X_IRQ_GPIO1_INPUT, +@@ -502,8 +504,9 @@ enum axp22x_irqs { + AXP22X_IRQ_LOW_PWR_LVL1, + AXP22X_IRQ_LOW_PWR_LVL2, + AXP22X_IRQ_TIMER, +- AXP22X_IRQ_PEK_RIS_EDGE, ++ /* out of bit order to make sure the press event is handled first */ + AXP22X_IRQ_PEK_FAL_EDGE, ++ AXP22X_IRQ_PEK_RIS_EDGE, + AXP22X_IRQ_GPIO1_INPUT, + AXP22X_IRQ_GPIO0_INPUT, + }; +@@ -571,8 +574,9 @@ enum axp803_irqs { + AXP803_IRQ_LOW_PWR_LVL1, + AXP803_IRQ_LOW_PWR_LVL2, + AXP803_IRQ_TIMER, +- AXP803_IRQ_PEK_RIS_EDGE, ++ /* out of bit order to make sure the press event is handled first */ + AXP803_IRQ_PEK_FAL_EDGE, ++ AXP803_IRQ_PEK_RIS_EDGE, + AXP803_IRQ_PEK_SHORT, + AXP803_IRQ_PEK_LONG, + AXP803_IRQ_PEK_OVER_OFF, +@@ -623,8 +627,9 @@ enum axp809_irqs { + AXP809_IRQ_LOW_PWR_LVL1, + AXP809_IRQ_LOW_PWR_LVL2, + AXP809_IRQ_TIMER, +- AXP809_IRQ_PEK_RIS_EDGE, ++ /* out of bit order to make sure the press event is handled first */ + AXP809_IRQ_PEK_FAL_EDGE, ++ AXP809_IRQ_PEK_RIS_EDGE, + AXP809_IRQ_PEK_SHORT, + AXP809_IRQ_PEK_LONG, + AXP809_IRQ_PEK_OVER_OFF, diff --git a/target/linux/generic/backport-6.1/880-v6.3-mfd-axp20x-Switch-to-the-sys-off-handler-API.patch b/target/linux/generic/backport-6.1/881-v6.3-mfd-axp20x-Switch-to-the-sys-off-handler-API.patch similarity index 100% rename from target/linux/generic/backport-6.1/880-v6.3-mfd-axp20x-Switch-to-the-sys-off-handler-API.patch rename to target/linux/generic/backport-6.1/881-v6.3-mfd-axp20x-Switch-to-the-sys-off-handler-API.patch diff --git a/target/linux/generic/backport-6.1/882-v6.3-mfd-axp20x-Fix-axp288-writable-ranges.patch b/target/linux/generic/backport-6.1/882-v6.3-mfd-axp20x-Fix-axp288-writable-ranges.patch new file mode 100644 index 000000000..b84815e1a --- /dev/null +++ b/target/linux/generic/backport-6.1/882-v6.3-mfd-axp20x-Fix-axp288-writable-ranges.patch @@ -0,0 +1,28 @@ +From 2405fbfb384ef39e9560d76d3f6e4c90519f90aa Mon Sep 17 00:00:00 2001 +From: Hans de Goede +Date: Wed, 29 Mar 2023 22:55:44 +0200 +Subject: [PATCH] mfd: axp20x: Fix axp288 writable-ranges + +Register AXP288_POWER_REASON is writable and needs to be written +to reset the reset- / power-on-reason bits. + +Add it to the axp288 writable-ranges so that the extcon-axp288 +driver can properly clear the reset- / power-on-reason bits. + +Signed-off-by: Hans de Goede +Signed-off-by: Lee Jones +Link: https://lore.kernel.org/r/20230329205544.1051393-1-hdegoede@redhat.com +--- + drivers/mfd/axp20x.c | 1 + + 1 file changed, 1 insertion(+) + +--- a/drivers/mfd/axp20x.c ++++ b/drivers/mfd/axp20x.c +@@ -119,6 +119,7 @@ static const struct regmap_access_table + + /* AXP288 ranges are shared with the AXP803, as they cover the same range */ + static const struct regmap_range axp288_writeable_ranges[] = { ++ regmap_reg_range(AXP288_POWER_REASON, AXP288_POWER_REASON), + regmap_reg_range(AXP20X_DATACACHE(0), AXP20X_IRQ6_STATE), + regmap_reg_range(AXP20X_DCDC_MODE, AXP288_FG_TUNE5), + }; diff --git a/target/linux/generic/backport-6.1/881-v6.4-mfd-axp20x-Add-support-for-AXP15060-PMIC.patch b/target/linux/generic/backport-6.1/883-v6.4-mfd-axp20x-Add-support-for-AXP15060-PMIC.patch similarity index 95% rename from target/linux/generic/backport-6.1/881-v6.4-mfd-axp20x-Add-support-for-AXP15060-PMIC.patch rename to target/linux/generic/backport-6.1/883-v6.4-mfd-axp20x-Add-support-for-AXP15060-PMIC.patch index ec8d38a18..83ef60aec 100644 --- a/target/linux/generic/backport-6.1/881-v6.4-mfd-axp20x-Add-support-for-AXP15060-PMIC.patch +++ b/target/linux/generic/backport-6.1/883-v6.4-mfd-axp20x-Add-support-for-AXP15060-PMIC.patch @@ -57,7 +57,7 @@ Link: https://lore.kernel.org/r/TY3P286MB261162D57695AC8164ED50E298609@TY3P286MB }; static const struct regmap_range axp152_writeable_ranges[] = { -@@ -168,6 +169,31 @@ static const struct regmap_access_table +@@ -169,6 +170,31 @@ static const struct regmap_access_table .n_yes_ranges = ARRAY_SIZE(axp806_volatile_ranges), }; @@ -89,7 +89,7 @@ Link: https://lore.kernel.org/r/TY3P286MB261162D57695AC8164ED50E298609@TY3P286MB static const struct resource axp152_pek_resources[] = { DEFINE_RES_IRQ_NAMED(AXP152_IRQ_PEK_RIS_EDGE, "PEK_DBR"), DEFINE_RES_IRQ_NAMED(AXP152_IRQ_PEK_FAL_EDGE, "PEK_DBF"), -@@ -236,6 +262,11 @@ static const struct resource axp809_pek_ +@@ -237,6 +263,11 @@ static const struct resource axp809_pek_ DEFINE_RES_IRQ_NAMED(AXP809_IRQ_PEK_FAL_EDGE, "PEK_DBF"), }; @@ -101,7 +101,7 @@ Link: https://lore.kernel.org/r/TY3P286MB261162D57695AC8164ED50E298609@TY3P286MB static const struct regmap_config axp152_regmap_config = { .reg_bits = 8, .val_bits = 8, -@@ -281,6 +312,15 @@ static const struct regmap_config axp806 +@@ -282,6 +313,15 @@ static const struct regmap_config axp806 .cache_type = REGCACHE_RBTREE, }; @@ -117,7 +117,7 @@ Link: https://lore.kernel.org/r/TY3P286MB261162D57695AC8164ED50E298609@TY3P286MB #define INIT_REGMAP_IRQ(_variant, _irq, _off, _mask) \ [_variant##_IRQ_##_irq] = { .reg_offset = (_off), .mask = BIT(_mask) } -@@ -502,6 +542,23 @@ static const struct regmap_irq axp809_re +@@ -503,6 +543,23 @@ static const struct regmap_irq axp809_re INIT_REGMAP_IRQ(AXP809, GPIO0_INPUT, 4, 0), }; @@ -141,7 +141,7 @@ Link: https://lore.kernel.org/r/TY3P286MB261162D57695AC8164ED50E298609@TY3P286MB static const struct regmap_irq_chip axp152_regmap_irq_chip = { .name = "axp152_irq_chip", .status_base = AXP152_IRQ1_STATE, -@@ -588,6 +645,17 @@ static const struct regmap_irq_chip axp8 +@@ -589,6 +646,17 @@ static const struct regmap_irq_chip axp8 .num_regs = 5, }; @@ -159,7 +159,7 @@ Link: https://lore.kernel.org/r/TY3P286MB261162D57695AC8164ED50E298609@TY3P286MB static const struct mfd_cell axp20x_cells[] = { { .name = "axp20x-gpio", -@@ -832,6 +900,23 @@ static const struct mfd_cell axp813_cell +@@ -833,6 +901,23 @@ static const struct mfd_cell axp813_cell }, }; @@ -183,7 +183,7 @@ Link: https://lore.kernel.org/r/TY3P286MB261162D57695AC8164ED50E298609@TY3P286MB static int axp20x_power_off(struct sys_off_data *data) { struct axp20x_dev *axp20x = data->cb_data; -@@ -941,6 +1026,28 @@ int axp20x_match_device(struct axp20x_de +@@ -942,6 +1027,28 @@ int axp20x_match_device(struct axp20x_de */ axp20x->regmap_irq_chip = &axp803_regmap_irq_chip; break; @@ -317,7 +317,7 @@ Link: https://lore.kernel.org/r/TY3P286MB261162D57695AC8164ED50E298609@TY3P286MB /* IRQs */ enum { AXP152_IRQ_LDO0IN_CONNECT = 1, -@@ -632,6 +700,23 @@ enum axp809_irqs { +@@ -637,6 +705,23 @@ enum axp809_irqs { AXP809_IRQ_GPIO0_INPUT, }; diff --git a/target/linux/generic/backport-6.1/882-v6.5-mfd-axp20x-Add-support-for-AXP313a-PMIC.patch b/target/linux/generic/backport-6.1/884-v6.5-mfd-axp20x-Add-support-for-AXP313a-PMIC.patch similarity index 93% rename from target/linux/generic/backport-6.1/882-v6.5-mfd-axp20x-Add-support-for-AXP313a-PMIC.patch rename to target/linux/generic/backport-6.1/884-v6.5-mfd-axp20x-Add-support-for-AXP313a-PMIC.patch index 9fe70c103..c9470eace 100644 --- a/target/linux/generic/backport-6.1/882-v6.5-mfd-axp20x-Add-support-for-AXP313a-PMIC.patch +++ b/target/linux/generic/backport-6.1/884-v6.5-mfd-axp20x-Add-support-for-AXP313a-PMIC.patch @@ -58,7 +58,7 @@ Signed-off-by: Lee Jones "AXP803", "AXP806", "AXP809", -@@ -155,6 +156,25 @@ static const struct regmap_range axp806_ +@@ -156,6 +157,25 @@ static const struct regmap_range axp806_ regmap_reg_range(AXP806_REG_ADDR_EXT, AXP806_REG_ADDR_EXT), }; @@ -84,7 +84,7 @@ Signed-off-by: Lee Jones static const struct regmap_range axp806_volatile_ranges[] = { regmap_reg_range(AXP20X_IRQ1_STATE, AXP20X_IRQ2_STATE), }; -@@ -247,6 +267,11 @@ static const struct resource axp288_fuel +@@ -248,6 +268,11 @@ static const struct resource axp288_fuel DEFINE_RES_IRQ(AXP288_IRQ_WL1), }; @@ -96,7 +96,7 @@ Signed-off-by: Lee Jones static const struct resource axp803_pek_resources[] = { DEFINE_RES_IRQ_NAMED(AXP803_IRQ_PEK_RIS_EDGE, "PEK_DBR"), DEFINE_RES_IRQ_NAMED(AXP803_IRQ_PEK_FAL_EDGE, "PEK_DBF"), -@@ -303,6 +328,15 @@ static const struct regmap_config axp288 +@@ -304,6 +329,15 @@ static const struct regmap_config axp288 .cache_type = REGCACHE_RBTREE, }; @@ -112,7 +112,7 @@ Signed-off-by: Lee Jones static const struct regmap_config axp806_regmap_config = { .reg_bits = 8, .val_bits = 8, -@@ -455,6 +489,16 @@ static const struct regmap_irq axp288_re +@@ -456,6 +490,16 @@ static const struct regmap_irq axp288_re INIT_REGMAP_IRQ(AXP288, BC_USB_CHNG, 5, 1), }; @@ -129,7 +129,7 @@ Signed-off-by: Lee Jones static const struct regmap_irq axp803_regmap_irqs[] = { INIT_REGMAP_IRQ(AXP803, ACIN_OVER_V, 0, 7), INIT_REGMAP_IRQ(AXP803, ACIN_PLUGIN, 0, 6), -@@ -609,6 +653,17 @@ static const struct regmap_irq_chip axp2 +@@ -610,6 +654,17 @@ static const struct regmap_irq_chip axp2 }; @@ -147,7 +147,7 @@ Signed-off-by: Lee Jones static const struct regmap_irq_chip axp803_regmap_irq_chip = { .name = "axp803", .status_base = AXP20X_IRQ1_STATE, -@@ -751,6 +806,11 @@ static const struct mfd_cell axp152_cell +@@ -752,6 +807,11 @@ static const struct mfd_cell axp152_cell }, }; @@ -159,7 +159,7 @@ Signed-off-by: Lee Jones static const struct resource axp288_adc_resources[] = { DEFINE_RES_IRQ_NAMED(AXP288_IRQ_GPADC, "GPADC"), }; -@@ -920,8 +980,18 @@ static const struct mfd_cell axp_regulat +@@ -921,8 +981,18 @@ static const struct mfd_cell axp_regulat static int axp20x_power_off(struct sys_off_data *data) { struct axp20x_dev *axp20x = data->cb_data; @@ -179,7 +179,7 @@ Signed-off-by: Lee Jones /* Give capacitors etc. time to drain to avoid kernel panic msg. */ mdelay(500); -@@ -984,6 +1054,12 @@ int axp20x_match_device(struct axp20x_de +@@ -985,6 +1055,12 @@ int axp20x_match_device(struct axp20x_de axp20x->regmap_irq_chip = &axp288_regmap_irq_chip; axp20x->irq_flags = IRQF_TRIGGER_LOW; break; @@ -237,7 +237,7 @@ Signed-off-by: Lee Jones AXP806_DCDCA = 0, AXP806_DCDCB, AXP806_DCDCC, -@@ -613,6 +635,16 @@ enum axp288_irqs { +@@ -616,6 +638,16 @@ enum axp288_irqs { AXP288_IRQ_BC_USB_CHNG, }; diff --git a/target/linux/generic/backport-6.1/883-v6.5-regulator-axp20x-Add-support-for-AXP313a-variant.patch b/target/linux/generic/backport-6.1/885-v6.5-regulator-axp20x-Add-support-for-AXP313a-variant.patch similarity index 100% rename from target/linux/generic/backport-6.1/883-v6.5-regulator-axp20x-Add-support-for-AXP313a-variant.patch rename to target/linux/generic/backport-6.1/885-v6.5-regulator-axp20x-Add-support-for-AXP313a-variant.patch diff --git a/target/linux/generic/backport-6.1/884-v6.5-regulator-axp20x-Add-AXP15060-support.patch b/target/linux/generic/backport-6.1/886-v6.5-regulator-axp20x-Add-AXP15060-support.patch similarity index 100% rename from target/linux/generic/backport-6.1/884-v6.5-regulator-axp20x-Add-AXP15060-support.patch rename to target/linux/generic/backport-6.1/886-v6.5-regulator-axp20x-Add-AXP15060-support.patch diff --git a/target/linux/generic/backport-6.1/885-v6.5-mfd-axp20x-Add-support-for-AXP192.patch b/target/linux/generic/backport-6.1/887-v6.5-mfd-axp20x-Add-support-for-AXP192.patch similarity index 96% rename from target/linux/generic/backport-6.1/885-v6.5-mfd-axp20x-Add-support-for-AXP192.patch rename to target/linux/generic/backport-6.1/887-v6.5-mfd-axp20x-Add-support-for-AXP192.patch index 20a26db3e..d75b4e745 100644 --- a/target/linux/generic/backport-6.1/885-v6.5-mfd-axp20x-Add-support-for-AXP192.patch +++ b/target/linux/generic/backport-6.1/887-v6.5-mfd-axp20x-Add-support-for-AXP192.patch @@ -79,7 +79,7 @@ Signed-off-by: Lee Jones /* AXP22x ranges are shared with the AXP809, as they cover the same range */ static const struct regmap_range axp22x_writeable_ranges[] = { regmap_reg_range(AXP20X_DATACACHE(0), AXP20X_IRQ5_STATE), -@@ -219,6 +249,19 @@ static const struct resource axp152_pek_ +@@ -220,6 +250,19 @@ static const struct resource axp152_pek_ DEFINE_RES_IRQ_NAMED(AXP152_IRQ_PEK_FAL_EDGE, "PEK_DBF"), }; @@ -99,7 +99,7 @@ Signed-off-by: Lee Jones static const struct resource axp20x_ac_power_supply_resources[] = { DEFINE_RES_IRQ_NAMED(AXP20X_IRQ_ACIN_PLUGIN, "ACIN_PLUGIN"), DEFINE_RES_IRQ_NAMED(AXP20X_IRQ_ACIN_REMOVAL, "ACIN_REMOVAL"), -@@ -301,6 +344,15 @@ static const struct regmap_config axp152 +@@ -302,6 +345,15 @@ static const struct regmap_config axp152 .cache_type = REGCACHE_RBTREE, }; @@ -115,7 +115,7 @@ Signed-off-by: Lee Jones static const struct regmap_config axp20x_regmap_config = { .reg_bits = 8, .val_bits = 8, -@@ -378,6 +430,42 @@ static const struct regmap_irq axp152_re +@@ -379,6 +431,42 @@ static const struct regmap_irq axp152_re INIT_REGMAP_IRQ(AXP152, GPIO0_INPUT, 2, 0), }; @@ -158,7 +158,7 @@ Signed-off-by: Lee Jones static const struct regmap_irq axp20x_regmap_irqs[] = { INIT_REGMAP_IRQ(AXP20X, ACIN_OVER_V, 0, 7), INIT_REGMAP_IRQ(AXP20X, ACIN_PLUGIN, 0, 6), -@@ -615,6 +703,32 @@ static const struct regmap_irq_chip axp1 +@@ -616,6 +704,32 @@ static const struct regmap_irq_chip axp1 .num_regs = 3, }; @@ -191,7 +191,7 @@ Signed-off-by: Lee Jones static const struct regmap_irq_chip axp20x_regmap_irq_chip = { .name = "axp20x_irq_chip", .status_base = AXP20X_IRQ1_STATE, -@@ -711,6 +825,27 @@ static const struct regmap_irq_chip axp1 +@@ -712,6 +826,27 @@ static const struct regmap_irq_chip axp1 .num_regs = 2, }; @@ -219,7 +219,7 @@ Signed-off-by: Lee Jones static const struct mfd_cell axp20x_cells[] = { { .name = "axp20x-gpio", -@@ -1028,6 +1163,12 @@ int axp20x_match_device(struct axp20x_de +@@ -1029,6 +1164,12 @@ int axp20x_match_device(struct axp20x_de axp20x->regmap_cfg = &axp152_regmap_config; axp20x->regmap_irq_chip = &axp152_regmap_irq_chip; break; @@ -338,7 +338,7 @@ Signed-off-by: Lee Jones AXP20X_LDO1 = 0, AXP20X_LDO2, AXP20X_LDO3, -@@ -530,6 +578,42 @@ enum { +@@ -531,6 +579,42 @@ enum { AXP152_IRQ_GPIO0_INPUT, }; diff --git a/target/linux/generic/backport-6.1/895-01-v6.8-net-phy-add-possible-interfaces.patch b/target/linux/generic/backport-6.1/895-01-v6.8-net-phy-add-possible-interfaces.patch new file mode 100644 index 000000000..3a9b856a2 --- /dev/null +++ b/target/linux/generic/backport-6.1/895-01-v6.8-net-phy-add-possible-interfaces.patch @@ -0,0 +1,60 @@ +From 1a7aa058bc92f0edae7a0d1ef1a7b05aec0c643a Mon Sep 17 00:00:00 2001 +From: "Russell King (Oracle)" +Date: Fri, 24 Nov 2023 12:27:52 +0000 +Subject: [PATCH 1/7] net: phy: add possible interfaces + +Add a possible_interfaces member to struct phy_device to indicate which +interfaces a clause 45 PHY may switch between depending on the media. +This must be populated by the PHY driver by the time the .config_init() +method completes according to the PHYs host-side configuration. + +For example, the Marvell 88x3310 PHY can switch between 10GBASE-R, +5GBASE-R, 2500BASE-X, and SGMII on the host side depending on the media +side speed, so all these interface modes are set in the +possible_interfaces member. + +This allows phylib users (such as phylink) to know in advance which +interface modes to expect, which allows them to appropriately restrict +the advertised link modes according to the capabilities of other parts +of the link. + +Tested-by: Luo Jie +Signed-off-by: Russell King (Oracle) +Reviewed-by: Andrew Lunn +Link: https://lore.kernel.org/r/E1r6VHk-00DDLN-I7@rmk-PC.armlinux.org.uk +Signed-off-by: Jakub Kicinski +--- + drivers/net/phy/phy_device.c | 2 ++ + include/linux/phy.h | 3 +++ + 2 files changed, 5 insertions(+) + +--- a/drivers/net/phy/phy_device.c ++++ b/drivers/net/phy/phy_device.c +@@ -1215,6 +1215,8 @@ int phy_init_hw(struct phy_device *phyde + if (ret < 0) + return ret; + ++ phy_interface_zero(phydev->possible_interfaces); ++ + if (phydev->drv->config_init) { + ret = phydev->drv->config_init(phydev); + if (ret < 0) +--- a/include/linux/phy.h ++++ b/include/linux/phy.h +@@ -600,6 +600,8 @@ struct macsec_ops; + * @irq_rerun: Flag indicating interrupts occurred while PHY was suspended, + * requiring a rerun of the interrupt handler after resume + * @interface: enum phy_interface_t value ++ * @possible_interfaces: bitmap if interface modes that the attached PHY ++ * will switch between depending on media speed. + * @skb: Netlink message for cable diagnostics + * @nest: Netlink nest used for cable diagnostics + * @ehdr: nNtlink header for cable diagnostics +@@ -665,6 +667,7 @@ struct phy_device { + u32 dev_flags; + + phy_interface_t interface; ++ DECLARE_PHY_INTERFACE_MASK(possible_interfaces); + + /* + * forced speed & duplex (no autoneg) diff --git a/target/linux/generic/backport-6.1/895-02-v6.8-net-phylink-use-for_each_set_bit.patch b/target/linux/generic/backport-6.1/895-02-v6.8-net-phylink-use-for_each_set_bit.patch new file mode 100644 index 000000000..155ec1c2d --- /dev/null +++ b/target/linux/generic/backport-6.1/895-02-v6.8-net-phylink-use-for_each_set_bit.patch @@ -0,0 +1,46 @@ +From 85631f5b33f2acce7d42dec1d0a062ab40de95b8 Mon Sep 17 00:00:00 2001 +From: "Russell King (Oracle)" +Date: Sun, 19 Nov 2023 21:07:43 +0000 +Subject: [PATCH 2/7] net: phylink: use for_each_set_bit() + +Use for_each_set_bit() rather than open coding the for() test_bit() +loop. + +Reviewed-by: Andrew Lunn +Signed-off-by: Russell King (Oracle) +Reviewed-by: Wojciech Drewek +Link: https://lore.kernel.org/r/E1r4p15-00Cpxe-C7@rmk-PC.armlinux.org.uk +Signed-off-by: Paolo Abeni +--- + drivers/net/phy/phylink.c | 18 ++++++++---------- + 1 file changed, 8 insertions(+), 10 deletions(-) + +--- a/drivers/net/phy/phylink.c ++++ b/drivers/net/phy/phylink.c +@@ -690,18 +690,16 @@ static int phylink_validate_mask(struct + __ETHTOOL_DECLARE_LINK_MODE_MASK(all_s) = { 0, }; + __ETHTOOL_DECLARE_LINK_MODE_MASK(s); + struct phylink_link_state t; +- int intf; ++ int interface; + +- for (intf = 0; intf < PHY_INTERFACE_MODE_MAX; intf++) { +- if (test_bit(intf, interfaces)) { +- linkmode_copy(s, supported); ++ for_each_set_bit(interface, interfaces, PHY_INTERFACE_MODE_MAX) { ++ linkmode_copy(s, supported); + +- t = *state; +- t.interface = intf; +- if (!phylink_validate_mac_and_pcs(pl, s, &t)) { +- linkmode_or(all_s, all_s, s); +- linkmode_or(all_adv, all_adv, t.advertising); +- } ++ t = *state; ++ t.interface = interface; ++ if (!phylink_validate_mac_and_pcs(pl, s, &t)) { ++ linkmode_or(all_s, all_s, s); ++ linkmode_or(all_adv, all_adv, t.advertising); + } + } + diff --git a/target/linux/generic/backport-6.1/895-03-v6.8-net-phylink-split-out-per-interface-validation.patch b/target/linux/generic/backport-6.1/895-03-v6.8-net-phylink-split-out-per-interface-validation.patch new file mode 100644 index 000000000..163cb953f --- /dev/null +++ b/target/linux/generic/backport-6.1/895-03-v6.8-net-phylink-split-out-per-interface-validation.patch @@ -0,0 +1,76 @@ +From d4788b4383ce5caeb4e68818357c81a02117a3f9 Mon Sep 17 00:00:00 2001 +From: "Russell King (Oracle)" +Date: Fri, 24 Nov 2023 12:28:19 +0000 +Subject: [PATCH 3/7] net: phylink: split out per-interface validation + +Split out the internals of phylink_validate_mask() to make the code +easier to read. + +Tested-by: Luo Jie +Signed-off-by: Russell King (Oracle) +Reviewed-by: Andrew Lunn +Link: https://lore.kernel.org/r/E1r6VIB-00DDLr-7g@rmk-PC.armlinux.org.uk +Signed-off-by: Jakub Kicinski +--- + drivers/net/phy/phylink.c | 42 ++++++++++++++++++++++++++++----------- + 1 file changed, 30 insertions(+), 12 deletions(-) + +--- a/drivers/net/phy/phylink.c ++++ b/drivers/net/phy/phylink.c +@@ -682,26 +682,44 @@ static int phylink_validate_mac_and_pcs( + return phylink_is_empty_linkmode(supported) ? -EINVAL : 0; + } + ++static void phylink_validate_one(struct phylink *pl, ++ const unsigned long *supported, ++ const struct phylink_link_state *state, ++ phy_interface_t interface, ++ unsigned long *accum_supported, ++ unsigned long *accum_advertising) ++{ ++ __ETHTOOL_DECLARE_LINK_MODE_MASK(tmp_supported); ++ struct phylink_link_state tmp_state; ++ ++ linkmode_copy(tmp_supported, supported); ++ ++ tmp_state = *state; ++ tmp_state.interface = interface; ++ ++ if (!phylink_validate_mac_and_pcs(pl, tmp_supported, &tmp_state)) { ++ phylink_dbg(pl, " interface %u (%s) rate match %s supports %*pbl\n", ++ interface, phy_modes(interface), ++ phy_rate_matching_to_str(tmp_state.rate_matching), ++ __ETHTOOL_LINK_MODE_MASK_NBITS, tmp_supported); ++ ++ linkmode_or(accum_supported, accum_supported, tmp_supported); ++ linkmode_or(accum_advertising, accum_advertising, ++ tmp_state.advertising); ++ } ++} ++ + static int phylink_validate_mask(struct phylink *pl, unsigned long *supported, + struct phylink_link_state *state, + const unsigned long *interfaces) + { + __ETHTOOL_DECLARE_LINK_MODE_MASK(all_adv) = { 0, }; + __ETHTOOL_DECLARE_LINK_MODE_MASK(all_s) = { 0, }; +- __ETHTOOL_DECLARE_LINK_MODE_MASK(s); +- struct phylink_link_state t; + int interface; + +- for_each_set_bit(interface, interfaces, PHY_INTERFACE_MODE_MAX) { +- linkmode_copy(s, supported); +- +- t = *state; +- t.interface = interface; +- if (!phylink_validate_mac_and_pcs(pl, s, &t)) { +- linkmode_or(all_s, all_s, s); +- linkmode_or(all_adv, all_adv, t.advertising); +- } +- } ++ for_each_set_bit(interface, interfaces, PHY_INTERFACE_MODE_MAX) ++ phylink_validate_one(pl, supported, state, interface, ++ all_s, all_adv); + + linkmode_copy(supported, all_s); + linkmode_copy(state->advertising, all_adv); diff --git a/target/linux/generic/backport-6.1/895-04-v6.8-net-phylink-pass-PHY-into-phylink_validate_one.patch b/target/linux/generic/backport-6.1/895-04-v6.8-net-phylink-pass-PHY-into-phylink_validate_one.patch new file mode 100644 index 000000000..501202067 --- /dev/null +++ b/target/linux/generic/backport-6.1/895-04-v6.8-net-phylink-pass-PHY-into-phylink_validate_one.patch @@ -0,0 +1,47 @@ +From ce7273c31fadb3143fc80c96a72a42adc19c2757 Mon Sep 17 00:00:00 2001 +From: "Russell King (Oracle)" +Date: Fri, 24 Nov 2023 12:28:24 +0000 +Subject: [PATCH 4/7] net: phylink: pass PHY into phylink_validate_one() + +Pass the phy (if any) into phylink_validate_one() so that we can +validate each interface with its rate matching setting. + +Tested-by: Luo Jie +Signed-off-by: Russell King (Oracle) +Reviewed-by: Andrew Lunn +Link: https://lore.kernel.org/r/E1r6VIG-00DDLx-Cb@rmk-PC.armlinux.org.uk +Signed-off-by: Jakub Kicinski +--- + drivers/net/phy/phylink.c | 7 +++++-- + 1 file changed, 5 insertions(+), 2 deletions(-) + +--- a/drivers/net/phy/phylink.c ++++ b/drivers/net/phy/phylink.c +@@ -682,7 +682,7 @@ static int phylink_validate_mac_and_pcs( + return phylink_is_empty_linkmode(supported) ? -EINVAL : 0; + } + +-static void phylink_validate_one(struct phylink *pl, ++static void phylink_validate_one(struct phylink *pl, struct phy_device *phy, + const unsigned long *supported, + const struct phylink_link_state *state, + phy_interface_t interface, +@@ -697,6 +697,9 @@ static void phylink_validate_one(struct + tmp_state = *state; + tmp_state.interface = interface; + ++ if (phy) ++ tmp_state.rate_matching = phy_get_rate_matching(phy, interface); ++ + if (!phylink_validate_mac_and_pcs(pl, tmp_supported, &tmp_state)) { + phylink_dbg(pl, " interface %u (%s) rate match %s supports %*pbl\n", + interface, phy_modes(interface), +@@ -718,7 +721,7 @@ static int phylink_validate_mask(struct + int interface; + + for_each_set_bit(interface, interfaces, PHY_INTERFACE_MODE_MAX) +- phylink_validate_one(pl, supported, state, interface, ++ phylink_validate_one(pl, NULL, supported, state, interface, + all_s, all_adv); + + linkmode_copy(supported, all_s); diff --git a/target/linux/generic/backport-6.1/895-05-v6.8-net-phylink-pass-PHY-into-phylink_validate_mask.patch b/target/linux/generic/backport-6.1/895-05-v6.8-net-phylink-pass-PHY-into-phylink_validate_mask.patch new file mode 100644 index 000000000..997c74346 --- /dev/null +++ b/target/linux/generic/backport-6.1/895-05-v6.8-net-phylink-pass-PHY-into-phylink_validate_mask.patch @@ -0,0 +1,58 @@ +From c6fec66d3cd76d797f70b30f1511bed10ba45a96 Mon Sep 17 00:00:00 2001 +From: "Russell King (Oracle)" +Date: Fri, 24 Nov 2023 12:28:29 +0000 +Subject: [PATCH 5/7] net: phylink: pass PHY into phylink_validate_mask() + +Pass the phy (if any) into phylink_validate_mask() so that we can +validate each interface with its rate matching setting. + +Tested-by: Luo Jie +Signed-off-by: Russell King (Oracle) +Reviewed-by: Andrew Lunn +Link: https://lore.kernel.org/r/E1r6VIL-00DDM3-HJ@rmk-PC.armlinux.org.uk +Signed-off-by: Jakub Kicinski +--- + drivers/net/phy/phylink.c | 11 +++++++---- + 1 file changed, 7 insertions(+), 4 deletions(-) + +--- a/drivers/net/phy/phylink.c ++++ b/drivers/net/phy/phylink.c +@@ -712,7 +712,8 @@ static void phylink_validate_one(struct + } + } + +-static int phylink_validate_mask(struct phylink *pl, unsigned long *supported, ++static int phylink_validate_mask(struct phylink *pl, struct phy_device *phy, ++ unsigned long *supported, + struct phylink_link_state *state, + const unsigned long *interfaces) + { +@@ -721,7 +722,7 @@ static int phylink_validate_mask(struct + int interface; + + for_each_set_bit(interface, interfaces, PHY_INTERFACE_MODE_MAX) +- phylink_validate_one(pl, NULL, supported, state, interface, ++ phylink_validate_one(pl, phy, supported, state, interface, + all_s, all_adv); + + linkmode_copy(supported, all_s); +@@ -736,7 +737,8 @@ static int phylink_validate(struct phyli + const unsigned long *interfaces = pl->config->supported_interfaces; + + if (state->interface == PHY_INTERFACE_MODE_NA) +- return phylink_validate_mask(pl, supported, state, interfaces); ++ return phylink_validate_mask(pl, NULL, supported, state, ++ interfaces); + + if (!test_bit(state->interface, interfaces)) + return -EINVAL; +@@ -3132,7 +3134,8 @@ static int phylink_sfp_config_optical(st + /* For all the interfaces that are supported, reduce the sfp_support + * mask to only those link modes that can be supported. + */ +- ret = phylink_validate_mask(pl, pl->sfp_support, &config, interfaces); ++ ret = phylink_validate_mask(pl, NULL, pl->sfp_support, &config, ++ interfaces); + if (ret) { + phylink_err(pl, "unsupported SFP module: validation with support %*pb failed\n", + __ETHTOOL_LINK_MODE_MASK_NBITS, support); diff --git a/target/linux/generic/backport-6.1/895-06-v6.8-net-phylink-split-out-PHY-validation-from-phylink_br.patch b/target/linux/generic/backport-6.1/895-06-v6.8-net-phylink-split-out-PHY-validation-from-phylink_br.patch new file mode 100644 index 000000000..d3d24c58f --- /dev/null +++ b/target/linux/generic/backport-6.1/895-06-v6.8-net-phylink-split-out-PHY-validation-from-phylink_br.patch @@ -0,0 +1,95 @@ +From ee0e0ddb910e7e989b65a19d72b6435baa641fc7 Mon Sep 17 00:00:00 2001 +From: "Russell King (Oracle)" +Date: Fri, 24 Nov 2023 12:28:34 +0000 +Subject: [PATCH 6/7] net: phylink: split out PHY validation from + phylink_bringup_phy() + +When bringing up a PHY, we need to work out which ethtool link modes it +should support and advertise. Clause 22 PHYs operate in a single +interface mode, which can be easily dealt with. However, clause 45 PHYs +tend to switch interface mode depending on the media. We need more +flexible validation at this point, so this patch splits out that code +in preparation to changing it. + +Tested-by: Luo Jie +Signed-off-by: Russell King (Oracle) +Reviewed-by: Andrew Lunn +Link: https://lore.kernel.org/r/E1r6VIQ-00DDM9-LK@rmk-PC.armlinux.org.uk +Signed-off-by: Jakub Kicinski +--- + drivers/net/phy/phylink.c | 56 ++++++++++++++++++++++----------------- + 1 file changed, 31 insertions(+), 25 deletions(-) + +--- a/drivers/net/phy/phylink.c ++++ b/drivers/net/phy/phylink.c +@@ -1738,6 +1738,35 @@ static void phylink_phy_change(struct ph + phylink_pause_to_str(pl->phy_state.pause)); + } + ++static int phylink_validate_phy(struct phylink *pl, struct phy_device *phy, ++ unsigned long *supported, ++ struct phylink_link_state *state) ++{ ++ /* Check whether we would use rate matching for the proposed interface ++ * mode. ++ */ ++ state->rate_matching = phy_get_rate_matching(phy, state->interface); ++ ++ /* Clause 45 PHYs may switch their Serdes lane between, e.g. 10GBASE-R, ++ * 5GBASE-R, 2500BASE-X and SGMII if they are not using rate matching. ++ * For some interface modes (e.g. RXAUI, XAUI and USXGMII) switching ++ * their Serdes is either unnecessary or not reasonable. ++ * ++ * For these which switch interface modes, we really need to know which ++ * interface modes the PHY supports to properly work out which ethtool ++ * linkmodes can be supported. For now, as a work-around, we validate ++ * against all interface modes, which may lead to more ethtool link ++ * modes being advertised than are actually supported. ++ */ ++ if (phy->is_c45 && state->rate_matching == RATE_MATCH_NONE && ++ state->interface != PHY_INTERFACE_MODE_RXAUI && ++ state->interface != PHY_INTERFACE_MODE_XAUI && ++ state->interface != PHY_INTERFACE_MODE_USXGMII) ++ state->interface = PHY_INTERFACE_MODE_NA; ++ ++ return phylink_validate(pl, supported, state); ++} ++ + static int phylink_bringup_phy(struct phylink *pl, struct phy_device *phy, + phy_interface_t interface) + { +@@ -1758,32 +1787,9 @@ static int phylink_bringup_phy(struct ph + memset(&config, 0, sizeof(config)); + linkmode_copy(supported, phy->supported); + linkmode_copy(config.advertising, phy->advertising); ++ config.interface = interface; + +- /* Check whether we would use rate matching for the proposed interface +- * mode. +- */ +- config.rate_matching = phy_get_rate_matching(phy, interface); +- +- /* Clause 45 PHYs may switch their Serdes lane between, e.g. 10GBASE-R, +- * 5GBASE-R, 2500BASE-X and SGMII if they are not using rate matching. +- * For some interface modes (e.g. RXAUI, XAUI and USXGMII) switching +- * their Serdes is either unnecessary or not reasonable. +- * +- * For these which switch interface modes, we really need to know which +- * interface modes the PHY supports to properly work out which ethtool +- * linkmodes can be supported. For now, as a work-around, we validate +- * against all interface modes, which may lead to more ethtool link +- * modes being advertised than are actually supported. +- */ +- if (phy->is_c45 && config.rate_matching == RATE_MATCH_NONE && +- interface != PHY_INTERFACE_MODE_RXAUI && +- interface != PHY_INTERFACE_MODE_XAUI && +- interface != PHY_INTERFACE_MODE_USXGMII) +- config.interface = PHY_INTERFACE_MODE_NA; +- else +- config.interface = interface; +- +- ret = phylink_validate(pl, supported, &config); ++ ret = phylink_validate_phy(pl, phy, supported, &config); + if (ret) { + phylink_warn(pl, "validation of %s with support %*pb and advertisement %*pb failed: %pe\n", + phy_modes(config.interface), diff --git a/target/linux/generic/backport-6.1/895-07-v6.8-net-phylink-use-the-PHY-s-possible_interfaces-if-pop.patch b/target/linux/generic/backport-6.1/895-07-v6.8-net-phylink-use-the-PHY-s-possible_interfaces-if-pop.patch new file mode 100644 index 000000000..201afbb7c --- /dev/null +++ b/target/linux/generic/backport-6.1/895-07-v6.8-net-phylink-use-the-PHY-s-possible_interfaces-if-pop.patch @@ -0,0 +1,130 @@ +From 8f7a9799c5949f94ecc3acfd71b36437a7ade73b Mon Sep 17 00:00:00 2001 +From: "Russell King (Oracle)" +Date: Fri, 24 Nov 2023 12:28:39 +0000 +Subject: [PATCH 7/7] net: phylink: use the PHY's possible_interfaces if + populated + +Some PHYs such as Aquantia, Broadcom 84881, and Marvell 88X33x0 can +switch between a set of interface types depending on the negotiated +media speed, or can use rate adaption for some or all of these +interface types. + +We currently assume that these are Clause 45 PHYs that are configured +not to use a specific set of interface modes, which has worked so far, +but is just a work-around. In this workaround, we validate using all +interfaces that the MAC supports, which can lead to extra modes being +advertised that can not be supported. + +To properly address this, switch to using the newly introduced PHY +possible_interfaces bitmap which indicates which interface modes will +be used by the PHY as configured. We calculate the union of the PHY's +possible interfaces and MACs supported interfaces, checking that is +non-empty. If the PHY is on a SFP, we further reduce the set by those +which can be used on a SFP module, again checking that is non-empty. +Finally, we validate the subset of interfaces, taking account of +whether rate matching will be used for each individual interface mode. + +This becomes independent of whether the PHY is clause 22 or clause 45. + +It is encouraged that all PHYs that switch interface modes or use +rate matching should populate phydev->possible_interfaces. + +Tested-by: Luo Jie +Signed-off-by: Russell King (Oracle) +Reviewed-by: Andrew Lunn +Link: https://lore.kernel.org/r/E1r6VIV-00DDMF-Pi@rmk-PC.armlinux.org.uk +Signed-off-by: Jakub Kicinski +--- + drivers/net/phy/phylink.c | 67 +++++++++++++++++++++++++++++++-------- + 1 file changed, 54 insertions(+), 13 deletions(-) + +--- a/drivers/net/phy/phylink.c ++++ b/drivers/net/phy/phylink.c +@@ -121,6 +121,19 @@ do { \ + }) + #endif + ++static const phy_interface_t phylink_sfp_interface_preference[] = { ++ PHY_INTERFACE_MODE_25GBASER, ++ PHY_INTERFACE_MODE_USXGMII, ++ PHY_INTERFACE_MODE_10GBASER, ++ PHY_INTERFACE_MODE_5GBASER, ++ PHY_INTERFACE_MODE_2500BASEX, ++ PHY_INTERFACE_MODE_SGMII, ++ PHY_INTERFACE_MODE_1000BASEX, ++ PHY_INTERFACE_MODE_100BASEX, ++}; ++ ++static DECLARE_PHY_INTERFACE_MASK(phylink_sfp_interfaces); ++ + /** + * phylink_set_port_modes() - set the port type modes in the ethtool mask + * @mask: ethtool link mode mask +@@ -1742,6 +1755,47 @@ static int phylink_validate_phy(struct p + unsigned long *supported, + struct phylink_link_state *state) + { ++ DECLARE_PHY_INTERFACE_MASK(interfaces); ++ ++ /* If the PHY provides a bitmap of the interfaces it will be using ++ * depending on the negotiated media speeds, use this to validate ++ * which ethtool link modes can be used. ++ */ ++ if (!phy_interface_empty(phy->possible_interfaces)) { ++ /* We only care about the union of the PHY's interfaces and ++ * those which the host supports. ++ */ ++ phy_interface_and(interfaces, phy->possible_interfaces, ++ pl->config->supported_interfaces); ++ ++ if (phy_interface_empty(interfaces)) { ++ phylink_err(pl, "PHY has no common interfaces\n"); ++ return -EINVAL; ++ } ++ ++ if (phy_on_sfp(phy)) { ++ /* If the PHY is on a SFP, limit the interfaces to ++ * those that can be used with a SFP module. ++ */ ++ phy_interface_and(interfaces, interfaces, ++ phylink_sfp_interfaces); ++ ++ if (phy_interface_empty(interfaces)) { ++ phylink_err(pl, "SFP PHY's possible interfaces becomes empty\n"); ++ return -EINVAL; ++ } ++ } ++ ++ phylink_dbg(pl, "PHY %s uses interfaces %*pbl, validating %*pbl\n", ++ phydev_name(phy), ++ (int)PHY_INTERFACE_MODE_MAX, ++ phy->possible_interfaces, ++ (int)PHY_INTERFACE_MODE_MAX, interfaces); ++ ++ return phylink_validate_mask(pl, phy, supported, state, ++ interfaces); ++ } ++ + /* Check whether we would use rate matching for the proposed interface + * mode. + */ +@@ -2985,19 +3039,6 @@ static void phylink_sfp_detach(void *ups + pl->netdev->sfp_bus = NULL; + } + +-static const phy_interface_t phylink_sfp_interface_preference[] = { +- PHY_INTERFACE_MODE_25GBASER, +- PHY_INTERFACE_MODE_USXGMII, +- PHY_INTERFACE_MODE_10GBASER, +- PHY_INTERFACE_MODE_5GBASER, +- PHY_INTERFACE_MODE_2500BASEX, +- PHY_INTERFACE_MODE_SGMII, +- PHY_INTERFACE_MODE_1000BASEX, +- PHY_INTERFACE_MODE_100BASEX, +-}; +- +-static DECLARE_PHY_INTERFACE_MASK(phylink_sfp_interfaces); +- + static phy_interface_t phylink_choose_sfp_interface(struct phylink *pl, + const unsigned long *intf) + { diff --git a/target/linux/generic/backport-6.1/897-01-v6.9-net-phy-qcom-qca808x-add-helper-for-checking-for-1G-.patch b/target/linux/generic/backport-6.1/897-01-v6.9-net-phy-qcom-qca808x-add-helper-for-checking-for-1G-.patch new file mode 100644 index 000000000..a11e80495 --- /dev/null +++ b/target/linux/generic/backport-6.1/897-01-v6.9-net-phy-qcom-qca808x-add-helper-for-checking-for-1G-.patch @@ -0,0 +1,50 @@ +From f058b2dd70b1a5503dff899010aeb53b436091e5 Mon Sep 17 00:00:00 2001 +From: Robert Marko +Date: Wed, 28 Feb 2024 18:24:09 +0100 +Subject: [PATCH 1/2] net: phy: qcom: qca808x: add helper for checking for 1G + only model + +There are 2 versions of QCA808x, one 2.5G capable and one 1G capable. +Currently, this matter only in the .get_features call however, it will +be required for filling supported interface modes so lets add a helper +that can be reused. + +Signed-off-by: Robert Marko +Signed-off-by: David S. Miller +--- + drivers/net/phy/qcom/qca808x.c | 17 ++++++++++++----- + 1 file changed, 12 insertions(+), 5 deletions(-) + +--- a/drivers/net/phy/qcom/qca808x.c ++++ b/drivers/net/phy/qcom/qca808x.c +@@ -156,6 +156,17 @@ static bool qca808x_has_fast_retrain_or_ + return linkmode_test_bit(ETHTOOL_LINK_MODE_2500baseT_Full_BIT, phydev->supported); + } + ++static bool qca808x_is_1g_only(struct phy_device *phydev) ++{ ++ int ret; ++ ++ ret = phy_read_mmd(phydev, MDIO_MMD_AN, QCA808X_PHY_MMD7_CHIP_TYPE); ++ if (ret < 0) ++ return true; ++ ++ return !!(QCA808X_PHY_CHIP_TYPE_1G & ret); ++} ++ + static int qca808x_probe(struct phy_device *phydev) + { + struct device *dev = &phydev->mdio.dev; +@@ -350,11 +361,7 @@ static int qca808x_get_features(struct p + * existed in the bit0 of MMD1.21, we need to remove it manually if + * it is the qca8081 1G chip according to the bit0 of MMD7.0x901d. + */ +- ret = phy_read_mmd(phydev, MDIO_MMD_AN, QCA808X_PHY_MMD7_CHIP_TYPE); +- if (ret < 0) +- return ret; +- +- if (QCA808X_PHY_CHIP_TYPE_1G & ret) ++ if (qca808x_is_1g_only(phydev)) + linkmode_clear_bit(ETHTOOL_LINK_MODE_2500baseT_Full_BIT, phydev->supported); + + return 0; diff --git a/target/linux/generic/backport-6.1/897-02-v6.9-net-phy-qcom-qca808x-fill-in-possible_interfaces.patch b/target/linux/generic/backport-6.1/897-02-v6.9-net-phy-qcom-qca808x-fill-in-possible_interfaces.patch new file mode 100644 index 000000000..c162fc734 --- /dev/null +++ b/target/linux/generic/backport-6.1/897-02-v6.9-net-phy-qcom-qca808x-fill-in-possible_interfaces.patch @@ -0,0 +1,44 @@ +From cb28f702960695e26597c332b0e46776e825cc34 Mon Sep 17 00:00:00 2001 +From: Robert Marko +Date: Wed, 28 Feb 2024 18:24:10 +0100 +Subject: [PATCH 2/2] net: phy: qcom: qca808x: fill in possible_interfaces + +Currently QCA808x driver does not fill the possible_interfaces. +2.5G QCA808x support SGMII and 2500Base-X while 1G model only supports +SGMII, so fill the possible_interfaces accordingly. + +Signed-off-by: Robert Marko +Reviewed-by: Russell King (Oracle) +Signed-off-by: David S. Miller +--- + drivers/net/phy/qcom/qca808x.c | 12 ++++++++++++ + 1 file changed, 12 insertions(+) + +--- a/drivers/net/phy/qcom/qca808x.c ++++ b/drivers/net/phy/qcom/qca808x.c +@@ -167,6 +167,16 @@ static bool qca808x_is_1g_only(struct ph + return !!(QCA808X_PHY_CHIP_TYPE_1G & ret); + } + ++static void qca808x_fill_possible_interfaces(struct phy_device *phydev) ++{ ++ unsigned long *possible = phydev->possible_interfaces; ++ ++ __set_bit(PHY_INTERFACE_MODE_SGMII, possible); ++ ++ if (!qca808x_is_1g_only(phydev)) ++ __set_bit(PHY_INTERFACE_MODE_2500BASEX, possible); ++} ++ + static int qca808x_probe(struct phy_device *phydev) + { + struct device *dev = &phydev->mdio.dev; +@@ -231,6 +241,8 @@ static int qca808x_config_init(struct ph + } + } + ++ qca808x_fill_possible_interfaces(phydev); ++ + /* Configure adc threshold as 100mv for the link 10M */ + return at803x_debug_reg_mask(phydev, QCA808X_PHY_DEBUG_ADC_THRESHOLD, + QCA808X_ADC_THRESHOLD_MASK, diff --git a/target/linux/generic/config-6.1 b/target/linux/generic/config-6.1 index 33bceba08..70e87665b 100644 --- a/target/linux/generic/config-6.1 +++ b/target/linux/generic/config-6.1 @@ -157,6 +157,7 @@ CONFIG_AF_UNIX_OOB=y CONFIG_AIO=y # CONFIG_AIRO is not set # CONFIG_AIRO_CS is not set +# CONFIG_AIR_EN8811H_PHY is not set # CONFIG_AIX_PARTITION is not set # CONFIG_AK09911 is not set # CONFIG_AK8974 is not set @@ -730,6 +731,7 @@ CONFIG_BLK_DEV_LOOP_MIN_COUNT=8 # CONFIG_BLK_DEV_VIA82CXXX is not set # CONFIG_BLK_DEV_ZONED is not set # CONFIG_BLK_INLINE_ENCRYPTION is not set +# CONFIG_BLK_NVMEM is not set # CONFIG_BLK_SED_OPAL is not set # CONFIG_BLK_WBT is not set CONFIG_BLOCK=y @@ -2345,6 +2347,7 @@ CONFIG_GPIO_SYSFS=y # CONFIG_GPIO_TPIC2810 is not set # CONFIG_GPIO_TS4900 is not set # CONFIG_GPIO_TS5500 is not set +# CONFIG_GPIO_VF610 is not set # CONFIG_GPIO_VIRTIO is not set # CONFIG_GPIO_VX855 is not set # CONFIG_GPIO_WATCHDOG is not set @@ -4039,6 +4042,7 @@ CONFIG_MTD_SPLIT_SUPPORT=y # CONFIG_MTD_UBI is not set # CONFIG_MTD_UBI_FASTMAP is not set # CONFIG_MTD_UBI_GLUEBI is not set +# CONFIG_MTD_UBI_NVMEM is not set # CONFIG_MTD_UIMAGE_SPLIT is not set # CONFIG_MTD_VIRT_CONCAT is not set # CONFIG_MTK_DEVAPC is not set @@ -5078,6 +5082,9 @@ CONFIG_PWRSEQ_SIMPLE=y # CONFIG_QCA7000 is not set # CONFIG_QCA7000_SPI is not set # CONFIG_QCA7000_UART is not set +# CONFIG_QCA807X_PHY is not set +# CONFIG_QCA808X_PHY is not set +# CONFIG_QCA83XX_PHY is not set # CONFIG_QCOM_A7PLL is not set # CONFIG_QCOM_BAM_DMUX is not set # CONFIG_QCOM_EMAC is not set @@ -5954,7 +5961,6 @@ CONFIG_SERIAL_EARLYCON=y # CONFIG_SG_SPLIT is not set # CONFIG_SHADOW_CALL_STACK is not set CONFIG_SHMEM=y -# CONFIG_SHORTCUT_FE is not set # CONFIG_SHRINKER_DEBUG is not set # CONFIG_SHUFFLE_PAGE_ALLOCATOR is not set # CONFIG_SH_ETH is not set @@ -6730,6 +6736,8 @@ CONFIG_TCP_CONG_CUBIC=y # CONFIG_TCS3414 is not set # CONFIG_TCS3472 is not set # CONFIG_TEE is not set +# CONFIG_TEGRA210_ADMA is not set +# CONFIG_TEGRA_ACONNECT is not set # CONFIG_TEGRA_AHB is not set # CONFIG_TEGRA_HOST1X is not set # CONFIG_TEHUTI is not set @@ -7813,15 +7821,15 @@ CONFIG_WATCHDOG_OPEN_TIMEOUT=0 # CONFIG_WDAT_WDT is not set # CONFIG_WDTPCI is not set # CONFIG_WERROR is not set -CONFIG_WEXT_CORE=y -CONFIG_WEXT_PRIV=y -CONFIG_WEXT_PROC=y -CONFIG_WEXT_SPY=y +# CONFIG_WEXT_CORE is not set +# CONFIG_WEXT_PRIV is not set +# CONFIG_WEXT_PROC is not set +# CONFIG_WEXT_SPY is not set CONFIG_WILINK_PLATFORM_DATA=y # CONFIG_WIMAX is not set # CONFIG_WIREGUARD is not set CONFIG_WIRELESS=y -CONFIG_WIRELESS_EXT=y +# CONFIG_WIRELESS_EXT is not set # CONFIG_WIRELESS_WDS is not set # CONFIG_WIZNET_W5100 is not set # CONFIG_WIZNET_W5300 is not set diff --git a/target/linux/generic/files/drivers/mtd/mtdsplit/Kconfig b/target/linux/generic/files/drivers/mtd/mtdsplit/Kconfig index 794a39f2c..17d590890 100644 --- a/target/linux/generic/files/drivers/mtd/mtdsplit/Kconfig +++ b/target/linux/generic/files/drivers/mtd/mtdsplit/Kconfig @@ -12,7 +12,6 @@ config MTD_SPLIT_SQUASHFS_ROOT bool "Squashfs based root partition parser" depends on MTD_SPLIT_SUPPORT select MTD_SPLIT - default n help This provides a parsing function which allows to detect the offset and size of the unused portion of a rootfs partition @@ -32,7 +31,7 @@ config MTD_SPLIT_BCM_WFI_FW config MTD_SPLIT_CFE_BOOTFS bool "Parser finding rootfs appended to the CFE bootfs" - depends on MTD_SPLIT_SUPPORT && ARCH_BCM4908 + depends on MTD_SPLIT_SUPPORT && (ARCH_BCM4908 || ARCH_BCMBCA) select MTD_SPLIT help cferom on BCM4908 (and bcm63xx) uses JFFS2 bootfs partition @@ -101,3 +100,13 @@ config MTD_SPLIT_ELF_FW bool "ELF loader firmware partition parser" depends on MTD_SPLIT_SUPPORT select MTD_SPLIT + +config MTD_SPLIT_H3C_VFS + bool "Parser finding rootfs appended to H3C VFS" + depends on MTD_SPLIT_SUPPORT + select MTD_SPLIT + +config MTD_SPLIT_SEIL_FW + bool "IIJ SEIL firmware parser" + depends on MTD_SPLIT_SUPPORT + select MTD_SPLIT diff --git a/target/linux/generic/files/drivers/mtd/mtdsplit/Makefile b/target/linux/generic/files/drivers/mtd/mtdsplit/Makefile index 1461099b7..e9d63c833 100644 --- a/target/linux/generic/files/drivers/mtd/mtdsplit/Makefile +++ b/target/linux/generic/files/drivers/mtd/mtdsplit/Makefile @@ -3,6 +3,7 @@ obj-$(CONFIG_MTD_SPLIT_BCM63XX_FW) += mtdsplit_bcm63xx.o obj-$(CONFIG_MTD_SPLIT_BCM_WFI_FW) += mtdsplit_bcm_wfi.o obj-$(CONFIG_MTD_SPLIT_CFE_BOOTFS) += mtdsplit_cfe_bootfs.o obj-$(CONFIG_MTD_SPLIT_SEAMA_FW) += mtdsplit_seama.o +obj-$(CONFIG_MTD_SPLIT_SEIL_FW) += mtdsplit_seil.o obj-$(CONFIG_MTD_SPLIT_SQUASHFS_ROOT) += mtdsplit_squashfs.o obj-$(CONFIG_MTD_SPLIT_UIMAGE_FW) += mtdsplit_uimage.o obj-$(CONFIG_MTD_SPLIT_FIT_FW) += mtdsplit_fit.o @@ -15,3 +16,4 @@ obj-$(CONFIG_MTD_SPLIT_WRGG_FW) += mtdsplit_wrgg.o obj-$(CONFIG_MTD_SPLIT_MINOR_FW) += mtdsplit_minor.o obj-$(CONFIG_MTD_SPLIT_JIMAGE_FW) += mtdsplit_jimage.o obj-$(CONFIG_MTD_SPLIT_ELF_FW) += mtdsplit_elf.o +obj-$(CONFIG_MTD_SPLIT_H3C_VFS) += mtdsplit_h3c_vfs.o diff --git a/target/linux/generic/files/drivers/mtd/mtdsplit/mtdsplit_bcm_wfi.c b/target/linux/generic/files/drivers/mtd/mtdsplit/mtdsplit_bcm_wfi.c index 1ddcf6745..1cafc91fd 100644 --- a/target/linux/generic/files/drivers/mtd/mtdsplit/mtdsplit_bcm_wfi.c +++ b/target/linux/generic/files/drivers/mtd/mtdsplit/mtdsplit_bcm_wfi.c @@ -31,6 +31,7 @@ #define CFERAM_NAME "cferam" #define CFERAM_NAME_LEN (sizeof(CFERAM_NAME) - 1) +#define CFERAM_NAME_MAX_LEN 32 #define KERNEL_NAME "vmlinux.lz" #define KERNEL_NAME_LEN (sizeof(KERNEL_NAME) - 1) #define OPENWRT_NAME "1-openwrt" @@ -157,17 +158,28 @@ static int parse_bcm_wfi(struct mtd_info *master, const struct mtd_partition **pparts, uint8_t *buf, loff_t off, loff_t size, bool cfe_part) { + struct device_node *mtd_node; struct mtd_partition *parts; loff_t cfe_off, kernel_off, rootfs_off; unsigned int num_parts = BCM_WFI_PARTS, cur_part = 0; + const char *cferam_name = CFERAM_NAME; + size_t cferam_name_len; int ret; + mtd_node = mtd_get_of_node(master); + if (mtd_node) + of_property_read_string(mtd_node, "brcm,cferam", &cferam_name); + + cferam_name_len = strnlen(cferam_name, CFERAM_NAME_MAX_LEN); + if (cferam_name_len > 0) + cferam_name_len--; + if (cfe_part) { num_parts++; cfe_off = off; - ret = jffs2_find_file(master, buf, CFERAM_NAME, - CFERAM_NAME_LEN, &cfe_off, + ret = jffs2_find_file(master, buf, cferam_name, + cferam_name_len, &cfe_off, size - (cfe_off - off), NULL, NULL); if (ret) return ret; diff --git a/target/linux/generic/files/drivers/mtd/mtdsplit/mtdsplit_fit.c b/target/linux/generic/files/drivers/mtd/mtdsplit/mtdsplit_fit.c index 3230d859b..3b71597d2 100644 --- a/target/linux/generic/files/drivers/mtd/mtdsplit/mtdsplit_fit.c +++ b/target/linux/generic/files/drivers/mtd/mtdsplit/mtdsplit_fit.c @@ -199,6 +199,7 @@ mtdsplit_fit_parse(struct mtd_info *mtd, struct fdt_header hdr; size_t hdr_len, retlen; size_t offset; + u32 offset_start = 0; size_t fit_offset, fit_size; size_t rootfs_offset, rootfs_size; size_t data_size, img_total, max_size = 0; @@ -211,11 +212,13 @@ mtdsplit_fit_parse(struct mtd_info *mtd, if (cmdline_match && !strstr(saved_command_line, cmdline_match)) return -ENODEV; + of_property_read_u32(np, "openwrt,fit-offset", &offset_start); + hdr_len = sizeof(struct fdt_header); /* Parse the MTD device & search for the FIT image location */ for(offset = 0; offset + hdr_len <= mtd->size; offset += mtd->erasesize) { - ret = mtd_read(mtd, offset, hdr_len, &retlen, (void*) &hdr); + ret = mtd_read(mtd, offset + offset_start, hdr_len, &retlen, (void*) &hdr); if (ret) { pr_err("read error in \"%s\" at offset 0x%llx\n", mtd->name, (unsigned long long) offset); @@ -256,9 +259,11 @@ mtdsplit_fit_parse(struct mtd_info *mtd, * last external data refernced. */ if (fit_size > 0x1000) { + enum mtdsplit_part_type type; + /* Search for the rootfs partition after the FIT image */ - ret = mtd_find_rootfs_from(mtd, fit_offset + fit_size, mtd->size, - &rootfs_offset, NULL); + ret = mtd_find_rootfs_from(mtd, fit_offset + fit_size + offset_start, mtd->size, + &rootfs_offset, &type); if (ret) { pr_info("no rootfs found after FIT image in \"%s\"\n", mtd->name); @@ -273,9 +278,12 @@ mtdsplit_fit_parse(struct mtd_info *mtd, parts[0].name = KERNEL_PART_NAME; parts[0].offset = fit_offset; - parts[0].size = mtd_rounddown_to_eb(fit_size, mtd) + mtd->erasesize; + parts[0].size = mtd_rounddown_to_eb(fit_size + offset_start, mtd) + mtd->erasesize; - parts[1].name = ROOTFS_PART_NAME; + if (type == MTDSPLIT_PART_TYPE_UBI) + parts[1].name = UBI_PART_NAME; + else + parts[1].name = ROOTFS_PART_NAME; parts[1].offset = rootfs_offset; parts[1].size = rootfs_size; @@ -285,7 +293,7 @@ mtdsplit_fit_parse(struct mtd_info *mtd, } else { /* Search for rootfs_data after FIT external data */ fit = kzalloc(fit_size, GFP_KERNEL); - ret = mtd_read(mtd, offset, fit_size, &retlen, fit); + ret = mtd_read(mtd, offset, fit_size + offset_start, &retlen, fit); if (ret) { pr_err("read error in \"%s\" at offset 0x%llx\n", mtd->name, (unsigned long long) offset); diff --git a/target/linux/generic/files/drivers/mtd/mtdsplit/mtdsplit_h3c_vfs.c b/target/linux/generic/files/drivers/mtd/mtdsplit/mtdsplit_h3c_vfs.c new file mode 100644 index 000000000..f264233db --- /dev/null +++ b/target/linux/generic/files/drivers/mtd/mtdsplit/mtdsplit_h3c_vfs.c @@ -0,0 +1,170 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Some devices made by H3C use a "VFS" filesystem to store firmware images. + * This parses the start of the filesystem to read the length of the first + * file (the kernel image). It then searches for the rootfs after the end of + * the file data. This driver assumes that the filesystem was generated by + * mkh3cvfs, and only works if the filesystem matches the expected layout, + * which includes the file name of the kernel image. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "mtdsplit.h" + +#define VFS_ERASEBLOCK_SIZE 0x10000 +#define VFS_BLOCK_SIZE 0x400 +#define VFS_BLOCKS_PER_ERASEBLOCK (VFS_ERASEBLOCK_SIZE / VFS_BLOCK_SIZE) + +#define FORMAT_FLAG_OFFSET 0x0 + +#define FORMAT_FLAG (VFS_ERASEBLOCK_SIZE << 12 | VFS_BLOCK_SIZE) + +#define FILE_ENTRY_OFFSET 0x800 + +#define FILE_ENTRY_FLAGS 0x3f +#define FILE_ENTRY_PARENT_BLOCK 0 +#define FILE_ENTRY_PARENT_INDEX 0 +#define FILE_ENTRY_DATA_BLOCK 2 +#define FILE_ENTRY_NAME "openwrt-kernel.bin" + +#define NR_PARTS 2 + +struct file_entry { + uint8_t flags; + + uint8_t res0[5]; + + uint16_t year; + uint8_t month; + uint8_t day; + uint8_t hour; + uint8_t minute; + uint8_t second; + + uint8_t res1[3]; + + uint32_t length; + + uint32_t parent_block; + uint16_t parent_index; + + uint8_t res2[2]; + + uint32_t data_block; + + char name[96]; +} __attribute__ ((packed)); + +static inline size_t block_offset(int block) +{ + return VFS_ERASEBLOCK_SIZE * (block / (VFS_BLOCKS_PER_ERASEBLOCK-1)) + + VFS_BLOCK_SIZE * (1 + (block % (VFS_BLOCKS_PER_ERASEBLOCK-1))); +} + +static inline int block_count(size_t size) +{ + return (size + VFS_BLOCK_SIZE - 1) / VFS_BLOCK_SIZE; +} + +static int mtdsplit_h3c_vfs_parse(struct mtd_info *mtd, + const struct mtd_partition **pparts, + struct mtd_part_parser_data *data) +{ + struct mtd_partition *parts; + uint32_t format_flag; + struct file_entry file_entry; + size_t retlen; + int err; + size_t kernel_size; + size_t expected_offset; + size_t rootfs_offset; + + if (mtd->erasesize != VFS_ERASEBLOCK_SIZE) + return -EINVAL; + + /* Check format flag */ + err = mtd_read(mtd, FORMAT_FLAG_OFFSET, sizeof(format_flag), &retlen, + (void *) &format_flag); + if (err) + return err; + + if (retlen != sizeof(format_flag)) + return -EIO; + + if (format_flag != FORMAT_FLAG) + return -EINVAL; + + /* Check file entry */ + err = mtd_read(mtd, FILE_ENTRY_OFFSET, sizeof(file_entry), &retlen, + (void *) &file_entry); + if (err) + return err; + + if (retlen != sizeof(file_entry)) + return -EIO; + + if (file_entry.flags != FILE_ENTRY_FLAGS) + return -EINVAL; + + if (file_entry.parent_block != FILE_ENTRY_PARENT_BLOCK) + return -EINVAL; + + if (file_entry.parent_index != FILE_ENTRY_PARENT_INDEX) + return -EINVAL; + + if (file_entry.data_block != FILE_ENTRY_DATA_BLOCK) + return -EINVAL; + + if (strncmp(file_entry.name, FILE_ENTRY_NAME, sizeof(file_entry.name)) != 0) + return -EINVAL; + + /* Find rootfs offset */ + kernel_size = block_offset(file_entry.data_block + + block_count(file_entry.length) - 1) + + VFS_BLOCK_SIZE; + + expected_offset = mtd_roundup_to_eb(kernel_size, mtd); + + err = mtd_find_rootfs_from(mtd, expected_offset, mtd->size, + &rootfs_offset, NULL); + if (err) + return err; + + parts = kzalloc(NR_PARTS * sizeof(*parts), GFP_KERNEL); + if (!parts) + return -ENOMEM; + + parts[0].name = KERNEL_PART_NAME; + parts[0].offset = 0; + parts[0].size = rootfs_offset; + + parts[1].name = ROOTFS_PART_NAME; + parts[1].offset = rootfs_offset; + parts[1].size = mtd->size - rootfs_offset; + + *pparts = parts; + return NR_PARTS; +} + +static const struct of_device_id mtdsplit_h3c_vfs_of_match_table[] = { + { .compatible = "h3c,vfs-firmware" }, + {}, +}; +MODULE_DEVICE_TABLE(of, mtdsplit_h3c_vfs_of_match_table); + +static struct mtd_part_parser mtdsplit_h3c_vfs_parser = { + .owner = THIS_MODULE, + .name = "h3c-vfs", + .of_match_table = mtdsplit_h3c_vfs_of_match_table, + .parse_fn = mtdsplit_h3c_vfs_parse, + .type = MTD_PARSER_TYPE_FIRMWARE, +}; + +module_mtd_part_parser(mtdsplit_h3c_vfs_parser); diff --git a/target/linux/generic/files/drivers/mtd/mtdsplit/mtdsplit_seil.c b/target/linux/generic/files/drivers/mtd/mtdsplit/mtdsplit_seil.c new file mode 100644 index 000000000..e58bb49b2 --- /dev/null +++ b/target/linux/generic/files/drivers/mtd/mtdsplit/mtdsplit_seil.c @@ -0,0 +1,191 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* a mtdsplit driver for IIJ SEIL devices */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "mtdsplit.h" + +#define NR_PARTS 2 +#define SEIL_VFMT 1 +#define LDR_ENV_PART_NAME "bootloader-env" +#define LDR_ENV_KEY_BOOTDEV "BOOTDEV" + +struct seil_header { + uint64_t id; /* Identifier */ + uint8_t copy[80]; /* Copyright */ + uint32_t dcrc; /* Data CRC Checksum */ + uint32_t vfmt; /* Image Version Format */ + uint32_t vmjr; /* Image Version Major */ + uint32_t vmnr; /* Image Version Minor */ + uint8_t vrel[32]; /* Image Version Release */ + uint32_t dxor; /* xor value for Data? */ + uint32_t dlen; /* Data Length */ +}; + +/* + * check whether the current mtd device is active or not + * + * example of BOOTDEV value (IIJ SA-W2): + * - "flash" : primary image on flash + * - "rescue" : secondary image on flash + * - "usb" : usb storage + * - "lan0/1" : network + */ +static bool seil_bootdev_is_active(struct device_node *np) +{ + struct mtd_info *env_mtd; + char *buf, *var, *value, *eq; + const char *devnm; + size_t rdlen; + int ret; + + /* + * read bootdev name of the partition + * if doesn't exist, return true and skip checking of active device + */ + ret = of_property_read_string(np, "iij,bootdev-name", &devnm); + if (ret == -EINVAL) + return true; + else if (ret < 0) + return false; + + env_mtd = get_mtd_device_nm(LDR_ENV_PART_NAME); + if (IS_ERR(env_mtd)) { + pr_err("failed to get mtd device \"%s\"", LDR_ENV_PART_NAME); + return false; + } + + buf = vmalloc(env_mtd->size); + if (!buf) + return false; + + ret = mtd_read(env_mtd, 0, env_mtd->size, &rdlen, buf); + if (ret || rdlen != env_mtd->size) { + pr_err("failed to read from mtd (%d)\n", ret); + ret = 0; + goto exit_vfree; + } + + for (var = buf, ret = false; + var < buf + env_mtd->size && *var; + var = value + strlen(value) + 1) { + eq = strchr(var, '='); + if (!eq) + break; + *eq = '\0'; + value = eq + 1; + + pr_debug("ENV: %s=%s\n", var, value); + if (!strcmp(var, LDR_ENV_KEY_BOOTDEV)) { + ret = !strcmp(devnm, value); + break; + } + } + +exit_vfree: + vfree(buf); + + return ret; +} + +static int mtdsplit_parse_seil_fw(struct mtd_info *master, + const struct mtd_partition **pparts, + struct mtd_part_parser_data *data) +{ + struct device_node *np = mtd_get_of_node(master); + struct mtd_partition *parts; + struct seil_header header; + size_t image_size = 0; + size_t rootfs_offset; + size_t hdrlen = sizeof(header); + size_t retlen; + int ret; + u64 id; + + if (!seil_bootdev_is_active(np)) + return -ENODEV; + + ret = of_property_read_u64(np, "iij,seil-id", &id); + if (ret) { + pr_err("failed to get iij,seil-id from dt\n"); + return ret; + } + pr_debug("got seil-id=0x%016llx from dt\n", id); + + parts = kcalloc(NR_PARTS, sizeof(*parts), GFP_KERNEL); + if (!parts) + return -ENOMEM; + + ret = mtd_read(master, 0, hdrlen, &retlen, (void *)&header); + if (ret) + goto err_free_parts; + + if (retlen != hdrlen) { + ret = -EIO; + goto err_free_parts; + } + + if (be64_to_cpu(header.id) != id || + be32_to_cpu(header.vfmt) != SEIL_VFMT) { + pr_debug("no valid seil image found in \"%s\"\n", master->name); + ret = -ENODEV; + goto err_free_parts; + } + + image_size = hdrlen + be32_to_cpu(header.dlen); + if (image_size > master->size) { + pr_err("seil image exceeds MTD device \"%s\"\n", master->name); + ret = -EINVAL; + goto err_free_parts; + } + + /* find the roots after the seil image */ + ret = mtd_find_rootfs_from(master, image_size, + master->size, &rootfs_offset, NULL); + if (ret || (master->size - rootfs_offset) == 0) { + pr_debug("no rootfs after seil image in \"%s\"\n", + master->name); + ret = -ENODEV; + goto err_free_parts; + } + + parts[0].name = KERNEL_PART_NAME; + parts[0].offset = 0; + parts[0].size = rootfs_offset; + + parts[1].name = ROOTFS_PART_NAME; + parts[1].offset = rootfs_offset; + parts[1].size = master->size - rootfs_offset; + + *pparts = parts; + return NR_PARTS; + +err_free_parts: + kfree(parts); + return ret; +} + +static const struct of_device_id mtdsplit_seil_fw_of_match_table[] = { + { .compatible = "iij,seil-firmware" }, + {}, +}; +MODULE_DEVICE_TABLE(of, mtdsplit_seil_fw_of_match_table); + +static struct mtd_part_parser mtdsplit_seil_fw_parser = { + .owner = THIS_MODULE, + .name = "seil-fw", + .of_match_table = mtdsplit_seil_fw_of_match_table, + .parse_fn = mtdsplit_parse_seil_fw, + .type = MTD_PARSER_TYPE_FIRMWARE, +}; + +module_mtd_part_parser(mtdsplit_seil_fw_parser); diff --git a/target/linux/generic/files/drivers/mtd/nand/mtk_bmt.h b/target/linux/generic/files/drivers/mtd/nand/mtk_bmt.h index dff1f28c8..517ff7414 100644 --- a/target/linux/generic/files/drivers/mtd/nand/mtk_bmt.h +++ b/target/linux/generic/files/drivers/mtd/nand/mtk_bmt.h @@ -95,8 +95,14 @@ bbt_nand_read(u32 page, unsigned char *dat, int dat_len, .datbuf = dat, .len = dat_len, }; + int ret; - return bmtd._read_oob(bmtd.mtd, page << bmtd.pg_shift, &ops); + ret = bmtd._read_oob(bmtd.mtd, page << bmtd.pg_shift, &ops); + if (ret < 0) + return ret; + if (ret) + pr_info("%s: %d bitflips\n", __func__, ret); + return 0; } static inline int bbt_nand_erase(u16 block) 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 index 2770376e9..6b06948c0 100644 --- a/target/linux/generic/files/drivers/mtd/nand/mtk_bmt_v2.c +++ b/target/linux/generic/files/drivers/mtd/nand/mtk_bmt_v2.c @@ -191,36 +191,6 @@ static u16 get_bmt_index(struct bbmt *bmt) 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] */ @@ -472,7 +442,30 @@ static int mtk_bmt_init_v2(struct device_node *np) /* Scanning start from the first page of the last block * of whole flash */ - bmtd.bbt = scan_bmt(bmtd.total_blks - 1); + bmtd.bbt = NULL; + for (u16 block = bmtd.total_blks - 1; !bmtd.bbt && block >= bmtd.pool_lba; block--) { + u8 fdm[4]; + + if (bbt_nand_read(blk_pg(block), bmtd.bbt_buf, bufsz, fdm, sizeof(fdm))) { + /* Read failed, try the previous block */ + continue; + } + + if (!is_valid_bmt(bmtd.bbt_buf, fdm)) { + /* No valid BMT found, try the previous block */ + continue; + } + + 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"); + break; + } + + pr_info("[BBT] BMT.v2 is found at 0x%x\n", block); + bmtd.bbt = (struct bbbt *)bmtd.bbt_buf; + } + if (!bmtd.bbt) { /* BMT not found */ if (bmtd.total_blks > BB_TABLE_MAX + BMT_TABLE_MAX) { diff --git a/target/linux/generic/files/drivers/mtd/parsers/routerbootpart.c b/target/linux/generic/files/drivers/mtd/parsers/routerbootpart.c index f9bba0f3b..aa786cd89 100644 --- a/target/linux/generic/files/drivers/mtd/parsers/routerbootpart.c +++ b/target/linux/generic/files/drivers/mtd/parsers/routerbootpart.c @@ -167,7 +167,7 @@ static void routerboot_find_dynparts(struct mtd_info *master) while (offset < master->size) { err = mtd_read(master, offset, sizeof(buf), &bytes_read, (u8 *)&buf); if (err) { - pr_err("%s: mtd_read error while parsing (offset: 0x%X): %d\n", + pr_err("%s: mtd_read error while parsing (offset: 0x%zX): %d\n", master->name, offset, err); continue; } diff --git a/target/linux/generic/files/drivers/net/phy/ar8216.c b/target/linux/generic/files/drivers/net/phy/ar8216.c index 11cb18e4f..850bcefb7 100644 --- a/target/linux/generic/files/drivers/net/phy/ar8216.c +++ b/target/linux/generic/files/drivers/net/phy/ar8216.c @@ -891,11 +891,7 @@ ar8216_phy_write(struct ar8xxx_priv *priv, int addr, int regnum, u16 val) static int ar8229_hw_init(struct ar8xxx_priv *priv) { -#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 5, 0) phy_interface_t phy_if_mode; -#else - int phy_if_mode; -#endif if (priv->initialized) return 0; @@ -903,11 +899,7 @@ ar8229_hw_init(struct ar8xxx_priv *priv) ar8xxx_write(priv, AR8216_REG_CTRL, AR8216_CTRL_RESET); ar8xxx_reg_wait(priv, AR8216_REG_CTRL, AR8216_CTRL_RESET, 0, 1000); -#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 5, 0) of_get_phy_mode(priv->pdev->of_node, &phy_if_mode); -#else - phy_if_mode = of_get_phy_mode(priv->pdev->of_node); -#endif if (phy_if_mode == PHY_INTERFACE_MODE_GMII) { ar8xxx_write(priv, AR8229_REG_OPER_MODE0, @@ -1427,8 +1419,7 @@ ar8xxx_sw_reset_switch(struct switch_dev *dev) int i; mutex_lock(&priv->reg_mutex); - memset(&priv->vlan, 0, sizeof(struct ar8xxx_priv) - - offsetof(struct ar8xxx_priv, vlan)); + memset(&priv->ar8xxx_priv_volatile, 0, sizeof(priv->ar8xxx_priv_volatile)); for (i = 0; i < dev->vlans; i++) priv->vlan_id[i] = i; @@ -2433,7 +2424,9 @@ static int ar8xxx_phy_config_init(struct phy_device *phydev) { struct ar8xxx_priv *priv = phydev->priv; +#ifdef CONFIG_ETHERNET_PACKET_MANGLE struct net_device *dev = phydev->attached_dev; +#endif int ret; if (WARN_ON(!priv)) @@ -2465,7 +2458,11 @@ ar8xxx_phy_config_init(struct phy_device *phydev) /* VID fixup only needed on ar8216 */ if (chip_is_ar8216(priv)) { dev->phy_ptr = priv; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(6,1,0) dev->priv_flags |= IFF_NO_IP_ALIGN; +#else + dev->extra_priv_flags |= IFF_NO_IP_ALIGN; +#endif dev->eth_mangle_rx = ar8216_mangle_rx; dev->eth_mangle_tx = ar8216_mangle_tx; } @@ -2700,7 +2697,11 @@ ar8xxx_phy_detach(struct phy_device *phydev) #ifdef CONFIG_ETHERNET_PACKET_MANGLE dev->phy_ptr = NULL; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(6,1,0) dev->priv_flags &= ~IFF_NO_IP_ALIGN; +#else + dev->extra_priv_flags &= ~IFF_NO_IP_ALIGN; +#endif dev->eth_mangle_rx = NULL; dev->eth_mangle_tx = NULL; #endif diff --git a/target/linux/generic/files/drivers/net/phy/ar8216.h b/target/linux/generic/files/drivers/net/phy/ar8216.h index d62cf60f5..f046b35f4 100644 --- a/target/linux/generic/files/drivers/net/phy/ar8216.h +++ b/target/linux/generic/files/drivers/net/phy/ar8216.h @@ -506,20 +506,22 @@ struct ar8xxx_priv { unsigned int use_count; /* all fields below are cleared on reset */ - bool vlan; + struct_group(ar8xxx_priv_volatile, + bool vlan; - u16 vlan_id[AR8XXX_MAX_VLANS]; - u8 vlan_table[AR8XXX_MAX_VLANS]; - u8 vlan_tagged; - u16 pvid[AR8X16_MAX_PORTS]; - int arl_age_time; + u16 vlan_id[AR8XXX_MAX_VLANS]; + u8 vlan_table[AR8XXX_MAX_VLANS]; + u8 vlan_tagged; + u16 pvid[AR8X16_MAX_PORTS]; + int arl_age_time; - /* mirroring */ - bool mirror_rx; - bool mirror_tx; - int source_port; - int monitor_port; - u8 port_vlan_prio[AR8X16_MAX_PORTS]; + /* mirroring */ + bool mirror_rx; + bool mirror_tx; + int source_port; + int monitor_port; + u8 port_vlan_prio[AR8X16_MAX_PORTS]; + ); }; u32 diff --git a/target/linux/generic/files/drivers/net/phy/ar8327.c b/target/linux/generic/files/drivers/net/phy/ar8327.c index dce52ce0e..331314955 100644 --- a/target/linux/generic/files/drivers/net/phy/ar8327.c +++ b/target/linux/generic/files/drivers/net/phy/ar8327.c @@ -183,7 +183,7 @@ ar8327_phy_fixup(struct ar8xxx_priv *priv, int phy) case 2: ar8xxx_phy_mmd_write(priv, phy, 0x7, 0x3c, 0x0); - /* fallthrough */ + fallthrough; case 4: ar8xxx_phy_mmd_write(priv, phy, 0x3, 0x800d, 0x803f); ar8xxx_phy_dbg_write(priv, phy, 0x3d, 0x6860); diff --git a/target/linux/generic/files/drivers/net/phy/b53/b53_common.c b/target/linux/generic/files/drivers/net/phy/b53/b53_common.c index a6dab255c..d5f9bfc2f 100644 --- a/target/linux/generic/files/drivers/net/phy/b53/b53_common.c +++ b/target/linux/generic/files/drivers/net/phy/b53/b53_common.c @@ -506,15 +506,9 @@ static int b53_configure_ports_of(struct b53_device *dev) if (fixed_link) { u32 spd; u8 po = GMII_PO_LINK; -#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 5, 0) phy_interface_t mode; -#else - int mode = of_get_phy_mode(pn); -#endif -#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 5, 0) of_get_phy_mode(pn, &mode); -#endif if (!of_property_read_u32(fixed_link, "speed", &spd)) { switch (spd) { @@ -529,7 +523,7 @@ static int b53_configure_ports_of(struct b53_device *dev) po |= PORT_OVERRIDE_SPEED_2000M; else po |= GMII_PO_SPEED_2000M; - __attribute__((__fallthrough__)); + fallthrough; case 1000: po |= GMII_PO_SPEED_1000M; break; @@ -1617,8 +1611,8 @@ static int b53_switch_init(struct b53_device *dev) return b53_switch_reset(dev); } -struct b53_device *b53_switch_alloc(struct device *base, struct b53_io_ops *ops, - void *priv) +struct b53_device *b53_swconfig_switch_alloc(struct device *base, struct b53_io_ops *ops, + void *priv) { struct b53_device *dev; @@ -1633,9 +1627,9 @@ struct b53_device *b53_switch_alloc(struct device *base, struct b53_io_ops *ops, return dev; } -EXPORT_SYMBOL(b53_switch_alloc); +EXPORT_SYMBOL(b53_swconfig_switch_alloc); -int b53_switch_detect(struct b53_device *dev) +int b53_swconfig_switch_detect(struct b53_device *dev) { u32 id32; u16 tmp; @@ -1700,9 +1694,9 @@ int b53_switch_detect(struct b53_device *dev) return b53_read8(dev, B53_MGMT_PAGE, B53_REV_ID, &dev->core_rev); } -EXPORT_SYMBOL(b53_switch_detect); +EXPORT_SYMBOL(b53_swconfig_switch_detect); -int b53_switch_register(struct b53_device *dev) +int b53_swconfig_switch_register(struct b53_device *dev) { int ret; @@ -1712,7 +1706,7 @@ int b53_switch_register(struct b53_device *dev) dev->sw_dev.alias = dev->pdata->alias; } - if (!dev->chip_id && b53_switch_detect(dev)) + if (!dev->chip_id && b53_swconfig_switch_detect(dev)) return -EINVAL; ret = b53_switch_init(dev); @@ -1723,7 +1717,7 @@ int b53_switch_register(struct b53_device *dev) return register_switch(&dev->sw_dev, NULL); } -EXPORT_SYMBOL(b53_switch_register); +EXPORT_SYMBOL(b53_swconfig_switch_register); MODULE_AUTHOR("Jonas Gorski "); MODULE_DESCRIPTION("B53 switch library"); diff --git a/target/linux/generic/files/drivers/net/phy/b53/b53_mdio.c b/target/linux/generic/files/drivers/net/phy/b53/b53_mdio.c index 98cdbffe7..c85df1f30 100644 --- a/target/linux/generic/files/drivers/net/phy/b53/b53_mdio.c +++ b/target/linux/generic/files/drivers/net/phy/b53/b53_mdio.c @@ -280,7 +280,7 @@ static int b53_phy_probe(struct phy_device *phydev) if (phydev->mdio.addr != B53_PSEUDO_PHY && phydev->mdio.addr != 0) return -ENODEV; - dev = b53_switch_alloc(&phydev->mdio.dev, &b53_mdio_ops, phydev->mdio.bus); + dev = b53_swconfig_switch_alloc(&phydev->mdio.dev, &b53_mdio_ops, phydev->mdio.bus); if (!dev) return -ENOMEM; @@ -290,7 +290,7 @@ static int b53_phy_probe(struct phy_device *phydev) dev->pdata = NULL; mutex_init(&dev->reg_mutex); - ret = b53_switch_detect(dev); + ret = b53_swconfig_switch_detect(dev); if (ret) return ret; @@ -302,7 +302,7 @@ static int b53_phy_probe(struct phy_device *phydev) linkmode_copy(phydev->advertising, phydev->supported); - ret = b53_switch_register(dev); + ret = b53_swconfig_switch_register(dev); if (ret) { dev_err(dev->dev, "failed to register switch: %i\n", ret); return ret; @@ -361,26 +361,6 @@ static int b53_phy_read_status(struct phy_device *phydev) return 0; } -static const struct of_device_id b53_of_match_1[] = { - { .compatible = "brcm,bcm5325" }, - { .compatible = "brcm,bcm5395" }, - { .compatible = "brcm,bcm5397" }, - { .compatible = "brcm,bcm5398" }, - { /* sentinel */ }, -}; - -static const struct of_device_id b53_of_match_2[] = { - { .compatible = "brcm,bcm53115" }, - { .compatible = "brcm,bcm53125" }, - { .compatible = "brcm,bcm53128" }, - { /* sentinel */ }, -}; - -static const struct of_device_id b53_of_match_3[] = { - { .compatible = "brcm,bcm5365" }, - { /* sentinel */ }, -}; - /* BCM5325, BCM539x */ static struct phy_driver b53_phy_driver_id1 = { .phy_id = 0x0143bc00, @@ -392,10 +372,6 @@ static struct phy_driver b53_phy_driver_id1 = { .config_aneg = b53_phy_config_aneg, .config_init = b53_phy_config_init, .read_status = b53_phy_read_status, - .mdiodrv.driver = { - .name = "bcm539x", - .of_match_table = b53_of_match_1, - }, }; /* BCM53125, BCM53128 */ @@ -409,10 +385,6 @@ static struct phy_driver b53_phy_driver_id2 = { .config_aneg = b53_phy_config_aneg, .config_init = b53_phy_config_init, .read_status = b53_phy_read_status, - .mdiodrv.driver = { - .name = "bcm531xx", - .of_match_table = b53_of_match_2, - }, }; /* BCM5365 */ @@ -426,10 +398,6 @@ static struct phy_driver b53_phy_driver_id3 = { .config_aneg = b53_phy_config_aneg, .config_init = b53_phy_config_init, .read_status = b53_phy_read_status, - .mdiodrv.driver = { - .name = "bcm5365", - .of_match_table = b53_of_match_3, - }, }; int __init b53_phy_driver_register(void) diff --git a/target/linux/generic/files/drivers/net/phy/b53/b53_mmap.c b/target/linux/generic/files/drivers/net/phy/b53/b53_mmap.c index ab1895e9b..0a21ff1de 100644 --- a/target/linux/generic/files/drivers/net/phy/b53/b53_mmap.c +++ b/target/linux/generic/files/drivers/net/phy/b53/b53_mmap.c @@ -205,7 +205,7 @@ static int b53_mmap_probe(struct platform_device *pdev) if (!pdata) return -EINVAL; - dev = b53_switch_alloc(&pdev->dev, &b53_mmap_ops, pdata->regs); + dev = b53_swconfig_switch_alloc(&pdev->dev, &b53_mmap_ops, pdata->regs); if (!dev) return -ENOMEM; @@ -214,7 +214,7 @@ static int b53_mmap_probe(struct platform_device *pdev) platform_set_drvdata(pdev, dev); - return b53_switch_register(dev); + return b53_swconfig_switch_register(dev); } static int b53_mmap_remove(struct platform_device *pdev) diff --git a/target/linux/generic/files/drivers/net/phy/b53/b53_priv.h b/target/linux/generic/files/drivers/net/phy/b53/b53_priv.h index 37c17aeb2..e455c755b 100644 --- a/target/linux/generic/files/drivers/net/phy/b53/b53_priv.h +++ b/target/linux/generic/files/drivers/net/phy/b53/b53_priv.h @@ -183,12 +183,12 @@ static inline struct b53_device *sw_to_b53(struct switch_dev *sw) return container_of(sw, struct b53_device, sw_dev); } -struct b53_device *b53_switch_alloc(struct device *base, struct b53_io_ops *ops, - void *priv); +struct b53_device *b53_swconfig_switch_alloc(struct device *base, struct b53_io_ops *ops, + void *priv); -int b53_switch_detect(struct b53_device *dev); +int b53_swconfig_switch_detect(struct b53_device *dev); -int b53_switch_register(struct b53_device *dev); +int b53_swconfig_switch_register(struct b53_device *dev); static inline void b53_switch_remove(struct b53_device *dev) { diff --git a/target/linux/generic/files/drivers/net/phy/b53/b53_spi.c b/target/linux/generic/files/drivers/net/phy/b53/b53_spi.c index efc8f7ee8..400454df1 100644 --- a/target/linux/generic/files/drivers/net/phy/b53/b53_spi.c +++ b/target/linux/generic/files/drivers/net/phy/b53/b53_spi.c @@ -288,14 +288,14 @@ static int b53_spi_probe(struct spi_device *spi) struct b53_device *dev; int ret; - dev = b53_switch_alloc(&spi->dev, &b53_spi_ops, spi); + dev = b53_swconfig_switch_alloc(&spi->dev, &b53_spi_ops, spi); if (!dev) return -ENOMEM; if (spi->dev.platform_data) dev->pdata = spi->dev.platform_data; - ret = b53_switch_register(dev); + ret = b53_swconfig_switch_register(dev); if (ret) return ret; diff --git a/target/linux/generic/files/drivers/net/phy/b53/b53_srab.c b/target/linux/generic/files/drivers/net/phy/b53/b53_srab.c index 012daa3a5..ead5209cf 100644 --- a/target/linux/generic/files/drivers/net/phy/b53/b53_srab.c +++ b/target/linux/generic/files/drivers/net/phy/b53/b53_srab.c @@ -342,7 +342,7 @@ static int b53_srab_probe(struct platform_device *pdev) if (!pdata) return -EINVAL; - dev = b53_switch_alloc(&pdev->dev, &b53_srab_ops, pdata->regs); + dev = b53_swconfig_switch_alloc(&pdev->dev, &b53_srab_ops, pdata->regs); if (!dev) return -ENOMEM; @@ -351,7 +351,7 @@ static int b53_srab_probe(struct platform_device *pdev) platform_set_drvdata(pdev, dev); - return b53_switch_register(dev); + return b53_swconfig_switch_register(dev); } static int b53_srab_remove(struct platform_device *pdev) diff --git a/target/linux/generic/files/drivers/net/phy/psb6970.c b/target/linux/generic/files/drivers/net/phy/psb6970.c index fb6ed0e20..2587b9991 100644 --- a/target/linux/generic/files/drivers/net/phy/psb6970.c +++ b/target/linux/generic/files/drivers/net/phy/psb6970.c @@ -60,11 +60,13 @@ struct psb6970_priv { struct mutex reg_mutex; /* all fields below are cleared on reset */ - bool vlan; - u16 vlan_id[PSB6970_MAX_VLANS]; - u8 vlan_table[PSB6970_MAX_VLANS]; - u8 vlan_tagged; - u16 pvid[PSB6970_NUM_PORTS]; + struct_group(psb6970_priv_volatile, + bool vlan; + u16 vlan_id[PSB6970_MAX_VLANS]; + u8 vlan_table[PSB6970_MAX_VLANS]; + u8 vlan_tagged; + u16 pvid[PSB6970_NUM_PORTS]; + ); }; #define to_psb6970(_dev) container_of(_dev, struct psb6970_priv, dev) @@ -272,8 +274,8 @@ static int psb6970_reset_switch(struct switch_dev *dev) mutex_lock(&priv->reg_mutex); - memset(&priv->vlan, 0, sizeof(struct psb6970_priv) - - offsetof(struct psb6970_priv, vlan)); + memset(&priv->psb6970_priv_volatile, 0, + sizeof(priv->psb6970_priv_volatile)); for (i = 0; i < PSB6970_MAX_VLANS; i++) priv->vlan_id[i] = i; @@ -307,7 +309,6 @@ static const struct switch_dev_ops psb6970_ops = { static int psb6970_config_init(struct phy_device *pdev) { struct psb6970_priv *priv; - struct net_device *dev = pdev->attached_dev; struct switch_dev *swdev; int ret; diff --git a/target/linux/generic/files/drivers/net/phy/rtl8366_smi.c b/target/linux/generic/files/drivers/net/phy/rtl8366_smi.c index e8375e514..a26fd204c 100644 --- a/target/linux/generic/files/drivers/net/phy/rtl8366_smi.c +++ b/target/linux/generic/files/drivers/net/phy/rtl8366_smi.c @@ -256,7 +256,7 @@ static int __rtl8366_smi_read_reg(struct rtl8366_smi *smi, u32 addr, u32 *data) int __rtl8366_mdio_read_reg(struct rtl8366_smi *smi, u32 addr, u32 *data) { - u32 phy_id = MDC_REALTEK_PHY_ADDR; + u32 phy_id = smi->phy_id; struct mii_bus *mbus = smi->ext_mbus; BUG_ON(in_interrupt()); @@ -293,7 +293,7 @@ int __rtl8366_mdio_read_reg(struct rtl8366_smi *smi, u32 addr, u32 *data) static int __rtl8366_mdio_write_reg(struct rtl8366_smi *smi, u32 addr, u32 data) { - u32 phy_id = MDC_REALTEK_PHY_ADDR; + u32 phy_id = smi->phy_id; struct mii_bus *mbus = smi->ext_mbus; BUG_ON(in_interrupt()); @@ -590,7 +590,7 @@ static int rtl8366_set_pvid(struct rtl8366_smi *smi, unsigned port, return -ENOSPC; } -int rtl8366_enable_vlan(struct rtl8366_smi *smi, int enable) +static int rtl8366_smi_enable_vlan(struct rtl8366_smi *smi, int enable) { int err; @@ -607,9 +607,8 @@ int rtl8366_enable_vlan(struct rtl8366_smi *smi, int enable) return err; } -EXPORT_SYMBOL_GPL(rtl8366_enable_vlan); -static int rtl8366_enable_vlan4k(struct rtl8366_smi *smi, int enable) +static int rtl8366_smi_enable_vlan4k(struct rtl8366_smi *smi, int enable) { int err; @@ -629,7 +628,7 @@ static int rtl8366_enable_vlan4k(struct rtl8366_smi *smi, int enable) return 0; } -int rtl8366_enable_all_ports(struct rtl8366_smi *smi, int enable) +static int rtl8366_smi_enable_all_ports(struct rtl8366_smi *smi, int enable) { int port; int err; @@ -642,16 +641,15 @@ int rtl8366_enable_all_ports(struct rtl8366_smi *smi, int enable) return 0; } -EXPORT_SYMBOL_GPL(rtl8366_enable_all_ports); -int rtl8366_reset_vlan(struct rtl8366_smi *smi) +static int rtl8366_smi_reset_vlan(struct rtl8366_smi *smi) { struct rtl8366_vlan_mc vlanmc; int err; int i; - rtl8366_enable_vlan(smi, 0); - rtl8366_enable_vlan4k(smi, 0); + rtl8366_smi_enable_vlan(smi, 0); + rtl8366_smi_enable_vlan4k(smi, 0); /* clear VLAN member configurations */ vlanmc.vid = 0; @@ -667,14 +665,13 @@ int rtl8366_reset_vlan(struct rtl8366_smi *smi) return 0; } -EXPORT_SYMBOL_GPL(rtl8366_reset_vlan); static int rtl8366_init_vlan(struct rtl8366_smi *smi) { int port; int err; - err = rtl8366_reset_vlan(smi); + err = rtl8366_smi_reset_vlan(smi); if (err) return err; @@ -695,7 +692,7 @@ static int rtl8366_init_vlan(struct rtl8366_smi *smi) return err; } - return rtl8366_enable_vlan(smi, 1); + return rtl8366_smi_enable_vlan(smi, 1); } #ifdef CONFIG_RTL8366_SMI_DEBUG_FS @@ -1073,15 +1070,15 @@ int rtl8366_sw_reset_switch(struct switch_dev *dev) if (err) return err; - err = rtl8366_reset_vlan(smi); + err = rtl8366_smi_reset_vlan(smi); if (err) return err; - err = rtl8366_enable_vlan(smi, 1); + err = rtl8366_smi_enable_vlan(smi, 1); if (err) return err; - return rtl8366_enable_all_ports(smi, 1); + return rtl8366_smi_enable_all_ports(smi, 1); } EXPORT_SYMBOL_GPL(rtl8366_sw_reset_switch); @@ -1343,9 +1340,9 @@ int rtl8366_sw_set_vlan_enable(struct switch_dev *dev, return -EINVAL; if (attr->ofs == 1) - err = rtl8366_enable_vlan(smi, val->value.i); + err = rtl8366_smi_enable_vlan(smi, val->value.i); else - err = rtl8366_enable_vlan4k(smi, val->value.i); + err = rtl8366_smi_enable_vlan4k(smi, val->value.i); return err; } @@ -1494,7 +1491,7 @@ int rtl8366_smi_init(struct rtl8366_smi *smi) goto err_free_sck; } - err = rtl8366_enable_all_ports(smi, 1); + err = rtl8366_smi_enable_all_ports(smi, 1); if (err) goto err_free_sck; @@ -1550,6 +1547,9 @@ int rtl8366_smi_probe_of(struct platform_device *pdev, struct rtl8366_smi *smi) goto try_gpio; } + if (of_property_read_u32(np, "phy-id", &smi->phy_id)) + smi->phy_id = MDC_REALTEK_PHY_ADDR; + return 0; try_gpio: @@ -1589,6 +1589,7 @@ int rtl8366_smi_probe_plat(struct platform_device *pdev, struct rtl8366_smi *smi smi->gpio_sda = pdata->gpio_sda; smi->gpio_sck = pdata->gpio_sck; smi->hw_reset = pdata->hw_reset; + smi->phy_id = MDC_REALTEK_PHY_ADDR; return 0; } diff --git a/target/linux/generic/files/drivers/net/phy/rtl8366_smi.h b/target/linux/generic/files/drivers/net/phy/rtl8366_smi.h index d1d988a37..3fcae81fa 100644 --- a/target/linux/generic/files/drivers/net/phy/rtl8366_smi.h +++ b/target/linux/generic/files/drivers/net/phy/rtl8366_smi.h @@ -63,6 +63,7 @@ struct rtl8366_smi { u16 dbg_reg; u8 dbg_vlan_4k_page; #endif + u32 phy_id; struct mii_bus *ext_mbus; }; @@ -115,10 +116,6 @@ int rtl8366_smi_write_reg_noack(struct rtl8366_smi *smi, u32 addr, u32 data); int rtl8366_smi_read_reg(struct rtl8366_smi *smi, u32 addr, u32 *data); int rtl8366_smi_rmwr(struct rtl8366_smi *smi, u32 addr, u32 mask, u32 data); -int rtl8366_reset_vlan(struct rtl8366_smi *smi); -int rtl8366_enable_vlan(struct rtl8366_smi *smi, int enable); -int rtl8366_enable_all_ports(struct rtl8366_smi *smi, int enable); - #ifdef CONFIG_RTL8366_SMI_DEBUG_FS int rtl8366_debugfs_open(struct inode *inode, struct file *file); #endif diff --git a/target/linux/generic/files/drivers/net/phy/rtl8367.c b/target/linux/generic/files/drivers/net/phy/rtl8367.c index 7f0569d03..b14b63e03 100644 --- a/target/linux/generic/files/drivers/net/phy/rtl8367.c +++ b/target/linux/generic/files/drivers/net/phy/rtl8367.c @@ -1641,7 +1641,7 @@ static int rtl8367_switch_init(struct rtl8366_smi *smi) int err; dev->name = "RTL8367"; - dev->cpu_port = RTL8367_CPU_PORT_NUM; + dev->cpu_port = smi->cpu_port; dev->ports = RTL8367_NUM_PORTS; dev->vlans = RTL8367_NUM_VIDS; dev->ops = &rtl8367_sw_ops; @@ -1722,6 +1722,11 @@ static int rtl8367_detect(struct rtl8366_smi *smi) dev_info(smi->parent, "RTL%s ver. %u chip found\n", chip_name, rtl_ver & RTL8367_RTL_VER_MASK); + if (of_property_present(smi->parent->of_node, "realtek,extif1")) + smi->cpu_port = RTL8367_CPU_PORT_NUM - 1; + + dev_info(smi->parent, "CPU port: %u\n", smi->cpu_port); + return 0; } diff --git a/target/linux/generic/files/drivers/net/phy/rtl8367b.c b/target/linux/generic/files/drivers/net/phy/rtl8367b.c index cd0d58cbf..04c790e92 100644 --- a/target/linux/generic/files/drivers/net/phy/rtl8367b.c +++ b/target/linux/generic/files/drivers/net/phy/rtl8367b.c @@ -1583,6 +1583,13 @@ static int rtl8367b_detect(struct rtl8366_smi *smi) dev_info(smi->parent, "RTL%s chip found\n", chip_name); + if (of_property_present(smi->parent->of_node, "realtek,extif2")) + smi->cpu_port = RTL8367B_CPU_PORT_NUM + 2; + else if (of_property_present(smi->parent->of_node, "realtek,extif1") && (chip_ver != 0x1010)) /* for the RTL8367R-VB chip, extif1 corresponds to cpu_port 5 */ + smi->cpu_port = RTL8367B_CPU_PORT_NUM + 1; + + dev_info(smi->parent, "CPU port: %u\n", smi->cpu_port); + return 0; } @@ -1621,9 +1628,7 @@ static int rtl8367b_probe(struct platform_device *pdev) smi->cmd_write = 0xb8; smi->ops = &rtl8367b_smi_ops; smi->num_ports = RTL8367B_NUM_PORTS; - if (of_property_read_u32(pdev->dev.of_node, "cpu_port", &smi->cpu_port) - || smi->cpu_port >= smi->num_ports) - smi->cpu_port = RTL8367B_CPU_PORT_NUM; + smi->cpu_port = RTL8367B_CPU_PORT_NUM; smi->num_vlan_mc = RTL8367B_NUM_VLANS; smi->mib_counters = rtl8367b_mib_counters; smi->num_mib_counters = ARRAY_SIZE(rtl8367b_mib_counters); diff --git a/target/linux/generic/files/drivers/net/phy/swconfig.c b/target/linux/generic/files/drivers/net/phy/swconfig.c index a734e5760..5fa2b147c 100644 --- a/target/linux/generic/files/drivers/net/phy/swconfig.c +++ b/target/linux/generic/files/drivers/net/phy/swconfig.c @@ -1054,6 +1054,9 @@ static struct genl_family switch_fam = { .module = THIS_MODULE, .ops = swconfig_ops, .n_ops = ARRAY_SIZE(swconfig_ops), +#if LINUX_VERSION_CODE > KERNEL_VERSION(6,0,0) + .resv_start_op = SWITCH_CMD_SET_VLAN + 1, +#endif }; #ifdef CONFIG_OF diff --git a/target/linux/generic/files/drivers/net/phy/swconfig_leds.c b/target/linux/generic/files/drivers/net/phy/swconfig_leds.c index 767d9221b..1d309c046 100644 --- a/target/linux/generic/files/drivers/net/phy/swconfig_leds.c +++ b/target/linux/generic/files/drivers/net/phy/swconfig_leds.c @@ -85,7 +85,7 @@ swconfig_trig_update_port_mask(struct led_trigger *trigger) sw_trig = (void *) trigger; port_mask = 0; -#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 18, 0) +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5,16,0) spin_lock(&trigger->leddev_list_lock); #else read_lock(&trigger->leddev_list_lock); @@ -102,11 +102,12 @@ swconfig_trig_update_port_mask(struct led_trigger *trigger) read_unlock(&trig_data->lock); } } -#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 18, 0) +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5,16,0) spin_unlock(&trigger->leddev_list_lock); #else read_unlock(&trigger->leddev_list_lock); #endif + sw_trig->port_mask = port_mask; if (port_mask) @@ -425,7 +426,7 @@ swconfig_trig_update_leds(struct switch_led_trigger *sw_trig) struct led_trigger *trigger; trigger = &sw_trig->trig; -#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 18, 0) +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5,16,0) spin_lock(&trigger->leddev_list_lock); #else read_lock(&trigger->leddev_list_lock); @@ -436,7 +437,7 @@ swconfig_trig_update_leds(struct switch_led_trigger *sw_trig) led_cdev = list_entry(entry, struct led_classdev, trig_list); swconfig_trig_led_event(sw_trig, led_cdev); } -#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 18, 0) +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5,16,0) spin_unlock(&trigger->leddev_list_lock); #else read_unlock(&trigger->leddev_list_lock); diff --git a/target/linux/generic/files/drivers/platform/mikrotik/Kconfig b/target/linux/generic/files/drivers/platform/mikrotik/Kconfig index 7499ba1e1..32ef8f29d 100644 --- a/target/linux/generic/files/drivers/platform/mikrotik/Kconfig +++ b/target/linux/generic/files/drivers/platform/mikrotik/Kconfig @@ -1,6 +1,5 @@ menuconfig MIKROTIK bool "Platform support for MikroTik RouterBoard virtual devices" - default n help Say Y here to get to see options for the MikroTik RouterBoard platform. This option alone does not add any kernel code. @@ -16,4 +15,10 @@ config MIKROTIK_RB_SYSFS help This driver exposes RouterBoot configuration in sysfs. +config NVMEM_LAYOUT_MIKROTIK + tristate "RouterBoot NVMEM layout support" + depends on NVMEM_LAYOUTS + help + This driver exposes MikroTik hard_config via NVMEM layout. + endif # MIKROTIK diff --git a/target/linux/generic/files/drivers/platform/mikrotik/Makefile b/target/linux/generic/files/drivers/platform/mikrotik/Makefile index a232e1a9e..164b23b70 100644 --- a/target/linux/generic/files/drivers/platform/mikrotik/Makefile +++ b/target/linux/generic/files/drivers/platform/mikrotik/Makefile @@ -2,3 +2,4 @@ # Makefile for MikroTik RouterBoard platform specific drivers # obj-$(CONFIG_MIKROTIK_RB_SYSFS) += routerboot.o rb_hardconfig.o rb_softconfig.o +obj-$(CONFIG_NVMEM_LAYOUT_MIKROTIK) += rb_nvmem.o diff --git a/target/linux/generic/files/drivers/platform/mikrotik/rb_hardconfig.c b/target/linux/generic/files/drivers/platform/mikrotik/rb_hardconfig.c index e6a692889..78e39a7f9 100644 --- a/target/linux/generic/files/drivers/platform/mikrotik/rb_hardconfig.c +++ b/target/linux/generic/files/drivers/platform/mikrotik/rb_hardconfig.c @@ -37,29 +37,12 @@ #include #include +#include "rb_hardconfig.h" #include "routerboot.h" -#define RB_HARDCONFIG_VER "0.06" +#define RB_HARDCONFIG_VER "0.07" #define RB_HC_PR_PFX "[rb_hardconfig] " -/* ID values for hardware settings */ -#define RB_ID_FLASH_INFO 0x03 -#define RB_ID_MAC_ADDRESS_PACK 0x04 -#define RB_ID_BOARD_PRODUCT_CODE 0x05 -#define RB_ID_BIOS_VERSION 0x06 -#define RB_ID_SDRAM_TIMINGS 0x08 -#define RB_ID_DEVICE_TIMINGS 0x09 -#define RB_ID_SOFTWARE_ID 0x0A -#define RB_ID_SERIAL_NUMBER 0x0B -#define RB_ID_MEMORY_SIZE 0x0D -#define RB_ID_MAC_ADDRESS_COUNT 0x0E -#define RB_ID_HW_OPTIONS 0x15 -#define RB_ID_WLAN_DATA 0x16 -#define RB_ID_BOARD_IDENTIFIER 0x17 -#define RB_ID_PRODUCT_NAME 0x21 -#define RB_ID_DEFCONF 0x26 -#define RB_ID_BOARD_REVISION 0x27 - /* Bit definitions for hardware options */ #define RB_HW_OPT_NO_UART BIT(0) #define RB_HW_OPT_HAS_VOLTAGE BIT(1) @@ -676,10 +659,9 @@ static ssize_t hc_wlan_data_bin_read(struct file *filp, struct kobject *kobj, return count; } -int __init rb_hardconfig_init(struct kobject *rb_kobj) +int rb_hardconfig_init(struct kobject *rb_kobj, struct mtd_info *mtd) { struct kobject *hc_wlan_kobj; - struct mtd_info *mtd; size_t bytes_read, buflen, outlen; const u8 *buf; void *outbuf; @@ -690,20 +672,19 @@ int __init rb_hardconfig_init(struct kobject *rb_kobj) hc_kobj = NULL; hc_wlan_kobj = NULL; - // TODO allow override - mtd = get_mtd_device_nm(RB_MTD_HARD_CONFIG); - if (IS_ERR(mtd)) + ret = __get_mtd_device(mtd); + if (ret) return -ENODEV; hc_buflen = mtd->size; hc_buf = kmalloc(hc_buflen, GFP_KERNEL); if (!hc_buf) { - put_mtd_device(mtd); + __put_mtd_device(mtd); return -ENOMEM; } ret = mtd_read(mtd, 0, hc_buflen, &bytes_read, hc_buf); - put_mtd_device(mtd); + __put_mtd_device(mtd); if (ret) goto fail; @@ -818,8 +799,10 @@ fail: return ret; } -void __exit rb_hardconfig_exit(void) +void rb_hardconfig_exit(void) { kobject_put(hc_kobj); + hc_kobj = NULL; kfree(hc_buf); + hc_buf = NULL; } diff --git a/target/linux/generic/files/drivers/platform/mikrotik/rb_hardconfig.h b/target/linux/generic/files/drivers/platform/mikrotik/rb_hardconfig.h new file mode 100644 index 000000000..328f4fe3c --- /dev/null +++ b/target/linux/generic/files/drivers/platform/mikrotik/rb_hardconfig.h @@ -0,0 +1,32 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Common definitions for MikroTik RouterBoot hard config data. + * + * Copyright (C) 2020 Thibaut VARÈNE + * + * Some constant defines extracted from routerboot.{c,h} by Gabor Juhos + * + */ + +#ifndef _ROUTERBOOT_HARD_CONFIG_H_ +#define _ROUTERBOOT_HARD_CONFIG_H_ + +/* ID values for hardware settings */ +#define RB_ID_FLASH_INFO 0x03 +#define RB_ID_MAC_ADDRESS_PACK 0x04 +#define RB_ID_BOARD_PRODUCT_CODE 0x05 +#define RB_ID_BIOS_VERSION 0x06 +#define RB_ID_SDRAM_TIMINGS 0x08 +#define RB_ID_DEVICE_TIMINGS 0x09 +#define RB_ID_SOFTWARE_ID 0x0A +#define RB_ID_SERIAL_NUMBER 0x0B +#define RB_ID_MEMORY_SIZE 0x0D +#define RB_ID_MAC_ADDRESS_COUNT 0x0E +#define RB_ID_HW_OPTIONS 0x15 +#define RB_ID_WLAN_DATA 0x16 +#define RB_ID_BOARD_IDENTIFIER 0x17 +#define RB_ID_PRODUCT_NAME 0x21 +#define RB_ID_DEFCONF 0x26 +#define RB_ID_BOARD_REVISION 0x27 + +#endif /* _ROUTERBOOT_HARD_CONFIG_H_ */ diff --git a/target/linux/generic/files/drivers/platform/mikrotik/rb_nvmem.c b/target/linux/generic/files/drivers/platform/mikrotik/rb_nvmem.c new file mode 100644 index 000000000..6f785ce7d --- /dev/null +++ b/target/linux/generic/files/drivers/platform/mikrotik/rb_nvmem.c @@ -0,0 +1,230 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * NVMEM layout driver for MikroTik Routerboard hard config cells + * + * Copyright (C) 2024 Robert Marko + * Based on the sysfs hard config driver by Thibaut VARÈNE + * Comments documenting the format carried over from routerboot.c + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "rb_hardconfig.h" +#include "routerboot.h" + +#define TLV_TAG_MASK GENMASK(15, 0) +#define TLV_LEN_MASK GENMASK(31, 16) + +static const char *rb_tlv_cell_name(u16 tag) +{ + switch (tag) { + case RB_ID_FLASH_INFO: + return "flash-info"; + case RB_ID_MAC_ADDRESS_PACK: + return "base-mac-address"; + case RB_ID_BOARD_PRODUCT_CODE: + return "board-product-code"; + case RB_ID_BIOS_VERSION: + return "booter-version"; + case RB_ID_SERIAL_NUMBER: + return "board-serial"; + case RB_ID_MEMORY_SIZE: + return "mem-size"; + case RB_ID_MAC_ADDRESS_COUNT: + return "mac-count"; + case RB_ID_HW_OPTIONS: + return "hw-options"; + case RB_ID_WLAN_DATA: + return "wlan-data"; + case RB_ID_BOARD_IDENTIFIER: + return "board-identifier"; + case RB_ID_PRODUCT_NAME: + return "product-name"; + case RB_ID_DEFCONF: + return "defconf"; + case RB_ID_BOARD_REVISION: + return "board-revision"; + default: + break; + } + + return NULL; +} + +static int rb_tlv_mac_read_cb(void *priv, const char *id, int index, + unsigned int offset, void *buf, + size_t bytes) +{ + if (index < 0) + return -EINVAL; + + if (!is_valid_ether_addr(buf)) + return -EINVAL; + + eth_addr_add(buf, index); + + return 0; +} + +static nvmem_cell_post_process_t rb_tlv_read_cb(u16 tag) +{ + switch (tag) { + case RB_ID_MAC_ADDRESS_PACK: + return &rb_tlv_mac_read_cb; + default: + break; + } + + return NULL; +} + +static int rb_add_cells(struct device *dev, struct nvmem_device *nvmem, + const size_t data_len, u8 *data) +{ + u32 node, offset = sizeof(RB_MAGIC_HARD); + struct nvmem_cell_info cell = {}; + struct device_node *layout; + u16 tlv_tag, tlv_len; + int ret; + + layout = of_nvmem_layout_get_container(nvmem); + if (!layout) + return -ENOENT; + + /* + * Routerboot tag nodes are u32 values: + * - The low nibble is the tag identification number, + * - The high nibble is the tag payload length (node excluded) in bytes. + * Tag nodes are CPU-endian. + * Tag nodes are 32bit-aligned. + * + * The payload immediately follows the tag node. + * Payload offset will always be aligned. while length may not end on 32bit + * boundary (the only known case is when parsing ERD data). + * The payload is CPU-endian when applicable. + * Tag nodes are not ordered (by ID) on flash. + */ + while ((offset + sizeof(node)) <= data_len) { + node = *((const u32 *) (data + offset)); + /* Tag list ends with null node */ + if (!node) + break; + + tlv_tag = FIELD_GET(TLV_TAG_MASK, node); + tlv_len = FIELD_GET(TLV_LEN_MASK, node); + + offset += sizeof(node); + if (offset + tlv_len > data_len) { + dev_err(dev, "Out of bounds field (0x%x bytes at 0x%x)\n", + tlv_len, offset); + break; + } + + cell.name = rb_tlv_cell_name(tlv_tag); + if (!cell.name) + goto skip; + + cell.offset = offset; + /* + * MikroTik stores MAC-s with length of 8 bytes, + * but kernel expects it to be ETH_ALEN (6 bytes), + * so we need to make sure that is the case. + */ + if (tlv_tag == RB_ID_MAC_ADDRESS_PACK) + cell.bytes = ETH_ALEN; + else + cell.bytes = tlv_len; + cell.np = of_get_child_by_name(layout, cell.name); + cell.read_post_process = rb_tlv_read_cb(tlv_tag); + + ret = nvmem_add_one_cell(nvmem, &cell); + if (ret) { + of_node_put(layout); + return ret; + } + + /* + * The only known situation where len may not end on 32bit + * boundary is within ERD data. Since we're only extracting + * one tag (the first and only one) from that data, we should + * never need to forcefully ALIGN(). Do it anyway, this is not a + * performance path. + */ +skip: + offset += ALIGN(tlv_len, sizeof(offset)); + } + + of_node_put(layout); + + return 0; +} + +static int rb_parse_table(struct nvmem_layout *layout) +{ + struct nvmem_device *nvmem = layout->nvmem; + struct device *dev = &layout->dev; + size_t mtd_size; + u8 *data; + u32 hdr; + int ret; + + ret = nvmem_device_read(nvmem, 0, sizeof(hdr), &hdr); + if (ret < 0) + return ret; + + if (hdr != RB_MAGIC_HARD) { + dev_err(dev, "Invalid header\n"); + return -EINVAL; + } + + mtd_size = nvmem_dev_size(nvmem); + + data = devm_kmalloc(dev, mtd_size, GFP_KERNEL); + if (!data) + return -ENOMEM; + + ret = nvmem_device_read(nvmem, 0, mtd_size, data); + if (ret != mtd_size) + return ret; + + return rb_add_cells(dev, nvmem, mtd_size, data); +} + +static int rb_nvmem_probe(struct nvmem_layout *layout) +{ + layout->add_cells = rb_parse_table; + + return nvmem_layout_register(layout); +} + +static void rb_nvmem_remove(struct nvmem_layout *layout) +{ + nvmem_layout_unregister(layout); +} + +static const struct of_device_id rb_nvmem_of_match_table[] = { + { .compatible = "mikrotik,routerboot-nvmem", }, + {}, +}; +MODULE_DEVICE_TABLE(of, rb_nvmem_of_match_table); + +static struct nvmem_layout_driver rb_nvmem_layout = { + .probe = rb_nvmem_probe, + .remove = rb_nvmem_remove, + .driver = { + .owner = THIS_MODULE, + .name = "rb_nvmem", + .of_match_table = rb_nvmem_of_match_table, + }, +}; +module_nvmem_layout_driver(rb_nvmem_layout); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Robert Marko "); +MODULE_DESCRIPTION("NVMEM layout driver for MikroTik Routerboard hard config cells"); diff --git a/target/linux/generic/files/drivers/platform/mikrotik/rb_softconfig.c b/target/linux/generic/files/drivers/platform/mikrotik/rb_softconfig.c index 070bd32d5..5acff6aa9 100644 --- a/target/linux/generic/files/drivers/platform/mikrotik/rb_softconfig.c +++ b/target/linux/generic/files/drivers/platform/mikrotik/rb_softconfig.c @@ -56,23 +56,12 @@ #include "routerboot.h" -#define RB_SOFTCONFIG_VER "0.03" +#define RB_SOFTCONFIG_VER "0.05" #define RB_SC_PR_PFX "[rb_softconfig] " -/* - * mtd operations before 4.17 are asynchronous, not handled by this code - * Also make the driver act read-only if 4K_SECTORS are not enabled, since they - * are require to handle partial erasing of the small soft_config partition. - */ -#if defined(CONFIG_MTD_SPI_NOR_USE_4K_SECTORS) - #define RB_SC_HAS_WRITE_SUPPORT true - #define RB_SC_WMODE S_IWUSR - #define RB_SC_RMODE S_IRUSR -#else - #define RB_SC_HAS_WRITE_SUPPORT false - #define RB_SC_WMODE 0 - #define RB_SC_RMODE S_IRUSR -#endif +#define RB_SC_HAS_WRITE_SUPPORT true +#define RB_SC_WMODE S_IWUSR +#define RB_SC_RMODE S_IRUSR /* ID values for software settings */ #define RB_SCID_UART_SPEED 0x01 // u32*1 @@ -705,9 +694,8 @@ mtdfail: static struct kobj_attribute sc_kattrcommit = __ATTR(commit, RB_SC_RMODE|RB_SC_WMODE, sc_commit_show, sc_commit_store); -int __init rb_softconfig_init(struct kobject *rb_kobj) +int rb_softconfig_init(struct kobject *rb_kobj, struct mtd_info *mtd) { - struct mtd_info *mtd; size_t bytes_read, buflen; const u8 *buf; int i, ret; @@ -716,20 +704,19 @@ int __init rb_softconfig_init(struct kobject *rb_kobj) sc_buf = NULL; sc_kobj = NULL; - // TODO allow override - mtd = get_mtd_device_nm(RB_MTD_SOFT_CONFIG); - if (IS_ERR(mtd)) + ret = __get_mtd_device(mtd); + if (ret) return -ENODEV; sc_buflen = mtd->size; sc_buf = kmalloc(sc_buflen, GFP_KERNEL); if (!sc_buf) { - put_mtd_device(mtd); + __put_mtd_device(mtd); return -ENOMEM; } ret = mtd_read(mtd, 0, sc_buflen, &bytes_read, sc_buf); - put_mtd_device(mtd); + __put_mtd_device(mtd); if (ret) goto fail; @@ -799,8 +786,10 @@ fail: return ret; } -void __exit rb_softconfig_exit(void) +void rb_softconfig_exit(void) { kobject_put(sc_kobj); + sc_kobj = NULL; kfree(sc_buf); + sc_buf = NULL; } diff --git a/target/linux/generic/files/drivers/platform/mikrotik/routerboot.c b/target/linux/generic/files/drivers/platform/mikrotik/routerboot.c index 4c8c0bfac..96f246091 100644 --- a/target/linux/generic/files/drivers/platform/mikrotik/routerboot.c +++ b/target/linux/generic/files/drivers/platform/mikrotik/routerboot.c @@ -13,6 +13,7 @@ #include #include #include +#include #include "routerboot.h" @@ -160,25 +161,57 @@ fail: return ret; } +static void routerboot_mtd_notifier_add(struct mtd_info *mtd) +{ + /* Currently routerboot is only known to live on NOR flash */ + if (mtd->type != MTD_NORFLASH) + return; + + /* + * We ignore the following return values and always register. + * These init() routines are designed so that their failed state is + * always manageable by the corresponding exit() calls. + * Notifier is called with MTD mutex held: use __get/__put variants. + * TODO: allow partition names override + */ + if (!strcmp(mtd->name, RB_MTD_HARD_CONFIG)) + rb_hardconfig_init(rb_kobj, mtd); + else if (!strcmp(mtd->name, RB_MTD_SOFT_CONFIG)) + rb_softconfig_init(rb_kobj, mtd); +} + +static void routerboot_mtd_notifier_remove(struct mtd_info *mtd) +{ + if (mtd->type != MTD_NORFLASH) + return; + + if (!strcmp(mtd->name, RB_MTD_HARD_CONFIG)) + rb_hardconfig_exit(); + else if (!strcmp(mtd->name, RB_MTD_SOFT_CONFIG)) + rb_softconfig_exit(); +} + +/* Note: using a notifier prevents qualifying init()/exit() functions with __init/__exit */ +static struct mtd_notifier routerboot_mtd_notifier = { + .add = routerboot_mtd_notifier_add, + .remove = routerboot_mtd_notifier_remove, +}; + static int __init routerboot_init(void) { rb_kobj = kobject_create_and_add("mikrotik", firmware_kobj); if (!rb_kobj) return -ENOMEM; - /* - * We ignore the following return values and always register. - * These init() routines are designed so that their failed state is - * always manageable by the corresponding exit() calls. - */ - rb_hardconfig_init(rb_kobj); - rb_softconfig_init(rb_kobj); + register_mtd_user(&routerboot_mtd_notifier); return 0; } static void __exit routerboot_exit(void) { + unregister_mtd_user(&routerboot_mtd_notifier); + /* Exit routines are idempotent */ rb_softconfig_exit(); rb_hardconfig_exit(); kobject_put(rb_kobj); // recursive afaict diff --git a/target/linux/generic/files/drivers/platform/mikrotik/routerboot.h b/target/linux/generic/files/drivers/platform/mikrotik/routerboot.h index 67d89808d..e858a524a 100644 --- a/target/linux/generic/files/drivers/platform/mikrotik/routerboot.h +++ b/target/linux/generic/files/drivers/platform/mikrotik/routerboot.h @@ -25,11 +25,11 @@ int routerboot_tag_find(const u8 *bufhead, const size_t buflen, const u16 tag_id, u16 *pld_ofs, u16 *pld_len); int routerboot_rle_decode(const u8 *in, size_t inlen, u8 *out, size_t *outlen); -int __init rb_hardconfig_init(struct kobject *rb_kobj); -void __exit rb_hardconfig_exit(void); +int rb_hardconfig_init(struct kobject *rb_kobj, struct mtd_info *mtd); +void rb_hardconfig_exit(void); -int __init rb_softconfig_init(struct kobject *rb_kobj); -void __exit rb_softconfig_exit(void); +int rb_softconfig_init(struct kobject *rb_kobj, struct mtd_info *mtd); +void rb_softconfig_exit(void); ssize_t routerboot_tag_show_string(const u8 *pld, u16 pld_len, char *buf); ssize_t routerboot_tag_show_u32s(const u8 *pld, u16 pld_len, char *buf); diff --git a/target/linux/generic/hack-6.1/205-kconfig-abort-configuration-on-unset-symbol.patch b/target/linux/generic/hack-6.1/205-kconfig-abort-configuration-on-unset-symbol.patch new file mode 100644 index 000000000..df1850704 --- /dev/null +++ b/target/linux/generic/hack-6.1/205-kconfig-abort-configuration-on-unset-symbol.patch @@ -0,0 +1,41 @@ +From 310e8e04a05d9eb43fa9dd7f00143300afcaa37a Mon Sep 17 00:00:00 2001 +From: David Bauer +Date: Fri, 11 Nov 2022 13:33:44 +0100 +Subject: [PATCH] kconfig: abort configuration on unset symbol + +When a target configuration has unset Kconfig symbols, the build will +fail when OpenWrt is compiled with V=s and stdin is connected to a tty. + +In case OpenWrt is compiled without either of these preconditions, the +build will succeed with the symbols in question being unset. + +Modify the kernel configuration in a way it fails on unset symbols +regardless of the aforementioned preconditions. + +Signed-off-by: David Bauer +--- + scripts/kconfig/conf.c | 6 ++++++ + 1 file changed, 6 insertions(+) + +--- a/scripts/kconfig/conf.c ++++ b/scripts/kconfig/conf.c +@@ -338,6 +338,9 @@ static int conf_askvalue(struct symbol * + } + /* fall through */ + default: ++ if (!tty_stdio && getenv("FAIL_ON_UNCONFIGURED")) { ++ exit(1); ++ } + fflush(stdout); + xfgets(line, sizeof(line), stdin); + break; +@@ -520,6 +523,9 @@ static int conf_choice(struct menu *menu + } + /* fall through */ + case oldaskconfig: ++ if (!tty_stdio && getenv("FAIL_ON_UNCONFIGURED")) { ++ exit(1); ++ } + fflush(stdout); + xfgets(line, sizeof(line), stdin); + strip(line); diff --git a/target/linux/generic/hack-6.1/205-kconfig-exit.patch b/target/linux/generic/hack-6.1/205-kconfig-exit.patch deleted file mode 100644 index 2c6989578..000000000 --- a/target/linux/generic/hack-6.1/205-kconfig-exit.patch +++ /dev/null @@ -1,20 +0,0 @@ -From 300d26562ce4dc427154cb247beb75db4b1f0774 Mon Sep 17 00:00:00 2001 -From: OpenWrt community -Date: Wed, 13 Jul 2022 13:29:57 +0200 -Subject: [PATCH] scripts/Kconfig: Kconfig exit - ---- - scripts/kconfig/conf.c | 2 ++ - 1 file changed, 2 insertions(+) - ---- a/scripts/kconfig/conf.c -+++ b/scripts/kconfig/conf.c -@@ -432,6 +432,8 @@ static int conf_sym(struct menu *menu) - break; - continue; - case 0: -+ if (!sym_has_value(sym) && !tty_stdio && getenv("FAIL_ON_UNCONFIGURED")) -+ exit(1); - newval = oldval; - break; - case '?': diff --git a/target/linux/generic/hack-6.1/212-tools_portability.patch b/target/linux/generic/hack-6.1/212-tools_portability.patch index 4b1c6c498..3ee439300 100644 --- a/target/linux/generic/hack-6.1/212-tools_portability.patch +++ b/target/linux/generic/hack-6.1/212-tools_portability.patch @@ -39,7 +39,7 @@ Signed-off-by: Felix Fietkau static inline uint16_t __get_unaligned_le16(const uint8_t *p) --- /dev/null +++ b/tools/include/tools/linux_types.h -@@ -0,0 +1,26 @@ +@@ -0,0 +1,18 @@ +#ifndef __LINUX_TYPES_H +#define __LINUX_TYPES_H + @@ -47,23 +47,15 @@ Signed-off-by: Felix Fietkau + +typedef int8_t __s8; +typedef uint8_t __u8; -+typedef uint8_t __be8; -+typedef uint8_t __le8; + +typedef int16_t __s16; +typedef uint16_t __u16; -+typedef uint16_t __be16; -+typedef uint16_t __le16; + +typedef int32_t __s32; +typedef uint32_t __u32; -+typedef uint32_t __be32; -+typedef uint32_t __le32; + +typedef int64_t __s64; +typedef uint64_t __u64; -+typedef uint64_t __be64; -+typedef uint64_t __le64; + +#endif --- a/tools/include/linux/types.h @@ -81,6 +73,16 @@ Signed-off-by: Felix Fietkau struct page; struct kmem_cache; +@@ -51,7 +55,9 @@ typedef __s8 s8; + #define __force + #define __user + #define __must_check ++#ifndef __cold + #define __cold ++#endif + + typedef __u16 __bitwise __le16; + typedef __u16 __bitwise __be16; --- a/tools/perf/pmu-events/jevents.py +++ b/tools/perf/pmu-events/jevents.py @@ -684,6 +684,7 @@ def main() -> None: @@ -91,3 +93,268 @@ Signed-off-by: Felix Fietkau #include struct compact_pmu_event { +--- a/tools/arch/x86/include/asm/insn.h ++++ b/tools/arch/x86/include/asm/insn.h +@@ -7,8 +7,8 @@ + * Copyright (C) IBM Corporation, 2009 + */ + +-#include + /* insn_attr_t is defined in inat.h */ ++#include + #include "inat.h" /* __ignore_sync_check__ */ + + #if defined(__BYTE_ORDER) ? __BYTE_ORDER == __LITTLE_ENDIAN : defined(__LITTLE_ENDIAN) +--- a/tools/arch/x86/include/asm/orc_types.h ++++ b/tools/arch/x86/include/asm/orc_types.h +@@ -40,7 +40,6 @@ + #define ORC_REG_MAX 15 + + #ifndef __ASSEMBLY__ +-#include + + /* + * This struct is more or less a vastly simplified version of the DWARF Call +@@ -53,12 +52,12 @@ + struct orc_entry { + s16 sp_offset; + s16 bp_offset; +-#if defined(__LITTLE_ENDIAN_BITFIELD) ++#if __BYTE_ORDER == __LITTLE_ENDIAN + unsigned sp_reg:4; + unsigned bp_reg:4; + unsigned type:2; + unsigned end:1; +-#elif defined(__BIG_ENDIAN_BITFIELD) ++#elif __BYTE_ORDER == __BIG_ENDIAN + unsigned bp_reg:4; + unsigned sp_reg:4; + unsigned unused:5; +--- a/tools/arch/x86/lib/insn.c ++++ b/tools/arch/x86/lib/insn.c +@@ -15,7 +15,11 @@ + #include "../include/asm/insn.h" /* __ignore_sync_check__ */ + #include "../include/asm-generic/unaligned.h" /* __ignore_sync_check__ */ + ++#ifdef __KERNEL__ + #include ++#else ++#include ++#endif + #include + + #include "../include/asm/emulate_prefix.h" /* __ignore_sync_check__ */ +--- a/tools/include/asm-generic/bitops/fls.h ++++ b/tools/include/asm-generic/bitops/fls.h +@@ -2,6 +2,8 @@ + #ifndef _ASM_GENERIC_BITOPS_FLS_H_ + #define _ASM_GENERIC_BITOPS_FLS_H_ + ++#include ++ + /** + * fls - find last (most-significant) bit set + * @x: the word to search +@@ -10,7 +12,7 @@ + * Note fls(0) = 0, fls(1) = 1, fls(0x80000000) = 32. + */ + +-static __always_inline int fls(unsigned int x) ++static __always_inline int __generic_fls(unsigned int x) + { + int r = 32; + +@@ -38,5 +40,6 @@ static __always_inline int fls(unsigned + } + return r; + } ++#define fls __generic_fls + + #endif /* _ASM_GENERIC_BITOPS_FLS_H_ */ +--- a/tools/include/asm-generic/bitsperlong.h ++++ b/tools/include/asm-generic/bitsperlong.h +@@ -4,11 +4,13 @@ + + #include + ++#ifndef BITS_PER_LONG + #ifdef __SIZEOF_LONG__ + #define BITS_PER_LONG (__CHAR_BIT__ * __SIZEOF_LONG__) + #else + #define BITS_PER_LONG __WORDSIZE + #endif ++#endif + + #if BITS_PER_LONG != __BITS_PER_LONG + #error Inconsistent word size. Check asm/bitsperlong.h +--- a/tools/include/linux/rbtree.h ++++ b/tools/include/linux/rbtree.h +@@ -18,7 +18,6 @@ + #define __TOOLS_LINUX_PERF_RBTREE_H + + #include +-#include + + struct rb_node { + unsigned long __rb_parent_color; +--- a/tools/objtool/Makefile ++++ b/tools/objtool/Makefile +@@ -4,7 +4,7 @@ include ../scripts/Makefile.arch + + # always use the host compiler + AR = $(HOSTAR) +-CC = $(HOSTCC) ++CC = $(HOSTCC) $(HOST_EXTRACFLAGS) + LD = $(HOSTLD) + + ifeq ($(srctree),) +@@ -43,6 +43,7 @@ BUILD_ORC := n + + ifeq ($(SRCARCH),x86) + BUILD_ORC := y ++ CFLAGS += -DBUILD_ORC + endif + + export BUILD_ORC +--- a/tools/objtool/check.c ++++ b/tools/objtool/check.c +@@ -1164,11 +1164,12 @@ static int add_ignore_alternatives(struc + return 0; + } + ++#ifndef BUILD_ORC + /* + * Symbols that replace INSN_CALL_DYNAMIC, every (tail) call to such a symbol + * will be added to the .retpoline_sites section. + */ +-__weak bool arch_is_retpoline(struct symbol *sym) ++bool arch_is_retpoline(struct symbol *sym) + { + return false; + } +@@ -1177,7 +1178,7 @@ __weak bool arch_is_retpoline(struct sym + * Symbols that replace INSN_RETURN, every (tail) call to such a symbol + * will be added to the .return_sites section. + */ +-__weak bool arch_is_rethunk(struct symbol *sym) ++bool arch_is_rethunk(struct symbol *sym) + { + return false; + } +@@ -1186,10 +1187,11 @@ __weak bool arch_is_rethunk(struct symbo + * Symbols that are embedded inside other instructions, because sometimes crazy + * code exists. These are mostly ignored for validation purposes. + */ +-__weak bool arch_is_embedded_insn(struct symbol *sym) ++bool arch_is_embedded_insn(struct symbol *sym) + { + return false; + } ++#endif + + #define NEGATIVE_RELOC ((void *)-1L) + +--- a/tools/objtool/include/objtool/objtool.h ++++ b/tools/objtool/include/objtool/objtool.h +@@ -12,7 +12,9 @@ + + #include + ++#ifndef __weak + #define __weak __attribute__((weak)) ++#endif + + struct pv_state { + bool clean; +--- a/tools/objtool/orc_dump.c ++++ b/tools/objtool/orc_dump.c +@@ -5,10 +5,10 @@ + + #include + #include +-#include + #include + #include + #include ++#include + + static const char *reg_name(unsigned int reg) + { +--- a/tools/objtool/orc_gen.c ++++ b/tools/objtool/orc_gen.c +@@ -7,11 +7,11 @@ + #include + + #include +-#include + + #include + #include + #include ++#include + + static int init_orc_entry(struct orc_entry *orc, struct cfi_state *cfi, + struct instruction *insn) +--- a/tools/objtool/special.c ++++ b/tools/objtool/special.c +@@ -54,9 +54,11 @@ struct special_entry entries[] = { + {}, + }; + +-void __weak arch_handle_alternative(unsigned short feature, struct special_alt *alt) ++#ifndef BUILD_ORC ++void arch_handle_alternative(unsigned short feature, struct special_alt *alt) + { + } ++#endif + + static void reloc_to_sec_off(struct reloc *reloc, struct section **sec, + unsigned long *off) +--- a/tools/objtool/weak.c ++++ b/tools/objtool/weak.c +@@ -15,12 +15,14 @@ + return ENOSYS; \ + }) + +-int __weak orc_dump(const char *_objname) ++#ifndef BUILD_ORC ++int orc_dump(const char *_objname) + { + UNSUPPORTED("ORC"); + } + +-int __weak orc_create(struct objtool_file *file) ++int orc_create(struct objtool_file *file) + { + UNSUPPORTED("ORC"); + } ++#endif +--- a/tools/scripts/Makefile.include ++++ b/tools/scripts/Makefile.include +@@ -92,7 +92,7 @@ LLVM_OBJCOPY ?= llvm-objcopy + LLVM_STRIP ?= llvm-strip + + ifeq ($(CC_NO_CLANG), 1) +-EXTRA_WARNINGS += -Wstrict-aliasing=3 ++# EXTRA_WARNINGS += -Wstrict-aliasing=3 + + else ifneq ($(CROSS_COMPILE),) + # Allow userspace to override CLANG_CROSS_FLAGS to specify their own +--- a/tools/lib/string.c ++++ b/tools/lib/string.c +@@ -100,6 +100,7 @@ int strtobool(const char *s, bool *res) + #pragma clang diagnostic push + #pragma clang diagnostic ignored "-Wignored-attributes" + #endif ++#ifndef __APPLE__ + size_t __weak strlcpy(char *dest, const char *src, size_t size) + { + size_t ret = strlen(src); +@@ -111,6 +112,7 @@ size_t __weak strlcpy(char *dest, const + } + return ret; + } ++#endif + #ifdef __clang__ + #pragma clang diagnostic pop + #endif diff --git a/target/linux/generic/hack-6.1/251-kconfig.patch b/target/linux/generic/hack-6.1/251-kconfig.patch index e1ecbca6f..fcf4eaf6e 100644 --- a/target/linux/generic/hack-6.1/251-kconfig.patch +++ b/target/linux/generic/hack-6.1/251-kconfig.patch @@ -124,59 +124,6 @@ Signed-off-by: John Crispin config NETFILTER_FAMILY_BRIDGE bool ---- a/net/wireless/Kconfig -+++ b/net/wireless/Kconfig -@@ -1,6 +1,6 @@ - # SPDX-License-Identifier: GPL-2.0-only - config WIRELESS_EXT -- bool -+ bool "Wireless extensions" - - config WEXT_CORE - def_bool y -@@ -12,10 +12,10 @@ config WEXT_PROC - depends on WEXT_CORE - - config WEXT_SPY -- bool -+ bool "WEXT_SPY" - - config WEXT_PRIV -- bool -+ bool "WEXT_PRIV" - - config CFG80211 - tristate "cfg80211 - wireless configuration API" -@@ -208,7 +208,7 @@ config CFG80211_WEXT_EXPORT - endif # CFG80211 - - config LIB80211 -- tristate -+ tristate "LIB80211" - default n - help - This options enables a library of common routines used -@@ -217,17 +217,17 @@ config LIB80211 - Drivers should select this themselves if needed. - - config LIB80211_CRYPT_WEP -- tristate -+ tristate "LIB80211_CRYPT_WEP" - select CRYPTO_LIB_ARC4 - - config LIB80211_CRYPT_CCMP -- tristate -+ tristate "LIB80211_CRYPT_CCMP" - select CRYPTO - select CRYPTO_AES - select CRYPTO_CCM - - config LIB80211_CRYPT_TKIP -- tristate -+ tristate "LIB80211_CRYPT_TKIP" - select CRYPTO_LIB_ARC4 - - config LIB80211_DEBUG --- a/sound/core/Kconfig +++ b/sound/core/Kconfig @@ -17,7 +17,7 @@ config SND_DMAENGINE_PCM diff --git a/target/linux/generic/hack-6.1/402-mtd-blktrans-call-add-disks-after-mtd-device.patch b/target/linux/generic/hack-6.1/402-mtd-blktrans-call-add-disks-after-mtd-device.patch index 7b8ae7680..a87b41370 100644 --- a/target/linux/generic/hack-6.1/402-mtd-blktrans-call-add-disks-after-mtd-device.patch +++ b/target/linux/generic/hack-6.1/402-mtd-blktrans-call-add-disks-after-mtd-device.patch @@ -91,7 +91,7 @@ Signed-off-by: Daniel Golle #include "mtdcore.h" -@@ -1076,6 +1077,8 @@ int mtd_device_parse_register(struct mtd +@@ -1100,6 +1101,8 @@ int mtd_device_parse_register(struct mtd register_reboot_notifier(&mtd->reboot_notifier); } diff --git a/target/linux/generic/hack-6.1/420-mtd-support-OpenWrt-s-MTD_ROOTFS_ROOT_DEV.patch b/target/linux/generic/hack-6.1/420-mtd-support-OpenWrt-s-MTD_ROOTFS_ROOT_DEV.patch index 8e8e5cea8..7c7e4c814 100644 --- a/target/linux/generic/hack-6.1/420-mtd-support-OpenWrt-s-MTD_ROOTFS_ROOT_DEV.patch +++ b/target/linux/generic/hack-6.1/420-mtd-support-OpenWrt-s-MTD_ROOTFS_ROOT_DEV.patch @@ -12,7 +12,7 @@ Signed-off-by: RafaÅ‚ MiÅ‚ecki --- a/drivers/mtd/mtdcore.c +++ b/drivers/mtd/mtdcore.c -@@ -750,7 +750,8 @@ int add_mtd_device(struct mtd_info *mtd) +@@ -773,7 +773,8 @@ int add_mtd_device(struct mtd_info *mtd) mutex_unlock(&mtd_table_mutex); diff --git a/target/linux/generic/hack-6.1/700-swconfig_switch_drivers.patch b/target/linux/generic/hack-6.1/700-swconfig_switch_drivers.patch index 5f49678a3..673082d84 100644 --- a/target/linux/generic/hack-6.1/700-swconfig_switch_drivers.patch +++ b/target/linux/generic/hack-6.1/700-swconfig_switch_drivers.patch @@ -12,7 +12,7 @@ Signed-off-by: Felix Fietkau --- a/drivers/net/phy/Kconfig +++ b/drivers/net/phy/Kconfig -@@ -69,6 +69,80 @@ config SFP +@@ -67,6 +67,80 @@ config SFP depends on HWMON || HWMON=n select MDIO_I2C @@ -92,7 +92,7 @@ Signed-off-by: Felix Fietkau + comment "MII PHY device drivers" - config AMD_PHY + config AIR_EN8811H_PHY --- a/drivers/net/phy/Makefile +++ b/drivers/net/phy/Makefile @@ -24,6 +24,21 @@ libphy-$(CONFIG_LED_TRIGGER_PHY) += phy_ diff --git a/target/linux/generic/hack-6.1/711-net-dsa-mv88e6xxx-disable-ATU-violation.patch b/target/linux/generic/hack-6.1/711-net-dsa-mv88e6xxx-disable-ATU-violation.patch index 696009d39..75907bf47 100644 --- a/target/linux/generic/hack-6.1/711-net-dsa-mv88e6xxx-disable-ATU-violation.patch +++ b/target/linux/generic/hack-6.1/711-net-dsa-mv88e6xxx-disable-ATU-violation.patch @@ -9,7 +9,7 @@ Subject: [PATCH] net/dsa/mv88e6xxx: disable ATU violation --- a/drivers/net/dsa/mv88e6xxx/chip.c +++ b/drivers/net/dsa/mv88e6xxx/chip.c -@@ -3492,6 +3492,9 @@ static int mv88e6xxx_setup_port(struct m +@@ -3510,6 +3510,9 @@ static int mv88e6xxx_setup_port(struct m else reg = 1 << port; diff --git a/target/linux/generic/hack-6.1/720-net-phy-add-aqr-phys.patch b/target/linux/generic/hack-6.1/720-net-phy-add-aqr-phys.patch deleted file mode 100644 index fd2687d8b..000000000 --- a/target/linux/generic/hack-6.1/720-net-phy-add-aqr-phys.patch +++ /dev/null @@ -1,120 +0,0 @@ -From: Birger Koblitz -Date: Sun, 5 Sep 2021 15:13:10 +0200 -Subject: [PATCH] kernel: Add AQR113C and AQR813 support - -This hack adds support for the Aquantia 4th generation, 10GBit -PHYs AQR113C and AQR813. - -Signed-off-by: Birger Koblitz - ---- a/drivers/net/phy/aquantia_main.c -+++ b/drivers/net/phy/aquantia_main.c -@@ -23,6 +23,7 @@ - #define PHY_ID_AQCS109 0x03a1b5c2 - #define PHY_ID_AQR405 0x03a1b4b0 - #define PHY_ID_AQR113C 0x31c31c12 -+#define PHY_ID_AQR813 0x31c31cb2 - - #define MDIO_PHYXS_VEND_IF_STATUS 0xe812 - #define MDIO_PHYXS_VEND_IF_STATUS_TYPE_MASK GENMASK(7, 3) -@@ -415,6 +416,49 @@ static int aqr107_read_rate(struct phy_d - return 0; - } - -+static int aqr113c_read_status(struct phy_device *phydev) -+{ -+ int val, ret; -+ -+ ret = aqr_read_status(phydev); -+ if (ret) -+ return ret; -+ -+ if (!phydev->link || phydev->autoneg == AUTONEG_DISABLE) -+ return 0; -+ -+ // On AQR113C, the speed returned by aqr_read_status is wrong -+ aqr107_read_rate(phydev); -+ -+ val = phy_read_mmd(phydev, MDIO_MMD_PHYXS, MDIO_PHYXS_VEND_IF_STATUS); -+ if (val < 0) -+ return val; -+ -+ switch (FIELD_GET(MDIO_PHYXS_VEND_IF_STATUS_TYPE_MASK, val)) { -+ case MDIO_PHYXS_VEND_IF_STATUS_TYPE_KR: -+ phydev->interface = PHY_INTERFACE_MODE_10GKR; -+ break; -+ case MDIO_PHYXS_VEND_IF_STATUS_TYPE_XFI: -+ phydev->interface = PHY_INTERFACE_MODE_10GBASER; -+ break; -+ case MDIO_PHYXS_VEND_IF_STATUS_TYPE_USXGMII: -+ phydev->interface = PHY_INTERFACE_MODE_USXGMII; -+ break; -+ case MDIO_PHYXS_VEND_IF_STATUS_TYPE_SGMII: -+ phydev->interface = PHY_INTERFACE_MODE_SGMII; -+ break; -+ case MDIO_PHYXS_VEND_IF_STATUS_TYPE_OCSGMII: -+ phydev->interface = PHY_INTERFACE_MODE_2500BASEX; -+ break; -+ default: -+ phydev->interface = PHY_INTERFACE_MODE_NA; -+ break; -+ } -+ -+ /* Read downshifted rate from vendor register */ -+ return aqr107_read_rate(phydev); -+} -+ - static int aqr107_read_status(struct phy_device *phydev) - { - int val, ret; -@@ -554,7 +598,7 @@ static void aqr107_chip_info(struct phy_ - build_id = FIELD_GET(VEND1_GLOBAL_RSVD_STAT1_FW_BUILD_ID, val); - prov_id = FIELD_GET(VEND1_GLOBAL_RSVD_STAT1_PROV_ID, val); - -- phydev_dbg(phydev, "FW %u.%u, Build %u, Provisioning %u\n", -+ phydev_info(phydev, "FW %u.%u, Build %u, Provisioning %u\n", - fw_major, fw_minor, build_id, prov_id); - } - -@@ -809,7 +853,7 @@ static struct phy_driver aqr_driver[] = - .config_aneg = aqr_config_aneg, - .config_intr = aqr_config_intr, - .handle_interrupt = aqr_handle_interrupt, -- .read_status = aqr107_read_status, -+ .read_status = aqr113c_read_status, - .get_tunable = aqr107_get_tunable, - .set_tunable = aqr107_set_tunable, - .suspend = aqr107_suspend, -@@ -819,6 +863,24 @@ static struct phy_driver aqr_driver[] = - .get_stats = aqr107_get_stats, - .link_change_notify = aqr107_link_change_notify, - }, -+{ -+ PHY_ID_MATCH_MODEL(PHY_ID_AQR813), -+ .name = "Aquantia AQR813", -+ .probe = aqr107_probe, -+ .config_init = aqr107_config_init, -+ .config_aneg = aqr_config_aneg, -+ .config_intr = aqr_config_intr, -+ .handle_interrupt = aqr_handle_interrupt, -+ .read_status = aqr113c_read_status, -+ .get_tunable = aqr107_get_tunable, -+ .set_tunable = aqr107_set_tunable, -+ .suspend = aqr107_suspend, -+ .resume = aqr107_resume, -+ .get_sset_count = aqr107_get_sset_count, -+ .get_strings = aqr107_get_strings, -+ .get_stats = aqr107_get_stats, -+ .link_change_notify = aqr107_link_change_notify, -+}, - }; - - module_phy_driver(aqr_driver); -@@ -832,6 +894,7 @@ static struct mdio_device_id __maybe_unu - { PHY_ID_MATCH_MODEL(PHY_ID_AQCS109) }, - { PHY_ID_MATCH_MODEL(PHY_ID_AQR405) }, - { PHY_ID_MATCH_MODEL(PHY_ID_AQR113C) }, -+ { PHY_ID_MATCH_MODEL(PHY_ID_AQR813) }, - { } - }; - diff --git a/target/linux/generic/hack-6.1/721-net-add-packet-mangeling.patch b/target/linux/generic/hack-6.1/721-net-add-packet-mangeling.patch index 435f390ea..c6306c85c 100644 --- a/target/linux/generic/hack-6.1/721-net-add-packet-mangeling.patch +++ b/target/linux/generic/hack-6.1/721-net-add-packet-mangeling.patch @@ -19,23 +19,23 @@ Signed-off-by: Felix Fietkau --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h -@@ -1707,6 +1707,7 @@ enum netdev_priv_flags { - IFF_LIVE_RENAME_OK = 1<<30, +@@ -1730,6 +1730,7 @@ enum netdev_priv_flags { + /* was IFF_LIVE_RENAME_OK */ IFF_TX_SKB_NO_LINEAR = BIT_ULL(31), IFF_CHANGE_PROTO_DOWN = BIT_ULL(32), + IFF_NO_IP_ALIGN = BIT_ULL(33), }; #define IFF_802_1Q_VLAN IFF_802_1Q_VLAN -@@ -1741,6 +1742,7 @@ enum netdev_priv_flags { +@@ -1763,6 +1764,7 @@ enum netdev_priv_flags { + #define IFF_FAILOVER_SLAVE IFF_FAILOVER_SLAVE #define IFF_L3MDEV_RX_HANDLER IFF_L3MDEV_RX_HANDLER - #define IFF_LIVE_RENAME_OK IFF_LIVE_RENAME_OK #define IFF_TX_SKB_NO_LINEAR IFF_TX_SKB_NO_LINEAR +#define IFF_NO_IP_ALIGN IFF_NO_IP_ALIGN /* Specifies the type of the struct net_device::ml_priv pointer */ enum netdev_ml_priv_type { -@@ -2109,6 +2111,11 @@ struct net_device { +@@ -2131,6 +2133,11 @@ struct net_device { const struct tlsdev_ops *tlsdev_ops; #endif @@ -47,7 +47,7 @@ Signed-off-by: Felix Fietkau const struct header_ops *header_ops; unsigned char operstate; -@@ -2182,6 +2189,10 @@ struct net_device { +@@ -2204,6 +2211,10 @@ struct net_device { struct mctp_dev __rcu *mctp_ptr; #endif @@ -105,7 +105,7 @@ Signed-off-by: Felix Fietkau help --- a/net/core/dev.c +++ b/net/core/dev.c -@@ -3622,6 +3622,11 @@ static int xmit_one(struct sk_buff *skb, +@@ -3607,6 +3607,11 @@ static int xmit_one(struct sk_buff *skb, if (dev_nit_active(dev)) dev_queue_xmit_nit(skb, dev); diff --git a/target/linux/generic/hack-6.1/722-net-phy-aquantia-enable-AQR112-and-AQR412.patch b/target/linux/generic/hack-6.1/722-net-phy-aquantia-enable-AQR112-and-AQR412.patch index 77b190464..faaa40f9b 100644 --- a/target/linux/generic/hack-6.1/722-net-phy-aquantia-enable-AQR112-and-AQR412.patch +++ b/target/linux/generic/hack-6.1/722-net-phy-aquantia-enable-AQR112-and-AQR412.patch @@ -10,13 +10,13 @@ different firmware on the PHY. Signed-off-by: Alex Marginean --- - drivers/net/phy/aquantia_main.c | 88 +++++++++++++++++++++++++++++++++++++++++ + drivers/net/phy/aquantia/aquantia_main.c | 88 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 88 insertions(+) ---- a/drivers/net/phy/aquantia_main.c -+++ b/drivers/net/phy/aquantia_main.c -@@ -24,6 +24,8 @@ - #define PHY_ID_AQR405 0x03a1b4b0 +--- a/drivers/net/phy/aquantia/aquantia_main.c ++++ b/drivers/net/phy/aquantia/aquantia_main.c +@@ -27,6 +27,8 @@ + #define PHY_ID_AQR113 0x31c31c40 #define PHY_ID_AQR113C 0x31c31c12 #define PHY_ID_AQR813 0x31c31cb2 +#define PHY_ID_AQR112 0x03a1b662 @@ -24,7 +24,7 @@ Signed-off-by: Alex Marginean #define MDIO_PHYXS_VEND_IF_STATUS 0xe812 #define MDIO_PHYXS_VEND_IF_STATUS_TYPE_MASK GENMASK(7, 3) -@@ -151,6 +153,29 @@ +@@ -99,6 +101,29 @@ #define AQR107_OP_IN_PROG_SLEEP 1000 #define AQR107_OP_IN_PROG_TIMEOUT 100000 @@ -54,7 +54,7 @@ Signed-off-by: Alex Marginean struct aqr107_hw_stat { const char *name; int reg; -@@ -282,6 +307,51 @@ static int aqr_config_aneg(struct phy_de +@@ -230,6 +255,51 @@ static int aqr_config_aneg(struct phy_de return genphy_c45_check_and_restart_aneg(phydev, changed); } @@ -106,7 +106,7 @@ Signed-off-by: Alex Marginean static int aqr_config_intr(struct phy_device *phydev) { bool en = phydev->interrupts == PHY_INTERRUPT_ENABLED; -@@ -881,6 +951,30 @@ static struct phy_driver aqr_driver[] = +@@ -860,6 +930,30 @@ static struct phy_driver aqr_driver[] = .get_stats = aqr107_get_stats, .link_change_notify = aqr107_link_change_notify, }, @@ -137,8 +137,8 @@ Signed-off-by: Alex Marginean }; module_phy_driver(aqr_driver); -@@ -895,6 +989,8 @@ static struct mdio_device_id __maybe_unu - { PHY_ID_MATCH_MODEL(PHY_ID_AQR405) }, +@@ -877,6 +971,8 @@ static struct mdio_device_id __maybe_unu + { PHY_ID_MATCH_MODEL(PHY_ID_AQR113) }, { PHY_ID_MATCH_MODEL(PHY_ID_AQR113C) }, { PHY_ID_MATCH_MODEL(PHY_ID_AQR813) }, + { PHY_ID_MATCH_MODEL(PHY_ID_AQR112) }, diff --git a/target/linux/generic/hack-6.1/723-net-phy-aquantia-fix-system-side-protocol-mi.patch b/target/linux/generic/hack-6.1/723-net-phy-aquantia-fix-system-side-protocol-mi.patch index 8e204cb14..72a70ebc1 100644 --- a/target/linux/generic/hack-6.1/723-net-phy-aquantia-fix-system-side-protocol-mi.patch +++ b/target/linux/generic/hack-6.1/723-net-phy-aquantia-fix-system-side-protocol-mi.patch @@ -9,12 +9,12 @@ these protocols leads to link issues on system side. Signed-off-by: Alex Marginean --- - drivers/net/phy/aquantia_main.c | 8 +++++++- + drivers/net/phy/aquantia/aquantia_main.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) ---- a/drivers/net/phy/aquantia_main.c -+++ b/drivers/net/phy/aquantia_main.c -@@ -340,10 +340,16 @@ static int aqr_config_aneg_set_prot(stru +--- a/drivers/net/phy/aquantia/aquantia_main.c ++++ b/drivers/net/phy/aquantia/aquantia_main.c +@@ -288,10 +288,16 @@ static int aqr_config_aneg_set_prot(stru phy_write_mmd(phydev, MDIO_MMD_VEND1, AQUANTIA_VND1_GSTART_RATE, aquantia_syscfg[if_type].start_rate); diff --git a/target/linux/generic/hack-6.1/724-net-phy-aquantia-Add-AQR113-driver-support.patch b/target/linux/generic/hack-6.1/724-net-phy-aquantia-Add-AQR113-driver-support.patch deleted file mode 100644 index 0a1e97062..000000000 --- a/target/linux/generic/hack-6.1/724-net-phy-aquantia-Add-AQR113-driver-support.patch +++ /dev/null @@ -1,43 +0,0 @@ -From 2e677e4ae8f8330f68013163b060d0fda3a43095 Mon Sep 17 00:00:00 2001 -From: "Langer, Thomas" -Date: Fri, 9 Jul 2021 17:36:46 +0200 -Subject: [PATCH] PONRTSYS-8842: aquantia: Add AQR113 driver support - -Add a new entry for AQR113 PHY_ID ---- - drivers/net/phy/aquantia_main.c | 10 ++++++++++ - 1 file changed, 10 insertions(+) - ---- a/drivers/net/phy/aquantia_main.c -+++ b/drivers/net/phy/aquantia_main.c -@@ -26,6 +26,7 @@ - #define PHY_ID_AQR813 0x31c31cb2 - #define PHY_ID_AQR112 0x03a1b662 - #define PHY_ID_AQR412 0x03a1b712 -+#define PHY_ID_AQR113 0x31c31c40 - - #define MDIO_PHYXS_VEND_IF_STATUS 0xe812 - #define MDIO_PHYXS_VEND_IF_STATUS_TYPE_MASK GENMASK(7, 3) -@@ -981,6 +982,14 @@ static struct phy_driver aqr_driver[] = - .get_strings = aqr107_get_strings, - .get_stats = aqr107_get_stats, - }, -+{ -+ PHY_ID_MATCH_MODEL(PHY_ID_AQR113), -+ .name = "Aquantia AQR113", -+ .config_aneg = aqr_config_aneg, -+ .config_intr = aqr_config_intr, -+ .handle_interrupt = aqr_handle_interrupt, -+ .read_status = aqr107_read_status, -+}, - }; - - module_phy_driver(aqr_driver); -@@ -997,6 +1006,7 @@ static struct mdio_device_id __maybe_unu - { PHY_ID_MATCH_MODEL(PHY_ID_AQR813) }, - { PHY_ID_MATCH_MODEL(PHY_ID_AQR112) }, - { PHY_ID_MATCH_MODEL(PHY_ID_AQR412) }, -+ { PHY_ID_MATCH_MODEL(PHY_ID_AQR113) }, - { } - }; - diff --git a/target/linux/generic/hack-6.1/725-net-phy-aquantia-add-PHY_IDs-for-AQR112-variants.patch b/target/linux/generic/hack-6.1/725-net-phy-aquantia-add-PHY_IDs-for-AQR112-variants.patch index 654fea566..d83cc48a3 100644 --- a/target/linux/generic/hack-6.1/725-net-phy-aquantia-add-PHY_IDs-for-AQR112-variants.patch +++ b/target/linux/generic/hack-6.1/725-net-phy-aquantia-add-PHY_IDs-for-AQR112-variants.patch @@ -7,23 +7,23 @@ As advised by Ian Chang this PHY is used in Puzzle devices. Signed-off-by: Daniel Golle --- - drivers/net/phy/aquantia_main.c | 10 ++++++++++ + drivers/net/phy/aquantia/aquantia_main.c | 10 ++++++++++ 1 file changed, 10 insertions(+) ---- a/drivers/net/phy/aquantia_main.c -+++ b/drivers/net/phy/aquantia_main.c -@@ -27,6 +27,8 @@ +--- a/drivers/net/phy/aquantia/aquantia_main.c ++++ b/drivers/net/phy/aquantia/aquantia_main.c +@@ -29,6 +29,8 @@ + #define PHY_ID_AQR813 0x31c31cb2 #define PHY_ID_AQR112 0x03a1b662 #define PHY_ID_AQR412 0x03a1b712 - #define PHY_ID_AQR113 0x31c31c40 +#define PHY_ID_AQR112C 0x03a1b790 +#define PHY_ID_AQR112R 0x31c31d12 #define MDIO_PHYXS_VEND_IF_STATUS 0xe812 #define MDIO_PHYXS_VEND_IF_STATUS_TYPE_MASK GENMASK(7, 3) -@@ -990,6 +992,30 @@ static struct phy_driver aqr_driver[] = - .handle_interrupt = aqr_handle_interrupt, - .read_status = aqr107_read_status, +@@ -960,6 +962,30 @@ static struct phy_driver aqr_driver[] = + .get_strings = aqr107_get_strings, + .get_stats = aqr107_get_stats, }, +{ + PHY_ID_MATCH_MODEL(PHY_ID_AQR112C), @@ -52,10 +52,10 @@ Signed-off-by: Daniel Golle }; module_phy_driver(aqr_driver); -@@ -1007,6 +1033,8 @@ static struct mdio_device_id __maybe_unu +@@ -979,6 +1005,8 @@ static struct mdio_device_id __maybe_unu + { PHY_ID_MATCH_MODEL(PHY_ID_AQR813) }, { PHY_ID_MATCH_MODEL(PHY_ID_AQR112) }, { PHY_ID_MATCH_MODEL(PHY_ID_AQR412) }, - { PHY_ID_MATCH_MODEL(PHY_ID_AQR113) }, + { PHY_ID_MATCH_MODEL(PHY_ID_AQR112C) }, + { PHY_ID_MATCH_MODEL(PHY_ID_AQR112R) }, { } diff --git a/target/linux/generic/hack-6.1/750-net-pcs-mtk-lynxi-workaround-2500BaseX-no-an.patch b/target/linux/generic/hack-6.1/750-net-pcs-mtk-lynxi-workaround-2500BaseX-no-an.patch index 8b7f2f095..a2bd3a3db 100644 --- a/target/linux/generic/hack-6.1/750-net-pcs-mtk-lynxi-workaround-2500BaseX-no-an.patch +++ b/target/linux/generic/hack-6.1/750-net-pcs-mtk-lynxi-workaround-2500BaseX-no-an.patch @@ -13,7 +13,7 @@ Signed-off-by: Daniel Golle --- a/drivers/net/pcs/pcs-mtk-lynxi.c +++ b/drivers/net/pcs/pcs-mtk-lynxi.c -@@ -92,14 +92,23 @@ static void mtk_pcs_lynxi_get_state(stru +@@ -114,14 +114,23 @@ static void mtk_pcs_lynxi_get_state(stru struct phylink_link_state *state) { struct mtk_pcs_lynxi *mpcs = pcs_to_mtk_pcs_lynxi(pcs); @@ -40,14 +40,14 @@ Signed-off-by: Daniel Golle + phylink_mii_c22_pcs_decode_state(state, bmsr, FIELD_GET(SGMII_LPA, adv)); } - static int mtk_pcs_lynxi_config(struct phylink_pcs *pcs, unsigned int mode, -@@ -134,7 +143,8 @@ static int mtk_pcs_lynxi_config(struct p - /* 1000base-X or 2500base-X autoneg */ - sgm_mode = SGMII_REMOTE_FAULT_DIS; - use_an = linkmode_test_bit(ETHTOOL_LINK_MODE_Autoneg_BIT, -- advertising); -+ advertising) && -+ !(interface == PHY_INTERFACE_MODE_2500BASEX); - } else { - /* 1000base-X or 2500base-X without autoneg */ - sgm_mode = 0; + static void mtk_sgmii_reset(struct mtk_pcs_lynxi *mpcs) +@@ -163,7 +172,8 @@ static int mtk_pcs_lynxi_config(struct p + if (neg_mode & PHYLINK_PCS_NEG_INBAND) + sgm_mode |= SGMII_REMOTE_FAULT_DIS; + +- if (neg_mode == PHYLINK_PCS_NEG_INBAND_ENABLED) { ++ if (neg_mode == PHYLINK_PCS_NEG_INBAND_ENABLED && ++ interface != PHY_INTERFACE_MODE_2500BASEX) { + if (interface == PHY_INTERFACE_MODE_SGMII) + sgm_mode |= SGMII_SPEED_DUPLEX_AN; + bmcr = BMCR_ANENABLE; diff --git a/target/linux/generic/hack-6.1/765-mxl-gpy-control-LED-reg-from-DT.patch b/target/linux/generic/hack-6.1/765-mxl-gpy-control-LED-reg-from-DT.patch index d7165cb30..2724efa15 100644 --- a/target/linux/generic/hack-6.1/765-mxl-gpy-control-LED-reg-from-DT.patch +++ b/target/linux/generic/hack-6.1/765-mxl-gpy-control-LED-reg-from-DT.patch @@ -55,7 +55,7 @@ Signed-off-by: David Bauer /* SGMII */ #define VSPEC1_SGMII_CTRL 0x08 #define VSPEC1_SGMII_CTRL_ANEN BIT(12) /* Aneg enable */ -@@ -241,6 +248,31 @@ out: +@@ -241,6 +248,35 @@ out: return ret; } @@ -64,6 +64,7 @@ Signed-off-by: David Bauer + struct device_node *node = phydev->mdio.dev.of_node; + u32 led_regs[PHY_LED_NUM_LEDS]; + int i, ret; ++ u16 val = 0xff00; + + if (!IS_ENABLED(CONFIG_OF_MDIO)) + return 0; @@ -71,8 +72,11 @@ Signed-off-by: David Bauer + if (of_property_read_u32_array(node, "mxl,led-config", led_regs, PHY_LED_NUM_LEDS)) + return 0; + ++ if (of_property_read_bool(node, "mxl,led-drive-vdd")) ++ val &= 0x0fff; ++ + /* Enable LED function handling on all ports*/ -+ phy_write(phydev, PHY_LED, 0xFF00); ++ phy_write(phydev, PHY_LED, val); + + /* Write LED register values */ + for (i = 0; i < PHY_LED_NUM_LEDS; i++) { @@ -87,7 +91,7 @@ Signed-off-by: David Bauer static int gpy_config_init(struct phy_device *phydev) { int ret; -@@ -252,7 +284,10 @@ static int gpy_config_init(struct phy_de +@@ -252,7 +288,10 @@ static int gpy_config_init(struct phy_de /* Clear all pending interrupts */ ret = phy_read(phydev, PHY_ISTAT); diff --git a/target/linux/generic/hack-6.1/781-usb-net-rndis-support-asr.patch b/target/linux/generic/hack-6.1/781-usb-net-rndis-support-asr.patch index 9934bb807..47339b6c2 100644 --- a/target/linux/generic/hack-6.1/781-usb-net-rndis-support-asr.patch +++ b/target/linux/generic/hack-6.1/781-usb-net-rndis-support-asr.patch @@ -1,3 +1,16 @@ +From 9fabf60187f1fa19e6f6bb5441587d485bd534b0 Mon Sep 17 00:00:00 2001 +From: Daniel Golle +Date: Tue, 9 Apr 2024 17:06:38 +0100 +Subject: [PATCH] rndis_host: add a bunch of USB IDs + +Add a bunch of USB IDs found in various places online to the +RNDIS USB network driver. + +Signed-off-by: Daniel Golle +--- + drivers/net/usb/rndis_host.c | 40 ++++++++++++++++++++++ + 1 file changed, 40 insertions(+) + --- a/drivers/net/usb/rndis_host.c +++ b/drivers/net/usb/rndis_host.c @@ -630,6 +630,16 @@ static const struct driver_info zte_rndi diff --git a/target/linux/generic/hack-6.1/790-SFP-GE-T-ignore-TX_FAULT.patch b/target/linux/generic/hack-6.1/790-SFP-GE-T-ignore-TX_FAULT.patch index 16d5c12dd..53f199d09 100644 --- a/target/linux/generic/hack-6.1/790-SFP-GE-T-ignore-TX_FAULT.patch +++ b/target/linux/generic/hack-6.1/790-SFP-GE-T-ignore-TX_FAULT.patch @@ -46,7 +46,7 @@ Signed-off-by: Daniel Golle } else if (event == SFP_E_TIMEOUT || event == SFP_E_TX_CLEAR) { init_done: /* Create mdiobus and start trying for PHY */ -@@ -2572,10 +2576,12 @@ static void sfp_check_state(struct sfp * +@@ -2577,10 +2581,12 @@ static void sfp_check_state(struct sfp * mutex_lock(&sfp->st_mutex); state = sfp_get_state(sfp); changed = state ^ sfp->state; diff --git a/target/linux/generic/hack-6.1/800-GPIO-add-named-gpio-exports.patch b/target/linux/generic/hack-6.1/800-GPIO-add-named-gpio-exports.patch index a892195e6..b60b428dc 100644 --- a/target/linux/generic/hack-6.1/800-GPIO-add-named-gpio-exports.patch +++ b/target/linux/generic/hack-6.1/800-GPIO-add-named-gpio-exports.patch @@ -15,7 +15,7 @@ Signed-off-by: John Crispin #include "gpiolib.h" #include "gpiolib-of.h" -@@ -1030,3 +1032,72 @@ void of_gpio_dev_init(struct gpio_chip * +@@ -1030,3 +1032,100 @@ void of_gpio_dev_init(struct gpio_chip * else gc->of_node = gdev->dev.of_node; } @@ -47,6 +47,7 @@ Signed-off-by: John Crispin + max_gpio = of_gpio_count(cnp); + + for (i = 0; i < max_gpio; i++) { ++ struct gpio_desc *desc; + unsigned flags = 0; + enum of_gpio_flags of_flags; + @@ -54,17 +55,44 @@ Signed-off-by: John Crispin + if (!gpio_is_valid(gpio)) + return gpio; + -+ if (of_flags == OF_GPIO_ACTIVE_LOW) ++ if (of_flags & OF_GPIO_ACTIVE_LOW) + flags |= GPIOF_ACTIVE_LOW; + -+ if (!of_property_read_u32(cnp, "gpio-export,output", &val)) -+ flags |= val ? GPIOF_OUT_INIT_HIGH : GPIOF_OUT_INIT_LOW; -+ else ++ if (!of_property_read_u32(cnp, "gpio-export,output", &val)) { ++ if (of_flags & OF_GPIO_SINGLE_ENDED) { ++ /* ++ * As gpiod_direction_output_raw() is used, we ++ * need to emulate open drain or open source here. ++ */ ++ if (of_flags & OF_GPIO_OPEN_DRAIN) { ++ flags |= GPIOF_OPEN_DRAIN; ++ flags |= val ? GPIOF_IN : GPIOF_OUT_INIT_LOW; ++ } else { ++ flags |= GPIOF_OPEN_SOURCE; ++ flags |= val ? GPIOF_OUT_INIT_HIGH : GPIOF_IN; ++ } ++ } else { ++ flags |= val ? GPIOF_OUT_INIT_HIGH : GPIOF_OUT_INIT_LOW; ++ } ++ } else { + flags |= GPIOF_IN; ++ } + + if (devm_gpio_request_one(&pdev->dev, gpio, flags, name ? name : of_node_full_name(np))) + continue; + ++ /* ++ * When emulating open-source or open-drain functionalities by not ++ * actively driving the line (setting mode to input) we still need to ++ * set the IS_OUT flag or otherwise we won't be able to set the line ++ * value anymore. ++ */ ++ if ((flags & GPIOF_IN) && ++ ((flags & GPIOF_OPEN_DRAIN) || (flags & GPIOF_OPEN_SOURCE))) { ++ desc = gpio_to_desc(gpio); ++ set_bit(FLAG_IS_OUT, &desc->flags); ++ } ++ + dmc = of_property_read_bool(cnp, "gpio-export,direction_may_change"); + gpio_export_with_name(gpio, dmc, name); + nb++; diff --git a/target/linux/generic/hack-6.1/901-debloat_sock_diag.patch b/target/linux/generic/hack-6.1/901-debloat_sock_diag.patch index c1a36b613..09b59478a 100644 --- a/target/linux/generic/hack-6.1/901-debloat_sock_diag.patch +++ b/target/linux/generic/hack-6.1/901-debloat_sock_diag.patch @@ -162,3 +162,13 @@ Signed-off-by: Felix Fietkau default n help Support for UNIX socket monitoring interface used by the ss tool. +--- a/net/xdp/Kconfig ++++ b/net/xdp/Kconfig +@@ -10,6 +10,7 @@ config XDP_SOCKETS + config XDP_SOCKETS_DIAG + tristate "XDP sockets: monitoring interface" + depends on XDP_SOCKETS ++ select SOCK_DIAG + default n + help + Support for PF_XDP sockets monitoring interface used by the ss tool. diff --git a/target/linux/generic/hack-6.1/902-debloat_proc.patch b/target/linux/generic/hack-6.1/902-debloat_proc.patch index eaf524dc5..06b3a04f7 100644 --- a/target/linux/generic/hack-6.1/902-debloat_proc.patch +++ b/target/linux/generic/hack-6.1/902-debloat_proc.patch @@ -406,3 +406,14 @@ Signed-off-by: Felix Fietkau return register_pernet_subsys(&ip_rt_proc_ops); } +--- a/net/ipv4/inet_timewait_sock.c ++++ b/net/ipv4/inet_timewait_sock.c +@@ -269,7 +269,7 @@ void __inet_twsk_schedule(struct inet_ti + */ + + if (!rearm) { +- bool kill = timeo <= 4*HZ; ++ bool __maybe_unused kill = timeo <= 4*HZ; + + __NET_INC_STATS(twsk_net(tw), kill ? LINUX_MIB_TIMEWAITKILLED : + LINUX_MIB_TIMEWAITED); diff --git a/target/linux/generic/hack-6.1/930-usb-net-for-fm350.patch b/target/linux/generic/hack-6.1/931-usb-net-for-fm350.patch similarity index 100% rename from target/linux/generic/hack-6.1/930-usb-net-for-fm350.patch rename to target/linux/generic/hack-6.1/931-usb-net-for-fm350.patch diff --git a/target/linux/generic/pending-6.1/151-net-bridge-do-not-send-arp-replies-if-src-and-target.patch b/target/linux/generic/pending-6.1/151-net-bridge-do-not-send-arp-replies-if-src-and-target.patch new file mode 100644 index 000000000..f420d210c --- /dev/null +++ b/target/linux/generic/pending-6.1/151-net-bridge-do-not-send-arp-replies-if-src-and-target.patch @@ -0,0 +1,37 @@ +From: Felix Fietkau +Date: Thu, 4 Jan 2024 15:21:21 +0100 +Subject: [PATCH] net: bridge: do not send arp replies if src and target hw + addr is the same + +There are broken devices in the wild that handle duplicate IP address +detection by sending out ARP requests for the IP that they received from a +DHCP server and refuse the address if they get a reply. +When proxyarp is enabled, they would go into a loop of requesting an address +and then NAKing it again. + +Link: https://github.com/openwrt/openwrt/issues/14309 +Signed-off-by: Felix Fietkau +--- + +--- a/net/bridge/br_arp_nd_proxy.c ++++ b/net/bridge/br_arp_nd_proxy.c +@@ -204,7 +204,10 @@ void br_do_proxy_suppress_arp(struct sk_ + if ((p && (p->flags & BR_PROXYARP)) || + (f->dst && (f->dst->flags & (BR_PROXYARP_WIFI | + BR_NEIGH_SUPPRESS)))) { +- if (!vid) ++ replied = true; ++ if (!memcmp(n->ha, sha, dev->addr_len)) ++ replied = false; ++ else if (!vid) + br_arp_send(br, p, skb->dev, sip, tip, + sha, n->ha, sha, 0, 0); + else +@@ -212,7 +215,6 @@ void br_do_proxy_suppress_arp(struct sk_ + sha, n->ha, sha, + skb->vlan_proto, + skb_vlan_tag_get(skb)); +- replied = true; + } + + /* If we have replied or as long as we know the diff --git a/target/linux/generic/pending-6.1/307-mips_highmem_offset.patch b/target/linux/generic/pending-6.1/307-mips_highmem_offset.patch deleted file mode 100644 index 0529b0c5c..000000000 --- a/target/linux/generic/pending-6.1/307-mips_highmem_offset.patch +++ /dev/null @@ -1,19 +0,0 @@ -From: Felix Fietkau -Subject: kernel: adjust mips highmem offset to avoid the need for -mlong-calls on systems with >256M RAM - -Signed-off-by: Felix Fietkau ---- - arch/mips/include/asm/mach-generic/spaces.h | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - ---- a/arch/mips/include/asm/mach-generic/spaces.h -+++ b/arch/mips/include/asm/mach-generic/spaces.h -@@ -46,7 +46,7 @@ - * Memory above this physical address will be considered highmem. - */ - #ifndef HIGHMEM_START --#define HIGHMEM_START _AC(0x20000000, UL) -+#define HIGHMEM_START _AC(0x10000000, UL) - #endif - - #endif /* CONFIG_32BIT */ diff --git a/target/linux/generic/pending-6.1/350-mips-kernel-fix-detect_memory_region-function.patch b/target/linux/generic/pending-6.1/350-mips-kernel-fix-detect_memory_region-function.patch new file mode 100644 index 000000000..3bf7ae98b --- /dev/null +++ b/target/linux/generic/pending-6.1/350-mips-kernel-fix-detect_memory_region-function.patch @@ -0,0 +1,74 @@ +From: Shiji Yang +Date: Wed, 13 Mar 2024 20:28:37 +0800 +Subject: [PATCH] mips: kernel: fix detect_memory_region() function + +1. Do not use memcmp() on unallocated memory, as the new introduced + fortify dynamic object size check[1] will report unexpected result. +2. Use a fixed pattern instead of a random function pointer as the + magic value. +3. Flip magic value and double check it. +4. Enable this feature only for 32-bit CPUs. Currently, only ath79 and + ralink CPUs are using it. + +[1] 439a1bcac648 ("fortify: Use __builtin_dynamic_object_size() when available") +Signed-off-by: Shiji Yang +--- + arch/mips/include/asm/bootinfo.h | 2 ++ + arch/mips/kernel/setup.c | 17 ++++++++++++----- + 2 files changed, 14 insertions(+), 5 deletions(-) + +--- a/arch/mips/include/asm/bootinfo.h ++++ b/arch/mips/include/asm/bootinfo.h +@@ -93,7 +93,9 @@ const char *get_system_type(void); + + extern unsigned long mips_machtype; + ++#ifndef CONFIG_64BIT + extern void detect_memory_region(phys_addr_t start, phys_addr_t sz_min, phys_addr_t sz_max); ++#endif + + extern void prom_init(void); + extern void prom_free_prom_memory(void); +--- a/arch/mips/kernel/setup.c ++++ b/arch/mips/kernel/setup.c +@@ -90,21 +90,27 @@ static struct resource bss_resource = { + unsigned long __kaslr_offset __ro_after_init; + EXPORT_SYMBOL(__kaslr_offset); + +-static void *detect_magic __initdata = detect_memory_region; +- + #ifdef CONFIG_MIPS_AUTO_PFN_OFFSET + unsigned long ARCH_PFN_OFFSET; + EXPORT_SYMBOL(ARCH_PFN_OFFSET); + #endif + ++#ifndef CONFIG_64BIT ++static u32 detect_magic __initdata; ++#define MIPS_MEM_TEST_PATTERN 0xaa5555aa ++ + void __init detect_memory_region(phys_addr_t start, phys_addr_t sz_min, phys_addr_t sz_max) + { +- void *dm = &detect_magic; ++ void *dm = (void *)KSEG1ADDR(&detect_magic); + phys_addr_t size; + + for (size = sz_min; size < sz_max; size <<= 1) { +- if (!memcmp(dm, dm + size, sizeof(detect_magic))) +- break; ++ __raw_writel(MIPS_MEM_TEST_PATTERN, dm); ++ if (__raw_readl(dm) == __raw_readl(dm + size)) { ++ __raw_writel(~MIPS_MEM_TEST_PATTERN, dm); ++ if (__raw_readl(dm) == __raw_readl(dm + size)) ++ break; ++ } + } + + pr_debug("Memory: %lluMB of RAM detected at 0x%llx (min: %lluMB, max: %lluMB)\n", +@@ -115,6 +121,7 @@ void __init detect_memory_region(phys_ad + + memblock_add(start, size); + } ++#endif /* CONFIG_64BIT */ + + /* + * Manage initrd diff --git a/target/linux/generic/pending-6.1/401-mtd-don-t-register-NVMEM-devices-for-partitions-with.patch b/target/linux/generic/pending-6.1/401-mtd-don-t-register-NVMEM-devices-for-partitions-with.patch new file mode 100644 index 000000000..42b5a564b --- /dev/null +++ b/target/linux/generic/pending-6.1/401-mtd-don-t-register-NVMEM-devices-for-partitions-with.patch @@ -0,0 +1,48 @@ +From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= +Date: Tue, 31 Oct 2023 15:51:01 +0100 +Subject: [PATCH] mtd: don't register NVMEM devices for partitions with custom + drivers +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +This fixes issue exposed by upstream commit f4cf4e5db331 ("Revert +"nvmem: add new config option""). + +Signed-off-by: RafaÅ‚ MiÅ‚ecki +--- + drivers/mtd/mtdcore.c | 23 +++++++++++++++++++++++ + 1 file changed, 23 insertions(+) + +--- a/drivers/mtd/mtdcore.c ++++ b/drivers/mtd/mtdcore.c +@@ -519,6 +519,29 @@ static int mtd_nvmem_add(struct mtd_info + struct device_node *node = mtd_get_of_node(mtd); + struct nvmem_config config = {}; + ++ /* ++ * Do NOT register NVMEM device for any partition that is meant to be ++ * handled by a U-Boot env driver. That would result in associating two ++ * different NVMEM devices with the same OF node. ++ * ++ * An example of unwanted behaviour of above (forwardtrace): ++ * of_get_mac_addr_nvmem() ++ * of_nvmem_cell_get() ++ * __nvmem_device_get() ++ * ++ * We can't have __nvmem_device_get() return "mtdX" NVMEM device instead ++ * of U-Boot env NVMEM device. That would result in failing to find ++ * NVMEM cell. ++ * ++ * This issue seems to affect U-Boot env case only and will go away with ++ * switch to NVMEM layouts. ++ */ ++ if (of_device_is_compatible(node, "u-boot,env") || ++ of_device_is_compatible(node, "u-boot,env-redundant-bool") || ++ of_device_is_compatible(node, "u-boot,env-redundant-count") || ++ of_device_is_compatible(node, "brcm,env")) ++ return 0; ++ + config.id = -1; + config.dev = &mtd->dev; + config.name = dev_name(&mtd->dev); diff --git a/target/linux/generic/pending-6.1/402-mtd-spi-nor-write-support-for-minor-aligned-partitions.patch b/target/linux/generic/pending-6.1/402-mtd-spi-nor-write-support-for-minor-aligned-partitions.patch index c7da2f883..66a6feff6 100644 --- a/target/linux/generic/pending-6.1/402-mtd-spi-nor-write-support-for-minor-aligned-partitions.patch +++ b/target/linux/generic/pending-6.1/402-mtd-spi-nor-write-support-for-minor-aligned-partitions.patch @@ -202,7 +202,7 @@ Reported-by: Dan Carpenter return !!nor->params->erase_map.uniform_erase_type; } -@@ -2158,6 +2160,7 @@ static int spi_nor_select_erase(struct s +@@ -2180,6 +2182,7 @@ static int spi_nor_select_erase(struct s { struct spi_nor_erase_map *map = &nor->params->erase_map; const struct spi_nor_erase_type *erase = NULL; @@ -210,7 +210,7 @@ Reported-by: Dan Carpenter struct mtd_info *mtd = &nor->mtd; u32 wanted_size = nor->info->sector_size; int i; -@@ -2190,8 +2193,9 @@ static int spi_nor_select_erase(struct s +@@ -2212,8 +2215,9 @@ static int spi_nor_select_erase(struct s */ for (i = SNOR_ERASE_TYPE_MAX - 1; i >= 0; i--) { if (map->erase_type[i].size) { @@ -222,7 +222,7 @@ Reported-by: Dan Carpenter } } -@@ -2199,6 +2203,9 @@ static int spi_nor_select_erase(struct s +@@ -2221,6 +2225,9 @@ static int spi_nor_select_erase(struct s return -EINVAL; mtd->erasesize = erase->size; diff --git a/target/linux/generic/pending-6.1/430-mtd-add-myloader-partition-parser.patch b/target/linux/generic/pending-6.1/430-mtd-add-myloader-partition-parser.patch index c8cf3f5d3..35e80d6dc 100644 --- a/target/linux/generic/pending-6.1/430-mtd-add-myloader-partition-parser.patch +++ b/target/linux/generic/pending-6.1/430-mtd-add-myloader-partition-parser.patch @@ -16,7 +16,7 @@ Signed-off-by: Adrian Schmutzler +config MTD_MYLOADER_PARTS + tristate "MyLoader partition parsing" -+ depends on ADM5120 || ATH25 || ATH79 ++ depends on ADM5120 || ATH79 + help + MyLoader is a bootloader which allows the user to define partitions + in flash devices, by putting a table in the second erase block diff --git a/target/linux/generic/pending-6.1/450-01-dt-bindings-mtd-add-basic-bindings-for-UBI.patch b/target/linux/generic/pending-6.1/450-01-dt-bindings-mtd-add-basic-bindings-for-UBI.patch new file mode 100644 index 000000000..063d3fa79 --- /dev/null +++ b/target/linux/generic/pending-6.1/450-01-dt-bindings-mtd-add-basic-bindings-for-UBI.patch @@ -0,0 +1,121 @@ +From ffbbe7d66872ff8957dad2136133e28a1fd5d437 Mon Sep 17 00:00:00 2001 +From: Daniel Golle +Date: Mon, 7 Aug 2023 22:51:05 +0100 +Subject: [PATCH 01/15] dt-bindings: mtd: add basic bindings for UBI + +Add basic bindings for UBI devices and volumes. + +Signed-off-by: Daniel Golle +--- + .../bindings/mtd/partitions/linux,ubi.yaml | 65 +++++++++++++++++++ + .../bindings/mtd/partitions/ubi-volume.yaml | 35 ++++++++++ + 2 files changed, 100 insertions(+) + create mode 100644 Documentation/devicetree/bindings/mtd/partitions/linux,ubi.yaml + create mode 100644 Documentation/devicetree/bindings/mtd/partitions/ubi-volume.yaml + +--- /dev/null ++++ b/Documentation/devicetree/bindings/mtd/partitions/linux,ubi.yaml +@@ -0,0 +1,65 @@ ++# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause ++%YAML 1.2 ++--- ++$id: http://devicetree.org/schemas/mtd/partitions/linux,ubi.yaml# ++$schema: http://devicetree.org/meta-schemas/core.yaml# ++ ++title: Unsorted Block Images ++ ++description: | ++ UBI ("Unsorted Block Images") is a volume management system for raw ++ flash devices which manages multiple logical volumes on a single ++ physical flash device and spreads the I/O load (i.e wear-leveling) ++ across the whole flash chip. ++ ++maintainers: ++ - Daniel Golle ++ ++allOf: ++ - $ref: partition.yaml# ++ ++properties: ++ compatible: ++ const: linux,ubi ++ ++ volumes: ++ type: object ++ description: UBI Volumes ++ ++ patternProperties: ++ "^ubi-volume-.*$": ++ $ref: /schemas/mtd/partitions/ubi-volume.yaml# ++ ++ unevaluatedProperties: false ++ ++required: ++ - compatible ++ ++unevaluatedProperties: false ++ ++examples: ++ - | ++ partitions { ++ compatible = "fixed-partitions"; ++ #address-cells = <1>; ++ #size-cells = <1>; ++ ++ partition@0 { ++ reg = <0x0 0x100000>; ++ label = "bootloader"; ++ read-only; ++ }; ++ ++ partition@100000 { ++ reg = <0x100000 0x1ff00000>; ++ label = "ubi"; ++ compatible = "linux,ubi"; ++ ++ volumes { ++ ubi-volume-caldata { ++ volid = <2>; ++ volname = "rf"; ++ }; ++ }; ++ }; ++ }; +--- /dev/null ++++ b/Documentation/devicetree/bindings/mtd/partitions/ubi-volume.yaml +@@ -0,0 +1,35 @@ ++# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause ++%YAML 1.2 ++--- ++$id: http://devicetree.org/schemas/mtd/partitions/ubi-volume.yaml# ++$schema: http://devicetree.org/meta-schemas/core.yaml# ++ ++title: UBI volume ++ ++description: | ++ This binding describes a single UBI volume. Volumes can be matches either ++ by their ID or their name, or both. ++ ++maintainers: ++ - Daniel Golle ++ ++properties: ++ volid: ++ $ref: "/schemas/types.yaml#/definitions/uint32" ++ description: ++ Match UBI volume ID ++ ++ volname: ++ $ref: "/schemas/types.yaml#/definitions/string" ++ description: ++ Match UBI volume ID ++ ++anyOf: ++ - required: ++ - volid ++ ++ - required: ++ - volname ++ ++# This is a generic file other binding inherit from and extend ++additionalProperties: true diff --git a/target/linux/generic/pending-6.1/450-02-dt-bindings-mtd-ubi-volume-allow-UBI-volumes-to-prov.patch b/target/linux/generic/pending-6.1/450-02-dt-bindings-mtd-ubi-volume-allow-UBI-volumes-to-prov.patch new file mode 100644 index 000000000..823c8e83b --- /dev/null +++ b/target/linux/generic/pending-6.1/450-02-dt-bindings-mtd-ubi-volume-allow-UBI-volumes-to-prov.patch @@ -0,0 +1,48 @@ +From e4dad3aa5c3ab9c553555dd23c0b85f725f2eb51 Mon Sep 17 00:00:00 2001 +From: Daniel Golle +Date: Mon, 7 Aug 2023 22:53:01 +0100 +Subject: [PATCH 02/15] dt-bindings: mtd: ubi-volume: allow UBI volumes to + provide NVMEM + +UBI volumes may be used to contain NVMEM bits, typically device MAC +addresses or wireless radio calibration data. + +Signed-off-by: Daniel Golle +--- + .../devicetree/bindings/mtd/partitions/linux,ubi.yaml | 10 ++++++++++ + .../devicetree/bindings/mtd/partitions/ubi-volume.yaml | 5 +++++ + 2 files changed, 15 insertions(+) + +--- a/Documentation/devicetree/bindings/mtd/partitions/linux,ubi.yaml ++++ b/Documentation/devicetree/bindings/mtd/partitions/linux,ubi.yaml +@@ -59,6 +59,16 @@ examples: + ubi-volume-caldata { + volid = <2>; + volname = "rf"; ++ ++ nvmem-layout { ++ compatible = "fixed-layout"; ++ #address-cells = <1>; ++ #size-cells = <1>; ++ ++ eeprom@0 { ++ reg = <0x0 0x1000>; ++ }; ++ }; + }; + }; + }; +--- a/Documentation/devicetree/bindings/mtd/partitions/ubi-volume.yaml ++++ b/Documentation/devicetree/bindings/mtd/partitions/ubi-volume.yaml +@@ -24,6 +24,11 @@ properties: + description: + Match UBI volume ID + ++ nvmem-layout: ++ $ref: /schemas/nvmem/layouts/nvmem-layout.yaml# ++ description: ++ This container may reference an NVMEM layout parser. ++ + anyOf: + - required: + - volid diff --git a/target/linux/generic/pending-6.1/450-03-mtd-ubi-block-use-notifier-to-create-ubiblock-from-p.patch b/target/linux/generic/pending-6.1/450-03-mtd-ubi-block-use-notifier-to-create-ubiblock-from-p.patch new file mode 100644 index 000000000..eda3b108d --- /dev/null +++ b/target/linux/generic/pending-6.1/450-03-mtd-ubi-block-use-notifier-to-create-ubiblock-from-p.patch @@ -0,0 +1,225 @@ +From e5cf19bd8204925f3bd2067df9e867313eac388b Mon Sep 17 00:00:00 2001 +From: Daniel Golle +Date: Mon, 1 May 2023 11:57:51 +0100 +Subject: [PATCH 03/15] mtd: ubi: block: use notifier to create ubiblock from + parameter + +Use UBI_VOLUME_ADDED notification to create ubiblock device specified +on kernel cmdline or module parameter. +This makes thing more simple and has the advantage that ubiblock devices +on volumes which are not present at the time the ubi module is probed +will still be created. + +Suggested-by: Zhihao Cheng +Signed-off-by: Daniel Golle +--- + drivers/mtd/ubi/block.c | 154 ++++++++++++++++++++++------------------ + 1 file changed, 85 insertions(+), 69 deletions(-) + +--- a/drivers/mtd/ubi/block.c ++++ b/drivers/mtd/ubi/block.c +@@ -33,6 +33,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -67,10 +68,10 @@ struct ubiblock_pdu { + }; + + /* Numbers of elements set in the @ubiblock_param array */ +-static int ubiblock_devs __initdata; ++static int ubiblock_devs; + + /* MTD devices specification parameters */ +-static struct ubiblock_param ubiblock_param[UBIBLOCK_MAX_DEVICES] __initdata; ++static struct ubiblock_param ubiblock_param[UBIBLOCK_MAX_DEVICES]; + + struct ubiblock { + struct ubi_volume_desc *desc; +@@ -504,7 +505,7 @@ int ubiblock_remove(struct ubi_volume_in + } + + /* Found a device, let's lock it so we can check if it's busy */ +- mutex_lock(&dev->dev_mutex); ++ mutex_lock_nested(&dev->dev_mutex, SINGLE_DEPTH_NESTING); + if (dev->refcnt > 0) { + ret = -EBUSY; + goto out_unlock_dev; +@@ -567,6 +568,85 @@ static int ubiblock_resize(struct ubi_vo + return 0; + } + ++static bool ++match_volume_desc(struct ubi_volume_info *vi, const char *name, int ubi_num, int vol_id) ++{ ++ int err, len; ++ struct path path; ++ struct kstat stat; ++ ++ if (ubi_num == -1) { ++ /* No ubi num, name must be a vol device path */ ++ err = kern_path(name, LOOKUP_FOLLOW, &path); ++ if (err) ++ return false; ++ ++ err = vfs_getattr(&path, &stat, STATX_TYPE, AT_STATX_SYNC_AS_STAT); ++ path_put(&path); ++ if (err) ++ return false; ++ ++ if (!S_ISCHR(stat.mode)) ++ return false; ++ ++ if (vi->ubi_num != ubi_major2num(MAJOR(stat.rdev))) ++ return false; ++ ++ if (vi->vol_id != MINOR(stat.rdev) - 1) ++ return false; ++ ++ return true; ++ } ++ ++ if (vol_id == -1) { ++ if (vi->ubi_num != ubi_num) ++ return false; ++ ++ len = strnlen(name, UBI_VOL_NAME_MAX + 1); ++ if (len < 1 || vi->name_len != len) ++ return false; ++ ++ if (strcmp(name, vi->name)) ++ return false; ++ ++ return true; ++ } ++ ++ if (vi->ubi_num != ubi_num) ++ return false; ++ ++ if (vi->vol_id != vol_id) ++ return false; ++ ++ return true; ++} ++ ++static void ++ubiblock_create_from_param(struct ubi_volume_info *vi) ++{ ++ int i, ret = 0; ++ struct ubiblock_param *p; ++ ++ /* ++ * Iterate over ubiblock cmdline parameters. If a parameter matches the ++ * newly added volume create the ubiblock device for it. ++ */ ++ for (i = 0; i < ubiblock_devs; i++) { ++ p = &ubiblock_param[i]; ++ ++ if (!match_volume_desc(vi, p->name, p->ubi_num, p->vol_id)) ++ continue; ++ ++ ret = ubiblock_create(vi); ++ if (ret) { ++ pr_err( ++ "UBI: block: can't add '%s' volume on ubi%d_%d, err=%d\n", ++ vi->name, p->ubi_num, p->vol_id, ret); ++ } ++ break; ++ } ++} ++ + static int ubiblock_notify(struct notifier_block *nb, + unsigned long notification_type, void *ns_ptr) + { +@@ -574,10 +654,7 @@ static int ubiblock_notify(struct notifi + + switch (notification_type) { + case UBI_VOLUME_ADDED: +- /* +- * We want to enforce explicit block device creation for +- * volumes, so when a volume is added we do nothing. +- */ ++ ubiblock_create_from_param(&nt->vi); + break; + case UBI_VOLUME_REMOVED: + ubiblock_remove(&nt->vi); +@@ -603,56 +680,6 @@ static struct notifier_block ubiblock_no + .notifier_call = ubiblock_notify, + }; + +-static struct ubi_volume_desc * __init +-open_volume_desc(const char *name, int ubi_num, int vol_id) +-{ +- if (ubi_num == -1) +- /* No ubi num, name must be a vol device path */ +- return ubi_open_volume_path(name, UBI_READONLY); +- else if (vol_id == -1) +- /* No vol_id, must be vol_name */ +- return ubi_open_volume_nm(ubi_num, name, UBI_READONLY); +- else +- return ubi_open_volume(ubi_num, vol_id, UBI_READONLY); +-} +- +-static void __init ubiblock_create_from_param(void) +-{ +- int i, ret = 0; +- struct ubiblock_param *p; +- struct ubi_volume_desc *desc; +- struct ubi_volume_info vi; +- +- /* +- * If there is an error creating one of the ubiblocks, continue on to +- * create the following ubiblocks. This helps in a circumstance where +- * the kernel command-line specifies multiple block devices and some +- * may be broken, but we still want the working ones to come up. +- */ +- for (i = 0; i < ubiblock_devs; i++) { +- p = &ubiblock_param[i]; +- +- desc = open_volume_desc(p->name, p->ubi_num, p->vol_id); +- if (IS_ERR(desc)) { +- pr_err( +- "UBI: block: can't open volume on ubi%d_%d, err=%ld\n", +- p->ubi_num, p->vol_id, PTR_ERR(desc)); +- continue; +- } +- +- ubi_get_volume_info(desc, &vi); +- ubi_close_volume(desc); +- +- ret = ubiblock_create(&vi); +- if (ret) { +- pr_err( +- "UBI: block: can't add '%s' volume on ubi%d_%d, err=%d\n", +- vi.name, p->ubi_num, p->vol_id, ret); +- continue; +- } +- } +-} +- + static void ubiblock_remove_all(void) + { + struct ubiblock *next; +@@ -678,18 +705,7 @@ int __init ubiblock_init(void) + if (ubiblock_major < 0) + return ubiblock_major; + +- /* +- * Attach block devices from 'block=' module param. +- * Even if one block device in the param list fails to come up, +- * still allow the module to load and leave any others up. +- */ +- ubiblock_create_from_param(); +- +- /* +- * Block devices are only created upon user requests, so we ignore +- * existing volumes. +- */ +- ret = ubi_register_volume_notifier(&ubiblock_notifier, 1); ++ ret = ubi_register_volume_notifier(&ubiblock_notifier, 0); + if (ret) + goto err_unreg; + return 0; diff --git a/target/linux/generic/pending-6.1/450-04-mtd-ubi-attach-from-device-tree.patch b/target/linux/generic/pending-6.1/450-04-mtd-ubi-attach-from-device-tree.patch new file mode 100644 index 000000000..6e10e5ebe --- /dev/null +++ b/target/linux/generic/pending-6.1/450-04-mtd-ubi-attach-from-device-tree.patch @@ -0,0 +1,264 @@ +From 471a17d8d1b838092d1a76e48cdce8b5b67ff809 Mon Sep 17 00:00:00 2001 +From: Daniel Golle +Date: Mon, 27 Nov 2023 01:54:28 +0000 +Subject: [PATCH 04/15] mtd: ubi: attach from device tree + +Introduce device tree compatible 'linux,ubi' and attach compatible MTD +devices using the MTD add notifier. This is needed for a UBI device to +be available early at boot (and not only after late_initcall), so +volumes on them can be used eg. as NVMEM providers for other drivers. + +Signed-off-by: Daniel Golle +--- + drivers/mtd/ubi/build.c | 146 ++++++++++++++++++++++++++++------------ + drivers/mtd/ubi/cdev.c | 2 +- + drivers/mtd/ubi/ubi.h | 2 +- + 3 files changed, 106 insertions(+), 44 deletions(-) + +--- a/drivers/mtd/ubi/build.c ++++ b/drivers/mtd/ubi/build.c +@@ -27,6 +27,7 @@ + #include + #include + #include ++#include + #include + #include + #include "ubi.h" +@@ -1071,6 +1072,7 @@ out_free: + * ubi_detach_mtd_dev - detach an MTD device. + * @ubi_num: UBI device number to detach from + * @anyway: detach MTD even if device reference count is not zero ++ * @have_lock: called by MTD notifier holding mtd_table_mutex + * + * This function destroys an UBI device number @ubi_num and detaches the + * underlying MTD device. Returns zero in case of success and %-EBUSY if the +@@ -1080,7 +1082,7 @@ out_free: + * Note, the invocations of this function has to be serialized by the + * @ubi_devices_mutex. + */ +-int ubi_detach_mtd_dev(int ubi_num, int anyway) ++int ubi_detach_mtd_dev(int ubi_num, int anyway, bool have_lock) + { + struct ubi_device *ubi; + +@@ -1136,7 +1138,11 @@ int ubi_detach_mtd_dev(int ubi_num, int + vfree(ubi->peb_buf); + vfree(ubi->fm_buf); + ubi_msg(ubi, "mtd%d is detached", ubi->mtd->index); +- put_mtd_device(ubi->mtd); ++ if (have_lock) ++ __put_mtd_device(ubi->mtd); ++ else ++ put_mtd_device(ubi->mtd); ++ + put_device(&ubi->dev); + return 0; + } +@@ -1213,43 +1219,43 @@ static struct mtd_info * __init open_mtd + return mtd; + } + +-static int __init ubi_init(void) ++static void ubi_notify_add(struct mtd_info *mtd) + { +- int err, i, k; ++ struct device_node *np = mtd_get_of_node(mtd); ++ int err; + +- /* Ensure that EC and VID headers have correct size */ +- BUILD_BUG_ON(sizeof(struct ubi_ec_hdr) != 64); +- BUILD_BUG_ON(sizeof(struct ubi_vid_hdr) != 64); ++ if (!of_device_is_compatible(np, "linux,ubi")) ++ return; + +- if (mtd_devs > UBI_MAX_DEVICES) { +- pr_err("UBI error: too many MTD devices, maximum is %d\n", +- UBI_MAX_DEVICES); +- return -EINVAL; +- } ++ /* ++ * we are already holding &mtd_table_mutex, but still need ++ * to bump refcount ++ */ ++ err = __get_mtd_device(mtd); ++ if (err) ++ return; + +- /* Create base sysfs directory and sysfs files */ +- err = class_register(&ubi_class); ++ /* called while holding mtd_table_mutex */ ++ mutex_lock_nested(&ubi_devices_mutex, SINGLE_DEPTH_NESTING); ++ err = ubi_attach_mtd_dev(mtd, UBI_DEV_NUM_AUTO, 0, 0, false); ++ mutex_unlock(&ubi_devices_mutex); + if (err < 0) +- return err; +- +- err = misc_register(&ubi_ctrl_cdev); +- if (err) { +- pr_err("UBI error: cannot register device\n"); +- goto out; +- } ++ __put_mtd_device(mtd); ++} + +- ubi_wl_entry_slab = kmem_cache_create("ubi_wl_entry_slab", +- sizeof(struct ubi_wl_entry), +- 0, 0, NULL); +- if (!ubi_wl_entry_slab) { +- err = -ENOMEM; +- goto out_dev_unreg; +- } ++static void ubi_notify_remove(struct mtd_info *mtd) ++{ ++ WARN(1, "mtd%d removed despite UBI still being attached", mtd->index); ++} + +- err = ubi_debugfs_init(); +- if (err) +- goto out_slab; ++static struct mtd_notifier ubi_mtd_notifier = { ++ .add = ubi_notify_add, ++ .remove = ubi_notify_remove, ++}; + ++static int __init ubi_init_attach(void) ++{ ++ int err, i, k; + + /* Attach MTD devices */ + for (i = 0; i < mtd_devs; i++) { +@@ -1297,25 +1303,79 @@ static int __init ubi_init(void) + } + } + ++ return 0; ++ ++out_detach: ++ for (k = 0; k < i; k++) ++ if (ubi_devices[k]) { ++ mutex_lock(&ubi_devices_mutex); ++ ubi_detach_mtd_dev(ubi_devices[k]->ubi_num, 1, false); ++ mutex_unlock(&ubi_devices_mutex); ++ } ++ return err; ++} ++#ifndef CONFIG_MTD_UBI_MODULE ++late_initcall(ubi_init_attach); ++#endif ++ ++static int __init ubi_init(void) ++{ ++ int err; ++ ++ /* Ensure that EC and VID headers have correct size */ ++ BUILD_BUG_ON(sizeof(struct ubi_ec_hdr) != 64); ++ BUILD_BUG_ON(sizeof(struct ubi_vid_hdr) != 64); ++ ++ if (mtd_devs > UBI_MAX_DEVICES) { ++ pr_err("UBI error: too many MTD devices, maximum is %d\n", ++ UBI_MAX_DEVICES); ++ return -EINVAL; ++ } ++ ++ /* Create base sysfs directory and sysfs files */ ++ err = class_register(&ubi_class); ++ if (err < 0) ++ return err; ++ ++ err = misc_register(&ubi_ctrl_cdev); ++ if (err) { ++ pr_err("UBI error: cannot register device\n"); ++ goto out; ++ } ++ ++ ubi_wl_entry_slab = kmem_cache_create("ubi_wl_entry_slab", ++ sizeof(struct ubi_wl_entry), ++ 0, 0, NULL); ++ if (!ubi_wl_entry_slab) { ++ err = -ENOMEM; ++ goto out_dev_unreg; ++ } ++ ++ err = ubi_debugfs_init(); ++ if (err) ++ goto out_slab; ++ + err = ubiblock_init(); + if (err) { + pr_err("UBI error: block: cannot initialize, error %d\n", err); + + /* See comment above re-ubi_is_module(). */ + if (ubi_is_module()) +- goto out_detach; ++ goto out_slab; ++ } ++ ++ register_mtd_user(&ubi_mtd_notifier); ++ ++ if (ubi_is_module()) { ++ err = ubi_init_attach(); ++ if (err) ++ goto out_mtd_notifier; + } + + return 0; + +-out_detach: +- for (k = 0; k < i; k++) +- if (ubi_devices[k]) { +- mutex_lock(&ubi_devices_mutex); +- ubi_detach_mtd_dev(ubi_devices[k]->ubi_num, 1); +- mutex_unlock(&ubi_devices_mutex); +- } +- ubi_debugfs_exit(); ++out_mtd_notifier: ++ unregister_mtd_user(&ubi_mtd_notifier); + out_slab: + kmem_cache_destroy(ubi_wl_entry_slab); + out_dev_unreg: +@@ -1325,18 +1385,20 @@ out: + pr_err("UBI error: cannot initialize UBI, error %d\n", err); + return err; + } +-late_initcall(ubi_init); ++device_initcall(ubi_init); ++ + + static void __exit ubi_exit(void) + { + int i; + + ubiblock_exit(); ++ unregister_mtd_user(&ubi_mtd_notifier); + + for (i = 0; i < UBI_MAX_DEVICES; i++) + if (ubi_devices[i]) { + mutex_lock(&ubi_devices_mutex); +- ubi_detach_mtd_dev(ubi_devices[i]->ubi_num, 1); ++ ubi_detach_mtd_dev(ubi_devices[i]->ubi_num, 1, false); + mutex_unlock(&ubi_devices_mutex); + } + ubi_debugfs_exit(); +--- a/drivers/mtd/ubi/cdev.c ++++ b/drivers/mtd/ubi/cdev.c +@@ -1065,7 +1065,7 @@ static long ctrl_cdev_ioctl(struct file + } + + mutex_lock(&ubi_devices_mutex); +- err = ubi_detach_mtd_dev(ubi_num, 0); ++ err = ubi_detach_mtd_dev(ubi_num, 0, false); + mutex_unlock(&ubi_devices_mutex); + break; + } +--- a/drivers/mtd/ubi/ubi.h ++++ b/drivers/mtd/ubi/ubi.h +@@ -939,7 +939,7 @@ int ubi_io_write_vid_hdr(struct ubi_devi + int ubi_attach_mtd_dev(struct mtd_info *mtd, int ubi_num, + int vid_hdr_offset, int max_beb_per1024, + bool disable_fm); +-int ubi_detach_mtd_dev(int ubi_num, int anyway); ++int ubi_detach_mtd_dev(int ubi_num, int anyway, bool have_lock); + struct ubi_device *ubi_get_device(int ubi_num); + void ubi_put_device(struct ubi_device *ubi); + struct ubi_device *ubi_get_by_major(int major); diff --git a/target/linux/generic/pending-6.1/450-05-mtd-ubi-introduce-pre-removal-notification-for-UBI-v.patch b/target/linux/generic/pending-6.1/450-05-mtd-ubi-introduce-pre-removal-notification-for-UBI-v.patch new file mode 100644 index 000000000..d5da37b85 --- /dev/null +++ b/target/linux/generic/pending-6.1/450-05-mtd-ubi-introduce-pre-removal-notification-for-UBI-v.patch @@ -0,0 +1,226 @@ +From 2d664266cfdd114cc7a1fa28dd64275e99222455 Mon Sep 17 00:00:00 2001 +From: Daniel Golle +Date: Thu, 8 Jun 2023 17:18:09 +0100 +Subject: [PATCH 05/15] mtd: ubi: introduce pre-removal notification for UBI + volumes + +Introduce a new notification type UBI_VOLUME_SHUTDOWN to inform users +that a volume is just about to be removed. +This is needed because users (such as the NVMEM subsystem) expect that +at the time their removal function is called, the parenting device is +still available (for removal of sysfs nodes, for example, in case of +NVMEM which otherwise WARNs on volume removal). + +Signed-off-by: Daniel Golle +--- + drivers/mtd/ubi/block.c | 26 ++++++++++++++++++++++++++ + drivers/mtd/ubi/build.c | 20 +++++++++++++++----- + drivers/mtd/ubi/kapi.c | 2 +- + drivers/mtd/ubi/ubi.h | 2 ++ + drivers/mtd/ubi/vmt.c | 17 +++++++++++++++-- + include/linux/mtd/ubi.h | 2 ++ + 6 files changed, 61 insertions(+), 8 deletions(-) + +--- a/drivers/mtd/ubi/block.c ++++ b/drivers/mtd/ubi/block.c +@@ -568,6 +568,29 @@ static int ubiblock_resize(struct ubi_vo + return 0; + } + ++static int ubiblock_shutdown(struct ubi_volume_info *vi) ++{ ++ struct ubiblock *dev; ++ struct gendisk *disk; ++ int ret = 0; ++ ++ mutex_lock(&devices_mutex); ++ dev = find_dev_nolock(vi->ubi_num, vi->vol_id); ++ if (!dev) { ++ ret = -ENODEV; ++ goto out_unlock; ++ } ++ disk = dev->gd; ++ ++out_unlock: ++ mutex_unlock(&devices_mutex); ++ ++ if (!ret) ++ blk_mark_disk_dead(disk); ++ ++ return ret; ++}; ++ + static bool + match_volume_desc(struct ubi_volume_info *vi, const char *name, int ubi_num, int vol_id) + { +@@ -659,6 +682,9 @@ static int ubiblock_notify(struct notifi + case UBI_VOLUME_REMOVED: + ubiblock_remove(&nt->vi); + break; ++ case UBI_VOLUME_SHUTDOWN: ++ ubiblock_shutdown(&nt->vi); ++ break; + case UBI_VOLUME_RESIZED: + ubiblock_resize(&nt->vi); + break; +--- a/drivers/mtd/ubi/build.c ++++ b/drivers/mtd/ubi/build.c +@@ -89,7 +89,7 @@ static struct ubi_device *ubi_devices[UB + /* Serializes UBI devices creations and removals */ + DEFINE_MUTEX(ubi_devices_mutex); + +-/* Protects @ubi_devices and @ubi->ref_count */ ++/* Protects @ubi_devices, @ubi->ref_count and @ubi->is_dead */ + static DEFINE_SPINLOCK(ubi_devices_lock); + + /* "Show" method for files in '//class/ubi/' */ +@@ -258,6 +258,9 @@ struct ubi_device *ubi_get_device(int ub + + spin_lock(&ubi_devices_lock); + ubi = ubi_devices[ubi_num]; ++ if (ubi && ubi->is_dead) ++ ubi = NULL; ++ + if (ubi) { + ubi_assert(ubi->ref_count >= 0); + ubi->ref_count += 1; +@@ -295,7 +298,7 @@ struct ubi_device *ubi_get_by_major(int + spin_lock(&ubi_devices_lock); + for (i = 0; i < UBI_MAX_DEVICES; i++) { + ubi = ubi_devices[i]; +- if (ubi && MAJOR(ubi->cdev.dev) == major) { ++ if (ubi && !ubi->is_dead && MAJOR(ubi->cdev.dev) == major) { + ubi_assert(ubi->ref_count >= 0); + ubi->ref_count += 1; + get_device(&ubi->dev); +@@ -324,7 +327,7 @@ int ubi_major2num(int major) + for (i = 0; i < UBI_MAX_DEVICES; i++) { + struct ubi_device *ubi = ubi_devices[i]; + +- if (ubi && MAJOR(ubi->cdev.dev) == major) { ++ if (ubi && !ubi->is_dead && MAJOR(ubi->cdev.dev) == major) { + ubi_num = ubi->ubi_num; + break; + } +@@ -511,7 +514,7 @@ static void ubi_free_volumes_from(struct + int i; + + for (i = from; i < ubi->vtbl_slots + UBI_INT_VOL_COUNT; i++) { +- if (!ubi->volumes[i]) ++ if (!ubi->volumes[i] || ubi->volumes[i]->is_dead) + continue; + ubi_eba_replace_table(ubi->volumes[i], NULL); + ubi_fastmap_destroy_checkmap(ubi->volumes[i]); +@@ -1094,10 +1097,10 @@ int ubi_detach_mtd_dev(int ubi_num, int + return -EINVAL; + + spin_lock(&ubi_devices_lock); +- put_device(&ubi->dev); + ubi->ref_count -= 1; + if (ubi->ref_count) { + if (!anyway) { ++ ubi->ref_count += 1; + spin_unlock(&ubi_devices_lock); + return -EBUSY; + } +@@ -1105,6 +1108,13 @@ int ubi_detach_mtd_dev(int ubi_num, int + ubi_err(ubi, "%s reference count %d, destroy anyway", + ubi->ubi_name, ubi->ref_count); + } ++ ubi->is_dead = true; ++ spin_unlock(&ubi_devices_lock); ++ ++ ubi_notify_all(ubi, UBI_VOLUME_SHUTDOWN, NULL); ++ ++ spin_lock(&ubi_devices_lock); ++ put_device(&ubi->dev); + ubi_devices[ubi_num] = NULL; + spin_unlock(&ubi_devices_lock); + +--- a/drivers/mtd/ubi/kapi.c ++++ b/drivers/mtd/ubi/kapi.c +@@ -152,7 +152,7 @@ struct ubi_volume_desc *ubi_open_volume( + + spin_lock(&ubi->volumes_lock); + vol = ubi->volumes[vol_id]; +- if (!vol) ++ if (!vol || vol->is_dead) + goto out_unlock; + + err = -EBUSY; +--- a/drivers/mtd/ubi/ubi.h ++++ b/drivers/mtd/ubi/ubi.h +@@ -345,6 +345,7 @@ struct ubi_volume { + int writers; + int exclusive; + int metaonly; ++ bool is_dead; + + int reserved_pebs; + int vol_type; +@@ -564,6 +565,7 @@ struct ubi_device { + spinlock_t volumes_lock; + int ref_count; + int image_seq; ++ bool is_dead; + + int rsvd_pebs; + int avail_pebs; +--- a/drivers/mtd/ubi/vmt.c ++++ b/drivers/mtd/ubi/vmt.c +@@ -59,7 +59,7 @@ static ssize_t vol_attribute_show(struct + struct ubi_device *ubi = vol->ubi; + + spin_lock(&ubi->volumes_lock); +- if (!ubi->volumes[vol->vol_id]) { ++ if (!ubi->volumes[vol->vol_id] || ubi->volumes[vol->vol_id]->is_dead) { + spin_unlock(&ubi->volumes_lock); + return -ENODEV; + } +@@ -189,7 +189,7 @@ int ubi_create_volume(struct ubi_device + + /* Ensure that the name is unique */ + for (i = 0; i < ubi->vtbl_slots; i++) +- if (ubi->volumes[i] && ++ if (ubi->volumes[i] && !ubi->volumes[i]->is_dead && + ubi->volumes[i]->name_len == req->name_len && + !strcmp(ubi->volumes[i]->name, req->name)) { + ubi_err(ubi, "volume \"%s\" exists (ID %d)", +@@ -352,6 +352,19 @@ int ubi_remove_volume(struct ubi_volume_ + err = -EBUSY; + goto out_unlock; + } ++ ++ /* ++ * Mark volume as dead at this point to prevent that anyone ++ * can take a reference to the volume from now on. ++ * This is necessary as we have to release the spinlock before ++ * calling ubi_volume_notify. ++ */ ++ vol->is_dead = true; ++ spin_unlock(&ubi->volumes_lock); ++ ++ ubi_volume_notify(ubi, vol, UBI_VOLUME_SHUTDOWN); ++ ++ spin_lock(&ubi->volumes_lock); + ubi->volumes[vol_id] = NULL; + spin_unlock(&ubi->volumes_lock); + +--- a/include/linux/mtd/ubi.h ++++ b/include/linux/mtd/ubi.h +@@ -192,6 +192,7 @@ struct ubi_device_info { + * or a volume was removed) + * @UBI_VOLUME_RESIZED: a volume has been re-sized + * @UBI_VOLUME_RENAMED: a volume has been re-named ++ * @UBI_VOLUME_SHUTDOWN: a volume is going to removed, shutdown users + * @UBI_VOLUME_UPDATED: data has been written to a volume + * + * These constants define which type of event has happened when a volume +@@ -202,6 +203,7 @@ enum { + UBI_VOLUME_REMOVED, + UBI_VOLUME_RESIZED, + UBI_VOLUME_RENAMED, ++ UBI_VOLUME_SHUTDOWN, + UBI_VOLUME_UPDATED, + }; + diff --git a/target/linux/generic/pending-6.1/450-06-mtd-ubi-populate-ubi-volume-fwnode.patch b/target/linux/generic/pending-6.1/450-06-mtd-ubi-populate-ubi-volume-fwnode.patch new file mode 100644 index 000000000..132276696 --- /dev/null +++ b/target/linux/generic/pending-6.1/450-06-mtd-ubi-populate-ubi-volume-fwnode.patch @@ -0,0 +1,65 @@ +From 3a041ee543cdf2e707a1dd72946cd6a583509b28 Mon Sep 17 00:00:00 2001 +From: Daniel Golle +Date: Fri, 21 Jul 2023 19:26:37 +0100 +Subject: [PATCH 06/15] mtd: ubi: populate ubi volume fwnode + +Look for the 'volumes' subnode of an MTD partition attached to a UBI +device and attach matching child nodes to UBI volumes. +This allows UBI volumes to be referenced in device tree, e.g. for use +as NVMEM providers. + +Signed-off-by: Daniel Golle +--- + drivers/mtd/ubi/vmt.c | 27 +++++++++++++++++++++++++++ + 1 file changed, 27 insertions(+) + +--- a/drivers/mtd/ubi/vmt.c ++++ b/drivers/mtd/ubi/vmt.c +@@ -124,6 +124,31 @@ static void vol_release(struct device *d + kfree(vol); + } + ++static struct fwnode_handle *find_volume_fwnode(struct ubi_volume *vol) ++{ ++ struct fwnode_handle *fw_vols, *fw_vol; ++ const char *volname; ++ u32 volid; ++ ++ fw_vols = device_get_named_child_node(vol->dev.parent->parent, "volumes"); ++ if (!fw_vols) ++ return NULL; ++ ++ fwnode_for_each_child_node(fw_vols, fw_vol) { ++ if (!fwnode_property_read_string(fw_vol, "volname", &volname) && ++ strncmp(volname, vol->name, vol->name_len)) ++ continue; ++ ++ if (!fwnode_property_read_u32(fw_vol, "volid", &volid) && ++ vol->vol_id != volid) ++ continue; ++ ++ return fw_vol; ++ } ++ ++ return NULL; ++} ++ + /** + * ubi_create_volume - create volume. + * @ubi: UBI device description object +@@ -223,6 +248,7 @@ int ubi_create_volume(struct ubi_device + vol->name_len = req->name_len; + memcpy(vol->name, req->name, vol->name_len); + vol->ubi = ubi; ++ device_set_node(&vol->dev, find_volume_fwnode(vol)); + + /* + * Finish all pending erases because there may be some LEBs belonging +@@ -605,6 +631,7 @@ int ubi_add_volume(struct ubi_device *ub + vol->dev.class = &ubi_class; + vol->dev.groups = volume_dev_groups; + dev_set_name(&vol->dev, "%s_%d", ubi->ubi_name, vol->vol_id); ++ device_set_node(&vol->dev, find_volume_fwnode(vol)); + err = device_register(&vol->dev); + if (err) { + cdev_del(&vol->cdev); diff --git a/target/linux/generic/pending-6.1/450-07-mtd-ubi-provide-NVMEM-layer-over-UBI-volumes.patch b/target/linux/generic/pending-6.1/450-07-mtd-ubi-provide-NVMEM-layer-over-UBI-volumes.patch new file mode 100644 index 000000000..59a1eb937 --- /dev/null +++ b/target/linux/generic/pending-6.1/450-07-mtd-ubi-provide-NVMEM-layer-over-UBI-volumes.patch @@ -0,0 +1,246 @@ +From 7eb6666348f3f2d1f7308c712fa5903cbe189401 Mon Sep 17 00:00:00 2001 +From: Daniel Golle +Date: Thu, 8 Jun 2023 17:22:04 +0100 +Subject: [PATCH 07/15] mtd: ubi: provide NVMEM layer over UBI volumes + +In an ideal world we would like UBI to be used where ever possible on a +NAND chip. And with UBI support in ARM Trusted Firmware and U-Boot it +is possible to achieve an (almost-)all-UBI flash layout. Hence the need +for a way to also use UBI volumes to store board-level constants, such +as MAC addresses and calibration data of wireless interfaces. + +Add UBI volume NVMEM driver module exposing UBI volumes as NVMEM +providers. Allow UBI devices to have a "volumes" firmware subnode with +volumes which may be compatible with "nvmem-cells". +Access to UBI volumes via the NVMEM interface at this point is +read-only, and it is slow, opening and closing the UBI volume for each +access due to limitations of the NVMEM provider API. + +Signed-off-by: Daniel Golle +--- + drivers/mtd/ubi/Kconfig | 12 +++ + drivers/mtd/ubi/Makefile | 1 + + drivers/mtd/ubi/nvmem.c | 188 +++++++++++++++++++++++++++++++++++++++ + 3 files changed, 201 insertions(+) + create mode 100644 drivers/mtd/ubi/nvmem.c + +--- a/drivers/mtd/ubi/Kconfig ++++ b/drivers/mtd/ubi/Kconfig +@@ -104,4 +104,16 @@ config MTD_UBI_BLOCK + + If in doubt, say "N". + ++config MTD_UBI_NVMEM ++ tristate "UBI virtual NVMEM" ++ default n ++ depends on NVMEM ++ help ++ This option enabled an additional driver exposing UBI volumes as NVMEM ++ providers, intended for platforms where UBI is part of the firmware ++ specification and used to store also e.g. MAC addresses or board- ++ specific Wi-Fi calibration data. ++ ++ If in doubt, say "N". ++ + endif # MTD_UBI +--- a/drivers/mtd/ubi/Makefile ++++ b/drivers/mtd/ubi/Makefile +@@ -7,3 +7,4 @@ ubi-$(CONFIG_MTD_UBI_FASTMAP) += fastmap + ubi-$(CONFIG_MTD_UBI_BLOCK) += block.o + + obj-$(CONFIG_MTD_UBI_GLUEBI) += gluebi.o ++obj-$(CONFIG_MTD_UBI_NVMEM) += nvmem.o +--- /dev/null ++++ b/drivers/mtd/ubi/nvmem.c +@@ -0,0 +1,191 @@ ++// SPDX-License-Identifier: GPL-2.0-or-later ++/* ++ * Copyright (c) 2023 Daniel Golle ++ */ ++ ++/* UBI NVMEM provider */ ++#include "ubi.h" ++#include ++#include ++ ++/* List of all NVMEM devices */ ++static LIST_HEAD(nvmem_devices); ++static DEFINE_MUTEX(devices_mutex); ++ ++struct ubi_nvmem { ++ struct nvmem_device *nvmem; ++ int ubi_num; ++ int vol_id; ++ int usable_leb_size; ++ struct list_head list; ++}; ++ ++static int ubi_nvmem_reg_read(void *priv, unsigned int from, ++ void *val, size_t bytes) ++{ ++ uint32_t offs, to_read, bytes_left; ++ struct ubi_nvmem *unv = priv; ++ struct ubi_volume_desc *desc; ++ uint64_t lnum = from; ++ int err = 0; ++ ++ desc = ubi_open_volume(unv->ubi_num, unv->vol_id, UBI_READONLY); ++ if (IS_ERR(desc)) ++ return PTR_ERR(desc); ++ ++ bytes_left = bytes; ++ offs = do_div(lnum, unv->usable_leb_size); ++ while (bytes_left) { ++ to_read = unv->usable_leb_size - offs; ++ ++ if (to_read > bytes_left) ++ to_read = bytes_left; ++ ++ err = ubi_read(desc, lnum, val, offs, to_read); ++ if (err) ++ break; ++ ++ lnum += 1; ++ offs = 0; ++ bytes_left -= to_read; ++ val += to_read; ++ } ++ ubi_close_volume(desc); ++ ++ if (err) ++ return err; ++ ++ return bytes_left == 0 ? 0 : -EIO; ++} ++ ++static int ubi_nvmem_add(struct ubi_volume_info *vi) ++{ ++ struct device_node *np = dev_of_node(vi->dev); ++ struct nvmem_config config = {}; ++ struct ubi_nvmem *unv; ++ int ret; ++ ++ if (!np) ++ return 0; ++ ++ if (!of_get_child_by_name(np, "nvmem-layout")) ++ return 0; ++ ++ if (WARN_ON_ONCE(vi->usable_leb_size <= 0) || ++ WARN_ON_ONCE(vi->size <= 0)) ++ return -EINVAL; ++ ++ unv = kzalloc(sizeof(struct ubi_nvmem), GFP_KERNEL); ++ if (!unv) ++ return -ENOMEM; ++ ++ config.id = NVMEM_DEVID_NONE; ++ config.dev = vi->dev; ++ config.name = dev_name(vi->dev); ++ config.owner = THIS_MODULE; ++ config.priv = unv; ++ config.reg_read = ubi_nvmem_reg_read; ++ config.size = vi->usable_leb_size * vi->size; ++ config.word_size = 1; ++ config.stride = 1; ++ config.read_only = true; ++ config.root_only = true; ++ config.ignore_wp = true; ++ config.of_node = np; ++ ++ unv->ubi_num = vi->ubi_num; ++ unv->vol_id = vi->vol_id; ++ unv->usable_leb_size = vi->usable_leb_size; ++ unv->nvmem = nvmem_register(&config); ++ if (IS_ERR(unv->nvmem)) { ++ ret = dev_err_probe(vi->dev, PTR_ERR(unv->nvmem), ++ "Failed to register NVMEM device\n"); ++ kfree(unv); ++ return ret; ++ } ++ ++ mutex_lock(&devices_mutex); ++ list_add_tail(&unv->list, &nvmem_devices); ++ mutex_unlock(&devices_mutex); ++ ++ return 0; ++} ++ ++static void ubi_nvmem_remove(struct ubi_volume_info *vi) ++{ ++ struct ubi_nvmem *unv_c, *unv = NULL; ++ ++ mutex_lock(&devices_mutex); ++ list_for_each_entry(unv_c, &nvmem_devices, list) ++ if (unv_c->ubi_num == vi->ubi_num && unv_c->vol_id == vi->vol_id) { ++ unv = unv_c; ++ break; ++ } ++ ++ if (!unv) { ++ mutex_unlock(&devices_mutex); ++ return; ++ } ++ ++ list_del(&unv->list); ++ mutex_unlock(&devices_mutex); ++ nvmem_unregister(unv->nvmem); ++ kfree(unv); ++} ++ ++/** ++ * nvmem_notify - UBI notification handler. ++ * @nb: registered notifier block ++ * @l: notification type ++ * @ns_ptr: pointer to the &struct ubi_notification object ++ */ ++static int nvmem_notify(struct notifier_block *nb, unsigned long l, ++ void *ns_ptr) ++{ ++ struct ubi_notification *nt = ns_ptr; ++ ++ switch (l) { ++ case UBI_VOLUME_RESIZED: ++ ubi_nvmem_remove(&nt->vi); ++ fallthrough; ++ case UBI_VOLUME_ADDED: ++ ubi_nvmem_add(&nt->vi); ++ break; ++ case UBI_VOLUME_SHUTDOWN: ++ ubi_nvmem_remove(&nt->vi); ++ break; ++ default: ++ break; ++ } ++ return NOTIFY_OK; ++} ++ ++static struct notifier_block nvmem_notifier = { ++ .notifier_call = nvmem_notify, ++}; ++ ++static int __init ubi_nvmem_init(void) ++{ ++ return ubi_register_volume_notifier(&nvmem_notifier, 0); ++} ++ ++static void __exit ubi_nvmem_exit(void) ++{ ++ struct ubi_nvmem *unv, *tmp; ++ ++ mutex_lock(&devices_mutex); ++ list_for_each_entry_safe(unv, tmp, &nvmem_devices, list) { ++ nvmem_unregister(unv->nvmem); ++ list_del(&unv->list); ++ kfree(unv); ++ } ++ mutex_unlock(&devices_mutex); ++ ++ ubi_unregister_volume_notifier(&nvmem_notifier); ++} ++ ++module_init(ubi_nvmem_init); ++module_exit(ubi_nvmem_exit); ++MODULE_DESCRIPTION("NVMEM layer over UBI volumes"); ++MODULE_AUTHOR("Daniel Golle"); ++MODULE_LICENSE("GPL"); diff --git a/target/linux/generic/pending-6.1/450-08-dt-bindings-block-add-basic-bindings-for-block-devic.patch b/target/linux/generic/pending-6.1/450-08-dt-bindings-block-add-basic-bindings-for-block-devic.patch new file mode 100644 index 000000000..d0727faf3 --- /dev/null +++ b/target/linux/generic/pending-6.1/450-08-dt-bindings-block-add-basic-bindings-for-block-devic.patch @@ -0,0 +1,120 @@ +From 9ffc1d7d73609a89eb264d6066340f8b7b3b0ebe Mon Sep 17 00:00:00 2001 +From: Daniel Golle +Date: Mon, 7 Aug 2023 21:19:45 +0100 +Subject: [PATCH 08/15] dt-bindings: block: add basic bindings for block + devices + +Add bindings for block devices which are used to allow referencing +nvmem bits on them. + +Signed-off-by: Daniel Golle +--- + .../bindings/block/block-device.yaml | 22 ++++++++ + .../devicetree/bindings/block/partition.yaml | 50 +++++++++++++++++++ + .../devicetree/bindings/block/partitions.yaml | 20 ++++++++ + 3 files changed, 92 insertions(+) + create mode 100644 Documentation/devicetree/bindings/block/block-device.yaml + create mode 100644 Documentation/devicetree/bindings/block/partition.yaml + create mode 100644 Documentation/devicetree/bindings/block/partitions.yaml + +--- /dev/null ++++ b/Documentation/devicetree/bindings/block/block-device.yaml +@@ -0,0 +1,22 @@ ++# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause ++%YAML 1.2 ++--- ++$id: http://devicetree.org/schemas/block/block-device.yaml# ++$schema: http://devicetree.org/meta-schemas/core.yaml# ++ ++title: block storage device ++ ++description: | ++ This binding is generic and describes a block-oriented storage device. ++ ++maintainers: ++ - Daniel Golle ++ ++properties: ++ partitions: ++ $ref: /schemas/block/partitions.yaml ++ ++ nvmem-layout: ++ $ref: /schemas/nvmem/layouts/nvmem-layout.yaml# ++ ++unevaluatedProperties: false +--- /dev/null ++++ b/Documentation/devicetree/bindings/block/partition.yaml +@@ -0,0 +1,50 @@ ++# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause ++%YAML 1.2 ++--- ++$id: http://devicetree.org/schemas/block/partition.yaml# ++$schema: http://devicetree.org/meta-schemas/core.yaml# ++ ++title: Partition on a block device ++ ++description: | ++ This binding describes a partition on a block device. ++ Partitions may be matched by a combination of partition number, name, ++ and UUID. ++ ++maintainers: ++ - Daniel Golle ++ ++properties: ++ $nodename: ++ pattern: '^block-partition-.+$' ++ ++ partnum: ++ description: ++ Matches partition by number if present. ++ ++ partname: ++ "$ref": "/schemas/types.yaml#/definitions/string" ++ description: ++ Matches partition by PARTNAME if present. ++ ++ uuid: ++ "$ref": "/schemas/types.yaml#/definitions/string" ++ description: ++ Matches partition by PARTUUID if present. ++ ++ nvmem-layout: ++ $ref: /schemas/nvmem/layouts/nvmem-layout.yaml# ++ description: ++ This container may reference an NVMEM layout parser. ++ ++anyOf: ++ - required: ++ - partnum ++ ++ - required: ++ - partname ++ ++ - required: ++ - uuid ++ ++unevaluatedProperties: false +--- /dev/null ++++ b/Documentation/devicetree/bindings/block/partitions.yaml +@@ -0,0 +1,20 @@ ++# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause ++%YAML 1.2 ++--- ++$id: http://devicetree.org/schemas/block/partitions.yaml# ++$schema: http://devicetree.org/meta-schemas/core.yaml# ++ ++title: Partitions on block devices ++ ++description: | ++ This binding is generic and describes the content of the partitions container ++ node. ++ ++maintainers: ++ - Daniel Golle ++ ++patternProperties: ++ "^block-partition-.+$": ++ $ref: partition.yaml ++ ++unevaluatedProperties: false diff --git a/target/linux/generic/pending-6.1/450-09-block-partitions-populate-fwnode.patch b/target/linux/generic/pending-6.1/450-09-block-partitions-populate-fwnode.patch new file mode 100644 index 000000000..8aa5cba67 --- /dev/null +++ b/target/linux/generic/pending-6.1/450-09-block-partitions-populate-fwnode.patch @@ -0,0 +1,77 @@ +From 614f4f6fdda09e30ecf7ef6c8091579db15018cb Mon Sep 17 00:00:00 2001 +From: Daniel Golle +Date: Fri, 21 Jul 2023 17:51:03 +0100 +Subject: [PATCH 09/15] block: partitions: populate fwnode + +Let block partitions to be represented by a firmware node and hence +allow them to being referenced e.g. for use with blk-nvmem. + +Signed-off-by: Daniel Golle +--- + block/partitions/core.c | 41 +++++++++++++++++++++++++++++++++++++++++ + 1 file changed, 41 insertions(+) + +--- a/block/partitions/core.c ++++ b/block/partitions/core.c +@@ -10,6 +10,8 @@ + #include + #include + #include ++#include ++ + #include "check.h" + + static int (*check_part[])(struct parsed_partitions *) = { +@@ -298,6 +300,43 @@ static ssize_t whole_disk_show(struct de + } + static DEVICE_ATTR(whole_disk, 0444, whole_disk_show, NULL); + ++static struct fwnode_handle *find_partition_fwnode(struct block_device *bdev) ++{ ++ struct fwnode_handle *fw_parts, *fw_part; ++ struct device *ddev = disk_to_dev(bdev->bd_disk); ++ const char *partname, *uuid; ++ u32 partno; ++ ++ fw_parts = device_get_named_child_node(ddev, "partitions"); ++ if (!fw_parts) ++ fw_parts = device_get_named_child_node(ddev->parent, "partitions"); ++ ++ if (!fw_parts) ++ return NULL; ++ ++ fwnode_for_each_child_node(fw_parts, fw_part) { ++ if (!fwnode_property_read_string(fw_part, "uuid", &uuid) && ++ (!bdev->bd_meta_info || strncmp(uuid, ++ bdev->bd_meta_info->uuid, ++ PARTITION_META_INFO_UUIDLTH))) ++ continue; ++ ++ if (!fwnode_property_read_string(fw_part, "partname", &partname) && ++ (!bdev->bd_meta_info || strncmp(partname, ++ bdev->bd_meta_info->volname, ++ PARTITION_META_INFO_VOLNAMELTH))) ++ continue; ++ ++ if (!fwnode_property_read_u32(fw_part, "partno", &partno) && ++ bdev->bd_partno != partno) ++ continue; ++ ++ return fw_part; ++ } ++ ++ return NULL; ++} ++ + /* + * Must be called either with open_mutex held, before a disk can be opened or + * after all disk users are gone. +@@ -380,6 +419,8 @@ static struct block_device *add_partitio + goto out_put; + } + ++ device_set_node(pdev, find_partition_fwnode(bdev)); ++ + /* delay uevent until 'holders' subdir is created */ + dev_set_uevent_suppress(pdev, 1); + err = device_add(pdev); diff --git a/target/linux/generic/pending-6.1/450-10-block-add-new-genhd-flag-GENHD_FL_NVMEM.patch b/target/linux/generic/pending-6.1/450-10-block-add-new-genhd-flag-GENHD_FL_NVMEM.patch new file mode 100644 index 000000000..4cbec14f5 --- /dev/null +++ b/target/linux/generic/pending-6.1/450-10-block-add-new-genhd-flag-GENHD_FL_NVMEM.patch @@ -0,0 +1,29 @@ +From 65f3ff9672ccd5ee78937047e7a2fc696eee1c8f Mon Sep 17 00:00:00 2001 +From: Daniel Golle +Date: Thu, 13 Jul 2023 04:07:16 +0100 +Subject: [PATCH 10/15] block: add new genhd flag GENHD_FL_NVMEM + +Add new flag to destinguish block devices which may act as an NVMEM +provider. + +Signed-off-by: Daniel Golle +--- + include/linux/blkdev.h | 2 ++ + 1 file changed, 2 insertions(+) + +--- a/include/linux/blkdev.h ++++ b/include/linux/blkdev.h +@@ -87,11 +87,13 @@ struct partition_meta_info { + * ``GENHD_FL_NO_PART``: partition support is disabled. The kernel will not + * scan for partitions from add_disk, and users can't add partitions manually. + * ++ * ``GENHD_FL_NVMEM``: the block device should be considered as NVMEM provider. + */ + enum { + GENHD_FL_REMOVABLE = 1 << 0, + GENHD_FL_HIDDEN = 1 << 1, + GENHD_FL_NO_PART = 1 << 2, ++ GENHD_FL_NVMEM = 1 << 3, + }; + + enum { diff --git a/target/linux/generic/pending-6.1/450-11-block-implement-NVMEM-provider.patch b/target/linux/generic/pending-6.1/450-11-block-implement-NVMEM-provider.patch new file mode 100644 index 000000000..e18b0c3a5 --- /dev/null +++ b/target/linux/generic/pending-6.1/450-11-block-implement-NVMEM-provider.patch @@ -0,0 +1,235 @@ +From b9936aa8a3775c2027f655d91a206d0e6e1c7ec0 Mon Sep 17 00:00:00 2001 +From: Daniel Golle +Date: Tue, 11 Jul 2023 00:17:31 +0100 +Subject: [PATCH 11/15] block: implement NVMEM provider + +On embedded devices using an eMMC it is common that one or more partitions +on the eMMC are used to store MAC addresses and Wi-Fi calibration EEPROM +data. Allow referencing the partition in device tree for the kernel and +Wi-Fi drivers accessing it via the NVMEM layer. + +Signed-off-by: Daniel Golle +--- + block/Kconfig | 9 +++ + block/Makefile | 1 + + block/blk-nvmem.c | 186 ++++++++++++++++++++++++++++++++++++++++++++++ + 3 files changed, 196 insertions(+) + create mode 100644 block/blk-nvmem.c + +--- a/block/Kconfig ++++ b/block/Kconfig +@@ -203,6 +203,15 @@ config BLK_INLINE_ENCRYPTION_FALLBACK + by falling back to the kernel crypto API when inline + encryption hardware is not present. + ++config BLK_NVMEM ++ bool "Block device NVMEM provider" ++ depends on OF ++ depends on NVMEM ++ help ++ Allow block devices (or partitions) to act as NVMEM prodivers, ++ typically used with eMMC to store MAC addresses or Wi-Fi ++ calibration data on embedded devices. ++ + source "block/partitions/Kconfig" + + config BLOCK_COMPAT +--- a/block/Makefile ++++ b/block/Makefile +@@ -35,6 +35,7 @@ obj-$(CONFIG_BLK_DEV_ZONED) += blk-zoned + obj-$(CONFIG_BLK_WBT) += blk-wbt.o + obj-$(CONFIG_BLK_DEBUG_FS) += blk-mq-debugfs.o + obj-$(CONFIG_BLK_DEBUG_FS_ZONED)+= blk-mq-debugfs-zoned.o ++obj-$(CONFIG_BLK_NVMEM) += blk-nvmem.o + obj-$(CONFIG_BLK_SED_OPAL) += sed-opal.o + obj-$(CONFIG_BLK_PM) += blk-pm.o + obj-$(CONFIG_BLK_INLINE_ENCRYPTION) += blk-crypto.o blk-crypto-profile.o \ +--- /dev/null ++++ b/block/blk-nvmem.c +@@ -0,0 +1,186 @@ ++// SPDX-License-Identifier: GPL-2.0-or-later ++/* ++ * block device NVMEM provider ++ * ++ * Copyright (c) 2023 Daniel Golle ++ * ++ * Useful on devices using a partition on an eMMC for MAC addresses or ++ * Wi-Fi calibration EEPROM data. ++ */ ++ ++#include "blk.h" ++#include ++#include ++#include ++#include ++ ++/* List of all NVMEM devices */ ++static LIST_HEAD(nvmem_devices); ++static DEFINE_MUTEX(devices_mutex); ++ ++struct blk_nvmem { ++ struct nvmem_device *nvmem; ++ struct block_device *bdev; ++ struct list_head list; ++}; ++ ++static int blk_nvmem_reg_read(void *priv, unsigned int from, ++ void *val, size_t bytes) ++{ ++ unsigned long offs = from & ~PAGE_MASK, to_read; ++ pgoff_t f_index = from >> PAGE_SHIFT; ++ struct address_space *mapping; ++ struct blk_nvmem *bnv = priv; ++ size_t bytes_left = bytes; ++ struct folio *folio; ++ void *p; ++ int ret; ++ ++ if (!bnv->bdev) ++ return -ENODEV; ++ ++ if (!bnv->bdev->bd_disk) ++ return -EINVAL; ++ ++ if (!bnv->bdev->bd_disk->fops) ++ return -EIO; ++ ++ if (!bnv->bdev->bd_disk->fops->open) ++ return -EIO; ++ ++ ret = bnv->bdev->bd_disk->fops->open(bnv->bdev, FMODE_READ); ++ if (ret) ++ return ret; ++ ++ mapping = bnv->bdev->bd_inode->i_mapping; ++ ++ while (bytes_left) { ++ folio = read_mapping_folio(mapping, f_index++, NULL); ++ if (IS_ERR(folio)) { ++ ret = PTR_ERR(folio); ++ goto err_release_bdev; ++ } ++ to_read = min_t(unsigned long, bytes_left, PAGE_SIZE - offs); ++ p = folio_address(folio) + offset_in_folio(folio, offs); ++ memcpy(val, p, to_read); ++ offs = 0; ++ bytes_left -= to_read; ++ val += to_read; ++ folio_put(folio); ++ } ++ ++err_release_bdev: ++ bnv->bdev->bd_disk->fops->release(bnv->bdev->bd_disk, FMODE_READ); ++ ++ return ret; ++} ++ ++static int blk_nvmem_register(struct device *dev, struct class_interface *iface) ++{ ++ struct device_node *np = dev_of_node(dev); ++ struct block_device *bdev = dev_to_bdev(dev); ++ struct nvmem_config config = {}; ++ struct blk_nvmem *bnv; ++ ++ /* skip devices which do not have a device tree node */ ++ if (!np) ++ return 0; ++ ++ /* skip devices without an nvmem layout defined */ ++ if (!of_get_child_by_name(np, "nvmem-layout")) ++ return 0; ++ ++ /* ++ * skip devices which don't have GENHD_FL_NVMEM set ++ * ++ * This flag is used for mtdblock and ubiblock devices because ++ * both, MTD and UBI already implement their own NVMEM provider. ++ * To avoid registering multiple NVMEM providers for the same ++ * device node, don't register the block NVMEM provider for them. ++ */ ++ if (!(bdev->bd_disk->flags & GENHD_FL_NVMEM)) ++ return 0; ++ ++ /* ++ * skip block device too large to be represented as NVMEM devices ++ * which are using an 'int' as address ++ */ ++ if (bdev_nr_bytes(bdev) > INT_MAX) ++ return -EFBIG; ++ ++ bnv = kzalloc(sizeof(struct blk_nvmem), GFP_KERNEL); ++ if (!bnv) ++ return -ENOMEM; ++ ++ config.id = NVMEM_DEVID_NONE; ++ config.dev = &bdev->bd_device; ++ config.name = dev_name(&bdev->bd_device); ++ config.owner = THIS_MODULE; ++ config.priv = bnv; ++ config.reg_read = blk_nvmem_reg_read; ++ config.size = bdev_nr_bytes(bdev); ++ config.word_size = 1; ++ config.stride = 1; ++ config.read_only = true; ++ config.root_only = true; ++ config.ignore_wp = true; ++ config.of_node = to_of_node(dev->fwnode); ++ ++ bnv->bdev = bdev; ++ bnv->nvmem = nvmem_register(&config); ++ if (IS_ERR(bnv->nvmem)) { ++ dev_err_probe(&bdev->bd_device, PTR_ERR(bnv->nvmem), ++ "Failed to register NVMEM device\n"); ++ ++ kfree(bnv); ++ return PTR_ERR(bnv->nvmem); ++ } ++ ++ mutex_lock(&devices_mutex); ++ list_add_tail(&bnv->list, &nvmem_devices); ++ mutex_unlock(&devices_mutex); ++ ++ return 0; ++} ++ ++static void blk_nvmem_unregister(struct device *dev, struct class_interface *iface) ++{ ++ struct block_device *bdev = dev_to_bdev(dev); ++ struct blk_nvmem *bnv_c, *bnv = NULL; ++ ++ mutex_lock(&devices_mutex); ++ list_for_each_entry(bnv_c, &nvmem_devices, list) { ++ if (bnv_c->bdev == bdev) { ++ bnv = bnv_c; ++ break; ++ } ++ } ++ ++ if (!bnv) { ++ mutex_unlock(&devices_mutex); ++ return; ++ } ++ ++ list_del(&bnv->list); ++ mutex_unlock(&devices_mutex); ++ nvmem_unregister(bnv->nvmem); ++ kfree(bnv); ++} ++ ++static struct class_interface blk_nvmem_bus_interface __refdata = { ++ .class = &block_class, ++ .add_dev = &blk_nvmem_register, ++ .remove_dev = &blk_nvmem_unregister, ++}; ++ ++static int __init blk_nvmem_init(void) ++{ ++ int ret; ++ ++ ret = class_interface_register(&blk_nvmem_bus_interface); ++ if (ret) ++ return ret; ++ ++ return 0; ++} ++device_initcall(blk_nvmem_init); diff --git a/target/linux/generic/pending-6.1/450-12-dt-bindings-mmc-mmc-card-add-block-device-nodes.patch b/target/linux/generic/pending-6.1/450-12-dt-bindings-mmc-mmc-card-add-block-device-nodes.patch new file mode 100644 index 000000000..77c9bf91a --- /dev/null +++ b/target/linux/generic/pending-6.1/450-12-dt-bindings-mmc-mmc-card-add-block-device-nodes.patch @@ -0,0 +1,74 @@ +From 86864bf8f40e84dc881c197ef470a88668329dbf Mon Sep 17 00:00:00 2001 +From: Daniel Golle +Date: Mon, 7 Aug 2023 21:21:45 +0100 +Subject: [PATCH 12/15] dt-bindings: mmc: mmc-card: add block device nodes + +Add nodes representing the block devices exposed by an MMC device +including an example involving nvmem-cells. + +Signed-off-by: Daniel Golle +--- + .../devicetree/bindings/mmc/mmc-card.yaml | 45 +++++++++++++++++++ + 1 file changed, 45 insertions(+) + +--- a/Documentation/devicetree/bindings/mmc/mmc-card.yaml ++++ b/Documentation/devicetree/bindings/mmc/mmc-card.yaml +@@ -26,6 +26,18 @@ properties: + Use this to indicate that the mmc-card has a broken hpi + implementation, and that hpi should not be used. + ++ block: ++ $ref: /schemas/block/block-device.yaml# ++ description: ++ Represents the block storage provided by an SD card or the ++ main hardware partition of an eMMC. ++ ++patternProperties: ++ '^boot[0-9]+': ++ $ref: /schemas/block/block-device.yaml# ++ description: ++ Represents a boot hardware partition on an eMMC. ++ + required: + - compatible + - reg +@@ -42,6 +54,39 @@ examples: + compatible = "mmc-card"; + reg = <0>; + broken-hpi; ++ ++ block { ++ partitions { ++ cal_data: block-partition-rf { ++ partnum = <3>; ++ partname = "rf"; ++ ++ nvmem-layout { ++ compatible = "fixed-layout"; ++ #address-cells = <1>; ++ #size-cells = <1>; ++ ++ eeprom@0 { ++ reg = <0x0 0x1000>; ++ }; ++ }; ++ }; ++ }; ++ }; ++ ++ boot1 { ++ nvmem-layout { ++ compatible = "fixed-layout"; ++ #address-cells = <1>; ++ #size-cells = <1>; ++ ++ macaddr: macaddr@a { ++ compatible = "mac-base"; ++ reg = <0xa 0x6>; ++ #nvmem-cell-cells = <1>; ++ }; ++ }; ++ }; + }; + }; + diff --git a/target/linux/generic/pending-6.1/450-13-mmc-core-set-card-fwnode_handle.patch b/target/linux/generic/pending-6.1/450-13-mmc-core-set-card-fwnode_handle.patch new file mode 100644 index 000000000..fada28043 --- /dev/null +++ b/target/linux/generic/pending-6.1/450-13-mmc-core-set-card-fwnode_handle.patch @@ -0,0 +1,23 @@ +From 644942a31719de674e2aa68f83d66bd8ae7e4fb7 Mon Sep 17 00:00:00 2001 +From: Daniel Golle +Date: Thu, 13 Jul 2023 04:12:21 +0100 +Subject: [PATCH 13/15] mmc: core: set card fwnode_handle + +Set fwnode in case it isn't set yet and of_node is present. + +Signed-off-by: Daniel Golle +--- + drivers/mmc/core/bus.c | 2 ++ + 1 file changed, 2 insertions(+) + +--- a/drivers/mmc/core/bus.c ++++ b/drivers/mmc/core/bus.c +@@ -363,6 +363,8 @@ int mmc_add_card(struct mmc_card *card) + mmc_add_card_debugfs(card); + #endif + card->dev.of_node = mmc_of_find_child_device(card->host, 0); ++ if (card->dev.of_node && !card->dev.fwnode) ++ card->dev.fwnode = &card->dev.of_node->fwnode; + + device_enable_async_suspend(&card->dev); + diff --git a/target/linux/generic/pending-6.1/450-14-mmc-block-set-fwnode-of-disk-devices.patch b/target/linux/generic/pending-6.1/450-14-mmc-block-set-fwnode-of-disk-devices.patch new file mode 100644 index 000000000..386282459 --- /dev/null +++ b/target/linux/generic/pending-6.1/450-14-mmc-block-set-fwnode-of-disk-devices.patch @@ -0,0 +1,39 @@ +From d9143f86330dd038fc48878558dd287ceee5d3d4 Mon Sep 17 00:00:00 2001 +From: Daniel Golle +Date: Thu, 13 Jul 2023 04:13:04 +0100 +Subject: [PATCH 14/15] mmc: block: set fwnode of disk devices + +Set fwnode of disk devices to 'block', 'boot0' and 'boot1' subnodes of +the mmc-card. This is done in preparation for having the eMMC act as +NVMEM provider. + +Signed-off-by: Daniel Golle +--- + drivers/mmc/core/block.c | 8 ++++++++ + 1 file changed, 8 insertions(+) + +--- a/drivers/mmc/core/block.c ++++ b/drivers/mmc/core/block.c +@@ -2486,6 +2486,8 @@ static struct mmc_blk_data *mmc_blk_allo + int area_type, + unsigned int part_type) + { ++ struct fwnode_handle *fwnode; ++ struct device *ddev; + struct mmc_blk_data *md; + int devidx, ret; + char cap_str[10]; +@@ -2582,6 +2584,13 @@ static struct mmc_blk_data *mmc_blk_allo + + blk_queue_write_cache(md->queue.queue, cache_enabled, fua_enabled); + ++ ddev = disk_to_dev(md->disk); ++ fwnode = device_get_named_child_node(subname ? md->parent->parent : ++ md->parent, ++ subname ? subname : "block"); ++ if (fwnode) ++ device_set_node(ddev, fwnode); ++ + string_get_size((u64)size, 512, STRING_UNITS_2, + cap_str, sizeof(cap_str)); + pr_info("%s: %s %s %s %s\n", diff --git a/target/linux/generic/pending-6.1/450-15-mmc-block-set-GENHD_FL_NVMEM.patch b/target/linux/generic/pending-6.1/450-15-mmc-block-set-GENHD_FL_NVMEM.patch new file mode 100644 index 000000000..41c257cde --- /dev/null +++ b/target/linux/generic/pending-6.1/450-15-mmc-block-set-GENHD_FL_NVMEM.patch @@ -0,0 +1,22 @@ +From 322035ab2b0113d98b6c0ea788d971e0df2952a4 Mon Sep 17 00:00:00 2001 +From: Daniel Golle +Date: Thu, 20 Jul 2023 17:36:44 +0100 +Subject: [PATCH 15/15] mmc: block: set GENHD_FL_NVMEM + +Set flag to consider MMC block devices as NVMEM providers. + +Signed-off-by: Daniel Golle +--- + drivers/mmc/core/block.c | 1 + + 1 file changed, 1 insertion(+) + +--- a/drivers/mmc/core/block.c ++++ b/drivers/mmc/core/block.c +@@ -2540,6 +2540,7 @@ static struct mmc_blk_data *mmc_blk_allo + md->disk->major = MMC_BLOCK_MAJOR; + md->disk->minors = perdev_minors; + md->disk->first_minor = devidx * perdev_minors; ++ md->disk->flags = GENHD_FL_NVMEM; + md->disk->fops = &mmc_bdops; + md->disk->private_data = md; + md->parent = parent; diff --git a/target/linux/generic/pending-6.1/479-mtd-spi-nor-add-xtx-xt25f128b.patch b/target/linux/generic/pending-6.1/479-mtd-spi-nor-add-xtx-xt25f128b.patch index d2a9fb343..371f1a727 100644 --- a/target/linux/generic/pending-6.1/479-mtd-spi-nor-add-xtx-xt25f128b.patch +++ b/target/linux/generic/pending-6.1/479-mtd-spi-nor-add-xtx-xt25f128b.patch @@ -68,7 +68,7 @@ Signed-off-by: Felix Fietkau + &spi_nor_xtx, }; - static const struct flash_info *spi_nor_match_id(struct spi_nor *nor, + static const struct flash_info spi_nor_generic_flash = { --- a/drivers/mtd/spi-nor/core.h +++ b/drivers/mtd/spi-nor/core.h @@ -633,6 +633,7 @@ extern const struct spi_nor_manufacturer diff --git a/target/linux/generic/pending-6.1/490-ubi-auto-attach-mtd-device-named-ubi-or-data-on-boot.patch b/target/linux/generic/pending-6.1/490-ubi-auto-attach-mtd-device-named-ubi-or-data-on-boot.patch index 765eecd9d..2eccb9d3b 100644 --- a/target/linux/generic/pending-6.1/490-ubi-auto-attach-mtd-device-named-ubi-or-data-on-boot.patch +++ b/target/linux/generic/pending-6.1/490-ubi-auto-attach-mtd-device-named-ubi-or-data-on-boot.patch @@ -8,10 +8,11 @@ Signed-off-by: Daniel Golle --- a/drivers/mtd/ubi/build.c +++ b/drivers/mtd/ubi/build.c -@@ -1212,6 +1212,73 @@ static struct mtd_info * __init open_mtd - return mtd; - } +@@ -1263,6 +1263,74 @@ static struct mtd_notifier ubi_mtd_notif + .remove = ubi_notify_remove, + }; ++ +/* + * This function tries attaching mtd partitions named either "ubi" or "data" + * during boot. @@ -79,10 +80,10 @@ Signed-off-by: Daniel Golle + put_mtd_device(mtd); +} + - static int __init ubi_init(void) + static int __init ubi_init_attach(void) { int err, i, k; -@@ -1296,6 +1363,12 @@ static int __init ubi_init(void) +@@ -1313,6 +1381,12 @@ static int __init ubi_init_attach(void) } } @@ -92,6 +93,6 @@ Signed-off-by: Daniel Golle + !ubi_is_module() && !mtd_devs) + ubi_auto_attach(); + - err = ubiblock_init(); - if (err) { - pr_err("UBI error: block: cannot initialize, error %d\n", err); + return 0; + + out_detach: diff --git a/target/linux/generic/pending-6.1/491-ubi-auto-create-ubiblock-device-for-rootfs.patch b/target/linux/generic/pending-6.1/491-ubi-auto-create-ubiblock-device-for-rootfs.patch index 17e8d8bed..a43da2a57 100644 --- a/target/linux/generic/pending-6.1/491-ubi-auto-create-ubiblock-device-for-rootfs.patch +++ b/target/linux/generic/pending-6.1/491-ubi-auto-create-ubiblock-device-for-rootfs.patch @@ -8,8 +8,8 @@ Signed-off-by: Daniel Golle --- a/drivers/mtd/ubi/block.c +++ b/drivers/mtd/ubi/block.c -@@ -653,6 +653,47 @@ static void __init ubiblock_create_from_ - } +@@ -644,10 +644,47 @@ match_volume_desc(struct ubi_volume_info + return true; } +#define UBIFS_NODE_MAGIC 0x06101831 @@ -24,46 +24,54 @@ Signed-off-by: Daniel Golle + return magic == UBIFS_NODE_MAGIC; +} + -+static void __init ubiblock_create_auto_rootfs(void) ++static void __init ubiblock_create_auto_rootfs(struct ubi_volume_info *vi) +{ -+ int ubi_num, ret, is_ubifs; ++ int ret, is_ubifs; + struct ubi_volume_desc *desc; -+ struct ubi_volume_info vi; + -+ for (ubi_num = 0; ubi_num < UBI_MAX_DEVICES; ubi_num++) { -+ desc = ubi_open_volume_nm(ubi_num, "rootfs", UBI_READONLY); -+ if (IS_ERR(desc)) -+ desc = ubi_open_volume_nm(ubi_num, "fit", UBI_READONLY);; ++ if (strcmp(vi->name, "rootfs") && ++ strcmp(vi->name, "fit")) ++ return; + -+ if (IS_ERR(desc)) -+ continue; ++ desc = ubi_open_volume(vi->ubi_num, vi->vol_id, UBI_READONLY); ++ if (IS_ERR(desc)) ++ return; + -+ ubi_get_volume_info(desc, &vi); -+ is_ubifs = ubi_vol_is_ubifs(desc); -+ ubi_close_volume(desc); -+ if (is_ubifs) -+ break; ++ is_ubifs = ubi_vol_is_ubifs(desc); ++ ubi_close_volume(desc); ++ if (is_ubifs) ++ return; + -+ ret = ubiblock_create(&vi); -+ if (ret) -+ pr_err("UBI error: block: can't add '%s' volume, err=%d\n", -+ vi.name, ret); -+ /* always break if we get here */ -+ break; -+ } ++ ret = ubiblock_create(vi); ++ if (ret) ++ pr_err("UBI error: block: can't add '%s' volume, err=%d\n", ++ vi->name, ret); +} + - static void ubiblock_remove_all(void) + static void + ubiblock_create_from_param(struct ubi_volume_info *vi) { - struct ubiblock *next; -@@ -685,6 +726,10 @@ int __init ubiblock_init(void) - */ - ubiblock_create_from_param(); + int i, ret = 0; ++ bool got_param = false; + struct ubiblock_param *p; -+ /* auto-attach "rootfs" volume if existing and non-ubifs */ -+ if (IS_ENABLED(CONFIG_MTD_ROOTFS_ROOT_DEV)) -+ ubiblock_create_auto_rootfs(); -+ /* - * Block devices are only created upon user requests, so we ignore - * existing volumes. +@@ -660,6 +697,7 @@ ubiblock_create_from_param(struct ubi_vo + if (!match_volume_desc(vi, p->name, p->ubi_num, p->vol_id)) + continue; + ++ got_param = true; + ret = ubiblock_create(vi); + if (ret) { + pr_err( +@@ -668,6 +706,10 @@ ubiblock_create_from_param(struct ubi_vo + } + break; + } ++ ++ /* auto-attach "rootfs" volume if existing and non-ubifs */ ++ if (!got_param && IS_ENABLED(CONFIG_MTD_ROOTFS_ROOT_DEV)) ++ ubiblock_create_auto_rootfs(vi); + } + + static int ubiblock_notify(struct notifier_block *nb, diff --git a/target/linux/generic/pending-6.1/493-ubi-set-ROOT_DEV-to-ubiblock-rootfs-if-unset.patch b/target/linux/generic/pending-6.1/493-ubi-set-ROOT_DEV-to-ubiblock-rootfs-if-unset.patch index e3493ef19..5357c7e15 100644 --- a/target/linux/generic/pending-6.1/493-ubi-set-ROOT_DEV-to-ubiblock-rootfs-if-unset.patch +++ b/target/linux/generic/pending-6.1/493-ubi-set-ROOT_DEV-to-ubiblock-rootfs-if-unset.patch @@ -8,7 +8,7 @@ Signed-off-by: Daniel Golle --- a/drivers/mtd/ubi/block.c +++ b/drivers/mtd/ubi/block.c -@@ -42,6 +42,7 @@ +@@ -43,6 +43,7 @@ #include #include #include @@ -16,7 +16,7 @@ Signed-off-by: Daniel Golle #include "ubi-media.h" #include "ubi.h" -@@ -459,6 +460,15 @@ int ubiblock_create(struct ubi_volume_in +@@ -460,6 +461,15 @@ int ubiblock_create(struct ubi_volume_in dev_info(disk_to_dev(dev->gd), "created from ubi%d:%d(%s)", dev->ubi_num, dev->vol_id, vi->name); mutex_unlock(&devices_mutex); diff --git a/target/linux/generic/pending-6.1/494-mtd-ubi-add-EOF-marker-support.patch b/target/linux/generic/pending-6.1/494-mtd-ubi-add-EOF-marker-support.patch index 413431755..fc4814622 100644 --- a/target/linux/generic/pending-6.1/494-mtd-ubi-add-EOF-marker-support.patch +++ b/target/linux/generic/pending-6.1/494-mtd-ubi-add-EOF-marker-support.patch @@ -50,7 +50,7 @@ Signed-off-by: Gabor Juhos break; --- a/drivers/mtd/ubi/ubi.h +++ b/drivers/mtd/ubi/ubi.h -@@ -778,6 +778,7 @@ struct ubi_attach_info { +@@ -780,6 +780,7 @@ struct ubi_attach_info { int mean_ec; uint64_t ec_sum; int ec_count; diff --git a/target/linux/generic/pending-6.1/510-block-add-uImage.FIT-subimage-block-driver.patch b/target/linux/generic/pending-6.1/510-block-add-uImage.FIT-subimage-block-driver.patch index 7ee66b318..f88136541 100644 --- a/target/linux/generic/pending-6.1/510-block-add-uImage.FIT-subimage-block-driver.patch +++ b/target/linux/generic/pending-6.1/510-block-add-uImage.FIT-subimage-block-driver.patch @@ -36,7 +36,7 @@ Signed-off-by: Daniel Golle --- a/MAINTAINERS +++ b/MAINTAINERS -@@ -21052,6 +21052,12 @@ F: Documentation/filesystems/ubifs-authe +@@ -21059,6 +21059,12 @@ F: Documentation/filesystems/ubifs-authe F: Documentation/filesystems/ubifs.rst F: fs/ubifs/ @@ -81,7 +81,7 @@ Signed-off-by: Daniel Golle swim_mod-y := swim.o swim_asm.o --- /dev/null +++ b/drivers/block/fitblk.c -@@ -0,0 +1,635 @@ +@@ -0,0 +1,636 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * uImage.FIT virtual block device driver. @@ -454,7 +454,8 @@ Signed-off-by: Daniel Golle + bytes_left = size; + fit_c = fit; + while (bytes_left > 0) { -+ bytes_to_copy = min(bytes_left, folio_size(folio) - offset_in_folio(folio, 0)); ++ bytes_to_copy = min_t(size_t, bytes_left, ++ folio_size(folio) - offset_in_folio(folio, 0)); + memcpy(fit_c, pre_fit, bytes_to_copy); + fit_c += bytes_to_copy; + bytes_left -= bytes_to_copy; diff --git a/target/linux/generic/pending-6.1/530-jffs2_make_lzma_available.patch b/target/linux/generic/pending-6.1/530-jffs2_make_lzma_available.patch index f17216c52..27a673399 100644 --- a/target/linux/generic/pending-6.1/530-jffs2_make_lzma_available.patch +++ b/target/linux/generic/pending-6.1/530-jffs2_make_lzma_available.patch @@ -351,7 +351,7 @@ Signed-off-by: Alexandros C. Couloumbis + LZMA_FREE(address); +} + -+static ISzAlloc lzma_alloc = {p_lzma_malloc, p_lzma_free}; ++static ISzAlloc lzma_alloc = { .Alloc = p_lzma_malloc, .Free = p_lzma_free }; + +#endif --- /dev/null diff --git a/target/linux/generic/pending-6.1/701-netfilter-nf_tables-ignore-EOPNOTSUPP-on-flowtable-d.patch b/target/linux/generic/pending-6.1/701-netfilter-nf_tables-ignore-EOPNOTSUPP-on-flowtable-d.patch new file mode 100644 index 000000000..9f8c3d6ff --- /dev/null +++ b/target/linux/generic/pending-6.1/701-netfilter-nf_tables-ignore-EOPNOTSUPP-on-flowtable-d.patch @@ -0,0 +1,29 @@ +From: Felix Fietkau +Date: Thu, 31 Aug 2023 21:48:38 +0200 +Subject: [PATCH] netfilter: nf_tables: ignore -EOPNOTSUPP on flowtable device + offload setup + +On many embedded devices, it is common to configure flowtable offloading for +a mix of different devices, some of which have hardware offload support and +some of which don't. +The current code limits the ability of user space to properly set up such a +configuration by only allowing adding devices with hardware offload support to +a offload-enabled flowtable. +Given that offload-enabled flowtables also imply fallback to pure software +offloading, this limitation makes little sense. +Fix it by not bailing out when the offload setup returns -EOPNOTSUPP + +Signed-off-by: Felix Fietkau +--- + +--- a/net/netfilter/nf_tables_api.c ++++ b/net/netfilter/nf_tables_api.c +@@ -7959,7 +7959,7 @@ static int nft_register_flowtable_net_ho + err = flowtable->data.type->setup(&flowtable->data, + hook->ops.dev, + FLOW_BLOCK_BIND); +- if (err < 0) ++ if (err < 0 && err != -EOPNOTSUPP) + goto err_unregister_net_hooks; + + err = nf_register_net_hook(net, &hook->ops); diff --git a/target/linux/generic/pending-6.1/702-net-ethernet-mtk_eth_soc-enable-threaded-NAPI.patch b/target/linux/generic/pending-6.1/702-net-ethernet-mtk_eth_soc-enable-threaded-NAPI.patch index 77a94dae8..90b60def6 100644 --- a/target/linux/generic/pending-6.1/702-net-ethernet-mtk_eth_soc-enable-threaded-NAPI.patch +++ b/target/linux/generic/pending-6.1/702-net-ethernet-mtk_eth_soc-enable-threaded-NAPI.patch @@ -10,7 +10,7 @@ Signed-off-by: Felix Fietkau --- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c +++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c -@@ -4939,6 +4939,8 @@ static int mtk_probe(struct platform_dev +@@ -4940,6 +4940,8 @@ static int mtk_probe(struct platform_dev * for NAPI to work */ init_dummy_netdev(ð->dummy_dev); diff --git a/target/linux/generic/pending-6.1/703-phy-add-detach-callback-to-struct-phy_driver.patch b/target/linux/generic/pending-6.1/703-phy-add-detach-callback-to-struct-phy_driver.patch index 8d2803158..d50bc9cd4 100644 --- a/target/linux/generic/pending-6.1/703-phy-add-detach-callback-to-struct-phy_driver.patch +++ b/target/linux/generic/pending-6.1/703-phy-add-detach-callback-to-struct-phy_driver.patch @@ -11,7 +11,7 @@ Signed-off-by: Gabor Juhos --- a/drivers/net/phy/phy_device.c +++ b/drivers/net/phy/phy_device.c -@@ -1753,6 +1753,9 @@ void phy_detach(struct phy_device *phyde +@@ -1852,6 +1852,9 @@ void phy_detach(struct phy_device *phyde struct module *ndev_owner = NULL; struct mii_bus *bus; @@ -23,7 +23,7 @@ Signed-off-by: Gabor Juhos sysfs_remove_link(&dev->dev.kobj, "phydev"); --- a/include/linux/phy.h +++ b/include/linux/phy.h -@@ -878,6 +878,12 @@ struct phy_driver { +@@ -903,6 +903,12 @@ struct phy_driver { /** @handle_interrupt: Override default interrupt handling */ irqreturn_t (*handle_interrupt)(struct phy_device *phydev); diff --git a/target/linux/generic/pending-6.1/711-01-net-dsa-qca8k-implement-lag_fdb_add-del-ops.patch b/target/linux/generic/pending-6.1/711-01-net-dsa-qca8k-implement-lag_fdb_add-del-ops.patch new file mode 100644 index 000000000..629b14157 --- /dev/null +++ b/target/linux/generic/pending-6.1/711-01-net-dsa-qca8k-implement-lag_fdb_add-del-ops.patch @@ -0,0 +1,86 @@ +From 3b4329230db8750bea7a56ef07f07cbbf5fc6c5a Mon Sep 17 00:00:00 2001 +From: Christian Marangi +Date: Tue, 4 Jul 2023 22:50:12 +0200 +Subject: [PATCH 19/20] net: dsa: qca8k: implement lag_fdb_add/del ops + +Implement lag_fdb_add/del ops to correctly support using LAG interface. +Qca8k switch supports declaring fdb entry for link aggregation by simply +setting the DES_PORT bits to all the LAG member. + +Signed-off-by: Christian Marangi +--- + drivers/net/dsa/qca/qca8k-8xxx.c | 2 ++ + drivers/net/dsa/qca/qca8k-common.c | 48 ++++++++++++++++++++++++++++++ + drivers/net/dsa/qca/qca8k.h | 6 ++++ + 3 files changed, 56 insertions(+) + +--- a/drivers/net/dsa/qca/qca8k-8xxx.c ++++ b/drivers/net/dsa/qca/qca8k-8xxx.c +@@ -2015,6 +2015,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, ++ .lag_fdb_add = qca8k_lag_fdb_add, ++ .lag_fdb_del = qca8k_lag_fdb_del, + .port_mdb_add = qca8k_port_mdb_add, + .port_mdb_del = qca8k_port_mdb_del, + .port_mirror_add = qca8k_port_mirror_add, +--- a/drivers/net/dsa/qca/qca8k-common.c ++++ b/drivers/net/dsa/qca/qca8k-common.c +@@ -1215,6 +1215,42 @@ int qca8k_port_lag_leave(struct dsa_swit + return qca8k_lag_refresh_portmap(ds, port, lag, true); + } + ++int qca8k_lag_fdb_add(struct dsa_switch *ds, struct dsa_lag lag, ++ const unsigned char *addr, u16 vid, ++ struct dsa_db db) ++{ ++ struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv; ++ struct dsa_port *dp; ++ u16 port_mask = 0; ++ ++ /* Set the vid to the port vlan id if no vid is set */ ++ if (!vid) ++ vid = QCA8K_PORT_VID_DEF; ++ ++ dsa_lag_foreach_port(dp, ds->dst, &lag) ++ port_mask |= BIT(dp->index); ++ ++ return qca8k_port_fdb_insert(priv, addr, port_mask, vid); ++} ++ ++int qca8k_lag_fdb_del(struct dsa_switch *ds, struct dsa_lag lag, ++ const unsigned char *addr, u16 vid, ++ struct dsa_db db) ++{ ++ struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv; ++ struct dsa_port *dp; ++ u16 port_mask = 0; ++ ++ /* Set the vid to the port vlan id if no vid is set */ ++ if (!vid) ++ vid = QCA8K_PORT_VID_DEF; ++ ++ dsa_lag_foreach_port(dp, ds->dst, &lag) ++ port_mask |= BIT(dp->index); ++ ++ return qca8k_fdb_del(priv, addr, port_mask, vid); ++} ++ + int qca8k_read_switch_id(struct qca8k_priv *priv) + { + u32 val; +--- a/drivers/net/dsa/qca/qca8k.h ++++ b/drivers/net/dsa/qca/qca8k.h +@@ -590,5 +590,11 @@ int qca8k_port_lag_join(struct dsa_switc + struct netlink_ext_ack *extack); + int qca8k_port_lag_leave(struct dsa_switch *ds, int port, + struct dsa_lag lag); ++int qca8k_lag_fdb_add(struct dsa_switch *ds, struct dsa_lag lag, ++ const unsigned char *addr, u16 vid, ++ struct dsa_db db); ++int qca8k_lag_fdb_del(struct dsa_switch *ds, struct dsa_lag lag, ++ const unsigned char *addr, u16 vid, ++ struct dsa_db db); + + #endif /* __QCA8K_H */ diff --git a/target/linux/generic/pending-6.1/711-02-net-dsa-qca8k-enable-flooding-to-both-CPU-port.patch b/target/linux/generic/pending-6.1/711-02-net-dsa-qca8k-enable-flooding-to-both-CPU-port.patch new file mode 100644 index 000000000..24243468a --- /dev/null +++ b/target/linux/generic/pending-6.1/711-02-net-dsa-qca8k-enable-flooding-to-both-CPU-port.patch @@ -0,0 +1,37 @@ +From b954d61d9ecfa64450fc178586719dc2a95b92a7 Mon Sep 17 00:00:00 2001 +From: Christian Marangi +Date: Tue, 20 Jun 2023 21:48:24 +0200 +Subject: [PATCH 3/4] net: dsa: qca8k: enable flooding to both CPU port + +To permit a multi-CPU setup, flood all unknown frames to all CPU ports. +Each CPU port should have correct LOOKUP MEMBER configuration to +prevent receiving duplicate packets from user ports. + +Signed-off-by: Christian Marangi +--- + drivers/net/dsa/qca/qca8k-8xxx.c | 13 +++++-------- + 1 file changed, 5 insertions(+), 8 deletions(-) + +--- a/drivers/net/dsa/qca/qca8k-8xxx.c ++++ b/drivers/net/dsa/qca/qca8k-8xxx.c +@@ -1904,15 +1904,12 @@ qca8k_setup(struct dsa_switch *ds) + } + } + +- /* 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 +- */ ++ /* Forward all unknown frames to CPU port for Linux processing */ + ret = qca8k_write(priv, QCA8K_REG_GLOBAL_FW_CTRL1, +- FIELD_PREP(QCA8K_GLOBAL_FW_CTRL1_IGMP_DP_MASK, BIT(cpu_port)) | +- FIELD_PREP(QCA8K_GLOBAL_FW_CTRL1_BC_DP_MASK, BIT(cpu_port)) | +- FIELD_PREP(QCA8K_GLOBAL_FW_CTRL1_MC_DP_MASK, BIT(cpu_port)) | +- FIELD_PREP(QCA8K_GLOBAL_FW_CTRL1_UC_DP_MASK, BIT(cpu_port))); ++ FIELD_PREP(QCA8K_GLOBAL_FW_CTRL1_IGMP_DP_MASK, dsa_cpu_ports(ds)) | ++ FIELD_PREP(QCA8K_GLOBAL_FW_CTRL1_BC_DP_MASK, dsa_cpu_ports(ds)) | ++ FIELD_PREP(QCA8K_GLOBAL_FW_CTRL1_MC_DP_MASK, dsa_cpu_ports(ds)) | ++ FIELD_PREP(QCA8K_GLOBAL_FW_CTRL1_UC_DP_MASK, dsa_cpu_ports(ds))); + if (ret) + return ret; + diff --git a/target/linux/generic/pending-6.1/711-03-net-dsa-qca8k-add-support-for-port_change_master.patch b/target/linux/generic/pending-6.1/711-03-net-dsa-qca8k-add-support-for-port_change_master.patch new file mode 100644 index 000000000..8a58e0f76 --- /dev/null +++ b/target/linux/generic/pending-6.1/711-03-net-dsa-qca8k-add-support-for-port_change_master.patch @@ -0,0 +1,158 @@ +From b2d6ebf2f92f8695c83fa6979f4ab579c588df76 Mon Sep 17 00:00:00 2001 +From: Christian Marangi +Date: Tue, 20 Jun 2023 07:57:38 +0200 +Subject: [PATCH 4/4] net: dsa: qca8k: add support for port_change_master + +Add support for port_change_master to permit assigning an alternative +CPU port if the switch have both CPU port connected or create a LAG on +both CPU port and assign the LAG as DSA master. + +On port change master request, we check if the master is a LAG. +With LAG we compose the cpu_port_mask with the CPU port in the LAG, if +master is a simple dsa_port, we derive the index. + +Finally we apply the new cpu_port_mask to the LOOKUP MEMBER to permit +the port to receive packet by the new CPU port setup for the port and we +refresh the CPU ports LOOKUP MEMBER configuration to reflect the new +user port state. + +port_lag_join/leave is updated to refresh the user ports if we detect +that the LAG is a DSA master and we have user port using it as a master. + +Signed-off-by: Christian Marangi +--- + drivers/net/dsa/qca/qca8k-8xxx.c | 116 ++++++++++++++++++++++++++++++- + 1 file changed, 114 insertions(+), 2 deletions(-) + +--- a/drivers/net/dsa/qca/qca8k-8xxx.c ++++ b/drivers/net/dsa/qca/qca8k-8xxx.c +@@ -1741,6 +1741,117 @@ qca8k_get_tag_protocol(struct dsa_switch + return DSA_TAG_PROTO_QCA; + } + ++static int qca8k_port_change_master(struct dsa_switch *ds, int port, ++ struct net_device *master, ++ struct netlink_ext_ack *extack) ++{ ++ struct dsa_switch_tree *dst = ds->dst; ++ struct qca8k_priv *priv = ds->priv; ++ u8 cpu_port_mask = 0; ++ struct dsa_port *dp; ++ u32 val; ++ int ret; ++ ++ /* With LAG of CPU port, compose the mask for port LOOKUP MEMBER */ ++ if (netif_is_lag_master(master)) { ++ struct dsa_lag *lag; ++ int id; ++ ++ id = dsa_lag_id(dst, master); ++ lag = dsa_lag_by_id(dst, id); ++ ++ dsa_lag_foreach_port(dp, dst, lag) ++ if (dsa_port_is_cpu(dp)) ++ cpu_port_mask |= BIT(dp->index); ++ } else { ++ dp = master->dsa_ptr; ++ cpu_port_mask |= BIT(dp->index); ++ } ++ ++ /* Connect port to new cpu port */ ++ ret = regmap_read(priv->regmap, QCA8K_PORT_LOOKUP_CTRL(port), &val); ++ if (ret) ++ return ret; ++ ++ /* Reset connected CPU port in port LOOKUP MEMBER */ ++ val &= ~dsa_cpu_ports(ds); ++ /* Assign the new CPU port in port LOOKUP MEMBER */ ++ val |= cpu_port_mask; ++ ++ ret = regmap_update_bits(priv->regmap, QCA8K_PORT_LOOKUP_CTRL(port), ++ QCA8K_PORT_LOOKUP_MEMBER, ++ val); ++ if (ret) ++ return ret; ++ ++ /* Refresh CPU port LOOKUP MEMBER with new port */ ++ dsa_tree_for_each_cpu_port(dp, ds->dst) { ++ u32 reg = QCA8K_PORT_LOOKUP_CTRL(dp->index); ++ ++ /* If CPU port in mask assign port, else remove port */ ++ if (BIT(dp->index) & cpu_port_mask) ++ ret = regmap_set_bits(priv->regmap, reg, BIT(port)); ++ else ++ ret = regmap_clear_bits(priv->regmap, reg, BIT(port)); ++ ++ if (ret) ++ return ret; ++ } ++ ++ return 0; ++} ++ ++static int qca8k_port_lag_refresh_user_ports(struct dsa_switch *ds, ++ struct dsa_lag lag) ++{ ++ struct net_device *lag_dev = lag.dev; ++ struct dsa_port *dp; ++ int ret; ++ ++ /* Ignore if LAG is not a DSA master */ ++ if (!netif_is_lag_master(lag_dev)) ++ return 0; ++ ++ dsa_switch_for_each_user_port(dp, ds) { ++ /* Skip if assigned master is not the LAG */ ++ if (dsa_port_to_master(dp) != lag_dev) ++ continue; ++ ++ ret = qca8k_port_change_master(ds, dp->index, ++ lag_dev, NULL); ++ if (ret) ++ return ret; ++ } ++ ++ return 0; ++} ++ ++static int qca8xxx_port_lag_join(struct dsa_switch *ds, int port, ++ struct dsa_lag lag, ++ struct netdev_lag_upper_info *info, ++ struct netlink_ext_ack *extack) ++{ ++ int ret; ++ ++ ret = qca8k_port_lag_join(ds, port, lag, info, extack); ++ if (ret) ++ return ret; ++ ++ return qca8k_port_lag_refresh_user_ports(ds, lag); ++} ++ ++static int qca8xxx_port_lag_leave(struct dsa_switch *ds, int port, ++ struct dsa_lag lag) ++{ ++ int ret; ++ ++ ret = qca8k_port_lag_leave(ds, port, lag); ++ if (ret) ++ return ret; ++ ++ return qca8k_port_lag_refresh_user_ports(ds, lag); ++} ++ + static void + qca8k_master_change(struct dsa_switch *ds, const struct net_device *master, + bool operational) +@@ -2027,8 +2138,9 @@ static const struct dsa_switch_ops qca8k + .phylink_mac_link_down = qca8k_phylink_mac_link_down, + .phylink_mac_link_up = qca8k_phylink_mac_link_up, + .get_phy_flags = qca8k_get_phy_flags, +- .port_lag_join = qca8k_port_lag_join, +- .port_lag_leave = qca8k_port_lag_leave, ++ .port_lag_join = qca8xxx_port_lag_join, ++ .port_lag_leave = qca8xxx_port_lag_leave, ++ .port_change_master = qca8k_port_change_master, + .master_state_change = qca8k_master_change, + .connect_tag_protocol = qca8k_connect_tag_protocol, + }; diff --git a/target/linux/generic/pending-6.1/712-net-dsa-qca8k-enable-assisted-learning-on-CPU-port.patch b/target/linux/generic/pending-6.1/712-net-dsa-qca8k-enable-assisted-learning-on-CPU-port.patch new file mode 100644 index 000000000..23816fe36 --- /dev/null +++ b/target/linux/generic/pending-6.1/712-net-dsa-qca8k-enable-assisted-learning-on-CPU-port.patch @@ -0,0 +1,57 @@ +From 0f6599167c126ce32c85d4f8a1f3d1775a268572 Mon Sep 17 00:00:00 2001 +From: Christian Marangi +Date: Fri, 6 Oct 2023 12:44:00 +0200 +Subject: [PATCH] net: dsa: qca8k: enable assisted learning on CPU port + +Enable assisted learning on CPU port. + +It has been verified that there is a problem in packet roaming +from one BSS to another in the same security settings from one +physical R7800 to another physical R7800 where they are in the +same L2 broadcast domain backhauled/linked together via one +of the ethernet ports. +DHCP will fail to complete and traffic cannot flow for around 300 +seconds. + +Signed-off-by: Christian Marangi +--- + drivers/net/dsa/qca/qca8k-8xxx.c | 14 +++++++++----- + 1 file changed, 9 insertions(+), 5 deletions(-) + +--- a/drivers/net/dsa/qca/qca8k-8xxx.c ++++ b/drivers/net/dsa/qca/qca8k-8xxx.c +@@ -2013,6 +2013,12 @@ qca8k_setup(struct dsa_switch *ds) + dev_err(priv->dev, "failed enabling QCA header mode on port %d", dp->index); + return ret; + } ++ ++ /* Disable learning by default on all ports */ ++ ret = regmap_clear_bits(priv->regmap, QCA8K_PORT_LOOKUP_CTRL(dp->index), ++ QCA8K_PORT_LOOKUP_LEARN); ++ if (ret) ++ return ret; + } + + /* Forward all unknown frames to CPU port for Linux processing */ +@@ -2042,11 +2048,6 @@ qca8k_setup(struct dsa_switch *ds) + if (ret) + return ret; + +- ret = regmap_clear_bits(priv->regmap, QCA8K_PORT_LOOKUP_CTRL(port), +- QCA8K_PORT_LOOKUP_LEARN); +- if (ret) +- return ret; +- + /* For port based vlans to work we need to set the + * default egress vid + */ +@@ -2098,6 +2099,9 @@ qca8k_setup(struct dsa_switch *ds) + /* Set max number of LAGs supported */ + ds->num_lag_ids = QCA8K_NUM_LAGS; + ++ /* HW learn on CPU port is limited and require manual setting */ ++ ds->assisted_learning_on_cpu_port = true; ++ + return 0; + } + diff --git a/target/linux/generic/pending-6.1/713-03-arm64-dts-qcom-ipq8074-add-clock-frequency-to-MDIO-n.patch b/target/linux/generic/pending-6.1/713-03-arm64-dts-qcom-ipq8074-add-clock-frequency-to-MDIO-n.patch new file mode 100644 index 000000000..55f116ec5 --- /dev/null +++ b/target/linux/generic/pending-6.1/713-03-arm64-dts-qcom-ipq8074-add-clock-frequency-to-MDIO-n.patch @@ -0,0 +1,25 @@ +From 3b5a603bf66236b956287909556fd7ad4904450c Mon Sep 17 00:00:00 2001 +From: Christian Marangi +Date: Wed, 24 Jan 2024 19:38:01 +0100 +Subject: [PATCH 3/3] arm64: dts: qcom: ipq8074: add clock-frequency to MDIO + node + +Add clock-frequency to MDIO node to set the MDC rate to 6.25Mhz instead +of using the default value of 390KHz from MDIO default divider. + +Signed-off-by: Christian Marangi +--- + arch/arm64/boot/dts/qcom/ipq8074.dtsi | 2 ++ + 1 file changed, 2 insertions(+) + +--- a/arch/arm64/boot/dts/qcom/ipq8074.dtsi ++++ b/arch/arm64/boot/dts/qcom/ipq8074.dtsi +@@ -263,6 +263,8 @@ + clocks = <&gcc GCC_MDIO_AHB_CLK>; + clock-names = "gcc_mdio_ahb_clk"; + ++ clock-frequency = <6250000>; ++ + status = "disabled"; + }; + diff --git a/target/linux/generic/pending-6.1/722-net-phy-realtek-support-switching-between-SGMII-and-.patch b/target/linux/generic/pending-6.1/722-net-phy-realtek-support-switching-between-SGMII-and-.patch index f3c850f7b..abe31e66a 100644 --- a/target/linux/generic/pending-6.1/722-net-phy-realtek-support-switching-between-SGMII-and-.patch +++ b/target/linux/generic/pending-6.1/722-net-phy-realtek-support-switching-between-SGMII-and-.patch @@ -1,3 +1,19 @@ +From 312753d0aadba0f58841ae513b80fdbabc887523 Mon Sep 17 00:00:00 2001 +From: Chukun Pan +Date: Wed, 8 Feb 2023 16:32:18 +0800 +Subject: [PATCH] net: phy: realtek: support switching between SGMII and + 2500BASE-X for RTL822x series + +After commit ace6aba ("net: phy: realtek: rtl8221: allow to configure +SERDES mode"), the rtl8221 phy can work in SGMII and 2500base-x modes +respectively. So add interface automatic switching for rtl8221 phy to +match various wire speeds. + +Signed-off-by: Chukun Pan +--- + drivers/net/phy/realtek.c | 26 ++++++++++++++++++++++++-- + 1 file changed, 24 insertions(+), 2 deletions(-) + --- a/drivers/net/phy/realtek.c +++ b/drivers/net/phy/realtek.c @@ -686,6 +686,25 @@ static int rtl822x_config_aneg(struct ph diff --git a/target/linux/generic/pending-6.1/723-net-mt7531-ensure-all-MACs-are-powered-down-before-r.patch b/target/linux/generic/pending-6.1/723-net-mt7531-ensure-all-MACs-are-powered-down-before-r.patch deleted file mode 100644 index d8440d7aa..000000000 --- a/target/linux/generic/pending-6.1/723-net-mt7531-ensure-all-MACs-are-powered-down-before-r.patch +++ /dev/null @@ -1,28 +0,0 @@ -From 3fb8841513c4ec3a2e5d366df86230c45f239a57 Mon Sep 17 00:00:00 2001 -From: Alexander Couzens -Date: Sat, 13 Aug 2022 13:08:22 +0200 -Subject: [PATCH 03/10] net: mt7531: ensure all MACs are powered down before - reset - -The datasheet [1] explicit describes it as requirement for a reset. - -[1] MT7531 Reference Manual for Development Board rev 1.0, page 735 - -Signed-off-by: Alexander Couzens ---- - drivers/net/dsa/mt7530.c | 4 ++++ - 1 file changed, 4 insertions(+) - ---- a/drivers/net/dsa/mt7530.c -+++ b/drivers/net/dsa/mt7530.c -@@ -2470,6 +2470,10 @@ mt7530_setup(struct dsa_switch *ds) - return -ENODEV; - } - -+ /* all MACs must be forced link-down before sw reset */ -+ for (i = 0; i < MT7530_NUM_PORTS; i++) -+ mt7530_write(priv, MT7530_PMCR_P(i), MT7531_FORCE_LNK); -+ - /* Reset the switch through internal reset */ - mt7530_write(priv, MT7530_SYS_CTRL, - SYS_CTRL_PHY_RST | SYS_CTRL_SW_RST | diff --git a/target/linux/generic/pending-6.1/725-net-phy-realtek-disable-SGMII-in-band-AN-for-2-5G-PHYs.patch b/target/linux/generic/pending-6.1/725-net-phy-realtek-disable-SGMII-in-band-AN-for-2-5G-PHYs.patch index 2bea60803..babaa47ae 100644 --- a/target/linux/generic/pending-6.1/725-net-phy-realtek-disable-SGMII-in-band-AN-for-2-5G-PHYs.patch +++ b/target/linux/generic/pending-6.1/725-net-phy-realtek-disable-SGMII-in-band-AN-for-2-5G-PHYs.patch @@ -9,6 +9,7 @@ access a PHY, in-band-status is unneeded as we have link-status via MDIO. Switch off SGMII in-band autonegotiation using magic values. Reported-by: Chen Minqiang +Reported-by: Chukun Pan Reported-by: Yevhen Kolomeiko Tested-by: Yevhen Kolomeiko Signed-off-by: Daniel Golle diff --git a/target/linux/generic/pending-6.1/729-net-phy-realtek-introduce-rtl822x_probe.patch b/target/linux/generic/pending-6.1/729-net-phy-realtek-introduce-rtl822x_probe.patch index 016779264..92e7a8742 100644 --- a/target/linux/generic/pending-6.1/729-net-phy-realtek-introduce-rtl822x_probe.patch +++ b/target/linux/generic/pending-6.1/729-net-phy-realtek-introduce-rtl822x_probe.patch @@ -50,23 +50,7 @@ Signed-off-by: Daniel Golle static int rtlgen_resume(struct phy_device *phydev) { int ret = genphy_resume(phydev); -@@ -1035,6 +1058,7 @@ static struct phy_driver realtek_drvs[] - .match_phy_device = rtl8226_match_phy_device, - .get_features = rtl822x_get_features, - .config_aneg = rtl822x_config_aneg, -+ .probe = rtl822x_probe, - .read_status = rtl822x_read_status, - .suspend = genphy_suspend, - .resume = rtlgen_resume, -@@ -1048,6 +1072,7 @@ static struct phy_driver realtek_drvs[] - .name = "RTL8226B_RTL8221B 2.5Gbps PHY", - .get_features = rtl822x_get_features, - .config_aneg = rtl822x_config_aneg, -+ .probe = rtl822x_probe, - .read_status = rtl822x_read_status, - .suspend = genphy_suspend, - .resume = rtlgen_resume, -@@ -1061,6 +1086,7 @@ static struct phy_driver realtek_drvs[] +@@ -1061,6 +1084,7 @@ static struct phy_driver realtek_drvs[] .name = "RTL8226-CG 2.5Gbps PHY", .get_features = rtl822x_get_features, .config_aneg = rtl822x_config_aneg, @@ -74,7 +58,7 @@ Signed-off-by: Daniel Golle .read_status = rtl822x_read_status, .suspend = genphy_suspend, .resume = rtlgen_resume, -@@ -1072,6 +1098,7 @@ static struct phy_driver realtek_drvs[] +@@ -1072,6 +1096,7 @@ static struct phy_driver realtek_drvs[] .name = "RTL8226B-CG_RTL8221B-CG 2.5Gbps PHY", .get_features = rtl822x_get_features, .config_aneg = rtl822x_config_aneg, @@ -82,7 +66,7 @@ Signed-off-by: Daniel Golle .read_status = rtl822x_read_status, .suspend = genphy_suspend, .resume = rtlgen_resume, -@@ -1084,6 +1111,7 @@ static struct phy_driver realtek_drvs[] +@@ -1084,6 +1109,7 @@ static struct phy_driver realtek_drvs[] .get_features = rtl822x_get_features, .config_init = rtl8221b_config_init, .config_aneg = rtl822x_config_aneg, @@ -90,7 +74,7 @@ Signed-off-by: Daniel Golle .read_status = rtl822x_read_status, .suspend = genphy_suspend, .resume = rtlgen_resume, -@@ -1096,6 +1124,7 @@ static struct phy_driver realtek_drvs[] +@@ -1096,6 +1122,7 @@ static struct phy_driver realtek_drvs[] .get_features = rtl822x_get_features, .config_aneg = rtl822x_config_aneg, .config_init = rtl8221b_config_init, diff --git a/target/linux/generic/pending-6.1/730-net-phy-realtek-detect-early-version-of-RTL8221B.patch b/target/linux/generic/pending-6.1/730-net-phy-realtek-detect-early-version-of-RTL8221B.patch index 8a4ea137a..05edcc8bf 100644 --- a/target/linux/generic/pending-6.1/730-net-phy-realtek-detect-early-version-of-RTL8221B.patch +++ b/target/linux/generic/pending-6.1/730-net-phy-realtek-detect-early-version-of-RTL8221B.patch @@ -13,7 +13,15 @@ Signed-off-by: Daniel Golle --- a/drivers/net/phy/realtek.c +++ b/drivers/net/phy/realtek.c -@@ -754,6 +754,38 @@ static int rtl8226_match_phy_device(stru +@@ -80,6 +80,7 @@ + + #define RTL_GENERIC_PHYID 0x001cc800 + #define RTL_8211FVD_PHYID 0x001cc878 ++#define RTL_8221B_VB_CG_PHYID 0x001cc849 + + MODULE_DESCRIPTION("Realtek PHY driver"); + MODULE_AUTHOR("Johnson Leung"); +@@ -754,6 +755,38 @@ static int rtl8226_match_phy_device(stru rtlgen_supports_2_5gbps(phydev); } @@ -46,13 +54,13 @@ Signed-off-by: Daniel Golle + id |= val; + } + -+ return (id == 0x001cc849); ++ return (id == RTL_8221B_VB_CG_PHYID); +} + static int rtl822x_probe(struct phy_device *phydev) { struct device *dev = &phydev->mdio.dev; -@@ -1106,7 +1138,7 @@ static struct phy_driver realtek_drvs[] +@@ -1104,7 +1137,7 @@ static struct phy_driver realtek_drvs[] .write_page = rtl821x_write_page, .soft_reset = genphy_soft_reset, }, { diff --git a/target/linux/generic/pending-6.1/731-net-permit-ieee80211_ptr-even-with-no-CFG82111-suppo.patch b/target/linux/generic/pending-6.1/731-net-permit-ieee80211_ptr-even-with-no-CFG82111-suppo.patch index 2c1ec55d0..f3725bf7d 100644 --- a/target/linux/generic/pending-6.1/731-net-permit-ieee80211_ptr-even-with-no-CFG82111-suppo.patch +++ b/target/linux/generic/pending-6.1/731-net-permit-ieee80211_ptr-even-with-no-CFG82111-suppo.patch @@ -17,7 +17,7 @@ Signed-off-by: Christian Marangi --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h -@@ -2168,7 +2168,7 @@ struct net_device { +@@ -2190,7 +2190,7 @@ struct net_device { #if IS_ENABLED(CONFIG_AX25) void *ax25_ptr; #endif diff --git a/target/linux/generic/pending-6.1/732-03-net-ethernet-mtk_eth_soc-fix-remaining-throughput-re.patch b/target/linux/generic/pending-6.1/732-03-net-ethernet-mtk_eth_soc-fix-remaining-throughput-re.patch index 1a3f56e94..aa8607541 100644 --- a/target/linux/generic/pending-6.1/732-03-net-ethernet-mtk_eth_soc-fix-remaining-throughput-re.patch +++ b/target/linux/generic/pending-6.1/732-03-net-ethernet-mtk_eth_soc-fix-remaining-throughput-re.patch @@ -22,7 +22,7 @@ Signed-off-by: Felix Fietkau --- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c +++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c -@@ -765,6 +765,7 @@ static void mtk_mac_link_up(struct phyli +@@ -766,6 +766,7 @@ static void mtk_mac_link_up(struct phyli MAC_MCR_FORCE_RX_FC); /* Configure speed */ @@ -30,7 +30,7 @@ Signed-off-by: Felix Fietkau switch (speed) { case SPEED_2500: case SPEED_1000: -@@ -3346,6 +3347,9 @@ found: +@@ -3347,6 +3348,9 @@ found: if (dp->index >= MTK_QDMA_NUM_QUEUES) return NOTIFY_DONE; diff --git a/target/linux/generic/pending-6.1/734-net-ethernet-mtk_eth_soc-ppe-fix-L2-offloading-with-.patch b/target/linux/generic/pending-6.1/734-net-ethernet-mtk_eth_soc-ppe-fix-L2-offloading-with-.patch index 72d574340..61042c1ad 100644 --- a/target/linux/generic/pending-6.1/734-net-ethernet-mtk_eth_soc-ppe-fix-L2-offloading-with-.patch +++ b/target/linux/generic/pending-6.1/734-net-ethernet-mtk_eth_soc-ppe-fix-L2-offloading-with-.patch @@ -20,7 +20,7 @@ Signed-off-by: Felix Fietkau #include #include "mtk_eth_soc.h" #include "mtk_ppe.h" -@@ -775,7 +776,9 @@ void __mtk_ppe_check_skb(struct mtk_ppe +@@ -829,7 +830,9 @@ void __mtk_ppe_check_skb(struct mtk_ppe skb->dev->dsa_ptr->tag_ops->proto != DSA_TAG_PROTO_MTK) goto out; diff --git a/target/linux/generic/pending-6.1/737-net-ethernet-mtk_eth_soc-add-paths-and-SerDes-modes-.patch b/target/linux/generic/pending-6.1/737-net-ethernet-mtk_eth_soc-add-paths-and-SerDes-modes-.patch index 519ece669..2b379c915 100644 --- a/target/linux/generic/pending-6.1/737-net-ethernet-mtk_eth_soc-add-paths-and-SerDes-modes-.patch +++ b/target/linux/generic/pending-6.1/737-net-ethernet-mtk_eth_soc-add-paths-and-SerDes-modes-.patch @@ -1,8 +1,8 @@ -From 1e25ca1147579bda8b941be1b9851f5911d44eb0 Mon Sep 17 00:00:00 2001 +From d5e337e7aecc2e1cc9e96768062610adb95f8f72 Mon Sep 17 00:00:00 2001 From: Daniel Golle -Date: Tue, 22 Aug 2023 19:04:42 +0100 -Subject: [PATCH 098/125] net: ethernet: mtk_eth_soc: add paths and SerDes - modes for MT7988 +Date: Tue, 12 Dec 2023 03:51:14 +0000 +Subject: [PATCH] net: ethernet: mtk_eth_soc: add paths and SerDes modes for + MT7988 MT7988 comes with a built-in 2.5G PHY as well as SerDes lanes to connect external PHYs or transceivers in USXGMII, 10GBase-R, 5GBase-R, @@ -18,50 +18,11 @@ modes. Signed-off-by: Daniel Golle --- - drivers/net/ethernet/mediatek/Kconfig | 16 + - drivers/net/ethernet/mediatek/Makefile | 1 + - drivers/net/ethernet/mediatek/mtk_eth_path.c | 123 +++- - drivers/net/ethernet/mediatek/mtk_eth_soc.c | 182 ++++- - drivers/net/ethernet/mediatek/mtk_eth_soc.h | 232 ++++++- - drivers/net/ethernet/mediatek/mtk_usxgmii.c | 692 +++++++++++++++++++ - 6 files changed, 1215 insertions(+), 31 deletions(-) - create mode 100644 drivers/net/ethernet/mediatek/mtk_usxgmii.c + drivers/net/ethernet/mediatek/mtk_eth_path.c | 122 +++++++- + drivers/net/ethernet/mediatek/mtk_eth_soc.c | 292 +++++++++++++++++-- + drivers/net/ethernet/mediatek/mtk_eth_soc.h | 107 ++++++- + 3 files changed, 470 insertions(+), 51 deletions(-) ---- a/drivers/net/ethernet/mediatek/Kconfig -+++ b/drivers/net/ethernet/mediatek/Kconfig -@@ -25,6 +25,22 @@ config NET_MEDIATEK_SOC - This driver supports the gigabit ethernet MACs in the - MediaTek SoC family. - -+config NET_MEDIATEK_SOC_USXGMII -+ bool "Support USXGMII SerDes on MT7988" -+ depends on (ARCH_MEDIATEK && ARM64) || COMPILE_TEST -+ def_bool NET_MEDIATEK_SOC != n -+ help -+ Include support for 10GE SerDes which can be found on MT7988. -+ If this kernel should run on SoCs with 10 GBit/s Ethernet you -+ will need to select this option to use GMAC2 and GMAC3 with -+ external PHYs, SFP(+) cages in 10GBase-R, 5GBase-R or USXGMII -+ interface modes. -+ -+ Note that as the 2500Base-X/1000Base-X/Cisco SGMII SerDes PCS -+ unit (MediaTek LynxI) in MT7988 is connected via the new 10GE -+ SerDes, you will also need to select this option in case you -+ want to use any of those SerDes modes. -+ - config NET_MEDIATEK_STAR_EMAC - tristate "MediaTek STAR Ethernet MAC support" - select PHYLIB ---- a/drivers/net/ethernet/mediatek/Makefile -+++ b/drivers/net/ethernet/mediatek/Makefile -@@ -5,6 +5,7 @@ - - obj-$(CONFIG_NET_MEDIATEK_SOC) += mtk_eth.o - mtk_eth-y := mtk_eth_soc.o mtk_eth_path.o mtk_ppe.o mtk_ppe_debugfs.o mtk_ppe_offload.o -+mtk_eth-$(CONFIG_NET_MEDIATEK_SOC_USXGMII) += mtk_usxgmii.o - mtk_eth-$(CONFIG_NET_MEDIATEK_SOC_WED) += mtk_wed.o mtk_wed_mcu.o mtk_wed_wo.o - ifdef CONFIG_DEBUG_FS - mtk_eth-$(CONFIG_NET_MEDIATEK_SOC_WED) += mtk_wed_debugfs.o --- a/drivers/net/ethernet/mediatek/mtk_eth_path.c +++ b/drivers/net/ethernet/mediatek/mtk_eth_path.c @@ -31,10 +31,20 @@ static const char *mtk_eth_path_name(u64 @@ -242,14 +203,31 @@ Signed-off-by: Daniel Golle /* Setup proper MUXes along the path */ return mtk_eth_mux_setup(eth, path); -@@ -284,4 +398,3 @@ int mtk_gmac_rgmii_path_setup(struct mtk - /* Setup proper MUXes along the path */ - return mtk_eth_mux_setup(eth, path); - } -- --- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c +++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c -@@ -474,6 +474,30 @@ static void mtk_setup_bridge_switch(stru +@@ -21,6 +21,8 @@ + #include + #include + #include ++#include ++#include + #include + #include + #include +@@ -258,12 +260,8 @@ static const char * const mtk_clks_sourc + "ethwarp_wocpu2", + "ethwarp_wocpu1", + "ethwarp_wocpu0", +- "top_usxgmii0_sel", +- "top_usxgmii1_sel", + "top_sgm0_sel", + "top_sgm1_sel", +- "top_xfi_phy0_xtal_sel", +- "top_xfi_phy1_xtal_sel", + "top_eth_gmii_sel", + "top_eth_refck_50m_sel", + "top_eth_sys_200m_sel", +@@ -475,6 +473,30 @@ static void mtk_setup_bridge_switch(stru MTK_GSW_CFG); } @@ -280,34 +258,29 @@ Signed-off-by: Daniel Golle static struct phylink_pcs *mtk_mac_select_pcs(struct phylink_config *config, phy_interface_t interface) { -@@ -482,12 +506,20 @@ static struct phylink_pcs *mtk_mac_selec +@@ -483,6 +505,21 @@ static struct phylink_pcs *mtk_mac_selec struct mtk_eth *eth = mac->hw; unsigned int sid; -- if (interface == PHY_INTERFACE_MODE_SGMII || -- phy_interface_mode_is_8023z(interface)) { -- sid = (MTK_HAS_CAPS(eth->soc->caps, MTK_SHARED_SGMII)) ? -- 0 : mac->id; -- -- return eth->sgmii_pcs[sid]; -+ if ((interface == PHY_INTERFACE_MODE_SGMII || -+ phy_interface_mode_is_8023z(interface)) && -+ MTK_HAS_CAPS(eth->soc->caps, MTK_SGMII)) { -+ sid = mtk_mac2xgmii_id(eth, mac->id); -+ if (MTK_HAS_CAPS(eth->soc->caps, MTK_USXGMII)) -+ return mtk_sgmii_wrapper_select_pcs(eth, mac->id); -+ else -+ return eth->sgmii_pcs[sid]; -+ } else if ((interface == PHY_INTERFACE_MODE_USXGMII || -+ interface == PHY_INTERFACE_MODE_10GBASER || -+ interface == PHY_INTERFACE_MODE_5GBASER) && -+ MTK_HAS_CAPS(eth->soc->caps, MTK_USXGMII) && -+ mac->id != MTK_GMAC1_ID) { -+ return mtk_usxgmii_select_pcs(eth, mac->id); - } - - return NULL; -@@ -543,7 +575,22 @@ static void mtk_mac_config(struct phylin ++ if (mtk_is_netsys_v3_or_greater(eth)) { ++ switch (interface) { ++ case PHY_INTERFACE_MODE_1000BASEX: ++ case PHY_INTERFACE_MODE_2500BASEX: ++ case PHY_INTERFACE_MODE_SGMII: ++ return mac->sgmii_pcs; ++ case PHY_INTERFACE_MODE_5GBASER: ++ case PHY_INTERFACE_MODE_10GBASER: ++ case PHY_INTERFACE_MODE_USXGMII: ++ return mac->usxgmii_pcs; ++ default: ++ return NULL; ++ } ++ } ++ + if (interface == PHY_INTERFACE_MODE_SGMII || + phy_interface_mode_is_8023z(interface)) { + sid = (MTK_HAS_CAPS(eth->soc->caps, MTK_SHARED_SGMII)) ? +@@ -544,7 +581,22 @@ static void mtk_mac_config(struct phylin goto init_err; } break; @@ -330,7 +303,7 @@ Signed-off-by: Daniel Golle break; default: goto err_phy; -@@ -598,8 +645,6 @@ static void mtk_mac_config(struct phylin +@@ -599,8 +651,6 @@ static void mtk_mac_config(struct phylin val &= ~SYSCFG0_GE_MODE(SYSCFG0_GE_MASK, mac->id); val |= SYSCFG0_GE_MODE(ge_mode, mac->id); regmap_write(eth->ethsys, ETHSYS_SYSCFG0, val); @@ -339,7 +312,7 @@ Signed-off-by: Daniel Golle } /* SGMII */ -@@ -616,21 +661,40 @@ static void mtk_mac_config(struct phylin +@@ -617,21 +667,40 @@ static void mtk_mac_config(struct phylin /* Save the syscfg0 value for mac_finish */ mac->syscfg0 = val; @@ -387,7 +360,37 @@ Signed-off-by: Daniel Golle return; err_phy: -@@ -675,10 +739,13 @@ static void mtk_mac_link_down(struct phy +@@ -644,6 +713,18 @@ init_err: + mac->id, phy_modes(state->interface), err); + } + ++static int mtk_mac_prepare(struct phylink_config *config, unsigned int mode, ++ phy_interface_t interface) ++{ ++ struct mtk_mac *mac = container_of(config, struct mtk_mac, ++ phylink_config); ++ ++ if (mac->pextp && mac->interface != interface) ++ phy_reset(mac->pextp); ++ ++ return 0; ++} ++ + static int mtk_mac_finish(struct phylink_config *config, unsigned int mode, + phy_interface_t interface) + { +@@ -652,6 +733,10 @@ static int mtk_mac_finish(struct phylink + struct mtk_eth *eth = mac->hw; + u32 mcr_cur, mcr_new; + ++ /* Setup PMA/PMD */ ++ if (mac->pextp) ++ phy_set_mode_ext(mac->pextp, PHY_MODE_ETHERNET, interface); ++ + /* Enable SGMII */ + if (interface == PHY_INTERFACE_MODE_SGMII || + phy_interface_mode_is_8023z(interface)) +@@ -676,10 +761,14 @@ static void mtk_mac_link_down(struct phy { struct mtk_mac *mac = container_of(config, struct mtk_mac, phylink_config); @@ -396,15 +399,16 @@ Signed-off-by: Daniel Golle - mcr &= ~(MAC_MCR_TX_EN | MAC_MCR_RX_EN | MAC_MCR_FORCE_LINK); - mtk_w32(mac->hw, mcr, MTK_MAC_MCR(mac->id)); + if (!mtk_interface_mode_is_xgmii(interface)) { -+ mtk_m32(mac->hw, MAC_MCR_TX_EN | MAC_MCR_RX_EN, 0, MTK_MAC_MCR(mac->id)); -+ mtk_m32(mac->hw, MTK_XGMAC_FORCE_LINK(mac->id), 0, MTK_XGMAC_STS(mac->id)); -+ } else if (mac->id != MTK_GMAC1_ID) { ++ mtk_m32(mac->hw, MAC_MCR_TX_EN | MAC_MCR_RX_EN | MAC_MCR_FORCE_LINK, 0, MTK_MAC_MCR(mac->id)); ++ if (mtk_is_netsys_v3_or_greater(mac->hw)) ++ mtk_m32(mac->hw, MTK_XGMAC_FORCE_LINK(mac->id), 0, MTK_XGMAC_STS(mac->id)); ++ } else if (mtk_is_netsys_v3_or_greater(mac->hw) && mac->id != MTK_GMAC1_ID) { + mtk_m32(mac->hw, XMAC_MCR_TRX_DISABLE, XMAC_MCR_TRX_DISABLE, MTK_XMAC_MCR(mac->id)); + } } static void mtk_set_queue_speed(struct mtk_eth *eth, unsigned int idx, -@@ -750,13 +817,11 @@ static void mtk_set_queue_speed(struct m +@@ -751,13 +840,11 @@ static void mtk_set_queue_speed(struct m mtk_w32(eth, val, soc->reg_map->qdma.qtx_sch + ofs); } @@ -422,7 +426,7 @@ Signed-off-by: Daniel Golle u32 mcr; mcr = mtk_r32(mac->hw, MTK_MAC_MCR(mac->id)); -@@ -790,6 +855,55 @@ static void mtk_mac_link_up(struct phyli +@@ -791,9 +878,63 @@ static void mtk_mac_link_up(struct phyli mtk_w32(mac->hw, mcr, MTK_MAC_MCR(mac->id)); } @@ -467,18 +471,104 @@ Signed-off-by: Daniel Golle + struct mtk_mac *mac = container_of(config, struct mtk_mac, + phylink_config); + -+ if (mtk_interface_mode_is_xgmii(interface)) ++ if (mtk_is_netsys_v3_or_greater(mac->hw) && mtk_interface_mode_is_xgmii(interface)) + mtk_xgdm_mac_link_up(mac, phy, mode, interface, speed, duplex, + tx_pause, rx_pause); + else + mtk_gdm_mac_link_up(mac, phy, mode, interface, speed, duplex, + tx_pause, rx_pause); ++ ++ /* Repeat pextp setup to tune link */ ++ if (mac->pextp) ++ phy_set_mode_ext(mac->pextp, PHY_MODE_ETHERNET, interface); +} + static const struct phylink_mac_ops mtk_phylink_ops = { - .validate = phylink_generic_validate, .mac_select_pcs = mtk_mac_select_pcs, -@@ -4614,8 +4728,21 @@ static int mtk_add_mac(struct mtk_eth *e + .mac_config = mtk_mac_config, ++ .mac_prepare = mtk_mac_prepare, + .mac_finish = mtk_mac_finish, + .mac_link_down = mtk_mac_link_down, + .mac_link_up = mtk_mac_link_up, +@@ -3372,6 +3513,9 @@ static int mtk_open(struct net_device *d + struct mtk_eth *eth = mac->hw; + int i, err; + ++ if (mac->pextp) ++ phy_power_on(mac->pextp); ++ + err = phylink_of_phy_connect(mac->phylink, mac->of_node, 0); + if (err) { + netdev_err(dev, "%s: could not attach PHY: %d\n", __func__, +@@ -3500,6 +3644,9 @@ static int mtk_stop(struct net_device *d + for (i = 0; i < ARRAY_SIZE(eth->ppe); i++) + mtk_ppe_stop(eth->ppe[i]); + ++ if (mac->pextp) ++ phy_power_off(mac->pextp); ++ + return 0; + } + +@@ -4497,6 +4644,7 @@ static const struct net_device_ops mtk_n + static int mtk_add_mac(struct mtk_eth *eth, struct device_node *np) + { + const __be32 *_id = of_get_property(np, "reg", NULL); ++ struct device_node *pcs_np; + phy_interface_t phy_mode; + struct phylink *phylink; + struct mtk_mac *mac; +@@ -4532,16 +4680,41 @@ static int mtk_add_mac(struct mtk_eth *e + mac->id = id; + mac->hw = eth; + mac->of_node = np; ++ pcs_np = of_parse_phandle(mac->of_node, "pcs-handle", 0); ++ if (pcs_np) { ++ mac->sgmii_pcs = mtk_pcs_lynxi_get(eth->dev, pcs_np); ++ if (IS_ERR(mac->sgmii_pcs)) { ++ if (PTR_ERR(mac->sgmii_pcs) == -EPROBE_DEFER) ++ return -EPROBE_DEFER; + +- err = of_get_ethdev_address(mac->of_node, eth->netdev[id]); +- if (err == -EPROBE_DEFER) +- return err; ++ dev_err(eth->dev, "cannot select SGMII PCS, error %ld\n", ++ PTR_ERR(mac->sgmii_pcs)); ++ return PTR_ERR(mac->sgmii_pcs); ++ } ++ } + +- if (err) { +- /* If the mac address is invalid, use random mac address */ +- eth_hw_addr_random(eth->netdev[id]); +- dev_err(eth->dev, "generated random MAC address %pM\n", +- eth->netdev[id]->dev_addr); ++ pcs_np = of_parse_phandle(mac->of_node, "pcs-handle", 1); ++ if (pcs_np) { ++ mac->usxgmii_pcs = mtk_usxgmii_pcs_get(eth->dev, pcs_np); ++ if (IS_ERR(mac->usxgmii_pcs)) { ++ if (PTR_ERR(mac->usxgmii_pcs) == -EPROBE_DEFER) ++ return -EPROBE_DEFER; ++ ++ dev_err(eth->dev, "cannot select USXGMII PCS, error %ld\n", ++ PTR_ERR(mac->usxgmii_pcs)); ++ return PTR_ERR(mac->usxgmii_pcs); ++ } ++ } ++ ++ if (mtk_is_netsys_v3_or_greater(eth) && (mac->sgmii_pcs || mac->usxgmii_pcs)) { ++ mac->pextp = devm_of_phy_get(eth->dev, mac->of_node, NULL); ++ if (IS_ERR(mac->pextp)) { ++ if (PTR_ERR(mac->pextp) != -EPROBE_DEFER) ++ dev_err(eth->dev, "cannot get PHY, error %ld\n", ++ PTR_ERR(mac->pextp)); ++ ++ return PTR_ERR(mac->pextp); ++ } + } + + memset(mac->hwlro_ip, 0, sizeof(mac->hwlro_ip)); +@@ -4615,8 +4788,21 @@ static int mtk_add_mac(struct mtk_eth *e phy_interface_zero(mac->phylink_config.supported_interfaces); __set_bit(PHY_INTERFACE_MODE_INTERNAL, mac->phylink_config.supported_interfaces); @@ -500,23 +590,91 @@ Signed-off-by: Daniel Golle phylink = phylink_create(&mac->phylink_config, of_fwnode_handle(mac->of_node), phy_mode, &mtk_phylink_ops); -@@ -4808,6 +4935,13 @@ static int mtk_probe(struct platform_dev +@@ -4661,6 +4847,26 @@ free_netdev: + return err; + } - if (err) - return err; ++static int mtk_mac_assign_address(struct mtk_eth *eth, int i, bool test_defer_only) ++{ ++ int err = of_get_ethdev_address(eth->mac[i]->of_node, eth->netdev[i]); ++ ++ if (err == -EPROBE_DEFER) ++ return err; ++ ++ if (test_defer_only) ++ return 0; ++ ++ if (err) { ++ /* If the mac address is invalid, use random mac address */ ++ eth_hw_addr_random(eth->netdev[i]); ++ dev_err(eth->dev, "generated random MAC address %pM\n", ++ eth->netdev[i]); + } + -+ if (MTK_HAS_CAPS(eth->soc->caps, MTK_USXGMII)) { -+ err = mtk_usxgmii_init(eth); ++ return 0; ++} + -+ if (err) -+ return err; + void mtk_eth_set_dma_device(struct mtk_eth *eth, struct device *dma_dev) + { + struct net_device *dev, *tmp; +@@ -4804,7 +5010,8 @@ static int mtk_probe(struct platform_dev + regmap_write(cci, 0, 3); } - if (eth->soc->required_pctl) { +- if (MTK_HAS_CAPS(eth->soc->caps, MTK_SGMII)) { ++ if (MTK_HAS_CAPS(eth->soc->caps, MTK_SGMII) && ++ !mtk_is_netsys_v3_or_greater(eth)) { + err = mtk_sgmii_init(eth); + + if (err) +@@ -4915,6 +5122,24 @@ static int mtk_probe(struct platform_dev + } + } + ++ for (i = 0; i < MTK_MAX_DEVS; i++) { ++ if (!eth->netdev[i]) ++ continue; ++ ++ err = mtk_mac_assign_address(eth, i, true); ++ if (err) ++ goto err_deinit_hw; ++ } ++ ++ for (i = 0; i < MTK_MAX_DEVS; i++) { ++ if (!eth->netdev[i]) ++ continue; ++ ++ err = mtk_mac_assign_address(eth, i, false); ++ if (err) ++ goto err_deinit_hw; ++ } ++ + if (MTK_HAS_CAPS(eth->soc->caps, MTK_SHARED_INT)) { + err = devm_request_irq(eth->dev, eth->irq[0], + mtk_handle_irq, 0, +@@ -5017,6 +5242,11 @@ static int mtk_remove(struct platform_de + mtk_stop(eth->netdev[i]); + mac = netdev_priv(eth->netdev[i]); + phylink_disconnect_phy(mac->phylink); ++ if (mac->sgmii_pcs) ++ mtk_pcs_lynxi_put(mac->sgmii_pcs); ++ ++ if (mac->usxgmii_pcs) ++ mtk_usxgmii_pcs_put(mac->usxgmii_pcs); + } + + mtk_wed_exit(); --- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h +++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h -@@ -502,6 +502,21 @@ +@@ -15,6 +15,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -502,6 +503,21 @@ #define INTF_MODE_RGMII_1000 (TRGMII_MODE | TRGMII_CENTRAL_ALIGNED) #define INTF_MODE_RGMII_10_100 0 @@ -538,7 +696,7 @@ Signed-off-by: Daniel Golle /* GPIO port control registers for GMAC 2*/ #define GPIO_OD33_CTRL8 0x4c0 #define GPIO_BIAS_CTRL 0xed0 -@@ -527,6 +542,7 @@ +@@ -527,6 +543,7 @@ #define SYSCFG0_SGMII_GMAC2 ((3 << 8) & SYSCFG0_SGMII_MASK) #define SYSCFG0_SGMII_GMAC1_V2 BIT(9) #define SYSCFG0_SGMII_GMAC2_V2 BIT(8) @@ -546,72 +704,9 @@ Signed-off-by: Daniel Golle /* ethernet subsystem clock register */ -@@ -559,12 +575,74 @@ - #define ETHSYS_DMA_AG_MAP_QDMA BIT(1) - #define ETHSYS_DMA_AG_MAP_PPE BIT(2) - -+/* USXGMII subsystem config registers */ -+/* Register to control speed */ -+#define RG_PHY_TOP_SPEED_CTRL1 0x80C -+#define USXGMII_RATE_UPDATE_MODE BIT(31) -+#define USXGMII_MAC_CK_GATED BIT(29) -+#define USXGMII_IF_FORCE_EN BIT(28) -+#define USXGMII_RATE_ADAPT_MODE GENMASK(10, 8) -+#define USXGMII_RATE_ADAPT_MODE_X1 0 -+#define USXGMII_RATE_ADAPT_MODE_X2 1 -+#define USXGMII_RATE_ADAPT_MODE_X4 2 -+#define USXGMII_RATE_ADAPT_MODE_X10 3 -+#define USXGMII_RATE_ADAPT_MODE_X100 4 -+#define USXGMII_RATE_ADAPT_MODE_X5 5 -+#define USXGMII_RATE_ADAPT_MODE_X50 6 -+#define USXGMII_XFI_RX_MODE GENMASK(6, 4) -+#define USXGMII_XFI_RX_MODE_10G 0 -+#define USXGMII_XFI_RX_MODE_5G 1 -+#define USXGMII_XFI_TX_MODE GENMASK(2, 0) -+#define USXGMII_XFI_TX_MODE_10G 0 -+#define USXGMII_XFI_TX_MODE_5G 1 -+ -+/* Register to control PCS AN */ -+#define RG_PCS_AN_CTRL0 0x810 -+#define USXGMII_AN_RESTART BIT(31) -+#define USXGMII_AN_SYNC_CNT GENMASK(30, 11) -+#define USXGMII_AN_ENABLE BIT(0) -+ -+#define RG_PCS_AN_CTRL2 0x818 -+#define USXGMII_LINK_TIMER_IDLE_DETECT GENMASK(29, 20) -+#define USXGMII_LINK_TIMER_COMP_ACK_DETECT GENMASK(19, 10) -+#define USXGMII_LINK_TIMER_AN_RESTART GENMASK(9, 0) -+ -+/* Register to read PCS AN status */ -+#define RG_PCS_AN_STS0 0x81c -+#define USXGMII_PCS_AN_WORD GENMASK(15, 0) -+#define USXGMII_LPA_LATCH BIT(31) -+ -+/* Register to control USXGMII XFI PLL digital */ -+#define XFI_PLL_DIG_GLB8 0x08 -+#define RG_XFI_PLL_EN BIT(31) -+ -+/* Register to control USXGMII XFI PLL analog */ -+#define XFI_PLL_ANA_GLB8 0x108 -+#define RG_XFI_PLL_ANA_SWWA 0x02283248 -+ - /* Infrasys subsystem config registers */ - #define INFRA_MISC2 0x70c - #define CO_QPHY_SEL BIT(0) +@@ -565,6 +582,11 @@ #define GEPHY_MAC_SEL BIT(1) -+/* Toprgu subsystem config registers */ -+#define TOPRGU_SWSYSRST 0x18 -+#define SWSYSRST_UNLOCK_KEY GENMASK(31, 24) -+#define SWSYSRST_XFI_PLL_GRST BIT(16) -+#define SWSYSRST_XFI_PEXPT1_GRST BIT(15) -+#define SWSYSRST_XFI_PEXPT0_GRST BIT(14) -+#define SWSYSRST_XFI1_GRST BIT(13) -+#define SWSYSRST_XFI0_GRST BIT(12) -+#define SWSYSRST_SGMII1_GRST BIT(2) -+#define SWSYSRST_SGMII0_GRST BIT(1) -+#define TOPRGU_SWSYSRST_EN 0xFC -+ /* Top misc registers */ +#define TOP_MISC_NETSYS_PCS_MUX 0x84 +#define NETSYS_PCS_MUX_MASK GENMASK(1, 0) @@ -621,7 +716,7 @@ Signed-off-by: Daniel Golle #define USB_PHY_SWITCH_REG 0x218 #define QPHY_SEL_MASK GENMASK(1, 0) #define SGMII_QPHY_SEL 0x2 -@@ -589,6 +667,8 @@ +@@ -589,6 +611,8 @@ #define MT7628_SDM_RBCNT (MT7628_SDM_OFFSET + 0x10c) #define MT7628_SDM_CS_ERR (MT7628_SDM_OFFSET + 0x110) @@ -630,7 +725,7 @@ Signed-off-by: Daniel Golle #define MTK_FE_CDM1_FSM 0x220 #define MTK_FE_CDM2_FSM 0x224 #define MTK_FE_CDM3_FSM 0x238 -@@ -597,6 +677,11 @@ +@@ -597,6 +621,11 @@ #define MTK_FE_CDM6_FSM 0x328 #define MTK_FE_GDM1_FSM 0x228 #define MTK_FE_GDM2_FSM 0x22C @@ -642,7 +737,40 @@ Signed-off-by: Daniel Golle #define MTK_MAC_FSM(x) (0x1010C + ((x) * 0x100)) -@@ -943,6 +1028,8 @@ enum mkt_eth_capabilities { +@@ -721,12 +750,8 @@ enum mtk_clks_map { + MTK_CLK_ETHWARP_WOCPU2, + MTK_CLK_ETHWARP_WOCPU1, + MTK_CLK_ETHWARP_WOCPU0, +- MTK_CLK_TOP_USXGMII_SBUS_0_SEL, +- MTK_CLK_TOP_USXGMII_SBUS_1_SEL, + MTK_CLK_TOP_SGM_0_SEL, + MTK_CLK_TOP_SGM_1_SEL, +- MTK_CLK_TOP_XFI_PHY_0_XTAL_SEL, +- MTK_CLK_TOP_XFI_PHY_1_XTAL_SEL, + MTK_CLK_TOP_ETH_GMII_SEL, + MTK_CLK_TOP_ETH_REFCK_50M_SEL, + MTK_CLK_TOP_ETH_SYS_200M_SEL, +@@ -797,19 +822,9 @@ enum mtk_clks_map { + BIT_ULL(MTK_CLK_GP3) | BIT_ULL(MTK_CLK_XGP1) | \ + BIT_ULL(MTK_CLK_XGP2) | BIT_ULL(MTK_CLK_XGP3) | \ + BIT_ULL(MTK_CLK_CRYPTO) | \ +- BIT_ULL(MTK_CLK_SGMII_TX_250M) | \ +- BIT_ULL(MTK_CLK_SGMII_RX_250M) | \ +- BIT_ULL(MTK_CLK_SGMII2_TX_250M) | \ +- BIT_ULL(MTK_CLK_SGMII2_RX_250M) | \ + BIT_ULL(MTK_CLK_ETHWARP_WOCPU2) | \ + BIT_ULL(MTK_CLK_ETHWARP_WOCPU1) | \ + BIT_ULL(MTK_CLK_ETHWARP_WOCPU0) | \ +- BIT_ULL(MTK_CLK_TOP_USXGMII_SBUS_0_SEL) | \ +- BIT_ULL(MTK_CLK_TOP_USXGMII_SBUS_1_SEL) | \ +- BIT_ULL(MTK_CLK_TOP_SGM_0_SEL) | \ +- BIT_ULL(MTK_CLK_TOP_SGM_1_SEL) | \ +- BIT_ULL(MTK_CLK_TOP_XFI_PHY_0_XTAL_SEL) | \ +- BIT_ULL(MTK_CLK_TOP_XFI_PHY_1_XTAL_SEL) | \ + BIT_ULL(MTK_CLK_TOP_ETH_GMII_SEL) | \ + BIT_ULL(MTK_CLK_TOP_ETH_REFCK_50M_SEL) | \ + BIT_ULL(MTK_CLK_TOP_ETH_SYS_200M_SEL) | \ +@@ -943,6 +958,8 @@ enum mkt_eth_capabilities { MTK_RGMII_BIT = 0, MTK_TRGMII_BIT, MTK_SGMII_BIT, @@ -651,7 +779,7 @@ Signed-off-by: Daniel Golle MTK_ESW_BIT, MTK_GEPHY_BIT, MTK_MUX_BIT, -@@ -963,8 +1050,11 @@ enum mkt_eth_capabilities { +@@ -963,8 +980,11 @@ enum mkt_eth_capabilities { MTK_ETH_MUX_GDM1_TO_GMAC1_ESW_BIT, MTK_ETH_MUX_GMAC2_GMAC0_TO_GEPHY_BIT, MTK_ETH_MUX_U3_GMAC2_TO_QPHY_BIT, @@ -663,7 +791,7 @@ Signed-off-by: Daniel Golle /* PATH BITS */ MTK_ETH_PATH_GMAC1_RGMII_BIT, -@@ -972,14 +1062,21 @@ enum mkt_eth_capabilities { +@@ -972,14 +992,21 @@ enum mkt_eth_capabilities { MTK_ETH_PATH_GMAC1_SGMII_BIT, MTK_ETH_PATH_GMAC2_RGMII_BIT, MTK_ETH_PATH_GMAC2_SGMII_BIT, @@ -685,7 +813,7 @@ Signed-off-by: Daniel Golle #define MTK_ESW BIT_ULL(MTK_ESW_BIT) #define MTK_GEPHY BIT_ULL(MTK_GEPHY_BIT) #define MTK_MUX BIT_ULL(MTK_MUX_BIT) -@@ -1002,10 +1099,16 @@ enum mkt_eth_capabilities { +@@ -1002,10 +1029,16 @@ enum mkt_eth_capabilities { BIT_ULL(MTK_ETH_MUX_GMAC2_GMAC0_TO_GEPHY_BIT) #define MTK_ETH_MUX_U3_GMAC2_TO_QPHY \ BIT_ULL(MTK_ETH_MUX_U3_GMAC2_TO_QPHY_BIT) @@ -702,7 +830,7 @@ Signed-off-by: Daniel Golle /* Supported path present on SoCs */ #define MTK_ETH_PATH_GMAC1_RGMII BIT_ULL(MTK_ETH_PATH_GMAC1_RGMII_BIT) -@@ -1013,8 +1116,13 @@ enum mkt_eth_capabilities { +@@ -1013,8 +1046,13 @@ enum mkt_eth_capabilities { #define MTK_ETH_PATH_GMAC1_SGMII BIT_ULL(MTK_ETH_PATH_GMAC1_SGMII_BIT) #define MTK_ETH_PATH_GMAC2_RGMII BIT_ULL(MTK_ETH_PATH_GMAC2_RGMII_BIT) #define MTK_ETH_PATH_GMAC2_SGMII BIT_ULL(MTK_ETH_PATH_GMAC2_SGMII_BIT) @@ -716,7 +844,7 @@ Signed-off-by: Daniel Golle #define MTK_GMAC1_RGMII (MTK_ETH_PATH_GMAC1_RGMII | MTK_RGMII) #define MTK_GMAC1_TRGMII (MTK_ETH_PATH_GMAC1_TRGMII | MTK_TRGMII) -@@ -1022,7 +1130,12 @@ enum mkt_eth_capabilities { +@@ -1022,7 +1060,12 @@ enum mkt_eth_capabilities { #define MTK_GMAC2_RGMII (MTK_ETH_PATH_GMAC2_RGMII | MTK_RGMII) #define MTK_GMAC2_SGMII (MTK_ETH_PATH_GMAC2_SGMII | MTK_SGMII) #define MTK_GMAC2_GEPHY (MTK_ETH_PATH_GMAC2_GEPHY | MTK_GEPHY) @@ -729,7 +857,7 @@ Signed-off-by: Daniel Golle /* MUXes present on SoCs */ /* 0: GDM1 -> GMAC1, 1: GDM1 -> ESW */ -@@ -1041,10 +1154,20 @@ enum mkt_eth_capabilities { +@@ -1041,10 +1084,20 @@ enum mkt_eth_capabilities { (MTK_ETH_MUX_GMAC1_GMAC2_TO_SGMII_RGMII | MTK_MUX | \ MTK_SHARED_SGMII) @@ -750,7 +878,7 @@ Signed-off-by: Daniel Golle #define MTK_HAS_CAPS(caps, _x) (((caps) & (_x)) == (_x)) #define MT7621_CAPS (MTK_GMAC1_RGMII | MTK_GMAC1_TRGMII | \ -@@ -1076,8 +1199,12 @@ enum mkt_eth_capabilities { +@@ -1076,8 +1129,12 @@ enum mkt_eth_capabilities { MTK_MUX_GMAC12_TO_GEPHY_SGMII | MTK_QDMA | \ MTK_RSTCTRL_PPE1 | MTK_SRAM) @@ -765,56 +893,17 @@ Signed-off-by: Daniel Golle struct mtk_tx_dma_desc_info { dma_addr_t addr; -@@ -1187,6 +1314,24 @@ struct mtk_soc_data { - /* currently no SoC has more than 3 macs */ - #define MTK_MAX_DEVS 3 - -+/* struct mtk_usxgmii_pcs - This structure holds each usxgmii regmap and -+ * associated data -+ * @regmap: The register map pointing at the range used to setup -+ * USXGMII modes -+ * @interface: Currently selected interface mode -+ * @id: The element is used to record the index of PCS -+ * @pcs: Phylink PCS structure -+ */ -+struct mtk_usxgmii_pcs { -+ struct mtk_eth *eth; -+ struct regmap *regmap; -+ struct phylink_pcs *wrapped_sgmii_pcs; -+ phy_interface_t interface; -+ u8 id; -+ unsigned int mode; -+ struct phylink_pcs pcs; -+}; -+ - /* struct mtk_eth - This is the main datasructure for holding the state - * of the driver - * @dev: The device pointer -@@ -1207,6 +1352,12 @@ struct mtk_soc_data { - * @infra: The register map pointing at the range used to setup - * SGMII and GePHY path - * @sgmii_pcs: Pointers to mtk-pcs-lynxi phylink_pcs instances -+ * @sgmii_wrapped_pcs: Pointers to NETSYSv3 wrapper PCS instances -+ * @usxgmii_pll: The register map pointing at the range used to control -+ * the USXGMII SerDes PLL -+ * @regmap_pextp: The register map pointing at the range used to setup -+ * PHYA -+ * @usxgmii_pcs: Pointer to array of pointers to struct for USXGMII PCS - * @pctl: The register map pointing at the range used to setup - * GMAC port drive/slew values - * @dma_refcnt: track how many netdevs are using the DMA engine -@@ -1250,6 +1401,10 @@ struct mtk_eth { - struct regmap *ethsys; - struct regmap *infra; - struct phylink_pcs *sgmii_pcs[MTK_MAX_DEVS]; -+ struct regmap *toprgu; -+ struct regmap *usxgmii_pll; -+ struct regmap *regmap_pextp[MTK_MAX_DEVS]; -+ struct mtk_usxgmii_pcs *usxgmii_pcs[MTK_MAX_DEVS]; - struct regmap *pctl; - bool hwlro; - refcount_t dma_refcnt; -@@ -1437,6 +1592,19 @@ static inline u32 mtk_get_ib2_multicast_ +@@ -1314,6 +1371,9 @@ struct mtk_mac { + struct device_node *of_node; + struct phylink *phylink; + struct phylink_config phylink_config; ++ struct phylink_pcs *sgmii_pcs; ++ struct phylink_pcs *usxgmii_pcs; ++ struct phy *pextp; + struct mtk_eth *hw; + struct mtk_hw_stats *hw_stats; + __be32 hwlro_ip[MTK_MAX_LRO_IP_CNT]; +@@ -1437,6 +1497,19 @@ static inline u32 mtk_get_ib2_multicast_ return MTK_FOE_IB2_MULTICAST; } @@ -834,7 +923,7 @@ Signed-off-by: Daniel Golle /* read the hardware status register */ void mtk_stats_update_mac(struct mtk_mac *mac); -@@ -1445,8 +1613,10 @@ u32 mtk_r32(struct mtk_eth *eth, unsigne +@@ -1445,8 +1518,10 @@ u32 mtk_r32(struct mtk_eth *eth, unsigne u32 mtk_m32(struct mtk_eth *eth, u32 mask, u32 set, unsigned int reg); int mtk_gmac_sgmii_path_setup(struct mtk_eth *eth, int mac_id); @@ -845,760 +934,3 @@ Signed-off-by: Daniel Golle int mtk_eth_offload_init(struct mtk_eth *eth); int mtk_eth_setup_tc(struct net_device *dev, enum tc_setup_type type, -@@ -1456,5 +1626,63 @@ int mtk_flow_offload_cmd(struct mtk_eth - void mtk_flow_offload_cleanup(struct mtk_eth *eth, struct list_head *list); - void mtk_eth_set_dma_device(struct mtk_eth *eth, struct device *dma_dev); - -+static inline int mtk_mac2xgmii_id(struct mtk_eth *eth, int mac_id) -+{ -+ int xgmii_id = mac_id; -+ -+ if (mtk_is_netsys_v3_or_greater(eth)) { -+ switch (mac_id) { -+ case MTK_GMAC1_ID: -+ case MTK_GMAC2_ID: -+ xgmii_id = 1; -+ break; -+ case MTK_GMAC3_ID: -+ xgmii_id = 0; -+ break; -+ default: -+ xgmii_id = -1; -+ } -+ } -+ -+ return MTK_HAS_CAPS(eth->soc->caps, MTK_SHARED_SGMII) ? 0 : xgmii_id; -+} -+ -+static inline int mtk_xgmii2mac_id(struct mtk_eth *eth, int xgmii_id) -+{ -+ int mac_id = xgmii_id; -+ -+ if (mtk_is_netsys_v3_or_greater(eth)) { -+ switch (xgmii_id) { -+ case 0: -+ mac_id = 2; -+ break; -+ case 1: -+ mac_id = 1; -+ break; -+ default: -+ mac_id = -1; -+ } -+ } -+ -+ return mac_id; -+} -+ -+#ifdef CONFIG_NET_MEDIATEK_SOC_USXGMII -+struct phylink_pcs *mtk_sgmii_wrapper_select_pcs(struct mtk_eth *eth, int id); -+struct phylink_pcs *mtk_usxgmii_select_pcs(struct mtk_eth *eth, int id); -+int mtk_usxgmii_init(struct mtk_eth *eth); -+#else -+static inline struct phylink_pcs *mtk_sgmii_wrapper_select_pcs(struct mtk_eth *eth, int id) -+{ -+ return NULL; -+} -+ -+static inline struct phylink_pcs *mtk_usxgmii_select_pcs(struct mtk_eth *eth, int id) -+{ -+ return NULL; -+} -+ -+static inline int mtk_usxgmii_init(struct mtk_eth *eth) { return 0; } -+#endif /* NET_MEDIATEK_SOC_USXGMII */ - - #endif /* MTK_ETH_H */ ---- /dev/null -+++ b/drivers/net/ethernet/mediatek/mtk_usxgmii.c -@@ -0,0 +1,690 @@ -+// SPDX-License-Identifier: GPL-2.0 -+/* -+ * Copyright (c) 2023 MediaTek Inc. -+ * Author: Henry Yen -+ * Daniel Golle -+ */ -+ -+#include -+#include -+#include -+#include "mtk_eth_soc.h" -+ -+static struct mtk_usxgmii_pcs *pcs_to_mtk_usxgmii_pcs(struct phylink_pcs *pcs) -+{ -+ return container_of(pcs, struct mtk_usxgmii_pcs, pcs); -+} -+ -+static int mtk_xfi_pextp_init(struct mtk_eth *eth) -+{ -+ struct device *dev = eth->dev; -+ struct device_node *r = dev->of_node; -+ struct device_node *np; -+ int i; -+ -+ for (i = 0; i < MTK_MAX_DEVS; i++) { -+ np = of_parse_phandle(r, "mediatek,xfi-pextp", i); -+ if (!np) -+ break; -+ -+ eth->regmap_pextp[i] = syscon_node_to_regmap(np); -+ if (IS_ERR(eth->regmap_pextp[i])) -+ return PTR_ERR(eth->regmap_pextp[i]); -+ } -+ -+ return 0; -+} -+ -+static int mtk_xfi_pll_init(struct mtk_eth *eth) -+{ -+ struct device_node *r = eth->dev->of_node; -+ struct device_node *np; -+ -+ np = of_parse_phandle(r, "mediatek,xfi-pll", 0); -+ if (!np) -+ return -1; -+ -+ eth->usxgmii_pll = syscon_node_to_regmap(np); -+ if (IS_ERR(eth->usxgmii_pll)) -+ return PTR_ERR(eth->usxgmii_pll); -+ -+ return 0; -+} -+ -+static int mtk_toprgu_init(struct mtk_eth *eth) -+{ -+ struct device_node *r = eth->dev->of_node; -+ struct device_node *np; -+ -+ np = of_parse_phandle(r, "mediatek,toprgu", 0); -+ if (!np) -+ return -1; -+ -+ eth->toprgu = syscon_node_to_regmap(np); -+ if (IS_ERR(eth->toprgu)) -+ return PTR_ERR(eth->toprgu); -+ -+ return 0; -+} -+ -+static int mtk_xfi_pll_enable(struct mtk_eth *eth) -+{ -+ u32 val = 0; -+ -+ if (!eth->usxgmii_pll) -+ return -EINVAL; -+ -+ /* Add software workaround for USXGMII PLL TCL issue */ -+ regmap_write(eth->usxgmii_pll, XFI_PLL_ANA_GLB8, RG_XFI_PLL_ANA_SWWA); -+ -+ regmap_read(eth->usxgmii_pll, XFI_PLL_DIG_GLB8, &val); -+ val |= RG_XFI_PLL_EN; -+ regmap_write(eth->usxgmii_pll, XFI_PLL_DIG_GLB8, val); -+ -+ return 0; -+} -+ -+static void mtk_usxgmii_setup_phya(struct regmap *pextp, phy_interface_t interface, int id) -+{ -+ bool is_10g = (interface == PHY_INTERFACE_MODE_10GBASER || -+ interface == PHY_INTERFACE_MODE_USXGMII); -+ bool is_2p5g = (interface == PHY_INTERFACE_MODE_2500BASEX); -+ bool is_5g = (interface == PHY_INTERFACE_MODE_5GBASER); -+ -+ /* Setup operation mode */ -+ if (is_10g) -+ regmap_write(pextp, 0x9024, 0x00C9071C); -+ else -+ regmap_write(pextp, 0x9024, 0x00D9071C); -+ -+ if (is_5g) -+ regmap_write(pextp, 0x2020, 0xAAA5A5AA); -+ else -+ regmap_write(pextp, 0x2020, 0xAA8585AA); -+ -+ if (is_2p5g || is_5g || is_10g) { -+ regmap_write(pextp, 0x2030, 0x0C020707); -+ regmap_write(pextp, 0x2034, 0x0E050F0F); -+ regmap_write(pextp, 0x2040, 0x00140032); -+ } else { -+ regmap_write(pextp, 0x2030, 0x0C020207); -+ regmap_write(pextp, 0x2034, 0x0E05050F); -+ regmap_write(pextp, 0x2040, 0x00200032); -+ } -+ -+ if (is_2p5g || is_10g) -+ regmap_write(pextp, 0x50F0, 0x00C014AA); -+ else if (is_5g) -+ regmap_write(pextp, 0x50F0, 0x00C018AA); -+ else -+ regmap_write(pextp, 0x50F0, 0x00C014BA); -+ -+ if (is_5g) { -+ regmap_write(pextp, 0x50E0, 0x3777812B); -+ regmap_write(pextp, 0x506C, 0x005C9CFF); -+ regmap_write(pextp, 0x5070, 0x9DFAFAFA); -+ regmap_write(pextp, 0x5074, 0x273F3F3F); -+ regmap_write(pextp, 0x5078, 0xA8883868); -+ regmap_write(pextp, 0x507C, 0x14661466); -+ } else { -+ regmap_write(pextp, 0x50E0, 0x3777C12B); -+ regmap_write(pextp, 0x506C, 0x005F9CFF); -+ regmap_write(pextp, 0x5070, 0x9D9DFAFA); -+ regmap_write(pextp, 0x5074, 0x27273F3F); -+ regmap_write(pextp, 0x5078, 0xA7883C68); -+ regmap_write(pextp, 0x507C, 0x11661166); -+ } -+ -+ if (is_2p5g || is_10g) { -+ regmap_write(pextp, 0x5080, 0x0E000AAF); -+ regmap_write(pextp, 0x5084, 0x08080D0D); -+ regmap_write(pextp, 0x5088, 0x02030909); -+ } else if (is_5g) { -+ regmap_write(pextp, 0x5080, 0x0E001ABF); -+ regmap_write(pextp, 0x5084, 0x080B0D0D); -+ regmap_write(pextp, 0x5088, 0x02050909); -+ } else { -+ regmap_write(pextp, 0x5080, 0x0E000EAF); -+ regmap_write(pextp, 0x5084, 0x08080E0D); -+ regmap_write(pextp, 0x5088, 0x02030B09); -+ } -+ -+ if (is_5g) { -+ regmap_write(pextp, 0x50E4, 0x0C000000); -+ regmap_write(pextp, 0x50E8, 0x04000000); -+ } else { -+ regmap_write(pextp, 0x50E4, 0x0C0C0000); -+ regmap_write(pextp, 0x50E8, 0x04040000); -+ } -+ -+ if (is_2p5g || mtk_interface_mode_is_xgmii(interface)) -+ regmap_write(pextp, 0x50EC, 0x0F0F0C06); -+ else -+ regmap_write(pextp, 0x50EC, 0x0F0F0606); -+ -+ if (is_5g) { -+ regmap_write(pextp, 0x50A8, 0x50808C8C); -+ regmap_write(pextp, 0x6004, 0x18000000); -+ } else { -+ regmap_write(pextp, 0x50A8, 0x506E8C8C); -+ regmap_write(pextp, 0x6004, 0x18190000); -+ } -+ -+ if (is_10g) -+ regmap_write(pextp, 0x00F8, 0x01423342); -+ else if (is_5g) -+ regmap_write(pextp, 0x00F8, 0x00A132A1); -+ else if (is_2p5g) -+ regmap_write(pextp, 0x00F8, 0x009C329C); -+ else -+ regmap_write(pextp, 0x00F8, 0x00FA32FA); -+ -+ /* Force SGDT_OUT off and select PCS */ -+ if (mtk_interface_mode_is_xgmii(interface)) -+ regmap_write(pextp, 0x00F4, 0x80201F20); -+ else -+ regmap_write(pextp, 0x00F4, 0x80201F21); -+ -+ /* Force GLB_CKDET_OUT */ -+ regmap_write(pextp, 0x0030, 0x00050C00); -+ -+ /* Force AEQ on */ -+ regmap_write(pextp, 0x0070, 0x02002800); -+ ndelay(1020); -+ -+ /* Setup DA default value */ -+ regmap_write(pextp, 0x30B0, 0x00000020); -+ regmap_write(pextp, 0x3028, 0x00008A01); -+ regmap_write(pextp, 0x302C, 0x0000A884); -+ regmap_write(pextp, 0x3024, 0x00083002); -+ if (mtk_interface_mode_is_xgmii(interface)) { -+ regmap_write(pextp, 0x3010, 0x00022220); -+ regmap_write(pextp, 0x5064, 0x0F020A01); -+ regmap_write(pextp, 0x50B4, 0x06100600); -+ if (interface == PHY_INTERFACE_MODE_USXGMII) -+ regmap_write(pextp, 0x3048, 0x40704000); -+ else -+ regmap_write(pextp, 0x3048, 0x47684100); -+ } else { -+ regmap_write(pextp, 0x3010, 0x00011110); -+ regmap_write(pextp, 0x3048, 0x40704000); -+ } -+ -+ if (!mtk_interface_mode_is_xgmii(interface) && !is_2p5g) -+ regmap_write(pextp, 0x3064, 0x0000C000); -+ -+ if (interface == PHY_INTERFACE_MODE_USXGMII) { -+ regmap_write(pextp, 0x3050, 0xA8000000); -+ regmap_write(pextp, 0x3054, 0x000000AA); -+ } else if (mtk_interface_mode_is_xgmii(interface)) { -+ regmap_write(pextp, 0x3050, 0x00000000); -+ regmap_write(pextp, 0x3054, 0x00000000); -+ } else { -+ regmap_write(pextp, 0x3050, 0xA8000000); -+ regmap_write(pextp, 0x3054, 0x000000AA); -+ } -+ -+ if (mtk_interface_mode_is_xgmii(interface)) -+ regmap_write(pextp, 0x306C, 0x00000F00); -+ else if (is_2p5g) -+ regmap_write(pextp, 0x306C, 0x22000F00); -+ else -+ regmap_write(pextp, 0x306C, 0x20200F00); -+ -+ if (interface == PHY_INTERFACE_MODE_10GBASER && id == 0) -+ regmap_write(pextp, 0xA008, 0x0007B400); -+ -+ if (mtk_interface_mode_is_xgmii(interface)) -+ regmap_write(pextp, 0xA060, 0x00040000); -+ else -+ regmap_write(pextp, 0xA060, 0x00050000); -+ -+ if (is_10g) -+ regmap_write(pextp, 0x90D0, 0x00000001); -+ else if (is_5g) -+ regmap_write(pextp, 0x90D0, 0x00000003); -+ else if (is_2p5g) -+ regmap_write(pextp, 0x90D0, 0x00000005); -+ else -+ regmap_write(pextp, 0x90D0, 0x00000007); -+ -+ /* Release reset */ -+ regmap_write(pextp, 0x0070, 0x0200E800); -+ usleep_range(150, 500); -+ -+ /* Switch to P0 */ -+ regmap_write(pextp, 0x0070, 0x0200C111); -+ ndelay(1020); -+ regmap_write(pextp, 0x0070, 0x0200C101); -+ usleep_range(15, 50); -+ -+ if (mtk_interface_mode_is_xgmii(interface)) { -+ /* Switch to Gen3 */ -+ regmap_write(pextp, 0x0070, 0x0202C111); -+ } else { -+ /* Switch to Gen2 */ -+ regmap_write(pextp, 0x0070, 0x0201C111); -+ } -+ ndelay(1020); -+ if (mtk_interface_mode_is_xgmii(interface)) -+ regmap_write(pextp, 0x0070, 0x0202C101); -+ else -+ regmap_write(pextp, 0x0070, 0x0201C101); -+ usleep_range(100, 500); -+ regmap_write(pextp, 0x30B0, 0x00000030); -+ if (mtk_interface_mode_is_xgmii(interface)) -+ regmap_write(pextp, 0x00F4, 0x80201F00); -+ else -+ regmap_write(pextp, 0x00F4, 0x80201F01); -+ -+ regmap_write(pextp, 0x3040, 0x30000000); -+ usleep_range(400, 1000); -+} -+ -+static void mtk_usxgmii_reset(struct mtk_eth *eth, int id) -+{ -+ u32 toggle, val; -+ -+ if (id >= MTK_MAX_DEVS || !eth->toprgu) -+ return; -+ -+ switch (id) { -+ case 0: -+ toggle = SWSYSRST_XFI_PEXPT0_GRST | SWSYSRST_XFI0_GRST | -+ SWSYSRST_SGMII0_GRST; -+ break; -+ case 1: -+ toggle = SWSYSRST_XFI_PEXPT1_GRST | SWSYSRST_XFI1_GRST | -+ SWSYSRST_SGMII1_GRST; -+ break; -+ default: -+ return; -+ } -+ -+ /* Enable software reset */ -+ regmap_set_bits(eth->toprgu, TOPRGU_SWSYSRST_EN, toggle); -+ -+ /* Assert USXGMII reset */ -+ regmap_set_bits(eth->toprgu, TOPRGU_SWSYSRST, -+ FIELD_PREP(SWSYSRST_UNLOCK_KEY, 0x88) | toggle); -+ -+ usleep_range(100, 500); -+ -+ /* De-assert USXGMII reset */ -+ regmap_read(eth->toprgu, TOPRGU_SWSYSRST, &val); -+ val |= FIELD_PREP(SWSYSRST_UNLOCK_KEY, 0x88); -+ val &= ~toggle; -+ regmap_write(eth->toprgu, TOPRGU_SWSYSRST, val); -+ -+ /* Disable software reset */ -+ regmap_clear_bits(eth->toprgu, TOPRGU_SWSYSRST_EN, toggle); -+ -+ mdelay(10); -+} -+ -+/* As the USXGMII PHYA is shared with the 1000Base-X/2500Base-X/Cisco SGMII unit -+ * the psc-mtk-lynxi instance needs to be wrapped, so that calls to .pcs_config -+ * also trigger an initial reset and subsequent configuration of the PHYA. -+ */ -+struct mtk_sgmii_wrapper_pcs { -+ struct mtk_eth *eth; -+ struct phylink_pcs *wrapped_pcs; -+ u8 id; -+ struct phylink_pcs pcs; -+}; -+ -+static int mtk_sgmii_wrapped_pcs_config(struct phylink_pcs *pcs, -+ unsigned int mode, -+ phy_interface_t interface, -+ const unsigned long *advertising, -+ bool permit_pause_to_mac) -+{ -+ struct mtk_sgmii_wrapper_pcs *wp = container_of(pcs, struct mtk_sgmii_wrapper_pcs, pcs); -+ bool full_reconf; -+ int ret; -+ -+ full_reconf = interface != wp->eth->usxgmii_pcs[wp->id]->interface; -+ if (full_reconf) { -+ mtk_xfi_pll_enable(wp->eth); -+ mtk_usxgmii_reset(wp->eth, wp->id); -+ } -+ -+ ret = wp->wrapped_pcs->ops->pcs_config(wp->wrapped_pcs, mode, interface, -+ advertising, permit_pause_to_mac); -+ -+ if (full_reconf) -+ mtk_usxgmii_setup_phya(wp->eth->regmap_pextp[wp->id], interface, wp->id); -+ -+ wp->eth->usxgmii_pcs[wp->id]->interface = interface; -+ -+ return ret; -+} -+ -+static void mtk_sgmii_wrapped_pcs_get_state(struct phylink_pcs *pcs, -+ struct phylink_link_state *state) -+{ -+ struct mtk_sgmii_wrapper_pcs *wp = container_of(pcs, struct mtk_sgmii_wrapper_pcs, pcs); -+ -+ return wp->wrapped_pcs->ops->pcs_get_state(wp->wrapped_pcs, state); -+} -+ -+static void mtk_sgmii_wrapped_pcs_an_restart(struct phylink_pcs *pcs) -+{ -+ struct mtk_sgmii_wrapper_pcs *wp = container_of(pcs, struct mtk_sgmii_wrapper_pcs, pcs); -+ -+ wp->wrapped_pcs->ops->pcs_an_restart(wp->wrapped_pcs); -+} -+ -+static void mtk_sgmii_wrapped_pcs_link_up(struct phylink_pcs *pcs, -+ unsigned int mode, -+ phy_interface_t interface, int speed, -+ int duplex) -+{ -+ struct mtk_sgmii_wrapper_pcs *wp = container_of(pcs, struct mtk_sgmii_wrapper_pcs, pcs); -+ -+ wp->wrapped_pcs->ops->pcs_link_up(wp->wrapped_pcs, mode, interface, speed, duplex); -+} -+ -+static void mtk_sgmii_wrapped_pcs_disable(struct phylink_pcs *pcs) -+{ -+ struct mtk_sgmii_wrapper_pcs *wp = container_of(pcs, struct mtk_sgmii_wrapper_pcs, pcs); -+ -+ wp->wrapped_pcs->ops->pcs_disable(wp->wrapped_pcs); -+ -+ wp->eth->usxgmii_pcs[wp->id]->interface = PHY_INTERFACE_MODE_NA; -+} -+ -+static const struct phylink_pcs_ops mtk_sgmii_wrapped_pcs_ops = { -+ .pcs_get_state = mtk_sgmii_wrapped_pcs_get_state, -+ .pcs_config = mtk_sgmii_wrapped_pcs_config, -+ .pcs_an_restart = mtk_sgmii_wrapped_pcs_an_restart, -+ .pcs_link_up = mtk_sgmii_wrapped_pcs_link_up, -+ .pcs_disable = mtk_sgmii_wrapped_pcs_disable, -+}; -+ -+static int mtk_sgmii_wrapper_init(struct mtk_eth *eth) -+{ -+ struct mtk_sgmii_wrapper_pcs *wp; -+ int i; -+ -+ for (i = 0; i < MTK_MAX_DEVS; i++) { -+ if (!eth->sgmii_pcs[i]) -+ continue; -+ -+ if (!eth->usxgmii_pcs[i]) -+ continue; -+ -+ /* Make sure all PCS ops are supported by wrapped PCS */ -+ if (!eth->sgmii_pcs[i]->ops->pcs_get_state || -+ !eth->sgmii_pcs[i]->ops->pcs_config || -+ !eth->sgmii_pcs[i]->ops->pcs_an_restart || -+ !eth->sgmii_pcs[i]->ops->pcs_link_up || -+ !eth->sgmii_pcs[i]->ops->pcs_disable) -+ return -EOPNOTSUPP; -+ -+ wp = devm_kzalloc(eth->dev, sizeof(*wp), GFP_KERNEL); -+ if (!wp) -+ return -ENOMEM; -+ -+ wp->wrapped_pcs = eth->sgmii_pcs[i]; -+ wp->id = i; -+ wp->pcs.poll = true; -+ wp->pcs.ops = &mtk_sgmii_wrapped_pcs_ops; -+ wp->eth = eth; -+ -+ eth->usxgmii_pcs[i]->wrapped_sgmii_pcs = &wp->pcs; -+ } -+ -+ return 0; -+} -+ -+struct phylink_pcs *mtk_sgmii_wrapper_select_pcs(struct mtk_eth *eth, int mac_id) -+{ -+ u32 xgmii_id = mtk_mac2xgmii_id(eth, mac_id); -+ -+ if (!eth->usxgmii_pcs[xgmii_id]) -+ return NULL; -+ -+ return eth->usxgmii_pcs[xgmii_id]->wrapped_sgmii_pcs; -+} -+ -+static int mtk_usxgmii_pcs_config(struct phylink_pcs *pcs, unsigned int mode, -+ phy_interface_t interface, -+ const unsigned long *advertising, -+ bool permit_pause_to_mac) -+{ -+ struct mtk_usxgmii_pcs *mpcs = pcs_to_mtk_usxgmii_pcs(pcs); -+ struct mtk_eth *eth = mpcs->eth; -+ struct regmap *pextp = eth->regmap_pextp[mpcs->id]; -+ unsigned int an_ctrl = 0, link_timer = 0, xfi_mode = 0, adapt_mode = 0; -+ bool mode_changed = false; -+ -+ if (!pextp) -+ return -ENODEV; -+ -+ if (interface == PHY_INTERFACE_MODE_USXGMII) { -+ an_ctrl = FIELD_PREP(USXGMII_AN_SYNC_CNT, 0x1FF) | USXGMII_AN_ENABLE; -+ link_timer = FIELD_PREP(USXGMII_LINK_TIMER_IDLE_DETECT, 0x7B) | -+ FIELD_PREP(USXGMII_LINK_TIMER_COMP_ACK_DETECT, 0x7B) | -+ FIELD_PREP(USXGMII_LINK_TIMER_AN_RESTART, 0x7B); -+ xfi_mode = FIELD_PREP(USXGMII_XFI_RX_MODE, USXGMII_XFI_RX_MODE_10G) | -+ FIELD_PREP(USXGMII_XFI_TX_MODE, USXGMII_XFI_TX_MODE_10G); -+ } else if (interface == PHY_INTERFACE_MODE_10GBASER) { -+ an_ctrl = FIELD_PREP(USXGMII_AN_SYNC_CNT, 0x1FF); -+ link_timer = FIELD_PREP(USXGMII_LINK_TIMER_IDLE_DETECT, 0x7B) | -+ FIELD_PREP(USXGMII_LINK_TIMER_COMP_ACK_DETECT, 0x7B) | -+ FIELD_PREP(USXGMII_LINK_TIMER_AN_RESTART, 0x7B); -+ xfi_mode = FIELD_PREP(USXGMII_XFI_RX_MODE, USXGMII_XFI_RX_MODE_10G) | -+ FIELD_PREP(USXGMII_XFI_TX_MODE, USXGMII_XFI_TX_MODE_10G); -+ adapt_mode = USXGMII_RATE_UPDATE_MODE; -+ } else if (interface == PHY_INTERFACE_MODE_5GBASER) { -+ an_ctrl = FIELD_PREP(USXGMII_AN_SYNC_CNT, 0xFF); -+ link_timer = FIELD_PREP(USXGMII_LINK_TIMER_IDLE_DETECT, 0x3D) | -+ FIELD_PREP(USXGMII_LINK_TIMER_COMP_ACK_DETECT, 0x3D) | -+ FIELD_PREP(USXGMII_LINK_TIMER_AN_RESTART, 0x3D); -+ xfi_mode = FIELD_PREP(USXGMII_XFI_RX_MODE, USXGMII_XFI_RX_MODE_5G) | -+ FIELD_PREP(USXGMII_XFI_TX_MODE, USXGMII_XFI_TX_MODE_5G); -+ adapt_mode = USXGMII_RATE_UPDATE_MODE; -+ } else { -+ return -EINVAL; -+ } -+ -+ adapt_mode |= FIELD_PREP(USXGMII_RATE_ADAPT_MODE, USXGMII_RATE_ADAPT_MODE_X1); -+ -+ if (mpcs->interface != interface) { -+ mpcs->interface = interface; -+ mode_changed = true; -+ } -+ -+ mtk_xfi_pll_enable(eth); -+ mtk_usxgmii_reset(eth, mpcs->id); -+ -+ /* Setup USXGMII AN ctrl */ -+ regmap_update_bits(mpcs->regmap, RG_PCS_AN_CTRL0, -+ USXGMII_AN_SYNC_CNT | USXGMII_AN_ENABLE, -+ an_ctrl); -+ -+ regmap_update_bits(mpcs->regmap, RG_PCS_AN_CTRL2, -+ USXGMII_LINK_TIMER_IDLE_DETECT | -+ USXGMII_LINK_TIMER_COMP_ACK_DETECT | -+ USXGMII_LINK_TIMER_AN_RESTART, -+ link_timer); -+ -+ mpcs->mode = mode; -+ -+ /* Gated MAC CK */ -+ regmap_update_bits(mpcs->regmap, RG_PHY_TOP_SPEED_CTRL1, -+ USXGMII_MAC_CK_GATED, USXGMII_MAC_CK_GATED); -+ -+ /* Enable interface force mode */ -+ regmap_update_bits(mpcs->regmap, RG_PHY_TOP_SPEED_CTRL1, -+ USXGMII_IF_FORCE_EN, USXGMII_IF_FORCE_EN); -+ -+ /* Setup USXGMII adapt mode */ -+ regmap_update_bits(mpcs->regmap, RG_PHY_TOP_SPEED_CTRL1, -+ USXGMII_RATE_UPDATE_MODE | USXGMII_RATE_ADAPT_MODE, -+ adapt_mode); -+ -+ /* Setup USXGMII speed */ -+ regmap_update_bits(mpcs->regmap, RG_PHY_TOP_SPEED_CTRL1, -+ USXGMII_XFI_RX_MODE | USXGMII_XFI_TX_MODE, -+ xfi_mode); -+ -+ usleep_range(1, 10); -+ -+ /* Un-gated MAC CK */ -+ regmap_update_bits(mpcs->regmap, RG_PHY_TOP_SPEED_CTRL1, -+ USXGMII_MAC_CK_GATED, 0); -+ -+ usleep_range(1, 10); -+ -+ /* Disable interface force mode for the AN mode */ -+ if (an_ctrl & USXGMII_AN_ENABLE) -+ regmap_update_bits(mpcs->regmap, RG_PHY_TOP_SPEED_CTRL1, -+ USXGMII_IF_FORCE_EN, 0); -+ -+ /* Setup USXGMIISYS with the determined property */ -+ mtk_usxgmii_setup_phya(pextp, interface, mpcs->id); -+ -+ return mode_changed; -+} -+ -+static void mtk_usxgmii_pcs_get_state(struct phylink_pcs *pcs, -+ struct phylink_link_state *state) -+{ -+ struct mtk_usxgmii_pcs *mpcs = pcs_to_mtk_usxgmii_pcs(pcs); -+ struct mtk_eth *eth = mpcs->eth; -+ struct mtk_mac *mac = eth->mac[mtk_xgmii2mac_id(eth, mpcs->id)]; -+ u32 val = 0; -+ -+ regmap_read(mpcs->regmap, RG_PCS_AN_CTRL0, &val); -+ if (FIELD_GET(USXGMII_AN_ENABLE, val)) { -+ /* Refresh LPA by inverting LPA_LATCH */ -+ regmap_read(mpcs->regmap, RG_PCS_AN_STS0, &val); -+ regmap_update_bits(mpcs->regmap, RG_PCS_AN_STS0, -+ USXGMII_LPA_LATCH, -+ !(val & USXGMII_LPA_LATCH)); -+ -+ regmap_read(mpcs->regmap, RG_PCS_AN_STS0, &val); -+ -+ phylink_decode_usxgmii_word(state, FIELD_GET(USXGMII_PCS_AN_WORD, -+ val)); -+ -+ state->interface = mpcs->interface; -+ } else { -+ val = mtk_r32(mac->hw, MTK_XGMAC_STS(mac->id)); -+ -+ if (mac->id == MTK_GMAC2_ID) -+ val >>= 16; -+ -+ switch (FIELD_GET(MTK_USXGMII_PCS_MODE, val)) { -+ case 0: -+ state->speed = SPEED_10000; -+ break; -+ case 1: -+ state->speed = SPEED_5000; -+ break; -+ case 2: -+ state->speed = SPEED_2500; -+ break; -+ case 3: -+ state->speed = SPEED_1000; -+ break; -+ } -+ -+ state->interface = mpcs->interface; -+ state->link = FIELD_GET(MTK_USXGMII_PCS_LINK, val); -+ state->duplex = DUPLEX_FULL; -+ } -+ -+ /* Continuously repeat re-configuration sequence until link comes up */ -+ if (state->link == 0) -+ mtk_usxgmii_pcs_config(pcs, mpcs->mode, -+ state->interface, NULL, false); -+} -+ -+static void mtk_usxgmii_pcs_restart_an(struct phylink_pcs *pcs) -+{ -+ struct mtk_usxgmii_pcs *mpcs = pcs_to_mtk_usxgmii_pcs(pcs); -+ unsigned int val = 0; -+ -+ if (!mpcs->regmap) -+ return; -+ -+ regmap_read(mpcs->regmap, RG_PCS_AN_CTRL0, &val); -+ val |= USXGMII_AN_RESTART; -+ regmap_write(mpcs->regmap, RG_PCS_AN_CTRL0, val); -+} -+ -+static void mtk_usxgmii_pcs_link_up(struct phylink_pcs *pcs, unsigned int mode, -+ phy_interface_t interface, -+ int speed, int duplex) -+{ -+ /* Reconfiguring USXGMII to ensure the quality of the RX signal -+ * after the line side link up. -+ */ -+ mtk_usxgmii_pcs_config(pcs, mode, -+ interface, NULL, false); -+} -+ -+static const struct phylink_pcs_ops mtk_usxgmii_pcs_ops = { -+ .pcs_config = mtk_usxgmii_pcs_config, -+ .pcs_get_state = mtk_usxgmii_pcs_get_state, -+ .pcs_an_restart = mtk_usxgmii_pcs_restart_an, -+ .pcs_link_up = mtk_usxgmii_pcs_link_up, -+}; -+ -+int mtk_usxgmii_init(struct mtk_eth *eth) -+{ -+ struct device_node *r = eth->dev->of_node; -+ struct device *dev = eth->dev; -+ struct device_node *np; -+ int i, ret; -+ -+ for (i = 0; i < MTK_MAX_DEVS; i++) { -+ np = of_parse_phandle(r, "mediatek,usxgmiisys", i); -+ if (!np) -+ break; -+ -+ eth->usxgmii_pcs[i] = devm_kzalloc(dev, sizeof(*eth->usxgmii_pcs[i]), GFP_KERNEL); -+ if (!eth->usxgmii_pcs[i]) -+ return -ENOMEM; -+ -+ eth->usxgmii_pcs[i]->id = i; -+ eth->usxgmii_pcs[i]->eth = eth; -+ eth->usxgmii_pcs[i]->regmap = syscon_node_to_regmap(np); -+ if (IS_ERR(eth->usxgmii_pcs[i]->regmap)) -+ return PTR_ERR(eth->usxgmii_pcs[i]->regmap); -+ -+ eth->usxgmii_pcs[i]->pcs.ops = &mtk_usxgmii_pcs_ops; -+ eth->usxgmii_pcs[i]->pcs.poll = true; -+ eth->usxgmii_pcs[i]->interface = PHY_INTERFACE_MODE_NA; -+ eth->usxgmii_pcs[i]->mode = -1; -+ -+ of_node_put(np); -+ } -+ -+ ret = mtk_xfi_pextp_init(eth); -+ if (ret) -+ return ret; -+ -+ ret = mtk_xfi_pll_init(eth); -+ if (ret) -+ return ret; -+ -+ ret = mtk_toprgu_init(eth); -+ if (ret) -+ return ret; -+ -+ return mtk_sgmii_wrapper_init(eth); -+} -+ -+struct phylink_pcs *mtk_usxgmii_select_pcs(struct mtk_eth *eth, int mac_id) -+{ -+ u32 xgmii_id = mtk_mac2xgmii_id(eth, mac_id); -+ -+ if (!eth->usxgmii_pcs[xgmii_id]->regmap) -+ return NULL; -+ -+ return ð->usxgmii_pcs[xgmii_id]->pcs; -+} diff --git a/target/linux/generic/pending-6.1/738-net-ethernet-mtk_eth_soc-set-coherent-mask-to-get-PP.patch b/target/linux/generic/pending-6.1/738-net-ethernet-mtk_eth_soc-set-coherent-mask-to-get-PP.patch new file mode 100644 index 000000000..b598d1586 --- /dev/null +++ b/target/linux/generic/pending-6.1/738-net-ethernet-mtk_eth_soc-set-coherent-mask-to-get-PP.patch @@ -0,0 +1,46 @@ +From dee3f591103910c8d8b2a6d57879ccd2a4be4b10 Mon Sep 17 00:00:00 2001 +Message-ID: +From: Daniel Golle +Date: Wed, 24 Jan 2024 03:19:49 +0000 +Subject: [PATCH net] net: ethernet: mtk_eth_soc: set coherent mask to get PPE + working +To: Felix Fietkau , + Sean Wang , + Mark Lee , + Lorenzo Bianconi , + David S. Miller , + Eric Dumazet , + Jakub Kicinski , + Paolo Abeni , + Matthias Brugger , + AngeloGioacchino Del Regno , + Daniel Golle , + netdev@vger.kernel.org, + linux-kernel@vger.kernel.org, + linux-arm-kernel@lists.infradead.org, + linux-mediatek@lists.infradead.org + +Set DMA coherent mask to 32-bit which makes PPE offloading engine start +working on BPi-R4 which got 4 GiB of RAM. + +Fixes: 2d75891ebc09 ("net: ethernet: mtk_eth_soc: support 36-bit DMA addressing on MT7988") +Suggested-by: Elad Yifee +Signed-off-by: Daniel Golle +--- + drivers/net/ethernet/mediatek/mtk_eth_soc.c | 5 ++++- + 1 file changed, 4 insertions(+), 1 deletion(-) + +--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c ++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c +@@ -4963,7 +4963,10 @@ static int mtk_probe(struct platform_dev + } + + if (MTK_HAS_CAPS(eth->soc->caps, MTK_36BIT_DMA)) { +- err = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(36)); ++ err = dma_set_mask(&pdev->dev, DMA_BIT_MASK(36)); ++ if (!err) ++ err = dma_set_coherent_mask(&pdev->dev, DMA_BIT_MASK(32)); ++ + if (err) { + dev_err(&pdev->dev, "Wrong DMA config\n"); + return -EINVAL; diff --git a/target/linux/generic/pending-6.1/739-01-dt-bindings-phy-mediatek-xfi-tphy-add-new-bindings.patch b/target/linux/generic/pending-6.1/739-01-dt-bindings-phy-mediatek-xfi-tphy-add-new-bindings.patch new file mode 100644 index 000000000..1f1c40b1d --- /dev/null +++ b/target/linux/generic/pending-6.1/739-01-dt-bindings-phy-mediatek-xfi-tphy-add-new-bindings.patch @@ -0,0 +1,136 @@ +From patchwork Thu Feb 1 21:52:20 2024 +Content-Type: text/plain; charset="utf-8" +MIME-Version: 1.0 +Content-Transfer-Encoding: 7bit +X-Patchwork-Submitter: Daniel Golle +X-Patchwork-Id: 13541842 +Date: Thu, 1 Feb 2024 21:52:20 +0000 +From: Daniel Golle +To: Bc-bocun Chen , + Steven Liu , + John Crispin , + Chunfeng Yun , + Vinod Koul , + Kishon Vijay Abraham I , + Rob Herring , + Krzysztof Kozlowski , + Conor Dooley , + Daniel Golle , + Qingfang Deng , + SkyLake Huang , + Matthias Brugger , + AngeloGioacchino Del Regno , + Philipp Zabel , + linux-arm-kernel@lists.infradead.org, + linux-mediatek@lists.infradead.org, linux-phy@lists.infradead.org, + devicetree@vger.kernel.org, linux-kernel@vger.kernel.org, + netdev@vger.kernel.org +Subject: [PATCH 1/2] dt-bindings: phy: mediatek,xfi-tphy: add new bindings +Message-ID: + <702afb0c1246d95c90b22e57105304028bdd3083.1706823233.git.daniel@makrotopia.org> +MIME-Version: 1.0 +Content-Disposition: inline +List-Id: Linux Phy Mailing list + +Add bindings for the MediaTek XFI T-PHY Ethernet SerDes PHY found in the +MediaTek MT7988 SoC which can operate at various interfaces modes: + +via USXGMII PCS: + * USXGMII + * 10GBase-R + * 5GBase-R + +via LynxI SGMII PCS: + * 2500Base-X + * 1000Base-X + * Cisco SGMII (MAC side) + +Signed-off-by: Daniel Golle +--- + .../bindings/phy/mediatek,xfi-tphy.yaml | 80 +++++++++++++++++++ + 1 file changed, 80 insertions(+) + create mode 100644 Documentation/devicetree/bindings/phy/mediatek,xfi-tphy.yaml + +--- /dev/null ++++ b/Documentation/devicetree/bindings/phy/mediatek,xfi-tphy.yaml +@@ -0,0 +1,80 @@ ++# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) ++%YAML 1.2 ++--- ++$id: http://devicetree.org/schemas/phy/mediatek,xfi-tphy.yaml# ++$schema: http://devicetree.org/meta-schemas/core.yaml# ++ ++title: MediaTek XFI T-PHY ++ ++maintainers: ++ - Daniel Golle ++ ++description: ++ The MediaTek XFI SerDes T-PHY provides the physical SerDes lanes ++ used by the (10G/5G) USXGMII PCS and (1G/2.5G) LynxI PCS found in ++ MediaTek's 10G-capabale SoCs. ++ ++properties: ++ $nodename: ++ pattern: "^phy@[0-9a-f]+$" ++ ++ compatible: ++ const: mediatek,mt7988-xfi-tphy ++ ++ reg: ++ maxItems: 1 ++ ++ clocks: ++ items: ++ - description: XFI PHY clock ++ - description: XFI register clock ++ ++ clock-names: ++ items: ++ - const: xfipll ++ - const: topxtal ++ ++ resets: ++ items: ++ - description: PEXTP reset ++ ++ mediatek,usxgmii-performance-errata: ++ $ref: /schemas/types.yaml#/definitions/flag ++ description: ++ One instance of the T-PHY on MT7988 suffers from a performance ++ problem in 10GBase-R mode which needs a work-around in the driver. ++ The work-around is enabled using this flag. ++ ++ "#phy-cells": ++ const: 0 ++ ++required: ++ - compatible ++ - reg ++ - clocks ++ - clock-names ++ - resets ++ - "#phy-cells" ++ ++additionalProperties: false ++ ++examples: ++ - | ++ #include ++ soc { ++ #address-cells = <2>; ++ #size-cells = <2>; ++ ++ phy@11f20000 { ++ compatible = "mediatek,mt7988-xfi-tphy"; ++ reg = <0 0x11f20000 0 0x10000>; ++ clocks = <&xfi_pll CLK_XFIPLL_PLL_EN>, ++ <&topckgen CLK_TOP_XFI_PHY_0_XTAL_SEL>; ++ clock-names = "xfipll", "topxtal"; ++ resets = <&watchdog 14>; ++ mediatek,usxgmii-performance-errata; ++ #phy-cells = <0>; ++ }; ++ }; ++ ++... diff --git a/target/linux/generic/pending-6.1/739-02-phy-add-driver-for-MediaTek-XFI-T-PHY.patch b/target/linux/generic/pending-6.1/739-02-phy-add-driver-for-MediaTek-XFI-T-PHY.patch new file mode 100644 index 000000000..1aa36fcd3 --- /dev/null +++ b/target/linux/generic/pending-6.1/739-02-phy-add-driver-for-MediaTek-XFI-T-PHY.patch @@ -0,0 +1,498 @@ +From patchwork Thu Feb 1 21:53:06 2024 +Content-Type: text/plain; charset="utf-8" +MIME-Version: 1.0 +Content-Transfer-Encoding: 7bit +X-Patchwork-Submitter: Daniel Golle +X-Patchwork-Id: 13541843 +Date: Thu, 1 Feb 2024 21:53:06 +0000 +From: Daniel Golle +To: Bc-bocun Chen , + Chunfeng Yun , + Vinod Koul , + Kishon Vijay Abraham I , + Rob Herring , + Krzysztof Kozlowski , + Conor Dooley , + Daniel Golle , + Qingfang Deng , + SkyLake Huang , + Matthias Brugger , + AngeloGioacchino Del Regno , + Philipp Zabel , + linux-arm-kernel@lists.infradead.org, + linux-mediatek@lists.infradead.org, linux-phy@lists.infradead.org, + devicetree@vger.kernel.org, linux-kernel@vger.kernel.org, + netdev@vger.kernel.org +Subject: [PATCH 2/2] phy: add driver for MediaTek XFI T-PHY +Message-ID: + +References: + <702afb0c1246d95c90b22e57105304028bdd3083.1706823233.git.daniel@makrotopia.org> +MIME-Version: 1.0 +Content-Disposition: inline +In-Reply-To: + <702afb0c1246d95c90b22e57105304028bdd3083.1706823233.git.daniel@makrotopia.org> +List-Id: Linux Phy Mailing list + +Add driver for MediaTek's XFI T-PHY, 10 Gigabit/s Ethernet SerDes PHY +which can be found in the MT7988 SoC. + +The PHY can operates only in PHY_MODE_ETHERNET, the submode is one of +PHY_INTERFACE_MODE_* corresponding to the supported modes: + + * USXGMII \ + * 10GBase-R }- USXGMII PCS - XGDM \ + * 5GBase-R / \ + }- Ethernet MAC + * 2500Base-X \ / + * 1000Base-X }- LynxI PCS - GDM / + * Cisco SGMII (MAC side) / + +In order to work-around a performance issue present on the first of +two XFI T-PHYs present in MT7988, special tuning is applied which can be +selected by adding the 'mediatek,usxgmii-performance-errata' property to +the device tree node. + +There is no documentation for most registers used for the +analog/tuning part, however, most of the registers have been partially +reverse-engineered from MediaTek's SDK implementation (an opaque +sequence of 32-bit register writes) and descriptions for all relevant +digital registers and bits such as resets and muxes have been supplied +by MediaTek. + +Signed-off-by: Daniel Golle +--- + MAINTAINERS | 1 + + drivers/phy/mediatek/Kconfig | 12 + + drivers/phy/mediatek/Makefile | 1 + + drivers/phy/mediatek/phy-mtk-xfi-tphy.c | 392 ++++++++++++++++++++++++ + 4 files changed, 406 insertions(+) + create mode 100644 drivers/phy/mediatek/phy-mtk-xfi-tphy.c + +--- a/drivers/phy/mediatek/Kconfig ++++ b/drivers/phy/mediatek/Kconfig +@@ -13,6 +13,18 @@ config PHY_MTK_PCIE + callback for PCIe GEN3 port, it supports software efuse + initialization. + ++config PHY_MTK_XFI_TPHY ++ tristate "MediaTek XFI T-PHY Driver" ++ depends on ARCH_MEDIATEK || COMPILE_TEST ++ depends on OF && OF_ADDRESS ++ depends on HAS_IOMEM ++ select GENERIC_PHY ++ help ++ Say 'Y' here to add support for MediaTek XFI T-PHY driver. ++ The driver provides access to the Ethernet SerDes T-PHY supporting ++ 1GE and 2.5GE modes via the LynxI PCS, and 5GE and 10GE modes ++ via the USXGMII PCS found in MediaTek SoCs with 10G Ethernet. ++ + config PHY_MTK_TPHY + tristate "MediaTek T-PHY Driver" + depends on ARCH_MEDIATEK || COMPILE_TEST +--- a/drivers/phy/mediatek/Makefile ++++ b/drivers/phy/mediatek/Makefile +@@ -8,6 +8,7 @@ obj-$(CONFIG_PHY_MTK_PCIE) += phy-mtk-p + obj-$(CONFIG_PHY_MTK_TPHY) += phy-mtk-tphy.o + obj-$(CONFIG_PHY_MTK_UFS) += phy-mtk-ufs.o + obj-$(CONFIG_PHY_MTK_XSPHY) += phy-mtk-xsphy.o ++obj-$(CONFIG_PHY_MTK_XFI_TPHY) += phy-mtk-xfi-tphy.o + + phy-mtk-hdmi-drv-y := phy-mtk-hdmi.o + phy-mtk-hdmi-drv-y += phy-mtk-hdmi-mt2701.o +--- /dev/null ++++ b/drivers/phy/mediatek/phy-mtk-xfi-tphy.c +@@ -0,0 +1,393 @@ ++// SPDX-License-Identifier: GPL-2.0-or-later ++/* MediaTek 10GE SerDes PHY driver ++ * ++ * Copyright (c) 2024 Daniel Golle ++ * Bc-bocun Chen ++ * based on mtk_usxgmii.c found in MediaTek's SDK released under GPL-2.0 ++ * Copyright (c) 2022 MediaTek Inc. ++ * Author: Henry Yen ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#define MTK_XFI_TPHY_NUM_CLOCKS 2 ++ ++#define REG_DIG_GLB_70 0x0070 ++#define XTP_PCS_RX_EQ_IN_PROGRESS(x) FIELD_PREP(GENMASK(25, 24), (x)) ++#define XTP_PCS_MODE_MASK GENMASK(17, 16) ++#define XTP_PCS_MODE(x) FIELD_PREP(GENMASK(17, 16), (x)) ++#define XTP_PCS_RST_B BIT(15) ++#define XTP_FRC_PCS_RST_B BIT(14) ++#define XTP_PCS_PWD_SYNC_MASK GENMASK(13, 12) ++#define XTP_PCS_PWD_SYNC(x) FIELD_PREP(XTP_PCS_PWD_SYNC_MASK, (x)) ++#define XTP_PCS_PWD_ASYNC_MASK GENMASK(11, 10) ++#define XTP_PCS_PWD_ASYNC(x) FIELD_PREP(XTP_PCS_PWD_ASYNC_MASK, (x)) ++#define XTP_FRC_PCS_PWD_ASYNC BIT(8) ++#define XTP_PCS_UPDT BIT(4) ++#define XTP_PCS_IN_FR_RG BIT(0) ++ ++#define REG_DIG_GLB_F4 0x00f4 ++#define XFI_DPHY_PCS_SEL BIT(0) ++#define XFI_DPHY_PCS_SEL_SGMII FIELD_PREP(XFI_DPHY_PCS_SEL, 1) ++#define XFI_DPHY_PCS_SEL_USXGMII FIELD_PREP(XFI_DPHY_PCS_SEL, 0) ++#define XFI_DPHY_AD_SGDT_FRC_EN BIT(5) ++ ++#define REG_DIG_LN_TRX_40 0x3040 ++#define XTP_LN_FRC_TX_DATA_EN BIT(29) ++#define XTP_LN_TX_DATA_EN BIT(28) ++ ++#define REG_DIG_LN_TRX_B0 0x30b0 ++#define XTP_LN_FRC_TX_MACCK_EN BIT(5) ++#define XTP_LN_TX_MACCK_EN BIT(4) ++ ++#define REG_ANA_GLB_D0 0x90d0 ++#define XTP_GLB_USXGMII_SEL_MASK GENMASK(3, 1) ++#define XTP_GLB_USXGMII_SEL(x) FIELD_PREP(GENMASK(3, 1), (x)) ++#define XTP_GLB_USXGMII_EN BIT(0) ++ ++struct mtk_xfi_tphy { ++ void __iomem *base; ++ struct device *dev; ++ struct reset_control *reset; ++ struct clk_bulk_data clocks[MTK_XFI_TPHY_NUM_CLOCKS]; ++ bool da_war; ++}; ++ ++static void mtk_xfi_tphy_write(struct mtk_xfi_tphy *xfi_tphy, u16 reg, ++ u32 value) ++{ ++ iowrite32(value, xfi_tphy->base + reg); ++} ++ ++static void mtk_xfi_tphy_rmw(struct mtk_xfi_tphy *xfi_tphy, u16 reg, ++ u32 clr, u32 set) ++{ ++ u32 val; ++ ++ val = ioread32(xfi_tphy->base + reg); ++ val &= ~clr; ++ val |= set; ++ iowrite32(val, xfi_tphy->base + reg); ++} ++ ++static void mtk_xfi_tphy_set(struct mtk_xfi_tphy *xfi_tphy, u16 reg, ++ u32 set) ++{ ++ mtk_xfi_tphy_rmw(xfi_tphy, reg, 0, set); ++} ++ ++static void mtk_xfi_tphy_clear(struct mtk_xfi_tphy *xfi_tphy, u16 reg, ++ u32 clr) ++{ ++ mtk_xfi_tphy_rmw(xfi_tphy, reg, clr, 0); ++} ++ ++static void mtk_xfi_tphy_setup(struct mtk_xfi_tphy *xfi_tphy, ++ phy_interface_t interface) ++{ ++ bool is_2p5g = (interface == PHY_INTERFACE_MODE_2500BASEX); ++ bool is_1g = (interface == PHY_INTERFACE_MODE_1000BASEX || ++ interface == PHY_INTERFACE_MODE_SGMII); ++ bool is_10g = (interface == PHY_INTERFACE_MODE_10GBASER || ++ interface == PHY_INTERFACE_MODE_USXGMII); ++ bool is_5g = (interface == PHY_INTERFACE_MODE_5GBASER); ++ bool is_xgmii = (is_10g || is_5g); ++ ++ dev_dbg(xfi_tphy->dev, "setting up for mode %s\n", phy_modes(interface)); ++ ++ /* Setup PLL setting */ ++ mtk_xfi_tphy_rmw(xfi_tphy, 0x9024, 0x100000, is_10g ? 0x0 : 0x100000); ++ mtk_xfi_tphy_rmw(xfi_tphy, 0x2020, 0x202000, is_5g ? 0x202000 : 0x0); ++ mtk_xfi_tphy_rmw(xfi_tphy, 0x2030, 0x500, is_1g ? 0x0 : 0x500); ++ mtk_xfi_tphy_rmw(xfi_tphy, 0x2034, 0xa00, is_1g ? 0x0 : 0xa00); ++ mtk_xfi_tphy_rmw(xfi_tphy, 0x2040, 0x340000, is_1g ? 0x200000 : ++ 0x140000); ++ ++ /* Setup RXFE BW setting */ ++ mtk_xfi_tphy_rmw(xfi_tphy, 0x50f0, 0xc10, is_1g ? 0x410 : ++ is_5g ? 0x800 : 0x400); ++ mtk_xfi_tphy_rmw(xfi_tphy, 0x50e0, 0x4000, is_5g ? 0x0 : 0x4000); ++ ++ /* Setup RX CDR setting */ ++ mtk_xfi_tphy_rmw(xfi_tphy, 0x506c, 0x30000, is_5g ? 0x0 : 0x30000); ++ mtk_xfi_tphy_rmw(xfi_tphy, 0x5070, 0x670000, is_5g ? 0x620000 : 0x50000); ++ mtk_xfi_tphy_rmw(xfi_tphy, 0x5074, 0x180000, is_5g ? 0x180000 : 0x0); ++ mtk_xfi_tphy_rmw(xfi_tphy, 0x5078, 0xf000400, is_5g ? 0x8000000 : ++ 0x7000400); ++ mtk_xfi_tphy_rmw(xfi_tphy, 0x507c, 0x5000500, is_5g ? 0x4000400 : ++ 0x1000100); ++ mtk_xfi_tphy_rmw(xfi_tphy, 0x5080, 0x1410, is_1g ? 0x400 : ++ is_5g ? 0x1010 : 0x0); ++ mtk_xfi_tphy_rmw(xfi_tphy, 0x5084, 0x30300, is_1g ? 0x30300 : ++ is_5g ? 0x30100 : ++ 0x100); ++ mtk_xfi_tphy_rmw(xfi_tphy, 0x5088, 0x60200, is_1g ? 0x20200 : ++ is_5g ? 0x40000 : ++ 0x20000); ++ ++ /* Setting RXFE adaptation range setting */ ++ mtk_xfi_tphy_rmw(xfi_tphy, 0x50e4, 0xc0000, is_5g ? 0x0 : 0xc0000); ++ mtk_xfi_tphy_rmw(xfi_tphy, 0x50e8, 0x40000, is_5g ? 0x0 : 0x40000); ++ mtk_xfi_tphy_rmw(xfi_tphy, 0x50ec, 0xa00, is_1g ? 0x200 : 0x800); ++ mtk_xfi_tphy_rmw(xfi_tphy, 0x50a8, 0xee0000, is_5g ? 0x800000 : ++ 0x6e0000); ++ mtk_xfi_tphy_rmw(xfi_tphy, 0x6004, 0x190000, is_5g ? 0x0 : 0x190000); ++ if (is_10g) ++ mtk_xfi_tphy_write(xfi_tphy, 0x00f8, 0x01423342); ++ else if (is_5g) ++ mtk_xfi_tphy_write(xfi_tphy, 0x00f8, 0x00a132a1); ++ else if (is_2p5g) ++ mtk_xfi_tphy_write(xfi_tphy, 0x00f8, 0x009c329c); ++ else ++ mtk_xfi_tphy_write(xfi_tphy, 0x00f8, 0x00fa32fa); ++ ++ /* Force SGDT_OUT off and select PCS */ ++ mtk_xfi_tphy_rmw(xfi_tphy, REG_DIG_GLB_F4, ++ XFI_DPHY_AD_SGDT_FRC_EN | XFI_DPHY_PCS_SEL, ++ XFI_DPHY_AD_SGDT_FRC_EN | ++ (is_xgmii ? XFI_DPHY_PCS_SEL_USXGMII : ++ XFI_DPHY_PCS_SEL_SGMII)); ++ ++ ++ /* Force GLB_CKDET_OUT */ ++ mtk_xfi_tphy_set(xfi_tphy, 0x0030, 0xc00); ++ ++ /* Force AEQ on */ ++ mtk_xfi_tphy_write(xfi_tphy, REG_DIG_GLB_70, ++ XTP_PCS_RX_EQ_IN_PROGRESS(2) | ++ XTP_PCS_PWD_SYNC(2) | ++ XTP_PCS_PWD_ASYNC(2)); ++ ++ usleep_range(1, 5); ++ writel(XTP_LN_FRC_TX_DATA_EN, xfi_tphy->base + REG_DIG_LN_TRX_40); ++ ++ /* Setup TX DA default value */ ++ mtk_xfi_tphy_rmw(xfi_tphy, 0x30b0, 0x30, 0x20); ++ mtk_xfi_tphy_write(xfi_tphy, 0x3028, 0x00008a01); ++ mtk_xfi_tphy_write(xfi_tphy, 0x302c, 0x0000a884); ++ mtk_xfi_tphy_write(xfi_tphy, 0x3024, 0x00083002); ++ ++ /* Setup RG default value */ ++ if (is_xgmii) { ++ mtk_xfi_tphy_write(xfi_tphy, 0x3010, 0x00022220); ++ mtk_xfi_tphy_write(xfi_tphy, 0x5064, 0x0f020a01); ++ mtk_xfi_tphy_write(xfi_tphy, 0x50b4, 0x06100600); ++ if (interface == PHY_INTERFACE_MODE_USXGMII) ++ mtk_xfi_tphy_write(xfi_tphy, 0x3048, 0x40704000); ++ else ++ mtk_xfi_tphy_write(xfi_tphy, 0x3048, 0x47684100); ++ } else { ++ mtk_xfi_tphy_write(xfi_tphy, 0x3010, 0x00011110); ++ mtk_xfi_tphy_write(xfi_tphy, 0x3048, 0x40704000); ++ } ++ ++ if (is_1g) ++ mtk_xfi_tphy_write(xfi_tphy, 0x3064, 0x0000c000); ++ ++ /* Setup RX EQ initial value */ ++ mtk_xfi_tphy_rmw(xfi_tphy, 0x3050, 0xa8000000, ++ (interface != PHY_INTERFACE_MODE_10GBASER) ? ++ 0xa8000000 : 0x0); ++ mtk_xfi_tphy_rmw(xfi_tphy, 0x3054, 0xaa, ++ (interface != PHY_INTERFACE_MODE_10GBASER) ? ++ 0xaa : 0x0); ++ ++ if (is_xgmii) ++ mtk_xfi_tphy_write(xfi_tphy, 0x306c, 0x00000f00); ++ else if (is_2p5g) ++ mtk_xfi_tphy_write(xfi_tphy, 0x306c, 0x22000f00); ++ else ++ mtk_xfi_tphy_write(xfi_tphy, 0x306c, 0x20200f00); ++ ++ if (interface == PHY_INTERFACE_MODE_10GBASER && xfi_tphy->da_war) ++ mtk_xfi_tphy_rmw(xfi_tphy, 0xa008, 0x10000, 0x10000); ++ ++ mtk_xfi_tphy_rmw(xfi_tphy, 0xa060, 0x50000, is_xgmii ? 0x40000 : ++ 0x50000); ++ ++ /* Setup PHYA speed */ ++ mtk_xfi_tphy_rmw(xfi_tphy, REG_ANA_GLB_D0, ++ XTP_GLB_USXGMII_SEL_MASK | XTP_GLB_USXGMII_EN, ++ is_10g ? XTP_GLB_USXGMII_SEL(0) : ++ is_5g ? XTP_GLB_USXGMII_SEL(1) : ++ is_2p5g ? XTP_GLB_USXGMII_SEL(2) : ++ XTP_GLB_USXGMII_SEL(3)); ++ mtk_xfi_tphy_set(xfi_tphy, REG_ANA_GLB_D0, XTP_GLB_USXGMII_EN); ++ ++ /* Release reset */ ++ mtk_xfi_tphy_set(xfi_tphy, REG_DIG_GLB_70, ++ XTP_PCS_RST_B | XTP_FRC_PCS_RST_B); ++ usleep_range(150, 500); ++ ++ /* Switch to P0 */ ++ mtk_xfi_tphy_rmw(xfi_tphy, REG_DIG_GLB_70, ++ XTP_PCS_PWD_SYNC_MASK | ++ XTP_PCS_PWD_ASYNC_MASK, ++ XTP_FRC_PCS_PWD_ASYNC | ++ XTP_PCS_UPDT | XTP_PCS_IN_FR_RG); ++ usleep_range(1, 5); ++ ++ mtk_xfi_tphy_clear(xfi_tphy, REG_DIG_GLB_70, XTP_PCS_UPDT); ++ usleep_range(15, 50); ++ ++ if (is_xgmii) { ++ /* Switch to Gen3 */ ++ mtk_xfi_tphy_rmw(xfi_tphy, REG_DIG_GLB_70, ++ XTP_PCS_MODE_MASK | XTP_PCS_UPDT, ++ XTP_PCS_MODE(2) | XTP_PCS_UPDT); ++ } else { ++ /* Switch to Gen2 */ ++ mtk_xfi_tphy_rmw(xfi_tphy, REG_DIG_GLB_70, ++ XTP_PCS_MODE_MASK | XTP_PCS_UPDT, ++ XTP_PCS_MODE(1) | XTP_PCS_UPDT); ++ } ++ usleep_range(1, 5); ++ ++ mtk_xfi_tphy_clear(xfi_tphy, REG_DIG_GLB_70, XTP_PCS_UPDT); ++ ++ usleep_range(100, 500); ++ ++ /* Enable MAC CK */ ++ mtk_xfi_tphy_set(xfi_tphy, REG_DIG_LN_TRX_B0, XTP_LN_TX_MACCK_EN); ++ mtk_xfi_tphy_clear(xfi_tphy, REG_DIG_GLB_F4, XFI_DPHY_AD_SGDT_FRC_EN); ++ ++ /* Enable TX data */ ++ mtk_xfi_tphy_set(xfi_tphy, REG_DIG_LN_TRX_40, ++ XTP_LN_FRC_TX_DATA_EN | XTP_LN_TX_DATA_EN); ++ usleep_range(400, 1000); ++} ++ ++static int mtk_xfi_tphy_set_mode(struct phy *phy, enum phy_mode mode, int ++ submode) ++{ ++ struct mtk_xfi_tphy *xfi_tphy = phy_get_drvdata(phy); ++ ++ if (mode != PHY_MODE_ETHERNET) ++ return -EINVAL; ++ ++ switch (submode) { ++ case PHY_INTERFACE_MODE_1000BASEX: ++ case PHY_INTERFACE_MODE_2500BASEX: ++ case PHY_INTERFACE_MODE_SGMII: ++ case PHY_INTERFACE_MODE_5GBASER: ++ case PHY_INTERFACE_MODE_10GBASER: ++ case PHY_INTERFACE_MODE_USXGMII: ++ mtk_xfi_tphy_setup(xfi_tphy, submode); ++ return 0; ++ default: ++ return -EINVAL; ++ } ++} ++ ++static int mtk_xfi_tphy_reset(struct phy *phy) ++{ ++ struct mtk_xfi_tphy *xfi_tphy = phy_get_drvdata(phy); ++ ++ reset_control_assert(xfi_tphy->reset); ++ usleep_range(100, 500); ++ reset_control_deassert(xfi_tphy->reset); ++ usleep_range(1, 10); ++ ++ return 0; ++} ++ ++static int mtk_xfi_tphy_power_on(struct phy *phy) ++{ ++ struct mtk_xfi_tphy *xfi_tphy = phy_get_drvdata(phy); ++ ++ return clk_bulk_prepare_enable(MTK_XFI_TPHY_NUM_CLOCKS, xfi_tphy->clocks); ++} ++ ++static int mtk_xfi_tphy_power_off(struct phy *phy) ++{ ++ struct mtk_xfi_tphy *xfi_tphy = phy_get_drvdata(phy); ++ ++ clk_bulk_disable_unprepare(MTK_XFI_TPHY_NUM_CLOCKS, xfi_tphy->clocks); ++ ++ return 0; ++} ++ ++static const struct phy_ops mtk_xfi_tphy_ops = { ++ .power_on = mtk_xfi_tphy_power_on, ++ .power_off = mtk_xfi_tphy_power_off, ++ .set_mode = mtk_xfi_tphy_set_mode, ++ .reset = mtk_xfi_tphy_reset, ++ .owner = THIS_MODULE, ++}; ++ ++static int mtk_xfi_tphy_probe(struct platform_device *pdev) ++{ ++ struct device_node *np = pdev->dev.of_node; ++ struct phy_provider *phy_provider; ++ struct mtk_xfi_tphy *xfi_tphy; ++ struct phy *phy; ++ ++ if (!np) ++ return -ENODEV; ++ ++ xfi_tphy = devm_kzalloc(&pdev->dev, sizeof(*xfi_tphy), GFP_KERNEL); ++ if (!xfi_tphy) ++ return -ENOMEM; ++ ++ xfi_tphy->base = devm_of_iomap(&pdev->dev, np, 0, NULL); ++ if (!xfi_tphy->base) ++ return -EIO; ++ ++ xfi_tphy->dev = &pdev->dev; ++ ++ xfi_tphy->clocks[0].id = "topxtal"; ++ xfi_tphy->clocks[0].clk = devm_clk_get(&pdev->dev, xfi_tphy->clocks[0].id); ++ if (IS_ERR(xfi_tphy->clocks[0].clk)) ++ return PTR_ERR(xfi_tphy->clocks[0].clk); ++ ++ xfi_tphy->clocks[1].id = "xfipll"; ++ xfi_tphy->clocks[1].clk = devm_clk_get(&pdev->dev, xfi_tphy->clocks[1].id); ++ if (IS_ERR(xfi_tphy->clocks[1].clk)) ++ return PTR_ERR(xfi_tphy->clocks[1].clk); ++ ++ xfi_tphy->reset = devm_reset_control_get_exclusive(&pdev->dev, NULL); ++ if (IS_ERR(xfi_tphy->reset)) ++ return PTR_ERR(xfi_tphy->reset); ++ ++ xfi_tphy->da_war = of_property_read_bool(np, ++ "mediatek,usxgmii-performance-errata"); ++ ++ phy = devm_phy_create(&pdev->dev, NULL, &mtk_xfi_tphy_ops); ++ if (IS_ERR(phy)) ++ return PTR_ERR(phy); ++ ++ phy_set_drvdata(phy, xfi_tphy); ++ ++ phy_provider = devm_of_phy_provider_register(&pdev->dev, ++ of_phy_simple_xlate); ++ ++ return PTR_ERR_OR_ZERO(phy_provider); ++} ++ ++static const struct of_device_id mtk_xfi_tphy_match[] = { ++ { .compatible = "mediatek,mt7988-xfi-tphy", }, ++ { } ++}; ++MODULE_DEVICE_TABLE(of, mtk_xfi_tphy_match); ++ ++static struct platform_driver mtk_xfi_tphy_driver = { ++ .probe = mtk_xfi_tphy_probe, ++ .driver = { ++ .name = "mtk-xfi-tphy", ++ .of_match_table = mtk_xfi_tphy_match, ++ }, ++}; ++module_platform_driver(mtk_xfi_tphy_driver); ++ ++MODULE_DESCRIPTION("MediaTek XFI T-PHY driver"); ++MODULE_AUTHOR("Daniel Golle "); ++MODULE_AUTHOR("Bc-bocun Chen "); ++MODULE_LICENSE("GPL"); diff --git a/target/linux/generic/pending-6.1/739-03-net-pcs-pcs-mtk-lynxi-add-platform-driver-for-MT7988.patch b/target/linux/generic/pending-6.1/739-03-net-pcs-pcs-mtk-lynxi-add-platform-driver-for-MT7988.patch new file mode 100644 index 000000000..adb95e958 --- /dev/null +++ b/target/linux/generic/pending-6.1/739-03-net-pcs-pcs-mtk-lynxi-add-platform-driver-for-MT7988.patch @@ -0,0 +1,371 @@ +From 4b1a2716299c0e96a698044aebf3f80513509ae7 Mon Sep 17 00:00:00 2001 +From: Daniel Golle +Date: Tue, 12 Dec 2023 03:47:18 +0000 +Subject: [PATCH 3/5] net: pcs: pcs-mtk-lynxi: add platform driver for MT7988 + +Introduce a proper platform MFD driver for the LynxI (H)SGMII PCS which +is going to initially be used for the MT7988 SoC. + +Signed-off-by: Daniel Golle +--- + drivers/net/pcs/pcs-mtk-lynxi.c | 227 ++++++++++++++++++++++++++++-- + include/linux/pcs/pcs-mtk-lynxi.h | 11 ++ + 2 files changed, 227 insertions(+), 11 deletions(-) + +--- a/drivers/net/pcs/pcs-mtk-lynxi.c ++++ b/drivers/net/pcs/pcs-mtk-lynxi.c +@@ -1,6 +1,6 @@ + // SPDX-License-Identifier: GPL-2.0 + // Copyright (c) 2018-2019 MediaTek Inc. +-/* A library for MediaTek SGMII circuit ++/* A library and platform driver for the MediaTek LynxI SGMII circuit + * + * Author: Sean Wang + * Author: Alexander Couzens +@@ -8,11 +8,17 @@ + * + */ + ++#include + #include ++#include ++#include + #include ++#include + #include + #include ++#include + #include ++#include + + /* SGMII subsystem config registers */ + /* BMCR (low 16) BMSR (high 16) */ +@@ -65,6 +71,8 @@ + #define SGMII_PN_SWAP_MASK GENMASK(1, 0) + #define SGMII_PN_SWAP_TX_RX (BIT(0) | BIT(1)) + ++#define MTK_NETSYS_V3_AMA_RGC3 0x128 ++ + /* struct mtk_pcs_lynxi - This structure holds each sgmii regmap andassociated + * data + * @regmap: The register map pointing at the range used to setup +@@ -74,15 +82,29 @@ + * @interface: Currently configured interface mode + * @pcs: Phylink PCS structure + * @flags: Flags indicating hardware properties ++ * @rstc: Reset controller ++ * @sgmii_sel: SGMII Register Clock ++ * @sgmii_rx: SGMII RX Clock ++ * @sgmii_tx: SGMII TX Clock ++ * @node: List node + */ + struct mtk_pcs_lynxi { + struct regmap *regmap; ++ struct device *dev; + u32 ana_rgc3; + phy_interface_t interface; + struct phylink_pcs pcs; + u32 flags; ++ struct reset_control *rstc; ++ struct clk *sgmii_sel; ++ struct clk *sgmii_rx; ++ struct clk *sgmii_tx; ++ struct list_head node; + }; + ++static LIST_HEAD(mtk_pcs_lynxi_instances); ++static DEFINE_MUTEX(instance_mutex); ++ + static struct mtk_pcs_lynxi *pcs_to_mtk_pcs_lynxi(struct phylink_pcs *pcs) + { + return container_of(pcs, struct mtk_pcs_lynxi, pcs); +@@ -102,6 +124,17 @@ static void mtk_pcs_lynxi_get_state(stru + FIELD_GET(SGMII_LPA, adv)); + } + ++static void mtk_sgmii_reset(struct mtk_pcs_lynxi *mpcs) ++{ ++ if (!mpcs->rstc) ++ return; ++ ++ reset_control_assert(mpcs->rstc); ++ udelay(100); ++ reset_control_deassert(mpcs->rstc); ++ mdelay(1); ++} ++ + static int mtk_pcs_lynxi_config(struct phylink_pcs *pcs, unsigned int neg_mode, + phy_interface_t interface, + const unsigned long *advertising, +@@ -148,6 +181,7 @@ static int mtk_pcs_lynxi_config(struct p + SGMII_PHYA_PWD); + + /* Reset SGMII PCS state */ ++ mtk_sgmii_reset(mpcs); + regmap_set_bits(mpcs->regmap, SGMSYS_RESERVED_0, + SGMII_SW_RESET); + +@@ -234,10 +268,29 @@ static void mtk_pcs_lynxi_link_up(struct + } + } + ++static int mtk_pcs_lynxi_enable(struct phylink_pcs *pcs) ++{ ++ struct mtk_pcs_lynxi *mpcs = pcs_to_mtk_pcs_lynxi(pcs); ++ ++ if (mpcs->sgmii_tx && mpcs->sgmii_rx) { ++ clk_prepare_enable(mpcs->sgmii_rx); ++ clk_prepare_enable(mpcs->sgmii_tx); ++ } ++ ++ return 0; ++} ++ + static void mtk_pcs_lynxi_disable(struct phylink_pcs *pcs) + { + struct mtk_pcs_lynxi *mpcs = pcs_to_mtk_pcs_lynxi(pcs); + ++ regmap_set_bits(mpcs->regmap, SGMSYS_QPHY_PWR_STATE_CTRL, SGMII_PHYA_PWD); ++ ++ if (mpcs->sgmii_tx && mpcs->sgmii_rx) { ++ clk_disable_unprepare(mpcs->sgmii_tx); ++ clk_disable_unprepare(mpcs->sgmii_rx); ++ } ++ + mpcs->interface = PHY_INTERFACE_MODE_NA; + } + +@@ -247,11 +300,12 @@ static const struct phylink_pcs_ops mtk_ + .pcs_an_restart = mtk_pcs_lynxi_restart_an, + .pcs_link_up = mtk_pcs_lynxi_link_up, + .pcs_disable = mtk_pcs_lynxi_disable, ++ .pcs_enable = mtk_pcs_lynxi_enable, + }; + +-struct phylink_pcs *mtk_pcs_lynxi_create(struct device *dev, +- struct regmap *regmap, u32 ana_rgc3, +- u32 flags) ++static struct phylink_pcs *mtk_pcs_lynxi_init(struct device *dev, struct regmap *regmap, ++ u32 ana_rgc3, u32 flags, ++ struct mtk_pcs_lynxi *prealloc) + { + struct mtk_pcs_lynxi *mpcs; + u32 id, ver; +@@ -259,29 +313,33 @@ struct phylink_pcs *mtk_pcs_lynxi_create + + ret = regmap_read(regmap, SGMSYS_PCS_DEVICE_ID, &id); + if (ret < 0) +- return NULL; ++ return ERR_PTR(ret); + + if (id != SGMII_LYNXI_DEV_ID) { + dev_err(dev, "unknown PCS device id %08x\n", id); +- return NULL; ++ return ERR_PTR(-ENODEV); + } + + ret = regmap_read(regmap, SGMSYS_PCS_SCRATCH, &ver); + if (ret < 0) +- return NULL; ++ return ERR_PTR(ret); + + ver = FIELD_GET(SGMII_DEV_VERSION, ver); + if (ver != 0x1) { + dev_err(dev, "unknown PCS device version %04x\n", ver); +- return NULL; ++ return ERR_PTR(-ENODEV); + } + + dev_dbg(dev, "MediaTek LynxI SGMII PCS (id 0x%08x, ver 0x%04x)\n", id, + ver); + +- mpcs = kzalloc(sizeof(*mpcs), GFP_KERNEL); +- if (!mpcs) +- return NULL; ++ if (prealloc) { ++ mpcs = prealloc; ++ } else { ++ mpcs = kzalloc(sizeof(*mpcs), GFP_KERNEL); ++ if (!mpcs) ++ return ERR_PTR(-ENOMEM); ++ }; + + mpcs->ana_rgc3 = ana_rgc3; + mpcs->regmap = regmap; +@@ -292,6 +350,13 @@ struct phylink_pcs *mtk_pcs_lynxi_create + mpcs->interface = PHY_INTERFACE_MODE_NA; + + return &mpcs->pcs; ++}; ++ ++struct phylink_pcs *mtk_pcs_lynxi_create(struct device *dev, ++ struct regmap *regmap, u32 ana_rgc3, ++ u32 flags) ++{ ++ return mtk_pcs_lynxi_init(dev, regmap, ana_rgc3, flags, NULL); + } + EXPORT_SYMBOL(mtk_pcs_lynxi_create); + +@@ -304,4 +369,144 @@ void mtk_pcs_lynxi_destroy(struct phylin + } + EXPORT_SYMBOL(mtk_pcs_lynxi_destroy); + ++static int mtk_pcs_lynxi_probe(struct platform_device *pdev) ++{ ++ struct device *dev = &pdev->dev; ++ struct device_node *np = dev->of_node; ++ struct mtk_pcs_lynxi *mpcs; ++ struct phylink_pcs *pcs; ++ struct regmap *regmap; ++ u32 flags = 0; ++ ++ mpcs = devm_kzalloc(dev, sizeof(*mpcs), GFP_KERNEL); ++ if (!mpcs) ++ return -ENOMEM; ++ ++ mpcs->dev = dev; ++ regmap = syscon_node_to_regmap(np->parent); ++ if (IS_ERR(regmap)) ++ return PTR_ERR(regmap); ++ ++ if (of_property_read_bool(np->parent, "mediatek,pnswap")) ++ flags |= MTK_SGMII_FLAG_PN_SWAP; ++ ++ mpcs->rstc = of_reset_control_get_shared(np->parent, NULL); ++ if (IS_ERR(mpcs->rstc)) ++ return PTR_ERR(mpcs->rstc); ++ ++ reset_control_deassert(mpcs->rstc); ++ mpcs->sgmii_sel = devm_clk_get_enabled(dev, "sgmii_sel"); ++ if (IS_ERR(mpcs->sgmii_sel)) ++ return PTR_ERR(mpcs->sgmii_sel); ++ ++ mpcs->sgmii_rx = devm_clk_get(dev, "sgmii_rx"); ++ if (IS_ERR(mpcs->sgmii_rx)) ++ return PTR_ERR(mpcs->sgmii_rx); ++ ++ mpcs->sgmii_tx = devm_clk_get(dev, "sgmii_tx"); ++ if (IS_ERR(mpcs->sgmii_tx)) ++ return PTR_ERR(mpcs->sgmii_tx); ++ ++ pcs = mtk_pcs_lynxi_init(dev, regmap, (uintptr_t)of_device_get_match_data(dev), ++ flags, mpcs); ++ if (IS_ERR(pcs)) ++ return PTR_ERR(pcs); ++ ++ regmap_set_bits(mpcs->regmap, SGMSYS_QPHY_PWR_STATE_CTRL, SGMII_PHYA_PWD); ++ ++ platform_set_drvdata(pdev, mpcs); ++ ++ mutex_lock(&instance_mutex); ++ list_add_tail(&mpcs->node, &mtk_pcs_lynxi_instances); ++ mutex_unlock(&instance_mutex); ++ ++ return 0; ++} ++ ++static int mtk_pcs_lynxi_remove(struct platform_device *pdev) ++{ ++ struct device *dev = &pdev->dev; ++ struct mtk_pcs_lynxi *cur, *tmp; ++ ++ mutex_lock(&instance_mutex); ++ list_for_each_entry_safe(cur, tmp, &mtk_pcs_lynxi_instances, node) ++ if (cur->dev == dev) { ++ list_del(&cur->node); ++ kfree(cur); ++ break; ++ } ++ mutex_unlock(&instance_mutex); ++ ++ return 0; ++} ++ ++static const struct of_device_id mtk_pcs_lynxi_of_match[] = { ++ { .compatible = "mediatek,mt7988-sgmii", .data = (void *)MTK_NETSYS_V3_AMA_RGC3 }, ++ { /* sentinel */ }, ++}; ++MODULE_DEVICE_TABLE(of, mtk_pcs_lynxi_of_match); ++ ++struct phylink_pcs *mtk_pcs_lynxi_get(struct device *dev, struct device_node *np) ++{ ++ struct platform_device *pdev; ++ struct mtk_pcs_lynxi *mpcs; ++ ++ if (!np) ++ return NULL; ++ ++ if (!of_device_is_available(np)) ++ return ERR_PTR(-ENODEV); ++ ++ if (!of_match_node(mtk_pcs_lynxi_of_match, np)) ++ return ERR_PTR(-EINVAL); ++ ++ pdev = of_find_device_by_node(np); ++ if (!pdev || !platform_get_drvdata(pdev)) { ++ if (pdev) ++ put_device(&pdev->dev); ++ return ERR_PTR(-EPROBE_DEFER); ++ } ++ ++ mpcs = platform_get_drvdata(pdev); ++ device_link_add(dev, mpcs->dev, DL_FLAG_AUTOREMOVE_CONSUMER); ++ ++ return &mpcs->pcs; ++} ++EXPORT_SYMBOL(mtk_pcs_lynxi_get); ++ ++void mtk_pcs_lynxi_put(struct phylink_pcs *pcs) ++{ ++ struct mtk_pcs_lynxi *cur, *mpcs = NULL; ++ ++ if (!pcs) ++ return; ++ ++ mutex_lock(&instance_mutex); ++ list_for_each_entry(cur, &mtk_pcs_lynxi_instances, node) ++ if (pcs == &cur->pcs) { ++ mpcs = cur; ++ break; ++ } ++ mutex_unlock(&instance_mutex); ++ ++ if (WARN_ON(!mpcs)) ++ return; ++ ++ put_device(mpcs->dev); ++} ++EXPORT_SYMBOL(mtk_pcs_lynxi_put); ++ ++static struct platform_driver mtk_pcs_lynxi_driver = { ++ .driver = { ++ .name = "mtk-pcs-lynxi", ++ .suppress_bind_attrs = true, ++ .of_match_table = mtk_pcs_lynxi_of_match, ++ }, ++ .probe = mtk_pcs_lynxi_probe, ++ .remove = mtk_pcs_lynxi_remove, ++}; ++module_platform_driver(mtk_pcs_lynxi_driver); ++ ++MODULE_AUTHOR("Daniel Golle "); ++MODULE_DESCRIPTION("MediaTek LynxI HSGMII PCS"); + MODULE_LICENSE("GPL"); +--- a/include/linux/pcs/pcs-mtk-lynxi.h ++++ b/include/linux/pcs/pcs-mtk-lynxi.h +@@ -10,4 +10,15 @@ struct phylink_pcs *mtk_pcs_lynxi_create + struct regmap *regmap, + u32 ana_rgc3, u32 flags); + void mtk_pcs_lynxi_destroy(struct phylink_pcs *pcs); ++ ++#if IS_ENABLED(CONFIG_PCS_MTK_LYNXI) ++struct phylink_pcs *mtk_pcs_lynxi_get(struct device *dev, struct device_node *np); ++void mtk_pcs_lynxi_put(struct phylink_pcs *pcs); ++#else ++static inline struct phylink_pcs *mtk_pcs_lynxi_get(struct device *dev, struct device_node *np) ++{ ++ return NULL; ++} ++static inline void mtk_pcs_lynxi_put(struct phylink_pcs *pcs) { } ++#endif /* IS_ENABLED(CONFIG_PCS_MTK_LYNXI) */ + #endif diff --git a/target/linux/generic/pending-6.1/739-04-dt-bindings-net-pcs-add-bindings-for-MediaTek-USXGMI.patch b/target/linux/generic/pending-6.1/739-04-dt-bindings-net-pcs-add-bindings-for-MediaTek-USXGMI.patch new file mode 100644 index 000000000..215bd2ca2 --- /dev/null +++ b/target/linux/generic/pending-6.1/739-04-dt-bindings-net-pcs-add-bindings-for-MediaTek-USXGMI.patch @@ -0,0 +1,81 @@ +From 7d88d79c0f65b27a92754d7547f7af098b3de67b Mon Sep 17 00:00:00 2001 +From: Daniel Golle +Date: Tue, 12 Dec 2023 03:47:31 +0000 +Subject: [PATCH 4/5] dt-bindings: net: pcs: add bindings for MediaTek USXGMII + PCS + +MediaTek's USXGMII can be found in the MT7988 SoC. We need to access +it in order to configure and monitor the Ethernet SerDes link in +USXGMII, 10GBase-R and 5GBase-R mode. By including a wrapped +legacy 1000Base-X/2500Base-X/Cisco SGMII LynxI PCS as well, those +interface modes are also available. + +Signed-off-by: Daniel Golle +--- + .../bindings/net/pcs/mediatek,usxgmii.yaml | 60 +++++++++++++++++++ + 1 file changed, 60 insertions(+) + create mode 100644 Documentation/devicetree/bindings/net/pcs/mediatek,usxgmii.yaml + +--- /dev/null ++++ b/Documentation/devicetree/bindings/net/pcs/mediatek,usxgmii.yaml +@@ -0,0 +1,60 @@ ++# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) ++%YAML 1.2 ++--- ++$id: http://devicetree.org/schemas/net/pcs/mediatek,usxgmii.yaml# ++$schema: http://devicetree.org/meta-schemas/core.yaml# ++ ++title: MediaTek USXGMII PCS ++ ++maintainers: ++ - Daniel Golle ++ ++description: ++ The MediaTek USXGMII PCS provides physical link control and status ++ for USXGMII, 10GBase-R and 5GBase-R links on the SerDes interfaces ++ provided by the PEXTP PHY. ++ In order to also support legacy 2500Base-X, 1000Base-X and Cisco ++ SGMII an existing mediatek,*-sgmiisys LynxI PCS is wrapped to ++ provide those interfaces modes on the same SerDes interfaces shared ++ with the USXGMII PCS. ++ ++properties: ++ $nodename: ++ pattern: "^pcs@[0-9a-f]+$" ++ ++ compatible: ++ const: mediatek,mt7988-usxgmiisys ++ ++ reg: ++ maxItems: 1 ++ ++ clocks: ++ items: ++ - description: USXGMII top-level clock ++ ++ resets: ++ items: ++ - description: XFI reset ++ ++required: ++ - compatible ++ - reg ++ - clocks ++ - resets ++ ++additionalProperties: false ++ ++examples: ++ - | ++ #include ++ #define MT7988_TOPRGU_XFI0_GRST 12 ++ soc { ++ #address-cells = <2>; ++ #size-cells = <2>; ++ usxgmiisys0: pcs@10080000 { ++ compatible = "mediatek,mt7988-usxgmiisys"; ++ reg = <0 0x10080000 0 0x1000>; ++ clocks = <&topckgen CLK_TOP_USXGMII_SBUS_0_SEL>; ++ resets = <&watchdog MT7988_TOPRGU_XFI0_GRST>; ++ }; ++ }; diff --git a/target/linux/generic/pending-6.1/739-05-net-pcs-add-driver-for-MediaTek-USXGMII-PCS.patch b/target/linux/generic/pending-6.1/739-05-net-pcs-add-driver-for-MediaTek-USXGMII-PCS.patch new file mode 100644 index 000000000..fd1bba308 --- /dev/null +++ b/target/linux/generic/pending-6.1/739-05-net-pcs-add-driver-for-MediaTek-USXGMII-PCS.patch @@ -0,0 +1,547 @@ +From dde0e95fff92e9f5009f3bea75278e0e34a48822 Mon Sep 17 00:00:00 2001 +From: Daniel Golle +Date: Tue, 12 Dec 2023 03:47:47 +0000 +Subject: [PATCH 5/5] net: pcs: add driver for MediaTek USXGMII PCS + +Add driver for USXGMII PCS found in the MediaTek MT7988 SoC and supporting +USXGMII, 10GBase-R and 5GBase-R interface modes. + +Signed-off-by: Daniel Golle +--- + MAINTAINERS | 2 + + drivers/net/pcs/Kconfig | 11 + + drivers/net/pcs/Makefile | 1 + + drivers/net/pcs/pcs-mtk-usxgmii.c | 456 ++++++++++++++++++++++++++++ + include/linux/pcs/pcs-mtk-usxgmii.h | 27 ++ + 5 files changed, 497 insertions(+) + create mode 100644 drivers/net/pcs/pcs-mtk-usxgmii.c + create mode 100644 include/linux/pcs/pcs-mtk-usxgmii.h + +--- a/MAINTAINERS ++++ b/MAINTAINERS +@@ -12941,7 +12941,9 @@ M: Daniel Golle + L: netdev@vger.kernel.org + S: Maintained + F: drivers/net/pcs/pcs-mtk-lynxi.c ++F: drivers/net/pcs/pcs-mtk-usxgmii.c + F: include/linux/pcs/pcs-mtk-lynxi.h ++F: include/linux/pcs/pcs-mtk-usxgmii.h + + MEDIATEK I2C CONTROLLER DRIVER + M: Qii Wang +--- a/drivers/net/pcs/Kconfig ++++ b/drivers/net/pcs/Kconfig +@@ -18,6 +18,17 @@ config PCS_LYNX + This module provides helpers to phylink for managing the Lynx PCS + which is part of the Layerscape and QorIQ Ethernet SERDES. + ++config PCS_MTK_USXGMII ++ tristate "MediaTek USXGMII PCS" ++ select PCS_MTK_LYNXI ++ select PHY_MTK_PEXTP ++ select PHYLINK ++ help ++ This module provides a driver for MediaTek's USXGMII PCS supporting ++ 10GBase-R, 5GBase-R and USXGMII interface modes. ++ 1000Base-X, 2500Base-X and Cisco SGMII are supported on the same ++ differential pairs via an embedded LynxI PHY. ++ + config PCS_RZN1_MIIC + tristate "Renesas RZ/N1 MII converter" + depends on OF && (ARCH_RZN1 || COMPILE_TEST) +--- a/drivers/net/pcs/Makefile ++++ b/drivers/net/pcs/Makefile +@@ -8,3 +8,4 @@ obj-$(CONFIG_PCS_LYNX) += pcs-lynx.o + obj-$(CONFIG_PCS_RZN1_MIIC) += pcs-rzn1-miic.o + obj-$(CONFIG_PCS_ALTERA_TSE) += pcs-altera-tse.o + obj-$(CONFIG_PCS_MTK_LYNXI) += pcs-mtk-lynxi.o ++obj-$(CONFIG_PCS_MTK_USXGMII) += pcs-mtk-usxgmii.o +--- /dev/null ++++ b/drivers/net/pcs/pcs-mtk-usxgmii.c +@@ -0,0 +1,456 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (c) 2023 MediaTek Inc. ++ * Author: Henry Yen ++ * Daniel Golle ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++/* USXGMII subsystem config registers */ ++/* Register to control speed */ ++#define RG_PHY_TOP_SPEED_CTRL1 0x80c ++#define USXGMII_RATE_UPDATE_MODE BIT(31) ++#define USXGMII_MAC_CK_GATED BIT(29) ++#define USXGMII_IF_FORCE_EN BIT(28) ++#define USXGMII_RATE_ADAPT_MODE GENMASK(10, 8) ++#define USXGMII_RATE_ADAPT_MODE_X1 0 ++#define USXGMII_RATE_ADAPT_MODE_X2 1 ++#define USXGMII_RATE_ADAPT_MODE_X4 2 ++#define USXGMII_RATE_ADAPT_MODE_X10 3 ++#define USXGMII_RATE_ADAPT_MODE_X100 4 ++#define USXGMII_RATE_ADAPT_MODE_X5 5 ++#define USXGMII_RATE_ADAPT_MODE_X50 6 ++#define USXGMII_XFI_RX_MODE GENMASK(6, 4) ++#define USXGMII_XFI_TX_MODE GENMASK(2, 0) ++#define USXGMII_XFI_MODE_10G 0 ++#define USXGMII_XFI_MODE_5G 1 ++#define USXGMII_XFI_MODE_2P5G 3 ++ ++/* Register to control PCS AN */ ++#define RG_PCS_AN_CTRL0 0x810 ++#define USXGMII_AN_RESTART BIT(31) ++#define USXGMII_AN_SYNC_CNT GENMASK(30, 11) ++#define USXGMII_AN_ENABLE BIT(0) ++ ++#define RG_PCS_AN_CTRL2 0x818 ++#define USXGMII_LINK_TIMER_IDLE_DETECT GENMASK(29, 20) ++#define USXGMII_LINK_TIMER_COMP_ACK_DETECT GENMASK(19, 10) ++#define USXGMII_LINK_TIMER_AN_RESTART GENMASK(9, 0) ++ ++/* Register to read PCS AN status */ ++#define RG_PCS_AN_STS0 0x81c ++#define USXGMII_LPA GENMASK(15, 0) ++#define USXGMII_LPA_LATCH BIT(31) ++ ++/* Register to read PCS link status */ ++#define RG_PCS_RX_STATUS0 0x904 ++#define RG_PCS_RX_STATUS_UPDATE BIT(16) ++#define RG_PCS_RX_LINK_STATUS BIT(2) ++ ++/* struct mtk_usxgmii_pcs - This structure holds each usxgmii PCS ++ * @pcs: Phylink PCS structure ++ * @dev: Pointer to device structure ++ * @base: IO memory to access PCS hardware ++ * @clk: Pointer to USXGMII clk ++ * @reset: Pointer to USXGMII reset control ++ * @interface: Currently selected interface mode ++ * @neg_mode: Currently used phylink neg_mode ++ * @node: List node ++ */ ++struct mtk_usxgmii_pcs { ++ struct phylink_pcs pcs; ++ struct device *dev; ++ void __iomem *base; ++ struct clk *clk; ++ struct reset_control *reset; ++ phy_interface_t interface; ++ unsigned int neg_mode; ++ struct list_head node; ++}; ++ ++static LIST_HEAD(mtk_usxgmii_pcs_instances); ++static DEFINE_MUTEX(instance_mutex); ++ ++static u32 mtk_r32(struct mtk_usxgmii_pcs *mpcs, unsigned int reg) ++{ ++ return ioread32(mpcs->base + reg); ++} ++ ++static void mtk_m32(struct mtk_usxgmii_pcs *mpcs, unsigned int reg, u32 mask, u32 set) ++{ ++ u32 val; ++ ++ val = ioread32(mpcs->base + reg); ++ val &= ~mask; ++ val |= set; ++ iowrite32(val, mpcs->base + reg); ++} ++ ++static struct mtk_usxgmii_pcs *pcs_to_mtk_usxgmii_pcs(struct phylink_pcs *pcs) ++{ ++ return container_of(pcs, struct mtk_usxgmii_pcs, pcs); ++} ++ ++static void mtk_usxgmii_reset(struct mtk_usxgmii_pcs *mpcs) ++{ ++ reset_control_assert(mpcs->reset); ++ udelay(100); ++ reset_control_deassert(mpcs->reset); ++ ++ mdelay(10); ++} ++ ++static int mtk_usxgmii_pcs_config(struct phylink_pcs *pcs, unsigned int neg_mode, ++ phy_interface_t interface, ++ const unsigned long *advertising, ++ bool permit_pause_to_mac) ++{ ++ struct mtk_usxgmii_pcs *mpcs = pcs_to_mtk_usxgmii_pcs(pcs); ++ unsigned int an_ctrl = 0, link_timer = 0, xfi_mode = 0, adapt_mode = 0; ++ bool mode_changed = false; ++ ++ if (interface == PHY_INTERFACE_MODE_USXGMII) { ++ an_ctrl = FIELD_PREP(USXGMII_AN_SYNC_CNT, 0x1FF) | USXGMII_AN_ENABLE; ++ link_timer = FIELD_PREP(USXGMII_LINK_TIMER_IDLE_DETECT, 0x7B) | ++ FIELD_PREP(USXGMII_LINK_TIMER_COMP_ACK_DETECT, 0x7B) | ++ FIELD_PREP(USXGMII_LINK_TIMER_AN_RESTART, 0x7B); ++ xfi_mode = FIELD_PREP(USXGMII_XFI_RX_MODE, USXGMII_XFI_MODE_10G) | ++ FIELD_PREP(USXGMII_XFI_TX_MODE, USXGMII_XFI_MODE_10G); ++ } else if (interface == PHY_INTERFACE_MODE_10GBASER) { ++ an_ctrl = FIELD_PREP(USXGMII_AN_SYNC_CNT, 0x1FF); ++ link_timer = FIELD_PREP(USXGMII_LINK_TIMER_IDLE_DETECT, 0x7B) | ++ FIELD_PREP(USXGMII_LINK_TIMER_COMP_ACK_DETECT, 0x7B) | ++ FIELD_PREP(USXGMII_LINK_TIMER_AN_RESTART, 0x7B); ++ xfi_mode = FIELD_PREP(USXGMII_XFI_RX_MODE, USXGMII_XFI_MODE_10G) | ++ FIELD_PREP(USXGMII_XFI_TX_MODE, USXGMII_XFI_MODE_10G); ++ adapt_mode = USXGMII_RATE_UPDATE_MODE; ++ } else if (interface == PHY_INTERFACE_MODE_5GBASER) { ++ an_ctrl = FIELD_PREP(USXGMII_AN_SYNC_CNT, 0xFF); ++ link_timer = FIELD_PREP(USXGMII_LINK_TIMER_IDLE_DETECT, 0x3D) | ++ FIELD_PREP(USXGMII_LINK_TIMER_COMP_ACK_DETECT, 0x3D) | ++ FIELD_PREP(USXGMII_LINK_TIMER_AN_RESTART, 0x3D); ++ xfi_mode = FIELD_PREP(USXGMII_XFI_RX_MODE, USXGMII_XFI_MODE_5G) | ++ FIELD_PREP(USXGMII_XFI_TX_MODE, USXGMII_XFI_MODE_5G); ++ adapt_mode = USXGMII_RATE_UPDATE_MODE; ++ } else { ++ return -EINVAL; ++ } ++ ++ adapt_mode |= FIELD_PREP(USXGMII_RATE_ADAPT_MODE, USXGMII_RATE_ADAPT_MODE_X1); ++ ++ if (mpcs->interface != interface) { ++ mpcs->interface = interface; ++ mode_changed = true; ++ } ++ ++ mtk_usxgmii_reset(mpcs); ++ ++ /* Setup USXGMII AN ctrl */ ++ mtk_m32(mpcs, RG_PCS_AN_CTRL0, ++ USXGMII_AN_SYNC_CNT | USXGMII_AN_ENABLE, ++ an_ctrl); ++ ++ mtk_m32(mpcs, RG_PCS_AN_CTRL2, ++ USXGMII_LINK_TIMER_IDLE_DETECT | ++ USXGMII_LINK_TIMER_COMP_ACK_DETECT | ++ USXGMII_LINK_TIMER_AN_RESTART, ++ link_timer); ++ ++ mpcs->neg_mode = neg_mode; ++ ++ /* Gated MAC CK */ ++ mtk_m32(mpcs, RG_PHY_TOP_SPEED_CTRL1, ++ USXGMII_MAC_CK_GATED, USXGMII_MAC_CK_GATED); ++ ++ /* Enable interface force mode */ ++ mtk_m32(mpcs, RG_PHY_TOP_SPEED_CTRL1, ++ USXGMII_IF_FORCE_EN, USXGMII_IF_FORCE_EN); ++ ++ /* Setup USXGMII adapt mode */ ++ mtk_m32(mpcs, RG_PHY_TOP_SPEED_CTRL1, ++ USXGMII_RATE_UPDATE_MODE | USXGMII_RATE_ADAPT_MODE, ++ adapt_mode); ++ ++ /* Setup USXGMII speed */ ++ mtk_m32(mpcs, RG_PHY_TOP_SPEED_CTRL1, ++ USXGMII_XFI_RX_MODE | USXGMII_XFI_TX_MODE, ++ xfi_mode); ++ ++ usleep_range(1, 10); ++ ++ /* Un-gated MAC CK */ ++ mtk_m32(mpcs, RG_PHY_TOP_SPEED_CTRL1, USXGMII_MAC_CK_GATED, 0); ++ ++ usleep_range(1, 10); ++ ++ /* Disable interface force mode for the AN mode */ ++ if (an_ctrl & USXGMII_AN_ENABLE) ++ mtk_m32(mpcs, RG_PHY_TOP_SPEED_CTRL1, USXGMII_IF_FORCE_EN, 0); ++ ++ return mode_changed; ++} ++ ++static void mtk_usxgmii_pcs_get_fixed_speed(struct mtk_usxgmii_pcs *mpcs, ++ struct phylink_link_state *state) ++{ ++ u32 val = mtk_r32(mpcs, RG_PHY_TOP_SPEED_CTRL1); ++ int speed; ++ ++ /* Calculate speed from interface speed and rate adapt mode */ ++ switch (FIELD_GET(USXGMII_XFI_RX_MODE, val)) { ++ case USXGMII_XFI_MODE_10G: ++ speed = 10000; ++ break; ++ case USXGMII_XFI_MODE_5G: ++ speed = 5000; ++ break; ++ case USXGMII_XFI_MODE_2P5G: ++ speed = 2500; ++ break; ++ default: ++ state->speed = SPEED_UNKNOWN; ++ return; ++ } ++ ++ switch (FIELD_GET(USXGMII_RATE_ADAPT_MODE, val)) { ++ case USXGMII_RATE_ADAPT_MODE_X100: ++ speed /= 100; ++ break; ++ case USXGMII_RATE_ADAPT_MODE_X50: ++ speed /= 50; ++ break; ++ case USXGMII_RATE_ADAPT_MODE_X10: ++ speed /= 10; ++ break; ++ case USXGMII_RATE_ADAPT_MODE_X5: ++ speed /= 5; ++ break; ++ case USXGMII_RATE_ADAPT_MODE_X4: ++ speed /= 4; ++ break; ++ case USXGMII_RATE_ADAPT_MODE_X2: ++ speed /= 2; ++ break; ++ case USXGMII_RATE_ADAPT_MODE_X1: ++ break; ++ default: ++ state->speed = SPEED_UNKNOWN; ++ return; ++ } ++ ++ state->speed = speed; ++ state->duplex = DUPLEX_FULL; ++} ++ ++static void mtk_usxgmii_pcs_get_an_state(struct mtk_usxgmii_pcs *mpcs, ++ struct phylink_link_state *state) ++{ ++ u16 lpa; ++ ++ /* Refresh LPA by toggling LPA_LATCH */ ++ mtk_m32(mpcs, RG_PCS_AN_STS0, USXGMII_LPA_LATCH, USXGMII_LPA_LATCH); ++ ndelay(1020); ++ mtk_m32(mpcs, RG_PCS_AN_STS0, USXGMII_LPA_LATCH, 0); ++ ndelay(1020); ++ lpa = FIELD_GET(USXGMII_LPA, mtk_r32(mpcs, RG_PCS_AN_STS0)); ++ ++ phylink_decode_usxgmii_word(state, lpa); ++} ++ ++static void mtk_usxgmii_pcs_get_state(struct phylink_pcs *pcs, ++ struct phylink_link_state *state) ++{ ++ struct mtk_usxgmii_pcs *mpcs = pcs_to_mtk_usxgmii_pcs(pcs); ++ ++ /* Refresh USXGMII link status by toggling RG_PCS_AN_STATUS_UPDATE */ ++ mtk_m32(mpcs, RG_PCS_RX_STATUS0, RG_PCS_RX_STATUS_UPDATE, ++ RG_PCS_RX_STATUS_UPDATE); ++ ndelay(1020); ++ mtk_m32(mpcs, RG_PCS_RX_STATUS0, RG_PCS_RX_STATUS_UPDATE, 0); ++ ndelay(1020); ++ ++ /* Read USXGMII link status */ ++ state->link = FIELD_GET(RG_PCS_RX_LINK_STATUS, ++ mtk_r32(mpcs, RG_PCS_RX_STATUS0)); ++ ++ /* Continuously repeat re-configuration sequence until link comes up */ ++ if (!state->link) { ++ mtk_usxgmii_pcs_config(pcs, mpcs->neg_mode, ++ state->interface, NULL, false); ++ return; ++ } ++ ++ if (FIELD_GET(USXGMII_AN_ENABLE, mtk_r32(mpcs, RG_PCS_AN_CTRL0))) ++ mtk_usxgmii_pcs_get_an_state(mpcs, state); ++ else ++ mtk_usxgmii_pcs_get_fixed_speed(mpcs, state); ++} ++ ++static void mtk_usxgmii_pcs_restart_an(struct phylink_pcs *pcs) ++{ ++ struct mtk_usxgmii_pcs *mpcs = pcs_to_mtk_usxgmii_pcs(pcs); ++ ++ mtk_m32(mpcs, RG_PCS_AN_CTRL0, USXGMII_AN_RESTART, USXGMII_AN_RESTART); ++} ++ ++static void mtk_usxgmii_pcs_link_up(struct phylink_pcs *pcs, unsigned int neg_mode, ++ phy_interface_t interface, ++ int speed, int duplex) ++{ ++ /* Reconfiguring USXGMII to ensure the quality of the RX signal ++ * after the line side link up. ++ */ ++ mtk_usxgmii_pcs_config(pcs, neg_mode, interface, NULL, false); ++} ++ ++static void mtk_usxgmii_pcs_disable(struct phylink_pcs *pcs) ++{ ++ struct mtk_usxgmii_pcs *mpcs = pcs_to_mtk_usxgmii_pcs(pcs); ++ ++ mpcs->interface = PHY_INTERFACE_MODE_NA; ++ mpcs->neg_mode = -1; ++} ++ ++static const struct phylink_pcs_ops mtk_usxgmii_pcs_ops = { ++ .pcs_config = mtk_usxgmii_pcs_config, ++ .pcs_get_state = mtk_usxgmii_pcs_get_state, ++ .pcs_an_restart = mtk_usxgmii_pcs_restart_an, ++ .pcs_link_up = mtk_usxgmii_pcs_link_up, ++ .pcs_disable = mtk_usxgmii_pcs_disable, ++}; ++ ++static int mtk_usxgmii_probe(struct platform_device *pdev) ++{ ++ struct device *dev = &pdev->dev; ++ struct mtk_usxgmii_pcs *mpcs; ++ ++ mpcs = devm_kzalloc(dev, sizeof(*mpcs), GFP_KERNEL); ++ if (!mpcs) ++ return -ENOMEM; ++ ++ mpcs->base = devm_platform_ioremap_resource(pdev, 0); ++ if (IS_ERR(mpcs->base)) ++ return PTR_ERR(mpcs->base); ++ ++ mpcs->dev = dev; ++ mpcs->pcs.ops = &mtk_usxgmii_pcs_ops; ++ mpcs->pcs.poll = true; ++ mpcs->pcs.neg_mode = true; ++ mpcs->interface = PHY_INTERFACE_MODE_NA; ++ mpcs->neg_mode = -1; ++ ++ mpcs->clk = devm_clk_get_enabled(mpcs->dev, NULL); ++ if (IS_ERR(mpcs->clk)) ++ return PTR_ERR(mpcs->clk); ++ ++ mpcs->reset = devm_reset_control_get_shared(dev, NULL); ++ if (IS_ERR(mpcs->reset)) ++ return PTR_ERR(mpcs->reset); ++ ++ reset_control_deassert(mpcs->reset); ++ ++ platform_set_drvdata(pdev, mpcs); ++ ++ mutex_lock(&instance_mutex); ++ list_add_tail(&mpcs->node, &mtk_usxgmii_pcs_instances); ++ mutex_unlock(&instance_mutex); ++ ++ return 0; ++} ++ ++static int mtk_usxgmii_remove(struct platform_device *pdev) ++{ ++ struct device *dev = &pdev->dev; ++ struct mtk_usxgmii_pcs *cur, *tmp; ++ ++ mutex_lock(&instance_mutex); ++ list_for_each_entry_safe(cur, tmp, &mtk_usxgmii_pcs_instances, node) ++ if (cur->dev == dev) { ++ list_del(&cur->node); ++ break; ++ } ++ mutex_unlock(&instance_mutex); ++ ++ return 0; ++} ++ ++static const struct of_device_id mtk_usxgmii_of_mtable[] = { ++ { .compatible = "mediatek,mt7988-usxgmiisys" }, ++ { /* sentinel */ }, ++}; ++MODULE_DEVICE_TABLE(of, mtk_usxgmii_of_mtable); ++ ++struct phylink_pcs *mtk_usxgmii_pcs_get(struct device *dev, struct device_node *np) ++{ ++ struct platform_device *pdev; ++ struct mtk_usxgmii_pcs *mpcs; ++ ++ if (!np) ++ return NULL; ++ ++ if (!of_device_is_available(np)) ++ return ERR_PTR(-ENODEV); ++ ++ if (!of_match_node(mtk_usxgmii_of_mtable, np)) ++ return ERR_PTR(-EINVAL); ++ ++ pdev = of_find_device_by_node(np); ++ if (!pdev || !platform_get_drvdata(pdev)) { ++ if (pdev) ++ put_device(&pdev->dev); ++ return ERR_PTR(-EPROBE_DEFER); ++ } ++ ++ mpcs = platform_get_drvdata(pdev); ++ device_link_add(dev, mpcs->dev, DL_FLAG_AUTOREMOVE_CONSUMER); ++ ++ return &mpcs->pcs; ++} ++EXPORT_SYMBOL(mtk_usxgmii_pcs_get); ++ ++void mtk_usxgmii_pcs_put(struct phylink_pcs *pcs) ++{ ++ struct mtk_usxgmii_pcs *cur, *mpcs = NULL; ++ ++ if (!pcs) ++ return; ++ ++ mutex_lock(&instance_mutex); ++ list_for_each_entry(cur, &mtk_usxgmii_pcs_instances, node) ++ if (pcs == &cur->pcs) { ++ mpcs = cur; ++ break; ++ } ++ mutex_unlock(&instance_mutex); ++ ++ if (WARN_ON(!mpcs)) ++ return; ++ ++ put_device(mpcs->dev); ++} ++EXPORT_SYMBOL(mtk_usxgmii_pcs_put); ++ ++static struct platform_driver mtk_usxgmii_driver = { ++ .driver = { ++ .name = "mtk_usxgmii", ++ .suppress_bind_attrs = true, ++ .of_match_table = mtk_usxgmii_of_mtable, ++ }, ++ .probe = mtk_usxgmii_probe, ++ .remove = mtk_usxgmii_remove, ++}; ++module_platform_driver(mtk_usxgmii_driver); ++ ++MODULE_LICENSE("GPL"); ++MODULE_DESCRIPTION("MediaTek USXGMII PCS driver"); ++MODULE_AUTHOR("Daniel Golle "); +--- /dev/null ++++ b/include/linux/pcs/pcs-mtk-usxgmii.h +@@ -0,0 +1,27 @@ ++/* SPDX-License-Identifier: GPL-2.0 */ ++#ifndef __LINUX_PCS_MTK_USXGMII_H ++#define __LINUX_PCS_MTK_USXGMII_H ++ ++#include ++ ++/** ++ * mtk_usxgmii_select_pcs() - Get MediaTek PCS instance ++ * @np: Pointer to device node indentifying a MediaTek USXGMII PCS ++ * @mode: Ethernet PHY interface mode ++ * ++ * Return PCS identified by a device node and the PHY interface mode in use ++ * ++ * Return: Pointer to phylink PCS instance of NULL ++ */ ++#if IS_ENABLED(CONFIG_PCS_MTK_USXGMII) ++struct phylink_pcs *mtk_usxgmii_pcs_get(struct device *dev, struct device_node *np); ++void mtk_usxgmii_pcs_put(struct phylink_pcs *pcs); ++#else ++static inline struct phylink_pcs *mtk_usxgmii_pcs_get(struct device *dev, struct device_node *np) ++{ ++ return NULL; ++} ++static inline void mtk_usxgmii_pcs_put(struct phylink_pcs *pcs) { } ++#endif /* IS_ENABLED(CONFIG_PCS_MTK_USXGMII) */ ++ ++#endif /* __LINUX_PCS_MTK_USXGMII_H */ diff --git a/target/linux/generic/pending-6.1/740-net-phy-motorcomm-Add-missing-include.patch b/target/linux/generic/pending-6.1/740-net-phy-motorcomm-Add-missing-include.patch new file mode 100644 index 000000000..2a1f908cf --- /dev/null +++ b/target/linux/generic/pending-6.1/740-net-phy-motorcomm-Add-missing-include.patch @@ -0,0 +1,22 @@ +From 6f291aa7da199c6486cc229b055dcbcd5cee7a21 Mon Sep 17 00:00:00 2001 +From: Hauke Mehrtens +Date: Sun, 21 May 2023 22:24:56 +0200 +Subject: [PATCH] net: phy: motorcomm: Add missing include + +Directly include linux/bitfield.h which provides FIELD_PREP. + +Signed-off-by: Hauke Mehrtens +--- + drivers/net/phy/motorcomm.c | 1 + + 1 file changed, 1 insertion(+) + +--- a/drivers/net/phy/motorcomm.c ++++ b/drivers/net/phy/motorcomm.c +@@ -6,6 +6,7 @@ + * Author: Frank + */ + ++#include + #include + #include + #include diff --git a/target/linux/generic/pending-6.1/741-net-phy-realtek-support-interrupt-of-RTL8221B.patch b/target/linux/generic/pending-6.1/741-net-phy-realtek-support-interrupt-of-RTL8221B.patch index bcace0e95..249ba5c49 100644 --- a/target/linux/generic/pending-6.1/741-net-phy-realtek-support-interrupt-of-RTL8221B.patch +++ b/target/linux/generic/pending-6.1/741-net-phy-realtek-support-interrupt-of-RTL8221B.patch @@ -1,6 +1,18 @@ +From d7943c31d57c11e1a517aa3ce2006fca44866870 Mon Sep 17 00:00:00 2001 +From: Jianhui Zhao +Date: Sun, 24 Sep 2023 22:15:00 +0800 +Subject: [PATCH] net: phy: realtek: add interrupt support for RTL8221B + +This commit introduces interrupt support for RTL8221B. + +Signed-off-by: Jianhui Zhao +--- + drivers/net/phy/realtek.c | 47 +++++++++++++++++++++++++++++++++++++++ + 1 file changed, 47 insertions(+) + --- a/drivers/net/phy/realtek.c +++ b/drivers/net/phy/realtek.c -@@ -981,6 +981,51 @@ static int rtl8221b_config_init(struct p +@@ -982,6 +982,51 @@ static int rtl8221b_config_init(struct p return 0; } @@ -52,7 +64,7 @@ static struct phy_driver realtek_drvs[] = { { PHY_ID_MATCH_EXACT(0x00008201), -@@ -1143,6 +1188,8 @@ static struct phy_driver realtek_drvs[] +@@ -1142,6 +1187,8 @@ static struct phy_driver realtek_drvs[] .get_features = rtl822x_get_features, .config_init = rtl8221b_config_init, .config_aneg = rtl822x_config_aneg, diff --git a/target/linux/generic/pending-6.1/742-net-phy-air_en8811h-reset-netdev-rules-when-LED-is-s.patch b/target/linux/generic/pending-6.1/742-net-phy-air_en8811h-reset-netdev-rules-when-LED-is-s.patch new file mode 100644 index 000000000..500567b4e --- /dev/null +++ b/target/linux/generic/pending-6.1/742-net-phy-air_en8811h-reset-netdev-rules-when-LED-is-s.patch @@ -0,0 +1,45 @@ +From 9be9a00adfac8118b6d685e71696f83187308c66 Mon Sep 17 00:00:00 2001 +Message-ID: <9be9a00adfac8118b6d685e71696f83187308c66.1715125851.git.daniel@makrotopia.org> +From: Daniel Golle +Date: Tue, 7 May 2024 22:43:30 +0100 +Subject: [PATCH net] net: phy: air_en8811h: reset netdev rules when LED is set + manually +To: Andrew Lunn , + Heiner Kallweit , + Russell King , + David S. Miller , + Eric Dumazet , + Jakub Kicinski , + Paolo Abeni , + SkyLake Huang , + Eric Woudstra , + netdev@vger.kernel.org, + linux-kernel@vger.kernel.org + +Setting LED_OFF via the brightness_set should deactivate hw control, +so make sure netdev trigger rules also get cleared in that case. + +Fixes: 71e79430117d ("net: phy: air_en8811h: Add the Airoha EN8811H PHY driver") +Signed-off-by: Daniel Golle +--- +This is basically a stop-gap measure until unified LED handling has +been implemented accross all MediaTek and Airoha PHYs. +See also +https://patchwork.kernel.org/project/netdevbpf/patch/20240425023325.15586-3-SkyLake.Huang@mediatek.com/ + + drivers/net/phy/air_en8811h.c | 4 ++++ + 1 file changed, 4 insertions(+) + +--- a/drivers/net/phy/air_en8811h.c ++++ b/drivers/net/phy/air_en8811h.c +@@ -544,6 +544,10 @@ static int air_hw_led_on_set(struct phy_ + + changed |= (priv->led[index].rules != 0); + ++ /* clear netdev trigger rules in case LED_OFF has been set */ ++ if (!on) ++ priv->led[index].rules = 0; ++ + if (changed) + return phy_modify_mmd(phydev, MDIO_MMD_VEND2, + AIR_PHY_LED_ON(index), diff --git a/target/linux/generic/pending-6.1/760-net-core-add-optional-threading-for-backlog-processi.patch b/target/linux/generic/pending-6.1/760-net-core-add-optional-threading-for-backlog-processi.patch index 558641f7c..7d5727bda 100644 --- a/target/linux/generic/pending-6.1/760-net-core-add-optional-threading-for-backlog-processi.patch +++ b/target/linux/generic/pending-6.1/760-net-core-add-optional-threading-for-backlog-processi.patch @@ -12,7 +12,7 @@ Signed-off-by: Felix Fietkau --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h -@@ -520,6 +520,7 @@ static inline bool napi_complete(struct +@@ -543,6 +543,7 @@ static inline bool napi_complete(struct } int dev_set_threaded(struct net_device *dev, bool threaded); @@ -20,7 +20,7 @@ Signed-off-by: Felix Fietkau /** * napi_disable - prevent NAPI from scheduling -@@ -3128,6 +3129,7 @@ struct softnet_data { +@@ -3150,6 +3151,7 @@ struct softnet_data { unsigned int processed; unsigned int time_squeeze; unsigned int received_rps; @@ -30,7 +30,7 @@ Signed-off-by: Felix Fietkau #endif --- a/net/core/dev.c +++ b/net/core/dev.c -@@ -4640,7 +4640,7 @@ static int napi_schedule_rps(struct soft +@@ -4625,7 +4625,7 @@ static int napi_schedule_rps(struct soft struct softnet_data *mysd = this_cpu_ptr(&softnet_data); #ifdef CONFIG_RPS @@ -39,7 +39,7 @@ Signed-off-by: Felix Fietkau sd->rps_ipi_next = mysd->rps_ipi_list; mysd->rps_ipi_list = sd; -@@ -5821,6 +5821,8 @@ static DEFINE_PER_CPU(struct work_struct +@@ -5806,6 +5806,8 @@ static DEFINE_PER_CPU(struct work_struct /* Network device is going away, flush any packets still pending */ static void flush_backlog(struct work_struct *work) { @@ -48,7 +48,7 @@ Signed-off-by: Felix Fietkau struct sk_buff *skb, *tmp; struct softnet_data *sd; -@@ -5835,8 +5837,17 @@ static void flush_backlog(struct work_st +@@ -5820,8 +5822,17 @@ static void flush_backlog(struct work_st input_queue_head_incr(sd); } } @@ -66,7 +66,7 @@ Signed-off-by: Felix Fietkau skb_queue_walk_safe(&sd->process_queue, skb, tmp) { if (skb->dev->reg_state == NETREG_UNREGISTERING) { __skb_unlink(skb, &sd->process_queue); -@@ -5844,7 +5855,16 @@ static void flush_backlog(struct work_st +@@ -5829,7 +5840,16 @@ static void flush_backlog(struct work_st input_queue_head_incr(sd); } } @@ -83,7 +83,7 @@ Signed-off-by: Felix Fietkau } static bool flush_required(int cpu) -@@ -5976,6 +5996,7 @@ static int process_backlog(struct napi_s +@@ -5961,6 +5981,7 @@ static int process_backlog(struct napi_s } rps_lock_irq_disable(sd); @@ -91,7 +91,7 @@ Signed-off-by: Felix Fietkau if (skb_queue_empty(&sd->input_pkt_queue)) { /* * Inline a custom version of __napi_complete(). -@@ -5985,7 +6006,8 @@ static int process_backlog(struct napi_s +@@ -5970,7 +5991,8 @@ static int process_backlog(struct napi_s * We can use a plain write instead of clear_bit(), * and we dont need an smp_mb() memory barrier. */ @@ -101,7 +101,7 @@ Signed-off-by: Felix Fietkau again = false; } else { skb_queue_splice_tail_init(&sd->input_pkt_queue, -@@ -6401,6 +6423,55 @@ int dev_set_threaded(struct net_device * +@@ -6386,6 +6408,55 @@ int dev_set_threaded(struct net_device * } EXPORT_SYMBOL(dev_set_threaded); @@ -157,7 +157,7 @@ Signed-off-by: Felix Fietkau void netif_napi_add_weight(struct net_device *dev, struct napi_struct *napi, int (*poll)(struct napi_struct *, int), int weight) { -@@ -11142,6 +11213,9 @@ static int dev_cpu_dead(unsigned int old +@@ -11127,6 +11198,9 @@ static int dev_cpu_dead(unsigned int old raise_softirq_irqoff(NET_TX_SOFTIRQ); local_irq_enable(); @@ -167,7 +167,7 @@ Signed-off-by: Felix Fietkau #ifdef CONFIG_RPS remsd = oldsd->rps_ipi_list; oldsd->rps_ipi_list = NULL; -@@ -11454,6 +11528,7 @@ static int __init net_dev_init(void) +@@ -11439,6 +11513,7 @@ static int __init net_dev_init(void) INIT_CSD(&sd->defer_csd, trigger_rx_softirq, sd); spin_lock_init(&sd->defer_lock); diff --git a/target/linux/generic/pending-6.1/768-net-dsa-mv88e6xxx-Request-assisted-learning-on-CPU-port.patch b/target/linux/generic/pending-6.1/768-net-dsa-mv88e6xxx-Request-assisted-learning-on-CPU-port.patch index 8458f8c1e..13a4d190e 100644 --- a/target/linux/generic/pending-6.1/768-net-dsa-mv88e6xxx-Request-assisted-learning-on-CPU-port.patch +++ b/target/linux/generic/pending-6.1/768-net-dsa-mv88e6xxx-Request-assisted-learning-on-CPU-port.patch @@ -17,7 +17,7 @@ Signed-off-by: Tobias Waldekranz --- a/drivers/net/dsa/mv88e6xxx/chip.c +++ b/drivers/net/dsa/mv88e6xxx/chip.c -@@ -7032,6 +7032,7 @@ static int mv88e6xxx_register_switch(str +@@ -7079,6 +7079,7 @@ static int mv88e6xxx_register_switch(str ds->ops = &mv88e6xxx_switch_ops; ds->ageing_time_min = chip->info->age_time_coeff; ds->ageing_time_max = chip->info->age_time_coeff * U8_MAX; diff --git a/target/linux/generic/pending-6.1/795-mt7530-register-OF-node-for-internal-MDIO-bus.patch b/target/linux/generic/pending-6.1/795-mt7530-register-OF-node-for-internal-MDIO-bus.patch deleted file mode 100644 index df52a6ac2..000000000 --- a/target/linux/generic/pending-6.1/795-mt7530-register-OF-node-for-internal-MDIO-bus.patch +++ /dev/null @@ -1,43 +0,0 @@ -From 1d81e51d6d79d9098013b2e8cdd677bae998c5d8 Mon Sep 17 00:00:00 2001 -From: David Bauer -Date: Fri, 28 Apr 2023 02:22:59 +0200 -Subject: [PATCH 1/2] mt7530: register OF node for internal MDIO bus - -The MT753x switches provide a switch-internal MDIO bus for the embedded -PHYs. - -Register a OF sub-node on the switch OF-node for this internal MDIO bus. -This allows to configure the embedded PHYs using device-tree. - -Signed-off-by: David Bauer ---- - drivers/net/dsa/mt7530.c | 7 ++++++- - 1 file changed, 6 insertions(+), 1 deletion(-) - ---- a/drivers/net/dsa/mt7530.c -+++ b/drivers/net/dsa/mt7530.c -@@ -2360,10 +2360,13 @@ mt7530_setup_mdio(struct mt7530_priv *pr - { - struct dsa_switch *ds = priv->ds; - struct device *dev = priv->dev; -+ struct device_node *np, *mnp; - struct mii_bus *bus; - static int idx; - int ret; - -+ np = priv->dev->of_node; -+ - bus = devm_mdiobus_alloc(dev); - if (!bus) - return -ENOMEM; -@@ -2380,7 +2383,9 @@ mt7530_setup_mdio(struct mt7530_priv *pr - if (priv->irq) - mt7530_setup_mdio_irq(priv); - -- ret = devm_mdiobus_register(dev, bus); -+ mnp = of_get_child_by_name(np, "mdio"); -+ ret = devm_of_mdiobus_register(dev, bus, mnp); -+ of_node_put(mnp); - if (ret) { - dev_err(dev, "failed to register MDIO bus: %d\n", ret); - if (priv->irq) diff --git a/target/linux/generic/pending-6.1/802-nvmem-u-boot-env-align-endianness-of-crc32-values.patch b/target/linux/generic/pending-6.1/802-nvmem-u-boot-env-align-endianness-of-crc32-values.patch index 9b111050e..d07447bcb 100644 --- a/target/linux/generic/pending-6.1/802-nvmem-u-boot-env-align-endianness-of-crc32-values.patch +++ b/target/linux/generic/pending-6.1/802-nvmem-u-boot-env-align-endianness-of-crc32-values.patch @@ -36,9 +36,9 @@ Signed-off-by: Srinivas Kandagatla --- a/drivers/nvmem/u-boot-env.c +++ b/drivers/nvmem/u-boot-env.c -@@ -182,7 +182,7 @@ static int u_boot_env_parse(struct u_boo - crc32_data_len = priv->mtd->size - crc32_data_offset; - data_len = priv->mtd->size - data_offset; +@@ -181,7 +181,7 @@ static int u_boot_env_parse(struct u_boo + crc32_data_len = dev_size - crc32_data_offset; + data_len = dev_size - data_offset; - calc = crc32(~0, buf + crc32_data_offset, crc32_data_len) ^ ~0L; + calc = le32_to_cpu((__le32)crc32(~0, buf + crc32_data_offset, crc32_data_len) ^ ~0L); diff --git a/target/linux/generic/pending-6.1/804-nvmem-core-support-mac-base-fixed-layout-cells.patch b/target/linux/generic/pending-6.1/804-nvmem-core-support-mac-base-fixed-layout-cells.patch new file mode 100644 index 000000000..9bb94a28b --- /dev/null +++ b/target/linux/generic/pending-6.1/804-nvmem-core-support-mac-base-fixed-layout-cells.patch @@ -0,0 +1,124 @@ +From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= +Date: Thu, 13 Jul 2023 18:29:19 +0200 +Subject: [PATCH] nvmem: core: support "mac-base" fixed layout cells + +Fixed layout binding allows specifying "mac-base" NVMEM cells. It's used +for base MAC address (that can be used for calculating relative +addresses). It can be stored in a raw binary format or as an ASCII +string. +--- + +--- a/drivers/nvmem/Kconfig ++++ b/drivers/nvmem/Kconfig +@@ -1,6 +1,7 @@ + # SPDX-License-Identifier: GPL-2.0-only + menuconfig NVMEM + bool "NVMEM Support" ++ select GENERIC_NET_UTILS + imply NVMEM_LAYOUTS + help + Support for NVMEM(Non Volatile Memory) devices like EEPROM, EFUSES... +--- a/drivers/nvmem/core.c ++++ b/drivers/nvmem/core.c +@@ -7,9 +7,12 @@ + */ + + #include ++#include ++#include + #include + #include + #include ++#include + #include + #include + #include +@@ -780,6 +783,62 @@ static int nvmem_validate_keepouts(struc + return 0; + } + ++static int nvmem_mac_base_raw_read(void *context, const char *id, int index, unsigned int offset, ++ void *buf, size_t bytes) ++{ ++ if (WARN_ON(bytes != ETH_ALEN)) ++ return -EINVAL; ++ ++ if (index) ++ eth_addr_add(buf, index); ++ ++ return 0; ++} ++ ++static int nvmem_mac_base_ascii_read(void *context, const char *id, int index, unsigned int offset, ++ void *buf, size_t bytes) ++{ ++ u8 mac[ETH_ALEN]; ++ ++ if (WARN_ON(bytes != 3 * ETH_ALEN - 1)) ++ return -EINVAL; ++ ++ if (!mac_pton(buf, mac)) ++ return -EINVAL; ++ ++ if (index) ++ eth_addr_add(mac, index); ++ ++ ether_addr_copy(buf, mac); ++ ++ return 0; ++} ++ ++static int nvmem_mac_base_hex_read(void *context, const char *id, int index, unsigned int offset, ++ void *buf, size_t bytes) ++{ ++ u8 mac[ETH_ALEN], *hexstr; ++ int i; ++ ++ if (WARN_ON(bytes != 2 * ETH_ALEN)) ++ return -EINVAL; ++ ++ hexstr = (u8 *)buf; ++ for (i = 0; i < ETH_ALEN; i++) { ++ if (!isxdigit(hexstr[i * 2]) || !isxdigit(hexstr[i * 2 + 1])) ++ return -EINVAL; ++ ++ mac[i] = (hex_to_bin(hexstr[i * 2]) << 4) | hex_to_bin(hexstr[i * 2 + 1]); ++ } ++ ++ if (index) ++ eth_addr_add(mac, index); ++ ++ ether_addr_copy(buf, mac); ++ ++ return 0; ++} ++ + static int nvmem_add_cells_from_dt(struct nvmem_device *nvmem, struct device_node *np) + { + struct device *dev = &nvmem->dev; +@@ -814,6 +873,25 @@ static int nvmem_add_cells_from_dt(struc + if (nvmem->fixup_dt_cell_info) + nvmem->fixup_dt_cell_info(nvmem, &info); + ++ if (of_device_is_compatible(np, "fixed-layout")) { ++ if (of_device_is_compatible(child, "mac-base")) { ++ if (info.bytes == ETH_ALEN) { ++ info.raw_len = info.bytes; ++ info.bytes = ETH_ALEN; ++ info.read_post_process = nvmem_mac_base_raw_read; ++ } else if (info.bytes == 2 * ETH_ALEN) { ++ info.raw_len = info.bytes; ++ info.bytes = ETH_ALEN; ++ info.read_post_process = nvmem_mac_base_hex_read; ++ } else if (info.bytes == 3 * ETH_ALEN - 1) { ++ info.raw_len = info.bytes; ++ info.bytes = ETH_ALEN; ++ info.read_post_process = nvmem_mac_base_ascii_read; ++ } ++ ++ } ++ } ++ + ret = nvmem_add_one_cell(nvmem, &info); + kfree(info.name); + if (ret) { diff --git a/target/linux/generic/pending-6.1/870-ARM-dts-nxp-imx7d-pico-add-cpu-supply-nodes.patch b/target/linux/generic/pending-6.1/870-ARM-dts-nxp-imx7d-pico-add-cpu-supply-nodes.patch new file mode 100644 index 000000000..e25389363 --- /dev/null +++ b/target/linux/generic/pending-6.1/870-ARM-dts-nxp-imx7d-pico-add-cpu-supply-nodes.patch @@ -0,0 +1,43 @@ +From d0562705bcd4cb9849156f095b2af0ec1bb53b56 Mon Sep 17 00:00:00 2001 +From: Lech Perczak +Date: Fri, 17 Nov 2023 21:33:04 +0100 +Subject: [PATCH] ARM: dts: nxp: imx7d-pico: add cpu-supply nodes + +The PICO-IMX7D SoM has the usual power supply configuration using +output sw1a of PF3000 PMIC, which was defined in downstream derivative +of linux-imx (see link) in the sources for "Android Things" devkit. +It is required to support CPU frequency scaling. + +Map the respective "cpu-supply" nodes of each core to sw1a of the PMIC. + +Enabling them causes cpufreq-dt, and imx-thermal drivers to probe +successfully, and CPU frequency scaling to function. + +Link: https://android.googlesource.com/platform/hardware/bsp/kernel/nxp/imx-v4.1/+/o-iot-preview-5/arch/arm/boot/dts/imx7d-pico.dtsi#849 + +Cc: Fabio Estevam +Cc: Shawn Guo +Cc: Sascha Hauer + +Signed-off-by: Lech Perczak +--- + arch/arm/boot/dts/imx7d-pico.dtsi | 8 ++++++++ + 1 file changed, 8 insertions(+) + +--- a/arch/arm/boot/dts/imx7d-pico.dtsi ++++ b/arch/arm/boot/dts/imx7d-pico.dtsi +@@ -108,6 +108,14 @@ + assigned-clock-rates = <0>, <32768>; + }; + ++&cpu0 { ++ cpu-supply = <&sw1a_reg>; ++}; ++ ++&cpu1 { ++ cpu-supply = <&sw1a_reg>; ++}; ++ + &ecspi3 { + cs-gpios = <&gpio4 11 GPIO_ACTIVE_LOW>; + pinctrl-names = "default"; diff --git a/target/linux/generic/pending-6.1/880-01-dt-bindings-leds-add-LED_FUNCTION_MOBILE-for-mobile-.patch b/target/linux/generic/pending-6.1/880-01-dt-bindings-leds-add-LED_FUNCTION_MOBILE-for-mobile-.patch new file mode 100644 index 000000000..3321b03f2 --- /dev/null +++ b/target/linux/generic/pending-6.1/880-01-dt-bindings-leds-add-LED_FUNCTION_MOBILE-for-mobile-.patch @@ -0,0 +1,37 @@ +From 38eb5b3370c29515d2ce92adac2d6eba96f276f5 Mon Sep 17 00:00:00 2001 +From: INAGAKI Hiroshi +Date: Wed, 20 Mar 2024 15:32:18 +0900 +Subject: [PATCH v2 1/2] dt-bindings: leds: add LED_FUNCTION_MOBILE for mobile + network + +Add LED_FUNCTION_MOBILE for LEDs that indicate status of mobile network +connection. This is useful to distinguish those LEDs from LEDs that +indicates status of wired "wan" connection. + +example (on stock fw): + +IIJ SA-W2 has "Mobile" LEDs that indicate status (no signal, too low, +low, good) of mobile network connection via dongle connected to USB +port. + +- no signal: (none, turned off) +- too low: green:mobile & red:mobile (amber, blink) +- low: green:mobile & red:mobile (amber, turned on) +- good: green:mobile (turned on) + +Suggested-by: Hauke Mehrtens +Signed-off-by: INAGAKI Hiroshi +--- + include/dt-bindings/leds/common.h | 1 + + 1 file changed, 1 insertion(+) + +--- a/include/dt-bindings/leds/common.h ++++ b/include/dt-bindings/leds/common.h +@@ -90,6 +90,7 @@ + #define LED_FUNCTION_INDICATOR "indicator" + #define LED_FUNCTION_LAN "lan" + #define LED_FUNCTION_MAIL "mail" ++#define LED_FUNCTION_MOBILE "mobile" + #define LED_FUNCTION_MTD "mtd" + #define LED_FUNCTION_PANIC "panic" + #define LED_FUNCTION_PROGRAMMING "programming" diff --git a/target/linux/generic/pending-6.1/880-02-dt-bindings-leds-add-LED_FUNCTION_SPEED_-for-link-sp.patch b/target/linux/generic/pending-6.1/880-02-dt-bindings-leds-add-LED_FUNCTION_SPEED_-for-link-sp.patch new file mode 100644 index 000000000..ab27cd339 --- /dev/null +++ b/target/linux/generic/pending-6.1/880-02-dt-bindings-leds-add-LED_FUNCTION_SPEED_-for-link-sp.patch @@ -0,0 +1,37 @@ +From e22afe910afcfb51b6ba6a0ae776939959727f54 Mon Sep 17 00:00:00 2001 +From: INAGAKI Hiroshi +Date: Wed, 20 Mar 2024 15:59:06 +0900 +Subject: [PATCH v2 2/2] dt-bindings: leds: add LED_FUNCTION_SPEED_* for link + speed on LAN/WAN + +Add LED_FUNCTION_SPEED_LAN and LED_FUNCTION_SPEED_WAN for LEDs that +indicate link speed of ethernet ports on LAN/WAN. This is useful to +distinguish those LEDs from LEDs that indicate link status (up/down). + +example: + +Fortinet FortiGate 30E/50E have LEDs that indicate link speed on each +of the ethernet ports in addition to LEDs that indicate link status +(up/down). + +- 1000 Mbps: green:speed-(lan|wan)-N +- 100 Mbps: amber:speed-(lan|wan)-N +- 10 Mbps: (none, turned off) + +Reviewed-by: Rob Herring +Signed-off-by: INAGAKI Hiroshi +--- + include/dt-bindings/leds/common.h | 2 ++ + 1 file changed, 2 insertions(+) + +--- a/include/dt-bindings/leds/common.h ++++ b/include/dt-bindings/leds/common.h +@@ -96,6 +96,8 @@ + #define LED_FUNCTION_PROGRAMMING "programming" + #define LED_FUNCTION_RX "rx" + #define LED_FUNCTION_SD "sd" ++#define LED_FUNCTION_SPEED_LAN "speed-lan" ++#define LED_FUNCTION_SPEED_WAN "speed-wan" + #define LED_FUNCTION_STANDBY "standby" + #define LED_FUNCTION_TORCH "torch" + #define LED_FUNCTION_TX "tx" diff --git a/target/linux/mediatek/patches-6.1/041-block-fit-partition-parser.patch b/target/linux/mediatek/patches-6.1/041-block-fit-partition-parser.patch index 0bd82e070..49fc7e638 100644 --- a/target/linux/mediatek/patches-6.1/041-block-fit-partition-parser.patch +++ b/target/linux/mediatek/patches-6.1/041-block-fit-partition-parser.patch @@ -19,7 +19,7 @@ Subject: [PATCH] kernel: add block fit partition parser --- a/block/blk.h +++ b/block/blk.h -@@ -415,6 +415,8 @@ void blk_free_ext_minor(unsigned int min +@@ -424,6 +424,8 @@ void blk_free_ext_minor(unsigned int min #define ADDPART_FLAG_NONE 0 #define ADDPART_FLAG_RAID 1 #define ADDPART_FLAG_WHOLEDISK 2 @@ -72,18 +72,17 @@ Subject: [PATCH] kernel: add block fit partition parser +int parse_fit_partitions(struct parsed_partitions *state, u64 start_sector, u64 nr_sectors, int *slot, int add_remain); --- a/block/partitions/core.c +++ b/block/partitions/core.c -@@ -10,6 +10,10 @@ - #include +@@ -11,6 +11,9 @@ #include #include + #include +#ifdef CONFIG_FIT_PARTITION +#include +#endif -+ + #include "check.h" - static int (*check_part[])(struct parsed_partitions *) = { -@@ -46,6 +50,9 @@ static int (*check_part[])(struct parsed +@@ -48,6 +51,9 @@ static int (*const check_part[])(struct #ifdef CONFIG_EFI_PARTITION efi_partition, /* this must come before msdos */ #endif @@ -93,7 +92,7 @@ Subject: [PATCH] kernel: add block fit partition parser #ifdef CONFIG_SGI_PARTITION sgi_partition, #endif -@@ -398,6 +405,11 @@ static struct block_device *add_partitio +@@ -430,6 +436,11 @@ static struct block_device *add_partitio goto out_del; } @@ -105,7 +104,7 @@ Subject: [PATCH] kernel: add block fit partition parser /* everything is up and running, commence */ err = xa_insert(&disk->part_tbl, partno, bdev, GFP_KERNEL); if (err) -@@ -590,6 +602,11 @@ static bool blk_add_partition(struct gen +@@ -622,6 +633,11 @@ static bool blk_add_partition(struct gen (state->parts[p].flags & ADDPART_FLAG_RAID)) md_autodetect_dev(part->bd_dev); @@ -194,7 +193,7 @@ Subject: [PATCH] kernel: add block fit partition parser set_capacity(gd, ((u64)new->size * tr->blksize) >> 9); --- a/drivers/mtd/ubi/block.c +++ b/drivers/mtd/ubi/block.c -@@ -431,7 +431,9 @@ int ubiblock_create(struct ubi_volume_in +@@ -410,7 +410,9 @@ int ubiblock_create(struct ubi_volume_in ret = -ENODEV; goto out_cleanup_disk; } diff --git a/target/linux/mediatek/patches-6.1/940-net-ethernet-mtk_wed-rename-mtk_wed_get_memory_regio.patch b/target/linux/mediatek/patches-6.1/940-net-ethernet-mtk_wed-rename-mtk_wed_get_memory_regio.patch index 2fe565f3b..465f0eaf2 100644 --- a/target/linux/mediatek/patches-6.1/940-net-ethernet-mtk_wed-rename-mtk_wed_get_memory_regio.patch +++ b/target/linux/mediatek/patches-6.1/940-net-ethernet-mtk_wed-rename-mtk_wed_get_memory_regio.patch @@ -15,30 +15,23 @@ Signed-off-by: Lorenzo Bianconi --- a/drivers/net/ethernet/mediatek/mtk_wed_mcu.c +++ b/drivers/net/ethernet/mediatek/mtk_wed_mcu.c -@@ -215,8 +215,8 @@ int mtk_wed_mcu_msg_update(struct mtk_we +@@ -234,8 +234,8 @@ int mtk_wed_mcu_msg_update(struct mtk_we } static int --mtk_wed_get_memory_region(struct mtk_wed_wo *wo, +-mtk_wed_get_memory_region(struct mtk_wed_hw *hw, int index, - struct mtk_wed_wo_memory_region *region) -+mtk_wed_get_reserved_memory_region(struct mtk_wed_wo *wo, ++mtk_wed_get_reserved_memory_region(struct mtk_wed_hw *hw, int index, + struct mtk_wed_wo_memory_region *region) { struct reserved_mem *rmem; struct device_node *np; -@@ -311,13 +311,13 @@ mtk_wed_mcu_load_firmware(struct mtk_wed +@@ -325,7 +325,7 @@ mtk_wed_mcu_load_firmware(struct mtk_wed + if (index < 0) + continue; - /* load firmware region metadata */ - for (i = 0; i < ARRAY_SIZE(mem_region); i++) { -- ret = mtk_wed_get_memory_region(wo, &mem_region[i]); -+ ret = mtk_wed_get_reserved_memory_region(wo, &mem_region[i]); +- ret = mtk_wed_get_memory_region(wo->hw, index, &mem_region[i]); ++ ret = mtk_wed_get_reserved_memory_region(wo->hw, index, &mem_region[i]); if (ret) return ret; } - - wo->boot.name = "wo-boot"; -- ret = mtk_wed_get_memory_region(wo, &wo->boot); -+ ret = mtk_wed_get_reserved_memory_region(wo, &wo->boot); - if (ret) - return ret; - diff --git a/target/linux/mediatek/patches-6.1/942-net-ethernet-mtk_wed-move-cpuboot-in-a-dedicated-dts.patch b/target/linux/mediatek/patches-6.1/942-net-ethernet-mtk_wed-move-cpuboot-in-a-dedicated-dts.patch index 9de4ffa43..b4bea2087 100644 --- a/target/linux/mediatek/patches-6.1/942-net-ethernet-mtk_wed-move-cpuboot-in-a-dedicated-dts.patch +++ b/target/linux/mediatek/patches-6.1/942-net-ethernet-mtk_wed-move-cpuboot-in-a-dedicated-dts.patch @@ -23,15 +23,15 @@ Signed-off-by: Lorenzo Bianconi --- a/drivers/net/ethernet/mediatek/mtk_wed_mcu.c +++ b/drivers/net/ethernet/mediatek/mtk_wed_mcu.c -@@ -18,12 +18,23 @@ +@@ -34,12 +34,23 @@ static struct mtk_wed_wo_memory_region m static u32 wo_r32(struct mtk_wed_wo *wo, u32 reg) { -- return readl(wo->boot.addr + reg); +- return readl(mem_region[MTK_WED_WO_REGION_BOOT].addr + reg); + u32 val; + + if (!wo->boot_regmap) -+ return readl(wo->boot.addr + reg); ++ return readl(mem_region[MTK_WED_WO_REGION_BOOT].addr + reg); + + if (regmap_read(wo->boot_regmap, reg, &val)) + val = ~0; @@ -41,49 +41,41 @@ Signed-off-by: Lorenzo Bianconi static void wo_w32(struct mtk_wed_wo *wo, u32 reg, u32 val) { -- writel(val, wo->boot.addr + reg); +- writel(val, mem_region[MTK_WED_WO_REGION_BOOT].addr + reg); + if (wo->boot_regmap) + regmap_write(wo->boot_regmap, reg, val); + else -+ writel(val, wo->boot.addr + reg); ++ writel(val, mem_region[MTK_WED_WO_REGION_BOOT].addr + reg); } static struct sk_buff * -@@ -316,10 +327,21 @@ mtk_wed_mcu_load_firmware(struct mtk_wed - return ret; - } +@@ -313,6 +324,9 @@ mtk_wed_mcu_load_firmware(struct mtk_wed + u32 val, boot_cr; + int ret, i; -- wo->boot.name = "wo-boot"; -- ret = mtk_wed_get_reserved_memory_region(wo, &wo->boot); -- if (ret) -- return ret; + wo->boot_regmap = syscon_regmap_lookup_by_phandle(wo->hw->node, + "mediatek,wo-cpuboot"); -+ if (IS_ERR(wo->boot_regmap)) { -+ if (wo->boot_regmap != ERR_PTR(-ENODEV)) -+ return PTR_ERR(wo->boot_regmap); + -+ /* For backward compatibility, we need to check if cpu_boot -+ * is defined through reserved memory property. -+ */ -+ wo->boot_regmap = NULL; -+ wo->boot.name = "wo-boot"; -+ ret = mtk_wed_get_reserved_memory_region(wo, &wo->boot); -+ if (ret) -+ return ret; -+ } + /* load firmware region metadata */ + for (i = 0; i < ARRAY_SIZE(mem_region); i++) { + int index = of_property_match_string(wo->hw->node, +@@ -321,6 +335,9 @@ mtk_wed_mcu_load_firmware(struct mtk_wed + if (index < 0) + continue; - /* set dummy cr */ - wed_w32(wo->hw->wed_dev, MTK_WED_SCR0 + 4 * MTK_WED_DUMMY_CR_FWDL, ++ if (index == MTK_WED_WO_REGION_BOOT && !IS_ERR(wo->boot_regmap)) ++ continue; ++ + ret = mtk_wed_get_reserved_memory_region(wo->hw, index, &mem_region[i]); + if (ret) + return ret; --- a/drivers/net/ethernet/mediatek/mtk_wed_wo.h +++ b/drivers/net/ethernet/mediatek/mtk_wed_wo.h -@@ -228,7 +228,8 @@ struct mtk_wed_wo_queue { - +@@ -231,6 +231,7 @@ struct mtk_wed_wo_queue { struct mtk_wed_wo { struct mtk_wed_hw *hw; -- struct mtk_wed_wo_memory_region boot; -+ struct mtk_wed_wo_memory_region boot; /* backward compatibility */ -+ struct regmap *boot_regmap; ++ struct regmap *boot_regmap; struct mtk_wed_wo_queue q_tx; struct mtk_wed_wo_queue q_rx; + diff --git a/target/linux/mediatek/patches-6.1/943-net-ethernet-mtk_wed-move-ilm-a-dedicated-dts-node.patch b/target/linux/mediatek/patches-6.1/943-net-ethernet-mtk_wed-move-ilm-a-dedicated-dts-node.patch index 7b6c5d1e2..b4ba5b0d2 100644 --- a/target/linux/mediatek/patches-6.1/943-net-ethernet-mtk_wed-move-ilm-a-dedicated-dts-node.patch +++ b/target/linux/mediatek/patches-6.1/943-net-ethernet-mtk_wed-move-ilm-a-dedicated-dts-node.patch @@ -20,72 +20,67 @@ Signed-off-by: Lorenzo Bianconi --- a/drivers/net/ethernet/mediatek/mtk_wed_mcu.c +++ b/drivers/net/ethernet/mediatek/mtk_wed_mcu.c -@@ -300,6 +300,52 @@ next: +@@ -316,6 +316,39 @@ next: } static int -+mtk_wed_mcu_load_memory_regions(struct mtk_wed_wo *wo, -+ struct mtk_wed_wo_memory_region *region) ++mtk_wed_mcu_load_ilm(struct mtk_wed_wo *wo) +{ ++ struct mtk_wed_wo_memory_region *ilm_region; ++ struct resource res; + struct device_node *np; + int ret; + -+ /* firmware EMI memory region */ -+ ret = mtk_wed_get_reserved_memory_region(wo, -+ ®ion[MTK_WED_WO_REGION_EMI]); -+ if (ret) -+ return ret; -+ -+ /* firmware DATA memory region */ -+ ret = mtk_wed_get_reserved_memory_region(wo, -+ ®ion[MTK_WED_WO_REGION_DATA]); -+ if (ret) -+ return ret; -+ + np = of_parse_phandle(wo->hw->node, "mediatek,wo-ilm", 0); -+ if (np) { -+ struct mtk_wed_wo_memory_region *ilm_region; -+ struct resource res; ++ if (!np) ++ return 0; + -+ ret = of_address_to_resource(np, 0, &res); -+ of_node_put(np); ++ ret = of_address_to_resource(np, 0, &res); ++ of_node_put(np); + -+ if (ret < 0) -+ return ret; ++ if (ret < 0) ++ return ret; + -+ ilm_region = ®ion[MTK_WED_WO_REGION_ILM]; -+ ilm_region->phy_addr = res.start; -+ ilm_region->size = resource_size(&res); -+ ilm_region->addr = devm_ioremap(wo->hw->dev, res.start, -+ resource_size(&res)); ++ ilm_region = &mem_region[MTK_WED_WO_REGION_ILM]; ++ ilm_region->phy_addr = res.start; ++ ilm_region->size = resource_size(&res); ++ ilm_region->addr = devm_ioremap(wo->hw->dev, res.start, ++ resource_size(&res)); + -+ return IS_ERR(ilm_region->addr) ? PTR_ERR(ilm_region->addr) : 0; -+ } ++ if (!IS_ERR(ilm_region->addr)) ++ return 0; + -+ /* For backward compatibility, we need to check if ILM -+ * node is defined through reserved memory property. -+ */ -+ return mtk_wed_get_reserved_memory_region(wo, -+ ®ion[MTK_WED_WO_REGION_ILM]); ++ ret = PTR_ERR(ilm_region->addr); ++ ilm_region->addr = NULL; ++ ++ return ret; +} + +static int mtk_wed_mcu_load_firmware(struct mtk_wed_wo *wo) { - static struct mtk_wed_wo_memory_region mem_region[] = { -@@ -320,12 +366,9 @@ mtk_wed_mcu_load_firmware(struct mtk_wed + const struct mtk_wed_fw_trailer *trailer; +@@ -324,14 +357,20 @@ mtk_wed_mcu_load_firmware(struct mtk_wed u32 val, boot_cr; int ret, i; -- /* load firmware region metadata */ -- for (i = 0; i < ARRAY_SIZE(mem_region); i++) { -- ret = mtk_wed_get_reserved_memory_region(wo, &mem_region[i]); -- if (ret) -- return ret; -- } -+ ret = mtk_wed_mcu_load_memory_regions(wo, mem_region); -+ if (ret) -+ return ret; - ++ mtk_wed_mcu_load_ilm(wo); wo->boot_regmap = syscon_regmap_lookup_by_phandle(wo->hw->node, "mediatek,wo-cpuboot"); + + /* load firmware region metadata */ + for (i = 0; i < ARRAY_SIZE(mem_region); i++) { +- int index = of_property_match_string(wo->hw->node, +- "memory-region-names", +- mem_region[i].name); ++ int index; ++ ++ if (mem_region[i].addr) ++ continue; ++ ++ index = of_property_match_string(wo->hw->node, ++ "memory-region-names", ++ mem_region[i].name); + if (index < 0) + continue; +